From c424d5835307ca53afe27b5105923aa36b36df15 Mon Sep 17 00:00:00 2001 From: "Prebid.js automated release" Date: Thu, 16 Nov 2023 20:02:11 +0000 Subject: [PATCH 001/246] Prebid 8.24.0 release --- package-lock.json | 2 +- package.json | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/package-lock.json b/package-lock.json index 138cd45fc61..a05ab54b862 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,6 +1,6 @@ { "name": "prebid.js", - "version": "8.24.0-pre", + "version": "8.24.0", "lockfileVersion": 2, "requires": true, "packages": { diff --git a/package.json b/package.json index 75481237d1b..2e8822cd0be 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "prebid.js", - "version": "8.24.0-pre", + "version": "8.24.0", "description": "Header Bidding Management Library", "main": "src/prebid.js", "scripts": { From 055a483f52c1a5ac1e374158f0aa59983a058f41 Mon Sep 17 00:00:00 2001 From: "Prebid.js automated release" Date: Thu, 16 Nov 2023 20:02:11 +0000 Subject: [PATCH 002/246] Increment version to 8.25.0-pre --- package-lock.json | 2 +- package.json | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/package-lock.json b/package-lock.json index a05ab54b862..caee07c67f6 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,6 +1,6 @@ { "name": "prebid.js", - "version": "8.24.0", + "version": "8.25.0-pre", "lockfileVersion": 2, "requires": true, "packages": { diff --git a/package.json b/package.json index 2e8822cd0be..7076d012285 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "prebid.js", - "version": "8.24.0", + "version": "8.25.0-pre", "description": "Header Bidding Management Library", "main": "src/prebid.js", "scripts": { From 879b9be7f0284ce9d1958065ecaf981efe428106 Mon Sep 17 00:00:00 2001 From: Denis Anoykin Date: Thu, 16 Nov 2023 21:57:07 +0100 Subject: [PATCH 003/246] AsterioBid Analytics Adapter : initial release (#10652) --- modules/asteriobidAnalyticsAdapter.js | 336 ++++++++++++++++++ modules/asteriobidAnalyticsAdapter.md | 41 +++ .../asteriobidAnalyticsAdapter_spec.js | 151 ++++++++ 3 files changed, 528 insertions(+) create mode 100644 modules/asteriobidAnalyticsAdapter.js create mode 100644 modules/asteriobidAnalyticsAdapter.md create mode 100644 test/spec/modules/asteriobidAnalyticsAdapter_spec.js diff --git a/modules/asteriobidAnalyticsAdapter.js b/modules/asteriobidAnalyticsAdapter.js new file mode 100644 index 00000000000..516a3a65667 --- /dev/null +++ b/modules/asteriobidAnalyticsAdapter.js @@ -0,0 +1,336 @@ +import { generateUUID, getParameterByName, logError, logInfo, parseUrl } from '../src/utils.js' +import { ajaxBuilder } from '../src/ajax.js' +import adapter from '../libraries/analyticsAdapter/AnalyticsAdapter.js' +import adapterManager from '../src/adapterManager.js' +import { getStorageManager } from '../src/storageManager.js' +import CONSTANTS from '../src/constants.json' +import { MODULE_TYPE_ANALYTICS } from '../src/activities/modules.js' +import {getRefererInfo} from '../src/refererDetection.js'; + +/** + * asteriobidAnalyticsAdapter.js - analytics adapter for AsterioBid + */ +export const storage = getStorageManager({ moduleType: MODULE_TYPE_ANALYTICS, moduleName: 'asteriobid' }) +const DEFAULT_EVENT_URL = 'https://endpt.asteriobid.com/endpoint' +const analyticsType = 'endpoint' +const analyticsName = 'AsterioBid Analytics' +const utmTags = ['utm_source', 'utm_medium', 'utm_campaign', 'utm_term', 'utm_content'] +const _VERSION = 1 + +let ajax = ajaxBuilder(20000) +let initOptions +let auctionStarts = {} +let auctionTimeouts = {} +let sampling +let pageViewId +let flushInterval +let eventQueue = [] +let asteriobidAnalyticsEnabled = false + +let asteriobidAnalytics = Object.assign(adapter({ url: DEFAULT_EVENT_URL, analyticsType }), { + track({ eventType, args }) { + handleEvent(eventType, args) + } +}) + +asteriobidAnalytics.originEnableAnalytics = asteriobidAnalytics.enableAnalytics +asteriobidAnalytics.enableAnalytics = function (config) { + initOptions = config.options || {} + + pageViewId = initOptions.pageViewId || generateUUID() + sampling = initOptions.sampling || 1 + + if (Math.floor(Math.random() * sampling) === 0) { + asteriobidAnalyticsEnabled = true + flushInterval = setInterval(flush, 1000) + } else { + logInfo(`${analyticsName} isn't enabled because of sampling`) + } + + asteriobidAnalytics.originEnableAnalytics(config) +} + +asteriobidAnalytics.originDisableAnalytics = asteriobidAnalytics.disableAnalytics +asteriobidAnalytics.disableAnalytics = function () { + if (!asteriobidAnalyticsEnabled) { + return + } + flush() + clearInterval(flushInterval) + asteriobidAnalytics.originDisableAnalytics() +} + +function collectUtmTagData() { + let newUtm = false + let pmUtmTags = {} + try { + utmTags.forEach(function (utmKey) { + let utmValue = getParameterByName(utmKey) + if (utmValue !== '') { + newUtm = true + } + pmUtmTags[utmKey] = utmValue + }) + if (newUtm === false) { + utmTags.forEach(function (utmKey) { + let itemValue = storage.getDataFromLocalStorage(`pm_${utmKey}`) + if (itemValue && itemValue.length !== 0) { + pmUtmTags[utmKey] = itemValue + } + }) + } else { + utmTags.forEach(function (utmKey) { + storage.setDataInLocalStorage(`pm_${utmKey}`, pmUtmTags[utmKey]) + }) + } + } catch (e) { + logError(`${analyticsName} Error`, e) + pmUtmTags['error_utm'] = 1 + } + return pmUtmTags +} + +function collectPageInfo() { + const pageInfo = { + domain: window.location.hostname, + } + if (document.referrer) { + pageInfo.referrerDomain = parseUrl(document.referrer).hostname + } + + const refererInfo = getRefererInfo() + pageInfo.page = refererInfo.page + pageInfo.ref = refererInfo.ref + + return pageInfo +} + +function flush() { + if (!asteriobidAnalyticsEnabled) { + return + } + + if (eventQueue.length > 0) { + const data = { + pageViewId: pageViewId, + ver: _VERSION, + bundleId: initOptions.bundleId, + events: eventQueue, + utmTags: collectUtmTagData(), + pageInfo: collectPageInfo(), + sampling: sampling + } + eventQueue = [] + + if ('version' in initOptions) { + data.version = initOptions.version + } + if ('tcf_compliant' in initOptions) { + data.tcf_compliant = initOptions.tcf_compliant + } + if ('adUnitDict' in initOptions) { + data.adUnitDict = initOptions.adUnitDict; + } + if ('customParam' in initOptions) { + data.customParam = initOptions.customParam; + } + + const url = initOptions.url ? initOptions.url : DEFAULT_EVENT_URL + ajax( + url, + () => logInfo(`${analyticsName} sent events batch`), + _VERSION + ':' + JSON.stringify(data), + { + contentType: 'text/plain', + method: 'POST', + withCredentials: true + } + ) + } +} + +function trimAdUnit(adUnit) { + if (!adUnit) return adUnit + const res = {} + res.code = adUnit.code + res.sizes = adUnit.sizes + return res +} + +function trimBid(bid) { + if (!bid) return bid + const res = {} + res.auctionId = bid.auctionId + res.bidder = bid.bidder + res.bidderRequestId = bid.bidderRequestId + res.bidId = bid.bidId + res.crumbs = bid.crumbs + res.cpm = bid.cpm + res.currency = bid.currency + res.mediaTypes = bid.mediaTypes + res.sizes = bid.sizes + res.transactionId = bid.transactionId + res.adUnitCode = bid.adUnitCode + res.bidRequestsCount = bid.bidRequestsCount + res.serverResponseTimeMs = bid.serverResponseTimeMs + return res +} + +function trimBidderRequest(bidderRequest) { + if (!bidderRequest) return bidderRequest + const res = {} + res.auctionId = bidderRequest.auctionId + res.auctionStart = bidderRequest.auctionStart + res.bidderRequestId = bidderRequest.bidderRequestId + res.bidderCode = bidderRequest.bidderCode + res.bids = bidderRequest.bids && bidderRequest.bids.map(trimBid) + return res +} + +function handleEvent(eventType, eventArgs) { + if (!asteriobidAnalyticsEnabled) { + return + } + + try { + eventArgs = eventArgs ? JSON.parse(JSON.stringify(eventArgs)) : {} + } catch (e) { + // keep eventArgs as is + } + + const pmEvent = {} + pmEvent.timestamp = eventArgs.timestamp || Date.now() + pmEvent.eventType = eventType + + switch (eventType) { + case CONSTANTS.EVENTS.AUCTION_INIT: { + pmEvent.auctionId = eventArgs.auctionId + pmEvent.timeout = eventArgs.timeout + pmEvent.adUnits = eventArgs.adUnits && eventArgs.adUnits.map(trimAdUnit) + pmEvent.bidderRequests = eventArgs.bidderRequests && eventArgs.bidderRequests.map(trimBidderRequest) + auctionStarts[pmEvent.auctionId] = pmEvent.timestamp + auctionTimeouts[pmEvent.auctionId] = pmEvent.timeout + break + } + case CONSTANTS.EVENTS.AUCTION_END: { + pmEvent.auctionId = eventArgs.auctionId + pmEvent.end = eventArgs.end + pmEvent.start = eventArgs.start + pmEvent.adUnitCodes = eventArgs.adUnitCodes + pmEvent.bidsReceived = eventArgs.bidsReceived && eventArgs.bidsReceived.map(trimBid) + pmEvent.start = auctionStarts[pmEvent.auctionId] + pmEvent.end = Date.now() + break + } + case CONSTANTS.EVENTS.BID_ADJUSTMENT: { + break + } + case CONSTANTS.EVENTS.BID_TIMEOUT: { + pmEvent.bidders = eventArgs && eventArgs.map ? eventArgs.map(trimBid) : eventArgs + pmEvent.duration = auctionTimeouts[pmEvent.auctionId] + break + } + case CONSTANTS.EVENTS.BID_REQUESTED: { + pmEvent.auctionId = eventArgs.auctionId + pmEvent.bidderCode = eventArgs.bidderCode + pmEvent.doneCbCallCount = eventArgs.doneCbCallCount + pmEvent.start = eventArgs.start + pmEvent.bidderRequestId = eventArgs.bidderRequestId + pmEvent.bids = eventArgs.bids && eventArgs.bids.map(trimBid) + pmEvent.auctionStart = eventArgs.auctionStart + pmEvent.timeout = eventArgs.timeout + break + } + case CONSTANTS.EVENTS.BID_RESPONSE: { + pmEvent.bidderCode = eventArgs.bidderCode + pmEvent.width = eventArgs.width + pmEvent.height = eventArgs.height + pmEvent.adId = eventArgs.adId + pmEvent.mediaType = eventArgs.mediaType + pmEvent.cpm = eventArgs.cpm + pmEvent.currency = eventArgs.currency + pmEvent.requestId = eventArgs.requestId + pmEvent.adUnitCode = eventArgs.adUnitCode + pmEvent.auctionId = eventArgs.auctionId + pmEvent.timeToRespond = eventArgs.timeToRespond + pmEvent.requestTimestamp = eventArgs.requestTimestamp + pmEvent.responseTimestamp = eventArgs.responseTimestamp + pmEvent.netRevenue = eventArgs.netRevenue + pmEvent.size = eventArgs.size + pmEvent.adserverTargeting = eventArgs.adserverTargeting + break + } + case CONSTANTS.EVENTS.BID_WON: { + pmEvent.auctionId = eventArgs.auctionId + pmEvent.adId = eventArgs.adId + pmEvent.adserverTargeting = eventArgs.adserverTargeting + pmEvent.adUnitCode = eventArgs.adUnitCode + pmEvent.bidderCode = eventArgs.bidderCode + pmEvent.height = eventArgs.height + pmEvent.mediaType = eventArgs.mediaType + pmEvent.netRevenue = eventArgs.netRevenue + pmEvent.cpm = eventArgs.cpm + pmEvent.requestTimestamp = eventArgs.requestTimestamp + pmEvent.responseTimestamp = eventArgs.responseTimestamp + pmEvent.size = eventArgs.size + pmEvent.width = eventArgs.width + pmEvent.currency = eventArgs.currency + pmEvent.bidder = eventArgs.bidder + break + } + case CONSTANTS.EVENTS.BIDDER_DONE: { + pmEvent.auctionId = eventArgs.auctionId + pmEvent.auctionStart = eventArgs.auctionStart + pmEvent.bidderCode = eventArgs.bidderCode + pmEvent.bidderRequestId = eventArgs.bidderRequestId + pmEvent.bids = eventArgs.bids && eventArgs.bids.map(trimBid) + pmEvent.doneCbCallCount = eventArgs.doneCbCallCount + pmEvent.start = eventArgs.start + pmEvent.timeout = eventArgs.timeout + pmEvent.tid = eventArgs.tid + pmEvent.src = eventArgs.src + break + } + case CONSTANTS.EVENTS.SET_TARGETING: { + break + } + case CONSTANTS.EVENTS.REQUEST_BIDS: { + break + } + case CONSTANTS.EVENTS.ADD_AD_UNITS: { + break + } + case CONSTANTS.EVENTS.AD_RENDER_FAILED: { + pmEvent.bid = eventArgs.bid + pmEvent.message = eventArgs.message + pmEvent.reason = eventArgs.reason + break + } + default: + return + } + + sendEvent(pmEvent) +} + +function sendEvent(event) { + eventQueue.push(event) + logInfo(`${analyticsName} Event ${event.eventType}:`, event) + + if (event.eventType === CONSTANTS.EVENTS.AUCTION_END) { + flush() + } +} + +adapterManager.registerAnalyticsAdapter({ + adapter: asteriobidAnalytics, + code: 'asteriobid' +}) + +asteriobidAnalytics.getOptions = function () { + return initOptions +} + +asteriobidAnalytics.flush = flush + +export default asteriobidAnalytics diff --git a/modules/asteriobidAnalyticsAdapter.md b/modules/asteriobidAnalyticsAdapter.md new file mode 100644 index 00000000000..524cf6e2721 --- /dev/null +++ b/modules/asteriobidAnalyticsAdapter.md @@ -0,0 +1,41 @@ +# Overview + +Module Name: AsterioBid Analytics Adapter +Module Type: Analytics Adapter +Maintainer: admin@asteriobid.com + +# Description +Analytics adapter for AsterioBid. Contact admin@asteriobid.com for information. + +# Test Parameters + +``` +pbjs.enableAnalytics({ + provider: 'asteriobid', + options: { + bundleId: '04bcf17b-9733-4675-9f67-d475f881ab78' + } +}); + +``` + +# Advanced Parameters + +``` +pbjs.enableAnalytics({ + provider: 'asteriobid', + options: { + bundleId: '04bcf17b-9733-4675-9f67-d475f881ab78', + version: 'v1', // configuration version for the comparison + adUnitDict: { // provide names of the ad units for better reporting + adunitid1: 'Top Banner', + adunitid2: 'Bottom Banner' + }, + customParam: { // provide custom parameters values that you want to collect and report + param1: 'value1', + param2: 'value2' + } + } +}); + +``` diff --git a/test/spec/modules/asteriobidAnalyticsAdapter_spec.js b/test/spec/modules/asteriobidAnalyticsAdapter_spec.js new file mode 100644 index 00000000000..9be6c1dedac --- /dev/null +++ b/test/spec/modules/asteriobidAnalyticsAdapter_spec.js @@ -0,0 +1,151 @@ +import asteriobidAnalytics, {storage} from 'modules/asteriobidAnalyticsAdapter.js'; +import {expect} from 'chai'; +import {server} from 'test/mocks/xhr.js'; +import * as utils from 'src/utils.js'; +import {expectEvents} from '../../helpers/analytics.js'; + +let events = require('src/events'); +let constants = require('src/constants.json'); + +describe('AsterioBid Analytics Adapter', function () { + let bidWonEvent = { + 'bidderCode': 'appnexus', + 'width': 300, + 'height': 250, + 'adId': '1ebb82ec35375e', + 'mediaType': 'banner', + 'cpm': 0.5, + 'requestId': '1582271863760569973', + 'creative_id': '96846035', + 'creativeId': '96846035', + 'ttl': 60, + 'currency': 'USD', + 'netRevenue': true, + 'auctionId': '9c7b70b9-b6ab-4439-9e71-b7b382797c18', + 'responseTimestamp': 1537521629657, + 'requestTimestamp': 1537521629331, + 'bidder': 'appnexus', + 'adUnitCode': 'div-gpt-ad-1460505748561-0', + 'timeToRespond': 326, + 'size': '300x250', + 'status': 'rendered', + 'eventType': 'bidWon', + 'ad': 'some ad', + 'adUrl': 'ad url' + }; + + describe('AsterioBid Analytic tests', function () { + beforeEach(function () { + sinon.stub(events, 'getEvents').returns([]); + }); + + afterEach(function () { + asteriobidAnalytics.disableAnalytics(); + events.getEvents.restore(); + }); + + it('support custom endpoint', function () { + let custom_url = 'custom url'; + asteriobidAnalytics.enableAnalytics({ + provider: 'asteriobid', + options: { + url: custom_url, + bundleId: 'xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx' + } + }); + + expect(asteriobidAnalytics.getOptions().url).to.equal(custom_url); + }); + + it('bid won event', function() { + let bundleId = 'xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx'; + asteriobidAnalytics.enableAnalytics({ + provider: 'asteriobid', + options: { + bundleId: bundleId + } + }); + + events.emit(constants.EVENTS.BID_WON, bidWonEvent); + asteriobidAnalytics.flush(); + + expect(server.requests.length).to.equal(1); + expect(server.requests[0].url).to.equal('https://endpt.asteriobid.com/endpoint'); + expect(server.requests[0].requestBody.substring(0, 2)).to.equal('1:'); + + const pmEvents = JSON.parse(server.requests[0].requestBody.substring(2)); + expect(pmEvents.pageViewId).to.exist; + expect(pmEvents.bundleId).to.equal(bundleId); + expect(pmEvents.ver).to.equal(1); + expect(pmEvents.events.length).to.equal(1); + expect(pmEvents.events[0].eventType).to.equal('bidWon'); + expect(pmEvents.events[0].ad).to.be.undefined; + expect(pmEvents.events[0].adUrl).to.be.undefined; + }); + + it('track event without errors', function () { + sinon.spy(asteriobidAnalytics, 'track'); + + asteriobidAnalytics.enableAnalytics({ + provider: 'asteriobid', + options: { + bundleId: 'xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx' + } + }); + + expectEvents().to.beTrackedBy(asteriobidAnalytics.track); + }); + }); + + 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'); + getDataFromLocalStorageStub.withArgs('pm_utm_medium').returns('utm_medium'); + getDataFromLocalStorageStub.withArgs('pm_utm_campaign').returns('utm_camp'); + getDataFromLocalStorageStub.withArgs('pm_utm_term').returns(''); + getDataFromLocalStorageStub.withArgs('pm_utm_content').returns(''); + }); + afterEach(function () { + getDataFromLocalStorageStub.restore(); + asteriobidAnalytics.disableAnalytics() + }); + it('should build utm data from local storage', function () { + asteriobidAnalytics.enableAnalytics({ + provider: 'asteriobid', + options: { + bundleId: 'xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx' + } + }); + + const pmEvents = JSON.parse(server.requests[0].requestBody.substring(2)); + + expect(pmEvents.utmTags.utm_source).to.equal('utm_source'); + expect(pmEvents.utmTags.utm_medium).to.equal('utm_medium'); + expect(pmEvents.utmTags.utm_campaign).to.equal('utm_camp'); + expect(pmEvents.utmTags.utm_term).to.equal(''); + expect(pmEvents.utmTags.utm_content).to.equal(''); + }); + }); + + describe('build page info', function () { + afterEach(function () { + asteriobidAnalytics.disableAnalytics() + }); + it('should build page info', function () { + asteriobidAnalytics.enableAnalytics({ + provider: 'asteriobid', + 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 cb9a616a50838c60d56b9186dab8dc0ff1ae5fd3 Mon Sep 17 00:00:00 2001 From: ecoeco163 <147788250+ecoeco163@users.noreply.github.com> Date: Fri, 17 Nov 2023 21:39:49 +0800 Subject: [PATCH 004/246] Discovery Bid Adapter : remove duplicate eids (#10743) * feat(getItems): add bid request params * feat(getItems): add bid request imp.ext params * feat(getItems): add plaintext * feat(spec):test buildRequests * feat(bid params): use utils.parseSizesInput to get screen size * feat(getScreenSize): add plaintext * feat(getItems): add bid request params) * feat(getItems): add bid request params) * feat(getItems): add bid request params) * feat(request): add bidId & keywords to imp[].ext * feat(getItems): add pbadslot to unit tests) * feat(request): Remove duplicate eids --------- Co-authored-by: yubei01 Co-authored-by: lvhuixin --- modules/discoveryBidAdapter.js | 1 - 1 file changed, 1 deletion(-) diff --git a/modules/discoveryBidAdapter.js b/modules/discoveryBidAdapter.js index 3b9f858e15c..816ec3cbe1f 100644 --- a/modules/discoveryBidAdapter.js +++ b/modules/discoveryBidAdapter.js @@ -386,7 +386,6 @@ function getParam(validBidRequests, bidderRequest) { buyeruid: getUserID(), id: sharedid || pubcid, }, - eids, tmax: timeout, site: { name: domain, From b7744ecc5b537ba652be81904bf6273f8a7bb101 Mon Sep 17 00:00:00 2001 From: adxcgcom <31470944+adxcgcom@users.noreply.github.com> Date: Fri, 17 Nov 2023 13:47:29 +0000 Subject: [PATCH 005/246] Adxcg Bid Adapter: use ortbConverter (#10525) * Update adxcgBidAdapter_spec.js * Update adxcgBidAdapter.js --- modules/adxcgBidAdapter.js | 413 ++----- test/spec/modules/adxcgBidAdapter_spec.js | 1372 ++++++++------------- 2 files changed, 642 insertions(+), 1143 deletions(-) diff --git a/modules/adxcgBidAdapter.js b/modules/adxcgBidAdapter.js index 5930f3adb67..ab0ae1cbd48 100644 --- a/modules/adxcgBidAdapter.js +++ b/modules/adxcgBidAdapter.js @@ -1,307 +1,65 @@ // jshint esversion: 6, es3: false, node: true -'use strict'; - -import {registerBidder} from '../src/adapters/bidderFactory.js'; -import {BANNER, NATIVE, VIDEO} from '../src/mediaTypes.js'; +import { ortbConverter } from '../libraries/ortbConverter/converter.js'; +import { registerBidder } from '../src/adapters/bidderFactory.js'; +import { BANNER, NATIVE, VIDEO } from '../src/mediaTypes.js'; import { - _map, - deepAccess, - deepSetValue, - getDNT, + convertTypes, isArray, - isPlainObject, - isStr, - mergeDeep, - parseSizesInput, replaceAuctionPrice, - triggerPixel + triggerPixel, + logMessage, + deepSetValue, + getBidIdParameter } from '../src/utils.js'; -import {config} from '../src/config.js'; -import { convertOrtbRequestToProprietaryNative } from '../src/native.js'; - -const { getConfig } = config; +import { config } from '../src/config.js'; const BIDDER_CODE = 'adxcg'; const SECURE_BID_URL = 'https://pbc.adxcg.net/rtb/ortb/pbc?adExchangeId=1'; -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 DEFAULT_CURRENCY = 'EUR'; +const KNOWN_PARAMS = ['cp', 'ct', 'cf', 'battr', 'deals']; +const DEFAULT_TMAX = 500; +/** + * Adxcg Bid Adapter. + * + */ export const spec = { + code: BIDDER_CODE, - supportedMediaTypes: [ NATIVE, BANNER, VIDEO ], + + aliases: ['mediaopti'], + + supportedMediaTypes: [BANNER, NATIVE, VIDEO], + isBidRequestValid: (bid) => { + logMessage('adxcg - validating isBidRequestValid'); const params = bid.params || {}; const { adzoneid } = params; return !!(adzoneid); }, - buildRequests: (validBidRequests, bidderRequest) => { - // convert Native ORTB definition to old-style prebid native definition - validBidRequests = convertOrtbRequestToProprietaryNative(validBidRequests); - - let app, site; - - const commonFpd = bidderRequest.ortb2 || {}; - let { user } = commonFpd; - - if (typeof getConfig('app') === 'object') { - app = getConfig('app') || {}; - if (commonFpd.app) { - mergeDeep(app, commonFpd.app); - } - } else { - site = getConfig('site') || {}; - if (commonFpd.site) { - mergeDeep(site, commonFpd.site); - } - - if (!site.page) { - site.page = bidderRequest.refererInfo.page; - site.domain = bidderRequest.refererInfo.domain; - } - } - - const device = getConfig('device') || {}; - device.w = device.w || window.innerWidth; - device.h = device.h || window.innerHeight; - device.ua = device.ua || navigator.userAgent; - device.dnt = getDNT() ? 1 : 0; - device.language = (navigator && navigator.language) ? navigator.language.split('-')[0] : ''; - - const tid = bidderRequest.ortb2?.source?.tid; - const test = setOnAny(validBidRequests, 'params.test'); - 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) => { - const floorInfo = bid.getFloor ? bid.getFloor({ - currency: currency || 'USD' - }) : {}; - const bidfloor = floorInfo.floor; - const bidfloorcur = floorInfo.currency; - const { adzoneid } = bid.params; - - const imp = { - id: id + 1, - tagid: adzoneid, - secure: 1, - bidfloor, - bidfloorcur, - ext: { - } - }; - - const assets = _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); - - if (assets.length) { - imp.native = { - request: JSON.stringify({assets: assets}) - }; - } - - const bannerParams = deepAccess(bid, 'mediaTypes.banner'); - - if (bannerParams && bannerParams.sizes) { - const sizes = 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 - }; - } - - const videoParams = deepAccess(bid, 'mediaTypes.video'); - if (videoParams) { - imp.video = videoParams; - } - - return imp; - }); - - const request = { - id: bidderRequest.auctionId, - site, - app, - user, - geo: { utcoffset: new Date().getTimezoneOffset() }, - device, - source: { tid, fd: 1 }, - ext: { - prebid: { - channel: { - name: 'pbjs', - version: '$prebid.version$' - } - } - }, - cur, - imp - }; - - if (test) { - request.is_debug = !!test; - request.test = 1; - } - if (deepAccess(bidderRequest, 'gdprConsent.gdprApplies') !== undefined) { - deepSetValue(request, 'user.ext.consent', bidderRequest.gdprConsent.consentString); - deepSetValue(request, 'regs.ext.gdpr', bidderRequest.gdprConsent.gdprApplies & 1); - } - - if (bidderRequest.uspConsent) { - deepSetValue(request, 'regs.ext.us_privacy', bidderRequest.uspConsent); - } - - if (eids) { - deepSetValue(request, 'user.ext.eids', eids); - } - - if (schain) { - deepSetValue(request, 'source.ext.schain', schain); - } + buildRequests: (bidRequests, bidderRequest) => { + const data = converter.toORTB({ bidRequests, bidderRequest }); return { method: 'POST', url: SECURE_BID_URL, - data: JSON.stringify(request), + data, options: { contentType: 'application/json' }, - bids: validBidRequests + bidderRequest }; }, - interpretResponse: function(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 mediaType = deepAccess(bidResponse, 'ext.crType'); - const result = { - requestId: bid.bidId, - cpm: bidResponse.price, - creativeId: bidResponse.crid, - ttl: bidResponse.ttl ? bidResponse.ttl : 300, - netRevenue: bid.netRevenue === 'net', - currency: cur, - burl: bid.burl || '', - mediaType: mediaType, - width: bidResponse.w, - height: bidResponse.h, - dealId: bidResponse.dealid, - }; - deepSetValue(result, 'meta.mediaType', mediaType); - if (isArray(bidResponse.adomain)) { - deepSetValue(result, 'meta.advertiserDomains', bidResponse.adomain); - } - - if (isPlainObject(bidResponse.ext)) { - if (isStr(bidResponse.ext.mediaType)) { - deepSetValue(result, 'meta.mediaType', mediaType); - } - if (isStr(bidResponse.ext.advertiser_id)) { - deepSetValue(result, 'meta.advertiserId', bidResponse.ext.advertiser_id); - } - if (isStr(bidResponse.ext.advertiser_name)) { - deepSetValue(result, 'meta.advertiserName', bidResponse.ext.advertiser_name); - } - if (isStr(bidResponse.ext.agency_name)) { - deepSetValue(result, 'meta.agencyName', bidResponse.ext.agency_name); - } - } - if (mediaType === BANNER) { - result.ad = bidResponse.adm; - } else if (mediaType === NATIVE) { - result.native = parseNative(bidResponse); - result.width = 0; - result.height = 0; - } else if (mediaType === VIDEO) { - result.vastUrl = bidResponse.nurl; - result.vastXml = bidResponse.adm; - } - - return result; - } - }).filter(Boolean); + interpretResponse: (response, request) => { + if (response.body) { + const bids = converter.fromORTB({ response: response.body, request: request.data }).bids; + return bids; + } + return []; }, + getUserSyncs: (syncOptions, responses, gdprConsent, uspConsent) => { const syncs = []; let syncUrl = config.getConfig('adxcg.usersyncUrl'); @@ -323,44 +81,95 @@ export const spec = { } return syncs; }, + onBidWon: (bid) => { // for native requests we put the nurl as an imp tracker, otherwise if the auction takes place on prebid server // the server JS adapter puts the nurl in the adm as a tracking pixel and removes the attribute if (bid.nurl) { triggerPixel(replaceAuctionPrice(bid.nurl, bid.originalCpm)) } + }, + transformBidParams: function (params) { + return convertTypes({ + 'cf': 'string', + 'cp': 'number', + 'ct': 'number', + 'adzoneid': 'string' + }, params); } }; -registerBidder(spec); +const converter = ortbConverter({ + context: { + netRevenue: true, + ttl: 300, + currency: 'EUR' + }, -function parseNative(bid) { - const { assets, link, imptrackers, jstracker } = JSON.parse(bid.adm); - 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 }; + imp(buildImp, bidRequest, context) { + const imp = buildImp(bidRequest, context); + // tagid + imp.tagid = bidRequest.params.adzoneid.toString(); + // unknown params + const unknownParams = slotUnknownParams(bidRequest); + if (imp.ext || unknownParams) { + imp.ext = Object.assign({}, imp.ext, unknownParams); + } + // battr + if (bidRequest.params.battr) { + ['banner', 'video', 'audio', 'native'].forEach(k => { + if (imp[k]) { + imp[k].battr = bidRequest.params.battr; + } + }); + } + // deals + if (bidRequest.params.deals && isArray(bidRequest.params.deals)) { + imp.pmp = { + private_auction: 0, + deals: bidRequest.params.deals + }; } - }); - return result; -} -function setOnAny(collection, key) { - for (let i = 0, result; i < collection.length; i++) { - result = deepAccess(collection[i], key); - if (result) { - return result; + imp.secure = Number(window.location.protocol === 'https:'); + + if (!imp.bidfloor && bidRequest.params.bidFloor) { + imp.bidfloor = bidRequest.params.bidFloor; + imp.bidfloorcur = getBidIdParameter('bidFloorCur', bidRequest.params).toUpperCase() || 'USD' } - } -} + return imp; + }, + + request(buildRequest, imps, bidderRequest, context) { + const request = buildRequest(imps, bidderRequest, context); + request.tmax = request.tmax || DEFAULT_TMAX; + request.test = config.getConfig('debug') ? 1 : 0; + request.at = 1; + deepSetValue(request, 'ext.prebid.channel.name', 'pbjs'); + deepSetValue(request, 'ext.prebid.channel.version', '$prebid.version$'); + return request; + }, -function flatten(arr) { - return [].concat(...arr); + bidResponse(buildBidResponse, bid, context) { + const bidResponse = buildBidResponse(bid, context); + bidResponse.cur = bid.cur || DEFAULT_CURRENCY; + return bidResponse; + }, +}); + +/** + * Unknown params are captured and sent on ext + */ +function slotUnknownParams(slot) { + const ext = {}; + const knownParamsMap = {}; + KNOWN_PARAMS.forEach(value => knownParamsMap[value] = 1); + Object.keys(slot.params).forEach(key => { + if (!knownParamsMap[key]) { + ext[key] = slot.params[key]; + } + }); + return Object.keys(ext).length > 0 ? { prebid: ext } : null; } + +registerBidder(spec); diff --git a/test/spec/modules/adxcgBidAdapter_spec.js b/test/spec/modules/adxcgBidAdapter_spec.js index 65c7584b428..e07e3a6e5d4 100644 --- a/test/spec/modules/adxcgBidAdapter_spec.js +++ b/test/spec/modules/adxcgBidAdapter_spec.js @@ -1,835 +1,19 @@ // jshint esversion: 6, es3: false, node: true -import {assert} from 'chai'; -import {spec} from 'modules/adxcgBidAdapter.js'; -import {config} from 'src/config.js'; -import {createEidsArray} from 'modules/userId/eids.js'; +import { assert } from 'chai'; +import { spec } from 'modules/adxcgBidAdapter.js'; +import { config } from 'src/config.js'; +import { createEidsArray } from 'modules/userId/eids.js'; +/* eslint dot-notation:0, quote-props:0 */ +import { expect } from 'chai'; + +import { syncAddFPDToBidderRequest } from '../../helpers/fpd.js'; +import { deepClone } from '../../../src/utils'; + const utils = require('src/utils'); describe('Adxcg adapter', function () { let bids = []; - describe('isBidRequestValid', function () { - let bid = { - 'bidder': 'adxcg', - 'params': { - 'adzoneid': '19910113' - } - }; - - it('should return true when required params found', function () { - assert(spec.isBidRequestValid(bid)); - - bid.params = { - adzoneid: 4332, - }; - assert(spec.isBidRequestValid(bid)); - }); - - it('should return false when required params are missing', function () { - bid.params = {}; - assert.isFalse(spec.isBidRequestValid(bid)); - - bid.params = { - mname: 'some-placement' - }; - assert.isFalse(spec.isBidRequestValid(bid)); - - bid.params = { - inv: 1234 - }; - assert.isFalse(spec.isBidRequestValid(bid)); - }); - }); - - describe('buildRequests', function () { - beforeEach(function () { - config.resetConfig(); - }); - it('should send request with correct structure', function () { - let validBidRequests = [{ - bidId: 'bidId', - params: { - adzoneid: '19910113' - } - }]; - let request = spec.buildRequests(validBidRequests, {refererInfo: {page: 'page', domain: 'localhost'}}); - - assert.equal(request.method, 'POST'); - assert.equal(request.url, 'https://pbc.adxcg.net/rtb/ortb/pbc?adExchangeId=1'); - assert.deepEqual(request.options, {contentType: 'application/json'}); - assert.ok(request.data); - }); - - describe('user privacy', function () { - it('should send GDPR Consent data to exchange if gdprApplies', function () { - 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); - - assert.equal(request.user.ext.consent, bidderRequest.gdprConsent.consentString); - assert.equal(request.regs.ext.gdpr, bidderRequest.gdprConsent.gdprApplies); - assert.equal(typeof request.regs.ext.gdpr, 'number'); - }); - - it('should send gdpr as number', function () { - 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); - - assert.equal(typeof request.regs.ext.gdpr, 'number'); - assert.equal(request.regs.ext.gdpr, 1); - }); - - it('should send CCPA Consent data to exchange', function () { - let validBidRequests = [{bidId: 'bidId', params: {test: 1}}]; - let bidderRequest = {uspConsent: '1YA-', refererInfo: {referer: 'page'}}; - let request = JSON.parse(spec.buildRequests(validBidRequests, bidderRequest).data); - - assert.equal(request.regs.ext.us_privacy, '1YA-'); - - bidderRequest = { - uspConsent: '1YA-', - gdprConsent: {gdprApplies: true, consentString: 'consentDataString'}, - refererInfo: {referer: 'page'} - }; - request = JSON.parse(spec.buildRequests(validBidRequests, bidderRequest).data); - - assert.equal(request.regs.ext.us_privacy, '1YA-'); - assert.equal(request.user.ext.consent, 'consentDataString'); - assert.equal(request.regs.ext.gdpr, 1); - }); - - it('should not send GDPR Consent data to adxcg if gdprApplies is undefined', function () { - let validBidRequests = [{ - bidId: 'bidId', - params: {siteId: 'siteId'} - }]; - let bidderRequest = { - gdprConsent: {gdprApplies: false, consentString: 'consentDataString'}, - refererInfo: {referer: 'page'} - }; - let request = JSON.parse(spec.buildRequests(validBidRequests, bidderRequest).data); - - assert.equal(request.user.ext.consent, 'consentDataString'); - assert.equal(request.regs.ext.gdpr, 0); - - bidderRequest = {gdprConsent: {consentString: 'consentDataString'}, refererInfo: {referer: 'page'}}; - request = JSON.parse(spec.buildRequests(validBidRequests, bidderRequest).data); - - assert.equal(request.user, undefined); - assert.equal(request.regs, undefined); - }); - it('should send default GDPR Consent data to exchange', function () { - let validBidRequests = [{ - bidId: 'bidId', - params: {siteId: 'siteId'} - }]; - let request = JSON.parse(spec.buildRequests(validBidRequests, {refererInfo: {referer: 'page'}}).data); - - assert.equal(request.user, undefined); - assert.equal(request.regs, undefined); - }); - }); - - it('should add test and is_debug to request, if test is set in parameters', function () { - let validBidRequests = [{ - bidId: 'bidId', - params: {test: 1} - }]; - let request = JSON.parse(spec.buildRequests(validBidRequests, {refererInfo: {referer: 'page'}}).data); - - assert.ok(request.is_debug); - assert.equal(request.test, 1); - }); - - it('should have default request structure', function () { - let keys = 'site,geo,device,source,ext,imp'.split(','); - let validBidRequests = [{ - bidId: 'bidId', - params: {siteId: 'siteId'} - }]; - let request = JSON.parse(spec.buildRequests(validBidRequests, {refererInfo: {referer: 'page'}}).data); - let data = Object.keys(request); - - assert.deepEqual(keys, data); - }); - - it('should set request keys correct values', function () { - let validBidRequests = [{ - bidId: 'bidId', - params: {siteId: 'siteId'}, - }]; - let request = JSON.parse(spec.buildRequests(validBidRequests, { - refererInfo: {referer: 'page'}, - ortb2: {source: {tid: 'tid'}} - }).data); - - assert.equal(request.source.tid, 'tid'); - assert.equal(request.source.fd, 1); - }); - - it('should send info about device', function () { - config.setConfig({ - device: {w: 100, h: 100} - }); - let validBidRequests = [{ - bidId: 'bidId', - params: {adzoneid: '1000'} - }]; - let request = JSON.parse(spec.buildRequests(validBidRequests, {refererInfo: {page: 'page', domain: 'localhost'}}).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'}, - }); - const ortb2 = {app: {name: 'appname'}} - let validBidRequests = [{ - bidId: 'bidId', - params: {adzoneid: '1000'}, - ortb2 - }]; - let request = JSON.parse(spec.buildRequests(validBidRequests, {refererInfo: {referer: 'page'}, ortb2}).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' - } - }, - }); - const ortb2 = { - site: { - publisher: { - id: 4441, - name: 'publisher\'s name' - } - } - }; - - let validBidRequests = [{ - bidId: 'bidId', - params: {adzoneid: '1000'}, - ortb2 - }]; - let refererInfo = {page: 'page', domain: 'localhost'}; - let request = JSON.parse(spec.buildRequests(validBidRequests, {refererInfo, ortb2}).data); - - assert.deepEqual(request.site, { - domain: 'localhost', - id: '123123', - page: refererInfo.page, - publisher: { - domain: 'publisher.domain.com', - id: 4441, - name: 'publisher\'s name' - } - }); - }); - - it('should pass extended ids', function () { - let validBidRequests = [{ - bidId: 'bidId', - params: {}, - userIdAsEids: createEidsArray({ - tdid: 'TTD_ID_FROM_USER_ID_MODULE', - pubcid: 'pubCommonId_FROM_USER_ID_MODULE' - }) - }]; - - let request = JSON.parse(spec.buildRequests(validBidRequests, {refererInfo: {referer: 'page'}}).data); - assert.deepEqual(request.user.ext.eids, [ - {source: 'adserver.org', uids: [{id: 'TTD_ID_FROM_USER_ID_MODULE', atype: 1, ext: {rtiPartner: 'TDID'}}]}, - {source: 'pubcid.org', uids: [{id: 'pubCommonId_FROM_USER_ID_MODULE', atype: 1}]} - ]); - }); - - it('should send currency if defined', function () { - config.setConfig({currency: {adServerCurrency: 'EUR'}}); - let validBidRequests = [{params: {}}]; - let refererInfo = {referer: 'page'}; - let request = JSON.parse(spec.buildRequests(validBidRequests, {refererInfo}).data); - - 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('bids', function () { - it('should add more than one bid to the request', function () { - let validBidRequests = [{ - bidId: 'bidId', - params: {siteId: 'siteId'} - }, { - bidId: 'bidId2', - params: {siteId: 'siteId'} - }]; - let request = JSON.parse(spec.buildRequests(validBidRequests, {refererInfo: {referer: 'page'}}).data); - - assert.equal(request.imp.length, 2); - }); - it('should add incrementing values of id', function () { - let validBidRequests = [{ - bidId: 'bidId', - params: {adzoneid: '1000'}, - mediaTypes: {video: {}} - }, { - bidId: 'bidId2', - params: {adzoneid: '1000'}, - mediaTypes: {video: {}} - }, { - bidId: 'bidId3', - params: {adzoneid: '1000'}, - 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].id, i + 1); - } - }); - - it('should add adzoneid', function () { - let validBidRequests = [{bidId: 'bidId', params: {adzoneid: 1000}, mediaTypes: {video: {}}}, - {bidId: 'bidId2', params: {adzoneid: 1001}, mediaTypes: {video: {}}}, - {bidId: 'bidId3', params: {adzoneid: 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.adzoneid); - } - }); - - describe('price floors', function () { - it('should not add if floors module not configured', function () { - const validBidRequests = [{bidId: 'bidId', params: {adzoneid: 1000}, mediaTypes: {video: {}}}]; - let imp = getRequestImps(validBidRequests)[0]; - - assert.equal(imp.bidfloor, undefined); - assert.equal(imp.bidfloorcur, undefined); - }); - - it('should not add if floor price not defined', function () { - const validBidRequests = [getBidWithFloor()]; - let imp = getRequestImps(validBidRequests)[0]; - - assert.equal(imp.bidfloor, undefined); - assert.equal(imp.bidfloorcur, 'USD'); - }); - - it('should request floor price in adserver currency', function () { - config.setConfig({currency: {adServerCurrency: 'DKK'}}); - const validBidRequests = [getBidWithFloor()]; - let imp = getRequestImps(validBidRequests)[0]; - - assert.equal(imp.bidfloor, undefined); - assert.equal(imp.bidfloorcur, 'DKK'); - }); - - it('should add correct floor values', function () { - const expectedFloors = [1, 1.3, 0.5]; - const validBidRequests = expectedFloors.map(getBidWithFloor); - let imps = getRequestImps(validBidRequests); - - expectedFloors.forEach((floor, index) => { - assert.equal(imps[index].bidfloor, floor); - assert.equal(imps[index].bidfloorcur, 'USD'); - }); - }); - - function getBidWithFloor(floor) { - return { - params: {adzoneid: 1}, - mediaTypes: {video: {}}, - getFloor: ({currency}) => { - return { - currency: currency, - floor - }; - } - }; - } - }); - - describe('multiple media types', function () { - it('should use all configured media types for bidding', function () { - let validBidRequests = [{ - bidId: 'bidId', - params: {adzoneid: 1000}, - mediaTypes: { - banner: { - sizes: [[100, 100], [200, 300]] - }, - video: {} - } - }, { - bidId: 'bidId1', - params: {adzoneid: 1000}, - mediaTypes: { - video: {}, - native: {} - } - }, { - bidId: 'bidId2', - params: {adzoneid: 1000}, - nativeParams: { - title: {required: true, len: 140} - }, - mediaTypes: { - banner: { - sizes: [[100, 100], [200, 300]] - }, - native: {}, - video: {} - } - }]; - let [first, second, third] = JSON.parse(spec.buildRequests(validBidRequests, {refererInfo: {referer: 'page'}}).data).imp; - - assert.ok(first.banner); - assert.ok(first.video); - assert.equal(first.native, undefined); - - assert.ok(second.video); - assert.equal(second.banner, undefined); - assert.equal(second.native, undefined); - - assert.ok(third.native); - assert.ok(third.video); - assert.ok(third.banner); - }); - }); - - describe('banner', function () { - it('should convert sizes to openrtb format', function () { - let validBidRequests = [{ - bidId: 'bidId', - params: {adzoneid: 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: {adzoneid: 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: {adzoneid: 1000}, - nativeParams: { - title: {required: true, len: 140}, - image: {required: false, wmin: 836, hmin: 627, w: 325, h: 300, mimes: ['image/jpg', 'image/gif']}, - body: {len: 140} - } - }]; - let nativeRequest = JSON.parse(spec.buildRequests(validBidRequests, {refererInfo: {referer: 'page'}}).data).imp[0].native.request; - let assets = JSON.parse(nativeRequest).assets; - - assert.equal(assets[0].id, 0); - assert.equal(assets[1].id, 3); - assert.equal(assets[2].id, 4); - }); - it('should add required key if it is necessary', function () { - let validBidRequests = [{ - bidId: 'bidId', - params: {adzoneid: 1000}, - nativeParams: { - title: {required: true, len: 140}, - image: {required: false, wmin: 836, hmin: 627, w: 325, h: 300, mimes: ['image/jpg', 'image/gif']}, - body: {len: 140}, - sponsoredBy: {required: true, len: 140} - } - }]; - - let nativeRequest = JSON.parse(spec.buildRequests(validBidRequests, {refererInfo: {referer: 'page'}}).data).imp[0].native.request; - let assets = JSON.parse(nativeRequest).assets; - - assert.equal(assets[0].required, 1); - assert.ok(!assets[1].required); - assert.ok(!assets[2].required); - assert.equal(assets[3].required, 1); - }); - - it('should map img and data assets', function () { - let validBidRequests = [{ - bidId: 'bidId', - params: {adzoneid: 1000}, - nativeParams: { - title: {required: true, len: 140}, - image: {required: true, sizes: [150, 50]}, - icon: {required: false, sizes: [50, 50]}, - body: {required: false, len: 140}, - sponsoredBy: {required: true}, - cta: {required: false}, - clickUrl: {required: false} - } - }]; - - let nativeRequest = JSON.parse(spec.buildRequests(validBidRequests, {refererInfo: {referer: 'page'}}).data).imp[0].native.request; - let assets = JSON.parse(nativeRequest).assets; - assert.ok(assets[0].title); - assert.equal(assets[0].title.len, 140); - assert.deepEqual(assets[1].img, {type: 3, w: 150, h: 50}); - assert.deepEqual(assets[2].img, {type: 1, w: 50, h: 50}); - assert.deepEqual(assets[3].data, {type: 2, len: 140}); - assert.deepEqual(assets[4].data, {type: 1}); - assert.deepEqual(assets[5].data, {type: 12}); - assert.ok(!assets[6]); - }); - - describe('icon/image sizing', function () { - it('should flatten sizes and utilise first pair', function () { - const validBidRequests = [{ - bidId: 'bidId', - params: {adzoneid: 1000}, - nativeParams: { - image: { - sizes: [[200, 300], [100, 200]] - }, - } - }]; - - let nativeRequest = JSON.parse(spec.buildRequests(validBidRequests, {refererInfo: {referer: 'page'}}).data).imp[0].native.request; - let assets = JSON.parse(nativeRequest).assets; - assert.ok(assets[0].img); - assert.equal(assets[0].img.w, 200); - assert.equal(assets[0].img.h, 300); - }); - }); - - it('should utilise aspect_ratios', function () { - const validBidRequests = [{ - bidId: 'bidId', - params: {adzoneid: 1000}, - nativeParams: { - image: { - aspect_ratios: [{ - min_width: 100, - ratio_height: 3, - ratio_width: 1 - }] - }, - icon: { - aspect_ratios: [{ - min_width: 10, - ratio_height: 5, - ratio_width: 2 - }] - } - } - }]; - - let nativeRequest = JSON.parse(spec.buildRequests(validBidRequests, {refererInfo: {referer: 'page'}}).data).imp[0].native.request; - let assets = JSON.parse(nativeRequest).assets; - assert.ok(assets[0].img); - assert.equal(assets[0].img.wmin, 100); - assert.equal(assets[0].img.hmin, 300); - - assert.ok(assets[1].img); - assert.equal(assets[1].img.wmin, 10); - assert.equal(assets[1].img.hmin, 25); - }); - - it('should not throw error if aspect_ratios config is not defined', function () { - const validBidRequests = [{ - bidId: 'bidId', - params: {adzoneid: 1000}, - nativeParams: { - image: { - aspect_ratios: [] - }, - icon: { - aspect_ratios: [] - } - } - }]; - - assert.doesNotThrow(() => spec.buildRequests(validBidRequests, {refererInfo: {referer: 'page'}})); - }); - }); - - it('should expect any dimensions if min_width not passed', function () { - const validBidRequests = [{ - bidId: 'bidId', - params: {adzoneid: 1000}, - nativeParams: { - image: { - aspect_ratios: [{ - ratio_height: 3, - ratio_width: 1 - }] - } - } - }]; - - let nativeRequest = JSON.parse(spec.buildRequests(validBidRequests, {refererInfo: {referer: 'page'}}).data).imp[0].native.request; - let assets = JSON.parse(nativeRequest).assets; - assert.ok(assets[0].img); - assert.equal(assets[0].img.wmin, 0); - assert.equal(assets[0].img.hmin, 0); - assert.ok(!assets[1]); - }); - }); - }); - - function getRequestImps(validBidRequests) { - return JSON.parse(spec.buildRequests(validBidRequests, {refererInfo: {referer: 'page'}}).data).imp; - } - }); - - describe('interpretResponse', function () { - it('should return if no body in response', function () { - let serverResponse = {}; - let bidRequest = {}; - - assert.ok(!spec.interpretResponse(serverResponse, bidRequest)); - }); - it('should return more than one bids', function () { - let serverResponse = { - body: { - seatbid: [{ - bid: [{ - impid: '1', - native: {ver: '1.1', link: {url: 'link'}, assets: [{id: 1, title: {text: 'Asset title text'}}]} - }] - }, { - bid: [{ - impid: '2', - native: {ver: '1.1', link: {url: 'link'}, assets: [{id: 1, data: {value: 'Asset title text'}}]} - }] - }] - } - }; - let bidRequest = { - data: {}, - bids: [ - { - bidId: 'bidId1', - params: {adzoneid: 1000}, - nativeParams: { - title: {required: true, len: 140}, - image: {required: false, wmin: 836, hmin: 627, w: 325, h: 300, mimes: ['image/jpg', 'image/gif']}, - body: {len: 140} - } - }, - { - bidId: 'bidId2', - params: {adzoneid: 1000}, - nativeParams: { - title: {required: true, len: 140}, - image: {required: false, wmin: 836, hmin: 627, w: 325, h: 300, mimes: ['image/jpg', 'image/gif']}, - body: {len: 140} - } - } - ] - }; - - bids = spec.interpretResponse(serverResponse, bidRequest); - assert.equal(spec.interpretResponse(serverResponse, bidRequest).length, 2); - }); - - it('should set correct values to bid', function () { - let nativeExample1 = { - assets: [], - link: {url: 'link'}, - imptrackers: ['imptrackers url1', 'imptrackers url2'] - } - - let serverResponse = { - body: { - id: null, - bidid: null, - seatbid: [{ - bid: [ - { - impid: '1', - price: 93.1231, - crid: '12312312', - adm: JSON.stringify(nativeExample1), - dealid: 'deal-id', - adomain: ['demo.com'], - ext: { - crType: 'native', - advertiser_id: 'adv1', - advertiser_name: 'advname', - agency_name: 'agname', - mediaType: 'native' - } - } - ] - }], - cur: 'EUR' - } - }; - let bidRequest = { - data: {}, - bids: [ - { - bidId: 'bidId1', - params: {adzoneid: 1000}, - nativeParams: { - title: {required: true, len: 140}, - image: {required: false, wmin: 836, hmin: 627, w: 325, h: 300, mimes: ['image/jpg', 'image/gif']}, - body: {len: 140} - } - } - ] - }; - - const bids = spec.interpretResponse(serverResponse, bidRequest); - const bid = serverResponse.body.seatbid[0].bid[0]; - assert.deepEqual(bids[0].requestId, bidRequest.bids[0].bidId); - assert.deepEqual(bids[0].cpm, bid.price); - assert.deepEqual(bids[0].creativeId, bid.crid); - assert.deepEqual(bids[0].ttl, 300); - 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].meta.advertiserName, 'advname'); - assert.deepEqual(bids[0].meta.agencyName, 'agname'); - - assert.deepEqual(bids[0].dealId, 'deal-id'); - }); - - it('should return empty when there is no bids in response', function () { - const serverResponse = { - body: { - id: null, - bidid: null, - seatbid: [{bid: []}], - cur: 'EUR' - } - }; - let bidRequest = { - data: {}, - bids: [{bidId: 'bidId1'}] - }; - 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: '', ext: {crType: 'banner'}}] - }] - } - }; - let bidRequest = { - data: {}, - bids: [ - { - bidId: 'bidId1', - params: {adzoneid: 1000} - } - ] - }; - - bids = spec.interpretResponse(serverResponse, bidRequest); - assert.equal(bids.length, 1); - assert.equal(bids[0].ad, ''); - assert.equal(bids[0].mediaType, 'banner'); - assert.equal(bids[0].meta.mediaType, 'banner'); - }); - }); - - describe('video', function () { - it('should set vastXml on response', function () { - let serverResponse = { - body: { - seatbid: [{ - bid: [{impid: '1', adm: '', ext: {crType: 'video'}}] - }] - } - }; - let bidRequest = { - data: {}, - bids: [ - { - bidId: 'bidId1', - params: {adzoneid: 1000} - } - ] - }; - - bids = spec.interpretResponse(serverResponse, bidRequest); - assert.equal(bids.length, 1); - assert.equal(bids[0].vastXml, ''); - assert.equal(bids[0].mediaType, 'video'); - assert.equal(bids[0].meta.mediaType, 'video'); - }); - }); - }); - describe('getUserSyncs', function () { const usersyncUrl = 'https://usersync-url.com'; beforeEach(() => { @@ -846,55 +30,55 @@ describe('Adxcg adapter', function () { }) it('should return user sync if pixel enabled with adxcg config', function () { - const ret = spec.getUserSyncs({pixelEnabled: true}) - expect(ret).to.deep.equal([{type: 'image', url: usersyncUrl}]) + const ret = spec.getUserSyncs({ pixelEnabled: true }) + expect(ret).to.deep.equal([{ type: 'image', url: usersyncUrl }]) }) it('should not return user sync if pixel disabled', function () { - const ret = spec.getUserSyncs({pixelEnabled: false}) + 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}) + 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([{ + 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([{ + 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([{ + 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() { + 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([{ + 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() { - beforeEach(function() { + describe('onBidWon', function () { + beforeEach(function () { sinon.stub(utils, 'triggerPixel'); }); - afterEach(function() { + afterEach(function () { utils.triggerPixel.restore(); }); - it('Should trigger pixel if bid nurl', function() { + it('Should trigger pixel if bid nurl', function () { const bid = { nurl: 'http://example.com/win/${AUCTION_PRICE}', cpm: 2.1, @@ -904,4 +88,510 @@ describe('Adxcg adapter', function () { expect(utils.triggerPixel.callCount).to.equal(1) }) }) + + it('should return just to have at least 1 karma test ok', function () { + assert(true); + }); +}); + +describe('adxcg v8 oRtbConverter Adapter Tests', function () { + const slotConfigs = [{ + placementCode: '/DfpAccount1/slot1', + mediaTypes: { + banner: { + sizes: [[728, 90], [160, 600]] + } + }, + bidId: 'bid12345', + params: { + cp: 'p10000', + ct: 't10000', + cf: '300x250', + adzoneid: '77' + } + }, { + placementCode: '/DfpAccount2/slot2', + mediaTypes: { + banner: { + sizes: [[728, 90]] + } + }, + bidId: 'bid23456', + params: { + cp: 'p10000', + ct: 't20000', + cf: '728x90', + adzoneid: '77' + } + }]; + const nativeOrtbRequest = { + assets: [{ + id: 1, + required: 1, + img: { + type: 3, + w: 150, + h: 50, + } + }, + { + id: 2, + required: 1, + title: { + len: 80 + } + }, + { + id: 3, + required: 0, + data: { + type: 1 + } + }] + }; + const nativeSlotConfig = [{ + placementCode: '/DfpAccount1/slot3', + bidId: 'bid12345', + mediaTypes: { + native: { + sendTargetingKeys: false, + ortb: nativeOrtbRequest + } + }, + nativeOrtbRequest, + params: { + cp: 'p10000', + ct: 't10000', + adzoneid: '77' + } + }]; + const videoSlotConfig = [{ + placementCode: '/DfpAccount1/slotVideo', + bidId: 'bid12345', + mediaTypes: { + video: { + playerSize: [400, 300], + w: 400, + h: 300, + minduration: 5, + maxduration: 10, + startdelay: 0, + skip: 1, + minbitrate: 200, + protocols: [1, 2, 4] + } + }, + params: { + cp: 'p10000', + ct: 't10000', + adzoneid: '77' + } + }]; + const additionalParamsConfig = [{ + placementCode: '/DfpAccount1/slot1', + mediaTypes: { + banner: { + sizes: [[1, 1]] + } + }, + bidId: 'bid12345', + params: { + cp: 'p10000', + ct: 't10000', + cf: '1x1', + adzoneid: '77', + extra_key1: 'extra_val1', + extra_key2: 12345, + extra_key3: { + key1: 'val1', + key2: 23456, + }, + extra_key4: [1, 2, 3] + } + }]; + + const schainParamsSlotConfig = [{ + placementCode: '/DfpAccount1/slot1', + mediaTypes: { + banner: { + sizes: [[1, 1]] + } + }, + bidId: 'bid12345', + params: { + cp: 'p10000', + ct: 't10000', + cf: '1x1', + adzoneid: '77', + bcat: ['IAB-1', 'IAB-20'], + battr: [1, 2, 3], + bidfloor: 1.5, + badv: ['cocacola.com', 'lays.com'] + }, + schain: { + 'ver': '1.0', + 'complete': 1, + 'nodes': [ + { + 'asi': 'exchange1.com', + 'sid': '1234', + 'hp': 1, + 'rid': 'bid-request-1', + 'name': 'publisher', + 'domain': 'publisher.com' + } + ] + }, + }]; + + const bidderRequest = { + refererInfo: { + page: 'https://publisher.com/home', + ref: 'https://referrer' + } + }; + + it('Verify build request', function () { + const request = spec.buildRequests(slotConfigs, syncAddFPDToBidderRequest(bidderRequest)); + expect(request.url).to.equal('https://pbc.adxcg.net/rtb/ortb/pbc?adExchangeId=1'); + expect(request.method).to.equal('POST'); + const ortbRequest = 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('p10000'); + expect(ortbRequest.site.page).to.equal('https://publisher.com/home'); + expect(ortbRequest.imp).to.have.lengthOf(2); + // device object + expect(ortbRequest.device).to.not.equal(null); + expect(ortbRequest.device.ua).to.equal(navigator.userAgent); + // slot 1 + // expect(ortbRequest.imp[0].tagid).to.equal('t10000'); + expect(ortbRequest.imp[0].banner).to.not.equal(null); + expect(ortbRequest.imp[0].banner.format).to.deep.eq([{ 'w': 728, 'h': 90 }, { 'w': 160, 'h': 600 }]); + // slot 2 + // expect(ortbRequest.imp[1].tagid).to.equal('t20000'); + expect(ortbRequest.imp[1].banner).to.not.equal(null); + expect(ortbRequest.imp[1].banner.format).to.deep.eq([{ 'w': 728, 'h': 90 }]); + }); + + it('Verify parse response', function () { + const request = spec.buildRequests(slotConfigs, syncAddFPDToBidderRequest(bidderRequest)); + const ortbRequest = request.data; + const ortbResponse = { + seatbid: [{ + bid: [{ + impid: ortbRequest.imp[0].id, + price: 1.25, + adm: 'This is an Ad', + crid: 'Creative#123', + mtype: 1, + w: 300, + h: 250, + exp: 20, + adomain: ['advertiser.com'] + }] + }] + }; + 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.creative_id).to.equal('Creative#123'); + expect(bid.creativeId).to.equal('Creative#123'); + expect(bid.netRevenue).to.equal(true); + expect(bid.currency).to.equal('EUR'); + expect(bid.ttl).to.equal(20); + expect(bid.meta).to.not.be.null; + expect(bid.meta.advertiserDomains).to.eql(['advertiser.com']); + }); + + it('Verify full passback', function () { + const request = spec.buildRequests(slotConfigs, bidderRequest); + const bids = spec.interpretResponse({ body: null }, request) + expect(bids).to.have.lengthOf(0); + }); + + if (FEATURES.NATIVE) { + it('Verify Native request', function () { + const request = spec.buildRequests(nativeSlotConfig, syncAddFPDToBidderRequest(bidderRequest)); + expect(request.url).to.equal('https://pbc.adxcg.net/rtb/ortb/pbc?adExchangeId=1'); + expect(request.method).to.equal('POST'); + const ortbRequest = request.data; + // native impression + expect(ortbRequest.imp[0].tagid).to.equal('77'); + expect(ortbRequest.imp[0].banner).to.be.undefined; + const nativePart = ortbRequest.imp[0]['native']; + expect(nativePart).to.not.equal(null); + 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(3); + // image asset + expect(nativeRequest.assets[0].id).to.equal(1); + expect(nativeRequest.assets[0].required).to.equal(1); + expect(nativeRequest.assets[0].title).to.be.undefined; + expect(nativeRequest.assets[0].img).to.not.equal(null); + expect(nativeRequest.assets[0].img.w).to.equal(150); + expect(nativeRequest.assets[0].img.h).to.equal(50); + expect(nativeRequest.assets[0].img.type).to.equal(3); + // title asset + expect(nativeRequest.assets[1].id).to.equal(2); + expect(nativeRequest.assets[1].required).to.equal(1); + expect(nativeRequest.assets[1].title).to.not.equal(null); + expect(nativeRequest.assets[1].title.len).to.equal(80); + // data asset + expect(nativeRequest.assets[2].id).to.equal(3); + expect(nativeRequest.assets[2].required).to.equal(0); + expect(nativeRequest.assets[2].title).to.be.undefined; + expect(nativeRequest.assets[2].data).to.not.equal(null); + expect(nativeRequest.assets[2].data.type).to.equal(1); + }); + + it('Verify Native response', function () { + const request = spec.buildRequests(nativeSlotConfig, syncAddFPDToBidderRequest(bidderRequest)); + expect(request.url).to.equal('https://pbc.adxcg.net/rtb/ortb/pbc?adExchangeId=1'); + expect(request.method).to.equal('POST'); + const ortbRequest = request.data; + const nativeResponse = { + assets: [ + { id: 1, img: { type: 3, url: 'https://images.cdn.brand.com/123' } }, + { id: 2, title: { text: 'Ad Title' } }, + { id: 3, data: { type: 1, value: 'Sponsored By: Brand' } } + ], + link: { url: 'https://brand.clickme.com/' }, + imptrackers: ['https://imp1.trackme.com/', 'https://imp1.contextweb.com/'] + + }; + const ortbResponse = { + seatbid: [{ + bid: [{ + impid: ortbRequest.imp[0].id, + price: 1.25, + adm: JSON.stringify(nativeResponse), + mtype: 4 + }] + }] + }; + const bids = spec.interpretResponse({ body: ortbResponse }, request); + // verify bid + const bid = bids[0]; + expect(bid.cpm).to.equal(1.25); + expect(bid.requestId).to.equal('bid12345'); + expect(bid.ad).to.be.undefined; + expect(bid.mediaType).to.equal('native'); + expect(bid['native']).to.not.be.null; + expect(bid['native'].ortb).to.not.be.null; + const nativeBid = bid['native'].ortb; + expect(nativeBid.assets).to.have.lengthOf(3); + expect(nativeBid.assets[0].id).to.equal(1); + expect(nativeBid.assets[0].img).to.not.be.null; + expect(nativeBid.assets[0].img.type).to.equal(3); + expect(nativeBid.assets[0].img.url).to.equal('https://images.cdn.brand.com/123'); + expect(nativeBid.assets[1].id).to.equal(2); + expect(nativeBid.assets[1].title).to.not.be.null; + expect(nativeBid.assets[1].title.text).to.equal('Ad Title'); + expect(nativeBid.assets[2].id).to.equal(3); + expect(nativeBid.assets[2].data).to.not.be.null; + expect(nativeBid.assets[2].data.type).to.equal(1); + expect(nativeBid.assets[2].data.value).to.equal('Sponsored By: Brand'); + expect(nativeBid.link).to.not.be.null; + expect(nativeBid.link.url).to.equal('https://brand.clickme.com/'); + expect(nativeBid.imptrackers).to.have.lengthOf(2); + expect(nativeBid.imptrackers[0]).to.equal('https://imp1.trackme.com/'); + expect(nativeBid.imptrackers[1]).to.equal('https://imp1.contextweb.com/'); + }); + } + + it('Verifies bidder code', function () { + expect(spec.code).to.equal('adxcg'); + }); + + it('Verifies bidder aliases', function () { + expect(spec.aliases).to.have.lengthOf(1); + expect(spec.aliases[0]).to.equal('mediaopti'); + }); + + 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'); + }); + + if (FEATURES.VIDEO) { + it('Verify Video request', function () { + const request = spec.buildRequests(videoSlotConfig, syncAddFPDToBidderRequest(bidderRequest)); + expect(request.url).to.equal('https://pbc.adxcg.net/rtb/ortb/pbc?adExchangeId=1'); + expect(request.method).to.equal('POST'); + 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.undefined; + expect(ortbRequest.imp[0].banner).to.be.undefined; + expect(ortbRequest.imp[0].video.w).to.equal(400); + expect(ortbRequest.imp[0].video.h).to.equal(300); + expect(ortbRequest.imp[0].video.minduration).to.equal(5); + expect(ortbRequest.imp[0].video.maxduration).to.equal(10); + expect(ortbRequest.imp[0].video.startdelay).to.equal(0); + expect(ortbRequest.imp[0].video.skip).to.equal(1); + expect(ortbRequest.imp[0].video.minbitrate).to.equal(200); + expect(ortbRequest.imp[0].video.protocols).to.eql([1, 2, 4]); + }); + } + + it('Verify extra parameters', function () { + let request = spec.buildRequests(additionalParamsConfig, syncAddFPDToBidderRequest(bidderRequest)); + let ortbRequest = request.data; + expect(ortbRequest).to.not.equal(null); + expect(ortbRequest.imp).to.have.lengthOf(1); + expect(ortbRequest.imp[0].ext).to.not.equal(null); + expect(ortbRequest.imp[0].ext.prebid).to.not.equal(null); + expect(ortbRequest.imp[0].ext.prebid).to.not.be.null; + expect(ortbRequest.imp[0].ext.prebid.extra_key1).to.equal('extra_val1'); + expect(ortbRequest.imp[0].ext.prebid.extra_key2).to.equal(12345); + expect(ortbRequest.imp[0].ext.prebid.extra_key3).to.not.be.null; + expect(ortbRequest.imp[0].ext.prebid.extra_key3.key1).to.equal('val1'); + expect(ortbRequest.imp[0].ext.prebid.extra_key3.key2).to.equal(23456); + expect(ortbRequest.imp[0].ext.prebid.extra_key4).to.eql([1, 2, 3]); + expect(Object.keys(ortbRequest.imp[0].ext.prebid)).to.eql(['adzoneid', 'extra_key1', 'extra_key2', 'extra_key3', 'extra_key4']); + // attempting with a configuration with no unknown params. + request = spec.buildRequests(videoSlotConfig, bidderRequest); + ortbRequest = request.data; + expect(ortbRequest).to.not.equal(null); + expect(ortbRequest.imp).to.have.lengthOf(1); + // expect(ortbRequest.imp[0].ext).to.be.undefined; + }); + + it('Verify user level first party data', function () { + const bidderRequest = { + refererInfo: { + page: 'https://publisher.com/home', + ref: 'https://referrer' + }, + gdprConsent: { + gdprApplies: true, + consentString: 'serialized_gpdr_data' + }, + ortb2: { + user: { + yob: 1985, + gender: 'm', + ext: { + data: { + registered: true, + interests: ['cars'] + } + } + } + } + }; + let request = spec.buildRequests(slotConfigs, syncAddFPDToBidderRequest(bidderRequest)); + let ortbRequest = request.data; + expect(ortbRequest).to.not.equal(null); + expect(ortbRequest.user).to.not.equal(null); + }); + + it('Verify site level first party data', function () { + const bidderRequest = { + ortb2: { + site: { + content: { + data: [{ + name: 'www.iris.com', + ext: { + segtax: 500, + cids: ['iris_c73g5jq96mwso4d8'] + } + }] + }, + page: 'http://pub.com/news', + ref: 'http://google.com', + publisher: { + domain: 'pub.com' + } + } + } + }; + let request = spec.buildRequests(slotConfigs, syncAddFPDToBidderRequest(bidderRequest)); + let ortbRequest = request.data; + expect(ortbRequest).to.not.equal(null); + expect(ortbRequest.site).to.not.equal(null); + expect(ortbRequest.site).to.deep.equal({ + content: { + data: [{ + name: 'www.iris.com', + ext: { + segtax: 500, + cids: ['iris_c73g5jq96mwso4d8'] + } + }] + }, + page: 'http://pub.com/news', + ref: 'http://google.com', + publisher: { + // id: 'p10000', + domain: 'pub.com' + } + }); + }); + + it('Verify impression/slot level first party data', function () { + const bidderRequests = [{ + placementCode: '/DfpAccount1/slot1', + mediaTypes: { + banner: { + sizes: [[1, 1]] + } + }, + bidId: 'bid12345', + params: { + cp: 'p10000', + ct: 't10000', + adzoneid: '77', + extra_key1: 'extra_val1', + extra_key2: 12345 + }, + ortb2Imp: { + ext: { + data: { + pbadslot: 'homepage-top-rect', + adUnitSpecificAttribute: '123' + } + } + } + }]; + let request = spec.buildRequests(bidderRequests, bidderRequest); + let ortbRequest = request.data; + expect(ortbRequest).to.not.equal(null); + expect(ortbRequest.imp).to.not.equal(null); + expect(ortbRequest.imp).to.have.lengthOf(1); + expect(ortbRequest.imp[0].ext).to.not.equal(null); + expect(ortbRequest.imp[0].ext).to.deep.equal({ + prebid: { + adzoneid: '77', + extra_key1: 'extra_val1', + extra_key2: 12345 + }, + data: { + pbadslot: 'homepage-top-rect', + adUnitSpecificAttribute: '123' + } + }); + }); + + it('Verify bid request timeouts', function () { + const mkRequest = (bidderRequest) => spec.buildRequests(slotConfigs, bidderRequest).data; + // assert default is used when no bidderRequest.timeout value is available + expect(mkRequest(bidderRequest).tmax).to.equal(500) + + // assert bidderRequest value is used when available + expect(mkRequest(Object.assign({}, { timeout: 6000 }, bidderRequest)).tmax).to.equal(6000) + }); }); From deff7e097b9db67c5505556e98abdd4ac6417ed5 Mon Sep 17 00:00:00 2001 From: Demetrio Girardi Date: Mon, 20 Nov 2023 13:31:43 -0800 Subject: [PATCH 006/246] Core: unify rendering paths; codify (and test) cross-domain render example (#9647) * Refactor rendering to go through a single code path * Build creative together with js * Fix pubUrl / pubDomain * Update dev tasks for creative building * Cross-domain render * Clean up empty fn * Autogenerated cross-domain creative example * Update text * Refactor creative * fix lint * Add test case for custom renderer * Always resize renderAd iframe --- gulpfile.js | 27 ++- .../gpt/x-domain/creative.html | 108 +-------- libraries/creativeRender/constants.js | 10 + libraries/creativeRender/crossDomain.js | 57 +++++ libraries/creativeRender/direct.js | 62 +++++ libraries/creativeRender/writer.js | 34 +++ package-lock.json | 211 ++++++++++++++++++ package.json | 1 + src/adRendering.js | 70 +++++- src/auction.js | 2 + src/prebid.js | 130 +---------- src/secureCreatives.js | 94 ++------ src/utils.js | 10 +- .../spec/creative/crossDomainCreative_spec.js | 182 +++++++++++++++ test/spec/unit/pbjs_api_spec.js | 28 +-- test/spec/unit/secureCreatives_spec.js | 73 +++--- webpack.creative.js | 19 ++ 17 files changed, 759 insertions(+), 359 deletions(-) create mode 100644 libraries/creativeRender/constants.js create mode 100644 libraries/creativeRender/crossDomain.js create mode 100644 libraries/creativeRender/direct.js create mode 100644 libraries/creativeRender/writer.js create mode 100644 test/spec/creative/crossDomainCreative_spec.js create mode 100644 webpack.creative.js diff --git a/gulpfile.js b/gulpfile.js index 09de874e389..5e16af8b0c1 100644 --- a/gulpfile.js +++ b/gulpfile.js @@ -158,6 +158,20 @@ function makeWebpackPkg(extraConfig = {}) { } } +function buildCreative() { + return gulp.src(['**/*']) + .pipe(webpackStream(require('./webpack.creative.js'))) + .pipe(gulp.dest('build/creative')) +} + +function updateCreativeExample(cb) { + const CREATIVE_EXAMPLE = 'integrationExamples/gpt/x-domain/creative.html'; + const root = require('node-html-parser').parse(fs.readFileSync(CREATIVE_EXAMPLE)); + root.querySelectorAll('script')[0].textContent = fs.readFileSync('build/creative/creative.js') + fs.writeFileSync(CREATIVE_EXAMPLE, root.toString()) + cb(); +} + function getModulesListToAddInBanner(modules) { if (!modules || modules.length === helpers.getModuleNames().length) { return 'All available modules for this version.' @@ -405,6 +419,7 @@ function watchTaskMaker(options = {}) { return function watch(done) { var mainWatcher = gulp.watch([ 'src/**/*.js', + 'libraries/**/*.js', 'modules/**/*.js', ].concat(options.alsoWatch)); @@ -415,8 +430,8 @@ function watchTaskMaker(options = {}) { } } -const watch = watchTaskMaker({alsoWatch: ['test/**/*.js'], task: () => gulp.series(clean, gulp.parallel(lint, 'build-bundle-dev', test))}); -const watchFast = watchTaskMaker({livereload: false, task: () => gulp.series('build-bundle-dev')}); +const watch = watchTaskMaker({alsoWatch: ['test/**/*.js'], task: () => gulp.series(clean, gulp.parallel(lint, 'build-bundle-dev', test, buildCreative))}); +const watchFast = watchTaskMaker({livereload: false, task: () => gulp.parallel('build-bundle-dev', buildCreative)}); // support tasks gulp.task(lint); @@ -447,21 +462,23 @@ gulp.task('build-bundle-verbose', gulp.series(makeWebpackPkg({ } }), gulpBundle.bind(null, false))); +gulp.task('build-creative', gulp.series(buildCreative, updateCreativeExample)); + // public tasks (dependencies are needed for each task since they can be ran on their own) gulp.task('test-only', test); gulp.task('test-all-features-disabled', testTaskMaker({disableFeatures: require('./features.json'), oneBrowser: 'chrome', watch: false})); -gulp.task('test', gulp.series(clean, lint, gulp.series('test-all-features-disabled', 'test-only'))); +gulp.task('test', gulp.series(clean, lint, gulp.parallel('build-creative', gulp.series('test-all-features-disabled', 'test-only')))); gulp.task('test-coverage', gulp.series(clean, testCoverage)); gulp.task(viewCoverage); gulp.task('coveralls', gulp.series('test-coverage', coveralls)); -gulp.task('build', gulp.series(clean, 'build-bundle-prod')); +gulp.task('build', gulp.series(clean, 'build-bundle-prod', 'build-creative')); gulp.task('build-postbid', gulp.series(escapePostbidConfig, buildPostbid)); gulp.task('serve', gulp.series(clean, lint, gulp.parallel('build-bundle-dev', watch, test))); -gulp.task('serve-fast', gulp.series(clean, gulp.parallel('build-bundle-dev', watchFast))); +gulp.task('serve-fast', gulp.series(clean, gulp.parallel('build-bundle-dev', buildCreative, watchFast))); gulp.task('serve-prod', gulp.series(clean, gulp.parallel('build-bundle-prod', startLocalServer))); gulp.task('serve-and-test', gulp.series(clean, gulp.parallel('build-bundle-dev', watchFast, testTaskMaker({watch: true})))); gulp.task('serve-e2e', gulp.series(clean, 'build-bundle-prod', gulp.parallel(() => startIntegServer(), startLocalServer))); diff --git a/integrationExamples/gpt/x-domain/creative.html b/integrationExamples/gpt/x-domain/creative.html index 2216d0ed6ae..f1c0c647e72 100644 --- a/integrationExamples/gpt/x-domain/creative.html +++ b/integrationExamples/gpt/x-domain/creative.html @@ -1,105 +1,13 @@ - -} - - -function requestAdFromPrebid() { - const message = JSON.stringify({ - message: 'Prebid Request', - adId - }); - const channel = new MessageChannel(); - channel.port1.onmessage = renderAd; - window.parent.postMessage(message, publisherDomain, [channel.port2]); -} - -function listenAdFromPrebid() { - window.addEventListener('message', receiveMessage, false); -} - -listenAdFromPrebid(); -requestAdFromPrebid(); + diff --git a/libraries/creativeRender/constants.js b/libraries/creativeRender/constants.js new file mode 100644 index 00000000000..7b67f8ed5cd --- /dev/null +++ b/libraries/creativeRender/constants.js @@ -0,0 +1,10 @@ +import events from '../../src/constants.json'; + +export const PREBID_NATIVE = 'Prebid Native'; +export const PREBID_REQUEST = 'Prebid Request'; +export const PREBID_RESPONSE = 'Prebid Response'; +export const PREBID_EVENT = 'Prebid Event'; +export const AD_RENDER_SUCCEEDED = events.EVENTS.AD_RENDER_SUCCEEDED; +export const AD_RENDER_FAILED = events.EVENTS.AD_RENDER_FAILED; +export const NO_AD = events.AD_RENDER_FAILED_REASON.NO_AD; +export const EXCEPTION = events.AD_RENDER_FAILED_REASON.EXCEPTION; diff --git a/libraries/creativeRender/crossDomain.js b/libraries/creativeRender/crossDomain.js new file mode 100644 index 00000000000..ffa8b468f12 --- /dev/null +++ b/libraries/creativeRender/crossDomain.js @@ -0,0 +1,57 @@ +import {mkFrame, writeAd} from './writer.js'; +import { + AD_RENDER_FAILED, + AD_RENDER_SUCCEEDED, + PREBID_EVENT, + PREBID_RESPONSE, + PREBID_REQUEST, + EXCEPTION +} from './constants.js'; + +export function renderer(win = window) { + return function ({adId, pubUrl, clickUrl}) { + const pubDomain = (function() { + const a = win.document.createElement('a'); + a.href = pubUrl; + return a.protocol + '//' + a.host; + })(); + function sendMessage(type, payload, transfer) { + win.parent.postMessage(JSON.stringify(Object.assign({message: type, adId}, payload)), pubDomain, transfer); + } + function cb(err) { + sendMessage(PREBID_EVENT, { + event: err == null ? AD_RENDER_SUCCEEDED : AD_RENDER_FAILED, + info: err + }); + } + function onMessage(ev) { + let data = {}; + try { + data = JSON.parse(ev[ev.message ? 'message' : 'data']); + } catch (e) { + return; + } + if (data.message === PREBID_RESPONSE && data.adId === adId) { + try { + let doc = win.document + if (data.ad) { + doc = mkFrame(doc, {width: data.width, height: data.height}).contentDocument; + doc.open(); + } + writeAd(data, cb, doc); + } catch (e) { + // eslint-disable-next-line standard/no-callback-literal + cb({ reason: EXCEPTION, message: e.message }) + } + } + } + + const channel = new MessageChannel(); + channel.port1.onmessage = onMessage; + sendMessage(PREBID_REQUEST, { + options: {clickUrl} + }, [channel.port2]); + win.addEventListener('message', onMessage, false); + } +} +window.renderAd = renderer(); diff --git a/libraries/creativeRender/direct.js b/libraries/creativeRender/direct.js new file mode 100644 index 00000000000..0b4fb2f0a75 --- /dev/null +++ b/libraries/creativeRender/direct.js @@ -0,0 +1,62 @@ +import {emitAdRenderFail, emitAdRenderSucceeded, handleRender} from '../../src/adRendering.js'; +import {writeAd} from './writer.js'; +import {auctionManager} from '../../src/auctionManager.js'; +import CONSTANTS from '../../src/constants.json'; +import {inIframe, insertElement} from '../../src/utils.js'; +import {getGlobal} from '../../src/prebidGlobal.js'; +import {EXCEPTION} from './constants.js'; + +export function renderAdDirect(doc, adId, options) { + let bid; + function cb(err) { + if (err != null) { + emitAdRenderFail(Object.assign({id: adId, bid}, err)); + } else { + emitAdRenderSucceeded({doc, bid, adId}) + } + } + function renderFn(adData) { + writeAd(adData, cb, doc); + if (doc.defaultView && doc.defaultView.frameElement) { + doc.defaultView.frameElement.width = adData.width; + doc.defaultView.frameElement.height = adData.height; + } + // TODO: this is almost certainly the wrong way to do this + const creativeComment = document.createComment(`Creative ${bid.creativeId} served by ${bid.bidder} Prebid.js Header Bidding`); + insertElement(creativeComment, doc, 'html'); + } + try { + if (!adId || !doc) { + // eslint-disable-next-line standard/no-callback-literal + cb({ + reason: CONSTANTS.AD_RENDER_FAILED_REASON.MISSING_DOC_OR_ADID, + message: `missing ${adId ? 'doc' : 'adId'}` + }); + } else { + bid = auctionManager.findBidByAdId(adId); + + if (FEATURES.VIDEO) { + // TODO: could the video module implement this as a custom renderer, rather than a special case in here? + const adUnit = bid && auctionManager.index.getAdUnit(bid); + const videoModule = getGlobal().videoModule; + if (adUnit?.video && videoModule) { + videoModule.renderBid(adUnit.video.divId, bid); + return; + } + } + + if ((doc === document && !inIframe())) { + // eslint-disable-next-line standard/no-callback-literal + cb({ + reason: CONSTANTS.AD_RENDER_FAILED_REASON.PREVENT_WRITING_ON_MAIN_DOCUMENT, + message: `renderAd was prevented from writing to the main document.` + }) + } else { + handleRender(renderFn, {adId, options: {clickUrl: options?.clickThrough}, bidResponse: bid}); + } + } + } catch (e) { + // eslint-disable-next-line standard/no-callback-literal + cb({reason: EXCEPTION, message: e.message}) + } +} diff --git a/libraries/creativeRender/writer.js b/libraries/creativeRender/writer.js new file mode 100644 index 00000000000..80bb0592a1f --- /dev/null +++ b/libraries/creativeRender/writer.js @@ -0,0 +1,34 @@ +import {NO_AD} from './constants.js'; + +const IFRAME_ATTRS = { + frameBorder: 0, + scrolling: 'no', + marginHeight: 0, + marginWidth: 0, + topMargin: 0, + leftMargin: 0, + allowTransparency: 'true', +}; + +export function mkFrame(doc, attrs) { + const frame = doc.createElement('iframe'); + attrs = Object.assign({}, attrs, IFRAME_ATTRS); + Object.entries(attrs).forEach(([k, v]) => frame.setAttribute(k, v)); + doc.body.appendChild(frame); + return frame; +} + +export function writeAd({ad, adUrl, width, height}, cb, doc = document) { + if (!ad && !adUrl) { + // eslint-disable-next-line standard/no-callback-literal + cb({reason: NO_AD, message: 'Missing ad markup or URL'}); + } else { + if (adUrl && !ad) { + mkFrame(doc, {width, height, src: adUrl}) + } else { + doc.write(ad); + doc.close(); + } + cb(); + } +} diff --git a/package-lock.json b/package-lock.json index caee07c67f6..4650db8f2ab 100644 --- a/package-lock.json +++ b/package-lock.json @@ -90,6 +90,7 @@ "lodash": "^4.17.21", "mocha": "^10.0.0", "morgan": "^1.10.0", + "node-html-parser": "^6.1.5", "opn": "^5.4.0", "resolve-from": "^5.0.0", "sinon": "^4.1.3", @@ -6491,6 +6492,12 @@ "integrity": "sha512-ev2QzSzWPYmy9GuqfIVildA4OdcGLeFZQrq5ys6RtiuF+RQQiZWr8TZNyAcuVXyQRYfEO+MsoB/1BuQVhOJuoQ==", "dev": true }, + "node_modules/boolbase": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/boolbase/-/boolbase-1.0.0.tgz", + "integrity": "sha512-JZOSA7Mo9sNGB8+UjSgzdLtokWAky1zbztM3WRLCbZ70/3cTANmQmOdR7y2g+J0e2WXywy1yS468tY+IruqEww==", + "dev": true + }, "node_modules/brace-expansion": { "version": "1.1.11", "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", @@ -7855,6 +7862,22 @@ "source-map-resolve": "^0.6.0" } }, + "node_modules/css-select": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/css-select/-/css-select-5.1.0.tgz", + "integrity": "sha512-nwoRF1rvRRnnCqqY7updORDsuqKzqYJ28+oSMaJMMgOauh3fvwHqMS7EZpIPqK8GL+g9mKxF1vP/ZjSeNjEVHg==", + "dev": true, + "dependencies": { + "boolbase": "^1.0.0", + "css-what": "^6.1.0", + "domhandler": "^5.0.2", + "domutils": "^3.0.1", + "nth-check": "^2.0.1" + }, + "funding": { + "url": "https://github.com/sponsors/fb55" + } + }, "node_modules/css-shorthand-properties": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/css-shorthand-properties/-/css-shorthand-properties-1.1.1.tgz", @@ -7867,6 +7890,18 @@ "integrity": "sha512-FUV3xaJ63buRLgHrLQVlVgQnQdR4yqdLGaDu7g8CQcWjInDfM9plBTPI9FRfpahju1UBSaMckeb2/46ApS/V1Q==", "dev": true }, + "node_modules/css-what": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/css-what/-/css-what-6.1.0.tgz", + "integrity": "sha512-HTUrgRJ7r4dsZKU6GjmpfRK1O76h97Z8MfS1G0FozR+oF2kG6Vfe8JE6zwrkbxigziPHinCJ+gCPjA9EaBDtRw==", + "dev": true, + "engines": { + "node": ">= 6" + }, + "funding": { + "url": "https://github.com/sponsors/fb55" + } + }, "node_modules/css/node_modules/source-map": { "version": "0.6.1", "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", @@ -8693,12 +8728,67 @@ "void-elements": "^2.0.0" } }, + "node_modules/dom-serializer": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-2.0.0.tgz", + "integrity": "sha512-wIkAryiqt/nV5EQKqQpo3SToSOV9J0DnbJqwK7Wv/Trc92zIAYZ4FlMu+JPFW1DfGFt81ZTCGgDEabffXeLyJg==", + "dev": true, + "dependencies": { + "domelementtype": "^2.3.0", + "domhandler": "^5.0.2", + "entities": "^4.2.0" + }, + "funding": { + "url": "https://github.com/cheeriojs/dom-serializer?sponsor=1" + } + }, "node_modules/dom-walk": { "version": "0.1.2", "resolved": "https://registry.npmjs.org/dom-walk/-/dom-walk-0.1.2.tgz", "integrity": "sha512-6QvTW9mrGeIegrFXdtQi9pk7O/nSK6lSdXW2eqUspN5LWD7UTji2Fqw5V2YLjBpHEoU9Xl/eUWNpDeZvoyOv2w==", "dev": true }, + "node_modules/domelementtype": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/domelementtype/-/domelementtype-2.3.0.tgz", + "integrity": "sha512-OLETBj6w0OsagBwdXnPdN0cnMfF9opN69co+7ZrbfPGrdpPVNBUj02spi6B1N7wChLQiPn4CSH/zJvXw56gmHw==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/fb55" + } + ] + }, + "node_modules/domhandler": { + "version": "5.0.3", + "resolved": "https://registry.npmjs.org/domhandler/-/domhandler-5.0.3.tgz", + "integrity": "sha512-cgwlv/1iFQiFnU96XXgROh8xTeetsnJiDsTc7TYCLFd9+/WNkIqPTxiM/8pSd8VIrhXGTf1Ny1q1hquVqDJB5w==", + "dev": true, + "dependencies": { + "domelementtype": "^2.3.0" + }, + "engines": { + "node": ">= 4" + }, + "funding": { + "url": "https://github.com/fb55/domhandler?sponsor=1" + } + }, + "node_modules/domutils": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/domutils/-/domutils-3.1.0.tgz", + "integrity": "sha512-H78uMmQtI2AhgDJjWeQmHwJJ2bLPD3GMmO7Zja/ZZh84wkm+4ut+IUnUdRa8uCGX88DiVx1j6FRe1XfxEgjEZA==", + "dev": true, + "dependencies": { + "dom-serializer": "^2.0.0", + "domelementtype": "^2.3.0", + "domhandler": "^5.0.3" + }, + "funding": { + "url": "https://github.com/fb55/domutils?sponsor=1" + } + }, "node_modules/dset": { "version": "3.1.2", "resolved": "https://registry.npmjs.org/dset/-/dset-3.1.2.tgz", @@ -8959,6 +9049,18 @@ "integrity": "sha512-GHrMyVZQWvTIdDtpiEXdHZnFQKzeO09apj8Cbl4pKWy4i0Oprcq17usfDt5aO63swf0JOeMWjWQE/LzgSRuWpA==", "dev": true }, + "node_modules/entities": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/entities/-/entities-4.5.0.tgz", + "integrity": "sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw==", + "dev": true, + "engines": { + "node": ">=0.12" + }, + "funding": { + "url": "https://github.com/fb55/entities?sponsor=1" + } + }, "node_modules/errno": { "version": "0.1.8", "resolved": "https://registry.npmjs.org/errno/-/errno-0.1.8.tgz", @@ -18708,6 +18810,16 @@ } } }, + "node_modules/node-html-parser": { + "version": "6.1.6", + "resolved": "https://registry.npmjs.org/node-html-parser/-/node-html-parser-6.1.6.tgz", + "integrity": "sha512-C/MGDQ2NjdjzUq41bW9kW00MPZecAe/oo89vZEFLDfWoQVDk/DdML1yuxVVKLDMFIFax2VTq6Vpfzyn7z5yYgQ==", + "dev": true, + "dependencies": { + "css-select": "^5.1.0", + "he": "1.2.0" + } + }, "node_modules/node-releases": { "version": "2.0.6", "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.6.tgz", @@ -18809,6 +18921,18 @@ "node": ">=4" } }, + "node_modules/nth-check": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/nth-check/-/nth-check-2.1.1.tgz", + "integrity": "sha512-lqjrjmaOoAnWfMmBPL+XNnynZh2+swxiX3WUE0s4yEHI6m+AwrK2UZOimIRl3X/4QctVqS8AiZjFqyOGrMXb/w==", + "dev": true, + "dependencies": { + "boolbase": "^1.0.0" + }, + "funding": { + "url": "https://github.com/fb55/nth-check?sponsor=1" + } + }, "node_modules/number-is-nan": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/number-is-nan/-/number-is-nan-1.0.1.tgz", @@ -30247,6 +30371,12 @@ } } }, + "boolbase": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/boolbase/-/boolbase-1.0.0.tgz", + "integrity": "sha512-JZOSA7Mo9sNGB8+UjSgzdLtokWAky1zbztM3WRLCbZ70/3cTANmQmOdR7y2g+J0e2WXywy1yS468tY+IruqEww==", + "dev": true + }, "brace-expansion": { "version": "1.1.11", "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", @@ -31305,6 +31435,19 @@ } } }, + "css-select": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/css-select/-/css-select-5.1.0.tgz", + "integrity": "sha512-nwoRF1rvRRnnCqqY7updORDsuqKzqYJ28+oSMaJMMgOauh3fvwHqMS7EZpIPqK8GL+g9mKxF1vP/ZjSeNjEVHg==", + "dev": true, + "requires": { + "boolbase": "^1.0.0", + "css-what": "^6.1.0", + "domhandler": "^5.0.2", + "domutils": "^3.0.1", + "nth-check": "^2.0.1" + } + }, "css-shorthand-properties": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/css-shorthand-properties/-/css-shorthand-properties-1.1.1.tgz", @@ -31317,6 +31460,12 @@ "integrity": "sha512-FUV3xaJ63buRLgHrLQVlVgQnQdR4yqdLGaDu7g8CQcWjInDfM9plBTPI9FRfpahju1UBSaMckeb2/46ApS/V1Q==", "dev": true }, + "css-what": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/css-what/-/css-what-6.1.0.tgz", + "integrity": "sha512-HTUrgRJ7r4dsZKU6GjmpfRK1O76h97Z8MfS1G0FozR+oF2kG6Vfe8JE6zwrkbxigziPHinCJ+gCPjA9EaBDtRw==", + "dev": true + }, "custom-event": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/custom-event/-/custom-event-1.0.1.tgz", @@ -31928,12 +32077,49 @@ "void-elements": "^2.0.0" } }, + "dom-serializer": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-2.0.0.tgz", + "integrity": "sha512-wIkAryiqt/nV5EQKqQpo3SToSOV9J0DnbJqwK7Wv/Trc92zIAYZ4FlMu+JPFW1DfGFt81ZTCGgDEabffXeLyJg==", + "dev": true, + "requires": { + "domelementtype": "^2.3.0", + "domhandler": "^5.0.2", + "entities": "^4.2.0" + } + }, "dom-walk": { "version": "0.1.2", "resolved": "https://registry.npmjs.org/dom-walk/-/dom-walk-0.1.2.tgz", "integrity": "sha512-6QvTW9mrGeIegrFXdtQi9pk7O/nSK6lSdXW2eqUspN5LWD7UTji2Fqw5V2YLjBpHEoU9Xl/eUWNpDeZvoyOv2w==", "dev": true }, + "domelementtype": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/domelementtype/-/domelementtype-2.3.0.tgz", + "integrity": "sha512-OLETBj6w0OsagBwdXnPdN0cnMfF9opN69co+7ZrbfPGrdpPVNBUj02spi6B1N7wChLQiPn4CSH/zJvXw56gmHw==", + "dev": true + }, + "domhandler": { + "version": "5.0.3", + "resolved": "https://registry.npmjs.org/domhandler/-/domhandler-5.0.3.tgz", + "integrity": "sha512-cgwlv/1iFQiFnU96XXgROh8xTeetsnJiDsTc7TYCLFd9+/WNkIqPTxiM/8pSd8VIrhXGTf1Ny1q1hquVqDJB5w==", + "dev": true, + "requires": { + "domelementtype": "^2.3.0" + } + }, + "domutils": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/domutils/-/domutils-3.1.0.tgz", + "integrity": "sha512-H78uMmQtI2AhgDJjWeQmHwJJ2bLPD3GMmO7Zja/ZZh84wkm+4ut+IUnUdRa8uCGX88DiVx1j6FRe1XfxEgjEZA==", + "dev": true, + "requires": { + "dom-serializer": "^2.0.0", + "domelementtype": "^2.3.0", + "domhandler": "^5.0.3" + } + }, "dset": { "version": "3.1.2", "resolved": "https://registry.npmjs.org/dset/-/dset-3.1.2.tgz", @@ -32164,6 +32350,12 @@ "integrity": "sha512-GHrMyVZQWvTIdDtpiEXdHZnFQKzeO09apj8Cbl4pKWy4i0Oprcq17usfDt5aO63swf0JOeMWjWQE/LzgSRuWpA==", "dev": true }, + "entities": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/entities/-/entities-4.5.0.tgz", + "integrity": "sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw==", + "dev": true + }, "errno": { "version": "0.1.8", "resolved": "https://registry.npmjs.org/errno/-/errno-0.1.8.tgz", @@ -39738,6 +39930,16 @@ "whatwg-url": "^5.0.0" } }, + "node-html-parser": { + "version": "6.1.6", + "resolved": "https://registry.npmjs.org/node-html-parser/-/node-html-parser-6.1.6.tgz", + "integrity": "sha512-C/MGDQ2NjdjzUq41bW9kW00MPZecAe/oo89vZEFLDfWoQVDk/DdML1yuxVVKLDMFIFax2VTq6Vpfzyn7z5yYgQ==", + "dev": true, + "requires": { + "css-select": "^5.1.0", + "he": "1.2.0" + } + }, "node-releases": { "version": "2.0.6", "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.6.tgz", @@ -39813,6 +40015,15 @@ } } }, + "nth-check": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/nth-check/-/nth-check-2.1.1.tgz", + "integrity": "sha512-lqjrjmaOoAnWfMmBPL+XNnynZh2+swxiX3WUE0s4yEHI6m+AwrK2UZOimIRl3X/4QctVqS8AiZjFqyOGrMXb/w==", + "dev": true, + "requires": { + "boolbase": "^1.0.0" + } + }, "number-is-nan": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/number-is-nan/-/number-is-nan-1.0.1.tgz", diff --git a/package.json b/package.json index 7076d012285..4ea9131920d 100644 --- a/package.json +++ b/package.json @@ -101,6 +101,7 @@ "lodash": "^4.17.21", "mocha": "^10.0.0", "morgan": "^1.10.0", + "node-html-parser": "^6.1.5", "opn": "^5.4.0", "resolve-from": "^5.0.0", "sinon": "^4.1.3", diff --git a/src/adRendering.js b/src/adRendering.js index 0a847d7cc25..c3e518eae4d 100644 --- a/src/adRendering.js +++ b/src/adRendering.js @@ -1,8 +1,12 @@ -import {logError} from './utils.js'; +import {deepAccess, logError, logWarn, replaceMacros} from './utils.js'; import * as events from './events.js'; -import CONSTANTS from './constants.json'; +import constants from './constants.json'; +import {config} from './config.js'; +import {executeRenderer, isRendererRequired} from './Renderer.js'; +import {VIDEO} from './mediaTypes.js'; +import {auctionManager} from './auctionManager.js'; -const {AD_RENDER_FAILED, AD_RENDER_SUCCEEDED} = CONSTANTS.EVENTS; +const {AD_RENDER_FAILED, AD_RENDER_SUCCEEDED, STALE_RENDER, BID_WON} = constants.EVENTS; /** * Emit the AD_RENDER_FAILED event. @@ -17,7 +21,7 @@ export function emitAdRenderFail({ reason, message, bid, id }) { if (bid) data.bid = bid; if (id) data.adId = id; - logError(message); + logError(`Error rendering ad (id: ${id}): ${message}`); events.emit(AD_RENDER_FAILED, data); } @@ -36,3 +40,61 @@ export function emitAdRenderSucceeded({ doc, bid, id }) { events.emit(AD_RENDER_SUCCEEDED, data); } + +export function handleRender(renderFn, {adId, options, bidResponse}) { + if (bidResponse == null) { + emitAdRenderFail({ + reason: constants.AD_RENDER_FAILED_REASON.CANNOT_FIND_AD, + message: `Cannot find ad '${adId}'`, + id: adId + }); + return; + } + if (bidResponse.status === constants.BID_STATUS.RENDERED) { + logWarn(`Ad id ${adId} has been rendered before`); + events.emit(STALE_RENDER, bidResponse); + if (deepAccess(config.getConfig('auctionOptions'), 'suppressStaleRender')) { + return; + } + } + try { + const {adId, ad, adUrl, width, height, renderer, cpm, originalCpm, mediaType} = bidResponse; + // rendering for outstream safeframe + if (isRendererRequired(renderer)) { + executeRenderer(renderer, bidResponse); + } else if (adId) { + if (mediaType === VIDEO) { + emitAdRenderFail({ + reason: constants.AD_RENDER_FAILED_REASON.PREVENT_WRITING_ON_MAIN_DOCUMENT, + message: 'Cannot render video ad', + bid: bidResponse, + id: adId + }); + return; + } + const repl = { + AUCTION_PRICE: originalCpm || cpm, + CLICKTHROUGH: options?.clickUrl || '' + }; + renderFn({ + ad: replaceMacros(ad, repl), + adUrl: replaceMacros(adUrl, repl), + adId, + width, + height + }); + } + } catch (e) { + emitAdRenderFail({ + reason: constants.AD_RENDER_FAILED_REASON.EXCEPTION, + message: e.message, + id: adId, + bid: bidResponse + }); + return; + } + // save winning bids + auctionManager.addWinningBid(bidResponse); + + events.emit(BID_WON, bidResponse); +} diff --git a/src/auction.js b/src/auction.js index efce69ea9f8..0d256bc3cd7 100644 --- a/src/auction.js +++ b/src/auction.js @@ -58,6 +58,7 @@ */ import { + callBurl, deepAccess, generateUUID, getValue, @@ -362,6 +363,7 @@ export function newAuction({adUnits, adUnitCodes, callback, cbTimeout, labels, a function addWinningBid(winningBid) { const winningAd = adUnits.find(adUnit => adUnit.transactionId === winningBid.transactionId); _winningBids = _winningBids.concat(winningBid); + callBurl(winningBid); adapterManager.callBidWonBidder(winningBid.adapterCode || winningBid.bidder, winningBid, adUnits); if (winningAd && !winningAd.deferBilling) adapterManager.callBidBillableBidder(winningBid); } diff --git a/src/prebid.js b/src/prebid.js index 6ad5120ce82..df224ab83cf 100644 --- a/src/prebid.js +++ b/src/prebid.js @@ -2,15 +2,11 @@ import {getGlobal} from './prebidGlobal.js'; import { - callBurl, - createInvisibleIframe, deepAccess, deepClone, deepSetValue, flatten, generateUUID, - inIframe, - insertElement, isArray, isArrayOfNums, isEmpty, @@ -22,8 +18,6 @@ import { logMessage, logWarn, mergeDeep, - replaceAuctionPrice, - replaceClickThrough, transformAdServerTargetingObj, uniques, unsupportedBidderMessage @@ -37,10 +31,8 @@ import {hook, wrapHook} from './hook.js'; import {loadSession} from './debugging.js'; import {includes} from './polyfill.js'; import {adunitCounter} from './adUnits.js'; -import {executeRenderer, isRendererRequired} from './Renderer.js'; import {createBid} from './bidfactory.js'; import {storageCallbacks} from './storageManager.js'; -import {emitAdRenderFail, emitAdRenderSucceeded} from './adRendering.js'; import {default as adapterManager, getS2SBidderSet} from './adapterManager.js'; import CONSTANTS from './constants.json'; import * as events from './events.js'; @@ -48,6 +40,7 @@ import {newMetrics, useMetrics} from './utils/perfMetrics.js'; import {defer, GreedyPromise} from './utils/promise.js'; import {enrichFPD} from './fpd/enrichment.js'; import {allConsent} from './consentHandler.js'; +import {renderAdDirect} from '../libraries/creativeRender/direct.js'; import {getHighestCpm} from './utils/reducers.js'; import {fillVideoDefaults} from './video.js'; @@ -55,8 +48,7 @@ const pbjsInstance = getGlobal(); const { triggerUserSyncs } = userSync; /* private variables */ -const { ADD_AD_UNITS, BID_WON, REQUEST_BIDS, SET_TARGETING, 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 { ADD_AD_UNITS, REQUEST_BIDS, SET_TARGETING } = CONSTANTS.EVENTS; const eventValidators = { bidWon: checkDefinedPlacement @@ -96,13 +88,6 @@ function checkDefinedPlacement(id) { return true; } -function setRenderSize(doc, width, height) { - if (doc.defaultView && doc.defaultView.frameElement) { - doc.defaultView.frameElement.width = width; - doc.defaultView.frameElement.height = height; - } -} - function validateSizes(sizes, targLength) { let cleanSizes = []; if (isArray(sizes) && ((targLength) ? sizes.length === targLength : sizes.length > 0)) { @@ -458,19 +443,6 @@ pbjsInstance.setTargetingForAst = function (adUnitCodes) { events.emit(SET_TARGETING, targeting.getAllTargeting()); }; -/** - * This function will check for presence of given node in given parent. If not present - will inject it. - * @param {Node} node node, whose existance is in question - * @param {Document} doc document element do look in - * @param {string} tagName tag name to look in - */ -function reinjectNodeIfRemoved(node, doc, tagName) { - const injectionNode = doc.querySelector(tagName); - if (!node.parentNode || node.parentNode !== injectionNode) { - insertElement(node, doc, tagName); - } -} - /** * 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 @@ -481,103 +453,7 @@ function reinjectNodeIfRemoved(node, doc, tagName) { pbjsInstance.renderAd = hook('async', function (doc, id, options) { logInfo('Invoking $$PREBID_GLOBAL$$.renderAd', arguments); logMessage('Calling renderAd with adId :' + id); - - if (!id) { - const message = `Error trying to write ad Id :${id} to the page. Missing adId`; - emitAdRenderFail({ reason: MISSING_DOC_OR_ADID, message, id }); - return; - } - - try { - // lookup ad by ad Id - const bid = auctionManager.findBidByAdId(id); - if (!bid) { - const message = `Error trying to write ad. Cannot find ad by given id : ${id}`; - emitAdRenderFail({ reason: CANNOT_FIND_AD, message, id }); - return; - } - - if (bid.status === CONSTANTS.BID_STATUS.RENDERED) { - logWarn(`Ad id ${bid.adId} has been rendered before`); - events.emit(STALE_RENDER, bid); - if (deepAccess(config.getConfig('auctionOptions'), 'suppressStaleRender')) { - return; - } - } - - // replace macros according to openRTB with price paid = bid.cpm - bid.ad = replaceAuctionPrice(bid.ad, bid.originalCpm || bid.cpm); - bid.adUrl = replaceAuctionPrice(bid.adUrl, bid.originalCpm || bid.cpm); - // replacing clickthrough if submitted - if (options && options.clickThrough) { - const {clickThrough} = options; - bid.ad = replaceClickThrough(bid.ad, clickThrough); - bid.adUrl = 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; - - // video module - if (FEATURES.VIDEO) { - const adUnitCode = bid.adUnitCode; - const adUnit = pbjsInstance.adUnits.filter(adUnit => adUnit.code === adUnitCode); - const videoModule = pbjsInstance.videoModule; - if (adUnit.video && videoModule) { - videoModule.renderBid(adUnit.video.divId, bid); - return; - } - } - - if (!doc) { - const message = `Error trying to write ad Id :${id} to the page. Missing document`; - emitAdRenderFail({ reason: MISSING_DOC_OR_ADID, message, id }); - return; - } - - const creativeComment = document.createComment(`Creative ${bid.creativeId} served by ${bid.bidder} Prebid.js Header Bidding`); - insertElement(creativeComment, doc, 'html'); - - if (isRendererRequired(renderer)) { - executeRenderer(renderer, bid, doc); - reinjectNodeIfRemoved(creativeComment, doc, 'html'); - emitAdRenderSucceeded({ doc, bid, id }); - } else if ((doc === document && !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) { - doc.write(ad); - doc.close(); - setRenderSize(doc, width, height); - reinjectNodeIfRemoved(creativeComment, doc, 'html'); - callBurl(bid); - emitAdRenderSucceeded({ doc, bid, id }); - } else if (adUrl) { - const iframe = createInvisibleIframe(); - iframe.height = height; - iframe.width = width; - iframe.style.display = 'inline'; - iframe.style.overflow = 'hidden'; - iframe.src = adUrl; - - insertElement(iframe, doc, 'body'); - setRenderSize(doc, width, height); - reinjectNodeIfRemoved(creativeComment, doc, 'html'); - 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}); - } - } catch (e) { - const message = `Error trying to write ad Id :${id} to the page:${e.message}`; - emitAdRenderFail({ reason: EXCEPTION, message, id }); - } + renderAdDirect(doc, id, options); }); /** diff --git a/src/secureCreatives.js b/src/secureCreatives.js index c719bc191f2..0ea93e7e4fb 100644 --- a/src/secureCreatives.js +++ b/src/secureCreatives.js @@ -6,26 +6,24 @@ import * as events from './events.js'; import {fireNativeTrackers, getAllAssetsMessage, getAssetMessage} from './native.js'; import constants from './constants.json'; -import {deepAccess, isApnGetTagDefined, isGptPubadsDefined, logError, logWarn, replaceAuctionPrice} from './utils.js'; +import {isApnGetTagDefined, isGptPubadsDefined, logError, logWarn} from './utils.js'; import {auctionManager} from './auctionManager.js'; import {find, includes} from './polyfill.js'; -import {executeRenderer, isRendererRequired} from './Renderer.js'; -import {config} from './config.js'; -import {emitAdRenderFail, emitAdRenderSucceeded} from './adRendering.js'; +import {emitAdRenderFail, emitAdRenderSucceeded, handleRender} from './adRendering.js'; +import {PREBID_EVENT, PREBID_NATIVE, PREBID_REQUEST, PREBID_RESPONSE} from '../libraries/creativeRender/constants.js'; const BID_WON = constants.EVENTS.BID_WON; -const STALE_RENDER = constants.EVENTS.STALE_RENDER; const WON_AD_IDS = new WeakSet(); const HANDLER_MAP = { - 'Prebid Request': handleRenderRequest, - 'Prebid Event': handleEventRequest, -} + [PREBID_REQUEST]: handleRenderRequest, + [PREBID_EVENT]: handleEventRequest, +}; if (FEATURES.NATIVE) { Object.assign(HANDLER_MAP, { - 'Prebid Native': handleNativeRequest, - }) + [PREBID_NATIVE]: handleNativeRequest, + }); } export function listenMessagesFromCreative() { @@ -35,18 +33,18 @@ export function listenMessagesFromCreative() { export function getReplier(ev) { if (ev.origin == null && ev.ports.length === 0) { return function () { - const msg = 'Cannot post message to a frame with null origin. Please update creatives to use MessageChannel, see https://github.com/prebid/Prebid.js/issues/7870' - logError(msg) + const msg = 'Cannot post message to a frame with null origin. Please update creatives to use MessageChannel, see https://github.com/prebid/Prebid.js/issues/7870'; + logError(msg); throw new Error(msg); - } + }; } else if (ev.ports.length > 0) { return function (message) { ev.ports[0].postMessage(JSON.stringify(message)); - } + }; } else { return function (message) { ev.source.postMessage(JSON.stringify(message), ev.origin); - } + }; } } @@ -69,39 +67,13 @@ export function receiveMessage(ev) { } } -function handleRenderRequest(reply, data, adObject) { - if (adObject == null) { - emitAdRenderFail({ - reason: constants.AD_RENDER_FAILED_REASON.CANNOT_FIND_AD, - message: `Cannot find ad for cross-origin render request: '${data.adId}'`, - id: data.adId - }); - return; - } - 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; - } - } - - try { - _sendAdToCreative(adObject, reply); - } catch (e) { - emitAdRenderFail({ - reason: constants.AD_RENDER_FAILED_REASON.EXCEPTION, - message: e.message, - id: data.adId, - bid: adObject - }); - return; - } - - // save winning bids - auctionManager.addWinningBid(adObject); - - events.emit(BID_WON, adObject); +function handleRenderRequest(reply, message, bidResponse) { + handleRender(function (adData) { + resizeRemoteCreative(bidResponse); + reply(Object.assign({ + message: PREBID_RESPONSE, + }, adData)); + }, {options: message.options, adId: message.adId, bidResponse}); } function handleNativeRequest(reply, data, adObject) { @@ -164,29 +136,11 @@ function handleEventRequest(reply, data, adObject) { }); break; default: - logError(`Received x-origin event request for unsupported event: '${data.event}' (adId: '${data.adId}')`) - } -} - -export function _sendAdToCreative(adObject, reply) { - const { adId, ad, adUrl, width, height, renderer, cpm, originalCpm } = adObject; - // rendering for outstream safeframe - if (isRendererRequired(renderer)) { - executeRenderer(renderer, adObject); - } else if (adId) { - resizeRemoteCreative(adObject); - reply({ - message: 'Prebid Response', - ad: replaceAuctionPrice(ad, originalCpm || cpm), - adUrl: replaceAuctionPrice(adUrl, originalCpm || cpm), - adId, - width, - height - }); + logError(`Received x-origin event request for unsupported event: '${data.event}' (adId: '${data.adId}')`); } } -function resizeRemoteCreative({ adId, adUnitCode, width, height }) { +export function resizeRemoteCreative({adId, adUnitCode, width, height}) { // resize both container div + iframe ['div', 'iframe'].forEach(elmType => { // not select element that gets removed after dfp render @@ -208,9 +162,9 @@ function resizeRemoteCreative({ adId, adUnitCode, width, height }) { function getElementIdBasedOnAdServer(adId, adUnitCode) { if (isGptPubadsDefined()) { - return getDfpElementId(adId) + return getDfpElementId(adId); } else if (isApnGetTagDefined()) { - return getAstElementId(adUnitCode) + return getAstElementId(adUnitCode); } else { return adUnitCode; } diff --git a/src/utils.js b/src/utils.js index 256dfb15174..288f0d5da5d 100644 --- a/src/utils.js +++ b/src/utils.js @@ -599,9 +599,15 @@ export function isSafariBrowser() { return /^((?!chrome|android|crios|fxios).)*safari/i.test(navigator.userAgent); } -export function replaceAuctionPrice(str, cpm) { +export function replaceMacros(str, subs) { if (!str) return; - return str.replace(/\$\{AUCTION_PRICE\}/g, cpm); + return Object.entries(subs).reduce((str, [key, val]) => { + return str.replace(new RegExp('\\$\\{' + key + '\\}', 'g'), val || ''); + }, str); +} + +export function replaceAuctionPrice(str, cpm) { + return replaceMacros(str, {AUCTION_PRICE: cpm}) } export function replaceClickThrough(str, clicktag) { diff --git a/test/spec/creative/crossDomainCreative_spec.js b/test/spec/creative/crossDomainCreative_spec.js new file mode 100644 index 00000000000..765d5e5311a --- /dev/null +++ b/test/spec/creative/crossDomainCreative_spec.js @@ -0,0 +1,182 @@ +import {renderer} from '../../../libraries/creativeRender/crossDomain.js'; +import { + AD_RENDER_FAILED, AD_RENDER_SUCCEEDED, EXCEPTION, NO_AD, + PREBID_EVENT, + PREBID_REQUEST, + PREBID_RESPONSE +} from '../../../libraries/creativeRender/constants.js'; + +describe('cross-domain creative', () => { + let win, renderAd, messages, mkIframe, listeners; + + beforeEach(() => { + messages = []; + listeners = []; + mkIframe = sinon.stub(); + win = { + document: { + body: { + appendChild: sinon.stub(), + }, + createElement: sinon.stub().callsFake(tagname => { + switch (tagname.toLowerCase()) { + case 'a': + return document.createElement('a') + case 'iframe': { + return mkIframe(); + } + } + }) + }, + addEventListener: sinon.stub().callsFake((_, listener) => listeners.push(listener)), + parent: { + postMessage: sinon.stub().callsFake((payload, targetOrigin, transfer) => { + messages.push({payload: JSON.parse(payload), targetOrigin, transfer}); + }) + } + }; + renderAd = renderer(win); + }) + + it('derives postMessage target origin from pubUrl ', () => { + renderAd({pubUrl: 'https://domain.com:123/path'}); + expect(messages[0].targetOrigin).to.eql('https://domain.com:123') + }); + + it('generates request message with adId and clickUrl', () => { + renderAd({adId: '123', clickUrl: 'https://click-url.com'}); + expect(messages[0].payload).to.eql({ + message: PREBID_REQUEST, + adId: '123', + options: { + clickUrl: 'https://click-url.com' + } + }) + }) + + Object.entries({ + 'MessageChannel': (msg) => messages[0].transfer[0].postMessage(msg), + 'message listener': (msg) => listeners.forEach((fn) => fn({data: msg})) + }).forEach(([t, transport]) => { + describe(`when using ${t}`, () => { + function reply(msg) { + transport(JSON.stringify(msg)) + }; + + it('ignores messages that are not a prebid response message', () => { + renderAd({adId: '123'}); + reply({adId: '123', ad: 'markup'}); + sinon.assert.notCalled(mkIframe); + }) + + describe('signals AD_RENDER_FAILED', () => { + it('on exception', (done) => { + mkIframe.callsFake(() => { throw new Error('error message') }); + renderAd({adId: '123'}); + reply({message: PREBID_RESPONSE, adId: '123', ad: 'markup'}); + setTimeout(() => { + expect(messages[1].payload).to.eql({ + message: PREBID_EVENT, + adId: '123', + event: AD_RENDER_FAILED, + info: { + reason: EXCEPTION, + message: 'error message' + } + }) + done(); + }, 100) + }); + + it('on missing ad', (done) => { + renderAd({adId: '123'}); + reply({message: PREBID_RESPONSE, adId: '123'}); + setTimeout(() => { + sinon.assert.match(messages[1].payload, { + message: PREBID_EVENT, + adId: '123', + event: AD_RENDER_FAILED, + info: { + reason: NO_AD, + } + }) + done(); + }, 100) + }) + }); + + describe('rendering', () => { + let iframe; + + beforeEach(() => { + iframe = { + attrs: {}, + setAttribute: sinon.stub().callsFake((k, v) => iframe.attrs[k.toLowerCase()] = v), + contentDocument: { + open: sinon.stub(), + write: sinon.stub(), + close: sinon.stub(), + } + } + mkIframe.callsFake(() => iframe); + }); + + it('renders adUrl as iframe src', (done) => { + renderAd({adId: '123'}); + reply({message: PREBID_RESPONSE, adId: '123', adUrl: 'some-url'}); + setTimeout(() => { + sinon.assert.calledWith(win.document.body.appendChild, iframe); + expect(iframe.attrs.src).to.eql('some-url'); + done(); + }, 100) + }); + + it('renders ad through document.write', (done) => { + renderAd({adId: '123'}); + reply({message: PREBID_RESPONSE, adId: '123', ad: 'some-markup'}); + setTimeout(() => { + sinon.assert.calledWith(win.document.body.appendChild, iframe); + sinon.assert.called(iframe.contentDocument.open); + sinon.assert.calledWith(iframe.contentDocument.write, 'some-markup'); + sinon.assert.called(iframe.contentDocument.close); + done(); + }, 100) + }); + + Object.entries({ + adUrl: 'mock-ad-url', + ad: 'mock-ad-markup' + }).forEach(([prop, propValue]) => { + describe(`when message has ${prop}`, () => { + beforeEach((done) => { + renderAd({adId: '123'}); + reply({ + message: PREBID_RESPONSE, + adId: '123', + [prop]: propValue, + width: 100, + height: 200 + }); + setTimeout(done, 100); + }); + + it('emits AD_RENDER_SUCCEEDED', () => { + expect(messages[1].payload).to.eql({ + message: PREBID_EVENT, + adId: '123', + event: AD_RENDER_SUCCEEDED + }) + }); + + it('sets iframe height / width to ad height / width', () => { + sinon.assert.match(iframe.attrs, { + width: 100, + height: 200 + }) + }) + }) + }) + }); + }); + }); +}); diff --git a/test/spec/unit/pbjs_api_spec.js b/test/spec/unit/pbjs_api_spec.js index 664f7ebb58f..00eedb9a2b5 100644 --- a/test/spec/unit/pbjs_api_spec.js +++ b/test/spec/unit/pbjs_api_spec.js @@ -14,7 +14,7 @@ import { config as configObj } from 'src/config.js'; import * as ajaxLib from 'src/ajax.js'; import * as auctionModule from 'src/auction.js'; import { registerBidder } from 'src/adapters/bidderFactory.js'; -import { _sendAdToCreative } from 'src/secureCreatives.js'; +import {resizeRemoteCreative} from 'src/secureCreatives.js'; import {find} from 'src/polyfill.js'; import * as pbjsModule from 'src/prebid.js'; import {hook} from '../../../src/hook.js'; @@ -1104,7 +1104,7 @@ describe('Unit: Prebid Module', function () { adUnitCode: config.adUnitCodes[0], }; - _sendAdToCreative(mockAdObject, sinon.stub()); + resizeRemoteCreative(mockAdObject); expect(slots[0].spyGetSlotElementId.called).to.equal(false); expect(slots[1].spyGetSlotElementId.called).to.equal(true); @@ -1233,7 +1233,8 @@ describe('Unit: Prebid Module', function () { } }, getElementsByTagName: sinon.stub(), - querySelector: sinon.stub() + querySelector: sinon.stub(), + createElement: sinon.stub(), }; elStub = { @@ -1264,7 +1265,7 @@ describe('Unit: Prebid Module', function () { it('should require doc and id params', function () { $$PREBID_GLOBAL$$.renderAd(); - var error = 'Error trying to write ad Id :undefined to the page. Missing adId'; + var error = 'Error rendering ad (id: undefined): missing adId'; assert.ok(spyLogError.calledWith(error), 'expected param error was logged'); }); @@ -1289,14 +1290,13 @@ describe('Unit: Prebid Module', function () { adUrl: 'http://server.example.com/ad/ad.js' }); $$PREBID_GLOBAL$$.renderAd(doc, bidId); - assert.ok(elStub.insertBefore.called, 'url was written to iframe in doc'); + sinon.assert.calledWith(doc.createElement, 'iframe'); }); it('should log an error when no ad or url', function () { pushBidResponseToAuction({}); $$PREBID_GLOBAL$$.renderAd(doc, bidId); - var error = 'Error trying to write ad. No ad for bid response id: ' + bidId; - assert.ok(spyLogError.calledWith(error), 'expected error was logged'); + sinon.assert.called(spyLogError); }); it('should log an error when not in an iFrame', function () { @@ -1305,7 +1305,7 @@ describe('Unit: Prebid Module', function () { }); inIframe = false; $$PREBID_GLOBAL$$.renderAd(document, bidId); - const error = 'Error trying to write ad. Ad render call ad id ' + bidId + ' was prevented from writing to the main document.'; + const error = `Error rendering ad (id: ${bidId}): renderAd was prevented from writing to the main document.`; assert.ok(spyLogError.calledWith(error), 'expected error was logged'); }); @@ -1326,14 +1326,14 @@ describe('Unit: Prebid Module', function () { doc.write = sinon.stub().throws(error); $$PREBID_GLOBAL$$.renderAd(doc, bidId); - var errorMessage = 'Error trying to write ad Id :' + bidId + ' to the page:' + error.message; + var errorMessage = `Error rendering ad (id: ${bidId}): doc write error` assert.ok(spyLogError.calledWith(errorMessage), 'expected error was logged'); }); it('should log an error when ad not found', function () { var fakeId = 99; $$PREBID_GLOBAL$$.renderAd(doc, fakeId); - var error = 'Error trying to write ad. Cannot find ad by given id : ' + fakeId; + var error = `Error rendering ad (id: ${fakeId}): Cannot find ad '${fakeId}'` assert.ok(spyLogError.calledWith(error), 'expected error was logged'); }); @@ -1345,14 +1345,6 @@ 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({ diff --git a/test/spec/unit/secureCreatives_spec.js b/test/spec/unit/secureCreatives_spec.js index 7d5f9af35dd..895bf03165a 100644 --- a/test/spec/unit/secureCreatives_spec.js +++ b/test/spec/unit/secureCreatives_spec.js @@ -1,6 +1,4 @@ -import { - _sendAdToCreative, getReplier, receiveMessage -} from 'src/secureCreatives.js'; +import {getReplier, 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'; @@ -8,10 +6,11 @@ import * as auctionModule from 'src/auction.js'; import * as native from 'src/native.js'; import {fireNativeTrackers, getAllAssetsMessage} from 'src/native.js'; import * as events from 'src/events.js'; -import { config as configObj } from 'src/config.js'; +import {config as configObj} from 'src/config.js'; import 'src/prebid.js'; -import { expect } from 'chai'; +import {expect} from 'chai'; +import {handleRender} from '../../../src/adRendering.js'; var CONSTANTS = require('src/constants.json'); @@ -54,37 +53,45 @@ describe('secureCreatives', () => { }); }); - describe('_sendAdToCreative', () => { - beforeEach(function () { - sinon.stub(utils, 'logError'); - sinon.stub(utils, 'logWarn'); + describe('handleRender', () => { + let bidResponse, renderFn, result; + beforeEach(() => { + result = null; + renderFn = sinon.stub().callsFake((r) => { result = r; }); + bidResponse = { + adId: 123 + } }); - afterEach(function () { - utils.logError.restore(); - utils.logWarn.restore(); + it('does not invoke renderFn, but the renderer instead, if the ad has one', () => { + const renderer = { + url: 'some-custom-renderer', + render: sinon.spy() + } + handleRender(renderFn, {bidResponse: {renderer}}); + sinon.assert.notCalled(renderFn); + sinon.assert.called(renderer.render); }); - it('should macro replace ${AUCTION_PRICE} with the winning bid for ad and adUrl', () => { - const oldVal = window.googletag; - const oldapntag = window.apntag; - window.apntag = null - window.googletag = null; - const mockAdObject = { - adId: 'someAdId', - ad: '', - adUrl: 'http://creative.prebid.org/${AUCTION_PRICE}', - width: 300, - height: 250, - renderer: null, - cpm: '1.00', - adUnitCode: 'some_dom_id' - }; - const reply = sinon.spy(); - _sendAdToCreative(mockAdObject, reply); - expect(reply.args[0][0].ad).to.equal(''); - expect(reply.args[0][0].adUrl).to.equal('http://creative.prebid.org/1.00'); - window.googletag = oldVal; - window.apntag = oldapntag; + + ['ad', 'adUrl'].forEach((prop) => { + describe(`on ${prop}`, () => { + it('replaces AUCTION_PRICE macro', () => { + bidResponse[prop] = 'pre${AUCTION_PRICE}post'; + bidResponse.cpm = 123; + handleRender(renderFn, {adId: 123, bidResponse}); + expect(result[prop]).to.eql('pre123post'); + }); + it('replaces CLICKTHROUGH macro', () => { + bidResponse[prop] = 'pre${CLICKTHROUGH}post'; + handleRender(renderFn, {adId: 123, bidResponse, options: {clickUrl: 'clk'}}); + expect(result[prop]).to.eql('preclkpost'); + }); + it('defaults CLICKTHROUGH to empty string', () => { + bidResponse[prop] = 'pre${CLICKTHROUGH}post'; + handleRender(renderFn, {adId: 123, bidResponse}); + expect(result[prop]).to.eql('prepost'); + }); + }); }); }); diff --git a/webpack.creative.js b/webpack.creative.js new file mode 100644 index 00000000000..7279455e155 --- /dev/null +++ b/webpack.creative.js @@ -0,0 +1,19 @@ +const path = require('path'); + +module.exports = { + mode: 'production', + resolve: { + modules: [ + path.resolve('.'), + 'node_modules' + ], + }, + entry: { + 'creative': { + import: './libraries/creativeRender/crossDomain.js', + }, + }, + output: { + path: path.resolve('./build/creative'), + }, +} From c9d34ed3774eca35bee9fe79fa2e037bf68c95d1 Mon Sep 17 00:00:00 2001 From: iprom-adserver <79305981+iprom-adserver@users.noreply.github.com> Date: Tue, 21 Nov 2023 14:54:28 +0100 Subject: [PATCH 007/246] feat: Add gvlid to iprom bid adapter (#10750) Co-authored-by: Gasper --- modules/ipromBidAdapter.js | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/modules/ipromBidAdapter.js b/modules/ipromBidAdapter.js index eaf20ad3ad3..1188af471a7 100644 --- a/modules/ipromBidAdapter.js +++ b/modules/ipromBidAdapter.js @@ -3,13 +3,15 @@ import { registerBidder } from '../src/adapters/bidderFactory.js'; const BIDDER_CODE = 'iprom'; const ENDPOINT_URL = 'https://core.iprom.net/programmatic'; -const VERSION = 'v1.0.2'; +const VERSION = 'v1.0.3'; const DEFAULT_CURRENCY = 'EUR'; const DEFAULT_NETREVENUE = true; const DEFAULT_TTL = 360; +const IAB_GVL_ID = 811; export const spec = { code: BIDDER_CODE, + gvlid: IAB_GVL_ID, isBidRequestValid: function ({ bidder, params = {} } = {}) { // id parameter checks if (!params.id) { From c8d83f06bef616fd8456b0402a7ff289c209382e Mon Sep 17 00:00:00 2001 From: Saar Amrani Date: Wed, 22 Nov 2023 10:02:26 +0200 Subject: [PATCH 008/246] ShinezRTB Bid Adapter : initial release (#10405) --- modules/shinezRtbBidAdapter.js | 336 +++++++++ modules/shinezRtbBidAdapter.md | 35 + test/spec/modules/shinezRtbBidAdapter_spec.js | 639 ++++++++++++++++++ 3 files changed, 1010 insertions(+) create mode 100644 modules/shinezRtbBidAdapter.js create mode 100644 modules/shinezRtbBidAdapter.md create mode 100644 test/spec/modules/shinezRtbBidAdapter_spec.js diff --git a/modules/shinezRtbBidAdapter.js b/modules/shinezRtbBidAdapter.js new file mode 100644 index 00000000000..d1d9f36a569 --- /dev/null +++ b/modules/shinezRtbBidAdapter.js @@ -0,0 +1,336 @@ +import {_each, deepAccess, parseSizesInput, parseUrl, uniques, isFn} 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 {config} from '../src/config.js'; + +const DEFAULT_SUB_DOMAIN = 'exchange'; +const BIDDER_CODE = 'shinezRtb'; +const BIDDER_VERSION = '1.0.0'; +const CURRENCY = 'USD'; +const TTL_SECONDS = 60 * 5; +const UNIQUE_DEAL_ID_EXPIRY = 1000 * 60 * 15; +const storage = getStorageManager({bidderCode: BIDDER_CODE}); + +function getTopWindowQueryParams() { + try { + const parsedUrl = parseUrl(window.top.document.URL, {decodeSearchAsString: true}); + return parsedUrl.search; + } catch (e) { + return ''; + } +} + +export function createDomain(subDomain = DEFAULT_SUB_DOMAIN) { + return `https://${subDomain}.sweetgum.io`; +} + +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, bidderTimeout) { + const { + params, + bidId, + userId, + adUnitCode, + schain, + mediaTypes, + ortb2Imp, + bidderRequestId, + bidRequestsCount, + bidderRequestsCount, + bidderWinsCount + } = bid; + let {bidFloor, ext} = params; + const hashUrl = hashCode(topWindowUrl); + const uniqueDealId = getUniqueDealId(hashUrl); + const cId = extractCID(params); + const pId = extractPID(params); + const subDomain = extractSubDomain(params); + + const gpid = deepAccess(bid, 'ortb2Imp.ext.gpid', deepAccess(bid, 'ortb2Imp.ext.data.pbadslot', '')); + + if (isFn(bid.getFloor)) { + const floorInfo = bid.getFloor({ + currency: 'USD', + mediaType: '*', + size: '*' + }); + + if (floorInfo.currency === 'USD') { + bidFloor = floorInfo.floor; + } + } + + let data = { + url: encodeURIComponent(topWindowUrl), + uqs: getTopWindowQueryParams(), + cb: Date.now(), + bidFloor: bidFloor, + bidId: bidId, + referrer: bidderRequest.refererInfo.ref, + adUnitCode: adUnitCode, + publisherId: pId, + sizes: sizes, + uniqueDealId: uniqueDealId, + bidderVersion: BIDDER_VERSION, + prebidVersion: '$prebid.version$', + res: `${screen.width}x${screen.height}`, + schain: schain, + mediaTypes: mediaTypes, + gpid: gpid, + transactionId: ortb2Imp?.ext?.tid, + bidderRequestId: bidderRequestId, + bidRequestsCount: bidRequestsCount, + bidderRequestsCount: bidderRequestsCount, + bidderWinsCount: bidderWinsCount, + bidderTimeout: bidderTimeout + }; + + appendUserIdsToRequestPayload(data, userId); + + const sua = deepAccess(bidderRequest, 'ortb2.device.sua'); + + if (sua) { + data.sua = sua; + } + + 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; + } + + if (bidderRequest.gppConsent) { + data.gppString = bidderRequest.gppConsent.gppString; + data.gppSid = bidderRequest.gppConsent.applicableSections; + } else if (bidderRequest.ortb2?.regs?.gpp) { + data.gppString = bidderRequest.ortb2.regs.gpp; + data.gppSid = bidderRequest.ortb2.regs.gpp_sid; + } + + const dto = { + method: 'POST', + url: `${createDomain(subDomain)}/prebid/multi/${cId}`, + data: data + }; + + _each(ext, (value, key) => { + dto.data['ext.' + key] = value; + }); + + return dto; +} + +function appendUserIdsToRequestPayload(payloadRef, userIds) { + let key; + _each(userIds, (userId, idSystemProviderName) => { + key = `uid.${idSystemProviderName}`; + + switch (idSystemProviderName) { + case 'digitrustid': + payloadRef[key] = 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.page || bidderRequest.refererInfo.topmostLocation; + const bidderTimeout = config.getConfig('bidderTimeout'); + const requests = []; + validBidRequests.forEach(validBidRequest => { + const sizes = parseSizesInput(validBidRequest.sizes); + const request = buildRequest(validBidRequest, topWindowUrl, sizes, bidderRequest, bidderTimeout); + 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, + metaData, + advertiserDomains, + mediaType = BANNER + } = result; + if (!ad || !price) { + return; + } + + const response = { + requestId: bidId, + cpm: price, + width: width, + height: height, + creativeId: creativeId, + currency: currency || CURRENCY, + netRevenue: true, + ttl: exp || TTL_SECONDS, + }; + + if (metaData) { + Object.assign(response, { + meta: metaData + }) + } else { + Object.assign(response, { + meta: { + advertiserDomains: advertiserDomains || [] + } + }) + } + + if (mediaType === BANNER) { + Object.assign(response, { + ad: ad, + }); + } else { + Object.assign(response, { + vastXml: ad, + mediaType: VIDEO + }); + } + output.push(response); + }); + return output; + } catch (e) { + return []; + } +} + +function getUserSyncs(syncOptions, responses, gdprConsent = {}, uspConsent = '') { + let syncs = []; + const {iframeEnabled, pixelEnabled} = syncOptions; + const {gdprApplies, consentString = ''} = gdprConsent; + + const cidArr = responses.filter(resp => deepAccess(resp, 'body.cid')).map(resp => resp.body.cid).filter(uniques); + const params = `?cid=${encodeURIComponent(cidArr.join(','))}&gdpr=${gdprApplies ? 1 : 0}&gdpr_consent=${encodeURIComponent(consentString || '')}&us_privacy=${encodeURIComponent(uspConsent || '')}` + if (iframeEnabled) { + syncs.push({ + type: 'iframe', + url: `https://sync.sweetgum.io/api/sync/iframe/${params}` + }); + } + if (pixelEnabled) { + syncs.push({ + type: 'image', + url: `https://sync.sweetgum.io/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 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 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, VIDEO], + isBidRequestValid, + buildRequests, + interpretResponse, + getUserSyncs +}; + +registerBidder(spec); diff --git a/modules/shinezRtbBidAdapter.md b/modules/shinezRtbBidAdapter.md new file mode 100644 index 00000000000..e9190c2a9c4 --- /dev/null +++ b/modules/shinezRtbBidAdapter.md @@ -0,0 +1,35 @@ +# Overview + +**Module Name:** Shinez RTB Bid Adapter + +**Module Type:** Bidder Adapter + +**Maintainer:** tech-team@shinez.io + +# Description + +Module that connects to Shinez RTB demand sources. + +# Test Parameters +```js +var adUnits = [ + { + code: 'test-ad', + sizes: [[300, 250]], + bids: [ + { + bidder: 'shinezRtb', + params: { + cId: '562524b21b1c1f08117fc7f9', + pId: '59ac17c192832d0011283fe3', + bidFloor: 0.0001, + ext: { + param1: 'loremipsum', + param2: 'dolorsitamet' + } + } + } + ] + } +]; +``` diff --git a/test/spec/modules/shinezRtbBidAdapter_spec.js b/test/spec/modules/shinezRtbBidAdapter_spec.js new file mode 100644 index 00000000000..3965cd69c5f --- /dev/null +++ b/test/spec/modules/shinezRtbBidAdapter_spec.js @@ -0,0 +1,639 @@ +import {expect} from 'chai'; +import { + spec as adapter, + createDomain, + hashCode, + extractPID, + extractCID, + extractSubDomain, + getStorageItem, + setStorageItem, + tryParseJSON, + getUniqueDealId, +} from 'modules/shinezRtbBidAdapter'; +import * as utils from 'src/utils.js'; +import {version} from 'package.json'; +import {useFakeTimers} from 'sinon'; +import {BANNER, VIDEO} from '../../../src/mediaTypes'; +import {config} from '../../../src/config'; +import {deepAccess} from 'src/utils.js'; + +export const TEST_ID_SYSTEMS = ['britepoolid', 'criteoId', 'id5id', 'idl_env', 'lipb', 'netId', 'parrableId', 'pubcid', 'tdid', 'pubProvidedId', 'digitrustid']; + +const SUB_DOMAIN = 'exchange'; + +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', + 'sizes': [[300, 250], [300, 600]], + 'bidderRequestId': '1fdb5ff1b6eaa7', + 'bidRequestsCount': 4, + 'bidderRequestsCount': 3, + 'bidderWinsCount': 1, + 'requestId': 'b0777d85-d061-450e-9bc7-260dd54bbb7a', + 'schain': 'a0819c69-005b-41ed-af06-1be1e0aefefc', + 'mediaTypes': [BANNER], + 'ortb2Imp': { + 'ext': { + 'gpid': '0123456789', + 'tid': '56e184c6-bde9-497b-b9b9-cf47a61381ee' + } + } +}; + +const VIDEO_BID = { + 'bidId': '2d52001cabd527', + 'adUnitCode': '63550ad1ff6642d368cba59dh5884270560', + 'bidderRequestId': '12a8ae9ada9c13', + 'bidRequestsCount': 4, + 'bidderRequestsCount': 3, + 'bidderWinsCount': 1, + 'schain': 'a0819c69-005b-41ed-af06-1be1e0aefefc', + 'params': { + 'subDomain': SUB_DOMAIN, + 'cId': '635509f7ff6642d368cb9837', + 'pId': '59ac17c192832d0011283fe3', + 'bidFloor': 0.1 + }, + 'sizes': [[545, 307]], + 'mediaTypes': { + 'video': { + 'playerSize': [[545, 307]], + 'context': 'instream', + 'mimes': [ + 'video/mp4', + 'application/javascript' + ], + 'protocols': [2, 3, 5, 6], + 'maxduration': 60, + 'minduration': 0, + 'startdelay': 0, + 'linearity': 1, + 'api': [2], + 'placement': 1 + } + }, + 'ortb2Imp': { + 'ext': { + 'gpid': '0123456789', + 'tid': '56e184c6-bde9-497b-b9b9-cf47a61381ee' + } + } +} + +const BIDDER_REQUEST = { + 'gdprConsent': { + 'consentString': 'consent_string', + 'gdprApplies': true + }, + 'gppConsent': { + 'gppString': 'gpp_string', + 'applicableSections': [7] + }, + 'uspConsent': 'consent_string', + 'refererInfo': { + 'page': 'https://www.greatsite.com', + 'ref': 'https://www.somereferrer.com' + }, + 'ortb2': { + 'regs': { + 'gpp': 'gpp_string', + 'gpp_sid': [7] + }, + 'device': { + 'sua': { + 'source': 2, + 'platform': { + 'brand': 'Android', + 'version': ['8', '0', '0'] + }, + 'browsers': [ + {'brand': 'Not_A Brand', 'version': ['99', '0', '0', '0']}, + {'brand': 'Google Chrome', 'version': ['109', '0', '5414', '119']}, + {'brand': 'Chromium', 'version': ['109', '0', '5414', '119']} + ], + 'mobile': 1, + 'model': 'SM-G955U', + 'bitness': '64', + 'architecture': '' + } + } + } +}; + +const SERVER_RESPONSE = { + body: { + cid: 'testcid123', + 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 VIDEO_SERVER_RESPONSE = { + body: { + 'cid': '635509f7ff6642d368cb9837', + 'results': [{ + 'ad': '', + 'advertiserDomains': ['sweetgum.io'], + 'exp': 60, + 'width': 545, + 'height': 307, + 'mediaType': 'video', + 'creativeId': '12610997325162499419', + 'price': 2, + 'cookies': [] + }] + } +}; + +const REQUEST = { + data: { + width: 300, + height: 250, + bidId: '2d52001cabd527' + } +}; + +function getTopWindowQueryParams() { + try { + const parsedUrl = utils.parseUrl(window.top.document.URL, {decodeSearchAsString: true}); + return parsedUrl.search; + } catch (e) { + return ''; + } +} + +describe('ShinezRtbBidAdapter', 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'); + }); + + it('exists and contains media types', function () { + expect(adapter.supportedMediaTypes).to.exist.and.to.be.an('array').with.length(2); + expect(adapter.supportedMediaTypes).to.contain.members([BANNER, VIDEO]); + }); + }); + + describe('validate bid requests', function () { + it('should require cId', function () { + const isValid = adapter.isBidRequestValid({ + params: { + pId: 'pid' + } + }); + expect(isValid).to.be.false; + }); + + it('should require pId', function () { + const isValid = adapter.isBidRequestValid({ + params: { + cId: 'cid' + } + }); + expect(isValid).to.be.false; + }); + + it('should validate correctly', function () { + const isValid = adapter.isBidRequestValid({ + params: { + cId: 'cid', + pId: 'pid' + } + }); + expect(isValid).to.be.true; + }); + }); + + describe('build requests', function () { + let sandbox; + before(function () { + $$PREBID_GLOBAL$$.bidderSettings = { + shinezRtb: { + storageAllowed: true + } + }; + sandbox = sinon.sandbox.create(); + sandbox.stub(Date, 'now').returns(1000); + }); + + it('should build video request', function () { + const hashUrl = hashCode(BIDDER_REQUEST.refererInfo.page); + config.setConfig({ + bidderTimeout: 3000, + enableTIDs: true + }); + const requests = adapter.buildRequests([VIDEO_BID], BIDDER_REQUEST); + expect(requests).to.have.length(1); + expect(requests[0]).to.deep.equal({ + method: 'POST', + url: `${createDomain(SUB_DOMAIN)}/prebid/multi/635509f7ff6642d368cb9837`, + data: { + adUnitCode: '63550ad1ff6642d368cba59dh5884270560', + bidFloor: 0.1, + bidId: '2d52001cabd527', + bidderVersion: adapter.version, + bidderRequestId: '12a8ae9ada9c13', + cb: 1000, + gdpr: 1, + gdprConsent: 'consent_string', + usPrivacy: 'consent_string', + gppString: 'gpp_string', + gppSid: [7], + transactionId: '56e184c6-bde9-497b-b9b9-cf47a61381ee', + prebidVersion: version, + bidRequestsCount: 4, + bidderRequestsCount: 3, + bidderWinsCount: 1, + bidderTimeout: 3000, + publisherId: '59ac17c192832d0011283fe3', + url: 'https%3A%2F%2Fwww.greatsite.com', + referrer: 'https://www.somereferrer.com', + res: `${window.top.screen.width}x${window.top.screen.height}`, + schain: VIDEO_BID.schain, + sizes: ['545x307'], + sua: { + 'source': 2, + 'platform': { + 'brand': 'Android', + 'version': ['8', '0', '0'] + }, + 'browsers': [ + {'brand': 'Not_A Brand', 'version': ['99', '0', '0', '0']}, + {'brand': 'Google Chrome', 'version': ['109', '0', '5414', '119']}, + {'brand': 'Chromium', 'version': ['109', '0', '5414', '119']} + ], + 'mobile': 1, + 'model': 'SM-G955U', + 'bitness': '64', + 'architecture': '' + }, + uniqueDealId: `${hashUrl}_${Date.now().toString()}`, + uqs: getTopWindowQueryParams(), + mediaTypes: { + video: { + api: [2], + context: 'instream', + linearity: 1, + maxduration: 60, + mimes: [ + 'video/mp4', + 'application/javascript' + ], + minduration: 0, + placement: 1, + playerSize: [[545, 307]], + protocols: [2, 3, 5, 6], + startdelay: 0 + } + }, + gpid: '0123456789' + } + }); + }); + + it('should build banner request for each size', function () { + const hashUrl = hashCode(BIDDER_REQUEST.refererInfo.page); + config.setConfig({ + bidderTimeout: 3000, + enableTIDs: true + }); + 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, + gppString: 'gpp_string', + gppSid: [7], + usPrivacy: 'consent_string', + bidRequestsCount: 4, + bidderRequestsCount: 3, + bidderWinsCount: 1, + bidderTimeout: 3000, + bidderRequestId: '1fdb5ff1b6eaa7', + transactionId: '56e184c6-bde9-497b-b9b9-cf47a61381ee', + sizes: ['300x250', '300x600'], + sua: { + 'source': 2, + 'platform': { + 'brand': 'Android', + 'version': ['8', '0', '0'] + }, + 'browsers': [ + {'brand': 'Not_A Brand', 'version': ['99', '0', '0', '0']}, + {'brand': 'Google Chrome', 'version': ['109', '0', '5414', '119']}, + {'brand': 'Chromium', 'version': ['109', '0', '5414', '119']} + ], + 'mobile': 1, + 'model': 'SM-G955U', + 'bitness': '64', + 'architecture': '' + }, + url: 'https%3A%2F%2Fwww.greatsite.com', + referrer: 'https://www.somereferrer.com', + cb: 1000, + bidFloor: 0.1, + bidId: '2d52001cabd527', + adUnitCode: 'div-gpt-ad-12345-0', + publisherId: '59ac17c192832d0011283fe3', + uniqueDealId: `${hashUrl}_${Date.now().toString()}`, + bidderVersion: adapter.version, + prebidVersion: version, + schain: BID.schain, + res: `${window.top.screen.width}x${window.top.screen.height}`, + mediaTypes: [BANNER], + gpid: '0123456789', + uqs: getTopWindowQueryParams(), + 'ext.param1': 'loremipsum', + 'ext.param2': 'dolorsitamet', + } + }); + }); + + after(function () { + $$PREBID_GLOBAL$$.bidderSettings = {}; + sandbox.restore(); + }); + }); + describe('getUserSyncs', function () { + it('should have valid user sync with iframeEnabled', function () { + const result = adapter.getUserSyncs({iframeEnabled: true}, [SERVER_RESPONSE]); + + expect(result).to.deep.equal([{ + type: 'iframe', + url: 'https://sync.sweetgum.io/api/sync/iframe/?cid=testcid123&gdpr=0&gdpr_consent=&us_privacy=' + }]); + }); + + it('should have valid user sync with cid on response', function () { + const result = adapter.getUserSyncs({iframeEnabled: true}, [SERVER_RESPONSE]); + expect(result).to.deep.equal([{ + type: 'iframe', + url: 'https://sync.sweetgum.io/api/sync/iframe/?cid=testcid123&gdpr=0&gdpr_consent=&us_privacy=' + }]); + }); + + it('should have valid user sync with pixelEnabled', function () { + const result = adapter.getUserSyncs({pixelEnabled: true}, [SERVER_RESPONSE]); + + expect(result).to.deep.equal([{ + 'url': 'https://sync.sweetgum.io/api/sync/image/?cid=testcid123&gdpr=0&gdpr_consent=&us_privacy=', + 'type': 'image' + }]); + }) + }); + + describe('interpret response', function () { + it('should return empty array when there is no response', function () { + const responses = adapter.interpretResponse(null); + expect(responses).to.be.empty; + }); + + it('should return empty array when there is no ad', function () { + const responses = adapter.interpretResponse({price: 1, ad: ''}); + expect(responses).to.be.empty; + }); + + it('should return empty array when there is no price', function () { + const responses = adapter.interpretResponse({price: null, ad: 'great ad'}); + expect(responses).to.be.empty; + }); + + it('should return an array of interpreted banner 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 get meta from response metaData', function () { + const serverResponse = utils.deepClone(SERVER_RESPONSE); + serverResponse.body.results[0].metaData = { + advertiserDomains: ['sweetgum.io'], + agencyName: 'Agency Name', + }; + const responses = adapter.interpretResponse(serverResponse, REQUEST); + expect(responses[0].meta).to.deep.equal({ + advertiserDomains: ['sweetgum.io'], + agencyName: 'Agency Name' + }); + }); + + it('should return an array of interpreted video responses', function () { + const responses = adapter.interpretResponse(VIDEO_SERVER_RESPONSE, REQUEST); + expect(responses).to.have.length(1); + expect(responses[0]).to.deep.equal({ + requestId: '2d52001cabd527', + cpm: 2, + width: 545, + height: 307, + mediaType: 'video', + creativeId: '12610997325162499419', + currency: 'USD', + netRevenue: true, + ttl: 60, + vastXml: '', + meta: { + advertiserDomains: ['sweetgum.io'] + } + }); + }); + + 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 () { + TEST_ID_SYSTEMS.forEach((idSystemProvider) => { + const id = Date.now().toString(); + const bid = utils.deepClone(BID); + + const userId = (function () { + switch (idSystemProvider) { + case 'digitrustid': + return {data: {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('unique deal id', function () { + before(function () { + $$PREBID_GLOBAL$$.bidderSettings = { + shinezRtb: { + storageAllowed: true + } + }; + }); + after(function () { + $$PREBID_GLOBAL$$.bidderSettings = {}; + }); + const key = 'myKey'; + let uniqueDealId; + beforeEach(() => { + uniqueDealId = getUniqueDealId(key, 0); + }) + + it('should get current unique deal id', function (done) { + // waiting some time so `now` will become past + setTimeout(() => { + const current = getUniqueDealId(key); + expect(current).to.be.equal(uniqueDealId); + done(); + }, 200); + }); + + it('should get new unique deal id on expiration', function (done) { + setTimeout(() => { + const current = getUniqueDealId(key, 100); + expect(current).to.not.be.equal(uniqueDealId); + done(); + }, 200) + }); + }); + + describe('storage utils', function () { + before(function () { + $$PREBID_GLOBAL$$.bidderSettings = { + shinezRtb: { + storageAllowed: true + } + }; + }); + after(function () { + $$PREBID_GLOBAL$$.bidderSettings = {}; + }); + it('should get value from storage with create param', function () { + const now = Date.now(); + const clock = useFakeTimers({ + shouldAdvanceTime: true, + now + }); + setStorageItem('myKey', 2020); + const {value, created} = getStorageItem('myKey'); + expect(created).to.be.equal(now); + expect(value).to.be.equal(2020); + expect(typeof value).to.be.equal('number'); + expect(typeof created).to.be.equal('number'); + clock.restore(); + }); + + it('should get external stored value', function () { + const value = 'superman' + window.localStorage.setItem('myExternalKey', value); + const item = getStorageItem('myExternalKey'); + expect(item).to.be.equal(value); + }); + + it('should parse JSON value', function () { + const data = JSON.stringify({event: 'send'}); + const {event} = tryParseJSON(data); + expect(event).to.be.equal('send'); + }); + + it('should get original value on parse fail', function () { + const value = 21; + const parsed = tryParseJSON(value); + expect(typeof parsed).to.be.equal('number'); + expect(parsed).to.be.equal(value); + }); + }); +}); From 59532d6c2c684ee2ad89fca440d06455e560f24d Mon Sep 17 00:00:00 2001 From: "Prebid.js automated release" Date: Sun, 26 Nov 2023 00:09:26 +0000 Subject: [PATCH 009/246] Prebid 8.25.0 release --- package-lock.json | 2 +- package.json | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/package-lock.json b/package-lock.json index 4650db8f2ab..981f34675f8 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,6 +1,6 @@ { "name": "prebid.js", - "version": "8.25.0-pre", + "version": "8.25.0", "lockfileVersion": 2, "requires": true, "packages": { diff --git a/package.json b/package.json index 4ea9131920d..7236673d372 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "prebid.js", - "version": "8.25.0-pre", + "version": "8.25.0", "description": "Header Bidding Management Library", "main": "src/prebid.js", "scripts": { From aa73d61dd9ce48d29a2e1607e2112d1887f6e870 Mon Sep 17 00:00:00 2001 From: "Prebid.js automated release" Date: Sun, 26 Nov 2023 00:09:26 +0000 Subject: [PATCH 010/246] Increment version to 8.26.0-pre --- package-lock.json | 2 +- package.json | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/package-lock.json b/package-lock.json index 981f34675f8..2563c9435f7 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,6 +1,6 @@ { "name": "prebid.js", - "version": "8.25.0", + "version": "8.26.0-pre", "lockfileVersion": 2, "requires": true, "packages": { diff --git a/package.json b/package.json index 7236673d372..8aef0e70e8c 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "prebid.js", - "version": "8.25.0", + "version": "8.26.0-pre", "description": "Header Bidding Management Library", "main": "src/prebid.js", "scripts": { From 1205d53ee866dc669666228923a2097fd08c8e05 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?L=C3=A9onard=20Labat?= Date: Sun, 26 Nov 2023 17:53:23 +0100 Subject: [PATCH 011/246] Criteo bid adapter: Bump fastbid version (#10696) --- modules/criteoBidAdapter.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/criteoBidAdapter.js b/modules/criteoBidAdapter.js index 7c9411ccc2d..45769edaf61 100644 --- a/modules/criteoBidAdapter.js +++ b/modules/criteoBidAdapter.js @@ -31,7 +31,7 @@ const LOG_PREFIX = 'Criteo: '; 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 = 139; +export const FAST_BID_VERSION_CURRENT = 144; 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'; From 656680b471685d0f777c726a0c52cc47075e794d Mon Sep 17 00:00:00 2001 From: mamatic <52153441+mamatic@users.noreply.github.com> Date: Mon, 27 Nov 2023 15:00:46 +0100 Subject: [PATCH 012/246] identityLinkIdSystem: include gpp in 3p envelope retrieval (#10745) * identityLinkIdSystem - liveramp - include gpp in 3p envelope retrieval * identityLinkIdSystem - liveramp - adjust test description --- modules/identityLinkIdSystem.js | 9 +++- .../spec/modules/identityLinkIdSystem_spec.js | 44 +++++++++++++++++++ 2 files changed, 51 insertions(+), 2 deletions(-) diff --git a/modules/identityLinkIdSystem.js b/modules/identityLinkIdSystem.js index ba794df1a9c..a57f8d22753 100644 --- a/modules/identityLinkIdSystem.js +++ b/modules/identityLinkIdSystem.js @@ -10,6 +10,7 @@ import { ajax } from '../src/ajax.js'; import { submodule } from '../src/hook.js'; import {getStorageManager} from '../src/storageManager.js'; import {MODULE_TYPE_UID} from '../src/activities/modules.js'; +import { gppDataHandler } from '../src/adapterManager.js'; const MODULE_NAME = 'identityLink'; @@ -59,12 +60,16 @@ export const identityLinkSubmodule = { utils.logInfo('identityLink: 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 : ''}`; + const gppData = gppDataHandler.getConsentData(); + const gppString = gppData && gppData.gppString ? gppData.gppString : false; + const gppSectionId = gppData && gppData.gppString && gppData.applicableSections.length > 0 && gppData.applicableSections[0] !== -1 ? gppData.applicableSections[0] : false; + const hasGpp = gppString && gppSectionId; + const url = `https://api.rlcdn.com/api/identity/envelope?pid=${configParams.pid}${hasGdpr ? (tcfPolicyV2 ? '&ct=4&cv=' : '&ct=1&cv=') + gdprConsentString : ''}${hasGpp ? '&gpp=' + gppString + '&gpp_sid=' + gppSectionId : ''}`; let resp; 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) { + if (window.ats && window.ats.retrieveEnvelope) { utils.logInfo('identityLink: ATS exists!'); window.ats.retrieveEnvelope(function (envelope) { if (envelope) { diff --git a/test/spec/modules/identityLinkIdSystem_spec.js b/test/spec/modules/identityLinkIdSystem_spec.js index a273f26b28b..f1c2773de07 100644 --- a/test/spec/modules/identityLinkIdSystem_spec.js +++ b/test/spec/modules/identityLinkIdSystem_spec.js @@ -3,6 +3,7 @@ import * as utils from 'src/utils.js'; import {server} from 'test/mocks/xhr.js'; import {getCoreStorageManager} from '../../../src/storageManager.js'; import {stub} from 'sinon'; +import { gppDataHandler } from '../../../src/adapterManager.js'; const storage = getCoreStorageManager(); @@ -20,6 +21,7 @@ function setTestEnvelopeCookie () { describe('IdentityLinkId tests', function () { let logErrorStub; + let gppConsentDataStub; beforeEach(function () { defaultConfigParams = { params: {pid: pid} }; @@ -112,6 +114,48 @@ describe('IdentityLinkId tests', function () { expect(callBackSpy.calledOnce).to.be.true; }); + it('should call the LiveRamp envelope endpoint with GPP consent string', function() { + gppConsentDataStub = sinon.stub(gppDataHandler, 'getConsentData'); + gppConsentDataStub.returns({ + ready: true, + gppString: 'DBABLA~BVVqAAAACqA.QA', + applicableSections: [7] + }); + 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&gpp=DBABLA~BVVqAAAACqA.QA&gpp_sid=7'); + request.respond( + 200, + responseHeader, + JSON.stringify({}) + ); + expect(callBackSpy.calledOnce).to.be.true; + gppConsentDataStub.restore(); + }); + + it('should call the LiveRamp envelope endpoint without GPP consent string if consent string is not provided', function () { + gppConsentDataStub = sinon.stub(gppDataHandler, 'getConsentData'); + gppConsentDataStub.returns({ + ready: true, + gppString: '', + applicableSections: [7] + }); + 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; + gppConsentDataStub.restore(); + }); + it('should not throw Uncaught TypeError when envelope endpoint returns empty response', function () { let callBackSpy = sinon.spy(); let submoduleCallback = identityLinkSubmodule.getId(defaultConfigParams).callback; From b1e601aa4e29553871b372d0aeee7086b613c008 Mon Sep 17 00:00:00 2001 From: barRubi <151519394+barRubi@users.noreply.github.com> Date: Mon, 27 Nov 2023 17:13:49 +0200 Subject: [PATCH 013/246] Taboola Bid Adapter: cast tmax type (#10766) * tmax * tmax * tmax * tmax --- modules/taboolaBidAdapter.js | 2 +- test/spec/modules/taboolaBidAdapter_spec.js | 20 ++++++++++++++++++++ 2 files changed, 21 insertions(+), 1 deletion(-) diff --git a/modules/taboolaBidAdapter.js b/modules/taboolaBidAdapter.js index 86766200aaa..0d56d6daec7 100644 --- a/modules/taboolaBidAdapter.js +++ b/modules/taboolaBidAdapter.js @@ -125,7 +125,7 @@ export const spec = { site, device, source: {fd: 1}, - tmax: bidderRequest.timeout, + tmax: (bidderRequest.timeout == undefined) ? undefined : parseInt(bidderRequest.timeout), bcat: ortb2.bcat || bidRequest.params.bcat || [], badv: ortb2.badv || bidRequest.params.badv || [], wlang: ortb2.wlang || bidRequest.params.wlang || [], diff --git a/test/spec/modules/taboolaBidAdapter_spec.js b/test/spec/modules/taboolaBidAdapter_spec.js index 16bbb525ee7..1b6104599d7 100644 --- a/test/spec/modules/taboolaBidAdapter_spec.js +++ b/test/spec/modules/taboolaBidAdapter_spec.js @@ -275,6 +275,26 @@ describe('Taboola Adapter', function () { expect(resData.tmax).to.equal(500); }); + it('should pass bidder tmax as int', function () { + const bidderRequest = { + ...commonBidderRequest, + timeout: '500' + } + const res = spec.buildRequests([defaultBidRequest], bidderRequest); + const resData = JSON.parse(res.data); + expect(resData.tmax).to.equal(500); + }); + + it('should pass bidder timeout as null', function () { + const bidderRequest = { + ...commonBidderRequest, + timeout: null + } + const res = spec.buildRequests([defaultBidRequest], bidderRequest); + const resData = JSON.parse(res.data); + expect(resData.tmax).to.equal(undefined); + }); + describe('first party data', function () { it('should parse first party data', function () { const bidderRequest = { From f215abd72ad5820f16011631a0e103f05ac2c9d1 Mon Sep 17 00:00:00 2001 From: LyricWulf Date: Mon, 27 Nov 2023 07:59:42 -0800 Subject: [PATCH 014/246] adxcg Bid Adapter : fix convertTypes util import (#10763) * adxcgBidAdapter: fix convertTypes util import * adxcgBidAdapter: remove previous import --- modules/adxcgBidAdapter.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/adxcgBidAdapter.js b/modules/adxcgBidAdapter.js index ab0ae1cbd48..dda88575ff5 100644 --- a/modules/adxcgBidAdapter.js +++ b/modules/adxcgBidAdapter.js @@ -1,9 +1,9 @@ // jshint esversion: 6, es3: false, node: true import { ortbConverter } from '../libraries/ortbConverter/converter.js'; +import { convertTypes } from '../libraries/transformParamsUtils/convertTypes.js'; import { registerBidder } from '../src/adapters/bidderFactory.js'; import { BANNER, NATIVE, VIDEO } from '../src/mediaTypes.js'; import { - convertTypes, isArray, replaceAuctionPrice, triggerPixel, From 5945465a4218a5278bdc3e9186e7c274f55a00b6 Mon Sep 17 00:00:00 2001 From: Wls-demo <67785512+Wls-demo@users.noreply.github.com> Date: Mon, 27 Nov 2023 19:25:16 +0200 Subject: [PATCH 015/246] Boldwin Bid Adapter : update sync url (#10757) * new boldwin bid adapter * fix * Restarting ci / circleci * changes * fixed conflicts * added endpointId param * kick off CircleCI tests * gpp * update sync url --------- Co-authored-by: Aiholkin Co-authored-by: Vladislav Isaiko Co-authored-by: Mykhailo Yaremchuk Co-authored-by: Chris Huie --- modules/boldwinBidAdapter.js | 2 +- test/spec/modules/boldwinBidAdapter_spec.js | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/modules/boldwinBidAdapter.js b/modules/boldwinBidAdapter.js index 4d97f830d33..c7def383b5e 100644 --- a/modules/boldwinBidAdapter.js +++ b/modules/boldwinBidAdapter.js @@ -5,7 +5,7 @@ import { convertOrtbRequestToProprietaryNative } from '../src/native.js'; const BIDDER_CODE = 'boldwin'; const AD_URL = 'https://ssp.videowalldirect.com/pbjs'; -const SYNC_URL = 'https://cs.videowalldirect.com' +const SYNC_URL = 'https://sync.videowalldirect.com'; function isBidResponseValid(bid) { if (!bid.requestId || !bid.cpm || !bid.creativeId || diff --git a/test/spec/modules/boldwinBidAdapter_spec.js b/test/spec/modules/boldwinBidAdapter_spec.js index 52a6ec03757..9a7b16c0914 100644 --- a/test/spec/modules/boldwinBidAdapter_spec.js +++ b/test/spec/modules/boldwinBidAdapter_spec.js @@ -314,7 +314,7 @@ describe('BoldwinBidAdapter', 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.be.equal('https://cs.videowalldirect.com'); + expect(userSync[0].url).to.be.equal('https://sync.videowalldirect.com'); }); }); }); From 498b6b50da4a8b02d753177f43afb7de9218c9d3 Mon Sep 17 00:00:00 2001 From: onetag-dev <38786435+onetag-dev@users.noreply.github.com> Date: Mon, 27 Nov 2023 18:40:06 +0100 Subject: [PATCH 016/246] Onetag Bid Adapter: FLEDGE/PAAPI support (#10755) Co-authored-by: federico --- modules/onetagBidAdapter.js | 16 ++++- test/spec/modules/onetagBidAdapter_spec.js | 69 +++++++++++++++++++++- 2 files changed, 81 insertions(+), 4 deletions(-) diff --git a/modules/onetagBidAdapter.js b/modules/onetagBidAdapter.js index eb381727a3b..ee6d2980385 100644 --- a/modules/onetagBidAdapter.js +++ b/modules/onetagBidAdapter.js @@ -87,6 +87,7 @@ function buildRequests(validBidRequests, bidderRequest) { const connection = navigator.connection || navigator.webkitConnection; payload.networkConnectionType = (connection && connection.type) ? connection.type : null; payload.networkEffectiveConnectionType = (connection && connection.effectiveType) ? connection.effectiveType : null; + payload.fledgeEnabled = Boolean(bidderRequest && bidderRequest.fledgeEnabled) return { method: 'POST', url: ENDPOINT, @@ -101,10 +102,10 @@ function interpretResponse(serverResponse, bidderRequest) { if (!body || (body.nobid && body.nobid === true)) { return bids; } - if (!body.bids || !Array.isArray(body.bids) || body.bids.length === 0) { + if (!body.fledgeAuctionConfigs && (!body.bids || !Array.isArray(body.bids) || body.bids.length === 0)) { return bids; } - body.bids.forEach(bid => { + Array.isArray(body.bids) && body.bids.forEach(bid => { const responseBid = { requestId: bid.requestId, cpm: bid.cpm, @@ -141,7 +142,16 @@ function interpretResponse(serverResponse, bidderRequest) { } bids.push(responseBid); }); - return bids; + + if (body.fledgeAuctionConfigs && Array.isArray(body.fledgeAuctionConfigs)) { + const fledgeAuctionConfigs = body.fledgeAuctionConfigs + return { + bids, + fledgeAuctionConfigs, + } + } else { + return bids; + } } function createRenderer(bid, rendererOptions = {}) { diff --git a/test/spec/modules/onetagBidAdapter_spec.js b/test/spec/modules/onetagBidAdapter_spec.js index 6d43dd2e40e..7960574c1a4 100644 --- a/test/spec/modules/onetagBidAdapter_spec.js +++ b/test/spec/modules/onetagBidAdapter_spec.js @@ -189,7 +189,7 @@ describe('onetag', function () { }); it('Should contain all keys', function () { expect(data).to.be.an('object'); - expect(data).to.include.all.keys('location', 'referrer', 'stack', 'numIframes', 'sHeight', 'sWidth', 'docHeight', 'wHeight', 'wWidth', 'oHeight', 'oWidth', 'aWidth', 'aHeight', 'sLeft', 'sTop', 'hLength', 'bids', 'docHidden', 'xOffset', 'yOffset', 'networkConnectionType', 'networkEffectiveConnectionType', 'timing', 'version'); + expect(data).to.include.all.keys('location', 'referrer', 'stack', 'numIframes', 'sHeight', 'sWidth', 'docHeight', 'wHeight', 'wWidth', 'oHeight', 'oWidth', 'aWidth', 'aHeight', 'sLeft', 'sTop', 'hLength', 'bids', 'docHidden', 'xOffset', 'yOffset', 'networkConnectionType', 'networkEffectiveConnectionType', 'timing', 'version', 'fledgeEnabled'); expect(data.location).to.satisfy(function (value) { return value === null || typeof value === 'string'; }); @@ -213,6 +213,7 @@ describe('onetag', function () { expect(data.networkEffectiveConnectionType).to.satisfy(function (value) { return value === null || typeof value === 'string' }); + expect(data.fledgeEnabled).to.be.a('boolean'); expect(data.bids).to.be.an('array'); expect(data.version).to.have.all.keys('prebid', 'adapter'); const bids = data['bids']; @@ -396,13 +397,61 @@ describe('onetag', function () { expect(payload.ortb2).to.exist.and.to.deep.equal(firtPartyData); }); }); + it('Should send FLEDGE eligibility flag when FLEDGE is enabled', function () { + let bidderRequest = { + 'bidderCode': 'onetag', + 'auctionId': '1d1a030790a475', + 'bidderRequestId': '22edbae2733bf6', + 'timeout': 3000, + 'fledgeEnabled': true + }; + let serverRequest = spec.buildRequests([bannerBid], bidderRequest); + const payload = JSON.parse(serverRequest.data); + + expect(payload.fledgeEnabled).to.exist; + expect(payload.fledgeEnabled).to.exist.and.to.equal(bidderRequest.fledgeEnabled); + }); + it('Should send FLEDGE eligibility flag when FLEDGE is not enabled', function () { + let bidderRequest = { + 'bidderCode': 'onetag', + 'auctionId': '1d1a030790a475', + 'bidderRequestId': '22edbae2733bf6', + 'timeout': 3000, + 'fledgeEnabled': false + }; + let serverRequest = spec.buildRequests([bannerBid], bidderRequest); + const payload = JSON.parse(serverRequest.data); + + expect(payload.fledgeEnabled).to.exist; + expect(payload.fledgeEnabled).to.exist.and.to.equal(bidderRequest.fledgeEnabled); + }); + it('Should send FLEDGE eligibility flag set to false when fledgeEnabled is not defined', function () { + let bidderRequest = { + 'bidderCode': 'onetag', + 'auctionId': '1d1a030790a475', + 'bidderRequestId': '22edbae2733bf6', + 'timeout': 3000, + }; + let serverRequest = spec.buildRequests([bannerBid], bidderRequest); + const payload = JSON.parse(serverRequest.data); + + expect(payload.fledgeEnabled).to.exist; + expect(payload.fledgeEnabled).to.exist.and.to.equal(false); + }); describe('interpretResponse', function () { const request = getBannerVideoRequest(); const response = getBannerVideoResponse(); + const fledgeResponse = getFledgeBannerResponse(); const requestData = JSON.parse(request.data); it('Returns an array of valid server responses if response object is valid', function () { const interpretedResponse = spec.interpretResponse(response, request); + const fledgeInterpretedResponse = spec.interpretResponse(fledgeResponse, request); expect(interpretedResponse).to.be.an('array').that.is.not.empty; + expect(fledgeInterpretedResponse).to.be.an('object'); + expect(fledgeInterpretedResponse.bids).to.satisfy(function (value) { + return value === null || Array.isArray(value); + }); + expect(fledgeInterpretedResponse.fledgeAuctionConfigs).to.be.an('array').that.is.not.empty; for (let i = 0; i < interpretedResponse.length; i++) { let dataItem = interpretedResponse[i]; expect(dataItem).to.include.all.keys('requestId', 'cpm', 'width', 'height', 'ttl', 'creativeId', 'netRevenue', 'currency', 'meta', 'dealId'); @@ -600,6 +649,24 @@ function getBannerVideoResponse() { }; } +function getFledgeBannerResponse() { + const bannerVideoResponse = getBannerVideoResponse(); + bannerVideoResponse.body.fledgeAuctionConfigs = [ + { + bidId: 'fledge', + config: { + seller: 'https://onetag-sys.com', + decisionLogicUrl: + 'https://onetag-sys.com/paapi/decision_logic.js', + interestGroupBuyers: [ + 'https://onetag-sys.com' + ], + } + } + ] + return bannerVideoResponse; +} + function getBannerVideoRequest() { return { data: JSON.stringify({ From ecf4272bf033fec84e54b26c6a6bcfdc414d9c8f Mon Sep 17 00:00:00 2001 From: Jason Quaccia Date: Mon, 27 Nov 2023 09:48:39 -0800 Subject: [PATCH 017/246] FPD Enrichment: support for Cookie Deprecation Label (#10683) * cookie deprecation label * reverted unneeded changes made during development * addressed feedback * removed unnecessary else statement * added comment for FIRST_PARTY_GVLID param * ensure promise resolves when label is falsey * addressed feedback * removed async/await from getCookieDeprecationLabel * updating tests * more changes * addressed feedback * Small improvements --------- Co-authored-by: Demetrio Girardi --- modules/gdprEnforcement.js | 15 ++++- src/activities/redactor.js | 2 +- src/consentHandler.js | 8 +++ src/fpd/enrichment.js | 25 +++++++- test/spec/fpd/enrichment_spec.js | 70 ++++++++++++++++++++++- test/spec/modules/gdprEnforcement_spec.js | 17 +++++- 6 files changed, 129 insertions(+), 8 deletions(-) diff --git a/modules/gdprEnforcement.js b/modules/gdprEnforcement.js index 4e600f71b90..5b73ec19e08 100644 --- a/modules/gdprEnforcement.js +++ b/modules/gdprEnforcement.js @@ -7,7 +7,7 @@ import {config} from '../src/config.js'; import adapterManager, {gdprDataHandler} from '../src/adapterManager.js'; import * as events from '../src/events.js'; import CONSTANTS from '../src/constants.json'; -import {GDPR_GVLIDS, VENDORLESS_GVLID} from '../src/consentHandler.js'; +import {GDPR_GVLIDS, VENDORLESS_GVLID, FIRST_PARTY_GVLID} from '../src/consentHandler.js'; import { MODULE_TYPE_ANALYTICS, MODULE_TYPE_BIDDER, @@ -111,7 +111,7 @@ export function getGvlid(moduleType, moduleName, fallbackFn) { if (gvlMapping && gvlMapping[moduleName]) { return gvlMapping[moduleName]; } else if (moduleType === MODULE_TYPE_PREBID) { - return VENDORLESS_GVLID; + return moduleName === 'cdep' ? FIRST_PARTY_GVLID : VENDORLESS_GVLID; } else { let {gvlid, modules} = GDPR_GVLIDS.get(moduleName); if (gvlid == null && Object.keys(modules).length > 0) { @@ -166,6 +166,7 @@ export function shouldEnforce(consentData, purpose, name) { function getConsent(consentData, type, id, gvlId) { let purpose = !!deepAccess(consentData, `vendorData.${CONSENT_PATHS[type]}.${id}`); let vendor = !!deepAccess(consentData, `vendorData.vendor.consents.${gvlId}`); + if (type === 'purpose' && LI_PURPOSES.includes(id)) { purpose ||= !!deepAccess(consentData, `vendorData.purpose.legitimateInterests.${id}`); vendor ||= !!deepAccess(consentData, `vendorData.vendor.legitimateInterests.${gvlId}`); @@ -191,13 +192,21 @@ export function validateRules(rule, consentData, currentModule, gvlId) { } const vendorConsentRequred = rule.enforceVendor && !((gvlId === VENDORLESS_GVLID || (rule.softVendorExceptions || []).includes(currentModule))); const {purpose, vendor} = getConsent(consentData, ruleOptions.type, ruleOptions.id, gvlId); - return (!rule.enforcePurpose || purpose) && (!vendorConsentRequred || vendor); + + let validation = (!rule.enforcePurpose || purpose) && (!vendorConsentRequred || vendor); + + if (gvlId === FIRST_PARTY_GVLID) { + validation = (!rule.enforcePurpose || !!deepAccess(consentData, `vendorData.publisher.consents.${ruleOptions.id}`)); + } + + return validation; } function gdprRule(purposeNo, checkConsent, blocked = null, gvlidFallback = () => null) { return function (params) { const consentData = gdprDataHandler.getConsentData(); const modName = params[ACTIVITY_PARAM_COMPONENT_NAME]; + if (shouldEnforce(consentData, purposeNo, modName)) { const gvlid = getGvlid(params[ACTIVITY_PARAM_COMPONENT_TYPE], modName, gvlidFallback(params)); let allow = !!checkConsent(consentData, modName, gvlid); diff --git a/src/activities/redactor.js b/src/activities/redactor.js index d052c029c13..694a96b2b14 100644 --- a/src/activities/redactor.js +++ b/src/activities/redactor.js @@ -18,7 +18,7 @@ export const ORTB_UFPD_PATHS = [ 'id', 'buyeruid', 'customdata' -].map(f => `user.${f}`); +].map(f => `user.${f}`).concat('device.ext.cdep'); export const ORTB_EIDS_PATHS = ['user.eids', 'user.ext.eids']; export const ORTB_GEO_PATHS = ['user.geo.lat', 'user.geo.lon', 'device.geo.lat', 'device.geo.lon']; diff --git a/src/consentHandler.js b/src/consentHandler.js index 9e3ee5b4c40..5b5d8b805cd 100644 --- a/src/consentHandler.js +++ b/src/consentHandler.js @@ -10,6 +10,14 @@ import {config} from './config.js'; */ export const VENDORLESS_GVLID = Object.freeze({}); +/** + * Placeholder gvlid for when device.ext.cdep is present (Privacy Sandbox cookie deprecation label). When this value is used as gvlid, the gdpr + * enforcement module will look to see that publisher consent was given. + * + * see https://github.com/prebid/Prebid.js/issues/10516 + */ +export const FIRST_PARTY_GVLID = Object.freeze({}); + export class ConsentHandler { #enabled; #data; diff --git a/src/fpd/enrichment.js b/src/fpd/enrichment.js index f812d8435d9..911509455e0 100644 --- a/src/fpd/enrichment.js +++ b/src/fpd/enrichment.js @@ -6,6 +6,10 @@ import {config} from '../config.js'; import {getHighEntropySUA, getLowEntropySUA} from './sua.js'; import {GreedyPromise} from '../utils/promise.js'; import {CLIENT_SECTIONS, clientSectionChecker, hasSection} from './oneClient.js'; +import {isActivityAllowed} from '../activities/rules.js'; +import {activityParams} from '../activities/activityParams.js'; +import {ACTIVITY_ACCESS_DEVICE} from '../activities/activities.js'; +import {MODULE_TYPE_PREBID} from '../activities/modules.js'; export const dep = { getRefererInfo, @@ -24,8 +28,10 @@ const oneClient = clientSectionChecker('FPD') * @returns: {Promise[{}]}: a promise to an enriched ortb2 object. */ export const enrichFPD = hook('sync', (fpd) => { - return GreedyPromise.all([fpd, getSUA().catch(() => null)]) - .then(([ortb2, sua]) => { + const promArr = [fpd, getSUA().catch(() => null), tryToGetCdepLabel().catch(() => null)]; + + return GreedyPromise.all(promArr) + .then(([ortb2, sua, cdep]) => { const ri = dep.getRefererInfo(); mergeLegacySetConfigs(ortb2); Object.entries(ENRICHMENTS).forEach(([section, getEnrichments]) => { @@ -34,9 +40,18 @@ export const enrichFPD = hook('sync', (fpd) => { ortb2[section] = mergeDeep({}, data, ortb2[section]); } }); + if (sua) { deepSetValue(ortb2, 'device.sua', Object.assign({}, sua, ortb2.device.sua)); } + + if (cdep) { + const ext = { + cdep + } + deepSetValue(ortb2, 'device.ext', Object.assign({}, ext, ortb2.device.ext)); + } + ortb2 = oneClient(ortb2); for (let section of CLIENT_SECTIONS) { if (hasSection(ortb2, section)) { @@ -44,6 +59,7 @@ export const enrichFPD = hook('sync', (fpd) => { break; } } + return ortb2; }); }); @@ -78,6 +94,10 @@ function removeUndef(obj) { return getDefinedParams(obj, Object.keys(obj)) } +function tryToGetCdepLabel() { + return GreedyPromise.resolve('cookieDeprecationLabel' in navigator && isActivityAllowed(ACTIVITY_ACCESS_DEVICE, activityParams(MODULE_TYPE_PREBID, 'cdep')) && navigator.cookieDeprecationLabel.getValue()); +} + const ENRICHMENTS = { site(ortb2, ri) { if (CLIENT_SECTIONS.filter(p => p !== 'site').some(hasSection.bind(null, ortb2))) { @@ -93,6 +113,7 @@ const ENRICHMENTS = { return winFallback((win) => { const w = win.innerWidth || win.document.documentElement.clientWidth || win.document.body.clientWidth; const h = win.innerHeight || win.document.documentElement.clientHeight || win.document.body.clientHeight; + return { w, h, diff --git a/test/spec/fpd/enrichment_spec.js b/test/spec/fpd/enrichment_spec.js index 3b3afb15f8c..40692360dca 100644 --- a/test/spec/fpd/enrichment_spec.js +++ b/test/spec/fpd/enrichment_spec.js @@ -3,7 +3,10 @@ import {hook} from '../../../src/hook.js'; import {expect} from 'chai/index.mjs'; import {config} from 'src/config.js'; import * as utils from 'src/utils.js'; +import * as activities from 'src/activities/rules.js' import {CLIENT_SECTIONS} from '../../../src/fpd/oneClient.js'; +import {ACTIVITY_ACCESS_DEVICE} from '../../../src/activities/activities.js'; +import {ACTIVITY_PARAM_COMPONENT} from '../../../src/activities/params.js'; describe('FPD enrichment', () => { let sandbox; @@ -213,7 +216,7 @@ describe('FPD enrichment', () => { ua: 'ua' }) }) - }) + }); }); }); @@ -310,6 +313,71 @@ describe('FPD enrichment', () => { }); }); + describe('privacy sandbox cookieDeprecationLabel', () => { + let isAllowed, cdep, shouldCleanupNav = false; + + before(() => { + if (!navigator.cookieDeprecationLabel) { + navigator.cookieDeprecationLabel = {}; + shouldCleanupNav = true; + } + }); + + after(() => { + if (shouldCleanupNav) { + delete navigator.cookieDeprecationLabel; + } + }); + + beforeEach(() => { + isAllowed = true; + sandbox.stub(activities, 'isActivityAllowed').callsFake((activity, params) => { + if (activity === ACTIVITY_ACCESS_DEVICE && params[ACTIVITY_PARAM_COMPONENT] === 'prebid.cdep') { + return isAllowed; + } else { + throw new Error('Unexpected activity check'); + } + }); + sandbox.stub(window.navigator, 'cookieDeprecationLabel').value({ + getValue: sinon.stub().callsFake(() => cdep) + }) + }) + + it('enrichment sets device.ext.cdep when allowed and navigator.getCookieDeprecationLabel exists', () => { + cdep = Promise.resolve('example-test-label'); + return fpd().then(ortb2 => { + expect(ortb2.device.ext.cdep).to.eql('example-test-label'); + }) + }); + + Object.entries({ + 'not allowed'() { + isAllowed = false; + }, + 'not supported'() { + delete navigator.cookieDeprecationLabel + } + }).forEach(([t, setup]) => { + it(`if ${t}, the navigator API is not called and no enrichment happens`, () => { + setup(); + cdep = Promise.resolve('example-test-label'); + return fpd().then(ortb2 => { + expect(ortb2.device.ext).to.not.exist; + if (navigator.cookieDeprecationLabel) { + sinon.assert.notCalled(navigator.cookieDeprecationLabel.getValue); + } + }) + }); + }) + + it('if the navigator API returns a promise that rejects, the enrichment does not halt forever', () => { + cdep = Promise.reject(new Error('oops, something went wrong')); + return fpd().then(ortb2 => { + expect(ortb2.device.ext).to.not.exist; + }) + }); + }); + it('leaves only one of app, site, dooh', () => { return fpd({ app: {p: 'val'}, diff --git a/test/spec/modules/gdprEnforcement_spec.js b/test/spec/modules/gdprEnforcement_spec.js index 2880b2fac5d..295b14dd796 100644 --- a/test/spec/modules/gdprEnforcement_spec.js +++ b/test/spec/modules/gdprEnforcement_spec.js @@ -26,7 +26,7 @@ import * as events from 'src/events.js'; import 'modules/appnexusBidAdapter.js'; // some tests expect this to be in the adapter registry import 'src/prebid.js'; import {hook} from '../../../src/hook.js'; -import {GDPR_GVLIDS, VENDORLESS_GVLID} from '../../../src/consentHandler.js'; +import {GDPR_GVLIDS, VENDORLESS_GVLID, FIRST_PARTY_GVLID} from '../../../src/consentHandler.js'; import {validateStorageEnforcement} from '../../../src/storageManager.js'; import {activityParams} from '../../../src/activities/activityParams.js'; @@ -789,6 +789,21 @@ describe('gdpr enforcement', function () { }) }) + it('if validateRules is passed FIRST_PARTY_GVLID, it will use publisher.consents', () => { + const rule = createGdprRule(); + const consentData = { + 'vendorData': { + 'publisher': { + 'consents': { + '1': true + } + }, + }, + }; + const result = validateRules(rule, consentData, 'cdep', FIRST_PARTY_GVLID); + expect(result).to.equal(true); + }); + describe('validateRules', function () { Object.entries({ '1 (which does not consider LI)': [1, 'storage', false], From 8993f7462cdba5dabbdbc9b586e02abba1e0b9e6 Mon Sep 17 00:00:00 2001 From: samuel-palmer-relevant-digital <77437973+samuel-palmer-relevant-digital@users.noreply.github.com> Date: Mon, 27 Nov 2023 20:48:14 +0100 Subject: [PATCH 018/246] Currency module: Don't block 'responsesReady' when rates are present and no conversion is done (#10736) * Currency module: Don't block 'responsesReady' when rates are present and no conversion is needed * Add test case for currency unnecessarily blocking auctions * Update currency_spec.js --------- Co-authored-by: Demetrio Girardi --- modules/currency.js | 2 ++ test/spec/modules/currency_spec.js | 13 +++++++++++++ 2 files changed, 15 insertions(+) diff --git a/modules/currency.js b/modules/currency.js index e42799c0864..eaed4c50df2 100644 --- a/modules/currency.js +++ b/modules/currency.js @@ -148,6 +148,8 @@ function loadRates() { } } ); + } else { + processBidResponseQueue(); } } diff --git a/test/spec/modules/currency_spec.js b/test/spec/modules/currency_spec.js index 623194cbee9..fa44b7daa7a 100644 --- a/test/spec/modules/currency_spec.js +++ b/test/spec/modules/currency_spec.js @@ -259,6 +259,19 @@ describe('currency', function () { expect(innerBid.getCpmInNewCurrency('JPY')).to.equal('100.000'); }); + it('does not block auctions if rates do not need to be fetched', () => { + sandbox.stub(responseReady, 'resolve'); + setConfig({ + adServerCurrency: 'USD', + rates: { + USD: { + JPY: 100 + } + } + }); + sinon.assert.called(responseReady.resolve); + }) + it('uses rates specified in json when provided and consider boosted bid', function () { setConfig({ adServerCurrency: 'USD', From d21fb7d1e51fbdbfa44ec3a434dbb650228743ce Mon Sep 17 00:00:00 2001 From: cpcpn-emil <115714010+cpcpn-emil@users.noreply.github.com> Date: Tue, 28 Nov 2023 02:19:44 +0100 Subject: [PATCH 019/246] ConceptX Bid Adapter : bugfix to avoid appending to the request url on build (#10749) * New adapter: concepx * Syntax change * Revert syntax change * Defensive check for response from bidder server * Add better validation for the request * Merge branch 'master' of https://github.com/prebid/Prebid.js * Don't append url on every buildrequest --- modules/conceptxBidAdapter.js | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/modules/conceptxBidAdapter.js b/modules/conceptxBidAdapter.js index 127b049bc99..87ac96f2131 100644 --- a/modules/conceptxBidAdapter.js +++ b/modules/conceptxBidAdapter.js @@ -3,23 +3,23 @@ import { BANNER } from '../src/mediaTypes.js'; // import { logError, logInfo, logWarn, parseUrl } from '../src/utils.js'; const BIDDER_CODE = 'conceptx'; -let ENDPOINT_URL = 'https://conceptx.cncpt-central.com/openrtb'; +const ENDPOINT_URL = 'https://conceptx.cncpt-central.com/openrtb'; // const LOG_PREFIX = 'ConceptX: '; export const spec = { code: BIDDER_CODE, supportedMediaTypes: [BANNER], isBidRequestValid: function (bid) { - return !!(bid.bidId); + return !!(bid.bidId && bid.params.site && bid.params.adunit); }, buildRequests: function (validBidRequests, bidderRequest) { // logWarn(LOG_PREFIX + 'all native assets containing URL should be sent as placeholders with sendId(icon, image, clickUrl, displayUrl, privacyLink, privacyIcon)'); const requests = []; - + let requestUrl = `${ENDPOINT_URL}` if (bidderRequest && bidderRequest.gdprConsent && bidderRequest.gdprConsent.gdprApplies) { - ENDPOINT_URL += '?gdpr_applies=' + bidderRequest.gdprConsent.gdprApplies; - ENDPOINT_URL += '&consentString=' + bidderRequest.gdprConsent.consentString; + requestUrl += '?gdpr_applies=' + bidderRequest.gdprConsent.gdprApplies; + requestUrl += '&consentString=' + bidderRequest.gdprConsent.consentString; } for (var i = 0; i < validBidRequests.length; i++) { const requestParent = { adUnits: [], meta: {} }; @@ -33,7 +33,7 @@ export const spec = { requestParent.adUnits.push(adUnit); requests.push({ method: 'POST', - url: ENDPOINT_URL, + url: requestUrl, options: { withCredentials: false, }, @@ -51,6 +51,9 @@ export const spec = { return bidResponses } const firstBid = bidResponsesFromServer[0] + if (!firstBid) { + return bidResponses + } const firstSeat = firstBid.ads[0] const bidResponse = { requestId: firstSeat.requestId, From 6a0c7f0be8b35b3a0a2c5b85ac13e9b32db8f89e Mon Sep 17 00:00:00 2001 From: Vincent Date: Tue, 28 Nov 2023 15:15:07 +0100 Subject: [PATCH 020/246] Grid bid adapter: call data deletion request c2s endpoint (#10732) Co-authored-by: v.raybaud --- modules/gridBidAdapter.js | 13 ++-------- test/spec/modules/gridBidAdapter_spec.js | 32 ++---------------------- 2 files changed, 4 insertions(+), 41 deletions(-) diff --git a/modules/gridBidAdapter.js b/modules/gridBidAdapter.js index aa00a84273c..9673633a0fe 100644 --- a/modules/gridBidAdapter.js +++ b/modules/gridBidAdapter.js @@ -18,7 +18,7 @@ import { getStorageManager } from '../src/storageManager.js'; const BIDDER_CODE = 'grid'; const ENDPOINT_URL = 'https://grid.bidswitch.net/hbjson'; -const USP_DELETE_DATA_HANDLER = 'https://media.grid.bidswitch.net/uspapi_delete' +const USP_DELETE_DATA_HANDLER = 'https://media.grid.bidswitch.net/uspapi_delete_c2s' const SYNC_URL = 'https://x.bidswitch.net/sync?ssp=themediagrid'; const TIME_TO_LIVE = 360; @@ -464,16 +464,7 @@ export const spec = { }, onDataDeletionRequest: function(data) { - const uids = []; - const aliases = [spec.code, ...spec.aliases.map((alias) => alias.code || alias)]; - data.forEach(({ bids }) => bids && bids.forEach(({ bidder, params }) => { - if (aliases.includes(bidder) && params && params.uid) { - uids.push(params.uid); - } - })); - if (uids.length) { - spec.ajaxCall(USP_DELETE_DATA_HANDLER, () => {}, JSON.stringify({ uids }), {contentType: 'application/json', method: 'POST'}); - } + spec.ajaxCall(USP_DELETE_DATA_HANDLER, null, null, {method: 'GET'}); } }; diff --git a/test/spec/modules/gridBidAdapter_spec.js b/test/spec/modules/gridBidAdapter_spec.js index b12083236a2..0611fa68bf8 100644 --- a/test/spec/modules/gridBidAdapter_spec.js +++ b/test/spec/modules/gridBidAdapter_spec.js @@ -1559,37 +1559,9 @@ describe('TheMediaGrid Adapter', function () { }); it('should send right request on onDataDeletionRequest call', function() { - spec.onDataDeletionRequest([{ - bids: [ - { - bidder: 'grid', - params: { - uid: 1 - } - }, - { - bidder: 'grid', - params: { - uid: 2 - } - }, - { - bidder: 'another', - params: { - uid: 3 - } - }, - { - bidder: 'gridNM', - params: { - uid: 4 - } - } - ], - }]); + spec.onDataDeletionRequest([{}]); expect(ajaxStub.calledOnce).to.equal(true); - expect(ajaxStub.firstCall.args[0]).to.equal('https://media.grid.bidswitch.net/uspapi_delete'); - expect(ajaxStub.firstCall.args[2]).to.equal('{"uids":[1,2,4]}'); + expect(ajaxStub.firstCall.args[0]).to.equal('https://media.grid.bidswitch.net/uspapi_delete_c2s'); }); }); From 4e323bf52cbf04d4b39796f9ebfd94ecfbb7c280 Mon Sep 17 00:00:00 2001 From: geoffray-viously <95097046+geoffray-viously@users.noreply.github.com> Date: Tue, 28 Nov 2023 15:24:56 +0100 Subject: [PATCH 021/246] SparteoBidAdapter: Send bidder params in OpenRTB request (#10760) --- modules/sparteoBidAdapter.js | 7 +++++ test/spec/modules/sparteoBidAdapter_spec.js | 33 +++++++++++++++++++-- 2 files changed, 37 insertions(+), 3 deletions(-) diff --git a/modules/sparteoBidAdapter.js b/modules/sparteoBidAdapter.js index 7d4f87c24f3..ac48ec4089d 100644 --- a/modules/sparteoBidAdapter.js +++ b/modules/sparteoBidAdapter.js @@ -28,6 +28,13 @@ const converter = ortbConverter({ return request; }, + imp(buildImp, bidRequest, context) { + const imp = buildImp(bidRequest, context); + + deepSetValue(imp, 'ext.sparteo.params', bidRequest.params); + + return imp; + }, bidResponse(buildBidResponse, bid, context) { context.mediaType = deepAccess(bid, 'ext.prebid.type'); diff --git a/test/spec/modules/sparteoBidAdapter_spec.js b/test/spec/modules/sparteoBidAdapter_spec.js index e82f23a1d4e..d6b8b0cc2d8 100644 --- a/test/spec/modules/sparteoBidAdapter_spec.js +++ b/test/spec/modules/sparteoBidAdapter_spec.js @@ -12,7 +12,8 @@ const VALID_BID_BANNER = { bidId: '1a2b3c4d', adUnitCode: 'id-1234', params: { - networkId: '1234567a-eb1b-1fae-1d23-e1fbaef234cf' + networkId: '1234567a-eb1b-1fae-1d23-e1fbaef234cf', + formats: ['corner'] }, mediaTypes: { banner: { @@ -64,6 +65,14 @@ const VALID_REQUEST_BANNER = { 'w': 1 }], 'topframe': 0 + }, + 'ext': { + 'sparteo': { + 'params': { + 'networkId': '1234567a-eb1b-1fae-1d23-e1fbaef234cf', + 'formats': ['corner'] + } + } } }], 'site': { @@ -99,7 +108,12 @@ const VALID_REQUEST_VIDEO = { 'maxduration': 30, }, 'ext': { - 'pbadslot': 'video' + 'pbadslot': 'video', + 'sparteo': { + 'params': { + 'networkId': '1234567a-eb1b-1fae-1d23-e1fbaef234cf' + } + } } }], 'site': { @@ -127,6 +141,14 @@ const VALID_REQUEST = { 'w': 1 }], 'topframe': 0 + }, + 'ext': { + 'sparteo': { + 'params': { + 'networkId': '1234567a-eb1b-1fae-1d23-e1fbaef234cf', + 'formats': ['corner'] + } + } } }, { 'id': '5e6f7g8h', @@ -144,7 +166,12 @@ const VALID_REQUEST = { 'maxduration': 30, }, 'ext': { - 'pbadslot': 'video' + 'pbadslot': 'video', + 'sparteo': { + 'params': { + 'networkId': '1234567a-eb1b-1fae-1d23-e1fbaef234cf' + } + } } }], 'site': { From 1e33758db4e1e77deb02bfee89664550be667be1 Mon Sep 17 00:00:00 2001 From: Thomas Date: Tue, 28 Nov 2023 18:24:05 +0100 Subject: [PATCH 022/246] Impactify Bid Adapter : Removing checks on bid request (#10769) * Remove use of local storage As requested, we remove the use of local storage. https://github.com/prebid/Prebid.js/issues/8689 * Update impactifyBidAdapter.js * Add differents mediatypes to Impactify bidder * Add differents mediatypes to Impactify bidder * Add differents mediatypes to Impactify bidder * Add format parameter for banner * add getFloor * add getFloor * add getFloor * add parsing of local storage * delete unused var * fix spacing with import * Add local storage key management * Adjustments * Fix eids object * Fix eids object * Fix eids object * Fix tests * Remove check on the request banner size parameter * Few adjustments --------- Co-authored-by: Thomas De Stefano Co-authored-by: Pang Ronnie --- modules/impactifyBidAdapter.js | 26 +++++++------------ test/spec/modules/impactifyBidAdapter_spec.js | 15 +++++------ 2 files changed, 16 insertions(+), 25 deletions(-) diff --git a/modules/impactifyBidAdapter.js b/modules/impactifyBidAdapter.js index f446050265a..04a45e64b19 100644 --- a/modules/impactifyBidAdapter.js +++ b/modules/impactifyBidAdapter.js @@ -17,7 +17,7 @@ const AUCTION_URI = '/bidder'; const COOKIE_SYNC_URI = '/static/cookie_sync.html'; const GVL_ID = 606; const GET_CONFIG = config.getConfig; -export const STORAGE = getStorageManager({gvlid: GVL_ID, bidderCode: BIDDER_CODE}); +export const STORAGE = getStorageManager({ gvlid: GVL_ID, bidderCode: BIDDER_CODE }); export const STORAGE_KEY = '_im_str' /** @@ -114,7 +114,7 @@ function createOpenRtbRequest(validBidRequests, bidderRequest) { validBidRequests, cur: [DEFAULT_CURRENCY], imp: [], - source: {tid: bidderRequest.ortb2?.source?.tid} + source: { tid: bidderRequest.ortb2?.source?.tid } }; // Get the url parameters @@ -149,7 +149,7 @@ function createOpenRtbRequest(validBidRequests, bidderRequest) { 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.page}; + request.site = { page: bidderRequest.refererInfo.page }; // Handle privacy settings for GDPR/CCPA/COPPA let gdprApplies = 0; @@ -176,7 +176,6 @@ function createOpenRtbRequest(validBidRequests, bidderRequest) { // Create imps with bids validBidRequests.forEach((bid) => { let bannerObj = deepAccess(bid.mediaTypes, `banner`); - let videoObj = deepAccess(bid.mediaTypes, `video`); let imp = { id: bid.bidId, @@ -184,16 +183,14 @@ function createOpenRtbRequest(validBidRequests, bidderRequest) { ext: helpers.getExtParamsFromBid(bid) }; - if (videoObj) { - imp.video = { - ...helpers.createOrtbImpVideoObj(bid) - } - } - if (bannerObj && typeof imp.ext.impactify.size == 'string') { imp.banner = { ...helpers.createOrtbImpBannerObj(bid, imp.ext.impactify.size) } + } else { + imp.video = { + ...helpers.createOrtbImpVideoObj(bid) + } } if (typeof bid.getFloor === 'function') { @@ -227,8 +224,6 @@ export const spec = { * @return boolean True if this is a valid bid, and false otherwise. */ isBidRequestValid: function (bid) { - let isBanner = deepAccess(bid.mediaTypes, `banner`); - if (typeof bid.params.appId != 'string' || !bid.params.appId) { return false; } @@ -241,9 +236,6 @@ export const spec = { if (bid.params.style !== 'inline' && bid.params.style !== 'impact' && bid.params.style !== 'static') { return false; } - if (isBanner && (typeof bid.params.size != 'string' || !bid.params.size.includes('x') || bid.params.size.split('x').length != 2)) { - return false; - } return true; }, @@ -369,7 +361,7 @@ 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 */ - onBidWon: function(bid) { + onBidWon: function (bid) { ajax(`${LOGGER_URI}/prebid/won`, null, JSON.stringify(bid), { method: 'POST', contentType: 'application/json' @@ -382,7 +374,7 @@ export const spec = { * Register bidder specific code, which will execute if bidder timed out after an auction * @param {data} Containing timeout specific data */ - onTimeout: function(data) { + onTimeout: function (data) { ajax(`${LOGGER_URI}/prebid/timeout`, null, JSON.stringify(data[0]), { method: 'POST', contentType: 'application/json' diff --git a/test/spec/modules/impactifyBidAdapter_spec.js b/test/spec/modules/impactifyBidAdapter_spec.js index adf968d610d..d9bf4becb22 100644 --- a/test/spec/modules/impactifyBidAdapter_spec.js +++ b/test/spec/modules/impactifyBidAdapter_spec.js @@ -36,7 +36,7 @@ describe('ImpactifyAdapter', function () { localStorageIsEnabledStub = sandbox.stub(STORAGE, 'localStorageIsEnabled'); }); - afterEach(function() { + afterEach(function () { $$PREBID_GLOBAL$$.bidderSettings = {}; document.body.appendChild.restore(); sandbox.restore(); @@ -57,7 +57,6 @@ describe('ImpactifyAdapter', function () { params: { appId: 'example.com', format: 'display', - size: '728x90', style: 'static' } } @@ -282,7 +281,7 @@ describe('ImpactifyAdapter', function () { }; it('should pass bidfloor', function () { - videoBidRequests[0].getFloor = function() { + videoBidRequests[0].getFloor = function () { return { currency: 'USD', floor: 1.23, @@ -342,7 +341,7 @@ describe('ImpactifyAdapter', function () { h: 1, hash: 'test', expiry: 166192938, - meta: {'advertiserDomains': ['testdomain.com']}, + meta: { 'advertiserDomains': ['testdomain.com'] }, ext: { prebid: { 'type': 'video' @@ -418,7 +417,7 @@ describe('ImpactifyAdapter', function () { height: 1, hash: 'test', expiry: 166192938, - meta: {'advertiserDomains': ['testdomain.com']}, + meta: { 'advertiserDomains': ['testdomain.com'] }, ttl: 300, creativeId: '97517771' } @@ -480,7 +479,7 @@ describe('ImpactifyAdapter', function () { h: 1, hash: 'test', expiry: 166192938, - meta: {'advertiserDomains': ['testdomain.com']}, + meta: { 'advertiserDomains': ['testdomain.com'] }, ext: { prebid: { 'type': 'video' @@ -536,8 +535,8 @@ describe('ImpactifyAdapter', 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); + 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'); }); From 1488d8c10c7f56081a8cad3abc8475f7f7543739 Mon Sep 17 00:00:00 2001 From: bhainesnexxen <146981622+bhainesnexxen@users.noreply.github.com> Date: Tue, 28 Nov 2023 22:39:53 -0800 Subject: [PATCH 023/246] Unruly: Protected Audience Support (#10709) * Unruly Protected Audience Support * Unruly Conditional Protected Audience Support * Unruly map bidIds to AuctionConfigs * Unruly: Update Protected Audience origin and descision logic URL. * Unruly: Fix lint error --------- Co-authored-by: Brian Haines --- modules/unrulyBidAdapter.js | 55 ++- test/spec/modules/unrulyBidAdapter_spec.js | 424 ++++++++++++++++++++- 2 files changed, 463 insertions(+), 16 deletions(-) diff --git a/modules/unrulyBidAdapter.js b/modules/unrulyBidAdapter.js index d893bfd3038..8fbbcf7d7c5 100644 --- a/modules/unrulyBidAdapter.js +++ b/modules/unrulyBidAdapter.js @@ -56,6 +56,12 @@ const RemoveDuplicateSizes = (validBid) => { } }; +const ConfigureProtectedAudience = (validBid, protectedAudienceEnabled) => { + if (!protectedAudienceEnabled && validBid.ortb2Imp && validBid.ortb2Imp.ext) { + delete validBid.ortb2Imp.ext.ae; + } +} + const getRequests = (conf, validBidRequests, bidderRequest) => { const {bids, bidderRequestId, bidderCode, ...bidderRequestData} = bidderRequest; const invalidBidsCount = bidderRequest.bids.length - validBidRequests.length; @@ -65,6 +71,7 @@ const getRequests = (conf, validBidRequests, bidderRequest) => { const currSiteId = validBid.params.siteId; addBidFloorInfo(validBid); RemoveDuplicateSizes(validBid); + ConfigureProtectedAudience(validBid, conf.protectedAudienceEnabled); requestBySiteId[currSiteId] = requestBySiteId[currSiteId] || []; requestBySiteId[currSiteId].push(validBid); }); @@ -206,21 +213,49 @@ export const adapter = { endPoint = deepAccess(validBidRequests[0], 'params.endpoint') || endPoint; } - const url = endPoint; - const method = 'POST'; - const options = {contentType: 'application/json'}; - return getRequests({url, method, options}, validBidRequests, bidderRequest); + return getRequests({ + 'url': endPoint, + 'method': 'POST', + 'options': { + 'contentType': 'application/json' + }, + 'protectedAudienceEnabled': bidderRequest.fledgeEnabled + }, validBidRequests, bidderRequest); }, - interpretResponse: function (serverResponse = {}) { + interpretResponse: function (serverResponse) { + if (!(serverResponse && serverResponse.body && (serverResponse.body.auctionConfigs || serverResponse.body.bids))) { + return []; + } + const serverResponseBody = serverResponse.body; + let bids = []; + let fledgeAuctionConfigs = null; + if (serverResponseBody.bids.length) { + bids = handleBidResponseByMediaType(serverResponseBody.bids); + } - const noBidsResponse = []; - const isInvalidResponse = !serverResponseBody || !serverResponseBody.bids; + if (serverResponseBody.auctionConfigs) { + let auctionConfigs = serverResponseBody.auctionConfigs; + let bidIdList = Object.keys(auctionConfigs); + if (bidIdList.length) { + bidIdList.forEach((bidId) => { + fledgeAuctionConfigs = [{ + 'bidId': bidId, + 'config': auctionConfigs[bidId] + }]; + }) + } + } - return isInvalidResponse - ? noBidsResponse - : handleBidResponseByMediaType(serverResponseBody.bids); + if (!fledgeAuctionConfigs) { + return bids; + } + + return { + bids, + fledgeAuctionConfigs + }; } }; diff --git a/test/spec/modules/unrulyBidAdapter_spec.js b/test/spec/modules/unrulyBidAdapter_spec.js index 6d1d8f9949f..f1f6517af67 100644 --- a/test/spec/modules/unrulyBidAdapter_spec.js +++ b/test/spec/modules/unrulyBidAdapter_spec.js @@ -42,9 +42,40 @@ describe('UnrulyAdapter', function () { } } - const createExchangeResponse = (...bids) => ({ - body: {bids} - }); + function createOutStreamExchangeAuctionConfig() { + return { + 'seller': 'https://nexxen.tech', + 'decisionLogicURL': 'https://nexxen.tech/padecisionlogic', + 'interestGroupBuyers': 'https://mydsp.com', + 'perBuyerSignals': { + 'https://mydsp.com': { + 'floor': 'bouttreefiddy' + } + } + } + }; + + function createExchangeResponse (bidList, auctionConfigs = null) { + let bids = []; + if (Array.isArray(bidList)) { + bids = bidList; + } else if (bidList) { + bids.push(bidList); + } + + if (!auctionConfigs) { + return { + 'body': {bids} + }; + } + + return { + 'body': { + bids, + auctionConfigs + } + } + }; const inStreamServerResponse = { 'requestId': '262594d5d1f8104', @@ -658,6 +689,227 @@ describe('UnrulyAdapter', function () { let result = adapter.buildRequests(mockBidRequests.bids, mockBidRequests); expect(result[0].data).to.deep.equal(expectedResult); }); + describe('Protected Audience Support', function() { + it('should return an array with 2 items and enabled protected audience', function () { + mockBidRequests = { + 'bidderCode': 'unruly', + 'fledgeEnabled': true, + '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', + 'ortb2Imp': { + 'ext': { + 'ae': 1 + } + } + }, + { + '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', + 'ortb2Imp': { + 'ext': { + 'ae': 1 + } + } + } + ] + }; + + 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); + expect(result[0].data.bidderRequest.bids[0].ortb2Imp.ext.ae).to.equal(1); + expect(result[1].data.bidderRequest.bids[0].ortb2Imp.ext.ae).to.equal(1); + }); + it('should return an array with 2 items and enabled protected audience on only one unit', function () { + mockBidRequests = { + 'bidderCode': 'unruly', + 'fledgeEnabled': true, + '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', + 'ortb2Imp': { + 'ext': { + 'ae': 1 + } + } + }, + { + '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', + 'ortb2Imp': { + 'ext': {} + } + } + ] + }; + + 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); + expect(result[0].data.bidderRequest.bids[0].ortb2Imp.ext.ae).to.equal(1); + expect(result[1].data.bidderRequest.bids[0].ortb2Imp.ext.ae).to.be.undefined; + }); + it('disables configured protected audience when fledge is not availble', function () { + mockBidRequests = { + 'bidderCode': 'unruly', + 'fledgeEnabled': false, + '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', + 'ortb2Imp': { + 'ext': { + 'ae': 1 + } + } + } + ] + }; + + 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(1); + expect(result[0].data.bidderRequest.bids[0].ortb2Imp.ext.ae).to.be.undefined; + }); + }); }); describe('interpretResponse', function () { @@ -705,7 +957,167 @@ describe('UnrulyAdapter', function () { renderer: fakeRenderer, mediaType: 'video' } - ]) + ]); + }); + + it('should return object with an array of bids and an array of auction configs when it receives a successful response from server', function () { + let bidId = '27a3ee1626a5c7' + const mockExchangeBid = createOutStreamExchangeBid({adUnitCode: 'video1', requestId: 'mockBidId'}); + const mockExchangeAuctionConfig = {}; + mockExchangeAuctionConfig[bidId] = createOutStreamExchangeAuctionConfig(); + const mockServerResponse = createExchangeResponse(mockExchangeBid, mockExchangeAuctionConfig); + const originalRequest = { + 'data': { + 'bidderRequest': { + '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': bidId, + 'bidderRequestId': '12e00d17dff07b', + } + ] + } + } + }; + + expect(adapter.interpretResponse(mockServerResponse, originalRequest)).to.deep.equal({ + 'bids': [ + { + '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, + 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', + netRevenue: true, + creativeId: 'mockBidId', + ttl: 360, + 'meta': { + 'mediaType': 'video', + 'videoContext': 'outstream' + }, + currency: 'USD', + renderer: fakeRenderer, + mediaType: 'video' + } + ], + 'fledgeAuctionConfigs': [{ + 'bidId': bidId, + 'config': { + 'seller': 'https://nexxen.tech', + 'decisionLogicURL': 'https://nexxen.tech/padecisionlogic', + 'interestGroupBuyers': 'https://mydsp.com', + 'perBuyerSignals': { + 'https://mydsp.com': { + 'floor': 'bouttreefiddy' + } + } + } + }] + }); + }); + + it('should return object with an array of auction configs when it receives a successful response from server without bids', function () { + let bidId = '27a3ee1626a5c7'; + const mockExchangeAuctionConfig = {}; + mockExchangeAuctionConfig[bidId] = createOutStreamExchangeAuctionConfig(); + const mockServerResponse = createExchangeResponse(null, mockExchangeAuctionConfig); + const originalRequest = { + 'data': { + 'bidderRequest': { + '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': bidId, + 'bidderRequestId': '12e00d17dff07b', + } + ] + } + } + }; + + expect(adapter.interpretResponse(mockServerResponse, originalRequest)).to.deep.equal({ + 'bids': [], + 'fledgeAuctionConfigs': [{ + 'bidId': bidId, + 'config': { + 'seller': 'https://nexxen.tech', + 'decisionLogicURL': 'https://nexxen.tech/padecisionlogic', + 'interestGroupBuyers': 'https://mydsp.com', + 'perBuyerSignals': { + 'https://mydsp.com': { + 'floor': 'bouttreefiddy' + } + } + } + }] + }); }); it('should initialize and set the renderer', function () { @@ -875,7 +1287,7 @@ describe('UnrulyAdapter', function () { it('should return correct response for multiple bids', function () { const outStreamServerResponse = createOutStreamExchangeBid({adUnitCode: 'video1', requestId: 'mockBidId'}); - const mockServerResponse = createExchangeResponse(outStreamServerResponse, inStreamServerResponse, bannerServerResponse); + const mockServerResponse = createExchangeResponse([outStreamServerResponse, inStreamServerResponse, bannerServerResponse]); const expectedOutStreamResponse = outStreamServerResponse; expectedOutStreamResponse.mediaType = 'video'; @@ -890,7 +1302,7 @@ describe('UnrulyAdapter', function () { it('should return only valid bids', function () { const {ad, ...bannerServerResponseNoAd} = bannerServerResponse; - const mockServerResponse = createExchangeResponse(bannerServerResponseNoAd, inStreamServerResponse); + const mockServerResponse = createExchangeResponse([bannerServerResponseNoAd, inStreamServerResponse]); const expectedInStreamResponse = inStreamServerResponse; expectedInStreamResponse.mediaType = 'video'; From a9b62f88fa3b4490429d5cefa492f86d29613652 Mon Sep 17 00:00:00 2001 From: relaido <63339139+relaido@users.noreply.github.com> Date: Wed, 29 Nov 2023 22:46:39 +0900 Subject: [PATCH 024/246] Relaido Bid Adapter : add userIdAsEids and canonical_url to request (#10762) * add relaido adapter * remove event listener * fixed UserSyncs and e.data * fix conflicts * add response placementID * add bidrequest some param --------- Co-authored-by: ishigami_shingo Co-authored-by: cmertv-sishigami Co-authored-by: t_bun Co-authored-by: n.maeura --- modules/relaidoBidAdapter.js | 5 ++++- test/spec/modules/relaidoBidAdapter_spec.js | 21 +++++++++++++++++++++ 2 files changed, 25 insertions(+), 1 deletion(-) diff --git a/modules/relaidoBidAdapter.js b/modules/relaidoBidAdapter.js index 1e702d812f0..751e8fa442c 100644 --- a/modules/relaidoBidAdapter.js +++ b/modules/relaidoBidAdapter.js @@ -104,7 +104,8 @@ function buildRequests(validBidRequests, bidderRequest) { width: width, height: height, banner_sizes: getBannerSizes(bidRequest), - media_type: mediaType + media_type: mediaType, + userIdAsEids: bidRequest.userIdAsEids || {}, }); } @@ -117,6 +118,7 @@ function buildRequests(validBidRequests, bidderRequest) { uuid: getUuid(), pv: '$prebid.version$', imuid: imuid, + canonical_url: bidderRequest.refererInfo?.canonicalUrl || null, canonical_url_hash: getCanonicalUrlHash(bidderRequest.refererInfo), ref: bidderRequest.refererInfo.page }); @@ -142,6 +144,7 @@ function interpretResponse(serverResponse, bidRequest) { const playerUrl = res.playerUrl || bidRequest.player || body.playerUrl; let bidResponse = { requestId: res.bidId, + placementId: res.placementId, width: res.width, height: res.height, cpm: res.price, diff --git a/test/spec/modules/relaidoBidAdapter_spec.js b/test/spec/modules/relaidoBidAdapter_spec.js index 7778e9cbf80..f0d019913e8 100644 --- a/test/spec/modules/relaidoBidAdapter_spec.js +++ b/test/spec/modules/relaidoBidAdapter_spec.js @@ -239,6 +239,7 @@ describe('RelaidoAdapter', function () { const request = data.bids[0]; expect(bidRequests.method).to.equal('POST'); expect(bidRequests.url).to.equal('https://api.relaido.jp/bid/v1/sprebid'); + expect(data.canonical_url).to.equal('https://publisher.com/home'); expect(data.canonical_url_hash).to.equal('e6092f44a0044903ae3764126eedd6187c1d9f04'); expect(data.ref).to.equal(bidderRequest.refererInfo.page); expect(data.timeout_ms).to.equal(bidderRequest.timeout); @@ -317,6 +318,23 @@ describe('RelaidoAdapter', function () { expect(data.bids).to.have.lengthOf(1); expect(data.imuid).to.equal('i.tjHcK_7fTcqnbrS_YA2vaw'); }); + + it('should get userIdAsEids', function () { + const userIdAsEids = [ + { + source: 'hogehoge.com', + uids: { + atype: 1, + id: 'hugahuga' + } + } + ] + bidRequest.userIdAsEids = userIdAsEids + const bidRequests = spec.buildRequests([bidRequest], bidderRequest); + const data = JSON.parse(bidRequests.data); + expect(data.bids[0].userIdAsEids).to.have.lengthOf(1); + expect(data.bids[0].userIdAsEids[0].source).to.equal('hogehoge.com'); + }); }); describe('spec.interpretResponse', function () { @@ -325,6 +343,7 @@ describe('RelaidoAdapter', function () { expect(bidResponses).to.have.lengthOf(1); const response = bidResponses[0]; expect(response.requestId).to.equal(serverRequest.data.bids[0].bidId); + expect(response.placementId).to.equal(serverResponse.body.ads[0].placementId); expect(response.width).to.equal(serverRequest.data.bids[0].width); expect(response.height).to.equal(serverRequest.data.bids[0].height); expect(response.cpm).to.equal(serverResponse.body.ads[0].price); @@ -343,6 +362,7 @@ describe('RelaidoAdapter', function () { expect(bidResponses).to.have.lengthOf(1); const response = bidResponses[0]; expect(response.requestId).to.equal(serverRequest.data.bids[0].bidId); + expect(response.placementId).to.equal(serverResponse.body.ads[0].placementId); expect(response.width).to.equal(serverRequest.data.bids[0].width); expect(response.height).to.equal(serverRequest.data.bids[0].height); expect(response.cpm).to.equal(serverResponse.body.ads[0].price); @@ -360,6 +380,7 @@ describe('RelaidoAdapter', function () { expect(bidResponses).to.have.lengthOf(1); const response = bidResponses[0]; expect(response.requestId).to.equal(serverRequest.data.bids[0].bidId); + expect(response.placementId).to.equal(serverResponseBanner.body.ads[0].placementId); expect(response.cpm).to.equal(serverResponseBanner.body.ads[0].price); expect(response.currency).to.equal(serverResponseBanner.body.ads[0].currency); expect(response.creativeId).to.equal(serverResponseBanner.body.ads[0].creativeId); From b154fc16286b336f3e55bb4b325ed4d6876b7cd2 Mon Sep 17 00:00:00 2001 From: Olivier Date: Wed, 29 Nov 2023 19:38:24 +0100 Subject: [PATCH 025/246] Adagio Bid Adapter: add GPID support (#10775) --- modules/adagioBidAdapter.js | 5 ++++ test/spec/modules/adagioBidAdapter_spec.js | 28 ++++++++++++++++++++++ 2 files changed, 33 insertions(+) diff --git a/modules/adagioBidAdapter.js b/modules/adagioBidAdapter.js index 90f62ba4694..1f3d6040d34 100644 --- a/modules/adagioBidAdapter.js +++ b/modules/adagioBidAdapter.js @@ -1115,6 +1115,11 @@ export const spec = { _buildVideoBidRequest(bidRequest); } + const gpid = deepAccess(bidRequest, 'ortb2Imp.ext.gpid') || deepAccess(bidRequest, 'ortb2Imp.ext.data.pbadslot'); + if (gpid) { + bidRequest.gpid = gpid; + } + storeRequestInAdagioNS(bidRequest); // Remove these fields at the very end, so we can still use them before. diff --git a/test/spec/modules/adagioBidAdapter_spec.js b/test/spec/modules/adagioBidAdapter_spec.js index 1f734a6a7fc..7f0b31b9680 100644 --- a/test/spec/modules/adagioBidAdapter_spec.js +++ b/test/spec/modules/adagioBidAdapter_spec.js @@ -958,6 +958,34 @@ describe('Adagio bid adapter', () => { expect(requests[0].data.usIfr).to.equal(false); }); }); + + describe('with GPID', function () { + const gpid = '/12345/my-gpt-tag-0'; + + it('should add preferred gpid to the request', function () { + const bid01 = new BidRequestBuilder().withParams().build(); + bid01.ortb2Imp = { + ext: { + gpid: gpid + } + }; + const bidderRequest = new BidderRequestBuilder().build(); + const requests = spec.buildRequests([bid01], bidderRequest); + expect(requests[0].data.adUnits[0].gpid).to.exist.and.equal(gpid); + }); + + it('should add backup gpid to the request', function () { + const bid01 = new BidRequestBuilder().withParams().build(); + bid01.ortb2Imp = { + ext: { + data: { pbadslot: gpid } + } + }; + const bidderRequest = new BidderRequestBuilder().build(); + const requests = spec.buildRequests([bid01], bidderRequest); + expect(requests[0].data.adUnits[0].gpid).to.exist.and.equal(gpid); + }); + }); }); describe('interpretResponse()', function() { From 73fb8d3a160d79b2e24707d4ecbf047afb13a3ca Mon Sep 17 00:00:00 2001 From: Demetrio Girardi Date: Wed, 29 Nov 2023 15:00:40 -0800 Subject: [PATCH 026/246] Core: add option to turn off `Sec-Browsing-Topics` header (#10776) --- src/adapters/bidderFactory.js | 2 +- test/spec/unit/core/bidderFactory_spec.js | 112 ++++++++++++++-------- 2 files changed, 72 insertions(+), 42 deletions(-) diff --git a/src/adapters/bidderFactory.js b/src/adapters/bidderFactory.js index 8628f2c60a0..44775b4e241 100644 --- a/src/adapters/bidderFactory.js +++ b/src/adapters/bidderFactory.js @@ -457,7 +457,7 @@ export const processBidderRequests = hook('sync', function (spec, bids, bidderRe return Object.assign(defaults, ro, { browsingTopics: ro?.hasOwnProperty('browsingTopics') && !ro.browsingTopics ? false - : isActivityAllowed(ACTIVITY_TRANSMIT_UFPD, activityParams(MODULE_TYPE_BIDDER, spec.code)) + : (bidderSettings.get(spec.code, 'topicsHeader') ?? true) && isActivityAllowed(ACTIVITY_TRANSMIT_UFPD, activityParams(MODULE_TYPE_BIDDER, spec.code)) }) } switch (request.method) { diff --git a/test/spec/unit/core/bidderFactory_spec.js b/test/spec/unit/core/bidderFactory_spec.js index f089059b65a..9dcdb627698 100644 --- a/test/spec/unit/core/bidderFactory_spec.js +++ b/test/spec/unit/core/bidderFactory_spec.js @@ -422,7 +422,15 @@ describe('bidderFactory', () => { }); describe('browsingTopics ajax option', () => { - let transmitUfpdAllowed, bidder; + let transmitUfpdAllowed, bidder, origBS; + before(() => { + origBS = window.$$PREBID_GLOBAL$$.bidderSettings; + }) + + after(() => { + window.$$PREBID_GLOBAL$$.bidderSettings = origBS; + }); + beforeEach(() => { activityRules.isActivityAllowed.reset(); activityRules.isActivityAllowed.callsFake((activity) => activity === ACTIVITY_TRANSMIT_UFPD ? transmitUfpdAllowed : true); @@ -448,49 +456,71 @@ describe('bidderFactory', () => { }); Object.entries({ - 'allowed': true, - 'not allowed': false - }).forEach(([t, allow]) => { - it(`should be set to ${allow} when transmitUfpd is ${t}`, () => { - transmitUfpdAllowed = allow; - spec.buildRequests.returns([ - { - method: 'GET', - url: '1', - }, - { - method: 'POST', - url: '2', - data: {} - }, - { - method: 'GET', - url: '3', - options: { - browsingTopics: true - } - }, - { - method: 'POST', - url: '4', - data: {}, - options: { - browsingTopics: true + 'omitted': [undefined, true], + 'enabled': [true, true], + 'disabled': [false, false] + }).forEach(([t, [topicsHeader, enabled]]) => { + describe(`when bidderSettings.topicsHeader is ${t}`, () => { + beforeEach(() => { + window.$$PREBID_GLOBAL$$.bidderSettings = { + [CODE]: { + topicsHeader: topicsHeader } } - ]); - bidder.callBids(MOCK_BIDS_REQUEST, addBidResponseStub, doneStub, ajaxStub, onTimelyResponseStub, wrappedCallback); - ['1', '2', '3', '4'].forEach(url => { - sinon.assert.calledWith( - ajaxStub, - url, - sinon.match.any, - sinon.match.any, - sinon.match({browsingTopics: allow}) - ); }); - }); - }); + + afterEach(() => { + delete window.$$PREBID_GLOBAL$$.bidderSettings[CODE]; + }); + + Object.entries({ + 'allowed': true, + 'not allowed': false + }).forEach(([t, allow]) => { + const shouldBeSet = allow && enabled; + + it(`should be set to ${shouldBeSet} when transmitUfpd is ${t}`, () => { + transmitUfpdAllowed = allow; + spec.buildRequests.returns([ + { + method: 'GET', + url: '1', + }, + { + method: 'POST', + url: '2', + data: {} + }, + { + method: 'GET', + url: '3', + options: { + browsingTopics: true + } + }, + { + method: 'POST', + url: '4', + data: {}, + options: { + browsingTopics: true + } + } + ]); + bidder.callBids(MOCK_BIDS_REQUEST, addBidResponseStub, doneStub, ajaxStub, onTimelyResponseStub, wrappedCallback); + ['1', '2', '3', '4'].forEach(url => { + sinon.assert.calledWith( + ajaxStub, + url, + sinon.match.any, + sinon.match.any, + sinon.match({browsingTopics: shouldBeSet}) + ); + }); + }); + }); + }) + }) }); it('should not add bids for each placement code if no requests are given', function () { From d70097e6671a1fa44449b22992bb8cf35d6252d8 Mon Sep 17 00:00:00 2001 From: Petre Damoc Date: Thu, 30 Nov 2023 13:22:53 +0200 Subject: [PATCH 027/246] Missena Bid Adapter: add capping support (#10746) * Missena Bid Adapter: add capping support * Switch to safeJSONParse * Update tests to cover capping --- modules/missenaBidAdapter.js | 16 +++++- test/spec/modules/missenaBidAdapter_spec.js | 59 +++++++++++++++------ 2 files changed, 57 insertions(+), 18 deletions(-) diff --git a/modules/missenaBidAdapter.js b/modules/missenaBidAdapter.js index 1ab6d0c6945..a06ba1ce29c 100644 --- a/modules/missenaBidAdapter.js +++ b/modules/missenaBidAdapter.js @@ -3,17 +3,21 @@ import { formatQS, isFn, logInfo, + safeJSONParse, triggerPixel, } from '../src/utils.js'; import { config } from '../src/config.js'; import { BANNER } from '../src/mediaTypes.js'; import { registerBidder } from '../src/adapters/bidderFactory.js'; +import { getStorageManager } from '../src/storageManager.js'; const BIDDER_CODE = 'missena'; const ENDPOINT_URL = 'https://bid.missena.io/'; const EVENTS_DOMAIN = 'events.missena.io'; const EVENTS_DOMAIN_DEV = 'events.staging.missena.xyz'; +export const storage = getStorageManager({ bidderCode: BIDDER_CODE }); + /* Get Floor price information */ function getFloor(bidRequest) { if (!isFn(bidRequest.getFloor)) { @@ -22,7 +26,7 @@ function getFloor(bidRequest) { const bidFloors = bidRequest.getFloor({ currency: 'USD', - mediaType: BANNER + mediaType: BANNER, }); if (!isNaN(bidFloors.floor)) { @@ -53,6 +57,16 @@ export const spec = { * @return ServerRequest Info describing the request to the server. */ buildRequests: function (validBidRequests, bidderRequest) { + const capKey = `missena.missena.capper.remove-bubble.${validBidRequests[0]?.params.apiKey}`; + const capping = safeJSONParse(storage.getDataFromLocalStorage(capKey)); + if ( + typeof capping?.expiry === 'number' && + new Date().getTime() < capping?.expiry + ) { + logInfo('Missena - Capped'); + return []; + } + return validBidRequests.map((bidRequest) => { const payload = { adunit: bidRequest.adUnitCode, diff --git a/test/spec/modules/missenaBidAdapter_spec.js b/test/spec/modules/missenaBidAdapter_spec.js index e874aabbc70..15f9eff89e7 100644 --- a/test/spec/modules/missenaBidAdapter_spec.js +++ b/test/spec/modules/missenaBidAdapter_spec.js @@ -1,13 +1,15 @@ import { expect } from 'chai'; -import { spec, _getPlatform } from 'modules/missenaBidAdapter.js'; -import { newBidder } from 'src/adapters/bidderFactory.js'; +import { spec, storage } from 'modules/missenaBidAdapter.js'; import { BANNER } from '../../../src/mediaTypes.js'; describe('Missena Adapter', function () { - const adapter = newBidder(spec); + $$PREBID_GLOBAL$$.bidderSettings = { + missena: { + storageAllowed: true, + }, + }; const bidId = 'abc'; - const bid = { bidder: 'missena', bidId: bidId, @@ -40,7 +42,20 @@ describe('Missena Adapter', function () { formats: ['sticky-banner'], }, }; + const consentString = 'AAAAAAAAA=='; + + const bidderRequest = { + gdprConsent: { + consentString: consentString, + gdprApplies: true, + }, + refererInfo: { + topmostLocation: 'https://referer', + canonicalUrl: 'https://canonical', + }, + }; + const bids = [bid, bidWithoutFloor]; describe('codes', function () { it('should return a bidder code of missena', function () { expect(spec.code).to.equal('missena'); @@ -66,20 +81,12 @@ describe('Missena Adapter', function () { }); describe('buildRequests', function () { - const consentString = 'AAAAAAAAA=='; - - const bidderRequest = { - gdprConsent: { - consentString: consentString, - gdprApplies: true, - }, - refererInfo: { - topmostLocation: 'https://referer', - canonicalUrl: 'https://canonical', - }, - }; + let getDataFromLocalStorageStub = sinon.stub( + storage, + 'getDataFromLocalStorage', + ); - const requests = spec.buildRequests([bid, bidWithoutFloor], bidderRequest); + const requests = spec.buildRequests(bids, bidderRequest); const request = requests[0]; const payload = JSON.parse(request.data); const payloadNoFloor = JSON.parse(requests[1].data); @@ -121,6 +128,24 @@ describe('Missena Adapter', function () { expect(payloadNoFloor.floor).to.equal(undefined); expect(payloadNoFloor.floor_currency).to.equal(undefined); }); + + getDataFromLocalStorageStub.restore(); + getDataFromLocalStorageStub = sinon.stub( + storage, + 'getDataFromLocalStorage', + ); + const localStorageData = { + [`missena.missena.capper.remove-bubble.${bid.params.apiKey}`]: + JSON.stringify({ + expiry: new Date().getTime() + 600_000, // 10 min into the future + }), + }; + getDataFromLocalStorageStub.callsFake((key) => localStorageData[key]); + const cappedRequests = spec.buildRequests(bids, bidderRequest); + + it('should not participate if capped', function () { + expect(cappedRequests.length).to.equal(0); + }); }); describe('interpretResponse', function () { From 7974ebc3ce6dd44bf64ae9fd6eaaab903b82f78c Mon Sep 17 00:00:00 2001 From: Antonios Sarhanis Date: Thu, 30 Nov 2023 22:37:26 +1100 Subject: [PATCH 028/246] Adnuntius Bid Adaptor: Handle voidAuIds from server response (#10646) - invalid ad units are put into a voidAuIds array from the ad server. - this stores those ad units into local storage and prevents calling to those ad units. - also handles any values from metaData from the adserver response. --- modules/adnuntiusBidAdapter.js | 222 ++++++++++++++---- test/spec/modules/adnuntiusBidAdapter_spec.js | 119 +++++++++- 2 files changed, 282 insertions(+), 59 deletions(-) diff --git a/modules/adnuntiusBidAdapter.js b/modules/adnuntiusBidAdapter.js index a2b695e55e0..b2653f16daf 100644 --- a/modules/adnuntiusBidAdapter.js +++ b/modules/adnuntiusBidAdapter.js @@ -15,39 +15,146 @@ const GVLID = 855; const DEFAULT_VAST_VERSION = 'vast4' const MAXIMUM_DEALS_LIMIT = 5; const VALID_BID_TYPES = ['netBid', 'grossBid']; +const META_DATA_KEY = 'adn.metaData'; -const checkSegment = function (segment) { - if (isStr(segment)) return segment; - if (segment.id) return segment.id -} +export const misc = { + getUnixTimestamp: function(addDays, asMinutes) { + const multiplication = addDays / (asMinutes ? 1440 : 1); + return Date.now() + (addDays && addDays > 0 ? (1000 * 60 * 60 * 24 * multiplication) : 0); + } +}; + +const storageTool = (function() { + const storage = getStorageManager({bidderCode: BIDDER_CODE}); + let metaInternal; + + const getMetaInternal = function() { + if (!storage.localStorageIsEnabled()) { + return {}; + } + + let parsedJson; + try { + parsedJson = JSON.parse(storage.getDataFromLocalStorage(META_DATA_KEY)); + } catch (e) { + return {}; + } + + let filteredEntries = parsedJson ? parsedJson.filter((datum) => { + if (datum.key === 'voidAuIds' && Array.isArray(datum.value)) { + return true; + } + return datum.key && datum.value && datum.exp && datum.exp > misc.getUnixTimestamp(); + }) : []; + const voidAuIdsEntry = filteredEntries.find(entry => entry.key === 'voidAuIds'); + if (voidAuIdsEntry) { + const now = misc.getUnixTimestamp(); + voidAuIdsEntry.value = voidAuIdsEntry.value.filter(voidAuId => voidAuId.auId && voidAuId.exp > now); + if (!voidAuIdsEntry.value.length) { + filteredEntries = filteredEntries.filter(entry => entry.key !== 'voidAuIds'); + } + } + return filteredEntries; + }; + + const setMetaInternal = function(apiResponse) { + if (!storage.localStorageIsEnabled()) { + return; + } + + const updateVoidAuIds = function(currentVoidAuIds, auIdsAsString) { + const newAuIds = auIdsAsString ? auIdsAsString.split(';') : []; + const notNewExistingAuIds = currentVoidAuIds.filter(auIdObj => { + return newAuIds.indexOf(auIdObj.value) < -1; + }) || []; + const oneDayFromNow = misc.getUnixTimestamp(1); + const apiIdsArray = newAuIds.map(auId => { + return {exp: oneDayFromNow, auId: auId}; + }) || []; + return notNewExistingAuIds.concat(apiIdsArray) || []; + } -const getSegmentsFromOrtb = function (ortb2) { - const userData = deepAccess(ortb2, 'user.data'); - let segments = []; - if (userData) { - userData.forEach(userdat => { - if (userdat.segment) { - segments.push(...userdat.segment.filter(checkSegment).map(checkSegment)); + const metaAsObj = getMetaInternal().reduce((a, entry) => ({...a, [entry.key]: {value: entry.value, exp: entry.exp}}), {}); + for (const key in apiResponse) { + if (key !== 'voidAuIds') { + metaAsObj[key] = { + value: apiResponse[key], + exp: misc.getUnixTimestamp(100) + } + } + } + const currentAuIds = updateVoidAuIds(metaAsObj.voidAuIds || [], apiResponse.voidAuIds || []); + if (currentAuIds.length > 0) { + metaAsObj.voidAuIds = {value: currentAuIds}; + } + const metaDataForSaving = Object.entries(metaAsObj).map((entrySet) => { + if (entrySet[0] === 'voidAuIds') { + return { + key: entrySet[0], + value: entrySet[1].value + }; + } + return { + key: entrySet[0], + value: entrySet[1].value, + exp: entrySet[1].exp } }); + storage.setDataInLocalStorage(META_DATA_KEY, JSON.stringify(metaDataForSaving)); + }; + + const getUsi = function(meta, ortb2) { + let usi = (meta && meta.usi) ? meta.usi : false; + if (ortb2 && ortb2.user && ortb2.user.id) { + usi = ortb2.user.id + } + return usi; } - return segments -} -const handleMeta = function () { - const storage = getStorageManager({ bidderCode: BIDDER_CODE }) - let adnMeta = null - if (storage.localStorageIsEnabled()) { - adnMeta = JSON.parse(storage.getDataFromLocalStorage('adn.metaData')) + const getSegmentsFromOrtb = function (ortb2) { + const userData = deepAccess(ortb2, 'user.data'); + let segments = []; + if (userData) { + userData.forEach(userdat => { + if (userdat.segment) { + segments.push(...userdat.segment.map((segment) => { + if (isStr(segment)) return segment; + if (isStr(segment.id)) return segment.id; + }).filter((seg) => !!seg)); + } + }); + } + return segments } - return (adnMeta !== null) ? adnMeta.reduce((acc, cur) => { return { ...acc, [cur.key]: cur.value } }, {}) : {} -} -const getUsi = function (meta, ortb2, bidderRequest) { - let usi = (meta !== null && meta.usi) ? meta.usi : false; - if (ortb2 && ortb2.user && ortb2.user.id) { usi = ortb2.user.id } - return usi -} + return { + refreshStorage: function(bidderRequest) { + const ortb2 = bidderRequest.ortb2 || {}; + metaInternal = getMetaInternal().reduce((a, entry) => ({...a, [entry.key]: entry.value}), {}); + metaInternal.usi = getUsi(metaInternal, ortb2); + if (!metaInternal.usi) { + delete metaInternal.usi; + } + if (metaInternal.voidAuIds) { + metaInternal.voidAuIdsArray = metaInternal.voidAuIds.map((voidAuId) => { + return voidAuId.auId; + }); + } + metaInternal.segments = getSegmentsFromOrtb(ortb2); + }, + saveToStorage: function(serverData) { + setMetaInternal(serverData); + }, + getUrlRelatedData: function() { + const {segments, usi, voidAuIdsArray} = metaInternal; + return {segments, usi, voidAuIdsArray}; + }, + getPayloadRelatedData: function() { + const {segments, usi, userId, voidAuIdsArray, voidAuIds, ...payloadRelatedData} = metaInternal; + return payloadRelatedData; + } + }; +})(); const validateBidType = function(bidTypeOption) { return VALID_BID_TYPES.indexOf(bidTypeOption || '') > -1 ? bidTypeOption : 'bid'; @@ -67,34 +174,34 @@ export const spec = { }, buildRequests: function (validBidRequests, bidderRequest) { - const networks = {}; - const bidRequests = {}; - const requests = []; - const request = []; - const ortb2 = bidderRequest.ortb2 || {}; - const bidderConfig = config.getConfig(); + const queryParamsAndValues = []; + queryParamsAndValues.push('tzo=' + new Date().getTimezoneOffset()) + queryParamsAndValues.push('format=json') - const adnMeta = handleMeta() - const usi = getUsi(adnMeta, ortb2, bidderRequest) - const segments = getSegmentsFromOrtb(ortb2); - const tzo = new Date().getTimezoneOffset(); const gdprApplies = deepAccess(bidderRequest, 'gdprConsent.gdprApplies'); const consentString = deepAccess(bidderRequest, 'gdprConsent.consentString'); + if (gdprApplies !== undefined) queryParamsAndValues.push('consentString=' + consentString); - request.push('tzo=' + tzo) - request.push('format=json') + storageTool.refreshStorage(bidderRequest); - if (gdprApplies !== undefined) request.push('consentString=' + consentString); - if (segments.length > 0) request.push('segments=' + segments.join(',')); - if (usi) request.push('userId=' + usi); - if (bidderConfig.useCookie === false) request.push('noCookies=true'); - if (bidderConfig.maxDeals > 0) request.push('ds=' + Math.min(bidderConfig.maxDeals, MAXIMUM_DEALS_LIMIT)); + const urlRelatedMetaData = storageTool.getUrlRelatedData(); + if (urlRelatedMetaData.segments.length > 0) queryParamsAndValues.push('segments=' + urlRelatedMetaData.segments.join(',')); + if (urlRelatedMetaData.usi) queryParamsAndValues.push('userId=' + urlRelatedMetaData.usi); + + const bidderConfig = config.getConfig(); + if (bidderConfig.useCookie === false) queryParamsAndValues.push('noCookies=true'); + if (bidderConfig.maxDeals > 0) queryParamsAndValues.push('ds=' + Math.min(bidderConfig.maxDeals, MAXIMUM_DEALS_LIMIT)); + + const bidRequests = {}; + const networks = {}; for (let i = 0; i < validBidRequests.length; i++) { - const bid = validBidRequests[i] - let network = bid.params.network || 'network'; - const maxDeals = Math.max(0, Math.min(bid.params.maxDeals || 0, MAXIMUM_DEALS_LIMIT)); - const targeting = bid.params.targeting || {}; + const bid = validBidRequests[i]; + if ((urlRelatedMetaData.voidAuIdsArray && (urlRelatedMetaData.voidAuIdsArray.indexOf(bid.params.auId) > -1 || urlRelatedMetaData.voidAuIdsArray.indexOf(bid.params.auId.padStart(16, '0')) > -1))) { + // This auId is void. Do NOT waste time and energy sending a request to the server + continue; + } + let network = bid.params.network || 'network'; if (bid.mediaTypes && bid.mediaTypes.video && bid.mediaTypes.video.context !== 'outstream') { network += '_video' } @@ -105,21 +212,31 @@ export const spec = { networks[network] = networks[network] || {}; networks[network].adUnits = networks[network].adUnits || []; if (bidderRequest && bidderRequest.refererInfo) networks[network].context = bidderRequest.refererInfo.page; - if (adnMeta) networks[network].metaData = adnMeta; - const adUnit = { ...targeting, auId: bid.params.auId, targetId: bid.bidId, maxDeals: maxDeals } + + const payloadRelatedData = storageTool.getPayloadRelatedData(); + if (Object.keys(payloadRelatedData).length > 0) { + networks[network].metaData = payloadRelatedData; + } + + const targeting = bid.params.targeting || {}; + const adUnit = { ...targeting, auId: bid.params.auId, targetId: bid.params.targetId || bid.bidId }; + const maxDeals = Math.max(0, Math.min(bid.params.maxDeals || 0, MAXIMUM_DEALS_LIMIT)); + if (maxDeals > 0) { + adUnit.maxDeals = maxDeals; + } if (bid.mediaTypes && bid.mediaTypes.banner && bid.mediaTypes.banner.sizes) adUnit.dimensions = bid.mediaTypes.banner.sizes networks[network].adUnits.push(adUnit); } + const requests = []; const networkKeys = Object.keys(networks) for (let j = 0; j < networkKeys.length; j++) { const network = networkKeys[j]; - const networkRequest = [...request] - if (network.indexOf('_video') > -1) { networkRequest.push('tt=' + DEFAULT_VAST_VERSION) } + if (network.indexOf('_video') > -1) { queryParamsAndValues.push('tt=' + DEFAULT_VAST_VERSION) } const requestURL = gdprApplies ? ENDPOINT_URL_EUROPE : ENDPOINT_URL requests.push({ method: 'POST', - url: requestURL + '?' + networkRequest.join('&'), + url: requestURL + '?' + queryParamsAndValues.join('&'), data: JSON.stringify(networks[network]), bid: bidRequests[network] }); @@ -129,6 +246,9 @@ export const spec = { }, interpretResponse: function (serverResponse, bidRequest) { + if (serverResponse.body.metaData) { + storageTool.saveToStorage(serverResponse.body.metaData); + } const adUnits = serverResponse.body.adUnits; let validatedBidType = validateBidType(config.getConfig().bidType); diff --git a/test/spec/modules/adnuntiusBidAdapter_spec.js b/test/spec/modules/adnuntiusBidAdapter_spec.js index 6a77c9205ca..8fa719f7af6 100644 --- a/test/spec/modules/adnuntiusBidAdapter_spec.js +++ b/test/spec/modules/adnuntiusBidAdapter_spec.js @@ -10,9 +10,10 @@ import {getGlobal} from '../../../src/prebidGlobal'; describe('adnuntiusBidAdapter', function() { const URL = 'https://ads.adnuntius.delivery/i?tzo='; const EURO_URL = 'https://europe.delivery.adnuntius.com/i?tzo='; - const GVLID = 855; const usi = utils.generateUUID() - const meta = [{key: 'usi', value: usi}] + + const meta = [{key: 'valueless'}, {value: 'keyless'}, {key: 'voidAuIds'}, {key: 'voidAuIds', value: [{auId: '11118b6bc', exp: misc.getUnixTimestamp()}, {exp: misc.getUnixTimestamp(1)}]}, {key: 'valid', value: 'also-valid', exp: misc.getUnixTimestamp(1)}, {key: 'expired', value: 'fwefew', exp: misc.getUnixTimestamp()}, {key: 'usi', value: 'should be skipped because timestamp', exp: misc.getUnixTimestamp()}, {key: 'usi', value: usi, exp: misc.getUnixTimestamp(100)}, {key: 'usi', value: 'should be skipped because timestamp', exp: misc.getUnixTimestamp()}] + let storage; before(() => { getGlobal().bidderSettings = { @@ -20,8 +21,11 @@ describe('adnuntiusBidAdapter', function() { storageAllowed: true } }; - const storage = getStorageManager({bidderCode: 'adnuntius'}) - storage.setDataInLocalStorage('adn.metaData', JSON.stringify(meta)) + storage = getStorageManager({bidderCode: 'adnuntius'}); + }); + + beforeEach(() => { + storage.setDataInLocalStorage('adn.metaData', JSON.stringify(meta)); }); after(() => { @@ -47,6 +51,7 @@ describe('adnuntiusBidAdapter', function() { bidder: 'adnuntius', params: { auId: '000000000008b6bc', + targetId: '123', network: 'adnuntius', maxDeals: 1 }, @@ -459,7 +464,78 @@ 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":"000000000008b6bc","targetId":"adn-000000000008b6bc","maxDeals":1,"dimensions":[[640,480],[600,400]]},{"auId":"0000000000000551","targetId":"adn-0000000000000551","maxDeals":0,"dimensions":[[1640,1480],[1600,1400]]}],"metaData":{"usi":"' + usi + '"}}'); + expect(request[0].data).to.equal('{"adUnits":[{"auId":"000000000008b6bc","targetId":"123","maxDeals":1,"dimensions":[[640,480],[600,400]]},{"auId":"0000000000000551","targetId":"adn-0000000000000551","dimensions":[[1640,1480],[1600,1400]]}],"metaData":{"valid":"also-valid"}}'); + }); + + it('Test requests with no local storage', function() { + storage.setDataInLocalStorage('adn.metaData', JSON.stringify([{}])); + const request = spec.buildRequests(bidderRequests, {}); + expect(request.length).to.equal(1); + expect(request[0]).to.have.property('bid'); + const bid = request[0].bid[0] + expect(bid).to.have.property('bidId'); + expect(request[0]).to.have.property('url'); + expect(request[0].url).to.equal(ENDPOINT_URL_BASE); + expect(request[0]).to.have.property('data'); + expect(request[0].data).to.equal('{"adUnits":[{"auId":"000000000008b6bc","targetId":"123","maxDeals":1,"dimensions":[[640,480],[600,400]]},{"auId":"0000000000000551","targetId":"adn-0000000000000551","dimensions":[[1640,1480],[1600,1400]]}]}'); + + localStorage.removeItem('adn.metaData'); + const request2 = spec.buildRequests(bidderRequests, {}); + expect(request2.length).to.equal(1); + expect(request2[0]).to.have.property('url'); + expect(request2[0].url).to.equal(ENDPOINT_URL_BASE); + }); + + it('Test request changes for voided au ids', function() { + storage.setDataInLocalStorage('adn.metaData', JSON.stringify([{key: 'voidAuIds', value: [{auId: '11118b6bc', exp: misc.getUnixTimestamp(1)}, {auId: '0000000000000023', exp: misc.getUnixTimestamp(1)}]}])); + const bRequests = bidderRequests.concat([{ + bidId: 'adn-11118b6bc', + bidder: 'adnuntius', + params: { + auId: '11118b6bc', + network: 'adnuntius', + }, + mediaTypes: { + banner: { + sizes: [[1640, 1480], [1600, 1400]], + } + }, + }]); + bRequests.push({ + bidId: 'adn-23', + bidder: 'adnuntius', + params: { + auId: '23', + network: 'adnuntius', + }, + mediaTypes: { + banner: { + sizes: [[1640, 1480], [1600, 1400]], + } + }, + }); + bRequests.push({ + bidId: 'adn-13', + bidder: 'adnuntius', + params: { + auId: '13', + network: 'adnuntius', + }, + mediaTypes: { + banner: { + sizes: [[164, 140], [10, 1400]], + } + }, + }); + const request = spec.buildRequests(bRequests, {}); + expect(request.length).to.equal(1); + expect(request[0]).to.have.property('bid'); + const bid = request[0].bid[0] + expect(bid).to.have.property('bidId'); + expect(request[0]).to.have.property('url'); + expect(request[0].url).to.equal(ENDPOINT_URL_BASE); + expect(request[0]).to.have.property('data'); + expect(request[0].data).to.equal('{"adUnits":[{"auId":"000000000008b6bc","targetId":"123","maxDeals":1,"dimensions":[[640,480],[600,400]]},{"auId":"0000000000000551","targetId":"adn-0000000000000551","dimensions":[[1640,1480],[1600,1400]]},{"auId":"13","targetId":"adn-13","dimensions":[[164,140],[10,1400]]}]}'); }); it('Test Video requests', function() { @@ -477,7 +553,7 @@ describe('adnuntiusBidAdapter', function() { user: { data: [{ name: 'adnuntius', - segment: [{id: 'segment1'}, {id: 'segment2'}] + segment: [{id: 'segment1'}, {id: 'segment2'}, {invalidSegment: 'invalid'}, {id: 123}, {id: ['3332']}] }, { name: 'other', @@ -660,7 +736,7 @@ describe('adnuntiusBidAdapter', function() { expect(bidderRequests[0].params.maxDeals).to.equal(1); expect(data.adUnits[0].maxDeals).to.equal(bidderRequests[0].params.maxDeals); expect(bidderRequests[1].params).to.not.have.property('maxBids'); - expect(data.adUnits[1].maxDeals).to.equal(0); + expect(data.adUnits[1].maxDeals).to.equal(undefined); }); it('Should allow a maximum of 5 deals.', function() { config.setBidderConfig({ @@ -706,7 +782,7 @@ describe('adnuntiusBidAdapter', function() { expect(request[0]).to.have.property('data'); const data = JSON.parse(request[0].data); expect(data.adUnits.length).to.equal(1); - expect(data.adUnits[0].maxDeals).to.equal(0); + expect(data.adUnits[0].maxDeals).to.equal(undefined); }); it('Should set max deals using bidder config.', function() { config.setBidderConfig({ @@ -794,6 +870,33 @@ describe('adnuntiusBidAdapter', function() { expect(interpretedResponse[1].ttl).to.equal(360); expect(interpretedResponse[1].dealId).to.equal('not-in-deal-array-here'); expect(interpretedResponse[1].dealCount).to.equal(0); + + const results = JSON.parse(storage.getDataFromLocalStorage('adn.metaData')); + const usiEntry = results.find(entry => entry.key === 'usi'); + expect(usiEntry.key).to.equal('usi'); + expect(usiEntry.value).to.equal('from-api-server dude'); + expect(usiEntry.exp).to.be.greaterThan(misc.getUnixTimestamp(90)); + + const voidAuIdsEntry = results.find(entry => entry.key === 'voidAuIds'); + expect(voidAuIdsEntry.key).to.equal('voidAuIds'); + expect(voidAuIdsEntry.exp).to.equal(undefined); + expect(voidAuIdsEntry.value[0].auId).to.equal('00000000000abcde'); + expect(voidAuIdsEntry.value[0].exp).to.be.greaterThan(misc.getUnixTimestamp()); + expect(voidAuIdsEntry.value[0].exp).to.be.lessThan(misc.getUnixTimestamp(2)); + expect(voidAuIdsEntry.value[1].auId).to.equal('00000000000fffff'); + expect(voidAuIdsEntry.value[1].exp).to.be.greaterThan(misc.getUnixTimestamp()); + expect(voidAuIdsEntry.value[1].exp).to.be.lessThan(misc.getUnixTimestamp(2)); + + const validEntry = results.find(entry => entry.key === 'valid'); + expect(validEntry.key).to.equal('valid'); + expect(validEntry.value).to.equal('also-valid'); + expect(validEntry.exp).to.be.greaterThan(misc.getUnixTimestamp()); + expect(validEntry.exp).to.be.lessThan(misc.getUnixTimestamp(2)); + + const randomApiEntry = results.find(entry => entry.key === 'randomApiKey'); + expect(randomApiEntry.key).to.equal('randomApiKey'); + expect(randomApiEntry.value).to.equal('randomApiValue'); + expect(randomApiEntry.exp).to.be.greaterThan(misc.getUnixTimestamp(90)); }); it('should not process valid response when passed alt bidder that is an adndeal', function() { From 32748b48a5943a4b607b3e18c66b7bf4964a4347 Mon Sep 17 00:00:00 2001 From: TPMN Admin Date: Thu, 30 Nov 2023 22:19:17 +0900 Subject: [PATCH 029/246] TPMN Bid Adapter: remove OptionsObject (#10777) * TPMN Support Video * add video param plcmt * update ortb bcat,badv,bapp * instream video is sound on default, outstream video sound off default * remove VIDEO_ORTB_PARAMS, BANNER_ORTB_PARAMS * fixed incorrect placement default format & update example * update test case (Your tests failed on CircleCI) * Revert "remove VIDEO_ORTB_PARAMS, BANNER_ORTB_PARAMS" This reverts commit 2699115a76ed891ec7f911ad924206b399790ad9. * Remove unnecessary code to meet guidelines. * Remove unnecessary code to meet guidelines. * fix Build fail * delete logging code * Remove OptionsObject(Credentials information needs to be sent.) --------- Co-authored-by: changjun Co-authored-by: yeongjaeju --- modules/tpmnBidAdapter.js | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/modules/tpmnBidAdapter.js b/modules/tpmnBidAdapter.js index 61f8a4c98af..89951a99f12 100644 --- a/modules/tpmnBidAdapter.js +++ b/modules/tpmnBidAdapter.js @@ -108,8 +108,7 @@ function createRequest(bidRequests, bidderRequest, mediaType) { return { method: 'POST', url: BIDDER_ENDPOINT_URL + '?v=' + ADAPTER_VERSION, - data: rtbData, - options: { contentType: 'application/json;charset=UTF-8', withCredentials: false } + data: rtbData } } From 7dd09d8bb53fb8a0c67af04f705168351838429c Mon Sep 17 00:00:00 2001 From: Chris Southern <79725079+southern-growthcode@users.noreply.github.com> Date: Thu, 30 Nov 2023 08:48:52 -0500 Subject: [PATCH 030/246] GC-157 Boune request when the PID is missing (#10774) --- modules/growthCodeRtdProvider.js | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/modules/growthCodeRtdProvider.js b/modules/growthCodeRtdProvider.js index ef5c7906ad7..e7dce81f7d0 100644 --- a/modules/growthCodeRtdProvider.js +++ b/modules/growthCodeRtdProvider.js @@ -60,7 +60,11 @@ function init(config, userConsent) { items = tryParse(storage.getDataFromLocalStorage(RTD_CACHE_KEY, null)); - return callServer(configParams, items, expiresAt, userConsent); + if (configParams.pid === undefined) { + return true; // Die gracefully + } else { + return callServer(configParams, items, expiresAt, userConsent); + } } function callServer(configParams, items, expiresAt, userConsent) { // Expire Cache From 9c597a3a226b7ab258cf97b5e81f9079cf8ab1ce Mon Sep 17 00:00:00 2001 From: adxpremium <55161519+adxpremium@users.noreply.github.com> Date: Thu, 30 Nov 2023 18:56:16 +0100 Subject: [PATCH 031/246] DynamicAdBoost Module : add to adloader list (#10752) * Add dynamicAdBoost Module * Add dynamicAdBoost spec file * dynamicAdBoost: fix ES Lint * dynamicAdBoost: ESLint fix * dynamicAdBoost: ESLint fix * dynamicAdBoost: fix ESLint * dynamicAdBoost: removed unused log warn * dynamicAdBoost: Fixed tests * dynamicAdBoost: export init() function * Delete dynamicAdBoost.md * Delete dynamicAdBoost.js * Delete dynamicAdBoost_spec.js * Rename dynamicAdBoost to dynamicAdBoostRtdProvider dynamicAdBoost is now submodule of the rtd * dynamicAdBoost tests * Added disconnect() and IO support check * dynamicAdBoostRtdProvider: markViewad added * dynamicAdBoostRtdProvider: markViewed added * dynamicAdBoostRtdProvider.js: observer fix * dynamicAdBoostRtdProvider.js: observer fix * dynamicAdBoostRtdProvider.js: ESLint fix * dynamicAdBoostRtdProvider.js: var to let * dynamicAdBoostRtdProvider.js: moved timers * dynamicAdBoostRtdProvider_spec.js: using clock * dynamicAdBoostRtdProvider_spec.js: fakeTimer * Update adloader.js Adding dynamicAdBoost module to approved list --- src/adloader.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/adloader.js b/src/adloader.js index cd4ad771774..d1dca9627d8 100644 --- a/src/adloader.js +++ b/src/adloader.js @@ -28,7 +28,8 @@ const _approvedLoadExternalJSList = [ 'a1Media', 'geoedge', 'mediafilter', - 'qortex' + 'qortex', + 'dynamicAdBoost' ] /** From a8fedc6c6ed23974e6c9185856c5d9206941992f Mon Sep 17 00:00:00 2001 From: Demetrio Girardi Date: Thu, 30 Nov 2023 10:03:13 -0800 Subject: [PATCH 032/246] Generic analytics: fix spurious warning messages (#10739) --- modules/genericAnalyticsAdapter.js | 12 ++++++------ src/events.js | 4 +++- test/spec/modules/genericAnalyticsAdapter_spec.js | 2 +- 3 files changed, 10 insertions(+), 8 deletions(-) diff --git a/modules/genericAnalyticsAdapter.js b/modules/genericAnalyticsAdapter.js index b52cb7e5464..7f721863912 100644 --- a/modules/genericAnalyticsAdapter.js +++ b/modules/genericAnalyticsAdapter.js @@ -1,6 +1,6 @@ import AnalyticsAdapter from '../libraries/analyticsAdapter/AnalyticsAdapter.js'; import {prefixLog, isPlainObject} from '../src/utils.js'; -import * as CONSTANTS from '../src/constants.json'; +import {has as hasEvent} from '../src/events.js'; import adapterManager from '../src/adapterManager.js'; import {ajaxBuilder} from '../src/ajax.js'; @@ -48,12 +48,12 @@ export function GenericAnalytics() { return false; } for (const [event, handler] of Object.entries(options.events)) { - if (!CONSTANTS.EVENTS.hasOwnProperty(event)) { + if (!hasEvent(event)) { logWarn(`options.events.${event} does not match any known Prebid event`); - if (typeof handler !== 'function') { - logError(`options.events.${event} must be a function`); - return false; - } + } + if (typeof handler !== 'function') { + logError(`options.events.${event} must be a function`); + return false; } } } diff --git a/src/events.js b/src/events.js index 7a1e25e0e49..6eb7ce49f11 100644 --- a/src/events.js +++ b/src/events.js @@ -89,6 +89,8 @@ const _public = (function () { return allEvents.includes(event) } + _public.has = _checkAvailableEvent; + _public.on = function (eventString, handler, id) { // check whether available event or not if (_checkAvailableEvent(eventString)) { @@ -163,7 +165,7 @@ const _public = (function () { utils._setEventEmitter(_public.emit.bind(_public)); -export const {on, off, get, getEvents, emit, addEvents} = _public; +export const {on, off, get, getEvents, emit, addEvents, has} = _public; export function clearEvents() { eventsFired.clear(); diff --git a/test/spec/modules/genericAnalyticsAdapter_spec.js b/test/spec/modules/genericAnalyticsAdapter_spec.js index a5a6074c425..79874f5d756 100644 --- a/test/spec/modules/genericAnalyticsAdapter_spec.js +++ b/test/spec/modules/genericAnalyticsAdapter_spec.js @@ -75,7 +75,7 @@ describe('Generic analytics', () => { options: { url: 'mock', events: { - bidResponse: null + mockEvent: null } } }); From 520278447a0f7dfe0ffc720ea289073313144024 Mon Sep 17 00:00:00 2001 From: "Prebid.js automated release" Date: Thu, 30 Nov 2023 19:02:24 +0000 Subject: [PATCH 033/246] Prebid 8.26.0 release --- package-lock.json | 2 +- package.json | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/package-lock.json b/package-lock.json index 2563c9435f7..1fbf6ef9aa2 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,6 +1,6 @@ { "name": "prebid.js", - "version": "8.26.0-pre", + "version": "8.26.0", "lockfileVersion": 2, "requires": true, "packages": { diff --git a/package.json b/package.json index 8aef0e70e8c..9470d5cf8c5 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "prebid.js", - "version": "8.26.0-pre", + "version": "8.26.0", "description": "Header Bidding Management Library", "main": "src/prebid.js", "scripts": { From ec42b130e89e25318583d23701d0a4d56f1c45bc Mon Sep 17 00:00:00 2001 From: "Prebid.js automated release" Date: Thu, 30 Nov 2023 19:02:24 +0000 Subject: [PATCH 034/246] Increment version to 8.27.0-pre --- package-lock.json | 2 +- package.json | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/package-lock.json b/package-lock.json index 1fbf6ef9aa2..e08d24d42d5 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,6 +1,6 @@ { "name": "prebid.js", - "version": "8.26.0", + "version": "8.27.0-pre", "lockfileVersion": 2, "requires": true, "packages": { diff --git a/package.json b/package.json index 9470d5cf8c5..a7dbe2f6905 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "prebid.js", - "version": "8.26.0", + "version": "8.27.0-pre", "description": "Header Bidding Management Library", "main": "src/prebid.js", "scripts": { From 2de3f8782c930873c9709aa72bfec5892f8c51b4 Mon Sep 17 00:00:00 2001 From: Mike Lei Date: Fri, 1 Dec 2023 05:01:15 -0800 Subject: [PATCH 035/246] Flipp Bid Adapter: update constant NETWORK_ID (#10781) * Flipp Bid Adapter: initial release * Added flippBidAdapter * OFF-372 Support DTX/Hero in flippBidAdapter (#2) * support creativeType * OFF-422 flippBidAdapter handle AdTypes --------- Co-authored-by: Jairo Panduro * OFF-465 Add getUserKey logic to prebid.js adapter (#3) * Support cookie sync and uid * address pr feedback * remove redundant check * OFF-500 Support "startCompact" param for Prebid.JS #4 * set startCompact default value (#5) * fix docs * use client bidding endpoint * update unit testing endpoint * OFF-876 [Prebid Adapter] Check userKey for empty string (#6) * add more checks to userKey * update document * add uuid format statement * modify docs * fix network id --------- Co-authored-by: Jairo Panduro --- modules/flippBidAdapter.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/flippBidAdapter.js b/modules/flippBidAdapter.js index 480e414992d..ea5e71ad81d 100644 --- a/modules/flippBidAdapter.js +++ b/modules/flippBidAdapter.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 NETWORK_ID = 11090; +const NETWORK_ID = 10922; const AD_TYPES = [4309, 641]; const DTX_TYPES = [5061]; const TARGET_NAME = 'inline'; From 16133c5192c78e4e63975fcfc62a681282bacaf6 Mon Sep 17 00:00:00 2001 From: mamatic <52153441+mamatic@users.noreply.github.com> Date: Fri, 1 Dec 2023 16:11:53 +0100 Subject: [PATCH 036/246] identityLinkIdSystem - liveramp - bug fix setting tcf consent string in 3p call (#10778) --- modules/identityLinkIdSystem.js | 3 +-- test/spec/modules/identityLinkIdSystem_spec.js | 18 ------------------ 2 files changed, 1 insertion(+), 20 deletions(-) diff --git a/modules/identityLinkIdSystem.js b/modules/identityLinkIdSystem.js index a57f8d22753..d70b5680b69 100644 --- a/modules/identityLinkIdSystem.js +++ b/modules/identityLinkIdSystem.js @@ -54,7 +54,6 @@ 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 if (hasGdpr && (!gdprConsentString || gdprConsentString === '')) { utils.logInfo('identityLink: Consent string is required to call envelope API.'); @@ -64,7 +63,7 @@ export const identityLinkSubmodule = { const gppString = gppData && gppData.gppString ? gppData.gppString : false; const gppSectionId = gppData && gppData.gppString && gppData.applicableSections.length > 0 && gppData.applicableSections[0] !== -1 ? gppData.applicableSections[0] : false; const hasGpp = gppString && gppSectionId; - const url = `https://api.rlcdn.com/api/identity/envelope?pid=${configParams.pid}${hasGdpr ? (tcfPolicyV2 ? '&ct=4&cv=' : '&ct=1&cv=') + gdprConsentString : ''}${hasGpp ? '&gpp=' + gppString + '&gpp_sid=' + gppSectionId : ''}`; + const url = `https://api.rlcdn.com/api/identity/envelope?pid=${configParams.pid}${hasGdpr ? '&ct=4&cv=' + gdprConsentString : ''}${hasGpp ? '&gpp=' + gppString + '&gpp_sid=' + gppSectionId : ''}`; let resp; resp = function (callback) { // Check ats during callback so it has a chance to initialise. diff --git a/test/spec/modules/identityLinkIdSystem_spec.js b/test/spec/modules/identityLinkIdSystem_spec.js index f1c2773de07..66d5a3edd00 100644 --- a/test/spec/modules/identityLinkIdSystem_spec.js +++ b/test/spec/modules/identityLinkIdSystem_spec.js @@ -75,24 +75,6 @@ describe('IdentityLinkId tests', function () { 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, - consentString: 'BOkIpDSOkIpDSADABAENCc-AAAApOAFAAMAAsAMIAcAA_g' - }; - 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=1&cv=BOkIpDSOkIpDSADABAENCc-AAAApOAFAAMAAsAMIAcAA_g'); - request.respond( - 200, - responseHeader, - JSON.stringify({}) - ); - 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 = { From faec1bbd6c22bf226515759555d7a9c1aa9f8f30 Mon Sep 17 00:00:00 2001 From: hybrid-ai <58724131+hybrid-ai@users.noreply.github.com> Date: Fri, 1 Dec 2023 20:45:16 +0300 Subject: [PATCH 037/246] Add GVLID to vox adapter (#10784) --- modules/voxBidAdapter.js | 2 ++ 1 file changed, 2 insertions(+) diff --git a/modules/voxBidAdapter.js b/modules/voxBidAdapter.js index 34bd46ccb98..431d0887334 100644 --- a/modules/voxBidAdapter.js +++ b/modules/voxBidAdapter.js @@ -12,6 +12,7 @@ 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; +const GVLID = 206; function buildBidRequests(validBidRequests) { return _map(validBidRequests, function(bid) { @@ -183,6 +184,7 @@ function wrapBanner(bid, bidData) { export const spec = { code: BIDDER_CODE, + gvlid: GVLID, supportedMediaTypes: [BANNER, VIDEO], /** From fe2f35e00e131045bb130006d476d28f4ebd01e7 Mon Sep 17 00:00:00 2001 From: Viktor Dreiling <34981284+3link@users.noreply.github.com> Date: Fri, 1 Dec 2023 18:59:16 +0100 Subject: [PATCH 038/246] LiveIntent UserId module: Add support for sovrn id (#10783) * Add EID translation for sovrn * Review comments: fix test --- modules/liveIntentIdSystem.js | 16 +++++++++ test/spec/modules/eids_spec.js | 33 +++++++++++++++++++ .../modules/liveIntentIdMinimalSystem_spec.js | 5 +++ test/spec/modules/liveIntentIdSystem_spec.js | 5 +++ 4 files changed, 59 insertions(+) diff --git a/modules/liveIntentIdSystem.js b/modules/liveIntentIdSystem.js index cc43f6f6f84..bb56355de31 100644 --- a/modules/liveIntentIdSystem.js +++ b/modules/liveIntentIdSystem.js @@ -223,6 +223,10 @@ export const liveIntentIdSubmodule = { result.pubmatic = { 'id': value.pubmatic, ext: { provider: LI_PROVIDER_DOMAIN } } } + if (value.sovrn) { + result.sovrn = { 'id': value.sovrn, ext: { provider: LI_PROVIDER_DOMAIN } } + } + return result } @@ -348,6 +352,18 @@ export const liveIntentIdSubmodule = { return data.ext; } } + }, + 'sovrn': { + source: 'liveintent.sovrn.com', + atype: 3, + getValue: function(data) { + return data.id; + }, + getUidExt: function(data) { + if (data.ext) { + return data.ext; + } + } } } }; diff --git a/test/spec/modules/eids_spec.js b/test/spec/modules/eids_spec.js index f2ccdcb3024..fa271b5114e 100644 --- a/test/spec/modules/eids_spec.js +++ b/test/spec/modules/eids_spec.js @@ -238,6 +238,39 @@ describe('eids array generation for known sub-modules', function() { }); }); + it('sovrn', function() { + const userId = { + sovrn: {'id': 'sample_id'} + }; + const newEids = createEidsArray(userId); + expect(newEids.length).to.equal(1); + expect(newEids[0]).to.deep.equal({ + source: 'liveintent.sovrn.com', + uids: [{ + id: 'sample_id', + atype: 3 + }] + }); + }); + + it('sovrn with ext', function() { + const userId = { + sovrn: {'id': 'sample_id', 'ext': {'provider': 'some.provider.com'}} + }; + const newEids = createEidsArray(userId); + expect(newEids.length).to.equal(1); + expect(newEids[0]).to.deep.equal({ + source: 'liveintent.sovrn.com', + uids: [{ + id: 'sample_id', + atype: 3, + ext: { + provider: 'some.provider.com' + } + }] + }); + }); + it('magnite', function() { const userId = { magnite: {'id': 'sample_id'} diff --git a/test/spec/modules/liveIntentIdMinimalSystem_spec.js b/test/spec/modules/liveIntentIdMinimalSystem_spec.js index ed78d8f6e40..0ada7adc594 100644 --- a/test/spec/modules/liveIntentIdMinimalSystem_spec.js +++ b/test/spec/modules/liveIntentIdMinimalSystem_spec.js @@ -261,6 +261,11 @@ describe('LiveIntentMinimalId', function() { expect(result).to.eql({'lipb': {'lipbid': 'foo', 'nonId': 'foo', 'medianet': 'bar'}, 'medianet': {'id': 'bar', 'ext': {'provider': 'liveintent.com'}}}); }); + it('should decode a sovrn id to a seperate object when present', function() { + const result = liveIntentIdSubmodule.decode({ nonId: 'foo', sovrn: 'bar' }); + expect(result).to.eql({'lipb': {'lipbid': 'foo', 'nonId': 'foo', 'sovrn': 'bar'}, 'sovrn': {'id': 'bar', 'ext': {'provider': 'liveintent.com'}}}); + }); + it('should decode a magnite id to a seperate object when present', function() { const result = liveIntentIdSubmodule.decode({ nonId: 'foo', magnite: 'bar' }); expect(result).to.eql({'lipb': {'lipbid': 'foo', 'nonId': 'foo', 'magnite': 'bar'}, 'magnite': {'id': 'bar', 'ext': {'provider': 'liveintent.com'}}}); diff --git a/test/spec/modules/liveIntentIdSystem_spec.js b/test/spec/modules/liveIntentIdSystem_spec.js index 9f57a5aa405..506c5b312cc 100644 --- a/test/spec/modules/liveIntentIdSystem_spec.js +++ b/test/spec/modules/liveIntentIdSystem_spec.js @@ -388,6 +388,11 @@ describe('LiveIntentId', function() { expect(result).to.eql({'lipb': {'lipbid': 'foo', 'nonId': 'foo', 'medianet': 'bar'}, 'medianet': {'id': 'bar', 'ext': {'provider': 'liveintent.com'}}}); }); + it('should decode a sovrn id to a seperate object when present', function() { + const result = liveIntentIdSubmodule.decode({ nonId: 'foo', sovrn: 'bar' }); + expect(result).to.eql({'lipb': {'lipbid': 'foo', 'nonId': 'foo', 'sovrn': 'bar'}, 'sovrn': {'id': 'bar', 'ext': {'provider': 'liveintent.com'}}}); + }); + it('should decode a magnite id to a seperate object when present', function() { const result = liveIntentIdSubmodule.decode({ nonId: 'foo', magnite: 'bar' }); expect(result).to.eql({'lipb': {'lipbid': 'foo', 'nonId': 'foo', 'magnite': 'bar'}, 'magnite': {'id': 'bar', 'ext': {'provider': 'liveintent.com'}}}); From 4f057b192fffdbfba369c84fe00048163ac66edc Mon Sep 17 00:00:00 2001 From: bhainesnexxen <146981622+bhainesnexxen@users.noreply.github.com> Date: Fri, 1 Dec 2023 11:26:36 -0800 Subject: [PATCH 039/246] Unruly Bid Adapter : include Prebid.js version in request (#10773) * Unruly Protected Audience Support * Unruly Conditional Protected Audience Support * Unruly map bidIds to AuctionConfigs * Unruly: Update Protected Audience origin and descision logic URL. * Unruly: Fix lint error * Unruly: report Prebid.js version * Unruly: Fix tests * Unruly: Remove Extra Comma * Unruly: Switch to strings to see if tests pass on Prebid.js --------- Co-authored-by: Brian Haines --- modules/unrulyBidAdapter.js | 9 ++++++++- test/spec/modules/unrulyBidAdapter_spec.js | 11 +++++++---- 2 files changed, 15 insertions(+), 5 deletions(-) diff --git a/modules/unrulyBidAdapter.js b/modules/unrulyBidAdapter.js index 8fbbcf7d7c5..b825003f36f 100644 --- a/modules/unrulyBidAdapter.js +++ b/modules/unrulyBidAdapter.js @@ -80,7 +80,14 @@ const getRequests = (conf, validBidRequests, bidderRequest) => { Object.keys(requestBySiteId).forEach((key) => { let data = { - bidderRequest: Object.assign({}, {bids: requestBySiteId[key], invalidBidsCount, ...bidderRequestData}) + bidderRequest: Object.assign({}, + { + bids: requestBySiteId[key], + invalidBidsCount, + prebidVersion: '$prebid.version$', + ...bidderRequestData + } + ) }; request.push(Object.assign({}, {data, ...conf})); diff --git a/test/spec/modules/unrulyBidAdapter_spec.js b/test/spec/modules/unrulyBidAdapter_spec.js index f1f6517af67..abf1a54787d 100644 --- a/test/spec/modules/unrulyBidAdapter_spec.js +++ b/test/spec/modules/unrulyBidAdapter_spec.js @@ -517,7 +517,8 @@ describe('UnrulyAdapter', function () { 'bidderRequestId': '12e00d17dff07b' } ], - 'invalidBidsCount': 0 + 'invalidBidsCount': 0, + 'prebidVersion': '$prebid.version$' } }; @@ -591,7 +592,8 @@ describe('UnrulyAdapter', function () { 'bidderRequestId': '12e00d17dff07b', } ], - 'invalidBidsCount': 0 + 'invalidBidsCount': 0, + 'prebidVersion': '$prebid.version$' } }; @@ -682,7 +684,8 @@ describe('UnrulyAdapter', function () { 'bidderRequestId': '12e00d17dff07b', } ], - 'invalidBidsCount': 0 + 'invalidBidsCount': 0, + 'prebidVersion': '$prebid.version$' } }; @@ -1095,7 +1098,7 @@ describe('UnrulyAdapter', function () { 'adUnitCode': 'video2', 'transactionId': 'a89619e3-137d-4cc5-9ed4-58a0b2a0bbc2', 'bidId': bidId, - 'bidderRequestId': '12e00d17dff07b', + 'bidderRequestId': '12e00d17dff07b' } ] } From cbf1e1580727a311dbd953566ed17cca7cf4f4e0 Mon Sep 17 00:00:00 2001 From: xmgiddev <133856186+xmgiddev@users.noreply.github.com> Date: Sat, 2 Dec 2023 14:49:55 +0200 Subject: [PATCH 040/246] MgidX Bid Adapter: refactor request.gdpr object generating (#10782) * new adapter - MgidX * add new required param host * Revert "add new required param host" This reverts commit 3bafe782a492d5d4b35476f8117260b08a237f2f. * refactoring generating request.gdpr object * fix test --------- Co-authored-by: Evgeny Nagorny Co-authored-by: xmgiddev <> --- modules/mgidXBidAdapter.js | 7 ++++++- test/spec/modules/mgidXBidAdapter_spec.js | 13 +++++++++---- 2 files changed, 15 insertions(+), 5 deletions(-) diff --git a/modules/mgidXBidAdapter.js b/modules/mgidXBidAdapter.js index 5789f0d8b95..7e084977cf1 100644 --- a/modules/mgidXBidAdapter.js +++ b/modules/mgidXBidAdapter.js @@ -166,10 +166,15 @@ export const spec = { placements, coppa: config.getConfig('coppa') === true ? 1 : 0, ccpa: bidderRequest.uspConsent || undefined, - gdpr: bidderRequest.gdprConsent || undefined, tmax: config.getConfig('bidderTimeout') }; + if (bidderRequest.gdprConsent) { + request.gdpr = { + consentString: bidderRequest.gdprConsent.consentString + }; + } + const len = validBidRequests.length; for (let i = 0; i < len; i++) { const bid = validBidRequests[i]; diff --git a/test/spec/modules/mgidXBidAdapter_spec.js b/test/spec/modules/mgidXBidAdapter_spec.js index 14619e9c0e1..978ca3de036 100644 --- a/test/spec/modules/mgidXBidAdapter_spec.js +++ b/test/spec/modules/mgidXBidAdapter_spec.js @@ -76,7 +76,10 @@ describe('MGIDXBidAdapter', function () { const bidderRequest = { uspConsent: '1---', - gdprConsent: 'COvFyGBOvFyGBAbAAAENAPCAAOAAAAAAAAAAAEEUACCKAAA.IFoEUQQgAIQwgIwQABAEAAAAOIAACAIAAAAQAIAgEAACEAAAAAgAQBAAAAAAAGBAAgAAAAAAAFAAECAAAgAAQARAEQAAAAAJAAIAAgAAAYQEAAAQmAgBC3ZAYzUw', + gdprConsent: { + consentString: 'COvFyGBOvFyGBAbAAAENAPCAAOAAAAAAAAAAAEEUACCKAAA.IFoEUQQgAIQwgIwQABAEAAAAOIAACAIAAAAQAIAgEAACEAAAAAgAQBAAAAAAAGBAAgAAAAAAAFAAECAAAgAAQARAEQAAAAAJAAIAAgAAAYQEAAAQmAgBC3ZAYzUw', + vendorData: {} + }, refererInfo: { referer: 'https://test.com' } @@ -131,7 +134,7 @@ describe('MGIDXBidAdapter', function () { 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.gdpr).to.be.a('object'); expect(data.ccpa).to.be.a('string'); expect(data.tmax).to.be.a('number'); expect(data.placements).to.have.lengthOf(3); @@ -172,8 +175,10 @@ describe('MGIDXBidAdapter', function () { 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.gdpr).to.be.a('object'); + expect(data.gdpr).to.have.property('consentString'); + expect(data.gdpr).to.not.have.property('vendorData'); + expect(data.gdpr.consentString).to.equal(bidderRequest.gdprConsent.consentString); expect(data.ccpa).to.not.exist; delete bidderRequest.gdprConsent; }); From 7be0a10bf24d5aba4821c9b52bf6300ab4f3dd70 Mon Sep 17 00:00:00 2001 From: Malkov Mikhail Date: Mon, 4 Dec 2023 11:24:11 +0300 Subject: [PATCH 041/246] NextMillennium Bid Adapter: support for GPP consent string, `site.pagecat`, `site.content.cat` and `site.content.language` (#10761) * added support for gpp consent string * changed test for nextMillenniumBidAdapter * added some tests * added site.pagecat, site.content.cat and site.content.language to request * lint fix * formated code * formated code * formated code * pachage-lock with prebid * pachage-lock with prebid * formatted code --- modules/nextMillenniumBidAdapter.js | 245 +++++---- .../modules/nextMillenniumBidAdapter_spec.js | 510 +++++++++++++++--- 2 files changed, 564 insertions(+), 191 deletions(-) diff --git a/modules/nextMillenniumBidAdapter.js b/modules/nextMillenniumBidAdapter.js index 0cbe954175c..26d417b9baf 100644 --- a/modules/nextMillenniumBidAdapter.js +++ b/modules/nextMillenniumBidAdapter.js @@ -1,7 +1,9 @@ import { _each, createTrackPixelHtml, - deepAccess, getBidIdParameter, + deepAccess, + deepSetValue, + getBidIdParameter, getDefinedParams, getWindowTop, isArray, @@ -20,17 +22,32 @@ import * as events from '../src/events.js'; import {registerBidder} from '../src/adapters/bidderFactory.js'; import {getRefererInfo} from '../src/refererDetection.js'; +const NM_VERSION = '3.0.0'; +const GVLID = 1060; const BIDDER_CODE = 'nextMillennium'; const ENDPOINT = 'https://pbs.nextmillmedia.com/openrtb2/auction'; const TEST_ENDPOINT = 'https://test.pbs.nextmillmedia.com/openrtb2/auction'; -const SYNC_ENDPOINT = 'https://cookies.nextmillmedia.com/sync?'; +const SYNC_ENDPOINT = 'https://cookies.nextmillmedia.com/sync?gdpr={{.GDPR}}&gdpr_consent={{.GDPRConsent}}&us_privacy={{.USPrivacy}}&gpp={{.GPP}}&gpp_sid={{.GPPSID}}&type={{.TYPE_PIXEL}}'; const REPORT_ENDPOINT = 'https://report2.hb.brainlyads.com/statistics/metric'; const TIME_TO_LIVE = 360; + const VIDEO_PARAMS = [ - 'api', 'linearity', 'maxduration', 'mimes', 'minduration', 'placement', - 'playbackmethod', 'protocols', 'startdelay' + 'api', + 'linearity', + 'maxduration', + 'mimes', + 'minduration', + 'placement', + 'playbackmethod', + 'protocols', + 'startdelay', +]; + +const ALLOWED_ORTB2_PARAMETERS = [ + 'site.pagecat', + 'site.content.cat', + 'site.content.language', ]; -const GVLID = 1060; const sendingDataStatistic = initSendingDataStatistic(); events.on(CONSTANTS.EVENTS.AUCTION_INIT, auctionInitHandler); @@ -61,85 +78,35 @@ export const spec = { const id = getPlacementId(bid); const auctionId = bid.auctionId; const bidId = bid.bidId; - let sizes = bid.sizes; - if (sizes && !Array.isArray(sizes[0])) sizes = [sizes]; const site = getSiteObj(); const device = getDeviceObj(); const postBody = { - 'id': bidderRequest?.bidderRequestId, - 'ext': { - 'prebid': { - 'storedrequest': { - 'id': id - } + id: bidderRequest?.bidderRequestId, + ext: { + prebid: { + storedrequest: { + id, + }, }, - 'nextMillennium': { - 'refresh_count': window.nmmRefreshCounts[bid.adUnitCode]++, - 'elOffsets': getBoundingClient(bid), - 'scrollTop': window.pageYOffset || document.documentElement.scrollTop - } + nextMillennium: { + nm_version: NM_VERSION, + refresh_count: window.nmmRefreshCounts[bid.adUnitCode]++, + elOffsets: getBoundingClient(bid), + scrollTop: window.pageYOffset || document.documentElement.scrollTop, + }, }, device, site, - imp: [] - }; - - const imp = { - id: bid.adUnitCode, - ext: { - prebid: { - storedrequest: {id} - } - } - }; - - if (deepAccess(bid, 'mediaTypes.banner')) { - imp.banner = { - format: (sizes || []).map(s => { return {w: s[0], h: s[1]} }) - }; + imp: [], }; - const video = deepAccess(bid, 'mediaTypes.video'); - if (video) { - imp.video = getDefinedParams(video, VIDEO_PARAMS); - if (video.playerSize) { - imp.video = Object.assign( - imp.video, parseGPTSingleSizeArrayToRtbSize(video.playerSize[0]) || {} - ); - } else if (video.w && video.h) { - imp.video.w = video.w; - imp.video.h = video.h; - }; - }; - - postBody.imp.push(imp); - - const gdprConsent = bidderRequest && bidderRequest.gdprConsent; - const uspConsent = bidderRequest && bidderRequest.uspConsent; - - if (gdprConsent || uspConsent) { - postBody.regs = { ext: {} }; - - if (uspConsent) { - postBody.regs.ext.us_privacy = uspConsent; - }; - - if (gdprConsent) { - if (typeof gdprConsent.gdprApplies !== 'undefined') { - postBody.regs.ext.gdpr = gdprConsent.gdprApplies ? 1 : 0; - }; - - if (typeof gdprConsent.consentString !== 'undefined') { - postBody.user = { - ext: { consent: gdprConsent.consentString } - }; - }; - }; - }; + postBody.imp.push(getImp(bid, id)); + setConsentStrings(postBody, bidderRequest); + setOrtb2Parameters(postBody, bidderRequest?.ortb2); const urlParameters = parseUrl(getWindowTop().location.href).search; const isTest = urlParameters['pbs'] && urlParameters['pbs'] === 'test'; @@ -151,7 +118,7 @@ export const spec = { data: JSON.stringify(postBody), options: { contentType: 'text/plain', - withCredentials: true + withCredentials: true, }, bidId, @@ -173,6 +140,7 @@ export const spec = { const params = bidRequest.params; const auctionId = bidRequest.auctionId; const wurl = deepAccess(bid, 'ext.prebid.events.win'); + // TODO: fix auctionId leak: https://github.com/prebid/Prebid.js/issues/9781 addWurl({auctionId, requestId, wurl}); @@ -189,8 +157,8 @@ export const spec = { netRevenue: true, ttl: TIME_TO_LIVE, meta: { - advertiserDomains: bid.adomain || [] - } + advertiserDomains: bid.adomain || [], + }, }; if (vastUrl || vastXml) { @@ -210,38 +178,29 @@ export const spec = { return bidResponses; }, - getUserSyncs: function (syncOptions, responses, gdprConsent, uspConsent) { + getUserSyncs: function (syncOptions, responses, gdprConsent, uspConsent, gppConsent) { + if (!syncOptions.iframeEnabled && !syncOptions.pixelEnabled) return []; + const pixels = []; + const getSetPixelFunc = type => url => { pixels.push({type, url: replaceUsersyncMacros(url, gdprConsent, uspConsent, gppConsent, type)}) }; + const getSetPixelsFunc = type => response => { deepAccess(response, `body.ext.sync.${type}`, []).forEach(getSetPixelFunc(type)) }; + + const setPixel = (type, url) => { (getSetPixelFunc(type))(url) }; + const setPixelImages = getSetPixelsFunc('image'); + const setPixelIframes = getSetPixelsFunc('iframe'); if (isArray(responses)) { responses.forEach(response => { - if (syncOptions.pixelEnabled) { - deepAccess(response, 'body.ext.sync.image', []).forEach(imgUrl => { - pixels.push({ - type: 'image', - url: replaceUsersyncMacros(imgUrl, gdprConsent, uspConsent) - }); - }) - } - - if (syncOptions.iframeEnabled) { - deepAccess(response, 'body.ext.sync.iframe', []).forEach(iframeUrl => { - pixels.push({ - type: 'iframe', - url: replaceUsersyncMacros(iframeUrl, gdprConsent, uspConsent) - }); - }) - } + if (syncOptions.pixelEnabled) setPixelImages(response); + if (syncOptions.iframeEnabled) setPixelIframes(response); }) } if (!pixels.length) { - let syncUrl = SYNC_ENDPOINT; - if (gdprConsent && gdprConsent.gdprApplies) syncUrl += 'gdpr=1&gdpr_consent=' + gdprConsent.consentString + '&'; - if (uspConsent) syncUrl += 'us_privacy=' + uspConsent + '&'; - if (syncOptions.iframeEnabled) pixels.push({type: 'iframe', url: syncUrl + 'type=iframe'}); - if (syncOptions.pixelEnabled) pixels.push({type: 'image', url: syncUrl + 'type=image'}); + if (syncOptions.pixelEnabled) setPixel('image', SYNC_ENDPOINT); + if (syncOptions.iframeEnabled) setPixel('iframe', SYNC_ENDPOINT); } + return pixels; }, @@ -281,24 +240,88 @@ export const spec = { }, }; -function replaceUsersyncMacros(url, gdprConsent, uspConsent) { - const { consentString, gdprApplies } = gdprConsent || {}; +export function getImp(bid, id) { + const imp = { + id: bid.adUnitCode, + ext: { + prebid: { + storedrequest: { + id, + }, + }, + }, + }; + + const banner = deepAccess(bid, 'mediaTypes.banner'); + if (banner) { + imp.banner = { + format: (banner?.sizes || []).map(s => { return {w: s[0], h: s[1]} }), + }; + }; + + const video = deepAccess(bid, 'mediaTypes.video'); + if (video) { + imp.video = getDefinedParams(video, VIDEO_PARAMS); + if (video.playerSize) { + imp.video = Object.assign(imp.video, parseGPTSingleSizeArrayToRtbSize(video.playerSize) || {}); + } else if (video.w && video.h) { + imp.video.w = video.w; + imp.video.h = video.h; + }; + }; - if (gdprApplies) { - const gdpr = Number(gdprApplies); - url = url.replace('{{.GDPR}}', gdpr); + return imp; +}; - if (gdpr == 1 && consentString && consentString.length > 0) { - url = url.replace('{{.GDPRConsent}}', consentString); - } - } else { - url = url.replace('{{.GDPR}}', 0); - url = url.replace('{{.GDPRConsent}}', ''); - } +export function setConsentStrings(postBody = {}, bidderRequest) { + const gdprConsent = bidderRequest?.gdprConsent; + const uspConsent = bidderRequest?.uspConsent; + let gppConsent = bidderRequest?.gppConsent?.gppString && bidderRequest?.gppConsent; + if (!gppConsent && bidderRequest?.ortb2?.regs?.gpp) gppConsent = bidderRequest?.ortb2?.regs; - if (uspConsent) { - url = url.replace('{{.USPrivacy}}', uspConsent); + if (gdprConsent || uspConsent || gppConsent) { + postBody.regs = { ext: {} }; + + if (uspConsent) { + postBody.regs.ext.us_privacy = uspConsent; + }; + + if (gppConsent) { + postBody.regs.gpp = gppConsent?.gppString || gppConsent?.gpp; + postBody.regs.gpp_sid = bidderRequest.gppConsent?.applicableSections || gppConsent?.gpp_sid; + }; + + if (gdprConsent) { + if (typeof gdprConsent.gdprApplies !== 'undefined') { + postBody.regs.ext.gdpr = gdprConsent.gdprApplies ? 1 : 0; + }; + + if (typeof gdprConsent.consentString !== 'undefined') { + postBody.user = { + ext: { consent: gdprConsent.consentString }, + }; + }; + }; + }; +}; + +export function setOrtb2Parameters(postBody, ortb2 = {}) { + for (let parameter of ALLOWED_ORTB2_PARAMETERS) { + const value = deepAccess(ortb2, parameter); + if (value) deepSetValue(postBody, parameter, value); } +} + +export function replaceUsersyncMacros(url, gdprConsent = {}, uspConsent = '', gppConsent = {}, type = '') { + const { consentString = '', gdprApplies = false } = gdprConsent; + const gdpr = Number(gdprApplies); + url = url + .replace('{{.GDPR}}', gdpr) + .replace('{{.GDPRConsent}}', consentString) + .replace('{{.USPrivacy}}', uspConsent) + .replace('{{.GPP}}', gppConsent.gppString || '') + .replace('{{.GPPSID}}', (gppConsent.applicableSections || []).join(',')) + .replace('{{.TYPE_PIXEL}}', type); return url; }; diff --git a/test/spec/modules/nextMillenniumBidAdapter_spec.js b/test/spec/modules/nextMillenniumBidAdapter_spec.js index 564788c8b56..8b512218d56 100644 --- a/test/spec/modules/nextMillenniumBidAdapter_spec.js +++ b/test/spec/modules/nextMillenniumBidAdapter_spec.js @@ -1,32 +1,437 @@ import { expect } from 'chai'; -import { spec } from 'modules/nextMillenniumBidAdapter.js'; +import { + getImp, + replaceUsersyncMacros, + setConsentStrings, + setOrtb2Parameters, + spec, +} from 'modules/nextMillenniumBidAdapter.js'; + +describe('nextMillenniumBidAdapterTests', () => { + describe('function getImp', () => { + const dataTests = [ + { + title: 'imp - banner', + data: { + id: '123', + bid: { + mediaTypes: {banner: {sizes: [[300, 250], [320, 250]]}}, + adUnitCode: 'test-banner-1', + }, + }, -describe('nextMillenniumBidAdapterTests', function() { - const bidRequestData = [ - { - adUnitCode: 'test-div', - bidId: 'bid1234', - auctionId: 'b06c5141-fe8f-4cdf-9d7d-54415490a917', - bidder: 'nextMillennium', - params: { placement_id: '-1' }, - sizes: [[300, 250]], - uspConsent: '1---', - gdprConsent: { - consentString: 'kjfdniwjnifwenrif3', - gdprApplies: true + expected: { + id: 'test-banner-1', + ext: {prebid: {storedrequest: {id: '123'}}}, + banner: {format: [{w: 300, h: 250}, {w: 320, h: 250}]}, + }, }, - ortb2: { - device: { - w: 1500, - h: 1000 + + { + title: 'imp - video', + data: { + id: '234', + bid: { + mediaTypes: {video: {playerSize: [400, 300]}}, + adUnitCode: 'test-video-1', + }, }, - site: { - domain: 'example.com', - page: 'http://example.com' - } + + expected: { + id: 'test-video-1', + ext: {prebid: {storedrequest: {id: '234'}}}, + video: {w: 400, h: 300}, + }, + }, + ]; + + for (let {title, data, expected} of dataTests) { + it(title, () => { + const {bid, id} = data; + const imp = getImp(bid, id); + expect(imp).to.deep.equal(expected); + }); + } + }); + + describe('function setConsentStrings', () => { + const dataTests = [ + { + title: 'full: uspConsent, gdprConsent and gppConsent', + data: { + postBody: {}, + bidderRequest: { + uspConsent: '1---', + gppConsent: {gppString: 'DBACNYA~CPXxRfAPXxR', applicableSections: [7]}, + gdprConsent: {consentString: 'kjfdniwjnifwenrif3', gdprApplies: true}, + ortb2: {regs: {gpp: 'DSFHFHWEUYVDC', gpp_sid: [8, 9, 10]}}, + }, + }, + + expected: { + user: {ext: {consent: 'kjfdniwjnifwenrif3'}}, + regs: { + gpp: 'DBACNYA~CPXxRfAPXxR', + gpp_sid: [7], + ext: {gdpr: 1, us_privacy: '1---'}, + }, + }, + }, + + { + title: 'gdprConsent(false) and ortb2(gpp)', + data: { + postBody: {}, + bidderRequest: { + gdprConsent: {consentString: 'ewtewbefbawyadexv', gdprApplies: false}, + ortb2: {regs: {gpp: 'DSFHFHWEUYVDC', gpp_sid: [8, 9, 10]}}, + }, + }, + + expected: { + user: {ext: {consent: 'ewtewbefbawyadexv'}}, + regs: { + gpp: 'DSFHFHWEUYVDC', + gpp_sid: [8, 9, 10], + ext: {gdpr: 0}, + }, + }, + }, + + { + title: 'gdprConsent(false)', + data: { + postBody: {}, + bidderRequest: {gdprConsent: {gdprApplies: false}}, + }, + + expected: { + regs: {ext: {gdpr: 0}}, + }, + }, + + { + title: 'empty', + data: { + postBody: {}, + bidderRequest: {}, + }, + + expected: {}, + }, + ]; + + for (let {title, data, expected} of dataTests) { + it(title, () => { + const {postBody, bidderRequest} = data; + setConsentStrings(postBody, bidderRequest); + expect(postBody).to.deep.equal(expected); + }); + } + }); + + describe('function replaceUsersyncMacros', () => { + const dataTests = [ + { + title: 'url with all macroses - consents full: uspConsent, gdprConsent and gppConsent', + data: { + url: 'https://some.url?gdpr={{.GDPR}}&gdpr_consent={{.GDPRConsent}}&us_privacy={{.USPrivacy}}&gpp={{.GPP}}&gpp_sid={{.GPPSID}}&type={{.TYPE_PIXEL}}', + uspConsent: '1---', + gppConsent: {gppString: 'DBACNYA~CPXxRfAPXxR', applicableSections: [7, 8]}, + gdprConsent: {consentString: 'kjfdniwjnifwenrif3', gdprApplies: true}, + type: 'image', + }, + + expected: 'https://some.url?gdpr=1&gdpr_consent=kjfdniwjnifwenrif3&us_privacy=1---&gpp=DBACNYA~CPXxRfAPXxR&gpp_sid=7,8&type=image', + }, + + { + title: 'url with some macroses - consents full: uspConsent, gdprConsent and gppConsent', + data: { + url: 'https://some.url?gdpr={{.GDPR}}&gdpr_consent={{.GDPRConsent}}&type={{.TYPE_PIXEL}}', + uspConsent: '1---', + gppConsent: {gppString: 'DBACNYA~CPXxRfAPXxR', applicableSections: [7, 8]}, + gdprConsent: {consentString: 'kjfdniwjnifwenrif3', gdprApplies: false}, + type: 'iframe', + }, + + expected: 'https://some.url?gdpr=0&gdpr_consent=kjfdniwjnifwenrif3&type=iframe', + }, + + { + title: 'url without macroses - consents full: uspConsent, gdprConsent and gppConsent', + data: { + url: 'https://some.url?param1=value1¶m2=value2', + uspConsent: '1---', + gppConsent: {gppString: 'DBACNYA~CPXxRfAPXxR', applicableSections: [7, 8]}, + gdprConsent: {consentString: 'kjfdniwjnifwenrif3', gdprApplies: false}, + type: 'iframe', + }, + + expected: 'https://some.url?param1=value1¶m2=value2', + }, + + { + title: 'url with all macroses - consents are empty', + data: { + url: 'https://some.url?gdpr={{.GDPR}}&gdpr_consent={{.GDPRConsent}}&us_privacy={{.USPrivacy}}&gpp={{.GPP}}&gpp_sid={{.GPPSID}}&type={{.TYPE_PIXEL}}', + }, + + expected: 'https://some.url?gdpr=0&gdpr_consent=&us_privacy=&gpp=&gpp_sid=&type=', + }, + ]; + + for (let {title, data, expected} of dataTests) { + it(title, () => { + const {url, gdprConsent, uspConsent, gppConsent, type} = data; + const newUrl = replaceUsersyncMacros(url, gdprConsent, uspConsent, gppConsent, type); + expect(newUrl).to.equal(expected); + }); + } + }); + + describe('function spec.getUserSyncs', () => { + const dataTests = [ + { + title: 'pixels from responses ({iframeEnabled: true, pixelEnabled: true})', + data: { + syncOptions: {iframeEnabled: true, pixelEnabled: true}, + responses: [ + {body: {ext: {sync: { + image: [ + 'https://some.1.url?gdpr={{.GDPR}}&gdpr_consent={{.GDPRConsent}}&us_privacy={{.USPrivacy}}&gpp={{.GPP}}&gpp_sid={{.GPPSID}}', + 'https://some.2.url?us_privacy={{.USPrivacy}}&gpp={{.GPP}}&gpp_sid={{.GPPSID}}', + 'https://some.3.url?param=1234', + ], + + iframe: [ + 'https://some.4.url?gdpr={{.GDPR}}&gdpr_consent={{.GDPRConsent}}&gpp={{.GPP}}&gpp_sid={{.GPPSID}}', + 'https://some.5.url?gdpr={{.GDPR}}&gdpr_consent={{.GDPRConsent}}&us_privacy={{.USPrivacy}}', + ], + }}}}, + + {body: {ext: {sync: { + iframe: [ + 'https://some.6.url?gdpr={{.GDPR}}&gdpr_consent={{.GDPRConsent}}&gpp={{.GPP}}&gpp_sid={{.GPPSID}}', + 'https://some.7.url?gdpr={{.GDPR}}&gdpr_consent={{.GDPRConsent}}&us_privacy={{.USPrivacy}}', + ], + }}}}, + + {body: {ext: {sync: { + image: [ + 'https://some.8.url?gdpr={{.GDPR}}&gdpr_consent={{.GDPRConsent}}&us_privacy={{.USPrivacy}}&gpp={{.GPP}}&gpp_sid={{.GPPSID}}', + ], + }}}}, + ], + + uspConsent: '1---', + gppConsent: {gppString: 'DBACNYA~CPXxRfAPXxR', applicableSections: [7, 8]}, + gdprConsent: {consentString: 'kjfdniwjnifwenrif3', gdprApplies: true}, + }, + + expected: [ + {type: 'image', url: 'https://some.1.url?gdpr=1&gdpr_consent=kjfdniwjnifwenrif3&us_privacy=1---&gpp=DBACNYA~CPXxRfAPXxR&gpp_sid=7,8'}, + {type: 'image', url: 'https://some.2.url?us_privacy=1---&gpp=DBACNYA~CPXxRfAPXxR&gpp_sid=7,8'}, + {type: 'image', url: 'https://some.3.url?param=1234'}, + {type: 'iframe', url: 'https://some.4.url?gdpr=1&gdpr_consent=kjfdniwjnifwenrif3&gpp=DBACNYA~CPXxRfAPXxR&gpp_sid=7,8'}, + {type: 'iframe', url: 'https://some.5.url?gdpr=1&gdpr_consent=kjfdniwjnifwenrif3&us_privacy=1---'}, + {type: 'iframe', url: 'https://some.6.url?gdpr=1&gdpr_consent=kjfdniwjnifwenrif3&gpp=DBACNYA~CPXxRfAPXxR&gpp_sid=7,8'}, + {type: 'iframe', url: 'https://some.7.url?gdpr=1&gdpr_consent=kjfdniwjnifwenrif3&us_privacy=1---'}, + {type: 'image', url: 'https://some.8.url?gdpr=1&gdpr_consent=kjfdniwjnifwenrif3&us_privacy=1---&gpp=DBACNYA~CPXxRfAPXxR&gpp_sid=7,8'}, + ], + }, + + { + title: 'pixels from responses ({iframeEnabled: true, pixelEnabled: false})', + data: { + syncOptions: {iframeEnabled: true, pixelEnabled: false}, + responses: [ + {body: {ext: {sync: { + image: [ + 'https://some.1.url?gdpr={{.GDPR}}&gdpr_consent={{.GDPRConsent}}&us_privacy={{.USPrivacy}}&gpp={{.GPP}}&gpp_sid={{.GPPSID}}', + 'https://some.2.url?us_privacy={{.USPrivacy}}&gpp={{.GPP}}&gpp_sid={{.GPPSID}}', + 'https://some.3.url?param=1234', + ], + + iframe: [ + 'https://some.4.url?gdpr={{.GDPR}}&gdpr_consent={{.GDPRConsent}}&gpp={{.GPP}}&gpp_sid={{.GPPSID}}', + 'https://some.5.url?gdpr={{.GDPR}}&gdpr_consent={{.GDPRConsent}}&us_privacy={{.USPrivacy}}', + ], + }}}}, + ], + + uspConsent: '1---', + gppConsent: {gppString: 'DBACNYA~CPXxRfAPXxR', applicableSections: [7, 8]}, + gdprConsent: {consentString: 'kjfdniwjnifwenrif3', gdprApplies: true}, + }, + + expected: [ + {type: 'iframe', url: 'https://some.4.url?gdpr=1&gdpr_consent=kjfdniwjnifwenrif3&gpp=DBACNYA~CPXxRfAPXxR&gpp_sid=7,8'}, + {type: 'iframe', url: 'https://some.5.url?gdpr=1&gdpr_consent=kjfdniwjnifwenrif3&us_privacy=1---'}, + ], + }, + + { + title: 'pixels from responses ({iframeEnabled: false, pixelEnabled: true})', + data: { + syncOptions: {iframeEnabled: false, pixelEnabled: true}, + responses: [ + {body: {ext: {sync: { + image: [ + 'https://some.1.url?gdpr={{.GDPR}}&gdpr_consent={{.GDPRConsent}}&us_privacy={{.USPrivacy}}&gpp={{.GPP}}&gpp_sid={{.GPPSID}}', + 'https://some.2.url?us_privacy={{.USPrivacy}}&gpp={{.GPP}}&gpp_sid={{.GPPSID}}', + 'https://some.3.url?param=1234', + ], + + iframe: [ + 'https://some.4.url?gdpr={{.GDPR}}&gdpr_consent={{.GDPRConsent}}&gpp={{.GPP}}&gpp_sid={{.GPPSID}}', + 'https://some.5.url?gdpr={{.GDPR}}&gdpr_consent={{.GDPRConsent}}&us_privacy={{.USPrivacy}}', + ], + }}}}, + ], + + uspConsent: '1---', + gppConsent: {gppString: 'DBACNYA~CPXxRfAPXxR', applicableSections: [7, 8]}, + gdprConsent: {consentString: 'kjfdniwjnifwenrif3', gdprApplies: true}, + }, + + expected: [ + {type: 'image', url: 'https://some.1.url?gdpr=1&gdpr_consent=kjfdniwjnifwenrif3&us_privacy=1---&gpp=DBACNYA~CPXxRfAPXxR&gpp_sid=7,8'}, + {type: 'image', url: 'https://some.2.url?us_privacy=1---&gpp=DBACNYA~CPXxRfAPXxR&gpp_sid=7,8'}, + {type: 'image', url: 'https://some.3.url?param=1234'}, + ], + }, + + { + title: 'pixels - responses is empty ({iframeEnabled: true, pixelEnabled: true})', + data: { + syncOptions: {iframeEnabled: true, pixelEnabled: true}, + responses: [], + uspConsent: '1---', + gppConsent: {gppString: 'DBACNYA~CPXxRfAPXxR', applicableSections: [7, 8]}, + gdprConsent: {consentString: 'kjfdniwjnifwenrif3', gdprApplies: true}, + }, + + expected: [ + {type: 'image', url: 'https://cookies.nextmillmedia.com/sync?gdpr=1&gdpr_consent=kjfdniwjnifwenrif3&us_privacy=1---&gpp=DBACNYA~CPXxRfAPXxR&gpp_sid=7,8&type=image'}, + {type: 'iframe', url: 'https://cookies.nextmillmedia.com/sync?gdpr=1&gdpr_consent=kjfdniwjnifwenrif3&us_privacy=1---&gpp=DBACNYA~CPXxRfAPXxR&gpp_sid=7,8&type=iframe'}, + ], + }, + + { + title: 'pixels - responses is empty ({iframeEnabled: true, pixelEnabled: false})', + data: { + syncOptions: {iframeEnabled: true, pixelEnabled: false}, + uspConsent: '1---', + gppConsent: {gppString: 'DBACNYA~CPXxRfAPXxR', applicableSections: [7, 8]}, + gdprConsent: {consentString: 'kjfdniwjnifwenrif3', gdprApplies: true}, + }, + + expected: [ + {type: 'iframe', url: 'https://cookies.nextmillmedia.com/sync?gdpr=1&gdpr_consent=kjfdniwjnifwenrif3&us_privacy=1---&gpp=DBACNYA~CPXxRfAPXxR&gpp_sid=7,8&type=iframe'}, + ], + }, + + { + title: 'pixels - responses is empty ({iframeEnabled: false, pixelEnabled: false})', + data: { + syncOptions: {iframeEnabled: false, pixelEnabled: false}, + uspConsent: '1---', + gppConsent: {gppString: 'DBACNYA~CPXxRfAPXxR', applicableSections: [7, 8]}, + gdprConsent: {consentString: 'kjfdniwjnifwenrif3', gdprApplies: true}, + }, + + expected: [], + }, + ]; + + for (let {title, data, expected} of dataTests) { + it(title, () => { + const {syncOptions, responses, gdprConsent, uspConsent, gppConsent} = data; + const pixels = spec.getUserSyncs(syncOptions, responses, gdprConsent, uspConsent, gppConsent); + expect(pixels).to.deep.equal(expected); + }); + } + }); + + describe('function setOrtb2Parameters', () => { + const dataTests = [ + { + title: 'site.pagecat, site.content.cat and site.content.language', + data: { + postBody: {}, + ortb2: {site: { + pagecat: ['IAB2-11', 'IAB2-12', 'IAB2-14'], + content: {cat: ['IAB2-11', 'IAB2-12', 'IAB2-14'], language: 'EN'}, + }}, + }, + + expected: {site: { + pagecat: ['IAB2-11', 'IAB2-12', 'IAB2-14'], + content: {cat: ['IAB2-11', 'IAB2-12', 'IAB2-14'], language: 'EN'}, + }}, + }, + + { + title: 'only site.content.language', + data: { + postBody: {site: {domain: 'some.domain'}}, + ortb2: {site: { + content: {language: 'EN'}, + }}, + }, + + expected: {site: { + domain: 'some.domain', + content: {language: 'EN'}, + }}, + }, + + { + title: 'object ortb2 is empty', + data: { + postBody: {imp: []}, + }, + + expected: {imp: []}, + }, + ]; + + for (let {title, data, expected} of dataTests) { + it(title, () => { + const {postBody, ortb2} = data; + setOrtb2Parameters(postBody, ortb2); + expect(postBody).to.deep.equal(expected); + }); + } + }); + + const bidRequestData = [{ + adUnitCode: 'test-div', + bidId: 'bid1234', + auctionId: 'b06c5141-fe8f-4cdf-9d7d-54415490a917', + bidder: 'nextMillennium', + params: { placement_id: '-1' }, + sizes: [[300, 250]], + uspConsent: '1---', + gppConsent: {gppString: 'DBACNYA~CPXxRfAPXxR', applicableSections: [7]}, + gdprConsent: { + consentString: 'kjfdniwjnifwenrif3', + gdprApplies: true + }, + + ortb2: { + device: { + w: 1500, + h: 1000 + }, + + site: { + domain: 'example.com', + page: 'http://example.com' } } - ]; + }]; const serverResponse = { body: { @@ -49,7 +454,7 @@ describe('nextMillenniumBidAdapterTests', function() { cur: 'USD', ext: { sync: { - image: ['urlA?gdpr={{.GDPR}}'], + image: ['urlA?gdpr={{.GDPR}}&gpp={{.GPP}}&gpp_sid={{.GPPSID}}'], iframe: ['urlB'], } } @@ -117,61 +522,6 @@ describe('nextMillenniumBidAdapterTests', function() { }, ]; - it('Request params check with GDPR and USP Consent', function () { - const request = spec.buildRequests(bidRequestData, bidRequestData[0]); - expect(JSON.parse(request[0].data).user.ext.consent).to.equal('kjfdniwjnifwenrif3'); - expect(JSON.parse(request[0].data).regs.ext.us_privacy).to.equal('1---'); - expect(JSON.parse(request[0].data).regs.ext.gdpr).to.equal(1); - }); - - it('Test getUserSyncs function', function () { - const syncOptions = { - 'iframeEnabled': false, - 'pixelEnabled': true - } - let userSync = spec.getUserSyncs(syncOptions, [serverResponse], bidRequestData[0].gdprConsent, bidRequestData[0].uspConsent); - expect(userSync).to.be.an('array').with.lengthOf(1); - expect(userSync[0].type).to.equal('image'); - expect(userSync[0].url).to.equal('urlA?gdpr=1'); - - syncOptions.iframeEnabled = true; - syncOptions.pixelEnabled = false; - userSync = spec.getUserSyncs(syncOptions, [serverResponse], bidRequestData[0].gdprConsent, bidRequestData[0].uspConsent); - expect(userSync).to.be.an('array').with.lengthOf(1); - expect(userSync[0].type).to.equal('iframe'); - expect(userSync[0].url).to.equal('urlB'); - }); - - it('Test getUserSyncs with no response', function () { - const syncOptions = { - 'iframeEnabled': true, - 'pixelEnabled': false - } - let userSync = spec.getUserSyncs(syncOptions, [], bidRequestData[0].gdprConsent, bidRequestData[0].uspConsent); - expect(userSync).to.be.an('array') - expect(userSync[0].type).to.equal('iframe') - expect(userSync[0].url).to.equal('https://cookies.nextmillmedia.com/sync?gdpr=1&gdpr_consent=kjfdniwjnifwenrif3&us_privacy=1---&type=iframe') - }) - - it('Test getUserSyncs function if GDPR is undefined', function () { - const syncOptions = { - 'iframeEnabled': false, - 'pixelEnabled': true - } - - let userSync = spec.getUserSyncs(syncOptions, [serverResponse], undefined, bidRequestData[0].uspConsent); - expect(userSync).to.be.an('array').with.lengthOf(1); - expect(userSync[0].type).to.equal('image'); - expect(userSync[0].url).to.equal('urlA?gdpr=0'); - }); - - it('Request params check without GDPR Consent', function () { - delete bidRequestData[0].gdprConsent - const request = spec.buildRequests(bidRequestData, bidRequestData[0]); - expect(JSON.parse(request[0].data).regs.ext.gdpr).to.be.undefined; - expect(JSON.parse(request[0].data).regs.ext.us_privacy).to.equal('1---'); - }); - it('validate_generated_params', function() { const request = spec.buildRequests(bidRequestData, {bidderRequestId: 'mock-uuid'}); expect(request[0].bidId).to.equal('bid1234'); @@ -190,7 +540,7 @@ describe('nextMillenniumBidAdapterTests', function() { it('Check if refresh_count param is incremented', function() { const request = spec.buildRequests(bidRequestData); - expect(JSON.parse(request[0].data).ext.nextMillennium.refresh_count).to.equal(3); + expect(JSON.parse(request[0].data).ext.nextMillennium.refresh_count).to.equal(1); }); it('Check if domain was added', function() { From 4f3826a1a89837ff26e5acde1116880763334a5d Mon Sep 17 00:00:00 2001 From: Viktor Dreiling <34981284+3link@users.noreply.github.com> Date: Mon, 4 Dec 2023 16:44:37 +0100 Subject: [PATCH 042/246] Replace openx.com with openx.net (#10786) --- modules/liveIntentIdSystem.js | 2 +- test/spec/modules/eids_spec.js | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/modules/liveIntentIdSystem.js b/modules/liveIntentIdSystem.js index bb56355de31..a1f156ca23d 100644 --- a/modules/liveIntentIdSystem.js +++ b/modules/liveIntentIdSystem.js @@ -330,7 +330,7 @@ export const liveIntentIdSubmodule = { } }, 'openx': { - source: 'openx.com', + source: 'openx.net', atype: 3, getValue: function(data) { return data.id; diff --git a/test/spec/modules/eids_spec.js b/test/spec/modules/eids_spec.js index fa271b5114e..25e70f12ced 100644 --- a/test/spec/modules/eids_spec.js +++ b/test/spec/modules/eids_spec.js @@ -344,7 +344,7 @@ describe('eids array generation for known sub-modules', function() { const newEids = createEidsArray(userId); expect(newEids.length).to.equal(1); expect(newEids[0]).to.deep.equal({ - source: 'openx.com', + source: 'openx.net', uids: [{ id: 'sample_id', atype: 3 @@ -359,7 +359,7 @@ describe('eids array generation for known sub-modules', function() { const newEids = createEidsArray(userId); expect(newEids.length).to.equal(1); expect(newEids[0]).to.deep.equal({ - source: 'openx.com', + source: 'openx.net', uids: [{ id: 'sample_id', atype: 3, From 27b57ab24488b3787587fce9217db2dc7e84f404 Mon Sep 17 00:00:00 2001 From: YakirLavi <73641910+YakirLavi@users.noreply.github.com> Date: Tue, 5 Dec 2023 06:32:06 +0200 Subject: [PATCH 043/246] Rise Bid Adapter : support currency (#10790) * add Rise adapter * fixes * change param isOrg to org * Rise adapter * change email for rise * fix circle failed * bump * bump * bump * remove space * Upgrade Rise adapter to 5.0 * added isWrapper param * addes is_wrapper parameter to documentation * added is_wrapper to test * removed isWrapper * Rise Bid Adapter: support Coppa param (#24) * MinuteMedia Bid Adapter: support Coppa param (#25) * Revert "MinuteMedia Bid Adapter: support Coppa param (#25)" (#26) This reverts commit 66c4e7b46121afc5331c8bca6e2fc972fc55f090. * bump * update coppa fetch * setting coppa param update * update Coppa tests * update test naming * Rise Bid Adapter: support plcmt and sua (#27) * update minuteMediaBidAdapter - support missing params (#29) * RIseBidAdapter: support currency (#35) --------- Co-authored-by: Noam Tzuberi Co-authored-by: noamtzu Co-authored-by: Noam Tzuberi Co-authored-by: Laslo Chechur Co-authored-by: OronW <41260031+OronW@users.noreply.github.com> Co-authored-by: lasloche <62240785+lasloche@users.noreply.github.com> Co-authored-by: inna Co-authored-by: YakirLavi --- modules/riseBidAdapter.js | 19 +++++++++++-------- modules/riseBidAdapter.md | 1 + test/spec/modules/riseBidAdapter_spec.js | 12 +++++++++++- 3 files changed, 23 insertions(+), 9 deletions(-) diff --git a/modules/riseBidAdapter.js b/modules/riseBidAdapter.js index 78740f7f87d..44cc91aa87b 100644 --- a/modules/riseBidAdapter.js +++ b/modules/riseBidAdapter.js @@ -19,7 +19,7 @@ const SUPPORTED_AD_TYPES = [BANNER, VIDEO]; const BIDDER_CODE = 'rise'; const ADAPTER_VERSION = '6.0.0'; const TTL = 360; -const CURRENCY = 'USD'; +const DEFAULT_CURRENCY = 'USD'; const DEFAULT_SELLER_ENDPOINT = 'https://hb.yellowblue.io/'; const MODES = { PRODUCTION: 'hb-multi', @@ -73,7 +73,7 @@ export const spec = { const bidResponse = { requestId: adUnit.requestId, cpm: adUnit.cpm, - currency: adUnit.currency || CURRENCY, + currency: adUnit.currency || DEFAULT_CURRENCY, width: adUnit.width, height: adUnit.height, ttl: adUnit.ttl || TTL, @@ -140,18 +140,20 @@ registerBidder(spec); /** * Get floor price * @param bid {bid} + * @param mediaType {string} + * @param currency {string} * @returns {Number} */ -function getFloor(bid, mediaType) { +function getFloor(bid, mediaType, currency) { if (!isFn(bid.getFloor)) { return 0; } let floorResult = bid.getFloor({ - currency: CURRENCY, + currency: currency, mediaType: mediaType, size: '*' }); - return floorResult.currency === CURRENCY && floorResult.floor ? floorResult.floor : 0; + return floorResult.currency === currency && floorResult.floor ? floorResult.floor : 0; } /** @@ -289,7 +291,7 @@ function generateBidParameters(bid, bidderRequest) { const {params} = bid; const mediaType = isBanner(bid) ? BANNER : VIDEO; const sizesArray = getSizesArray(bid, mediaType); - + const currency = params.currency || config.getConfig('currency.adServerCurrency') || DEFAULT_CURRENCY; // fix floor price in case of NAN if (isNaN(params.floorPrice)) { params.floorPrice = 0; @@ -299,12 +301,13 @@ function generateBidParameters(bid, bidderRequest) { mediaType, adUnitCode: getBidIdParameter('adUnitCode', bid), sizes: sizesArray, - floorPrice: Math.max(getFloor(bid, mediaType), params.floorPrice), + currency: currency, + floorPrice: Math.max(getFloor(bid, mediaType, currency), params.floorPrice), bidId: getBidIdParameter('bidId', bid), bidderRequestId: getBidIdParameter('bidderRequestId', bid), loop: getBidIdParameter('bidderRequestsCount', bid), transactionId: bid.ortb2Imp?.ext?.tid, - coppa: 0 + coppa: 0, }; const pos = deepAccess(bid, `mediaTypes.${mediaType}.pos`); diff --git a/modules/riseBidAdapter.md b/modules/riseBidAdapter.md index f0837cb5508..ac0ea559c88 100644 --- a/modules/riseBidAdapter.md +++ b/modules/riseBidAdapter.md @@ -26,6 +26,7 @@ The adapter supports Video(instream). | `testMode` | optional | Boolean | This activates the test mode | false | `rtbDomain` | optional | String | Sets the seller end point | "www.test.com" | `is_wrapper` | private | Boolean | Please don't use unless your account manager asked you to | false +| `currency` | optional | String | 3 letters currency | "EUR" # Test Parameters diff --git a/test/spec/modules/riseBidAdapter_spec.js b/test/spec/modules/riseBidAdapter_spec.js index eed8d74f271..4be56c79913 100644 --- a/test/spec/modules/riseBidAdapter_spec.js +++ b/test/spec/modules/riseBidAdapter_spec.js @@ -53,7 +53,7 @@ describe('riseAdapter', function () { 'adUnitCode': 'adunit-code', 'sizes': [[640, 480]], 'params': { - 'org': 'jdye8weeyirk00000001' + 'org': 'jdye8weeyirk00000001', }, 'bidId': '299ffc8cca0b87', 'loop': 1, @@ -195,6 +195,16 @@ describe('riseAdapter', function () { expect(request.data.bids[1].mediaType).to.equal(BANNER) }); + it('should send the correct currency in bid request', function () { + const bid = utils.deepClone(bidRequests[0]); + bid.params = { + 'currency': 'EUR' + }; + const expectedCurrency = bid.params.currency; + const request = spec.buildRequests([bid], bidderRequest); + expect(request.data.bids[0].currency).to.equal(expectedCurrency); + }); + it('should respect syncEnabled option', function() { config.setConfig({ userSync: { From 645b562078ac32ebcda54738b2e9e2fb68c5b878 Mon Sep 17 00:00:00 2001 From: YakirLavi <73641910+YakirLavi@users.noreply.github.com> Date: Tue, 5 Dec 2023 06:43:03 +0200 Subject: [PATCH 044/246] MinuteMedia Bid Adapter : support currency (#10791) * add Rise adapter * fixes * change param isOrg to org * Rise adapter * change email for rise * fix circle failed * bump * bump * bump * remove space * Upgrade Rise adapter to 5.0 * added isWrapper param * addes is_wrapper parameter to documentation * added is_wrapper to test * removed isWrapper * Rise Bid Adapter: support Coppa param (#24) * MinuteMedia Bid Adapter: support Coppa param (#25) * Revert "MinuteMedia Bid Adapter: support Coppa param (#25)" (#26) This reverts commit 66c4e7b46121afc5331c8bca6e2fc972fc55f090. * bump * update coppa fetch * setting coppa param update * update Coppa tests * update test naming * Rise Bid Adapter: support plcmt and sua (#27) * update minuteMediaBidAdapter - support missing params (#29) * add currency param to bid object and tests * update getFloor function and tests * adding test to currency param * adding doc & currency bidfloor support & update tests * update currency test * remove default test --------- Co-authored-by: Noam Tzuberi Co-authored-by: noamtzu Co-authored-by: Noam Tzuberi Co-authored-by: Laslo Chechur Co-authored-by: OronW <41260031+OronW@users.noreply.github.com> Co-authored-by: lasloche <62240785+lasloche@users.noreply.github.com> Co-authored-by: inna Co-authored-by: YakirLavi --- modules/minutemediaBidAdapter.js | 16 +++++++++------- modules/minutemediaBidAdapter.md | 1 + test/spec/modules/minutemediaBidAdapter_spec.js | 10 ++++++++++ 3 files changed, 20 insertions(+), 7 deletions(-) diff --git a/modules/minutemediaBidAdapter.js b/modules/minutemediaBidAdapter.js index e67534d74fe..a4158e2abfa 100644 --- a/modules/minutemediaBidAdapter.js +++ b/modules/minutemediaBidAdapter.js @@ -19,7 +19,7 @@ const SUPPORTED_AD_TYPES = [BANNER, VIDEO]; const BIDDER_CODE = 'minutemedia'; const ADAPTER_VERSION = '6.0.0'; const TTL = 360; -const CURRENCY = 'USD'; +const DEFAULT_CURRENCY = 'USD'; const SELLER_ENDPOINT = 'https://hb.minutemedia-prebid.com/'; const MODES = { PRODUCTION: 'hb-mm-multi', @@ -72,7 +72,7 @@ export const spec = { const bidResponse = { requestId: adUnit.requestId, cpm: adUnit.cpm, - currency: adUnit.currency || CURRENCY, + currency: adUnit.currency || DEFAULT_CURRENCY, width: adUnit.width, height: adUnit.height, ttl: adUnit.ttl || TTL, @@ -141,16 +141,16 @@ registerBidder(spec); * @param bid {bid} * @returns {Number} */ -function getFloor(bid, mediaType) { +function getFloor(bid, mediaType, currency) { if (!isFn(bid.getFloor)) { return 0; } let floorResult = bid.getFloor({ - currency: CURRENCY, + currency: currency, mediaType: mediaType, size: '*' }); - return floorResult.currency === CURRENCY && floorResult.floor ? floorResult.floor : 0; + return floorResult.currency === currency && floorResult.floor ? floorResult.floor : 0; } /** @@ -286,6 +286,7 @@ function generateBidParameters(bid, bidderRequest) { const {params} = bid; const mediaType = isBanner(bid) ? BANNER : VIDEO; const sizesArray = getSizesArray(bid, mediaType); + const currency = params.currency || config.getConfig('currency.adServerCurrency') || DEFAULT_CURRENCY; // fix floor price in case of NAN if (isNaN(params.floorPrice)) { @@ -296,12 +297,13 @@ function generateBidParameters(bid, bidderRequest) { mediaType, adUnitCode: getBidIdParameter('adUnitCode', bid), sizes: sizesArray, - floorPrice: Math.max(getFloor(bid, mediaType), params.floorPrice), + currency: currency, + floorPrice: Math.max(getFloor(bid, mediaType, currency), params.floorPrice), bidId: getBidIdParameter('bidId', bid), loop: getBidIdParameter('bidderRequestsCount', bid), bidderRequestId: getBidIdParameter('bidderRequestId', bid), transactionId: bid.ortb2Imp?.ext?.tid || '', - coppa: 0 + coppa: 0, }; const pos = deepAccess(bid, `mediaTypes.${mediaType}.pos`); diff --git a/modules/minutemediaBidAdapter.md b/modules/minutemediaBidAdapter.md index 66b54adaf0e..fdfdf1b32bf 100644 --- a/modules/minutemediaBidAdapter.md +++ b/modules/minutemediaBidAdapter.md @@ -24,6 +24,7 @@ The adapter supports Video(instream) & Banner. | `floorPrice` | optional | Number | Minimum price in USD. Misuse of this parameter can impact revenue | 2.00 | `placementId` | optional | String | A unique placement identifier | "12345678" | `testMode` | optional | Boolean | This activates the test mode | false +| `currency` | optional | String | 3 letters currency | "EUR" # Test Parameters ```javascript diff --git a/test/spec/modules/minutemediaBidAdapter_spec.js b/test/spec/modules/minutemediaBidAdapter_spec.js index 48f694bc79d..7e22114aeed 100644 --- a/test/spec/modules/minutemediaBidAdapter_spec.js +++ b/test/spec/modules/minutemediaBidAdapter_spec.js @@ -178,6 +178,16 @@ describe('minutemediaAdapter', function () { expect(request.data.bids[1].mediaType).to.equal(BANNER) }); + it('should send the correct currency in bid request', function () { + const bid = utils.deepClone(bidRequests[0]); + bid.params = { + 'currency': 'EUR' + }; + const expectedCurrency = bid.params.currency; + const request = spec.buildRequests([bid], bidderRequest); + expect(request.data.bids[0].currency).to.equal(expectedCurrency); + }); + it('should respect syncEnabled option', function() { config.setConfig({ userSync: { From 55ba11177e9e59d7a6fcb8a9c1c9d896447117f0 Mon Sep 17 00:00:00 2001 From: Patrick Clinger Date: Tue, 5 Dec 2023 12:02:23 -0800 Subject: [PATCH 045/246] Core: Add eventString to error executing handler message (#10797) * Add detail to error executing handler * Add test case for including eventString --- src/events.js | 2 +- test/spec/unit/core/events_spec.js | 19 +++++++++++++++++-- 2 files changed, 18 insertions(+), 3 deletions(-) diff --git a/src/events.js b/src/events.js index 6eb7ce49f11..750ecdd8eee 100644 --- a/src/events.js +++ b/src/events.js @@ -80,7 +80,7 @@ const _public = (function () { try { fn.apply(null, args); } catch (e) { - utils.logError('Error executing handler:', 'events.js', e); + utils.logError('Error executing handler:', 'events.js', e, eventString); } }); } diff --git a/test/spec/unit/core/events_spec.js b/test/spec/unit/core/events_spec.js index 6551c9f2456..e1451f657b5 100644 --- a/test/spec/unit/core/events_spec.js +++ b/test/spec/unit/core/events_spec.js @@ -1,5 +1,6 @@ import {config} from 'src/config.js'; -import {emit, clearEvents, getEvents} from '../../../../src/events.js'; +import {emit, clearEvents, getEvents, on, off} from '../../../../src/events.js'; +import * as utils from '../../../../src/utils.js' describe('events', () => { let clock; @@ -26,5 +27,19 @@ describe('events', () => { config.setConfig({eventHistoryTTL: 1000}); clock.tick(10000); expect(getEvents().length).to.eql(1); - }) + }); + + it('should include the eventString if a callback fails', () => { + const logErrorStub = sinon.stub(utils, 'logError'); + const eventString = 'bidWon'; + let fn = function() { throw new Error('Test error'); }; + on(eventString, fn); + + emit(eventString, {}); + + sinon.assert.calledWith(logErrorStub, 'Error executing handler:', 'events.js', sinon.match.instanceOf(Error), eventString); + + off(eventString, fn); + logErrorStub.restore(); + }); }) From f818b8f8356d7f5b27e02d92624fe52ad7bfb11d Mon Sep 17 00:00:00 2001 From: Olivier Date: Tue, 5 Dec 2023 21:19:30 +0100 Subject: [PATCH 046/246] Adagio Bid Adapter: remove useless adrequest fields (#10794) --- modules/adagioBidAdapter.js | 41 +++++++++++++--------- test/spec/modules/adagioBidAdapter_spec.js | 11 ------ 2 files changed, 25 insertions(+), 27 deletions(-) diff --git a/modules/adagioBidAdapter.js b/modules/adagioBidAdapter.js index 1f3d6040d34..59d1fa72cbb 100644 --- a/modules/adagioBidAdapter.js +++ b/modules/adagioBidAdapter.js @@ -1020,6 +1020,9 @@ export const spec = { } } + // Enforce the organizationId param to be a string + bidRequest.params.organizationId = bidRequest.params.organizationId.toString(); + // Force the Data Layer key and value to be a String if (bidRequest.params.dataLayer) { if (isStr(bidRequest.params.dataLayer) || isNumber(bidRequest.params.dataLayer) || isArray(bidRequest.params.dataLayer) || isFn(bidRequest.params.dataLayer)) { @@ -1120,30 +1123,36 @@ export const spec = { bidRequest.gpid = gpid; } + // store the whole bidRequest (adUnit) object in the ADAGIO namespace. storeRequestInAdagioNS(bidRequest); - // Remove these fields at the very end, so we can still use them before. - delete bidRequest.transactionId; - delete bidRequest.ortb2Imp; - delete bidRequest.ortb2; - delete bidRequest.sizes; + // Remove some params that are not needed on the server side. + delete bidRequest.params.siteId; + + // whitelist the fields that are allowed to be sent to the server. + const adUnit = { + adUnitCode: bidRequest.adUnitCode, + auctionId: bidRequest.auctionId, + bidder: bidRequest.bidder, + bidId: bidRequest.bidId, + params: bidRequest.params, + features: bidRequest.features, + gpid: bidRequest.gpid, + mediaTypes: bidRequest.mediaTypes, + nativeParams: bidRequest.nativeParams, + score: bidRequest.score, + transactionId: bidRequest.transactionId, + } - return bidRequest; + return adUnit; }); // Group ad units by organizationId const groupedAdUnits = adUnits.reduce((groupedAdUnits, adUnit) => { - const adUnitCopy = deepClone(adUnit); - adUnitCopy.params.organizationId = adUnitCopy.params.organizationId.toString(); - - // remove useless props - delete adUnitCopy.floorData; - delete adUnitCopy.params.siteId; - delete adUnitCopy.userId; - delete adUnitCopy.userIdAsEids; + const organizationId = adUnit.params.organizationId - groupedAdUnits[adUnitCopy.params.organizationId] = groupedAdUnits[adUnitCopy.params.organizationId] || []; - groupedAdUnits[adUnitCopy.params.organizationId].push(adUnitCopy); + groupedAdUnits[organizationId] = groupedAdUnits[organizationId] || []; + groupedAdUnits[organizationId].push(adUnit); return groupedAdUnits; }, {}); diff --git a/test/spec/modules/adagioBidAdapter_spec.js b/test/spec/modules/adagioBidAdapter_spec.js index 7f0b31b9680..744f3c69e83 100644 --- a/test/spec/modules/adagioBidAdapter_spec.js +++ b/test/spec/modules/adagioBidAdapter_spec.js @@ -861,11 +861,6 @@ describe('Adagio bid adapter', () => { } 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'}); - expect(requests[0].data.adUnits[0].mediaTypes.banner.sizes.length).to.equal(2); expect(requests[0].data.adUnits[0].mediaTypes.banner.bannerSizes[0]).to.deep.equal({size: [300, 250], floor: 1}); expect(requests[0].data.adUnits[0].mediaTypes.banner.bannerSizes[1]).to.deep.equal({size: [300, 600], floor: 1}); @@ -890,10 +885,6 @@ describe('Adagio bid adapter', () => { } 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'}); - expect(requests[0].data.adUnits[0].mediaTypes.video.floor).to.equal(1); expect(requests[0].data.adUnits[0].mediaTypes.native.floor).to.equal(1); }); @@ -913,8 +904,6 @@ describe('Adagio bid adapter', () => { } 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({mt: 'video'}); expect(requests[0].data.adUnits[0].mediaTypes.video.floor).to.be.undefined; }); }); From 9a65d49ca70fd0145dcc1c67739c183d541337a3 Mon Sep 17 00:00:00 2001 From: Vladimir Fedoseev Date: Tue, 5 Dec 2023 22:59:35 +0100 Subject: [PATCH 047/246] VIS.X: add response time tracking on bidWon event (#10772) --- modules/visxBidAdapter.js | 23 ++++++++++++++ test/spec/modules/visxBidAdapter_spec.js | 40 +++++++++++++++++++++++- 2 files changed, 62 insertions(+), 1 deletion(-) diff --git a/modules/visxBidAdapter.js b/modules/visxBidAdapter.js index a45a1db9ece..a86c958392e 100644 --- a/modules/visxBidAdapter.js +++ b/modules/visxBidAdapter.js @@ -15,6 +15,7 @@ const TIME_TO_LIVE = 360; const DEFAULT_CUR = 'EUR'; const ADAPTER_SYNC_PATH = '/push_sync'; const TRACK_TIMEOUT_PATH = '/track/bid_timeout'; +const RUNTIME_STATUS_RESPONSE_TIME = 999000; const LOG_ERROR_MESS = { noAuid: 'Bid from response has no auid parameter - ', noAdm: 'Bid from response has no adm parameter - ', @@ -33,6 +34,7 @@ const LOG_ERROR_MESS = { }; const currencyWhiteList = ['EUR', 'USD', 'GBP', 'PLN']; export const storage = getStorageManager({bidderCode: BIDDER_CODE}); +const _bidResponseTimeLogged = []; export const spec = { code: BIDDER_CODE, gvlid: GVLID, @@ -208,6 +210,12 @@ export const spec = { if (bid.ext && bid.ext.events && bid.ext.events.win) { triggerPixel(bid.ext.events.win); } + // Call 'track/runtime' with the corresponding bid.requestId - only once per auction + if (bid.ext && bid.ext.events && bid.ext.events.runtime && !_bidResponseTimeLogged.includes(bid.auctionId)) { + _bidResponseTimeLogged.push(bid.auctionId); + const _roundedTime = _roundResponseTime(bid.timeToRespond, 50); + triggerPixel(bid.ext.events.runtime.replace('{STATUS_CODE}', RUNTIME_STATUS_RESPONSE_TIME + _roundedTime)); + } }, onTimeout: function(timeoutData) { // Call '/track/bid_timeout' with timeout data @@ -321,6 +329,10 @@ function _addBidResponse(serverBid, bidsMap, currency, bidResponses) { if (serverBid.ext && serverBid.ext.prebid) { bidResponse.ext = serverBid.ext.prebid; + if (serverBid.ext.visx && serverBid.ext.visx.events) { + const prebidExtEvents = bidResponse.ext.events || {}; + bidResponse.ext.events = Object.assign(prebidExtEvents, serverBid.ext.visx.events); + } } const visxTargeting = deepAccess(serverBid, 'ext.prebid.targeting'); @@ -434,4 +446,15 @@ function _getUserId() { return null; } +function _roundResponseTime(time, timeRange) { + if (time <= 0) { + return 0; // Special code for scriptLoadTime of 0 ms or less + } else if (time > 5000) { + return 100; // Constant code for scriptLoadTime greater than 5000 ms + } else { + const roundedValue = Math.floor((time - 1) / timeRange) + 1; + return roundedValue; + } +} + registerBidder(spec); diff --git a/test/spec/modules/visxBidAdapter_spec.js b/test/spec/modules/visxBidAdapter_spec.js index 139349ceead..5528705efd7 100755 --- a/test/spec/modules/visxBidAdapter_spec.js +++ b/test/spec/modules/visxBidAdapter_spec.js @@ -1257,6 +1257,7 @@ describe('VisxAdapter', function () { const request = spec.buildRequests(bidRequests); const pendingUrl = 'https://t.visx.net/track/pending/123123123'; const winUrl = 'https://t.visx.net/track/win/53245341'; + const runtimeUrl = 'https://t.visx.net/track/status/12345678'; const expectedResponse = [ { 'requestId': '300bfeb0d71a5b', @@ -1281,7 +1282,8 @@ describe('VisxAdapter', function () { 'ext': { 'events': { 'pending': pendingUrl, - 'win': winUrl + 'win': winUrl, + 'runtime': runtimeUrl }, 'targeting': { 'hb_visx_product': 'understitial', @@ -1298,6 +1300,9 @@ describe('VisxAdapter', function () { pending: pendingUrl, win: winUrl, }); + utils.deepSetValue(serverResponse.bid[0], 'ext.visx.events', { + runtime: runtimeUrl + }); const result = spec.interpretResponse({'body': {'seatbid': [serverResponse]}}, request); expect(result).to.deep.equal(expectedResponse); }); @@ -1325,6 +1330,39 @@ describe('VisxAdapter', function () { expect(utils.triggerPixel.calledOnceWith(trackUrl)).to.equal(true); }); + it('onBidWon with runtime tracker (0 < timeToRespond <= 5000 )', function () { + const trackUrl = 'https://t.visx.net/track/win/123123123'; + const runtimeUrl = 'https://t.visx.net/track/status/12345678/{STATUS_CODE}'; + const bid = { auctionId: '1', ext: { events: { win: trackUrl, runtime: runtimeUrl } }, timeToRespond: 100 }; + spec.onBidWon(bid); + expect(utils.triggerPixel.calledTwice).to.equal(true); + expect(utils.triggerPixel.calledWith(trackUrl)).to.equal(true); + expect(utils.triggerPixel.calledWith(runtimeUrl.replace('{STATUS_CODE}', 999002))).to.equal(true); + }); + + it('onBidWon with runtime tracker (timeToRespond <= 0 )', function () { + const runtimeUrl = 'https://t.visx.net/track/status/12345678/{STATUS_CODE}'; + const bid = { auctionId: '2', ext: { events: { runtime: runtimeUrl } }, timeToRespond: 0 }; + spec.onBidWon(bid); + expect(utils.triggerPixel.calledOnceWith(runtimeUrl.replace('{STATUS_CODE}', 999000))).to.equal(true); + }); + + it('onBidWon with runtime tracker (timeToRespond > 5000 )', function () { + const runtimeUrl = 'https://t.visx.net/track/status/12345678/{STATUS_CODE}'; + const bid = { auctionId: '3', ext: { events: { runtime: runtimeUrl } }, timeToRespond: 5001 }; + spec.onBidWon(bid); + expect(utils.triggerPixel.calledOnceWith(runtimeUrl.replace('{STATUS_CODE}', 999100))).to.equal(true); + }); + + it('onBidWon runtime tracker should be called once per auction', function () { + const runtimeUrl = 'https://t.visx.net/track/status/12345678/{STATUS_CODE}'; + const bid1 = { auctionId: '4', ext: { events: { runtime: runtimeUrl } }, timeToRespond: 100 }; + spec.onBidWon(bid1); + const bid2 = { auctionId: '4', ext: { events: { runtime: runtimeUrl } }, timeToRespond: 200 }; + spec.onBidWon(bid2); + expect(utils.triggerPixel.calledOnceWith(runtimeUrl.replace('{STATUS_CODE}', 999002))).to.equal(true); + }); + it('onTimeout', function () { const data = [{ timeout: 3000, adUnitCode: 'adunit-code-1', auctionId: '1cbd2feafe5e8b', bidder: 'visx', bidId: '23423', params: [{ uid: '1' }] }]; const expectedData = [{ timeout: 3000, params: [{ uid: 1 }] }]; From 264efa7f1cbe4bd7e81452a6b70892b4717805af Mon Sep 17 00:00:00 2001 From: tkrishnaviant Date: Wed, 6 Dec 2023 04:07:53 -0800 Subject: [PATCH 048/246] ViantOrtb Bid Adapter : update endpoint url (#10801) * Edited viantOrtbBidAdapter bidder endpoint url. * Update viantOrtbBidAdapter.js * Update viantOrtbBidAdapter.js --- modules/viantOrtbBidAdapter.js | 2 +- test/spec/modules/viantOrtbBidAdapter_spec.js | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/modules/viantOrtbBidAdapter.js b/modules/viantOrtbBidAdapter.js index e7bf9129a62..0f7953a192a 100644 --- a/modules/viantOrtbBidAdapter.js +++ b/modules/viantOrtbBidAdapter.js @@ -5,7 +5,7 @@ import {ortbConverter} from '../libraries/ortbConverter/converter.js' import {deepAccess, getBidIdParameter, logError} from '../src/utils.js'; const BIDDER_CODE = 'viant'; -const ENDPOINT = 'https://bidders-us-east-1.adelphic.net/d/rtb/v25/prebid/bidder_test' +const ENDPOINT = 'https://bidders-us-east-1.adelphic.net/d/rtb/v25/prebid/bidder' const DEFAULT_BID_TTL = 300; const DEFAULT_CURRENCY = 'USD'; diff --git a/test/spec/modules/viantOrtbBidAdapter_spec.js b/test/spec/modules/viantOrtbBidAdapter_spec.js index ef537d50986..6d5014bd639 100644 --- a/test/spec/modules/viantOrtbBidAdapter_spec.js +++ b/test/spec/modules/viantOrtbBidAdapter_spec.js @@ -172,7 +172,7 @@ describe('viantOrtbBidAdapter', function () { }); it('sends bid requests to the correct endpoint', function () { const url = testBuildRequests(baseBannerBidRequests, baseBidderRequest)[0].url; - expect(url).to.equal('https://bidders-us-east-1.adelphic.net/d/rtb/v25/prebid/bidder_test'); + expect(url).to.equal('https://bidders-us-east-1.adelphic.net/d/rtb/v25/prebid/bidder'); }); it('sends site', function () { From bf909eaa066c997ee169640008ee5933c1965874 Mon Sep 17 00:00:00 2001 From: github-saad-elmahfoudi <87966599+github-saad-elmahfoudi@users.noreply.github.com> Date: Wed, 6 Dec 2023 13:19:18 +0100 Subject: [PATCH 049/246] Teads Bid Adapter: add new window features to request (#10796) * Add new window features to HB request * fixup! Add new window features to HB request --- modules/teadsBidAdapter.js | 7 +++ test/spec/modules/teadsBidAdapter_spec.js | 58 +++++++++++++++++++++++ 2 files changed, 65 insertions(+) diff --git a/modules/teadsBidAdapter.js b/modules/teadsBidAdapter.js index 5e92af640b8..ba16c6ddf82 100644 --- a/modules/teadsBidAdapter.js +++ b/modules/teadsBidAdapter.js @@ -45,6 +45,7 @@ export const spec = { */ buildRequests: function(validBidRequests, bidderRequest) { const bids = validBidRequests.map(buildRequestObject); + const topWindow = window.top; const payload = { referrer: getReferrerInfo(bidderRequest), @@ -55,6 +56,12 @@ export const spec = { timeToFirstByte: getTimeToFirstByte(window), data: bids, deviceWidth: screen.width, + screenOrientation: screen.orientation?.type, + historyLength: topWindow.history?.length, + viewportHeight: topWindow.visualViewport?.height, + viewportWidth: topWindow.visualViewport?.width, + hardwareConcurrency: topWindow.navigator?.hardwareConcurrency, + deviceMemory: topWindow.navigator?.deviceMemory, hb_version: '$prebid.version$', ...getSharedViewerIdParameters(validBidRequests), ...getFirstPartyTeadsIdParameter(validBidRequests) diff --git a/test/spec/modules/teadsBidAdapter_spec.js b/test/spec/modules/teadsBidAdapter_spec.js index b0d5f436e0b..98f706f5193 100644 --- a/test/spec/modules/teadsBidAdapter_spec.js +++ b/test/spec/modules/teadsBidAdapter_spec.js @@ -1,6 +1,7 @@ import {expect} from 'chai'; import {spec, storage} from 'modules/teadsBidAdapter.js'; import {newBidder} from 'src/adapters/bidderFactory.js'; +import { off } from '../../../src/events'; const ENDPOINT = 'https://a.teads.tv/hb/bid-request'; const AD_SCRIPT = '"'; @@ -254,6 +255,63 @@ describe('teadsBidAdapter', () => { expect(payload.pageReferrer).to.deep.equal(document.referrer); }); + it('should add screenOrientation info to payload', function () { + const request = spec.buildRequests(bidRequests, bidderRequestDefault); + const payload = JSON.parse(request.data); + const screenOrientation = window.top.screen.orientation?.type + + if (screenOrientation) { + expect(payload.screenOrientation).to.exist; + expect(payload.screenOrientation).to.deep.equal(screenOrientation); + } else expect(payload.screenOrientation).to.not.exist; + }); + + it('should add historyLength info to payload', function () { + const request = spec.buildRequests(bidRequests, bidderRequestDefault); + const payload = JSON.parse(request.data); + + expect(payload.historyLength).to.exist; + expect(payload.historyLength).to.deep.equal(window.top.history.length); + }); + + it('should add viewportHeight info to payload', function () { + const request = spec.buildRequests(bidRequests, bidderRequestDefault); + const payload = JSON.parse(request.data); + + expect(payload.viewportHeight).to.exist; + expect(payload.viewportHeight).to.deep.equal(window.top.visualViewport.height); + }); + + it('should add viewportWidth info to payload', function () { + const request = spec.buildRequests(bidRequests, bidderRequestDefault); + const payload = JSON.parse(request.data); + + expect(payload.viewportWidth).to.exist; + expect(payload.viewportWidth).to.deep.equal(window.top.visualViewport.width); + }); + + it('should add hardwareConcurrency info to payload', function () { + const request = spec.buildRequests(bidRequests, bidderRequestDefault); + const payload = JSON.parse(request.data); + const hardwareConcurrency = window.top.navigator?.hardwareConcurrency + + if (hardwareConcurrency) { + expect(payload.hardwareConcurrency).to.exist; + expect(payload.hardwareConcurrency).to.deep.equal(hardwareConcurrency); + } else expect(payload.hardwareConcurrency).to.not.exist + }); + + it('should add deviceMemory info to payload', function () { + const request = spec.buildRequests(bidRequests, bidderRequestDefault); + const payload = JSON.parse(request.data); + const deviceMemory = window.top.navigator.deviceMemory + + if (deviceMemory) { + expect(payload.deviceMemory).to.exist; + expect(payload.deviceMemory).to.deep.equal(deviceMemory); + } else expect(payload.deviceMemory).to.not.exist; + }); + describe('pageTitle', function () { it('should add pageTitle info to payload based on document title', function () { const testText = 'This is a title'; From 0de4a099d2748b2aef3fd111fad9295947d055c2 Mon Sep 17 00:00:00 2001 From: geoffray-viously <95097046+geoffray-viously@users.noreply.github.com> Date: Thu, 7 Dec 2023 15:26:20 +0100 Subject: [PATCH 050/246] Sparteo Bid Adapter : handle vastUrl for video context (#10805) * SparteoBidAdapter: handle vastUrl for video context * Add: video tests --- modules/sparteoBidAdapter.js | 15 ++++++++++----- test/spec/modules/sparteoBidAdapter_spec.js | 10 ++++++++-- 2 files changed, 18 insertions(+), 7 deletions(-) diff --git a/modules/sparteoBidAdapter.js b/modules/sparteoBidAdapter.js index ac48ec4089d..93353c5dda7 100644 --- a/modules/sparteoBidAdapter.js +++ b/modules/sparteoBidAdapter.js @@ -38,7 +38,14 @@ const converter = ortbConverter({ bidResponse(buildBidResponse, bid, context) { context.mediaType = deepAccess(bid, 'ext.prebid.type'); - return buildBidResponse(bid, context) + const response = buildBidResponse(bid, context); + + if (context.mediaType == 'video') { + response.nurl = bid.nurl; + response.vastUrl = deepAccess(bid, 'ext.prebid.cache.vastXml.url') ?? null; + } + + return response; } }); @@ -120,10 +127,8 @@ export const spec = { onTimeout: function(timeoutData) {}, onBidWon: function(bid) { - if (bid && bid.nurl && bid.nurl.length > 0) { - bid.nurl.forEach(function(winUrl) { - triggerPixel(winUrl, null); - }); + if (bid && bid.nurl) { + triggerPixel(bid.nurl, null); } }, diff --git a/test/spec/modules/sparteoBidAdapter_spec.js b/test/spec/modules/sparteoBidAdapter_spec.js index d6b8b0cc2d8..71191b44a29 100644 --- a/test/spec/modules/sparteoBidAdapter_spec.js +++ b/test/spec/modules/sparteoBidAdapter_spec.js @@ -323,7 +323,12 @@ describe('SparteoAdapter', function () { 'price': 5, 'ext': { 'prebid': { - 'type': 'video' + 'type': 'video', + 'cache': { + 'vastXml': { + 'url': 'https://pbs.tet.com/cache?uuid=1234' + } + } } }, 'adm': 'tag', @@ -368,7 +373,8 @@ describe('SparteoAdapter', function () { ttl: TTL, mediaType: 'video', meta: {}, - vastUrl: 'https://t.bidder.sparteo.com/img', + nurl: 'https://t.bidder.sparteo.com/img', + vastUrl: 'https://pbs.tet.com/cache?uuid=1234', vastXml: 'tag' }); } From 864dc02001bbd6b3df67371adccf4e6f5e87ade2 Mon Sep 17 00:00:00 2001 From: mustafa kemal Date: Thu, 7 Dec 2023 17:42:52 +0300 Subject: [PATCH 051/246] deal support added (#10802) Co-authored-by: mustafakemal --- modules/theAdxBidAdapter.js | 12 +++- modules/theAdxBidAdapter.md | 19 +++--- test/spec/modules/theAdxBidAdapter_spec.js | 72 ++++++++++++++++++++++ 3 files changed, 92 insertions(+), 11 deletions(-) diff --git a/modules/theAdxBidAdapter.js b/modules/theAdxBidAdapter.js index 03f303498a1..def2446635b 100644 --- a/modules/theAdxBidAdapter.js +++ b/modules/theAdxBidAdapter.js @@ -159,7 +159,6 @@ export const spec = { withCredentials: true, }, bidder: 'theadx', - // TODO: is 'page' the right value here? referrer: encodeURIComponent(bidderRequest.refererInfo.page || ''), data: generatePayload(bidRequest, bidderRequest), mediaTypes: bidRequest['mediaTypes'], @@ -261,6 +260,7 @@ export const spec = { ad: creative, ttl: ttl || 3000, creativeId: bid.crid, + dealId: bid.dealid || null, netRevenue: true, currency: responseBody.cur, mediaType: mediaType, @@ -466,11 +466,19 @@ let generateImpBody = (bidRequest, bidderRequest) => { } else if (mediaTypes && mediaTypes.native) { native = generateNativeComponent(bidRequest, bidderRequest); } - const result = { id: bidRequest.index, tagid: bidRequest.params.tagId + '', }; + + // deals support + if (bidRequest.params.deals && Array.isArray(bidRequest.params.deals) && bidRequest.params.deals.length > 0) { + result.pmp = { + deals: bidRequest.params.deals, + private_auction: 0, + }; + } + if (banner) { result['banner'] = banner; } diff --git a/modules/theAdxBidAdapter.md b/modules/theAdxBidAdapter.md index 2392bfaa819..cdc7b8410a8 100644 --- a/modules/theAdxBidAdapter.md +++ b/modules/theAdxBidAdapter.md @@ -26,9 +26,9 @@ Module that connects to TheAdx demand sources { bidder: "theadx", params: { - pid: 1000, // publisher id - wid: 2000, //website id - tagId: 5000, //zone id + pid: 1, // publisher id + wid: 7, //website id + tagId: 19, //zone id } } ] @@ -43,9 +43,10 @@ Module that connects to TheAdx demand sources { bidder: "theadx", params: { - pid: 1000, // publisher id - wid: 2000, //website id - tagId: 5000, //zone id + pid: 1, // publisher id + wid: 7, //website id + tagId: 18, //zone id + deals:[{"id":"theadx:137"}] //optional } } ] @@ -80,9 +81,9 @@ Module that connects to TheAdx demand sources { bidder: "theadx", params: { - pid: 1000, // publisher id - wid: 2000, //website id - tagId: 5000, //zone id + pid: 1, // publisher id + wid: 7, //website id + tagId: 20, //zone id } } ] diff --git a/test/spec/modules/theAdxBidAdapter_spec.js b/test/spec/modules/theAdxBidAdapter_spec.js index 99e5156190c..eb00834421a 100644 --- a/test/spec/modules/theAdxBidAdapter_spec.js +++ b/test/spec/modules/theAdxBidAdapter_spec.js @@ -446,6 +446,78 @@ describe('TheAdxAdapter', function () { expect(processedBid.currency).to.equal(responseCurrency); }); + it('returns a valid deal bid response on sucessful banner request with deal', function () { + let incomingRequestId = 'XXtestingXX'; + let responsePrice = 3.14 + + let responseCreative = 'sample_creative&{FOR_COVARAGE}'; + + let responseCreativeId = '274'; + let responseCurrency = 'TRY'; + + let responseWidth = 300; + let responseHeight = 250; + let responseTtl = 213; + let dealId = 'theadx_deal_id'; + + let sampleResponse = { + id: '66043f5ca44ecd8f8769093b1615b2d9', + seatbid: [{ + bid: [{ + id: 'c21bab0e-7668-4d8f-908a-63e094c09197', + dealid: 'theadx_deal_id', + impid: '1', + price: responsePrice, + adid: responseCreativeId, + crid: responseCreativeId, + adm: responseCreative, + adomain: [ + 'www.domain.com' + ], + cid: '274', + attr: [], + w: responseWidth, + h: responseHeight, + ext: { + ttl: responseTtl + } + }], + seat: '201', + group: 0 + }], + bidid: 'c21bab0e-7668-4d8f-908a-63e094c09197', + cur: responseCurrency + }; + + let sampleRequest = { + bidId: incomingRequestId, + mediaTypes: { + banner: {} + }, + requestId: incomingRequestId, + deals: [{id: dealId}] + }; + let serverResponse = { + body: sampleResponse + } + let result = spec.interpretResponse(serverResponse, sampleRequest); + + expect(result.length).to.equal(1); + + let processedBid = result[0]; + + // expect(processedBid.requestId).to.equal(incomingRequestId); + expect(processedBid.cpm).to.equal(responsePrice); + expect(processedBid.width).to.equal(responseWidth); + expect(processedBid.height).to.equal(responseHeight); + expect(processedBid.ad).to.equal(responseCreative); + expect(processedBid.ttl).to.equal(responseTtl); + expect(processedBid.creativeId).to.equal(responseCreativeId); + expect(processedBid.netRevenue).to.equal(true); + expect(processedBid.currency).to.equal(responseCurrency); + expect(processedBid.dealId).to.equal(dealId); + }); + it('returns an valid bid response on sucessful video request', function () { let incomingRequestId = 'XXtesting-275XX'; let responsePrice = 6 From d0747e4618874b3c71c8ef494e0c273a496c361b Mon Sep 17 00:00:00 2001 From: BaronJHYu <254878848@qq.com> Date: Thu, 7 Dec 2023 23:16:21 +0800 Subject: [PATCH 052/246] Mediago Bid Adapter : add cookieSync and gdpr (#10724) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Mediago Bid Adapter:new adapter * remove console * change spec file to fix CircleCI * change spec file to fix CircleCI * change spec file * Update mediagoBidAdapter.js * Update mediagoBidAdapter.js * rerun CurcleCi * update mediagoBidAdapter * update discoveryBidAdapter * Discovery Bid Adapter : parameter updates * Mediago Bid Adapter : parameter updates * Mediago Bid Adapter : code style format * rerun circleci * rerun circleci * rerun circleci * rerun circleci * Update mediagoBidAdapter & discoveryBidAdapter:report eids to server * Update mediagoBidAdapter & discoveryBidAdapter:report eids to server * update Mediago & Discovery BidAdapter:remove size filter * update Mediago & Discovery BidAdapter:code format * update Mediago & Discovery BidAdapter:code format * update Mediago & Discovery BidAdapter:add param in banner format * update mediago & discovery:first party data * update mediago & discovery:first party data * update mediago & discovery:first party data * fix(mediago & discovery): update param tagid * fix(mediago & discovery): update param tagid * feat:add imp params for analysis * fix:add imp params for analysis * code format * test:add test data & test * format code * rerun circleci * feat: 1. add cookieSync 2. add gdpr * code format * rerun circleci * add gvlid * feat:add param 1.ppuid 2.content 3.cat 4.reqtime * fix: getUserSyncs param fixed * fix: getUserSyncs param fixed * feat: add param 1. adUnitCode 2. referrer --------- Co-authored-by: BaronYu --- modules/mediagoBidAdapter.js | 136 +++++++++++++++----- test/spec/modules/mediagoBidAdapter_spec.js | 130 +++++++++++++++---- 2 files changed, 212 insertions(+), 54 deletions(-) diff --git a/modules/mediagoBidAdapter.js b/modules/mediagoBidAdapter.js index efd730acace..72b06dfe7ed 100644 --- a/modules/mediagoBidAdapter.js +++ b/modules/mediagoBidAdapter.js @@ -13,14 +13,20 @@ const BIDDER_CODE = 'mediago'; const ENDPOINT_URL = // ((PROTOCOL === 'https:') ? 'https' : 'http') + 'https://rtb-us.mediago.io/api/bid?tn='; +const COOKY_SYNC_URL = 'https://trace.mediago.io/ju/cs/eplist'; +const COOKY_SYNC_IFRAME_URL = 'https://cdn.mediago.io/js/cookieSync.html'; + const TIME_TO_LIVE = 500; +const GVLID = 1020; // const ENDPOINT_URL = '/api/bid?tn='; -const storage = getStorageManager({bidderCode: BIDDER_CODE}); +const storage = getStorageManager({ bidderCode: BIDDER_CODE }); let globals = {}; let itemMaps = {}; /* ----- mguid:start ------ */ const COOKIE_KEY_MGUID = '__mguid_'; +const STORE_MAX_AGE = 1000 * 60 * 60 * 24 * 365; +let reqTimes = 0; /** * 获取用户id @@ -31,7 +37,7 @@ const getUserID = () => { if (i === null) { const uuid = utils.generateUUID(); - storage.setCookie(COOKIE_KEY_MGUID, uuid); + storage.setCookie(COOKIE_KEY_MGUID, uuid, STORE_MAX_AGE); return uuid; } return i; @@ -72,7 +78,7 @@ function isMobileAndTablet() { '.+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', + '|windows ce|xda|xiino|android|ipad|playbook|silk' ].join(''), 'i' ); @@ -96,7 +102,7 @@ function isMobileAndTablet() { '|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-', + '|your|zeto|zte-' ].join(''), 'i' ); @@ -138,7 +144,7 @@ function getBidFloor(bid) { const bidFloor = bid.getFloor({ currency: 'USD', mediaType: '*', - size: '*', + size: '*' }); return bidFloor.floor; } catch (_) { @@ -156,11 +162,7 @@ function transformSizes(requestSizes) { let sizes = []; let sizeObj = {}; - if ( - utils.isArray(requestSizes) && - requestSizes.length === 2 && - !utils.isArray(requestSizes[0]) - ) { + 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); @@ -187,7 +189,7 @@ const mediagoAdSize = [ { w: 160, h: 600 }, { w: 320, h: 180 }, { w: 320, h: 100 }, - { w: 336, h: 280 }, + { w: 336, h: 280 } ]; /** @@ -207,17 +209,13 @@ function getItems(validBidRequests, bidderRequest) { // 确认尺寸是否符合我们要求 for (let size of sizes) { - matchSize = mediagoAdSize.find( - (item) => size.width === item.w && size.height === item.h - ); + matchSize = mediagoAdSize.find(item => size.width === item.w && size.height === item.h); if (matchSize) { break; } } if (!matchSize) { - matchSize = sizes[0] - ? { h: sizes[0].height || 0, w: sizes[0].width || 0 } - : { h: 0, w: 0 }; + matchSize = sizes[0] ? { h: sizes[0].height || 0, w: sizes[0].width || 0 } : { h: 0, w: 0 }; } const bidFloor = getBidFloor(req); @@ -226,6 +224,18 @@ function getItems(validBidRequests, bidderRequest) { utils.deepAccess(req, 'ortb2Imp.ext.data.pbadslot') || utils.deepAccess(req, 'params.placementId', 0); + const gdprConsent = {}; + if (bidderRequest && bidderRequest.gdprConsent) { + gdprConsent.consent = bidderRequest.gdprConsent.consentString; + gdprConsent.gdpr = bidderRequest.gdprConsent.gdprApplies ? 1 : 0; + // if (bidderRequest.gdprConsent.addtlConsent && bidderRequest.gdprConsent.addtlConsent.indexOf('~') !== -1) { + // let ac = bidderRequest.gdprConsent.addtlConsent; + // // pull only the ids from the string (after the ~) and convert them to an array of ints + // let acStr = ac.substring(ac.indexOf('~') + 1); + // gdpr_consent.addtl_consent = acStr.split('.').map(id => parseInt(id, 10)); + // } + } + // if (mediaTypes.native) {} // banner广告类型 if (mediaTypes.banner) { @@ -237,17 +247,21 @@ function getItems(validBidRequests, bidderRequest) { h: matchSize.h, w: matchSize.w, pos: 1, - format: sizes, + format: sizes }, ext: { + adUnitCode: req.adUnitCode, + referrer: getReferrer(req, bidderRequest), ortb2Imp: utils.deepAccess(req, 'ortb2Imp'), // 传入完整对象,分析日志数据 gpid: gpid, // 加入后无法返回广告 + adslot: utils.deepAccess(req, 'ortb2Imp.ext.data.adserver.adslot', '', ''), + ...gdprConsent // gdpr }, - tagid: req.params && req.params.tagid, + tagid: req.params && req.params.tagid }; itemMaps[id] = { req, - ret, + ret }; } @@ -256,6 +270,21 @@ function getItems(validBidRequests, bidderRequest) { return items; } +/** + * @param {BidRequest} bidRequest + * @param bidderRequest + * @returns {string} + */ +function getReferrer(bidRequest = {}, bidderRequest = {}) { + let pageUrl; + if (bidRequest.params && bidRequest.params.referrer) { + pageUrl = bidRequest.params.referrer; + } else { + pageUrl = utils.deepAccess(bidderRequest, 'refererInfo.page'); + } + return pageUrl; +} + /** * 获取rtb请求参数 * @@ -268,7 +297,14 @@ function getParam(validBidRequests, bidderRequest) { const sharedid = utils.deepAccess(validBidRequests[0], 'userId.sharedid.id') || utils.deepAccess(validBidRequests[0], 'userId.pubcid'); - const eids = validBidRequests[0].userIdAsEids || validBidRequests[0].userId; + + const bidsUserIdAsEids = validBidRequests[0].userIdAsEids; + const bidsUserid = validBidRequests[0].userId; + const eids = bidsUserIdAsEids || bidsUserid; + const ppuid = bidsUserid && bidsUserid.pubProvidedId; + const content = utils.deepAccess(bidderRequest, 'ortb2.site.content'); + const cat = utils.deepAccess(bidderRequest, 'ortb2.site.cat'); + reqTimes += 1; let isMobile = isMobileAndTablet() ? 1 : 0; // input test status by Publisher. more frequently for test true req @@ -276,8 +312,7 @@ function getParam(validBidRequests, bidderRequest) { let auctionId = getProperty(bidderRequest, 'auctionId'); let items = getItems(validBidRequests, bidderRequest); - const domain = - utils.deepAccess(bidderRequest, 'refererInfo.domain') || document.domain; + const domain = utils.deepAccess(bidderRequest, 'refererInfo.domain') || document.domain; const location = utils.deepAccess(bidderRequest, 'refererInfo.location'); const page = utils.deepAccess(bidderRequest, 'refererInfo.page'); const referer = utils.deepAccess(bidderRequest, 'refererInfo.ref'); @@ -301,15 +336,21 @@ function getParam(validBidRequests, bidderRequest) { // ua: 'Mozilla/5.0 (Linux; Android 12; SM-G970U) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/109.0.0.0 Mobile Safari/537.36', os: navigator.platform || '', ua: navigator.userAgent, - language: /en/.test(navigator.language) ? 'en' : navigator.language, + language: /en/.test(navigator.language) ? 'en' : navigator.language }, ext: { eids, + bidsUserIdAsEids, + bidsUserid, + ppuid, firstPartyData, + content, + cat, + reqTimes }, user: { buyeruid: getUserID(), - id: sharedid || pubcid, + id: sharedid || pubcid }, eids, site: { @@ -322,11 +363,11 @@ function getParam(validBidRequests, bidderRequest) { publisher: { // todo id: domain, - name: domain, - }, + name: domain + } }, imp: items, - tmax: timeout, + tmax: timeout }; return c; } else { @@ -336,6 +377,7 @@ function getParam(validBidRequests, bidderRequest) { export const spec = { code: BIDDER_CODE, + gvlid: GVLID, // aliases: ['ex'], // short code /** * Determines whether or not the given bid request is valid. @@ -367,7 +409,7 @@ export const spec = { return { method: 'POST', url: ENDPOINT_URL + globals['token'], - data: payloadString, + data: payloadString }; }, @@ -398,7 +440,7 @@ export const spec = { ttl: TIME_TO_LIVE, // referrer: REFERER, ad: getProperty(bid, 'adm'), - nurl: getProperty(bid, 'nurl'), + nurl: getProperty(bid, 'nurl') // adserverTargeting: { // granularityMultiplier: 0.1, // priceGranularity: 'pbHg', @@ -415,6 +457,38 @@ export const spec = { return bidResponses; }, + getUserSyncs: function (syncOptions, serverResponse, gdprConsent, uspConsent, gppConsent) { + const origin = encodeURIComponent(location.origin || `https://${location.host}`); + let syncParamUrl = `dm=${origin}`; + + if (gdprConsent && gdprConsent.consentString) { + if (typeof gdprConsent.gdprApplies === 'boolean') { + syncParamUrl += `&gdpr=${Number(gdprConsent.gdprApplies)}&gdpr_consent=${gdprConsent.consentString}`; + } else { + syncParamUrl += `&gdpr=0&gdpr_consent=${gdprConsent.consentString}`; + } + } + if (uspConsent && uspConsent.consentString) { + syncParamUrl += `&ccpa_consent=${uspConsent.consentString}`; + } + + if (syncOptions.iframeEnabled) { + return [ + { + type: 'iframe', + url: `${COOKY_SYNC_IFRAME_URL}?${syncParamUrl}` + } + ]; + } else { + return [ + { + type: 'image', + url: `${COOKY_SYNC_URL}?${syncParamUrl}` + } + ]; + } + }, + /** * Register bidder specific code, which will execute if bidder timed out after an auction * @param {data} Containing timeout specific data @@ -434,7 +508,7 @@ export const spec = { if (bid['nurl']) { utils.triggerPixel(bid['nurl']); } - }, + } /** * Register bidder specific code, which will execute when the adserver targeting has been set for a bid from this bidder diff --git a/test/spec/modules/mediagoBidAdapter_spec.js b/test/spec/modules/mediagoBidAdapter_spec.js index 5eb362893e3..601bcff527a 100644 --- a/test/spec/modules/mediagoBidAdapter_spec.js +++ b/test/spec/modules/mediagoBidAdapter_spec.js @@ -23,29 +23,37 @@ describe('mediago:BidAdapterTests', function () { tid: 'tid_01', data: { browsi: { - browsiViewability: 'NA', + browsiViewability: 'NA' }, adserver: { name: 'adserver_name', - adslot: 'adslot_name', + adslot: 'adslot_name' }, - }, - }, - }, + pbadslot: '/12345/my-gpt-tag-0' + } + } + } }, mediaTypes: { banner: { sizes: [[300, 250]], - pos: 'left', - }, + pos: 'left' + } }, ortb2: { - user: { - ext: { - data: { - }, + site: { + cat: ['IAB2'], + keywords: 'power tools, drills, tools=industrial', + content: { + keywords: 'video, source=streaming' }, + }, + user: { + ext: { + data: {} + } + } }, adUnitCode: 'regular_iframe', transactionId: '7b26fdae-96e6-4c35-a18b-218dda11397d', @@ -56,9 +64,83 @@ describe('mediago:BidAdapterTests', function () { src: 'client', bidRequestsCount: 1, bidderRequestsCount: 1, - bidderWinsCount: 0, - }, + bidderWinsCount: 0 + } ], + gdprConsent: { + consentString: 'BOJ8RZsOJ8RZsABAB8AAAAAZ+A==', + gdprApplies: true, + apiVersion: 2, + vendorData: { + purpose: { + consents: { + 1: false + } + } + } + }, + userId: { + tdid: 'sample-userid', + uid2: { id: 'sample-uid2-value' }, + criteoId: 'sample-criteo-userid', + netId: 'sample-netId-userid', + idl_env: 'sample-idl-userid', + pubProvidedId: [ + { + source: 'puburl.com', + uids: [ + { + id: 'pubid2', + atype: 1, + ext: { + stype: 'ppuid' + } + } + ] + }, + { + source: 'puburl2.com', + uids: [ + { + id: 'pubid2' + }, + { + id: 'pubid2-123' + } + ] + } + ] + }, + userIdAsEids: [ + { + source: 'adserver.org', + uids: [{ id: 'sample-userid' }] + }, + { + source: 'criteo.com', + uids: [{ id: 'sample-criteo-userid' }] + }, + { + source: 'netid.de', + uids: [{ id: 'sample-netId-userid' }] + }, + { + source: 'liveramp.com', + uids: [{ id: 'sample-idl-userid' }] + }, + { + source: 'uidapi.com', + uids: [{ id: 'sample-uid2-value' }] + }, + { + source: 'puburl.com', + uids: [{ id: 'pubid1' }] + }, + { + source: 'puburl2.com', + uids: [{ id: 'pubid2' }, { id: 'pubid2-123' }] + } + ] }; let request = []; @@ -67,8 +149,8 @@ describe('mediago:BidAdapterTests', function () { spec.isBidRequestValid({ bidder: 'mediago', params: { - token: ['85a6b01e41ac36d49744fad726e3655d'], - }, + token: ['85a6b01e41ac36d49744fad726e3655d'] + } }) ).to.equal(true); }); @@ -80,10 +162,12 @@ describe('mediago:BidAdapterTests', function () { }); it('mediago:validate_response_params', function () { - let adm = ""; + let adm = + ''; let temp = '%3Cscr'; temp += 'ipt%3E'; - temp += '!function()%7B%22use%20strict%22%3Bfunction%20f(t)%7Breturn(f%3D%22function%22%3D%3Dtypeof%20Symbol%26%26%22symbol%22%3D%3Dtypeof%20Symbol.iterator%3Ffunction(t)%7Breturn%20typeof%20t%7D%3Afunction(t)%7Breturn%20t%26%26%22function%22%3D%3Dtypeof%20Symbol%26%26t.constructor%3D%3D%3DSymbol%26%26t!%3D%3DSymbol.prototype%3F%22symbol%22%3Atypeof%20t%7D)(t)%7Dfunction%20l(t)%7Bvar%20e%3D0%3Carguments.length%26%26void%200!%3D%3Dt%3Ft%3A%7B%7D%3Btry%7Be.random_t%3D(new%20Date).getTime()%2Cg(function(t)%7Bvar%20e%3D1%3Carguments.length%26%26void%200!%3D%3Darguments%5B1%5D%3Farguments%5B1%5D%3A%22%22%3Bif(%22object%22!%3D%3Df(t))return%20e%3Bvar%20n%3Dfunction(t)%7Bfor(var%20e%2Cn%3D%5B%5D%2Co%3D0%2Ci%3DObject.keys(t)%3Bo%3Ci.length%3Bo%2B%2B)e%3Di%5Bo%5D%2Cn.push(%22%22.concat(e%2C%22%3D%22).concat(t%5Be%5D))%3Breturn%20n%7D(t).join(%22%26%22)%2Co%3De.indexOf(%22%23%22)%2Ci%3De%2Ct%3D%22%22%3Breturn-1!%3D%3Do%26%26(i%3De.slice(0%2Co)%2Ct%3De.slice(o))%2Cn%26%26(i%26%26-1!%3D%3Di.indexOf(%22%3F%22)%3Fi%2B%3D%22%26%22%2Bn%3Ai%2B%3D%22%3F%22%2Bn)%2Ci%2Bt%7D(e%2C%22https%3A%2F%2Ftrace.mediago.io%2Fapi%2Flog%2Ftrack%22))%7Dcatch(t)%7B%7D%7Dfunction%20g(t%2Ce%2Cn)%7B(t%3Dt%3Ft.split(%22%3B%3B%3B%22)%3A%5B%5D).map(function(t)%7Btry%7B0%3C%3Dt.indexOf(%22%2Fapi%2Fbidder%2Ftrack%22)%26%26n%26%26(t%2B%3D%22%26inIframe%3D%22.concat(!(!self.frameElement%7C%7C%22IFRAME%22!%3Dself.frameElement.tagName)%7C%7Cwindow.frames.length!%3Dparent.frames.length%7C%7Cself!%3Dtop)%2Ct%2B%3D%22%26pos_x%3D%22.concat(n.left%2C%22%26pos_y%3D%22).concat(n.top%2C%22%26page_w%3D%22).concat(n.page_width%2C%22%26page_h%3D%22).concat(n.page_height))%7Dcatch(t)%7Bl(%7Btn%3As%2Cwinloss%3A1%2Cfe%3A2%2Cpos_err_c%3A1002%2Cpos_err_m%3At.toString()%7D)%7Dvar%20e%3Dnew%20Image%3Be.src%3Dt%2Ce.style.display%3D%22none%22%2Ce.style.visibility%3D%22hidden%22%2Ce.width%3D0%2Ce.height%3D0%2Cdocument.body.appendChild(e)%7D)%7Dvar%20d%3D%5B%22https%3A%2F%2Ftrace.mediago.io%2Fapi%2Fbidder%2Ftrack%3Ftn%3D39934c2bda4debbe4c680be1dd02f5d3%26price%3DdjUJcggeuWWfbm28q4WXHdgMFkO28DrGw49FnubQ0Bk%26evt%3D101%26rid%3D6e28cfaf115a354ea1ad8e1304d6d7b8%26campaignid%3D1339145%26impid%3D44-300x250-1%26offerid%3D24054386%26test%3D0%26time%3D1660789795%26cp%3DjZDh1xu6_QqJLlKVtCkiHIP_TER6gL9jeTrlHCBoxOM%26acid%3D599%26trackingid%3D99afea272c2b0e8626489674ddb7a0bb%26uid%3Da865b9ae-fa9e-4c09-8204-2db99ac7c8f7%26bm%3D2%26la%3Den%26cn%3Dus%26cid%3D3998296%26info%3DSi3oM-qfCbw2iZRYs01BkUWyH6c5CQWHrA8CQLE0VHcXAcf4ljY9dyLzQ4vAlTWd6-j_ou4ySor3e70Ll7wlKiiauQKaUkZqNoTizHm73C4FK8DYJSTP3VkhJV8RzrYk%26sid%3D128__110__1__12__28__38__163__96__58__24__47__99%26sp%3DdjUJcggeuWWfbm28q4WXHdgMFkO28DrGw49FnubQ0Bk%26scp%3DzK0DRYY1UV-syqSpmcMYBpOebtoQJV9ZEJT0JFqbTQg%26acu%3DUSD%26scu%3DUSD%26sgcp%3DzK0DRYY1UV-syqSpmcMYBpOebtoQJV9ZEJT0JFqbTQg%26gprice%3DdjUJcggeuWWfbm28q4WXHdgMFkO28DrGw49FnubQ0Bk%26gcp%3DzK0DRYY1UV-syqSpmcMYBpOebtoQJV9ZEJT0JFqbTQg%26ah%3D%26de%3Dwjh.popin.cc%26iv%3D0%22%2C%22%24%7BITRACKER2%7D%22%2C%22%24%7BITRACKER3%7D%22%2C%22%24%7BITRACKER4%7D%22%2C%22%24%7BITRACKER5%7D%22%2C%22%24%7BITRACKER6%7D%22%5D%2Cp%3D%5B%22https%3A%2F%2Ftrace.mediago.io%2Fapi%2Fbidder%2Ftrack%3Ftn%3D39934c2bda4debbe4c680be1dd02f5d3%26price%3DdjUJcggeuWWfbm28q4WXHdgMFkO28DrGw49FnubQ0Bk%26evt%3D104%26rid%3D6e28cfaf115a354ea1ad8e1304d6d7b8%26campaignid%3D1339145%26impid%3D44-300x250-1%26offerid%3D24054386%26test%3D0%26time%3D1660789795%26cp%3DjZDh1xu6_QqJLlKVtCkiHIP_TER6gL9jeTrlHCBoxOM%26acid%3D599%26trackingid%3D99afea272c2b0e8626489674ddb7a0bb%26uid%3Da865b9ae-fa9e-4c09-8204-2db99ac7c8f7%26sid%3D128__110__1__12__28__38__163__96__58__24__47__99%26format%3D%26crid%3Dff32b6f9b3bbc45c00b78b6674a2952e%26bm%3D2%26la%3Den%26cn%3Dus%26cid%3D3998296%26info%3DSi3oM-qfCbw2iZRYs01BkUWyH6c5CQWHrA8CQLE0VHcXAcf4ljY9dyLzQ4vAlTWd6-j_ou4ySor3e70Ll7wlKiiauQKaUkZqNoTizHm73C4FK8DYJSTP3VkhJV8RzrYk%26sp%3DdjUJcggeuWWfbm28q4WXHdgMFkO28DrGw49FnubQ0Bk%26scp%3DzK0DRYY1UV-syqSpmcMYBpOebtoQJV9ZEJT0JFqbTQg%26acu%3DUSD%26scu%3DUSD%26sgcp%3DzK0DRYY1UV-syqSpmcMYBpOebtoQJV9ZEJT0JFqbTQg%26gprice%3DdjUJcggeuWWfbm28q4WXHdgMFkO28DrGw49FnubQ0Bk%26gcp%3DzK0DRYY1UV-syqSpmcMYBpOebtoQJV9ZEJT0JFqbTQg%26ah%3D%26de%3Dwjh.popin.cc%26iv%3D0%22%2C%22%24%7BVTRACKER2%7D%22%2C%22%24%7BVTRACKER3%7D%22%2C%22%24%7BVTRACKER4%7D%22%2C%22%24%7BVTRACKER5%7D%22%2C%22%24%7BVTRACKER6%7D%22%5D%2Cs%3D%22f9f2b1ef23fe2759c2cad0953029a94b%22%2Cn%3Ddocument.getElementById(%22mgcontainer-99afea272c2b0e8626489674ddb7a0bb%22)%3Bn%26%26function()%7Bvar%20a%3Dn.getElementsByClassName(%22mediago-placement-track%22)%3Bif(a%26%26a.length)%7Bvar%20t%2Ce%3Dfunction(t)%7Bvar%20e%2Cn%2Co%2Ci%2Cc%2Cr%3B%22object%22%3D%3D%3Df(r%3Da%5Bt%5D)%26%26(e%3Dfunction(t)%7Btry%7Bvar%20e%3Dt.getBoundingClientRect()%2Cn%3De%26%26e.top%7C%7C-1%2Co%3De%26%26e.left%7C%7C-1%2Ci%3Ddocument.body.scrollWidth%7C%7C-1%2Ce%3Ddocument.body.scrollHeight%7C%7C-1%3Breturn%7Btop%3An.toFixed(0)%2Cleft%3Ao.toFixed(0)%2Cpage_width%3Ai%2Cpage_height%3Ae%7D%7Dcatch(o)%7Breturn%20l(%7Btn%3As%2Cwinloss%3A1%2Cfe%3A2%2Cpos_err_c%3A1001%2Cpos_err_m%3Ao.toString()%7D)%2C%7Btop%3A%22-1%22%2Cleft%3A%22-1%22%2Cpage_width%3A%22-1%22%2Cpage_height%3A%22-1%22%7D%7D%7D(r)%2C(n%3Dd%5Bt%5D)%26%26g(n%2C0%2Ce)%2Co%3Dp%5Bt%5D%2Ci%3D!1%2C(c%3Dfunction()%7BsetTimeout(function()%7Bvar%20t%2Ce%3B!i%26%26(t%3Dr%2Ce%3Dwindow.innerHeight%7C%7Cdocument.documentElement.clientHeight%7C%7Cdocument.body.clientHeight%2C(t.getBoundingClientRect()%26%26t.getBoundingClientRect().top)%3C%3De-.75*(t.offsetHeight%7C%7Ct.clientHeight))%3F(i%3D!0%2Co%26%26g(o))%3Ac()%7D%2C500)%7D)())%7D%3Bfor(t%20in%20a)e(t)%7D%7D()%7D()'; + temp += + '!function()%7B%22use%20strict%22%3Bfunction%20f(t)%7Breturn(f%3D%22function%22%3D%3Dtypeof%20Symbol%26%26%22symbol%22%3D%3Dtypeof%20Symbol.iterator%3Ffunction(t)%7Breturn%20typeof%20t%7D%3Afunction(t)%7Breturn%20t%26%26%22function%22%3D%3Dtypeof%20Symbol%26%26t.constructor%3D%3D%3DSymbol%26%26t!%3D%3DSymbol.prototype%3F%22symbol%22%3Atypeof%20t%7D)(t)%7Dfunction%20l(t)%7Bvar%20e%3D0%3Carguments.length%26%26void%200!%3D%3Dt%3Ft%3A%7B%7D%3Btry%7Be.random_t%3D(new%20Date).getTime()%2Cg(function(t)%7Bvar%20e%3D1%3Carguments.length%26%26void%200!%3D%3Darguments%5B1%5D%3Farguments%5B1%5D%3A%22%22%3Bif(%22object%22!%3D%3Df(t))return%20e%3Bvar%20n%3Dfunction(t)%7Bfor(var%20e%2Cn%3D%5B%5D%2Co%3D0%2Ci%3DObject.keys(t)%3Bo%3Ci.length%3Bo%2B%2B)e%3Di%5Bo%5D%2Cn.push(%22%22.concat(e%2C%22%3D%22).concat(t%5Be%5D))%3Breturn%20n%7D(t).join(%22%26%22)%2Co%3De.indexOf(%22%23%22)%2Ci%3De%2Ct%3D%22%22%3Breturn-1!%3D%3Do%26%26(i%3De.slice(0%2Co)%2Ct%3De.slice(o))%2Cn%26%26(i%26%26-1!%3D%3Di.indexOf(%22%3F%22)%3Fi%2B%3D%22%26%22%2Bn%3Ai%2B%3D%22%3F%22%2Bn)%2Ci%2Bt%7D(e%2C%22https%3A%2F%2Ftrace.mediago.io%2Fapi%2Flog%2Ftrack%22))%7Dcatch(t)%7B%7D%7Dfunction%20g(t%2Ce%2Cn)%7B(t%3Dt%3Ft.split(%22%3B%3B%3B%22)%3A%5B%5D).map(function(t)%7Btry%7B0%3C%3Dt.indexOf(%22%2Fapi%2Fbidder%2Ftrack%22)%26%26n%26%26(t%2B%3D%22%26inIframe%3D%22.concat(!(!self.frameElement%7C%7C%22IFRAME%22!%3Dself.frameElement.tagName)%7C%7Cwindow.frames.length!%3Dparent.frames.length%7C%7Cself!%3Dtop)%2Ct%2B%3D%22%26pos_x%3D%22.concat(n.left%2C%22%26pos_y%3D%22).concat(n.top%2C%22%26page_w%3D%22).concat(n.page_width%2C%22%26page_h%3D%22).concat(n.page_height))%7Dcatch(t)%7Bl(%7Btn%3As%2Cwinloss%3A1%2Cfe%3A2%2Cpos_err_c%3A1002%2Cpos_err_m%3At.toString()%7D)%7Dvar%20e%3Dnew%20Image%3Be.src%3Dt%2Ce.style.display%3D%22none%22%2Ce.style.visibility%3D%22hidden%22%2Ce.width%3D0%2Ce.height%3D0%2Cdocument.body.appendChild(e)%7D)%7Dvar%20d%3D%5B%22https%3A%2F%2Ftrace.mediago.io%2Fapi%2Fbidder%2Ftrack%3Ftn%3D39934c2bda4debbe4c680be1dd02f5d3%26price%3DdjUJcggeuWWfbm28q4WXHdgMFkO28DrGw49FnubQ0Bk%26evt%3D101%26rid%3D6e28cfaf115a354ea1ad8e1304d6d7b8%26campaignid%3D1339145%26impid%3D44-300x250-1%26offerid%3D24054386%26test%3D0%26time%3D1660789795%26cp%3DjZDh1xu6_QqJLlKVtCkiHIP_TER6gL9jeTrlHCBoxOM%26acid%3D599%26trackingid%3D99afea272c2b0e8626489674ddb7a0bb%26uid%3Da865b9ae-fa9e-4c09-8204-2db99ac7c8f7%26bm%3D2%26la%3Den%26cn%3Dus%26cid%3D3998296%26info%3DSi3oM-qfCbw2iZRYs01BkUWyH6c5CQWHrA8CQLE0VHcXAcf4ljY9dyLzQ4vAlTWd6-j_ou4ySor3e70Ll7wlKiiauQKaUkZqNoTizHm73C4FK8DYJSTP3VkhJV8RzrYk%26sid%3D128__110__1__12__28__38__163__96__58__24__47__99%26sp%3DdjUJcggeuWWfbm28q4WXHdgMFkO28DrGw49FnubQ0Bk%26scp%3DzK0DRYY1UV-syqSpmcMYBpOebtoQJV9ZEJT0JFqbTQg%26acu%3DUSD%26scu%3DUSD%26sgcp%3DzK0DRYY1UV-syqSpmcMYBpOebtoQJV9ZEJT0JFqbTQg%26gprice%3DdjUJcggeuWWfbm28q4WXHdgMFkO28DrGw49FnubQ0Bk%26gcp%3DzK0DRYY1UV-syqSpmcMYBpOebtoQJV9ZEJT0JFqbTQg%26ah%3D%26de%3Dwjh.popin.cc%26iv%3D0%22%2C%22%24%7BITRACKER2%7D%22%2C%22%24%7BITRACKER3%7D%22%2C%22%24%7BITRACKER4%7D%22%2C%22%24%7BITRACKER5%7D%22%2C%22%24%7BITRACKER6%7D%22%5D%2Cp%3D%5B%22https%3A%2F%2Ftrace.mediago.io%2Fapi%2Fbidder%2Ftrack%3Ftn%3D39934c2bda4debbe4c680be1dd02f5d3%26price%3DdjUJcggeuWWfbm28q4WXHdgMFkO28DrGw49FnubQ0Bk%26evt%3D104%26rid%3D6e28cfaf115a354ea1ad8e1304d6d7b8%26campaignid%3D1339145%26impid%3D44-300x250-1%26offerid%3D24054386%26test%3D0%26time%3D1660789795%26cp%3DjZDh1xu6_QqJLlKVtCkiHIP_TER6gL9jeTrlHCBoxOM%26acid%3D599%26trackingid%3D99afea272c2b0e8626489674ddb7a0bb%26uid%3Da865b9ae-fa9e-4c09-8204-2db99ac7c8f7%26sid%3D128__110__1__12__28__38__163__96__58__24__47__99%26format%3D%26crid%3Dff32b6f9b3bbc45c00b78b6674a2952e%26bm%3D2%26la%3Den%26cn%3Dus%26cid%3D3998296%26info%3DSi3oM-qfCbw2iZRYs01BkUWyH6c5CQWHrA8CQLE0VHcXAcf4ljY9dyLzQ4vAlTWd6-j_ou4ySor3e70Ll7wlKiiauQKaUkZqNoTizHm73C4FK8DYJSTP3VkhJV8RzrYk%26sp%3DdjUJcggeuWWfbm28q4WXHdgMFkO28DrGw49FnubQ0Bk%26scp%3DzK0DRYY1UV-syqSpmcMYBpOebtoQJV9ZEJT0JFqbTQg%26acu%3DUSD%26scu%3DUSD%26sgcp%3DzK0DRYY1UV-syqSpmcMYBpOebtoQJV9ZEJT0JFqbTQg%26gprice%3DdjUJcggeuWWfbm28q4WXHdgMFkO28DrGw49FnubQ0Bk%26gcp%3DzK0DRYY1UV-syqSpmcMYBpOebtoQJV9ZEJT0JFqbTQg%26ah%3D%26de%3Dwjh.popin.cc%26iv%3D0%22%2C%22%24%7BVTRACKER2%7D%22%2C%22%24%7BVTRACKER3%7D%22%2C%22%24%7BVTRACKER4%7D%22%2C%22%24%7BVTRACKER5%7D%22%2C%22%24%7BVTRACKER6%7D%22%5D%2Cs%3D%22f9f2b1ef23fe2759c2cad0953029a94b%22%2Cn%3Ddocument.getElementById(%22mgcontainer-99afea272c2b0e8626489674ddb7a0bb%22)%3Bn%26%26function()%7Bvar%20a%3Dn.getElementsByClassName(%22mediago-placement-track%22)%3Bif(a%26%26a.length)%7Bvar%20t%2Ce%3Dfunction(t)%7Bvar%20e%2Cn%2Co%2Ci%2Cc%2Cr%3B%22object%22%3D%3D%3Df(r%3Da%5Bt%5D)%26%26(e%3Dfunction(t)%7Btry%7Bvar%20e%3Dt.getBoundingClientRect()%2Cn%3De%26%26e.top%7C%7C-1%2Co%3De%26%26e.left%7C%7C-1%2Ci%3Ddocument.body.scrollWidth%7C%7C-1%2Ce%3Ddocument.body.scrollHeight%7C%7C-1%3Breturn%7Btop%3An.toFixed(0)%2Cleft%3Ao.toFixed(0)%2Cpage_width%3Ai%2Cpage_height%3Ae%7D%7Dcatch(o)%7Breturn%20l(%7Btn%3As%2Cwinloss%3A1%2Cfe%3A2%2Cpos_err_c%3A1001%2Cpos_err_m%3Ao.toString()%7D)%2C%7Btop%3A%22-1%22%2Cleft%3A%22-1%22%2Cpage_width%3A%22-1%22%2Cpage_height%3A%22-1%22%7D%7D%7D(r)%2C(n%3Dd%5Bt%5D)%26%26g(n%2C0%2Ce)%2Co%3Dp%5Bt%5D%2Ci%3D!1%2C(c%3Dfunction()%7BsetTimeout(function()%7Bvar%20t%2Ce%3B!i%26%26(t%3Dr%2Ce%3Dwindow.innerHeight%7C%7Cdocument.documentElement.clientHeight%7C%7Cdocument.body.clientHeight%2C(t.getBoundingClientRect()%26%26t.getBoundingClientRect().top)%3C%3De-.75*(t.offsetHeight%7C%7Ct.clientHeight))%3F(i%3D!0%2Co%26%26g(o))%3Ac()%7D%2C500)%7D)())%7D%3Bfor(t%20in%20a)e(t)%7D%7D()%7D()'; temp += '%3B%3C%2Fscri'; temp += 'pt%3E'; adm += decodeURIComponent(temp); @@ -101,13 +185,13 @@ describe('mediago:BidAdapterTests', function () { cid: '1339145', crid: 'ff32b6f9b3bbc45c00b78b6674a2952e', w: 300, - h: 250, - }, - ], - }, + h: 250 + } + ] + } ], - cur: 'USD', - }, + cur: 'USD' + } }; let bids = spec.interpretResponse(serverResponse); From cb82c47030435998deb4eed41627ff3cc3e1c015 Mon Sep 17 00:00:00 2001 From: Irakli Gotsiridze Date: Thu, 7 Dec 2023 19:03:37 +0300 Subject: [PATCH 053/246] Sovrn Bid Adapter : send coppa signal (#10729) * set coppa from config * minor fix * get coppa from ortb2.regs instead of config --- modules/sovrnBidAdapter.js | 5 ++++ test/spec/modules/sovrnBidAdapter_spec.js | 35 +++++++++++++++++++++++ 2 files changed, 40 insertions(+) diff --git a/modules/sovrnBidAdapter.js b/modules/sovrnBidAdapter.js index f72c482735a..a7f4166579b 100644 --- a/modules/sovrnBidAdapter.js +++ b/modules/sovrnBidAdapter.js @@ -172,6 +172,11 @@ export const spec = { deepSetValue(sovrnBidReq, 'source.tid', tid) } + const coppa = deepAccess(bidderRequest, 'ortb2.regs.coppa'); + if (coppa) { + deepSetValue(sovrnBidReq, 'regs.coppa', 1); + } + if (bidderRequest.gdprConsent) { deepSetValue(sovrnBidReq, 'regs.ext.gdpr', +bidderRequest.gdprConsent.gdprApplies); deepSetValue(sovrnBidReq, 'user.ext.consent', bidderRequest.gdprConsent.consentString) diff --git a/test/spec/modules/sovrnBidAdapter_spec.js b/test/spec/modules/sovrnBidAdapter_spec.js index 032f959e559..f165a6da6d1 100644 --- a/test/spec/modules/sovrnBidAdapter_spec.js +++ b/test/spec/modules/sovrnBidAdapter_spec.js @@ -318,6 +318,41 @@ describe('sovrnBidAdapter', function() { expect(data.regs.ext['us_privacy']).to.equal(bidderRequest.uspConsent) }) + it('should not set coppa when coppa is undefined', function () { + const bidderRequest = { + ...baseBidderRequest, + bidderCode: 'sovrn', + auctionId: '1d1a030790a475', + bidderRequestId: '22edbae2733bf6', + timeout: 3000, + bids: [baseBidRequest], + gdprConsent: { + consentString: 'BOJ8RZsOJ8RZsABAB8AAAAAZ+A==', + gdprApplies: true + }, + } + const {regs} = JSON.parse(spec.buildRequests([baseBidRequest], bidderRequest).data) + expect(regs.coppa).to.be.undefined + }) + + it('should set coppa to 1 when coppa is provided with value true', function () { + const bidderRequest = { + ...baseBidderRequest, + ortb2: { + regs: { + coppa: true + } + }, + bidderCode: 'sovrn', + auctionId: '1d1a030790a475', + bidderRequestId: '22edbae2733bf6', + timeout: 3000, + bids: [baseBidRequest] + } + const {regs} = JSON.parse(spec.buildRequests([baseBidRequest], bidderRequest).data) + expect(regs.coppa).to.equal(1) + }) + it('should send gpp info in OpenRTB 2.6 location when gppConsent defined', function () { const bidderRequest = { ...baseBidderRequest, From 4fe6292e874229160b298f6705db6e67bf8f7377 Mon Sep 17 00:00:00 2001 From: Mikael Lundin Date: Thu, 7 Dec 2023 17:12:53 +0100 Subject: [PATCH 054/246] Adnuntius Bid Adapter : forces gdpr=1 flag if consent is required (#10767) * Removed linting issues * Fixed merge issues. --- modules/adnuntiusBidAdapter.js | 46 ++++++++++--------- test/spec/modules/adnuntiusBidAdapter_spec.js | 2 +- 2 files changed, 26 insertions(+), 22 deletions(-) diff --git a/modules/adnuntiusBidAdapter.js b/modules/adnuntiusBidAdapter.js index b2653f16daf..8f841006524 100644 --- a/modules/adnuntiusBidAdapter.js +++ b/modules/adnuntiusBidAdapter.js @@ -18,17 +18,17 @@ const VALID_BID_TYPES = ['netBid', 'grossBid']; const META_DATA_KEY = 'adn.metaData'; export const misc = { - getUnixTimestamp: function(addDays, asMinutes) { + getUnixTimestamp: function (addDays, asMinutes) { const multiplication = addDays / (asMinutes ? 1440 : 1); return Date.now() + (addDays && addDays > 0 ? (1000 * 60 * 60 * 24 * multiplication) : 0); } }; -const storageTool = (function() { - const storage = getStorageManager({bidderCode: BIDDER_CODE}); +const storageTool = (function () { + const storage = getStorageManager({ bidderCode: BIDDER_CODE }); let metaInternal; - const getMetaInternal = function() { + const getMetaInternal = function () { if (!storage.localStorageIsEnabled()) { return {}; } @@ -57,24 +57,24 @@ const storageTool = (function() { return filteredEntries; }; - const setMetaInternal = function(apiResponse) { + const setMetaInternal = function (apiResponse) { if (!storage.localStorageIsEnabled()) { return; } - const updateVoidAuIds = function(currentVoidAuIds, auIdsAsString) { + const updateVoidAuIds = function (currentVoidAuIds, auIdsAsString) { const newAuIds = auIdsAsString ? auIdsAsString.split(';') : []; const notNewExistingAuIds = currentVoidAuIds.filter(auIdObj => { return newAuIds.indexOf(auIdObj.value) < -1; }) || []; const oneDayFromNow = misc.getUnixTimestamp(1); const apiIdsArray = newAuIds.map(auId => { - return {exp: oneDayFromNow, auId: auId}; + return { exp: oneDayFromNow, auId: auId }; }) || []; return notNewExistingAuIds.concat(apiIdsArray) || []; } - const metaAsObj = getMetaInternal().reduce((a, entry) => ({...a, [entry.key]: {value: entry.value, exp: entry.exp}}), {}); + const metaAsObj = getMetaInternal().reduce((a, entry) => ({ ...a, [entry.key]: { value: entry.value, exp: entry.exp } }), {}); for (const key in apiResponse) { if (key !== 'voidAuIds') { metaAsObj[key] = { @@ -85,7 +85,7 @@ const storageTool = (function() { } const currentAuIds = updateVoidAuIds(metaAsObj.voidAuIds || [], apiResponse.voidAuIds || []); if (currentAuIds.length > 0) { - metaAsObj.voidAuIds = {value: currentAuIds}; + metaAsObj.voidAuIds = { value: currentAuIds }; } const metaDataForSaving = Object.entries(metaAsObj).map((entrySet) => { if (entrySet[0] === 'voidAuIds') { @@ -103,7 +103,7 @@ const storageTool = (function() { storage.setDataInLocalStorage(META_DATA_KEY, JSON.stringify(metaDataForSaving)); }; - const getUsi = function(meta, ortb2) { + const getUsi = function (meta, ortb2) { let usi = (meta && meta.usi) ? meta.usi : false; if (ortb2 && ortb2.user && ortb2.user.id) { usi = ortb2.user.id @@ -128,9 +128,9 @@ const storageTool = (function() { } return { - refreshStorage: function(bidderRequest) { + refreshStorage: function (bidderRequest) { const ortb2 = bidderRequest.ortb2 || {}; - metaInternal = getMetaInternal().reduce((a, entry) => ({...a, [entry.key]: entry.value}), {}); + metaInternal = getMetaInternal().reduce((a, entry) => ({ ...a, [entry.key]: entry.value }), {}); metaInternal.usi = getUsi(metaInternal, ortb2); if (!metaInternal.usi) { delete metaInternal.usi; @@ -142,21 +142,21 @@ const storageTool = (function() { } metaInternal.segments = getSegmentsFromOrtb(ortb2); }, - saveToStorage: function(serverData) { + saveToStorage: function (serverData) { setMetaInternal(serverData); }, - getUrlRelatedData: function() { - const {segments, usi, voidAuIdsArray} = metaInternal; - return {segments, usi, voidAuIdsArray}; + getUrlRelatedData: function () { + const { segments, usi, voidAuIdsArray } = metaInternal; + return { segments, usi, voidAuIdsArray }; }, - getPayloadRelatedData: function() { - const {segments, usi, userId, voidAuIdsArray, voidAuIds, ...payloadRelatedData} = metaInternal; + getPayloadRelatedData: function () { + const { segments, usi, userId, voidAuIdsArray, voidAuIds, ...payloadRelatedData } = metaInternal; return payloadRelatedData; } }; })(); -const validateBidType = function(bidTypeOption) { +const validateBidType = function (bidTypeOption) { return VALID_BID_TYPES.indexOf(bidTypeOption || '') > -1 ? bidTypeOption : 'bid'; } @@ -177,10 +177,13 @@ export const spec = { const queryParamsAndValues = []; queryParamsAndValues.push('tzo=' + new Date().getTimezoneOffset()) queryParamsAndValues.push('format=json') - const gdprApplies = deepAccess(bidderRequest, 'gdprConsent.gdprApplies'); const consentString = deepAccess(bidderRequest, 'gdprConsent.consentString'); - if (gdprApplies !== undefined) queryParamsAndValues.push('consentString=' + consentString); + if (gdprApplies !== undefined) { + const flag = gdprApplies ? '1' : '0' + queryParamsAndValues.push('consentString=' + consentString); + queryParamsAndValues.push('gdpr=' + flag); + } storageTool.refreshStorage(bidderRequest); @@ -194,6 +197,7 @@ export const spec = { const bidRequests = {}; const networks = {}; + for (let i = 0; i < validBidRequests.length; i++) { const bid = validBidRequests[i]; if ((urlRelatedMetaData.voidAuIdsArray && (urlRelatedMetaData.voidAuIdsArray.indexOf(bid.params.auId) > -1 || urlRelatedMetaData.voidAuIdsArray.indexOf(bid.params.auId.padStart(16, '0')) > -1))) { diff --git a/test/spec/modules/adnuntiusBidAdapter_spec.js b/test/spec/modules/adnuntiusBidAdapter_spec.js index 8fa719f7af6..e4902ca11b2 100644 --- a/test/spec/modules/adnuntiusBidAdapter_spec.js +++ b/test/spec/modules/adnuntiusBidAdapter_spec.js @@ -42,7 +42,7 @@ describe('adnuntiusBidAdapter', function() { const ENDPOINT_URL_VIDEO = `${ENDPOINT_URL_BASE}&userId=${usi}&tt=vast4`; const ENDPOINT_URL_NOCOOKIE = `${ENDPOINT_URL_BASE}&userId=${usi}&noCookies=true`; const ENDPOINT_URL_SEGMENTS = `${ENDPOINT_URL_BASE}&segments=segment1,segment2,segment3&userId=${usi}`; - const ENDPOINT_URL_CONSENT = `${EURO_URL}${tzo}&format=json&consentString=consentString&userId=${usi}`; + const ENDPOINT_URL_CONSENT = `${EURO_URL}${tzo}&format=json&consentString=consentString&gdpr=1&userId=${usi}`; const adapter = newBidder(spec); const bidderRequests = [ From 8ab882f324ae71ff38252ebc9e7c3a7ac73b485c Mon Sep 17 00:00:00 2001 From: GeoEdge-r-and-d <72186958+GeoEdge-r-and-d@users.noreply.github.com> Date: Thu, 7 Dec 2023 18:23:01 +0200 Subject: [PATCH 055/246] GeoEdge RTD module: collect CPM and curency from the winning bid (#10765) * Collect CPM and cuurency from the winnning bid * Export getMacros for test * Update tests --------- Co-authored-by: daniel manan --- modules/geoedgeRtdProvider.js | 6 ++++-- test/spec/modules/geoedgeRtdProvider_spec.js | 10 ++++++++++ 2 files changed, 14 insertions(+), 2 deletions(-) diff --git a/modules/geoedgeRtdProvider.js b/modules/geoedgeRtdProvider.js index 646d2f4e786..afaaef93169 100644 --- a/modules/geoedgeRtdProvider.js +++ b/modules/geoedgeRtdProvider.js @@ -103,7 +103,7 @@ export function wrapHtml(wrapper, html) { * @param {string} key * @return {Object} */ -function getMacros(bid, key) { +export function getMacros(bid, key) { return { '${key}': key, '%%ADUNIT%%': bid.adUnitCode, @@ -116,7 +116,9 @@ function getMacros(bid, key) { '%_hbadomains': bid.meta && bid.meta.advertiserDomains, '%%PATTERN:hb_pb%%': bid.pbHg, '%%SITE%%': location.hostname, - '%_pimp%': PV_ID + '%_pimp%': PV_ID, + '%_hbCpm!': bid.cpm, + '%_hbCurrency!': bid.currency }; } diff --git a/test/spec/modules/geoedgeRtdProvider_spec.js b/test/spec/modules/geoedgeRtdProvider_spec.js index 96da2e3dbd7..b0307a60a3c 100644 --- a/test/spec/modules/geoedgeRtdProvider_spec.js +++ b/test/spec/modules/geoedgeRtdProvider_spec.js @@ -6,6 +6,7 @@ import { getInPageUrl, htmlPlaceholder, setWrapper, + getMacros, wrapper, WRAPPER_URL } from '../../../modules/geoedgeRtdProvider.js'; @@ -117,6 +118,15 @@ describe('Geoedge RTD module', function () { expect(wrapper).to.equal(mockWrapper); }); }); + describe('getMacros', function () { + it('return a dictionary of macros replaced with values from bid object', function () { + let bid = mockBid('testBidder'); + let dict = getMacros(bid, key); + let hasCpm = dict['%_hbCpm!'] === bid.cpm; + let hasCurrency = dict['%_hbCurrency!'] === bid.currency; + expect(hasCpm && hasCurrency); + }); + }); describe('onBidResponseEvent', function () { let bidFromA = mockBid('bidderA'); it('should wrap bid html when bidder is configured', function () { From c6a13cadfc848930f5c6253bab7aa1a95c747fd2 Mon Sep 17 00:00:00 2001 From: Demetrio Girardi Date: Thu, 7 Dec 2023 08:36:22 -0800 Subject: [PATCH 056/246] userId: fix bug where pubProvidedId UIDs are duplicated (#10795) --- modules/userId/eids.js | 4 ++-- test/spec/modules/userId_spec.js | 20 +++++++++++++++++++- 2 files changed, 21 insertions(+), 3 deletions(-) diff --git a/modules/userId/eids.js b/modules/userId/eids.js index aad570f20df..e5f7e3b8fb2 100644 --- a/modules/userId/eids.js +++ b/modules/userId/eids.js @@ -1,4 +1,4 @@ -import {deepAccess, isFn, isPlainObject, isStr} from '../../src/utils.js'; +import {deepAccess, deepClone, isFn, isPlainObject, isStr} from '../../src/utils.js'; export const EID_CONFIG = new Map(); @@ -45,7 +45,7 @@ export function createEidsArray(bidRequestUserId) { Object.entries(bidRequestUserId).forEach(([name, values]) => { values = Array.isArray(values) ? values : [values]; - const eids = name === 'pubProvidedId' ? values : values.map(value => createEidObject(value, name)); + const eids = name === 'pubProvidedId' ? deepClone(values) : values.map(value => createEidObject(value, name)); eids.filter(eid => eid != null).forEach(collect); }) return Object.values(allEids); diff --git a/test/spec/modules/userId_spec.js b/test/spec/modules/userId_spec.js index 17a865796a2..18f49f4943e 100644 --- a/test/spec/modules/userId_spec.js +++ b/test/spec/modules/userId_spec.js @@ -448,7 +448,25 @@ describe('User ID', function () { ] } ]) - }) + }); + + it('when merging with pubCommonId, should not alter its eids', () => { + const uid = { + pubProvidedId: [ + { + source: 'mock1Source', + uids: [ + {id: 'uid2'} + ] + } + ], + mockId1: 'uid1', + }; + const eids = createEidsArray(uid); + expect(eids).to.have.length(1); + expect(eids[0].uids.map(u => u.id)).to.have.members(['uid1', 'uid2']); + expect(uid.pubProvidedId[0].uids).to.eql([{id: 'uid2'}]); + }); }) it('pbjs.getUserIds', function (done) { From 37b7e05bc5044ec452ea224e4cd3f5ea939783d2 Mon Sep 17 00:00:00 2001 From: "Prebid.js automated release" Date: Thu, 7 Dec 2023 17:16:14 +0000 Subject: [PATCH 057/246] Prebid 8.27.0 release --- package-lock.json | 2 +- package.json | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/package-lock.json b/package-lock.json index e08d24d42d5..79a6983b8a4 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,6 +1,6 @@ { "name": "prebid.js", - "version": "8.27.0-pre", + "version": "8.27.0", "lockfileVersion": 2, "requires": true, "packages": { diff --git a/package.json b/package.json index a7dbe2f6905..f0550299023 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "prebid.js", - "version": "8.27.0-pre", + "version": "8.27.0", "description": "Header Bidding Management Library", "main": "src/prebid.js", "scripts": { From 7f815d219b172e426414239c0bc56b4abc606ee0 Mon Sep 17 00:00:00 2001 From: "Prebid.js automated release" Date: Thu, 7 Dec 2023 17:16:15 +0000 Subject: [PATCH 058/246] Increment version to 8.28.0-pre --- package-lock.json | 2 +- package.json | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/package-lock.json b/package-lock.json index 79a6983b8a4..1e0990121a8 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,6 +1,6 @@ { "name": "prebid.js", - "version": "8.27.0", + "version": "8.28.0-pre", "lockfileVersion": 2, "requires": true, "packages": { diff --git a/package.json b/package.json index f0550299023..bca287c1a03 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "prebid.js", - "version": "8.27.0", + "version": "8.28.0-pre", "description": "Header Bidding Management Library", "main": "src/prebid.js", "scripts": { From 32ff2569521a04029eb55a8bfb04ccc9551681a0 Mon Sep 17 00:00:00 2001 From: PGAMSSP <142323401+PGAMSSP@users.noreply.github.com> Date: Thu, 7 Dec 2023 23:11:57 +0200 Subject: [PATCH 059/246] Support UID 2.0 pgamssp module (#10771) * new adapter PGAMSSP * upd * support UID 2.0 * del obj --- modules/pgamsspBidAdapter.js | 19 ++++++++++++++++++- test/spec/modules/pgamsspBidAdapter_spec.js | 1 + 2 files changed, 19 insertions(+), 1 deletion(-) diff --git a/modules/pgamsspBidAdapter.js b/modules/pgamsspBidAdapter.js index 7d285daf3c6..b38131c1c18 100644 --- a/modules/pgamsspBidAdapter.js +++ b/modules/pgamsspBidAdapter.js @@ -35,9 +35,14 @@ function getPlacementReqData(bid) { const placement = { bidId, schain, - bidfloor + bidfloor, + eids: [] }; + if (bid.userId) { + getUserId(placement.eids, bid.userId.uid2 && bid.userId.uid2.id, 'uidapi.com'); + } + if (placementId) { placement.placementId = placementId; placement.type = 'publisher'; @@ -91,6 +96,18 @@ function getBidFloor(bid) { return 0; } } +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, diff --git a/test/spec/modules/pgamsspBidAdapter_spec.js b/test/spec/modules/pgamsspBidAdapter_spec.js index 7e2323d4b81..0766219eda8 100644 --- a/test/spec/modules/pgamsspBidAdapter_spec.js +++ b/test/spec/modules/pgamsspBidAdapter_spec.js @@ -145,6 +145,7 @@ describe('PGAMBidAdapter', function () { expect(placement.schain).to.be.an('object'); expect(placement.bidfloor).to.exist.and.to.equal(0); expect(placement.type).to.exist.and.to.equal('publisher'); + expect(placement.eids).to.exist.and.to.be.an('array'); if (placement.adFormat === BANNER) { expect(placement.sizes).to.be.an('array'); From b47a38bb1b47934afd6544a889cf89c9dafe1df9 Mon Sep 17 00:00:00 2001 From: John Ivan Bauzon Date: Fri, 8 Dec 2023 14:14:48 -0800 Subject: [PATCH 060/246] ADSS-1756-pass-gpid-to-video-requests (#10809) Co-authored-by: John Bauzon --- modules/gumgumBidAdapter.js | 6 +++--- test/spec/modules/gumgumBidAdapter_spec.js | 8 ++++++++ 2 files changed, 11 insertions(+), 3 deletions(-) diff --git a/modules/gumgumBidAdapter.js b/modules/gumgumBidAdapter.js index 83fd726fde5..deee906298e 100644 --- a/modules/gumgumBidAdapter.js +++ b/modules/gumgumBidAdapter.js @@ -345,9 +345,6 @@ function buildRequests(validBidRequests, bidderRequest) { // ADTS-134 Retrieve ID envelopes for (const eid in eids) data[eid] = eids[eid]; - // ADJS-1024 & ADSS-1297 & ADTS-175 - gpid && (data.gpid = gpid); - if (mediaTypes.banner) { sizes = mediaTypes.banner.sizes; } else if (mediaTypes.video) { @@ -355,6 +352,9 @@ function buildRequests(validBidRequests, bidderRequest) { data = _getVidParams(mediaTypes.video); } + // ADJS-1024 & ADSS-1297 & ADTS-175 + gpid && (data.gpid = gpid); + if (pageViewId) { data.pv = pageViewId; } diff --git a/test/spec/modules/gumgumBidAdapter_spec.js b/test/spec/modules/gumgumBidAdapter_spec.js index f541a69abe8..e0529a895f5 100644 --- a/test/spec/modules/gumgumBidAdapter_spec.js +++ b/test/spec/modules/gumgumBidAdapter_spec.js @@ -297,6 +297,14 @@ describe('gumgumAdapter', function () { expect(bidRequest.data.gpid).to.equal(pbadslot); }); + it('should set the global placement id (gpid) if media type is video', function () { + const pbadslot = 'cde456' + const req = { ...bidRequests[0], ortb2Imp: { ext: { data: { pbadslot } } }, params: zoneParam, mediaTypes: vidMediaTypes } + const bidRequest = spec.buildRequests([req])[0]; + expect(bidRequest.data).to.have.property('gpid'); + expect(bidRequest.data.gpid).to.equal(pbadslot); + }); + 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]; From 260269d33c6d1125c7a9926d24e52f34021c8f27 Mon Sep 17 00:00:00 2001 From: Omer Dotan <54346241+omerBrowsi@users.noreply.github.com> Date: Mon, 11 Dec 2023 12:49:43 +0200 Subject: [PATCH 061/246] Browsi RTD : fix for targeting (#10814) * real time data module, browsi sub module for real time data, new hook bidsBackCallback, fix for config unsubscribe * change timeout&primary ad server only to auctionDelay update docs * support multiple providers * change promise to callbacks configure submodule on submodules.json * bug fixes * use Prebid ajax * tests fix * browsi real time data provider improvements * real time data module, browsi sub module for real time data, new hook bidsBackCallback, fix for config unsubscribe * change timeout&primary ad server only to auctionDelay update docs * support multiple providers * change promise to callbacks configure submodule on submodules.json * bug fixes * use Prebid ajax * tests fix * browsi real time data provider improvements * browsi-rtd-targeting-fix --- modules/browsiRtdProvider.js | 4 ++-- test/spec/modules/browsiRtdProvider_spec.js | 6 ------ 2 files changed, 2 insertions(+), 8 deletions(-) diff --git a/modules/browsiRtdProvider.js b/modules/browsiRtdProvider.js index 4a61f40600d..1f37c7c5bc0 100644 --- a/modules/browsiRtdProvider.js +++ b/modules/browsiRtdProvider.js @@ -88,6 +88,7 @@ export function collectData() { let predictorData = { ...{ sk: _moduleParams.siteKey, + pk: _moduleParams.pubKey, sw: (win.screen && win.screen.width) || -1, sh: (win.screen && win.screen.height) || -1, url: `${doc.location.protocol}//${doc.location.host}${doc.location.pathname}`, @@ -134,7 +135,6 @@ function getRTD(auc) { const adSlot = getSlotByCode(uc); const identifier = adSlot ? getMacroId(_browsiData['pmd'], adSlot) : uc; const _pd = _bp[identifier]; - rp[uc] = getKVObject(-1); if (!_pd) { return rp } @@ -275,7 +275,7 @@ function getPredictionsFromServer(url) { if (req.status === 200) { try { const data = JSON.parse(response); - if (data && data.p && data.kn) { + if (data) { setData({p: data.p, kn: data.kn, pmd: data.pmd, bet: data.bet}); } else { setData({}); diff --git a/test/spec/modules/browsiRtdProvider_spec.js b/test/spec/modules/browsiRtdProvider_spec.js index 75120aa7505..5fcc78f4322 100644 --- a/test/spec/modules/browsiRtdProvider_spec.js +++ b/test/spec/modules/browsiRtdProvider_spec.js @@ -89,12 +89,6 @@ describe('browsi Real time data sub module', function () { expect(browsiRTD.browsiSubmodule.getTargetingData([], null, null, auction)).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'], null, null, auction)).to.eql({adMock: {bv: 'NA'}}); - }); - it('should return prediction from server', function () { makeSlot({code: 'hasPrediction', divId: 'hasPrediction'}); const data = { From 2493f985e9049c8f0071e02a1f6d45c1fc0a856d Mon Sep 17 00:00:00 2001 From: Malkov Mikhail Date: Mon, 11 Dec 2023 17:30:25 +0300 Subject: [PATCH 062/246] nextMillennium Bid Adapter : add pbjs version and support for user.eids (#10812) * added support for gpp consent string * changed test for nextMillenniumBidAdapter * added some tests * added site.pagecat, site.content.cat and site.content.language to request * lint fix * formated code * formated code * formated code * pachage-lock with prebid * pachage-lock with prebid * formatted code * added device.sua, user.eids * formatted * fixed tests * fixed bug functio getSua --- modules/nextMillenniumBidAdapter.js | 40 +++++++++- .../modules/nextMillenniumBidAdapter_spec.js | 73 +++++++++++++++++++ 2 files changed, 110 insertions(+), 3 deletions(-) diff --git a/modules/nextMillenniumBidAdapter.js b/modules/nextMillenniumBidAdapter.js index 26d417b9baf..5dea4f9b651 100644 --- a/modules/nextMillenniumBidAdapter.js +++ b/modules/nextMillenniumBidAdapter.js @@ -14,6 +14,7 @@ import { triggerPixel, } from '../src/utils.js'; +import {getGlobal} from '../src/prebidGlobal.js'; import CONSTANTS from '../src/constants.json'; import {BANNER, VIDEO} from '../src/mediaTypes.js'; import {config} from '../src/config.js'; @@ -22,7 +23,7 @@ import * as events from '../src/events.js'; import {registerBidder} from '../src/adapters/bidderFactory.js'; import {getRefererInfo} from '../src/refererDetection.js'; -const NM_VERSION = '3.0.0'; +const NM_VERSION = '3.1.0'; const GVLID = 1060; const BIDDER_CODE = 'nextMillennium'; const ENDPOINT = 'https://pbs.nextmillmedia.com/openrtb2/auction'; @@ -47,6 +48,7 @@ const ALLOWED_ORTB2_PARAMETERS = [ 'site.pagecat', 'site.content.cat', 'site.content.language', + 'device.sua', ]; const sendingDataStatistic = initSendingDataStatistic(); @@ -93,6 +95,7 @@ export const spec = { nextMillennium: { nm_version: NM_VERSION, + pbjs_version: getGlobal()?.version || undefined, refresh_count: window.nmmRefreshCounts[bid.adUnitCode]++, elOffsets: getBoundingClient(bid), scrollTop: window.pageYOffset || document.documentElement.scrollTop, @@ -107,6 +110,7 @@ export const spec = { postBody.imp.push(getImp(bid, id)); setConsentStrings(postBody, bidderRequest); setOrtb2Parameters(postBody, bidderRequest?.ortb2); + setEids(postBody, bid); const urlParameters = parseUrl(getWindowTop().location.href).search; const isTest = urlParameters['pbs'] && urlParameters['pbs'] === 'test'; @@ -312,6 +316,12 @@ export function setOrtb2Parameters(postBody, ortb2 = {}) { } } +export function setEids(postBody, bid) { + if (!isArray(bid.userIdAsEids) || !bid.userIdAsEids.length) return; + + deepSetValue(postBody, 'user.eids', bid.userIdAsEids); +} + export function replaceUsersyncMacros(url, gdprConsent = {}, uspConsent = '', gppConsent = {}, type = '') { const { consentString = '', gdprApplies = false } = gdprConsent; const gdpr = Number(gdprApplies); @@ -391,7 +401,7 @@ function getAd(bid) { } else if (bid.nurl) { adUrl = bid.nurl; }; - } + }; return {ad, adUrl, vastXml, vastUrl}; } @@ -399,10 +409,21 @@ function getAd(bid) { function getSiteObj() { const refInfo = (getRefererInfo && getRefererInfo()) || {}; + let language = navigator.language; + let content; + if (language) { + // get ISO-639-1-alpha-2 (2 character language) + language = language.split('-')[0]; + content = { + language, + }; + }; + return { page: refInfo.page, ref: refInfo.ref, - domain: refInfo.domain + domain: refInfo.domain, + content, }; } @@ -410,6 +431,19 @@ function getDeviceObj() { return { w: window.innerWidth || window.document.documentElement.clientWidth || window.document.body.clientWidth || 0, h: window.innerHeight || window.document.documentElement.clientHeight || window.document.body.clientHeight || 0, + ua: window.navigator.userAgent || undefined, + sua: getSua(), + }; +} + +function getSua() { + let {brands, mobile, platform} = (window?.navigator?.userAgentData || {}); + if (!(brands && platform)) return undefined; + + return { + brands, + mobile: Number(!!mobile), + platform: (platform && {brand: platform}) || undefined, }; } diff --git a/test/spec/modules/nextMillenniumBidAdapter_spec.js b/test/spec/modules/nextMillenniumBidAdapter_spec.js index 8b512218d56..f57db82aedc 100644 --- a/test/spec/modules/nextMillenniumBidAdapter_spec.js +++ b/test/spec/modules/nextMillenniumBidAdapter_spec.js @@ -4,6 +4,7 @@ import { replaceUsersyncMacros, setConsentStrings, setOrtb2Parameters, + setEids, spec, } from 'modules/nextMillenniumBidAdapter.js'; @@ -403,6 +404,78 @@ describe('nextMillenniumBidAdapterTests', () => { setOrtb2Parameters(postBody, ortb2); expect(postBody).to.deep.equal(expected); }); + }; + }); + + describe('function setEids', () => { + const dataTests = [ + { + title: 'setEids - userIdAsEids is empty', + data: { + postBody: {}, + bid: { + userIdAsEids: undefined, + }, + }, + + expected: {}, + }, + + { + title: 'setEids - userIdAsEids - array is empty', + data: { + postBody: {}, + bid: { + userIdAsEids: [], + }, + }, + + expected: {}, + }, + + { + title: 'setEids - userIdAsEids is', + data: { + postBody: {}, + bid: { + userIdAsEids: [ + { + source: '33across.com', + uids: [{id: 'some-random-id-value', atype: 1}], + }, + + { + source: 'utiq.com', + uids: [{id: 'some-random-id-value', atype: 1}], + }, + ], + }, + }, + + expected: { + user: { + eids: [ + { + source: '33across.com', + uids: [{id: 'some-random-id-value', atype: 1}], + }, + + { + source: 'utiq.com', + uids: [{id: 'some-random-id-value', atype: 1}], + }, + ], + }, + }, + }, + ]; + + for (let { title, data, expected } of dataTests) { + it(title, () => { + const { postBody, bid } = data; + setEids(postBody, bid); + expect(postBody).to.deep.equal(expected); + }); } }); From eecdc2e92d0f186ce897dda5f4aa0ea9fec66529 Mon Sep 17 00:00:00 2001 From: Patrick McCann Date: Mon, 11 Dec 2023 16:07:26 -0500 Subject: [PATCH 063/246] Update deepintentBidAdapter.js (#10818) --- modules/deepintentBidAdapter.js | 2 ++ 1 file changed, 2 insertions(+) diff --git a/modules/deepintentBidAdapter.js b/modules/deepintentBidAdapter.js index e062686b320..7c24cd6a8f6 100644 --- a/modules/deepintentBidAdapter.js +++ b/modules/deepintentBidAdapter.js @@ -2,6 +2,7 @@ import { generateUUID, deepSetValue, deepAccess, isArray, isInteger, logError, l import {registerBidder} from '../src/adapters/bidderFactory.js'; import {BANNER, VIDEO} from '../src/mediaTypes.js'; const BIDDER_CODE = 'deepintent'; +const GVL_ID = 541; const BIDDER_ENDPOINT = 'https://prebid.deepintent.com/prebid'; const USER_SYNC_URL = 'https://cdn.deepintent.com/syncpixel.html'; const DI_M_V = '1.0.0'; @@ -32,6 +33,7 @@ export const ORTB_VIDEO_PARAMS = { }; export const spec = { code: BIDDER_CODE, + gvlid: GVL_ID, supportedMediaTypes: [BANNER, VIDEO], aliases: [], From 222b4428c36a71e898836ff3477ed2a3b88cea01 Mon Sep 17 00:00:00 2001 From: Chris Southern <79725079+southern-growthcode@users.noreply.github.com> Date: Mon, 11 Dec 2023 16:14:46 -0500 Subject: [PATCH 064/246] GC-158 When accessing the TCF it is pulling from the wrong location, use userConsent.gdpr.consentString (#10808) --- modules/growthCodeRtdProvider.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/modules/growthCodeRtdProvider.js b/modules/growthCodeRtdProvider.js index e7dce81f7d0..b12b25a0951 100644 --- a/modules/growthCodeRtdProvider.js +++ b/modules/growthCodeRtdProvider.js @@ -81,8 +81,8 @@ function callServer(configParams, items, expiresAt, userConsent) { url = tryAppendQueryString(url, 'pid', configParams.pid); url = tryAppendQueryString(url, 'u', window.location.href); url = tryAppendQueryString(url, 'gcid', gcid); - if ((userConsent !== null) && (userConsent.gdpr !== null) && (userConsent.gdpr.consentData.getTCData.tcString)) { - url = tryAppendQueryString(url, 'tcf', userConsent.gdpr.consentData.getTCData.tcString) + if ((userConsent !== null) && (userConsent.gdpr !== null) && (userConsent.gdpr.consentString)) { + url = tryAppendQueryString(url, 'tcf', userConsent.gdpr.consentString) } ajax.ajaxBuilder()(url, { From 3ec81e61e6cb51c4a9420427c1ca9c504a7848a3 Mon Sep 17 00:00:00 2001 From: Olivier Date: Mon, 11 Dec 2023 22:44:48 +0100 Subject: [PATCH 065/246] Adagio Analytics Adapter: listen to AUCTION_END (#10798) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Adagio Analytics Adapter: add tracker on auction_end * Adagio Analytics Adapter: refacto metadata transfer * Adagio Analytics Adapter: update pba props --------- Co-authored-by: François Rotta --- modules/adagioAnalyticsAdapter.js | 58 +++++++++- .../modules/adagioAnalyticsAdapter_spec.js | 109 +++++++++++++----- 2 files changed, 135 insertions(+), 32 deletions(-) diff --git a/modules/adagioAnalyticsAdapter.js b/modules/adagioAnalyticsAdapter.js index 9c4c0e8fea7..ee2368fde08 100644 --- a/modules/adagioAnalyticsAdapter.js +++ b/modules/adagioAnalyticsAdapter.js @@ -22,6 +22,12 @@ const cache = { getAuction: function(auctionId, adUnitCode) { return this.auctions[auctionId][adUnitCode]; }, + getBiddersFromAuction: function(auctionId, adUnitCode) { + return this.getAuction(auctionId, adUnitCode).bdrs.split(','); + }, + getAllAdUnitCodes: function(auctionId) { + return Object.keys(this.auctions[auctionId]); + }, updateAuction: function(auctionId, adUnitCode, values) { this.auctions[auctionId][adUnitCode] = { ...this.auctions[auctionId][adUnitCode], @@ -74,7 +80,8 @@ const adagioEnqueue = function adagioEnqueue(action, data) { const guard = { adagio: (value) => isAdagio(value), - bidTracked: (auctionId, adUnitCode) => deepAccess(cache, `auctions.${auctionId}.${adUnitCode}`, false) + bidTracked: (auctionId, adUnitCode) => deepAccess(cache, `auctions.${auctionId}.${adUnitCode}`, false), + auctionTracked: (auctionId) => deepAccess(cache, `auctions.${auctionId}`, false) }; function removeDuplicates(arr, getKey) { @@ -105,6 +112,19 @@ function getMediaTypeAlias(mediaType) { return mediaTypesMap[mediaType] || mediaType; }; +function addKeyPrefix(obj, prefix) { + return Object.keys(obj).reduce((acc, key) => { + // We don't want to prefix already prefixed keys. + if (key.startsWith(prefix)) { + acc[key] = obj[key]; + return acc; + } + + acc[`${prefix}${key}`] = obj[key]; + return acc; + }, {}); +} + /** * sendRequest to Adagio. It filter null values and encode each query param. * @param {Object} qp @@ -146,6 +166,7 @@ function getTargetedAuctionId(bid) { * HANDLERS * - handlerAuctionInit * - handlerBidResponse + * - handlerAuctionEnd * - handlerBidWon * - handlerAdRender * @@ -227,11 +248,10 @@ function handlerAuctionInit(event) { auct_id: adagioAuctionId, adu_code: adUnitCode, url_dmn: w.location.hostname, - dvc: params.environment, pgtyp: params.pagetype, plcmt: params.placement, - tname: params.testName || null, - tvname: params.testVariationName || null, + t_n: params.testName || null, + t_v: params.testVersion || null, mts: mediaTypesKeys.join(','), ban_szs: bannerSizes.join(','), bdrs: bidders.map(bidder => getAdapterNameForAlias(bidder.bidder)).sort().join(','), @@ -257,11 +277,33 @@ function handlerBidResponse(event) { return; } + if (!event.pba) { + return; + } + cache.updateAuction(event.auctionId, event.adUnitCode, { - adg_sid: event.seatId || null + ...addKeyPrefix(event.pba, 'e_') }); }; +function handlerAuctionEnd(event) { + const { auctionId } = event; + + if (!guard.auctionTracked(auctionId)) { + return; + } + + const adUnitCodes = cache.getAllAdUnitCodes(auctionId); + adUnitCodes.forEach(adUnitCode => { + const mapper = (bidder) => event.bidsReceived.find(bid => bid.adUnitCode === adUnitCode && bid.bidder === bidder) ? '1' : '0'; + + cache.updateAuction(auctionId, adUnitCode, { + bdrs_bid: cache.getBiddersFromAuction(auctionId, adUnitCode).map(mapper).join(',') + }); + sendNewBeacon(auctionId, adUnitCode); + }); +} + function handlerBidWon(event) { let auctionId = getTargetedAuctionId(event); @@ -340,10 +382,14 @@ let adagioAdapter = Object.assign(adapter({ emptyUrl, analyticsType }), { case CONSTANTS.EVENTS.BID_RESPONSE: handlerBidResponse(args); break; + case CONSTANTS.EVENTS.AUCTION_END: + handlerAuctionEnd(args); + break; case CONSTANTS.EVENTS.BID_WON: handlerBidWon(args); break; - case CONSTANTS.EVENTS.AD_RENDER_SUCCEEDED: + // AD_RENDER_SUCCEEDED seems redundant with BID_WON. + // case CONSTANTS.EVENTS.AD_RENDER_SUCCEEDED: case CONSTANTS.EVENTS.AD_RENDER_FAILED: handlerAdRender(args, eventType === CONSTANTS.EVENTS.AD_RENDER_SUCCEEDED); break; diff --git a/test/spec/modules/adagioAnalyticsAdapter_spec.js b/test/spec/modules/adagioAnalyticsAdapter_spec.js index 8359a34baa0..5ffd7b0b685 100644 --- a/test/spec/modules/adagioAnalyticsAdapter_spec.js +++ b/test/spec/modules/adagioAnalyticsAdapter_spec.js @@ -211,7 +211,10 @@ const BID_ADAGIO = Object.assign({}, BID_ADAGIO, { meta: { advertiserDomains: ['example.com'] }, - seatId: '42', + pba: { + sid: '42', + e_pba_test: true + } }); const BID_ANOTHER = Object.assign({}, BID_ANOTHER, { @@ -256,7 +259,9 @@ const PARAMS_ADG = { pageviewId: 'a68e6d70-213b-496c-be0a-c468ff387106', environment: 'desktop', pagetype: 'article', - placement: 'pave_top' + placement: 'pave_top', + testName: 'test', + testVersion: 'version', }; const AUCTION_INIT_ANOTHER = { @@ -285,6 +290,11 @@ const AUCTION_INIT_ANOTHER = { 'params': { 'publisherId': '1001' }, + }, { + 'bidder': 'nobid', + 'params': { + 'publisherId': '1002' + }, }, { 'bidder': 'adagio', 'params': { @@ -354,6 +364,24 @@ const AUCTION_INIT_ANOTHER = { 'auctionId': AUCTION_ID, 'src': 'client', 'bidRequestsCount': 1 + }, { + 'bidder': 'nobid', + 'params': { + 'publisherId': '1001' + }, + 'mediaTypes': { + 'banner': { + 'sizes': [[640, 480]] + } + }, + 'adUnitCode': '/19968336/footer-bid-tag-1', + 'transactionId': 'ca4af27a-6d02-4f90-949d-d5541fa12014', + 'sizes': [[640, 480]], + 'bidId': '2ecff0db240757', + 'bidderRequestId': '1be65d7958826a', + 'auctionId': AUCTION_ID, + 'src': 'client', + 'bidRequestsCount': 1 } ], 'timeout': 3000, @@ -533,6 +561,14 @@ const AUCTION_INIT_CACHE = { 'timeout': 3000 }; +const AUCTION_END_ANOTHER = Object.assign({}, AUCTION_INIT_ANOTHER, { + bidsReceived: [BID_ANOTHER, BID_ADAGIO] +}); + +const AUCTION_END_ANOTHER_NOBID = Object.assign({}, AUCTION_INIT_ANOTHER, { + bidsReceived: [] +}); + const MOCK = { SET_TARGETING: { [BID_ADAGIO.adUnitCode]: BID_ADAGIO.adserverTargeting, @@ -546,6 +582,10 @@ const MOCK = { adagio: BID_ADAGIO, another: BID_ANOTHER }, + AUCTION_END: { + another: AUCTION_END_ANOTHER, + another_nobid: AUCTION_END_ANOTHER_NOBID + }, BID_WON: { adagio: Object.assign({}, BID_ADAGIO, { 'status': 'rendered' @@ -569,6 +609,12 @@ const MOCK = { bid: BID_CACHED } }, + AD_RENDER_FAILED: { + bidcached: { + adId: 'fake_ad_id_2', + bid: BID_CACHED + } + } }; describe('adagio analytics adapter', () => { @@ -616,10 +662,11 @@ describe('adagio analytics adapter', () => { events.emit(constants.EVENTS.AUCTION_INIT, MOCK.AUCTION_INIT.another); events.emit(constants.EVENTS.BID_RESPONSE, MOCK.BID_RESPONSE.adagio); events.emit(constants.EVENTS.BID_RESPONSE, MOCK.BID_RESPONSE.another); + events.emit(constants.EVENTS.AUCTION_END, MOCK.AUCTION_END.another); events.emit(constants.EVENTS.BID_WON, MOCK.BID_WON.another); events.emit(constants.EVENTS.AD_RENDER_SUCCEEDED, MOCK.AD_RENDER_SUCCEEDED.another); - expect(server.requests.length).to.equal(3); + expect(server.requests.length).to.equal(3, 'requests count'); { const { protocol, hostname, pathname, search } = utils.parseUrl(server.requests[0].url); expect(protocol).to.equal('https'); @@ -633,12 +680,11 @@ describe('adagio analytics adapter', () => { expect(search.site).to.equal('test-com'); expect(search.pv_id).to.equal('a68e6d70-213b-496c-be0a-c468ff387106'); expect(search.url_dmn).to.equal(window.location.hostname); - expect(search.dvc).to.equal('desktop'); expect(search.pgtyp).to.equal('article'); expect(search.plcmt).to.equal('pave_top'); expect(search.mts).to.equal('ban'); expect(search.ban_szs).to.equal('640x100,640x480'); - expect(search.bdrs).to.equal('adagio,another'); + expect(search.bdrs).to.equal('adagio,another,nobid'); expect(search.adg_mts).to.equal('ban'); } @@ -648,9 +694,19 @@ describe('adagio analytics adapter', () => { expect(hostname).to.equal('c.4dex.io'); expect(pathname).to.equal('/pba.gif'); expect(search.v).to.equal('2'); + expect(search.e_sid).to.equal('42'); + expect(search.e_pba_test).to.equal('true'); + expect(search.bdrs_bid).to.equal('1,1,0'); + } + + { + const { protocol, hostname, pathname, search } = utils.parseUrl(server.requests[2].url); + expect(protocol).to.equal('https'); + expect(hostname).to.equal('c.4dex.io'); + expect(pathname).to.equal('/pba.gif'); + expect(search.v).to.equal('3'); expect(search.auct_id).to.equal(AUCTION_ID_ADAGIO); expect(search.adu_code).to.equal('/19968336/header-bid-tag-1'); - expect(search.adg_sid).to.equal('42'); expect(search.win_bdr).to.equal('another'); expect(search.win_mt).to.equal('ban'); expect(search.win_ban_sz).to.equal('728x90'); @@ -661,17 +717,6 @@ describe('adagio analytics adapter', () => { expect(search.og_cur).to.equal('GBP'); expect(search.og_cur_rate).to.equal('1.6'); } - - { - const { protocol, hostname, pathname, search } = utils.parseUrl(server.requests[2].url); - expect(protocol).to.equal('https'); - expect(hostname).to.equal('c.4dex.io'); - expect(pathname).to.equal('/pba.gif'); - expect(search.v).to.equal('3'); - expect(search.auct_id).to.equal(AUCTION_ID_ADAGIO); - expect(search.adu_code).to.equal('/19968336/header-bid-tag-1'); - expect(search.rndr).to.equal('1'); - } }); it('builds and sends auction data with a cached bid win', () => { @@ -691,10 +736,11 @@ describe('adagio analytics adapter', () => { events.emit(constants.EVENTS.AUCTION_INIT, MOCK.AUCTION_INIT.another); events.emit(constants.EVENTS.BID_RESPONSE, MOCK.BID_RESPONSE.adagio); events.emit(constants.EVENTS.BID_RESPONSE, MOCK.BID_RESPONSE.another); + events.emit(constants.EVENTS.AUCTION_END, MOCK.AUCTION_END.another_nobid); events.emit(constants.EVENTS.BID_WON, MOCK.BID_WON.bidcached); - events.emit(constants.EVENTS.AD_RENDER_SUCCEEDED, MOCK.AD_RENDER_SUCCEEDED.bidcached); + events.emit(constants.EVENTS.AD_RENDER_FAILED, MOCK.AD_RENDER_FAILED.bidcached); - expect(server.requests.length).to.equal(4); + expect(server.requests.length).to.equal(5, 'requests count'); { const { protocol, hostname, pathname, search } = utils.parseUrl(server.requests[0].url); expect(protocol).to.equal('https'); @@ -708,13 +754,14 @@ describe('adagio analytics adapter', () => { expect(search.site).to.equal('test-com'); expect(search.pv_id).to.equal('a68e6d70-213b-496c-be0a-c468ff387106'); expect(search.url_dmn).to.equal(window.location.hostname); - expect(search.dvc).to.equal('desktop'); expect(search.pgtyp).to.equal('article'); expect(search.plcmt).to.equal('pave_top'); expect(search.mts).to.equal('ban'); expect(search.ban_szs).to.equal('640x100,640x480'); expect(search.bdrs).to.equal('adagio,another'); expect(search.adg_mts).to.equal('ban'); + expect(search.t_n).to.equal('test'); + expect(search.t_v).to.equal('version'); } { @@ -730,12 +777,11 @@ describe('adagio analytics adapter', () => { expect(search.site).to.equal('test-com'); expect(search.pv_id).to.equal('a68e6d70-213b-496c-be0a-c468ff387106'); expect(search.url_dmn).to.equal(window.location.hostname); - expect(search.dvc).to.equal('desktop'); expect(search.pgtyp).to.equal('article'); expect(search.plcmt).to.equal('pave_top'); expect(search.mts).to.equal('ban'); expect(search.ban_szs).to.equal('640x100,640x480'); - expect(search.bdrs).to.equal('adagio,another'); + expect(search.bdrs).to.equal('adagio,another,nobid'); expect(search.adg_mts).to.equal('ban'); } @@ -745,10 +791,20 @@ describe('adagio analytics adapter', () => { expect(hostname).to.equal('c.4dex.io'); expect(pathname).to.equal('/pba.gif'); expect(search.v).to.equal('2'); + expect(search.e_sid).to.equal('42'); + expect(search.e_pba_test).to.equal('true'); + expect(search.bdrs_bid).to.equal('0,0,0'); + } + + { + const { protocol, hostname, pathname, search } = utils.parseUrl(server.requests[3].url); + expect(protocol).to.equal('https'); + expect(hostname).to.equal('c.4dex.io'); + expect(pathname).to.equal('/pba.gif'); + expect(search.v).to.equal('3'); expect(search.auct_id).to.equal(AUCTION_ID_ADAGIO); expect(search.auct_id_c).to.equal(AUCTION_ID_CACHE_ADAGIO); expect(search.adu_code).to.equal('/19968336/header-bid-tag-1'); - expect(search.adg_sid).to.equal('42'); expect(search.win_bdr).to.equal('adagio'); expect(search.win_mt).to.equal('ban'); expect(search.win_ban_sz).to.equal('728x90'); @@ -758,18 +814,19 @@ describe('adagio analytics adapter', () => { expect(search.og_cpm).to.equal('1.42'); expect(search.og_cur).to.equal('USD'); expect(search.og_cur_rate).to.equal('1'); + expect(search.rndr).to.not.exist; } { - const { protocol, hostname, pathname, search } = utils.parseUrl(server.requests[3].url); + const { protocol, hostname, pathname, search } = utils.parseUrl(server.requests[4].url); expect(protocol).to.equal('https'); expect(hostname).to.equal('c.4dex.io'); expect(pathname).to.equal('/pba.gif'); - expect(search.v).to.equal('3'); + expect(search.v).to.equal('4'); expect(search.auct_id).to.equal(AUCTION_ID_ADAGIO); expect(search.auct_id_c).to.equal(AUCTION_ID_CACHE_ADAGIO); expect(search.adu_code).to.equal('/19968336/header-bid-tag-1'); - expect(search.rndr).to.equal('1'); + expect(search.rndr).to.equal('0'); } }); }); From 6275c00cdc47fefd521b4a1f8cbae44944560367 Mon Sep 17 00:00:00 2001 From: kapil-tuptewar <91458408+kapil-tuptewar@users.noreply.github.com> Date: Tue, 12 Dec 2023 03:31:13 +0530 Subject: [PATCH 066/246] PubMaticAnalyticAdapter : Added new fields related to floors (fetch status, source of floor, provider and unique id) in analytics call (#10768) * Added floors field to logger call * Added sid in logger records --- modules/pubmaticAnalyticsAdapter.js | 30 ++++++++--- src/constants.json | 4 +- .../modules/pubmaticAnalyticsAdapter_spec.js | 51 +++++++++++++++++++ 3 files changed, 77 insertions(+), 8 deletions(-) diff --git a/modules/pubmaticAnalyticsAdapter.js b/modules/pubmaticAnalyticsAdapter.js index a8f2b8d20ee..fbc45f1a8d7 100755 --- a/modules/pubmaticAnalyticsAdapter.js +++ b/modules/pubmaticAnalyticsAdapter.js @@ -1,4 +1,4 @@ -import {_each, isArray, isStr, logError, logWarn, pick} from '../src/utils.js'; +import {_each, isArray, isStr, logError, logWarn, pick, generateUUID} from '../src/utils.js'; import adapter from '../libraries/analyticsAdapter/AnalyticsAdapter.js'; import adapterManager from '../src/adapterManager.js'; import CONSTANTS from '../src/constants.json'; @@ -286,7 +286,7 @@ function gatherPartnerBidsForAdUnitForLogger(adUnit, adUnitId, highestBid) { 'ocpm': bid.bidResponse ? (bid.bidResponse.originalCpm || 0) : 0, 'ocry': bid.bidResponse ? (bid.bidResponse.originalCurrency || CURRENCY_USD) : CURRENCY_USD, 'piid': bid.bidResponse ? (bid.bidResponse.partnerImpId || EMPTY_STRING) : EMPTY_STRING, - 'frv': (bid.bidResponse ? (bid.bidResponse.floorData ? bid.bidResponse.floorData.floorRuleValue : undefined) : undefined), + 'frv': bid.bidResponse ? bid.bidResponse.floorData?.floorRuleValue : undefined, 'md': bid.bidResponse ? getMetadata(bid.bidResponse.meta) : undefined }); }); @@ -337,11 +337,10 @@ function executeBidsLoggerCall(e, highestCpmBids) { let auctionId = e.auctionId; let referrer = config.getConfig('pageUrl') || cache.auctions[auctionId].referer || ''; let auctionCache = cache.auctions[auctionId]; - let floorData = auctionCache.floorData; + let floorData = auctionCache?.floorData; + let floorFetchStatus = getFloorFetchStatus(auctionCache?.floorData); let outputObj = { s: [] }; let pixelURL = END_POINT_BID_LOGGER; - // will return true if floor data is present. - let fetchStatus = getFloorFetchStatus(auctionCache.floorData); if (!auctionCache) { return; @@ -364,7 +363,7 @@ function executeBidsLoggerCall(e, highestCpmBids) { outputObj['tgid'] = getTgId(); outputObj['pbv'] = getGlobal()?.version || '-1'; - if (floorData && fetchStatus) { + if (floorData && floorFetchStatus) { outputObj['fmv'] = floorData.floorRequestData ? floorData.floorRequestData.modelVersion || undefined : undefined; outputObj['ft'] = floorData.floorResponseData ? (floorData.floorResponseData.enforcements.enforceJS == false ? 0 : 1) : undefined; } @@ -379,8 +378,25 @@ function executeBidsLoggerCall(e, highestCpmBids) { 'mt': getAdUnitAdFormats(origAdUnit), 'sz': getSizesForAdUnit(adUnit, adUnitId), 'ps': gatherPartnerBidsForAdUnitForLogger(adUnit, adUnitId, highestCpmBids.filter(bid => bid.adUnitCode === adUnitId)), - 'fskp': (floorData && fetchStatus) ? (floorData.floorRequestData ? (floorData.floorRequestData.skipped == false ? 0 : 1) : undefined) : undefined, + 'fskp': floorData && floorFetchStatus ? (floorData.floorRequestData ? (floorData.floorRequestData.skipped == false ? 0 : 1) : undefined) : undefined, + 'sid': generateUUID() }; + if (floorData?.floorRequestData) { + const { location, fetchStatus, floorProvider } = floorData?.floorRequestData; + slotObject.ffs = { + [CONSTANTS.FLOOR_VALUES.SUCCESS]: 1, + [CONSTANTS.FLOOR_VALUES.ERROR]: 2, + [CONSTANTS.FLOOR_VALUES.TIMEOUT]: 4, + undefined: 0 + }[fetchStatus]; + slotObject.fsrc = { + [CONSTANTS.FLOOR_VALUES.FETCH]: 2, + [CONSTANTS.FLOOR_VALUES.NO_DATA]: 2, + [CONSTANTS.FLOOR_VALUES.AD_UNIT]: 1, + [CONSTANTS.FLOOR_VALUES.SET_CONFIG]: 1 + }[location]; + slotObject.fp = floorProvider; + } slotsArray.push(slotObject); return slotsArray; }, []); diff --git a/src/constants.json b/src/constants.json index ad0f5b3a71b..c8266ff85f8 100644 --- a/src/constants.json +++ b/src/constants.json @@ -176,6 +176,8 @@ "AD_UNIT": "adUnit", "SET_CONFIG": "setConfig", "FETCH": "fetch", - "SUCCESS": "success" + "SUCCESS": "success", + "ERROR": "error", + "TIMEOUT": "timeout" } } diff --git a/test/spec/modules/pubmaticAnalyticsAdapter_spec.js b/test/spec/modules/pubmaticAnalyticsAdapter_spec.js index 7fce08fc24f..6af497bd2e9 100755 --- a/test/spec/modules/pubmaticAnalyticsAdapter_spec.js +++ b/test/spec/modules/pubmaticAnalyticsAdapter_spec.js @@ -374,6 +374,10 @@ describe('pubmatic analytics adapter', function () { // slot 1 expect(data.s[0].sn).to.equal('/19968336/header-bid-tag-0'); expect(data.s[0].fskp).to.equal(0); + expect(data.s[0].sid).not.to.be.undefined; + expect(data.s[0].ffs).to.equal(1); + expect(data.s[0].fsrc).to.equal(2); + expect(data.s[0].fp).to.equal('pubmatic'); expect(data.s[0].sz).to.deep.equal(['640x480']); expect(data.s[0].ps).to.be.an('array'); expect(data.s[0].au).to.equal('/19968336/header-bid-tag-0'); @@ -403,6 +407,10 @@ describe('pubmatic analytics adapter', function () { // slot 2 expect(data.s[1].sn).to.equal('/19968336/header-bid-tag-1'); expect(data.s[1].fskp).to.equal(0); + expect(data.s[1].sid).not.to.be.undefined; + expect(data.s[1].ffs).to.equal(1); + expect(data.s[1].fsrc).to.equal(2); + expect(data.s[1].fp).to.equal('pubmatic'); 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); @@ -583,6 +591,10 @@ describe('pubmatic analytics adapter', function () { // slot 1 expect(data.s[0].sn).to.equal('/19968336/header-bid-tag-0'); expect(data.s[0].fskp).to.equal(0); + expect(data.s[0].sid).not.to.be.undefined; + expect(data.s[0].ffs).to.equal(1); + expect(data.s[0].fsrc).to.equal(2); + expect(data.s[0].fp).to.equal('pubmatic'); expect(data.s[0].sz).to.deep.equal(['640x480']); expect(data.s[0].ps).to.be.an('array'); expect(data.s[0].au).to.equal('/19968336/header-bid-tag-0'); @@ -702,6 +714,13 @@ describe('pubmatic analytics adapter', function () { expect(data.tgid).to.equal(0);// test group id should be an INT between 0-15 else set to 0 expect(data.s[1].sn).to.equal('/19968336/header-bid-tag-1'); expect(data.s[1].fskp).to.equal(0); + + expect(data.s[1].sid).not.to.be.undefined; + + expect(data.s[1].ffs).to.equal(1); + expect(data.s[1].fsrc).to.equal(2); + expect(data.s[1].fp).to.equal('pubmatic'); + 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); @@ -784,6 +803,10 @@ describe('pubmatic analytics adapter', function () { let data = getLoggerJsonFromRequest(request.requestBody); expect(data.s[1].sn).to.equal('/19968336/header-bid-tag-1'); expect(data.s[1].fskp).to.equal(0); + expect(data.s[1].sid).not.to.be.undefined; + expect(data.s[1].ffs).to.equal(1); + expect(data.s[1].fsrc).to.equal(2); + expect(data.s[1].fp).to.equal('pubmatic'); 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); @@ -846,6 +869,7 @@ describe('pubmatic analytics adapter', function () { 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].sid).not.to.be.undefined; 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'); @@ -893,6 +917,10 @@ describe('pubmatic analytics adapter', function () { let data = getLoggerJsonFromRequest(request.requestBody); expect(data.s[1].sn).to.equal('/19968336/header-bid-tag-1'); expect(data.s[1].fskp).to.equal(0); + expect(data.s[1].sid).not.to.be.undefined; + expect(data.s[1].ffs).to.equal(1); + expect(data.s[1].fsrc).to.equal(2); + expect(data.s[1].fp).to.equal('pubmatic'); 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); @@ -952,6 +980,7 @@ describe('pubmatic analytics adapter', function () { 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].sid).not.to.be.undefined; 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'); @@ -1006,6 +1035,10 @@ describe('pubmatic analytics adapter', function () { let data = getLoggerJsonFromRequest(request.requestBody); expect(data.s[1].sn).to.equal('/19968336/header-bid-tag-1'); expect(data.s[1].fskp).to.equal(0); + expect(data.s[1].sid).not.to.be.undefined; + expect(data.s[1].ffs).to.equal(1); + expect(data.s[1].fsrc).to.equal(2); + expect(data.s[1].fp).to.equal('pubmatic'); 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); @@ -1063,6 +1096,7 @@ describe('pubmatic analytics adapter', function () { 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].sid).not.to.be.undefined; 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'); @@ -1121,7 +1155,11 @@ describe('pubmatic analytics adapter', function () { // Testing only for rejected bid as other scenarios will be covered under other TCs expect(data.s[1].sn).to.equal('/19968336/header-bid-tag-1'); expect(data.s[1].fskp).to.equal(0); + expect(data.s[1].ffs).to.equal(1); + expect(data.s[1].fsrc).to.equal(2); + expect(data.s[1].fp).to.equal('pubmatic'); expect(data.s[1].sz).to.deep.equal(['1000x300', '970x250', '728x90']); + expect(data.s[1].sid).not.to.be.undefined; 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'); @@ -1196,7 +1234,11 @@ describe('pubmatic analytics adapter', function () { // slot 1 expect(data.s[0].sn).to.equal('/19968336/header-bid-tag-0'); expect(data.s[0].fskp).to.equal(0); + expect(data.s[0].ffs).to.equal(1); + expect(data.s[0].fsrc).to.equal(2); + expect(data.s[0].fp).to.equal('pubmatic'); expect(data.s[0].sz).to.deep.equal(['640x480']); + expect(data.s[0].sid).not.to.be.undefined; expect(data.s[0].ps).to.be.an('array'); expect(data.s[0].au).to.equal('/19968336/header-bid-tag-0'); expect(data.s[0].ps.length).to.equal(1); @@ -1226,7 +1268,11 @@ describe('pubmatic analytics adapter', function () { // slot 2 expect(data.s[1].sn).to.equal('/19968336/header-bid-tag-1'); expect(data.s[1].fskp).to.equal(0); + expect(data.s[1].ffs).to.equal(1); + expect(data.s[1].fsrc).to.equal(2); + expect(data.s[1].fp).to.equal('pubmatic'); expect(data.s[1].sz).to.deep.equal(['1000x300', '970x250', '728x90']); + expect(data.s[1].sid).not.to.be.undefined; 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'); @@ -1318,7 +1364,11 @@ describe('pubmatic analytics adapter', function () { // slot 1 expect(data.s[0].sn).to.equal('/19968336/header-bid-tag-0'); expect(data.s[0].fskp).to.equal(0); + expect(data.s[0].ffs).to.equal(1); + expect(data.s[0].fsrc).to.equal(2); + expect(data.s[0].fp).to.equal('pubmatic'); expect(data.s[0].sz).to.deep.equal(['640x480']); + expect(data.s[0].sid).not.to.be.undefined; expect(data.s[0].ps).to.be.an('array'); expect(data.s[0].au).to.equal('/19968336/header-bid-tag-0'); expect(data.s[0].ps.length).to.equal(1); @@ -1348,6 +1398,7 @@ describe('pubmatic analytics adapter', function () { // 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].sid).not.to.be.undefined; 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'); From 048f735ccea9907ca72f068042bd35394c58689b Mon Sep 17 00:00:00 2001 From: pm-priyanka-deshmane <107103300+pm-priyanka-deshmane@users.noreply.github.com> Date: Tue, 12 Dec 2023 22:05:14 +0530 Subject: [PATCH 067/246] Pubmatic Analytics Adapter : skip duplicate data for pubmatic bids in logger and tracker (#10787) * Adding config for openwrap metadapter * Changed from http to https and timeout to 500 * Timeout value update * 1.Added safechecks for s2s metadapter case 2.Skipped firing client side tracker for pubmatic 3.Skipped adding pubmatic bid in logger * Analytics adapter changes to avoid duplicate data in case of OW S2S setup * Moved to bottom * kick off tests --------- Co-authored-by: Chris Huie --- modules/pubmaticAnalyticsAdapter.js | 38 ++- .../modules/pubmaticAnalyticsAdapter_spec.js | 248 ++++++++++++++++-- 2 files changed, 259 insertions(+), 27 deletions(-) diff --git a/modules/pubmaticAnalyticsAdapter.js b/modules/pubmaticAnalyticsAdapter.js index fbc45f1a8d7..66898301cd0 100755 --- a/modules/pubmaticAnalyticsAdapter.js +++ b/modules/pubmaticAnalyticsAdapter.js @@ -9,6 +9,7 @@ import {getGptSlotInfoForAdUnitCode} from '../libraries/gptUtils/gptUtils.js'; /// /////////// CONSTANTS ////////////// const ADAPTER_CODE = 'pubmatic'; +const VENDOR_OPENWRAP = 'openwrap'; const SEND_TIMEOUT = 2000; const END_POINT_HOST = 'https://t.pubmatic.com/'; const END_POINT_BID_LOGGER = END_POINT_HOST + 'wl?'; @@ -258,12 +259,27 @@ function isS2SBidder(bidder) { return (s2sBidders.indexOf(bidder) > -1) ? 1 : 0 } +function isOWPubmaticBid(adapterName) { + let s2sConf = config.getConfig('s2sConfig'); + let s2sConfArray = isArray(s2sConf) ? s2sConf : [s2sConf]; + return s2sConfArray.some(conf => { + if (adapterName === ADAPTER_CODE && conf.defaultVendor === VENDOR_OPENWRAP && + conf.bidders.indexOf(ADAPTER_CODE) > -1) { + return true; + } + }) +} + function gatherPartnerBidsForAdUnitForLogger(adUnit, adUnitId, highestBid) { highestBid = (highestBid && highestBid.length > 0) ? highestBid[0] : null; return Object.keys(adUnit.bids).reduce(function(partnerBids, bidId) { adUnit.bids[bidId].forEach(function(bid) { + let adapterName = getAdapterNameForAlias(bid.adapterCode || bid.bidder); + if (isOWPubmaticBid(adapterName) && isS2SBidder(bid.bidder)) { + return; + } partnerBids.push({ - 'pn': getAdapterNameForAlias(bid.adapterCode || bid.bidder), + 'pn': adapterName, 'bc': bid.bidderCode || bid.bidder, 'bidid': bid.bidId || bidId, 'db': bid.bidResponse ? 0 : 1, @@ -418,13 +434,20 @@ function executeBidsLoggerCall(e, highestCpmBids) { function executeBidWonLoggerCall(auctionId, adUnitId) { const winningBidId = cache.auctions[auctionId].adUnitCodes[adUnitId].bidWon; const winningBids = cache.auctions[auctionId].adUnitCodes[adUnitId].bids[winningBidId]; - let winningBid = winningBids[0]; + if (!winningBids) { + logWarn(LOG_PRE_FIX + 'Could not find winningBids for : ', auctionId); + return; + } + let winningBid = winningBids[0]; if (winningBids.length > 1) { winningBid = winningBids.filter(bid => bid.adId === cache.auctions[auctionId].adUnitCodes[adUnitId].bidWonAdId)[0]; } const adapterName = getAdapterNameForAlias(winningBid.adapterCode || winningBid.bidder); + if (isOWPubmaticBid(adapterName) && isS2SBidder(winningBid.bidder)) { + return; + } let origAdUnit = getAdUnit(cache.auctions[auctionId].origAdUnits, adUnitId) || {}; let auctionCache = cache.auctions[auctionId]; let floorData = auctionCache.floorData; @@ -477,7 +500,10 @@ function executeBidWonLoggerCall(auctionId, adUnitId) { function auctionInitHandler(args) { s2sBidders = (function() { let s2sConf = config.getConfig('s2sConfig'); - return (s2sConf && isArray(s2sConf.bidders)) ? s2sConf.bidders : []; + let s2sBidders = []; + (s2sConf || []) && + isArray(s2sConf) ? s2sConf.map(conf => s2sBidders.push(...conf.bidders)) : s2sBidders.push(...s2sConf.bidders); + return s2sBidders || []; }()); let cacheEntry = pick(args, [ 'timestamp', @@ -508,6 +534,10 @@ function bidRequestedHandler(args) { } function bidResponseHandler(args) { + if (!args.requestId) { + logWarn(LOG_PRE_FIX + 'Got null requestId in bidResponseHandler'); + return; + } let bid = cache.auctions[args.auctionId].adUnitCodes[args.adUnitCode].bids[args.requestId][0]; if (!bid) { logError(LOG_PRE_FIX + 'Could not find associated bid request for bid response with requestId: ', args.requestId); @@ -572,7 +602,7 @@ function auctionEndHandler(args) { let highestCpmBids = getGlobal().getHighestCpmBids() || []; setTimeout(() => { executeBidsLoggerCall.call(this, args, highestCpmBids); - }, (cache.auctions[args.auctionId].bidderDonePendingCount === 0 ? 500 : SEND_TIMEOUT)); + }, (cache.auctions[args.auctionId]?.bidderDonePendingCount === 0 ? 500 : SEND_TIMEOUT)); } function bidTimeoutHandler(args) { diff --git a/test/spec/modules/pubmaticAnalyticsAdapter_spec.js b/test/spec/modules/pubmaticAnalyticsAdapter_spec.js index 6af497bd2e9..fe31361d3ba 100755 --- a/test/spec/modules/pubmaticAnalyticsAdapter_spec.js +++ b/test/spec/modules/pubmaticAnalyticsAdapter_spec.js @@ -380,8 +380,8 @@ describe('pubmatic analytics adapter', function () { expect(data.s[0].fp).to.equal('pubmatic'); expect(data.s[0].sz).to.deep.equal(['640x480']); expect(data.s[0].ps).to.be.an('array'); - expect(data.s[0].au).to.equal('/19968336/header-bid-tag-0'); - expect(data.s[0].ps.length).to.equal(1); + expect(data.s[0].au).to.equal('/19968336/header-bid-tag-0'); + 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'); @@ -394,8 +394,8 @@ describe('pubmatic analytics adapter', function () { expect(data.s[0].ps[0].en).to.equal(1.23); expect(data.s[0].ps[0].di).to.equal('-1'); expect(data.s[0].ps[0].dc).to.equal(''); - expect(data.s[0].ps[0].l1).to.equal(944); - expect(data.s[0].ps[0].ol1).to.equal(3214); + expect(data.s[0].ps[0].l1).to.equal(944); + expect(data.s[0].ps[0].ol1).to.equal(3214); expect(data.s[0].ps[0].l2).to.equal(0); expect(data.s[0].ps[0].ss).to.equal(1); expect(data.s[0].ps[0].t).to.equal(0); @@ -429,7 +429,7 @@ describe('pubmatic analytics adapter', function () { expect(data.s[1].ps[0].mi).to.equal('matched-impression'); expect(data.s[1].ps[0].adv).to.equal('example.com'); expect(data.s[0].ps[0].l1).to.equal(944); - expect(data.s[0].ps[0].ol1).to.equal(3214); + expect(data.s[0].ps[0].ol1).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); @@ -467,7 +467,7 @@ describe('pubmatic analytics adapter', function () { expect(data.af).to.equal('video'); }); - it('Logger: do not log floor fields when prebids floor shows noData in location property', function() { + it('Logger : do not log floor fields when prebids floor shows noData in location property', function() { const BID_REQUESTED_COPY = utils.deepClone(MOCK.BID_REQUESTED); BID_REQUESTED_COPY['bids'][1]['floorData']['location'] = 'noData'; @@ -597,7 +597,7 @@ describe('pubmatic analytics adapter', function () { expect(data.s[0].fp).to.equal('pubmatic'); expect(data.s[0].sz).to.deep.equal(['640x480']); expect(data.s[0].ps).to.be.an('array'); - expect(data.s[0].au).to.equal('/19968336/header-bid-tag-0'); + expect(data.s[0].au).to.equal('/19968336/header-bid-tag-0'); 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'); @@ -671,7 +671,7 @@ describe('pubmatic analytics adapter', function () { 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].au).to.equal('/19968336/header-bid-tag-0'); + expect(data.s[0].au).to.equal('/19968336/header-bid-tag-0'); 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'); @@ -823,8 +823,8 @@ describe('pubmatic analytics adapter', function () { 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].adv).to.equal('example.com'); - expect(data.s[0].ps[0].l1).to.equal(0); - expect(data.s[0].ps[0].ol1).to.equal(0); + expect(data.s[0].ps[0].l1).to.equal(0); + expect(data.s[0].ps[0].ol1).to.equal(0); 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(1); @@ -885,7 +885,7 @@ describe('pubmatic analytics adapter', function () { expect(data.s[1].ps[0].mi).to.equal('matched-impression'); expect(data.s[1].ps[0].adv).to.equal('example.com'); expect(data.s[0].ps[0].l1).to.equal(944); - expect(data.s[0].ps[0].ol1).to.equal(3214); + expect(data.s[0].ps[0].ol1).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); @@ -938,7 +938,7 @@ describe('pubmatic analytics adapter', function () { expect(data.s[1].ps[0].mi).to.equal('matched-impression'); expect(data.s[1].ps[0].adv).to.equal('example.com'); expect(data.s[0].ps[0].l1).to.equal(944); - expect(data.s[0].ps[0].ol1).to.equal(3214); + expect(data.s[0].ps[0].ol1).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); @@ -997,7 +997,7 @@ describe('pubmatic analytics adapter', function () { expect(data.s[1].ps[0].mi).to.equal('matched-impression'); expect(data.s[1].ps[0].adv).to.equal('example.com'); expect(data.s[0].ps[0].l1).to.equal(944); - expect(data.s[0].ps[0].ol1).to.equal(3214); + expect(data.s[0].ps[0].ol1).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); @@ -1056,7 +1056,7 @@ describe('pubmatic analytics adapter', function () { expect(data.s[1].ps[0].mi).to.equal('matched-impression'); expect(data.s[1].ps[0].adv).to.equal('example.com'); expect(data.s[0].ps[0].l1).to.equal(944); - expect(data.s[0].ps[0].ol1).to.equal(3214); + expect(data.s[0].ps[0].ol1).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); @@ -1113,7 +1113,7 @@ describe('pubmatic analytics adapter', function () { expect(data.s[1].ps[0].mi).to.equal('matched-impression'); expect(data.s[1].ps[0].adv).to.equal('example.com'); expect(data.s[0].ps[0].l1).to.equal(944); - expect(data.s[0].ps[0].ol1).to.equal(3214); + expect(data.s[0].ps[0].ol1).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); @@ -1177,7 +1177,7 @@ describe('pubmatic analytics adapter', function () { expect(data.s[1].ps[0].mi).to.equal('matched-impression'); expect(data.s[1].ps[0].adv).to.equal('example.com'); expect(data.s[0].ps[0].l1).to.equal(944); - expect(data.s[0].ps[0].ol1).to.equal(3214); + expect(data.s[0].ps[0].ol1).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); @@ -1240,7 +1240,7 @@ describe('pubmatic analytics adapter', function () { expect(data.s[0].sz).to.deep.equal(['640x480']); expect(data.s[0].sid).not.to.be.undefined; expect(data.s[0].ps).to.be.an('array'); - expect(data.s[0].au).to.equal('/19968336/header-bid-tag-0'); + expect(data.s[0].au).to.equal('/19968336/header-bid-tag-0'); 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'); @@ -1255,7 +1255,7 @@ describe('pubmatic analytics adapter', function () { expect(data.s[0].ps[0].di).to.equal('-1'); expect(data.s[0].ps[0].dc).to.equal(''); expect(data.s[0].ps[0].l1).to.equal(944); - expect(data.s[0].ps[0].ol1).to.equal(3214); + expect(data.s[0].ps[0].ol1).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); @@ -1289,8 +1289,8 @@ describe('pubmatic analytics adapter', function () { 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].adv).to.equal('example.com'); - expect(data.s[0].ps[0].l1).to.equal(944); - expect(data.s[0].ps[0].ol1).to.equal(3214); + expect(data.s[0].ps[0].l1).to.equal(944); + expect(data.s[0].ps[0].ol1).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); @@ -1370,7 +1370,7 @@ describe('pubmatic analytics adapter', function () { expect(data.s[0].sz).to.deep.equal(['640x480']); expect(data.s[0].sid).not.to.be.undefined; expect(data.s[0].ps).to.be.an('array'); - expect(data.s[0].au).to.equal('/19968336/header-bid-tag-0'); + expect(data.s[0].au).to.equal('/19968336/header-bid-tag-0'); 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('groupm'); @@ -1385,7 +1385,7 @@ describe('pubmatic analytics adapter', function () { expect(data.s[0].ps[0].di).to.equal('-1'); expect(data.s[0].ps[0].dc).to.equal(''); expect(data.s[0].ps[0].l1).to.equal(944); - expect(data.s[0].ps[0].ol1).to.equal(3214); + expect(data.s[0].ps[0].ol1).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); @@ -1416,7 +1416,7 @@ describe('pubmatic analytics adapter', function () { expect(data.s[1].ps[0].mi).to.equal('matched-impression'); expect(data.s[1].ps[0].adv).to.equal('example.com'); expect(data.s[0].ps[0].l1).to.equal(944); - expect(data.s[0].ps[0].ol1).to.equal(3214); + expect(data.s[0].ps[0].ol1).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); @@ -1501,4 +1501,206 @@ describe('pubmatic analytics adapter', function () { expect(metadataObj).to.equal(undefined); }); }); + + describe('OW S2S', function() { + this.beforeEach(function() { + pubmaticAnalyticsAdapter.enableAnalytics({ + options: { + publisherId: 9999, + profileId: 1111, + profileVersionId: 20 + } + }); + config.setConfig({ + s2sConfig: { + accountId: '1234', + bidders: ['pubmatic'], + defaultVendor: 'openwrap', + timeout: 500 + } + }); + }); + + this.afterEach(function() { + pubmaticAnalyticsAdapter.disableAnalytics(); + }); + + it('Pubmatic Won: No tracker fired', function() { + this.timeout(5000) + + 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(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]); + + clock.tick(2000 + 1000); + expect(requests.length).to.equal(1); // only logger is fired + let request = requests[0]; + 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'); + }); + + it('Non-pubmatic won: logger, tracker fired', function() { + const APPNEXUS_BID = Object.assign({}, BID, { + 'bidder': 'appnexus', + 'adserverTargeting': { + 'hb_bidder': 'appnexus', + 'hb_adid': '2ecff0db240757', + 'hb_pb': 1.20, + 'hb_size': '640x480', + 'hb_source': 'server' + } + }); + + const MOCK_AUCTION_INIT_APPNEXUS = { + 'auctionId': '25c6d7f5-699a-4bfc-87c9-996f915341fa', + 'timestamp': 1519767010567, + 'auctionStatus': 'inProgress', + 'adUnits': [ { + 'code': '/19968336/header-bid-tag-1', + 'sizes': [[640, 480]], + 'bids': [ { + 'bidder': 'appnexus', + 'params': { + 'publisherId': '1001' + } + } ], + 'transactionId': 'ca4af27a-6d02-4f90-949d-d5541fa12014' + } + ], + 'adUnitCodes': ['/19968336/header-bid-tag-1'], + 'bidderRequests': [ { + 'bidderCode': 'appnexus', + 'auctionId': '25c6d7f5-699a-4bfc-87c9-996f915341fa', + 'bidderRequestId': '1be65d7958826a', + 'bids': [ { + 'bidder': 'appnexus', + 'params': { + 'publisherId': '1001', + 'kgpv': 'this-is-a-kgpv' + }, + 'mediaTypes': { + 'banner': { + 'sizes': [[640, 480]] + } + }, + 'adUnitCode': '/19968336/header-bid-tag-1', + 'transactionId': 'ca4af27a-6d02-4f90-949d-d5541fa12014', + 'sizes': [[640, 480]], + 'bidId': '2ecff0db240757', + 'bidderRequestId': '1be65d7958826a', + 'auctionId': '25c6d7f5-699a-4bfc-87c9-996f915341fa', + 'src': 'client', + 'bidRequestsCount': 1 + } + ], + 'timeout': 3000, + 'refererInfo': { + 'topmostLocation': 'http://www.test.com/page.html', 'reachedTop': true, 'numIframes': 0, 'stack': ['http://www.test.com/page.html'] + } + } + ], + 'bidsReceived': [], + 'winningBids': [], + 'timeout': 3000 + }; + + const MOCK_BID_REQUESTED_APPNEXUS = { + 'bidder': 'appnexus', + 'auctionId': '25c6d7f5-699a-4bfc-87c9-996f915341fa', + 'bidderRequestId': '1be65d7958826a', + 'bids': [ + { + 'bidder': 'appnexus', + 'adapterCode': 'appnexus', + 'bidderCode': 'appnexus', + 'params': { + 'publisherId': '1001', + 'video': { + 'minduration': 30, + 'skippable': true + } + }, + 'mediaType': 'video', + 'adUnitCode': '/19968336/header-bid-tag-0', + 'transactionId': 'ca4af27a-6d02-4f90-949d-d5541fa12014', + 'sizes': [[640, 480]], + 'bidId': '2ecff0db240757', + 'bidderRequestId': '1be65d7958826a', + 'auctionId': '25c6d7f5-699a-4bfc-87c9-996f915341fa' + } + ], + 'auctionStart': 1519149536560, + 'timeout': 5000, + 'start': 1519149562216, + 'refererInfo': { + 'topmostLocation': 'http://www.test.com/page.html', 'reachedTop': true, 'numIframes': 0, 'stack': ['http://www.test.com/page.html'] + }, + 'gdprConsent': { + 'consentString': 'here-goes-gdpr-consent-string', + 'gdprApplies': true + } + }; + + this.timeout(5000) + + sandbox.stub($$PREBID_GLOBAL$$, 'getHighestCpmBids').callsFake((key) => { + return [APPNEXUS_BID] + }); + + events.emit(AUCTION_INIT, MOCK_AUCTION_INIT_APPNEXUS); + events.emit(BID_REQUESTED, MOCK_BID_REQUESTED_APPNEXUS); + events.emit(BID_RESPONSE, APPNEXUS_BID); + events.emit(BIDDER_DONE, { + 'bidderCode': 'appnexus', + 'bids': [ + APPNEXUS_BID, + Object.assign({}, APPNEXUS_BID, { + 'serverResponseTimeMs': 42, + }) + ] + }); + events.emit(AUCTION_END, MOCK.AUCTION_END); + events.emit(SET_TARGETING, { + [APPNEXUS_BID.adUnitCode]: APPNEXUS_BID.adserverTargeting, + }); + events.emit(BID_WON, Object.assign({}, APPNEXUS_BID, { + 'status': 'rendered' + })); + + clock.tick(2000 + 1000); + expect(requests.length).to.equal(2); // logger as well as tracker is fired + let request = requests[1]; // 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'); + + let firstTracker = requests[0].url; + expect(firstTracker.split('?')[0]).to.equal('https://t.pubmatic.com/wt'); + 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.s).to.be.an('array'); + expect(data.s.length).to.equal(1); + expect(data.s[0].ps[0].pn).to.equal('appnexus'); + expect(data.s[0].ps[0].bc).to.equal('appnexus'); + }) + }); }); From 947a487550e3fea398ae6887287635cf755bacb8 Mon Sep 17 00:00:00 2001 From: Marco Muths Date: Tue, 12 Dec 2023 17:50:29 +0100 Subject: [PATCH 068/246] support ad unit name (#10825) --- modules/teadsBidAdapter.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/teadsBidAdapter.js b/modules/teadsBidAdapter.js index ba16c6ddf82..4d8a55f6031 100644 --- a/modules/teadsBidAdapter.js +++ b/modules/teadsBidAdapter.js @@ -262,7 +262,7 @@ function buildRequestObject(bid) { reqObj.bidderRequestId = getBidIdParameter('bidderRequestId', bid); reqObj.placementId = parseInt(placementId, 10); reqObj.pageId = parseInt(pageId, 10); - reqObj.adUnitCode = getBidIdParameter('adUnitCode', bid); + reqObj.adUnitCode = getValue(bid.params, 'adUnitCode') || getBidIdParameter('adUnitCode', bid); reqObj.transactionId = bid.ortb2Imp?.ext?.tid || ''; if (gpid) { reqObj.gpid = gpid; } if (videoPlcmt) { reqObj.videoPlcmt = videoPlcmt; } From 5dbbf1ba39ae894cc9628ac1e9aafc62e79feef9 Mon Sep 17 00:00:00 2001 From: matthieularere-msq <63732822+matthieularere-msq@users.noreply.github.com> Date: Tue, 12 Dec 2023 18:13:38 +0100 Subject: [PATCH 069/246] PBjs Core : new event fired before add bid response (#10685) * new event before add bid response * test new event is emit * change new event name --- modules/rtdModule/index.js | 3 ++- src/auction.js | 2 +- src/constants.json | 3 ++- test/spec/unit/pbjs_api_spec.js | 7 +++++++ 4 files changed, 12 insertions(+), 3 deletions(-) diff --git a/modules/rtdModule/index.js b/modules/rtdModule/index.js index 633c4f4cdc1..fb6e65e8a65 100644 --- a/modules/rtdModule/index.js +++ b/modules/rtdModule/index.js @@ -215,7 +215,8 @@ const setEventsListeners = (function () { [CONSTANTS.EVENTS.AUCTION_INIT]: ['onAuctionInitEvent'], [CONSTANTS.EVENTS.AUCTION_END]: ['onAuctionEndEvent', getAdUnitTargeting], [CONSTANTS.EVENTS.BID_RESPONSE]: ['onBidResponseEvent'], - [CONSTANTS.EVENTS.BID_REQUESTED]: ['onBidRequestEvent'] + [CONSTANTS.EVENTS.BID_REQUESTED]: ['onBidRequestEvent'], + [CONSTANTS.EVENTS.BID_ACCEPTED]: ['onBidAcceptedEvent'] }).forEach(([ev, [handler, preprocess]]) => { events.on(ev, (args) => { preprocess && preprocess(args); diff --git a/src/auction.js b/src/auction.js index 0d256bc3cd7..10b28ad44c9 100644 --- a/src/auction.js +++ b/src/auction.js @@ -455,7 +455,7 @@ export function auctionCallbacks(auctionDone, auctionInstance, {index = auctionM function acceptBidResponse(adUnitCode, bid) { handleBidResponse(adUnitCode, bid, (done) => { let bidResponse = getPreparedBidForAuction(bid); - + events.emit(CONSTANTS.EVENTS.BID_ACCEPTED, bidResponse); if (FEATURES.VIDEO && bidResponse.mediaType === VIDEO) { tryAddVideoBid(auctionInstance, bidResponse, done); } else { diff --git a/src/constants.json b/src/constants.json index c8266ff85f8..24882c160f8 100644 --- a/src/constants.json +++ b/src/constants.json @@ -46,7 +46,8 @@ "AUCTION_DEBUG": "auctionDebug", "BID_VIEWABLE": "bidViewable", "STALE_RENDER": "staleRender", - "BILLABLE_EVENT": "billableEvent" + "BILLABLE_EVENT": "billableEvent", + "BID_ACCEPTED": "bidAccepted" }, "AD_RENDER_FAILED_REASON": { "PREVENT_WRITING_ON_MAIN_DOCUMENT": "preventWritingOnMainDocument", diff --git a/test/spec/unit/pbjs_api_spec.js b/test/spec/unit/pbjs_api_spec.js index 00eedb9a2b5..39123d4aa41 100644 --- a/test/spec/unit/pbjs_api_spec.js +++ b/test/spec/unit/pbjs_api_spec.js @@ -2728,6 +2728,13 @@ describe('Unit: Prebid Module', function () { events.on.restore(); }); + it('should emit event BID_ACCEPTED when invoked', function () { + var callback = sinon.spy(); + $$PREBID_GLOBAL$$.onEvent('bidAccepted', callback); + events.emit(CONSTANTS.EVENTS.BID_ACCEPTED); + sinon.assert.calledOnce(callback); + }); + describe('beforeRequestBids', function () { let bidRequestedHandler; let beforeRequestBidsHandler; From 85817e7ca7240d5b753a933f1d778def0fc344f1 Mon Sep 17 00:00:00 2001 From: Patrick McCann Date: Tue, 12 Dec 2023 12:57:56 -0500 Subject: [PATCH 070/246] Revert "Pubmatic Analytics Adapter : skip duplicate data for pubmatic bids in logger and tracker (#10787)" (#10830) This reverts commit 048f735ccea9907ca72f068042bd35394c58689b. --- modules/pubmaticAnalyticsAdapter.js | 38 +-- .../modules/pubmaticAnalyticsAdapter_spec.js | 248 ++---------------- 2 files changed, 27 insertions(+), 259 deletions(-) diff --git a/modules/pubmaticAnalyticsAdapter.js b/modules/pubmaticAnalyticsAdapter.js index 66898301cd0..fbc45f1a8d7 100755 --- a/modules/pubmaticAnalyticsAdapter.js +++ b/modules/pubmaticAnalyticsAdapter.js @@ -9,7 +9,6 @@ import {getGptSlotInfoForAdUnitCode} from '../libraries/gptUtils/gptUtils.js'; /// /////////// CONSTANTS ////////////// const ADAPTER_CODE = 'pubmatic'; -const VENDOR_OPENWRAP = 'openwrap'; const SEND_TIMEOUT = 2000; const END_POINT_HOST = 'https://t.pubmatic.com/'; const END_POINT_BID_LOGGER = END_POINT_HOST + 'wl?'; @@ -259,27 +258,12 @@ function isS2SBidder(bidder) { return (s2sBidders.indexOf(bidder) > -1) ? 1 : 0 } -function isOWPubmaticBid(adapterName) { - let s2sConf = config.getConfig('s2sConfig'); - let s2sConfArray = isArray(s2sConf) ? s2sConf : [s2sConf]; - return s2sConfArray.some(conf => { - if (adapterName === ADAPTER_CODE && conf.defaultVendor === VENDOR_OPENWRAP && - conf.bidders.indexOf(ADAPTER_CODE) > -1) { - return true; - } - }) -} - function gatherPartnerBidsForAdUnitForLogger(adUnit, adUnitId, highestBid) { highestBid = (highestBid && highestBid.length > 0) ? highestBid[0] : null; return Object.keys(adUnit.bids).reduce(function(partnerBids, bidId) { adUnit.bids[bidId].forEach(function(bid) { - let adapterName = getAdapterNameForAlias(bid.adapterCode || bid.bidder); - if (isOWPubmaticBid(adapterName) && isS2SBidder(bid.bidder)) { - return; - } partnerBids.push({ - 'pn': adapterName, + 'pn': getAdapterNameForAlias(bid.adapterCode || bid.bidder), 'bc': bid.bidderCode || bid.bidder, 'bidid': bid.bidId || bidId, 'db': bid.bidResponse ? 0 : 1, @@ -434,20 +418,13 @@ function executeBidsLoggerCall(e, highestCpmBids) { function executeBidWonLoggerCall(auctionId, adUnitId) { const winningBidId = cache.auctions[auctionId].adUnitCodes[adUnitId].bidWon; const winningBids = cache.auctions[auctionId].adUnitCodes[adUnitId].bids[winningBidId]; - if (!winningBids) { - logWarn(LOG_PRE_FIX + 'Could not find winningBids for : ', auctionId); - return; - } - let winningBid = winningBids[0]; + if (winningBids.length > 1) { winningBid = winningBids.filter(bid => bid.adId === cache.auctions[auctionId].adUnitCodes[adUnitId].bidWonAdId)[0]; } const adapterName = getAdapterNameForAlias(winningBid.adapterCode || winningBid.bidder); - if (isOWPubmaticBid(adapterName) && isS2SBidder(winningBid.bidder)) { - return; - } let origAdUnit = getAdUnit(cache.auctions[auctionId].origAdUnits, adUnitId) || {}; let auctionCache = cache.auctions[auctionId]; let floorData = auctionCache.floorData; @@ -500,10 +477,7 @@ function executeBidWonLoggerCall(auctionId, adUnitId) { function auctionInitHandler(args) { s2sBidders = (function() { let s2sConf = config.getConfig('s2sConfig'); - let s2sBidders = []; - (s2sConf || []) && - isArray(s2sConf) ? s2sConf.map(conf => s2sBidders.push(...conf.bidders)) : s2sBidders.push(...s2sConf.bidders); - return s2sBidders || []; + return (s2sConf && isArray(s2sConf.bidders)) ? s2sConf.bidders : []; }()); let cacheEntry = pick(args, [ 'timestamp', @@ -534,10 +508,6 @@ function bidRequestedHandler(args) { } function bidResponseHandler(args) { - if (!args.requestId) { - logWarn(LOG_PRE_FIX + 'Got null requestId in bidResponseHandler'); - return; - } let bid = cache.auctions[args.auctionId].adUnitCodes[args.adUnitCode].bids[args.requestId][0]; if (!bid) { logError(LOG_PRE_FIX + 'Could not find associated bid request for bid response with requestId: ', args.requestId); @@ -602,7 +572,7 @@ function auctionEndHandler(args) { let highestCpmBids = getGlobal().getHighestCpmBids() || []; setTimeout(() => { executeBidsLoggerCall.call(this, args, highestCpmBids); - }, (cache.auctions[args.auctionId]?.bidderDonePendingCount === 0 ? 500 : SEND_TIMEOUT)); + }, (cache.auctions[args.auctionId].bidderDonePendingCount === 0 ? 500 : SEND_TIMEOUT)); } function bidTimeoutHandler(args) { diff --git a/test/spec/modules/pubmaticAnalyticsAdapter_spec.js b/test/spec/modules/pubmaticAnalyticsAdapter_spec.js index fe31361d3ba..6af497bd2e9 100755 --- a/test/spec/modules/pubmaticAnalyticsAdapter_spec.js +++ b/test/spec/modules/pubmaticAnalyticsAdapter_spec.js @@ -380,8 +380,8 @@ describe('pubmatic analytics adapter', function () { expect(data.s[0].fp).to.equal('pubmatic'); expect(data.s[0].sz).to.deep.equal(['640x480']); expect(data.s[0].ps).to.be.an('array'); - expect(data.s[0].au).to.equal('/19968336/header-bid-tag-0'); - expect(data.s[0].ps.length).to.equal(1); + expect(data.s[0].au).to.equal('/19968336/header-bid-tag-0'); + 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'); @@ -394,8 +394,8 @@ describe('pubmatic analytics adapter', function () { expect(data.s[0].ps[0].en).to.equal(1.23); expect(data.s[0].ps[0].di).to.equal('-1'); expect(data.s[0].ps[0].dc).to.equal(''); - expect(data.s[0].ps[0].l1).to.equal(944); - expect(data.s[0].ps[0].ol1).to.equal(3214); + expect(data.s[0].ps[0].l1).to.equal(944); + expect(data.s[0].ps[0].ol1).to.equal(3214); expect(data.s[0].ps[0].l2).to.equal(0); expect(data.s[0].ps[0].ss).to.equal(1); expect(data.s[0].ps[0].t).to.equal(0); @@ -429,7 +429,7 @@ describe('pubmatic analytics adapter', function () { expect(data.s[1].ps[0].mi).to.equal('matched-impression'); expect(data.s[1].ps[0].adv).to.equal('example.com'); expect(data.s[0].ps[0].l1).to.equal(944); - expect(data.s[0].ps[0].ol1).to.equal(3214); + expect(data.s[0].ps[0].ol1).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); @@ -467,7 +467,7 @@ describe('pubmatic analytics adapter', function () { expect(data.af).to.equal('video'); }); - it('Logger : do not log floor fields when prebids floor shows noData in location property', function() { + it('Logger: do not log floor fields when prebids floor shows noData in location property', function() { const BID_REQUESTED_COPY = utils.deepClone(MOCK.BID_REQUESTED); BID_REQUESTED_COPY['bids'][1]['floorData']['location'] = 'noData'; @@ -597,7 +597,7 @@ describe('pubmatic analytics adapter', function () { expect(data.s[0].fp).to.equal('pubmatic'); expect(data.s[0].sz).to.deep.equal(['640x480']); expect(data.s[0].ps).to.be.an('array'); - expect(data.s[0].au).to.equal('/19968336/header-bid-tag-0'); + expect(data.s[0].au).to.equal('/19968336/header-bid-tag-0'); 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'); @@ -671,7 +671,7 @@ describe('pubmatic analytics adapter', function () { 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].au).to.equal('/19968336/header-bid-tag-0'); + expect(data.s[0].au).to.equal('/19968336/header-bid-tag-0'); 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'); @@ -823,8 +823,8 @@ describe('pubmatic analytics adapter', function () { 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].adv).to.equal('example.com'); - expect(data.s[0].ps[0].l1).to.equal(0); - expect(data.s[0].ps[0].ol1).to.equal(0); + expect(data.s[0].ps[0].l1).to.equal(0); + expect(data.s[0].ps[0].ol1).to.equal(0); 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(1); @@ -885,7 +885,7 @@ describe('pubmatic analytics adapter', function () { expect(data.s[1].ps[0].mi).to.equal('matched-impression'); expect(data.s[1].ps[0].adv).to.equal('example.com'); expect(data.s[0].ps[0].l1).to.equal(944); - expect(data.s[0].ps[0].ol1).to.equal(3214); + expect(data.s[0].ps[0].ol1).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); @@ -938,7 +938,7 @@ describe('pubmatic analytics adapter', function () { expect(data.s[1].ps[0].mi).to.equal('matched-impression'); expect(data.s[1].ps[0].adv).to.equal('example.com'); expect(data.s[0].ps[0].l1).to.equal(944); - expect(data.s[0].ps[0].ol1).to.equal(3214); + expect(data.s[0].ps[0].ol1).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); @@ -997,7 +997,7 @@ describe('pubmatic analytics adapter', function () { expect(data.s[1].ps[0].mi).to.equal('matched-impression'); expect(data.s[1].ps[0].adv).to.equal('example.com'); expect(data.s[0].ps[0].l1).to.equal(944); - expect(data.s[0].ps[0].ol1).to.equal(3214); + expect(data.s[0].ps[0].ol1).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); @@ -1056,7 +1056,7 @@ describe('pubmatic analytics adapter', function () { expect(data.s[1].ps[0].mi).to.equal('matched-impression'); expect(data.s[1].ps[0].adv).to.equal('example.com'); expect(data.s[0].ps[0].l1).to.equal(944); - expect(data.s[0].ps[0].ol1).to.equal(3214); + expect(data.s[0].ps[0].ol1).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); @@ -1113,7 +1113,7 @@ describe('pubmatic analytics adapter', function () { expect(data.s[1].ps[0].mi).to.equal('matched-impression'); expect(data.s[1].ps[0].adv).to.equal('example.com'); expect(data.s[0].ps[0].l1).to.equal(944); - expect(data.s[0].ps[0].ol1).to.equal(3214); + expect(data.s[0].ps[0].ol1).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); @@ -1177,7 +1177,7 @@ describe('pubmatic analytics adapter', function () { expect(data.s[1].ps[0].mi).to.equal('matched-impression'); expect(data.s[1].ps[0].adv).to.equal('example.com'); expect(data.s[0].ps[0].l1).to.equal(944); - expect(data.s[0].ps[0].ol1).to.equal(3214); + expect(data.s[0].ps[0].ol1).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); @@ -1240,7 +1240,7 @@ describe('pubmatic analytics adapter', function () { expect(data.s[0].sz).to.deep.equal(['640x480']); expect(data.s[0].sid).not.to.be.undefined; expect(data.s[0].ps).to.be.an('array'); - expect(data.s[0].au).to.equal('/19968336/header-bid-tag-0'); + expect(data.s[0].au).to.equal('/19968336/header-bid-tag-0'); 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'); @@ -1255,7 +1255,7 @@ describe('pubmatic analytics adapter', function () { expect(data.s[0].ps[0].di).to.equal('-1'); expect(data.s[0].ps[0].dc).to.equal(''); expect(data.s[0].ps[0].l1).to.equal(944); - expect(data.s[0].ps[0].ol1).to.equal(3214); + expect(data.s[0].ps[0].ol1).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); @@ -1289,8 +1289,8 @@ describe('pubmatic analytics adapter', function () { 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].adv).to.equal('example.com'); - expect(data.s[0].ps[0].l1).to.equal(944); - expect(data.s[0].ps[0].ol1).to.equal(3214); + expect(data.s[0].ps[0].l1).to.equal(944); + expect(data.s[0].ps[0].ol1).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); @@ -1370,7 +1370,7 @@ describe('pubmatic analytics adapter', function () { expect(data.s[0].sz).to.deep.equal(['640x480']); expect(data.s[0].sid).not.to.be.undefined; expect(data.s[0].ps).to.be.an('array'); - expect(data.s[0].au).to.equal('/19968336/header-bid-tag-0'); + expect(data.s[0].au).to.equal('/19968336/header-bid-tag-0'); 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('groupm'); @@ -1385,7 +1385,7 @@ describe('pubmatic analytics adapter', function () { expect(data.s[0].ps[0].di).to.equal('-1'); expect(data.s[0].ps[0].dc).to.equal(''); expect(data.s[0].ps[0].l1).to.equal(944); - expect(data.s[0].ps[0].ol1).to.equal(3214); + expect(data.s[0].ps[0].ol1).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); @@ -1416,7 +1416,7 @@ describe('pubmatic analytics adapter', function () { expect(data.s[1].ps[0].mi).to.equal('matched-impression'); expect(data.s[1].ps[0].adv).to.equal('example.com'); expect(data.s[0].ps[0].l1).to.equal(944); - expect(data.s[0].ps[0].ol1).to.equal(3214); + expect(data.s[0].ps[0].ol1).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); @@ -1501,206 +1501,4 @@ describe('pubmatic analytics adapter', function () { expect(metadataObj).to.equal(undefined); }); }); - - describe('OW S2S', function() { - this.beforeEach(function() { - pubmaticAnalyticsAdapter.enableAnalytics({ - options: { - publisherId: 9999, - profileId: 1111, - profileVersionId: 20 - } - }); - config.setConfig({ - s2sConfig: { - accountId: '1234', - bidders: ['pubmatic'], - defaultVendor: 'openwrap', - timeout: 500 - } - }); - }); - - this.afterEach(function() { - pubmaticAnalyticsAdapter.disableAnalytics(); - }); - - it('Pubmatic Won: No tracker fired', function() { - this.timeout(5000) - - 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(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]); - - clock.tick(2000 + 1000); - expect(requests.length).to.equal(1); // only logger is fired - let request = requests[0]; - 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'); - }); - - it('Non-pubmatic won: logger, tracker fired', function() { - const APPNEXUS_BID = Object.assign({}, BID, { - 'bidder': 'appnexus', - 'adserverTargeting': { - 'hb_bidder': 'appnexus', - 'hb_adid': '2ecff0db240757', - 'hb_pb': 1.20, - 'hb_size': '640x480', - 'hb_source': 'server' - } - }); - - const MOCK_AUCTION_INIT_APPNEXUS = { - 'auctionId': '25c6d7f5-699a-4bfc-87c9-996f915341fa', - 'timestamp': 1519767010567, - 'auctionStatus': 'inProgress', - 'adUnits': [ { - 'code': '/19968336/header-bid-tag-1', - 'sizes': [[640, 480]], - 'bids': [ { - 'bidder': 'appnexus', - 'params': { - 'publisherId': '1001' - } - } ], - 'transactionId': 'ca4af27a-6d02-4f90-949d-d5541fa12014' - } - ], - 'adUnitCodes': ['/19968336/header-bid-tag-1'], - 'bidderRequests': [ { - 'bidderCode': 'appnexus', - 'auctionId': '25c6d7f5-699a-4bfc-87c9-996f915341fa', - 'bidderRequestId': '1be65d7958826a', - 'bids': [ { - 'bidder': 'appnexus', - 'params': { - 'publisherId': '1001', - 'kgpv': 'this-is-a-kgpv' - }, - 'mediaTypes': { - 'banner': { - 'sizes': [[640, 480]] - } - }, - 'adUnitCode': '/19968336/header-bid-tag-1', - 'transactionId': 'ca4af27a-6d02-4f90-949d-d5541fa12014', - 'sizes': [[640, 480]], - 'bidId': '2ecff0db240757', - 'bidderRequestId': '1be65d7958826a', - 'auctionId': '25c6d7f5-699a-4bfc-87c9-996f915341fa', - 'src': 'client', - 'bidRequestsCount': 1 - } - ], - 'timeout': 3000, - 'refererInfo': { - 'topmostLocation': 'http://www.test.com/page.html', 'reachedTop': true, 'numIframes': 0, 'stack': ['http://www.test.com/page.html'] - } - } - ], - 'bidsReceived': [], - 'winningBids': [], - 'timeout': 3000 - }; - - const MOCK_BID_REQUESTED_APPNEXUS = { - 'bidder': 'appnexus', - 'auctionId': '25c6d7f5-699a-4bfc-87c9-996f915341fa', - 'bidderRequestId': '1be65d7958826a', - 'bids': [ - { - 'bidder': 'appnexus', - 'adapterCode': 'appnexus', - 'bidderCode': 'appnexus', - 'params': { - 'publisherId': '1001', - 'video': { - 'minduration': 30, - 'skippable': true - } - }, - 'mediaType': 'video', - 'adUnitCode': '/19968336/header-bid-tag-0', - 'transactionId': 'ca4af27a-6d02-4f90-949d-d5541fa12014', - 'sizes': [[640, 480]], - 'bidId': '2ecff0db240757', - 'bidderRequestId': '1be65d7958826a', - 'auctionId': '25c6d7f5-699a-4bfc-87c9-996f915341fa' - } - ], - 'auctionStart': 1519149536560, - 'timeout': 5000, - 'start': 1519149562216, - 'refererInfo': { - 'topmostLocation': 'http://www.test.com/page.html', 'reachedTop': true, 'numIframes': 0, 'stack': ['http://www.test.com/page.html'] - }, - 'gdprConsent': { - 'consentString': 'here-goes-gdpr-consent-string', - 'gdprApplies': true - } - }; - - this.timeout(5000) - - sandbox.stub($$PREBID_GLOBAL$$, 'getHighestCpmBids').callsFake((key) => { - return [APPNEXUS_BID] - }); - - events.emit(AUCTION_INIT, MOCK_AUCTION_INIT_APPNEXUS); - events.emit(BID_REQUESTED, MOCK_BID_REQUESTED_APPNEXUS); - events.emit(BID_RESPONSE, APPNEXUS_BID); - events.emit(BIDDER_DONE, { - 'bidderCode': 'appnexus', - 'bids': [ - APPNEXUS_BID, - Object.assign({}, APPNEXUS_BID, { - 'serverResponseTimeMs': 42, - }) - ] - }); - events.emit(AUCTION_END, MOCK.AUCTION_END); - events.emit(SET_TARGETING, { - [APPNEXUS_BID.adUnitCode]: APPNEXUS_BID.adserverTargeting, - }); - events.emit(BID_WON, Object.assign({}, APPNEXUS_BID, { - 'status': 'rendered' - })); - - clock.tick(2000 + 1000); - expect(requests.length).to.equal(2); // logger as well as tracker is fired - let request = requests[1]; // 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'); - - let firstTracker = requests[0].url; - expect(firstTracker.split('?')[0]).to.equal('https://t.pubmatic.com/wt'); - 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.s).to.be.an('array'); - expect(data.s.length).to.equal(1); - expect(data.s[0].ps[0].pn).to.equal('appnexus'); - expect(data.s[0].ps[0].bc).to.equal('appnexus'); - }) - }); }); From fd35856f8ff929399e9f88bf61244530aa3d3eed Mon Sep 17 00:00:00 2001 From: Chris Huie Date: Tue, 12 Dec 2023 12:30:56 -0700 Subject: [PATCH 071/246] Revert "support ad unit name (#10825)" (#10831) This reverts commit 947a487550e3fea398ae6887287635cf755bacb8. --- modules/teadsBidAdapter.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/teadsBidAdapter.js b/modules/teadsBidAdapter.js index 4d8a55f6031..ba16c6ddf82 100644 --- a/modules/teadsBidAdapter.js +++ b/modules/teadsBidAdapter.js @@ -262,7 +262,7 @@ function buildRequestObject(bid) { reqObj.bidderRequestId = getBidIdParameter('bidderRequestId', bid); reqObj.placementId = parseInt(placementId, 10); reqObj.pageId = parseInt(pageId, 10); - reqObj.adUnitCode = getValue(bid.params, 'adUnitCode') || getBidIdParameter('adUnitCode', bid); + reqObj.adUnitCode = getBidIdParameter('adUnitCode', bid); reqObj.transactionId = bid.ortb2Imp?.ext?.tid || ''; if (gpid) { reqObj.gpid = gpid; } if (videoPlcmt) { reqObj.videoPlcmt = videoPlcmt; } From 1c4bc7a82ac4e56a1d729e82098f165db6aad1bf Mon Sep 17 00:00:00 2001 From: gabrielIvbs <112690249+gabrielIvbs@users.noreply.github.com> Date: Tue, 12 Dec 2023 23:05:34 +0200 Subject: [PATCH 072/246] Invibes Bid Adapter : infinite scroll tcf (#10764) * added prevent page view event param in order to differentiate from the first and following request on multi request; added disableUserSyncs in order to disable that functionality as default and added the option to activate it from the bidRequest * added infinite scroll and page refresh params, updated tcf, changed randomId to guid, updated the subdomain * reverted subdomain * Update invibesBidAdapter.js with code review suggestion * Moved new variables to invibes object * Update invibesBidAdapter.js add placementIds to invibes object * initialized window.invibes --- modules/invibesBidAdapter.js | 56 ++++++++++-- test/spec/modules/invibesBidAdapter_spec.js | 99 +++++++++++++++++++++ 2 files changed, 149 insertions(+), 6 deletions(-) diff --git a/modules/invibesBidAdapter.js b/modules/invibesBidAdapter.js index 0c0d1cdef87..1d608d7136b 100644 --- a/modules/invibesBidAdapter.js +++ b/modules/invibesBidAdapter.js @@ -9,7 +9,7 @@ const CONSTANTS = { SYNC_ENDPOINT: 'https://k.r66net.com/GetUserSync', TIME_TO_LIVE: 300, DEFAULT_CURRENCY: 'EUR', - PREBID_VERSION: 10, + PREBID_VERSION: 11, METHOD: 'GET', INVIBES_VENDOR_ID: 436, USERID_PROVIDERS: ['pubcid', 'pubProvidedId', 'uid2', 'zeotapIdPlus', 'id5id'], @@ -49,14 +49,36 @@ 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]; -invibes.legitimateInterests = invibes.legitimateInterests || [false, false, false, false, false, false, false, false, false, false]; +invibes.purposes = invibes.purposes || [false, false, false, false, false, false, false, false, false, false, false]; +invibes.legitimateInterests = invibes.legitimateInterests || [false, false, false, false, false, false, false, false, false, false, false]; invibes.placementBids = invibes.placementBids || []; invibes.pushedCids = invibes.pushedCids || {}; let preventPageViewEvent = false; +let isInfiniteScrollPage = false; +let isPlacementRefresh = false; let _customUserSync; let _disableUserSyncs; +function updateInfiniteScrollFlag() { + const { scrollHeight } = document.documentElement; + + if (invibes.originalURL === undefined) { + invibes.originalURL = window.location.href; + return; + } + + if (invibes.originalScrollHeight === undefined) { + invibes.originalScrollHeight = scrollHeight; + return; + } + + const currentURL = window.location.href; + + if (scrollHeight > invibes.originalScrollHeight && invibes.originalURL !== currentURL) { + isInfiniteScrollPage = true; + } +} + function isBidRequestValid(bid) { if (typeof bid.params !== 'object') { return false; @@ -87,10 +109,24 @@ function buildRequest(bidRequests, bidderRequest) { const _placementIds = []; const _adUnitCodes = []; let _customEndpoint, _userId, _domainId; - let _ivAuctionStart = bidderRequest.auctionStart || Date.now(); + let _ivAuctionStart = Date.now(); + window.invibes = window.invibes || {}; + window.invibes.placementIds = window.invibes.placementIds || []; + + if (isInfiniteScrollPage == false) { + updateInfiniteScrollFlag(); + } bidRequests.forEach(function (bidRequest) { bidRequest.startTime = new Date().getTime(); + + if (window.invibes.placementIds.includes(bidRequest.params.placementId)) { + isPlacementRefresh = true; + } + + window.invibes.placementIds.push(bidRequest.params.placementId); + + _placementIds.push(bidRequest.params.placementId); _placementIds.push(bidRequest.params.placementId); _adUnitCodes.push(bidRequest.adUnitCode); _domainId = _domainId || bidRequest.params.domainId; @@ -138,6 +174,8 @@ function buildRequest(bidRequests, bidderRequest) { tc: invibes.gdpr_consent, isLocalStorageEnabled: storage.hasLocalStorage(), preventPageViewEvent: preventPageViewEvent, + isPlacementRefresh: isPlacementRefresh, + isInfiniteScrollPage: isInfiniteScrollPage, }; let lid = readFromLocalStorage('ivbsdid'); @@ -368,7 +406,9 @@ function addMeta(bidModelMeta) { } function generateRandomId() { - return (Math.round(Math.random() * 1e12)).toString(36).substring(0, 10); + return '10000000100040008000100000000000'.replace(/[018]/g, c => + (c ^ crypto.getRandomValues(new Uint8Array(1))[0] & 15 >> c / 4).toString(16) + ); } function getDocumentLocation(bidderRequest) { @@ -568,7 +608,7 @@ function readGdprConsent(gdprConsent) { } let legitimateInterests = getLegitimateInterests(gdprConsent.vendorData); - tryCopyValueToArray(legitimateInterests, invibes.legitimateInterests, 10); + tryCopyValueToArray(legitimateInterests, invibes.legitimateInterests, purposesLength); let invibesVendorId = CONSTANTS.INVIBES_VENDOR_ID.toString(10); let vendorConsents = getVendorConsents(gdprConsent.vendorData); @@ -621,6 +661,10 @@ function tryCopyValueToArray(value, target, length) { function getPurposeConsentsCounter(vendorData) { if (vendorData.purpose && vendorData.purpose.consents) { + if (vendorData.tcfPolicyVersion >= 4) { + return 11; + } + return 10; } diff --git a/test/spec/modules/invibesBidAdapter_spec.js b/test/spec/modules/invibesBidAdapter_spec.js index 7ee6b464996..056255c7738 100644 --- a/test/spec/modules/invibesBidAdapter_spec.js +++ b/test/spec/modules/invibesBidAdapter_spec.js @@ -44,6 +44,78 @@ describe('invibesBidAdapter:', function () { } ]; + let bidRequestsWithDuplicatedplacementId = [ + { + bidId: 'b1', + bidder: BIDDER_CODE, + bidderRequestId: 'r1', + params: { + placementId: PLACEMENT_ID, + disableUserSyncs: false + + }, + adUnitCode: 'test-div1', + auctionId: 'a1', + sizes: [ + [300, 250], + [400, 300], + [125, 125] + ], + transactionId: 't1' + }, { + bidId: 'b2', + bidder: BIDDER_CODE, + bidderRequestId: 'r2', + params: { + placementId: PLACEMENT_ID, + disableUserSyncs: false + }, + adUnitCode: 'test-div2', + auctionId: 'a2', + sizes: [ + [300, 250], + [400, 300] + ], + transactionId: 't2' + } + ]; + + let bidRequestsWithUniquePlacementId = [ + { + bidId: 'b1', + bidder: BIDDER_CODE, + bidderRequestId: 'r1', + params: { + placementId: 'PLACEMENT_ID_1', + disableUserSyncs: false + + }, + adUnitCode: 'test-div1', + auctionId: 'a1', + sizes: [ + [300, 250], + [400, 300], + [125, 125] + ], + transactionId: 't1' + }, { + bidId: 'b2', + bidder: BIDDER_CODE, + bidderRequestId: 'r2', + params: { + placementId: 'PLACEMENT_ID_2', + disableUserSyncs: false + }, + adUnitCode: 'test-div2', + auctionId: 'a2', + sizes: [ + [300, 250], + [400, 300] + ], + transactionId: 't2' + } + ]; + let bidRequestsWithUserId = [ { bidId: 'b1', @@ -185,17 +257,44 @@ describe('invibesBidAdapter:', function () { expect(request.data.preventPageViewEvent).to.be.false; }); + it('sends isPlacementRefresh as false when the placement ids are used for the first time', function () { + let request = spec.buildRequests(bidRequestsWithUniquePlacementId, bidderRequestWithPageInfo); + expect(request.data.isPlacementRefresh).to.be.false; + }); + it('sends preventPageViewEvent as true on 2nd call', function () { let request = spec.buildRequests(bidRequests, bidderRequestWithPageInfo); expect(request.data.preventPageViewEvent).to.be.true; }); + it('sends isPlacementRefresh as true on multi requests on the same placement id', function () { + let request = spec.buildRequests(bidRequestsWithDuplicatedplacementId, bidderRequestWithPageInfo); + expect(request.data.isPlacementRefresh).to.be.true; + }); + + it('sends isInfiniteScrollPage as false initially', function () { + let request = spec.buildRequests(bidRequests, bidderRequestWithPageInfo); + expect(request.data.isInfiniteScrollPage).to.be.false; + }); + + it('sends isPlacementRefresh as true on multi requests multiple calls with the same placement id from second call', function () { + let request = spec.buildRequests(bidRequests, bidderRequestWithPageInfo); + expect(request.data.isInfiniteScrollPage).to.be.false; + let duplicatedRequest = spec.buildRequests(bidRequests, bidderRequestWithPageInfo); + expect(duplicatedRequest.data.isPlacementRefresh).to.be.true; + }); + it('sends bid request to ENDPOINT via GET', function () { const request = spec.buildRequests(bidRequests, bidderRequestWithPageInfo); expect(request.url).to.equal(ENDPOINT); expect(request.method).to.equal('GET'); }); + it('generates a visitId of length 32', function () { + spec.buildRequests(bidRequests, bidderRequestWithPageInfo); + expect(top.window.invibes.visitId.length).to.equal(32); + }); + it('sends bid request to custom endpoint via GET', function () { const request = spec.buildRequests([{ bidId: 'b1', From 2e0789671b5a080d969da8878dbf8f0c47a0c3b6 Mon Sep 17 00:00:00 2001 From: BaronJHYu <254878848@qq.com> Date: Wed, 13 Dec 2023 21:12:30 +0800 Subject: [PATCH 073/246] Mediago Bid Adapter : switch domain name (#10833) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Mediago Bid Adapter:new adapter * remove console * change spec file to fix CircleCI * change spec file to fix CircleCI * change spec file * Update mediagoBidAdapter.js * Update mediagoBidAdapter.js * rerun CurcleCi * update mediagoBidAdapter * update discoveryBidAdapter * Discovery Bid Adapter : parameter updates * Mediago Bid Adapter : parameter updates * Mediago Bid Adapter : code style format * rerun circleci * rerun circleci * rerun circleci * rerun circleci * Update mediagoBidAdapter & discoveryBidAdapter:report eids to server * Update mediagoBidAdapter & discoveryBidAdapter:report eids to server * update Mediago & Discovery BidAdapter:remove size filter * update Mediago & Discovery BidAdapter:code format * update Mediago & Discovery BidAdapter:code format * update Mediago & Discovery BidAdapter:add param in banner format * update mediago & discovery:first party data * update mediago & discovery:first party data * update mediago & discovery:first party data * fix(mediago & discovery): update param tagid * fix(mediago & discovery): update param tagid * feat:add imp params for analysis * fix:add imp params for analysis * code format * test:add test data & test * format code * rerun circleci * feat: 1. add cookieSync 2. add gdpr * code format * rerun circleci * add gvlid * feat:add param 1.ppuid 2.content 3.cat 4.reqtime * fix: getUserSyncs param fixed * fix: getUserSyncs param fixed * feat: add param 1. adUnitCode 2. referrer * feat: Switch domain * rerun circle ci --------- Co-authored-by: BaronYu --- modules/mediagoBidAdapter.js | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/modules/mediagoBidAdapter.js b/modules/mediagoBidAdapter.js index 72b06dfe7ed..a9c670f35d8 100644 --- a/modules/mediagoBidAdapter.js +++ b/modules/mediagoBidAdapter.js @@ -10,10 +10,8 @@ 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 COOKY_SYNC_URL = 'https://trace.mediago.io/ju/cs/eplist'; +const ENDPOINT_URL = 'https://gbid.mediago.io/api/bid?tn='; +const COOKY_SYNC_URL = 'https://gtrace.mediago.io/ju/cs/eplist'; const COOKY_SYNC_IFRAME_URL = 'https://cdn.mediago.io/js/cookieSync.html'; const TIME_TO_LIVE = 500; From 75cb1663dc229811513fddf400d5934654455ee8 Mon Sep 17 00:00:00 2001 From: BizzClick <73241175+BizzClick@users.noreply.github.com> Date: Wed, 13 Dec 2023 20:34:15 +0200 Subject: [PATCH 074/246] Bizzclick Bid Adapter : update adapter, used ortbConverter lib (#10828) * update bizzclick adapter, used ortbConverter lib * fix: removed gdpr, ccpa, coppa --- modules/bizzclickBidAdapter.js | 355 ++--------- modules/bizzclickBidAdapter.md | 183 +++--- test/spec/modules/bizzclickBidAdapter_spec.js | 569 +++++++----------- 3 files changed, 378 insertions(+), 729 deletions(-) diff --git a/modules/bizzclickBidAdapter.js b/modules/bizzclickBidAdapter.js index dc7731231ab..d2eba3f0f81 100644 --- a/modules/bizzclickBidAdapter.js +++ b/modules/bizzclickBidAdapter.js @@ -1,322 +1,77 @@ -import {_map, deepAccess, deepSetValue, getDNT, logMessage, logWarn} from '../src/utils.js'; -import {registerBidder} from '../src/adapters/bidderFactory.js'; -import {BANNER, NATIVE, VIDEO} from '../src/mediaTypes.js'; -import {config} from '../src/config.js'; -import {convertOrtbRequestToProprietaryNative} from '../src/native.js'; +import { ortbConverter } from '../libraries/ortbConverter/converter.js'; +import { registerBidder } from '../src/adapters/bidderFactory.js'; +import { config } from '../src/config.js'; +import { BANNER, NATIVE, VIDEO } from '../src/mediaTypes.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' +const SOURCE_ID_MACRO = '[sourceid]'; +const ACCOUNT_ID_MACRO = '[accountid]'; +const HOST_MACRO = '[host]'; +const URL = `https://${HOST_MACRO}.bizzclick.com/bid?rtb_seat_id=${SOURCE_ID_MACRO}&secret_key=${ACCOUNT_ID_MACRO}&integration_type=prebidjs`; +const DEFAULT_CURRENCY = 'USD'; +const DEFAULT_HOST = 'us-e-node1'; + +const converter = ortbConverter({ + context: { + netRevenue: true, + ttl: 20, }, - sponsoredBy: { - id: 5, - name: 'data', - type: 1 + imp(buildImp, bidRequest, context) { + const imp = buildImp(bidRequest, context); + if (!imp.bidfloor) imp.bidfloor = bidRequest.params.bidfloor || 0; + imp.ext = { + [BIDDER_CODE]: { + accountId: bidRequest.params.accountId, + sourceId: bidRequest.params.sourceId, + host: bidRequest.params.host || DEFAULT_HOST, + } + } + return imp; }, - body: { - id: 4, - name: 'data', - type: 2 + request(buildRequest, imps, bidderRequest, context) { + const request = buildRequest(imps, bidderRequest, context); + const bid = context.bidRequests[0]; + request.test = config.getConfig('debug') ? 1 : 0; + if (!request.cur) request.cur = [bid.params.currency || DEFAULT_CURRENCY]; + return request; }, - cta: { - id: 1, - type: 12, - name: 'data' + bidResponse(buildBidResponse, bid, context) { + const bidResponse = buildBidResponse(bid, context); + bidResponse.cur = bid.cur || DEFAULT_CURRENCY; + return bidResponse; } -}; -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) + return Boolean(bid.params.sourceId) && Boolean(bid.params.accountId); }, - /** - * 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) => { - // convert Native ORTB definition to old-style prebid native definition - validBidRequests = convertOrtbRequestToProprietaryNative(validBidRequests); - 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.page) - winTop = window.top; - } catch (e) { - location = winTop.location; - 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: 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.ortb2Imp?.ext?.tid, - ext: { - schain: {} - } - }, - regs: { - coppa: config.getConfig('coppa') === true ? 1 : 0, - ext: {} - }, - user: { - ext: {} - }, - ext: { - ts: Date.now() - }, - tmax: bidRequest.timeout, - imp: [impObject], - }; - - let connection = navigator.connection || navigator.webkitConnection; - if (connection && connection.effectiveType) { - data.device.connectiontype = connection.effectiveType; - } - if (bidRequest) { - if (bidRequest.schain) { - deepSetValue(data, 'source.ext.schain', bidRequest.schain); - } - - if (bidRequest.gdprConsent && bidRequest.gdprConsent.gdprApplies) { - deepSetValue(data, 'regs.ext.gdpr', bidRequest.gdprConsent.gdprApplies ? 1 : 0); - deepSetValue(data, 'user.ext.consent', bidRequest.gdprConsent.consentString); - } - - if (bidRequest.uspConsent !== undefined) { - deepSetValue(data, 'regs.ext.us_privacy', bidRequest.uspConsent); - } - } - bids.push(data) - } + buildRequests: (validBidRequests, bidderRequest) => { + if (validBidRequests && validBidRequests.length === 0) return []; + const { sourceId, accountId } = validBidRequests[0].params; + const host = validBidRequests[0].params.host || 'USE'; + const endpointURL = URL.replace(HOST_MACRO, host || DEFAULT_HOST) + .replace(ACCOUNT_ID_MACRO, accountId) + .replace(SOURCE_ID_MACRO, sourceId); + const request = converter.toORTB({ bidRequests: validBidRequests, bidderRequest }); return { method: 'POST', url: endpointURL, - data: bids + 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) => { - 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 - }; - - 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 - case NATIVE: - bid.native = parseNative(response.seatbid[0].bid[0].adm) - break - default: - bid.ad = response.seatbid[0].bid[0].adm - } - bids.push(bid); + interpretResponse: (response, request) => { + if (response?.body) { + const bids = converter.fromORTB({ response: response.body, request: request.data }).bids; + return bids; } - return bids; + return []; }, }; -/** - * Determine type of request - * - * @param bidRequest - * @param type - * @returns {boolean} - */ -const checkRequestType = (bidRequest, type) => { - return (typeof 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 = { - // TODO: top-level ID is not in ORTB native 1.2, is this intentional? - // (despite the name, this appears to be an ORTB native request - not an imp - object) - id: bidRequest.bidId, - ver: NATIVE_VERSION, - }; - const assets = _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(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 { - 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 6fc1bebf546..ad342f34e07 100644 --- a/modules/bizzclickBidAdapter.md +++ b/modules/bizzclickBidAdapter.md @@ -11,94 +11,99 @@ Maintainer: support@bizzclick.com Module that connects to BizzClick SSP demand sources # Test Parameters -``` - var adUnits = [{ - code: 'placementId', - mediaTypes: { - banner: { - sizes: [[300, 250], [300,600]] - } - }, - bids: [{ - bidder: 'bizzclick', - params: { - 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 - ] - } }, +```js +const adUnits = [ + { + code: "placementId", + mediaTypes: { + banner: { + sizes: [ + [300, 250], + [300, 600], + ], + }, + }, + bids: [ + { + bidder: "bizzclick", + params: { + placementId: "hash", + accountId: "accountId", + host: "host", + }, + }, + ], + }, + { + 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' - } - } - ] - } - ]; -``` \ No newline at end of file + { + bidder: "bizzclick", + params: { + placementId: "hash", + accountId: "accountId", + host: "host", + }, + }, + ], + }, + { + 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", + host: "host", + }, + }, + ], + }, +]; +``` diff --git a/test/spec/modules/bizzclickBidAdapter_spec.js b/test/spec/modules/bizzclickBidAdapter_spec.js index f80051b0a50..f8e66caf657 100644 --- a/test/spec/modules/bizzclickBidAdapter_spec.js +++ b/test/spec/modules/bizzclickBidAdapter_spec.js @@ -1,6 +1,102 @@ import { expect } from 'chai'; -import { spec } from 'modules/bizzclickBidAdapter.js'; -import {config} from 'src/config.js'; +import { spec } from 'modules/bizzclickBidAdapter'; +import 'modules/priceFloors.js'; +import { newBidder } from 'src/adapters/bidderFactory'; +import { config } from '../../../src/config.js'; +import { syncAddFPDToBidderRequest } from '../../helpers/fpd.js'; + +// load modules that register ORTB processors +import 'src/prebid.js'; +import 'modules/currency.js'; +import 'modules/userId/index.js'; +import 'modules/multibid/index.js'; +import 'modules/priceFloors.js'; +import 'modules/consentManagement.js'; +import 'modules/consentManagementUsp.js'; +import 'modules/schain.js'; + +const SIMPLE_BID_REQUEST = { + bidder: 'bizzclick', + params: { + accountId: 'testAccountId', + sourceId: 'testSourceId', + host: 'USE', + }, + mediaTypes: { + banner: { + sizes: [ + [320, 250], + [300, 600], + ], + }, + }, + adUnitCode: 'div-gpt-ad-1499748733608-0', + transactionId: 'f183e871-fbed-45f0-a427-c8a63c4c01eb', + bidId: '33e9500b21129f', + bidderRequestId: '2772c1e566670b', + auctionId: '192721e36a0239', + sizes: [[300, 250], [160, 600]], + gdprConsent: { + apiVersion: 2, + consentString: 'CONSENT', + vendorData: { purpose: { consents: { 1: true } } }, + gdprApplies: true, + addtlConsent: '1~1.35.41.101', + }, +} + +const BANNER_BID_REQUEST = { + bidder: 'bizzclick', + params: { + accountId: 'testAccountId', + sourceId: 'testSourceId', + host: 'USE', + }, + mediaTypes: { + banner: { + sizes: [ + [300, 250], + [300, 600], + ], + }, + }, + adUnitCode: '/adunit-code/test-path', + bidId: 'test-bid-id-1', + bidderRequestId: 'test-bid-request-1', + auctionId: 'test-auction-1', + transactionId: 'test-transactionId-1', + code: 'banner_example', + timeout: 1000, +} + +const VIDEO_BID_REQUEST = { + placementCode: '/DfpAccount1/slotVideo', + bidId: 'test-bid-id-2', + mediaTypes: { + video: { + playerSize: [400, 300], + w: 400, + h: 300, + minduration: 5, + maxduration: 10, + startdelay: 0, + skip: 1, + minbitrate: 200, + protocols: [1, 2, 4] + } + }, + bidder: 'bizzclick', + params: { + accountId: '123', + sourceId: '123', + host: 'USE', + }, + adUnitCode: '/adunit-code/test-path', + bidderRequestId: 'test-bid-request-1', + auctionId: 'test-auction-1', + transactionId: 'test-transactionId-1', + timeout: 1000, +} const NATIVE_BID_REQUEST = { code: 'native_example', @@ -34,386 +130,179 @@ const NATIVE_BID_REQUEST = { }, bidder: 'bizzclick', params: { - placementId: 'hash', - accountId: 'accountId' - }, - timeout: 1000 - -}; - -const BANNER_BID_REQUEST = { - code: 'banner_example', - mediaTypes: { - banner: { - sizes: [[300, 250], [300, 600]] - } - }, - schain: { - ver: '1.0', - complete: 1, - nodes: [ - { - asi: 'example.com', - sid: '164', - hp: 1 - } - ] - }, - bidder: 'bizzclick', - params: { - placementId: 'hash', - accountId: 'accountId' + accountId: 'testAccountId', + sourceId: 'testSourceId', + host: 'USE', }, + adUnitCode: '/adunit-code/test-path', + bidId: 'test-bid-id-1', + bidderRequestId: 'test-bid-request-1', + auctionId: 'test-auction-1', + transactionId: 'test-transactionId-1', timeout: 1000, - gdprConsent: { - consentString: 'BOEFEAyOEFEAyAHABDENAI4AAAB9vABAASA', - gdprApplies: 1, - }, uspConsent: 'uspConsent' -} +}; -const bidRequest = { +const bidderRequest = { 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 - ] + page: 'https://publisher.com/home', + ref: 'https://referrer' } - }, - - 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 gdprConsent = { + apiVersion: 2, + consentString: 'CONSENT', + vendorData: { purpose: { consents: { 1: true } } }, + gdprApplies: true, + addtlConsent: '1~1.35.41.101', +} -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 () { + 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('BizzclickAdapter', function() { - describe('with COPPA', function() { - beforeEach(function() { + describe('with user privacy regulations', function () { + it('should send the Coppa "required" flag set to "1" in the request', function () { sinon.stub(config, 'getConfig') .withArgs('coppa') .returns(true); - }); - afterEach(function() { + const serverRequest = spec.buildRequests([SIMPLE_BID_REQUEST], syncAddFPDToBidderRequest(bidderRequest)); + expect(serverRequest.data.regs.coppa).to.equal(1); 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); + it('should send the GDPR Consent data in the request', function () { + const serverRequest = spec.buildRequests([SIMPLE_BID_REQUEST], syncAddFPDToBidderRequest({ ...bidderRequest, gdprConsent })); + expect(serverRequest.data.regs.ext.gdpr).to.exist.and.to.equal(1); + expect(serverRequest.data.user.ext.consent).to.equal('CONSENT'); }); - }); - 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); + it('should send the CCPA data in the request', function () { + const serverRequest = spec.buildRequests([SIMPLE_BID_REQUEST], syncAddFPDToBidderRequest({...bidderRequest, ...{ uspConsent: '1YYY' }})); + expect(serverRequest.data.regs.ext.us_privacy).to.equal('1YYY'); }); }); - 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'); + describe('isBidRequestValid', function () { + it('should return true when required params found', function () { + expect(spec.isBidRequestValid(BANNER_BID_REQUEST)).to.equal(true); }); - it('Returns empty data if no valid requests are passed', function () { - let serverRequest = spec.buildRequests([]); - expect(serverRequest).to.be.an('array').that.is.empty; + it('should return false when accountID/sourceId is missing', function () { + let localbid = Object.assign({}, BANNER_BID_REQUEST); + delete localbid.params.accountId; + delete localbid.params.sourceId; + expect(spec.isBidRequestValid(BANNER_BID_REQUEST)).to.equal(false); }); }); - 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; + describe('build request', function () { + it('should return an empty array when no bid requests', function () { + const bidRequest = spec.buildRequests([], syncAddFPDToBidderRequest(bidderRequest)); + expect(bidRequest).to.be.an('array'); + expect(bidRequest.length).to.equal(0); }); - it('sends bid request to our endpoint via POST', function () { + it('should return a valid bid request object', function () { + const request = spec.buildRequests([SIMPLE_BID_REQUEST], syncAddFPDToBidderRequest(bidderRequest)); + expect(request).to.not.equal('array'); + expect(request.data).to.be.an('object'); expect(request.method).to.equal('POST'); + expect(request.url).to.not.equal(''); + expect(request.url).to.not.equal(undefined); + expect(request.url).to.not.equal(null); + + expect(request.data.site).to.have.property('page'); + expect(request.data.site).to.have.property('domain'); + expect(request.data).to.have.property('id'); + expect(request.data).to.have.property('imp'); + expect(request.data).to.have.property('device'); }); - 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('check schain is set properly', function() { - expect(request.data[0].source.ext.schain.complete).to.equal(1); - expect(request.data[0].source.ext.schain.ver).to.equal('1.0'); - }) - - 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('should return a valid bid BANNER request object', function () { + const request = spec.buildRequests([BANNER_BID_REQUEST], syncAddFPDToBidderRequest(bidderRequest)); + expect(request.data.imp[0].banner).to.exist; + expect(request.data.imp[0].banner.format[0].w).to.be.an('number'); + expect(request.data.imp[0].banner.format[0].h).to.be.an('number'); }); - }); - 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; - }); + if (FEATURES.VIDEO) { + it('should return a valid bid VIDEO request object', function () { + const request = spec.buildRequests([VIDEO_BID_REQUEST], syncAddFPDToBidderRequest(bidderRequest)); + expect(request.data.imp[0].video).to.exist; + expect(request.data.imp[0].video.w).to.be.an('number'); + expect(request.data.imp[0].video.h).to.be.an('number'); + }); + } - it('sends bid request to our endpoint via POST', function () { - expect(request.method).to.equal('POST'); + it('should return a valid bid NATIVE request object', function () { + const request = spec.buildRequests([NATIVE_BID_REQUEST], syncAddFPDToBidderRequest(bidderRequest)); + expect(request.data.imp[0]).to.be.an('object'); }); + }) - 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 () { + let bidRequests, bidderRequest; + beforeEach(function () { + bidRequests = [{ + 'bidId': '28ffdk2B952532', + 'bidder': 'bizzclick', + 'userId': { + 'freepassId': { + 'userIp': '172.21.0.1', + 'userId': '123', + 'commonId': 'commonIdValue' + } + }, + 'adUnitCode': 'adunit-code', + 'params': { + 'publisherId': 'publisherIdValue' + } + }]; + bidderRequest = {}; }); - }); - 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); + let response = spec.interpretResponse(emptyResponse, BANNER_BID_REQUEST); 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, - - meta: {advertiserDomains: BANNER_BID_RESPONSE.seatbid[0].bid[0].adomain}, - 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', 'meta', '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.meta.advertiserDomains).to.equal(expectedBidResponse.meta.advertiserDomains); - 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, - meta: {advertiserDomains: VIDEO_BID_RESPONSE.seatbid[0].bid[0].adomain}, - 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', 'meta', '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.meta.advertiserDomains).to.equal(expectedBidResponse.meta.advertiserDomains); - 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', - meta: {advertiserDomains: NATIVE_BID_RESPONSE.seatbid[0].bid[0].adomain}, - 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', 'meta'); - expect(dataItem.requestId).to.equal(expectedBidResponse.requestId); - expect(dataItem.cpm).to.equal(expectedBidResponse.cpm); - expect(dataItem.meta.advertiserDomains).to.equal(expectedBidResponse.meta.advertiserDomains); - 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); - }); + const serverResponse = { + body: { + 'cur': 'USD', + 'seatbid': [{ + 'bid': [{ + 'impid': '28ffdk2B952532', + 'price': 97, + 'adm': '', + 'w': 300, + 'h': 250, + 'crid': 'creative0' + }] + }] + } + }; + it('should interpret server response', function () { + const bidRequest = spec.buildRequests(bidRequests, syncAddFPDToBidderRequest(bidderRequest)); + const bids = spec.interpretResponse(serverResponse, bidRequest); + expect(bids).to.be.an('array'); + const bid = bids[0]; + expect(bid).to.be.an('object'); + expect(bid.currency).to.equal('USD'); + expect(bid.cpm).to.equal(97); + expect(bid.ad).to.equal(ad) + expect(bid.width).to.equal(300); + expect(bid.height).to.equal(250); + expect(bid.creativeId).to.equal('creative0'); + }); + }) }); -}) +}); From dc530cf23527852f7ca8ead50e6e02c2c298b4f2 Mon Sep 17 00:00:00 2001 From: pm-azhar-mulla <75726247+pm-azhar-mulla@users.noreply.github.com> Date: Thu, 14 Dec 2023 00:11:46 +0530 Subject: [PATCH 075/246] PubMatic Bid Adapter : passing a unique wiid to pubmatic ssp and logger call (#10821) * Added wiid as UUID * Handled undefined wiid in logger and tracker * updated test cases * updated test cases --------- Co-authored-by: pm-azhar-mulla --- modules/pubmaticAnalyticsAdapter.js | 9 +++++++-- modules/pubmaticBidAdapter.js | 4 +++- test/spec/modules/pubmaticBidAdapter_spec.js | 8 ++++++++ 3 files changed, 18 insertions(+), 3 deletions(-) diff --git a/modules/pubmaticAnalyticsAdapter.js b/modules/pubmaticAnalyticsAdapter.js index fbc45f1a8d7..9b910caa992 100755 --- a/modules/pubmaticAnalyticsAdapter.js +++ b/modules/pubmaticAnalyticsAdapter.js @@ -337,6 +337,7 @@ function executeBidsLoggerCall(e, highestCpmBids) { let auctionId = e.auctionId; let referrer = config.getConfig('pageUrl') || cache.auctions[auctionId].referer || ''; let auctionCache = cache.auctions[auctionId]; + let wiid = auctionCache?.wiid || auctionId; let floorData = auctionCache?.floorData; let floorFetchStatus = getFloorFetchStatus(auctionCache?.floorData); let outputObj = { s: [] }; @@ -352,7 +353,7 @@ function executeBidsLoggerCall(e, highestCpmBids) { pixelURL += 'pubid=' + publisherId; outputObj['pubid'] = '' + publisherId; - outputObj['iid'] = '' + auctionId; + outputObj['iid'] = '' + wiid; outputObj['to'] = '' + auctionCache.timeout; outputObj['purl'] = referrer; outputObj['orig'] = getDomainFromUrl(referrer); @@ -428,6 +429,7 @@ function executeBidWonLoggerCall(auctionId, adUnitId) { let origAdUnit = getAdUnit(cache.auctions[auctionId].origAdUnits, adUnitId) || {}; let auctionCache = cache.auctions[auctionId]; let floorData = auctionCache.floorData; + let wiid = cache.auctions[auctionId]?.wiid || auctionId; let referrer = config.getConfig('pageUrl') || cache.auctions[auctionId].referer || ''; let adv = winningBid.bidResponse ? getAdDomain(winningBid.bidResponse) || undefined : undefined; let fskp = floorData ? (floorData.floorRequestData ? (floorData.floorRequestData.skipped == false ? 0 : 1) : undefined) : undefined; @@ -436,7 +438,7 @@ function executeBidWonLoggerCall(auctionId, adUnitId) { pixelURL += 'pubid=' + publisherId; pixelURL += '&purl=' + enc(config.getConfig('pageUrl') || cache.auctions[auctionId].referer || ''); pixelURL += '&tst=' + Math.round((new window.Date()).getTime() / 1000); - pixelURL += '&iid=' + enc(auctionId); + pixelURL += '&iid=' + enc(wiid); pixelURL += '&bidid=' + enc(winningBidId); pixelURL += '&pid=' + enc(profileId); pixelURL += '&pdvid=' + enc(profileVersionId); @@ -500,6 +502,9 @@ function bidRequestedHandler(args) { dimensions: bid.sizes }; } + if (bid.bidder === 'pubmatic' && !!bid?.params?.wiid) { + cache.auctions[args.auctionId].wiid = bid.params.wiid; + } cache.auctions[args.auctionId].adUnitCodes[bid.adUnitCode].bids[bid.bidId] = [copyRequiredBidDetails(bid)]; if (bid.floorData) { cache.auctions[args.auctionId].floorData['floorRequestData'] = bid.floorData; diff --git a/modules/pubmaticBidAdapter.js b/modules/pubmaticBidAdapter.js index ee80de03c03..a6c7dda510c 100644 --- a/modules/pubmaticBidAdapter.js +++ b/modules/pubmaticBidAdapter.js @@ -1,4 +1,4 @@ -import { getBidRequest, logWarn, isBoolean, isStr, isArray, inIframe, mergeDeep, deepAccess, isNumber, deepSetValue, logInfo, logError, deepClone, uniques, isPlainObject, isInteger } from '../src/utils.js'; +import { getBidRequest, logWarn, isBoolean, isStr, isArray, inIframe, mergeDeep, deepAccess, isNumber, deepSetValue, logInfo, logError, deepClone, uniques, isPlainObject, isInteger, generateUUID } from '../src/utils.js'; import { registerBidder } from '../src/adapters/bidderFactory.js'; import { BANNER, VIDEO, NATIVE, ADPOD } from '../src/mediaTypes.js'; import { config } from '../src/config.js'; @@ -1081,8 +1081,10 @@ export const spec = { var bid; var blockedIabCategories = []; var allowedIabCategories = []; + var wiid = generateUUID(); validBidRequests.forEach(originalBid => { + originalBid.params.wiid = originalBid.params.wiid || bidderRequest.auctionId || wiid; bid = deepClone(originalBid); bid.params.adSlot = bid.params.adSlot || ''; _parseAdSlot(bid); diff --git a/test/spec/modules/pubmaticBidAdapter_spec.js b/test/spec/modules/pubmaticBidAdapter_spec.js index 154a8e1253b..544ba3d0fbf 100644 --- a/test/spec/modules/pubmaticBidAdapter_spec.js +++ b/test/spec/modules/pubmaticBidAdapter_spec.js @@ -104,6 +104,7 @@ describe('PubMatic adapter', function () { params: { publisherId: '5890', adSlot: 'Div1@0x0', // ad_id or tagid + wiid: 'new-unique-wiid', video: { mimes: ['video/mp4', 'video/x-flv'], skippable: true, @@ -154,6 +155,7 @@ describe('PubMatic adapter', function () { params: { publisherId: '5890', adSlot: 'Div1@640x480', // ad_id or tagid + wiid: '1234567890', video: { mimes: ['video/mp4', 'video/x-flv'], skippable: true, @@ -213,6 +215,7 @@ describe('PubMatic adapter', function () { params: { publisherId: '5670', adSlot: '/43743431/NativeAutomationPrebid@1x1', + wiid: 'new-unique-wiid' }, bidId: '2a5571261281d4', requestId: 'B68287E1-DC39-4B38-9790-FE4F179739D6', @@ -278,6 +281,7 @@ describe('PubMatic adapter', function () { params: { publisherId: '5670', adSlot: '/43743431/NativeAutomationPrebid@1x1', + wiid: 'new-unique-wiid' }, bidId: '2a5571261281d4', requestId: 'B68287E1-DC39-4B38-9790-FE4F179739D6', @@ -304,6 +308,7 @@ describe('PubMatic adapter', function () { params: { publisherId: '5670', adSlot: '/43743431/NativeAutomationPrebid@1x1', + wiid: 'new-unique-wiid' } }]; @@ -344,6 +349,7 @@ describe('PubMatic adapter', function () { params: { publisherId: '5670', adSlot: '/43743431/NativeAutomationPrebid@1x1', + wiid: 'new-unique-wiid' } }]; @@ -502,6 +508,7 @@ describe('PubMatic adapter', function () { params: { publisherId: '301', adSlot: '/15671365/DMDemo@300x250:0', + wiid: 'new-unique-wiid', video: { mimes: ['video/mp4', 'video/x-flv'], skippable: true, @@ -572,6 +579,7 @@ describe('PubMatic adapter', function () { params: { publisherId: '301', adSlot: '/15671365/DMDemo@300x250:0', + wiid: 'new-unique-wiid', video: { mimes: ['video/mp4', 'video/x-flv'], skippable: true, From a631892df98179b9d69299bc90b8bd3d2722dd78 Mon Sep 17 00:00:00 2001 From: Vincent Date: Thu, 14 Dec 2023 14:16:45 +0100 Subject: [PATCH 076/246] Criteo Bid Adapter: Add support for app.publisher.id in bid request (#10837) Co-authored-by: v.raybaud --- modules/criteoBidAdapter.js | 1 + test/spec/modules/criteoBidAdapter_spec.js | 24 ++++++++++++++++++++++ 2 files changed, 25 insertions(+) diff --git a/modules/criteoBidAdapter.js b/modules/criteoBidAdapter.js index 45769edaf61..4fe9ca7d0f4 100644 --- a/modules/criteoBidAdapter.js +++ b/modules/criteoBidAdapter.js @@ -607,6 +607,7 @@ function buildCdbRequest(context, bidRequests, bidderRequest) { }; request.user = bidderRequest.ortb2?.user || {}; request.site = bidderRequest.ortb2?.site || {}; + request.app = bidderRequest.ortb2?.app || {}; if (bidderRequest && bidderRequest.ceh) { request.user.ceh = bidderRequest.ceh; } diff --git a/test/spec/modules/criteoBidAdapter_spec.js b/test/spec/modules/criteoBidAdapter_spec.js index 97b80ce95db..9987df34f2b 100755 --- a/test/spec/modules/criteoBidAdapter_spec.js +++ b/test/spec/modules/criteoBidAdapter_spec.js @@ -1122,6 +1122,30 @@ describe('The Criteo bidding adapter', function () { expect(request.data.user.uspIab).to.equal('1YNY'); }); + it('should properly build a request with site and app ortb fields', function () { + const bidRequests = []; + let app = { + publisher: { + id: 'appPublisherId' + } + }; + let site = { + publisher: { + id: 'sitePublisherId' + } + }; + const bidderRequest = { + ortb2: { + app: app, + site: site + } + }; + const request = spec.buildRequests(bidRequests, bidderRequest); + + expect(request.data.app).to.equal(app); + expect(request.data.site).to.equal(site); + }); + it('should properly build a request with device sua field', function () { const sua = {} const bidRequests = [ From 3e57af399bf1d085d3043ff28a133ea8313922f0 Mon Sep 17 00:00:00 2001 From: barRubi <151519394+barRubi@users.noreply.github.com> Date: Thu, 14 Dec 2023 16:44:30 +0200 Subject: [PATCH 077/246] providing visibility in case of adapter timeout and bidError (#10807) Co-authored-by: ahmadlob <109217988+ahmadlob@users.noreply.github.com> --- modules/taboolaBidAdapter.js | 8 ++++ test/spec/modules/taboolaBidAdapter_spec.js | 46 ++++++++++++++++++++- 2 files changed, 53 insertions(+), 1 deletion(-) diff --git a/modules/taboolaBidAdapter.js b/modules/taboolaBidAdapter.js index 0d56d6daec7..0bdb0568a10 100644 --- a/modules/taboolaBidAdapter.js +++ b/modules/taboolaBidAdapter.js @@ -15,6 +15,7 @@ export const USER_SYNC_IMG_URL = 'https://trc.taboola.com/sg/prebidJS/1/cm'; const USER_ID = 'user-id'; const STORAGE_KEY = `taboola global:${USER_ID}`; const COOKIE_KEY = 'trc_cookie_storage'; +export const EVENT_ENDPOINT = 'https://beacon.bidder.taboola.com'; /** * extract User Id by that order: @@ -190,6 +191,13 @@ export const spec = { } return syncs; }, + onTimeout: (timeoutData) => { + ajax(EVENT_ENDPOINT + '/timeout', null, JSON.stringify(timeoutData), {method: 'POST'}); + }, + + onBidderError: ({ error, bidderRequest }) => { + ajax(EVENT_ENDPOINT + '/bidError', null, JSON.stringify(error, bidderRequest), {method: 'POST'}); + }, }; function getSiteProperties({publisherId}, refererInfo, ortb2) { diff --git a/test/spec/modules/taboolaBidAdapter_spec.js b/test/spec/modules/taboolaBidAdapter_spec.js index 1b6104599d7..2bfe93bf880 100644 --- a/test/spec/modules/taboolaBidAdapter_spec.js +++ b/test/spec/modules/taboolaBidAdapter_spec.js @@ -1,5 +1,5 @@ import {expect} from 'chai'; -import {spec, internal, END_POINT_URL, userData} from 'modules/taboolaBidAdapter.js'; +import {spec, internal, END_POINT_URL, userData, EVENT_ENDPOINT} from 'modules/taboolaBidAdapter.js'; import {config} from '../../../src/config' import * as utils from '../../../src/utils' import {server} from '../../mocks/xhr' @@ -113,6 +113,50 @@ describe('Taboola Adapter', function () { }); }); + describe('onTimeout', function () { + it('onTimeout exist as a function', () => { + expect(spec.onTimeout).to.exist.and.to.be.a('function'); + }); + it('should send timeout', function () { + const timeoutData = [{ + bidder: 'taboola', + bidId: 'da43860a-4644-442a-b5e0-93f268cf8d19', + params: [{ + publisherId: 'publisherId' + }], + adUnitCode: 'adUnit-code', + timeout: 3000, + auctionId: '12a34b56c' + }] + spec.onTimeout(timeoutData); + expect(server.requests[0].method).to.equal('POST'); + expect(server.requests[0].url).to.equal(EVENT_ENDPOINT + '/timeout'); + expect(JSON.parse(server.requests[0].requestBody)).to.deep.equal(timeoutData); + }); + }); + + describe('onBidderError', function () { + it('onBidderError exist as a function', () => { + expect(spec.onBidderError).to.exist.and.to.be.a('function'); + }); + it('should send bidder error', function () { + const error = { + status: 204, + statusText: 'No Content' + }; + const bidderRequest = { + bidder: 'taboola', + params: { + publisherId: 'publisherId' + } + } + spec.onBidderError({error, bidderRequest}); + expect(server.requests[0].method).to.equal('POST'); + expect(server.requests[0].url).to.equal(EVENT_ENDPOINT + '/bidError'); + expect(JSON.parse(server.requests[0].requestBody)).to.deep.equal(error, bidderRequest); + }); + }); + describe('buildRequests', function () { const defaultBidRequest = { ...createBidRequest(), From f58a0a32fc5f0191f81649420b16c9d28d64255e Mon Sep 17 00:00:00 2001 From: "Prebid.js automated release" Date: Thu, 14 Dec 2023 18:05:30 +0000 Subject: [PATCH 078/246] Prebid 8.28.0 release --- package-lock.json | 2 +- package.json | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/package-lock.json b/package-lock.json index 1e0990121a8..b3612fba397 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,6 +1,6 @@ { "name": "prebid.js", - "version": "8.28.0-pre", + "version": "8.28.0", "lockfileVersion": 2, "requires": true, "packages": { diff --git a/package.json b/package.json index bca287c1a03..9e68756b23f 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "prebid.js", - "version": "8.28.0-pre", + "version": "8.28.0", "description": "Header Bidding Management Library", "main": "src/prebid.js", "scripts": { From 2732783683ffb6b161a56fdd52fede78809ddd47 Mon Sep 17 00:00:00 2001 From: "Prebid.js automated release" Date: Thu, 14 Dec 2023 18:05:30 +0000 Subject: [PATCH 079/246] Increment version to 8.29.0-pre --- package-lock.json | 2 +- package.json | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/package-lock.json b/package-lock.json index b3612fba397..592fcb3d16d 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,6 +1,6 @@ { "name": "prebid.js", - "version": "8.28.0", + "version": "8.29.0-pre", "lockfileVersion": 2, "requires": true, "packages": { diff --git a/package.json b/package.json index 9e68756b23f..62e99c75911 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "prebid.js", - "version": "8.28.0", + "version": "8.29.0-pre", "description": "Header Bidding Management Library", "main": "src/prebid.js", "scripts": { From 5292b63e820bf1147d1d2c30a2b5048dc703fcc0 Mon Sep 17 00:00:00 2001 From: Nayan Savla Date: Fri, 15 Dec 2023 18:01:52 +0300 Subject: [PATCH 080/246] Adding support for video.plcmt (#10841) --- modules/yieldmoBidAdapter.js | 4 ++-- test/spec/modules/yieldmoBidAdapter_spec.js | 18 ++++++++++++++---- 2 files changed, 16 insertions(+), 6 deletions(-) diff --git a/modules/yieldmoBidAdapter.js b/modules/yieldmoBidAdapter.js index e72a671ac74..9109c6e2a80 100644 --- a/modules/yieldmoBidAdapter.js +++ b/modules/yieldmoBidAdapter.js @@ -29,7 +29,7 @@ const VIDEO_PATH = '/exchange/prebidvideo'; const STAGE_DOMAIN = 'https://ads-stg.yieldmo.com'; const PROD_DOMAIN = 'https://ads.yieldmo.com'; const OUTSTREAM_VIDEO_PLAYER_URL = 'https://prebid-outstream.yieldmo.com/bundle.js'; -const OPENRTB_VIDEO_BIDPARAMS = ['mimes', 'startdelay', 'placement', 'startdelay', 'skipafter', 'protocols', 'api', +const OPENRTB_VIDEO_BIDPARAMS = ['mimes', 'startdelay', 'placement', 'plcmt', 'skipafter', 'protocols', 'api', 'playbackmethod', 'maxduration', 'minduration', 'pos', 'skip', 'skippable']; const OPENRTB_VIDEO_SITEPARAMS = ['name', 'domain', 'cat', 'keywords']; const LOCAL_WINDOW = getWindowTop(); @@ -449,7 +449,7 @@ function openRtbImpression(bidRequest) { imp.video.skip = 1; delete imp.video.skippable; } - if (imp.video.placement !== 1) { + if (imp.video.plcmt !== 1 || imp.video.placement !== 1) { imp.video.startdelay = DEFAULT_START_DELAY; imp.video.playbackmethod = [ DEFAULT_PLAYBACK_METHOD ]; } diff --git a/test/spec/modules/yieldmoBidAdapter_spec.js b/test/spec/modules/yieldmoBidAdapter_spec.js index a80d0a842b7..edb3ef3af27 100644 --- a/test/spec/modules/yieldmoBidAdapter_spec.js +++ b/test/spec/modules/yieldmoBidAdapter_spec.js @@ -47,7 +47,7 @@ describe('YieldmoAdapter', function () { video: { playerSize: [640, 480], context: 'instream', - mimes: ['video/mp4'] + mimes: ['video/mp4'], }, }, params: { @@ -61,11 +61,11 @@ describe('YieldmoAdapter', function () { api: [2, 3], skipppable: true, playbackmethod: [1, 2], - ...videoParams - } + ...videoParams, + }, }, transactionId: '54a58774-7a41-494e-8cbc-fa7b79164f0c', - ...rootParams + ...rootParams, }); const mockBidderRequest = (params = {}, bids = [mockBannerBid()]) => ({ @@ -460,6 +460,16 @@ describe('YieldmoAdapter', function () { expect(buildVideoBidAndGetVideoParam().minduration).to.deep.equal(['video/mp4']); }); + it('should add plcmt value to the imp.video', function () { + const videoBid = mockVideoBid({}, {}, { plcmt: 1 }); + expect(utils.deepAccess(videoBid, 'params.video')['plcmt']).to.equal(1); + }); + + it('should add start delay if plcmt value is not 1', function () { + const videoBid = mockVideoBid({}, {}, { plcmt: 2 }); + expect(build([videoBid])[0].data.imp[0].video.startdelay).to.equal(0); + }); + 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']; From a90d6c851f512b4f44c6f11b58dbd798a3adb302 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 18 Dec 2023 09:27:48 -0700 Subject: [PATCH 081/246] Bump github/codeql-action from 2 to 3 (#10856) Bumps [github/codeql-action](https://github.com/github/codeql-action) from 2 to 3. - [Release notes](https://github.com/github/codeql-action/releases) - [Changelog](https://github.com/github/codeql-action/blob/main/CHANGELOG.md) - [Commits](https://github.com/github/codeql-action/compare/v2...v3) --- updated-dependencies: - dependency-name: github/codeql-action dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/workflows/codeql-analysis.yml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/codeql-analysis.yml b/.github/workflows/codeql-analysis.yml index 84c97376a3e..3bee8f7c947 100644 --- a/.github/workflows/codeql-analysis.yml +++ b/.github/workflows/codeql-analysis.yml @@ -42,7 +42,7 @@ jobs: # Initializes the CodeQL tools for scanning. - name: Initialize CodeQL - uses: github/codeql-action/init@v2 + uses: github/codeql-action/init@v3 with: languages: ${{ matrix.language }} config-file: ./.github/codeql/codeql-config.yml @@ -57,7 +57,7 @@ jobs: # Autobuild attempts to build any compiled languages (C/C++, C#, or Java). # If this step fails, then you should remove it and run the build manually (see below) - name: Autobuild - uses: github/codeql-action/autobuild@v2 + uses: github/codeql-action/autobuild@v3 # ℹ️ Command-line programs to run using the OS shell. # 📚 See https://docs.github.com/en/actions/using-workflows/workflow-syntax-for-github-actions#jobsjob_idstepsrun @@ -70,4 +70,4 @@ jobs: # ./location_of_script_within_repo/buildscript.sh - name: Perform CodeQL Analysis - uses: github/codeql-action/analyze@v2 + uses: github/codeql-action/analyze@v3 From 5f8ff5f1bbedfe42c923e9b436f3b7abbe80c929 Mon Sep 17 00:00:00 2001 From: joseluis laso Date: Mon, 18 Dec 2023 08:47:18 -0800 Subject: [PATCH 082/246] HadronId Module: implementing user consent in backend calls (#10845) * Implementing user consent in HadronId module * fixing tests accepting just the start of url --- modules/hadronIdSystem.js | 34 +++++++++++++++++++++--- test/spec/modules/hadronIdSystem_spec.js | 4 +-- 2 files changed, 32 insertions(+), 6 deletions(-) diff --git a/modules/hadronIdSystem.js b/modules/hadronIdSystem.js index c60f0f812a4..4141e9a01f8 100644 --- a/modules/hadronIdSystem.js +++ b/modules/hadronIdSystem.js @@ -9,8 +9,11 @@ import {ajax} from '../src/ajax.js'; import {getStorageManager} from '../src/storageManager.js'; import {submodule} from '../src/hook.js'; import {isFn, isStr, isPlainObject, logError, logInfo} from '../src/utils.js'; +import { config } from '../src/config.js'; import {MODULE_TYPE_UID} from '../src/activities/modules.js'; +import { gdprDataHandler, uspDataHandler, gppDataHandler } from '../src/adapterManager.js'; +const LOG_PREFIX = '[hadronIdSystem]'; const HADRONID_LOCAL_NAME = 'auHadronId'; const MODULE_NAME = 'hadronId'; const AU_GVLID = 561; @@ -42,6 +45,8 @@ const urlAddParams = (url, params) => { return url + (url.indexOf('?') > -1 ? '&' : '?') + params } +const isDebug = config.getConfig('debug') || false; + /** @type {Submodule} */ export const hadronIdSubmodule = { /** @@ -88,7 +93,7 @@ export const hadronIdSubmodule = { } catch (error) { logError(error); } - logInfo(`Response from backend is ${responseObj}`); + logInfo(LOG_PREFIX, `Response from backend is ${response}`, responseObj); hadronId = responseObj['hadronId']; storage.setDataInLocalStorage(HADRONID_LOCAL_NAME, hadronId); responseObj = {id: {hadronId}}; @@ -100,13 +105,34 @@ export const hadronIdSubmodule = { callback(); } }; - logInfo('HadronId not found in storage, calling backend...'); - const url = urlAddParams( + let url = urlAddParams( // config.params.url and config.params.urlArg are not documented // since their use is for debugging purposes only paramOrDefault(config.params.url, DEFAULT_HADRON_URL_ENDPOINT, config.params.urlArg), - `partner_id=${partnerId}&_it=prebid` + `partner_id=${partnerId}&_it=prebid&t=1&src=id` // src=id => the backend was called from getId ); + if (isDebug) { + url += '&debug=1' + } + const gdprConsent = gdprDataHandler.getConsentData() + if (gdprConsent) { + url += `${gdprConsent.consentString ? '&gdprString=' + encodeURIComponent(gdprConsent.consentString) : ''}`; + url += `&gdpr=${gdprConsent.gdprApplies === true ? 1 : 0}`; + } + + const usPrivacyString = uspDataHandler.getConsentData(); + if (usPrivacyString) { + url += `&us_privacy=${encodeURIComponent(usPrivacyString)}`; + } + + const gppConsent = gppDataHandler.getConsentData(); + if (gppConsent) { + url += `${gppConsent.gppString ? '&gpp=' + encodeURIComponent(gppConsent.gppString) : ''}`; + url += `${gppConsent.applicableSections ? '&gpp_sid=' + encodeURIComponent(gppConsent.applicableSections) : ''}`; + } + + logInfo(LOG_PREFIX, `hadronId not found in storage, calling home (${url})`); + ajax(url, callbacks, undefined, {method: 'GET'}); }; return {callback: resp}; diff --git a/test/spec/modules/hadronIdSystem_spec.js b/test/spec/modules/hadronIdSystem_spec.js index cc0118d4659..85c8cc11c9e 100644 --- a/test/spec/modules/hadronIdSystem_spec.js +++ b/test/spec/modules/hadronIdSystem_spec.js @@ -22,7 +22,7 @@ describe('HadronIdSystem', function () { const callback = hadronIdSubmodule.getId(config).callback; callback(callbackSpy); const request = server.requests[0]; - expect(request.url).to.eq(`https://id.hadron.ad.gt/api/v1/pbhid?partner_id=0&_it=prebid`); + expect(request.url).to.match(/^https:\/\/id\.hadron\.ad\.gt\/api\/v1\/pbhid/); request.respond(200, { 'Content-Type': 'application/json' }, JSON.stringify({ hadronId: 'testHadronId1' })); expect(callbackSpy.lastCall.lastArg).to.deep.equal({ id: { hadronId: 'testHadronId1' } }); }); @@ -47,7 +47,7 @@ describe('HadronIdSystem', function () { const callback = hadronIdSubmodule.getId(config).callback; callback(callbackSpy); const request = server.requests[0]; - expect(request.url).to.eq('https://hadronid.publync.com/?partner_id=0&_it=prebid'); + expect(request.url).to.match(/^https:\/\/hadronid\.publync\.com\//); request.respond(200, { 'Content-Type': 'application/json' }, JSON.stringify({ hadronId: 'testHadronId1' })); expect(callbackSpy.lastCall.lastArg).to.deep.equal({ id: { hadronId: 'testHadronId1' } }); }); From 9a0f012cf4a08a63bc4dbfc50afb570507b49aa1 Mon Sep 17 00:00:00 2001 From: Viktor Dreiling <34981284+3link@users.noreply.github.com> Date: Mon, 18 Dec 2023 17:50:09 +0100 Subject: [PATCH 083/246] Eids Docs : add missing EID examples (#10844) * Add missing brackets * Add missing examples * Add back missing ext --- modules/userId/eids.md | 45 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 45 insertions(+) diff --git a/modules/userId/eids.md b/modules/userId/eids.md index 04073923ed1..eb37c50f0a3 100644 --- a/modules/userId/eids.md +++ b/modules/userId/eids.md @@ -77,6 +77,7 @@ userIdAsEids = [ uids: [{ id: 'the-ids-object-stringified', atype: 1 + }] }, { @@ -117,6 +118,50 @@ userIdAsEids = [ }] }, + { + source: 'liveintent.indexexchange.com', + uids: [{ + id: 'some-random-id-value', + atype: 3, + ext: { + provider: 'liveintent.com' + } + }] + }, + + { + source: 'liveintent.sovrn.com'', + uids: [{ + id: 'some-random-id-value', + atype: 3, + ext: { + provider: 'liveintent.com' + } + }] + }, + + { + source: 'openx.net'', + uids: [{ + id: 'some-random-id-value', + atype: 3, + ext: { + provider: 'liveintent.com' + } + }] + }, + + { + source: 'pubmatic.com'', + uids: [{ + id: 'some-random-id-value', + atype: 3, + ext: { + provider: 'liveintent.com' + } + }] + }, + { source: 'media.net', uids: [{ From 332a7d266f0c9e488f822d8d542ad5b74e0a77af Mon Sep 17 00:00:00 2001 From: AdmixerTech <35560933+AdmixerTech@users.noreply.github.com> Date: Tue, 19 Dec 2023 16:43:25 +0200 Subject: [PATCH 084/246] Admixer Bid Adaper: add admixerwl alias (#10859) * Update README.md update * Add admixerwl alias for admixerBidAdapter. --------- Co-authored-by: Yaroslav Masenko --- modules/admixerBidAdapter.js | 10 +++-- modules/admixerBidAdapter.md | 45 +++++++++++++++++++++ test/spec/modules/admixerBidAdapter_spec.js | 43 ++++++++++++++++++-- 3 files changed, 92 insertions(+), 6 deletions(-) diff --git a/modules/admixerBidAdapter.js b/modules/admixerBidAdapter.js index 6cbc36c1dcd..f5f0b5bf665 100644 --- a/modules/admixerBidAdapter.js +++ b/modules/admixerBidAdapter.js @@ -14,6 +14,7 @@ const ALIASES = [ {code: 'futureads', endpoint: 'https://ads.futureads.io/prebid.1.2.aspx'}, {code: 'smn', endpoint: 'https://ads.smn.rs/prebid.1.2.aspx'}, {code: 'admixeradx', endpoint: 'https://inv-nets.admixer.net/adxprebid.1.2.aspx'}, + {code: 'admixerwl', endpoint: 'https://inv-nets-adxwl.admixer.com/adxwlprebid.aspx'}, ]; export const spec = { code: BIDDER_CODE, @@ -23,7 +24,9 @@ export const spec = { * Determines whether or not the given bid request is valid. */ isBidRequestValid: function (bid) { - return !!bid.params.zone; + return bid.bidder === 'admixerwl' + ? !!bid.params.clientId && !!bid.params.endpointId + : !!bid.params.zone; }, /** * Make a server request from the list of BidRequests. @@ -76,10 +79,11 @@ export const spec = { imp.ortb2 && delete imp.ortb2; payload.imps.push(imp); }); + + let urlForRequest = endpointUrl || getEndpointUrl(bidderRequest.bidderCode) return { method: 'POST', - url: - endpointUrl || getEndpointUrl(bidderRequest.bidderCode), + url: bidderRequest.bidderCode === 'admixerwl' ? `${urlForRequest}?client=${payload.imps[0]?.params?.clientId}` : urlForRequest, data: payload, }; }, diff --git a/modules/admixerBidAdapter.md b/modules/admixerBidAdapter.md index 682f5629115..64f8dd64ee4 100644 --- a/modules/admixerBidAdapter.md +++ b/modules/admixerBidAdapter.md @@ -50,3 +50,48 @@ Please use ```admixer``` as the bidder code. }, ]; ``` + +### AdmixerWL Test Parameters +``` + var adUnits = [ + { + code: 'desktop-banner-ad-div', + sizes: [[300, 250]], // a display size + bids: [ + { + bidder: "admixer", + params: { + endpointId: 41512, + clientId: 62 + } + } + ] + },{ + code: 'mobile-banner-ad-div', + sizes: [[300, 50]], // a mobile size + bids: [ + { + bidder: "admixer", + params: { + endpointId: 41512, + clientId: 62 + } + } + ] + },{ + code: 'video-ad', + sizes: [[300, 50]], + mediaType: 'video', + bids: [ + { + bidder: "admixer", + params: { + endpointId: 41512, + clientId: 62 + } + } + ] + }, + ]; +``` + diff --git a/test/spec/modules/admixerBidAdapter_spec.js b/test/spec/modules/admixerBidAdapter_spec.js index 8cf433460b7..85538efc957 100644 --- a/test/spec/modules/admixerBidAdapter_spec.js +++ b/test/spec/modules/admixerBidAdapter_spec.js @@ -4,11 +4,12 @@ import {newBidder} from 'src/adapters/bidderFactory.js'; import {config} from '../../../src/config.js'; const BIDDER_CODE = 'admixer'; -const BIDDER_CODE_ADX = 'admixeradx'; +const WL_BIDDER_CODE = 'admixerwl' const ENDPOINT_URL = 'https://inv-nets.admixer.net/prebid.1.2.aspx'; const ENDPOINT_URL_CUSTOM = 'https://custom.admixer.net/prebid.aspx'; -const ENDPOINT_URL_ADX = 'https://inv-nets.admixer.net/adxprebid.1.2.aspx'; const ZONE_ID = '2eb6bd58-865c-47ce-af7f-a918108c3fd2'; +const CLIENT_ID = 5124; +const ENDPOINT_ID = 81264; describe('AdmixerAdapter', function () { const adapter = newBidder(spec); @@ -36,9 +37,28 @@ describe('AdmixerAdapter', function () { auctionId: '1d1a030790a475', }; + let wlBid = { + bidder: WL_BIDDER_CODE, + params: { + clientId: CLIENT_ID, + endpointId: ENDPOINT_ID, + }, + 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 params required by WL found', function () { + expect(spec.isBidRequestValid(wlBid)).to.equal(true); + }); it('should return false when required params are not passed', function () { let bid = Object.assign({}, bid); @@ -48,6 +68,14 @@ describe('AdmixerAdapter', function () { }; expect(spec.isBidRequestValid(bid)).to.equal(false); }); + it('should return false when params required by WL are not passed', function () { + let wlBid = Object.assign({}, wlBid); + delete wlBid.params; + wlBid.params = { + clientId: 0, + }; + expect(spec.isBidRequestValid(wlBid)).to.equal(false); + }); }); describe('buildRequests', function () { @@ -105,7 +133,10 @@ describe('AdmixerAdapter', function () { validRequest: [ { bidder: bidder, - params: { + params: bidder === 'admixerwl' ? { + clientId: CLIENT_ID, + endpointId: ENDPOINT_ID + } : { zone: ZONE_ID, }, adUnitCode: 'adunit-code', @@ -168,6 +199,12 @@ describe('AdmixerAdapter', function () { expect(request.url).to.equal('https://inv-nets.admixer.net/adxprebid.1.2.aspx'); expect(request.method).to.equal('POST'); }); + it('build request for admixerwl', function () { + const requestParams = requestParamsFor('admixerwl'); + const request = spec.buildRequests(requestParams.validRequest, requestParams.bidderRequest); + expect(request.url).to.equal(`https://inv-nets-adxwl.admixer.com/adxwlprebid.aspx?client=${CLIENT_ID}`); + expect(request.method).to.equal('POST'); + }); }); describe('checkFloorGetting', function () { From cd787ebd7caab60888e8bdf731afa2dcecffa53a Mon Sep 17 00:00:00 2001 From: Olivier Date: Tue, 19 Dec 2023 16:05:05 +0100 Subject: [PATCH 085/246] AdagioAnalyticsAdapter: fix function param check (#10860) --- modules/adagioAnalyticsAdapter.js | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/modules/adagioAnalyticsAdapter.js b/modules/adagioAnalyticsAdapter.js index ee2368fde08..807937ba3dd 100644 --- a/modules/adagioAnalyticsAdapter.js +++ b/modules/adagioAnalyticsAdapter.js @@ -97,6 +97,10 @@ function getAdapterNameForAlias(aliasName) { }; function isAdagio(value) { + if (!value) { + return false + } + return value.toLowerCase().includes('adagio') || getAdapterNameForAlias(value).toLowerCase().includes('adagio'); }; From 0b59d4d7fbd2224a9225bbdb0e9819aac0cdf970 Mon Sep 17 00:00:00 2001 From: redaguermas Date: Wed, 20 Dec 2023 04:35:29 -0800 Subject: [PATCH 086/246] NoBid Analytics Adapter: support for counting blocked requests for the Optimizer (#10842) * Enable supplyChain support * Added support for COPPA * rebuilt * Added support for Extended User IDs. * Added support for the "meta" attribute in bid response. * Delete nobidBidAdapter.js.orig * Delete a * Delete .jsdtscope * Delete org.eclipse.wst.jsdt.ui.superType.container * Delete org.eclipse.wst.jsdt.ui.superType.name * Delete .project * Added support for counting blocked requests for the Optimizer. * Added missing function for testing. * Added unit tests --------- Co-authored-by: Reda Guermas --- modules/nobidAnalyticsAdapter.js | 67 +++++++++++++++---- .../modules/nobidAnalyticsAdapter_spec.js | 8 ++- 2 files changed, 61 insertions(+), 14 deletions(-) diff --git a/modules/nobidAnalyticsAdapter.js b/modules/nobidAnalyticsAdapter.js index 27ec1cd9451..3a7912c37e1 100644 --- a/modules/nobidAnalyticsAdapter.js +++ b/modules/nobidAnalyticsAdapter.js @@ -6,10 +6,10 @@ import CONSTANTS from '../src/constants.json'; import adapterManager from '../src/adapterManager.js'; import {MODULE_TYPE_ANALYTICS} from '../src/activities/modules.js'; -const VERSION = '1.0.4'; +const VERSION = '1.1.0'; const MODULE_NAME = 'nobidAnalyticsAdapter'; -const ANALYTICS_DATA_NAME = 'analytics.nobid.io'; -const RETENTION_SECONDS = 7 * 24 * 3600; +const ANALYTICS_OPT_FLUSH_TIMEOUT_SECONDS = 5 * 1000; +const RETENTION_SECONDS = 1 * 24 * 3600; const TEST_ALLOCATION_PERCENTAGE = 5; // dont block 5% of the time; window.nobidAnalyticsVersion = VERSION; const analyticsType = 'endpoint'; @@ -148,7 +148,7 @@ nobidAnalytics = { return isExpired(data, this.retentionSeconds); }, isAnalyticsDisabled () { - let stored = storage.getDataFromLocalStorage(ANALYTICS_DATA_NAME); + let stored = storage.getDataFromLocalStorage(this.ANALYTICS_DATA_NAME); if (!isJson(stored)) return false; stored = JSON.parse(stored); if (this.isExpired(stored)) return false; @@ -157,8 +157,10 @@ nobidAnalytics = { processServerResponse (response) { if (!isJson(response)) return; const resp = JSON.parse(response); - storage.setDataInLocalStorage(ANALYTICS_DATA_NAME, JSON.stringify({ ...resp, ts: Date.now() })); - } + storage.setDataInLocalStorage(this.ANALYTICS_DATA_NAME, JSON.stringify({ ...resp, ts: Date.now() })); + }, + ANALYTICS_DATA_NAME: 'analytics.nobid.io', + ANALYTICS_OPT_NAME: 'analytics.nobid.io.optData' } adapterManager.registerAnalyticsAdapter({ @@ -166,33 +168,74 @@ adapterManager.registerAnalyticsAdapter({ code: 'nobidAnalytics', gvlid: GVLID }); +nobidAnalytics.originalAdUnits = {}; window.nobidCarbonizer = { getStoredLocalData: function () { - return storage.getDataFromLocalStorage(ANALYTICS_DATA_NAME); + const a = storage.getDataFromLocalStorage(nobidAnalytics.ANALYTICS_DATA_NAME); + const b = storage.getDataFromLocalStorage(nobidAnalytics.ANALYTICS_OPT_NAME); + const ret = {}; + if (a) ret[nobidAnalytics.ANALYTICS_DATA_NAME] = a; + if (b) ret[nobidAnalytics.ANALYTICS_OPT_NAME] = b + return ret; }, isActive: function () { - let stored = storage.getDataFromLocalStorage(ANALYTICS_DATA_NAME); + let stored = storage.getDataFromLocalStorage(nobidAnalytics.ANALYTICS_DATA_NAME); if (!isJson(stored)) return false; stored = JSON.parse(stored); if (isExpired(stored, nobidAnalytics.retentionSeconds)) return false; return stored.carbonizer_active || false; }, carbonizeAdunits: function (adunits, skipTestGroup) { + function processBlockedBidders (blockedBidders) { + function sendOptimizerData() { + let optData = storage.getDataFromLocalStorage(nobidAnalytics.ANALYTICS_OPT_NAME); + storage.removeDataFromLocalStorage(nobidAnalytics.ANALYTICS_OPT_NAME); + if (isJson(optData)) { + optData = JSON.parse(optData); + if (Object.getOwnPropertyNames(optData).length > 0) { + const event = { o_bidders: optData }; + if (nobidAnalytics.topLocation) event.topLocation = nobidAnalytics.topLocation; + sendEvent(event, 'optData'); + } + } + } + if (blockedBidders && blockedBidders.length > 0) { + let optData = storage.getDataFromLocalStorage(nobidAnalytics.ANALYTICS_OPT_NAME); + optData = isJson(optData) ? JSON.parse(optData) : {}; + const bidders = blockedBidders.map(rec => rec.bidder); + if (bidders && bidders.length > 0) { + bidders.forEach(bidder => { + if (!optData[bidder]) optData[bidder] = 1; + else optData[bidder] += 1; + }); + storage.setDataInLocalStorage(nobidAnalytics.ANALYTICS_OPT_NAME, JSON.stringify(optData)); + if (window.nobidAnalyticsOptTimer) return; + window.nobidAnalyticsOptTimer = setInterval(sendOptimizerData, ANALYTICS_OPT_FLUSH_TIMEOUT_SECONDS); + } + } + } function carbonizeAdunit (adunit) { - let stored = storage.getDataFromLocalStorage(ANALYTICS_DATA_NAME); + let stored = storage.getDataFromLocalStorage(nobidAnalytics.ANALYTICS_DATA_NAME); if (!isJson(stored)) return; stored = JSON.parse(stored); if (isExpired(stored, nobidAnalytics.retentionSeconds)) return; const carbonizerBidders = stored.bidders || []; - const allowedBidders = adunit.bids.filter(rec => carbonizerBidders.includes(rec.bidder)); + let originalAdUnit = null; + if (nobidAnalytics.originalAdUnits && nobidAnalytics.originalAdUnits[adunit.code]) originalAdUnit = nobidAnalytics.originalAdUnits[adunit.code]; + const allowedBidders = originalAdUnit.bids.filter(rec => carbonizerBidders.includes(rec.bidder)); + const blockedBidders = originalAdUnit.bids.filter(rec => !carbonizerBidders.includes(rec.bidder)); + processBlockedBidders(blockedBidders); adunit.bids = allowedBidders; } + for (const adunit of adunits) { + if (!nobidAnalytics.originalAdUnits[adunit.code]) nobidAnalytics.originalAdUnits[adunit.code] = JSON.parse(JSON.stringify(adunit)); + }; if (this.isActive()) { // 5% of the time do not block; if (!skipTestGroup && Math.floor(Math.random() * 101) <= TEST_ALLOCATION_PERCENTAGE) return; - adunits.forEach(adunit => { + for (const adunit of adunits) { carbonizeAdunit(adunit); - }); + }; } } }; diff --git a/test/spec/modules/nobidAnalyticsAdapter_spec.js b/test/spec/modules/nobidAnalyticsAdapter_spec.js index 742b4c16abb..06a39ffd020 100644 --- a/test/spec/modules/nobidAnalyticsAdapter_spec.js +++ b/test/spec/modules/nobidAnalyticsAdapter_spec.js @@ -466,8 +466,8 @@ describe('NoBid Prebid Analytic', function () { const previousRetention = nobidAnalytics.retentionSeconds; nobidAnalytics.retentionSeconds = 3; nobidAnalytics.processServerResponse(JSON.stringify({carbonizer_active: true})); - const stored = nobidCarbonizer.getStoredLocalData(); - expect(stored).to.contain(`{"carbonizer_active":true,"ts":`); + let stored = nobidCarbonizer.getStoredLocalData(); + expect(stored[nobidAnalytics.ANALYTICS_DATA_NAME]).to.contain(`{"carbonizer_active":true,"ts":`); clock.tick(5000); active = nobidCarbonizer.isActive(adunits, true); expect(active).to.equal(false); @@ -486,6 +486,10 @@ describe('NoBid Prebid Analytic', function () { } ] nobidCarbonizer.carbonizeAdunits(adunits, true); + stored = nobidCarbonizer.getStoredLocalData(); + expect(stored[nobidAnalytics.ANALYTICS_DATA_NAME]).to.contain('{"carbonizer_active":true,"ts":'); + expect(stored[nobidAnalytics.ANALYTICS_OPT_NAME]).to.contain('{"bidder1":1,"bidder2":1}'); + clock.tick(5000); expect(adunits[0].bids.length).to.equal(0); done(); From a5c2e06c836ab366112457aa04a2961a8d59990e Mon Sep 17 00:00:00 2001 From: Demetrio Girardi Date: Wed, 20 Dec 2023 10:36:32 -0800 Subject: [PATCH 087/246] consentManagementGpp: fix handling of CMP errors (#10811) --- modules/consentManagementGpp.js | 13 +++++++++---- test/spec/modules/consentManagementGpp_spec.js | 2 +- 2 files changed, 10 insertions(+), 5 deletions(-) diff --git a/modules/consentManagementGpp.js b/modules/consentManagementGpp.js index 8160ee2378c..f696ce25902 100644 --- a/modules/consentManagementGpp.js +++ b/modules/consentManagementGpp.js @@ -70,13 +70,18 @@ export class GPPClient { * - a promise to GPP data. */ static init(mkCmp = cmpClient) { - if (this.INST == null) { - this.INST = this.ping(mkCmp).catch(e => { - this.INST = null; + let inst = this.INST; + if (!inst) { + let err; + const reset = () => err && (this.INST = null); + inst = this.INST = this.ping(mkCmp).catch(e => { + err = true; + reset(); throw e; }); + reset(); } - return this.INST.then(([client, pingData]) => [ + return inst.then(([client, pingData]) => [ client, client.initialized ? client.refresh() : client.init(pingData) ]); diff --git a/test/spec/modules/consentManagementGpp_spec.js b/test/spec/modules/consentManagementGpp_spec.js index 99d4f94f502..93a876d0233 100644 --- a/test/spec/modules/consentManagementGpp_spec.js +++ b/test/spec/modules/consentManagementGpp_spec.js @@ -290,7 +290,7 @@ describe('consentManagementGpp', function () { }); it('should not re-use errors', (done) => { - cmpResult = Promise.reject(new Error()); + cmpResult = GreedyPromise.reject(new Error()); GPPClient.init(makeCmp).catch(() => { cmpResult = {signalStatus: 'ready'}; return GPPClient.init(makeCmp).then(([client]) => { From 104f53f0b7b8716c97b7758f58af48d61c219c30 Mon Sep 17 00:00:00 2001 From: ahmadlob <109217988+ahmadlob@users.noreply.github.com> Date: Wed, 20 Dec 2023 20:37:03 +0200 Subject: [PATCH 088/246] Taboola Bid Adapter: implement Iframe user sync (#10789) * iframe-sync * iframe-sync-add-tests --- modules/taboolaBidAdapter.js | 8 ++++++++ test/spec/modules/taboolaBidAdapter_spec.js | 17 ++++++++++++++--- 2 files changed, 22 insertions(+), 3 deletions(-) diff --git a/modules/taboolaBidAdapter.js b/modules/taboolaBidAdapter.js index 0bdb0568a10..a8745449ee4 100644 --- a/modules/taboolaBidAdapter.js +++ b/modules/taboolaBidAdapter.js @@ -12,6 +12,7 @@ const GVLID = 42; const CURRENCY = 'USD'; export const END_POINT_URL = 'https://display.bidder.taboola.com/OpenRTB/TaboolaHB/auction'; export const USER_SYNC_IMG_URL = 'https://trc.taboola.com/sg/prebidJS/1/cm'; +export const USER_SYNC_IFRAME_URL = 'https://cdn.taboola.com/scripts/prebid_iframe_sync.html'; const USER_ID = 'user-id'; const STORAGE_KEY = `taboola global:${USER_ID}`; const COOKIE_KEY = 'trc_cookie_storage'; @@ -183,6 +184,13 @@ export const spec = { queryParams.push('gpp=' + encodeURIComponent(gppConsent)); } + if (syncOptions.iframeEnabled) { + syncs.push({ + type: 'iframe', + url: USER_SYNC_IFRAME_URL + (queryParams.length ? '?' + queryParams.join('&') : '') + }); + } + if (syncOptions.pixelEnabled) { syncs.push({ type: 'image', diff --git a/test/spec/modules/taboolaBidAdapter_spec.js b/test/spec/modules/taboolaBidAdapter_spec.js index 2bfe93bf880..402b4990166 100644 --- a/test/spec/modules/taboolaBidAdapter_spec.js +++ b/test/spec/modules/taboolaBidAdapter_spec.js @@ -835,17 +835,28 @@ describe('Taboola Adapter', function () { describe('getUserSyncs', function () { const usersyncUrl = 'https://trc.taboola.com/sg/prebidJS/1/cm'; + const iframeUrl = 'https://cdn.taboola.com/scripts/prebid_iframe_sync.html'; - it('should not return user sync if pixelEnabled is false', function () { - const res = spec.getUserSyncs({pixelEnabled: false}); + it('should not return user sync if pixelEnabled is false and iframe disabled', function () { + const res = spec.getUserSyncs({pixelEnabled: false, iframeEnabled: false}); expect(res).to.be.an('array').that.is.empty; }); it('should return user sync if pixelEnabled is true', function () { - const res = spec.getUserSyncs({pixelEnabled: true}); + const res = spec.getUserSyncs({pixelEnabled: true, iframeEnabled: false}); expect(res).to.deep.equal([{type: 'image', url: usersyncUrl}]); }); + it('should return user sync if iframeEnabled is true', function () { + const res = spec.getUserSyncs({iframeEnabled: true, pixelEnabled: false}); + expect(res).to.deep.equal([{type: 'iframe', url: iframeUrl}]); + }); + + it('should return both user syncs if iframeEnabled is true and pixelEnabled is true', function () { + const res = spec.getUserSyncs({iframeEnabled: true, pixelEnabled: true}); + expect(res).to.deep.equal([{type: 'iframe', url: iframeUrl}, {type: 'image', url: usersyncUrl}]); + }); + it('should pass consent tokens values', function() { expect(spec.getUserSyncs({ pixelEnabled: true }, {}, {gdprApplies: true, consentString: 'GDPR_CONSENT'}, 'USP_CONSENT')).to.deep.equal([{ type: 'image', url: `${usersyncUrl}?gdpr=1&gdpr_consent=GDPR_CONSENT&us_privacy=USP_CONSENT` From 2548949ec03ee755536356a1d12d2acc05e6af71 Mon Sep 17 00:00:00 2001 From: Nick Jacob Date: Wed, 20 Dec 2023 13:37:44 -0500 Subject: [PATCH 089/246] AMX bid adapter: fix timeout handler, bump version (#10744) * amx bid adapter: fix timeout handler, bump version * restore package-lock to master * remove sendbeacon, use mock xhr * add keepalive option for ajax * fix firefox test * CR changes * CR changes: restore files * CR changes --- modules/amxBidAdapter.js | 116 ++++++++++++--- test/spec/modules/amxBidAdapter_spec.js | 187 ++++++++++++++++-------- 2 files changed, 220 insertions(+), 83 deletions(-) diff --git a/modules/amxBidAdapter.js b/modules/amxBidAdapter.js index a773ac70559..6e14f65b0c8 100644 --- a/modules/amxBidAdapter.js +++ b/modules/amxBidAdapter.js @@ -14,20 +14,31 @@ import { } from '../src/utils.js'; import { config } from '../src/config.js'; import { getStorageManager } from '../src/storageManager.js'; +import { fetch } from '../src/ajax.js'; const BIDDER_CODE = 'amx'; const storage = getStorageManager({ bidderCode: BIDDER_CODE }); const SIMPLE_TLD_TEST = /\.com?\.\w{2,4}$/; const DEFAULT_ENDPOINT = 'https://prebid.a-mo.net/a/c'; -const VERSION = 'pba1.3.3'; +const VERSION = 'pba1.3.4'; const VAST_RXP = /^\s*<\??(?:vast|xml)/i; -const TRACKING_ENDPOINT = 'https://1x1.a-mo.net/hbx/'; +const TRACKING_BASE = 'https://1x1.a-mo.net/'; +const TRACKING_ENDPOINT = TRACKING_BASE + 'hbx/'; +const POST_TRACKING_ENDPOINT = TRACKING_BASE + 'e'; const AMUID_KEY = '__amuidpb'; function getLocation(request) { return parseUrl(request.refererInfo?.topmostLocation || window.location.href); } +function getTimeoutSize(timeoutData) { + if (timeoutData.sizes == null || timeoutData.sizes.length === 0) { + return [0, 0]; + } + + return timeoutData.sizes[0]; +} + const largestSize = (sizes, mediaTypes) => { const allSizes = sizes .concat(deepAccess(mediaTypes, `${BANNER}.sizes`, []) || []) @@ -149,7 +160,9 @@ function convertRequest(bid) { const tid = deepAccess(bid, 'params.tagId'); const au = - bid.params != null && typeof bid.params.adUnitId === 'string' && bid.params.adUnitId !== '' + bid.params != null && + typeof bid.params.adUnitId === 'string' && + bid.params.adUnitId !== '' ? bid.params.adUnitId : bid.adUnitCode; @@ -202,7 +215,10 @@ function isSyncEnabled(syncConfigP, syncType) { return false; } - if (syncConfig.bidders === '*' || (isArray(syncConfig.bidders) && syncConfig.bidders.indexOf('amx') !== -1)) { + if ( + syncConfig.bidders === '*' || + (isArray(syncConfig.bidders) && syncConfig.bidders.indexOf('amx') !== -1) + ) { return syncConfig.filter == null || syncConfig.filter === 'include'; } @@ -219,12 +235,17 @@ function getSyncSettings() { d: 0, l: 0, t: 0, - e: true + e: true, }; } - const settings = { d: syncConfig.syncDelay, l: syncConfig.syncsPerBidder, t: 0, e: syncConfig.syncEnabled } - const all = isSyncEnabled(syncConfig.filterSettings, 'all') + const settings = { + d: syncConfig.syncDelay, + l: syncConfig.syncsPerBidder, + t: 0, + e: syncConfig.syncEnabled, + }; + const all = isSyncEnabled(syncConfig.filterSettings, 'all'); if (all) { settings.t = SYNC_IMAGE & SYNC_IFRAME; @@ -256,12 +277,14 @@ function getGpp(bidderRequest) { return bidderRequest.gppConsent; } - return bidderRequest?.ortb2?.regs?.gpp ?? { gppString: '', applicableSections: '' }; + return ( + bidderRequest?.ortb2?.regs?.gpp ?? { gppString: '', applicableSections: '' } + ); } function buildReferrerInfo(bidderRequest) { if (bidderRequest.refererInfo == null) { - return { r: '', t: false, c: '', l: 0, s: [] } + return { r: '', t: false, c: '', l: 0, s: [] }; } const re = bidderRequest.refererInfo; @@ -272,7 +295,7 @@ function buildReferrerInfo(bidderRequest) { l: re.numIframes, s: re.stack, c: re.canonicalUrl, - } + }; } const isTrue = (boolValue) => @@ -358,28 +381,35 @@ export const spec = { return { data: payload, method: 'POST', + browsingTopics: true, url: deepAccess(bidRequests[0], 'params.endpoint', DEFAULT_ENDPOINT), withCredentials: true, }; }, - getUserSyncs(syncOptions, serverResponses, gdprConsent, uspConsent, gppConsent) { + getUserSyncs( + syncOptions, + serverResponses, + gdprConsent, + uspConsent, + gppConsent + ) { const qp = { gdpr_consent: enc(gdprConsent?.consentString || ''), gdpr: enc(gdprConsent?.gdprApplies ? 1 : 0), us_privacy: enc(uspConsent || ''), gpp: enc(gppConsent?.gppString || ''), - gpp_sid: enc(gppConsent?.applicableSections || '') + gpp_sid: enc(gppConsent?.applicableSections || ''), }; const iframeSync = { url: `https://prebid.a-mo.net/isyn?${formatQS(qp)}`, - type: 'iframe' + type: 'iframe', }; if (serverResponses == null || serverResponses.length === 0) { if (syncOptions.iframeEnabled) { - return [iframeSync] + return [iframeSync]; } return []; @@ -394,7 +424,10 @@ export const spec = { const pixelType = syncPixel.indexOf('__st=iframe') !== -1 ? 'iframe' : 'image'; if (syncOptions.iframeEnabled || pixelType === 'image') { - hasFrame = hasFrame || (pixelType === 'iframe') || (syncPixel.indexOf('cchain') !== -1) + hasFrame = + hasFrame || + pixelType === 'iframe' || + syncPixel.indexOf('cchain') !== -1; output.push({ url: syncPixel, type: pixelType, @@ -405,7 +438,7 @@ export const spec = { }); if (!hasFrame && output.length < 2) { - output.push(iframeSync) + output.push(iframeSync); } return output; @@ -470,19 +503,58 @@ export const spec = { aud: targetingData.requestId, a: targetingData.adUnitCode, c2: nestedQs(targetingData.adserverTargeting), + cn3: targetingData.timeToRespond, }); }, onTimeout(timeoutData) { - if (timeoutData == null) { + if (timeoutData == null || !timeoutData.length) { return; } - trackEvent('pbto', { - A: timeoutData.bidder, - bid: timeoutData.bidId, - a: timeoutData.adUnitCode, - cn: timeoutData.timeout, + let common = null; + const events = timeoutData.map((timeout) => { + const params = timeout.params || {}; + const size = getTimeoutSize(timeout); + const { domain, page, ref } = + timeout.ortb2 != null && timeout.ortb2.site != null + ? timeout.ortb2.site + : {}; + + if (common == null) { + common = { + do: domain, + u: page, + U: getUIDSafe(), + re: ref, + V: '$prebid.version$', + vg: '$$PREBID_GLOBAL$$', + }; + } + + return { + A: timeout.bidder, + mid: params.tagId, + a: params.adunitId || timeout.adUnitCode, + bid: timeout.bidId, + n: 'g_pbto', + aud: timeout.transactionId, + w: size[0], + h: size[1], + cn: timeout.timeout, + cn2: timeout.bidderRequestsCount, + cn3: timeout.bidderWinsCount, + }; + }); + + const payload = JSON.stringify({ c: common, e: events }); + fetch(POST_TRACKING_ENDPOINT, { + body: payload, + keepalive: true, + withCredentials: true, + method: 'POST' + }).catch((_e) => { + // do nothing; ignore errors }); }, diff --git a/test/spec/modules/amxBidAdapter_spec.js b/test/spec/modules/amxBidAdapter_spec.js index 984c443344d..21fa2e2617c 100644 --- a/test/spec/modules/amxBidAdapter_spec.js +++ b/test/spec/modules/amxBidAdapter_spec.js @@ -3,6 +3,7 @@ import { spec } from 'modules/amxBidAdapter.js'; import { createEidsArray } from 'modules/userId/eids.js'; import { BANNER, VIDEO } from 'src/mediaTypes.js'; import { config } from 'src/config.js'; +import { server } from 'test/mocks/xhr.js'; import * as utils from 'src/utils.js'; const sampleRequestId = '82c91e127a9b93e'; @@ -11,7 +12,7 @@ const sampleDisplayCRID = '78827819'; // minimal example vast const sampleVideoAd = (addlImpression) => ` -00:00:15${addlImpression} +00:00:15${addlImpression} `.replace(/\n+/g, ''); const sampleFPD = { @@ -37,7 +38,7 @@ const sampleBidderRequest = { }, gppConsent: { gppString: 'example', - applicableSections: 'example' + applicableSections: 'example', }, auctionId: null, @@ -209,10 +210,12 @@ describe('AmxBidAdapter', () => { describe('getUserSync', () => { it('Will perform an iframe sync even if there is no server response..', () => { const syncs = spec.getUserSyncs({ iframeEnabled: true }); - expect(syncs).to.eql([{ - type: 'iframe', - url: 'https://prebid.a-mo.net/isyn?gdpr_consent=&gdpr=0&us_privacy=&gpp=&gpp_sid=' - }]); + expect(syncs).to.eql([ + { + type: 'iframe', + url: 'https://prebid.a-mo.net/isyn?gdpr_consent=&gdpr=0&us_privacy=&gpp=&gpp_sid=', + }, + ]); }); it('will return valid syncs from a server response', () => { @@ -276,8 +279,13 @@ describe('AmxBidAdapter', () => { }); it('will attach additional referrer info data', () => { - const { data } = spec.buildRequests([sampleBidRequestBase], sampleBidderRequest); - expect(data.ri.r).to.equal(sampleBidderRequest.refererInfo.topmostLocation); + const { data } = spec.buildRequests( + [sampleBidRequestBase], + sampleBidderRequest + ); + expect(data.ri.r).to.equal( + sampleBidderRequest.refererInfo.topmostLocation + ); expect(data.ri.t).to.equal(sampleBidderRequest.refererInfo.reachedTop); expect(data.ri.l).to.equal(sampleBidderRequest.refererInfo.numIframes); expect(data.ri.s).to.equal(sampleBidderRequest.refererInfo.stack); @@ -315,7 +323,7 @@ describe('AmxBidAdapter', () => { [sampleBidRequestBase], sampleBidderRequest ); - delete data.m; // don't deal with "m" in this test + 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); @@ -343,10 +351,8 @@ describe('AmxBidAdapter', () => { }); it('will attach sync configuration', () => { - const request = () => spec.buildRequests( - [sampleBidRequestBase], - sampleBidderRequest - ); + const request = () => + spec.buildRequests([sampleBidRequestBase], sampleBidderRequest); const setConfig = (filterSettings) => config.setConfig({ @@ -355,56 +361,73 @@ describe('AmxBidAdapter', () => { syncDelay: 2300, syncEnabled: true, filterSettings, - } + }, }); const test = (filterSettings) => { setConfig(filterSettings); return request().data.sync; - } + }; const base = { d: 2300, l: 2, e: true }; - const tests = [[ - undefined, - { ...base, t: 0 } - ], [{ - image: { - bidders: '*', - filter: 'include' - }, - iframe: { - bidders: '*', - filter: 'include' - } - }, { ...base, t: 3 }], [{ - image: { - bidders: ['amx'], - }, - iframe: { - bidders: '*', - filter: 'include' - } - }, { ...base, t: 3 }], [{ - image: { - bidders: ['other'], - }, - iframe: { - bidders: '*' - } - }, { ...base, t: 2 }], [{ - image: { - bidders: ['amx'] - }, - iframe: { - bidders: ['amx'], - filter: 'exclude' - } - }, { ...base, t: 1 }]] + const tests = [ + [undefined, { ...base, t: 0 }], + [ + { + image: { + bidders: '*', + filter: 'include', + }, + iframe: { + bidders: '*', + filter: 'include', + }, + }, + { ...base, t: 3 }, + ], + [ + { + image: { + bidders: ['amx'], + }, + iframe: { + bidders: '*', + filter: 'include', + }, + }, + { ...base, t: 3 }, + ], + [ + { + image: { + bidders: ['other'], + }, + iframe: { + bidders: '*', + }, + }, + { ...base, t: 2 }, + ], + [ + { + image: { + bidders: ['amx'], + }, + iframe: { + bidders: ['amx'], + filter: 'exclude', + }, + }, + { ...base, t: 1 }, + ], + ]; for (let i = 0, l = tests.length; i < l; i++) { const [result, expected] = tests[i]; - expect(test(result), `input: ${JSON.stringify(result)}`).to.deep.equal(expected); + expect(test(result), `input: ${JSON.stringify(result)}`).to.deep.equal( + expected + ); } }); @@ -497,7 +520,15 @@ describe('AmxBidAdapter', () => { it('can build a video request', () => { const { data } = spec.buildRequests( - [{ ...sampleBidRequestVideo, params: { ...sampleBidRequestVideo.params, adUnitId: 'custom-auid' } }], + [ + { + ...sampleBidRequestVideo, + params: { + ...sampleBidRequestVideo.params, + adUnitId: 'custom-auid', + }, + }, + ], sampleBidderRequest ); expect(Object.keys(data.m).length).to.equal(1); @@ -659,15 +690,49 @@ describe('AmxBidAdapter', () => { }); it('will log an event for timeout', () => { - spec.onTimeout({ - bidder: 'example', - bidId: 'test-bid-id', - adUnitCode: 'div-gpt-ad', - timeout: 300, - auctionId: utils.getUniqueIdentifierStr(), + // this will use sendBeacon.. + spec.onTimeout([ + { + bidder: 'example', + bidId: 'test-bid-id', + adUnitCode: 'div-gpt-ad', + ortb2: { + site: { + ref: 'https://example.com', + }, + }, + params: { + tagId: 'tag-id', + }, + timeout: 300, + auctionId: utils.getUniqueIdentifierStr(), + }, + ]); + + const [request] = server.requests; + request.respond(204, {'Content-Type': 'text/html'}, null); + expect(request.url).to.equal('https://1x1.a-mo.net/e'); + + if (typeof Request !== 'undefined' && 'keepalive' in Request.prototype) { + expect(request.fetch.request.keepalive).to.equal(true); + } + + const {c: common, e: events} = JSON.parse(request.requestBody) + expect(common).to.deep.equal({ + V: '$prebid.version$', + vg: '$$PREBID_GLOBAL$$', + U: null, + re: 'https://example.com', }); - expect(firedPixels.length).to.equal(1); - expect(firedPixels[0]).to.match(/\/hbx\/g_pbto/); + + expect(events.length).to.equal(1); + const [event] = events; + expect(event.n).to.equal('g_pbto') + expect(event.A).to.equal('example'); + expect(event.mid).to.equal('tag-id'); + expect(event.cn).to.equal(300); + expect(event.bid).to.equal('test-bid-id'); + expect(event.a).to.equal('div-gpt-ad'); }); it('will log an event for prebid win', () => { From 2b8aacbd7886be767722c285a233906ace280634 Mon Sep 17 00:00:00 2001 From: jackhsiehucf <77815341+jackhsiehucf@users.noreply.github.com> Date: Thu, 21 Dec 2023 03:09:09 +0800 Subject: [PATCH 090/246] Ucfunnel Bid Adapter: add format support (#10862) * 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 * ucfunnel Bid Adapter: add GPID support and fix page and domain parameter. * ucfunnel Bid Adapter: add format support. --------- Co-authored-by: root Co-authored-by: Ryan Chou Co-authored-by: ucfunnel Co-authored-by: jack.hsieh --- modules/ucfunnelBidAdapter.js | 53 ++++++++++---------- test/spec/modules/ucfunnelBidAdapter_spec.js | 18 +++---- 2 files changed, 36 insertions(+), 35 deletions(-) diff --git a/modules/ucfunnelBidAdapter.js b/modules/ucfunnelBidAdapter.js index 2225deaa900..67f3cebdce8 100644 --- a/modules/ucfunnelBidAdapter.js +++ b/modules/ucfunnelBidAdapter.js @@ -68,7 +68,6 @@ export const spec = { interpretResponse: function (ucfunnelResponseObj, request) { const bidRequest = request.bidRequest; const ad = ucfunnelResponseObj ? ucfunnelResponseObj.body : {}; - const videoPlayerSize = parseSizes(bidRequest); let bid = { requestId: bidRequest.bidId, @@ -117,10 +116,10 @@ export const spec = { vastXml: ad.vastXml }); - if (videoPlayerSize && videoPlayerSize.length === 2) { + if (bidRequest.sizes && bidRequest.sizes.length > 0) { Object.assign(bid, { - width: videoPlayerSize[0], - height: videoPlayerSize[1] + width: bidRequest.sizes[0][0], + height: bidRequest.sizes[0][1] }); } break; @@ -128,8 +127,8 @@ export const spec = { default: var size = parseSizes(bidRequest); Object.assign(bid, { - width: ad.width || size[0], - height: ad.height || size[1], + width: ad.width || size[0][0], + height: ad.height || size[0][1], ad: ad.adm || '' }); } @@ -156,12 +155,6 @@ export const spec = { }; registerBidder(spec); -function transformSizes(requestSizes) { - if (typeof requestSizes === 'object' && requestSizes.length) { - return requestSizes[0]; - } -} - function getCookieSyncParameter(gdprApplies, apiVersion, consentString, uspConsent) { let param = '?'; if (gdprApplies == '1') { @@ -187,11 +180,10 @@ function parseSizes(bid) { params.video.playerWidth, params.video.playerHeight ]; - return size; + return [size]; } } - - return transformSizes(bid.sizes); + return bid.sizes; } function getSupplyChain(schain) { @@ -244,6 +236,20 @@ function getFloor(bid, size, mediaTypes) { return undefined; } +function addBidData(bidData, key, value) { + if (value) { + bidData[key] = value; + } +} + +function getFormat(size) { + let formatList = [] + for (var i = 0; i < size.length; i++) { + formatList.push(size[i].join(',')); + } + return (formatList.length > 0) ? formatList.join(';') : ''; +} + function getRequestData(bid, bidderRequest) { const size = parseSizes(bid); const language = navigator.language; @@ -264,14 +270,8 @@ function getRequestData(bid, bidderRequest) { schain: supplyChain }; - if (bidFloor) { - bidData.fp = bidFloor; - } - - if (gpid) { - bidData.gpid = gpid; - } - + addBidData(bidData, 'fp', bidFloor); + addBidData(bidData, 'gpid', gpid); addUserId(bidData, bid.userId); bidData.u = bidderRequest.refererInfo.page || bidderRequest.refererInfo.topmostLocation; @@ -293,10 +293,11 @@ function getRequestData(bid, bidderRequest) { } } - if (size != undefined && size.length == 2) { - bidData.w = size[0]; - bidData.h = size[1]; + if (size != undefined && size.length > 0 && size[0].length == 2) { + bidData.w = size[0][0]; + bidData.h = size[0][1]; } + addBidData(bidData, 'format', getFormat(size)); if (bidderRequest && bidderRequest.uspConsent) { Object.assign(bidData, { diff --git a/test/spec/modules/ucfunnelBidAdapter_spec.js b/test/spec/modules/ucfunnelBidAdapter_spec.js index 9bec7229450..998e0db6fe8 100644 --- a/test/spec/modules/ucfunnelBidAdapter_spec.js +++ b/test/spec/modules/ucfunnelBidAdapter_spec.js @@ -30,7 +30,7 @@ const validBannerBidReq = { params: { adid: 'ad-34BBD2AA24B678BBFD4E7B9EE3B872D' }, - sizes: [[300, 250]], + sizes: [[300, 250], [336, 280]], bidId: '263be71e91dd9d', auctionId: '9ad1fa8d-2297-4660-a018-b39945054746', ortb2Imp: { @@ -180,15 +180,15 @@ describe('ucfunnel Adapter', function () { expect(data.schain).to.equal('1.0,1!exchange1.com,1234,1,bid-request-1,publisher,publisher.com'); }); - it('must parse bid size from a nested array', function () { - const width = 640; - const height = 480; - const bid = deepClone(validBannerBidReq); - bid.sizes = [[ width, height ]]; - const requests = spec.buildRequests([ bid ], bidderRequest); + it('should support multiple size', function () { + const sizes = [[300, 250], [336, 280]]; + const format = '300,250;336,280'; + validBannerBidReq.sizes = sizes; + const requests = spec.buildRequests([ validBannerBidReq ], bidderRequest); const data = requests[0].data; - expect(data.w).to.equal(width); - expect(data.h).to.equal(height); + expect(data.w).to.equal(sizes[0][0]); + expect(data.h).to.equal(sizes[0][1]); + expect(data.format).to.equal(format); }); it('should set bidfloor if configured', function() { From 3c632ca35a8060b1530ebded1a4746b828b52af1 Mon Sep 17 00:00:00 2001 From: alexalukr2b2 <136449554+alexalukr2b2@users.noreply.github.com> Date: Thu, 21 Dec 2023 09:41:19 +0100 Subject: [PATCH 091/246] R2B2 Bid Adapter: Initial release (#10702) * R2B2 bidder adapter * R2B2 bid adapter: fix * conditional renderer --------- Co-authored-by: jenda --- modules/r2b2BidAdapter.js | 309 ++++++++++ modules/r2b2BidAdapter.md | 37 ++ test/spec/modules/r2b2BidAdapter_spec.js | 689 +++++++++++++++++++++++ 3 files changed, 1035 insertions(+) create mode 100644 modules/r2b2BidAdapter.js create mode 100644 modules/r2b2BidAdapter.md create mode 100644 test/spec/modules/r2b2BidAdapter_spec.js diff --git a/modules/r2b2BidAdapter.js b/modules/r2b2BidAdapter.js new file mode 100644 index 00000000000..15a65e3924c --- /dev/null +++ b/modules/r2b2BidAdapter.js @@ -0,0 +1,309 @@ +import {logWarn, logError, triggerPixel, deepSetValue, getParameterByName} from '../src/utils.js'; +import {ortbConverter} from '../libraries/ortbConverter/converter.js' +import {registerBidder} from '../src/adapters/bidderFactory.js'; +import {Renderer} from '../src/Renderer.js'; +import {BANNER, VIDEO, NATIVE} from '../src/mediaTypes.js'; +import {pbsExtensions} from '../libraries/pbsExtensions/pbsExtensions.js'; +import {bidderSettings} from '../src/bidderSettings.js'; + +const ADAPTER_VERSION = '1.0.0'; +const BIDDER_CODE = 'r2b2'; +const GVL_ID = 1235; + +const DEFAULT_CURRENCY = 'USD'; +const DEFAULT_TTL = 360; +const DEFAULT_NET_REVENUE = true; +const DEBUG_PARAM = 'pbjs_test_r2b2'; +const RENDERER_URL = 'https://delivery.r2b2.io/static/rendering.js'; + +const ENDPOINT = bidderSettings.get(BIDDER_CODE, 'endpoint') || 'hb.r2b2.cz'; +const SERVER_URL = 'https://' + ENDPOINT; +const URL_BID = SERVER_URL + '/openrtb2/bid'; +const URL_SYNC = SERVER_URL + '/cookieSync'; +const URL_EVENT = SERVER_URL + '/event'; + +const URL_EVENT_ON_BIDDER_ERROR = URL_EVENT + '/bidError'; +const URL_EVENT_ON_TIMEOUT = URL_EVENT + '/timeout'; + +const R2B2_TEST_UNIT = 'selfpromo'; + +export const internal = { + placementsToSync: [], + mappedParams: {} +} + +let r2b2Error = function(message, params) { + logError(message, params, BIDDER_CODE) +} + +function getIdParamsFromPID(pid) { + // selfpromo test creative + if (pid === R2B2_TEST_UNIT) { + return { d: 'test', g: 'test', p: 'selfpromo', m: 0, selfpromo: 1 } + } + if (!isNaN(pid)) { + return { pid: Number(pid) } + } + if (typeof pid === 'string') { + const params = pid.split('/'); + if (params.length === 3 || params.length === 4) { + const paramNames = ['d', 'g', 'p', 'm']; + return paramNames.reduce((p, paramName, index) => { + let param = params[index]; + if (paramName === 'm') { + param = ['desktop', 'classic', '0'].includes(param) ? 0 : Number(!!param) + } + p[paramName] = param; + return p + }, {}); + } + } +} + +function pickIdFromParams(params) { + if (!params) return null; + const { d, g, p, m, pid } = params; + return d ? { d, g, p, m } : { pid }; +} + +function getIdsFromBids(bids) { + return bids.reduce((ids, bid) => { + const params = internal.mappedParams[bid.bidId]; + const id = pickIdFromParams(params); + if (id) { + ids.push(id); + } + return ids + }, []); +} + +function triggerEvent(eventUrl, ids) { + if (ids && !ids.length) return; + const timeStamp = new Date().getTime(); + const symbol = (eventUrl.indexOf('?') === -1 ? '?' : '&'); + const url = eventUrl + symbol + `p=${btoa(JSON.stringify(ids))}&cb=${timeStamp}`; + triggerPixel(url) +} + +const converter = ortbConverter({ + imp(buildImp, bidRequest, context) { + const imp = buildImp(bidRequest, context); + const idParams = getIdParamsFromPID(bidRequest.params.pid); + deepSetValue(imp, 'ext.r2b2', idParams); + internal.placementsToSync.push(idParams); + internal.mappedParams[imp.id] = Object.assign({}, bidRequest.params, idParams); + return imp; + }, + request(buildRequest, imps, bidderRequest, context) { + const request = buildRequest(imps, bidderRequest, context); + deepSetValue(request, 'ext.version', ADAPTER_VERSION); + request.cur = [DEFAULT_CURRENCY]; + const test = getParameterByName(DEBUG_PARAM) === '1' ? 1 : 0; + deepSetValue(request, 'test', test); + return request; + }, + context: { + netRevenue: DEFAULT_NET_REVENUE, + ttl: DEFAULT_TTL + }, + processors: pbsExtensions +}); + +function setUpRenderer(adUnitCode, bid) { + // let renderer load once in main window, but pass the renderDocument + let renderDoc; + const config = { + documentResolver: (bid, sourceDocument, renderDocument) => { + renderDoc = renderDocument; + return sourceDocument; + } + } + let renderer = Renderer.install({ + url: RENDERER_URL, + config: config, + id: bid.requestId, + adUnitCode + }); + + renderer.setRender(function (bid, doc) { + doc = renderDoc || doc; + window.R2B2 = window.R2B2 || {}; + let main = window.R2B2; + main.HB = main.HB || {}; + main.HB.Render = main.HB.Render || {}; + main.HB.Render.queue = main.HB.Render.queue || []; + main.HB.Render.queue.push(() => { + const id = pickIdFromParams(internal.mappedParams[bid.requestId]) + main.HB.Renderer.render(id, bid, null, doc) + }) + }) + + return renderer +} + +function getExtMediaType(bidMediaType, responseBid) { + switch (bidMediaType) { + case BANNER: + return { + type: 'banner', + settings: { + chd: null, + width: responseBid.w, + height: responseBid.h, + ad: { + type: 'content', + data: responseBid.adm + } + } + }; + case NATIVE: + break; + case VIDEO: + break; + default: + break; + } +} + +function createPrebidResponseBid(requestImp, bidResponse, serverResponse, bids) { + const bidId = requestImp.id; + const adUnitCode = bids[0].adUnitCode; + const mediaType = bidResponse.ext.prebid.type; + let bidOut = { + requestId: bidId, + cpm: bidResponse.price, + creativeId: bidResponse.crid, + width: bidResponse.w, + height: bidResponse.h, + ttl: bidResponse.ttl ?? DEFAULT_TTL, + netRevenue: serverResponse.netRevenue ?? DEFAULT_NET_REVENUE, + currency: serverResponse.cur ?? DEFAULT_CURRENCY, + ad: bidResponse.adm, + mediaType: mediaType, + winUrl: bidResponse.nurl, + ext: { + cid: bidResponse.ext?.r2b2?.cid, + cdid: bidResponse.ext?.r2b2?.cdid, + mediaType: getExtMediaType(mediaType, bidResponse), + adUnit: adUnitCode, + dgpm: internal.mappedParams[bidId], + events: bidResponse.ext?.r2b2?.events + } + }; + if (bidResponse.ext?.r2b2?.useRenderer) { + bidOut.renderer = setUpRenderer(adUnitCode, bidOut); + } + return bidOut; +} + +export const spec = { + code: BIDDER_CODE, + gvlid: GVL_ID, + supportedMediaTypes: [BANNER], + + isBidRequestValid: function(bid) { + if (!bid.params || !bid.params.pid) { + logWarn('Bad params, "pid" required.'); + return false + } + const id = getIdParamsFromPID(bid.params.pid); + if (!id || !(id.pid || (id.d && id.g && id.p))) { + logWarn('Bad params, "pid" has to be either a number or a correctly assembled string.'); + return false + } + return true + }, + buildRequests: function(validBidRequests, bidderRequest) { + const data = converter.toORTB({ + bidRequests: validBidRequests, + bidderRequest + }); + return [{ + method: 'POST', + url: URL_BID, + data, + bids: bidderRequest.bids + }] + }, + + interpretResponse: function(serverResponse, request) { + // r2b2Error('error message', {params: 1}); + let prebidResponses = []; + + const response = serverResponse.body; + if (!response || !response.seatbid || !response.seatbid[0] || !response.seatbid[0].bid) { + return prebidResponses; + } + let requestImps = request.data.imp || []; + try { + response.seatbid.forEach(seat => { + let bids = seat.bid; + + for (let responseBid of bids) { + let responseImpId = responseBid.impid; + let requestCurrentImp = requestImps.find((requestImp) => requestImp.id === responseImpId); + if (!requestCurrentImp) { + r2b2Error('Cant match bid response.', {impid: Boolean(responseBid.impid)}); + continue;// Skip this iteration if there's no match + } + prebidResponses.push(createPrebidResponseBid(requestCurrentImp, responseBid, response, request.bids)); + } + }) + } catch (e) { + r2b2Error('Error while interpreting response:', {msg: e.message}); + } + return prebidResponses; + }, + getUserSyncs: function(syncOptions, serverResponses, gdprConsent, uspConsent) { + const syncs = []; + + if (!syncOptions.iframeEnabled) { + logWarn('Please enable iframe based user sync.'); + return syncs; + } + + let plString; + try { + plString = btoa(JSON.stringify(internal.placementsToSync || [])); + } catch (e) { + logWarn('User sync failed: ' + e.message); + return syncs + } + + let url = URL_SYNC + `?p=${plString}`; + + if (gdprConsent) { + url += `&gdpr=${Number(gdprConsent.gdprApplies)}&gdpr_consent=${gdprConsent.consentString}` + } + + if (uspConsent) { + url += `&us_privacy=${uspConsent}` + } + + syncs.push({ + type: 'iframe', + url: url + }) + return syncs; + }, + onBidWon: function(bid) { + const url = bid.ext?.events?.onBidWon; + if (url) { + triggerEvent(url) + } + }, + onSetTargeting: function(bid) { + const url = bid.ext?.events?.onSetTargeting; + if (url) { + triggerEvent(url) + } + }, + onTimeout: function(bids) { + triggerEvent(URL_EVENT_ON_TIMEOUT, getIdsFromBids(bids)) + }, + onBidderError: function(params) { + let { bidderRequest } = params; + triggerEvent(URL_EVENT_ON_BIDDER_ERROR, getIdsFromBids(bidderRequest.bids)) + } +} +registerBidder(spec); diff --git a/modules/r2b2BidAdapter.md b/modules/r2b2BidAdapter.md new file mode 100644 index 00000000000..43b59133215 --- /dev/null +++ b/modules/r2b2BidAdapter.md @@ -0,0 +1,37 @@ +# Overview + +``` +Module Name: R2B2 Bid Adapter +Module Type: Bidder Adapter +Maintainer: dev@r2b2.cz +``` + +## Description + +Module that integrates R2B2 demand sources. To get your bidder configuration reach out to our account team on partner@r2b2.io + + + +## Test unit + +```javascript + var adUnits = [ + { + code: 'test-r2b2', + mediaTypes: { + banner: { + sizes: [[300, 250], [300, 600]], + } + }, + bids: [{ + bidder: 'r2b2', + params: { + pid: 'selfpromo' + } + }] + } + ]; +``` +## Rendering + +Our adapter can feature a custom renderer specifically for display ads, tailored to enhance ad presentation and functionality. This is particularly beneficial for non-standard ad formats that require more complex logic. It's important to note that our rendering process operates outside of SafeFrames. For additional information, not limited to rendering aspects, please feel free to contact us at partner@r2b2.io diff --git a/test/spec/modules/r2b2BidAdapter_spec.js b/test/spec/modules/r2b2BidAdapter_spec.js new file mode 100644 index 00000000000..b94b400a71d --- /dev/null +++ b/test/spec/modules/r2b2BidAdapter_spec.js @@ -0,0 +1,689 @@ +import {expect} from 'chai'; +import {spec, internal as r2b2, internal} from 'modules/r2b2BidAdapter.js'; +import * as utils from '../../../src/utils'; +import 'modules/schain.js'; +import 'modules/userId/index.js'; + +function encodePlacementIds (ids) { + return btoa(JSON.stringify(ids)); +} + +describe('R2B2 adapter', function () { + let serverResponse, requestForInterpretResponse; + let bidderRequest; + let bids = []; + let gdprConsent = { + gdprApplies: true, + consentString: 'consent-string', + }; + let schain = { + ver: '1.0', + complete: 1, + nodes: [{ + asi: 'example.com', + sid: '00001', + hp: 1 + }] + }; + const usPrivacyString = '1YNN'; + const impId = 'impID'; + const price = 10.6; + const ad = 'adm'; + const creativeId = 'creativeID'; + const cid = 41849; + const cdid = 595121; + const unitCode = 'unitCode'; + const bidId1 = '1'; + const bidId2 = '2'; + const bidId3 = '3'; + const bidId4 = '4'; + const bidId5 = '5'; + const bidWonUrl = 'url1'; + const setTargetingUrl = 'url2'; + const bidder = 'r2b2'; + const foreignBidder = 'differentBidder'; + const id1 = { pid: 'd/g/p' }; + const id1Object = { d: 'd', g: 'g', p: 'p', m: 0 }; + const id2 = { pid: 'd/g/p/1' }; + const id2Object = { d: 'd', g: 'g', p: 'p', m: 1 }; + const badId = { pid: 'd/g/' }; + const bid1 = { bidId: bidId1, bidder, params: [ id1 ] }; + const bid2 = { bidId: bidId2, bidder, params: [ id2 ] }; + const bidWithBadSetup = { bidId: bidId3, bidder, params: [ badId ] }; + const bidForeign1 = { bidId: bidId4, bidder: foreignBidder, params: [ { id: 'abc' } ] }; + const bidForeign2 = { bidId: bidId5, bidder: foreignBidder, params: [ { id: 'xyz' } ] }; + const fakeTime = 1234567890; + const cacheBusterRegex = /[\?&]cb=([^&]+)/; + let bidStub, time; + + beforeEach(function () { + bids = [{ + bidder: 'r2b2', + params: { + pid: 'example.com/generic/300x250/1' + }, + mediaTypes: { + banner: { + sizes: [ + [300, 250] + ] + } + }, + adUnitCode: unitCode, + transactionId: '29c408b9-65ce-48b1-9167-18a57791f908', + sizes: [ + [300, 250] + ], + bidId: '20917a54ee9858', + bidderRequestId: '15270d403778d', + auctionId: '36acef1b-f635-4f57-b693-5cc55ee16346', + src: 'client', + ortb2: { + regs: { + ext: { + gdpr: 1, + us_privacy: '1YYY' + } + }, + user: { + ext: { + consent: 'consent-string' + } + }, + site: {}, + device: {} + }, + schain + }, { + bidder: 'r2b2', + params: { + pid: 'example.com/generic/300x600/0' + }, + mediaTypes: { + banner: { + sizes: [ + [300, 600] + ] + } + }, + adUnitCode: unitCode, + transactionId: '29c408b9-65ce-48b1-9167-18a57791f908', + sizes: [ + [300, 600] + ], + bidId: '3dd53d30c691fe', + bidderRequestId: '15270d403778d', + auctionId: '36acef1b-f635-4f57-b693-5cc55ee16346', + src: 'client', + ortb2: { + regs: { + ext: { + gdpr: 1, + us_privacy: '1YYY' + } + }, + user: { + ext: { + consent: 'consent-string' + } + }, + site: {}, + device: {} + }, + schain + }]; + bidderRequest = { + bidderCode: 'r2b2', + auctionId: '36acef1b-f635-4f57-b693-5cc55ee16346', + bidderRequestId: '15270d403778d', + bids: bids, + ortb2: { + regs: { + ext: { + gdpr: 1, + us_privacy: '1YYY' + } + }, + user: { + ext: { + consent: 'consent-string' + } + }, + site: {}, + device: {} + }, + gdprConsent: { + consentString: 'consent-string', + vendorData: {}, + gdprApplies: true, + apiVersion: 2 + }, + uspConsent: '1YYY', + }; + serverResponse = { + id: 'a66a6e32-2a7d-4ed3-bb13-6f3c9bdcf6a1', + seatbid: [{ + bid: [{ + id: '4756cc9e9b504fd0bd39fdd594506545', + impid: impId, + price: price, + adm: ad, + crid: creativeId, + w: 300, + h: 250, + ext: { + prebid: { + meta: { + adaptercode: 'r2b2' + }, + type: 'banner' + }, + r2b2: { + cdid: cdid, + cid: cid, + useRenderer: true + } + } + }], + seat: 'seat' + }] + }; + requestForInterpretResponse = { + data: { + imp: [ + {id: impId} + ] + }, + bids + }; + }); + + describe('isBidRequestValid', function () { + let bid = {}; + + it('should return false when missing required "pid" param', function () { + bid.params = {random: 'param'}; + expect(spec.isBidRequestValid(bid)).to.equal(false); + bid.params = {d: 'd', g: 'g', p: 'p', m: 1}; + expect(spec.isBidRequestValid(bid)).to.equal(false) + }); + + it('should return false when "pid" is malformed', function () { + bid.params = {pid: 'pid'}; + expect(spec.isBidRequestValid(bid)).to.equal(false); + bid.params = {pid: '///'}; + expect(spec.isBidRequestValid(bid)).to.equal(false); + bid.params = {pid: '/g/p/m'}; + expect(spec.isBidRequestValid(bid)).to.equal(false); + bid.params = {pid: 'd//p/m'}; + expect(spec.isBidRequestValid(bid)).to.equal(false); + bid.params = {pid: 'd/g//m'}; + expect(spec.isBidRequestValid(bid)).to.equal(false); + bid.params = {pid: 'd/p/'}; + expect(spec.isBidRequestValid(bid)).to.equal(false); + bid.params = {pid: 'd/g/p/m/t'}; + expect(spec.isBidRequestValid(bid)).to.equal(false); + }); + + it('should return true when "pid" is a correct dgpm', function () { + bid.params = {pid: 'd/g/p/m'}; + expect(spec.isBidRequestValid(bid)).to.equal(true); + }); + it('should return true when type is blank', function () { + bid.params = {pid: 'd/g/p/'}; + expect(spec.isBidRequestValid(bid)).to.equal(true); + }); + it('should return true when type is missing', function () { + bid.params = {pid: 'd/g/p'}; + expect(spec.isBidRequestValid(bid)).to.equal(true); + }); + it('should return true when "pid" is a number', function () { + bid.params = {pid: 12356}; + expect(spec.isBidRequestValid(bid)).to.equal(true); + }); + it('should return true when "pid" is a numeric string', function () { + bid.params = {pid: '12356'}; + expect(spec.isBidRequestValid(bid)).to.equal(true); + }); + it('should return true for selfpromo unit', function () { + bid.params = {pid: 'selfpromo'}; + expect(spec.isBidRequestValid(bid)).to.equal(true) + }); + }); + + describe('buildRequests', function () { + beforeEach(function () { + r2b2.placementsToSync = []; + r2b2.mappedParams = {}; + }); + + it('should set correct request method and url and pass bids', function () { + let requests = spec.buildRequests([bids[0]], bidderRequest); + expect(requests).to.be.an('array').that.has.lengthOf(1); + let request = requests[0] + expect(request.method).to.equal('POST'); + expect(request.url).to.equal('https://hb.r2b2.cz/openrtb2/bid'); + expect(request.data).to.be.an('object'); + expect(request.bids).to.deep.equal(bids); + }); + + it('should pass correct parameters', function () { + let requests = spec.buildRequests([bids[0]], bidderRequest); + let {data} = requests[0]; + let {imp, device, site, source, ext, cur, test} = data; + expect(imp).to.be.an('array').that.has.lengthOf(1); + expect(device).to.be.an('object'); + expect(site).to.be.an('object'); + expect(source).to.be.an('object'); + expect(cur).to.deep.equal(['USD']); + expect(ext.version).to.equal('1.0.0'); + expect(test).to.equal(0); + }); + + it('should pass correct imp', function () { + let requests = spec.buildRequests([bids[0]], bidderRequest); + let {data} = requests[0]; + let {imp} = data; + expect(imp).to.be.an('array').that.has.lengthOf(1); + expect(imp[0]).to.be.an('object'); + let bid = imp[0]; + expect(bid.id).to.equal('20917a54ee9858'); + expect(bid.banner).to.deep.equal({topframe: 0, format: [{w: 300, h: 250}]}); + expect(bid.ext).to.be.an('object'); + expect(bid.ext.r2b2).to.deep.equal({d: 'example.com', g: 'generic', p: '300x250', m: 1}); + }); + + it('should map type correctly', function () { + let result, bid; + let requestWithId = function(id) { + let b = bids[0]; + b.params.pid = id; + let passedBids = [b]; + bidderRequest.bids = passedBids; + return spec.buildRequests(passedBids, bidderRequest); + }; + + result = requestWithId('example.com/generic/300x250/mobile'); + bid = result[0].data.imp[0]; + expect(bid.ext.r2b2.m).to.be.a('number').that.is.equal(1); + + result = requestWithId('example.com/generic/300x250/desktop'); + bid = result[0].data.imp[0]; + expect(bid.ext.r2b2.m).to.be.a('number').that.is.equal(0); + + result = requestWithId('example.com/generic/300x250/1'); + bid = result[0].data.imp[0]; + expect(bid.ext.r2b2.m).to.be.a('number').that.is.equal(1); + + result = requestWithId('example.com/generic/300x250/0'); + bid = result[0].data.imp[0]; + expect(bid.ext.r2b2.m).to.be.a('number').that.is.equal(0); + + result = requestWithId('example.com/generic/300x250/m'); + bid = result[0].data.imp[0]; + expect(bid.ext.r2b2.m).to.be.a('number').that.is.equal(1); + + result = requestWithId('example.com/generic/300x250'); + bid = result[0].data.imp[0]; + expect(bid.ext.r2b2.m).to.be.a('number').that.is.equal(0); + }); + + it('should pass correct parameters for test ad', function () { + let testAdBid = bids[0]; + testAdBid.params = {pid: 'selfpromo'}; + let requests = spec.buildRequests([testAdBid], bidderRequest); + let {data} = requests[0]; + let {imp} = data; + expect(imp).to.be.an('array').that.has.lengthOf(1); + expect(imp[0]).to.be.an('object'); + let bid = imp[0]; + expect(bid.ext).to.be.an('object'); + expect(bid.ext.r2b2).to.deep.equal({d: 'test', g: 'test', p: 'selfpromo', m: 0, 'selfpromo': 1}); + }); + + it('should pass multiple bids', function () { + let requests = spec.buildRequests(bids, bidderRequest); + expect(requests).to.be.an('array').that.has.lengthOf(1); + let {data} = requests[0]; + let {imp} = data; + expect(imp).to.be.an('array').that.has.lengthOf(bids.length); + let bid1 = imp[0]; + expect(bid1.ext.r2b2).to.deep.equal({d: 'example.com', g: 'generic', p: '300x250', m: 1}); + let bid2 = imp[1]; + expect(bid2.ext.r2b2).to.deep.equal({d: 'example.com', g: 'generic', p: '300x600', m: 0}); + }); + + it('should set up internal variables', function () { + let requests = spec.buildRequests(bids, bidderRequest); + let bid1Id = bids[0].bidId; + let bid2Id = bids[1].bidId; + expect(r2b2.placementsToSync).to.be.an('array').that.has.lengthOf(2); + expect(r2b2.mappedParams).to.have.property(bid1Id); + expect(r2b2.mappedParams[bid1Id]).to.deep.equal({d: 'example.com', g: 'generic', p: '300x250', m: 1, pid: 'example.com/generic/300x250/1'}); + expect(r2b2.mappedParams).to.have.property(bid2Id); + expect(r2b2.mappedParams[bid2Id]).to.deep.equal({d: 'example.com', g: 'generic', p: '300x600', m: 0, pid: 'example.com/generic/300x600/0'}); + }); + + it('should pass gdpr properties', function () { + let requests = spec.buildRequests(bids, bidderRequest); + let {data} = requests[0]; + let {user, regs} = data; + expect(user).to.be.an('object').that.has.property('ext'); + expect(regs).to.be.an('object').that.has.property('ext'); + expect(user.ext.consent).to.equal('consent-string'); + expect(regs.ext.gdpr).to.equal(1); + }); + + it('should pass us privacy properties', function () { + let requests = spec.buildRequests(bids, bidderRequest); + let {data} = requests[0]; + let {regs} = data; + expect(regs).to.be.an('object').that.has.property('ext'); + expect(regs.ext.us_privacy).to.equal('1YYY'); + }); + + it('should pass supply chain', function () { + let requests = spec.buildRequests(bids, bidderRequest); + let {data} = requests[0]; + let {source} = data; + expect(source).to.be.an('object').that.has.property('ext'); + expect(source.ext.schain).to.deep.equal({ + complete: 1, + nodes: [ + {asi: 'example.com', hp: 1, sid: '00001'} + ], + ver: '1.0' + }) + }); + + it('should pass extended ids', function () { + let eidsArray = [ + { + source: 'adserver.org', + uids: [ + { + atype: 1, + ext: { + rtiPartner: 'TDID', + }, + id: 'TTD_ID_FROM_USER_ID_MODULE', + }, + ], + }, + { + source: 'pubcid.org', + uids: [ + { + atype: 1, + id: 'pubCommonId_FROM_USER_ID_MODULE', + }, + ], + }, + ]; + bids[0].userIdAsEids = eidsArray; + let requests = spec.buildRequests(bids, bidderRequest); + let request = requests[0]; + let eids = request.data.user.ext.eids; + + expect(eids).to.deep.equal(eidsArray); + }); + }); + + describe('interpretResponse', function () { + it('should respond with empty response when there are no bids', function () { + let result = spec.interpretResponse({ body: {} }, {}); + expect(result).to.be.an('array').that.has.lengthOf(0); + result = spec.interpretResponse({ body: { seatbid: [] } }, {}); + expect(result).to.be.an('array').that.has.lengthOf(0); + result = spec.interpretResponse({ body: { seatbid: [ {} ] } }, {}); + expect(result).to.be.an('array').that.has.lengthOf(0); + result = spec.interpretResponse({ body: { seatbid: [ { bids: [] } ] } }, {}); + expect(result).to.be.an('array').that.has.lengthOf(0); + }); + + it('should map params correctly', function () { + let result = spec.interpretResponse({ body: serverResponse }, requestForInterpretResponse); + expect(result).to.be.an('array').that.has.lengthOf(1); + let bid = result[0]; + expect(bid.requestId).to.equal(impId); + expect(bid.cpm).to.equal(price); + expect(bid.ad).to.equal(ad); + expect(bid.currency).to.equal('USD'); + expect(bid.mediaType).to.equal('banner'); + expect(bid.width).to.equal(300); + expect(bid.height).to.equal(250); + expect(bid.netRevenue).to.equal(true); + expect(bid.ttl).to.equal(360); + expect(bid.creativeId).to.equal(creativeId); + }); + + it('should set up renderer on bid', function () { + let result = spec.interpretResponse({ body: serverResponse }, requestForInterpretResponse); + expect(result).to.be.an('array').that.has.lengthOf(1); + let bid = result[0]; + expect(bid.renderer).to.be.an('object'); + expect(bid.renderer).to.have.property('render').that.is.a('function'); + expect(bid.renderer).to.have.property('url').that.is.a('string'); + }); + + it('should map ext params correctly', function() { + let dgpm = {something: 'something'}; + r2b2.mappedParams = {}; + r2b2.mappedParams[impId] = dgpm; + let result = spec.interpretResponse({ body: serverResponse }, requestForInterpretResponse); + expect(result).to.be.an('array').that.has.lengthOf(1); + let bid = result[0]; + expect(bid.ext).to.be.an('object'); + let { ext } = bid; + expect(ext.dgpm).to.deep.equal(dgpm); + expect(ext.cid).to.equal(cid); + expect(ext.cdid).to.equal(cdid); + expect(ext.adUnit).to.equal(unitCode); + expect(ext.mediaType).to.deep.equal({ + type: 'banner', + settings: { + chd: null, + width: 300, + height: 250, + ad: { + type: 'content', + data: ad + } + } + }); + }); + + it('should handle multiple bids', function() { + const impId2 = '123456'; + const price2 = 12; + const ad2 = 'gaeouho'; + const w2 = 300; + const h2 = 600; + let b = serverResponse.seatbid[0].bid[0]; + let b2 = Object.assign({}, b); + b2.impid = impId2; + b2.price = price2; + b2.adm = ad2; + b2.w = w2; + b2.h = h2; + serverResponse.seatbid[0].bid.push(b2); + requestForInterpretResponse.data.imp.push({id: impId2}); + let result = spec.interpretResponse({ body: serverResponse }, requestForInterpretResponse); + expect(result).to.be.an('array').that.has.lengthOf(2); + let firstBid = result[0]; + let secondBid = result[1]; + expect(firstBid.requestId).to.equal(impId); + expect(firstBid.ad).to.equal(ad); + expect(firstBid.cpm).to.equal(price); + expect(firstBid.width).to.equal(300); + expect(firstBid.height).to.equal(250); + expect(secondBid.requestId).to.equal(impId2); + expect(secondBid.ad).to.equal(ad2); + expect(secondBid.cpm).to.equal(price2); + expect(secondBid.width).to.equal(w2); + expect(secondBid.height).to.equal(h2); + }); + }); + + describe('getUserSyncs', function() { + const syncOptions = { + iframeEnabled: true, + pixelEnabled: true + }; + + it('should return an array with a sync for all bids', function() { + r2b2.placementsToSync = [id1Object, id2Object]; + const expectedEncodedIds = encodePlacementIds(r2b2.placementsToSync); + const syncs = spec.getUserSyncs(syncOptions); + expect(syncs).to.be.an('array').that.has.lengthOf(1); + const sync = syncs[0]; + expect(sync).to.be.an('object'); + expect(sync.type).to.equal('iframe'); + expect(sync.url).to.include(`?p=${expectedEncodedIds}`); + }); + + it('should return the sync and include gdpr and usp parameters in the url', function() { + r2b2.placementsToSync = [id1Object, id2Object]; + const syncs = spec.getUserSyncs(syncOptions, {}, gdprConsent, usPrivacyString); + const sync = syncs[0]; + expect(sync).to.be.an('object'); + expect(sync.url).to.include(`&gdpr=1`); + expect(sync.url).to.include(`&gdpr_consent=${gdprConsent.consentString}`); + expect(sync.url).to.include(`&us_privacy=${usPrivacyString}`); + }); + }); + + describe('events', function() { + beforeEach(function() { + time = sinon.useFakeTimers(fakeTime); + sinon.stub(utils, 'triggerPixel'); + r2b2.mappedParams = {}; + r2b2.mappedParams[bidId1] = id1Object; + r2b2.mappedParams[bidId2] = id2Object; + bidStub = { + adserverTargeting: { hb_bidder: bidder, hb_pb: '10.00', hb_size: '300x300' }, + cpm: 10, + currency: 'USD', + ext: { + dgpm: { d: 'r2b2.cz', g: 'generic', m: 1, p: '300x300', pid: 'r2b2.cz/generic/300x300/1' } + }, + params: [ { pid: 'r2b2.cz/generic/300x300/1' } ], + }; + }); + afterEach(function() { + utils.triggerPixel.restore(); + time.restore(); + }); + + describe('onBidWon', function () { + it('exists and is a function', () => { + expect(spec.onBidWon).to.exist.and.to.be.a('function'); + }); + it('should return nothing and trigger a pixel with passed url', function () { + bidStub.ext.events = { + onBidWon: bidWonUrl, + onSetTargeting: setTargetingUrl + }; + const response = spec.onBidWon(bidStub); + expect(response).to.be.an('undefined'); + expect(utils.triggerPixel.called).to.equal(true); + expect(utils.triggerPixel.callCount).to.equal(1); + expect(utils.triggerPixel.calledWithMatch(bidWonUrl)).to.equal(true); + }); + it('should not trigger a pixel if url is not available', function () { + bidStub.ext.events = null; + spec.onBidWon(bidStub); + expect(utils.triggerPixel.callCount).to.equal(0); + bidStub.ext.events = { + onBidWon: '', + onSetTargeting: '', + }; + spec.onBidWon(bidStub); + expect(utils.triggerPixel.callCount).to.equal(0); + }); + }); + + describe('onSetTargeting', function () { + it('exists and is a function', () => { + expect(spec.onSetTargeting).to.exist.and.to.be.a('function'); + }); + it('should return nothing and trigger a pixel with passed url', function () { + bidStub.ext.events = { + onBidWon: bidWonUrl, + onSetTargeting: setTargetingUrl + }; + const response = spec.onSetTargeting(bidStub); + expect(response).to.be.an('undefined'); + expect(utils.triggerPixel.called).to.equal(true); + expect(utils.triggerPixel.callCount).to.equal(1); + expect(utils.triggerPixel.calledWithMatch(setTargetingUrl)).to.equal(true); + }); + it('should not trigger a pixel if url is not available', function () { + bidStub.ext.events = null; + spec.onSetTargeting(bidStub); + expect(utils.triggerPixel.callCount).to.equal(0); + bidStub.ext.events = { + onBidWon: '', + onSetTargeting: '', + }; + spec.onSetTargeting(bidStub); + expect(utils.triggerPixel.callCount).to.equal(0); + }); + }); + + describe('onTimeout', function () { + it('exists and is a function', () => { + expect(spec.onTimeout).to.exist.and.to.be.a('function'); + }); + it('should return nothing and trigger a pixel', function () { + const bids = [bid1, bid2]; + const response = spec.onTimeout(bids); + expect(response).to.be.an('undefined'); + expect(utils.triggerPixel.callCount).to.equal(1); + }); + it('should not trigger a pixel if no bids available', function () { + const bids = []; + spec.onTimeout(bids); + expect(utils.triggerPixel.callCount).to.equal(0); + }); + it('should trigger a pixel with correct ids and a cache buster', function () { + const bids = [bid1, bidForeign1, bidForeign2, bid2, bidWithBadSetup]; + const expectedIds = [id1Object, id2Object]; + const expectedEncodedIds = encodePlacementIds(expectedIds); + spec.onTimeout(bids); + expect(utils.triggerPixel.callCount).to.equal(1); + const triggeredUrl = utils.triggerPixel.args[0][0]; + expect(triggeredUrl).to.include(`p=${expectedEncodedIds}`); + expect(triggeredUrl.match(cacheBusterRegex)).to.exist; + }); + }); + + describe('onBidderError', function () { + it('exists and is a function', () => { + expect(spec.onBidderError).to.exist.and.to.be.a('function'); + }); + it('should return nothing and trigger a pixel', function () { + const bidderRequest = { bids: [bid1, bid2] }; + const response = spec.onBidderError({ bidderRequest }); + expect(response).to.be.an('undefined') + expect(utils.triggerPixel.callCount).to.equal(1); + }); + it('should not trigger a pixel if no bids available', function () { + const bidderRequest = { bids: [] }; + spec.onBidderError({ bidderRequest }); + expect(utils.triggerPixel.callCount).to.equal(0); + }); + it('should call triggerEvent with correct ids and a cache buster', function () { + const bids = [bid1, bid2, bidWithBadSetup] + const bidderRequest = { bids }; + const expectedIds = [id1Object, id2Object]; + const expectedEncodedIds = encodePlacementIds(expectedIds); + spec.onBidderError({ bidderRequest }); + expect(utils.triggerPixel.callCount).to.equal(1); + const triggeredUrl = utils.triggerPixel.args[0][0]; + expect(triggeredUrl).to.include(`p=${expectedEncodedIds}`); + expect(triggeredUrl.match(cacheBusterRegex)).to.exist; + }); + }); + }); +}); From f5e19885057300c645764eb421f0013b9b172810 Mon Sep 17 00:00:00 2001 From: Denis <7009699+someden@users.noreply.github.com> Date: Wed, 27 Dec 2023 20:20:51 +0300 Subject: [PATCH 092/246] Core: fix headers in ortbConverter readme (#10874) --- libraries/ortbConverter/README.md | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/libraries/ortbConverter/README.md b/libraries/ortbConverter/README.md index 31f56b4c754..751971eebdc 100644 --- a/libraries/ortbConverter/README.md +++ b/libraries/ortbConverter/README.md @@ -80,8 +80,7 @@ However, there are two restrictions (to avoid them, use the [other customization ) ``` - -### Fine grained customization - imp, request, bidResponse, response +### Fine grained customization - imp, request, bidResponse, response When invoked, `toORTB({bidRequests, bidderRequest})` first loops through each request in `bidRequests`, converting them into ORTB `imp` objects. It then packages them into a single ORTB request, adding other parameters that are not imp-specific (such as for example `request.tmax`). @@ -91,7 +90,7 @@ a single return value. You can customize each of these steps using the `ortbConverter` arguments `imp`, `request`, `bidResponse` and `response`: -### Customizing imps: `imp(buildImp, bidRequest, context)` +### Customizing imps: `imp(buildImp, bidRequest, context)` Invoked once for each input `bidRequest`; should return the ORTB `imp` object to include in the request. The arguments are: @@ -101,7 +100,7 @@ The arguments are: - `context`: a [context object](#context) that contains at least: - `bidderRequest`: the `bidderRequest` argument passed to `toORTB`. -#### Example: attaching custom bid params +#### Example: attaching custom bid params ```javascript const converter = ortbConverter({ @@ -351,7 +350,7 @@ const converter = ortbConverter({ - the `context` argument of `ortbConverter`: e.g. `ortbConverter({context: {ttl: 30}})`. This will set `context.ttl = 30` globally for the converter. - the `context` argument of `toORTB`: e.g. `converter.toORTB({bidRequests, bidderRequest, context: {ttl: 30}})`. This will set `context.ttl = 30` only for this request. -### Special `context` properties +### Special `context` properties For ease of use, the conversion logic gives special meaning to some context properties: From c6b45c41367f58e5d26f176c509215541e44dcf1 Mon Sep 17 00:00:00 2001 From: geoffray-viously <95097046+geoffray-viously@users.noreply.github.com> Date: Wed, 27 Dec 2023 18:22:05 +0100 Subject: [PATCH 093/246] SparteoBidAdapter: Add UserSync (#10822) --- modules/sparteoBidAdapter.js | 42 +++++++++++++++++---- test/spec/modules/sparteoBidAdapter_spec.js | 26 +++++++++++++ 2 files changed, 61 insertions(+), 7 deletions(-) diff --git a/modules/sparteoBidAdapter.js b/modules/sparteoBidAdapter.js index 93353c5dda7..bfb527d46f2 100644 --- a/modules/sparteoBidAdapter.js +++ b/modules/sparteoBidAdapter.js @@ -8,6 +8,8 @@ const GVLID = 1028; const TTL = 60; const HTTP_METHOD = 'POST'; const REQUEST_URL = 'https://bid.sparteo.com/auction'; +const USER_SYNC_URL_IFRAME = 'https://sync.sparteo.com/sync/iframe.html?from=prebidjs'; +let isSynced = window.sparteoCrossfire?.started || false; const converter = ortbConverter({ context: { @@ -60,7 +62,7 @@ export const spec = { * @param {BidRequest} bid The bid params to validate. * @return boolean True if this is a valid bid, and false otherwise. */ - isBidRequestValid: function(bid) { + isBidRequestValid: function (bid) { let bannerParams = deepAccess(bid, 'mediaTypes.banner'); let videoParams = deepAccess(bid, 'mediaTypes.video'); @@ -106,7 +108,7 @@ export const spec = { return true; }, - buildRequests: function(bidRequests, bidderRequest) { + buildRequests: function (bidRequests, bidderRequest) { const payload = converter.toORTB({bidRequests, bidderRequest}) return { @@ -116,23 +118,49 @@ export const spec = { }; }, - interpretResponse: function(serverResponse, requests) { + interpretResponse: function (serverResponse, requests) { const bids = converter.fromORTB({response: serverResponse.body, request: requests.data}).bids; return bids; }, - getUserSyncs: function(syncOptions, serverResponses, gdprConsent, uspConsent) {}, + getUserSyncs: function (syncOptions, serverResponses, gdprConsent, uspConsent) { + let syncurl = ''; - onTimeout: function(timeoutData) {}, + if (!isSynced && !window.sparteoCrossfire?.started) { + // Attaching GDPR Consent Params in UserSync url + if (gdprConsent) { + syncurl += '&gdpr=' + (gdprConsent.gdprApplies ? 1 : 0); + syncurl += '&gdpr_consent=' + encodeURIComponent(gdprConsent.consentString || ''); + } + if (uspConsent && uspConsent.consentString) { + syncurl += `&usp_consent=${uspConsent.consentString}`; + } + + if (syncOptions.iframeEnabled) { + isSynced = true; + + window.sparteoCrossfire = { + started: true + }; + + return [{ + type: 'iframe', + url: USER_SYNC_URL_IFRAME + syncurl + }]; + } + } + }, + + onTimeout: function (timeoutData) {}, - onBidWon: function(bid) { + onBidWon: function (bid) { if (bid && bid.nurl) { triggerPixel(bid.nurl, null); } }, - onSetTargeting: function(bid) {} + onSetTargeting: function (bid) {} }; registerBidder(spec); diff --git a/test/spec/modules/sparteoBidAdapter_spec.js b/test/spec/modules/sparteoBidAdapter_spec.js index 71191b44a29..293f7da30a1 100644 --- a/test/spec/modules/sparteoBidAdapter_spec.js +++ b/test/spec/modules/sparteoBidAdapter_spec.js @@ -6,6 +6,7 @@ const CURRENCY = 'EUR'; const TTL = 60; const HTTP_METHOD = 'POST'; const REQUEST_URL = 'https://bid.sparteo.com/auction'; +const USER_SYNC_URL_IFRAME = 'https://sync.sparteo.com/sync/iframe.html?from=prebidjs'; const VALID_BID_BANNER = { bidder: 'sparteo', @@ -438,4 +439,29 @@ describe('SparteoAdapter', function () { }); }); }); + + describe('getUserSyncs', function() { + describe('Check methods succeed', function () { + it('should return the sync url', function() { + const syncOptions = { + 'iframeEnabled': true, + 'pixelEnabled': false + }; + const gdprConsent = { + gdprApplies: 1, + consentString: 'tcfv2' + }; + const uspConsent = { + consentString: '1Y---' + }; + + const syncUrls = [{ + type: 'iframe', + url: USER_SYNC_URL_IFRAME + '&gdpr=1&gdpr_consent=tcfv2&usp_consent=1Y---' + }]; + + expect(adapter.getUserSyncs(syncOptions, null, gdprConsent, uspConsent)).to.deep.equal(syncUrls); + }); + }); + }); }); From f625c1e916986275297f53c92c234468834e69b3 Mon Sep 17 00:00:00 2001 From: Konstantin Korobkov Date: Wed, 27 Dec 2023 18:22:50 +0100 Subject: [PATCH 094/246] Yandex Bid Adapter: Add rtt (roud trip time) tracking via nurl (#10846) * Add rtt (roud trip time) tracking via nurl in yandexBidAdapter * Yandex Bid Adapter: Fix let -> const --------- Co-authored-by: Konstantin Korobkov --- modules/yandexBidAdapter.js | 22 ++++++++- test/spec/modules/yandexBidAdapter_spec.js | 55 ++++++++++++++++++++-- 2 files changed, 73 insertions(+), 4 deletions(-) diff --git a/modules/yandexBidAdapter.js b/modules/yandexBidAdapter.js index 9ca989b2259..4a7f9114a17 100644 --- a/modules/yandexBidAdapter.js +++ b/modules/yandexBidAdapter.js @@ -147,7 +147,7 @@ export const spec = { interpretResponse: interpretResponse, onBidWon: function (bid) { - const nurl = bid['nurl']; + const nurl = addRTT(bid['nurl'], bid.timeToRespond); if (!nurl) { return; @@ -385,4 +385,24 @@ function replaceAuctionPrice(url, price, currency) { .replace(/\${AUCTION_CURRENCY}/, currency); } +function addRTT(url, rtt) { + if (!url) return; + + if (url.indexOf(`\${RTT}`) > -1) { + return url.replace(/\${RTT}/, rtt ?? -1); + } + + const urlObj = new URL(url); + + if (Number.isInteger(rtt)) { + urlObj.searchParams.set('rtt', rtt); + } else { + urlObj.searchParams.delete('rtt'); + } + + url = urlObj.toString(); + + return url; +} + registerBidder(spec); diff --git a/test/spec/modules/yandexBidAdapter_spec.js b/test/spec/modules/yandexBidAdapter_spec.js index f14e8df6c09..d5cfc26b31a 100644 --- a/test/spec/modules/yandexBidAdapter_spec.js +++ b/test/spec/modules/yandexBidAdapter_spec.js @@ -1,6 +1,6 @@ import { assert, expect } from 'chai'; import { spec, NATIVE_ASSETS } from 'modules/yandexBidAdapter.js'; -import { parseUrl } from 'src/utils.js'; +import * as utils from 'src/utils.js'; import { BANNER, NATIVE } from '../../../src/mediaTypes'; import { config } from '../../../src/config'; @@ -71,7 +71,7 @@ describe('Yandex adapter', function () { expect(method).to.equal('POST'); - const parsedRequestUrl = parseUrl(url); + const parsedRequestUrl = utils.parseUrl(url); const { search: query } = parsedRequestUrl expect(parsedRequestUrl.hostname).to.equal('bs.yandex.ru'); @@ -100,7 +100,7 @@ describe('Yandex adapter', function () { const bannerRequest = getBidRequest(); const requests = spec.buildRequests([bannerRequest], bidderRequest); const { url } = requests[0]; - const parsedRequestUrl = parseUrl(url); + const parsedRequestUrl = utils.parseUrl(url); const { search: query } = parsedRequestUrl expect(query['ssp-cur']).to.equal('USD'); @@ -478,6 +478,55 @@ describe('Yandex adapter', function () { }); }); }); + + 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() { + const result = spec.onBidWon({}); + expect(utils.triggerPixel.callCount).to.equal(0) + }) + + it('Should trigger pixel if bid has nurl', function() { + const result = spec.onBidWon({ + nurl: 'https://example.com/some-tracker', + timeToRespond: 378, + }); + expect(utils.triggerPixel.callCount).to.equal(1) + expect(utils.triggerPixel.getCall(0).args[0]).to.equal('https://example.com/some-tracker?rtt=378') + }) + + it('Should trigger pixel if bid has nurl with path & params', function() { + const result = spec.onBidWon({ + nurl: 'https://example.com/some-tracker/abcdxyz?param1=1¶m2=2', + timeToRespond: 378, + }); + expect(utils.triggerPixel.callCount).to.equal(1) + expect(utils.triggerPixel.getCall(0).args[0]).to.equal('https://example.com/some-tracker/abcdxyz?param1=1¶m2=2&rtt=378') + }) + + it('Should trigger pixel if bid has nurl with path & params and rtt macros', function() { + const result = spec.onBidWon({ + nurl: 'https://example.com/some-tracker/abcdxyz?param1=1¶m2=2&custom-rtt=${RTT}', + timeToRespond: 378, + }); + expect(utils.triggerPixel.callCount).to.equal(1) + expect(utils.triggerPixel.getCall(0).args[0]).to.equal('https://example.com/some-tracker/abcdxyz?param1=1¶m2=2&custom-rtt=378') + }) + + it('Should trigger pixel if bid has nurl and there is no timeToRespond param, but has rtt macros in nurl', function() { + const result = spec.onBidWon({ + nurl: 'https://example.com/some-tracker/abcdxyz?param1=1¶m2=2&custom-rtt=${RTT}', + }); + expect(utils.triggerPixel.callCount).to.equal(1) + expect(utils.triggerPixel.getCall(0).args[0]).to.equal('https://example.com/some-tracker/abcdxyz?param1=1¶m2=2&custom-rtt=-1') + }) + }) }); function getBidConfig() { From ed48e349c50c7bfdeed61129ea426b87ab714aab Mon Sep 17 00:00:00 2001 From: Fatih Kaya Date: Wed, 27 Dec 2023 20:23:45 +0300 Subject: [PATCH 095/246] AdMatic Bid Adapter : consent management features added (#10813) * Admatic Bidder Adaptor * Update admaticBidAdapter.md * Update admaticBidAdapter.md * remove floor parameter * Update admaticBidAdapter.js * Admatic Bid Adapter: alias and bid floor features activated * Admatic adapter: host param control changed * Alias name changed. * Revert "Admatic adapter: host param control changed" This reverts commit de7ac85981b1ba3ad8c5d1dc95c5dadbdf5b9895. * added alias feature and host param * Revert "added alias feature and host param" This reverts commit 6ec8f4539ea6be403a0d7e08dad5c7a5228f28a1. * Revert "Alias name changed." This reverts commit 661c54f9b2397e8f25c257144d73161e13466281. * Revert "Admatic Bid Adapter: alias and bid floor features activated" This reverts commit 7a2e0e29c49e2f876b68aafe886b336fe2fe6fcb. * Revert "Update admaticBidAdapter.js" This reverts commit 7a845b7151bbb08addfb58ea9bd5b44167cc8a4e. * Revert "remove floor parameter" This reverts commit 7a23b055ccd4ea23d23e73248e82b21bc6f69d90. * Admatic adapter: host param control && Add new Bidder * Revert "Admatic adapter: host param control && Add new Bidder" This reverts commit 3c797b120c8e0fe2b851381300ac5c4b1f92c6e2. * commit new features * Update admaticBidAdapter.js * updated for coverage * sync updated * Update adloader.js * AdMatic Bidder: development of user sync url * Update admaticBidAdapter.js * Set currency for AdserverCurrency: bug fix * Update admaticBidAdapter.js * update * admatic adapter video params update * Update admaticBidAdapter.js * update * Update admaticBidAdapter.js * update * update * Update admaticBidAdapter_spec.js * Update admaticBidAdapter.js * Update admaticBidAdapter.js * Revert "Update admaticBidAdapter.js" This reverts commit 1216892fe55e5ab24dda8e045ea007ee6bb40ff8. * Revert "Update admaticBidAdapter.js" This reverts commit b1929ece33bb4040a3bcd6b9332b50335356829c. * Revert "Update admaticBidAdapter_spec.js" This reverts commit 1ca659798b0c9b912634b1673e15e54e547b81e7. * Revert "update" This reverts commit 689ce9d21e08c27be49adb35c5fd5205aef5c35c. * Revert "update" This reverts commit f381a453f9389bebd58dcfa719e9ec17f939f338. * Revert "Update admaticBidAdapter.js" This reverts commit 38fd7abec701d8a4750f9e95eaeb40fb67e9f0e6. * Revert "update" This reverts commit a5316e74b612a5b2cd16cf42586334321fc87770. * Revert "Update admaticBidAdapter.js" This reverts commit 60a28cae302b711366dab0bff9f49b11862fb8ee. * Revert "admatic adapter video params update" This reverts commit 31e69e88fd9355e143f736754ac2e47fe49b65b6. * update * Update admaticBidAdapter.js * Update admaticBidAdapter_spec.js * mime_type add * add native adapter * AdMatic Adapter: Consent Management --- modules/admaticBidAdapter.js | 131 ++- test/spec/modules/admaticBidAdapter_spec.js | 1057 ++++++++++++------- 2 files changed, 818 insertions(+), 370 deletions(-) diff --git a/modules/admaticBidAdapter.js b/modules/admaticBidAdapter.js index fc5cf9c8f7b..236114f6396 100644 --- a/modules/admaticBidAdapter.js +++ b/modules/admaticBidAdapter.js @@ -1,4 +1,4 @@ -import {getValue, logError, isEmpty, deepAccess, isArray, getBidIdParameter} from '../src/utils.js'; +import {getValue, formatQS, logError, deepAccess, isArray, getBidIdParameter} 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'; @@ -56,18 +56,16 @@ export const spec = { * @return {ServerRequest} */ buildRequests: (validBidRequests, bidderRequest) => { + const tmax = bidderRequest.timeout; const bids = validBidRequests.map(buildRequestObject); - const blacklist = bidderRequest.ortb2; + const ortb = bidderRequest.ortb2; const networkId = getValue(validBidRequests[0].params, 'networkId'); const host = getValue(validBidRequests[0].params, 'host'); const currency = config.getConfig('currency.adServerCurrency') || 'TRY'; const bidderName = validBidRequests[0].bidder; const payload = { - user: { - ua: navigator.userAgent - }, - blacklist: [], + ortb, site: { page: bidderRequest.refererInfo.page, ref: bidderRequest.refererInfo.page, @@ -80,17 +78,59 @@ export const spec = { ext: { cur: currency, bidder: bidderName - } + }, + schain: {}, + regs: { + ext: { + } + }, + user: { + ext: {} + }, + at: 1, + tmax: parseInt(tmax) }; - if (!isEmpty(blacklist.badv)) { - payload.blacklist = blacklist.badv; - }; + if (bidderRequest && bidderRequest.gdprConsent && bidderRequest.gdprConsent.gdprApplies) { + const consentStr = (bidderRequest.gdprConsent.consentString) + ? bidderRequest.gdprConsent.consentString.replace(/\+/g, '-').replace(/\//g, '_').replace(/=+$/, '') : ''; + const gdpr = bidderRequest.gdprConsent.gdprApplies ? 1 : 0; + payload.regs.ext.gdpr = gdpr; + payload.regs.ext.consent = consentStr; + } + + if (bidderRequest && bidderRequest.coppa) { + payload.regs.ext.coppa = bidderRequest.coppa === true ? 1 : (bidderRequest.coppa === false ? 0 : undefined); + } + + if (bidderRequest && bidderRequest.ortb2?.regs?.gpp) { + payload.regs.ext.gpp = bidderRequest.ortb2?.regs?.gpp; + } + + if (bidderRequest && bidderRequest.ortb2?.regs?.gpp_sid) { + payload.regs.ext.gpp_sid = bidderRequest.ortb2?.regs?.gpp_sid; + } + + if (bidderRequest && bidderRequest.uspConsent) { + payload.regs.ext.uspIab = bidderRequest.uspConsent; + } + + if (validBidRequests[0].schain) { + const schain = mapSchain(validBidRequests[0].schain); + if (schain) { + payload.schain = schain; + } + } + + if (validBidRequests[0].userIdAsEids) { + const eids = { eids: validBidRequests[0].userIdAsEids }; + payload.user.ext = { ...payload.user.ext, ...eids }; + } if (payload) { switch (bidderName) { case 'pixad': - SYNC_URL = 'https://static.pixad.com.tr/sync.html'; + SYNC_URL = 'https://static.cdn.pixad.com.tr/sync.html'; break; default: SYNC_URL = 'https://cdn.serve.admatic.com.tr/showad/sync.html'; @@ -101,12 +141,36 @@ export const spec = { } }, - getUserSyncs: function (syncOptions, responses) { - if (syncOptions.iframeEnabled) { - return [{ + getUserSyncs: function (syncOptions, responses, gdprConsent, uspConsent, gppConsent) { + if (!hasSynced && syncOptions.iframeEnabled) { + // data is only assigned if params are available to pass to syncEndpoint + let params = {}; + + if (gdprConsent) { + if (typeof gdprConsent.gdprApplies === 'boolean') { + params['gdpr'] = Number(gdprConsent.gdprApplies); + } + if (typeof gdprConsent.consentString === 'string') { + params['gdpr_consent'] = gdprConsent.consentString; + } + } + + if (uspConsent) { + params['us_privacy'] = encodeURIComponent(uspConsent); + } + + if (gppConsent?.gppString) { + params['gpp'] = gppConsent.gppString; + params['gpp_sid'] = gppConsent.applicableSections?.toString(); + } + + params = Object.keys(params).length ? `?${formatQS(params)}` : ''; + + hasSynced = true; + return { type: 'iframe', - url: SYNC_URL - }]; + url: SYNC_URL + params + }; } }, @@ -156,6 +220,41 @@ export const spec = { } }; +var hasSynced = false; + +export function resetUserSync() { + hasSynced = false; +} + +/** + * @param {object} schain object set by Publisher + * @returns {object} OpenRTB SupplyChain object + */ +function mapSchain(schain) { + if (!schain) { + return null; + } + if (!validateSchain(schain)) { + logError('AdMatic: required schain params missing'); + return null; + } + return schain; +} + +/** + * @param {object} schain object set by Publisher + * @returns {object} bool + */ +function validateSchain(schain) { + if (!schain.nodes) { + return false; + } + const requiredFields = ['asi', 'sid', 'hp']; + return schain.nodes.every(node => { + return requiredFields.every(field => node[field]); + }); +} + function isUrl(str) { try { URL(str); diff --git a/test/spec/modules/admaticBidAdapter_spec.js b/test/spec/modules/admaticBidAdapter_spec.js index bd409958b1a..b4d84634962 100644 --- a/test/spec/modules/admaticBidAdapter_spec.js +++ b/test/spec/modules/admaticBidAdapter_spec.js @@ -1,12 +1,553 @@ -import {expect} from 'chai'; -import {spec, storage} from 'modules/admaticBidAdapter.js'; -import {newBidder} from 'src/adapters/bidderFactory.js'; -import {getStorageManager} from 'src/storageManager'; +import { expect } from 'chai'; +import { spec } from 'modules/admaticBidAdapter.js'; +import { newBidder } from 'src/adapters/bidderFactory.js'; +import { config } from 'src/config.js'; const ENDPOINT = 'https://layer.serve.admatic.com.tr/pb'; describe('admaticBidAdapter', () => { const adapter = newBidder(spec); + let validRequest = [ { + 'refererInfo': { + 'page': 'https://www.admatic.com.tr', + 'domain': 'https://www.admatic.com.tr', + }, + 'bidder': 'admatic', + 'params': { + 'networkId': 10433394, + 'host': 'layer.serve.admatic.com.tr' + }, + 'ortb2Imp': { 'ext': { 'instl': 1 } }, + 'ortb2': { 'badv': ['admatic.com.tr'] }, + 'mediaTypes': { + 'banner': { + 'sizes': [[300, 250], [728, 90]] + }, + 'native': { + }, + 'video': { + } + }, + getFloor: inputParams => { + if (inputParams.mediaType === BANNER && inputParams.size[0] === 300 && inputParams.size[1] === 250) { + return { + currency: 'USD', + floor: 1.0 + }; + } else if (inputParams.mediaType === BANNER && inputParams.size[0] === 728 && inputParams.size[1] === 90) { + return { + currency: 'USD', + floor: 2.0 + }; + } else if (inputParams.mediaType === VIDEO) { + return { + currency: 'USD', + floor: 1.0 + }; + } else if (inputParams.mediaType === NATIVE) { + return { + currency: 'USD', + floor: 1.0 + }; + } else { + return {} + } + }, + 'schain': { + 'ver': '1.0', + 'complete': 1, + 'nodes': [ + { + 'asi': 'pixad.com.tr', + 'sid': 'px-pub-3000856707', + 'hp': 1 + } + ] + }, + 'at': 1, + 'tmax': 1000, + 'user': { + 'ext': { + 'eids': [ + { + 'source': 'id5-sync.com', + 'uids': [ + { + 'id': '0', + 'atype': 1, + 'ext': { + 'linkType': 0, + 'pba': 'wMh3sAXcnhDq7CfSa6ji1g==' + } + } + ] + }, + { + 'source': 'pubcid.org', + 'uids': [ + { + 'id': '5a49273f-a424-454b-b478-169c3551aa72', + 'atype': 1 + } + ] + } + ] + } + }, + 'ortb': { + 'badv': [], + 'bcat': [], + 'site': { + 'page': 'http://localhost:8888/admatic.html', + 'ref': 'http://localhost:8888', + 'publisher': { + 'name': 'localhost' + } + }, + 'device': { + 'ua': 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/119.0.0.0 Safari/537.36' + } + }, + 'site': { + 'page': 'http://localhost:8888/admatic.html', + 'ref': 'http://localhost:8888', + 'publisher': { + 'name': 'localhost', + 'publisherId': 12321312 + } + }, + 'imp': [ + { + 'size': [ + { + 'w': 300, + 'h': 250 + }, + { + 'w': 728, + 'h': 90 + } + ], + 'mediatype': {}, + 'type': 'banner', + 'id': '2205da7a81846b', + 'floors': { + 'banner': { + '300x250': { 'currency': 'USD', 'floor': 1 }, + '728x90': { 'currency': 'USD', 'floor': 2 } + } + } + }, + { + 'size': [ + { + 'w': 338, + 'h': 280 + } + ], + 'type': 'video', + 'mediatype': { + 'context': 'instream', + 'mimes': [ + 'video/mp4' + ], + 'maxduration': 240, + 'api': [ + 1, + 2 + ], + 'playerSize': [ + [ + 338, + 280 + ] + ], + 'protocols': [ + 1, + 2, + 3, + 4, + 5, + 6, + 7, + 8 + ], + 'skip': 1, + 'playbackmethod': [ + 2 + ], + 'linearity': 1, + 'placement': 2 + }, + 'floors': { + 'video': { + '338x280': { 'currency': 'USD', 'floor': 1 } + } + }, + 'id': '45e86fc7ce7fc93' + }, + { + 'size': [ + { + 'w': 1, + 'h': 1 + } + ], + 'type': 'native', + 'mediatype': { + 'title': { + 'required': true, + 'len': 120 + }, + 'image': { + 'required': true + }, + 'icon': { + 'required': false, + 'sizes': [ + 640, + 480 + ] + }, + 'sponsoredBy': { + 'required': false + }, + 'body': { + 'required': false + }, + 'clickUrl': { + 'required': false + }, + 'displayUrl': { + 'required': false + } + }, + 'ext': { + 'instl': 0, + 'gpid': 'native-INS_b1b1269f-9570-fe3c-9bf4-f187827ec94a', + 'data': { + 'pbadslot': 'native-INS_b1b1269f-9570-fe3c-9bf4-f187827ec94a' + } + }, + 'floors': { + 'native': { + '*': { 'currency': 'USD', 'floor': 1 } + } + }, + 'id': '16e0c8982318f91' + } + ], + 'ext': { + 'cur': 'USD', + 'bidder': 'admatic' + } + } ]; + let bidderRequest = { + 'refererInfo': { + 'page': 'https://www.admatic.com.tr', + 'domain': 'https://www.admatic.com.tr', + }, + 'bidder': 'admatic', + 'params': { + 'networkId': 10433394, + 'host': 'layer.serve.admatic.com.tr' + }, + 'ortb2Imp': { 'ext': { 'instl': 1 } }, + 'ortb2': { 'badv': ['admatic.com.tr'] }, + 'mediaTypes': { + 'banner': { + 'sizes': [[300, 250], [728, 90]] + }, + 'native': { + }, + 'video': { + 'playerSize': [ + 336, + 280 + ] + } + }, + 'userId': { + 'id5id': { + 'uid': '0', + 'ext': { + 'linkType': 0, + 'pba': 'wMh3sAXcnhDq7CfSa6ji1g==' + } + }, + 'pubcid': '5a49273f-a424-454b-b478-169c3551aa72' + }, + 'userIdAsEids': [ + { + 'source': 'id5-sync.com', + 'uids': [ + { + 'id': '0', + 'atype': 1, + 'ext': { + 'linkType': 0, + 'pba': 'wMh3sAXcnhDq7CfSa6ji1g==' + } + } + ] + }, + { + 'source': 'pubcid.org', + 'uids': [ + { + 'id': '5a49273f-a424-454b-b478-169c3551aa72', + 'atype': 1 + } + ] + } + ], + getFloor: inputParams => { + if (inputParams.mediaType === BANNER && inputParams.size[0] === 300 && inputParams.size[1] === 250) { + return { + currency: 'USD', + floor: 1.0 + }; + } else if (inputParams.mediaType === BANNER && inputParams.size[0] === 728 && inputParams.size[1] === 90) { + return { + currency: 'USD', + floor: 2.0 + }; + } else if (inputParams.mediaType === VIDEO) { + return { + currency: 'USD', + floor: 1.0 + }; + } else if (inputParams.mediaType === NATIVE) { + return { + currency: 'USD', + floor: 1.0 + }; + } else { + return {} + } + }, + 'schain': { + 'ver': '1.0', + 'complete': 1, + 'nodes': [ + { + 'asi': 'pixad.com.tr', + 'sid': 'px-pub-3000856707', + 'hp': 1 + } + ] + }, + 'at': 1, + 'tmax': 1000, + 'user': { + 'ext': { + 'eids': [ + { + 'source': 'id5-sync.com', + 'uids': [ + { + 'id': '0', + 'atype': 1, + 'ext': { + 'linkType': 0, + 'pba': 'wMh3sAXcnhDq7CfSa6ji1g==' + } + } + ] + }, + { + 'source': 'pubcid.org', + 'uids': [ + { + 'id': '5a49273f-a424-454b-b478-169c3551aa72', + 'atype': 1 + } + ] + } + ] + } + }, + 'ortb': { + 'source': {}, + 'site': { + 'domain': 'localhost:8888', + 'publisher': { + 'domain': 'localhost:8888' + }, + 'page': 'http://localhost:8888/', + 'name': 'http://localhost:8888' + }, + 'badv': [], + 'bcat': [], + 'device': { + 'w': 896, + 'h': 979, + 'dnt': 0, + 'ua': 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/119.0.0.0 Safari/537.36', + 'language': 'tr', + 'sua': { + 'source': 1, + 'platform': { + 'brand': 'macOS' + }, + 'browsers': [ + { + 'brand': 'Google Chrome', + 'version': [ + '119' + ] + }, + { + 'brand': 'Chromium', + 'version': [ + '119' + ] + }, + { + 'brand': 'Not?A_Brand', + 'version': [ + '24' + ] + } + ], + 'mobile': 0 + } + } + }, + 'site': { + 'page': 'http://localhost:8888/admatic.html', + 'ref': 'http://localhost:8888', + 'publisher': { + 'name': 'localhost', + 'publisherId': 12321312 + } + }, + 'imp': [ + { + 'size': [ + { + 'w': 300, + 'h': 250 + }, + { + 'w': 728, + 'h': 90 + } + ], + 'id': '2205da7a81846b', + 'mediatype': {}, + 'type': 'banner', + 'floors': { + 'banner': { + '300x250': { 'currency': 'USD', 'floor': 1 }, + '728x90': { 'currency': 'USD', 'floor': 2 } + } + } + }, + { + 'size': [ + { + 'w': 338, + 'h': 280 + } + ], + 'type': 'video', + 'mediatype': { + 'context': 'instream', + 'mimes': [ + 'video/mp4' + ], + 'maxduration': 240, + 'api': [ + 1, + 2 + ], + 'playerSize': [ + [ + 338, + 280 + ] + ], + 'protocols': [ + 1, + 2, + 3, + 4, + 5, + 6, + 7, + 8 + ], + 'skip': 1, + 'playbackmethod': [ + 2 + ], + 'linearity': 1, + 'placement': 2 + }, + 'floors': { + 'video': { + '338x280': { 'currency': 'USD', 'floor': 1 } + } + }, + 'id': '45e86fc7ce7fc93' + }, + { + 'size': [ + { + 'w': 1, + 'h': 1 + } + ], + 'type': 'native', + 'mediatype': { + 'title': { + 'required': true, + 'len': 120 + }, + 'image': { + 'required': true + }, + 'icon': { + 'required': false, + 'sizes': [ + 640, + 480 + ] + }, + 'sponsoredBy': { + 'required': false + }, + 'body': { + 'required': false + }, + 'clickUrl': { + 'required': false + }, + 'displayUrl': { + 'required': false + } + }, + 'ext': { + 'instl': 0, + 'gpid': 'native-INS_b1b1269f-9570-fe3c-9bf4-f187827ec94a', + 'data': { + 'pbadslot': 'native-INS_b1b1269f-9570-fe3c-9bf4-f187827ec94a' + } + }, + 'floors': { + 'native': { + '*': { 'currency': 'USD', 'floor': 1 } + } + }, + 'id': '16e0c8982318f91' + } + ], + 'ext': { + 'cur': 'USD', + 'bidder': 'admatic' + } + }; describe('inherited functions', () => { it('exists and is a function', () => { @@ -51,353 +592,147 @@ describe('admaticBidAdapter', () => { describe('buildRequests', function () { it('sends bid request to ENDPOINT via POST', function () { - let validRequest = [ { - 'refererInfo': { - 'page': 'https://www.admatic.com.tr', - 'domain': 'https://www.admatic.com.tr', - }, - 'bidder': 'admatic', - 'params': { - 'networkId': 10433394, - 'host': 'layer.serve.admatic.com.tr' - }, - 'ortb2Imp': { 'ext': { 'instl': 1 } }, - 'ortb2': { 'badv': ['admatic.com.tr'] }, - 'mediaTypes': { - 'banner': { - 'sizes': [[300, 250], [728, 90]] - } - }, - getFloor: inputParams => { - if (inputParams.mediaType === BANNER && inputParams.size[0] === 300 && inputParams.size[1] === 250) { - return { - currency: 'USD', - floor: 1.0 - }; - } else if (inputParams.mediaType === BANNER && inputParams.size[0] === 728 && inputParams.size[1] === 90) { - return { - currency: 'USD', - floor: 2.0 - }; - } else { - return {} - } - }, - 'user': { - 'ua': 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/107.0.0.0 Safari/537.36' - }, - 'blacklist': [], - 'site': { - 'page': 'http://localhost:8888/admatic.html', - 'ref': 'http://localhost:8888', - 'publisher': { - 'name': 'localhost', - 'publisherId': 12321312 + const request = spec.buildRequests(validRequest, bidderRequest); + expect(request.url).to.equal(ENDPOINT); + expect(request.method).to.equal('POST'); + }); + + it('should not populate GDPR if for non-EEA users', function () { + let bidRequest = Object.assign([], validRequest); + const request = spec.buildRequests( + bidRequest, + Object.assign({}, bidderRequest, { + gdprConsent: { + gdprApplies: true, + consentString: 'BOJ8RZsOJ8RZsABAB8AAAAAZ+A==' } - }, - 'imp': [ - { - 'size': [ - { - 'w': 300, - 'h': 250 - }, - { - 'w': 728, - 'h': 90 - } - ], - 'mediatype': {}, - 'type': 'banner', - 'id': '2205da7a81846b', - 'floors': { - 'banner': { - '300x250': { 'currency': 'USD', 'floor': 1 }, - '728x90': { 'currency': 'USD', 'floor': 2 } - } - } - }, - { - 'size': [ - { - 'w': 338, - 'h': 280 - } - ], - 'type': 'video', - 'mediatype': { - 'context': 'instream', - 'mimes': [ - 'video/mp4' - ], - 'maxduration': 240, - 'api': [ - 1, - 2 - ], - 'playerSize': [ - [ - 338, - 280 - ] - ], - 'protocols': [ - 1, - 2, - 3, - 4, - 5, - 6, - 7, - 8 - ], - 'skip': 1, - 'playbackmethod': [ - 2 - ], - 'linearity': 1, - 'placement': 2 - }, - 'id': '45e86fc7ce7fc93' - }, - { - 'size': [ - { - 'w': 1, - 'h': 1 - } - ], - 'type': 'native', - 'mediatype': { - 'title': { - 'required': true, - 'len': 120 - }, - 'image': { - 'required': true - }, - 'icon': { - 'required': false, - 'sizes': [ - 640, - 480 - ] - }, - 'sponsoredBy': { - 'required': false - }, - 'body': { - 'required': false - }, - 'clickUrl': { - 'required': false - }, - 'displayUrl': { - 'required': false - } - }, - 'ext': { - 'instl': 0, - 'gpid': 'native-INS_b1b1269f-9570-fe3c-9bf4-f187827ec94a', - 'data': { - 'pbadslot': 'native-INS_b1b1269f-9570-fe3c-9bf4-f187827ec94a' - } - }, - 'id': '16e0c8982318f91' + }) + ); + expect(request.data.regs.ext.gdpr).to.equal(1); + expect(request.data.regs.ext.consent).to.equal('BOJ8RZsOJ8RZsABAB8AAAAAZ-A'); + }); + + it('should populate GDPR and empty consent string if available for EEA users without consent string but with consent', function () { + let bidRequest = Object.assign([], validRequest); + const request = spec.buildRequests( + bidRequest, + Object.assign({}, bidderRequest, { + gdprConsent: { + gdprApplies: true } - ], - 'ext': { - 'cur': 'USD', - 'bidder': 'admatic' + }) + ); + expect(request.data.regs.ext.gdpr).to.equal(1); + expect(request.data.regs.ext.consent).to.equal(''); + }); + + it('should properly build a request when coppa flag is true', function () { + let bidRequest = Object.assign([], validRequest); + const request = spec.buildRequests( + bidRequest, + Object.assign({}, bidderRequest, { + coppa: true + }) + ); + expect(request.data.regs.ext.coppa).to.not.be.undefined; + expect(request.data.regs.ext.coppa).to.equal(1); + }); + + it('should properly build a request with gpp consent field', function () { + let bidRequest = Object.assign([], validRequest); + const ortb2 = { + regs: { + gpp: 'gpp_consent_string', + gpp_sid: [0, 1, 2] } - } ]; - let bidderRequest = { - 'refererInfo': { - 'page': 'https://www.admatic.com.tr', - 'domain': 'https://www.admatic.com.tr', - }, - 'bidder': 'admatic', - 'params': { - 'networkId': 10433394, - 'host': 'layer.serve.admatic.com.tr' - }, - 'ortb2Imp': { 'ext': { 'instl': 1 } }, - 'ortb2': { 'badv': ['admatic.com.tr'] }, - 'mediaTypes': { - 'banner': { - 'sizes': [[300, 250], [728, 90]] - } - }, - getFloor: inputParams => { - if (inputParams.mediaType === BANNER && inputParams.size[0] === 300 && inputParams.size[1] === 250) { - return { - currency: 'USD', - floor: 1.0 - }; - } else if (inputParams.mediaType === BANNER && inputParams.size[0] === 728 && inputParams.size[1] === 90) { - return { - currency: 'USD', - floor: 2.0 - }; - } else { - return {} - } - }, - 'user': { - 'ua': 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/107.0.0.0 Safari/537.36' - }, - 'blacklist': [], - 'site': { - 'page': 'http://localhost:8888/admatic.html', - 'ref': 'http://localhost:8888', - 'publisher': { - 'name': 'localhost', - 'publisherId': 12321312 - } - }, - 'imp': [ - { - 'size': [ - { - 'w': 300, - 'h': 250 - }, - { - 'w': 728, - 'h': 90 - } - ], - 'id': '2205da7a81846b', - 'mediatype': {}, - 'type': 'banner', - 'floors': { - 'banner': { - '300x250': { 'currency': 'USD', 'floor': 1 }, - '728x90': { 'currency': 'USD', 'floor': 2 } - } + }; + const request = spec.buildRequests(bidRequest, { ...bidderRequest, ortb2 }); + expect(request.data.regs.ext.gpp).to.equal('gpp_consent_string'); + expect(request.data.regs.ext.gpp_sid).to.deep.equal([0, 1, 2]); + }); + + it('should properly build a request with ccpa consent field', function () { + let bidRequest = Object.assign([], validRequest); + const request = spec.buildRequests( + bidRequest, + Object.assign({}, bidderRequest, { + uspConsent: '1---' + }) + ); + expect(request.data.regs.ext.uspIab).to.not.be.null; + expect(request.data.regs.ext.uspIab).to.equal('1---'); + }); + + it('should properly forward eids', function () { + const bidRequests = [ + { + bidder: 'admatic', + adUnitCode: 'bid-123', + transactionId: 'transaction-123', + mediaTypes: { + banner: { + sizes: [[728, 90]] } }, - { - 'size': [ - { - 'w': 338, - 'h': 280 - } - ], - 'type': 'video', - 'mediatype': { - 'context': 'instream', - 'mimes': [ - 'video/mp4' - ], - 'maxduration': 240, - 'api': [ - 1, - 2 - ], - 'playerSize': [ - [ - 338, - 280 - ] - ], - 'protocols': [ - 1, - 2, - 3, - 4, - 5, - 6, - 7, - 8 - ], - 'skip': 1, - 'playbackmethod': [ - 2 - ], - 'linearity': 1, - 'placement': 2 - }, - 'id': '45e86fc7ce7fc93' - }, - { - 'size': [ - { - 'w': 1, - 'h': 1 - } - ], - 'type': 'native', - 'mediatype': { - 'title': { - 'required': true, - 'len': 120 - }, - 'image': { - 'required': true - }, - 'icon': { - 'required': false, - 'sizes': [ - 640, - 480 - ] - }, - 'sponsoredBy': { - 'required': false - }, - 'body': { - 'required': false - }, - 'clickUrl': { - 'required': false - }, - 'displayUrl': { - 'required': false - } - }, - 'ext': { - 'instl': 0, - 'gpid': 'native-INS_b1b1269f-9570-fe3c-9bf4-f187827ec94a', - 'data': { - 'pbadslot': 'native-INS_b1b1269f-9570-fe3c-9bf4-f187827ec94a' - } - }, - 'id': '16e0c8982318f91' - } - ], - 'ext': { - 'cur': 'USD', - 'bidder': 'admatic' + userIdAsEids: [ + { + source: 'admatic.com.tr', + uids: [{ + id: 'abc', + atype: 1 + }] + } + ], + params: {} + }, + ]; + const request = spec.buildRequests(bidRequests, bidderRequest); + const ortbRequest = request.data; + expect(ortbRequest.user.ext.eids).to.deep.equal([ + { + source: 'admatic.com.tr', + uids: [{ + id: 'abc', + atype: 1 + }] } - }; - const request = spec.buildRequests(validRequest, bidderRequest); - expect(request.url).to.equal(ENDPOINT); - expect(request.method).to.equal('POST'); + ]); }); it('should properly build a banner request with floors', function () { - let bidRequests = [ + const request = spec.buildRequests(validRequest, bidderRequest); + request.data.imp[0].floors = { + 'banner': { + '300x250': { 'currency': 'USD', 'floor': 1 }, + '728x90': { 'currency': 'USD', 'floor': 2 } + } + }; + }); + + it('should properly build a video request with several player sizes with floors', function () { + const bidRequests = [ { 'bidder': 'admatic', - 'params': { - 'networkId': 10433394, - 'host': 'layer.serve.admatic.com.tr' - }, + 'adUnitCode': 'bid-123', + 'transactionId': 'transaction-123', 'mediaTypes': { - 'banner': { - 'sizes': [[300, 250], [728, 90]] + 'video': { + 'playerSize': [[300, 250], [728, 90]] } }, 'ortb2Imp': { 'ext': { 'instl': 1 } }, 'ortb2': { 'badv': ['admatic.com.tr'] }, + 'params': { + 'networkId': 10433394, + 'host': 'layer.serve.admatic.com.tr' + }, getFloor: inputParams => { - if (inputParams.mediaType === BANNER && inputParams.size[0] === 300 && inputParams.size[1] === 250) { + if (inputParams.mediaType === VIDEO && inputParams.size[0] === 300 && inputParams.size[1] === 250) { return { currency: 'USD', floor: 1.0 }; - } else if (inputParams.mediaType === BANNER && inputParams.size[0] === 728 && inputParams.size[1] === 90) { + } else if (inputParams.mediaType === VIDEO && inputParams.size[0] === 728 && inputParams.size[1] === 90) { return { currency: 'USD', floor: 2.0 @@ -408,37 +743,50 @@ describe('admaticBidAdapter', () => { } }, ]; - let bidderRequest = { + const bidderRequest = { 'refererInfo': { 'page': 'https://www.admatic.com.tr', 'domain': 'https://www.admatic.com.tr', - }, - 'bidder': 'admatic', - 'params': { - 'networkId': 10433394, - 'host': 'layer.serve.admatic.com.tr' - }, - 'ortb2Imp': { 'ext': { 'instl': 1 } }, - 'ortb2': { 'badv': ['admatic.com.tr'] }, - 'adUnitCode': 'adunit-code', - 'sizes': [[300, 250], [728, 90]], - 'bidId': '30b31c1838de1e', - 'bidderRequestId': '22edbae2733bf6', - 'auctionId': '1d1a030790a475', - 'creativeId': 'er2ee', - 'mediaTypes': { - 'banner': { - 'sizes': [[300, 250], [728, 90]] - } } }; const request = spec.buildRequests(bidRequests, bidderRequest); - request.data.imp[0].floors = { - 'banner': { - '300x250': { 'currency': 'USD', 'floor': 1 }, - '728x90': { 'currency': 'USD', 'floor': 2 } + }); + + it('should properly build a native request with floors', function () { + const bidRequests = [ + { + 'bidder': 'admatic', + 'adUnitCode': 'bid-123', + 'transactionId': 'transaction-123', + 'mediaTypes': { + 'native': { + } + }, + 'ortb2Imp': { 'ext': { 'instl': 1 } }, + 'ortb2': { 'badv': ['admatic.com.tr'] }, + 'params': { + 'networkId': 10433394, + 'host': 'layer.serve.admatic.com.tr' + }, + getFloor: inputParams => { + if (inputParams.mediaType === NATIVE) { + return { + currency: 'USD', + floor: 1.0 + }; + } else { + return {} + } + } + }, + ]; + const bidderRequest = { + 'refererInfo': { + 'page': 'https://www.admatic.com.tr', + 'domain': 'https://www.admatic.com.tr', } }; + const request = spec.buildRequests(bidRequests, bidderRequest); }); }); @@ -495,7 +843,7 @@ describe('admaticBidAdapter', () => { 'mime_type': 'iframe', 'bidder': 'admatic', 'adomain': ['admatic.com.tr'], - 'party_tag': '{"native":{"ver":"1.1","assets":[{"id":1,"title":{"text":"title"}},{"id":4,"data":{"value":"body"}},{"id":5,"data":{"value":"sponsored"}},{"id":2,"img":{"url":"https://www.admatic.com.tr","w":1200,"h":628}},{"id":3,"img":{"url":"https://www.admatic.com.tr","w":640,"h":480}}],"link":{"url":"https://www.admatic.com.tr"},"imptrackers":["https://www.admatic.com.tr"]}}', + 'party_tag': '{"native":{"ver":"1.1","assets":[{"id":1,"title":{"text":"title"}},{"id":4,"data":{"value":"body"}},{"id":5,"data":{"value":"sponsored"}},{"id":6,"data":{"value":"cta"}},{"id":2,"img":{"url":"https://www.admatic.com.tr","w":1200,"h":628}},{"id":3,"img":{"url":"https://www.admatic.com.tr","w":640,"h":480}}],"link":{"url":"https://www.admatic.com.tr"},"imptrackers":["https://www.admatic.com.tr"]}}', 'iurl': 'https://www.admatic.com.tr' } ], @@ -571,6 +919,7 @@ describe('admaticBidAdapter', () => { 'title': 'title', 'body': 'body', 'sponsoredBy': 'sponsored', + 'cta': 'cta', 'image': { 'url': 'https://www.admatic.com.tr', 'width': 1200, From 9ec20924a6d444e5ecea5e9f503b8cd479666422 Mon Sep 17 00:00:00 2001 From: ahmadlob <109217988+ahmadlob@users.noreply.github.com> Date: Wed, 27 Dec 2023 19:24:43 +0200 Subject: [PATCH 096/246] Taboola Bid Adapter: Cookie Look Up Logic Fix (#10873) * cookie-look-up-logic-fix * cookie-look-up-logic-fix * cookie-look-up-logic-fix --- modules/taboolaBidAdapter.js | 12 +++++++++++- test/spec/modules/taboolaBidAdapter_spec.js | 16 ++++++++++++++++ 2 files changed, 27 insertions(+), 1 deletion(-) diff --git a/modules/taboolaBidAdapter.js b/modules/taboolaBidAdapter.js index a8745449ee4..7402b725fd2 100644 --- a/modules/taboolaBidAdapter.js +++ b/modules/taboolaBidAdapter.js @@ -16,6 +16,8 @@ export const USER_SYNC_IFRAME_URL = 'https://cdn.taboola.com/scripts/prebid_ifra const USER_ID = 'user-id'; const STORAGE_KEY = `taboola global:${USER_ID}`; const COOKIE_KEY = 'trc_cookie_storage'; +const TGID_COOKIE_KEY = 't_gid'; +const TBLA_ID_COOKIE_KEY = 'tbla_id'; export const EVENT_ENDPOINT = 'https://beacon.bidder.taboola.com'; /** @@ -40,10 +42,18 @@ export const userData = { const {cookiesAreEnabled, getCookie} = userData.storageManager; if (cookiesAreEnabled()) { const cookieData = getCookie(COOKIE_KEY); - const userId = userData.getCookieDataByKey(cookieData, USER_ID); + let userId = userData.getCookieDataByKey(cookieData, USER_ID); if (userId) { return userId; } + userId = getCookie(TGID_COOKIE_KEY); + if (userId) { + return userId; + } + const tblaId = getCookie(TBLA_ID_COOKIE_KEY); + if (tblaId) { + return tblaId; + } } }, getCookieDataByKey(cookieData, key) { diff --git a/test/spec/modules/taboolaBidAdapter_spec.js b/test/spec/modules/taboolaBidAdapter_spec.js index 402b4990166..280376e4e56 100644 --- a/test/spec/modules/taboolaBidAdapter_spec.js +++ b/test/spec/modules/taboolaBidAdapter_spec.js @@ -459,6 +459,22 @@ describe('Taboola Adapter', function () { expect(resData.user.buyeruid).to.equal('12121212'); }); + it('should get user id from tgid cookie if local storage isn`t defined', function () { + getDataFromLocalStorage.returns(51525152); + hasLocalStorage.returns(false); + localStorageIsEnabled.returns(false); + cookiesAreEnabled.returns(true); + getCookie.returns('d966c5be-c49f-4f73-8cd1-37b6b5790653-tuct9f7bf10'); + + const bidderRequest = { + ...commonBidderRequest + }; + const res = spec.buildRequests([defaultBidRequest], bidderRequest); + const resData = JSON.parse(res.data); + + expect(resData.user.buyeruid).to.equal('d966c5be-c49f-4f73-8cd1-37b6b5790653-tuct9f7bf10'); + }); + it('should get user id from TRC if local storage and cookie isn`t defined', function () { hasLocalStorage.returns(false); cookiesAreEnabled.returns(false); From 984abfa19c44c33cfdbc88fa311831d3fc26ee57 Mon Sep 17 00:00:00 2001 From: asurovenko-zeta <80847074+asurovenko-zeta@users.noreply.github.com> Date: Wed, 27 Dec 2023 18:28:08 +0100 Subject: [PATCH 097/246] ZetaGlobalSsp Bid Adapter : support topics module (#10803) * add getTopics() * provide get segments from ortb2 * rename const * additional check * fix test --------- Co-authored-by: Surovenko Alexey Co-authored-by: Alexey Surovenko --- modules/zeta_global_sspBidAdapter.js | 20 +++++++++++++ .../modules/zeta_global_sspBidAdapter_spec.js | 28 ++++++++++++++++++- 2 files changed, 47 insertions(+), 1 deletion(-) diff --git a/modules/zeta_global_sspBidAdapter.js b/modules/zeta_global_sspBidAdapter.js index db31791d86f..0c5db541286 100644 --- a/modules/zeta_global_sspBidAdapter.js +++ b/modules/zeta_global_sspBidAdapter.js @@ -163,6 +163,7 @@ export const spec = { } provideEids(validBidRequests[0], payload); + provideSegments(bidderRequest, payload); const url = params.sid ? ENDPOINT_URL.concat('?sid=', params.sid) : ENDPOINT_URL; return { method: 'POST', @@ -335,6 +336,25 @@ function provideEids(request, payload) { } } +function provideSegments(bidderRequest, payload) { + const data = bidderRequest.ortb2?.user?.data; + if (isArray(data)) { + const segments = data.filter(d => d?.segment).map(d => d.segment).filter(s => isArray(s)).flatMap(s => s).filter(s => s?.id); + if (segments.length > 0) { + if (!payload.user) { + payload.user = {}; + } + if (!isArray(payload.user.data)) { + payload.user.data = []; + } + const payloadData = { + segment: segments + }; + payload.user.data.push(payloadData); + } + } +} + function provideMediaType(zetaBid, bid, bidRequest) { if (zetaBid.ext && zetaBid.ext.prebid && zetaBid.ext.prebid.type) { bid.mediaType = zetaBid.ext.prebid.type === VIDEO ? VIDEO : BANNER; diff --git a/test/spec/modules/zeta_global_sspBidAdapter_spec.js b/test/spec/modules/zeta_global_sspBidAdapter_spec.js index d4fe28eff90..9ef97019a98 100644 --- a/test/spec/modules/zeta_global_sspBidAdapter_spec.js +++ b/test/spec/modules/zeta_global_sspBidAdapter_spec.js @@ -124,7 +124,24 @@ describe('Zeta Ssp Bid Adapter', function () { uspConsent: 'someCCPAString', params: params, userIdAsEids: eids, - timeout: 500 + timeout: 500, + ortb2: { + user: { + data: [ + { + ext: { + segtax: 600, + segclass: 'classifier_v1' + }, + segment: [ + { id: '3' }, + { id: '44' }, + { id: '59' } + ] + } + ] + } + } }]; const bannerWithFewSizesRequest = [{ @@ -606,4 +623,13 @@ describe('Zeta Ssp Bid Adapter', function () { expect(bidResponse[0].ad).to.eql(zetaResponse.body.seatbid[0].bid[0].adm); expect(bidResponse[0].vastXml).to.be.undefined; }); + + it('Test provide segments into the request', function () { + const request = spec.buildRequests(bannerRequest, bannerRequest[0]); + const payload = JSON.parse(request.data); + expect(payload.user.data[0].segment.length).to.eql(3); + expect(payload.user.data[0].segment[0].id).to.eql('3'); + expect(payload.user.data[0].segment[1].id).to.eql('44'); + expect(payload.user.data[0].segment[2].id).to.eql('59'); + }); }); From 325cd3c51cb3e0708c85a132337fde2d589ed11f Mon Sep 17 00:00:00 2001 From: Mikael Lundin Date: Thu, 28 Dec 2023 11:02:18 +0100 Subject: [PATCH 098/246] Adnuntius Bid Adapter: bugfix for storageFunction (#10869) * Removed linting issues * Fixed merge issues. * Bugfix on storageTool. --- modules/adnuntiusBidAdapter.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/modules/adnuntiusBidAdapter.js b/modules/adnuntiusBidAdapter.js index 8f841006524..6f3ed6eb5b6 100644 --- a/modules/adnuntiusBidAdapter.js +++ b/modules/adnuntiusBidAdapter.js @@ -30,14 +30,14 @@ const storageTool = (function () { const getMetaInternal = function () { if (!storage.localStorageIsEnabled()) { - return {}; + return []; } let parsedJson; try { parsedJson = JSON.parse(storage.getDataFromLocalStorage(META_DATA_KEY)); } catch (e) { - return {}; + return []; } let filteredEntries = parsedJson ? parsedJson.filter((datum) => { From bdbbf09a3182eb1c4198865d5542a79ed38a29c6 Mon Sep 17 00:00:00 2001 From: "Prebid.js automated release" Date: Thu, 28 Dec 2023 20:52:14 +0000 Subject: [PATCH 099/246] Prebid 8.29.0 release --- package-lock.json | 2 +- package.json | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/package-lock.json b/package-lock.json index 592fcb3d16d..fc1b8d76509 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,6 +1,6 @@ { "name": "prebid.js", - "version": "8.29.0-pre", + "version": "8.29.0", "lockfileVersion": 2, "requires": true, "packages": { diff --git a/package.json b/package.json index 62e99c75911..79d7aa1c74d 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "prebid.js", - "version": "8.29.0-pre", + "version": "8.29.0", "description": "Header Bidding Management Library", "main": "src/prebid.js", "scripts": { From 46784ab77ea2349a5c8b4b487b28d7980d3f7f94 Mon Sep 17 00:00:00 2001 From: "Prebid.js automated release" Date: Thu, 28 Dec 2023 20:52:14 +0000 Subject: [PATCH 100/246] Increment version to 8.30.0-pre --- package-lock.json | 2 +- package.json | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/package-lock.json b/package-lock.json index fc1b8d76509..65b4011bca8 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,6 +1,6 @@ { "name": "prebid.js", - "version": "8.29.0", + "version": "8.30.0-pre", "lockfileVersion": 2, "requires": true, "packages": { diff --git a/package.json b/package.json index 79d7aa1c74d..85642969f06 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "prebid.js", - "version": "8.29.0", + "version": "8.30.0-pre", "description": "Header Bidding Management Library", "main": "src/prebid.js", "scripts": { From cc499687325c378cf7bcf49c393b5ccfbf3ca67d Mon Sep 17 00:00:00 2001 From: Denis <7009699+someden@users.noreply.github.com> Date: Sun, 31 Dec 2023 23:06:42 +0300 Subject: [PATCH 101/246] Yandex Bid Adapter: add support for topicsFpdModule (#10875) * Yandex Bid Adapter: add support for topicsFpdModule * add test --- modules/yandexBidAdapter.js | 5 +++ test/spec/modules/yandexBidAdapter_spec.js | 50 +++++++++++++--------- 2 files changed, 34 insertions(+), 21 deletions(-) diff --git a/modules/yandexBidAdapter.js b/modules/yandexBidAdapter.js index 4a7f9114a17..038c1f3aa70 100644 --- a/modules/yandexBidAdapter.js +++ b/modules/yandexBidAdapter.js @@ -131,6 +131,11 @@ export const spec = { deepSetValue(data, 'user.ext.eids', eids); } + const userData = deepAccess(bidRequest, 'ortb2.user.data'); + if (userData && userData.length) { + deepSetValue(data, 'user.data', userData); + } + const queryParamsString = formatQS(queryParams); return { method: 'POST', diff --git a/test/spec/modules/yandexBidAdapter_spec.js b/test/spec/modules/yandexBidAdapter_spec.js index d5cfc26b31a..c5f088a2306 100644 --- a/test/spec/modules/yandexBidAdapter_spec.js +++ b/test/spec/modules/yandexBidAdapter_spec.js @@ -106,19 +106,37 @@ describe('Yandex adapter', function () { expect(query['ssp-cur']).to.equal('USD'); }); - it('should send eids if defined', function() { - const bannerRequest = getBidRequest({ + it('should send eids and ortb2 user data if defined', function() { + const bidRequestExtra = { userIdAsEids: [{ source: 'sharedid.org', - uids: [ - { - id: '01', - atype: 1 - } - ] - }] - }); + uids: [{ id: '01', atype: 1 }], + }], + ortb2: { + user: { + data: [ + { + ext: { segtax: 600, segclass: '1' }, + name: 'example.com', + segment: [{ id: '243' }], + }, + { + ext: { segtax: 600, segclass: '1' }, + name: 'ads.example.org', + segment: [{ id: '243' }], + }, + ], + }, + }, + }; + const expected = { + ext: { + eids: bidRequestExtra.userIdAsEids, + }, + data: bidRequestExtra.ortb2.user.data, + }; + const bannerRequest = getBidRequest(bidRequestExtra); const requests = spec.buildRequests([bannerRequest], bidderRequest); expect(requests).to.have.lengthOf(1); @@ -128,17 +146,7 @@ describe('Yandex adapter', function () { const { data } = request; expect(data.user).to.exist; - expect(data.user).to.deep.equal({ - ext: { - eids: [{ - source: 'sharedid.org', - uids: [{ - id: '01', - atype: 1, - }], - }], - } - }); + expect(data.user).to.deep.equal(expected); }); describe('banner', () => { From ff276735733334d1990cb3ccc0ec543229bf80e4 Mon Sep 17 00:00:00 2001 From: Denis <7009699+someden@users.noreply.github.com> Date: Sun, 31 Dec 2023 23:07:40 +0300 Subject: [PATCH 102/246] Yandex Bid Adapter: add jsdoc (#10884) --- modules/yandexBidAdapter.js | 85 ++++++++++++++++++++++++++++++++++++- 1 file changed, 84 insertions(+), 1 deletion(-) diff --git a/modules/yandexBidAdapter.js b/modules/yandexBidAdapter.js index 038c1f3aa70..cc4a8c18241 100644 --- a/modules/yandexBidAdapter.js +++ b/modules/yandexBidAdapter.js @@ -4,10 +4,55 @@ import { BANNER, NATIVE } from '../src/mediaTypes.js' import { convertOrtbRequestToProprietaryNative } from '../src/native.js'; import { config } from '../src/config.js'; +/** + * @typedef {import('../src/adapters/bidderFactory.js').Bid} Bid + * @typedef {import('../src/adapters/bidderFactory.js').BidRequest} BidRequest + * @typedef {import('../src/adapters/bidderFactory.js').BidderSpec} BidderSpec + * @typedef {import('../src/adapters/bidderFactory.js').ServerRequest} ServerRequest + * @typedef {import('../src/adapters/bidderFactory.js').ServerResponse} ServerResponse + * @typedef {import('../src/adapters/bidderFactory.js').SyncOptions} SyncOptions + * @typedef {import('../src/adapters/bidderFactory.js').UserSync} UserSync + * @typedef {import('../src/auction.js').BidderRequest} BidderRequest + * @typedef {import('../src/mediaTypes.js').MediaType} MediaType + * @typedef {import('../src/utils.js').MediaTypes} MediaTypes + * @typedef {import('../modules/priceFloors.js').getFloor} GetFloor + */ + +/** + * @typedef {Object} CustomServerRequestFields + * @property {BidRequest} bidRequest + */ + +/** + * @typedef {ServerRequest & CustomServerRequestFields} YandexServerRequest + */ + +/** + * Yandex bidder-specific params which the publisher used in their bid request. + * + * @typedef {Object} YandexBidRequestParams + * @property {string} placementId Possible formats: `R-I-123456-2`, `R-123456-1`, `123456-789`. + * @property {number} [pageId] Deprecated. Please use `placementId` instead. + * @property {number} [impId] Deprecated. Please use `placementId` instead. + */ + +/** + * @typedef {Object} AdditionalBidRequestFields + * @property {GetFloor} [getFloor] + * @property {MediaTypes} [mediaTypes] + */ + +/** + * @typedef {BidRequest & AdditionalBidRequestFields} ExtendedBidRequest + */ + const BIDDER_CODE = 'yandex'; const BIDDER_URL = 'https://bs.yandex.ru/prebid'; const DEFAULT_TTL = 180; const DEFAULT_CURRENCY = 'EUR'; +/** + * @type {MediaType[]} + */ const SUPPORTED_MEDIA_TYPES = [ BANNER, NATIVE ]; const SSP_ID = 10500; @@ -42,11 +87,18 @@ export const NATIVE_ASSETS = { const NATIVE_ASSETS_IDS = {}; _each(NATIVE_ASSETS, (asset, key) => { NATIVE_ASSETS_IDS[asset[0]] = key }); +/** @type BidderSpec */ export const spec = { code: BIDDER_CODE, aliases: ['ya'], // short code supportedMediaTypes: SUPPORTED_MEDIA_TYPES, + /** + * Determines whether or not the given bid request is valid. + * + * @param {BidRequest} bid The bid request to validate. + * @returns {boolean} True if this is a valid bid, and false otherwise. + */ isBidRequestValid: function(bid) { const { params } = bid; if (!params) { @@ -59,6 +111,13 @@ export const spec = { return true; }, + /** + * Make a server request from the list of BidRequests. + * + * @param {ExtendedBidRequest[]} validBidRequests An array of bids. + * @param {BidderRequest} bidderRequest Bidder request object. + * @returns {YandexServerRequest[]} Objects describing the requests to the server. + */ buildRequests: function(validBidRequests, bidderRequest) { validBidRequests = convertOrtbRequestToProprietaryNative(validBidRequests); @@ -151,7 +210,11 @@ export const spec = { interpretResponse: interpretResponse, - onBidWon: function (bid) { + /** + * 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) { const nurl = addRTT(bid['nurl'], bid.timeToRespond); if (!nurl) { @@ -162,6 +225,9 @@ export const spec = { } } +/** + * @param {YandexBidRequestParams} bidRequestParams + */ function extractPlacementIds(bidRequestParams) { const { placementId } = bidRequestParams; const result = { pageId: null, impId: null }; @@ -196,6 +262,9 @@ function extractPlacementIds(bidRequestParams) { return result; } +/** + * @param {ExtendedBidRequest} bidRequest + */ function getBidfloor(bidRequest) { const floors = []; @@ -215,6 +284,9 @@ function getBidfloor(bidRequest) { return floors.sort((a, b) => b.floor - a.floor)[0]; } +/** + * @param {ExtendedBidRequest} bidRequest + */ function mapBanner(bidRequest) { if (deepAccess(bidRequest, 'mediaTypes.banner')) { const sizes = bidRequest.sizes || bidRequest.mediaTypes.banner.sizes; @@ -232,6 +304,9 @@ function mapBanner(bidRequest) { } } +/** + * @param {ExtendedBidRequest} bidRequest + */ function mapNative(bidRequest) { const adUnitNativeAssets = deepAccess(bidRequest, 'mediaTypes.native'); if (adUnitNativeAssets) { @@ -304,6 +379,13 @@ function mapImageAsset(adUnitImageAssetParams, nativeAssetType) { return img; } +/** + * Unpack the response from the server into a list of bids. + * + * @param {ServerResponse} serverResponse A successful response from the server. + * @param {YandexServerRequest} yandexServerRequest + * @return {Bid[]} An array of bids which were nested inside the server. + */ function interpretResponse(serverResponse, { bidRequest }) { let response = serverResponse.body; if (!response.seatbid) { @@ -318,6 +400,7 @@ function interpretResponse(serverResponse, { bidRequest }) { return bidsReceived.map(bidReceived => { const price = bidReceived.price; + /** @type {Bid} */ let prBid = { requestId: bidRequest.bidId, cpm: price, From 233695d377cb815b2f1c8991efbdb2b8fb8496dd Mon Sep 17 00:00:00 2001 From: kmdevops <126434358+kmdevops@users.noreply.github.com> Date: Tue, 2 Jan 2024 18:35:02 +0100 Subject: [PATCH 103/246] DxKulture Bid Adapter : user syncs improvements (#10738) * Initial implementation of kulturemedia bid adapter * Changing outstream to instream * Enriching md file with test examples * Changing nId to networkId * Cleaning up md file * Submitting rebranded dxkultureBidAdapter * dxkultureBidAdapter - Improve UserSyncs * Include gdpr/usp params in iframe usersync url * Add gdpr/usp data to iframe usync urls * Cleaning up testing html file * Adding outstream support * Updating exchange endpoint * Resolve requests test * Resolving iframe/pixel priority when iframeEnabled/pixelEnabled * Improving userSync filtering condition * Prioritize iframe user syncing --------- Co-authored-by: Danijel Predarski Co-authored-by: dani-nova <73398187+dani-nova@users.noreply.github.com> Co-authored-by: Slavisa Petkovic Co-authored-by: Slavisa Petkovic <32300768+spetkovic@users.noreply.github.com> --- modules/dxkultureBidAdapter.js | 111 +++++++++++++----- test/spec/modules/dxkultureBidAdapter_spec.js | 79 ++++--------- 2 files changed, 107 insertions(+), 83 deletions(-) diff --git a/modules/dxkultureBidAdapter.js b/modules/dxkultureBidAdapter.js index 9e4768d12bb..282b54e0823 100644 --- a/modules/dxkultureBidAdapter.js +++ b/modules/dxkultureBidAdapter.js @@ -1,5 +1,6 @@ import { logInfo, + logWarn, logError, logMessage, deepAccess, @@ -8,13 +9,14 @@ import { } from '../src/utils.js'; import {registerBidder} from '../src/adapters/bidderFactory.js'; import {BANNER, VIDEO} from '../src/mediaTypes.js'; +import { Renderer } from '../src/Renderer.js'; import {ortbConverter} from '../libraries/ortbConverter/converter.js' const BIDDER_CODE = 'dxkulture'; const DEFAULT_BID_TTL = 300; const DEFAULT_NET_REVENUE = true; const DEFAULT_CURRENCY = 'USD'; -const SYNC_URL = 'https://ads.kulture.media/usync'; +const DEFAULT_OUTSTREAM_RENDERER_URL = 'https://cdn.dxkulture.com/players/dxOutstreamPlayer.js'; const converter = ortbConverter({ context: { @@ -55,20 +57,14 @@ const converter = ortbConverter({ }, bidResponse(buildBidResponse, bid, context) { let resMediaType; + const {bidRequest} = context; + if (bid.adm?.trim().startsWith(' { + const value = userSync[key]; + if (value.syncs && value.syncs.length) { + syncDetails = syncDetails.concat(value.syncs); + } + }); + syncDetails.forEach(syncDetails => { + let queryParamStrings = []; + let syncUrl = syncDetails.url; + + if (syncDetails.type === 'iframe') { + if (gdprConsent) { + queryParamStrings.push('gdpr=' + (gdprConsent.gdprApplies ? 1 : 0)); + queryParamStrings.push('gdpr_consent=' + encodeURIComponent(gdprConsent.consentString || '')); + } + if (uspConsent) { + queryParamStrings.push('us_privacy=' + encodeURIComponent(uspConsent)); + } + syncUrl = `${syncDetails.url}${queryParamStrings.length > 0 ? '?' + queryParamStrings.join('&') : ''}` + } + + syncs.push({ + type: syncDetails.type === 'iframe' ? 'iframe' : 'image', + url: syncUrl + }); + }); + + if (syncOptions.iframeEnabled) { + syncs = syncs.filter(s => s.type == 'iframe'); + } else if (syncOptions.pixelEnabled) { + syncs = syncs.filter(s => s.type == 'image'); + } } + }); + logInfo('dxkulture.getUserSyncs result=%o', syncs); + return syncs; + }, - return [{ - type: pixelType, - url: `${syncUrl}${queryParamStrings.length > 0 ? '?' + queryParamStrings.join('&') : ''}` - }]; - } +}; + +function outstreamRenderer(bid) { + const rendererConfig = { + width: bid.width, + height: bid.height, + vastTimeout: 5000, + maxAllowedVastTagRedirects: 3, + allowVpaid: false, + autoPlay: true, + preload: true, + mute: false } -}; + const renderer = Renderer.install({ + id: bid.adId, + url: DEFAULT_OUTSTREAM_RENDERER_URL, + config: rendererConfig, + loaded: false, + targetId: bid.adUnitCode, + adUnitCode: bid.adUnitCode + }); + + try { + renderer.setRender(function (bid) { + bid.renderer.push(() => { + const { id, config } = bid.renderer; + window.dxOutstreamPlayer(bid, id, config); + }); + }); + } catch (err) { + logWarn('dxkulture: Prebid Error calling setRender on renderer', err); + } + + return renderer; +} /* ======================================= * Util Functions diff --git a/test/spec/modules/dxkultureBidAdapter_spec.js b/test/spec/modules/dxkultureBidAdapter_spec.js index d3ae8ec5124..a752c81cb6e 100644 --- a/test/spec/modules/dxkultureBidAdapter_spec.js +++ b/test/spec/modules/dxkultureBidAdapter_spec.js @@ -281,7 +281,7 @@ describe('dxkultureBidAdapter', function() { it('should return expected request object', function() { const bidRequest = spec.buildRequests(bidderRequest.bids, bidderRequest); - expect(bidRequest.url).equal('https://ads.kulture.media/pbjs?pid=publisherId&placementId=123456'); + expect(bidRequest.url).equal('https://ads.dxkulture.com/pbjs?pid=publisherId&placementId=123456'); expect(bidRequest.method).equal('POST'); }); }); @@ -606,7 +606,14 @@ describe('dxkultureBidAdapter', function() { }); }); - describe('user sync', function () { + describe('getUserSyncs', function () { + let bidRequest, bidderResponse; + beforeEach(function() { + const bidderRequest = getVideoRequest(); + bidRequest = spec.buildRequests(bidderRequest.bids, bidderRequest); + bidderResponse = getBidderResponse(); + }); + it('handles no parameters', function () { let opts = spec.getUserSyncs({}); expect(opts).to.be.an('array').that.is.empty; @@ -617,66 +624,26 @@ describe('dxkultureBidAdapter', function() { expect(opts).to.be.an('array').that.is.empty; }); - describe('when gdpr applies', function () { - let gdprConsent; - let gdprPixelUrl; - const consentString = 'gdpr-pixel-consent'; - const gdprApplies = '1'; - beforeEach(() => { - gdprConsent = { - consentString, - gdprApplies: true - }; - - gdprPixelUrl = `${SYNC_URL}&gdpr=${gdprApplies}&gdpr_consent=${consentString}`; - }); + it('iframe sync enabled should return results', function () { + let opts = spec.getUserSyncs({iframeEnabled: true, pixelEnabled: false}, [bidderResponse]); - it('when there is a response, it should have the gdpr query params', () => { - let [{url}] = spec.getUserSyncs( - {iframeEnabled: true, pixelEnabled: true}, - [], - gdprConsent - ); + expect(opts.length).to.equal(1); + expect(opts[0].type).to.equal('iframe'); + expect(opts[0].url).to.equal(bidderResponse.body.ext.usersync['sovrn'].syncs[0].url); + }); - expect(url).to.have.string(`gdpr_consent=${consentString}`); - expect(url).to.have.string(`gdpr=${gdprApplies}`); - }); + it('pixel sync enabled should return results', function () { + let opts = spec.getUserSyncs({iframeEnabled: false, pixelEnabled: true}, [bidderResponse]); - it('should not send signals if no consent object is available', function () { - let [{url}] = spec.getUserSyncs( - {iframeEnabled: true, pixelEnabled: true}, - [], - ); - expect(url).to.not.have.string('gdpr_consent='); - expect(url).to.not.have.string('gdpr='); - }); + expect(opts.length).to.equal(1); + expect(opts[0].type).to.equal('image'); + expect(opts[0].url).to.equal(bidderResponse.body.ext.usersync['appnexus'].syncs[0].url); }); - describe('when ccpa applies', function () { - let usPrivacyConsent; - let uspPixelUrl; - const privacyString = 'TEST'; - beforeEach(() => { - usPrivacyConsent = 'TEST'; - uspPixelUrl = `${SYNC_URL}&us_privacy=${privacyString}` - }); - it('should send the us privacy string, ', () => { - let [{url}] = spec.getUserSyncs( - {iframeEnabled: true, pixelEnabled: true}, - [], - undefined, - usPrivacyConsent - ); - expect(url).to.have.string(`us_privacy=${privacyString}`); - }); + it('all sync enabled should prioritize iframe', function () { + let opts = spec.getUserSyncs({iframeEnabled: true, pixelEnabled: true}, [bidderResponse]); - it('should not send signals if no consent string is available', function () { - let [{url}] = spec.getUserSyncs( - {iframeEnabled: true, pixelEnabled: true}, - [], - ); - expect(url).to.not.have.string('us_privacy='); - }); + expect(opts.length).to.equal(1); }); }); }); From 04a7659e19767e5cbcef96e21ea7c052d80b12f5 Mon Sep 17 00:00:00 2001 From: Gena Date: Tue, 2 Jan 2024 22:44:17 +0200 Subject: [PATCH 104/246] Mygaru Id System: Initial release (#10848) --- modules/.submodules.json | 3 +- modules/mygaruIdSystem.js | 98 ++++++++++++++++++++++++ modules/mygaruIdSystem.md | 24 ++++++ modules/userId/eids.md | 7 ++ modules/userId/userId.md | 3 + test/spec/modules/mygaruIdSystem_spec.js | 62 +++++++++++++++ 6 files changed, 196 insertions(+), 1 deletion(-) create mode 100644 modules/mygaruIdSystem.js create mode 100644 modules/mygaruIdSystem.md create mode 100644 test/spec/modules/mygaruIdSystem_spec.js diff --git a/modules/.submodules.json b/modules/.submodules.json index 830d3c9b2f3..d2a13a57330 100644 --- a/modules/.submodules.json +++ b/modules/.submodules.json @@ -47,7 +47,8 @@ "adqueryIdSystem", "gravitoIdSystem", "freepassIdSystem", - "operaadsIdSystem" + "operaadsIdSystem", + "mygaruIdSystem" ], "adpod": [ "freeWheelAdserverVideo", diff --git a/modules/mygaruIdSystem.js b/modules/mygaruIdSystem.js new file mode 100644 index 00000000000..4d50de16d48 --- /dev/null +++ b/modules/mygaruIdSystem.js @@ -0,0 +1,98 @@ +/** + * This module adds MyGaru Real Time User Sync to the User ID module + * The {@link module:modules/userId} module is required + * @module modules/mygaruIdSystem + * @requires module:modules/userId + */ + +import { ajax } from '../src/ajax.js'; +import { submodule } from '../src/hook.js'; + +const bidderCode = 'mygaruId'; +const syncUrl = 'https://ident.mygaru.com/v2/id'; + +export function buildUrl(opts) { + const queryPairs = []; + for (let key in opts) { + if (opts[key] !== undefined) { + queryPairs.push(`${key}=${encodeURIComponent(opts[key])}`); + } + } + return `${syncUrl}?${queryPairs.join('&')}`; +} + +function requestRemoteIdAsync(url) { + return new Promise((resolve) => { + ajax( + url, + { + success: response => { + try { + const jsonResponse = JSON.parse(response); + const { iuid } = jsonResponse; + resolve(iuid); + } catch (e) { + resolve(); + } + }, + error: () => { + resolve(); + }, + }, + undefined, + { + method: 'GET', + contentType: 'application/json' + } + ); + }); +} + +/** @type {Submodule} */ +export const mygaruIdSubmodule = { + /** + * used to link submodule with config + * @type {string} + */ + name: bidderCode, + /** + * decode the stored id value for passing to bid requests + * @function + * @returns {{id: string} | null} + */ + decode(id) { + return id; + }, + /** + * get the MyGaru Id from local storages and initiate a new user sync + * @function + * @param {SubmoduleConfig} [config] + * @param {ConsentData} [consentData] + * @returns {{id: string | undefined}} + */ + getId(config, consentData) { + const gdprApplies = consentData && typeof consentData.gdprApplies === 'boolean' && consentData.gdprApplies ? 1 : 0; + const gdprConsentString = gdprApplies ? consentData.consentString : undefined; + const url = buildUrl({ + gdprApplies, + gdprConsentString + }); + + return { + url, + callback: function (done) { + return requestRemoteIdAsync(url).then((id) => { + done({ mygaruId: id }); + }) + } + } + }, + eids: { + 'mygaruId': { + source: 'mygaru.com', + atype: 1 + }, + } +}; + +submodule('userId', mygaruIdSubmodule); diff --git a/modules/mygaruIdSystem.md b/modules/mygaruIdSystem.md new file mode 100644 index 00000000000..92724f99469 --- /dev/null +++ b/modules/mygaruIdSystem.md @@ -0,0 +1,24 @@ +## Mygaru User ID Submodule + +MyGaru provides single use tokens as a UserId for SSPs and DSP that consume telecom DMP data. + +## Building Prebid with Mygaru ID Support + +First, make sure to add submodule to your Prebid.js package with: + +``` +gulp build --modules=userId,mygaruIdSystem +``` +Params configuration is not required. +Also mygaru is async, in order to get ids for initial ad auctions you need to add auctionDelay param to userSync config. + +```javascript +pbjs.setConfig({ + userSync: { + auctionDelay: 100, + userIds: [{ + name: 'mygaruId', + }] + } +}); +``` diff --git a/modules/userId/eids.md b/modules/userId/eids.md index eb37c50f0a3..c8d27cc9d52 100644 --- a/modules/userId/eids.md +++ b/modules/userId/eids.md @@ -324,6 +324,13 @@ userIdAsEids = [ id: 'some-random-id-value', atype: 3 }] + }, + { + source: 'mygaru.com', + uids: [{ + id: 'some-random-id-value', + atype: 1 + }] } ] ``` diff --git a/modules/userId/userId.md b/modules/userId/userId.md index 01930a67dda..7a01e128814 100644 --- a/modules/userId/userId.md +++ b/modules/userId/userId.md @@ -158,6 +158,9 @@ pbjs.setConfig({ }, { name: "gravitompId" + }, + { + name: "mygaruId" } ], syncDelay: 5000, diff --git a/test/spec/modules/mygaruIdSystem_spec.js b/test/spec/modules/mygaruIdSystem_spec.js new file mode 100644 index 00000000000..2bfb5fdd4af --- /dev/null +++ b/test/spec/modules/mygaruIdSystem_spec.js @@ -0,0 +1,62 @@ +import { mygaruIdSubmodule } from 'modules/mygaruIdSystem.js'; +import { server } from '../../mocks/xhr'; + +describe('MygaruID module', function () { + it('should respond with async callback and get valid id', async () => { + const callBackSpy = sinon.spy(); + const expectedUrl = `https://ident.mygaru.com/v2/id?gdprApplies=0`; + const result = mygaruIdSubmodule.getId({}); + + expect(result.callback).to.be.an('function'); + const promise = result.callback(callBackSpy); + + const request = server.requests[0]; + expect(request.url).to.be.eq(expectedUrl); + + request.respond( + 200, + { 'Content-Type': 'application/json' }, + JSON.stringify({ iuid: '123' }) + ); + await promise; + + expect(callBackSpy.calledOnce).to.be.true; + expect(callBackSpy.calledWith({mygaruId: '123'})).to.be.true; + }); + it('should not fail on error', async () => { + const callBackSpy = sinon.spy(); + const expectedUrl = `https://ident.mygaru.com/v2/id?gdprApplies=0`; + const result = mygaruIdSubmodule.getId({}); + + expect(result.callback).to.be.an('function'); + const promise = result.callback(callBackSpy); + + const request = server.requests[0]; + expect(request.url).to.be.eq(expectedUrl); + + request.respond( + 500, + {}, + '' + ); + await promise; + + expect(callBackSpy.calledOnce).to.be.true; + expect(callBackSpy.calledWith({mygaruId: undefined})).to.be.true; + }); + + it('should not modify while decoding', () => { + const id = '222'; + const newId = mygaruIdSubmodule.decode(id) + + expect(id).to.eq(newId); + }) + it('should buildUrl with consent data', () => { + const result = mygaruIdSubmodule.getId({}, { + gdprApplies: true, + consentString: 'consentString' + }); + + expect(result.url).to.eq('https://ident.mygaru.com/v2/id?gdprApplies=1&gdprConsentString=consentString'); + }) +}); From ed054783d255943538ee427a3b019ab8973aa5df Mon Sep 17 00:00:00 2001 From: tkrishnaviant Date: Wed, 3 Jan 2024 06:07:13 -0800 Subject: [PATCH 105/246] Update viantOrtbBidAdapter_spec.js (#10888) Added a test case for native ads --- test/spec/modules/viantOrtbBidAdapter_spec.js | 43 +++++++++++++++++++ 1 file changed, 43 insertions(+) diff --git a/test/spec/modules/viantOrtbBidAdapter_spec.js b/test/spec/modules/viantOrtbBidAdapter_spec.js index 6d5014bd639..73fdb7f3dc8 100644 --- a/test/spec/modules/viantOrtbBidAdapter_spec.js +++ b/test/spec/modules/viantOrtbBidAdapter_spec.js @@ -109,6 +109,49 @@ describe('viantOrtbBidAdapter', function () { }); }); }); + + describe('native', function () { + describe('and request config uses mediaTypes', () => { + function makeBid() { + return { + 'bidder': 'viant', + 'params': { + 'unit': '12345678', + 'delDomain': 'test-del-domain', + 'publisherId': '464', + 'placementId': 'some-PlacementId_2' + }, + 'mediaTypes': { + 'video': { + 'context': 'instream', + 'playerSize': [[640, 480]], + 'mimes': ['video/mp4'], + 'protocols': [1, 2, 3, 4, 5, 6, 7, 8], + 'api': [1, 3], + 'skip': 1, + 'skipafter': 5, + 'minduration': 10, + 'maxduration': 30 + } + }, + 'adUnitCode': 'adunit-code', + 'bidId': '30b31c1838de1e', + 'bidderRequestId': '22edbae2733bf6', + 'auctionId': '1d1a030790a475', + 'transactionId': '4008d88a-8137-410b-aa35-fbfdabcb478e' + } + } + it('should return true when required params found', function () { + expect(spec.isBidRequestValid(makeBid())).to.equal(true); + }); + + it('should return false when required params are not passed', function () { + let nativeBidWithMediaTypes = Object.assign({}, makeBid()); + nativeBidWithMediaTypes.params = {}; + expect(spec.isBidRequestValid(nativeBidWithMediaTypes)).to.equal(false); + }); + }); + }); }); describe('buildRequests-banner', function () { From 665c89498e059eb550540985acc84ba1ae275e06 Mon Sep 17 00:00:00 2001 From: theo-stv <120092078+theo-stv@users.noreply.github.com> Date: Wed, 3 Jan 2024 15:24:08 +0100 Subject: [PATCH 106/246] Stv Bid Adapter: added user id support, adaptation of schain support (#10849) * initial commit * adapted buildRequests function * refinement pfilter and bcat * refinement * adapted tests for isBidRequestValid,buildRequests * adaptations for test * finished building stvBidAdapter.js * finished: ran tests, coverage 99% * update: rename w->srw, h->srh * adapt stvBidAdapter.md * remove dspx from stv adapters * some changes (missing: getUserSyncs, but is the same as in radsBidAdapter) * added checks in getUserSyncs; ran tests * added schain support (94.8% coverage) * correct schain encoding * added serializeUids and adapted serializeSChain --------- Co-authored-by: theo_ --- modules/stvBidAdapter.js | 59 +++++++++++++++++++++++-- test/spec/modules/stvBidAdapter_spec.js | 44 ++++++++++++++++-- 2 files changed, 97 insertions(+), 6 deletions(-) diff --git a/modules/stvBidAdapter.js b/modules/stvBidAdapter.js index 8d368b996fc..0dacf389955 100644 --- a/modules/stvBidAdapter.js +++ b/modules/stvBidAdapter.js @@ -51,6 +51,7 @@ export const spec = { bid_id: bidId, pbver: '$prebid.version$', schain: '', + uids: '', }; if (!isVideoRequest(bidRequest)) { payload._f = 'html'; @@ -61,6 +62,11 @@ export const spec = { delete payload.schain; } + payload.uids = serializeUids(bidRequest); + if (payload.uids == '') { + delete payload.uids; + } + payload.pfilter = { ...params }; delete payload.pfilter.placement; if (params.bcat !== undefined) { delete payload.pfilter.bcat; } @@ -201,7 +207,7 @@ function objectToQueryString(obj, prefix) { let v = obj[p]; str.push((v !== null && typeof v === 'object') ? objectToQueryString(v, k) - : (k == 'schain' ? k + '=' + v : encodeURIComponent(k) + '=' + encodeURIComponent(v))); + : (k == 'schain' || k == 'uids' ? k + '=' + v : encodeURIComponent(k) + '=' + encodeURIComponent(v))); } } return str.join('&'); @@ -227,13 +233,60 @@ function serializeSChain(schain) { ret += encodeURIComponent(node.name ?? ''); ret += ','; ret += encodeURIComponent(node.domain ?? ''); - ret += ','; - ret += encodeURIComponent(node.ext ?? ''); + if (node.ext) { + ret += ','; + ret += encodeURIComponent(node.ext ?? ''); + } } return ret; } +function serializeUids(bidRequest) { + let uids = []; + + let id5 = deepAccess(bidRequest, 'userId.id5id.uid'); + if (id5) { + uids.push(encodeURIComponent('id5:' + id5)); + let id5Linktype = deepAccess(bidRequest, 'userId.id5id.ext.linkType'); + if (id5Linktype) { + uids.push(encodeURIComponent('id5_linktype:' + id5Linktype)); + } + } + let netId = deepAccess(bidRequest, 'userId.netId'); + if (netId) { + uids.push(encodeURIComponent('netid:' + netId)); + } + let uId2 = deepAccess(bidRequest, 'userId.uid2.id'); + if (uId2) { + uids.push(encodeURIComponent('uid2:' + uId2)); + } + let sharedId = deepAccess(bidRequest, 'userId.sharedid.id'); + if (sharedId) { + uids.push(encodeURIComponent('sharedid:' + sharedId)); + } + let liverampId = deepAccess(bidRequest, 'userId.idl_env'); + if (liverampId) { + uids.push(encodeURIComponent('liverampid:' + liverampId)); + } + let criteoId = deepAccess(bidRequest, 'userId.criteoId'); + if (criteoId) { + uids.push(encodeURIComponent('criteoid:' + criteoId)); + } + // documentation missing... + let utiqId = deepAccess(bidRequest, 'userId.utiq.id'); + if (utiqId) { + uids.push(encodeURIComponent('utiq:' + utiqId)); + } else { + utiqId = deepAccess(bidRequest, 'userId.utiq'); + if (utiqId) { + uids.push(encodeURIComponent('utiq:' + utiqId)); + } + } + + return uids.join(','); +} + /** * Check if it's a banner bid request * diff --git a/test/spec/modules/stvBidAdapter_spec.js b/test/spec/modules/stvBidAdapter_spec.js index 41f29cced34..3ef865ed2f1 100644 --- a/test/spec/modules/stvBidAdapter_spec.js +++ b/test/spec/modules/stvBidAdapter_spec.js @@ -71,6 +71,24 @@ describe('stvAdapter', function() { 'hp': 1 } ] + }, + 'userId': { + 'id5id': { + 'uid': '1234', + 'ext': { + 'linkType': 'abc' + } + }, + 'netId': '2345', + 'uid2': { + 'id': '3456', + }, + 'sharedid': { + 'id': '4567', + }, + 'idl_env': '5678', + 'criteoId': '6789', + 'utiq': '7890', } }, { @@ -84,7 +102,27 @@ describe('stvAdapter', function() { ], 'bidId': '30b31c1838de1e2', 'bidderRequestId': '22edbae2733bf62', - 'auctionId': '1d1a030790a476' + 'auctionId': '1d1a030790a476', + 'userId': { // with other utiq variant + 'id5id': { + 'uid': '1234', + 'ext': { + 'linkType': 'abc' + } + }, + 'netId': '2345', + 'uid2': { + 'id': '3456', + }, + 'sharedid': { + 'id': '4567', + }, + 'idl_env': '5678', + 'criteoId': '6789', + 'utiq': { + 'id': '7890' + }, + } }, { 'bidder': 'stv', 'params': { @@ -181,7 +219,7 @@ describe('stvAdapter', 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').replace(/pbver=.*?&/g, 'pbver=test&'); - expect(data).to.equal('_f=html&alternative=prebid_js&_ps=6682&srw=300&srh=250&idt=100&bid_id=30b31c1838de1e1&pbver=test&schain=1.0,0!reseller.com,aaaaa,1,BidRequest4,,,&pfilter%5Bfloorprice%5D=1000000&pfilter%5Bgeo%5D%5Bcountry%5D=DE&gdpr_consent=BOJ%2FP2HOJ%2FP2HABABMAAAAAZ%2BA%3D%3D&gdpr=true&bcat=IAB2%2CIAB4&dvt=desktop&pbcode=testDiv1&media_types%5Bbanner%5D=300x250'); + expect(data).to.equal('_f=html&alternative=prebid_js&_ps=6682&srw=300&srh=250&idt=100&bid_id=30b31c1838de1e1&pbver=test&schain=1.0,0!reseller.com,aaaaa,1,BidRequest4,,&uids=id5%3A1234,id5_linktype%3Aabc,netid%3A2345,uid2%3A3456,sharedid%3A4567,liverampid%3A5678,criteoid%3A6789,utiq%3A7890&pfilter%5Bfloorprice%5D=1000000&pfilter%5Bgeo%5D%5Bcountry%5D=DE&gdpr_consent=BOJ%2FP2HOJ%2FP2HABABMAAAAAZ%2BA%3D%3D&gdpr=true&bcat=IAB2%2CIAB4&dvt=desktop&pbcode=testDiv1&media_types%5Bbanner%5D=300x250'); }); var request2 = spec.buildRequests([bidRequests[1]], bidderRequest)[0]; @@ -189,7 +227,7 @@ describe('stvAdapter', function() { expect(request2.method).to.equal('GET'); expect(request2.url).to.equal(ENDPOINT_URL); let data = request2.data.replace(/rnd=\d+\&/g, '').replace(/ref=.*\&bid/g, 'bid').replace(/pbver=.*?&/g, 'pbver=test&'); - expect(data).to.equal('_f=html&alternative=prebid_js&_ps=101&srw=300&srh=250&idt=100&bid_id=30b31c1838de1e2&pbver=test&gdpr_consent=BOJ%2FP2HOJ%2FP2HABABMAAAAAZ%2BA%3D%3D&gdpr=true&prebidDevMode=1&media_types%5Bbanner%5D=300x250'); + expect(data).to.equal('_f=html&alternative=prebid_js&_ps=101&srw=300&srh=250&idt=100&bid_id=30b31c1838de1e2&pbver=test&uids=id5%3A1234,id5_linktype%3Aabc,netid%3A2345,uid2%3A3456,sharedid%3A4567,liverampid%3A5678,criteoid%3A6789,utiq%3A7890&gdpr_consent=BOJ%2FP2HOJ%2FP2HABABMAAAAAZ%2BA%3D%3D&gdpr=true&prebidDevMode=1&media_types%5Bbanner%5D=300x250'); }); // Without gdprConsent From 185e97f9cd073ee8cf29ac85c2ebb10249526afa Mon Sep 17 00:00:00 2001 From: Petre Damoc Date: Wed, 3 Jan 2024 16:40:32 +0200 Subject: [PATCH 107/246] Missena Bid Adapter: allow per page capping (#10863) --- modules/missenaBidAdapter.js | 4 +- test/spec/modules/missenaBidAdapter_spec.js | 41 ++++++++++++++++++++- 2 files changed, 42 insertions(+), 3 deletions(-) diff --git a/modules/missenaBidAdapter.js b/modules/missenaBidAdapter.js index a06ba1ce29c..5e68f56ed1c 100644 --- a/modules/missenaBidAdapter.js +++ b/modules/missenaBidAdapter.js @@ -59,9 +59,11 @@ export const spec = { buildRequests: function (validBidRequests, bidderRequest) { const capKey = `missena.missena.capper.remove-bubble.${validBidRequests[0]?.params.apiKey}`; const capping = safeJSONParse(storage.getDataFromLocalStorage(capKey)); + const referer = bidderRequest?.refererInfo?.topmostLocation; if ( typeof capping?.expiry === 'number' && - new Date().getTime() < capping?.expiry + new Date().getTime() < capping?.expiry && + (!capping?.referer || capping?.referer == referer) ) { logInfo('Missena - Capped'); return []; diff --git a/test/spec/modules/missenaBidAdapter_spec.js b/test/spec/modules/missenaBidAdapter_spec.js index 15f9eff89e7..a51cb8bbac9 100644 --- a/test/spec/modules/missenaBidAdapter_spec.js +++ b/test/spec/modules/missenaBidAdapter_spec.js @@ -2,6 +2,9 @@ import { expect } from 'chai'; import { spec, storage } from 'modules/missenaBidAdapter.js'; import { BANNER } from '../../../src/mediaTypes.js'; +const REFERRER = 'https://referer'; +const REFERRER2 = 'https://referer2'; + describe('Missena Adapter', function () { $$PREBID_GLOBAL$$.bidderSettings = { missena: { @@ -50,7 +53,7 @@ describe('Missena Adapter', function () { gdprApplies: true, }, refererInfo: { - topmostLocation: 'https://referer', + topmostLocation: REFERRER, canonicalUrl: 'https://canonical', }, }; @@ -112,7 +115,7 @@ describe('Missena Adapter', function () { }); it('should send referer information to the request', function () { - expect(payload.referer).to.equal('https://referer'); + expect(payload.referer).to.equal(REFERRER); expect(payload.referer_canonical).to.equal('https://canonical'); }); @@ -146,6 +149,40 @@ describe('Missena Adapter', function () { it('should not participate if capped', function () { expect(cappedRequests.length).to.equal(0); }); + + const localStorageDataSamePage = { + [`missena.missena.capper.remove-bubble.${bid.params.apiKey}`]: + JSON.stringify({ + expiry: new Date().getTime() + 600_000, // 10 min into the future + referer: REFERRER, + }), + }; + + getDataFromLocalStorageStub.callsFake( + (key) => localStorageDataSamePage[key], + ); + const cappedRequestsSamePage = spec.buildRequests(bids, bidderRequest); + + it('should not participate if capped on same page', function () { + expect(cappedRequestsSamePage.length).to.equal(0); + }); + + const localStorageDataOtherPage = { + [`missena.missena.capper.remove-bubble.${bid.params.apiKey}`]: + JSON.stringify({ + expiry: new Date().getTime() + 600_000, // 10 min into the future + referer: REFERRER2, + }), + }; + + getDataFromLocalStorageStub.callsFake( + (key) => localStorageDataOtherPage[key], + ); + const cappedRequestsOtherPage = spec.buildRequests(bids, bidderRequest); + + it('should participate if capped on a different page', function () { + expect(cappedRequestsOtherPage.length).to.equal(2); + }); }); describe('interpretResponse', function () { From 49904a04c1796187eae6fecb4f827a375b3cb73d Mon Sep 17 00:00:00 2001 From: Wiem Zine El Abidine Date: Wed, 3 Jan 2024 21:15:45 +0100 Subject: [PATCH 108/246] LiveIntent ID Module: Update live-connect version (#10894) * update lc version * fix typo --- package-lock.json | 32 +++++++------- package.json | 2 +- .../modules/liveIntentIdMinimalSystem_spec.js | 4 +- test/spec/modules/liveIntentIdSystem_spec.js | 42 +++++++++---------- 4 files changed, 40 insertions(+), 40 deletions(-) diff --git a/package-lock.json b/package-lock.json index 65b4011bca8..11d2f1fd427 100644 --- a/package-lock.json +++ b/package-lock.json @@ -6,7 +6,7 @@ "packages": { "": { "name": "prebid.js", - "version": "8.24.0-pre", + "version": "8.30.0-pre", "license": "Apache-2.0", "dependencies": { "@babel/core": "^7.16.7", @@ -22,7 +22,7 @@ "express": "^4.15.4", "fun-hooks": "^0.9.9", "just-clone": "^1.0.2", - "live-connect-js": "^6.3.0" + "live-connect-js": "^6.3.2" }, "devDependencies": { "@babel/eslint-parser": "^7.16.5", @@ -16336,19 +16336,19 @@ "dev": true }, "node_modules/live-connect-common": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/live-connect-common/-/live-connect-common-3.0.2.tgz", - "integrity": "sha512-K3LNKd9CpREDJbXGdwKqPojjQaxd4G6c7OAD6Yzp3wsCWTH2hV8xNAbUksSOpOcVyyOT9ilteEFXIJQJrbODxQ==", + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/live-connect-common/-/live-connect-common-3.0.3.tgz", + "integrity": "sha512-ZPycT04ROBUvPiksnLTunrKC3ROhBSeO99fQ+4qMIkgKwP2CvS44L7fK+0WFV4nAi+65KbzSng7JWcSlckfw8w==", "engines": { "node": ">=18" } }, "node_modules/live-connect-js": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/live-connect-js/-/live-connect-js-6.3.0.tgz", - "integrity": "sha512-1SnXQZq9gxHIb0scXPX1Da1rQ0oY2sloMGgeRreTAwhCtdQEuip/IYwgOh3/ZeZ6yT6iG9FLb7+AjORC4pO46g==", + "version": "6.3.3", + "resolved": "https://registry.npmjs.org/live-connect-js/-/live-connect-js-6.3.3.tgz", + "integrity": "sha512-njpWQgoJuXhxm+XBJQvT672wR20/qROPUfUKwIXPwEtGXznJYlrUDPJDChEknBiKIo7P4jOeirAMwcZWPCN4ow==", "dependencies": { - "live-connect-common": "^v3.0.2", + "live-connect-common": "^v3.0.3", "tiny-hashes": "1.0.1" }, "engines": { @@ -38079,16 +38079,16 @@ "dev": true }, "live-connect-common": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/live-connect-common/-/live-connect-common-3.0.2.tgz", - "integrity": "sha512-K3LNKd9CpREDJbXGdwKqPojjQaxd4G6c7OAD6Yzp3wsCWTH2hV8xNAbUksSOpOcVyyOT9ilteEFXIJQJrbODxQ==" + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/live-connect-common/-/live-connect-common-3.0.3.tgz", + "integrity": "sha512-ZPycT04ROBUvPiksnLTunrKC3ROhBSeO99fQ+4qMIkgKwP2CvS44L7fK+0WFV4nAi+65KbzSng7JWcSlckfw8w==" }, "live-connect-js": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/live-connect-js/-/live-connect-js-6.3.0.tgz", - "integrity": "sha512-1SnXQZq9gxHIb0scXPX1Da1rQ0oY2sloMGgeRreTAwhCtdQEuip/IYwgOh3/ZeZ6yT6iG9FLb7+AjORC4pO46g==", + "version": "6.3.3", + "resolved": "https://registry.npmjs.org/live-connect-js/-/live-connect-js-6.3.3.tgz", + "integrity": "sha512-njpWQgoJuXhxm+XBJQvT672wR20/qROPUfUKwIXPwEtGXznJYlrUDPJDChEknBiKIo7P4jOeirAMwcZWPCN4ow==", "requires": { - "live-connect-common": "^v3.0.2", + "live-connect-common": "^v3.0.3", "tiny-hashes": "1.0.1" } }, diff --git a/package.json b/package.json index 85642969f06..443cf0fc503 100644 --- a/package.json +++ b/package.json @@ -133,7 +133,7 @@ "express": "^4.15.4", "fun-hooks": "^0.9.9", "just-clone": "^1.0.2", - "live-connect-js": "^6.3.0" + "live-connect-js": "^6.3.2" }, "optionalDependencies": { "fsevents": "^2.3.2" diff --git a/test/spec/modules/liveIntentIdMinimalSystem_spec.js b/test/spec/modules/liveIntentIdMinimalSystem_spec.js index 0ada7adc594..ad21d7b6763 100644 --- a/test/spec/modules/liveIntentIdMinimalSystem_spec.js +++ b/test/spec/modules/liveIntentIdMinimalSystem_spec.js @@ -73,7 +73,7 @@ describe('LiveIntentMinimalId', function() { expect(callBackSpy.calledOnce).to.be.true; }); - it('should call the Identity Exchange endpoint with the privided distributorId', function() { + it('should call the Identity Exchange endpoint with the provided distributorId', function() { getCookieStub.returns(null); let callBackSpy = sinon.spy(); let submoduleCallback = liveIntentIdSubmodule.getId({ params: { fireEventDelay: 1, distributorId: 'did-1111' } }).callback; @@ -87,7 +87,7 @@ describe('LiveIntentMinimalId', function() { expect(callBackSpy.calledOnceWith({})).to.be.true; }); - it('should call the Identity Exchange endpoint without the privided distributorId when appId is provided', function() { + it('should call the Identity Exchange endpoint without the provided distributorId when appId is provided', function() { getCookieStub.returns(null); let callBackSpy = sinon.spy(); let submoduleCallback = liveIntentIdSubmodule.getId({ params: { fireEventDelay: 1, distributorId: 'did-1111', liCollectConfig: { appId: 'a-0001' } } }).callback; diff --git a/test/spec/modules/liveIntentIdSystem_spec.js b/test/spec/modules/liveIntentIdSystem_spec.js index 506c5b312cc..4af88e1a894 100644 --- a/test/spec/modules/liveIntentIdSystem_spec.js +++ b/test/spec/modules/liveIntentIdSystem_spec.js @@ -186,7 +186,7 @@ describe('LiveIntentId', function() { 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?resolve=nonId'); + expect(request.url).to.be.eq('https://dummy.liveintent.com/idex/prebid/89899?cd=.localhost&resolve=nonId'); request.respond( 204, responseHeader @@ -194,13 +194,13 @@ describe('LiveIntentId', function() { expect(callBackSpy.calledOnceWith({})).to.be.true; }); - it('should call the Identity Exchange endpoint with the privided distributorId', function() { + it('should call the Identity Exchange endpoint with the provided distributorId', function() { getCookieStub.returns(null); let callBackSpy = sinon.spy(); let submoduleCallback = liveIntentIdSubmodule.getId({ params: { fireEventDelay: 1, distributorId: 'did-1111' } }).callback; submoduleCallback(callBackSpy); let request = server.requests[0]; - expect(request.url).to.be.eq('https://idx.liadm.com/idex/did-1111/any?did=did-1111&resolve=nonId'); + expect(request.url).to.be.eq('https://idx.liadm.com/idex/did-1111/any?did=did-1111&cd=.localhost&resolve=nonId'); request.respond( 204, responseHeader @@ -208,13 +208,13 @@ describe('LiveIntentId', function() { expect(callBackSpy.calledOnceWith({})).to.be.true; }); - it('should call the Identity Exchange endpoint without the privided distributorId when appId is provided', function() { + it('should call the Identity Exchange endpoint without the provided distributorId when appId is provided', function() { getCookieStub.returns(null); let callBackSpy = sinon.spy(); let submoduleCallback = liveIntentIdSubmodule.getId({ params: { fireEventDelay: 1, distributorId: 'did-1111', liCollectConfig: { appId: 'a-0001' } } }).callback; submoduleCallback(callBackSpy); let request = server.requests[0]; - expect(request.url).to.be.eq('https://idx.liadm.com/idex/prebid/any?resolve=nonId'); + expect(request.url).to.be.eq('https://idx.liadm.com/idex/prebid/any?cd=.localhost&resolve=nonId'); request.respond( 204, responseHeader @@ -234,7 +234,7 @@ describe('LiveIntentId', function() { } }).callback; submoduleCallback(callBackSpy); let request = server.requests[0]; - expect(request.url).to.be.eq('https://dummy.liveintent.com/idex/rubicon/89899?resolve=nonId'); + expect(request.url).to.be.eq('https://dummy.liveintent.com/idex/rubicon/89899?cd=.localhost&resolve=nonId'); request.respond( 200, responseHeader, @@ -249,7 +249,7 @@ describe('LiveIntentId', function() { let submoduleCallback = liveIntentIdSubmodule.getId(defaultConfigParams).callback; submoduleCallback(callBackSpy); let request = server.requests[0]; - expect(request.url).to.be.eq('https://idx.liadm.com/idex/prebid/89899?resolve=nonId'); + expect(request.url).to.be.eq('https://idx.liadm.com/idex/prebid/89899?cd=.localhost&resolve=nonId'); request.respond( 200, responseHeader, @@ -264,7 +264,7 @@ describe('LiveIntentId', function() { let submoduleCallback = liveIntentIdSubmodule.getId(defaultConfigParams).callback; submoduleCallback(callBackSpy); let request = server.requests[0]; - expect(request.url).to.be.eq('https://idx.liadm.com/idex/prebid/89899?resolve=nonId'); + expect(request.url).to.be.eq('https://idx.liadm.com/idex/prebid/89899?cd=.localhost&resolve=nonId'); request.respond( 503, responseHeader, @@ -281,7 +281,7 @@ describe('LiveIntentId', function() { let submoduleCallback = liveIntentIdSubmodule.getId(defaultConfigParams).callback; submoduleCallback(callBackSpy); let request = server.requests[0]; - expect(request.url).to.be.eq(`https://idx.liadm.com/idex/prebid/89899?duid=${oldCookie}&resolve=nonId`); + expect(request.url).to.be.eq(`https://idx.liadm.com/idex/prebid/89899?duid=${oldCookie}&cd=.localhost&resolve=nonId`); request.respond( 200, responseHeader, @@ -304,7 +304,7 @@ describe('LiveIntentId', function() { let submoduleCallback = liveIntentIdSubmodule.getId(configParams).callback; submoduleCallback(callBackSpy); let request = server.requests[0]; - expect(request.url).to.be.eq(`https://idx.liadm.com/idex/prebid/89899?duid=${oldCookie}&_thirdPC=third-pc&resolve=nonId`); + expect(request.url).to.be.eq(`https://idx.liadm.com/idex/prebid/89899?duid=${oldCookie}&cd=.localhost&_thirdPC=third-pc&resolve=nonId`); request.respond( 200, responseHeader, @@ -326,7 +326,7 @@ describe('LiveIntentId', function() { let submoduleCallback = liveIntentIdSubmodule.getId(configParams).callback; submoduleCallback(callBackSpy); let request = server.requests[0]; - expect(request.url).to.be.eq('https://idx.liadm.com/idex/prebid/89899?_thirdPC=%7B%22key%22%3A%22value%22%7D&resolve=nonId'); + expect(request.url).to.be.eq('https://idx.liadm.com/idex/prebid/89899?cd=.localhost&_thirdPC=%7B%22key%22%3A%22value%22%7D&resolve=nonId'); request.respond( 200, responseHeader, @@ -359,7 +359,7 @@ describe('LiveIntentId', function() { } }).callback; submoduleCallback(callBackSpy); let request = server.requests[0]; - expect(request.url).to.be.eq(`https://idx.liadm.com/idex/prebid/89899?resolve=nonId&resolve=foo`); + expect(request.url).to.be.eq(`https://idx.liadm.com/idex/prebid/89899?cd=.localhost&resolve=nonId&resolve=foo`); request.respond( 200, responseHeader, @@ -368,7 +368,7 @@ describe('LiveIntentId', function() { expect(callBackSpy.calledOnce).to.be.true; }); - it('should decode a uid2 to a seperate object when present', function() { + it('should decode a uid2 to a separate object when present', function() { const result = liveIntentIdSubmodule.decode({ nonId: 'foo', uid2: 'bar' }); expect(result).to.eql({'lipb': {'lipbid': 'foo', 'nonId': 'foo', 'uid2': 'bar'}, 'uid2': {'id': 'bar', 'ext': {'provider': 'liveintent.com'}}}); }); @@ -378,37 +378,37 @@ describe('LiveIntentId', function() { expect(result).to.eql({'uid2': {'id': 'bar', 'ext': {'provider': 'liveintent.com'}}}); }); - it('should decode a bidswitch id to a seperate object when present', function() { + it('should decode a bidswitch id to a separate object when present', function() { const result = liveIntentIdSubmodule.decode({ nonId: 'foo', bidswitch: 'bar' }); expect(result).to.eql({'lipb': {'lipbid': 'foo', 'nonId': 'foo', 'bidswitch': 'bar'}, 'bidswitch': {'id': 'bar', 'ext': {'provider': 'liveintent.com'}}}); }); - it('should decode a medianet id to a seperate object when present', function() { + it('should decode a medianet id to a separate object when present', function() { const result = liveIntentIdSubmodule.decode({ nonId: 'foo', medianet: 'bar' }); expect(result).to.eql({'lipb': {'lipbid': 'foo', 'nonId': 'foo', 'medianet': 'bar'}, 'medianet': {'id': 'bar', 'ext': {'provider': 'liveintent.com'}}}); }); - it('should decode a sovrn id to a seperate object when present', function() { + it('should decode a sovrn id to a separate object when present', function() { const result = liveIntentIdSubmodule.decode({ nonId: 'foo', sovrn: 'bar' }); expect(result).to.eql({'lipb': {'lipbid': 'foo', 'nonId': 'foo', 'sovrn': 'bar'}, 'sovrn': {'id': 'bar', 'ext': {'provider': 'liveintent.com'}}}); }); - it('should decode a magnite id to a seperate object when present', function() { + it('should decode a magnite id to a separate object when present', function() { const result = liveIntentIdSubmodule.decode({ nonId: 'foo', magnite: 'bar' }); expect(result).to.eql({'lipb': {'lipbid': 'foo', 'nonId': 'foo', 'magnite': 'bar'}, 'magnite': {'id': 'bar', 'ext': {'provider': 'liveintent.com'}}}); }); - it('should decode an index id to a seperate object when present', function() { + it('should decode an index id to a separate object when present', function() { const result = liveIntentIdSubmodule.decode({ nonId: 'foo', index: 'bar' }); expect(result).to.eql({'lipb': {'lipbid': 'foo', 'nonId': 'foo', 'index': 'bar'}, 'index': {'id': 'bar', 'ext': {'provider': 'liveintent.com'}}}); }); - it('should decode an openx id to a seperate object when present', function () { + it('should decode an openx id to a separate object when present', function () { const result = liveIntentIdSubmodule.decode({ nonId: 'foo', openx: 'bar' }); expect(result).to.eql({'lipb': {'lipbid': 'foo', 'nonId': 'foo', 'openx': 'bar'}, 'openx': {'id': 'bar', 'ext': {'provider': 'liveintent.com'}}}); }); - it('should decode an pubmatic id to a seperate object when present', function() { + it('should decode an pubmatic id to a separate object when present', function() { const result = liveIntentIdSubmodule.decode({ nonId: 'foo', pubmatic: 'bar' }); expect(result).to.eql({'lipb': {'lipbid': 'foo', 'nonId': 'foo', 'pubmatic': 'bar'}, 'pubmatic': {'id': 'bar', 'ext': {'provider': 'liveintent.com'}}}); }); @@ -421,7 +421,7 @@ describe('LiveIntentId', function() { } }).callback; submoduleCallback(callBackSpy); let request = server.requests[0]; - expect(request.url).to.be.eq(`https://idx.liadm.com/idex/prebid/89899?resolve=uid2`); + expect(request.url).to.be.eq(`https://idx.liadm.com/idex/prebid/89899?cd=.localhost&resolve=uid2`); request.respond( 200, responseHeader, From 136f0bba595d4edda0023774ce59b65a64bb28a7 Mon Sep 17 00:00:00 2001 From: Robert Ray Martinez III Date: Wed, 3 Jan 2024 12:18:53 -0800 Subject: [PATCH 109/246] Do not require API for video requests (#10895) --- modules/rubiconBidAdapter.js | 3 +-- test/spec/modules/rubiconBidAdapter_spec.js | 10 ---------- 2 files changed, 1 insertion(+), 12 deletions(-) diff --git a/modules/rubiconBidAdapter.js b/modules/rubiconBidAdapter.js index bff7661455a..226950875e2 100644 --- a/modules/rubiconBidAdapter.js +++ b/modules/rubiconBidAdapter.js @@ -1183,8 +1183,7 @@ export function hasValidVideoParams(bid) { var requiredParams = { mimes: arrayType, protocols: arrayType, - linearity: numberType, - api: arrayType + linearity: numberType } // loop through each param and verify it has the correct Object.keys(requiredParams).forEach(function(param) { diff --git a/test/spec/modules/rubiconBidAdapter_spec.js b/test/spec/modules/rubiconBidAdapter_spec.js index 3f3cbfbb46c..f0e33ce940e 100644 --- a/test/spec/modules/rubiconBidAdapter_spec.js +++ b/test/spec/modules/rubiconBidAdapter_spec.js @@ -2381,16 +2381,6 @@ describe('the rubicon adapter', function () { bidderRequest = createVideoBidderRequest(); delete bidderRequest.bids[0].mediaTypes.video.linearity; expect(spec.isBidRequestValid(bidderRequest.bids[0])).to.equal(false); - - // change api to an string, no good - bidderRequest = createVideoBidderRequest(); - bidderRequest.bids[0].mediaTypes.video.api = 'string'; - expect(spec.isBidRequestValid(bidderRequest.bids[0])).to.equal(false); - - // delete api, no good - bidderRequest = createVideoBidderRequest(); - delete bidderRequest.bids[0].mediaTypes.video.api; - expect(spec.isBidRequestValid(bidderRequest.bids[0])).to.equal(false); }); it('bid request is valid when video context is outstream', function () { From 7a09d81d65d72ebdca928c4efb628f00afdd4d30 Mon Sep 17 00:00:00 2001 From: bbaresic <153560835+bbaresic@users.noreply.github.com> Date: Thu, 4 Jan 2024 09:34:08 -0600 Subject: [PATCH 110/246] Price Floors: Failure to Account for 'data.skipRate' (#10872) * Update skipRate handling in priceFloors.js and add unit tests for the changes. * Update wording on tests and remove unecessary spread. --- modules/priceFloors.js | 4 +- test/spec/modules/priceFloors_spec.js | 86 ++++++++++++++++++++++++++- 2 files changed, 87 insertions(+), 3 deletions(-) diff --git a/modules/priceFloors.js b/modules/priceFloors.js index 4ec8a8e4b7e..3837f56366c 100644 --- a/modules/priceFloors.js +++ b/modules/priceFloors.js @@ -347,7 +347,7 @@ export function updateAdUnitsForAuction(adUnits, floorData, auctionId) { bid.auctionId = auctionId; bid.floorData = { skipped: floorData.skipped, - skipRate: floorData.skipRate, + skipRate: deepAccess(floorData, 'data.skipRate') ?? floorData.skipRate, floorMin: floorData.floorMin, modelVersion: deepAccess(floorData, 'data.modelVersion'), modelWeight: deepAccess(floorData, 'data.modelWeight'), @@ -396,7 +396,7 @@ export function createFloorsDataForAuction(adUnits, auctionId) { resolvedFloorsData.skipped = true; } else { // determine the skip rate now - const auctionSkipRate = getParameterByName('pbjs_skipRate') || resolvedFloorsData.skipRate; + const auctionSkipRate = getParameterByName('pbjs_skipRate') || (deepAccess(resolvedFloorsData, 'data.skipRate') ?? resolvedFloorsData.skipRate); const isSkipped = Math.random() * 100 < parseFloat(auctionSkipRate); resolvedFloorsData.skipped = isSkipped; } diff --git a/test/spec/modules/priceFloors_spec.js b/test/spec/modules/priceFloors_spec.js index 673a821b497..f618e57e60d 100644 --- a/test/spec/modules/priceFloors_spec.js +++ b/test/spec/modules/priceFloors_spec.js @@ -12,7 +12,7 @@ import { isFloorsDataValid, addBidResponseHook, fieldMatchingFunctions, - allowedFields, parseFloorData, normalizeDefault, getFloorDataFromAdUnits + allowedFields, parseFloorData, normalizeDefault, getFloorDataFromAdUnits, updateAdUnitsForAuction, createFloorsDataForAuction } from 'modules/priceFloors.js'; import * as events from 'src/events.js'; import * as mockGpt from '../integration/faker/googletag.js'; @@ -648,6 +648,90 @@ describe('the price floors module', function () { }); }); }); + + describe('updateAdUnitsForAuction', function() { + let inputFloorData; + let adUnits; + + beforeEach(function() { + adUnits = [getAdUnitMock()]; + inputFloorData = utils.deepClone(minFloorConfigLow); + inputFloorData.skipRate = 0.5; + }); + + it('should set the skipRate to the skipRate from the data property before using the skipRate from floorData directly', function() { + utils.deepSetValue(inputFloorData, 'data', { + skipRate: 0.7 + }); + updateAdUnitsForAuction(adUnits, inputFloorData, 'id'); + + const skipRate = utils.deepAccess(adUnits, '0.bids.0.floorData.skipRate'); + expect(skipRate).to.equal(0.7); + }); + + it('should set the skipRate to the skipRate from floorData directly if it does not exist in the data property of floorData', function() { + updateAdUnitsForAuction(adUnits, inputFloorData, 'id'); + + const skipRate = utils.deepAccess(adUnits, '0.bids.0.floorData.skipRate'); + expect(skipRate).to.equal(0.5); + }); + + it('should set the skipRate in the bid floorData to undefined if both skipRate and skipRate in the data property are undefined', function() { + inputFloorData.skipRate = undefined; + utils.deepSetValue(inputFloorData, 'data', { + skipRate: undefined, + }); + updateAdUnitsForAuction(adUnits, inputFloorData, 'id'); + + const skipRate = utils.deepAccess(adUnits, '0.bids.0.floorData.skipRate'); + expect(skipRate).to.equal(undefined); + }); + }); + + describe('createFloorsDataForAuction', function() { + let adUnits; + let floorConfig; + + beforeEach(function() { + adUnits = [getAdUnitMock()]; + floorConfig = utils.deepClone(basicFloorConfig); + }); + + it('should return skipRate as 0 if both skipRate and skipRate in the data property are undefined', function() { + floorConfig.skipRate = undefined; + floorConfig.data.skipRate = undefined; + handleSetFloorsConfig(floorConfig); + + const floorData = createFloorsDataForAuction(adUnits, 'id'); + + expect(floorData.skipRate).to.equal(0); + expect(floorData.skipped).to.equal(false); + }); + + it('should properly set skipRate if it is available in the data property', function() { + // this will force skipped to be true + floorConfig.skipRate = 101; + floorConfig.data.skipRate = 201; + handleSetFloorsConfig(floorConfig); + + const floorData = createFloorsDataForAuction(adUnits, 'id'); + + expect(floorData.data.skipRate).to.equal(201); + expect(floorData.skipped).to.equal(true); + }); + + it('should should use the skipRate if its not available in the data property ', function() { + // this will force skipped to be true + floorConfig.skipRate = 101; + handleSetFloorsConfig(floorConfig); + + const floorData = createFloorsDataForAuction(adUnits, 'id'); + + expect(floorData.skipRate).to.equal(101); + expect(floorData.skipped).to.equal(true); + }); + }); + describe('pre-auction tests', function () { let exposedAdUnits; const validateBidRequests = (getFloorExpected, FloorDataExpected) => { From aa21bfd0e226fe7344ffbf1cf1f383fbc6919adc Mon Sep 17 00:00:00 2001 From: "Prebid.js automated release" Date: Thu, 4 Jan 2024 18:27:53 +0000 Subject: [PATCH 111/246] Prebid 8.30.0 release --- package-lock.json | 2 +- package.json | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/package-lock.json b/package-lock.json index 11d2f1fd427..577b65c74f7 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,6 +1,6 @@ { "name": "prebid.js", - "version": "8.30.0-pre", + "version": "8.30.0", "lockfileVersion": 2, "requires": true, "packages": { diff --git a/package.json b/package.json index 443cf0fc503..ad2801bf812 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "prebid.js", - "version": "8.30.0-pre", + "version": "8.30.0", "description": "Header Bidding Management Library", "main": "src/prebid.js", "scripts": { From 714acd35f2a92417ebcd9bd7add9fe16428d4601 Mon Sep 17 00:00:00 2001 From: "Prebid.js automated release" Date: Thu, 4 Jan 2024 18:27:53 +0000 Subject: [PATCH 112/246] Increment version to 8.31.0-pre --- package-lock.json | 2 +- package.json | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/package-lock.json b/package-lock.json index 577b65c74f7..cfdc8669c2f 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,6 +1,6 @@ { "name": "prebid.js", - "version": "8.30.0", + "version": "8.31.0-pre", "lockfileVersion": 2, "requires": true, "packages": { diff --git a/package.json b/package.json index ad2801bf812..830f700537d 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "prebid.js", - "version": "8.30.0", + "version": "8.31.0-pre", "description": "Header Bidding Management Library", "main": "src/prebid.js", "scripts": { From 866be6e48ca21bb3fdd727a99f8ff09befb8cc43 Mon Sep 17 00:00:00 2001 From: kapil-tuptewar <91458408+kapil-tuptewar@users.noreply.github.com> Date: Fri, 5 Jan 2024 00:29:13 +0530 Subject: [PATCH 113/246] 1.Added safechecks for s2s metadapter case 2.Skipped firing client side tracker for pubmatic 3.Skipped adding pubmatic bid in logger (#10897) --- modules/pubmaticAnalyticsAdapter.js | 38 +- .../modules/pubmaticAnalyticsAdapter_spec.js | 346 ++++++++++++++---- 2 files changed, 308 insertions(+), 76 deletions(-) diff --git a/modules/pubmaticAnalyticsAdapter.js b/modules/pubmaticAnalyticsAdapter.js index 9b910caa992..ad2a06ea86d 100755 --- a/modules/pubmaticAnalyticsAdapter.js +++ b/modules/pubmaticAnalyticsAdapter.js @@ -9,6 +9,7 @@ import {getGptSlotInfoForAdUnitCode} from '../libraries/gptUtils/gptUtils.js'; /// /////////// CONSTANTS ////////////// const ADAPTER_CODE = 'pubmatic'; +const VENDOR_OPENWRAP = 'openwrap'; const SEND_TIMEOUT = 2000; const END_POINT_HOST = 'https://t.pubmatic.com/'; const END_POINT_BID_LOGGER = END_POINT_HOST + 'wl?'; @@ -258,12 +259,27 @@ function isS2SBidder(bidder) { return (s2sBidders.indexOf(bidder) > -1) ? 1 : 0 } +function isOWPubmaticBid(adapterName) { + let s2sConf = config.getConfig('s2sConfig'); + let s2sConfArray = isArray(s2sConf) ? s2sConf : [s2sConf]; + return s2sConfArray.some(conf => { + if (adapterName === ADAPTER_CODE && conf.defaultVendor === VENDOR_OPENWRAP && + conf.bidders.indexOf(ADAPTER_CODE) > -1) { + return true; + } + }) +} + function gatherPartnerBidsForAdUnitForLogger(adUnit, adUnitId, highestBid) { highestBid = (highestBid && highestBid.length > 0) ? highestBid[0] : null; return Object.keys(adUnit.bids).reduce(function(partnerBids, bidId) { adUnit.bids[bidId].forEach(function(bid) { + let adapterName = getAdapterNameForAlias(bid.adapterCode || bid.bidder); + if (isOWPubmaticBid(adapterName) && isS2SBidder(bid.bidder)) { + return; + } partnerBids.push({ - 'pn': getAdapterNameForAlias(bid.adapterCode || bid.bidder), + 'pn': adapterName, 'bc': bid.bidderCode || bid.bidder, 'bidid': bid.bidId || bidId, 'db': bid.bidResponse ? 0 : 1, @@ -419,13 +435,20 @@ function executeBidsLoggerCall(e, highestCpmBids) { function executeBidWonLoggerCall(auctionId, adUnitId) { const winningBidId = cache.auctions[auctionId].adUnitCodes[adUnitId].bidWon; const winningBids = cache.auctions[auctionId].adUnitCodes[adUnitId].bids[winningBidId]; - let winningBid = winningBids[0]; + if (!winningBids) { + logWarn(LOG_PRE_FIX + 'Could not find winningBids for : ', auctionId); + return; + } + let winningBid = winningBids[0]; if (winningBids.length > 1) { winningBid = winningBids.filter(bid => bid.adId === cache.auctions[auctionId].adUnitCodes[adUnitId].bidWonAdId)[0]; } const adapterName = getAdapterNameForAlias(winningBid.adapterCode || winningBid.bidder); + if (isOWPubmaticBid(adapterName) && isS2SBidder(winningBid.bidder)) { + return; + } let origAdUnit = getAdUnit(cache.auctions[auctionId].origAdUnits, adUnitId) || {}; let auctionCache = cache.auctions[auctionId]; let floorData = auctionCache.floorData; @@ -479,7 +502,10 @@ function executeBidWonLoggerCall(auctionId, adUnitId) { function auctionInitHandler(args) { s2sBidders = (function() { let s2sConf = config.getConfig('s2sConfig'); - return (s2sConf && isArray(s2sConf.bidders)) ? s2sConf.bidders : []; + let s2sBidders = []; + (s2sConf || []) && + isArray(s2sConf) ? s2sConf.map(conf => s2sBidders.push(...conf.bidders)) : s2sBidders.push(...s2sConf.bidders); + return s2sBidders || []; }()); let cacheEntry = pick(args, [ 'timestamp', @@ -513,6 +539,10 @@ function bidRequestedHandler(args) { } function bidResponseHandler(args) { + if (!args.requestId) { + logWarn(LOG_PRE_FIX + 'Got null requestId in bidResponseHandler'); + return; + } let bid = cache.auctions[args.auctionId].adUnitCodes[args.adUnitCode].bids[args.requestId][0]; if (!bid) { logError(LOG_PRE_FIX + 'Could not find associated bid request for bid response with requestId: ', args.requestId); @@ -577,7 +607,7 @@ function auctionEndHandler(args) { let highestCpmBids = getGlobal().getHighestCpmBids() || []; setTimeout(() => { executeBidsLoggerCall.call(this, args, highestCpmBids); - }, (cache.auctions[args.auctionId].bidderDonePendingCount === 0 ? 500 : SEND_TIMEOUT)); + }, (cache.auctions[args.auctionId]?.bidderDonePendingCount === 0 ? 500 : SEND_TIMEOUT)); } function bidTimeoutHandler(args) { diff --git a/test/spec/modules/pubmaticAnalyticsAdapter_spec.js b/test/spec/modules/pubmaticAnalyticsAdapter_spec.js index 6af497bd2e9..c6447905ecd 100755 --- a/test/spec/modules/pubmaticAnalyticsAdapter_spec.js +++ b/test/spec/modules/pubmaticAnalyticsAdapter_spec.js @@ -1,4 +1,4 @@ -import pubmaticAnalyticsAdapter, {getMetadata} from 'modules/pubmaticAnalyticsAdapter.js'; +import pubmaticAnalyticsAdapter, { getMetadata } from 'modules/pubmaticAnalyticsAdapter.js'; import adapterManager from 'src/adapterManager.js'; import CONSTANTS from 'src/constants.json'; import { config } from 'src/config.js'; @@ -316,6 +316,208 @@ describe('pubmatic analytics adapter', function () { expect(utils.logError.called).to.equal(true); }); + describe('OW S2S', function() { + this.beforeEach(function() { + pubmaticAnalyticsAdapter.enableAnalytics({ + options: { + publisherId: 9999, + profileId: 1111, + profileVersionId: 20 + } + }); + config.setConfig({ + s2sConfig: { + accountId: '1234', + bidders: ['pubmatic'], + defaultVendor: 'openwrap', + timeout: 500 + } + }); + }); + + this.afterEach(function() { + pubmaticAnalyticsAdapter.disableAnalytics(); + }); + + it('Pubmatic Won: No tracker fired', function() { + this.timeout(5000) + + 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(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]); + + clock.tick(2000 + 1000); + expect(requests.length).to.equal(1); // only logger is fired + let request = requests[0]; + 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'); + }); + + it('Non-pubmatic won: logger, tracker fired', function() { + const APPNEXUS_BID = Object.assign({}, BID, { + 'bidder': 'appnexus', + 'adserverTargeting': { + 'hb_bidder': 'appnexus', + 'hb_adid': '2ecff0db240757', + 'hb_pb': 1.20, + 'hb_size': '640x480', + 'hb_source': 'server' + } + }); + + const MOCK_AUCTION_INIT_APPNEXUS = { + 'auctionId': '25c6d7f5-699a-4bfc-87c9-996f915341fa', + 'timestamp': 1519767010567, + 'auctionStatus': 'inProgress', + 'adUnits': [ { + 'code': '/19968336/header-bid-tag-1', + 'sizes': [[640, 480]], + 'bids': [ { + 'bidder': 'appnexus', + 'params': { + 'publisherId': '1001' + } + } ], + 'transactionId': 'ca4af27a-6d02-4f90-949d-d5541fa12014' + } + ], + 'adUnitCodes': ['/19968336/header-bid-tag-1'], + 'bidderRequests': [ { + 'bidderCode': 'appnexus', + 'auctionId': '25c6d7f5-699a-4bfc-87c9-996f915341fa', + 'bidderRequestId': '1be65d7958826a', + 'bids': [ { + 'bidder': 'appnexus', + 'params': { + 'publisherId': '1001', + 'kgpv': 'this-is-a-kgpv' + }, + 'mediaTypes': { + 'banner': { + 'sizes': [[640, 480]] + } + }, + 'adUnitCode': '/19968336/header-bid-tag-1', + 'transactionId': 'ca4af27a-6d02-4f90-949d-d5541fa12014', + 'sizes': [[640, 480]], + 'bidId': '2ecff0db240757', + 'bidderRequestId': '1be65d7958826a', + 'auctionId': '25c6d7f5-699a-4bfc-87c9-996f915341fa', + 'src': 'client', + 'bidRequestsCount': 1 + } + ], + 'timeout': 3000, + 'refererInfo': { + 'topmostLocation': 'http://www.test.com/page.html', 'reachedTop': true, 'numIframes': 0, 'stack': ['http://www.test.com/page.html'] + } + } + ], + 'bidsReceived': [], + 'winningBids': [], + 'timeout': 3000 + }; + + const MOCK_BID_REQUESTED_APPNEXUS = { + 'bidder': 'appnexus', + 'auctionId': '25c6d7f5-699a-4bfc-87c9-996f915341fa', + 'bidderRequestId': '1be65d7958826a', + 'bids': [ + { + 'bidder': 'appnexus', + 'adapterCode': 'appnexus', + 'bidderCode': 'appnexus', + 'params': { + 'publisherId': '1001', + 'video': { + 'minduration': 30, + 'skippable': true + } + }, + 'mediaType': 'video', + 'adUnitCode': '/19968336/header-bid-tag-0', + 'transactionId': 'ca4af27a-6d02-4f90-949d-d5541fa12014', + 'sizes': [[640, 480]], + 'bidId': '2ecff0db240757', + 'bidderRequestId': '1be65d7958826a', + 'auctionId': '25c6d7f5-699a-4bfc-87c9-996f915341fa' + } + ], + 'auctionStart': 1519149536560, + 'timeout': 5000, + 'start': 1519149562216, + 'refererInfo': { + 'topmostLocation': 'http://www.test.com/page.html', 'reachedTop': true, 'numIframes': 0, 'stack': ['http://www.test.com/page.html'] + }, + 'gdprConsent': { + 'consentString': 'here-goes-gdpr-consent-string', + 'gdprApplies': true + } + }; + + this.timeout(5000) + + sandbox.stub($$PREBID_GLOBAL$$, 'getHighestCpmBids').callsFake((key) => { + return [APPNEXUS_BID] + }); + + events.emit(AUCTION_INIT, MOCK_AUCTION_INIT_APPNEXUS); + events.emit(BID_REQUESTED, MOCK_BID_REQUESTED_APPNEXUS); + events.emit(BID_RESPONSE, APPNEXUS_BID); + events.emit(BIDDER_DONE, { + 'bidderCode': 'appnexus', + 'bids': [ + APPNEXUS_BID, + Object.assign({}, APPNEXUS_BID, { + 'serverResponseTimeMs': 42, + }) + ] + }); + events.emit(AUCTION_END, MOCK.AUCTION_END); + events.emit(SET_TARGETING, { + [APPNEXUS_BID.adUnitCode]: APPNEXUS_BID.adserverTargeting, + }); + events.emit(BID_WON, Object.assign({}, APPNEXUS_BID, { + 'status': 'rendered' + })); + + clock.tick(2000 + 1000); + expect(requests.length).to.equal(2); // logger as well as tracker is fired + let request = requests[1]; // 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'); + + let firstTracker = requests[0].url; + expect(firstTracker.split('?')[0]).to.equal('https://t.pubmatic.com/wt'); + 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.s).to.be.an('array'); + expect(data.s.length).to.equal(1); + expect(data.s[0].ps[0].pn).to.equal('appnexus'); + expect(data.s[0].ps[0].bc).to.equal('appnexus'); + }) + }); + describe('when handling events', function() { beforeEach(function () { pubmaticAnalyticsAdapter.enableAnalytics({ @@ -374,14 +576,14 @@ describe('pubmatic analytics adapter', function () { // slot 1 expect(data.s[0].sn).to.equal('/19968336/header-bid-tag-0'); expect(data.s[0].fskp).to.equal(0); - expect(data.s[0].sid).not.to.be.undefined; - expect(data.s[0].ffs).to.equal(1); - expect(data.s[0].fsrc).to.equal(2); - expect(data.s[0].fp).to.equal('pubmatic'); + expect(data.s[0].sid).not.to.be.undefined; + expect(data.s[0].ffs).to.equal(1); + expect(data.s[0].fsrc).to.equal(2); + expect(data.s[0].fp).to.equal('pubmatic'); expect(data.s[0].sz).to.deep.equal(['640x480']); expect(data.s[0].ps).to.be.an('array'); - expect(data.s[0].au).to.equal('/19968336/header-bid-tag-0'); - expect(data.s[0].ps.length).to.equal(1); + expect(data.s[0].au).to.equal('/19968336/header-bid-tag-0'); + 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'); @@ -394,8 +596,8 @@ describe('pubmatic analytics adapter', function () { expect(data.s[0].ps[0].en).to.equal(1.23); expect(data.s[0].ps[0].di).to.equal('-1'); expect(data.s[0].ps[0].dc).to.equal(''); - expect(data.s[0].ps[0].l1).to.equal(944); - expect(data.s[0].ps[0].ol1).to.equal(3214); + expect(data.s[0].ps[0].l1).to.equal(944); + expect(data.s[0].ps[0].ol1).to.equal(3214); expect(data.s[0].ps[0].l2).to.equal(0); expect(data.s[0].ps[0].ss).to.equal(1); expect(data.s[0].ps[0].t).to.equal(0); @@ -407,10 +609,10 @@ describe('pubmatic analytics adapter', function () { // slot 2 expect(data.s[1].sn).to.equal('/19968336/header-bid-tag-1'); expect(data.s[1].fskp).to.equal(0); - expect(data.s[1].sid).not.to.be.undefined; - expect(data.s[1].ffs).to.equal(1); - expect(data.s[1].fsrc).to.equal(2); - expect(data.s[1].fp).to.equal('pubmatic'); + expect(data.s[1].sid).not.to.be.undefined; + expect(data.s[1].ffs).to.equal(1); + expect(data.s[1].fsrc).to.equal(2); + expect(data.s[1].fp).to.equal('pubmatic'); 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); @@ -429,7 +631,7 @@ describe('pubmatic analytics adapter', function () { expect(data.s[1].ps[0].mi).to.equal('matched-impression'); expect(data.s[1].ps[0].adv).to.equal('example.com'); expect(data.s[0].ps[0].l1).to.equal(944); - expect(data.s[0].ps[0].ol1).to.equal(3214); + expect(data.s[0].ps[0].ol1).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); @@ -467,7 +669,7 @@ describe('pubmatic analytics adapter', function () { expect(data.af).to.equal('video'); }); - it('Logger: do not log floor fields when prebids floor shows noData in location property', function() { + it('Logger : do not log floor fields when prebids floor shows noData in location property', function() { const BID_REQUESTED_COPY = utils.deepClone(MOCK.BID_REQUESTED); BID_REQUESTED_COPY['bids'][1]['floorData']['location'] = 'noData'; @@ -591,13 +793,13 @@ describe('pubmatic analytics adapter', function () { // slot 1 expect(data.s[0].sn).to.equal('/19968336/header-bid-tag-0'); expect(data.s[0].fskp).to.equal(0); - expect(data.s[0].sid).not.to.be.undefined; - expect(data.s[0].ffs).to.equal(1); - expect(data.s[0].fsrc).to.equal(2); - expect(data.s[0].fp).to.equal('pubmatic'); + expect(data.s[0].sid).not.to.be.undefined; + expect(data.s[0].ffs).to.equal(1); + expect(data.s[0].fsrc).to.equal(2); + expect(data.s[0].fp).to.equal('pubmatic'); expect(data.s[0].sz).to.deep.equal(['640x480']); expect(data.s[0].ps).to.be.an('array'); - expect(data.s[0].au).to.equal('/19968336/header-bid-tag-0'); + expect(data.s[0].au).to.equal('/19968336/header-bid-tag-0'); 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'); @@ -671,7 +873,7 @@ describe('pubmatic analytics adapter', function () { 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].au).to.equal('/19968336/header-bid-tag-0'); + expect(data.s[0].au).to.equal('/19968336/header-bid-tag-0'); 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'); @@ -715,11 +917,11 @@ describe('pubmatic analytics adapter', function () { expect(data.s[1].sn).to.equal('/19968336/header-bid-tag-1'); expect(data.s[1].fskp).to.equal(0); - expect(data.s[1].sid).not.to.be.undefined; + expect(data.s[1].sid).not.to.be.undefined; - expect(data.s[1].ffs).to.equal(1); - expect(data.s[1].fsrc).to.equal(2); - expect(data.s[1].fp).to.equal('pubmatic'); + expect(data.s[1].ffs).to.equal(1); + expect(data.s[1].fsrc).to.equal(2); + expect(data.s[1].fp).to.equal('pubmatic'); expect(data.s[1].sz).to.deep.equal(['1000x300', '970x250', '728x90']); expect(data.s[1].ps).to.be.an('array'); @@ -803,10 +1005,10 @@ describe('pubmatic analytics adapter', function () { let data = getLoggerJsonFromRequest(request.requestBody); expect(data.s[1].sn).to.equal('/19968336/header-bid-tag-1'); expect(data.s[1].fskp).to.equal(0); - expect(data.s[1].sid).not.to.be.undefined; - expect(data.s[1].ffs).to.equal(1); - expect(data.s[1].fsrc).to.equal(2); - expect(data.s[1].fp).to.equal('pubmatic'); + expect(data.s[1].sid).not.to.be.undefined; + expect(data.s[1].ffs).to.equal(1); + expect(data.s[1].fsrc).to.equal(2); + expect(data.s[1].fp).to.equal('pubmatic'); 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); @@ -823,8 +1025,8 @@ describe('pubmatic analytics adapter', function () { 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].adv).to.equal('example.com'); - expect(data.s[0].ps[0].l1).to.equal(0); - expect(data.s[0].ps[0].ol1).to.equal(0); + expect(data.s[0].ps[0].l1).to.equal(0); + expect(data.s[0].ps[0].ol1).to.equal(0); 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(1); @@ -869,7 +1071,7 @@ describe('pubmatic analytics adapter', function () { 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].sid).not.to.be.undefined; + expect(data.s[1].sid).not.to.be.undefined; 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'); @@ -885,7 +1087,7 @@ describe('pubmatic analytics adapter', function () { expect(data.s[1].ps[0].mi).to.equal('matched-impression'); expect(data.s[1].ps[0].adv).to.equal('example.com'); expect(data.s[0].ps[0].l1).to.equal(944); - expect(data.s[0].ps[0].ol1).to.equal(3214); + expect(data.s[0].ps[0].ol1).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); @@ -917,10 +1119,10 @@ describe('pubmatic analytics adapter', function () { let data = getLoggerJsonFromRequest(request.requestBody); expect(data.s[1].sn).to.equal('/19968336/header-bid-tag-1'); expect(data.s[1].fskp).to.equal(0); - expect(data.s[1].sid).not.to.be.undefined; - expect(data.s[1].ffs).to.equal(1); - expect(data.s[1].fsrc).to.equal(2); - expect(data.s[1].fp).to.equal('pubmatic'); + expect(data.s[1].sid).not.to.be.undefined; + expect(data.s[1].ffs).to.equal(1); + expect(data.s[1].fsrc).to.equal(2); + expect(data.s[1].fp).to.equal('pubmatic'); 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); @@ -938,7 +1140,7 @@ describe('pubmatic analytics adapter', function () { expect(data.s[1].ps[0].mi).to.equal('matched-impression'); expect(data.s[1].ps[0].adv).to.equal('example.com'); expect(data.s[0].ps[0].l1).to.equal(944); - expect(data.s[0].ps[0].ol1).to.equal(3214); + expect(data.s[0].ps[0].ol1).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); @@ -980,7 +1182,7 @@ describe('pubmatic analytics adapter', function () { 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].sid).not.to.be.undefined; + expect(data.s[1].sid).not.to.be.undefined; 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'); @@ -997,7 +1199,7 @@ describe('pubmatic analytics adapter', function () { expect(data.s[1].ps[0].mi).to.equal('matched-impression'); expect(data.s[1].ps[0].adv).to.equal('example.com'); expect(data.s[0].ps[0].l1).to.equal(944); - expect(data.s[0].ps[0].ol1).to.equal(3214); + expect(data.s[0].ps[0].ol1).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); @@ -1035,10 +1237,10 @@ describe('pubmatic analytics adapter', function () { let data = getLoggerJsonFromRequest(request.requestBody); expect(data.s[1].sn).to.equal('/19968336/header-bid-tag-1'); expect(data.s[1].fskp).to.equal(0); - expect(data.s[1].sid).not.to.be.undefined; - expect(data.s[1].ffs).to.equal(1); - expect(data.s[1].fsrc).to.equal(2); - expect(data.s[1].fp).to.equal('pubmatic'); + expect(data.s[1].sid).not.to.be.undefined; + expect(data.s[1].ffs).to.equal(1); + expect(data.s[1].fsrc).to.equal(2); + expect(data.s[1].fp).to.equal('pubmatic'); 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); @@ -1056,7 +1258,7 @@ describe('pubmatic analytics adapter', function () { expect(data.s[1].ps[0].mi).to.equal('matched-impression'); expect(data.s[1].ps[0].adv).to.equal('example.com'); expect(data.s[0].ps[0].l1).to.equal(944); - expect(data.s[0].ps[0].ol1).to.equal(3214); + expect(data.s[0].ps[0].ol1).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); @@ -1096,7 +1298,7 @@ describe('pubmatic analytics adapter', function () { 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].sid).not.to.be.undefined; + expect(data.s[1].sid).not.to.be.undefined; 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'); @@ -1113,7 +1315,7 @@ describe('pubmatic analytics adapter', function () { expect(data.s[1].ps[0].mi).to.equal('matched-impression'); expect(data.s[1].ps[0].adv).to.equal('example.com'); expect(data.s[0].ps[0].l1).to.equal(944); - expect(data.s[0].ps[0].ol1).to.equal(3214); + expect(data.s[0].ps[0].ol1).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); @@ -1155,11 +1357,11 @@ describe('pubmatic analytics adapter', function () { // Testing only for rejected bid as other scenarios will be covered under other TCs expect(data.s[1].sn).to.equal('/19968336/header-bid-tag-1'); expect(data.s[1].fskp).to.equal(0); - expect(data.s[1].ffs).to.equal(1); - expect(data.s[1].fsrc).to.equal(2); - expect(data.s[1].fp).to.equal('pubmatic'); + expect(data.s[1].ffs).to.equal(1); + expect(data.s[1].fsrc).to.equal(2); + expect(data.s[1].fp).to.equal('pubmatic'); expect(data.s[1].sz).to.deep.equal(['1000x300', '970x250', '728x90']); - expect(data.s[1].sid).not.to.be.undefined; + expect(data.s[1].sid).not.to.be.undefined; 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'); @@ -1177,7 +1379,7 @@ describe('pubmatic analytics adapter', function () { expect(data.s[1].ps[0].mi).to.equal('matched-impression'); expect(data.s[1].ps[0].adv).to.equal('example.com'); expect(data.s[0].ps[0].l1).to.equal(944); - expect(data.s[0].ps[0].ol1).to.equal(3214); + expect(data.s[0].ps[0].ol1).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); @@ -1234,13 +1436,13 @@ describe('pubmatic analytics adapter', function () { // slot 1 expect(data.s[0].sn).to.equal('/19968336/header-bid-tag-0'); expect(data.s[0].fskp).to.equal(0); - expect(data.s[0].ffs).to.equal(1); - expect(data.s[0].fsrc).to.equal(2); - expect(data.s[0].fp).to.equal('pubmatic'); + expect(data.s[0].ffs).to.equal(1); + expect(data.s[0].fsrc).to.equal(2); + expect(data.s[0].fp).to.equal('pubmatic'); expect(data.s[0].sz).to.deep.equal(['640x480']); - expect(data.s[0].sid).not.to.be.undefined; + expect(data.s[0].sid).not.to.be.undefined; expect(data.s[0].ps).to.be.an('array'); - expect(data.s[0].au).to.equal('/19968336/header-bid-tag-0'); + expect(data.s[0].au).to.equal('/19968336/header-bid-tag-0'); 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'); @@ -1255,7 +1457,7 @@ describe('pubmatic analytics adapter', function () { expect(data.s[0].ps[0].di).to.equal('-1'); expect(data.s[0].ps[0].dc).to.equal(''); expect(data.s[0].ps[0].l1).to.equal(944); - expect(data.s[0].ps[0].ol1).to.equal(3214); + expect(data.s[0].ps[0].ol1).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); @@ -1268,11 +1470,11 @@ describe('pubmatic analytics adapter', function () { // slot 2 expect(data.s[1].sn).to.equal('/19968336/header-bid-tag-1'); expect(data.s[1].fskp).to.equal(0); - expect(data.s[1].ffs).to.equal(1); - expect(data.s[1].fsrc).to.equal(2); - expect(data.s[1].fp).to.equal('pubmatic'); + expect(data.s[1].ffs).to.equal(1); + expect(data.s[1].fsrc).to.equal(2); + expect(data.s[1].fp).to.equal('pubmatic'); expect(data.s[1].sz).to.deep.equal(['1000x300', '970x250', '728x90']); - expect(data.s[1].sid).not.to.be.undefined; + expect(data.s[1].sid).not.to.be.undefined; 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'); @@ -1289,8 +1491,8 @@ describe('pubmatic analytics adapter', function () { 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].adv).to.equal('example.com'); - expect(data.s[0].ps[0].l1).to.equal(944); - expect(data.s[0].ps[0].ol1).to.equal(3214); + expect(data.s[0].ps[0].l1).to.equal(944); + expect(data.s[0].ps[0].ol1).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); @@ -1364,13 +1566,13 @@ describe('pubmatic analytics adapter', function () { // slot 1 expect(data.s[0].sn).to.equal('/19968336/header-bid-tag-0'); expect(data.s[0].fskp).to.equal(0); - expect(data.s[0].ffs).to.equal(1); - expect(data.s[0].fsrc).to.equal(2); - expect(data.s[0].fp).to.equal('pubmatic'); + expect(data.s[0].ffs).to.equal(1); + expect(data.s[0].fsrc).to.equal(2); + expect(data.s[0].fp).to.equal('pubmatic'); expect(data.s[0].sz).to.deep.equal(['640x480']); - expect(data.s[0].sid).not.to.be.undefined; + expect(data.s[0].sid).not.to.be.undefined; expect(data.s[0].ps).to.be.an('array'); - expect(data.s[0].au).to.equal('/19968336/header-bid-tag-0'); + expect(data.s[0].au).to.equal('/19968336/header-bid-tag-0'); 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('groupm'); @@ -1385,7 +1587,7 @@ describe('pubmatic analytics adapter', function () { expect(data.s[0].ps[0].di).to.equal('-1'); expect(data.s[0].ps[0].dc).to.equal(''); expect(data.s[0].ps[0].l1).to.equal(944); - expect(data.s[0].ps[0].ol1).to.equal(3214); + expect(data.s[0].ps[0].ol1).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); @@ -1398,7 +1600,7 @@ describe('pubmatic analytics adapter', function () { // 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].sid).not.to.be.undefined; + expect(data.s[1].sid).not.to.be.undefined; 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'); @@ -1416,7 +1618,7 @@ describe('pubmatic analytics adapter', function () { expect(data.s[1].ps[0].mi).to.equal('matched-impression'); expect(data.s[1].ps[0].adv).to.equal('example.com'); expect(data.s[0].ps[0].l1).to.equal(944); - expect(data.s[0].ps[0].ol1).to.equal(3214); + expect(data.s[0].ps[0].ol1).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); From 60e7e96769e03aa1f900385dd757fe2822fcc360 Mon Sep 17 00:00:00 2001 From: ecoeco163 <147788250+ecoeco163@users.noreply.github.com> Date: Fri, 5 Jan 2024 04:22:06 +0800 Subject: [PATCH 114/246] Discovery Bid Adapter: update buyerId, add ssppid & pmguid, add getUserSyncs (#10800) --- modules/discoveryBidAdapter.js | 62 ++++++++++---- test/spec/modules/discoveryBidAdapter_spec.js | 82 ++++++++++++++++++- 2 files changed, 126 insertions(+), 18 deletions(-) diff --git a/modules/discoveryBidAdapter.js b/modules/discoveryBidAdapter.js index 816ec3cbe1f..315df48fa5d 100644 --- a/modules/discoveryBidAdapter.js +++ b/modules/discoveryBidAdapter.js @@ -6,7 +6,7 @@ import { BANNER, NATIVE } from '../src/mediaTypes.js'; const BIDDER_CODE = 'discovery'; const ENDPOINT_URL = 'https://rtb-jp.mediago.io/api/bid?tn='; const TIME_TO_LIVE = 500; -const storage = getStorageManager({bidderCode: BIDDER_CODE}); +export const storage = getStorageManager({bidderCode: BIDDER_CODE}); let globals = {}; let itemMaps = {}; const MEDIATYPE = [BANNER, NATIVE]; @@ -14,6 +14,9 @@ const MEDIATYPE = [BANNER, NATIVE]; /* ----- _ss_pp_id:start ------ */ const COOKIE_KEY_SSPPID = '_ss_pp_id'; const COOKIE_KEY_MGUID = '__mguid_'; +const COOKIE_KEY_PMGUID = '__pmguid_'; +const COOKIE_RETENTION_TIME = 365 * 24 * 60 * 60 * 1000; // 1 year +const COOKY_SYNC_IFRAME_URL = 'https://asset.popin.cc/js/cookieSync.html'; const NATIVERET = { id: 'id', @@ -55,24 +58,22 @@ const NATIVERET = { }; /** - * 获取用户id + * 获取并生成用户的id * @return {string} */ -const getUserID = () => { - let idd = storage.getCookie(COOKIE_KEY_SSPPID); - let idm = storage.getCookie(COOKIE_KEY_MGUID); - - if (idd && !idm) { - idm = idd; - } else if (idm && !idd) { - idd = idm; - } else if (!idd && !idm) { - const uuid = utils.generateUUID(); - storage.setCookie(COOKIE_KEY_MGUID, uuid); - storage.setCookie(COOKIE_KEY_SSPPID, uuid); - return uuid; +export const getPmgUID = () => { + if (!storage.cookiesAreEnabled()) return; + + let pmgUid = storage.getCookie(COOKIE_KEY_PMGUID); + if (!pmgUid) { + pmgUid = utils.generateUUID(); + const date = new Date(); + date.setTime(date.getTime() + COOKIE_RETENTION_TIME); + try { + storage.setCookie(COOKIE_KEY_PMGUID, pmgUid, date.toUTCString()); + } catch (e) {} } - return idd; + return pmgUid; }; /* ----- _ss_pp_id:end ------ */ @@ -381,9 +382,11 @@ function getParam(validBidRequests, bidderRequest) { ext: { eids, firstPartyData, + ssppid: storage.getCookie(COOKIE_KEY_SSPPID) || undefined, + pmguid: getPmgUID(), }, user: { - buyeruid: getUserID(), + buyeruid: storage.getCookie(COOKIE_KEY_MGUID) || undefined, id: sharedid || pubcid, }, tmax: timeout, @@ -547,6 +550,31 @@ export const spec = { return bidResponses; }, + getUserSyncs: function (syncOptions, serverResponse, gdprConsent, uspConsent, gppConsent) { + const origin = encodeURIComponent(location.origin || `https://${location.host}`); + let syncParamUrl = `dm=${origin}`; + + if (gdprConsent && gdprConsent.consentString) { + if (typeof gdprConsent.gdprApplies === 'boolean') { + syncParamUrl += `&gdpr=${Number(gdprConsent.gdprApplies)}&gdpr_consent=${gdprConsent.consentString}`; + } else { + syncParamUrl += `&gdpr=0&gdpr_consent=${gdprConsent.consentString}`; + } + } + if (uspConsent && uspConsent.consentString) { + syncParamUrl += `&ccpa_consent=${uspConsent.consentString}`; + } + + if (syncOptions.iframeEnabled) { + return [ + { + type: 'iframe', + url: `${COOKY_SYNC_IFRAME_URL}?${syncParamUrl}` + } + ]; + } + }, + /** * Register bidder specific code, which will execute if bidder timed out after an auction * @param {data} Containing timeout specific data diff --git a/test/spec/modules/discoveryBidAdapter_spec.js b/test/spec/modules/discoveryBidAdapter_spec.js index 8957f8bbd40..acfc519bef9 100644 --- a/test/spec/modules/discoveryBidAdapter_spec.js +++ b/test/spec/modules/discoveryBidAdapter_spec.js @@ -1,5 +1,6 @@ import { expect } from 'chai'; -import { spec } from 'modules/discoveryBidAdapter.js'; +import { spec, getPmgUID, storage } from 'modules/discoveryBidAdapter.js'; +import * as utils from 'src/utils.js'; describe('discovery:BidAdapterTests', function () { let bidRequestData = { @@ -98,6 +99,47 @@ describe('discovery:BidAdapterTests', function () { expect(req_data.imp).to.have.lengthOf(1); }); + describe('discovery: buildRequests', function() { + describe('getPmgUID function', function() { + let sandbox; + + beforeEach(() => { + sandbox = sinon.sandbox.create(); + sandbox.stub(storage, 'getCookie'); + sandbox.stub(storage, 'setCookie'); + sandbox.stub(utils, 'generateUUID').returns('new-uuid'); + sandbox.stub(storage, 'cookiesAreEnabled'); + }) + + afterEach(() => { + sandbox.restore(); + }); + + it('should generate new UUID and set cookie if not exists', () => { + storage.cookiesAreEnabled.callsFake(() => true); + storage.getCookie.callsFake(() => null); + const uid = getPmgUID(); + expect(uid).to.equal('new-uuid'); + expect(storage.setCookie.calledOnce).to.be.true; + }); + + it('should return existing UUID from cookie', () => { + storage.cookiesAreEnabled.callsFake(() => true); + storage.getCookie.callsFake(() => 'existing-uuid'); + const uid = getPmgUID(); + expect(uid).to.equal('existing-uuid'); + expect(storage.setCookie.called).to.be.false; + }); + + it('should not set new UUID when cookies are not enabled', () => { + storage.cookiesAreEnabled.callsFake(() => false); + storage.getCookie.callsFake(() => null); + getPmgUID(); + expect(storage.setCookie.calledOnce).to.be.false; + }); + }) + }); + it('discovery:validate_response_params', function () { let tempAdm = '' tempAdm += '%3Cscr'; @@ -137,4 +179,42 @@ describe('discovery:BidAdapterTests', function () { expect(bid.height).to.equal(250); expect(bid.currency).to.equal('USD'); }); + + describe('discovery: getUserSyncs', function() { + const COOKY_SYNC_IFRAME_URL = 'https://asset.popin.cc/js/cookieSync.html'; + const IFRAME_ENABLED = { + iframeEnabled: true, + pixelEnabled: false, + }; + const IFRAME_DISABLED = { + iframeEnabled: false, + pixelEnabled: false, + }; + const GDPR_CONSENT = { + consentString: 'gdprConsentString', + gdprApplies: true + }; + const USP_CONSENT = { + consentString: 'uspConsentString' + } + + let syncParamUrl = `dm=${encodeURIComponent(location.origin || `https://${location.host}`)}`; + syncParamUrl += '&gdpr=1&gdpr_consent=gdprConsentString&ccpa_consent=uspConsentString'; + const expectedIframeSyncs = [ + { + type: 'iframe', + url: `${COOKY_SYNC_IFRAME_URL}?${syncParamUrl}` + } + ]; + + it('should return nothing if iframe is disabled', () => { + const userSyncs = spec.getUserSyncs(IFRAME_DISABLED, undefined, GDPR_CONSENT, USP_CONSENT, undefined); + expect(userSyncs).to.be.undefined; + }); + + it('should do userSyncs if iframe is enabled', () => { + const userSyncs = spec.getUserSyncs(IFRAME_ENABLED, undefined, GDPR_CONSENT, USP_CONSENT, undefined); + expect(userSyncs).to.deep.equal(expectedIframeSyncs); + }); + }); }); From cdbe68c3a0d3c4c5abfca86a927bb981fd2c9797 Mon Sep 17 00:00:00 2001 From: abazylewicz-id5 <106807984+abazylewicz-id5@users.noreply.github.com> Date: Fri, 5 Jan 2024 15:45:23 +0100 Subject: [PATCH 115/246] ID5 User Id module - pass gpp string and sid in getId request (#10899) --- modules/id5IdSystem.js | 12 +++++++++--- test/spec/modules/id5IdSystem_spec.js | 24 +++++++++++++++++++++++- 2 files changed, 32 insertions(+), 4 deletions(-) diff --git a/modules/id5IdSystem.js b/modules/id5IdSystem.js index e12aea5f8d1..5ed12d8def2 100644 --- a/modules/id5IdSystem.js +++ b/modules/id5IdSystem.js @@ -19,7 +19,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'; +import {uspDataHandler, gppDataHandler} from '../src/adapterManager.js'; import {MODULE_TYPE_UID} from '../src/activities/modules.js'; const MODULE_NAME = 'id5Id'; @@ -118,7 +118,7 @@ export const id5IdSubmodule = { } const resp = function (cbFunction) { - new IdFetchFlow(submoduleConfig, consentData, cacheIdObj, uspDataHandler.getConsentData()).execute() + new IdFetchFlow(submoduleConfig, consentData, cacheIdObj, uspDataHandler.getConsentData(), gppDataHandler.getConsentData()).execute() .then(response => { cbFunction(response) }) @@ -170,11 +170,12 @@ export const id5IdSubmodule = { }; class IdFetchFlow { - constructor(submoduleConfig, gdprConsentData, cacheIdObj, usPrivacyData) { + constructor(submoduleConfig, gdprConsentData, cacheIdObj, usPrivacyData, gppData) { this.submoduleConfig = submoduleConfig this.gdprConsentData = gdprConsentData this.cacheIdObj = cacheIdObj this.usPrivacyData = usPrivacyData + this.gppData = gppData } execute() { @@ -285,6 +286,11 @@ class IdFetchFlow { if (this.usPrivacyData !== undefined && !isEmpty(this.usPrivacyData) && !isEmptyStr(this.usPrivacyData)) { data.us_privacy = this.usPrivacyData; } + if (this.gppData) { + data.gpp_string = this.gppData.gppString; + data.gpp_sid = this.gppData.applicableSections; + } + if (signature !== undefined && !isEmptyStr(signature)) { data.s = signature; } diff --git a/test/spec/modules/id5IdSystem_spec.js b/test/spec/modules/id5IdSystem_spec.js index 56b23ba9634..d3d91db010d 100644 --- a/test/spec/modules/id5IdSystem_spec.js +++ b/test/spec/modules/id5IdSystem_spec.js @@ -15,7 +15,7 @@ import {config} from 'src/config.js'; import * as events from 'src/events.js'; import CONSTANTS from 'src/constants.json'; import * as utils from 'src/utils.js'; -import {uspDataHandler} from 'src/adapterManager.js'; +import {uspDataHandler, gppDataHandler} from 'src/adapterManager.js'; import 'src/prebid.js'; import {hook} from '../../../src/hook.js'; import {mockGdprConsent} from '../../helpers/consentData.js'; @@ -259,12 +259,14 @@ describe('ID5 ID System', function () { describe('Xhr Requests from getId()', function () { const responseHeader = HEADERS_CONTENT_TYPE_JSON + let gppStub beforeEach(function () { }); afterEach(function () { uspDataHandler.reset() + gppStub?.restore() }); it('should call the ID5 server and handle a valid response', function () { @@ -730,6 +732,26 @@ describe('ID5 ID System', function () { }); }); + it('should pass gpp_string and gpp_sid to ID5 server', function () { + let xhrServerMock = new XhrServerMock(server) + gppStub = sinon.stub(gppDataHandler, 'getConsentData'); + gppStub.returns({ + ready: true, + gppString: 'GPP_STRING', + applicableSections: [2] + }); + let submoduleResponse = callSubmoduleGetId(getId5FetchConfig(), undefined, ID5_STORED_OBJ); + + return xhrServerMock.expectFetchRequest() + .then(fetchRequest => { + let requestBody = JSON.parse(fetchRequest.requestBody); + expect(requestBody.gpp_string).is.equal('GPP_STRING'); + expect(requestBody.gpp_sid).contains(2); + fetchRequest.respond(200, responseHeader, JSON.stringify(ID5_JSON_RESPONSE)); + return submoduleResponse + }); + }); + describe('when legacy cookies are set', () => { let sandbox; beforeEach(() => { From 7f2763d58229d66ec25bc347924fbf495cecf5a1 Mon Sep 17 00:00:00 2001 From: Antonios Sarhanis Date: Sat, 6 Jan 2024 03:52:51 +1100 Subject: [PATCH 116/246] Adnuntius Bid Adapter : bugfix void au ids (#10890) * Removed linting issues * Fixed merge issues. * Bugfix on storageTool. * Adnuntius Bid Adapter: bugfix handling ad response Handle no voidAuIds better. --------- Co-authored-by: Mikael Lundin Co-authored-by: Mikael Lundin --- modules/adnuntiusBidAdapter.js | 4 ++-- test/spec/modules/adnuntiusBidAdapter_spec.js | 1 + 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/modules/adnuntiusBidAdapter.js b/modules/adnuntiusBidAdapter.js index 6f3ed6eb5b6..a498d056513 100644 --- a/modules/adnuntiusBidAdapter.js +++ b/modules/adnuntiusBidAdapter.js @@ -63,7 +63,7 @@ const storageTool = (function () { } const updateVoidAuIds = function (currentVoidAuIds, auIdsAsString) { - const newAuIds = auIdsAsString ? auIdsAsString.split(';') : []; + const newAuIds = isStr(auIdsAsString) ? auIdsAsString.split(';') : []; const notNewExistingAuIds = currentVoidAuIds.filter(auIdObj => { return newAuIds.indexOf(auIdObj.value) < -1; }) || []; @@ -83,7 +83,7 @@ const storageTool = (function () { } } } - const currentAuIds = updateVoidAuIds(metaAsObj.voidAuIds || [], apiResponse.voidAuIds || []); + const currentAuIds = updateVoidAuIds(metaAsObj.voidAuIds || [], apiResponse.voidAuIds); if (currentAuIds.length > 0) { metaAsObj.voidAuIds = { value: currentAuIds }; } diff --git a/test/spec/modules/adnuntiusBidAdapter_spec.js b/test/spec/modules/adnuntiusBidAdapter_spec.js index e4902ca11b2..e109ca1829c 100644 --- a/test/spec/modules/adnuntiusBidAdapter_spec.js +++ b/test/spec/modules/adnuntiusBidAdapter_spec.js @@ -909,6 +909,7 @@ describe('adnuntiusBidAdapter', function() { ] }; serverResponse.body.adUnits[0].deals = []; + delete serverResponse.body.metaData.voidAuIds; // test response with no voidAuIds const interpretedResponse = spec.interpretResponse(serverResponse, altBidder); expect(interpretedResponse).to.have.lengthOf(0); From 3e6b546efe38ee91eca3475710a9559944d11c9d Mon Sep 17 00:00:00 2001 From: Jean-Baptiste Date: Fri, 5 Jan 2024 21:03:56 +0100 Subject: [PATCH 117/246] GreenbidsAnalyticsAdapter and GreenbidsRtdProvider: Rework greenbids sampling and improve transparency (#10792) * track billing events and modify sampling pattern * review updates --- modules/greenbidsAnalyticsAdapter.js | 110 +++++++--- modules/greenbidsAnalyticsAdapter.md | 37 ++-- modules/greenbidsRtdProvider.js | 72 +++++-- modules/greenbidsRtdProvider.md | 2 +- .../modules/greenbidsAnalyticsAdapter_spec.js | 137 ++++++------- .../spec/modules/greenbidsRtdProvider_spec.js | 188 ++++++++++++++++-- 6 files changed, 401 insertions(+), 145 deletions(-) diff --git a/modules/greenbidsAnalyticsAdapter.js b/modules/greenbidsAnalyticsAdapter.js index 2189172e16f..5d1f35f24ff 100644 --- a/modules/greenbidsAnalyticsAdapter.js +++ b/modules/greenbidsAnalyticsAdapter.js @@ -2,18 +2,20 @@ import {ajax} from '../src/ajax.js'; import adapter from '../libraries/analyticsAdapter/AnalyticsAdapter.js'; import CONSTANTS from '../src/constants.json'; import adapterManager from '../src/adapterManager.js'; -import {deepClone, logError, logInfo} from '../src/utils.js'; +import {deepClone, generateUUID, logError, logInfo, logWarn} from '../src/utils.js'; const analyticsType = 'endpoint'; -export const ANALYTICS_VERSION = '1.0.0'; +export const ANALYTICS_VERSION = '2.0.0'; const ANALYTICS_SERVER = 'https://a.greenbids.ai'; const { EVENTS: { + AUCTION_INIT, AUCTION_END, BID_TIMEOUT, + BILLABLE_EVENT, } } = CONSTANTS; @@ -25,28 +27,53 @@ export const BIDDER_STATUS = { const analyticsOptions = {}; -export const parseBidderCode = function (bid) { - let bidderCode = bid.bidderCode || bid.bidder; - return bidderCode.toLowerCase(); -}; +export const isSampled = function(greenbidsId, samplingRate) { + if (samplingRate < 0 || samplingRate > 1) { + logWarn('Sampling rate must be between 0 and 1'); + return true; + } + const hashInt = parseInt(greenbidsId.slice(-4), 16); + + return hashInt < samplingRate * (0xFFFF + 1); +} export const greenbidsAnalyticsAdapter = Object.assign(adapter({ANALYTICS_SERVER, analyticsType}), { cachedAuctions: {}, initConfig(config) { + analyticsOptions.options = deepClone(config.options); /** * Required option: pbuid * @type {boolean} */ - analyticsOptions.options = deepClone(config.options); - if (typeof config.options.pbuid !== 'string' || config.options.pbuid.length < 1) { + if (typeof analyticsOptions.options.pbuid !== 'string' || analyticsOptions.options.pbuid.length < 1) { logError('"options.pbuid" is required.'); return false; } + /** + * Deprecate use of integerated 'sampling' config + * replace by greenbidsSampling + */ + if (typeof analyticsOptions.options.sampling === 'number') { + logWarn('"options.sampling" is deprecated, please use "greenbidsSampling" instead.'); + analyticsOptions.options.greenbidsSampling = analyticsOptions.options.sampling; + // Set sampling to null to prevent prebid analytics integrated sampling to happen + analyticsOptions.options.sampling = null; + } + + /** + * Discourage unsampled analytics + */ + if (typeof analyticsOptions.options.greenbidsSampling !== 'number' || analyticsOptions.options.greenbidsSampling >= 1) { + logWarn('"options.greenbidsSampling" is not set or >=1, using this analytics module unsampled is discouraged.'); + analyticsOptions.options.greenbidsSampling = 1; + } + analyticsOptions.pbuid = config.options.pbuid analyticsOptions.server = ANALYTICS_SERVER; + return true; }, sendEventMessage(endPoint, data) { @@ -57,13 +84,16 @@ export const greenbidsAnalyticsAdapter = Object.assign(adapter({ANALYTICS_SERVER }); }, createCommonMessage(auctionId) { + const cachedAuction = this.getCachedAuction(auctionId); return { version: ANALYTICS_VERSION, auctionId: auctionId, referrer: window.location.href, - sampling: analyticsOptions.options.sampling, + sampling: analyticsOptions.options.greenbidsSampling, prebid: '$prebid.version$', + greenbidsId: cachedAuction.greenbidsId, pbuid: analyticsOptions.pbuid, + billingId: cachedAuction.billingId, adUnits: [], }; }, @@ -96,21 +126,22 @@ export const greenbidsAnalyticsAdapter = Object.assign(adapter({ANALYTICS_SERVER } } }, - createBidMessage(auctionEndArgs, timeoutBids) { - logInfo(auctionEndArgs) + createBidMessage(auctionEndArgs) { const {auctionId, timestamp, auctionEnd, adUnits, bidsReceived, noBids} = auctionEndArgs; + const cachedAuction = this.getCachedAuction(auctionId); const message = this.createCommonMessage(auctionId); + const timeoutBids = cachedAuction.timeoutBids || []; message.auctionElapsed = (auctionEnd - timestamp); adUnits.forEach((adUnit) => { - const adUnitCode = adUnit.code.toLowerCase(); + const adUnitCode = adUnit.code?.toLowerCase() || 'unknown_adunit_code'; message.adUnits.push({ code: adUnitCode, mediaTypes: { - ...(adUnit.mediaTypes.banner !== undefined) && {banner: adUnit.mediaTypes.banner}, - ...(adUnit.mediaTypes.video !== undefined) && {video: adUnit.mediaTypes.video}, - ...(adUnit.mediaTypes.native !== undefined) && {native: adUnit.mediaTypes.native} + ...(adUnit.mediaTypes?.banner !== undefined) && {banner: adUnit.mediaTypes.banner}, + ...(adUnit.mediaTypes?.video !== undefined) && {video: adUnit.mediaTypes.video}, + ...(adUnit.mediaTypes?.native !== undefined) && {native: adUnit.mediaTypes.native} }, ortb2Imp: adUnit.ortb2Imp || {}, bidders: [], @@ -130,13 +161,26 @@ export const greenbidsAnalyticsAdapter = Object.assign(adapter({ANALYTICS_SERVER getCachedAuction(auctionId) { this.cachedAuctions[auctionId] = this.cachedAuctions[auctionId] || { timeoutBids: [], + greenbidsId: null, + billingId: null, + isSampled: true, }; return this.cachedAuctions[auctionId]; }, + handleAuctionInit(auctionInitArgs) { + const cachedAuction = this.getCachedAuction(auctionInitArgs.auctionId); + try { + cachedAuction.greenbidsId = auctionInitArgs.adUnits[0].ortb2Imp.ext.greenbids.greenbidsId; + } catch (e) { + logInfo("Couldn't find Greenbids RTD info, assuming analytics only"); + cachedAuction.greenbidsId = generateUUID(); + } + cachedAuction.isSampled = isSampled(cachedAuction.greenbidsId, analyticsOptions.options.greenbidsSampling); + }, handleAuctionEnd(auctionEndArgs) { const cachedAuction = this.getCachedAuction(auctionEndArgs.auctionId); this.sendEventMessage('/', - this.createBidMessage(auctionEndArgs, cachedAuction.timeoutBids) + this.createBidMessage(auctionEndArgs, cachedAuction) ); }, handleBidTimeout(timeoutBids) { @@ -145,14 +189,34 @@ export const greenbidsAnalyticsAdapter = Object.assign(adapter({ANALYTICS_SERVER cachedAuction.timeoutBids.push(bid); }); }, + handleBillable(billableArgs) { + const cachedAuction = this.getCachedAuction(billableArgs.auctionId); + /* Filter Greenbids Billable Events only */ + if (billableArgs.vendor === 'greenbidsRtdProvider') { + cachedAuction.billingId = billableArgs.billingId || 'unknown_billing_id'; + } + }, track({eventType, args}) { - switch (eventType) { - case BID_TIMEOUT: - this.handleBidTimeout(args); - break; - case AUCTION_END: - this.handleAuctionEnd(args); - break; + try { + if (eventType === AUCTION_INIT) { + this.handleAuctionInit(args); + } + + if (this.getCachedAuction(args?.auctionId)?.isSampled ?? true) { + switch (eventType) { + case BID_TIMEOUT: + this.handleBidTimeout(args); + break; + case AUCTION_END: + this.handleAuctionEnd(args); + break; + case BILLABLE_EVENT: + this.handleBillable(args); + break; + } + } + } catch (e) { + logWarn('There was an error handling event ' + eventType); } }, getAnalyticsOptions() { diff --git a/modules/greenbidsAnalyticsAdapter.md b/modules/greenbidsAnalyticsAdapter.md index 46e3af2c5e2..1be2c1741ed 100644 --- a/modules/greenbidsAnalyticsAdapter.md +++ b/modules/greenbidsAnalyticsAdapter.md @@ -1,23 +1,24 @@ -# Overview +#### Registration -``` -Module Name: Greenbids Analytics Adapter -Module Type: Analytics Adapter -Maintainer: jb@greenbids.ai -``` +The Greenbids Analytics adapter requires setup and approval from the +Greenbids team. Please reach out to our team for more information [greenbids.ai](https://greenbids.ai). -# Description +#### Analytics Options -Analytics adapter for Greenbids +{: .table .table-bordered .table-striped } +| Name | Scope | Description | Example | Type | +|-------------|---------|--------------------|-----------------------------------------------------------------------------------------------------------------------------|-------------------------------------------------------------------------------------|------------------| +| pbuid | required | The Greenbids Publisher ID | greenbids-publisher-1 | string | +| greenbidsSampling | optional | sampling factor [0-1] (a value of 0.1 will filter 90% of the traffic) | 1.0 | float | -# Test Parameters +### Example Configuration -``` -{ - provider: 'greenbids', - options: { - pbuid: "PBUID_FROM_GREENBIDS" - sampling: 1.0 - } -} -``` +```javascript + pbjs.enableAnalytics({ + provider: 'greenbids', + options: { + pbuid: "greenbids-publisher-1" // please contact Greenbids to get a pbuid for yourself + greenbidsSampling: 1.0 + } + }); +``` \ No newline at end of file diff --git a/modules/greenbidsRtdProvider.js b/modules/greenbidsRtdProvider.js index b3d79f05996..7fcd163a7c2 100644 --- a/modules/greenbidsRtdProvider.js +++ b/modules/greenbidsRtdProvider.js @@ -1,12 +1,13 @@ -import { logError } from '../src/utils.js'; +import { logError, deepClone, generateUUID, deepSetValue, deepAccess } from '../src/utils.js'; import { ajax } from '../src/ajax.js'; import { submodule } from '../src/hook.js'; +import * as events from '../src/events.js'; +import CONSTANTS from '../src/constants.json'; const MODULE_NAME = 'greenbidsRtdProvider'; -const MODULE_VERSION = '1.0.0'; +const MODULE_VERSION = '2.0.0'; const ENDPOINT = 'https://t.greenbids.ai'; -const auctionInfo = {}; const rtdOptions = {}; function init(moduleConfig) { @@ -16,22 +17,33 @@ function init(moduleConfig) { return false; } else { rtdOptions.pbuid = params?.pbuid; - rtdOptions.targetTPR = params?.targetTPR || 0.99; rtdOptions.timeout = params?.timeout || 200; return true; } } function onAuctionInitEvent(auctionDetails) { - auctionInfo.auctionId = auctionDetails.auctionId; + /* Emitting one billing event per auction */ + let defaultId = 'default_id'; + let greenbidsId = deepAccess(auctionDetails.adUnits[0], 'ortb2Imp.ext.greenbids.greenbidsId', defaultId); + /* greenbids was successfully called so we emit the event */ + if (greenbidsId !== defaultId) { + events.emit(CONSTANTS.EVENTS.BILLABLE_EVENT, { + type: 'auction', + billingId: generateUUID(), + auctionId: auctionDetails.auctionId, + vendor: MODULE_NAME + }); + } } function getBidRequestData(reqBidsConfigObj, callback, config, userConsent) { - let promise = createPromise(reqBidsConfigObj); + let greenbidsId = generateUUID(); + let promise = createPromise(reqBidsConfigObj, greenbidsId); promise.then(callback); } -function createPromise(reqBidsConfigObj) { +function createPromise(reqBidsConfigObj, greenbidsId) { return new Promise((resolve) => { const timeoutId = setTimeout(() => { resolve(reqBidsConfigObj); @@ -40,7 +52,7 @@ function createPromise(reqBidsConfigObj) { ENDPOINT, { success: (response) => { - processSuccessResponse(response, timeoutId, reqBidsConfigObj); + processSuccessResponse(response, timeoutId, reqBidsConfigObj, greenbidsId); resolve(reqBidsConfigObj); }, error: () => { @@ -48,24 +60,35 @@ function createPromise(reqBidsConfigObj) { resolve(reqBidsConfigObj); }, }, - createPayload(reqBidsConfigObj), - { contentType: 'application/json' } + createPayload(reqBidsConfigObj, greenbidsId), + { + contentType: 'application/json', + customHeaders: { + 'Greenbids-Pbuid': rtdOptions.pbuid + } + } ); }); } -function processSuccessResponse(response, timeoutId, reqBidsConfigObj) { +function processSuccessResponse(response, timeoutId, reqBidsConfigObj, greenbidsId) { clearTimeout(timeoutId); const responseAdUnits = JSON.parse(response); - - updateAdUnitsBasedOnResponse(reqBidsConfigObj.adUnits, responseAdUnits); + updateAdUnitsBasedOnResponse(reqBidsConfigObj.adUnits, responseAdUnits, greenbidsId); } -function updateAdUnitsBasedOnResponse(adUnits, responseAdUnits) { +function updateAdUnitsBasedOnResponse(adUnits, responseAdUnits, greenbidsId) { adUnits.forEach((adUnit) => { const matchingAdUnit = findMatchingAdUnit(responseAdUnits, adUnit.code); if (matchingAdUnit) { - removeFalseBidders(adUnit, matchingAdUnit); + deepSetValue(adUnit, 'ortb2Imp.ext.greenbids', { + greenbidsId: greenbidsId, + keptInAuction: matchingAdUnit.bidders, + isExploration: matchingAdUnit.isExploration + }); + if (!matchingAdUnit.isExploration) { + removeFalseBidders(adUnit, matchingAdUnit); + } } }); } @@ -85,14 +108,24 @@ function getFalseBidders(bidders) { .map(([bidder]) => bidder); } -function createPayload(reqBidsConfigObj) { +function stripAdUnits(adUnits) { + const stripedAdUnits = deepClone(adUnits); + return stripedAdUnits.map(adUnit => { + adUnit.bids = adUnit.bids.map(bid => { + return { bidder: bid.bidder }; + }); + return adUnit; + }); +} + +function createPayload(reqBidsConfigObj, greenbidsId) { return JSON.stringify({ - auctionId: auctionInfo.auctionId, version: MODULE_VERSION, + ...rtdOptions, referrer: window.location.href, prebid: '$prebid.version$', - rtdOptions: rtdOptions, - adUnits: reqBidsConfigObj.adUnits, + greenbidsId: greenbidsId, + adUnits: stripAdUnits(reqBidsConfigObj.adUnits), }); } @@ -105,6 +138,7 @@ export const greenbidsSubmodule = { findMatchingAdUnit: findMatchingAdUnit, removeFalseBidders: removeFalseBidders, getFalseBidders: getFalseBidders, + stripAdUnits: stripAdUnits, }; submodule('realTimeData', greenbidsSubmodule); diff --git a/modules/greenbidsRtdProvider.md b/modules/greenbidsRtdProvider.md index 85b8f5a7859..ab8105a4537 100644 --- a/modules/greenbidsRtdProvider.md +++ b/modules/greenbidsRtdProvider.md @@ -2,6 +2,7 @@ ``` Module Name: Greenbids RTD Provider +Module Version: 2.0.0 Module Type: RTD Provider Maintainer: jb@greenbids.ai ``` @@ -21,7 +22,6 @@ This module is configured as part of the `realTimeData.dataProviders` object. | `waitForIt ` | required (mandatory true value) | Tells prebid auction to wait for the result of this module | `'true'` | `boolean` | | `params` | required | | | `Object` | | `params.pbuid` | required | The client site id provided by Greenbids. | `'TEST_FROM_GREENBIDS'` | `string` | -| `params.targetTPR` | optional (default 0.95) | Target True positive rate for the throttling model | `0.99` | `[0-1]` | | `params.timeout` | optional (default 200) | Maximum amount of milliseconds allowed for module to finish working (has to be <= to the realTimeData.auctionDelay property) | `200` | `number` | #### Example diff --git a/test/spec/modules/greenbidsAnalyticsAdapter_spec.js b/test/spec/modules/greenbidsAnalyticsAdapter_spec.js index 3cfdc9b9749..30361ca5661 100644 --- a/test/spec/modules/greenbidsAnalyticsAdapter_spec.js +++ b/test/spec/modules/greenbidsAnalyticsAdapter_spec.js @@ -1,8 +1,11 @@ import { - greenbidsAnalyticsAdapter, parseBidderCode, + greenbidsAnalyticsAdapter, + isSampled, ANALYTICS_VERSION, BIDDER_STATUS } from 'modules/greenbidsAnalyticsAdapter.js'; - +import { + generateUUID, +} from '../../../src/utils.js'; import {expect} from 'chai'; import sinon from 'sinon'; @@ -13,11 +16,42 @@ const pbuid = 'pbuid-AA778D8A796AEA7A0843E2BBEB677766'; const auctionId = 'b0b39610-b941-4659-a87c-de9f62d3e13e'; describe('Greenbids Prebid AnalyticsAdapter Testing', function () { + describe('enableAnalytics and config parser', function () { + const configOptions = { + pbuid: pbuid, + greenbidsSampling: 1, + }; + beforeEach(function () { + greenbidsAnalyticsAdapter.enableAnalytics({ + provider: 'greenbidsAnalytics', + options: configOptions + }); + }); + + afterEach(function () { + greenbidsAnalyticsAdapter.disableAnalytics(); + }); + + it('should parse config correctly with optional values', function () { + expect(greenbidsAnalyticsAdapter.getAnalyticsOptions().options).to.deep.equal(configOptions); + expect(greenbidsAnalyticsAdapter.getAnalyticsOptions().pbuid).to.equal(configOptions.pbuid); + }); + + it('should not enable Analytics when pbuid is missing', function () { + const configOptions = { + options: { + } + }; + const validConfig = greenbidsAnalyticsAdapter.initConfig(configOptions); + expect(validConfig).to.equal(false); + }); + }); + describe('event tracking and message cache manager', function () { beforeEach(function () { const configOptions = { pbuid: pbuid, - sampling: 0, + greenbidsSampling: 1, }; greenbidsAnalyticsAdapter.enableAnalytics({ @@ -30,43 +64,6 @@ describe('Greenbids Prebid AnalyticsAdapter Testing', function () { greenbidsAnalyticsAdapter.disableAnalytics(); }); - describe('#parseBidderCode()', function() { - it('should get lower case bidder code from bidderCode field value', function() { - const receivedBids = [ - { - auctionId: auctionId, - adUnitCode: 'adunit_1', - bidder: 'greenbids', - bidderCode: 'GREENBIDS', - requestId: 'a1b2c3d4', - timeToRespond: 72, - cpm: 0.1, - currency: 'USD', - ad: 'fake ad1' - }, - ]; - const result = parseBidderCode(receivedBids[0]); - expect(result).to.equal('greenbids'); - }); - 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: 'greenbids', - bidderCode: '', - requestId: 'a1b2c3d4', - timeToRespond: 72, - cpm: 0.1, - currency: 'USD', - ad: 'fake ad1' - }, - ]; - const result = parseBidderCode(receivedBids[0]); - expect(result).to.equal('greenbids'); - }); - }); - describe('#getCachedAuction()', function() { const existing = {timeoutBids: [{}]}; greenbidsAnalyticsAdapter.cachedAuctions['test_auction_id'] = existing; @@ -146,7 +143,7 @@ describe('Greenbids Prebid AnalyticsAdapter Testing', function () { auctionId: auctionId, pbuid: pbuid, referrer: window.location.href, - sampling: 0, + sampling: 1, prebid: '$prebid.version$', }); } @@ -260,7 +257,9 @@ describe('Greenbids Prebid AnalyticsAdapter Testing', function () { noBids: noBids }; + sinon.stub(greenbidsAnalyticsAdapter, 'getCachedAuction').returns({timeoutBids: timeoutBids}); const result = greenbidsAnalyticsAdapter.createBidMessage(args, timeoutBids); + greenbidsAnalyticsAdapter.getCachedAuction.restore(); assertHavingRequiredMessageFields(result); expect(result).to.deep.include({ @@ -330,7 +329,7 @@ describe('Greenbids Prebid AnalyticsAdapter Testing', function () { timeout: 3000, auctionEnd: 1234567990, bidsReceived: receivedBids, - noBids: noBids + noBids: noBids, }]; greenbidsAnalyticsAdapter.handleBidTimeout(args); @@ -353,7 +352,7 @@ describe('Greenbids Prebid AnalyticsAdapter Testing', function () { describe('greenbids Analytics Adapter track handler ', function () { const configOptions = { pbuid: pbuid, - sampling: 1, + greenbidsSampling: 1, }; beforeEach(function () { @@ -369,50 +368,52 @@ describe('Greenbids Prebid AnalyticsAdapter Testing', function () { events.getEvents.restore(); }); + it('should call handleAuctionInit as AUCTION_INIT trigger event', function() { + sinon.spy(greenbidsAnalyticsAdapter, 'handleAuctionInit'); + events.emit(constants.EVENTS.AUCTION_INIT, {auctionId: 'auctionId'}); + sinon.assert.callCount(greenbidsAnalyticsAdapter.handleAuctionInit, 1); + greenbidsAnalyticsAdapter.handleAuctionInit.restore(); + }); + it('should call handleBidTimeout as BID_TIMEOUT trigger event', function() { sinon.spy(greenbidsAnalyticsAdapter, 'handleBidTimeout'); - events.emit(constants.EVENTS.BID_TIMEOUT, {}); + events.emit(constants.EVENTS.BID_TIMEOUT, {auctionId: 'auctionId'}); sinon.assert.callCount(greenbidsAnalyticsAdapter.handleBidTimeout, 1); greenbidsAnalyticsAdapter.handleBidTimeout.restore(); }); it('should call handleAuctionEnd as AUCTION_END trigger event', function() { sinon.spy(greenbidsAnalyticsAdapter, 'handleAuctionEnd'); - events.emit(constants.EVENTS.AUCTION_END, {}); + events.emit(constants.EVENTS.AUCTION_END, {auctionId: 'auctionId'}); sinon.assert.callCount(greenbidsAnalyticsAdapter.handleAuctionEnd, 1); greenbidsAnalyticsAdapter.handleAuctionEnd.restore(); }); - }); - - describe('enableAnalytics and config parser', function () { - const configOptions = { - pbuid: pbuid, - sampling: 0, - }; - beforeEach(function () { - greenbidsAnalyticsAdapter.enableAnalytics({ - provider: 'greenbidsAnalytics', - options: configOptions + it('should call handleBillable as BILLABLE_EVENT trigger event', function() { + sinon.spy(greenbidsAnalyticsAdapter, 'handleBillable'); + events.emit(constants.EVENTS.BILLABLE_EVENT, { + type: 'auction', + billingId: generateUUID(), + auctionId: 'auctionId', + vendor: 'greenbidsRtdProvider' }); + sinon.assert.callCount(greenbidsAnalyticsAdapter.handleBillable, 1); + greenbidsAnalyticsAdapter.handleBillable.restore(); }); + }); - afterEach(function () { - greenbidsAnalyticsAdapter.disableAnalytics(); + describe('isSampled', function() { + it('should return true for invalid sampling rates', function() { + expect(isSampled('ce1f3692-632c-4cfd-9e40-0c2ad625ec56', -1)).to.be.true; + expect(isSampled('ce1f3692-632c-4cfd-9e40-0c2ad625ec56', 1.2)).to.be.true; }); - it('should parse config correctly with optional values', function () { - expect(greenbidsAnalyticsAdapter.getAnalyticsOptions().options).to.deep.equal(configOptions); - expect(greenbidsAnalyticsAdapter.getAnalyticsOptions().pbuid).to.equal(configOptions.pbuid); + it('should return determinist falsevalue for valid sampling rate given the predifined id and rate', function() { + expect(isSampled('ce1f3692-632c-4cfd-9e40-0c2ad625ec56', 0.0001)).to.be.false; }); - it('should not enable Analytics when pbuid is missing', function () { - const configOptions = { - options: { - } - }; - const validConfig = greenbidsAnalyticsAdapter.initConfig(configOptions); - expect(validConfig).to.equal(false); + it('should return determinist true value for valid sampling rate given the predifined id and rate', function() { + expect(isSampled('ce1f3692-632c-4cfd-9e40-0c2ad625ec56', 0.9999)).to.be.true; }); }); }); diff --git a/test/spec/modules/greenbidsRtdProvider_spec.js b/test/spec/modules/greenbidsRtdProvider_spec.js index cd93e9013c0..d0083d4dc7a 100644 --- a/test/spec/modules/greenbidsRtdProvider_spec.js +++ b/test/spec/modules/greenbidsRtdProvider_spec.js @@ -6,7 +6,9 @@ import { import { greenbidsSubmodule } from 'modules/greenbidsRtdProvider.js'; -import {server} from '../../mocks/xhr.js'; +import { server } from '../../mocks/xhr.js'; +import * as events from '../../../src/events.js'; +import CONSTANTS from '../../../src/constants.json'; describe('greenbidsRtdProvider', () => { const endPoint = 't.greenbids.ai'; @@ -39,14 +41,36 @@ describe('greenbidsRtdProvider', () => { }] }; - const SAMPLE_RESPONSE_ADUNITS = [ + const SAMPLE_RESPONSE_ADUNITS_NOT_EXPLORED = [ { code: 'adUnit1', bidders: { 'appnexus': true, 'rubicon': false, 'ix': true - } + }, + isExploration: false + }, + { + code: 'adUnit2', + bidders: { + 'appnexus': false, + 'rubicon': true, + 'openx': true + }, + isExploration: false + + }]; + + const SAMPLE_RESPONSE_ADUNITS_EXPLORED = [ + { + code: 'adUnit1', + bidders: { + 'appnexus': true, + 'rubicon': false, + 'ix': true + }, + isExploration: true }, { code: 'adUnit2', @@ -54,7 +78,9 @@ describe('greenbidsRtdProvider', () => { 'appnexus': false, 'rubicon': true, 'openx': true - } + }, + isExploration: true + }]; describe('init', () => { @@ -70,22 +96,37 @@ describe('greenbidsRtdProvider', () => { }); describe('updateAdUnitsBasedOnResponse', () => { - it('should update ad units based on response', () => { + it('should update ad units based on response if not exploring', () => { const adUnits = JSON.parse(JSON.stringify(SAMPLE_REQUEST_BIDS_CONFIG_OBJ.adUnits)); - greenbidsSubmodule.updateAdUnitsBasedOnResponse(adUnits, SAMPLE_RESPONSE_ADUNITS); + greenbidsSubmodule.updateAdUnitsBasedOnResponse(adUnits, SAMPLE_RESPONSE_ADUNITS_NOT_EXPLORED); expect(adUnits[0].bids).to.have.length(2); expect(adUnits[1].bids).to.have.length(2); }); + + it('should not update ad units based on response if exploring', () => { + const adUnits = JSON.parse(JSON.stringify(SAMPLE_REQUEST_BIDS_CONFIG_OBJ.adUnits)); + greenbidsSubmodule.updateAdUnitsBasedOnResponse(adUnits, SAMPLE_RESPONSE_ADUNITS_EXPLORED); + + expect(adUnits[0].bids).to.have.length(3); + expect(adUnits[1].bids).to.have.length(3); + expect(adUnits[0].ortb2Imp.ext.greenbids.greenbidsId).to.be.a.string; + expect(adUnits[1].ortb2Imp.ext.greenbids.greenbidsId).to.be.a.string; + expect(adUnits[0].ortb2Imp.ext.greenbids.greenbidsId).to.equal(adUnits[0].ortb2Imp.ext.greenbids.greenbidsId); + expect(adUnits[0].ortb2Imp.ext.greenbids.keptInAuction).to.deep.equal(SAMPLE_RESPONSE_ADUNITS_EXPLORED[0].bidders); + expect(adUnits[1].ortb2Imp.ext.greenbids.keptInAuction).to.deep.equal(SAMPLE_RESPONSE_ADUNITS_EXPLORED[1].bidders); + expect(adUnits[0].ortb2Imp.ext.greenbids.isExploration).to.equal(SAMPLE_RESPONSE_ADUNITS_EXPLORED[0].isExploration); + expect(adUnits[1].ortb2Imp.ext.greenbids.isExploration).to.equal(SAMPLE_RESPONSE_ADUNITS_EXPLORED[1].isExploration); + }); }); describe('findMatchingAdUnit', () => { it('should find matching ad unit by code', () => { - const matchingAdUnit = greenbidsSubmodule.findMatchingAdUnit(SAMPLE_RESPONSE_ADUNITS, 'adUnit1'); - expect(matchingAdUnit).to.deep.equal(SAMPLE_RESPONSE_ADUNITS[0]); + const matchingAdUnit = greenbidsSubmodule.findMatchingAdUnit(SAMPLE_RESPONSE_ADUNITS_NOT_EXPLORED, 'adUnit1'); + expect(matchingAdUnit).to.deep.equal(SAMPLE_RESPONSE_ADUNITS_NOT_EXPLORED[0]); }); it('should return undefined if no matching ad unit is found', () => { - const matchingAdUnit = greenbidsSubmodule.findMatchingAdUnit(SAMPLE_RESPONSE_ADUNITS, 'nonexistent'); + const matchingAdUnit = greenbidsSubmodule.findMatchingAdUnit(SAMPLE_RESPONSE_ADUNITS_NOT_EXPLORED, 'nonexistent'); expect(matchingAdUnit).to.be.undefined; }); }); @@ -93,7 +134,7 @@ describe('greenbidsRtdProvider', () => { describe('removeFalseBidders', () => { it('should remove bidders with false value', () => { const adUnit = JSON.parse(JSON.stringify(SAMPLE_REQUEST_BIDS_CONFIG_OBJ.adUnits[0])); - const matchingAdUnit = SAMPLE_RESPONSE_ADUNITS[0]; + const matchingAdUnit = SAMPLE_RESPONSE_ADUNITS_NOT_EXPLORED[0]; greenbidsSubmodule.removeFalseBidders(adUnit, matchingAdUnit); expect(adUnit.bids).to.have.length(2); expect(adUnit.bids.map((bid) => bid.bidder)).to.not.include('rubicon'); @@ -125,14 +166,15 @@ describe('greenbidsRtdProvider', () => { setTimeout(() => { server.requests[0].respond( 200, - {'Content-Type': 'application/json'}, - JSON.stringify(SAMPLE_RESPONSE_ADUNITS) + { 'Content-Type': 'application/json' }, + JSON.stringify(SAMPLE_RESPONSE_ADUNITS_NOT_EXPLORED) ); }, 50); setTimeout(() => { const requestUrl = new URL(server.requests[0].url); expect(requestUrl.host).to.be.eq(endPoint); + expect(requestBids.greenbidsId).to.be.a.string; expect(requestBids.adUnits[0].bids).to.have.length(2); expect(requestBids.adUnits[0].bids.map((bid) => bid.bidder)).to.not.include('rubicon'); expect(requestBids.adUnits[0].bids.map((bid) => bid.bidder)).to.include('ix'); @@ -157,8 +199,8 @@ describe('greenbidsRtdProvider', () => { setTimeout(() => { server.requests[0].respond( 200, - {'Content-Type': 'application/json'}, - JSON.stringify(SAMPLE_RESPONSE_ADUNITS) + { 'Content-Type': 'application/json' }, + JSON.stringify(SAMPLE_RESPONSE_ADUNITS_NOT_EXPLORED) ); done(); }, 300); @@ -166,6 +208,7 @@ describe('greenbidsRtdProvider', () => { setTimeout(() => { const requestUrl = new URL(server.requests[0].url); expect(requestUrl.host).to.be.eq(endPoint); + expect(requestBids.greenbidsId).to.be.a.string; expect(requestBids.adUnits[0].bids).to.have.length(3); expect(requestBids.adUnits[1].bids).to.have.length(3); expect(callback.calledOnce).to.be.true; @@ -183,14 +226,15 @@ describe('greenbidsRtdProvider', () => { setTimeout(() => { server.requests[0].respond( 500, - {'Content-Type': 'application/json'}, - JSON.stringify({'failure': 'fail'}) + { 'Content-Type': 'application/json' }, + JSON.stringify({ 'failure': 'fail' }) ); }, 50); setTimeout(() => { const requestUrl = new URL(server.requests[0].url); expect(requestUrl.host).to.be.eq(endPoint); + expect(requestBids.greenbidsId).to.be.a.string; expect(requestBids.adUnits[0].bids).to.have.length(3); expect(requestBids.adUnits[1].bids).to.have.length(3); expect(callback.calledOnce).to.be.true; @@ -198,4 +242,116 @@ describe('greenbidsRtdProvider', () => { }, 60); }); }); + + describe('stripAdUnits', function () { + it('should strip all properties except bidder from each bid in adUnits', function () { + const adUnits = + [ + { + bids: [ + { bidder: 'bidder1', otherProp: 'value1' }, + { bidder: 'bidder2', otherProp: 'value2' } + ], + mediaTypes: { 'banner': { prop: 'value3' } } + } + ]; + const expectedOutput = [ + { + bids: [ + { bidder: 'bidder1' }, + { bidder: 'bidder2' } + ], + mediaTypes: { 'banner': { prop: 'value3' } } + } + ]; + + // Perform the test + const output = greenbidsSubmodule.stripAdUnits(adUnits); + expect(output).to.deep.equal(expectedOutput); + }); + + it('should strip all properties except bidder from each bid in adUnits but keep ortb2Imp', function () { + const adUnits = + [ + { + bids: [ + { bidder: 'bidder1', otherProp: 'value1' }, + { bidder: 'bidder2', otherProp: 'value2' } + ], + mediaTypes: { 'banner': { prop: 'value3' } }, + ortb2Imp: { + ext: { + greenbids: { + greenbidsId: 'test' + } + } + } + } + ]; + const expectedOutput = [ + { + bids: [ + { bidder: 'bidder1' }, + { bidder: 'bidder2' } + ], + mediaTypes: { 'banner': { prop: 'value3' } }, + ortb2Imp: { + ext: { + greenbids: { + greenbidsId: 'test' + } + } + } + } + ]; + + // Perform the test + const output = greenbidsSubmodule.stripAdUnits(adUnits); + expect(output).to.deep.equal(expectedOutput); + }); + }); + + describe('onAuctionInitEvent', function () { + it('should not emit billable event if greenbids hasn\'t set the adunit.ext value', function () { + sinon.spy(events, 'emit'); + greenbidsSubmodule.onAuctionInitEvent({ + auctionId: 'test', + adUnits: [ + { + bids: [ + { bidder: 'bidder1', otherProp: 'value1' }, + { bidder: 'bidder2', otherProp: 'value2' } + ], + mediaTypes: { 'banner': { prop: 'value3' } }, + } + ] + }); + sinon.assert.callCount(events.emit, 0); + events.emit.restore(); + }); + + it('should emit billable event if greenbids has set the adunit.ext value', function (done) { + let counter = 0; + events.on(CONSTANTS.EVENTS.BILLABLE_EVENT, function (event) { + if (event.vendor === 'greenbidsRtdProvider' && event.type === 'auction') { + counter += 1; + } + expect(counter).to.equal(1); + done(); + }); + greenbidsSubmodule.onAuctionInitEvent({ + auctionId: 'test', + adUnits: [ + { + bids: [ + { bidder: 'bidder1', otherProp: 'value1' }, + { bidder: 'bidder2', otherProp: 'value2' } + ], + mediaTypes: { 'banner': { prop: 'value3' } }, + ortb2Imp: { ext: { greenbids: { greenbidsId: 'b0b39610-b941-4659-a87c-de9f62d3e13e' } } } + } + ] + }); + }); + }); }); From d390f6c3d84e65f0d4a5d60020acc7b9201be9b1 Mon Sep 17 00:00:00 2001 From: Denis <7009699+someden@users.noreply.github.com> Date: Sat, 6 Jan 2024 20:18:51 +0300 Subject: [PATCH 118/246] Core: add eslint-plugin-jsdoc (#10893) --- .eslintrc.js | 35 +++- modules/adagioAnalyticsAdapter.js | 24 +-- modules/adagioBidAdapter.js | 10 +- modules/admaticBidAdapter.js | 3 +- modules/adpod.js | 2 +- modules/adqueryBidAdapter.js | 3 +- modules/beopBidAdapter.js | 22 +-- modules/bliinkBidAdapter.js | 4 +- modules/blueconicRtdProvider.js | 6 +- modules/brandmetricsRtdProvider.js | 12 +- modules/bucksenseBidAdapter.js | 12 +- modules/codefuelBidAdapter.js | 42 ++--- modules/criteoBidAdapter.js | 3 +- modules/deltaprojectsBidAdapter.js | 16 +- modules/fledgeForGpt.js | 4 +- modules/freewheel-sspBidAdapter.js | 36 ++--- modules/geoedgeRtdProvider.js | 8 +- modules/getintentBidAdapter.js | 8 +- modules/gjirafaBidAdapter.js | 10 +- modules/goldfishAdsRtdProvider.js | 12 +- modules/gravitoIdSystem.js | 24 +-- modules/idWardRtdProvider.js | 28 ++-- modules/imRtdProvider.js | 4 +- modules/impactifyBidAdapter.js | 4 +- modules/incrxBidAdapter.js | 32 ++-- modules/integr8BidAdapter.js | 10 +- modules/ixBidAdapter.js | 14 +- modules/jwplayerRtdProvider.js | 16 +- modules/lemmaDigitalBidAdapter.js | 76 ++++----- modules/malltvBidAdapter.js | 10 +- modules/mediafilterRtdProvider.js | 4 +- modules/mediasquareBidAdapter.js | 48 +++--- modules/merkleIdSystem.js | 28 ++-- modules/multibid/index.js | 58 +++---- modules/mwOpenLinkIdSystem.js | 26 +-- modules/naveggIdSystem.js | 26 +-- modules/nobidBidAdapter.js | 48 +++--- modules/novatiqIdSystem.js | 24 +-- modules/oneKeyIdSystem.js | 30 ++-- modules/operaadsIdSystem.js | 30 ++-- modules/optidigitalBidAdapter.js | 42 ++--- modules/optimeraRtdProvider.js | 3 +- modules/optimonAnalyticsAdapter.js | 16 +- modules/pairIdSystem.js | 30 ++-- modules/pilotxBidAdapter.js | 30 ++-- modules/prebidServerBidAdapter/index.js | 2 +- modules/priceFloors.js | 4 +- modules/prismaBidAdapter.js | 48 +++--- modules/pubmaticBidAdapter.js | 16 +- modules/relevantdigitalBidAdapter.js | 2 +- modules/sizeMappingV2.js | 2 +- modules/slimcutBidAdapter.js | 30 ++-- modules/smilewantedBidAdapter.js | 6 +- modules/sovrnBidAdapter.js | 2 +- modules/sspBCBidAdapter.js | 2 +- modules/tappxBidAdapter.js | 14 +- modules/teadsIdSystem.js | 28 ++-- modules/temedyaBidAdapter.js | 30 ++-- modules/topicsFpdModule.js | 8 +- modules/ucfunnelBidAdapter.js | 2 +- modules/userId/index.js | 14 +- modules/weboramaRtdProvider.js | 81 ++++++---- modules/xeBidAdapter.js | 22 +-- modules/yandexBidAdapter.js | 2 +- modules/zetaBidAdapter.js | 34 ++-- package-lock.json | 153 ++++++++++++++++++ package.json | 1 + src/adapters/bidderFactory.js | 11 +- src/auction.js | 42 ++--- src/events.js | 5 +- src/targeting.js | 40 ++--- src/userSync.js | 2 +- .../e2e/modules/e2e_consent_mgt_gdpr.spec.js | 4 +- 73 files changed, 881 insertions(+), 659 deletions(-) diff --git a/.eslintrc.js b/.eslintrc.js index fc3ad3afe66..511e78048e4 100644 --- a/.eslintrc.js +++ b/.eslintrc.js @@ -11,12 +11,25 @@ module.exports = { node: { moduleDirectory: ['node_modules', './'] } + }, + 'jsdoc': { + mode: 'typescript', + tagNamePreference: { + 'tag constructor': 'constructor', + extends: 'extends', + method: 'method', + return: 'return', + } } }, - extends: 'standard', + extends: [ + 'standard', + 'plugin:jsdoc/recommended' + ], plugins: [ 'prebid', - 'import' + 'import', + 'jsdoc' ], globals: { 'BROWSERSTACK_USERNAME': false, @@ -46,6 +59,24 @@ module.exports = { 'no-undef': 2, 'no-useless-escape': 'off', 'no-console': 'error', + 'jsdoc/check-types': 'off', + 'jsdoc/newline-after-description': 'off', + 'jsdoc/require-jsdoc': 'off', + 'jsdoc/require-param': 'off', + 'jsdoc/require-param-description': 'off', + 'jsdoc/require-param-name': 'off', + 'jsdoc/require-param-type': 'off', + 'jsdoc/require-property': 'off', + 'jsdoc/require-property-description': 'off', + 'jsdoc/require-property-name': 'off', + 'jsdoc/require-property-type': 'off', + 'jsdoc/require-returns': 'off', + 'jsdoc/require-returns-check': 'off', + 'jsdoc/require-returns-description': 'off', + 'jsdoc/require-returns-type': 'off', + 'jsdoc/require-yields': 'off', + 'jsdoc/require-yields-check': 'off', + 'jsdoc/tag-lines': 'off' }, overrides: Object.keys(allowedModules).map((key) => ({ files: key + '/**/*.js', diff --git a/modules/adagioAnalyticsAdapter.js b/modules/adagioAnalyticsAdapter.js index 807937ba3dd..de0aa1cb5d7 100644 --- a/modules/adagioAnalyticsAdapter.js +++ b/modules/adagioAnalyticsAdapter.js @@ -48,7 +48,7 @@ const enc = window.encodeURIComponent; /** /* BEGIN ADAGIO.JS CODE -*/ + */ function canAccessTopWindow() { try { @@ -71,12 +71,12 @@ const adagioEnqueue = function adagioEnqueue(action, data) { }; /** -* END ADAGIO.JS CODE -*/ + * END ADAGIO.JS CODE + */ /** -* UTILS FUNCTIONS -*/ + * UTILS FUNCTIONS + */ const guard = { adagio: (value) => isAdagio(value), @@ -130,9 +130,9 @@ function addKeyPrefix(obj, prefix) { } /** -* sendRequest to Adagio. It filter null values and encode each query param. -* @param {Object} qp -*/ + * sendRequest to Adagio. It filter null values and encode each query param. + * @param {Object} qp + */ function sendRequest(qp) { // Removing null values qp = Object.keys(qp).reduce((acc, key) => { @@ -164,7 +164,7 @@ function getTargetedAuctionId(bid) { /** * END UTILS FUNCTIONS -*/ + */ /** * HANDLERS @@ -175,7 +175,7 @@ function getTargetedAuctionId(bid) { * - handlerAdRender * * Each handler is called when the event is fired. -*/ + */ function handlerAuctionInit(event) { const w = getCurrentWindow(); @@ -271,7 +271,7 @@ function handlerAuctionInit(event) { * handlerBidResponse allow to track the adagio bid response * and to update the auction cache with the seat ID. * No beacon is sent here. -*/ + */ function handlerBidResponse(event) { if (!guard.adagio(event.bidder)) { return; @@ -372,7 +372,7 @@ function handlerAdRender(event, isSuccess) { /** * END HANDLERS -*/ + */ let adagioAdapter = Object.assign(adapter({ emptyUrl, analyticsType }), { track: function(event) { diff --git a/modules/adagioBidAdapter.js b/modules/adagioBidAdapter.js index 59d1fa72cbb..112722383a9 100644 --- a/modules/adagioBidAdapter.js +++ b/modules/adagioBidAdapter.js @@ -700,9 +700,9 @@ function getPageDimensions() { } /** -* @todo Move to prebid Core as Utils. -* @returns -*/ + * @todo Move to prebid Core as Utils. + * @returns + */ function getViewPortDimensions() { if (!isSafeFrameWindow() && !canAccessTopWindow()) { return ''; @@ -832,8 +832,8 @@ function getPrintNumber(adUnitCode, bidderRequest) { } /** - * domLoading feature is computed on window.top if reachable. - */ + * domLoading feature is computed on window.top if reachable. + */ function getDomLoadingDuration() { let domLoadingDuration = -1; let performance; diff --git a/modules/admaticBidAdapter.js b/modules/admaticBidAdapter.js index 236114f6396..5afe7781d7d 100644 --- a/modules/admaticBidAdapter.js +++ b/modules/admaticBidAdapter.js @@ -33,7 +33,8 @@ export const spec = { {code: 'pixad'} ], supportedMediaTypes: [BANNER, VIDEO, NATIVE], - /** f + /** + * f * @param {object} bid * @return {boolean} */ diff --git a/modules/adpod.js b/modules/adpod.js index d2fd817ee62..f6d8309cd9f 100644 --- a/modules/adpod.js +++ b/modules/adpod.js @@ -319,7 +319,7 @@ export function checkAdUnitSetupHook(fn, adUnits) { * @param {Object} videoMediaType 'mediaTypes.video' associated to bidResponse * @param {Object} bidResponse incoming bidResponse being evaluated by bidderFactory * @returns {boolean} return false if bid duration is deemed invalid as per adUnit configuration; return true if fine -*/ + */ function checkBidDuration(videoMediaType, bidResponse) { const buffer = 2; let bidDuration = deepAccess(bidResponse, 'video.durationSeconds'); diff --git a/modules/adqueryBidAdapter.js b/modules/adqueryBidAdapter.js index 0f445fdfd78..6b0d642468b 100644 --- a/modules/adqueryBidAdapter.js +++ b/modules/adqueryBidAdapter.js @@ -17,7 +17,8 @@ export const spec = { gvlid: ADQUERY_GVLID, supportedMediaTypes: [BANNER], - /** f + /** + * f * @param {object} bid * @return {boolean} */ diff --git a/modules/beopBidAdapter.js b/modules/beopBidAdapter.js index b6b6107ddd0..ad52af824ee 100644 --- a/modules/beopBidAdapter.js +++ b/modules/beopBidAdapter.js @@ -23,11 +23,11 @@ export const spec = { gvlid: TCF_VENDOR_ID, aliases: ['bp'], /** - * Test if the bid request is valid. - * - * @param {bid} : The Bid params - * @return boolean true if the bid request is valid (aka contains a valid accountId or networkId and is open for BANNER), false otherwise. - */ + * Test if the bid request is valid. + * + * @param {bid} : The Bid params + * @return boolean true if the bid request is valid (aka contains a valid accountId or networkId and is open for BANNER), false otherwise. + */ isBidRequestValid: function(bid) { const id = bid.params.accountId || bid.params.networkId; if (id === null || typeof id === 'undefined') { @@ -39,12 +39,12 @@ export const spec = { return bid.mediaTypes.banner !== null && typeof bid.mediaTypes.banner !== 'undefined'; }, /** - * Create a BeOp server request from a list of BidRequest - * - * @param {validBidRequests[], ...} : The array of validated bidRequests - * @param {... , bidderRequest} : Common params for each bidRequests - * @return ServerRequest Info describing the request to the BeOp's server - */ + * Create a BeOp server request from a list of BidRequest + * + * @param {validBidRequests[], ...} : The array of validated bidRequests + * @param {... , bidderRequest} : Common params for each bidRequests + * @return ServerRequest Info describing the request to the BeOp's server + */ buildRequests: function(validBidRequests, bidderRequest) { const slots = validBidRequests.map(beOpRequestSlotsMaker); const firstPartyData = bidderRequest.ortb2 || {}; diff --git a/modules/bliinkBidAdapter.js b/modules/bliinkBidAdapter.js index e674c6987c3..debe956d7eb 100644 --- a/modules/bliinkBidAdapter.js +++ b/modules/bliinkBidAdapter.js @@ -134,8 +134,8 @@ function canAccessTopWindow() { } /** - * domLoading feature is computed on window.top if reachable. - */ + * domLoading feature is computed on window.top if reachable. + */ export function getDomLoadingDuration() { let domLoadingDuration = -1; let performance; diff --git a/modules/blueconicRtdProvider.js b/modules/blueconicRtdProvider.js index b6eb9374671..bf0c457bebd 100644 --- a/modules/blueconicRtdProvider.js +++ b/modules/blueconicRtdProvider.js @@ -19,9 +19,9 @@ export const RTD_LOCAL_NAME = 'bcPrebidData'; export const storage = getStorageManager({moduleType: MODULE_TYPE_RTD, moduleName: SUBMODULE_NAME}); /** -* Try parsing stringified array of data. -* @param {String} data -*/ + * Try parsing stringified array of data. + * @param {String} data + */ function parseJson(data) { try { return JSON.parse(data); diff --git a/modules/brandmetricsRtdProvider.js b/modules/brandmetricsRtdProvider.js index bd7a33ff037..20b1125cee9 100644 --- a/modules/brandmetricsRtdProvider.js +++ b/modules/brandmetricsRtdProvider.js @@ -72,10 +72,10 @@ function checkConsent (userConsent) { } /** -* Add event- listeners to hook in to brandmetrics events -* @param {Object} reqBidsConfigObj -* @param {function} callback -*/ + * Add event- listeners to hook in to brandmetrics events + * @param {Object} reqBidsConfigObj + * @param {function} callback + */ function processBrandmetricsEvents (reqBidsConfigObj, moduleConfig, callback) { const callBidTargeting = (event) => { if (event.available && event.conf) { @@ -139,8 +139,8 @@ function initializeBrandmetrics(scriptId) { } /** -* Hook in to brandmetrics creative_in_view- event and emit billable- event for creatives measured by brandmetrics. -*/ + * Hook in to brandmetrics creative_in_view- event and emit billable- event for creatives measured by brandmetrics. + */ function initializeBillableEvents() { if (!billableEventsInitialized) { window._brandmetrics.push({ diff --git a/modules/bucksenseBidAdapter.js b/modules/bucksenseBidAdapter.js index 42edb783d00..ecf27de997d 100644 --- a/modules/bucksenseBidAdapter.js +++ b/modules/bucksenseBidAdapter.js @@ -16,7 +16,7 @@ export const spec = { * * @param {object} bid The bid to validate. * @return boolean True if this is a valid bid, and false otherwise. - */ + */ isBidRequestValid: function (bid) { logInfo(WHO + ' isBidRequestValid() - INPUT bid:', bid); if (bid.bidder !== BIDDER_CODE || typeof bid.params === 'undefined') { @@ -29,10 +29,10 @@ export const spec = { }, /** - * 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. + * Make a server request from the list of BidRequests. + * + * @param {BidRequest[]} validBidRequests A non-empty list of valid bid requests that should be sent to the Server. + * @return ServerRequest Info describing the request to the server. */ buildRequests: function (validBidRequests, bidderRequest) { logInfo(WHO + ' buildRequests() - INPUT validBidRequests:', validBidRequests, 'INPUT bidderRequest:', bidderRequest); @@ -74,7 +74,7 @@ export const spec = { * * @param {*} serverResponse A successful response from the server. * @return {Bid[]} An array of bids which were nested inside the server. - */ + */ interpretResponse: function (serverResponse, request) { logInfo(WHO + ' interpretResponse() - INPUT serverResponse:', serverResponse, 'INPUT request:', request); diff --git a/modules/codefuelBidAdapter.js b/modules/codefuelBidAdapter.js index 2548b20189b..4fef3fb2494 100644 --- a/modules/codefuelBidAdapter.js +++ b/modules/codefuelBidAdapter.js @@ -10,11 +10,11 @@ export const spec = { supportedMediaTypes: [ BANNER ], 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. - */ + * 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.nativeParams) { return false; @@ -22,11 +22,11 @@ export const spec = { return !!(bid.params.placementId || (bid.params.member && bid.params.invCode)); }, /** - * 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 {validBidRequests[]} - an array of bids + * @return ServerRequest Info describing the request to the server. + */ buildRequests: function(validBidRequests, bidderRequest) { const page = bidderRequest.refererInfo.page; const domain = bidderRequest.refererInfo.domain; @@ -78,11 +78,11 @@ 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. + * @return {Bid[]} An array of bids which were nested inside the server. + */ interpretResponse: (serverResponse, { bids }) => { if (!serverResponse.body) { return []; @@ -116,12 +116,12 @@ 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: function(syncOptions, serverResponses, gdprConsent, uspConsent) { return []; } diff --git a/modules/criteoBidAdapter.js b/modules/criteoBidAdapter.js index 4fe9ca7d0f4..03aaeae434c 100644 --- a/modules/criteoBidAdapter.js +++ b/modules/criteoBidAdapter.js @@ -125,7 +125,8 @@ export const spec = { return []; }, - /** f + /** + * f * @param {object} bid * @return {boolean} */ diff --git a/modules/deltaprojectsBidAdapter.js b/modules/deltaprojectsBidAdapter.js index c66e381b8f1..870378a13dd 100644 --- a/modules/deltaprojectsBidAdapter.js +++ b/modules/deltaprojectsBidAdapter.js @@ -16,7 +16,7 @@ export const BIDDER_CODE = 'deltaprojects'; export const BIDDER_ENDPOINT_URL = 'https://d5p.de17a.com/dogfight/prebid'; export const USERSYNC_URL = 'https://userservice.de17a.com/getuid/prebid'; -/** -- isBidRequestValid --**/ +/** -- isBidRequestValid -- */ function isBidRequestValid(bid) { if (!bid) return false; @@ -32,9 +32,9 @@ function isBidRequestValid(bid) { return true; } -/** -- Build requests --**/ +/** -- Build requests -- */ function buildRequests(validBidRequests, bidderRequest) { - /** == shared ==**/ + /** == shared == */ // -- build id const id = bidderRequest.bidderRequestId; @@ -146,7 +146,7 @@ function buildImpressionBanner(bid, bannerMediaType) { }; } -/** -- Interpret response --**/ +/** -- Interpret response -- */ function interpretResponse(serverResponse) { if (!serverResponse.body) { logWarn('Response body is invalid, return !!'); @@ -189,7 +189,7 @@ function interpretResponse(serverResponse) { return bidResponses; } -/** -- On Bid Won -- **/ +/** -- On Bid Won -- */ function onBidWon(bid) { let cpm = bid.cpm; if (bid.currency && bid.currency !== bid.originalCurrency && typeof bid.getCpmInNewCurrency === 'function') { @@ -200,7 +200,7 @@ function onBidWon(bid) { bid.ad = bid.ad.replace(wonPriceMacroPatten, wonPrice); } -/** -- Get user syncs --**/ +/** -- Get user syncs -- */ function getUserSyncs(syncOptions, serverResponses, gdprConsent) { const syncs = [] @@ -223,7 +223,7 @@ function getUserSyncs(syncOptions, serverResponses, gdprConsent) { return syncs; } -/** -- Get bid floor --**/ +/** -- Get bid floor -- */ export function getBidFloor(bid, mediaType, size, currency) { if (isFn(bid.getFloor)) { const bidFloorCurrency = currency || 'USD'; @@ -234,7 +234,7 @@ export function getBidFloor(bid, mediaType, size, currency) { } } -/** -- Helper methods --**/ +/** -- Helper methods -- */ function setOnAny(collection, key) { for (let i = 0, result; i < collection.length; i++) { result = deepAccess(collection[i], key); diff --git a/modules/fledgeForGpt.js b/modules/fledgeForGpt.js index 566dd5f40e2..7162f4e9230 100644 --- a/modules/fledgeForGpt.js +++ b/modules/fledgeForGpt.js @@ -20,8 +20,8 @@ export let isEnabled = false; config.getConfig('fledgeForGpt', config => init(config.fledgeForGpt)); /** - * Module init. - */ + * Module init. + */ export function init(cfg) { if (cfg && cfg.enabled === true) { if (!isEnabled) { diff --git a/modules/freewheel-sspBidAdapter.js b/modules/freewheel-sspBidAdapter.js index ba819abd687..73c56160b00 100644 --- a/modules/freewheel-sspBidAdapter.js +++ b/modules/freewheel-sspBidAdapter.js @@ -183,8 +183,8 @@ function getCampaignId(xmlNode) { } /** -* returns the top most accessible window -*/ + * returns the top most accessible window + */ function getTopMostWindow() { var res = window; @@ -319,21 +319,21 @@ export const spec = { supportedMediaTypes: [BANNER, VIDEO], aliases: ['stickyadstv', 'freewheelssp'], // aliases for freewheel-ssp /** - * 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. - */ + * 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.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. - */ + * 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) { // var currency = config.getConfig(currency); @@ -474,12 +474,12 @@ export const spec = { }, /** - * Unpack the response from the server into a list of bids. - * - * @param {*} serverResponse A successful response from the server. - * @param {object} request: the built request object containing the initial bidRequest. - * @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 A successful response from the server. + * @param {object} request: the built request object containing the initial bidRequest. + * @return {Bid[]} An array of bids which were nested inside the server. + */ interpretResponse: function(serverResponse, request) { var bidrequest = request.bidRequest; var playerSize = []; diff --git a/modules/geoedgeRtdProvider.js b/modules/geoedgeRtdProvider.js index afaaef93169..9bdf74c6e3c 100644 --- a/modules/geoedgeRtdProvider.js +++ b/modules/geoedgeRtdProvider.js @@ -71,7 +71,7 @@ export function setWrapper(responseText) { /** * preloads the client - * @param {string} key + * @param {string} key */ export function preloadClient(key) { let link = document.createElement('link'); @@ -252,9 +252,9 @@ function init(config, userConsent) { /** @type {RtdSubmodule} */ export const geoedgeSubmodule = { /** - * used to link submodule with realTimeData - * @type {string} - */ + * used to link submodule with realTimeData + * @type {string} + */ name: SUBMODULE_NAME, init, onBidResponseEvent: conditionallyWrap diff --git a/modules/getintentBidAdapter.js b/modules/getintentBidAdapter.js index 2b6ea1c2c2a..8cffccb9f30 100644 --- a/modules/getintentBidAdapter.js +++ b/modules/getintentBidAdapter.js @@ -38,7 +38,7 @@ export const spec = { * * @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); }, @@ -108,7 +108,7 @@ function buildUrl(bid) { * * @param {BidRequest} bidRequest. * @return {object} GI bid request. - * */ + */ function buildGiBidRequest(bidRequest) { let giBidRequest = { bid_id: bidRequest.bidId, @@ -191,7 +191,7 @@ function addOptional(params, request, props) { /** * @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); } @@ -200,7 +200,7 @@ function parseSize(s) { * @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])) { diff --git a/modules/gjirafaBidAdapter.js b/modules/gjirafaBidAdapter.js index 91ed5c9b3fb..86cbb1d89de 100644 --- a/modules/gjirafaBidAdapter.js +++ b/modules/gjirafaBidAdapter.js @@ -116,11 +116,11 @@ export const spec = { }; /** -* 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. -*/ + * 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); } diff --git a/modules/goldfishAdsRtdProvider.js b/modules/goldfishAdsRtdProvider.js index c466ac5ec25..c90d3104b56 100755 --- a/modules/goldfishAdsRtdProvider.js +++ b/modules/goldfishAdsRtdProvider.js @@ -74,9 +74,9 @@ const getTargetingDataFromApi = (key) => { /** * @returns {{ * name: 'golfishads.com', - * ext: { segtax: 4}, - * segment: string[] - * } | null } + * ext: { segtax: 4}, + * segment: string[] + * } | null } */ export const getStorageData = () => { const now = new Date(); @@ -140,9 +140,9 @@ const init = (config, userConsent) => { * * @param {{ * name: string, -* ext: { segtax: 4}, -* segment: {id: string}[] -* } | null } userData + * ext: { segtax: 4}, + * segment: {id: string}[] + * } | null } userData * @param {*} reqBidsConfigObj * @returns */ diff --git a/modules/gravitoIdSystem.js b/modules/gravitoIdSystem.js index 70031ebd06e..cc02c6a103e 100644 --- a/modules/gravitoIdSystem.js +++ b/modules/gravitoIdSystem.js @@ -16,16 +16,16 @@ export const cookieKey = 'gravitompId'; export const gravitoIdSystemSubmodule = { /** - * used to link submodule with config - * @type {string} - */ + * used to link submodule with config + * @type {string} + */ name: MODULE_NAME, /** - * performs action to obtain id - * @function - * @returns { {id: {gravitompId: string}} | undefined } - */ + * performs action to obtain id + * @function + * @returns { {id: {gravitompId: string}} | undefined } + */ getId: function() { const newId = storage.getCookie(cookieKey); if (!newId) { @@ -38,11 +38,11 @@ export const gravitoIdSystemSubmodule = { }, /** - * decode the stored id value for passing to bid requests - * @function - * @param { {gravitompId: string} } value - * @returns { {gravitompId: {string} } | undefined } - */ + * decode the stored id value for passing to bid requests + * @function + * @param { {gravitompId: string} } value + * @returns { {gravitompId: {string} } | undefined } + */ decode: function(value) { if (value && typeof value === 'object') { var result = {}; diff --git a/modules/idWardRtdProvider.js b/modules/idWardRtdProvider.js index 29dda216fdc..4e51f87cc81 100644 --- a/modules/idWardRtdProvider.js +++ b/modules/idWardRtdProvider.js @@ -28,9 +28,9 @@ function addRealTimeData(ortb2, rtd) { } /** - * Try parsing stringified array of segment IDs. - * @param {String} data - */ + * Try parsing stringified array of segment IDs. + * @param {String} data + */ function tryParse(data) { try { return JSON.parse(data); @@ -41,12 +41,12 @@ function tryParse(data) { } /** - * Real-time data retrieval from ID Ward - * @param {Object} reqBidsConfigObj - * @param {function} onDone - * @param {Object} rtdConfig - * @param {Object} userConsent - */ + * Real-time data retrieval from ID Ward + * @param {Object} reqBidsConfigObj + * @param {function} onDone + * @param {Object} rtdConfig + * @param {Object} userConsent + */ export function getRealTimeData(reqBidsConfigObj, onDone, rtdConfig, userConsent) { if (rtdConfig && isPlainObject(rtdConfig.params)) { const jsonData = storage.getDataFromLocalStorage(rtdConfig.params.cohortStorageKey) @@ -85,11 +85,11 @@ export function getRealTimeData(reqBidsConfigObj, onDone, rtdConfig, userConsent } /** - * Module init - * @param {Object} provider - * @param {Object} userConsent - * @return {boolean} - */ + * Module init + * @param {Object} provider + * @param {Object} userConsent + * @return {boolean} + */ function init(provider, userConsent) { return true; } diff --git a/modules/imRtdProvider.js b/modules/imRtdProvider.js index 26d49c49f8c..5aa1d4e4dbe 100644 --- a/modules/imRtdProvider.js +++ b/modules/imRtdProvider.js @@ -50,8 +50,8 @@ function getSegments(segments, moduleConfig) { } /** -* @param {string} bidderName -*/ + * @param {string} bidderName + */ export function getBidderFunction(bidderName) { const biddersFunction = { pubmatic: function (bid, data, moduleConfig) { diff --git a/modules/impactifyBidAdapter.js b/modules/impactifyBidAdapter.js index 04a45e64b19..4c0b9801a36 100644 --- a/modules/impactifyBidAdapter.js +++ b/modules/impactifyBidAdapter.js @@ -360,7 +360,7 @@ 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 - */ + */ onBidWon: function (bid) { ajax(`${LOGGER_URI}/prebid/won`, null, JSON.stringify(bid), { method: 'POST', @@ -373,7 +373,7 @@ export const spec = { /** * 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}/prebid/timeout`, null, JSON.stringify(data[0]), { method: 'POST', diff --git a/modules/incrxBidAdapter.js b/modules/incrxBidAdapter.js index d054309ee40..46be7fb75d6 100644 --- a/modules/incrxBidAdapter.js +++ b/modules/incrxBidAdapter.js @@ -12,22 +12,22 @@ export const spec = { 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. - */ + * 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. - */ + * 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 = parseSizesInput(bidRequest.params.size || bidRequest.sizes); @@ -53,11 +53,11 @@ 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. + * @return {Bid[]} An array of bids which were nested inside the server. + */ interpretResponse: function (serverResponse) { const response = serverResponse.body; const bids = []; diff --git a/modules/integr8BidAdapter.js b/modules/integr8BidAdapter.js index cc19daf31a3..79c6533d122 100644 --- a/modules/integr8BidAdapter.js +++ b/modules/integr8BidAdapter.js @@ -128,11 +128,11 @@ export const spec = { }; /** -* 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. -*/ + * 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); } diff --git a/modules/ixBidAdapter.js b/modules/ixBidAdapter.js index 41e0d8e97c5..06bad99bfe8 100644 --- a/modules/ixBidAdapter.js +++ b/modules/ixBidAdapter.js @@ -1113,7 +1113,7 @@ This function retrieves the page URL and appends first party data query paramete to it without adding duplicate query parameters. Returns original referer URL if no IX FPD exists. @param {Object} bidderRequest - The bidder request object containing information about the bid and the page. @returns {string} - The modified page URL with first party data query parameters appended. -*/ + */ function getIxFirstPartyDataPageUrl (bidderRequest) { // Parse additional runtime configs. const bidderCode = (bidderRequest && bidderRequest.bidderCode) || 'ix'; @@ -1143,7 +1143,7 @@ This function appends the provided query parameters to the given URL without add @param {string} url - The base URL to which query parameters will be appended. @param {Object} params - An object containing key-value pairs of query parameters to append. @returns {string} - The modified URL with the provided query parameters appended. -*/ + */ function appendIXQueryParams(bidderRequest, url, params) { let urlObj; try { @@ -2008,11 +2008,11 @@ export const spec = { }; /** - * Build img user sync url - * @param {int} syncsPerBidder number of syncs Per Bidder - * @param {int} index index to pass - * @returns {string} img user sync url - */ + * Build img user sync url + * @param {int} syncsPerBidder number of syncs Per Bidder + * @param {int} index index to pass + * @returns {string} img user sync url + */ function buildImgSyncUrl(syncsPerBidder, index) { let consentString = ''; let gdprApplies = '0'; diff --git a/modules/jwplayerRtdProvider.js b/modules/jwplayerRtdProvider.js index b79843dccfd..18f89c33ad2 100644 --- a/modules/jwplayerRtdProvider.js +++ b/modules/jwplayerRtdProvider.js @@ -26,16 +26,16 @@ let resumeBidRequest; /** @type {RtdSubmodule} */ export const jwplayerSubmodule = { /** - * used to link submodule with realTimeData - * @type {string} - */ + * used to link submodule with realTimeData + * @type {string} + */ name: SUBMODULE_NAME, /** - * add targeting data to bids and signal completion to realTimeData module - * @function - * @param {Obj} bidReqConfig - * @param {function} onDone - */ + * add targeting data to bids and signal completion to realTimeData module + * @function + * @param {Obj} bidReqConfig + * @param {function} onDone + */ getBidRequestData: enrichBidRequest, init }; diff --git a/modules/lemmaDigitalBidAdapter.js b/modules/lemmaDigitalBidAdapter.js index 9fa3081a47e..8a3b05b7ed3 100644 --- a/modules/lemmaDigitalBidAdapter.js +++ b/modules/lemmaDigitalBidAdapter.js @@ -26,7 +26,7 @@ export var spec = { * * @param {BidRequest} bid The bid params to validate. * @return boolean True if this is a valid bid, and false otherwise. - **/ + */ isBidRequestValid: (bid) => { if (!bid || !bid.params) { utils.logError(LOG_WARN_PREFIX, 'nil/empty bid object'); @@ -51,11 +51,11 @@ export var spec = { }, /** - * 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 {validBidRequests[]} - an array of bids + * @return ServerRequest Info describing the request to the server. + */ buildRequests: (validBidRequests, bidderRequest) => { if (validBidRequests.length === 0) { return; @@ -79,11 +79,11 @@ export var spec = { }, /** - * Unpack the response from the server into a list of bids. - * - * @param {ServerResponse} response 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} response A successful response from the server. + * @return {Bid[]} An array of bids which were nested inside the server. + */ interpretResponse: (response, request) => { return spec._parseRTBResponse(request, response.body); }, @@ -93,7 +93,7 @@ export var spec = { * @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) => { let syncurl = USER_SYNC + 'pid=' + pubId; if (syncOptions.iframeEnabled) { @@ -115,7 +115,7 @@ export var spec = { /** * parse object - **/ + */ _parseJSON: function (rawPayload) { try { if (rawPayload) { @@ -155,7 +155,7 @@ export var spec = { /** * create IAB standard OpenRTB bid request - **/ + */ _createoRTBRequest: (bidRequests, conf) => { var oRTBObject = {}; try { @@ -202,7 +202,7 @@ export var spec = { /** * create impression array objects - **/ + */ _getImpressionArray: (request) => { var impArray = []; var map = request.map(bid => spec._getImpressionObject(bid)); @@ -218,7 +218,7 @@ export var spec = { /** * create impression (single) object - **/ + */ _getImpressionObject: (bid) => { var impression = {}; var bObj; @@ -277,8 +277,8 @@ export var spec = { }, /** - * set bid floor - **/ + * set bid floor + */ _setFloor: (impObj, bid) => { let bidFloor = -1; // get lowest floor from floorModule @@ -304,8 +304,8 @@ export var spec = { }, /** - * parse Open RTB response - **/ + * parse Open RTB response + */ _parseRTBResponse: (request, response) => { var bidResponses = []; try { @@ -358,8 +358,8 @@ export var spec = { }, /** - * get bid request api end point url - **/ + * get bid request api end point url + */ _endPointURL: (request) => { var params = request && request[0].params ? request[0].params : null; if (params) { @@ -371,8 +371,8 @@ export var spec = { }, /** - * get domain name from url - **/ + * get domain name from url + */ _getDomain: (url) => { var a = document.createElement('a'); a.setAttribute('href', url); @@ -380,8 +380,8 @@ export var spec = { }, /** - * create the site object - **/ + * create the site object + */ _getSiteObject: (request, conf) => { var params = request && request.params ? request.params : null; if (params) { @@ -406,8 +406,8 @@ export var spec = { }, /** - * create the app object - **/ + * create the app object + */ _getAppObject: (request) => { var params = request && request.params ? request.params : null; if (params) { @@ -432,8 +432,8 @@ export var spec = { }, /** - * create the device object - **/ + * create the device object + */ _getDeviceObject: (request) => { var params = request && request.params ? request.params : null; if (params) { @@ -481,8 +481,8 @@ export var spec = { }, /** - * get request ad sizes - **/ + * get request ad sizes + */ _getSizes: (request) => { if (request && request.sizes && utils.isArray(request.sizes[0]) && request.sizes[0].length > 0) { return request.sizes[0]; @@ -491,8 +491,8 @@ export var spec = { }, /** - * create the banner object - **/ + * create the banner object + */ _getBannerRequest: (bid) => { var bObj; var adFormat = []; @@ -531,8 +531,8 @@ export var spec = { }, /** - * create the video object - **/ + * create the video object + */ _getVideoRequest: (bid) => { var vObj; if (utils.deepAccess(bid, 'mediaTypes.video')) { @@ -554,8 +554,8 @@ export var spec = { }, /** - * check media type - **/ + * check media type + */ _checkMediaType: (adm, newBid) => { // Create a regex here to check the strings var videoRegex = new RegExp(/VAST.*version/); diff --git a/modules/malltvBidAdapter.js b/modules/malltvBidAdapter.js index 5ac50936ed6..1d50ad4e4c1 100644 --- a/modules/malltvBidAdapter.js +++ b/modules/malltvBidAdapter.js @@ -124,11 +124,11 @@ export const spec = { }; /** -* 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. -*/ + * 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); } diff --git a/modules/mediafilterRtdProvider.js b/modules/mediafilterRtdProvider.js index 277b19c10b2..8a082ad4d59 100644 --- a/modules/mediafilterRtdProvider.js +++ b/modules/mediafilterRtdProvider.js @@ -16,9 +16,9 @@ import { loadExternalScript } from '../src/adloader.js'; import * as events from '../src/events.js'; import CONSTANTS from '../src/constants.json'; -/** @const {string} MEDIAFILTER_EVENT_TYPE - The event type for Media Filter. */ +/** The event type for Media Filter. */ export const MEDIAFILTER_EVENT_TYPE = 'com.mediatrust.pbjs.'; -/** @const {string} MEDIAFILTER_BASE_URL - The base URL for Media Filter scripts. */ +/** The base URL for Media Filter scripts. */ export const MEDIAFILTER_BASE_URL = 'https://scripts.webcontentassessor.com/scripts/'; export const MediaFilter = { diff --git a/modules/mediasquareBidAdapter.js b/modules/mediasquareBidAdapter.js index 1db4dad84cc..fb580d81b94 100644 --- a/modules/mediasquareBidAdapter.js +++ b/modules/mediasquareBidAdapter.js @@ -20,20 +20,20 @@ export const spec = { aliases: ['msq'], // short code supportedMediaTypes: [BANNER, NATIVE, 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: function(bid) { return !!(bid.params.owner && bid.params.code); }, /** - * 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 {validBidRequests[]} - an array of bids + * @return ServerRequest Info describing the request to the server. + */ buildRequests: function(validBidRequests, bidderRequest) { // convert Native ORTB definition to old-style prebid native definition validBidRequests = convertOrtbRequestToProprietaryNative(validBidRequests); @@ -96,11 +96,11 @@ 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. + * @return {Bid[]} An array of bids which were nested inside the server. + */ interpretResponse: function(serverResponse, bidRequest) { const serverBody = serverResponse.body; // const headerValue = serverResponse.headers.get('some-response-header'); @@ -148,12 +148,12 @@ 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: function(syncOptions, serverResponses, gdprConsent, uspConsent) { if (typeof serverResponses === 'object' && serverResponses != null && serverResponses.length > 0 && serverResponses[0].hasOwnProperty('body') && serverResponses[0].body.hasOwnProperty('cookies') && typeof serverResponses[0].body.cookies === 'object') { @@ -164,9 +164,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) { // fires a pixel to confirm a winning bid if (bid.hasOwnProperty('mediaType') && bid.mediaType == 'video') { diff --git a/modules/merkleIdSystem.js b/modules/merkleIdSystem.js index fc77c7cc97d..20c6f817dca 100644 --- a/modules/merkleIdSystem.js +++ b/modules/merkleIdSystem.js @@ -87,17 +87,17 @@ function generateId(configParams, configStorage) { /** @type {Submodule} */ export const merkleIdSubmodule = { /** - * used to link submodule with config - * @type {string} - */ + * 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 {{eids:arrayofields}} - */ + * decode the stored id value for passing to bid requests + * @function + * @param {string} value + * @returns {{eids:arrayofields}} + */ decode(value) { // Legacy support for a single id const id = (value && value.pam_id && typeof value.pam_id.id === 'string') ? value.pam_id : undefined; @@ -115,12 +115,12 @@ export const merkleIdSubmodule = { }, /** - * performs action to obtain id and return a value in the callback's response argument - * @function - * @param {SubmoduleConfig} [config] - * @param {ConsentData} [consentData] - * @returns {IdResponse|undefined} - */ + * performs action to obtain id and return a value in the callback's response argument + * @function + * @param {SubmoduleConfig} [config] + * @param {ConsentData} [consentData] + * @returns {IdResponse|undefined} + */ getId(config, consentData) { logInfo('User ID - merkleId generating id'); diff --git a/modules/multibid/index.js b/modules/multibid/index.js index df77a157bee..27b88d47cf7 100644 --- a/modules/multibid/index.js +++ b/modules/multibid/index.js @@ -45,10 +45,10 @@ config.getConfig(MODULE_NAME, conf => { }); /** - * @summary validates multibid configuration entries - * @param {Object[]} multibid - example [{bidder: 'bidderA', maxbids: 2, prefix: 'bidA'}, {bidder: 'bidderB', maxbids: 2}] - * @return {Boolean} -*/ + * @summary validates multibid configuration entries + * @param {Object[]} multibid - example [{bidder: 'bidderA', maxbids: 2, prefix: 'bidA'}, {bidder: 'bidderB', maxbids: 2}] + * @return {Boolean} + */ export function validateMultibid(conf) { let check = true; let duplicate = conf.filter(entry => { @@ -77,10 +77,10 @@ export function validateMultibid(conf) { } /** - * @summary addBidderRequests before hook - * @param {Function} fn reference to original function (used by hook logic) - * @param {Object[]} array containing copy of each bidderRequest object -*/ + * @summary addBidderRequests before hook + * @param {Function} fn reference to original function (used by hook logic) + * @param {Object[]} array containing copy of each bidderRequest object + */ export function adjustBidderRequestsHook(fn, bidderRequests) { bidderRequests.map(bidRequest => { // Loop through bidderRequests and check if bidderCode exists in multiconfig @@ -95,11 +95,11 @@ export function adjustBidderRequestsHook(fn, bidderRequests) { } /** - * @summary addBidResponse before hook - * @param {Function} fn reference to original function (used by hook logic) - * @param {String} ad unit code for bid - * @param {Object} bid object -*/ + * @summary addBidResponse before hook + * @param {Function} fn reference to original function (used by hook logic) + * @param {String} ad unit code for bid + * @param {Object} bid object + */ export const addBidResponseHook = timedBidResponseHook('multibid', function addBidResponseHook(fn, adUnitCode, bid, reject) { let floor = deepAccess(bid, 'floorData.floorValue'); @@ -146,9 +146,9 @@ export const addBidResponseHook = timedBidResponseHook('multibid', function addB }); /** -* A descending sort function that will sort the list of objects based on the following: -* - bids without dynamic aliases are sorted before bids with dynamic aliases -*/ + * A descending sort function that will sort the list of objects based on the following: + * - bids without dynamic aliases are sorted before bids with dynamic aliases + */ export function sortByMultibid(a, b) { if (a.bidder !== a.bidderCode && b.bidder === b.bidderCode) { return 1; @@ -162,13 +162,13 @@ export function sortByMultibid(a, b) { } /** - * @summary getHighestCpmBidsFromBidPool before hook - * @param {Function} fn reference to original function (used by hook logic) - * @param {Object[]} array of objects containing all bids from bid pool - * @param {Function} function to reduce to only highest cpm value for each bidderCode - * @param {Number} adUnit bidder targeting limit, default set to 0 - * @param {Boolean} default set to false, this hook modifies targeting and sets to true -*/ + * @summary getHighestCpmBidsFromBidPool before hook + * @param {Function} fn reference to original function (used by hook logic) + * @param {Object[]} array of objects containing all bids from bid pool + * @param {Function} function to reduce to only highest cpm value for each bidderCode + * @param {Number} adUnit bidder targeting limit, default set to 0 + * @param {Boolean} default set to false, this hook modifies targeting and sets to true + */ export function targetBidPoolHook(fn, bidsReceived, highestCpmCallback, adUnitBidLimit = 0, hasModified = false) { if (!config.getConfig('multibid')) resetMultiConfig(); if (hasMultibid) { @@ -216,18 +216,18 @@ export function targetBidPoolHook(fn, bidsReceived, highestCpmCallback, adUnitBi } /** -* Resets globally stored multibid configuration -*/ + * Resets globally stored multibid configuration + */ export const resetMultiConfig = () => { hasMultibid = false; multiConfig = {}; }; /** -* Resets globally stored multibid ad unit bids -*/ + * Resets globally stored multibid ad unit bids + */ export const resetMultibidUnits = () => multibidUnits = {}; /** -* Set up hooks on init -*/ + * Set up hooks on init + */ function init() { // TODO: does this reset logic make sense - what about simultaneous auctions? events.on(CONSTANTS.EVENTS.AUCTION_INIT, resetMultibidUnits); diff --git a/modules/mwOpenLinkIdSystem.js b/modules/mwOpenLinkIdSystem.js index ff23547224b..8cc5cd8bb4a 100644 --- a/modules/mwOpenLinkIdSystem.js +++ b/modules/mwOpenLinkIdSystem.js @@ -112,27 +112,27 @@ export { writeCookie }; /** @type {Submodule} */ export const mwOpenLinkIdSubModule = { /** - * used to link submodule with config - * @type {string} - */ + * 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 the stored id value for passing to bid requests + * @function + * @param {MwOlId} mwOlId + * @return {(Object|undefined} + */ decode(mwOlId) { const id = mwOlId && 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} - */ + * 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; diff --git a/modules/naveggIdSystem.js b/modules/naveggIdSystem.js index 8a472259873..f82bf4002ca 100644 --- a/modules/naveggIdSystem.js +++ b/modules/naveggIdSystem.js @@ -74,16 +74,16 @@ function readnavIDFromCookie() { /** @type {Submodule} */ export const naveggIdSubmodule = { /** - * used to link submodule with config - * @type {string} - */ + * used to link submodule with config + * @type {string} + */ name: MODULE_NAME, /** - * decode the stored id value for passing to bid requests - * @function - * @param { Object | string | undefined } value - * @return { Object | string | undefined } - */ + * decode the stored id value for passing to bid requests + * @function + * @param { Object | string | undefined } value + * @return { Object | string | undefined } + */ decode(value) { const naveggIdVal = value ? isStr(value) ? value : isPlainObject(value) ? value.id : undefined : undefined; return naveggIdVal ? { @@ -91,11 +91,11 @@ export const naveggIdSubmodule = { } : 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} - */ + * 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 naveggIdString = readnaveggIdFromLocalStorage() || readnaveggIDFromCookie() || getNaveggIdFromApi() || readoldnaveggIDFromCookie() || readnvgIDFromCookie() || readnavIDFromCookie(); diff --git a/modules/nobidBidAdapter.js b/modules/nobidBidAdapter.js index 68010b32b37..77de3c6d97b 100644 --- a/modules/nobidBidAdapter.js +++ b/modules/nobidBidAdapter.js @@ -367,21 +367,21 @@ 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: function(bid) { log('isBidRequestValid', bid); return !!bid.params.siteId; }, /** - * 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 {validBidRequests[]} - an array of bids + * @return ServerRequest Info describing the request to the server. + */ buildRequests: function(validBidRequests, bidderRequest) { function resolveEndpoint() { var ret = 'https://ads.servenobid.com/'; @@ -421,11 +421,11 @@ 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. + * @return {Bid[]} An array of bids which were nested inside the server. + */ interpretResponse: function(serverResponse, bidRequest) { log('interpretResponse -> serverResponse', serverResponse); log('interpretResponse -> bidRequest', bidRequest); @@ -433,12 +433,12 @@ 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: function(syncOptions, serverResponses, gdprConsent, usPrivacy, gppConsent) { if (syncOptions.iframeEnabled) { let params = ''; @@ -483,9 +483,9 @@ export const spec = { }, /** - * Register bidder specific code, which will execute if bidder timed out after an auction - * @param {data} Containing timeout specific data - */ + * Register bidder specific code, which will execute if bidder timed out after an auction + * @param {data} Containing timeout specific data + */ onTimeout: function(data) { window.nobid.timeoutTotal++; log('Timeout total: ' + window.nobid.timeoutTotal, data); diff --git a/modules/novatiqIdSystem.js b/modules/novatiqIdSystem.js index 7eced81d35e..d0cc94b513b 100644 --- a/modules/novatiqIdSystem.js +++ b/modules/novatiqIdSystem.js @@ -17,9 +17,9 @@ const MODULE_NAME = 'novatiq'; export const novatiqIdSubmodule = { /** - * used to link submodule with config - * @type {string} - */ + * used to link submodule with config + * @type {string} + */ name: MODULE_NAME, /** * used to specify vendor id @@ -28,10 +28,10 @@ export const novatiqIdSubmodule = { gvlid: 1119, /** - * decode the stored id value for passing to bid requests - * @function - * @returns {novatiq: {snowflake: string}} - */ + * decode the stored id value for passing to bid requests + * @function + * @returns {novatiq: {snowflake: string}} + */ decode(novatiqId, config) { let responseObj = { novatiq: { @@ -52,11 +52,11 @@ export const novatiqIdSubmodule = { }, /** - * performs action to obtain id and return a value in the callback's response argument - * @function - * @param {SubmoduleConfig} config - * @returns {id: string} - */ + * performs action to obtain id and return a value in the callback's response argument + * @function + * @param {SubmoduleConfig} config + * @returns {id: string} + */ getId(config) { const configParams = config.params || {}; const urlParams = this.getUrlParams(configParams); diff --git a/modules/oneKeyIdSystem.js b/modules/oneKeyIdSystem.js index 699a7a6ab95..7aa9e7b48ac 100644 --- a/modules/oneKeyIdSystem.js +++ b/modules/oneKeyIdSystem.js @@ -47,27 +47,27 @@ const getIdsAndPreferences = (callback) => { /** @type {Submodule} */ export const oneKeyIdSubmodule = { /** - * used to link submodule with config - * @type {string} - */ + * used to link submodule with config + * @type {string} + */ name: 'oneKeyData', /** - * decode the stored data value for passing to bid requests - * @function decode - * @param {(Object|string)} value - * @returns {(Object|undefined)} - */ + * decode the stored data value for passing to bid requests + * @function decode + * @param {(Object|string)} value + * @returns {(Object|undefined)} + */ decode(data) { return { oneKeyData: data }; }, /** - * performs action to obtain id and return a value in the callback's response argument - * @function - * @param {SubmoduleConfig} [config] - * @param {ConsentData} [consentData] - * @param {(Object|undefined)} cacheIdObj - * @returns {IdResponse|undefined} - */ + * performs action to obtain id and return a value in the callback's response argument + * @function + * @param {SubmoduleConfig} [config] + * @param {ConsentData} [consentData] + * @param {(Object|undefined)} cacheIdObj + * @returns {IdResponse|undefined} + */ getId(config) { return { callback: getIdsAndPreferences diff --git a/modules/operaadsIdSystem.js b/modules/operaadsIdSystem.js index 09dd8512a2b..c16d5a9c009 100644 --- a/modules/operaadsIdSystem.js +++ b/modules/operaadsIdSystem.js @@ -50,33 +50,33 @@ function asyncRequest(url, cb) { export const operaIdSubmodule = { /** - * used to link submodule with config - * @type {string} - */ + * used to link submodule with config + * @type {string} + */ name: MODULE_NAME, /** - * @type {string} - */ + * @type {string} + */ version, /** - * decode the stored id value for passing to bid requests - * @function - * @param {string} id - * @returns {{'operaId': string}} - */ + * decode the stored id value for passing to bid requests + * @function + * @param {string} id + * @returns {{'operaId': string}} + */ decode: (id) => id != null && id.length > 0 ? { [ID_KEY]: id } : undefined, /** - * performs action to obtain id and return a value in the callback's response argument - * @function - * @param {SubmoduleConfig} [config] - * @returns {IdResponse|undefined} - */ + * performs action to obtain id and return a value in the callback's response argument + * @function + * @param {SubmoduleConfig} [config] + * @returns {IdResponse|undefined} + */ getId(config, consentData) { logMessage(`${MODULE_NAME}: start synchronizing opera uid`); const params = (config && config.params) || {}; diff --git a/modules/optidigitalBidAdapter.js b/modules/optidigitalBidAdapter.js index 9f27ae49d1e..152876b8d5d 100755 --- a/modules/optidigitalBidAdapter.js +++ b/modules/optidigitalBidAdapter.js @@ -15,11 +15,11 @@ export const spec = { gvlid: GVL_ID, 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. - */ + * 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' && bid.params.placementId && bid.params.publisherId) { @@ -29,11 +29,11 @@ export const spec = { 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. - */ + * 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 || validBidRequests.length === 0 || !bidderRequest || !bidderRequest.bids) { return []; @@ -105,11 +105,11 @@ 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. + * @return {Bid[]} An array of bids which were nested inside the server. + */ interpretResponse: function(serverResponse, bidRequest) { const bidResponses = []; serverResponse = serverResponse.body; @@ -138,12 +138,12 @@ 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: function(syncOptions, serverResponses, gdprConsent, uspConsent) { let syncurl = ''; if (!isSynced) { diff --git a/modules/optimeraRtdProvider.js b/modules/optimeraRtdProvider.js index 04d9b9d1b9f..074d8d39bbf 100644 --- a/modules/optimeraRtdProvider.js +++ b/modules/optimeraRtdProvider.js @@ -30,7 +30,8 @@ let _moduleParams = {}; * Default Optimera Key Name * This can default to hb_deal_optimera for publishers * who used the previous Optimera Bidder Adapter. - * @type {string} */ + * @type {string} + */ export let optimeraKeyName = 'hb_deal_optimera'; /** diff --git a/modules/optimonAnalyticsAdapter.js b/modules/optimonAnalyticsAdapter.js index 82bc18f605d..68baf007563 100644 --- a/modules/optimonAnalyticsAdapter.js +++ b/modules/optimonAnalyticsAdapter.js @@ -1,12 +1,12 @@ /** -* -********************************************************* -* -* Optimon.io Prebid Analytics Adapter -* -********************************************************* -* -*/ + * + ********************************************************* + * + * Optimon.io Prebid Analytics Adapter + * + ********************************************************* + * + */ import adapter from '../libraries/analyticsAdapter/AnalyticsAdapter.js'; import adapterManager from '../src/adapterManager.js'; diff --git a/modules/pairIdSystem.js b/modules/pairIdSystem.js index 489b97d02e3..1009846e06a 100644 --- a/modules/pairIdSystem.js +++ b/modules/pairIdSystem.js @@ -27,29 +27,29 @@ function pairIdFromCookie(key) { /** @type {Submodule} */ export const pairIdSubmodule = { /** - * used to link submodule with config - * @type {string} - */ + * used to link submodule with config + * @type {string} + */ name: MODULE_NAME, /** - * used to specify vendor id - * @type {number} - */ + * used to specify vendor id + * @type {number} + */ gvlid: 755, /** - * decode the stored id value for passing to bid requests - * @function - * @param { string | undefined } value - * @returns {{pairId:string} | undefined } - */ + * decode the stored id value for passing to bid requests + * @function + * @param { string | undefined } value + * @returns {{pairId:string} | undefined } + */ decode(value) { return value && Array.isArray(value) ? {'pairId': value} : undefined }, /** - * performs action to obtain id and return a value in the callback's response argument - * @function - * @returns {id: string | undefined } - */ + * performs action to obtain id and return a value in the callback's response argument + * @function + * @returns {id: string | undefined } + */ getId(config) { const pairIdsString = pairIdFromLocalStorage(PAIR_ID_KEY) || pairIdFromCookie(PAIR_ID_KEY) let ids = [] diff --git a/modules/pilotxBidAdapter.js b/modules/pilotxBidAdapter.js index 335c461e3d9..bd3612d6429 100644 --- a/modules/pilotxBidAdapter.js +++ b/modules/pilotxBidAdapter.js @@ -6,11 +6,11 @@ export const spec = { supportedMediaTypes: ['banner', 'video'], aliases: ['pilotx'], // 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. - */ + * 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 sizesCheck = !!bid.sizes let paramSizesCheck = !!bid.params.sizes @@ -35,11 +35,11 @@ export const spec = { return !!(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. - */ + * 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 payloadItems = {}; validBidRequests.forEach(bidRequest => { @@ -84,11 +84,11 @@ 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. + * @return {Bid[]} An array of bids which were nested inside the server. + */ interpretResponse: function (serverResponse, bidRequest) { const serverBody = serverResponse.body; const bidResponses = []; diff --git a/modules/prebidServerBidAdapter/index.js b/modules/prebidServerBidAdapter/index.js index a530c415839..bbc1ea21822 100644 --- a/modules/prebidServerBidAdapter/index.js +++ b/modules/prebidServerBidAdapter/index.js @@ -207,7 +207,7 @@ getConfig('s2sConfig', ({s2sConfig}) => setS2sConfig(s2sConfig)); /** * resets the _synced variable back to false, primiarily used for testing purposes -*/ + */ export function resetSyncedStatus() { _syncCount = 0; } diff --git a/modules/priceFloors.js b/modules/priceFloors.js index 3837f56366c..f114f142bac 100644 --- a/modules/priceFloors.js +++ b/modules/priceFloors.js @@ -51,12 +51,12 @@ export let allowedFields = [SYN_FIELD, 'gptSlot', 'adUnitCode', 'size', 'domain' /** * @summary This is a flag to indicate if a AJAX call is processing for a floors request -*/ + */ let fetching = false; /** * @summary so we only register for our hooks once -*/ + */ let addedFloorsHook = false; /** diff --git a/modules/prismaBidAdapter.js b/modules/prismaBidAdapter.js index c13e6e1c330..931c5e7d7e6 100644 --- a/modules/prismaBidAdapter.js +++ b/modules/prismaBidAdapter.js @@ -44,20 +44,20 @@ export const spec = { aliases: ['prismadirect'], // short code supportedMediaTypes: [BANNER, VIDEO], /** - * Determines whether or not the given bid request is valid. - * - * @param {BidRequest} bid The bid params to validate. - * @return boolean True if this is a valid bid, and false otherwise. - */ + * Determines whether or not the given bid request is valid. + * + * @param {BidRequest} bid The bid params to validate. + * @return boolean True if this is a valid bid, and false otherwise. + */ isBidRequestValid: function(bid) { return !!(bid.params.account && bid.params.tagId); }, /** - * Make a server request from the list of BidRequests. - * - * @param {validBidRequests[]} - an array of bids - * @return ServerRequest Info describing the request to the server. - */ + * Make a server request from the list of BidRequests. + * + * @param {validBidRequests[]} - an array of bids + * @return ServerRequest Info describing the request to the server. + */ buildRequests: function(validBidRequests, bidderRequest) { const adUnits = []; const test = config.getConfig('debug') ? 1 : 0; @@ -109,11 +109,11 @@ 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. + * @return {Bid[]} An array of bids which were nested inside the server. + */ interpretResponse: function(serverResponse, bidRequest) { const serverBody = serverResponse.body; const bidResponses = []; @@ -163,12 +163,12 @@ 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: function(syncOptions, serverResponses, gdprConsent, uspConsent) { if (typeof serverResponses === 'object' && serverResponses != null && serverResponses.length > 0 && serverResponses[0].hasOwnProperty('body') && serverResponses[0].body.hasOwnProperty('cookies') && typeof serverResponses[0].body.cookies === 'object') { @@ -179,9 +179,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) { // fires a pixel to confirm a winning bid const params = { type: 'prebid', mediatype: 'banner' }; diff --git a/modules/pubmaticBidAdapter.js b/modules/pubmaticBidAdapter.js index a6c7dda510c..3371573ecc2 100644 --- a/modules/pubmaticBidAdapter.js +++ b/modules/pubmaticBidAdapter.js @@ -734,9 +734,9 @@ function _addImpressionFPD(imp, bid) { const ortb2 = {...deepAccess(bid, 'ortb2Imp.ext.data')}; Object.keys(ortb2).forEach(prop => { /** - * Prebid AdSlot - * @type {(string|undefined)} - */ + * Prebid AdSlot + * @type {(string|undefined)} + */ if (prop === 'pbadslot') { if (typeof ortb2[prop] === 'string' && ortb2[prop]) deepSetValue(imp, 'ext.data.pbadslot', ortb2[prop]); } else if (prop === 'adserver') { @@ -1011,11 +1011,11 @@ export const spec = { gvlid: 76, supportedMediaTypes: [BANNER, VIDEO, NATIVE], /** - * 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. - */ + * 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 => { if (bid && bid.params) { if (!isStr(bid.params.publisherId)) { diff --git a/modules/relevantdigitalBidAdapter.js b/modules/relevantdigitalBidAdapter.js index ef4e1c8e33d..8d1265075f9 100644 --- a/modules/relevantdigitalBidAdapter.js +++ b/modules/relevantdigitalBidAdapter.js @@ -94,7 +94,7 @@ export const spec = { gvlid: 1100, supportedMediaTypes: [BANNER, VIDEO, NATIVE], - /** We need both params.placementId + a complete configuration (pbsHost + accountId) to continue **/ + /** We need both params.placementId + a complete configuration (pbsHost + accountId) to continue */ isBidRequestValid: (bid) => bid.params?.placementId && getBidderConfig([bid]).complete, /** Trigger impression-pixel */ diff --git a/modules/sizeMappingV2.js b/modules/sizeMappingV2.js index d212d98f50b..5ddb2e410cb 100644 --- a/modules/sizeMappingV2.js +++ b/modules/sizeMappingV2.js @@ -63,7 +63,7 @@ export function isUsingNewSizeMapping(adUnits) { does not recognize. @params {Array} adUnits @returns {Array} validateAdUnits - Unrecognized properties are deleted. -*/ + */ export function checkAdUnitSetupHook(adUnits) { const validateSizeConfig = function (mediaType, sizeConfig, adUnitCode) { let isValid = true; diff --git a/modules/slimcutBidAdapter.js b/modules/slimcutBidAdapter.js index c3f06556652..9cdb85d4f25 100644 --- a/modules/slimcutBidAdapter.js +++ b/modules/slimcutBidAdapter.js @@ -13,11 +13,11 @@ export const spec = { aliases: [{ code: 'scm', gvlid: 102 }], 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. - */ + * 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(getValue(bid.params, 'placementId'))) && parseInt(getValue(bid.params, 'placementId')) > 0) { @@ -26,11 +26,11 @@ export const spec = { 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. - */ + * 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 = { @@ -55,11 +55,11 @@ export const spec = { }; }, /** - * 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. - */ + * 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; diff --git a/modules/smilewantedBidAdapter.js b/modules/smilewantedBidAdapter.js index 2fbfcaa79af..09c39e52825 100644 --- a/modules/smilewantedBidAdapter.js +++ b/modules/smilewantedBidAdapter.js @@ -40,11 +40,13 @@ export const spec = { transactionId: bid.ortb2Imp?.ext?.tid, timeout: bidderRequest?.timeout, bidId: bid.bidId, - /** positionType is undocumented + /** + positionType is undocumented It is unclear what this parameter means. If it means the same as pos in openRTB, It should read from openRTB object - or from mediaTypes.banner.pos */ + or from mediaTypes.banner.pos + */ positionType: bid.params.positionType || '', prebidVersion: '$prebid.version$' }; diff --git a/modules/sovrnBidAdapter.js b/modules/sovrnBidAdapter.js index a7f4166579b..0ff5d842135 100644 --- a/modules/sovrnBidAdapter.js +++ b/modules/sovrnBidAdapter.js @@ -214,7 +214,7 @@ export const spec = { * Format Sovrn responses as Prebid bid responses * @param {id, seatbid} sovrnResponse A successful response from Sovrn. * @return {Bid[]} An array of formatted bids. - */ + */ interpretResponse: function({ body: {id, seatbid} }) { if (!id || !seatbid || !Array.isArray(seatbid)) return [] diff --git a/modules/sspBCBidAdapter.js b/modules/sspBCBidAdapter.js index 93aa0973cc7..f24af853a54 100644 --- a/modules/sspBCBidAdapter.js +++ b/modules/sspBCBidAdapter.js @@ -164,7 +164,7 @@ const applyClientHints = ortbRequest => { Check / generate page view id Should be generated dureing first call to applyClientHints(), and re-generated if pathname has changed - */ + */ if (!pageView.id || location.pathname !== pageView.path) { pageView.path = location.pathname; pageView.id = Math.floor(1E20 * Math.random()).toString(); diff --git a/modules/tappxBidAdapter.js b/modules/tappxBidAdapter.js index 90438f92e1b..898f6a4185d 100644 --- a/modules/tappxBidAdapter.js +++ b/modules/tappxBidAdapter.js @@ -53,7 +53,7 @@ export const spec = { * * @param {BidRequest} bid The bid params to validate. * @return boolean True if this is a valid bid, and false otherwise. - */ + */ isBidRequestValid: function(bid) { // bid.params.host if ((new RegExp(`^(vz.*|zz.*)\\.*$`, 'i')).test(bid.params.host)) { // New endpoint @@ -234,12 +234,12 @@ function interpretBid(serverBid, request) { } /** -* Build and makes the request -* -* @param {*} validBidRequests -* @param {*} bidderRequest -* @return response ad -*/ + * Build and makes the request + * + * @param {*} validBidRequests + * @param {*} bidderRequest + * @return response ad + */ function buildOneRequest(validBidRequests, bidderRequest) { let hostInfo = _getHostInfo(validBidRequests); const ENDPOINT = hostInfo.endpoint; diff --git a/modules/teadsIdSystem.js b/modules/teadsIdSystem.js index b4067bf75c3..4744fd5d37b 100644 --- a/modules/teadsIdSystem.js +++ b/modules/teadsIdSystem.js @@ -34,9 +34,9 @@ export const storage = getStorageManager({moduleType: MODULE_TYPE_UID, moduleNam /** @type {Submodule} */ export const teadsIdSubmodule = { /** - * used to link submodule with config - * @type {string} - */ + * used to link submodule with config + * @type {string} + */ name: MODULE_NAME, /** * Vendor id of Teads @@ -44,21 +44,21 @@ export const teadsIdSubmodule = { */ gvlid: GVL_ID, /** - * decode the stored id value for passing to bid requests - * @function - * @param {string} value - * @returns {{teadsId:string}} - */ + * decode the stored id value for passing to bid requests + * @function + * @param {string} value + * @returns {{teadsId:string}} + */ decode(value) { return {teadsId: value} }, /** - * performs action to obtain id and return a value in the callback's response argument - * @function - * @param {SubmoduleConfig} [submoduleConfig] - * @param {ConsentData} [consentData] - * @returns {IdResponse|undefined} - */ + * performs action to obtain id and return a value in the callback's response argument + * @function + * @param {SubmoduleConfig} [submoduleConfig] + * @param {ConsentData} [consentData] + * @returns {IdResponse|undefined} + */ getId(submoduleConfig, consentData) { const resp = function (callback) { const url = buildAnalyticsTagUrl(submoduleConfig, consentData); diff --git a/modules/temedyaBidAdapter.js b/modules/temedyaBidAdapter.js index 4eac0bc641f..cb9fe46d21a 100644 --- a/modules/temedyaBidAdapter.js +++ b/modules/temedyaBidAdapter.js @@ -12,20 +12,20 @@ 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. - */ + * 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.widgetId); }, /** - * 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 {validBidRequests[]} - an array of bids + * @return ServerRequest Info describing the request to the server. + */ buildRequests: function (validBidRequests, bidderRequest) { // convert Native ORTB definition to old-style prebid native definition validBidRequests = convertOrtbRequestToProprietaryNative(validBidRequests); @@ -54,11 +54,11 @@ 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. + * @return {Bid[]} An array of bids which were nested inside the server. + */ interpretResponse: function (serverResponse, bidRequest) { try { const bidResponse = serverResponse.body; diff --git a/modules/topicsFpdModule.js b/modules/topicsFpdModule.js index dffb8bc4860..617ce0ad6f4 100644 --- a/modules/topicsFpdModule.js +++ b/modules/topicsFpdModule.js @@ -178,8 +178,8 @@ export function receiveMessage(evt) { /** Function to store Topics data received from iframe in storage(name: "prebid:topics") -* @param {Topics} topics -*/ + * @param {Topics} topics + */ export function storeInLocalStorage(bidder, topics) { const storedSegments = new Map(safeJSONParse(coreStorage.getDataFromLocalStorage(topicStorageName))); const topicsObj = { @@ -202,8 +202,8 @@ function isCachedDataExpired(storedTime, cacheTime) { } /** -* Function to get random bidders based on count passed with array of bidders -**/ + * Function to get random bidders based on count passed with array of bidders + */ function getRandomBidders(arr, count) { return ([...arr].sort(() => 0.5 - Math.random())).slice(0, count) } diff --git a/modules/ucfunnelBidAdapter.js b/modules/ucfunnelBidAdapter.js index 67f3cebdce8..1ef72f293fc 100644 --- a/modules/ucfunnelBidAdapter.js +++ b/modules/ucfunnelBidAdapter.js @@ -64,7 +64,7 @@ export const spec = { * Format ucfunnel responses as Prebid bid responses * @param {ucfunnelResponseObj} ucfunnelResponse A successful response from ucfunnel. * @return {Bid[]} An array of formatted bids. - */ + */ interpretResponse: function (ucfunnelResponseObj, request) { const bidRequest = request.bidRequest; const ad = ucfunnelResponseObj ? ucfunnelResponseObj.body : {}; diff --git a/modules/userId/index.js b/modules/userId/index.js index b20b38f0e40..5a088b27319 100644 --- a/modules/userId/index.js +++ b/modules/userId/index.js @@ -668,8 +668,8 @@ function encryptSignals(signals, version = 1) { } /** -* This function will be exposed in the global-name-space so that publisher can register the signals-ESP. -*/ + * This function will be exposed in the global-name-space so that publisher can register the signals-ESP. + */ function registerSignalSources() { if (!isGptPubadsDefined()) { return; @@ -886,14 +886,12 @@ function updateInitializedSubmodules(dest, 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 - * * value config: object properties that are copied to bids (without saving to storage) + * storage config: contains values for storing/retrieving User ID data in browser storage + * value config: object properties that are copied to bids (without saving to storage) * @param {SubmoduleConfig[]} configRegistry - * @param {Submodule[]} submoduleRegistry - * @param {string[]} activeStorageTypes * @returns {SubmoduleConfig[]} */ -function getValidSubmoduleConfigs(configRegistry, submoduleRegistry) { +function getValidSubmoduleConfigs(configRegistry) { if (!Array.isArray(configRegistry)) { return []; } @@ -958,7 +956,7 @@ function updateEIDConfig(submodules) { */ function updateSubmodules() { updateEIDConfig(submoduleRegistry); - const configs = getValidSubmoduleConfigs(configRegistry, submoduleRegistry); + const configs = getValidSubmoduleConfigs(configRegistry); if (!configs.length) { return; } diff --git a/modules/weboramaRtdProvider.js b/modules/weboramaRtdProvider.js index 14abba95323..a0963b844e1 100644 --- a/modules/weboramaRtdProvider.js +++ b/modules/weboramaRtdProvider.js @@ -7,25 +7,29 @@ * @requires module:modules/realTimeData */ -/** profile metadata +/** + * profile metadata * @typedef dataCallbackMetadata * @property {boolean} user if true it is user-centric data * @property {string} source describe the source of data, if 'contextual' or 'wam' * @property {boolean} isDefault if true it the default profile defined in the configuration */ -/** profile from contextual, wam or sfbx +/** + * profile from contextual, wam or sfbx * @typedef {Object.} Profile */ -/** onData callback type +/** + * onData callback type * @callback dataCallback * @param {Profile} data profile data * @param {dataCallbackMetadata} meta metadata * @returns {void} */ -/** setPrebidTargeting callback type +/** + * setPrebidTargeting callback type * @callback setPrebidTargetingCallback * @param {string} adUnitCode * @param {Profile} data @@ -33,7 +37,8 @@ * @returns {boolean} */ -/** sendToBidders callback type +/** + * sendToBidders callback type * @callback sendToBiddersCallback * @param {Object} bid * @param {string} adUnitCode @@ -90,7 +95,8 @@ * @property {?boolean} enabled if false, will ignore this configuration */ -/** common configuration between contextual, wam and sfbx +/** + * common configuration between contextual, wam and sfbx * @typedef {WeboCtxConf|WeboUserDataConf|SfbxLiteDataConf} CommonConf */ @@ -188,7 +194,8 @@ class WeboramaRtdProvider { constructor(components) { this.#components = components; } - /** Initialize module + /** + * Initialize module * @method * @param {Object} moduleConfig * @param {?ModuleParams} moduleConfig.params @@ -219,7 +226,8 @@ class WeboramaRtdProvider { return Object.values(this.#components).some((c) => c.initialized); } - /** function that will allow RTD sub-modules to modify the AdUnit object for each auction + /** + * function that will allow RTD sub-modules to modify the AdUnit object for each auction * @method * @param {Object} reqBidsConfigObj * @param {doneCallback} onDone @@ -253,7 +261,8 @@ class WeboramaRtdProvider { }); } - /** function that provides ad server targeting data to RTD-core + /** + * function that provides ad server targeting data to RTD-core * @method * @param {string[]} adUnitsCodes * @param {Object} moduleConfig @@ -294,7 +303,8 @@ class WeboramaRtdProvider { } } - /** Initialize subsection module + /** + * Initialize subsection module * @method * @private * @param {ModuleParams} moduleParams @@ -330,7 +340,8 @@ class WeboramaRtdProvider { return true; } - /** normalize submodule configuration + /** + * normalize submodule configuration * @method * @private * @param {ModuleParams} moduleParams @@ -363,7 +374,8 @@ class WeboramaRtdProvider { } } - /** coerce setPrebidTargeting to a callback + /** + * coerce setPrebidTargeting to a callback * @method * @private * @param {CommonConf} submoduleParams @@ -379,7 +391,8 @@ class WeboramaRtdProvider { } } - /** coerce sendToBidders to a callback + /** + * coerce sendToBidders to a callback * @method * @private * @param {CommonConf} submoduleParams @@ -426,7 +439,8 @@ class WeboramaRtdProvider { * @typedef {Object} AdUnit * @property {Object[]} bids */ - /** function that handles bid request data + /** + * function that handles bid request data * @method * @private * @param {Object} reqBidsConfigObj @@ -476,18 +490,21 @@ class WeboramaRtdProvider { }); } - /** onSuccess callback type + /** + * onSuccess callback type * @callback successCallback * @param {?Object} data * @returns {void} */ - /** onDone callback type + /** + * onDone callback type * @callback doneCallback * @returns {void} */ - /** Fetch Bigsea Contextual Profile + /** + * Fetch Bigsea Contextual Profile * @method * @private * @param {WeboCtxConf} weboCtxConf @@ -566,7 +583,8 @@ class WeboramaRtdProvider { ajax(urlProfileAPI, callback, null, options); } - /** set bigsea contextual profile on module state + /** + * set bigsea contextual profile on module state * @method * @private * @param {?Object} data @@ -579,7 +597,8 @@ class WeboramaRtdProvider { } } - /** function that provides data handlers based on the configuration + /** + * function that provides data handlers based on the configuration * @method * @private * @param {ModuleParams} moduleParams @@ -667,7 +686,8 @@ class WeboramaRtdProvider { onData: dataConf.onData, }; } - /** handle individual bid + /** + * handle individual bid * @method * @private * @param {Object} reqBidsConfigObj @@ -694,7 +714,8 @@ class WeboramaRtdProvider { } } - /** function that handles bid request data + /** + * function that handles bid request data * @method * @private * @param {ProfileHandler} ph profile handler @@ -705,7 +726,8 @@ class WeboramaRtdProvider { return [deepClone(ph.data), deepClone(ph.metadata)]; } - /** handle appnexus/xandr bid + /** + * handle appnexus/xandr bid * @method * @private * @param {Object} reqBidsConfigObj @@ -723,7 +745,8 @@ class WeboramaRtdProvider { // this.#setBidderOrtb2(reqBidsConfigObj.ortb2Fragments?.bidder, bid.bidder, base, profile); } - /** handle generic bid via ortb2 arbitrary data + /** + * handle generic bid via ortb2 arbitrary data * @method * @private * @param {Object} reqBidsConfigObj @@ -844,7 +867,8 @@ export function isValidProfile(profile) { * @returns {buildProfileHandlerCallback} */ function getContextualProfile(component /* equivalent to this */) { - /** return contextual profile + /** + * return contextual profile * @param {WeboCtxConf} weboCtxConf * @returns {[Profile,boolean]} contextual profile + isDefault boolean flag */ @@ -865,7 +889,8 @@ function getContextualProfile(component /* equivalent to this */) { * @returns {buildProfileHandlerCallback} */ function getWeboUserDataProfile(component /* equivalent to this */) { - /** return weboUserData profile + /** + * return weboUserData profile * @param {WeboUserDataConf} weboUserDataConf * @returns {[Profile,boolean]} weboUserData profile + isDefault boolean flag */ @@ -885,7 +910,8 @@ function getWeboUserDataProfile(component /* equivalent to this */) { * @returns {buildProfileHandlerCallback} */ function getSfbxLiteDataProfile(component /* equivalent to this */) { - /** return weboUserData profile + /** + * return weboUserData profile * @param {SfbxLiteDataConf} sfbxLiteDataConf * @returns {[Profile,boolean]} sfbxLiteData profile + isDefault boolean flag */ @@ -909,7 +935,8 @@ function getSfbxLiteDataProfile(component /* equivalent to this */) { * @returns {void} */ -/** return generic webo data profile +/** + * return generic webo data profile * @param {WeboUserDataConf|SfbxLiteDataConf} weboDataConf * @param {cacheGetCallback} cacheGet * @param {cacheSetCallback} cacheSet diff --git a/modules/xeBidAdapter.js b/modules/xeBidAdapter.js index 6f527d905d6..bff76aae172 100644 --- a/modules/xeBidAdapter.js +++ b/modules/xeBidAdapter.js @@ -142,12 +142,12 @@ function interpretResponse(serverResponse, { bidderRequest }) { } /** -* 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. + */ function getUserSyncs(syncOptions, serverResponses, gdprConsent = {}, uspConsent = '') { const syncs = []; const pixels = deepAccess(serverResponses, '0.body.data.0.ext.pixels'); @@ -172,11 +172,11 @@ function getUserSyncs(syncOptions, serverResponses, gdprConsent = {}, uspConsent } /** -* Get valid floor value from getFloor fuction. -* -* @param {Object} bid Current bid request. -* @return {null|Number} Returns floor value when bid.getFloor is function and returns valid floor object with USD currency, otherwise returns null. -*/ + * Get valid floor value from getFloor fuction. + * + * @param {Object} bid Current bid request. + * @return {null|Number} Returns floor value when bid.getFloor is function and returns valid floor object with USD currency, otherwise returns null. + */ export function getBidFloor(bid) { if (!isFn(bid.getFloor)) { return null; diff --git a/modules/yandexBidAdapter.js b/modules/yandexBidAdapter.js index cc4a8c18241..cb1cbd89c84 100644 --- a/modules/yandexBidAdapter.js +++ b/modules/yandexBidAdapter.js @@ -87,7 +87,7 @@ export const NATIVE_ASSETS = { const NATIVE_ASSETS_IDS = {}; _each(NATIVE_ASSETS, (asset, key) => { NATIVE_ASSETS_IDS[asset[0]] = key }); -/** @type BidderSpec */ +/** @type {BidderSpec} */ export const spec = { code: BIDDER_CODE, aliases: ['ya'], // short code diff --git a/modules/zetaBidAdapter.js b/modules/zetaBidAdapter.js index 527030efc9a..9a58e391a17 100644 --- a/modules/zetaBidAdapter.js +++ b/modules/zetaBidAdapter.js @@ -15,11 +15,11 @@ export const spec = { 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. - */ + * 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 if (!(bid && @@ -50,12 +50,12 @@ export const spec = { }, /** - * 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. - */ + * 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]; @@ -117,12 +117,12 @@ export const spec = { }, /** - * Unpack the response from the server into a list of bids. - * - * @param {ServerResponse} serverResponse A successful response from the server. - * @param bidRequest The payload from the server's response. - * @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 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) { diff --git a/package-lock.json b/package-lock.json index cfdc8669c2f..3020105714f 100644 --- a/package-lock.json +++ b/package-lock.json @@ -47,6 +47,7 @@ "eslint": "^7.27.0", "eslint-config-standard": "^10.2.1", "eslint-plugin-import": "^2.20.2", + "eslint-plugin-jsdoc": "^38.1.6", "eslint-plugin-node": "^11.1.0", "eslint-plugin-prebid": "file:./plugins/eslint", "eslint-plugin-promise": "^5.1.0", @@ -1669,6 +1670,20 @@ "node": ">=0.1.90" } }, + "node_modules/@es-joy/jsdoccomment": { + "version": "0.22.2", + "resolved": "https://registry.npmjs.org/@es-joy/jsdoccomment/-/jsdoccomment-0.22.2.tgz", + "integrity": "sha512-pM6WQKcuAtdYoqCsXSvVSu3Ij8K0HY50L8tIheOKHDl0wH1uA4zbP88etY8SIeP16NVCMCTFU+Q2DahSKheGGQ==", + "dev": true, + "dependencies": { + "comment-parser": "1.3.1", + "esquery": "^1.4.0", + "jsdoc-type-pratt-parser": "~2.2.5" + }, + "engines": { + "node": "^12 || ^14 || ^16 || ^17" + } + }, "node_modules/@eslint/eslintrc": { "version": "0.4.3", "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-0.4.3.tgz", @@ -7489,6 +7504,15 @@ "integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==", "dev": true }, + "node_modules/comment-parser": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/comment-parser/-/comment-parser-1.3.1.tgz", + "integrity": "sha512-B52sN2VNghyq5ofvUsqZjmk6YkihBX5vMSChmSK9v4ShjKf3Vk5Xcmgpw4o+iIgtrnM/u5FiMpz9VKb8lpBveA==", + "dev": true, + "engines": { + "node": ">= 12.0.0" + } + }, "node_modules/commondir": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/commondir/-/commondir-1.0.1.tgz", @@ -9562,6 +9586,55 @@ "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", "dev": true }, + "node_modules/eslint-plugin-jsdoc": { + "version": "38.1.6", + "resolved": "https://registry.npmjs.org/eslint-plugin-jsdoc/-/eslint-plugin-jsdoc-38.1.6.tgz", + "integrity": "sha512-n4s95oYlg0L43Bs8C0dkzIldxYf8pLCutC/tCbjIdF7VDiobuzPI+HZn9Q0BvgOvgPNgh5n7CSStql25HUG4Tw==", + "dev": true, + "dependencies": { + "@es-joy/jsdoccomment": "~0.22.1", + "comment-parser": "1.3.1", + "debug": "^4.3.4", + "escape-string-regexp": "^4.0.0", + "esquery": "^1.4.0", + "regextras": "^0.8.0", + "semver": "^7.3.5", + "spdx-expression-parse": "^3.0.1" + }, + "engines": { + "node": "^12 || ^14 || ^16 || ^17" + }, + "peerDependencies": { + "eslint": "^7.0.0 || ^8.0.0" + } + }, + "node_modules/eslint-plugin-jsdoc/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-plugin-jsdoc/node_modules/semver": { + "version": "7.5.4", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.4.tgz", + "integrity": "sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA==", + "dev": true, + "dependencies": { + "lru-cache": "^6.0.0" + }, + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, "node_modules/eslint-plugin-node": { "version": "11.1.0", "resolved": "https://registry.npmjs.org/eslint-plugin-node/-/eslint-plugin-node-11.1.0.tgz", @@ -15610,6 +15683,15 @@ "integrity": "sha512-UVU9dibq2JcFWxQPA6KCqj5O42VOmAY3zQUfEKxU0KpTGXwNoCjkX1e13eHNvw/xPynt6pU0rZ1htjWTNTSXsg==", "dev": true }, + "node_modules/jsdoc-type-pratt-parser": { + "version": "2.2.5", + "resolved": "https://registry.npmjs.org/jsdoc-type-pratt-parser/-/jsdoc-type-pratt-parser-2.2.5.tgz", + "integrity": "sha512-2a6eRxSxp1BW040hFvaJxhsCMI9lT8QB8t14t+NY5tC5rckIR0U9cr2tjOeaFirmEOy6MHvmJnY7zTBHq431Lw==", + "dev": true, + "engines": { + "node": ">=12.0.0" + } + }, "node_modules/jsesc": { "version": "2.5.2", "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-2.5.2.tgz", @@ -20567,6 +20649,15 @@ "node": ">=4" } }, + "node_modules/regextras": { + "version": "0.8.0", + "resolved": "https://registry.npmjs.org/regextras/-/regextras-0.8.0.tgz", + "integrity": "sha512-k519uI04Z3SaY0fLX843MRXnDeG2+vHOFsyhiPZvNLe7r8rD2YNRjq4BQLZZ0oAr2NrtvZlICsXysGNFPGa3CQ==", + "dev": true, + "engines": { + "node": ">=0.1.14" + } + }, "node_modules/regjsgen": { "version": "0.7.1", "resolved": "https://registry.npmjs.org/regjsgen/-/regjsgen-0.7.1.tgz", @@ -26452,6 +26543,17 @@ "integrity": "sha512-ooWCrlZP11i8GImSjTHYHLkvFDP48nS4+204nGb1RiX/WXYHmJA2III9/e2DWVabCESdW7hBAEzHRqUn9OUVvQ==", "dev": true }, + "@es-joy/jsdoccomment": { + "version": "0.22.2", + "resolved": "https://registry.npmjs.org/@es-joy/jsdoccomment/-/jsdoccomment-0.22.2.tgz", + "integrity": "sha512-pM6WQKcuAtdYoqCsXSvVSu3Ij8K0HY50L8tIheOKHDl0wH1uA4zbP88etY8SIeP16NVCMCTFU+Q2DahSKheGGQ==", + "dev": true, + "requires": { + "comment-parser": "1.3.1", + "esquery": "^1.4.0", + "jsdoc-type-pratt-parser": "~2.2.5" + } + }, "@eslint/eslintrc": { "version": "0.4.3", "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-0.4.3.tgz", @@ -31123,6 +31225,12 @@ "integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==", "dev": true }, + "comment-parser": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/comment-parser/-/comment-parser-1.3.1.tgz", + "integrity": "sha512-B52sN2VNghyq5ofvUsqZjmk6YkihBX5vMSChmSK9v4ShjKf3Vk5Xcmgpw4o+iIgtrnM/u5FiMpz9VKb8lpBveA==", + "dev": true + }, "commondir": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/commondir/-/commondir-1.0.1.tgz", @@ -32862,6 +32970,39 @@ } } }, + "eslint-plugin-jsdoc": { + "version": "38.1.6", + "resolved": "https://registry.npmjs.org/eslint-plugin-jsdoc/-/eslint-plugin-jsdoc-38.1.6.tgz", + "integrity": "sha512-n4s95oYlg0L43Bs8C0dkzIldxYf8pLCutC/tCbjIdF7VDiobuzPI+HZn9Q0BvgOvgPNgh5n7CSStql25HUG4Tw==", + "dev": true, + "requires": { + "@es-joy/jsdoccomment": "~0.22.1", + "comment-parser": "1.3.1", + "debug": "^4.3.4", + "escape-string-regexp": "^4.0.0", + "esquery": "^1.4.0", + "regextras": "^0.8.0", + "semver": "^7.3.5", + "spdx-expression-parse": "^3.0.1" + }, + "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 + }, + "semver": { + "version": "7.5.4", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.4.tgz", + "integrity": "sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA==", + "dev": true, + "requires": { + "lru-cache": "^6.0.0" + } + } + } + }, "eslint-plugin-node": { "version": "11.1.0", "resolved": "https://registry.npmjs.org/eslint-plugin-node/-/eslint-plugin-node-11.1.0.tgz", @@ -37487,6 +37628,12 @@ "integrity": "sha512-UVU9dibq2JcFWxQPA6KCqj5O42VOmAY3zQUfEKxU0KpTGXwNoCjkX1e13eHNvw/xPynt6pU0rZ1htjWTNTSXsg==", "dev": true }, + "jsdoc-type-pratt-parser": { + "version": "2.2.5", + "resolved": "https://registry.npmjs.org/jsdoc-type-pratt-parser/-/jsdoc-type-pratt-parser-2.2.5.tgz", + "integrity": "sha512-2a6eRxSxp1BW040hFvaJxhsCMI9lT8QB8t14t+NY5tC5rckIR0U9cr2tjOeaFirmEOy6MHvmJnY7zTBHq431Lw==", + "dev": true + }, "jsesc": { "version": "2.5.2", "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-2.5.2.tgz", @@ -41242,6 +41389,12 @@ "unicode-match-property-value-ecmascript": "^2.0.0" } }, + "regextras": { + "version": "0.8.0", + "resolved": "https://registry.npmjs.org/regextras/-/regextras-0.8.0.tgz", + "integrity": "sha512-k519uI04Z3SaY0fLX843MRXnDeG2+vHOFsyhiPZvNLe7r8rD2YNRjq4BQLZZ0oAr2NrtvZlICsXysGNFPGa3CQ==", + "dev": true + }, "regjsgen": { "version": "0.7.1", "resolved": "https://registry.npmjs.org/regjsgen/-/regjsgen-0.7.1.tgz", diff --git a/package.json b/package.json index 830f700537d..dda2e4e3b38 100644 --- a/package.json +++ b/package.json @@ -58,6 +58,7 @@ "eslint": "^7.27.0", "eslint-config-standard": "^10.2.1", "eslint-plugin-import": "^2.20.2", + "eslint-plugin-jsdoc": "^38.1.6", "eslint-plugin-node": "^11.1.0", "eslint-plugin-prebid": "file:./plugins/eslint", "eslint-plugin-promise": "^5.1.0", diff --git a/src/adapters/bidderFactory.js b/src/adapters/bidderFactory.js index 44775b4e241..df7b2fb0eea 100644 --- a/src/adapters/bidderFactory.js +++ b/src/adapters/bidderFactory.js @@ -27,6 +27,11 @@ import {activityParams} from '../activities/activityParams.js'; import {MODULE_TYPE_BIDDER} from '../activities/modules.js'; import {ACTIVITY_TRANSMIT_TID, ACTIVITY_TRANSMIT_UFPD} from '../activities/activities.js'; +/** + * @typedef {import('../mediaTypes.js').MediaType} MediaType + * @typedef {import('../Renderer.js').Renderer} Renderer + */ + /** * This file aims to support Adapters during the Prebid 0.x -> 1.x transition. * @@ -57,7 +62,7 @@ import {ACTIVITY_TRANSMIT_TID, ACTIVITY_TRANSMIT_UFPD} from '../activities/activ * @property {string} code A code which will be used to uniquely identify this bidder. This should be the same * one as is used in the call to registerBidAdapter * @property {string[]} [aliases] A list of aliases which should also resolve to this bidder. - * @property {MediaType[]} [supportedMediaTypes]: A list of Media Types which the adapter supports. + * @property {MediaType[]} [supportedMediaTypes] A list of Media Types which the adapter supports. * @property {function(object): boolean} isBidRequestValid Determines whether or not the given bid has all the params * needed to make a valid request. * @property {function(BidRequest[], bidderRequest): ServerRequest|ServerRequest[]} buildRequests Build the request to the Server @@ -105,7 +110,7 @@ import {ACTIVITY_TRANSMIT_TID, ACTIVITY_TRANSMIT_UFPD} from '../activities/activ * * @property {*} body The response body. If this is legal JSON, then it will be parsed. Otherwise it'll be a * string with the body's content. - * @property {{get: function(string): string} headers The response headers. + * @property {{get: function(string): string}} headers The response headers. * Call this like `ServerResponse.headers.get("Content-Type")` */ @@ -126,7 +131,7 @@ import {ACTIVITY_TRANSMIT_TID, ACTIVITY_TRANSMIT_UFPD} from '../activities/activ * @property {object} [video] Object for storing video response data * @property {object} [meta] Object for storing bid meta data * @property {string} [meta.primaryCatId] The IAB primary category ID - * @property [Renderer] renderer A Renderer which can be used as a default for this bid, + * @property {Renderer} renderer A Renderer which can be used as a default for this bid, * if the publisher doesn't override it. This is only relevant for Outstream Video bids. */ diff --git a/src/auction.js b/src/auction.js index 10b28ad44c9..0d677c540b9 100644 --- a/src/auction.js +++ b/src/auction.js @@ -9,14 +9,14 @@ */ /** - * @typedef {Object} AdUnit An object containing the adUnit configuration. - * - * @property {string} code A code which will be used to uniquely identify this bidder. This should be the same - * one as is used in the call to registerBidAdapter - * @property {Array.} sizes A list of size for adUnit. - * @property {object} params Any bidder-specific params which the publisher used in their bid request. - * This is guaranteed to have passed the spec.areParamsValid() test. - */ + * @typedef {Object} AdUnit An object containing the adUnit configuration. + * + * @property {string} code A code which will be used to uniquely identify this bidder. This should be the same + * one as is used in the call to registerBidAdapter + * @property {Array.} sizes A list of size for adUnit. + * @property {object} params Any bidder-specific params which the publisher used in their bid request. + * This is guaranteed to have passed the spec.areParamsValid() test. + */ /** * @typedef {Array.} size @@ -119,19 +119,19 @@ export function resetAuctionState() { } /** - * Creates new auction instance - * - * @param {Object} requestConfig - * @param {AdUnit} requestConfig.adUnits - * @param {AdUnitCode} requestConfig.adUnitCodes - * @param {function():void} requestConfig.callback - * @param {number} requestConfig.cbTimeout - * @param {Array.} requestConfig.labels - * @param {string} requestConfig.auctionId - * @param {{global: {}, bidder: {}}} ortb2Fragments first party data, separated into global - * (from getConfig('ortb2') + requestBids({ortb2})) and bidder (a map from bidderCode to ortb2) - * @returns {Auction} auction instance - */ + * Creates new auction instance + * + * @param {Object} requestConfig + * @param {AdUnit} requestConfig.adUnits + * @param {AdUnitCode} requestConfig.adUnitCodes + * @param {function():void} requestConfig.callback + * @param {number} requestConfig.cbTimeout + * @param {Array.} requestConfig.labels + * @param {string} requestConfig.auctionId + * @param {{global: {}, bidder: {}}} ortb2Fragments first party data, separated into global + * (from getConfig('ortb2') + requestBids({ortb2})) and bidder (a map from bidderCode to ortb2) + * @returns {Auction} auction instance + */ export function newAuction({adUnits, adUnitCodes, callback, cbTimeout, labels, auctionId, ortb2Fragments, metrics}) { metrics = useMetrics(metrics); const _adUnits = adUnits; diff --git a/src/events.js b/src/events.js index 750ecdd8eee..d98991180bf 100644 --- a/src/events.js +++ b/src/events.js @@ -61,12 +61,13 @@ const _public = (function () { elapsedTime: utils.getPerformanceNow(), }); - /** Push each specific callback to the `callbacks` array. + /** + * Push each specific callback to the `callbacks` array. * If the `event` map has a key that matches the value of the * event payload id path, e.g. `eventPayload[idPath]`, then apply * each function in the `que` array as an argument to push to the * `callbacks` array - * */ + */ if (key && eventKeys.includes(key)) { push.apply(callbacks, event[key].que); } diff --git a/src/targeting.js b/src/targeting.js index e46d1ae47a4..2c2ddefa6c2 100644 --- a/src/targeting.js +++ b/src/targeting.js @@ -85,26 +85,26 @@ export const getHighestCpmBidsFromBidPool = hook('sync', function(bidsReceived, }); /** -* A descending sort function that will sort the list of objects based on the following two dimensions: -* - bids with a deal are sorted before bids w/o a deal -* - then sort bids in each grouping based on the hb_pb value -* eg: the following list of bids would be sorted like: -* [{ -* "hb_adid": "vwx", -* "hb_pb": "28", -* "hb_deal": "7747" -* }, { -* "hb_adid": "jkl", -* "hb_pb": "10", -* "hb_deal": "9234" -* }, { -* "hb_adid": "stu", -* "hb_pb": "50" -* }, { -* "hb_adid": "def", -* "hb_pb": "2" -* }] -*/ + * A descending sort function that will sort the list of objects based on the following two dimensions: + * - bids with a deal are sorted before bids w/o a deal + * - then sort bids in each grouping based on the hb_pb value + * eg: the following list of bids would be sorted like: + * [{ + * "hb_adid": "vwx", + * "hb_pb": "28", + * "hb_deal": "7747" + * }, { + * "hb_adid": "jkl", + * "hb_pb": "10", + * "hb_deal": "9234" + * }, { + * "hb_adid": "stu", + * "hb_pb": "50" + * }, { + * "hb_adid": "def", + * "hb_pb": "2" + * }] + */ export function sortByDealAndPriceBucketOrCpm(useCpm = false) { return function(a, b) { if (a.adserverTargeting.hb_deal !== undefined && b.adserverTargeting.hb_deal === undefined) { diff --git a/src/userSync.js b/src/userSync.js index 936836eb12e..7e23598a318 100644 --- a/src/userSync.js +++ b/src/userSync.js @@ -244,7 +244,7 @@ export function newUserSync(deps) { * @param {string} type The type of the sync; either image or iframe * @param {string} bidder The name of the adapter. e.g. "rubicon" * @returns {boolean} true => bidder is not allowed to register; false => bidder can register - */ + */ function shouldBidderBeBlocked(type, bidder) { let filterConfig = usConfig.filterSettings; diff --git a/test/spec/e2e/modules/e2e_consent_mgt_gdpr.spec.js b/test/spec/e2e/modules/e2e_consent_mgt_gdpr.spec.js index 5b5ea2ef2cd..d1803c9784a 100644 --- a/test/spec/e2e/modules/e2e_consent_mgt_gdpr.spec.js +++ b/test/spec/e2e/modules/e2e_consent_mgt_gdpr.spec.js @@ -1,4 +1,4 @@ -/** +/* TODO: old CMP no longer works; see if we can fix this with https://github.com/prebid/Prebid.js/issues/6377 const expect = require('chai').expect; const { testPageURL, switchFrame, waitForElement } = require('../../../helpers/testing-utils'); @@ -59,4 +59,4 @@ describe('Prebid.js GDPR Ad Unit Test', function () { expect(ele.isExisting()).to.be.true; }); }); -**/ + */ From 759c52df5e0ae3531cde62810fc701f9d8615761 Mon Sep 17 00:00:00 2001 From: pangle-fe <149553960+pangle-fe@users.noreply.github.com> Date: Tue, 9 Jan 2024 01:15:20 +0800 Subject: [PATCH 119/246] Pangle Bid Adapter : add multi format support (#10909) * feat: pangle multi format * feat: multi format --- modules/pangleBidAdapter.js | 23 +++- test/spec/modules/pangleBidAdapter_spec.js | 134 +++++++++++++++++---- 2 files changed, 131 insertions(+), 26 deletions(-) diff --git a/modules/pangleBidAdapter.js b/modules/pangleBidAdapter.js index e75a96048b4..f4a52168743 100644 --- a/modules/pangleBidAdapter.js +++ b/modules/pangleBidAdapter.js @@ -18,6 +18,11 @@ const DEFAULT_CURRENCY = 'USD'; const DEFAULT_NET_REVENUE = true; const PANGLE_COOKIE = '_pangle_id'; const COOKIE_EXP = 86400 * 1000 * 365 * 1; // 1 year +const MEDIA_TYPES = { + Banner: 1, + Video: 2 +}; + export const storage = getStorageManager({ moduleType: MODULE_TYPE_RTD, moduleName: BIDDER_CODE }) export function isValidUuid(uuid) { @@ -104,12 +109,20 @@ const converter = ortbConverter({ currency: DEFAULT_CURRENCY, }, bidResponse(buildBidResponse, bid, context) { - const bidResponse = buildBidResponse(bid, context); const { bidRequest } = context; - if (bidRequest.mediaTypes.video?.context === 'outstream') { - const renderer = Renderer.install({id: bid.bidId, url: OUTSTREAM_RENDERER_URL, adUnitCode: bid.adUnitCode}); - renderer.setRender(renderOutstream); - bidResponse.renderer = renderer; + let bidResponse; + if (bid.mtype === MEDIA_TYPES.Video) { + context.mediaType = VIDEO; + bidResponse = buildBidResponse(bid, context); + if (bidRequest.mediaTypes.video?.context === 'outstream') { + const renderer = Renderer.install({id: bid.bidId, url: OUTSTREAM_RENDERER_URL, adUnitCode: bid.adUnitCode}); + renderer.setRender(renderOutstream); + bidResponse.renderer = renderer; + } + } + if (bid.mtype === MEDIA_TYPES.Banner) { + context.mediaType = BANNER; + bidResponse = buildBidResponse(bid, context); } return bidResponse; }, diff --git a/test/spec/modules/pangleBidAdapter_spec.js b/test/spec/modules/pangleBidAdapter_spec.js index 94f5a6e4b1d..6d8f9a66bcf 100644 --- a/test/spec/modules/pangleBidAdapter_spec.js +++ b/test/spec/modules/pangleBidAdapter_spec.js @@ -1,6 +1,5 @@ import { expect } from 'chai'; import { spec } from 'modules/pangleBidAdapter.js'; -import { logInfo } from '../../../src/utils'; const REQUEST = [{ adUnitCode: 'adUnitCode1', @@ -84,6 +83,7 @@ const RESPONSE = { 'cat': [], 'w': 300, 'h': 250, + 'mtype': 1, 'ext': { 'prebid': { 'type': 'banner' @@ -186,26 +186,6 @@ describe('pangle bid adapter', function () { expect(deviceType).to.equal(2); }); }); - - // describe('video', function () { - // it('video config', function() { - // logInfo(spec.buildRequests(VIDEO_REQUEST, DEFAULT_OPTIONS)[0].data, 'spec.buildRequests(videoConfig, DEFAULT_OPTIONS)[0].data.imp[0]'); - // const request = spec.buildRequests(VIDEO_REQUEST, DEFAULT_OPTIONS)[0]; - - // expect(request).to.exist.and.to.be.a('object'); - // const payload = request.data; - // expect(payload).to.exist.and.to.be.a('object'); - // const video = payload.imp[0].video; - // expect(video).to.exist.and.to.be.a('object'); - // // console.log(video, 'video???') - // // expect(url).to.equal('https://pangle.pangleglobal.com/api/ad/union/web_js/common/get_ads'); - // // assert.deepEqual(video, { - // // h: 380, - // // mimes: ['video/mp4'], - // // w: 240 - // // }) - // }) - // }) }); describe('Pangle Adapter with video', function() { @@ -247,6 +227,7 @@ describe('Pangle Adapter with video', function() { ], 'w': 640, 'h': 640, + 'mtype': 1, 'ext': { 'pangle': { 'adtype': 8 @@ -293,3 +274,114 @@ describe('Pangle Adapter with video', function() { }); }); }); + +describe('pangle multi-format ads', function () { + const bidderRequest = { + refererInfo: { + referer: 'https://example.com' + } + }; + const multiRequest = [ + { + bidId: '2820132fe18114', + mediaTypes: { banner: { sizes: [[300, 250]] }, video: { context: 'outstream', playerSize: [[300, 250]] } }, + params: { token: 'test-token' } + } + ]; + const videoResponse = { + 'headers': null, + 'body': { + 'id': '233f1693-68d1-470a-ad85-c156c3faaf6f', + 'seatbid': [ + { + 'bid': [ + { + 'id': '2820132fe18114', + 'impid': '2820132fe18114', + 'price': 0.03294, + 'nurl': 'https://api16-event-sg2.pangle.io/api/ad/union/openrtb/win/?req_id=233f1693-68d1-470a-ad85-c156c3faaf6fu1450&ttdsp_adx_index=256&rit=980589944&extra=oqveoB%2Bg4%2ByNz9L8wwu%2Fy%2FwKxQsGaKsJHuB4NMK77uqZ9%2FJKpnsVZculJX8%2FxrRBAtaktU1DRN%2Fy6TKAqibCbj%2FM3%2BZ6biAKQG%2BCyt4eIV0KVvri9jCCnaajbkN7YNJWJJw2lW6cJ6Va3SuJG9H7a%2FAJd2PMbhK7fXWhoW72TwgOcKHKBgjM6sNDISBKbWlZyY3L1PhKSX%2FM8LOvL6qahsb%2FDpEObIx24vhQLNWp28XY1L4UqeibuRjam3eCvN7nXoQq74KkJ45QQsTgvV4j6I6EbLOdjOi%2FURhWMDjUD1VCMpqUT%2B6L8ZROgrX9Tp53eJ3bFOczmSTOmDSazKMHa%2B3uZZ7JHcSx32eoY4hfYc99NOJmYBKXNKCmoXyJvS3PCM3PlAz97hKrDMGnVv1wAQ7QGDCbittF0vZwtsRAvvx2mWINNIB3%2FUB2PjhxFsoDA%2BWE2urVZwEdyu%2FJrCznJsMwenXjcbMD5jmUF5vDkkLS%2B7TMDIEawJPJKZ62pK35enrwGxCs6ePXi21rJJkA0bF8tgAdl4mU1illBIVO4kCL%2ByRASskHPjgg%2FcdFe9HP%2Fi8byjAprH%2BhRerN%2FRKFxC3xv8b75x2pb1g7dY%2FTj9IjT0evsBSPVwFNqtKmPId35IcY%2FSXiqPHh%2FrAHZzr5BPsTT19P49SlNMR9UZYTzViX1iJpcCL1UFjuDdrdff%2BhHCviXxo%2FkRmufEF3umHZwxbdDOPAghuZ0DtRCY6S1rnb%2FK9BbpsVKSndOtgfCwMHFwiPmdw1XjEXGc1eOWXY6qfSp90PIfL6WS7Neh3ba2qMv6WxG3HSOBYvrcCqVTsNxk4UdVm3qb1J0CMVByweTMo45usSkCTdvX3JuEB7tVA6%2BrEk57b3XJd5Phf2AN8hon%2F7lmcXE41kwMQuXq89ViwQmW0G247UFWOQx4t1cmBqFiP6qNA%2F%2BunkZDno1pmAsGnTv7Mz9xtpOaIqKl8BKrVQSTopZ9WcUVzdBUutF19mn1f43BvyA9gIEhcDJHOj&win_price=${AUCTION_PRICE}&auction_mwb=${AUCTION_BID_TO_WIN}&use_pb=1', + 'lurl': 'https://api16-event-sg2.pangle.io/api/ad/union/openrtb/loss/?req_id=233f1693-68d1-470a-ad85-c156c3faaf6fu1450&ttdsp_adx_index=256&rit=980589944&extra=oqveoB%2Bg4%2ByNz9L8wwu%2Fy%2FwKxQsGaKsJHuB4NMK77uqZ9%2FJKpnsVZculJX8%2FxrRBAtaktU1DRN%2Fy6TKAqibCbj%2FM3%2BZ6biAKQG%2BCyt4eIV0KVvri9jCCnaajbkN7YNJWJJw2lW6cJ6Va3SuJG9H7a%2FAJd2PMbhK7fXWhoW72TwgOcKHKBgjM6sNDISBKbWlZyY3L1PhKSX%2FM8LOvL6qahsb%2FDpEObIx24vhQLNWp28XY1L4UqeibuRjam3eCvN7nXoQq74KkJ45QQsTgvV4j6I6EbLOdjOi%2FURhWMDjUD1VCMpqUT%2B6L8ZROgrX9Tp53eJ3bFOczmSTOmDSazKMHa%2B3uZZ7JHcSx32eoY4hfYc99NOJmYBKXNKCmoXyJvS3PCM3PlAz97hKrDMGnVv1wAQ7QGDCbittF0vZwtsRAvvx2mWINNIB3%2FUB2PjhxFsoDA%2BWE2urVZwEdyu%2FJrCznJsMwenXjcbMD5jmUF5vDkkLS%2B7TMDIEawJPJKZ62pK35enrwGxCs6ePXi21rJJkA0bF8tgAdl4mU1illBIVO4kCL%2ByRASskHPjgg%2FcdFe9HP%2Fi8byjAprH%2BhRerN%2FRKFxC3xv8b75x2pb1g7dY%2FTj9IjT0evsBSPVwFNqtKmPId35IcY%2FSXiqPHh%2FrAHZzr5BPsTT19P49SlNMR9UZYTzViX1iJpcCL1UFjuDdrdff%2BhHCviXxo%2FkRmufEF3umHZwxbdDOPAghuZ0DtRCY6S1rnb%2FK9BbpsVKSndOtgfCwMHFwiPmdw1XjEXGc1eOWXY6qfSp90PIfL6WS7Neh3ba2qMv6WxG3HSOBYvrcCqVTsNxk4UdVm3qb1J0CMVByweTMo45usSkCTdvX3JuEB7tVA6%2BrEk57b3XJd5Phf2AN8hon%2F7lmcXE41kwMQuXq89ViwQmW0G247UFWOQx4t1cmBqFiP6qNA%2F%2BunkZDno1pmAsGnTv7Mz9xtpOaIqKl8BKrVQSTopZ9WcUVzdBUutF19mn1f43BvyA9gIEhcDJHOj&reason=${AUCTION_LOSS}&ad_slot_type=8&auction_mwb=${AUCTION_PRICE}&use_pb=1', + 'adm': '', + 'adid': '1780626232977441', + 'adomain': [ + 'swi.esxcmnb.com' + ], + 'iurl': 'https://p16-ttam-va.ibyteimg.com/origin/ad-site-i18n-sg/202310245d0d598b3ff5993c4f129a8b', + 'cid': '1780626232977441', + 'crid': '1780626232977441', + 'attr': [ + 4 + ], + 'w': 640, + 'h': 640, + 'mtype': 2, + 'ext': { + 'pangle': { + 'adtype': 8 + }, + 'event_notification_token': { + 'payload': '980589944:8:1450:7492' + } + } + } + ], + 'seat': 'pangle' + } + ] + } + }; + const bannerResponse = { + 'headers': null, + 'body': { + 'id': '233f1693-68d1-470a-ad85-c156c3faaf6f', + 'seatbid': [ + { + 'bid': [ + { + 'id': '2820132fe18114', + 'impid': '2820132fe18114', + 'price': 0.03294, + 'nurl': 'https://api16-event-sg2.pangle.io/api/ad/union/openrtb/win/?req_id=233f1693-68d1-470a-ad85-c156c3faaf6fu1450&ttdsp_adx_index=256&rit=980589944&extra=oqveoB%2Bg4%2ByNz9L8wwu%2Fy%2FwKxQsGaKsJHuB4NMK77uqZ9%2FJKpnsVZculJX8%2FxrRBAtaktU1DRN%2Fy6TKAqibCbj%2FM3%2BZ6biAKQG%2BCyt4eIV0KVvri9jCCnaajbkN7YNJWJJw2lW6cJ6Va3SuJG9H7a%2FAJd2PMbhK7fXWhoW72TwgOcKHKBgjM6sNDISBKbWlZyY3L1PhKSX%2FM8LOvL6qahsb%2FDpEObIx24vhQLNWp28XY1L4UqeibuRjam3eCvN7nXoQq74KkJ45QQsTgvV4j6I6EbLOdjOi%2FURhWMDjUD1VCMpqUT%2B6L8ZROgrX9Tp53eJ3bFOczmSTOmDSazKMHa%2B3uZZ7JHcSx32eoY4hfYc99NOJmYBKXNKCmoXyJvS3PCM3PlAz97hKrDMGnVv1wAQ7QGDCbittF0vZwtsRAvvx2mWINNIB3%2FUB2PjhxFsoDA%2BWE2urVZwEdyu%2FJrCznJsMwenXjcbMD5jmUF5vDkkLS%2B7TMDIEawJPJKZ62pK35enrwGxCs6ePXi21rJJkA0bF8tgAdl4mU1illBIVO4kCL%2ByRASskHPjgg%2FcdFe9HP%2Fi8byjAprH%2BhRerN%2FRKFxC3xv8b75x2pb1g7dY%2FTj9IjT0evsBSPVwFNqtKmPId35IcY%2FSXiqPHh%2FrAHZzr5BPsTT19P49SlNMR9UZYTzViX1iJpcCL1UFjuDdrdff%2BhHCviXxo%2FkRmufEF3umHZwxbdDOPAghuZ0DtRCY6S1rnb%2FK9BbpsVKSndOtgfCwMHFwiPmdw1XjEXGc1eOWXY6qfSp90PIfL6WS7Neh3ba2qMv6WxG3HSOBYvrcCqVTsNxk4UdVm3qb1J0CMVByweTMo45usSkCTdvX3JuEB7tVA6%2BrEk57b3XJd5Phf2AN8hon%2F7lmcXE41kwMQuXq89ViwQmW0G247UFWOQx4t1cmBqFiP6qNA%2F%2BunkZDno1pmAsGnTv7Mz9xtpOaIqKl8BKrVQSTopZ9WcUVzdBUutF19mn1f43BvyA9gIEhcDJHOj&win_price=${AUCTION_PRICE}&auction_mwb=${AUCTION_BID_TO_WIN}&use_pb=1', + 'lurl': 'https://api16-event-sg2.pangle.io/api/ad/union/openrtb/loss/?req_id=233f1693-68d1-470a-ad85-c156c3faaf6fu1450&ttdsp_adx_index=256&rit=980589944&extra=oqveoB%2Bg4%2ByNz9L8wwu%2Fy%2FwKxQsGaKsJHuB4NMK77uqZ9%2FJKpnsVZculJX8%2FxrRBAtaktU1DRN%2Fy6TKAqibCbj%2FM3%2BZ6biAKQG%2BCyt4eIV0KVvri9jCCnaajbkN7YNJWJJw2lW6cJ6Va3SuJG9H7a%2FAJd2PMbhK7fXWhoW72TwgOcKHKBgjM6sNDISBKbWlZyY3L1PhKSX%2FM8LOvL6qahsb%2FDpEObIx24vhQLNWp28XY1L4UqeibuRjam3eCvN7nXoQq74KkJ45QQsTgvV4j6I6EbLOdjOi%2FURhWMDjUD1VCMpqUT%2B6L8ZROgrX9Tp53eJ3bFOczmSTOmDSazKMHa%2B3uZZ7JHcSx32eoY4hfYc99NOJmYBKXNKCmoXyJvS3PCM3PlAz97hKrDMGnVv1wAQ7QGDCbittF0vZwtsRAvvx2mWINNIB3%2FUB2PjhxFsoDA%2BWE2urVZwEdyu%2FJrCznJsMwenXjcbMD5jmUF5vDkkLS%2B7TMDIEawJPJKZ62pK35enrwGxCs6ePXi21rJJkA0bF8tgAdl4mU1illBIVO4kCL%2ByRASskHPjgg%2FcdFe9HP%2Fi8byjAprH%2BhRerN%2FRKFxC3xv8b75x2pb1g7dY%2FTj9IjT0evsBSPVwFNqtKmPId35IcY%2FSXiqPHh%2FrAHZzr5BPsTT19P49SlNMR9UZYTzViX1iJpcCL1UFjuDdrdff%2BhHCviXxo%2FkRmufEF3umHZwxbdDOPAghuZ0DtRCY6S1rnb%2FK9BbpsVKSndOtgfCwMHFwiPmdw1XjEXGc1eOWXY6qfSp90PIfL6WS7Neh3ba2qMv6WxG3HSOBYvrcCqVTsNxk4UdVm3qb1J0CMVByweTMo45usSkCTdvX3JuEB7tVA6%2BrEk57b3XJd5Phf2AN8hon%2F7lmcXE41kwMQuXq89ViwQmW0G247UFWOQx4t1cmBqFiP6qNA%2F%2BunkZDno1pmAsGnTv7Mz9xtpOaIqKl8BKrVQSTopZ9WcUVzdBUutF19mn1f43BvyA9gIEhcDJHOj&reason=${AUCTION_LOSS}&ad_slot_type=8&auction_mwb=${AUCTION_PRICE}&use_pb=1', + 'adm': '', + 'adid': '1780626232977441', + 'adomain': [ + 'swi.esxcmnb.com' + ], + 'iurl': 'https://p16-ttam-va.ibyteimg.com/origin/ad-site-i18n-sg/202310245d0d598b3ff5993c4f129a8b', + 'cid': '1780626232977441', + 'crid': '1780626232977441', + 'attr': [ + 4 + ], + 'w': 640, + 'h': 640, + 'mtype': 1, + 'ext': { + 'pangle': { + 'adtype': 8 + }, + 'event_notification_token': { + 'payload': '980589944:8:1450:7492' + } + } + } + ], + 'seat': 'pangle' + } + ] + } + }; + it('should set mediaType to banner', function() { + const request = spec.buildRequests(multiRequest, bidderRequest)[0]; + const interpretedResponse = spec.interpretResponse(bannerResponse, request); + const bid = interpretedResponse[0]; + expect(bid.mediaType).to.equal('banner'); + }) + it('should set mediaType to video', function() { + const request = spec.buildRequests(multiRequest, bidderRequest)[0]; + const interpretedResponse = spec.interpretResponse(videoResponse, request); + const bid = interpretedResponse[0]; + expect(bid.mediaType).to.equal('video'); + }) +}); From 66893733caf9404a582f6c7db20f9308e35448fe Mon Sep 17 00:00:00 2001 From: dzhang-criteo <87757739+dzhang-criteo@users.noreply.github.com> Date: Mon, 8 Jan 2024 20:43:08 +0100 Subject: [PATCH 120/246] Criteo bid adapter: remove checks on video context and placement (#10912) Those checks are no longer useful so we decided to remove them. --- modules/criteoBidAdapter.js | 12 +-- test/spec/modules/criteoBidAdapter_spec.js | 87 ---------------------- 2 files changed, 1 insertion(+), 98 deletions(-) diff --git a/modules/criteoBidAdapter.js b/modules/criteoBidAdapter.js index 03aaeae434c..dfc88ba653f 100644 --- a/modules/criteoBidAdapter.js +++ b/modules/criteoBidAdapter.js @@ -683,17 +683,7 @@ function hasValidVideoMediaType(bidRequest) { } }); - if (isValid) { - const videoPlacement = bidRequest.mediaTypes.video.placement || bidRequest.params.video.placement; - // We do not support long form for now, also we have to check that context & placement are consistent - if (bidRequest.mediaTypes.video.context == 'instream' && videoPlacement === 1) { - return true; - } else if (bidRequest.mediaTypes.video.context == 'outstream' && videoPlacement !== 1) { - return true; - } - } - - return false; + return isValid; } /** diff --git a/test/spec/modules/criteoBidAdapter_spec.js b/test/spec/modules/criteoBidAdapter_spec.js index 9987df34f2b..89d1d02b9d2 100755 --- a/test/spec/modules/criteoBidAdapter_spec.js +++ b/test/spec/modules/criteoBidAdapter_spec.js @@ -364,93 +364,6 @@ describe('The Criteo bidding adapter', function () { }); it('should return false when given an invalid video bid request', function () { - expect(spec.isBidRequestValid({ - bidder: 'criteo', - mediaTypes: { - video: { - mimes: ['video/mpeg'], - playerSize: [640, 480], - protocols: [5, 6], - maxduration: 30, - api: [1, 2] - } - }, - params: { - networkId: 456, - video: { - skip: 1, - placement: 1, - playbackmethod: 1 - } - }, - })).to.equal(false); - - expect(spec.isBidRequestValid({ - bidder: 'criteo', - mediaTypes: { - video: { - context: 'instream', - mimes: ['video/mpeg'], - playerSize: [640, 480], - protocols: [5, 6], - maxduration: 30, - api: [1, 2] - } - }, - params: { - networkId: 456, - video: { - skip: 1, - placement: 2, - playbackmethod: 1 - } - }, - })).to.equal(false); - - expect(spec.isBidRequestValid({ - bidder: 'criteo', - mediaTypes: { - video: { - context: 'outstream', - mimes: ['video/mpeg'], - playerSize: [640, 480], - protocols: [5, 6], - maxduration: 30, - api: [1, 2] - } - }, - params: { - networkId: 456, - video: { - skip: 1, - placement: 1, - playbackmethod: 1 - } - }, - })).to.equal(false); - - expect(spec.isBidRequestValid({ - bidder: 'criteo', - mediaTypes: { - video: { - context: 'adpod', - mimes: ['video/mpeg'], - playerSize: [640, 480], - protocols: [5, 6], - maxduration: 30, - api: [1, 2] - } - }, - params: { - networkId: 456, - video: { - skip: 1, - placement: 1, - playbackmethod: 1 - } - }, - })).to.equal(false); - expect(spec.isBidRequestValid({ bidder: 'criteo', mediaTypes: { From b6d7b0ad6f02f3e25b2d789509d52a61e3e7e15b Mon Sep 17 00:00:00 2001 From: tudou <42998776+lhxx121@users.noreply.github.com> Date: Wed, 10 Jan 2024 01:19:05 +0800 Subject: [PATCH 121/246] Discovery Bid Adapter : add title, description, keywords (#10900) * Discovery Bid Adapter : add title, desc, keywords, hLen, nbw, hc, dm add unit test resolve conflict * Discovery Bid Adapter : add title, desc, keywords, hLen, nbw, hc, dm add unit test --------- Co-authored-by: lvhuixin --- modules/discoveryBidAdapter.js | 74 ++++++ test/spec/modules/discoveryBidAdapter_spec.js | 229 +++++++++++++++++- 2 files changed, 302 insertions(+), 1 deletion(-) diff --git a/modules/discoveryBidAdapter.js b/modules/discoveryBidAdapter.js index 315df48fa5d..551c78c1e24 100644 --- a/modules/discoveryBidAdapter.js +++ b/modules/discoveryBidAdapter.js @@ -58,6 +58,65 @@ const NATIVERET = { }; /** + * get page title + * @returns {string} + */ + +export function getPageTitle(win = window) { + try { + const ogTitle = win.top.document.querySelector('meta[property="og:title"]') + return win.top.document.title || (ogTitle && ogTitle.content) || ''; + } catch (e) { + const ogTitle = document.querySelector('meta[property="og:title"]') + return document.title || (ogTitle && ogTitle.content) || ''; + } +} + +/** + * get page description + * @returns {string} + */ +export function getPageDescription(win = window) { + let element; + + try { + element = win.top.document.querySelector('meta[name="description"]') || + win.top.document.querySelector('meta[property="og:description"]') + } catch (e) { + element = document.querySelector('meta[name="description"]') || + document.querySelector('meta[property="og:description"]') + } + + return (element && element.content) || ''; +} + +/** + * get page keywords + * @returns {string} + */ +export function getPageKeywords(win = window) { + let element; + + try { + element = win.top.document.querySelector('meta[name="keywords"]'); + } catch (e) { + element = document.querySelector('meta[name="keywords"]'); + } + + return (element && element.content) || ''; +} + +/** + * get connection downlink + * @returns {number} + */ +export function getConnectionDownLink(win = window) { + const nav = win.navigator || {}; + return nav && nav.connection && nav.connection.downlink >= 0 ? nav.connection.downlink.toString() : undefined; +} + +/** + * get pmg uid * 获取并生成用户的id * @return {string} */ @@ -362,6 +421,10 @@ function getParam(validBidRequests, bidderRequest) { const page = utils.deepAccess(bidderRequest, 'refererInfo.page'); const referer = utils.deepAccess(bidderRequest, 'refererInfo.ref'); const firstPartyData = bidderRequest.ortb2; + const topWindow = window.top; + const title = getPageTitle(); + const desc = getPageDescription(); + const keywords = getPageKeywords(); if (items && items.length) { let c = { @@ -384,6 +447,17 @@ function getParam(validBidRequests, bidderRequest) { firstPartyData, ssppid: storage.getCookie(COOKIE_KEY_SSPPID) || undefined, pmguid: getPmgUID(), + page: { + title: title ? title.slice(0, 100) : undefined, + desc: desc ? desc.slice(0, 300) : undefined, + keywords: keywords ? keywords.slice(0, 100) : undefined, + hLen: topWindow.history?.length || undefined, + }, + device: { + nbw: getConnectionDownLink(), + hc: topWindow.navigator?.hardwareConcurrency || undefined, + dm: topWindow.navigator?.deviceMemory || undefined, + } }, user: { buyeruid: storage.getCookie(COOKIE_KEY_MGUID) || undefined, diff --git a/test/spec/modules/discoveryBidAdapter_spec.js b/test/spec/modules/discoveryBidAdapter_spec.js index acfc519bef9..05216ff126c 100644 --- a/test/spec/modules/discoveryBidAdapter_spec.js +++ b/test/spec/modules/discoveryBidAdapter_spec.js @@ -1,5 +1,5 @@ import { expect } from 'chai'; -import { spec, getPmgUID, storage } from 'modules/discoveryBidAdapter.js'; +import { spec, getPmgUID, storage, getPageTitle, getPageDescription, getPageKeywords, getConnectionDownLink } from 'modules/discoveryBidAdapter.js'; import * as utils from 'src/utils.js'; describe('discovery:BidAdapterTests', function () { @@ -218,3 +218,230 @@ describe('discovery:BidAdapterTests', function () { }); }); }); + +describe('discovery Bid Adapter Tests', function () { + describe('buildRequests', () => { + describe('getPageTitle function', function() { + let sandbox; + + beforeEach(() => { + sandbox = sinon.createSandbox(); + }); + + afterEach(() => { + sandbox.restore(); + }); + + it('should return the top document title if available', function() { + const fakeTopDocument = { + title: 'Top Document Title', + querySelector: () => ({ content: 'Top Document Title test' }) + }; + const fakeTopWindow = { + document: fakeTopDocument + }; + const result = getPageTitle({ top: fakeTopWindow }); + expect(result).to.equal('Top Document Title'); + }); + + it('should return the content of top og:title meta tag if title is empty', function() { + const ogTitleContent = 'Top OG Title Content'; + const fakeTopWindow = { + document: { + title: '', + querySelector: sandbox.stub().withArgs('meta[property="og:title"]').returns({ content: ogTitleContent }) + } + }; + + const result = getPageTitle({ top: fakeTopWindow }); + expect(result).to.equal(ogTitleContent); + }); + + it('should return the document title if no og:title meta tag is present', function() { + document.title = 'Test Page Title'; + sandbox.stub(document, 'querySelector').withArgs('meta[property="og:title"]').returns(null); + + const result = getPageTitle({ top: undefined }); + expect(result).to.equal('Test Page Title'); + }); + + it('should return the content of og:title meta tag if present', function() { + document.title = ''; + const ogTitleContent = 'Top OG Title Content'; + sandbox.stub(document, 'querySelector').withArgs('meta[property="og:title"]').returns({ content: ogTitleContent }); + const result = getPageTitle({ top: undefined }); + expect(result).to.equal(ogTitleContent); + }); + + it('should return an empty string if no title or og:title meta tag is found', function() { + document.title = ''; + sandbox.stub(document, 'querySelector').withArgs('meta[property="og:title"]').returns(null); + const result = getPageTitle({ top: undefined }); + expect(result).to.equal(''); + }); + + it('should handle exceptions when accessing top.document and fallback to current document', function() { + const fakeWindow = { + get top() { + throw new Error('Access denied'); + } + }; + const ogTitleContent = 'Current OG Title Content'; + document.title = 'Current Document Title'; + sandbox.stub(document, 'querySelector').withArgs('meta[property="og:title"]').returns({ content: ogTitleContent }); + const result = getPageTitle(fakeWindow); + expect(result).to.equal('Current Document Title'); + }); + }); + + describe('getPageDescription function', function() { + let sandbox; + + beforeEach(() => { + sandbox = sinon.createSandbox(); + }); + + afterEach(() => { + sandbox.restore(); + }); + + it('should return the top document description if available', function() { + const descriptionContent = 'Top Document Description'; + const fakeTopDocument = { + querySelector: sandbox.stub().withArgs('meta[name="description"]').returns({ content: descriptionContent }) + }; + const fakeTopWindow = { document: fakeTopDocument }; + const result = getPageDescription({ top: fakeTopWindow }); + expect(result).to.equal(descriptionContent); + }); + + it('should return the top document og:description if description is not present', function() { + const ogDescriptionContent = 'Top OG Description'; + const fakeTopDocument = { + querySelector: sandbox.stub().withArgs('meta[property="og:description"]').returns({ content: ogDescriptionContent }) + }; + const fakeTopWindow = { document: fakeTopDocument }; + const result = getPageDescription({ top: fakeTopWindow }); + expect(result).to.equal(ogDescriptionContent); + }); + + it('should return the current document description if top document is not accessible', function() { + const descriptionContent = 'Current Document Description'; + sandbox.stub(document, 'querySelector') + .withArgs('meta[name="description"]').returns({ content: descriptionContent }) + const fakeWindow = { + get top() { + throw new Error('Access denied'); + } + }; + const result = getPageDescription(fakeWindow); + expect(result).to.equal(descriptionContent); + }); + + it('should return the current document og:description if description is not present and top document is not accessible', function() { + const ogDescriptionContent = 'Current OG Description'; + sandbox.stub(document, 'querySelector') + .withArgs('meta[property="og:description"]').returns({ content: ogDescriptionContent }); + + const fakeWindow = { + get top() { + throw new Error('Access denied'); + } + }; + const result = getPageDescription(fakeWindow); + expect(result).to.equal(ogDescriptionContent); + }); + }); + + describe('getPageKeywords function', function() { + let sandbox; + + beforeEach(() => { + sandbox = sinon.createSandbox(); + }); + + afterEach(() => { + sandbox.restore(); + }); + + it('should return the top document keywords if available', function() { + const keywordsContent = 'keyword1, keyword2, keyword3'; + const fakeTopDocument = { + querySelector: sandbox.stub() + .withArgs('meta[name="keywords"]').returns({ content: keywordsContent }) + }; + const fakeTopWindow = { document: fakeTopDocument }; + + const result = getPageKeywords({ top: fakeTopWindow }); + expect(result).to.equal(keywordsContent); + }); + + it('should return the current document keywords if top document is not accessible', function() { + const keywordsContent = 'keyword1, keyword2, keyword3'; + sandbox.stub(document, 'querySelector') + .withArgs('meta[name="keywords"]').returns({ content: keywordsContent }); + + // 模拟顶层窗口访问异常 + const fakeWindow = { + get top() { + throw new Error('Access denied'); + } + }; + + const result = getPageKeywords(fakeWindow); + expect(result).to.equal(keywordsContent); + }); + + it('should return an empty string if no keywords meta tag is found', function() { + sandbox.stub(document, 'querySelector').withArgs('meta[name="keywords"]').returns(null); + + const result = getPageKeywords(); + expect(result).to.equal(''); + }); + }); + describe('getConnectionDownLink function', function() { + let sandbox; + + beforeEach(() => { + sandbox = sinon.createSandbox(); + }); + + afterEach(() => { + sandbox.restore(); + }); + + it('should return the downlink value as a string if available', function() { + const downlinkValue = 2.5; + const fakeNavigator = { + connection: { + downlink: downlinkValue + } + }; + + const result = getConnectionDownLink({ navigator: fakeNavigator }); + expect(result).to.equal(downlinkValue.toString()); + }); + + it('should return undefined if downlink is not available', function() { + const fakeNavigator = { + connection: {} + }; + + const result = getConnectionDownLink({ navigator: fakeNavigator }); + expect(result).to.be.undefined; + }); + + it('should return undefined if connection is not available', function() { + const fakeNavigator = {}; + + const result = getConnectionDownLink({ navigator: fakeNavigator }); + expect(result).to.be.undefined; + }); + + it('should handle cases where navigator is not defined', function() { + const result = getConnectionDownLink({}); + expect(result).to.be.undefined; + }); + }); + }); +}); From cf1f150f74c2dfdba97800d45cabcf74a5c9c16c Mon Sep 17 00:00:00 2001 From: vicenteampliffy Date: Tue, 9 Jan 2024 18:35:02 +0100 Subject: [PATCH 122/246] Ampliffy Bid Adapter: initial commit (#10883) * ampliffyBidAdapter initial commit * Add tests --- modules/ampliffyBidAdapter.js | 419 +++++++++++++++++ modules/ampliffyBidAdapter.md | 39 ++ test/spec/modules/ampliffyBidAdapter_spec.js | 453 +++++++++++++++++++ 3 files changed, 911 insertions(+) create mode 100644 modules/ampliffyBidAdapter.js create mode 100644 modules/ampliffyBidAdapter.md create mode 100644 test/spec/modules/ampliffyBidAdapter_spec.js diff --git a/modules/ampliffyBidAdapter.js b/modules/ampliffyBidAdapter.js new file mode 100644 index 00000000000..bcd28e5bcf1 --- /dev/null +++ b/modules/ampliffyBidAdapter.js @@ -0,0 +1,419 @@ +import {registerBidder} from '../src/adapters/bidderFactory.js'; +import {logError, logInfo, triggerPixel} from '../src/utils.js'; + +const BIDDER_CODE = 'ampliffy'; +const GVLID = 1258; +const DEFAULT_ENDPOINT = 'bidder.ampliffy.com'; +const TTL = 600; // Time-to-Live - how long (in seconds) Prebid can use this bid. +const LOG_PREFIX = 'AmpliffyBidder: '; + +function isBidRequestValid(bid) { + logInfo(LOG_PREFIX + 'isBidRequestValid: Code: ' + bid.adUnitCode + ': Param' + JSON.stringify(bid.params), bid.adUnitCode); + if (bid.params) { + if (!bid.params.placementId || !bid.params.format) return false; + + if (bid.params.format.toLowerCase() !== 'video' && bid.params.format.toLowerCase() !== 'display' && bid.params.format.toLowerCase() !== 'all') return false; + if (bid.params.format.toLowerCase() === 'video' && !bid.mediaTypes['video']) return false; + if (bid.params.format.toLowerCase() === 'display' && !bid.mediaTypes['banner']) return false; + + if (!bid.params.server || bid.params.server === '') { + const server = bid.params.type + bid.params.region + bid.params.adnetwork; + if (server && server !== '') bid.params.server = server; + else bid.params.server = DEFAULT_ENDPOINT; + } + return true; + } + return false; +} + +function manageConsentArguments(bidderRequest) { + let consent = null; + if (bidderRequest?.gdprConsent) { + consent = { + gdpr: bidderRequest.gdprConsent.gdprApplies ? '1' : '0', + }; + if (bidderRequest.gdprConsent.consentString) { + consent.consent_string = bidderRequest.gdprConsent.consentString; + } + if (bidderRequest.gdprConsent.addtlConsent && bidderRequest.gdprConsent.addtlConsent.indexOf('~') !== -1) { + consent.addtl_consent = bidderRequest.gdprConsent.addtlConsent; + } + } + return consent; +} + +function buildRequests(validBidRequests, bidderRequest) { + const bidRequests = []; + for (const bidRequest of validBidRequests) { + for (const sizes of bidRequest.sizes) { + let extraParams = mergeParams(getDefaultParams(), bidRequest.params.extraParams); + // Apply GDPR parameters to request. + extraParams = mergeParams(extraParams, manageConsentArguments(bidderRequest)); + const serverURL = getServerURL(bidRequest.params.server, sizes, bidRequest.params.placementId, extraParams); + logInfo(LOG_PREFIX + serverURL, 'requests'); + extraParams.bidId = bidRequest.bidId; + bidRequests.push({ + method: 'GET', + url: serverURL, + data: extraParams, + bidRequest, + }); + } + logInfo(LOG_PREFIX + 'Building request from: ' + bidderRequest.url + ': ' + JSON.stringify(bidRequests), bidRequest.adUnitCode); + } + return bidRequests; +} +export function getDefaultParams() { + return { + ciu_szs: '1x1', + gdfp_req: '1', + env: 'vp', + output: 'xml_vast4', + unviewed_position_start: '1' + }; +} +export function mergeParams(params, extraParams) { + if (extraParams) { + for (const k in extraParams) { + params[k] = extraParams[k]; + } + } + return params; +} +export function paramsToQueryString(params) { + return Object.entries(params).filter(e => typeof e[1] !== 'undefined').map(e => { + if (e[1]) return encodeURIComponent(e[0]) + '=' + encodeURIComponent(e[1]); + else return encodeURIComponent(e[0]); + }).join('&'); +} +const getCacheBuster = () => Math.floor(Math.random() * (9999999999 - 1000000000)); + +// For testing purposes +let currentUrl = null; +export function getCurrentURL() { + if (!currentUrl) currentUrl = top.location.href; + return currentUrl; +} +export function setCurrentURL(url) { + currentUrl = url; +} +const getCurrentURLEncoded = () => encodeURIComponent(getCurrentURL()); +function getServerURL(server, sizes, iu, queryParams) { + const random = getCacheBuster(); + const size = sizes[0] + 'x' + sizes[1]; + let serverURL = '//' + server + '/gampad/ads'; + queryParams.sz = size; + queryParams.iu = iu; + queryParams.url = getCurrentURL(); + queryParams.description_url = getCurrentURL(); + queryParams.correlator = random; + + return serverURL; +} +function interpretResponse(serverResponse, bidRequest) { + const bidResponses = []; + + const bidResponse = {}; + let mediaType = 'video'; + if ( + bidRequest.bidRequest?.mediaTypes && + !bidRequest.bidRequest.mediaTypes['video'] + ) { + mediaType = 'banner'; + } + bidResponse.requestId = bidRequest.bidRequest.bidId; + bidResponse.width = bidRequest.bidRequest?.sizes[0][0]; + bidResponse.height = bidRequest.bidRequest?.sizes[0][1]; + bidResponse.ttl = TTL; + bidResponse.creativeId = 'ampCreativeID134'; + bidResponse.netRevenue = true; + bidResponse.mediaType = mediaType; + bidResponse.meta = { + advertiserDomains: [], + }; + let xmlStr = serverResponse.body; + const xml = new window.DOMParser().parseFromString(xmlStr, 'text/xml'); + const xmlData = parseXML(xml, bidResponse); + logInfo(LOG_PREFIX + 'Response from: ' + bidRequest.url + ': ' + JSON.stringify(xmlData), bidRequest.bidRequest.adUnitCode); + if (xmlData.cpm < 0 || !xmlData.creativeURL || !xmlData.bidUp) { + return []; + } + bidResponse.cpm = xmlData.cpm; + bidResponse.currency = xmlData.currency; + + if (mediaType === 'video') { + logInfo(LOG_PREFIX + xmlData.creativeURL, 'requests'); + bidResponse.vastUrl = xmlData.creativeURL; + } else { + bidResponse.adUrl = xmlData.creativeURL; + } + if (xmlData.trackingUrl) { + bidResponse.vastImpUrl = xmlData.trackingUrl; + bidResponse.trackingUrl = xmlData.trackingUrl; + } + bidResponses.push(bidResponse); + return bidResponses; +} +const replaceMacros = (txt, cpm, bid) => { + const size = bid.width + 'x' + bid.height; + txt = txt.replaceAll('%%CACHEBUSTER%%', getCacheBuster()); + txt = txt.replaceAll('@@CACHEBUSTER@@', getCacheBuster()); + txt = txt.replaceAll('%%REFERER%%', getCurrentURLEncoded()); + txt = txt.replaceAll('@@REFERER@@', getCurrentURLEncoded()); + txt = txt.replaceAll('%%REFERRER_URL_UNESC%%', getCurrentURLEncoded()); + txt = txt.replaceAll('@@REFERRER_URL_UNESC@@', getCurrentURLEncoded()); + txt = txt.replaceAll('%%PRICE_ESC%%', encodePrice(cpm)); + txt = txt.replaceAll('@@PRICE_ESC@@', encodePrice(cpm)); + txt = txt.replaceAll('%%SIZES%%', size); + txt = txt.replaceAll('@@SIZES@@', size); + return txt; +} +const encodePrice = (price) => { + price = parseFloat(price); + const s = 116.54; + const c = 1; + const a = 1; + let encodedPrice = s * Math.log10(price + a) + c; + encodedPrice = Math.min(200, encodedPrice); + encodedPrice = Math.round(Math.max(1, encodedPrice)); + + // Format the encoded price with leading zeros if necessary + const formattedEncodedPrice = encodedPrice.toString().padStart(3, '0'); + + // Build the encoding key + const encodingKey = `H--${formattedEncodedPrice}`; + + return encodeURIComponent(`vch=${encodingKey}`); +}; + +function extractCT(xml) { + let ct = null; + try { + try { + const vastAdTagURI = xml.getElementsByTagName('VASTAdTagURI')[0] + if (vastAdTagURI) { + let url = null; + for (const childNode of vastAdTagURI.childNodes) { + if (childNode.nodeValue.trim().includes('http')) { + url = decodeURIComponent(childNode.nodeValue); + } + } + const urlParams = new URLSearchParams(url); + ct = urlParams.get('ct') + } + } catch (e) { + } + if (!ct) { + const geoExtensions = xml.querySelectorAll('Extension[type="geo"]'); + geoExtensions.forEach((geoExtension) => { + const countryElement = geoExtension.querySelector('Country'); + if (countryElement) { + ct = countryElement.textContent; + } + }); + } + } catch (e) {} + return ct; +} + +function extractCPM(htmlContent, ct, cpm) { + const cpmMapDiv = htmlContent.querySelectorAll('[cpmMap]')[0]; + if (cpmMapDiv) { + let cpmMapJSON = JSON.parse(cpmMapDiv.getAttribute('cpmMap')); + if ((cpmMapJSON)) { + if (cpmMapJSON[ct]) { + cpm = cpmMapJSON[ct]; + } else if (cpmMapJSON['default']) { + cpm = cpmMapJSON['default']; + } + } + } + return cpm; +} + +function extractCurrency(htmlContent, currency) { + const currencyDiv = htmlContent.querySelectorAll('[cpmCurrency]')[0]; + if (currencyDiv) { + const currencyValue = currencyDiv.getAttribute('cpmCurrency'); + if (currencyValue && currencyValue !== '') { + currency = currencyValue; + } + } + return currency; +} + +function extractCreativeURL(htmlContent, ct, cpm, bid) { + let creativeURL = null; + const creativeMap = htmlContent.querySelectorAll('[creativeMap]')[0]; + if (creativeMap) { + const creativeMapString = creativeMap.getAttribute('creativeMap'); + + const creativeMapJSON = JSON.parse(creativeMapString); + let defaultURL = null; + for (const url of Object.keys(creativeMapJSON)) { + const geo = creativeMapJSON[url]; + if (geo.includes(ct)) { + creativeURL = replaceMacros(url, cpm, bid); + } else if (geo.includes('default')) { + defaultURL = url; + } + } + if (!creativeURL && defaultURL) creativeURL = replaceMacros(defaultURL, cpm, bid); + } + return creativeURL; +} + +function extractSyncs(htmlContent) { + let userSyncsJSON = null; + const userSyncs = htmlContent.querySelectorAll('[userSyncs]')[0]; + if (userSyncs) { + const userSyncsString = userSyncs.getAttribute('userSyncs'); + + userSyncsJSON = JSON.parse(userSyncsString); + } + return userSyncsJSON; +} + +function extractTrackingURL(htmlContent, ret) { + const trackingUrlDiv = htmlContent.querySelectorAll('[bidder-tracking-url]')[0]; + if (trackingUrlDiv) { + const trackingUrl = trackingUrlDiv.getAttribute('bidder-tracking-url'); + // eslint-disable-next-line no-console + logInfo(LOG_PREFIX + 'parseXML: trackingUrl: ', trackingUrl) + ret.trackingUrl = trackingUrl; + } +} + +export function parseXML(xml, bid) { + const ret = { cpm: 0.001, currency: 'EUR', creativeURL: null, bidUp: false }; + const ct = extractCT(xml); + if (!ct) return ret; + + try { + if (ct) { + const companion = xml.getElementsByTagName('Companion')[0]; + const htmlResource = companion.getElementsByTagName('HTMLResource')[0]; + const htmlContent = document.createElement('html'); + htmlContent.innerHTML = htmlResource.textContent; + + ret.cpm = extractCPM(htmlContent, ct, ret.cpm); + ret.currency = extractCurrency(htmlContent, ret.currency); + ret.creativeURL = extractCreativeURL(htmlContent, ct, ret.cpm, bid); + extractTrackingURL(htmlContent, ret); + ret.bidUp = isAllowedToBidUp(htmlContent, getCurrentURL()); + ret.userSyncs = extractSyncs(htmlContent); + } + } catch (e) { + // eslint-disable-next-line no-console + logError(LOG_PREFIX + 'Error parsing XML', e); + } + // eslint-disable-next-line no-console + logInfo(LOG_PREFIX + 'parseXML RET:', ret); + + return ret; +} +export function isAllowedToBidUp(html, currentURL) { + currentURL = currentURL.split('?')[0]; // Remove parameters + let allowedToPush = false; + try { + const domainsMap = html.querySelectorAll('[domainMap]')[0]; + if (domainsMap) { + let domains = JSON.parse(domainsMap.getAttribute('domainMap')); + if (domains.domainMap) { + domains = domains.domainMap; + } + domains.forEach((d) => { + if (currentURL.includes(d) || d === 'all' || d === '*') allowedToPush = true; + }) + } else { + allowedToPush = true; + } + if (allowedToPush) { + const excludedURL = html.querySelectorAll('[excludedURLs]')[0]; + if (excludedURL) { + const excludedURLsString = domainsMap.getAttribute('excludedURLs'); + if (excludedURLsString !== '') { + let excluded = JSON.parse(excludedURLsString); + excluded.forEach((d) => { + if (currentURL.includes(d)) allowedToPush = false; + }) + } + } + } + } catch (e) { + // eslint-disable-next-line no-console + logError(LOG_PREFIX + 'isAllowedToBidUp', e); + } + return allowedToPush; +} + +function getSyncData(options, syncs) { + const ret = []; + if (syncs?.length) { + for (const sync of syncs) { + if (sync.type === 'syncImage' && options.pixelEnabled) { + ret.push({url: sync.url, type: 'image'}); + } else if (sync.type === 'syncIframe' && options.iframeEnabled) { + ret.push({url: sync.url, type: 'iframe'}); + } + } + } + return ret; +} + +function getUserSyncs(syncOptions, serverResponses) { + const userSyncs = []; + for (const serverResponse of serverResponses) { + if (serverResponse.body) { + try { + const xmlStr = serverResponse.body; + const xml = new window.DOMParser().parseFromString(xmlStr, 'text/xml'); + const xmlData = parseXML(xml, {}); + if (xmlData.userSyncs) { + userSyncs.push(...getSyncData(syncOptions, xmlData.userSyncs)); + } + } catch (e) {} + } + } + return userSyncs; +} + +function onBidWon(bid) { + logInfo(`${LOG_PREFIX} WON AMPLIFFY`); + if (bid.trackingUrl) { + let url = bid.trackingUrl; + + // Replace macros with URL-encoded bid parameters + Object.keys(bid).forEach(key => { + const macroKey = `%%${key.toUpperCase()}%%`; + const value = encodeURIComponent(JSON.stringify(bid[key])); + url = url.split(macroKey).join(value); + }); + + triggerPixel(url, () => { + logInfo(`${LOG_PREFIX} send data success`); + }, + (e) => { + logError(`${LOG_PREFIX} send data error`, e); + }); + } +} +function onTimeOut() { + // eslint-disable-next-line no-console + logInfo(LOG_PREFIX + 'TIMEOUT'); +} + +export const spec = { + code: BIDDER_CODE, + gvlid: GVLID, + aliases: ['ampliffy', 'amp', 'videoffy', 'publiffy'], + supportedMediaTypes: ['video', 'banner'], + isBidRequestValid, + buildRequests, + interpretResponse, + getUserSyncs, + onTimeOut, + onBidWon, +}; + +registerBidder(spec); diff --git a/modules/ampliffyBidAdapter.md b/modules/ampliffyBidAdapter.md new file mode 100644 index 00000000000..a425d910582 --- /dev/null +++ b/modules/ampliffyBidAdapter.md @@ -0,0 +1,39 @@ +# Overview + +``` +Module Name: Ampliffy Bidder Adapter +Module Type: Bidder Adapter +Maintainer: bidder@ampliffy.com +``` + +# Description + +Connects to Ampliffy Ad server for bids. + +Ampliffy bid adapter supports Video currently, and has initial support for Banner. + +For more information about [Ampliffy](https://www.ampliffy.com/en/), please contact [info@ampliffy.com](info@ampliffy.com). + +# Sample Ad Unit: For Publishers +```javascript +var videoAdUnit = [ +{ + code: 'video1', + mediaTypes: { + video: { + playerSize: [[640, 480]], + context: 'instream' + }, + }, + bids: [{ + bidder: 'ampliffy', + params: { + server: 'bidder.ampliffy.com', + placementId: '1213213/example/vrutal_/', + format: 'video' + } + }] +}]; +``` + +``` diff --git a/test/spec/modules/ampliffyBidAdapter_spec.js b/test/spec/modules/ampliffyBidAdapter_spec.js new file mode 100644 index 00000000000..5b86f692d7e --- /dev/null +++ b/test/spec/modules/ampliffyBidAdapter_spec.js @@ -0,0 +1,453 @@ +import { + parseXML, + isAllowedToBidUp, + spec, + getDefaultParams, + mergeParams, + paramsToQueryString, setCurrentURL +} from 'modules/ampliffyBidAdapter.js'; +import {expect} from 'chai'; +import {BANNER, VIDEO} from 'src/mediaTypes'; +import {newBidder} from 'src/adapters/bidderFactory'; + +describe('Ampliffy 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'); + }); + }); + // Global definitions for all tests + const xmlStr = ` + + + + ]]> + + + + ES + `; + const xml = new window.DOMParser().parseFromString(xmlStr, 'text/xml'); + let companion = xml.getElementsByTagName('Companion')[0]; + let htmlResource = companion.getElementsByTagName('HTMLResource')[0]; + let htmlContent = document.createElement('html'); + htmlContent.innerHTML = htmlResource.textContent; + + describe('Is allowed to bid up', function () { + it('Should return true using a URL that is in domainMap', () => { + let allowedToBidUp = isAllowedToBidUp(htmlContent, 'https://testSports.com?id=131313&text=aaaaa&foo=foo'); + expect(allowedToBidUp).to.be.true; + }) + + it('Should return false using an url that is not in domainMap', () => { + let allowedToBidUp = isAllowedToBidUp(htmlContent, 'https://test.com'); + expect(allowedToBidUp).to.be.false; + }) + + it('Should return false using an url that is excluded.', () => { + let allowedToBidUp = isAllowedToBidUp(htmlContent, 'https://www.no-allowed.com/busqueda/sexo/sexo?test=1#item1'); + expect(allowedToBidUp).to.be.false; + }) + }) + + describe('Helper functions', function () { + it('Should default params not to be null', () => { + const defaultParams = getDefaultParams(); + + expect(defaultParams).not.to.be.null; + }) + it('Should the merge two object params into a new object', () => { + const params1 = { + 'hello': 'world', + 'ampTest': 'this will be replaced' + } + const params2 = { + 'test': 1, + 'ampTest': 'This will be replace the param with the same name in other array' + } + const allParams = mergeParams(params1, params2); + + const paramsComplete = + { + 'hello': 'world', + 'ampTest': 'This will be replace the param with the same name in other array', + 'test': 1, + } + expect(allParams).not.to.be.null; + expect(JSON.stringify(allParams)).to.equal(JSON.stringify(paramsComplete)); + }) + it('Params to QueryString', () => { + const params = { + 'test': 1, + 'ampTest': 'ret', + 'empty': null, + 'quoteMark': '?', + 'test1': undefined + } + const queryString = paramsToQueryString(params); + + expect(queryString).not.to.be.null; + expect(queryString).to.equal('test=1&Test=ret&empty"eMark=%3F'); + }) + }) + + describe('isBidRequestValid', function () { + it('Should return true when required params found', function () { + const bidRequest = { + bidder: 'ampliffy', + params: { + server: 'bidder.ampliffy.com', + placementId: 1235465798, + format: 'all' + }, + mediaTypes: { + banner: { + sizes: [1, 1] + } + }, + } + expect(spec.isBidRequestValid(bidRequest)).to.be.true; + }) + it('Should return false when param format is display but mediaTypes are for video', function () { + const bidRequest = { + bidder: 'ampliffy', + params: { + server: 'bidder.ampliffy.com', + placementId: 1235465798, + format: 'display' + }, + mediaTypes: { + video: { + sizes: [1, 1] + } + }, + } + expect(spec.isBidRequestValid(bidRequest)).to.be.false; + }) + it('Should return false when param format is video but mediaTypes are for banner', function () { + const bidRequest = { + bidder: 'ampliffy', + params: { + server: 'bidder.ampliffy.com', + placementId: 1235465798, + format: 'video' + }, + mediaTypes: { + banner: { + sizes: [1, 1] + } + }, + } + expect(spec.isBidRequestValid(bidRequest)).to.be.false; + }) + it('Should return true when param format is video and mediaTypes are for video', function () { + const bidRequest = { + bidder: 'ampliffy', + params: { + server: 'bidder.ampliffy.com', + placementId: 1235465798, + format: 'video' + }, + mediaTypes: { + video: { + sizes: [1, 1] + } + }, + } + expect(spec.isBidRequestValid(bidRequest)).to.be.true; + }) + it('Should return true when param format is display and mediaTypes are for banner', function () { + const bidRequest = { + bidder: 'ampliffy', + params: { + server: 'bidder.ampliffy.com', + placementId: 1235465798, + format: 'display' + }, + mediaTypes: { + banner: { + sizes: [1, 1] + } + }, + } + expect(spec.isBidRequestValid(bidRequest)).to.be.true; + }) + it('Should return true when param format is all and mediaTypes are for banner', function () { + const bidRequest = { + bidder: 'ampliffy', + params: { + server: 'bidder.ampliffy.com', + placementId: 1235465798, + format: 'all' + }, + mediaTypes: { + banner: { + sizes: [1, 1] + } + }, + } + expect(spec.isBidRequestValid(bidRequest)).to.be.true; + }) + it('Should return true when param format is all and mediaTypes are for video', function () { + const bidRequest = { + bidder: 'ampliffy', + params: { + server: 'bidder.ampliffy.com', + placementId: 1235465798, + format: 'all' + }, + mediaTypes: { + video: { + sizes: [1, 1] + } + }, + } + expect(spec.isBidRequestValid(bidRequest)).to.be.true; + }) + it('Should return false without placementId param', function () { + const bidRequest = { + bidder: 'ampliffy', + params: {} + } + expect(spec.isBidRequestValid(bidRequest)).to.be.false; + }) + it('Should return false without param object', function () { + const bidRequest = { + bidder: 'ampliffy', + } + expect(spec.isBidRequestValid(bidRequest)).to.be.false; + }) + }); + + describe('Build request function', function () { + const bidderRequest = { + 'bidderCode': 'ampliffy', + 'auctionId': 'c4a771bf-1791-4513-82b3-96c48d19ddff', + 'bidderRequestId': '1134bdcbe47f25', + 'bids': [{ + 'bidder': 'ampliffy', + 'params': { + 'placementId': 1235465798, + 'type': 'bidder.', + 'region': 'alan-development.k8s.', + 'adnetwork': 'ampliffy.com', + 'SERVER': 'bidder.ampliffy.com' + }, + 'crumbs': {'pubcid': '29844d69-c4e5-4b00-8602-6dd09815363a'}, + 'ortb2Imp': {'ext': {'data': {'pbadslot': 'video1'}}}, + 'mediaTypes': { + 'video': { + 'context': 'instream', + 'playerSize': [[640, 480]], + 'mimes': ['video/mp4'], + 'protocols': [1, 2, 3, 4, 5, 6, 7, 8], + 'playbackmethod': [2], + 'skip': 1 + } + }, + 'adUnitCode': 'video1', + 'transactionId': 'f85c1b10-bad3-4c3f-a2bb-2c484c405bc9', + 'sizes': [[640, 480]], + 'bidId': '2bc71d9c058842', + 'bidderRequestId': '1134bdcbe47f25', + 'auctionId': 'c4a771bf-1791-4513-82b3-96c48d19ddff', + 'src': 'client', + 'bidRequestsCount': 1, + 'bidderRequestsCount': 1, + 'bidderWinsCount': 0 + }], + 'auctionStart': 1644029483655, + 'timeout': 3000, + 'refererInfo': { + 'referer': 'http://localhost:9999/integrationExamples/gpt/hello_world_video.html?pbjs_debug=true', + 'reachedTop': true, + 'isAmp': false, + 'numIframes': 0, + 'stack': ['http://localhost:9999/integrationExamples/gpt/hello_world_video.html?pbjs_debug=true'], + 'canonicalUrl': null + }, + 'start': 1644029483708 + } + const validBidRequests = [ + { + 'bidder': 'ampliffy', + 'params': { + 'placementId': 1235465798, + 'type': 'bidder.', + 'region': 'alan-development.k8s.', + 'adnetwork': 'ampliffy.com', + 'SERVER': 'bidder.ampliffy.com' + }, + 'crumbs': {'pubcid': '29844d69-c4e5-4b00-8602-6dd09815363a'}, + 'ortb2Imp': {'ext': {'data': {'pbadslot': 'video1'}}}, + 'mediaTypes': { + 'video': { + 'context': 'instream', + 'playerSize': [[640, 480]], + 'mimes': ['video/mp4'], + 'protocols': [1, 2, 3, 4, 5, 6, 7, 8], + 'playbackmethod': [2], + 'skip': 1 + } + }, + 'adUnitCode': 'video1', + 'transactionId': 'f85c1b10-bad3-4c3f-a2bb-2c484c405bc9', + 'sizes': [[640, 480]], + 'bidId': '2bc71d9c058842', + 'bidderRequestId': '1134bdcbe47f25', + 'auctionId': 'c4a771bf-1791-4513-82b3-96c48d19ddff', + 'src': 'client', + 'bidRequestsCount': 1, + 'bidderRequestsCount': 1, + 'bidderWinsCount': 0 + } + ]; + it('Should return one or more bid requests', function () { + expect(spec.buildRequests(validBidRequests, bidderRequest).length).to.be.greaterThan(0); + }); + }) + describe('Interpret response', function () { + let bidRequest = { + bidRequest: { + adUnitCode: 'div-gpt-ad-1460505748561-0', + auctionId: '469bb2e2-351f-4d01-b782-cdbca5e3e0ed', + bidId: '2d40b8dcd02ade', + bidRequestsCount: 1, + bidder: 'ampliffy', + bidderRequestId: '128c07edc4680f', + bidderRequestsCount: 1, + bidderWinsCount: 0, + crumbs: { + pubcid: '29844d69-c4e5-4b00-8602-6dd09815363a' + }, + mediaTypes: { + banner: { + sizes: [ + [300, 250], + [300, 600] + ] + } + }, + ortb2Imp: {ext: {}}, + params: {placementId: 13144370}, + sizes: [ + [300, 250], + [300, 600] + ], + src: 'client', + transactionId: '103b2b58-6ed1-45e9-9486-c942d6042e3' + }, + data: {bidId: '2d40b8dcd02ade'}, + method: 'GET', + url: 'https://test.com', + }; + + it('Should extract a CPM and currency from the xml', () => { + let cpmData = parseXML(xml); + expect(cpmData).to.not.be.a('null'); + expect(cpmData.cpm).to.equal('.23'); + expect(cpmData.currency).to.equal('USD'); + }); + + it('It should return no ads when the CPM is less than zero.', () => { + const xmlStr1 = ` + + + + + + + + +
+
+
+
+ + + ]]> +
+
+ + ES +
+
+
`; + let serverResponse = { + 'body': xmlStr1, + } + const bidResponses = spec.interpretResponse(serverResponse, bidRequest); + expect(bidResponses.length).to.equal(0); + }) + + it('It should return no ads when the creative url is not in the xml', () => { + const xmlStr1 = ` + + + + + + + + +
+
+
+
+ + ]]> + + + ES + + + `; + let serverResponse = { + 'body': xmlStr1, + } + const bidResponses = spec.interpretResponse(serverResponse, bidRequest); + expect(bidResponses.length).to.equal(0); + }) + it('It should return a banner ad.', () => { + let serverResponse = { + 'body': xmlStr, + } + setCurrentURL('https://www.sports.com'); + const bidResponses = spec.interpretResponse(serverResponse, bidRequest); + expect(bidResponses.length).greaterThan(0); + expect(bidResponses[0].mediaType).to.be.equal(BANNER); + expect(bidResponses[0].ad).not.to.be.null; + }) + it('It should return a video ad.', () => { + let serverResponse = { + 'body': xmlStr, + } + setCurrentURL('https://www.sports.com'); + bidRequest.bidRequest.mediaTypes = { + video: { + sizes: [ + [300, 250], + [300, 600] + ] + } + } + const bidResponses = spec.interpretResponse(serverResponse, bidRequest); + expect(bidResponses.length).greaterThan(0); + expect(bidResponses[0].mediaType).to.be.equal(VIDEO); + expect(bidResponses[0].vastUrl).not.to.be.null; + }) + }); +}); From b93416b2a6838d7ae54691cd9d9167d336386e5c Mon Sep 17 00:00:00 2001 From: Chris Huie Date: Tue, 9 Jan 2024 12:24:57 -0700 Subject: [PATCH 123/246] Utils.js (Warning in Integration Tests): add back getWindowFromDocument (#10865) * add back getWindowFromDocument * do not use core for 1 consumer --------- Co-authored-by: Demetrio Girardi --- modules/tpmnBidAdapter.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/tpmnBidAdapter.js b/modules/tpmnBidAdapter.js index 89951a99f12..e3adb80fb2f 100644 --- a/modules/tpmnBidAdapter.js +++ b/modules/tpmnBidAdapter.js @@ -184,7 +184,7 @@ function createRenderer(bid) { function outstreamRender(bid, doc) { bid.renderer.push(() => { - const win = utils.getWindowFromDocument(doc) || window; + const win = (doc) ? doc.defaultView : window; win.ANOutstreamVideo.renderAd({ sizes: [bid.playerWidth, bid.playerHeight], targetId: bid.adUnitCode, From 2b25ec49305248d1565d09d15c4528615cede6ab Mon Sep 17 00:00:00 2001 From: bretg Date: Tue, 9 Jan 2024 14:28:54 -0500 Subject: [PATCH 124/246] Rubicon: remove transformBidParams function (#10919) Per https://github.com/prebid/Prebid.js/issues/6361 Confirmed that the server-side adapters for both PBS-Go and PBS-Java handle the type conversion. --- modules/rubiconBidAdapter.js | 14 -------------- 1 file changed, 14 deletions(-) diff --git a/modules/rubiconBidAdapter.js b/modules/rubiconBidAdapter.js index 226950875e2..e9556b9afc6 100644 --- a/modules/rubiconBidAdapter.js +++ b/modules/rubiconBidAdapter.js @@ -22,7 +22,6 @@ import { _each } from '../src/utils.js'; import {getAllOrtbKeywords} from '../libraries/keywords/keywords.js'; -import {convertTypes} from '../libraries/transformParamsUtils/convertTypes.js'; const DEFAULT_INTEGRATION = 'pbjs_lite'; const DEFAULT_PBS_INTEGRATION = 'pbjs'; @@ -766,19 +765,6 @@ export const spec = { url: `https://${rubiConf.syncHost || 'eus'}.rubiconproject.com/usync.html` + params }; } - }, - /** - * Covert bid param types for S2S - * @param {Object} params bid params - * @param {Boolean} isOpenRtb boolean to check openrtb2 protocol - * @return {Object} params bid params - */ - transformBidParams: function(params, isOpenRtb) { - return convertTypes({ - 'accountId': 'number', - 'siteId': 'number', - 'zoneId': 'number' - }, params); } }; From 621cedc22a3375882fda416cea206776671f2bb1 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 10 Jan 2024 06:42:36 -0700 Subject: [PATCH 125/246] Bump follow-redirects from 1.15.2 to 1.15.4 (#10922) Bumps [follow-redirects](https://github.com/follow-redirects/follow-redirects) from 1.15.2 to 1.15.4. - [Release notes](https://github.com/follow-redirects/follow-redirects/releases) - [Commits](https://github.com/follow-redirects/follow-redirects/compare/v1.15.2...v1.15.4) --- updated-dependencies: - dependency-name: follow-redirects dependency-type: indirect ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- package-lock.json | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/package-lock.json b/package-lock.json index 3020105714f..6c288e114d5 100644 --- a/package-lock.json +++ b/package-lock.json @@ -6,7 +6,7 @@ "packages": { "": { "name": "prebid.js", - "version": "8.30.0-pre", + "version": "8.31.0-pre", "license": "Apache-2.0", "dependencies": { "@babel/core": "^7.16.7", @@ -11009,9 +11009,9 @@ } }, "node_modules/follow-redirects": { - "version": "1.15.2", - "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.2.tgz", - "integrity": "sha512-VQLG33o04KaQ8uYi2tVNbdrWp1QWxNNea+nmIB4EVM28v0hmP17z7aG1+wAkNzVq4KeXTq3221ye5qTJP91JwA==", + "version": "1.15.4", + "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.4.tgz", + "integrity": "sha512-Cr4D/5wlrb0z9dgERpUL3LrmPKVDsETIJhaCMeDfuFYcqa5bldGV6wBsAN6X/vxlXQtFBMrXdXxdL8CbDTGniw==", "dev": true, "funding": [ { @@ -34011,9 +34011,9 @@ } }, "follow-redirects": { - "version": "1.15.2", - "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.2.tgz", - "integrity": "sha512-VQLG33o04KaQ8uYi2tVNbdrWp1QWxNNea+nmIB4EVM28v0hmP17z7aG1+wAkNzVq4KeXTq3221ye5qTJP91JwA==", + "version": "1.15.4", + "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.4.tgz", + "integrity": "sha512-Cr4D/5wlrb0z9dgERpUL3LrmPKVDsETIJhaCMeDfuFYcqa5bldGV6wBsAN6X/vxlXQtFBMrXdXxdL8CbDTGniw==", "dev": true }, "for-each": { From cdcfe15eba97b9b772e36528521396ede90ef7d2 Mon Sep 17 00:00:00 2001 From: Jeff Mahoney Date: Wed, 10 Jan 2024 09:09:52 -0500 Subject: [PATCH 126/246] Support cdep (#10921) Changes to bid adapter logic and tests to support passing `cdep` along in requests to Sharethrough's adserver endpoint. --- modules/sharethroughBidAdapter.js | 9 +++-- .../modules/sharethroughBidAdapter_spec.js | 37 +++++++++++++++++++ 2 files changed, 43 insertions(+), 3 deletions(-) diff --git a/modules/sharethroughBidAdapter.js b/modules/sharethroughBidAdapter.js index 3684e793dcb..53cb67c4e6d 100644 --- a/modules/sharethroughBidAdapter.js +++ b/modules/sharethroughBidAdapter.js @@ -45,6 +45,7 @@ export const sharethroughAdapterSpec = { dnt: navigator.doNotTrack === '1' ? 1 : 0, h: window.screen.height, w: window.screen.width, + ext: {}, }, regs: { coppa: config.getConfig('coppa') === true ? 1 : 0, @@ -63,6 +64,10 @@ export const sharethroughAdapterSpec = { test: 0, }; + if (bidderRequest.ortb2?.device?.ext?.cdep) { + req.device.ext['cdep'] = bidderRequest.ortb2.device.ext.cdep; + } + req.user = nullish(firstPartyData.user, {}); if (!req.user.ext) req.user.ext = {}; req.user.ext.eids = bidRequests[0].userIdAsEids || []; @@ -220,9 +225,7 @@ export const sharethroughAdapterSpec = { const shouldCookieSync = syncOptions.pixelEnabled && deepAccess(serverResponses, '0.body.cookieSyncUrls') !== undefined; - return shouldCookieSync - ? serverResponses[0].body.cookieSyncUrls.map((url) => ({ type: 'image', url: url })) - : []; + return shouldCookieSync ? serverResponses[0].body.cookieSyncUrls.map((url) => ({ type: 'image', url: url })) : []; }, // Empty implementation for prebid core to be able to find it diff --git a/test/spec/modules/sharethroughBidAdapter_spec.js b/test/spec/modules/sharethroughBidAdapter_spec.js index eb0b45dda11..6a63ae681e7 100644 --- a/test/spec/modules/sharethroughBidAdapter_spec.js +++ b/test/spec/modules/sharethroughBidAdapter_spec.js @@ -588,6 +588,43 @@ describe('sharethrough adapter spec', function () { }); }); + describe('cookie deprecation', () => { + it('should not add cdep if we do not get it in an impression request', () => { + const builtRequests = spec.buildRequests(bidRequests, { + auctionId: 'new-auction-id', + ortb2: { + device: { + ext: { + propThatIsNotCdep: 'value-we-dont-care-about', + }, + }, + }, + }); + const noCdep = builtRequests.every((builtRequest) => { + const ourCdepValue = builtRequest.data.device?.ext?.cdep; + return ourCdepValue === undefined; + }); + expect(noCdep).to.be.true; + }); + + it('should add cdep if we DO get it in an impression request', () => { + const builtRequests = spec.buildRequests(bidRequests, { + auctionId: 'new-auction-id', + ortb2: { + device: { + ext: { + cdep: 'cdep-value', + }, + }, + }, + }); + const cdepPresent = builtRequests.every((builtRequest) => { + return builtRequest.data.device.ext.cdep === 'cdep-value'; + }); + expect(cdepPresent).to.be.true; + }); + }); + describe('first party data', () => { const firstPartyData = { site: { From 74f6bcea65a4c42d3738313663dd0e69bdf9d1b2 Mon Sep 17 00:00:00 2001 From: Denis Logachov Date: Wed, 10 Jan 2024 16:31:47 +0200 Subject: [PATCH 127/246] Adkernel Bid Adapter: multiformat imp splitting (#10918) * Adkernel: multiformat adunit support * fix --- modules/adkernelBidAdapter.js | 103 +++++++++++++------ test/spec/modules/adkernelBidAdapter_spec.js | 48 +++++++-- 2 files changed, 114 insertions(+), 37 deletions(-) diff --git a/modules/adkernelBidAdapter.js b/modules/adkernelBidAdapter.js index 9d9da8cb0ab..add24772463 100644 --- a/modules/adkernelBidAdapter.js +++ b/modules/adkernelBidAdapter.js @@ -67,6 +67,11 @@ const NATIVE_INDEX = NATIVE_MODEL.reduce((acc, val, idx) => { return acc; }, {}); +const MULTI_FORMAT_SUFFIX = '__mf'; +const MULTI_FORMAT_SUFFIX_BANNER = 'b' + MULTI_FORMAT_SUFFIX; +const MULTI_FORMAT_SUFFIX_VIDEO = 'v' + MULTI_FORMAT_SUFFIX; +const MULTI_FORMAT_SUFFIX_NATIVE = 'n' + MULTI_FORMAT_SUFFIX; + /** * Adapter for requesting bids from AdKernel white-label display platform */ @@ -173,6 +178,9 @@ export const spec = { ttl: 360, netRevenue: true }; + if (prBid.requestId.endsWith(MULTI_FORMAT_SUFFIX)) { + prBid.requestId = stripMultiformatSuffix(prBid.requestId); + } if ('banner' in imp) { prBid.mediaType = BANNER; prBid.width = rtbBid.w; @@ -239,13 +247,13 @@ registerBidder(spec); function groupImpressionsByHostZone(bidRequests, refererInfo) { let secure = (refererInfo && refererInfo.page?.indexOf('https:') === 0); return Object.values( - bidRequests.map(bidRequest => buildImp(bidRequest, secure)) + bidRequests.map(bidRequest => buildImps(bidRequest, secure)) .reduce((acc, curr, index) => { let bidRequest = bidRequests[index]; let {zoneId, host} = bidRequest.params; let key = `${host}_${zoneId}`; acc[key] = acc[key] || {host: host, zoneId: zoneId, imps: []}; - acc[key].imps.push(curr); + acc[key].imps.push(...curr); return acc; }, {}) ); @@ -264,61 +272,90 @@ function getBidFloor(bid, mediaType, sizes) { } /** - * Builds rtb imp object for single adunit + * Builds rtb imp object(s) for single adunit * @param bidRequest {BidRequest} * @param secure {boolean} */ -function buildImp(bidRequest, secure) { - const imp = { +function buildImps(bidRequest, secure) { + let imp = { 'id': bidRequest.bidId, 'tagid': bidRequest.adUnitCode }; - var mediaType; + if (secure) { + imp.secure = 1; + } var sizes = []; - - if (bidRequest.mediaTypes?.banner) { + let mediaTypes = bidRequest.mediaTypes; + let isMultiformat = (~~!!mediaTypes?.banner + ~~!!mediaTypes?.video + ~~!!mediaTypes?.native) > 1; + let result = []; + let typedImp; + + if (mediaTypes?.banner) { + if (isMultiformat) { + typedImp = {...imp}; + typedImp.id = imp.id + MULTI_FORMAT_SUFFIX_BANNER; + } else { + typedImp = imp; + } sizes = getAdUnitSizes(bidRequest); - let pbBanner = bidRequest.mediaTypes.banner; - imp.banner = { + let pbBanner = mediaTypes.banner; + typedImp.banner = { ...getDefinedParamsOrEmpty(bidRequest.ortb2Imp, BANNER_FPD), ...getDefinedParamsOrEmpty(pbBanner, BANNER_PARAMS), format: sizes.map(wh => parseGPTSingleSizeArrayToRtbSize(wh)), topframe: 0 }; - mediaType = BANNER; - } else if (bidRequest.mediaTypes?.video) { - let pbVideo = bidRequest.mediaTypes.video; - imp.video = { + initImpBidfloor(typedImp, bidRequest, sizes, isMultiformat ? '*' : BANNER); + result.push(typedImp); + } + + if (mediaTypes?.video) { + if (isMultiformat) { + typedImp = {...imp}; + typedImp.id = typedImp.id + MULTI_FORMAT_SUFFIX_VIDEO; + } else { + typedImp = imp; + } + let pbVideo = mediaTypes.video; + typedImp.video = { ...getDefinedParamsOrEmpty(bidRequest.ortb2Imp, VIDEO_FPD), ...getDefinedParamsOrEmpty(pbVideo, VIDEO_PARAMS) }; if (pbVideo.playerSize) { sizes = pbVideo.playerSize[0]; - imp.video = Object.assign(imp.video, parseGPTSingleSizeArrayToRtbSize(sizes) || {}); + typedImp.video = Object.assign(typedImp.video, parseGPTSingleSizeArrayToRtbSize(sizes) || {}); } else if (pbVideo.w && pbVideo.h) { - imp.video.w = pbVideo.w; - imp.video.h = pbVideo.h; + typedImp.video.w = pbVideo.w; + typedImp.video.h = pbVideo.h; } - mediaType = VIDEO; - } else if (bidRequest.mediaTypes?.native) { - let nativeRequest = buildNativeRequest(bidRequest.mediaTypes.native); - imp.native = { + initImpBidfloor(typedImp, bidRequest, sizes, isMultiformat ? '*' : VIDEO); + result.push(typedImp); + } + + if (mediaTypes?.native) { + if (isMultiformat) { + typedImp = {...imp}; + typedImp.id = typedImp.id + MULTI_FORMAT_SUFFIX_NATIVE; + } else { + typedImp = imp; + } + let nativeRequest = buildNativeRequest(mediaTypes.native); + typedImp.native = { ...getDefinedParamsOrEmpty(bidRequest.ortb2Imp, NATIVE_FPD), ver: '1.1', request: JSON.stringify(nativeRequest) }; - mediaType = NATIVE; - } else { - throw new Error('Unsupported bid received'); - } - let floor = getBidFloor(bidRequest, mediaType, sizes); - if (floor) { - imp.bidfloor = floor; + initImpBidfloor(typedImp, bidRequest, sizes, isMultiformat ? '*' : NATIVE); + result.push(typedImp); } - if (secure) { - imp.secure = 1; + return result; +} + +function initImpBidfloor(imp, bid, sizes, mediaType) { + let bidfloor = getBidFloor(bid, mediaType, sizes); + if (bidfloor) { + imp.bidfloor = bidfloor; } - return imp; } function getDefinedParamsOrEmpty(object, params) { @@ -643,3 +680,7 @@ function buildNativeAd(nativeResp) { }); return cleanObj(nativeAd); } + +function stripMultiformatSuffix(impid) { + return impid.substr(0, impid.length - MULTI_FORMAT_SUFFIX.length - 1); +} diff --git a/test/spec/modules/adkernelBidAdapter_spec.js b/test/spec/modules/adkernelBidAdapter_spec.js index ac2e3785780..ade34478c20 100644 --- a/test/spec/modules/adkernelBidAdapter_spec.js +++ b/test/spec/modules/adkernelBidAdapter_spec.js @@ -250,6 +250,31 @@ describe('Adkernel adapter', function () { }], bidid: 'pTuOlf5KHUo', cur: 'EUR' + }, + multiformat_response = { + id: '47ce4badcf7482', + seatbid: [{ + bid: [{ + id: 'sZSYq5zYMxo_0', + impid: 'Bid_01b__mf', + crid: '100_003', + price: 0.00145, + adid: '158801', + adm: '', + nurl: 'https://rtb.com/win?i=sZSYq5zYMxo_0&f=nurl', + cid: '16855' + }, { + id: 'sZSYq5zYMxo_1', + impid: 'Bid_01v__mf', + crid: '100_003', + price: 0.25, + adid: '158801', + nurl: 'https://rtb.com/win?i=sZSYq5zYMxo_1&f=nurl', + cid: '16855' + }] + }], + bidid: 'pTuOlf5KHUo', + cur: 'USD' }; var sandbox; @@ -460,18 +485,29 @@ describe('Adkernel adapter', function () { }); describe('multiformat request building', function () { - let _, bidRequests; + let pbRequests, bidRequests; before(function () { - [_, bidRequests] = buildRequest([bid_multiformat]); + [pbRequests, bidRequests] = buildRequest([bid_multiformat]); }); it('should contain single request', function () { expect(bidRequests).to.have.length(1); - expect(bidRequests[0].imp).to.have.length(1); }); - it('should contain banner-only impression', function () { - expect(bidRequests[0].imp).to.have.length(1); + it('should contain both impression', function () { + expect(bidRequests[0].imp).to.have.length(2); expect(bidRequests[0].imp[0]).to.have.property('banner'); - expect(bidRequests[0].imp[0]).to.not.have.property('video'); + expect(bidRequests[0].imp[1]).to.have.property('video'); + // check that splitted imps do not share same impid + expect(bidRequests[0].imp[0].id).to.be.not.eql('Bid_01'); + expect(bidRequests[0].imp[1].id).to.be.not.eql('Bid_01'); + expect(bidRequests[0].imp[1].id).to.be.not.eql(bidRequests[0].imp[0].id); + }); + it('x', function() { + let bids = spec.interpretResponse({body: multiformat_response}, pbRequests[0]); + expect(bids).to.have.length(2); + expect(bids[0].requestId).to.be.eql('Bid_01'); + expect(bids[0].mediaType).to.be.eql('banner'); + expect(bids[1].requestId).to.be.eql('Bid_01'); + expect(bids[1].mediaType).to.be.eql('video'); }); }); From c94eb4e98abbc8fd2488b3d2d76955b294265eef Mon Sep 17 00:00:00 2001 From: GeoEdge-r-and-d <72186958+GeoEdge-r-and-d@users.noreply.github.com> Date: Thu, 11 Jan 2024 06:12:51 +0200 Subject: [PATCH 128/246] Geodge RTD module: update preload mechanism (#10911) * Update preload mechanism to work with an iframe * Update tests --------- Co-authored-by: daniel manan --- modules/geoedgeRtdProvider.js | 36 +++++++++++--- test/spec/modules/geoedgeRtdProvider_spec.js | 51 +++++++++++++------- 2 files changed, 63 insertions(+), 24 deletions(-) diff --git a/modules/geoedgeRtdProvider.js b/modules/geoedgeRtdProvider.js index 9bdf74c6e3c..37db5860001 100644 --- a/modules/geoedgeRtdProvider.js +++ b/modules/geoedgeRtdProvider.js @@ -17,11 +17,12 @@ import { submodule } from '../src/hook.js'; import { ajax } from '../src/ajax.js'; -import { generateUUID, insertElement, isEmpty, logError } from '../src/utils.js'; +import { generateUUID, createInvisibleIframe, insertElement, isEmpty, logError } from '../src/utils.js'; import * as events from '../src/events.js'; import CONSTANTS from '../src/constants.json'; import { loadExternalScript } from '../src/adloader.js'; import { auctionManager } from '../src/auctionManager.js'; +import { getRefererInfo } from '../src/refererDetection.js'; /** @type {string} */ const SUBMODULE_NAME = 'geoedge'; @@ -69,17 +70,38 @@ export function setWrapper(responseText) { wrapper = responseText; } +export function getInitialParams(key) { + let refererInfo = getRefererInfo(); + let params = { + wver: 'pbjs', + wtype: 'pbjs-module', + key, + meta: { + topUrl: refererInfo.page + }, + site: refererInfo.domain, + pimp: PV_ID, + fsRan: true, + frameApi: true + }; + return params; +} + +export function markAsLoaded() { + preloaded = true; +} + /** * 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); + let iframe = createInvisibleIframe(); + iframe.id = 'grumiFrame'; + insertElement(iframe); + iframe.contentWindow.grumi = getInitialParams(key); + let url = getClientUrl(key); + loadExternalScript(url, SUBMODULE_NAME, markAsLoaded, iframe.contentDocument); } /** diff --git a/test/spec/modules/geoedgeRtdProvider_spec.js b/test/spec/modules/geoedgeRtdProvider_spec.js index b0307a60a3c..211a3efa3c6 100644 --- a/test/spec/modules/geoedgeRtdProvider_spec.js +++ b/test/spec/modules/geoedgeRtdProvider_spec.js @@ -1,18 +1,21 @@ import * as utils from '../../../src/utils.js'; import {loadExternalScript} from '../../../src/adloader.js'; -import { +import * as geoedgeRtdModule from '../../../modules/geoedgeRtdProvider.js'; +import {server} from '../../../test/mocks/xhr.js'; +import * as events from '../../../src/events.js'; +import CONSTANTS from '../../../src/constants.json'; + +let { geoedgeSubmodule, getClientUrl, getInPageUrl, htmlPlaceholder, setWrapper, getMacros, - wrapper, - WRAPPER_URL -} from '../../../modules/geoedgeRtdProvider.js'; -import {server} from '../../../test/mocks/xhr.js'; -import * as events from '../../../src/events.js'; -import CONSTANTS from '../../../src/constants.json'; + WRAPPER_URL, + preloadClient, + markAsLoaded +} = geoedgeRtdModule; let key = '123123123'; function makeConfig(gpt) { @@ -65,13 +68,11 @@ describe('Geoedge RTD module', function () { }); }); describe('init', function () { - let insertElementStub; - before(function () { - insertElementStub = sinon.stub(utils, 'insertElement'); + sinon.spy(geoedgeRtdModule, 'preloadClient'); }); after(function () { - utils.insertElement.restore(); + geoedgeRtdModule.preloadClient.restore(); }); it('should return false when missing params or key', function () { let missingParams = geoedgeSubmodule.init({}); @@ -87,14 +88,13 @@ describe('Geoedge RTD module', function () { let isWrapperRequest = request && request.url && request.url && request.url === WRAPPER_URL; expect(isWrapperRequest).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); + it('should call preloadClient', function () { + expect(preloadClient.called); }); it('should emit billable events with applicable winning bids', function (done) { let counter = 0; events.on(CONSTANTS.EVENTS.BILLABLE_EVENT, function (event) { - if (event.vendor === 'geoedge' && event.type === 'impression') { + if (event.vendor === geoedgeSubmodule.name && event.type === 'impression') { counter += 1; } expect(counter).to.equal(1); @@ -104,7 +104,7 @@ describe('Geoedge RTD module', function () { }); it('should load the in page code when gpt params is true', function () { geoedgeSubmodule.init(makeConfig(true)); - let isInPageUrl = arg => arg == getInPageUrl(key); + let isInPageUrl = arg => arg === getInPageUrl(key); expect(loadExternalScript.calledWith(sinon.match(isInPageUrl))).to.equal(true); }); it('should set the window.grumi config object when gpt params is true', function () { @@ -112,10 +112,27 @@ describe('Geoedge RTD module', function () { expect(hasGrumiObj && window.grumi.key === key && window.grumi.fromPrebid).to.equal(true); }); }); + describe('preloadClient', function () { + let iframe; + preloadClient(key); + let loadExternalScriptCall = loadExternalScript.getCall(0); + it('should create an invisible iframe and insert it to the DOM', function () { + iframe = document.getElementById('grumiFrame'); + expect(iframe && iframe.style.display === 'none'); + }); + it('should assign params object to the iframe\'s window', function () { + let grumi = iframe.contentWindow.grumi; + expect(grumi.key).to.equal(key); + }); + it('should preload the client into the iframe', function () { + let isClientUrl = arg => arg === getClientUrl(key); + expect(loadExternalScriptCall.calledWithMatch(isClientUrl)).to.equal(true); + }); + }); describe('setWrapper', function () { it('should set the wrapper', function () { setWrapper(mockWrapper); - expect(wrapper).to.equal(mockWrapper); + expect(geoedgeRtdModule.wrapper).to.equal(mockWrapper); }); }); describe('getMacros', function () { From 28ba8bce409d246933bca301db34502ee3b710d3 Mon Sep 17 00:00:00 2001 From: Andrii Pukh <152202940+apukh-magnite@users.noreply.github.com> Date: Thu, 11 Jan 2024 06:13:06 +0200 Subject: [PATCH 129/246] Add option for no signal bidders (#10867) --- modules/priceFloors.js | 22 ++++- test/spec/modules/priceFloors_spec.js | 118 ++++++++++++++++++++++++++ 2 files changed, 138 insertions(+), 2 deletions(-) diff --git a/modules/priceFloors.js b/modules/priceFloors.js index f114f142bac..9c3869c480a 100644 --- a/modules/priceFloors.js +++ b/modules/priceFloors.js @@ -332,13 +332,29 @@ export function getFloorDataFromAdUnits(adUnits) { }, {}); } +function getNoFloorSignalBidersArray(floorData) { + const { data, enforcement } = floorData + // The data.noFloorSignalBidders higher priority then the enforcment + if (data?.noFloorSignalBidders?.length > 0) { + return data.noFloorSignalBidders + } else if (enforcement?.noFloorSignalBidders?.length > 0) { + return enforcement.noFloorSignalBidders + } + return [] +} + /** * @summary This function takes the adUnits for the auction and update them accordingly as well as returns the rules hashmap for the auction */ export function updateAdUnitsForAuction(adUnits, floorData, auctionId) { + const noFloorSignalBiddersArray = getNoFloorSignalBidersArray(floorData) + adUnits.forEach((adUnit) => { adUnit.bids.forEach(bid => { - if (floorData.skipped) { + // check if the bidder is in the no signal list + const isNoFloorSignaled = noFloorSignalBiddersArray.some(bidderName => bidderName === bid.bidder) + if (floorData.skipped || isNoFloorSignaled) { + isNoFloorSignaled && logInfo(`noFloorSignal to ${bid.bidder}`) delete bid.getFloor; } else { bid.getFloor = getFloor; @@ -346,6 +362,7 @@ export function updateAdUnitsForAuction(adUnits, floorData, auctionId) { // information for bid and analytics adapters bid.auctionId = auctionId; bid.floorData = { + noFloorSignaled: isNoFloorSignaled, skipped: floorData.skipped, skipRate: deepAccess(floorData, 'data.skipRate') ?? floorData.skipRate, floorMin: floorData.floorMin, @@ -663,7 +680,8 @@ export function handleSetFloorsConfig(config) { 'enforceJS', enforceJS => enforceJS !== false, // defaults to true 'enforcePBS', enforcePBS => enforcePBS === true, // defaults to false 'floorDeals', floorDeals => floorDeals === true, // defaults to false - 'bidAdjustment', bidAdjustment => bidAdjustment !== false, // defaults to true + 'bidAdjustment', bidAdjustment => bidAdjustment !== false, // defaults to true, + 'noFloorSignalBidders', noFloorSignalBidders => noFloorSignalBidders || [] ]), 'additionalSchemaFields', additionalSchemaFields => typeof additionalSchemaFields === 'object' && Object.keys(additionalSchemaFields).length > 0 ? addFieldOverrides(additionalSchemaFields) : undefined, 'data', data => (data && parseFloorData(data, 'setConfig')) || undefined diff --git a/test/spec/modules/priceFloors_spec.js b/test/spec/modules/priceFloors_spec.js index f618e57e60d..a31f07fecef 100644 --- a/test/spec/modules/priceFloors_spec.js +++ b/test/spec/modules/priceFloors_spec.js @@ -773,6 +773,124 @@ describe('the price floors module', function () { floorProvider: undefined }); }); + it('should not do floor stuff if floors.data is defined by noFloorSignalBidders[]', function() { + handleSetFloorsConfig({ + ...basicFloorConfig, + data: { + ...basicFloorDataLow, + noFloorSignalBidders: ['someBidder', 'someOtherBidder'] + }}); + runStandardAuction(); + validateBidRequests(false, { + skipped: false, + floorMin: undefined, + modelVersion: 'basic model', + modelWeight: 10, + modelTimestamp: undefined, + location: 'setConfig', + skipRate: 0, + fetchStatus: undefined, + floorProvider: undefined, + noFloorSignaled: true + }) + }); + it('should not do floor stuff if floors.enforcement is defined by noFloorSignalBidders[]', function() { + handleSetFloorsConfig({ ...basicFloorConfig, + enforcement: { + enforceJS: true, + noFloorSignalBidders: ['someBidder', 'someOtherBidder'] + }, + data: basicFloorDataLow + }); + runStandardAuction(); + validateBidRequests(false, { + skipped: false, + floorMin: undefined, + modelVersion: 'basic model', + modelWeight: 10, + modelTimestamp: undefined, + location: 'setConfig', + skipRate: 0, + fetchStatus: undefined, + floorProvider: undefined, + noFloorSignaled: true + }) + }); + it('should not do floor stuff and use first floors.data.noFloorSignalBidders if its defined betwen enforcement.noFloorSignalBidders', function() { + handleSetFloorsConfig({ ...basicFloorConfig, + enforcement: { + enforceJS: true, + noFloorSignalBidders: ['someBidder'] + }, + data: { + ...basicFloorDataLow, + noFloorSignalBidders: ['someBidder', 'someOtherBidder'] + } + }); + runStandardAuction(); + validateBidRequests(false, { + skipped: false, + floorMin: undefined, + modelVersion: 'basic model', + modelWeight: 10, + modelTimestamp: undefined, + location: 'setConfig', + skipRate: 0, + fetchStatus: undefined, + floorProvider: undefined, + noFloorSignaled: true + }) + }); + it('it shouldn`t return floor stuff for bidder in the noFloorSignalBidders list', function() { + handleSetFloorsConfig({ ...basicFloorConfig, + enforcement: { + enforceJS: true, + }, + data: { + ...basicFloorDataLow, + noFloorSignalBidders: ['someBidder'] + } + }); + runStandardAuction() + const bidRequestData = exposedAdUnits[0].bids.find(bid => bid.bidder === 'someBidder'); + expect(bidRequestData.hasOwnProperty('getFloor')).to.equal(false); + sinon.assert.match(bidRequestData.floorData, { + skipped: false, + floorMin: undefined, + modelVersion: 'basic model', + modelWeight: 10, + modelTimestamp: undefined, + location: 'setConfig', + skipRate: 0, + fetchStatus: undefined, + floorProvider: undefined, + noFloorSignaled: true + }); + }) + it('it should return floor stuff if we defined wrong bidder name in data.noFloorSignalBidders', function() { + handleSetFloorsConfig({ ...basicFloorConfig, + enforcement: { + enforceJS: true, + }, + data: { + ...basicFloorDataLow, + noFloorSignalBidders: ['randomBiider'] + } + }); + runStandardAuction(); + validateBidRequests(true, { + skipped: false, + floorMin: undefined, + modelVersion: 'basic model', + modelWeight: 10, + modelTimestamp: undefined, + location: 'setConfig', + skipRate: 0, + fetchStatus: undefined, + floorProvider: undefined, + noFloorSignaled: false + }) + }); it('should use adUnit level data if not setConfig or fetch has occured', function () { handleSetFloorsConfig({ ...basicFloorConfig, From a491505377fba8028fc1ecf837f3e03c565332cd Mon Sep 17 00:00:00 2001 From: dzhang-criteo <87757739+dzhang-criteo@users.noreply.github.com> Date: Thu, 11 Jan 2024 13:10:03 +0100 Subject: [PATCH 130/246] Criteo bid adapter: add device object to backend request (#10926) --- modules/criteoBidAdapter.js | 1 + test/spec/modules/criteoBidAdapter_spec.js | 16 ++++++++++++++++ 2 files changed, 17 insertions(+) diff --git a/modules/criteoBidAdapter.js b/modules/criteoBidAdapter.js index dfc88ba653f..d91d8c56cf9 100644 --- a/modules/criteoBidAdapter.js +++ b/modules/criteoBidAdapter.js @@ -609,6 +609,7 @@ function buildCdbRequest(context, bidRequests, bidderRequest) { request.user = bidderRequest.ortb2?.user || {}; request.site = bidderRequest.ortb2?.site || {}; request.app = bidderRequest.ortb2?.app || {}; + request.device = bidderRequest.ortb2?.device || {}; if (bidderRequest && bidderRequest.ceh) { request.user.ceh = bidderRequest.ceh; } diff --git a/test/spec/modules/criteoBidAdapter_spec.js b/test/spec/modules/criteoBidAdapter_spec.js index 89d1d02b9d2..36132fe963b 100755 --- a/test/spec/modules/criteoBidAdapter_spec.js +++ b/test/spec/modules/criteoBidAdapter_spec.js @@ -1906,6 +1906,22 @@ describe('The Criteo bidding adapter', function () { const request = spec.buildRequests(bidRequests, bidderRequest); expect(request.data.slots[0].ext).to.not.have.property('ae'); }); + + it('should properly transmit device.ext.cdep if available', function () { + const bidderRequest = { + ortb2: { + device: { + ext: { + cdep: 'cookieDeprecationLabel' + } + } + } + }; + const bidRequests = []; + const request = spec.buildRequests(bidRequests, bidderRequest); + const ortbRequest = request.data; + expect(ortbRequest.device.ext.cdep).to.equal('cookieDeprecationLabel'); + }); }); describe('interpretResponse', function () { From 1c5d9255e810d1baa848628a591053118d2018e3 Mon Sep 17 00:00:00 2001 From: Demetrio Girardi Date: Thu, 11 Jan 2024 04:13:29 -0800 Subject: [PATCH 131/246] Geolocation RTD provider: make module 'vendorless' for the purposes of TCF (#10931) --- modules/geolocationRtdProvider.js | 2 ++ 1 file changed, 2 insertions(+) diff --git a/modules/geolocationRtdProvider.js b/modules/geolocationRtdProvider.js index b46a25e9246..6bfed7ee934 100644 --- a/modules/geolocationRtdProvider.js +++ b/modules/geolocationRtdProvider.js @@ -4,6 +4,7 @@ import { ACTIVITY_TRANSMIT_PRECISE_GEO } from '../src/activities/activities.js'; import { MODULE_TYPE_RTD } from '../src/activities/modules.js'; import { isActivityAllowed } from '../src/activities/rules.js'; import { activityParams } from '../src/activities/activityParams.js'; +import {VENDORLESS_GVLID} from '../src/consentHandler.js'; let permissionsAvailable = true; let geolocation; @@ -54,6 +55,7 @@ function init(moduleConfig) { } export const geolocationSubmodule = { name: 'geolocation', + gvlid: VENDORLESS_GVLID, getBidRequestData: getGeolocationData, init: init, }; From c3401f985a1d5a32010e3d3f40ee9ef45a182132 Mon Sep 17 00:00:00 2001 From: Wiem Zine El Abidine Date: Thu, 11 Jan 2024 17:15:02 +0100 Subject: [PATCH 132/246] LiveConnect Bid Adapter : add prebid version to liveConnect (#10920) * forward trackerVersion * refactoring * adjust test * use a proper lc version * adjust test * refactoring * test case * lint * package-lock * fix test * fix test --- modules/liveIntentIdSystem.js | 1 + package-lock.json | 14 +++++++------- package.json | 2 +- test/spec/modules/liveIntentIdSystem_spec.js | 10 ++++++++++ 4 files changed, 19 insertions(+), 8 deletions(-) diff --git a/modules/liveIntentIdSystem.js b/modules/liveIntentIdSystem.js index a1f156ca23d..2b4fc2476f8 100644 --- a/modules/liveIntentIdSystem.js +++ b/modules/liveIntentIdSystem.js @@ -115,6 +115,7 @@ function initializeLiveConnect(configParams) { } liveConnectConfig.wrapperName = 'prebid'; + liveConnectConfig.trackerVersion = '$prebid.version$'; liveConnectConfig.identityResolutionConfig = identityResolutionConfig; liveConnectConfig.identifiersToResolve = configParams.identifiersToResolve || []; liveConnectConfig.fireEventDelay = configParams.fireEventDelay; diff --git a/package-lock.json b/package-lock.json index 6c288e114d5..3f529edb774 100644 --- a/package-lock.json +++ b/package-lock.json @@ -22,7 +22,7 @@ "express": "^4.15.4", "fun-hooks": "^0.9.9", "just-clone": "^1.0.2", - "live-connect-js": "^6.3.2" + "live-connect-js": "^6.3.4" }, "devDependencies": { "@babel/eslint-parser": "^7.16.5", @@ -16426,9 +16426,9 @@ } }, "node_modules/live-connect-js": { - "version": "6.3.3", - "resolved": "https://registry.npmjs.org/live-connect-js/-/live-connect-js-6.3.3.tgz", - "integrity": "sha512-njpWQgoJuXhxm+XBJQvT672wR20/qROPUfUKwIXPwEtGXznJYlrUDPJDChEknBiKIo7P4jOeirAMwcZWPCN4ow==", + "version": "6.3.4", + "resolved": "https://registry.npmjs.org/live-connect-js/-/live-connect-js-6.3.4.tgz", + "integrity": "sha512-lg2XeCaj/eEbK66QGGDEdz9IdT/K3ExZ83Qo6xGVLdP5XJ33xAUCk/gds34rRTmpIwUfAnboOpyj3UoYtS3QUQ==", "dependencies": { "live-connect-common": "^v3.0.3", "tiny-hashes": "1.0.1" @@ -38231,9 +38231,9 @@ "integrity": "sha512-ZPycT04ROBUvPiksnLTunrKC3ROhBSeO99fQ+4qMIkgKwP2CvS44L7fK+0WFV4nAi+65KbzSng7JWcSlckfw8w==" }, "live-connect-js": { - "version": "6.3.3", - "resolved": "https://registry.npmjs.org/live-connect-js/-/live-connect-js-6.3.3.tgz", - "integrity": "sha512-njpWQgoJuXhxm+XBJQvT672wR20/qROPUfUKwIXPwEtGXznJYlrUDPJDChEknBiKIo7P4jOeirAMwcZWPCN4ow==", + "version": "6.3.4", + "resolved": "https://registry.npmjs.org/live-connect-js/-/live-connect-js-6.3.4.tgz", + "integrity": "sha512-lg2XeCaj/eEbK66QGGDEdz9IdT/K3ExZ83Qo6xGVLdP5XJ33xAUCk/gds34rRTmpIwUfAnboOpyj3UoYtS3QUQ==", "requires": { "live-connect-common": "^v3.0.3", "tiny-hashes": "1.0.1" diff --git a/package.json b/package.json index dda2e4e3b38..70ebfb0898c 100644 --- a/package.json +++ b/package.json @@ -134,7 +134,7 @@ "express": "^4.15.4", "fun-hooks": "^0.9.9", "just-clone": "^1.0.2", - "live-connect-js": "^6.3.2" + "live-connect-js": "^6.3.4" }, "optionalDependencies": { "fsevents": "^2.3.2" diff --git a/test/spec/modules/liveIntentIdSystem_spec.js b/test/spec/modules/liveIntentIdSystem_spec.js index 4af88e1a894..3af598c5d4e 100644 --- a/test/spec/modules/liveIntentIdSystem_spec.js +++ b/test/spec/modules/liveIntentIdSystem_spec.js @@ -94,6 +94,16 @@ describe('LiveIntentId', function() { }, 200); }); + it('should initialize LiveConnect and forward the prebid version when decode and emit an event', function(done) { + liveIntentIdSubmodule.decode({}, { params: { + ...defaultConfigParams + }}); + setTimeout(() => { + expect(server.requests[0].url).to.contain('tv=$prebid.version$') + done(); + }, 200); + }); + it('should initialize LiveConnect with the config params when decode and emit an event', function (done) { liveIntentIdSubmodule.decode({}, { params: { ...defaultConfigParams.params, From 000a0ad0fd3c8d3f23e4b63ed104dd2f46c8855a Mon Sep 17 00:00:00 2001 From: Shubham <127132399+shubhamc-ins@users.noreply.github.com> Date: Thu, 11 Jan 2024 21:52:54 +0530 Subject: [PATCH 133/246] Insticator Bid Adaptor : add support for playerSize, plcmt, vastXML, & vastURL (#10903) * enhance insticatorBidAdapter: - support playerSize for video - Support plcmt tag for video * conditionall check for playerSize * - remove hardcoded banner type for video - Support vastXml and vasturl for video bids * remove trailing spaces * refactor spaces * add unit tests --- modules/insticatorBidAdapter.js | 78 +++++++++++- .../spec/modules/insticatorBidAdapter_spec.js | 120 ++++++++++++++++++ 2 files changed, 191 insertions(+), 7 deletions(-) diff --git a/modules/insticatorBidAdapter.js b/modules/insticatorBidAdapter.js index a18c893b5fc..193337d1503 100644 --- a/modules/insticatorBidAdapter.js +++ b/modules/insticatorBidAdapter.js @@ -68,17 +68,42 @@ function buildBanner(bidRequest) { } function buildVideo(bidRequest) { - const w = deepAccess(bidRequest, 'mediaTypes.video.w'); - const h = deepAccess(bidRequest, 'mediaTypes.video.h'); + let w = deepAccess(bidRequest, 'mediaTypes.video.w'); + let h = deepAccess(bidRequest, 'mediaTypes.video.h'); const mimes = deepAccess(bidRequest, 'mediaTypes.video.mimes'); const placement = deepAccess(bidRequest, 'mediaTypes.video.placement') || 3; + const plcmt = deepAccess(bidRequest, 'mediaTypes.video.plcmt') || undefined; + const playerSize = deepAccess(bidRequest, 'mediaTypes.video.playerSize'); + + if (!w && playerSize) { + if (Array.isArray(playerSize[0])) { + w = parseInt(playerSize[0][0], 10); + } else if (typeof playerSize[0] === 'number' && !isNaN(playerSize[0])) { + w = parseInt(playerSize[0], 10); + } + } + if (!h && playerSize) { + if (Array.isArray(playerSize[0])) { + h = parseInt(playerSize[0][1], 10); + } else if (typeof playerSize[1] === 'number' && !isNaN(playerSize[1])) { + h = parseInt(playerSize[1], 10); + } + } - return { + let videoObj = { placement, mimes, w, h, } + + if (plcmt) { + videoObj = { + ...videoObj, + plcmt + } + } + return videoObj } function buildImpression(bidRequest) { @@ -235,7 +260,11 @@ function buildBid(bid, bidderRequest) { meta.advertiserDomains = bid.adomain } - return { + let mediaType = 'banner'; + if (bid.adm && bid.adm.includes(' 0 ? {meta} : {}) }; + + if (mediaType === 'video') { + bidResponse.vastXml = bid.adm; + } + + // Inticator bid adaptor only returns `vastXml` for video bids. No VastUrl or videoCache. + if (!bidResponse.vastUrl && bidResponse.vastXml) { + bidResponse.vastUrl = 'data:text/xml;charset=utf-8;base64,' + window.btoa(bidResponse.vastXml.replace(/\\"/g, '"')); + } + + return bidResponse; } function buildBidSet(seatbid, bidderRequest) { @@ -315,9 +355,26 @@ function validateVideo(bid) { return true; } + let w = deepAccess(bid, 'mediaTypes.video.w'); + let h = deepAccess(bid, 'mediaTypes.video.h'); + const playerSize = deepAccess(bid, 'mediaTypes.video.playerSize'); + if (!w && playerSize) { + if (Array.isArray(playerSize[0])) { + w = parseInt(playerSize[0][0], 10); + } else if (typeof playerSize[0] === 'number' && !isNaN(playerSize[0])) { + w = parseInt(playerSize[0], 10); + } + } + if (!h && playerSize) { + if (Array.isArray(playerSize[0])) { + h = parseInt(playerSize[0][1], 10); + } else if (typeof playerSize[1] === 'number' && !isNaN(playerSize[1])) { + h = parseInt(playerSize[1], 10); + } + } const videoSize = [ - deepAccess(bid, 'mediaTypes.video.w'), - deepAccess(bid, 'mediaTypes.video.h'), + w, + h, ]; if ( @@ -341,6 +398,13 @@ function validateVideo(bid) { return false; } + const plcmt = deepAccess(bid, 'mediaTypes.video.plcmt'); + + if (typeof plcmt !== 'undefined' && typeof plcmt !== 'number') { + logError('insticator: video plcmt is not a number'); + return false; + } + return true; } diff --git a/test/spec/modules/insticatorBidAdapter_spec.js b/test/spec/modules/insticatorBidAdapter_spec.js index e24bcb3b455..36b6dd8fbf2 100644 --- a/test/spec/modules/insticatorBidAdapter_spec.js +++ b/test/spec/modules/insticatorBidAdapter_spec.js @@ -179,6 +179,43 @@ describe('InsticatorBidAdapter', function () { } })).to.be.false; }); + + it('should return false if video plcmt is not a number', () => { + expect(spec.isBidRequestValid({ + ...bidRequest, + ...{ + mediaTypes: { + video: { + mimes: [ + 'video/mp4', + 'video/mpeg', + ], + w: 250, + h: 300, + plcmt: 'NaN', + }, + } + } + })).to.be.false; + }); + + it('should return true if playerSize is present instead of w and h', () => { + expect(spec.isBidRequestValid({ + ...bidRequest, + ...{ + mediaTypes: { + video: { + mimes: [ + 'video/mp4', + 'video/mpeg', + ], + playerSize: [250, 300], + placement: 1, + }, + } + } + })).to.be.true; + }); }); describe('buildRequests', function () { @@ -570,4 +607,87 @@ describe('InsticatorBidAdapter', function () { expect(spec.getUserSyncs({}, [response])).to.have.length(0); }) }); + + describe('Response with video Instream', function () { + const bidRequestVid = { + method: 'POST', + url: 'https://ex.ingage.tech/v1/openrtb', + options: { + contentType: 'application/json', + withCredentials: true, + }, + data: '', + bidderRequest: { + bidderRequestId: '22edbae2733bf6', + auctionId: '74f78609-a92d-4cf1-869f-1b244bbfb5d2', + timeout: 300, + bids: [ + { + bidder: 'insticator', + params: { + adUnitId: '1a2b3c4d5e6f1a2b3c4d' + }, + adUnitCode: 'adunit-code-1', + mediaTypes: { + video: { + mimes: [ + 'video/mp4', + 'video/mpeg', + ], + playerSize: [[250, 300]], + placement: 2, + plcmt: 2, + } + }, + bidId: 'bid1', + } + ] + } + }; + + const bidResponseVid = { + body: { + id: '22edbae2733bf6', + bidid: 'foo9876', + cur: 'USD', + seatbid: [ + { + seat: 'some-dsp', + bid: [ + { + ad: '', + impid: 'bid1', + crid: 'crid1', + price: 0.5, + w: 300, + h: 250, + adm: '', + exp: 60, + adomain: ['test1.com'], + ext: { + meta: { + test: 1 + } + }, + } + ], + }, + ] + } + }; + const bidRequestWithVideo = utils.deepClone(bidRequestVid); + + it('should have related properties for video Instream', function() { + const serverResponseWithInstream = utils.deepClone(bidResponseVid); + serverResponseWithInstream.body.seatbid[0].bid[0].vastXml = ''; + serverResponseWithInstream.body.seatbid[0].bid[0].mediaType = 'video'; + const bidResponse = spec.interpretResponse(serverResponseWithInstream, bidRequestWithVideo)[0]; + expect(bidResponse).to.have.any.keys('mediaType', 'vastXml', 'vastUrl'); + expect(bidResponse).to.have.property('mediaType', 'video'); + expect(bidResponse.width).to.equal(300); + expect(bidResponse.height).to.equal(250); + expect(bidResponse).to.have.property('vastXml', ''); + expect(bidResponse.vastUrl).to.match(/^data:text\/xml;charset=utf-8;base64,[\w+/=]+$/) + }); + }) }); From c2d4a88e9a422988b653fa58449a6c5c9b1be08c Mon Sep 17 00:00:00 2001 From: Yohan Boutin Date: Thu, 11 Jan 2024 17:37:08 +0100 Subject: [PATCH 134/246] Seedtag Bid Adapter : add geom to bidRequest (#10906) * add adunit geometry parameter to the bid request * lint * add unit test * add size check * use global slot for all tests * fix test when slot is not available$ --- modules/seedtagBidAdapter.js | 23 ++++++++- test/spec/modules/seedtagBidAdapter_spec.js | 54 ++++++++++++++++++++- 2 files changed, 74 insertions(+), 3 deletions(-) diff --git a/modules/seedtagBidAdapter.js b/modules/seedtagBidAdapter.js index 7ac7d048c50..51c326c2954 100644 --- a/modules/seedtagBidAdapter.js +++ b/modules/seedtagBidAdapter.js @@ -107,7 +107,6 @@ function buildBidRequest(validBidRequest) { return mediaTypesMap[pbjsType]; } ); - const bidRequest = { id: validBidRequest.bidId, transactionId: validBidRequest.ortb2Imp?.ext?.tid, @@ -115,6 +114,7 @@ function buildBidRequest(validBidRequest) { supplyTypes: mediaTypes, adUnitId: params.adUnitId, adUnitCode: validBidRequest.adUnitCode, + geom: geom(validBidRequest.adUnitCode), placement: params.placement, requestCount: validBidRequest.bidderRequestsCount || 1, // FIXME : in unit test the parameter bidderRequestsCount is undefined }; @@ -198,6 +198,27 @@ function ttfb() { return ttfb >= 0 && ttfb <= performance.now() ? ttfb : 0; } +function geom(adunitCode) { + const slot = document.getElementById(adunitCode); + if (slot) { + const scrollY = window.scrollY; + const { top, left, width, height } = slot.getBoundingClientRect(); + const viewport = { + width: window.innerWidth, + height: window.innerHeight, + }; + + return { + scrollY, + top, + left, + width, + height, + viewport, + }; + } +} + export function getTimeoutUrl(data) { let queryParams = ''; if ( diff --git a/test/spec/modules/seedtagBidAdapter_spec.js b/test/spec/modules/seedtagBidAdapter_spec.js index fb666e89f73..516c5ec933a 100644 --- a/test/spec/modules/seedtagBidAdapter_spec.js +++ b/test/spec/modules/seedtagBidAdapter_spec.js @@ -2,10 +2,24 @@ import { expect } from 'chai'; import { spec, getTimeoutUrl } from 'modules/seedtagBidAdapter.js'; import * as utils from 'src/utils.js'; import { config } from '../../../src/config.js'; +import * as mockGpt from 'test/spec/integration/faker/googletag.js'; const PUBLISHER_ID = '0000-0000-01'; const ADUNIT_ID = '000000'; +const adUnitCode = '/19968336/header-bid-tag-0' + +// create a default adunit +const slot = document.createElement('div'); +slot.id = adUnitCode; +slot.style.width = '300px' +slot.style.height = '250px' +slot.style.position = 'absolute' +slot.style.top = '10px' +slot.style.left = '20px' + +document.body.appendChild(slot); + function getSlotConfigs(mediaTypes, params) { return { params: params, @@ -25,7 +39,7 @@ function getSlotConfigs(mediaTypes, params) { tid: 'd704d006-0d6e-4a09-ad6c-179e7e758096', } }, - adUnitCode: 'adunit-code', + adUnitCode: adUnitCode, }; } @@ -46,6 +60,13 @@ const createBannerSlotConfig = (placement, mediatypes) => { }; describe('Seedtag Adapter', function () { + beforeEach(function () { + mockGpt.reset(); + }); + + afterEach(function () { + mockGpt.enable(); + }); describe('isBidRequestValid method', function () { describe('returns true', function () { describe('when banner slot config has all mandatory params', () => { @@ -277,7 +298,7 @@ describe('Seedtag Adapter', function () { expect(data.auctionStart).to.be.greaterThanOrEqual(now); expect(data.ttfb).to.be.greaterThanOrEqual(0); - expect(data.bidRequests[0].adUnitCode).to.equal('adunit-code'); + expect(data.bidRequests[0].adUnitCode).to.equal(adUnitCode); }); describe('GDPR params', function () { @@ -374,6 +395,35 @@ describe('Seedtag Adapter', function () { expect(videoBid.sizes[1][1]).to.equal(600); expect(videoBid.requestCount).to.equal(1); }); + + it('should have geom parameters if slot is available', function() { + const request = spec.buildRequests(validBidRequests, bidderRequest); + const data = JSON.parse(request.data); + const bidRequests = data.bidRequests; + const bannerBid = bidRequests[0]; + + // on some CI, the DOM is not initialized, so we need to check if the slot is available + const slot = document.getElementById(adUnitCode) + if (slot) { + expect(bannerBid).to.have.property('geom') + + const params = [['width', 300], ['height', 250], ['top', 10], ['left', 20], ['scrollY', 0]] + params.forEach(([param, value]) => { + expect(bannerBid.geom).to.have.property(param) + expect(bannerBid.geom[param]).to.be.a('number') + expect(bannerBid.geom[param]).to.be.equal(value) + }) + + expect(bannerBid.geom).to.have.property('viewport') + const viewportParams = ['width', 'height'] + viewportParams.forEach(param => { + expect(bannerBid.geom.viewport).to.have.property(param) + expect(bannerBid.geom.viewport[param]).to.be.a('number') + }) + } else { + expect(bannerBid).to.not.have.property('geom') + } + }) }); describe('COPPA param', function () { From 1837f79b27040c0a4555f236a327ab055d0793c6 Mon Sep 17 00:00:00 2001 From: kiyoshi0205 <44894523+kiyoshi0205@users.noreply.github.com> Date: Fri, 12 Jan 2024 01:40:42 +0900 Subject: [PATCH 135/246] add dsp_genieeBidAdapter (#10834) Co-authored-by: kiyoshi fujiwara --- modules/dsp_genieeBidAdapter.js | 123 +++++++++++++ modules/dsp_genieeBidAdapter.md | 39 ++++ .../spec/modules/dsp_genieeBidAdapter_spec.js | 173 ++++++++++++++++++ 3 files changed, 335 insertions(+) create mode 100644 modules/dsp_genieeBidAdapter.js create mode 100644 modules/dsp_genieeBidAdapter.md create mode 100644 test/spec/modules/dsp_genieeBidAdapter_spec.js diff --git a/modules/dsp_genieeBidAdapter.js b/modules/dsp_genieeBidAdapter.js new file mode 100644 index 00000000000..517d6a93177 --- /dev/null +++ b/modules/dsp_genieeBidAdapter.js @@ -0,0 +1,123 @@ +import { registerBidder } from '../src/adapters/bidderFactory.js'; +import { BANNER } from '../src/mediaTypes.js'; +import { ortbConverter } from '../libraries/ortbConverter/converter.js'; +import { deepAccess, deepSetValue } from '../src/utils.js'; +import { config } from '../src/config.js'; +const BIDDER_CODE = 'dsp_geniee'; +const ENDPOINT_URL = 'https://rt.gsspat.jp/prebid_auction'; +const ENDPOINT_URL_UNCOMFORTABLE = 'https://rt.gsspat.jp/prebid_uncomfortable'; +const ENDPOINT_USERSYNC = 'https://rt.gsspat.jp/prebid_cs'; +const VALID_CURRENCIES = ['USD', 'JPY']; +const converter = ortbConverter({ + context: { ttl: 300, netRevenue: true }, + // set optional parameters + imp(buildImp, bidRequest, context) { + const imp = buildImp(bidRequest, context); + deepSetValue(imp, 'ext', bidRequest.params); + return imp; + } +}); + +function USPConsent(consent) { + return typeof consent === 'string' && consent[0] === '1' && consent.toUpperCase()[2] === 'Y'; +} + +function invalidCurrency(currency) { + return typeof currency === 'string' && VALID_CURRENCIES.indexOf(currency.toUpperCase()) === -1; +} + +function hasTest(imp) { + if (typeof imp !== 'object') { + return false; + } + for (let i = 0; i < imp.length; i++) { + if (deepAccess(imp[i], 'ext.test') === 1) { + return true; + } + } + return false; +} + +export const spec = { + code: BIDDER_CODE, + supportedMediaTypes: [BANNER], + /** + * Determines whether or not the given bid request is valid. + * + * @param {BidRequest} - The bid params to validate. + * @return boolean True if this is a valid bid, and false otherwise. + */ + isBidRequestValid: function (_) { + return true; + }, + /** + * Make a server request from the list of BidRequests. + * + * @param {validBidRequests[]} - an array of bids + * @param {bidderRequest} - the master bidRequest object + * @return ServerRequest Info describing the request to the server. + */ + buildRequests: function (validBidRequests, bidderRequest) { + if (deepAccess(bidderRequest, 'gdprConsent.gdprApplies') || // gdpr + USPConsent(bidderRequest.uspConsent) || // usp + config.getConfig('coppa') || // coppa + invalidCurrency(config.getConfig('currency.adServerCurrency')) // currency validation + ) { + return { + method: 'GET', + url: ENDPOINT_URL_UNCOMFORTABLE + }; + } + + const payload = converter.toORTB({ validBidRequests, bidderRequest }); + + if (hasTest(deepAccess(payload, 'imp'))) { + deepSetValue(payload, 'test', 1); + } + + deepSetValue(payload, 'at', 1); // first price auction only + + 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} bidRequest - the master bidRequest object + * @return {bids} - An array of bids which were nested inside the server. + */ + interpretResponse: function (serverResponse, bidRequest) { + if (!serverResponse.body) { // empty response (no bids) + return []; + } + const bids = converter.fromORTB({ response: serverResponse.body, request: bidRequest.data }).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: function (syncOptions, serverResponses, gdprConsent, uspConsent) { + const syncs = []; + // gdpr & usp + if (deepAccess(gdprConsent, 'gdprApplies') || USPConsent(uspConsent)) { + return syncs; + } + if (syncOptions.pixelEnabled) { + syncs.push({ + type: 'image', + url: ENDPOINT_USERSYNC + }); + } + return syncs; + } +}; +registerBidder(spec); diff --git a/modules/dsp_genieeBidAdapter.md b/modules/dsp_genieeBidAdapter.md new file mode 100644 index 00000000000..d51d66884af --- /dev/null +++ b/modules/dsp_genieeBidAdapter.md @@ -0,0 +1,39 @@ +# Overview + +```markdown +Module Name: Geniee Bid Adapter +Module Type: Bidder Adapter +Maintainer: dsp_back@geniee.co.jp +``` + +# Description +This is [Geniee](https://geniee.co.jp) Bidder Adapter for Prebid.js. + +Please contact us before using the adapter. + +We will provide ads when satisfy the following conditions: + +- There are a certain number bid requests by zone +- The request is a Banner ad +- Payment is possible in Japanese yen or US dollars +- The request is not for GDPR or COPPA users + +Thus, even if the following test, it will be no bids if the request does not reach a certain requests. + +# Test AdUnits +```javascript +var adUnits={ + code: 'geniee-test-ad', + bids: [{ + bidder: 'dsp_geniee', + params: { + test: 1, + } + }], + mediaTypes: { + banner: { + sizes: [[300, 250]] + } + } +}; +``` diff --git a/test/spec/modules/dsp_genieeBidAdapter_spec.js b/test/spec/modules/dsp_genieeBidAdapter_spec.js new file mode 100644 index 00000000000..94ec1011fbf --- /dev/null +++ b/test/spec/modules/dsp_genieeBidAdapter_spec.js @@ -0,0 +1,173 @@ +import { expect } from 'chai'; +import { spec } from 'modules/dsp_genieeBidAdapter.js'; +import { config } from 'src/config'; + +describe('Geniee adapter tests', () => { + const validBidderRequest = { + code: 'sample_request', + bids: [{ + bidId: 'bid-id', + bidder: 'dsp_geniee', + params: { + test: 1 + } + }], + gdprConsent: { + gdprApplies: false + }, + uspConsent: '1YNY' + }; + + describe('isBidRequestValid function test', () => { + it('valid', () => { + expect(spec.isBidRequestValid(validBidderRequest.bids[0])).equal(true); + }); + }); + describe('buildRequests function test', () => { + it('auction', () => { + const request = spec.buildRequests(validBidderRequest.bids, validBidderRequest); + const auction_id = request.data.id; + expect(request).deep.equal({ + method: 'POST', + url: 'https://rt.gsspat.jp/prebid_auction', + data: { + at: 1, + id: auction_id, + imp: [ + { + ext: { + test: 1 + }, + id: 'bid-id' + } + ], + test: 1 + }, + }); + }); + it('uncomfortable (gdpr)', () => { + validBidderRequest.gdprConsent.gdprApplies = true; + const request = spec.buildRequests(validBidderRequest.bids, validBidderRequest); + expect(request).deep.equal({ + method: 'GET', + url: 'https://rt.gsspat.jp/prebid_uncomfortable', + }); + validBidderRequest.gdprConsent.gdprApplies = false; + }); + it('uncomfortable (usp)', () => { + validBidderRequest.uspConsent = '1YYY'; + const request = spec.buildRequests(validBidderRequest.bids, validBidderRequest); + expect(request).deep.equal({ + method: 'GET', + url: 'https://rt.gsspat.jp/prebid_uncomfortable', + }); + validBidderRequest.uspConsent = '1YNY'; + }); + it('uncomfortable (coppa)', () => { + config.setConfig({ coppa: true }); + const request = spec.buildRequests(validBidderRequest.bids, validBidderRequest); + expect(request).deep.equal({ + method: 'GET', + url: 'https://rt.gsspat.jp/prebid_uncomfortable', + }); + config.resetConfig(); + }); + it('uncomfortable (currency)', () => { + config.setConfig({ currency: { adServerCurrency: 'TWD' } }); + const request = spec.buildRequests(validBidderRequest.bids, validBidderRequest); + expect(request).deep.equal({ + method: 'GET', + url: 'https://rt.gsspat.jp/prebid_uncomfortable', + }); + config.resetConfig(); + }); + }); + describe('interpretResponse function test', () => { + it('sample bid', () => { + const request = spec.buildRequests(validBidderRequest.bids, validBidderRequest); + const auction_id = request.data.id; + const adm = "\n"; + const serverResponse = { + body: { + id: auction_id, + cur: 'JPY', + seatbid: [{ + bid: [{ + id: '7b77235d599e06d289e58ddfa9390443e22d7071', + impid: 'bid-id', + price: 0.6666000000000001, + adid: '8405715', + adm: adm, + adomain: ['geniee.co.jp'], + iurl: 'http://img.gsspat.jp/e/068c8e1eafbf0cb6ac1ee95c36152bd2/04f4bd4e6b71f978d343d84ecede3877.png', + cid: '8405715', + crid: '1383823', + cat: ['IAB1'], + w: 300, + h: 250, + mtype: 1 + }] + }] + } + }; + const bids = spec.interpretResponse(serverResponse, request); + expect(bids).deep.equal([{ + ad: adm, + cpm: 0.6666000000000001, + creativeId: '1383823', + creative_id: '1383823', + height: 250, + width: 300, + currency: 'JPY', + mediaType: 'banner', + meta: { + advertiserDomains: ['geniee.co.jp'] + }, + netRevenue: true, + requestId: 'bid-id', + seatBidId: '7b77235d599e06d289e58ddfa9390443e22d7071', + ttl: 300 + }]); + }); + it('no bid', () => { + const serverResponse = {}; + const bids = spec.interpretResponse(serverResponse, validBidderRequest); + expect(bids).deep.equal([]); + }); + }); + describe('getUserSyncs function test', () => { + it('sync enabled', () => { + const syncOptions = { + iframeEnabled: true, + pixelEnabled: true + }; + const serverResponses = []; + const syncs = spec.getUserSyncs(syncOptions, serverResponses); + expect(syncs).deep.equal([{ + type: 'image', + url: 'https://rt.gsspat.jp/prebid_cs' + }]); + }); + it('sync disabled (option false)', () => { + const syncOptions = { + iframeEnabled: false, + pixelEnabled: false + }; + const serverResponses = []; + const syncs = spec.getUserSyncs(syncOptions, serverResponses); + expect(syncs).deep.equal([]); + }); + it('sync disabled (gdpr)', () => { + const syncOptions = { + iframeEnabled: true, + pixelEnabled: true + }; + const serverResponses = []; + const gdprConsent = { + gdprApplies: true + }; + const syncs = spec.getUserSyncs(syncOptions, serverResponses, gdprConsent); + expect(syncs).deep.equal([]); + }); + }); +}); From 07f480966a1c81b93f81532de0aa95fbbe7c003b Mon Sep 17 00:00:00 2001 From: sebastienrufiange <131205907+sebastienrufiange@users.noreply.github.com> Date: Thu, 11 Jan 2024 11:44:31 -0500 Subject: [PATCH 136/246] Contxtful RTD Provider: Initial Release (#10550) * feat: added contxtfulRtdProvider * fix: removed id in query param * fix: googletag * doc: typo * fix: added contxtful in adloader * doc: extra line * fix: added connector config option --- .../gpt/contxtfulRtdProvider_example.html | 91 ++++++++ modules/contxtfulRtdProvider.js | 150 +++++++++++++ modules/contxtfulRtdProvider.md | 65 ++++++ src/adloader.js | 3 +- .../spec/modules/contxtfulRtdProvider_spec.js | 200 ++++++++++++++++++ 5 files changed, 508 insertions(+), 1 deletion(-) create mode 100644 integrationExamples/gpt/contxtfulRtdProvider_example.html create mode 100644 modules/contxtfulRtdProvider.js create mode 100644 modules/contxtfulRtdProvider.md create mode 100644 test/spec/modules/contxtfulRtdProvider_spec.js diff --git a/integrationExamples/gpt/contxtfulRtdProvider_example.html b/integrationExamples/gpt/contxtfulRtdProvider_example.html new file mode 100644 index 00000000000..29284de81a2 --- /dev/null +++ b/integrationExamples/gpt/contxtfulRtdProvider_example.html @@ -0,0 +1,91 @@ + + + + + + + + + +

Contxtful RTD Provider

+
+
+ + + \ No newline at end of file diff --git a/modules/contxtfulRtdProvider.js b/modules/contxtfulRtdProvider.js new file mode 100644 index 00000000000..69ff3c85079 --- /dev/null +++ b/modules/contxtfulRtdProvider.js @@ -0,0 +1,150 @@ +/** + * Contxtful Technologies Inc + * This RTD module provides receptivity feature that can be accessed using the + * getReceptivity() function. The value returned by this function enriches the ad-units + * that are passed within the `getTargetingData` functions and GAM. + */ + +import { submodule } from '../src/hook.js'; +import { + logInfo, + logError, + isStr, + isEmptyStr, + buildUrl, +} from '../src/utils.js'; +import { loadExternalScript } from '../src/adloader.js'; + +const MODULE_NAME = 'contxtful'; +const MODULE = `${MODULE_NAME}RtdProvider`; + +const CONTXTFUL_RECEPTIVITY_DOMAIN = 'api.receptivity.io'; + +let initialReceptivity = null; +let contxtfulModule = null; + +/** + * Init function used to start sub module + * @param { { params: { version: String, customer: String, hostname: String } } } config + * @return { Boolean } + */ +function init(config) { + logInfo(MODULE, 'init', config); + initialReceptivity = null; + contxtfulModule = null; + + try { + const {version, customer, hostname} = extractParameters(config); + initCustomer(version, customer, hostname); + return true; + } catch (error) { + logError(MODULE, error); + return false; + } +} + +/** + * Extract required configuration for the sub module. + * validate that all required configuration are present and are valid. + * Throws an error if any config is missing of invalid. + * @param { { params: { version: String, customer: String, hostname: String } } } config + * @return { { version: String, customer: String, hostname: String } } + * @throws params.{name} should be a non-empty string + */ +function extractParameters(config) { + const version = config?.params?.version; + if (!isStr(version) || isEmptyStr(version)) { + throw Error(`${MODULE}: params.version should be a non-empty string`); + } + + const customer = config?.params?.customer; + if (!isStr(customer) || isEmptyStr(customer)) { + throw Error(`${MODULE}: params.customer should be a non-empty string`); + } + + const hostname = config?.params?.hostname || CONTXTFUL_RECEPTIVITY_DOMAIN; + + return {version, customer, hostname}; +} + +/** + * Initialize sub module for a customer. + * This will load the external resources for the sub module. + * @param { String } version + * @param { String } customer + * @param { String } hostname + */ +function initCustomer(version, customer, hostname) { + const CONNECTOR_URL = buildUrl({ + protocol: 'https', + host: hostname, + pathname: `/${version}/prebid/${customer}/connector/p.js`, + }); + + const externalScript = loadExternalScript(CONNECTOR_URL, MODULE_NAME); + addExternalScriptEventListener(externalScript); +} + +/** + * Add event listener to the script tag for the expected events from the external script. + * @param { HTMLScriptElement } script + */ +function addExternalScriptEventListener(script) { + if (!script) { + return; + } + + script.addEventListener('initialReceptivity', ({ detail }) => { + let receptivityState = detail?.ReceptivityState; + if (isStr(receptivityState) && !isEmptyStr(receptivityState)) { + initialReceptivity = receptivityState; + } + }); + + script.addEventListener('rxEngineIsReady', ({ detail: api }) => { + contxtfulModule = api; + }); +} + +/** + * Return current receptivity. + * @return { { ReceptivityState: String } } + */ +function getReceptivity() { + return { + ReceptivityState: contxtfulModule?.GetReceptivity()?.ReceptivityState || initialReceptivity + }; +} + +/** + * Set targeting data for ad server + * @param { [String] } adUnits + * @param {*} _config + * @param {*} _userConsent +* @return {{ code: { ReceptivityState: String } }} + */ +function getTargetingData(adUnits, _config, _userConsent) { + logInfo(MODULE, 'getTargetingData'); + if (!adUnits) { + return {}; + } + + const receptivity = getReceptivity(); + if (!receptivity?.ReceptivityState) { + return {}; + } + + return adUnits.reduce((targets, code) => { + targets[code] = receptivity; + return targets; + }, {}); +} + +export const contxtfulSubmodule = { + name: MODULE_NAME, + init, + extractParameters, + getTargetingData, +}; + +submodule('realTimeData', contxtfulSubmodule); diff --git a/modules/contxtfulRtdProvider.md b/modules/contxtfulRtdProvider.md new file mode 100644 index 00000000000..dfefca2067a --- /dev/null +++ b/modules/contxtfulRtdProvider.md @@ -0,0 +1,65 @@ +# Overview + +**Module Name:** Contxtful RTD Provider +**Module Type:** RTD Provider +**Maintainer:** [prebid@contxtful.com](mailto:prebid@contxtful.com) + +# Description + +The Contxtful RTD module offers a unique feature—Receptivity. Receptivity is an efficiency metric, enabling the qualification of any instant in a session in real time based on attention. The core idea is straightforward: the likelihood of an ad’s success increases when it grabs attention and is presented in the right context at the right time. + +To utilize this module, you need to register for an account with [Contxtful](https://contxtful.com). For inquiries, please contact [prebid@contxtful.com](mailto:prebid@contxtful.com). + +# Configuration + +## Build Instructions + +To incorporate this module into your `prebid.js`, compile the module using the following command: + +```sh +gulp build --modules=contxtfulRtdProvider, +``` + +## Module Configuration + +Configure the `contxtfulRtdProvider` by passing the required settings through the `setConfig` function in `prebid.js`. + +```js +import pbjs from 'prebid.js'; + +pbjs.setConfig({ + "realTimeData": { + "auctionDelay": 1000, + "dataProviders": [ + { + "name": "contxtful", + "waitForIt": true, + "params": { + "version": "", + "customer": "" + } + } + ] + } +}); +``` + +### Configuration Parameters + +| Name | Type | Scope | Description | +|------------|----------|----------|-------------------------------------------| +| `version` | `string` | Required | Specifies the API version of Contxtful. | +| `customer` | `string` | Required | Your unique customer identifier. | + +# Usage + +The `contxtfulRtdProvider` module loads an external JavaScript file and authenticates with Contxtful APIs. The `getTargetingData` function then adds a `ReceptivityState` to each ad slot, which can have one of two values: `Receptive` or `NonReceptive`. + +```json +{ + "adUnitCode1": { "ReceptivityState": "Receptive" }, + "adUnitCode2": { "ReceptivityState": "NonReceptive" } +} +``` + +This module also integrates seamlessly with Google Ad Manager, ensuring that the `ReceptivityState` is available as early as possible in the ad serving process. \ No newline at end of file diff --git a/src/adloader.js b/src/adloader.js index d1dca9627d8..664fd03d673 100644 --- a/src/adloader.js +++ b/src/adloader.js @@ -29,7 +29,8 @@ const _approvedLoadExternalJSList = [ 'geoedge', 'mediafilter', 'qortex', - 'dynamicAdBoost' + 'dynamicAdBoost', + 'contxtful' ] /** diff --git a/test/spec/modules/contxtfulRtdProvider_spec.js b/test/spec/modules/contxtfulRtdProvider_spec.js new file mode 100644 index 00000000000..541c0e6e6dd --- /dev/null +++ b/test/spec/modules/contxtfulRtdProvider_spec.js @@ -0,0 +1,200 @@ +import { contxtfulSubmodule } from '../../../modules/contxtfulRtdProvider.js'; +import { expect } from 'chai'; +import { loadExternalScriptStub } from 'test/mocks/adloaderStub.js'; + +import * as events from '../../../src/events'; + +const _ = null; +const VERSION = 'v1'; +const CUSTOMER = 'CUSTOMER'; +const CONTXTFUL_CONNECTOR_ENDPOINT = `https://api.receptivity.io/${VERSION}/prebid/${CUSTOMER}/connector/p.js`; +const INITIAL_RECEPTIVITY = { ReceptivityState: 'INITIAL_RECEPTIVITY' }; +const INITIAL_RECEPTIVITY_EVENT = new CustomEvent('initialReceptivity', { detail: INITIAL_RECEPTIVITY }); + +const CONTXTFUL_API = { GetReceptivity: sinon.stub() } +const RX_ENGINE_IS_READY_EVENT = new CustomEvent('rxEngineIsReady', {detail: CONTXTFUL_API}); + +function buildInitConfig(version, customer) { + return { + name: 'contxtful', + params: { + version, + customer, + }, + }; +} + +describe('contxtfulRtdProvider', function () { + let sandbox = sinon.sandbox.create(); + let loadExternalScriptTag; + let eventsEmitSpy; + + beforeEach(() => { + loadExternalScriptTag = document.createElement('script'); + loadExternalScriptStub.callsFake((_url, _moduleName) => loadExternalScriptTag); + + CONTXTFUL_API.GetReceptivity.reset(); + + eventsEmitSpy = sandbox.spy(events, ['emit']); + }); + + afterEach(function () { + delete window.Contxtful; + sandbox.restore(); + }); + + describe('extractParameters with invalid configuration', () => { + const { + params: { customer, version }, + } = buildInitConfig(VERSION, CUSTOMER); + const theories = [ + [ + null, + 'params.version should be a non-empty string', + 'null object for config', + ], + [ + {}, + 'params.version should be a non-empty string', + 'empty object for config', + ], + [ + { customer }, + 'params.version should be a non-empty string', + 'customer only in config', + ], + [ + { version }, + 'params.customer should be a non-empty string', + 'version only in config', + ], + [ + { customer, version: '' }, + 'params.version should be a non-empty string', + 'empty string for version', + ], + [ + { customer: '', version }, + 'params.customer should be a non-empty string', + 'empty string for customer', + ], + [ + { customer: '', version: '' }, + 'params.version should be a non-empty string', + 'empty string for version & customer', + ], + ]; + + theories.forEach(([params, expectedErrorMessage, _description]) => { + const config = { name: 'contxtful', params }; + it('throws the expected error', () => { + expect(() => contxtfulSubmodule.extractParameters(config)).to.throw( + expectedErrorMessage + ); + }); + }); + }); + + describe('initialization with invalid config', function () { + it('returns false', () => { + expect(contxtfulSubmodule.init({})).to.be.false; + }); + }); + + describe('initialization with valid config', function () { + it('returns true when initializing', () => { + const config = buildInitConfig(VERSION, CUSTOMER); + expect(contxtfulSubmodule.init(config)).to.be.true; + }); + + it('loads contxtful module script asynchronously', (done) => { + contxtfulSubmodule.init(buildInitConfig(VERSION, CUSTOMER)); + + setTimeout(() => { + expect(loadExternalScriptStub.calledOnce).to.be.true; + expect(loadExternalScriptStub.args[0][0]).to.equal( + CONTXTFUL_CONNECTOR_ENDPOINT + ); + done(); + }, 10); + }); + }); + + describe('load external script return falsy', function () { + it('returns true when initializing', () => { + loadExternalScriptStub.callsFake(() => {}); + const config = buildInitConfig(VERSION, CUSTOMER); + expect(contxtfulSubmodule.init(config)).to.be.true; + }); + }); + + describe('rxEngine from external script', function () { + it('use rxEngine api to get receptivity', () => { + contxtfulSubmodule.init(buildInitConfig(VERSION, CUSTOMER)); + loadExternalScriptTag.dispatchEvent(RX_ENGINE_IS_READY_EVENT); + + contxtfulSubmodule.getTargetingData(['ad-slot']); + + expect(CONTXTFUL_API.GetReceptivity.calledOnce).to.be.true; + }); + }); + + describe('initial receptivity is not dispatched', function () { + it('does not initialize receptivity value', () => { + contxtfulSubmodule.init(buildInitConfig(VERSION, CUSTOMER)); + + let targetingData = contxtfulSubmodule.getTargetingData(['ad-slot']); + expect(targetingData).to.deep.equal({}); + }); + }); + + describe('initial receptivity is invalid', function () { + const theories = [ + [new Event('initialReceptivity'), 'event without details'], + [new CustomEvent('initialReceptivity', { }), 'custom event without details'], + [new CustomEvent('initialReceptivity', { detail: {} }), 'custom event with invalid details'], + [new CustomEvent('initialReceptivity', { detail: { ReceptivityState: '' } }), 'custom event with details without ReceptivityState'], + ]; + + theories.forEach(([initialReceptivityEvent, _description]) => { + it('does not initialize receptivity value', () => { + contxtfulSubmodule.init(buildInitConfig(VERSION, CUSTOMER)); + loadExternalScriptTag.dispatchEvent(initialReceptivityEvent); + + let targetingData = contxtfulSubmodule.getTargetingData(['ad-slot']); + expect(targetingData).to.deep.equal({}); + }); + }) + }); + + describe('getTargetingData', function () { + const theories = [ + [undefined, {}, 'undefined ad-slots'], + [[], {}, 'empty ad-slots'], + [ + ['ad-slot'], + { 'ad-slot': { ReceptivityState: 'INITIAL_RECEPTIVITY' } }, + 'single ad-slot', + ], + [ + ['ad-slot-1', 'ad-slot-2'], + { + 'ad-slot-1': { ReceptivityState: 'INITIAL_RECEPTIVITY' }, + 'ad-slot-2': { ReceptivityState: 'INITIAL_RECEPTIVITY' }, + }, + 'many ad-slots', + ], + ]; + + theories.forEach(([adUnits, expected, _description]) => { + it('adds "ReceptivityState" to the adUnits', function () { + contxtfulSubmodule.init(buildInitConfig(VERSION, CUSTOMER)); + loadExternalScriptTag.dispatchEvent(INITIAL_RECEPTIVITY_EVENT); + + expect(contxtfulSubmodule.getTargetingData(adUnits)).to.deep.equal( + expected + ); + }); + }); + }); +}); From b8c6918a47a4869e5d9fb0ac25c350335b9bf28a Mon Sep 17 00:00:00 2001 From: "Prebid.js automated release" Date: Thu, 11 Jan 2024 17:33:42 +0000 Subject: [PATCH 137/246] Prebid 8.31.0 release --- package-lock.json | 2 +- package.json | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/package-lock.json b/package-lock.json index 3f529edb774..af255ec674c 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,6 +1,6 @@ { "name": "prebid.js", - "version": "8.31.0-pre", + "version": "8.31.0", "lockfileVersion": 2, "requires": true, "packages": { diff --git a/package.json b/package.json index 70ebfb0898c..b2d9cabcad5 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "prebid.js", - "version": "8.31.0-pre", + "version": "8.31.0", "description": "Header Bidding Management Library", "main": "src/prebid.js", "scripts": { From 759d5ac4f0046cb3a9f940c5a677401e871adbae Mon Sep 17 00:00:00 2001 From: "Prebid.js automated release" Date: Thu, 11 Jan 2024 17:33:43 +0000 Subject: [PATCH 138/246] Increment version to 8.32.0-pre --- package-lock.json | 2 +- package.json | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/package-lock.json b/package-lock.json index af255ec674c..7114a39d509 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,6 +1,6 @@ { "name": "prebid.js", - "version": "8.31.0", + "version": "8.32.0-pre", "lockfileVersion": 2, "requires": true, "packages": { diff --git a/package.json b/package.json index b2d9cabcad5..82db5e9f7c2 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "prebid.js", - "version": "8.31.0", + "version": "8.32.0-pre", "description": "Header Bidding Management Library", "main": "src/prebid.js", "scripts": { From 672b53579b200f8497f82c3cb954a143f669b200 Mon Sep 17 00:00:00 2001 From: charlie0228 Date: Fri, 12 Jan 2024 23:32:14 +0800 Subject: [PATCH 139/246] Ad2iction Bid Adapter: initial release (#10877) * Import the project * Update version * fix: typo * refactor: rewrite * feat: add pre-defined function & add logger * feat: add test for bid adapter * feat: add readme for ad2iction bid adaptor * feat: remove unneeded callback event * feat: lint & add missing test * feat: remove debugger flag * feat: refactor & new feature for adapter * feat: update Maintainer & Description info --------- Co-authored-by: Richard Lee <14349+dlackty@users.noreply.github.com> Co-authored-by: Charlie --- modules/ad2ictionBidAdapter.js | 59 +++++ modules/ad2ictionBidAdapter.md | 30 +++ test/spec/modules/ad2ictionBidAdapter_spec.js | 223 ++++++++++++++++++ 3 files changed, 312 insertions(+) create mode 100644 modules/ad2ictionBidAdapter.js create mode 100644 modules/ad2ictionBidAdapter.md create mode 100644 test/spec/modules/ad2ictionBidAdapter_spec.js diff --git a/modules/ad2ictionBidAdapter.js b/modules/ad2ictionBidAdapter.js new file mode 100644 index 00000000000..0f7cea45d14 --- /dev/null +++ b/modules/ad2ictionBidAdapter.js @@ -0,0 +1,59 @@ +import { registerBidder } from '../src/adapters/bidderFactory.js'; +import { BANNER } from '../src/mediaTypes.js'; +import { getStorageManager } from '../src/storageManager.js'; + +export const BIDDER_CODE = 'ad2iction'; +export const SUPPORTED_AD_TYPES = [BANNER]; +export const API_ENDPOINT = 'https://ads.ad2iction.com/html/prebid/'; +export const API_VERSION_NUMBER = 3; +export const COOKIE_NAME = 'ad2udid'; + +export const storage = getStorageManager({ bidderCode: BIDDER_CODE }); + +export const spec = { + code: BIDDER_CODE, + aliases: ['ad2'], + supportedMediaTypes: SUPPORTED_AD_TYPES, + isBidRequestValid: (bid) => { + return !!bid.params.id && typeof bid.params.id === 'string'; + }, + buildRequests: (validBidRequests, bidderRequest) => { + const ids = validBidRequests.map((bid) => { + return { bannerId: bid.params.id, bidId: bid.bidId }; + }); + + const options = { + contentType: 'application/json', + withCredentials: false, + }; + + const udid = storage.cookiesAreEnabled() && storage.getCookie(COOKIE_NAME); + + const data = { + ids: JSON.stringify(ids), + ortb2: bidderRequest.ortb2, + refererInfo: bidderRequest.refererInfo, + v: API_VERSION_NUMBER, + udid: udid || '', + _: Math.round(new Date().getTime()), + }; + + return { + method: 'POST', + url: API_ENDPOINT, + data, + options, + }; + }, + interpretResponse: (serverResponse, bidRequest) => { + if (!Array.isArray(serverResponse.body)) { + return []; + } + + const bidResponses = serverResponse.body; + + return bidResponses; + }, +}; + +registerBidder(spec); diff --git a/modules/ad2ictionBidAdapter.md b/modules/ad2ictionBidAdapter.md new file mode 100644 index 00000000000..47e355aa795 --- /dev/null +++ b/modules/ad2ictionBidAdapter.md @@ -0,0 +1,30 @@ +# Overview + +**Module Name**: Ad2iction Bidder Adapter +**Module Type**: Bidder Adapter +**Maintainer**: prebid@ad2iction.com + +# Description + +The Ad2iction Bidding adapter requires setup before beginning. Please contact us on https://www.ad2iction.com. + +# Sample Ad Unit Config +``` +var adUnits = [ + // Banner adUnit + { + code: 'banner-div', + mediaTypes: { + banner: { + sizes: [[300, 250], [336, 280]] + } + }, + bids: [{ + bidder: 'ad2iction', + params: { + id: 'accepted-uuid' + } + }] + } +]; +``` diff --git a/test/spec/modules/ad2ictionBidAdapter_spec.js b/test/spec/modules/ad2ictionBidAdapter_spec.js new file mode 100644 index 00000000000..99800c6dd01 --- /dev/null +++ b/test/spec/modules/ad2ictionBidAdapter_spec.js @@ -0,0 +1,223 @@ +import { expect } from 'chai'; +import { + spec, + API_ENDPOINT, + API_VERSION_NUMBER, +} from 'modules/ad2ictionBidAdapter.js'; +import { newBidder } from 'src/adapters/bidderFactory.js'; + +describe('ad2ictionBidAdapter', 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: 'ad2iction', + params: { id: '11ab384c-e936-11ed-a6a7-f23c9173ed43' }, + mediaTypes: { + banner: { + sizes: [ + [300, 250], + [336, 280], + ], + }, + }, + adUnitCode: 'adunit-code', + sizes: [ + [300, 250], + [336, 280], + ], + bidId: '2a7a3b48778a1b', + bidderRequestId: '1e6509293abe6b', + }; + + it('should return true when required params found', function () { + expect(spec.isBidRequestValid(bid)).to.equal(true); + }); + + it('should return false when params id is not valid (letters)', function () { + const mockBid = { + ...bid, + params: { id: 1234 }, + }; + + expect(spec.isBidRequestValid(mockBid)).to.equal(false); + }); + + it('should return false when params id is not exist', function () { + const mockBid = { + ...bid, + }; + delete mockBid.params.id; + + expect(spec.isBidRequestValid(mockBid)).to.equal(false); + }); + }); + + describe('buildRequests', function () { + const mockValidBidRequests = [ + { + bidder: 'ad2iction', + params: { id: '11ab384c-e936-11ed-a6a7-f23c9173ed43' }, + adUnitCode: 'adunit-code', + sizes: [ + [300, 250], + [336, 280], + ], + bidId: '57ffc0667379e1', + bidderRequestId: '4ddea14478a651', + }, + ]; + + const mockBidderRequest = { + bidderCode: 'ad2iction', + bidderRequestId: '4ddea14478a651', + bids: [ + { + bidder: 'ad2iction', + params: { id: '11ab384c-e936-11ed-a6a7-f23c9173ed43' }, + adUnitCode: 'adunit-code', + transactionId: null, + sizes: [ + [300, 250], + [336, 280], + ], + bidId: '57ffc0667379e1', + bidderRequestId: '4ddea14478a651', + }, + ], + timeout: 1200, + refererInfo: { + ref: 'https://example.com/referer.html', + }, + ortb2: { + source: {}, + site: { + ref: 'https://example.com/referer.html', + }, + device: { + w: 390, + h: 844, + language: 'zh', + }, + }, + start: 1702526505498, + }; + + it('should send bid request to API_ENDPOINT via POST', function () { + const request = spec.buildRequests( + mockValidBidRequests, + mockBidderRequest + ); + + expect(request.url).to.equal(API_ENDPOINT); + expect(request.method).to.equal('POST'); + }); + + it('should send bid request with API version', function () { + const request = spec.buildRequests( + mockValidBidRequests, + mockBidderRequest + ); + + expect(request.data.v).to.equal(API_VERSION_NUMBER); + }); + + it('should send bid request with dada fields', function () { + const request = spec.buildRequests( + mockValidBidRequests, + mockBidderRequest + ); + + expect(request.data).to.include.all.keys('udid', '_'); + expect(request.data).to.have.property('refererInfo'); + expect(request.data).to.have.property('ortb2'); + }); + }); + + describe('interpretResponse', function () { + it('should return an empty array to indicate no valid bids', function () { + const mockServerResponse = {}; + + const bidResponses = spec.interpretResponse(mockServerResponse); + + expect(bidResponses).is.an('array').that.is.empty; + }); + + it('should return a valid bid response', function () { + const MOCK_AD_DOM = "
" + const mockServerResponse = { + body: [ + { + requestId: '23a3d87fb6bde9', + cpm: 1.61, + currency: 'USD', + width: '336', + height: '280', + creativeId: '46271', + netRevenue: 'false', + ad: MOCK_AD_DOM, + meta: { + advertiserDomains: [''], + }, + ttl: 360, + }, + { + requestId: '3ce3efc40c890b', + cpm: 1.61, + currency: 'USD', + width: '336', + height: '280', + creativeId: '46271', + netRevenue: 'false', + ad: MOCK_AD_DOM, + meta: { + advertiserDomains: [''], + }, + ttl: 360, + }, + ], + }; + + const exceptServerResponse = [ + { + requestId: '23a3d87fb6bde9', + cpm: 1.61, + currency: 'USD', + width: '336', + height: '280', + creativeId: '46271', + netRevenue: 'false', + ad: MOCK_AD_DOM, + meta: { + advertiserDomains: [''], + }, + ttl: 360, + }, + { + requestId: '3ce3efc40c890b', + cpm: 1.61, + currency: 'USD', + width: '336', + height: '280', + creativeId: '46271', + netRevenue: 'false', + ad: MOCK_AD_DOM, + meta: { + advertiserDomains: [''], + }, + ttl: 360, + }, + ] + + const bidResponses = spec.interpretResponse(mockServerResponse); + + expect(bidResponses).to.eql(exceptServerResponse); + }); + }); +}); From c7f4d18318a92736f083f1bfeb3b0fe24dae2e0b Mon Sep 17 00:00:00 2001 From: Viktor Dreiling <34981284+3link@users.noreply.github.com> Date: Fri, 12 Jan 2024 18:46:16 +0100 Subject: [PATCH 140/246] LiveIntent UserId module: Make process and process.env optional (#10905) * Drop the minimal mode * Revert "Drop the minimal mode" This reverts commit 9f33731a528d4b5e45e526c46303f9ae729cb290. * Make process and process.env optional * Revert changes in spec * Use pbjsGlobals for confguring moduleMode * Bump LiveConnect version --- modules/liveIntentIdSystem.js | 2 +- plugins/pbjsGlobals.js | 3 ++- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/modules/liveIntentIdSystem.js b/modules/liveIntentIdSystem.js index 2b4fc2476f8..8dc7102091d 100644 --- a/modules/liveIntentIdSystem.js +++ b/modules/liveIntentIdSystem.js @@ -157,7 +157,7 @@ function tryFireEvent() { /** @type {Submodule} */ export const liveIntentIdSubmodule = { - moduleMode: process.env.LiveConnectMode, + moduleMode: '$$LIVE_INTENT_MODULE_MODE$$', /** * used to link submodule with config * @type {string} diff --git a/plugins/pbjsGlobals.js b/plugins/pbjsGlobals.js index 6d1eeb0c57d..62d29b567ed 100644 --- a/plugins/pbjsGlobals.js +++ b/plugins/pbjsGlobals.js @@ -34,7 +34,8 @@ module.exports = function(api, options) { '$$PREBID_GLOBAL$$': pbGlobal, '$$DEFINE_PREBID_GLOBAL$$': defineGlobal, '$$REPO_AND_VERSION$$': `${prebid.repository.url.split('/')[3]}_prebid_${prebid.version}`, - '$$PREBID_DIST_URL_BASE$$': options.prebidDistUrlBase || `https://cdn.jsdelivr.net/npm/prebid.js@${getNpmVersion(prebid.version)}/dist/` + '$$PREBID_DIST_URL_BASE$$': options.prebidDistUrlBase || `https://cdn.jsdelivr.net/npm/prebid.js@${getNpmVersion(prebid.version)}/dist/`, + '$$LIVE_INTENT_MODULE_MODE$$': (process && process.env && process.env.LiveConnectMode) || 'standard' }; let identifierToStringLiteral = [ From 19a328194ff24d5050900a3d8352e08a5184fc66 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Krzysztof=20Sok=C3=B3=C5=82?= <88041828+krzysztofequativ@users.noreply.github.com> Date: Fri, 12 Jan 2024 20:52:42 +0100 Subject: [PATCH 141/246] Smartadserver Bid Adapter: support additional video parameters (#10815) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Smartadserver Bid Adapter: Add support for SDA user and site * Smartadserver Bid Adapter: Fix SDA support getConfig and add to unit testing * support floors per media type * Add GPP support * Rework payloads enriching * Add gpid support * Support additional video params * vpmt as array of numbers * Fix comment --------- Co-authored-by: Meven Courouble Co-authored-by: Krzysztof Sokół <88041828+smart-adserver@users.noreply.github.com> --- modules/smartadserverBidAdapter.js | 47 ++++++-- .../modules/smartadserverBidAdapter_spec.js | 108 +++++++++++++++++- 2 files changed, 146 insertions(+), 9 deletions(-) diff --git a/modules/smartadserverBidAdapter.js b/modules/smartadserverBidAdapter.js index ca43c26ffd7..313d466bd2e 100644 --- a/modules/smartadserverBidAdapter.js +++ b/modules/smartadserverBidAdapter.js @@ -1,4 +1,4 @@ -import { deepAccess, deepClone, logError, isFn, isPlainObject } from '../src/utils.js'; +import { deepAccess, deepClone, isArrayOfNums, isFn, isInteger, isPlainObject, logError } from '../src/utils.js'; import { BANNER, VIDEO } from '../src/mediaTypes.js'; import { config } from '../src/config.js'; import { registerBidder } from '../src/adapters/bidderFactory.js'; @@ -55,20 +55,53 @@ export const spec = { * Fills the payload with specific video attributes. * * @param {*} payload Payload that will be sent in the ServerRequest - * @param {*} videoMediaType Video media type. + * @param {*} videoMediaType Video media type */ fillPayloadForVideoBidRequest: function(payload, videoMediaType, videoParams) { const playerSize = videoMediaType.playerSize[0]; - payload.isVideo = videoMediaType.context === 'instream'; + const map = { + maxbitrate: 'vbrmax', + maxduration: 'vdmax', + minbitrate: 'vbrmin', + minduration: 'vdmin', + placement: 'vpt', + plcmt: 'vplcmt', + skip: 'skip' + }; + payload.mediaType = VIDEO; + payload.isVideo = videoMediaType.context === 'instream'; + payload.videoData = {}; + + for (const [key, value] of Object.entries(map)) { + payload.videoData = { + ...payload.videoData, + ...this.getValuableProperty(value, videoMediaType[key]) + }; + } + payload.videoData = { - videoProtocol: this.getProtocolForVideoBidRequest(videoMediaType, videoParams), - playerWidth: playerSize[0], - playerHeight: playerSize[1], - adBreak: this.getStartDelayForVideoBidRequest(videoMediaType, videoParams) + ...payload.videoData, + ...this.getValuableProperty('playerWidth', playerSize[0]), + ...this.getValuableProperty('playerHeight', playerSize[1]), + ...this.getValuableProperty('adBreak', this.getStartDelayForVideoBidRequest(videoMediaType, videoParams)), + ...this.getValuableProperty('videoProtocol', this.getProtocolForVideoBidRequest(videoMediaType, videoParams)), + ...(isArrayOfNums(videoMediaType.api) && videoMediaType.api.length ? { iabframeworks: videoMediaType.api.toString() } : {}), + ...(isArrayOfNums(videoMediaType.playbackmethod) && videoMediaType.playbackmethod.length ? { vpmt: videoMediaType.playbackmethod } : {}) }; }, + /** + * Gets a property object if the value not falsy + * @param {string} property + * @param {number} value + * @returns object with the property or empty + */ + getValuableProperty: function(property, value) { + return typeof property === 'string' && isInteger(value) && value + ? { [property]: value } : {}; + }, + /** * Gets the protocols from either videoParams or VideoMediaType * @param {*} videoMediaType diff --git a/test/spec/modules/smartadserverBidAdapter_spec.js b/test/spec/modules/smartadserverBidAdapter_spec.js index 9daa6a87826..58b4cd8c0d0 100644 --- a/test/spec/modules/smartadserverBidAdapter_spec.js +++ b/test/spec/modules/smartadserverBidAdapter_spec.js @@ -786,7 +786,7 @@ describe('Smart bid 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('videoData'); - expect(requestContent.videoData).to.have.property('videoProtocol').and.to.equal(null); + expect(requestContent.videoData).not.to.have.property('videoProtocol').eq(true); expect(requestContent.videoData).to.have.property('adBreak').and.to.equal(2); }); @@ -833,6 +833,73 @@ describe('Smart bid adapter tests', function () { expect(requestContent.videoData).to.have.property('videoProtocol').and.to.equal(6); expect(requestContent.videoData).to.have.property('adBreak').and.to.equal(3); }); + + it('should pass additional parameters', function () { + const request = spec.buildRequests([{ + bidder: 'smartadserver', + mediaTypes: { + video: { + context: 'instream', + api: [1, 2, 3], + maxbitrate: 50, + minbitrate: 20, + maxduration: 30, + minduration: 5, + placement: 3, + playbackmethod: [2, 4], + playerSize: [[640, 480]], + plcmt: 1, + skip: 0 + } + }, + params: { + siteId: '123' + } + }]); + const requestContent = JSON.parse(request[0].data); + + expect(requestContent.videoData).to.have.property('iabframeworks').and.to.equal('1,2,3'); + expect(requestContent.videoData).not.to.have.property('skip'); + expect(requestContent.videoData).to.have.property('vbrmax').and.to.equal(50); + expect(requestContent.videoData).to.have.property('vbrmin').and.to.equal(20); + expect(requestContent.videoData).to.have.property('vdmax').and.to.equal(30); + expect(requestContent.videoData).to.have.property('vdmin').and.to.equal(5); + expect(requestContent.videoData).to.have.property('vplcmt').and.to.equal(1); + expect(requestContent.videoData).to.have.property('vpmt').and.to.have.lengthOf(2); + expect(requestContent.videoData.vpmt[0]).to.equal(2); + expect(requestContent.videoData.vpmt[1]).to.equal(4); + expect(requestContent.videoData).to.have.property('vpt').and.to.equal(3); + }); + + it('should not pass not valuable parameters', function () { + const request = spec.buildRequests([{ + bidder: 'smartadserver', + mediaTypes: { + video: { + context: 'instream', + maxbitrate: 20, + minbitrate: null, + maxduration: 0, + playbackmethod: [], + playerSize: [[640, 480]], + plcmt: 1 + } + }, + params: { + siteId: '123' + } + }]); + const requestContent = JSON.parse(request[0].data); + + expect(requestContent.videoData).not.to.have.property('iabframeworks'); + expect(requestContent.videoData).to.have.property('vbrmax').and.to.equal(20); + expect(requestContent.videoData).not.to.have.property('vbrmin'); + expect(requestContent.videoData).not.to.have.property('vdmax'); + expect(requestContent.videoData).not.to.have.property('vdmin'); + expect(requestContent.videoData).to.have.property('vplcmt').and.to.equal(1); + expect(requestContent.videoData).not.to.have.property('vpmt'); + expect(requestContent.videoData).not.to.have.property('vpt'); + }); }); }); @@ -1029,7 +1096,7 @@ describe('Smart bid 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('videoData'); - expect(requestContent.videoData).to.have.property('videoProtocol').and.to.equal(null); + expect(requestContent.videoData).not.to.have.property('videoProtocol').eq(true); expect(requestContent.videoData).to.have.property('adBreak').and.to.equal(2); }); @@ -1393,4 +1460,41 @@ describe('Smart bid adapter tests', function () { expect(requestContent).to.have.property('gpid').and.to.equal(gpid); }); }); + + describe('#getValuableProperty method', function () { + it('should return an object when calling with a number value', () => { + const obj = spec.getValuableProperty('prop', 3); + expect(obj).to.deep.equal({ prop: 3 }); + }); + + it('should return an empty object when calling with a string value', () => { + const obj = spec.getValuableProperty('prop', 'str'); + expect(obj).to.deep.equal({}); + }); + + it('should return an empty object when calling with a number property', () => { + const obj = spec.getValuableProperty(7, 'str'); + expect(obj).to.deep.equal({}); + }); + + it('should return an empty object when calling with a null value', () => { + const obj = spec.getValuableProperty('prop', null); + expect(obj).to.deep.equal({}); + }); + + it('should return an empty object when calling with an object value', () => { + const obj = spec.getValuableProperty('prop', {}); + expect(obj).to.deep.equal({}); + }); + + it('should return an empty object when calling with a 0 value', () => { + const obj = spec.getValuableProperty('prop', 0); + expect(obj).to.deep.equal({}); + }); + + it('should return an empty object when calling without the value argument', () => { + const obj = spec.getValuableProperty('prop'); + expect(obj).to.deep.equal({}); + }); + }); }); From 869edc7e0af19476a677eb965d84a78c5212685a Mon Sep 17 00:00:00 2001 From: Denis <7009699+someden@users.noreply.github.com> Date: Sun, 14 Jan 2024 19:17:56 +0300 Subject: [PATCH 142/246] Core: fix jsdoc errors (#10941) --- libraries/cmp/cmpClient.js | 48 +++++++------- modules/ixBidAdapter.js | 66 +++++++++---------- modules/neuwoRtdProvider.js | 2 +- modules/prebidServerBidAdapter/index.js | 2 +- modules/pubxBidAdapter.js | 4 +- modules/rtbhouseBidAdapter.js | 6 +- modules/rtdModule/index.js | 2 +- modules/rubiconBidAdapter.js | 2 +- modules/sspBCBidAdapter.js | 2 +- modules/timeoutRtdProvider.js | 2 +- modules/yahoosspBidAdapter.js | 2 +- src/adRendering.js | 16 +++-- src/adServerManager.js | 2 +- src/adloader.js | 2 +- src/auction.js | 19 ++++-- src/auctionIndex.js | 45 +++++-------- src/auctionManager.js | 1 + src/config.js | 11 ++-- src/mediaTypes.js | 8 +-- src/userSync.js | 4 +- src/utils.js | 4 +- .../modules/sovrnAnalyticsAdapter_spec.js | 2 +- 22 files changed, 123 insertions(+), 129 deletions(-) diff --git a/libraries/cmp/cmpClient.js b/libraries/cmp/cmpClient.js index 03a50c37bb3..1d0b327cee4 100644 --- a/libraries/cmp/cmpClient.js +++ b/libraries/cmp/cmpClient.js @@ -4,46 +4,46 @@ import {GreedyPromise} from '../../src/utils/promise.js'; * @typedef {function} CMPClient * * @param {{}} params CMP parameters. Currently this is a subset of {command, callback, parameter, version}. - * @param {bool} once if true, discard cross-frame event listeners once a reply message is received. + * @param {boolean} once if true, discard cross-frame event listeners once a reply message is received. * @returns {Promise<*>} a promise to the API's "result" - see the `mode` argument to `cmpClient` on how that's determined. * @property {boolean} isDirect true if the CMP is directly accessible (no postMessage required) * @property {() => void} close close the client; currently, this just stops listening for cross-frame messages. */ +export const MODE_MIXED = 0; +export const MODE_RETURN = 1; +export const MODE_CALLBACK = 2; + /** * Returns a client function that can interface with a CMP regardless of where it's located. * - * @param apiName name of the CMP api, e.g. "__gpp" - * @param apiVersion? CMP API version - * @param apiArgs? names of the arguments taken by the api function, in order. - * @param callbackArgs? names of the cross-frame response payload properties that should be passed as callback arguments, in order - * @param mode? controls the callbacks passed to the underlying API, and how the promises returned by the client are resolved. + * @param {object} obj + * @param obj.apiName name of the CMP api, e.g. "__gpp" + * @param [obj.apiVersion] CMP API version + * @param [obj.apiArgs] names of the arguments taken by the api function, in order. + * @param [obj.callbackArgs] names of the cross-frame response payload properties that should be passed as callback arguments, in order + * @param [obj.mode] controls the callbacks passed to the underlying API, and how the promises returned by the client are resolved. * - * The client behaves differently when it's provided a `callback` argument vs when it's not - for short, let's name these - * cases "subscriptions" and "one-shot calls" respectively: + * The client behaves differently when it's provided a `callback` argument vs when it's not - for short, let's name these + * cases "subscriptions" and "one-shot calls" respectively: * - * With `mode: MODE_MIXED` (the default), promises returned on subscriptions are resolved to undefined when the callback - * is first run (that is, the promise resolves when the CMP replies, but what it replies with is discarded and - * left for the callback to deal with). For one-shot calls, the returned promise is resolved to the API's - * return value when it's directly accessible, or with the result from the first (and, presumably, the only) - * cross-frame reply when it's not; + * With `mode: MODE_MIXED` (the default), promises returned on subscriptions are resolved to undefined when the callback + * is first run (that is, the promise resolves when the CMP replies, but what it replies with is discarded and + * left for the callback to deal with). For one-shot calls, the returned promise is resolved to the API's + * return value when it's directly accessible, or with the result from the first (and, presumably, the only) + * cross-frame reply when it's not; * - * With `mode: MODE_RETURN`, the returned promise always resolves to the API's return value - which is taken to be undefined - * when cross-frame; + * With `mode: MODE_RETURN`, the returned promise always resolves to the API's return value - which is taken to be undefined + * when cross-frame; * - * With `mode: MODE_CALLBACK`, the underlying API is expected to never directly return anything significant; instead, - * it should always accept a callback and - for one-shot calls - invoke it only once with the result. The client will - * automatically generate an appropriate callback for one-shot calls and use the result it's given to resolve - * the returned promise. Subscriptions are treated in the same way as MODE_MIXED. + * With `mode: MODE_CALLBACK`, the underlying API is expected to never directly return anything significant; instead, + * it should always accept a callback and - for one-shot calls - invoke it only once with the result. The client will + * automatically generate an appropriate callback for one-shot calls and use the result it's given to resolve + * the returned promise. Subscriptions are treated in the same way as MODE_MIXED. * * @param win * @returns {CMPClient} CMP invocation function (or null if no CMP was found). */ - -export const MODE_MIXED = 0; -export const MODE_RETURN = 1; -export const MODE_CALLBACK = 2; - export function cmpClient( { apiName, diff --git a/modules/ixBidAdapter.js b/modules/ixBidAdapter.js index 06bad99bfe8..4cc6807b670 100644 --- a/modules/ixBidAdapter.js +++ b/modules/ixBidAdapter.js @@ -535,7 +535,7 @@ function isValidSize(size) { * Determines whether or not the given size object is an element of the size * array. * - * @param {array} sizeArray The size array. + * @param {Array} sizeArray The size array. * @param {object} size The size object. * @return {boolean} True if the size object is an element of the size array, and false * otherwise. @@ -590,7 +590,7 @@ function checkVideoParams(mediaTypeVideoRef, paramsVideoRef) { * Get One size from Size Array * [[250,350]] -> [250, 350] * [250, 350] -> [250, 350] - * @param {array} sizes array of sizes + * @param {Array} sizes array of sizes */ function getFirstSize(sizes = []) { if (isValidSize(sizes)) { @@ -607,7 +607,7 @@ function getFirstSize(sizes = []) { * * @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 + * @return {boolean} True if this is a valid bidFloor parameters format, and false * otherwise. */ function isValidBidFloorParams(bidFloor, bidFloorCur) { @@ -630,7 +630,7 @@ function nativeMediaTypeValid(bid) { * Get bid request object with the associated id. * * @param {*} id Id of the impression. - * @param {array} impressions List of impressions sent in the request. + * @param {Array} impressions List of impressions sent in the request. * @return {object} The impression with the associated id. */ function getBidRequest(id, impressions, validBidRequests) { @@ -648,7 +648,7 @@ function getBidRequest(id, impressions, validBidRequests) { /** * 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 {Array} allEids userIdAsEids passed in by prebid * @return {object} contains toSend (eids to send to the adserver) and seenSources (used to filter * identity info from IX Library) */ @@ -676,11 +676,11 @@ function getEidInfo(allEids) { /** * Builds a request object to be sent to the ad server based on bid requests. * - * @param {array} validBidRequests A list of valid bid request config objects. + * @param {Array} validBidRequests A list of valid bid request config objects. * @param {object} bidderRequest An object containing other info like gdprConsent. * @param {object} impressions An object containing a list of impression objects describing the bids for each transaction - * @param {array} version Endpoint version denoting banner, video or native. - * @return {array} List of objects describing the request to the server. + * @param {Array} version Endpoint version denoting banner, video or native. + * @return {Array} List of objects describing the request to the server. * */ function buildRequest(validBidRequests, bidderRequest, impressions, version) { @@ -789,8 +789,8 @@ function buildRequest(validBidRequests, bidderRequest, impressions, version) { /** * addRTI adds RTI info of the partner to retrieved user IDs from prebid ID module. * - * @param {array} userEids userEids info retrieved from prebid - * @param {array} eidInfo eidInfo info from prebid + * @param {Array} userEids userEids info retrieved from prebid + * @param {Array} eidInfo eidInfo info from prebid */ function addRTI(userEids, eidInfo) { let identityInfo = window.headertag.getIdentityInfo(); @@ -809,7 +809,7 @@ function addRTI(userEids, eidInfo) { /** * createRequest creates the base request object - * @param {array} validBidRequests A list of valid bid request config objects. + * @param {Array} validBidRequests A list of valid bid request config objects. * @return {object} Object describing the request to the server. */ function createRequest(validBidRequests) { @@ -849,9 +849,9 @@ function addRequestedFeatureToggles(r, requestedFeatureToggles) { * * @param {object} r Base reuqest object. * @param {object} bidderRequest An object containing other info like gdprConsent. - * @param {array} impressions A list of impressions to be added to the request. - * @param {array} validBidRequests A list of valid bid request config objects. - * @param {array} userEids User ID info retrieved from Prebid ID module. + * @param {Array} impressions A list of impressions to be added to the request. + * @param {Array} validBidRequests A list of valid bid request config objects. + * @param {Array} userEids User ID info retrieved from Prebid ID module. * @return {object} Enriched object describing the request to the server. */ function enrichRequest(r, bidderRequest, impressions, validBidRequests, userEids) { @@ -958,10 +958,10 @@ function applyRegulations(r, bidderRequest) { /** * addImpressions adds impressions to request object * - * @param {array} impressions List of impressions to be added to the request. - * @param {array} impKeys List of impression keys. + * @param {Array} impressions List of impressions to be added to the request. + * @param {Array} impKeys List of impression keys. * @param {object} r Reuqest object. - * @param {int} adUnitIndex Index of the current add unit + * @param {number} adUnitIndex Index of the current add unit * @return {object} Reqyest object with added impressions describing the request to the server. */ function addImpressions(impressions, impKeys, r, adUnitIndex) { @@ -1228,10 +1228,10 @@ function addAdUnitFPD(imp, bid) { /** * addIdentifiersInfo adds indentifier info to ixDaig. * - * @param {array} impressions List of impressions to be added to the request. + * @param {Array} impressions List of impressions to be added to the request. * @param {object} r Reuqest object. - * @param {array} impKeys List of impression keys. - * @param {int} adUnitIndex Index of the current add unit + * @param {Array} impKeys List of impression keys. + * @param {number} adUnitIndex Index of the current add unit * @param {object} payload Request payload object. * @param {string} baseUrl Base exchagne URL. * @return {object} Reqyest object with added indentigfier info to ixDiag. @@ -1254,7 +1254,7 @@ function addIdentifiersInfo(impressions, r, impKeys, adUnitIndex, payload, baseU /** * Return an object of user IDs stored by Prebid User ID module * - * @returns {array} ID providers that are present in userIds + * @returns {Array} ID providers that are present in userIds */ function _getUserIds(bidRequest) { const userIds = bidRequest.userId || {}; @@ -1265,8 +1265,8 @@ function _getUserIds(bidRequest) { /** * Calculates IX diagnostics values and packages them into an object * - * @param {array} validBidRequests - The valid bid requests from prebid - * @param {bool} fledgeEnabled - Flag indicating if protected audience (fledge) is enabled + * @param {Array} validBidRequests - The valid bid requests from prebid + * @param {boolean} fledgeEnabled - Flag indicating if protected audience (fledge) is enabled * @return {Object} IX diag values for ad units */ function buildIXDiag(validBidRequests, fledgeEnabled) { @@ -1327,8 +1327,8 @@ function buildIXDiag(validBidRequests, fledgeEnabled) { /** * - * @param {array} bannerSizeList list of banner sizes - * @param {array} bannerSize the size to be removed + * @param {Array} bannerSizeList list of banner sizes + * @param {Array} bannerSize the size to be removed * @return {boolean} true if successfully removed, false if not found */ @@ -1487,7 +1487,7 @@ 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 + * @param {Array} newSize The new size to be applied * @return {object} newImp Updated impression object */ function createMissingBannerImp(bid, imp, newSize) { @@ -1786,7 +1786,7 @@ export const spec = { /** * Make a server request from the list of BidRequests. * - * @param {array} validBidRequests A list of valid bid request config objects. + * @param {Array} validBidRequests A list of valid bid request config objects. * @param {object} bidderRequest A object contains bids and other info like gdprConsent. * @return {object} Info describing the request to the server. */ @@ -1874,7 +1874,7 @@ export const spec = { * * @param {object} serverResponse A successful response from the server. * @param {object} bidderRequest The bid request sent to the server. - * @return {array} An array of bids which were nested inside the server. + * @return {Array} An array of bids which were nested inside the server. */ interpretResponse: function (serverResponse, bidderRequest) { const bids = []; @@ -1966,8 +1966,8 @@ export const spec = { /** * Determine which user syncs should occur * @param {object} syncOptions - * @param {array} serverResponses - * @returns {array} User sync pixels + * @param {Array} serverResponses + * @returns {Array} User sync pixels */ getUserSyncs: function (syncOptions, serverResponses) { const syncs = []; @@ -2009,8 +2009,8 @@ export const spec = { /** * Build img user sync url - * @param {int} syncsPerBidder number of syncs Per Bidder - * @param {int} index index to pass + * @param {number} syncsPerBidder number of syncs Per Bidder + * @param {number} index index to pass * @returns {string} img user sync url */ function buildImgSyncUrl(syncsPerBidder, index) { @@ -2029,7 +2029,7 @@ function buildImgSyncUrl(syncsPerBidder, index) { /** * Combines all imps into a single object - * @param {array} imps array of imps + * @param {Array} imps array of imps * @returns object */ export function combineImps(imps) { diff --git a/modules/neuwoRtdProvider.js b/modules/neuwoRtdProvider.js index 881bbc10b11..7c594e2a1c3 100644 --- a/modules/neuwoRtdProvider.js +++ b/modules/neuwoRtdProvider.js @@ -71,7 +71,7 @@ export function addFragment(base, path, addition) { /** * Concatenate a base array and an array within an object * non-array bases will be arrays, non-arrays at object key will be discarded - * @param {array} base base array to add to + * @param {Array} base base array to add to * @param {object} source object to get an array from * @param {string} key dot-notated path to array within object * @returns base + source[key] if that's an array diff --git a/modules/prebidServerBidAdapter/index.js b/modules/prebidServerBidAdapter/index.js index bbc1ea21822..5ae9a9fc27a 100644 --- a/modules/prebidServerBidAdapter/index.js +++ b/modules/prebidServerBidAdapter/index.js @@ -593,7 +593,7 @@ function shouldEmitNonbids(s2sConfig, response) { /** * Global setter that sets eids permissions for bidders * This setter is to be used by userId module when included - * @param {array} newEidPermissions + * @param {Array} newEidPermissions */ function setEidPermissions(newEidPermissions) { eidPermissions = newEidPermissions; diff --git a/modules/pubxBidAdapter.js b/modules/pubxBidAdapter.js index ee28d549475..60e5be2a321 100644 --- a/modules/pubxBidAdapter.js +++ b/modules/pubxBidAdapter.js @@ -55,8 +55,8 @@ export const spec = { /** * Determine which user syncs should occur * @param {object} syncOptions - * @param {array} serverResponses - * @returns {array} User sync pixels + * @param {Array} serverResponses + * @returns {Array} User sync pixels */ getUserSyncs: function (syncOptions, serverResponses) { const kwTag = document.getElementsByName('keywords'); diff --git a/modules/rtbhouseBidAdapter.js b/modules/rtbhouseBidAdapter.js index 4ca4e4f90a9..5f94ec3dea4 100644 --- a/modules/rtbhouseBidAdapter.js +++ b/modules/rtbhouseBidAdapter.js @@ -195,7 +195,7 @@ registerBidder(spec); /** * @param {object} slot Ad Unit Params by Prebid - * @returns {int} floor by imp type + * @returns {number} floor by imp type */ function applyFloor(slot) { const floors = []; @@ -350,7 +350,7 @@ function mapNative(slot) { /** * @param {object} slot Slot config by Prebid - * @returns {array} Request Assets by OpenRTB Native Ads 1.1 §4.2 + * @returns {Array} Request Assets by OpenRTB Native Ads 1.1 §4.2 */ function mapNativeAssets(slot) { const params = slot.nativeParams || deepAccess(slot, 'mediaTypes.native'); @@ -413,7 +413,7 @@ function mapNativeAssets(slot) { /** * @param {object} image Prebid native.image/icon - * @param {int} type Image or icon code + * @param {number} type Image or icon code * @returns {object} Request Image by OpenRTB Native Ads 1.1 §4.4 */ function mapNativeImage(image, type) { diff --git a/modules/rtdModule/index.js b/modules/rtdModule/index.js index fb6e65e8a65..8968d4c795a 100644 --- a/modules/rtdModule/index.js +++ b/modules/rtdModule/index.js @@ -386,7 +386,7 @@ export function getAdUnitTargeting(auction) { /** * deep merge array of objects - * @param {array} arr - objects array + * @param {Array} arr - objects array * @return {Object} merged object */ export function deepMerge(arr) { diff --git a/modules/rubiconBidAdapter.js b/modules/rubiconBidAdapter.js index e9556b9afc6..9882eb23ac3 100644 --- a/modules/rubiconBidAdapter.js +++ b/modules/rubiconBidAdapter.js @@ -1184,7 +1184,7 @@ export function hasValidVideoParams(bid) { /** * Make sure the required params are present * @param {Object} schain - * @param {Bool} + * @param {boolean} */ export function hasValidSupplyChainParams(schain) { let isValid = false; diff --git a/modules/sspBCBidAdapter.js b/modules/sspBCBidAdapter.js index f24af853a54..c351b76d7ea 100644 --- a/modules/sspBCBidAdapter.js +++ b/modules/sspBCBidAdapter.js @@ -38,7 +38,7 @@ var nativeAssetMap = { /** * return native asset type, based on asset id - * @param {int} id - native asset id + * @param {number} id - native asset id * @returns {string} asset type */ const getNativeAssetType = id => { diff --git a/modules/timeoutRtdProvider.js b/modules/timeoutRtdProvider.js index 323a5291e2d..fe9597f6f4e 100644 --- a/modules/timeoutRtdProvider.js +++ b/modules/timeoutRtdProvider.js @@ -67,7 +67,7 @@ function getConnectionSpeed() { * Calculate the time to be added to the timeout * @param {Array} adUnits * @param {Object} rules - * @return {int} + * @return {number} */ function calculateTimeoutModifier(adUnits, rules) { logInfo('Timeout rules', rules); diff --git a/modules/yahoosspBidAdapter.js b/modules/yahoosspBidAdapter.js index a66d76f8689..0453350a85a 100644 --- a/modules/yahoosspBidAdapter.js +++ b/modules/yahoosspBidAdapter.js @@ -111,7 +111,7 @@ function extractUserSyncUrls(syncOptions, pixels) { * @param {object} consentData * @param {object} consentData.gpp * @param {string} consentData.gpp.gppConsent - * @param {array} consentData.gpp.applicableSections + * @param {Array} consentData.gpp.applicableSections * @param {object} consentData.gdpr * @param {object} consentData.gdpr.consentString * @param {object} consentData.gdpr.gdprApplies diff --git a/src/adRendering.js b/src/adRendering.js index c3e518eae4d..ac2639c4052 100644 --- a/src/adRendering.js +++ b/src/adRendering.js @@ -11,10 +11,11 @@ const {AD_RENDER_FAILED, AD_RENDER_SUCCEEDED, STALE_RENDER, BID_WON} = constants /** * Emit the AD_RENDER_FAILED event. * - * @param reason one of the values in CONSTANTS.AD_RENDER_FAILED_REASON - * @param message failure description - * @param bid? bid response object that failed to render - * @param id? adId that failed to render + * @param {Object} data + * @param data.reason one of the values in CONSTANTS.AD_RENDER_FAILED_REASON + * @param data.message failure description + * @param [data.bid] bid response object that failed to render + * @param [data.id] adId that failed to render */ export function emitAdRenderFail({ reason, message, bid, id }) { const data = { reason, message }; @@ -28,10 +29,11 @@ export function emitAdRenderFail({ reason, message, bid, id }) { /** * Emit the AD_RENDER_SUCCEEDED event. * (Note: Invocation of this function indicates that the render function did not generate an error, it does not guarantee that tracking for this event has occurred yet.) - * @param doc document object that was used to `.write` the ad. Should be `null` if unavailable (e.g. for documents in + * @param {Object} data + * @param data.doc document object that was used to `.write` the ad. Should be `null` if unavailable (e.g. for documents in * a cross-origin frame). - * @param bid bid response object for the ad that was rendered - * @param id adId that was rendered. + * @param [data.bid] bid response object for the ad that was rendered + * @param [data.id] adId that was rendered. */ export function emitAdRenderSucceeded({ doc, bid, id }) { const data = { doc }; diff --git a/src/adServerManager.js b/src/adServerManager.js index af8fe34920e..7e1290b3983 100644 --- a/src/adServerManager.js +++ b/src/adServerManager.js @@ -34,7 +34,7 @@ const prebid = getGlobal(); /** * @typedef {Object} VideoSupport * - * @function {VideoAdUrlBuilder} buildVideoAdUrl + * @property {VideoAdUrlBuilder} buildVideoAdUrl */ /** diff --git a/src/adloader.js b/src/adloader.js index 664fd03d673..87683828aa8 100644 --- a/src/adloader.js +++ b/src/adloader.js @@ -40,7 +40,7 @@ const _approvedLoadExternalJSList = [ * @param {string} moduleCode bidderCode or module code of the module requesting this resource * @param {function} [callback] callback function to be called after the script is loaded * @param {Document} [doc] the context document, in which the script will be loaded, defaults to loaded document - * @param {object} an object of attributes to be added to the script with setAttribute by [key] and [value]; Only the attributes passed in the first request of a url will be added. + * @param {object} attributes an object of attributes to be added to the script with setAttribute by [key] and [value]; Only the attributes passed in the first request of a url will be added. */ export function loadExternalScript(url, moduleCode, callback, doc, attributes) { if (!moduleCode || !url) { diff --git a/src/auction.js b/src/auction.js index 0d677c540b9..37bb2604167 100644 --- a/src/auction.js +++ b/src/auction.js @@ -8,6 +8,13 @@ * */ +/** + * @typedef {import('../src/adapters/bidderFactory.js').BidRequest} BidRequest + * @typedef {import('../src/adapters/bidderFactory.js').Bid} Bid + * @typedef {import('../src/config.js').MediaTypePriceGranularity} MediaTypePriceGranularity + * @typedef {import('../src/mediaTypes.js').MediaType} MediaType + */ + /** * @typedef {Object} AdUnit An object containing the adUnit configuration. * @@ -128,8 +135,9 @@ export function resetAuctionState() { * @param {number} requestConfig.cbTimeout * @param {Array.} requestConfig.labels * @param {string} requestConfig.auctionId - * @param {{global: {}, bidder: {}}} ortb2Fragments first party data, separated into global + * @param {{global: {}, bidder: {}}} requestConfig.ortb2Fragments first party data, separated into global * (from getConfig('ortb2') + requestBids({ortb2})) and bidder (a map from bidderCode to ortb2) + * @param {Object} requestConfig.metrics * @returns {Auction} auction instance */ export function newAuction({adUnits, adUnitCodes, callback, cbTimeout, labels, auctionId, ortb2Fragments, metrics}) { @@ -406,9 +414,10 @@ export function newAuction({adUnits, adUnitCodes, callback, cbTimeout, labels, a /** * Hook into this to intercept bids before they are added to an auction. * + * @type {Function} * @param adUnitCode * @param bid - * @param {function(String)} reject: a function that, when called, rejects `bid` with the given reason. + * @param {function(String): void} reject a function that, when called, rejects `bid` with the given reason. */ export const addBidResponse = hook('sync', function(adUnitCode, bid, reject) { this.dispatch.call(null, adUnitCode, bid); @@ -750,8 +759,9 @@ export function getMediaTypeGranularity(mediaType, mediaTypes, mediaTypePriceGra /** * This function returns the price granularity defined. It can be either publisher defined or default value - * @param bid bid response object - * @param index + * @param {Bid} bid bid response object + * @param {object} obj + * @param {object} obj.index * @returns {string} granularity */ export const getPriceGranularity = (bid, {index = auctionManager.index} = {}) => { @@ -859,7 +869,6 @@ function defaultAdserverTargeting() { /** * @param {string} mediaType * @param {string} bidderCode - * @param {BidRequest} bidReq * @returns {*} */ export function getStandardBidderSettings(mediaType, bidderCode) { diff --git a/src/auctionIndex.js b/src/auctionIndex.js index bdd2b42f9c6..bb3f535b41a 100644 --- a/src/auctionIndex.js +++ b/src/auctionIndex.js @@ -1,24 +1,30 @@ +/** + * @typedef {Object} AuctionIndex + * + * @property {function({ auctionId: * }): *} getAuction Returns auction instance for `auctionId` + * @property {function({ transactionId: * }): *} getAdUnit Returns `adUnit` object for `transactionId`. + * You should prefer `getMediaTypes` for looking up bid media types. + * @property {function({ transactionId: *, requestId: * }): *} getMediaTypes Returns mediaTypes object from bidRequest (through `requestId`) falling back to the adUnit (through `transactionId`). + * The bidRequest is given precedence because its mediaTypes can differ from the adUnit's (if bidder-specific labels are in use). + * Bids that have no associated request do not have labels either, and use the adUnit's mediaTypes. + * @property {function({ requestId: *, bidderRequestId: * }): *} getBidderRequest Returns bidderRequest that matches both requestId and bidderRequestId (if either or both are provided). + * Bid responses are not guaranteed to have a corresponding request. + * @property {function({ requestId: * }): *} getBidRequest Returns bidRequest object for requestId. + * Bid responses are not guaranteed to have a corresponding request. + */ + /** * Retrieves request-related bid data. * All methods are designed to work with Bid (response) objects returned by bid adapters. */ export function AuctionIndex(getAuctions) { Object.assign(this, { - /** - * @param auctionId - * @returns {*} Auction instance for `auctionId` - */ getAuction({auctionId}) { if (auctionId != null) { return getAuctions() .find(auction => auction.getAuctionId() === auctionId); } }, - /** - * NOTE: you should prefer {@link #getMediaTypes} for looking up bid media types. - * @param transactionId - * @returns adUnit object for `transactionId` - */ getAdUnit({transactionId}) { if (transactionId != null) { return getAuctions() @@ -26,14 +32,6 @@ export function AuctionIndex(getAuctions) { .find(au => au.transactionId === transactionId); } }, - /** - * @param transactionId - * @param requestId? - * @returns {*} mediaTypes object from bidRequest (through requestId) falling back to the adUnit (through transactionId). - * - * The bidRequest is given precedence because its mediaTypes can differ from the adUnit's (if bidder-specific labels are in use). - * Bids that have no associated request do not have labels either, and use the adUnit's mediaTypes. - */ getMediaTypes({transactionId, requestId}) { if (requestId != null) { const req = this.getBidRequest({requestId}); @@ -47,13 +45,6 @@ export function AuctionIndex(getAuctions) { } } }, - /** - * @param requestId? - * @param bidderRequestId? - * @returns {*} bidderRequest that matches both requestId and bidderRequestId (if either or both are provided). - * - * NOTE: Bid responses are not guaranteed to have a corresponding request. - */ getBidderRequest({requestId, bidderRequestId}) { if (requestId != null || bidderRequestId != null) { let bers = getAuctions().flatMap(a => a.getBidRequests()); @@ -67,12 +58,6 @@ export function AuctionIndex(getAuctions) { } } }, - /** - * @param requestId - * @returns {*} bidRequest object for requestId - * - * NOTE: Bid responses are not guaranteed to have a corresponding request. - */ getBidRequest({requestId}) { if (requestId != null) { return getAuctions() diff --git a/src/auctionManager.js b/src/auctionManager.js index 498c200ba21..8f3cbb56333 100644 --- a/src/auctionManager.js +++ b/src/auctionManager.js @@ -17,6 +17,7 @@ * @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 + * @property {AuctionIndex} index */ import { uniques, logWarn } from './utils.js'; diff --git a/src/config.js b/src/config.js index d4dc07989af..e3bb5f146ed 100644 --- a/src/config.js +++ b/src/config.js @@ -59,13 +59,6 @@ const GRANULARITY_OPTIONS = { const ALL_TOPICS = '*'; -/** - * @typedef {object} PrebidConfig - * - * @property {string} cache.url Set a url if we should use prebid-cache to store video bids before adding - * bids to the auction. **NOTE** This must be set if you want to use the dfpAdServerVideo module. - */ - export function newConfig() { let listeners = []; let defaults; @@ -551,4 +544,8 @@ export function newConfig() { }; } +/** + * Set a `cache.url` if we should use prebid-cache to store video bids before adding bids to the auction. + * This must be set if you want to use the dfpAdServerVideo module. + */ export const config = newConfig(); diff --git a/src/mediaTypes.js b/src/mediaTypes.js index eea286f7af5..2afa2aefaf9 100644 --- a/src/mediaTypes.js +++ b/src/mediaTypes.js @@ -10,11 +10,11 @@ * @typedef {('adpod')} VideoContext */ -/** @type MediaType */ +/** @type {MediaType} */ export const NATIVE = 'native'; -/** @type MediaType */ +/** @type {MediaType} */ export const VIDEO = 'video'; -/** @type MediaType */ +/** @type {MediaType} */ export const BANNER = 'banner'; -/** @type VideoContext */ +/** @type {VideoContext} */ export const ADPOD = 'adpod'; diff --git a/src/userSync.js b/src/userSync.js index 7e23598a318..153b36816a4 100644 --- a/src/userSync.js +++ b/src/userSync.js @@ -309,7 +309,7 @@ export function newUserSync(deps) { * @function syncUsers * @summary Trigger all the user syncs based on publisher-defined timeout * @public - * @params {int} timeout The delay in ms before syncing data - default 0 + * @params {number} timeout The delay in ms before syncing data - default 0 */ publicApi.syncUsers = (timeout = 0) => { if (timeout) { @@ -358,7 +358,7 @@ export const userSync = newUserSync(Object.defineProperties({ * * @property {boolean} enableOverride * @property {boolean} syncEnabled - * @property {int} syncsPerBidder + * @property {number} syncsPerBidder * @property {string[]} enabledBidders * @property {Object} filterSettings */ diff --git a/src/utils.js b/src/utils.js index 288f0d5da5d..67ed7339675 100644 --- a/src/utils.js +++ b/src/utils.js @@ -653,7 +653,7 @@ export function checkCookieSupport() { * * @param {function} func The function which should be executed, once the returned function has been executed * numRequiredCalls times. - * @param {int} numRequiredCalls The number of times which the returned function needs to be called before + * @param {number} numRequiredCalls The number of times which the returned function needs to be called before * func is. */ export function delayExecution(func, numRequiredCalls) { @@ -672,7 +672,7 @@ export function delayExecution(func, numRequiredCalls) { /** * https://stackoverflow.com/a/34890276/428704 * @export - * @param {array} xs + * @param {Array} xs * @param {string} key * @returns {Object} {${key_value}: ${groupByArray}, key_value: {groupByArray}} */ diff --git a/test/spec/modules/sovrnAnalyticsAdapter_spec.js b/test/spec/modules/sovrnAnalyticsAdapter_spec.js index 68552eb3d8a..973e90abd5a 100644 --- a/test/spec/modules/sovrnAnalyticsAdapter_spec.js +++ b/test/spec/modules/sovrnAnalyticsAdapter_spec.js @@ -12,7 +12,7 @@ let constants = require('src/constants.json'); /** * Emit analytics events - * @param {array} eventArr - array of objects to define the events that will fire + * @param {Array} eventArr - array of objects to define the events that will fire * @param {object} eventObj - key is eventType, value is event * @param {string} auctionId - the auction id to attached to the events */ From 8069abdd8d32fe83050704d6c13f939a8e3e0a4d Mon Sep 17 00:00:00 2001 From: Rich Audience Date: Mon, 15 Jan 2024 22:31:35 +0100 Subject: [PATCH 143/246] Richaudience Bid Adapter : add function onTimeout (#10891) * RichaudienceBidAdapter add function onTimeout * Add unit test * revert: Revert changes in integrationExamples/creative.html * fix: Remove useless package in richaudiences test module --------- Co-authored-by: Sergi Gimenez --- modules/richaudienceBidAdapter.js | 21 ++++++++- .../modules/richaudienceBidAdapter_spec.js | 44 +++++++++++++++++++ 2 files changed, 64 insertions(+), 1 deletion(-) diff --git a/modules/richaudienceBidAdapter.js b/modules/richaudienceBidAdapter.js index 1625912ddb8..9668435e2fe 100755 --- a/modules/richaudienceBidAdapter.js +++ b/modules/richaudienceBidAdapter.js @@ -1,4 +1,4 @@ -import {deepAccess, isStr} from '../src/utils.js'; +import {deepAccess, isStr, triggerPixel} from '../src/utils.js'; import {registerBidder} from '../src/adapters/bidderFactory.js'; import {config} from '../src/config.js'; import {BANNER, VIDEO} from '../src/mediaTypes.js'; @@ -183,6 +183,13 @@ export const spec = { } return syncs }, + + onTimeout: function (data) { + let url = raiGetTimeoutURL(data); + if (url) { + triggerPixel(url); + } + } }; registerBidder(spec); @@ -332,3 +339,15 @@ function raiGetFloor(bid, config) { return 0 } } + +function raiGetTimeoutURL(data) { + let {params, timeout} = data[0] + let url = 'https://s.richaudience.com/err/?ec=6&ev=[timeout_publisher]&pla=[placement_hash]&int=PREBID&pltfm=&node=&dm=[domain]'; + + url = url.replace('[timeout_publisher]', timeout) + url = url.replace('[placement_hash]', params[0].pid) + if (REFERER != null) { + url = url.replace('[domain]', REFERER) + } + return url +} diff --git a/test/spec/modules/richaudienceBidAdapter_spec.js b/test/spec/modules/richaudienceBidAdapter_spec.js index ea45ff7e0b0..52ec6db500e 100644 --- a/test/spec/modules/richaudienceBidAdapter_spec.js +++ b/test/spec/modules/richaudienceBidAdapter_spec.js @@ -4,6 +4,8 @@ import { spec } from 'modules/richaudienceBidAdapter.js'; import {config} from 'src/config.js'; +import * as utils from 'src/utils.js'; +import sinon from 'sinon'; describe('Richaudience adapter tests', function () { var DEFAULT_PARAMS_NEW_SIZES = [{ @@ -64,6 +66,30 @@ describe('Richaudience adapter tests', function () { user: {} }]; + var DEFAULT_PARAMS_VIDEO_TIMEOUT = [{ + adUnitCode: 'test-div', + bidId: '2c7c8e9c900244', + mediaTypes: { + video: { + context: 'instream', + playerSize: [640, 480], + mimes: ['video/mp4'] + } + }, + bidder: 'richaudience', + params: [{ + bidfloor: 0.5, + pid: 'ADb1f40rmi', + supplyType: 'site' + }], + timeout: 3000, + auctionId: '0cb3144c-d084-4686-b0d6-f5dbe917c563', + bidRequestsCount: 1, + bidderRequestId: '1858b7382993ca', + transactionId: '29df2112-348b-4961-8863-1b33684d95e6', + user: {} + }] + var DEFAULT_PARAMS_VIDEO_IN = [{ adUnitCode: 'test-div', bidId: '2c7c8e9c900244', @@ -879,6 +905,24 @@ describe('Richaudience adapter tests', function () { expect(requestContent).to.have.property('gpid').and.to.equal('/19968336/header-bid-tag-1#example-2'); }) + describe('onTimeout', function () { + beforeEach(function() { + sinon.stub(utils, 'triggerPixel'); + }); + + afterEach(function() { + utils.triggerPixel.restore(); + }); + it('onTimeout exist as a function', () => { + expect(spec.onTimeout).to.exist.and.to.be.a('function'); + }); + it('should send timeout', function () { + spec.onTimeout(DEFAULT_PARAMS_VIDEO_TIMEOUT); + expect(utils.triggerPixel.called).to.equal(true); + expect(utils.triggerPixel.firstCall.args[0]).to.equal('https://s.richaudience.com/err/?ec=6&ev=3000&pla=ADb1f40rmi&int=PREBID&pltfm=&node=&dm=http%3A%2F%2Fdomain.com'); + }); + }); + describe('userSync', function () { it('Verifies user syncs iframe include', function () { config.setConfig({ From aed0f067e8eeea40a7d9a89097c0998e22c13de4 Mon Sep 17 00:00:00 2001 From: pm-azhar-mulla <75726247+pm-azhar-mulla@users.noreply.github.com> Date: Tue, 16 Jan 2024 04:39:30 +0530 Subject: [PATCH 144/246] PubMatic Bid Adapter : add support to read and pass badv to adserver (#10943) * Added support for badv parameter of ortb2 * Added test cases --------- Co-authored-by: pm-azhar-mulla --- modules/pubmaticBidAdapter.js | 5 ++++- test/spec/modules/pubmaticBidAdapter_spec.js | 9 +++++++++ 2 files changed, 13 insertions(+), 1 deletion(-) diff --git a/modules/pubmaticBidAdapter.js b/modules/pubmaticBidAdapter.js index 3371573ecc2..192c657d976 100644 --- a/modules/pubmaticBidAdapter.js +++ b/modules/pubmaticBidAdapter.js @@ -1215,7 +1215,7 @@ export const spec = { // First Party Data const commonFpd = (bidderRequest && bidderRequest.ortb2) || {}; - const { user, device, site, bcat } = commonFpd; + const { user, device, site, bcat, badv } = commonFpd; if (site) { const { page, domain, ref } = payload.site; mergeDeep(payload, {site: site}); @@ -1226,6 +1226,9 @@ export const spec = { if (user) { mergeDeep(payload, {user: user}); } + if (badv) { + mergeDeep(payload, {badv: badv}); + } if (bcat) { blockedIabCategories = blockedIabCategories.concat(bcat); } diff --git a/test/spec/modules/pubmaticBidAdapter_spec.js b/test/spec/modules/pubmaticBidAdapter_spec.js index 544ba3d0fbf..5d59ff99a89 100644 --- a/test/spec/modules/pubmaticBidAdapter_spec.js +++ b/test/spec/modules/pubmaticBidAdapter_spec.js @@ -1922,6 +1922,15 @@ describe('PubMatic adapter', function () { expect(data.user.yob).to.equal(1985); }); + it('ortb2.badv should be merged in the request', function() { + const ortb2 = { + badv: ['example.com'] + }; + const request = spec.buildRequests(bidRequests, {ortb2}); + let data = JSON.parse(request.data); + expect(data.badv).to.deep.equal(['example.com']); + }); + describe('ortb2Imp', function() { describe('ortb2Imp.ext.gpid', function() { beforeEach(function () { From dc80cff18092f6902095f4a0d0efb691cecc2f4c Mon Sep 17 00:00:00 2001 From: barRubi <151519394+barRubi@users.noreply.github.com> Date: Tue, 16 Jan 2024 09:46:41 +0200 Subject: [PATCH 145/246] Taboola Bid Adapter : refactor using ortb conversion library (#10910) * refactor using ORTB conversion library * refactor using ORTB conversion library * refactor using ORTB conversion library * refactor using ORTB conversion library --------- Co-authored-by: ahmadlob <109217988+ahmadlob@users.noreply.github.com> --- modules/taboolaBidAdapter.js | 240 +++++++++----------- test/spec/modules/taboolaBidAdapter_spec.js | 188 +++++++-------- 2 files changed, 202 insertions(+), 226 deletions(-) diff --git a/modules/taboolaBidAdapter.js b/modules/taboolaBidAdapter.js index 7402b725fd2..3b83514b6fd 100644 --- a/modules/taboolaBidAdapter.js +++ b/modules/taboolaBidAdapter.js @@ -3,9 +3,10 @@ import {registerBidder} from '../src/adapters/bidderFactory.js'; import {BANNER} from '../src/mediaTypes.js'; import {config} from '../src/config.js'; -import {deepAccess, getWindowSelf, replaceAuctionPrice} from '../src/utils.js'; +import {deepAccess, deepSetValue, getWindowSelf, replaceAuctionPrice} from '../src/utils.js'; import {getStorageManager} from '../src/storageManager.js'; import {ajax} from '../src/ajax.js'; +import {ortbConverter} from '../libraries/ortbConverter/converter.js'; const BIDDER_CODE = 'taboola'; const GVLID = 42; @@ -81,6 +82,30 @@ export const internal = { } } +const converter = ortbConverter({ + context: { + netRevenue: true, + mediaType: BANNER, + ttl: 300 + }, + imp(buildImp, bidRequest, context) { + let imp = buildImp(bidRequest, context); + fillTaboolaImpData(bidRequest, imp); + return imp; + }, + request(buildRequest, imps, bidderRequest, context) { + const reqData = buildRequest(imps, bidderRequest, context); + fillTaboolaReqData(bidderRequest, context.bidRequests[0], reqData) + return reqData; + }, + bidResponse(buildBidResponse, bid, context) { + const bidResponse = buildBidResponse(bid, context); + bidResponse.nurl = bid.nurl; + bidResponse.ad = replaceAuctionPrice(bid.adm, bid.price); + return bidResponse + } +}); + export const spec = { supportedMediaTypes: [BANNER], gvlid: GVLID, @@ -93,85 +118,35 @@ export const spec = { }, buildRequests: (validBidRequests, bidderRequest) => { const [bidRequest] = validBidRequests; - const {refererInfo, gdprConsent = {}, uspConsent} = bidderRequest; + const data = converter.toORTB({bidderRequest: bidderRequest, bidRequests: validBidRequests}); const {publisherId} = bidRequest.params; - const site = getSiteProperties(bidRequest.params, refererInfo, bidderRequest.ortb2); - const device = {ua: navigator.userAgent}; - const imps = getImps(validBidRequests); - const user = { - buyeruid: userData.getUserId(gdprConsent, uspConsent), - ext: {} - }; - const regs = { - coppa: 0, - ext: {} - }; - - if (gdprConsent.gdprApplies) { - user.ext.consent = bidderRequest.gdprConsent.consentString; - regs.ext.gdpr = 1; - } - - if (uspConsent) { - regs.ext.us_privacy = uspConsent; - } - - if (bidderRequest.ortb2?.regs?.gpp) { - regs.ext.gpp = bidderRequest.ortb2.regs.gpp; - regs.ext.gpp_sid = bidderRequest.ortb2.regs.gpp_sid; - } - - if (config.getConfig('coppa')) { - regs.coppa = 1; - } - - const ortb2 = bidderRequest.ortb2 || { - bcat: [], - badv: [], - wlang: [] - }; - - const request = { - id: bidderRequest.bidderRequestId, - imp: imps, - site, - device, - source: {fd: 1}, - tmax: (bidderRequest.timeout == undefined) ? undefined : parseInt(bidderRequest.timeout), - bcat: ortb2.bcat || bidRequest.params.bcat || [], - badv: ortb2.badv || bidRequest.params.badv || [], - wlang: ortb2.wlang || bidRequest.params.wlang || [], - user, - regs, - ext: { - pageType: ortb2?.ext?.data?.pageType || ortb2?.ext?.data?.section || bidRequest.params.pageType - } - }; - const url = END_POINT_URL + '?publisher=' + publisherId; return { url, method: 'POST', - data: JSON.stringify(request), + data: data, bids: validBidRequests, options: { withCredentials: false }, }; }, - interpretResponse: (serverResponse, {bids}) => { - if (!bids) { + interpretResponse: (serverResponse, request) => { + if (!request || !request.bids || !request.data) { return []; } - const {bidResponses, cur: currency} = getBidResponses(serverResponse); + if (!serverResponse || !serverResponse.body) { + return []; + } - if (!bidResponses) { + if (!serverResponse.body.seatbid || !serverResponse.body.seatbid.length || !serverResponse.body.seatbid[0].bid || !serverResponse.body.seatbid[0].bid.length) { return []; } - return bidResponses.map((bidResponse) => getBid(bids, currency, bidResponse)).filter(Boolean); + const bids = converter.fromORTB({response: serverResponse.body, request: request.data}).bids; + return bids; }, onBidWon: (bid) => { if (bid.nurl) { @@ -235,34 +210,76 @@ function getSiteProperties({publisherId}, refererInfo, ortb2) { } } -function getImps(validBidRequests) { - return validBidRequests.map((bid, id) => { - const {tagId, position} = bid.params; - const imp = { - id: id + 1, - banner: getBanners(bid, position), - tagid: tagId - } - if (typeof bid.getFloor === 'function') { - const floorInfo = bid.getFloor({ - currency: CURRENCY, - mediaType: BANNER, - size: '*' - }); - if (typeof floorInfo === 'object' && floorInfo.currency === CURRENCY && !isNaN(parseFloat(floorInfo.floor))) { - imp.bidfloor = parseFloat(floorInfo.floor); - imp.bidfloorcur = CURRENCY; - } - } else { - const {bidfloor = null, bidfloorcur = CURRENCY} = bid.params; - imp.bidfloor = bidfloor; - imp.bidfloorcur = bidfloorcur; - } - imp['ext'] = { - gpid: deepAccess(bid, 'ortb2Imp.ext.gpid') +function fillTaboolaReqData(bidderRequest, bidRequest, data) { + const {refererInfo, gdprConsent = {}, uspConsent} = bidderRequest; + const site = getSiteProperties(bidRequest.params, refererInfo, bidderRequest.ortb2); + const device = {ua: navigator.userAgent}; + const user = { + buyeruid: userData.getUserId(gdprConsent, uspConsent), + ext: {} + }; + const regs = { + coppa: 0, + ext: {} + }; + + if (gdprConsent.gdprApplies) { + user.ext.consent = bidderRequest.gdprConsent.consentString; + regs.ext.gdpr = 1; + } + + if (uspConsent) { + regs.ext.us_privacy = uspConsent; + } + + if (bidderRequest.ortb2?.regs?.gpp) { + regs.ext.gpp = bidderRequest.ortb2.regs.gpp; + regs.ext.gpp_sid = bidderRequest.ortb2.regs.gpp_sid; + } + + if (config.getConfig('coppa')) { + regs.coppa = 1; + } + + const ortb2 = bidderRequest.ortb2 || { + bcat: [], + badv: [], + wlang: [] + }; + + data.id = bidderRequest.bidderRequestId; + data.site = site; + data.device = device; + data.source = {fd: 1}; + data.tmax = (bidderRequest.timeout == undefined) ? undefined : parseInt(bidderRequest.timeout); + data.bcat = ortb2.bcat || bidRequest.params.bcat || []; + data.badv = ortb2.badv || bidRequest.params.badv || []; + data.wlang = ortb2.wlang || bidRequest.params.wlang || []; + data.user = user; + data.regs = regs; + deepSetValue(data, 'ext.pageType', ortb2?.ext?.data?.pageType || ortb2?.ext?.data?.section || bidRequest.params.pageType); +} + +function fillTaboolaImpData(bid, imp) { + const {tagId, position} = bid.params; + imp.banner = getBanners(bid, position); + imp.tagid = tagId; + + if (typeof bid.getFloor === 'function') { + const floorInfo = bid.getFloor({ + currency: CURRENCY, + size: '*' + }); + if (typeof floorInfo === 'object' && floorInfo.currency === CURRENCY && !isNaN(parseFloat(floorInfo.floor))) { + imp.bidfloor = parseFloat(floorInfo.floor); + imp.bidfloorcur = CURRENCY; } - return imp; - }); + } else { + const {bidfloor = null, bidfloorcur = CURRENCY} = bid.params; + imp.bidfloor = bidfloor; + imp.bidfloorcur = bidfloorcur; + } + deepSetValue(imp, 'ext.gpid', deepAccess(bid, 'ortb2Imp.ext.gpid')); } function getBanners(bid, pos) { @@ -283,51 +300,4 @@ function getSizes(sizes) { } } -function getBidResponses({body}) { - if (!body) { - return []; - } - - const {seatbid, cur} = body; - - if (!seatbid.length || !seatbid[0].bid || !seatbid[0].bid.length) { - return []; - } - - return { - bidResponses: seatbid[0].bid, - cur - }; -} - -function getBid(bids, currency, bidResponse) { - if (!bidResponse) { - return; - } - let { - price: cpm, nurl, crid: creativeId, adm: ad, w: width, h: height, exp: ttl, adomain: advertiserDomains, meta = {} - } = bidResponse; - let requestId = bids[bidResponse.impid - 1].bidId; - if (advertiserDomains && advertiserDomains.length > 0) { - meta.advertiserDomains = advertiserDomains - } - - ad = replaceAuctionPrice(ad, cpm); - - return { - requestId, - ttl, - mediaType: BANNER, - cpm, - creativeId, - currency, - ad, - width, - height, - meta, - nurl, - netRevenue: true - }; -} - registerBidder(spec); diff --git a/test/spec/modules/taboolaBidAdapter_spec.js b/test/spec/modules/taboolaBidAdapter_spec.js index 280376e4e56..dd91c410d08 100644 --- a/test/spec/modules/taboolaBidAdapter_spec.js +++ b/test/spec/modules/taboolaBidAdapter_spec.js @@ -173,10 +173,10 @@ describe('Taboola Adapter', function () { } it('should build display request', function () { + const res = spec.buildRequests([defaultBidRequest], commonBidderRequest); const expectedData = { - id: 'mock-uuid', 'imp': [{ - 'id': 1, + 'id': res.data.imp[0].id, 'banner': { format: [{ w: displayBidRequestParams.sizes[0][0], @@ -193,6 +193,8 @@ describe('Taboola Adapter', function () { 'bidfloorcur': 'USD', 'ext': {} }], + id: 'mock-uuid', + 'test': 0, 'site': { 'id': commonBidRequest.params.publisherId, 'name': commonBidRequest.params.publisherId, @@ -215,10 +217,8 @@ describe('Taboola Adapter', function () { 'ext': {} }; - const res = spec.buildRequests([defaultBidRequest], commonBidderRequest); - expect(res.url).to.equal(`${END_POINT_URL}?publisher=${commonBidRequest.params.publisherId}`); - expect(res.data).to.deep.equal(JSON.stringify(expectedData)); + expect(JSON.stringify(res.data)).to.deep.equal(JSON.stringify(expectedData)); }); it('should pass optional parameters in request', function () { @@ -233,9 +233,8 @@ describe('Taboola Adapter', function () { }; const res = spec.buildRequests([bidRequest], commonBidderRequest); - const resData = JSON.parse(res.data); - expect(resData.imp[0].bidfloor).to.deep.equal(0.25); - expect(resData.imp[0].bidfloorcur).to.deep.equal('EUR'); + expect(res.data.imp[0].bidfloor).to.deep.equal(0.25); + expect(res.data.imp[0].bidfloorcur).to.deep.equal('EUR'); }); it('should pass bid floor', function () { @@ -250,9 +249,8 @@ describe('Taboola Adapter', function () { } }; const res = spec.buildRequests([bidRequest], commonBidderRequest); - const resData = JSON.parse(res.data); - expect(resData.imp[0].bidfloor).to.deep.equal(2.7); - expect(resData.imp[0].bidfloorcur).to.deep.equal('USD'); + expect(res.data.imp[0].bidfloor).to.deep.equal(2.7); + expect(res.data.imp[0].bidfloorcur).to.deep.equal('USD'); }); it('should pass bid floor even if it is a bid floor param', function () { @@ -272,9 +270,8 @@ describe('Taboola Adapter', function () { } }; const res = spec.buildRequests([bidRequest], commonBidderRequest); - const resData = JSON.parse(res.data); - expect(resData.imp[0].bidfloor).to.deep.equal(2.7); - expect(resData.imp[0].bidfloorcur).to.deep.equal('USD'); + expect(res.data.imp[0].bidfloor).to.deep.equal(2.7); + expect(res.data.imp[0].bidfloorcur).to.deep.equal('USD'); }); it('should pass impression position', function () { @@ -288,8 +285,7 @@ describe('Taboola Adapter', function () { }; const res = spec.buildRequests([bidRequest], commonBidderRequest); - const resData = JSON.parse(res.data); - expect(resData.imp[0].banner.pos).to.deep.equal(2); + expect(res.data.imp[0].banner.pos).to.deep.equal(2); }); it('should pass gpid if configured', function () { @@ -305,8 +301,23 @@ describe('Taboola Adapter', function () { }; const res = spec.buildRequests([bidRequest], commonBidderRequest); - const resData = JSON.parse(res.data); - expect(resData.imp[0].ext.gpid).to.deep.equal('/homepage/#1'); + expect(res.data.imp[0].ext.gpid).to.deep.equal('/homepage/#1'); + }); + + it('should pass new parameter to imp ext', function () { + const ortb2Imp = { + ext: { + example: 'example' + } + } + const bidRequest = { + ...defaultBidRequest, + ortb2Imp: ortb2Imp, + params: {...commonBidRequest.params} + }; + + const res = spec.buildRequests([bidRequest], commonBidderRequest); + expect(res.data.imp[0].ext.example).to.deep.equal('example'); }); it('should pass bidder timeout', function () { @@ -315,8 +326,7 @@ describe('Taboola Adapter', function () { timeout: 500 } const res = spec.buildRequests([defaultBidRequest], bidderRequest); - const resData = JSON.parse(res.data); - expect(resData.tmax).to.equal(500); + expect(res.data.tmax).to.equal(500); }); it('should pass bidder tmax as int', function () { @@ -325,8 +335,7 @@ describe('Taboola Adapter', function () { timeout: '500' } const res = spec.buildRequests([defaultBidRequest], bidderRequest); - const resData = JSON.parse(res.data); - expect(resData.tmax).to.equal(500); + expect(res.data.tmax).to.equal(500); }); it('should pass bidder timeout as null', function () { @@ -335,8 +344,7 @@ describe('Taboola Adapter', function () { timeout: null } const res = spec.buildRequests([defaultBidRequest], bidderRequest); - const resData = JSON.parse(res.data); - expect(resData.tmax).to.equal(undefined); + expect(res.data.tmax).to.equal(undefined); }); describe('first party data', function () { @@ -350,10 +358,9 @@ describe('Taboola Adapter', function () { } } const res = spec.buildRequests([defaultBidRequest], bidderRequest); - const resData = JSON.parse(res.data); - expect(resData.bcat).to.deep.equal(bidderRequest.ortb2.bcat) - expect(resData.badv).to.deep.equal(bidderRequest.ortb2.badv) - expect(resData.wlang).to.deep.equal(bidderRequest.ortb2.wlang) + expect(res.data.bcat).to.deep.equal(bidderRequest.ortb2.bcat) + expect(res.data.badv).to.deep.equal(bidderRequest.ortb2.badv) + expect(res.data.wlang).to.deep.equal(bidderRequest.ortb2.wlang) }); it('should pass pageType if exists in ortb2', function () { @@ -368,8 +375,20 @@ describe('Taboola Adapter', function () { } } const res = spec.buildRequests([defaultBidRequest], bidderRequest); - const resData = JSON.parse(res.data); - expect(resData.ext.pageType).to.deep.equal(bidderRequest.ortb2.ext.data.pageType); + expect(res.data.ext.pageType).to.deep.equal(bidderRequest.ortb2.ext.data.pageType); + }); + + it('should pass additional parameter in request', function () { + const bidderRequest = { + ...commonBidderRequest, + ortb2: { + ext: { + example: 'example' + } + } + } + const res = spec.buildRequests([defaultBidRequest], bidderRequest); + expect(res.data.ext.example).to.deep.equal(bidderRequest.ortb2.ext.example); }); }); @@ -386,9 +405,8 @@ describe('Taboola Adapter', function () { }; const res = spec.buildRequests([defaultBidRequest], bidderRequest) - const resData = JSON.parse(res.data) - expect(resData.user.ext.consent).to.equal('consentString') - expect(resData.regs.ext.gdpr).to.equal(1) + expect(res.data.user.ext.consent).to.equal('consentString') + expect(res.data.regs.ext.gdpr).to.equal(1) }); it('should pass GPP consent if exist in ortb2', function () { @@ -400,9 +418,8 @@ describe('Taboola Adapter', function () { } const res = spec.buildRequests([defaultBidRequest], {...commonBidderRequest, ortb2}) - const resData = JSON.parse(res.data) - expect(resData.regs.ext.gpp).to.equal('testGpp') - expect(resData.regs.ext.gpp_sid).to.deep.equal([1, 2, 3]) + expect(res.data.regs.ext.gpp).to.equal('testGpp') + expect(res.data.regs.ext.gpp_sid).to.deep.equal([1, 2, 3]) }); it('should pass us privacy consent', function () { @@ -413,16 +430,14 @@ describe('Taboola Adapter', function () { uspConsent: 'consentString' } const res = spec.buildRequests([defaultBidRequest], bidderRequest); - const resData = JSON.parse(res.data); - expect(resData.regs.ext.us_privacy).to.equal('consentString'); + expect(res.data.regs.ext.us_privacy).to.equal('consentString'); }); it('should pass coppa consent', function () { config.setConfig({coppa: true}) const res = spec.buildRequests([defaultBidRequest], commonBidderRequest) - const resData = JSON.parse(res.data); - expect(resData.regs.coppa).to.equal(1) + expect(res.data.regs.coppa).to.equal(1) config.resetConfig() }); @@ -439,8 +454,7 @@ describe('Taboola Adapter', function () { timeout: 500 } const res = spec.buildRequests([defaultBidRequest], bidderRequest); - const resData = JSON.parse(res.data); - expect(resData.user.buyeruid).to.equal(51525152); + expect(res.data.user.buyeruid).to.equal(51525152); }); it('should get user id from cookie if local storage isn`t defined', function () { @@ -454,9 +468,7 @@ describe('Taboola Adapter', function () { ...commonBidderRequest }; const res = spec.buildRequests([defaultBidRequest], bidderRequest); - const resData = JSON.parse(res.data); - - expect(resData.user.buyeruid).to.equal('12121212'); + expect(res.data.user.buyeruid).to.equal('12121212'); }); it('should get user id from tgid cookie if local storage isn`t defined', function () { @@ -470,9 +482,8 @@ describe('Taboola Adapter', function () { ...commonBidderRequest }; const res = spec.buildRequests([defaultBidRequest], bidderRequest); - const resData = JSON.parse(res.data); - expect(resData.user.buyeruid).to.equal('d966c5be-c49f-4f73-8cd1-37b6b5790653-tuct9f7bf10'); + expect(res.data.user.buyeruid).to.equal('d966c5be-c49f-4f73-8cd1-37b6b5790653-tuct9f7bf10'); }); it('should get user id from TRC if local storage and cookie isn`t defined', function () { @@ -488,8 +499,7 @@ describe('Taboola Adapter', function () { ...commonBidderRequest } const res = spec.buildRequests([defaultBidRequest], bidderRequest); - const resData = JSON.parse(res.data); - expect(resData.user.buyeruid).to.equal(window.TRC.user_id); + expect(res.data.user.buyeruid).to.equal(window.TRC.user_id); delete window.TRC; }); @@ -502,8 +512,7 @@ describe('Taboola Adapter', function () { ...commonBidderRequest } const res = spec.buildRequests([defaultBidRequest], bidderRequest); - const resData = JSON.parse(res.data); - expect(resData.user.buyeruid).to.equal(0); + expect(res.data.user.buyeruid).to.equal(0); }); it('should set buyeruid to be 0 if it`s a new user', function () { @@ -511,13 +520,29 @@ describe('Taboola Adapter', function () { ...commonBidderRequest } const res = spec.buildRequests([defaultBidRequest], bidderRequest); - const resData = JSON.parse(res.data); - expect(resData.user.buyeruid).to.equal(0); + expect(res.data.user.buyeruid).to.equal(0); }); }); }) describe('interpretResponse', function () { + const defaultBidRequest = { + ...createBidRequest(), + ...displayBidRequestParams, + }; + const commonBidderRequest = { + bidderRequestId: 'mock-uuid', + refererInfo: { + page: 'https://example.com/ref', + ref: 'https://ref', + domain: 'example.com', + } + }; + const bidderRequest = { + ...commonBidderRequest + }; + const request = spec.buildRequests([defaultBidRequest], bidderRequest); + const serverResponse = { body: { 'id': '49ffg4d58ef9a163a69fhgfghd4fad03621b9e036f24f7_15', @@ -526,7 +551,7 @@ describe('Taboola Adapter', function () { 'bid': [ { 'id': '0b3dd94348-134b-435f-8db5-6bf5afgfc39e86c', - 'impid': '1', + 'impid': request.data.imp[0].id, 'price': 0.342068, 'adid': '2785119545551083381', 'adm': '\u003chtml\u003e\n\u003chead\u003e\n\u003cmeta charset\u003d"UTF-8"\u003e\n\u003cmeta http-equiv\u003d"Content-Type" content\u003d"text/html; charset\u003dutf-8"/\u003e\u003c/head\u003e\n\u003cbody style\u003d"margin: 0px; overflow:hidden;"\u003e \n\u003cscript type\u003d"text/javascript"\u003e\nwindow.tbl_trc_domain \u003d \u0027us-trc.taboola.com\u0027;\nwindow._taboola \u003d window._taboola || [];\n_taboola.push({article:\u0027auto\u0027});\n!function (e, f, u, i) {\nif (!document.getElementById(i)){\ne.async \u003d 1;\ne.src \u003d u;\ne.id \u003d i;\nf.parentNode.insertBefore(e, f);\n}\n}(document.createElement(\u0027script\u0027),\ndocument.getElementsByTagName(\u0027script\u0027)[0],\n\u0027//cdn.taboola.com/libtrc/wattpad-placement-255/loader.js\u0027,\n\u0027tb_loader_script\u0027);\nif(window.performance \u0026\u0026 typeof window.performance.mark \u003d\u003d \u0027function\u0027)\n{window.performance.mark(\u0027tbl_ic\u0027);}\n\u003c/script\u003e\n\n\u003cdiv id\u003d"taboola-below-article-thumbnails" style\u003d"height: 250px; width: 300px;"\u003e\u003c/div\u003e\n\u003cscript type\u003d"text/javascript"\u003e\nwindow._taboola \u003d window._taboola || [];\n_taboola.push({\nmode: \u0027Rbox_300x250_1x1\u0027,\ncontainer: \u0027taboola-below-article-thumbnails\u0027,\nplacement: \u0027wattpad.com_P18694_S257846_W300_H250_N1_TB\u0027,\ntarget_type: \u0027mix\u0027,\n"rtb-win":{ \nbi:\u002749ff4d58ef9a163a696d4fad03621b9e036f24f7_15\u0027,\ncu:\u0027USD\u0027,\nwp:\u0027${AUCTION_PRICE:BF}\u0027,\nwcb:\u0027~!audex-display-impression!~\u0027,\nrt:\u00271643227025284\u0027,\nrdc:\u0027us.taboolasyndication.com\u0027,\nti:\u00274212\u0027,\nex:\u0027MagniteSCoD\u0027,\nbs:\u0027xapi:257846:lvvSm6Ak7_wE\u0027,\nbp:\u002718694\u0027,\nbd:\u0027wattpad.com\u0027,\nsi:\u00279964\u0027\n} \n,\nrec: {"trc":{"si":"a69c7df43b2334f0aa337c37e2d80c21","sd":"v2_a69c7df43b2334f0aa337c37e2d80c21_3c70f7c7d64a65b15e4a4175c9a2cfa51072f04bMagniteSCoD_1643227025_1643227025_CJS1tQEQ5NdWGPLA0d76xo-9ngEgASgEMCY4iegHQIroB0iB09kDUKPPB1gAYABop-G2i_Hl-eVucAA","ui":"3c70f7c7d64a65b15e4a4175c9a2cfa51072f04bMagniteSCoD","plc":"PHON","wi":"-643136642229425433","cc":"CA","route":"US:US:V","el2r":["bulk-metrics","debug","social","metrics","perf"],"uvpw":"1","pi":"1420260","cpb":"GNO629MGIJz__________wEqGXVzLnRhYm9vbGFzeW5kaWNhdGlvbi5jb20yC3RyYy1zY29kMTI5OIDwmrUMQInoB0iK6AdQgdPZA1ijzwdjCN3__________wEQ3f__________ARgjZGMI3AoQoBAYFmRjCNIDEOAGGAhkYwiWFBCcHBgYZGMI9AUQiwoYC2RjCNkUEPkcGB1kYwj0FBCeHRgfZGorNDlmZjRkNThlZjlhMTYzYTY5NmQ0ZmFkMDM2MjFiOWUwMzZmMjRmN18xNXgCgAHpbIgBrPvTxQE","dcga":{"pubConfigOverride":{"border-color":"black","font-weight":"bold","inherit-title-color":"true","module-name":"cta-lazy-module","enable-call-to-action-creative-component":"true","disable-cta-on-custom-module":"true"}},"tslt":{"p-video-overlay":{"cancel":"סגור","goto":"עבור לדף"},"read-more":{"DEFAULT_CAPTION":"%D7%A7%D7%A8%D7%90%20%D7%A2%D7%95%D7%93"},"next-up":{"BTN_TEXT":"לקריאת התוכן הבא"},"time-ago":{"now":"עכשיו","today":"היום","yesterday":"אתמול","minutes":"לפני {0} דקות","hour":"לפני שעה","hours":"לפני {0} שעות","days":"לפני {0} ימים"},"explore-more":{"TITLE_TEXT":"המשיכו לקרוא","POPUP_TEXT":"אל תפספסו הזדמנות לקרוא עוד תוכן מעולה, רגע לפני שתעזבו"}},"evh":"-1964913910","vl":[{"ri":"185db6d274ce94b27caaabd9eed7915b","uip":"wattpad.com_P18694_S257846_W300_H250_N1_TB","ppb":"COIF","estimation_method":"EcpmEstimationMethodType_ESTIMATION","baseline_variant":"false","original_ecpm":"0.4750949889421463","v":[{"thumbnail":"https://cdn.taboola.com/libtrc/static/thumbnails/a2b272be514ca3ebe3f97a4a32a41db5.jpg","all-thumbnails":"https://cdn.taboola.com/libtrc/static/thumbnails/a2b272be514ca3ebe3f97a4a32a41db5.jpg!-#@1600x1000","origin":"default","thumb-size":"1600x1000","title":"Get Roofing Services At Prices You Can Afford In Edmonton","type":"text","published-date":"1641997069","branding-text":"Roofing Services | Search Ads","url":"https://inneth-conded.xyz/9ad2e613-8777-4fe7-9a52-386c88879289?site\u003dwattpad-placement-255\u0026site_id\u003d1420260\u0026title\u003dGet+Roofing+Services+At+Prices+You+Can+Afford+In+Edmonton\u0026platform\u003dSmartphone\u0026campaign_id\u003d15573949\u0026campaign_item_id\u003d3108610633\u0026thumbnail\u003dhttp%3A%2F%2Fcdn.taboola.com%2Flibtrc%2Fstatic%2Fthumbnails%2Fa2b272be514ca3ebe3f97a4a32a41db5.jpg\u0026cpc\u003d{cpc}\u0026click_id\u003dGiCIypnAQogsMTFL3e_mPaVM2qLvK3KRU6LWzEMUgeB6piCit1Uox6CNr5v5n-x1\u0026tblci\u003dGiCIypnAQogsMTFL3e_mPaVM2qLvK3KRU6LWzEMUgeB6piCit1Uox6CNr5v5n-x1#tblciGiCIypnAQogsMTFL3e_mPaVM2qLvK3KRU6LWzEMUgeB6piCit1Uox6CNr5v5n-x1","duration":"0","sig":"328243c4127ff16e3fdcd7270bab908f6f3fc5b4c98d","item-id":"~~V1~~2785119550041083381~~PnBkfBE9JnQxpahv0adkcuIcmMhroRAHXwLZd-7zhunTxvAnL2wqac4MyzR7uD46gj3kUkbS3FhelBtnsiJV6MhkDZRZzzIqDobN6rWmCPA3hYz5D3PLat6nhIftiT1lwdxwdlxkeV_Mfb3eos_TQavImGhxk0e7psNAZxHJ9RKL2w3lppALGgQJoy2o6lkf-pOqODtX1VkgWpEEM4WsVoWOnUTAwdyGd-8yrze8CWNp752y28hl7lleicyO1vByRdbgwlJdnqyroTPEQNNEn1JRxBOSYSWt-Xm3vkPm-G4","uploader":"","is-syndicated":"true","publisher":"search","id":"~~V1~~2785119550041083381~~PnBkfBE9JnQxpahv0adkcuIcmMhroRAHXwLZd-7zhunTxvAnL2wqac4MyzR7uD46gj3kUkbS3FhelBtnsiJV6MhkDZRZzzIqDobN6rWmCPA3hYz5D3PLat6nhIftiT1lwdxwdlxkeV_Mfb3eos_TQavImGhxk0e7psNAZxHJ9RKL2w3lppALGgQJoy2o6lkf-pOqODtX1VkgWpEEM4WsVoWOnUTAwdyGd-8yrze8CWNp752y28hl7lleicyO1vByRdbgwlJdnqyroTPEQNNEn1JRxBOSYSWt-Xm3vkPm-G4","category":"home","views":"0","itp":[{"u":"https://trc.taboola.com/1326786/log/3/unip?en\u003dclickersusa","t":"c"}],"description":""}]}],"cpcud":{"upc":"0.0","upr":"0.0"}}}\n});\n\u003c/script\u003e\n\n\u003cscript type\u003d"text/javascript"\u003e\nwindow._taboola \u003d window._taboola || [];\n_taboola.push({flush: true});\n\u003c/script\u003e\n\n\u003c/body\u003e\n\u003c/html\u003e', @@ -551,15 +576,6 @@ describe('Taboola Adapter', function () { } }; - const request = { - bids: [ - { - ...commonBidRequest, - ...displayBidRequestParams - } - ] - } - it('should return empty array if no valid bids', function () { const res = spec.interpretResponse(serverResponse, []) expect(res).to.be.an('array').that.is.empty @@ -593,18 +609,7 @@ describe('Taboola Adapter', function () { }); it('should interpret multi impression request', function () { - const multiRequest = { - bids: [ - { - ...createBidRequest(), - ...displayBidRequestParams - }, - { - ...createBidRequest(), - ...displayBidRequestParams - } - ] - } + const multiRequest = spec.buildRequests([defaultBidRequest, defaultBidRequest], bidderRequest); const multiServerResponse = { body: { @@ -614,7 +619,7 @@ describe('Taboola Adapter', function () { 'bid': [ { 'id': '0b3dd94348-134b-435f-8db5-6bf5afgfc39e86c', - 'impid': '2', + 'impid': multiRequest.data.imp[0].id, 'price': 0.342068, 'adid': '2785119545551083381', 'adm': 'ADM2', @@ -631,7 +636,7 @@ describe('Taboola Adapter', function () { }, { 'id': '0b3dd94348-134b-435f-8db5-6bf5afgfc39e86c', - 'impid': '1', + 'impid': multiRequest.data.imp[1].id, 'price': 0.342068, 'adid': '2785119545551083381', 'adm': 'ADM1', @@ -662,6 +667,8 @@ describe('Taboola Adapter', function () { requestId: multiRequest.bids[1].bidId, cpm: bid.price, creativeId: bid.crid, + creative_id: bid.crid, + seatBidId: multiServerResponse.body.seatbid[0].bid[0].id, ttl: 60, netRevenue: true, currency: multiServerResponse.body.cur, @@ -678,6 +685,8 @@ describe('Taboola Adapter', function () { requestId: multiRequest.bids[0].bidId, cpm: bid.price, creativeId: bid.crid, + creative_id: bid.crid, + seatBidId: multiServerResponse.body.seatbid[0].bid[1].id, ttl: 60, netRevenue: true, currency: multiServerResponse.body.cur, @@ -701,8 +710,10 @@ describe('Taboola Adapter', function () { const expectedRes = [ { requestId: request.bids[0].bidId, + seatBidId: serverResponse.body.seatbid[0].bid[0].id, cpm: bid.price, creativeId: bid.crid, + creative_id: bid.crid, ttl: 60, netRevenue: true, currency: serverResponse.body.cur, @@ -728,8 +739,10 @@ describe('Taboola Adapter', function () { const expectedRes = [ { requestId: request.bids[0].bidId, + seatBidId: serverResponse.body.seatbid[0].bid[0].id, cpm: bid.price, creativeId: bid.crid, + creative_id: bid.crid, ttl: 125, netRevenue: true, currency: serverResponse.body.cur, @@ -748,18 +761,7 @@ describe('Taboola Adapter', function () { }); it('should replace AUCTION_PRICE macro in adm', function () { - const multiRequest = { - bids: [ - { - ...createBidRequest(), - ...displayBidRequestParams - }, - { - ...createBidRequest(), - ...displayBidRequestParams - } - ] - } + const multiRequest = spec.buildRequests([defaultBidRequest, defaultBidRequest], bidderRequest); const multiServerResponseWithMacro = { body: { 'id': '49ffg4d58ef9a163a69fhgfghd4fad03621b9e036f24f7_15', @@ -768,7 +770,7 @@ describe('Taboola Adapter', function () { 'bid': [ { 'id': '0b3dd94348-134b-435f-8db5-6bf5afgfc39e86c', - 'impid': '2', + 'impid': multiRequest.data.imp[0].id, 'price': 0.34, 'adid': '2785119545551083381', 'adm': 'ADM2,\\nwp:\'${AUCTION_PRICE}\'', @@ -785,7 +787,7 @@ describe('Taboola Adapter', function () { }, { 'id': '0b3dd94348-134b-435f-8db5-6bf5afgfc39e86c', - 'impid': '1', + 'impid': multiRequest.data.imp[1].id, 'price': 0.35, 'adid': '2785119545551083381', 'adm': 'ADM2,\\nwp:\'${AUCTION_PRICE}\'', @@ -815,6 +817,8 @@ describe('Taboola Adapter', function () { requestId: multiRequest.bids[1].bidId, cpm: multiServerResponseWithMacro.body.seatbid[0].bid[0].price, creativeId: bid.crid, + creative_id: bid.crid, + seatBidId: multiServerResponseWithMacro.body.seatbid[0].bid[0].id, ttl: 60, netRevenue: true, currency: multiServerResponseWithMacro.body.cur, @@ -831,6 +835,8 @@ describe('Taboola Adapter', function () { requestId: multiRequest.bids[0].bidId, cpm: multiServerResponseWithMacro.body.seatbid[0].bid[1].price, creativeId: bid.crid, + creative_id: bid.crid, + seatBidId: multiServerResponseWithMacro.body.seatbid[0].bid[1].id, ttl: 60, netRevenue: true, currency: multiServerResponseWithMacro.body.cur, From 37aba0a35e34ce9a1a4df276777008f50f895b72 Mon Sep 17 00:00:00 2001 From: Tachfine Date: Tue, 16 Jan 2024 14:24:40 +0100 Subject: [PATCH 146/246] [Fledge] Add support for seller signals per imp (#10948) Load seller signals for each impression and include them with the response level seller signals before returning the bid and fledge auction config. --- modules/criteoBidAdapter.js | 10 +++- test/spec/modules/criteoBidAdapter_spec.js | 59 +++++++++++++++++++++- 2 files changed, 66 insertions(+), 3 deletions(-) diff --git a/modules/criteoBidAdapter.js b/modules/criteoBidAdapter.js index d91d8c56cf9..2fbf3d9ab9e 100644 --- a/modules/criteoBidAdapter.js +++ b/modules/criteoBidAdapter.js @@ -276,20 +276,26 @@ export const spec = { if (isArray(body.ext?.igbid)) { const seller = body.ext.seller || FLEDGE_SELLER_DOMAIN; const sellerTimeout = body.ext.sellerTimeout || FLEDGE_SELLER_TIMEOUT; - const sellerSignals = body.ext.sellerSignals || {}; body.ext.igbid.forEach((igbid) => { const perBuyerSignals = {}; igbid.igbuyer.forEach(buyerItem => { perBuyerSignals[buyerItem.origin] = buyerItem.buyerdata; }); const bidRequest = request.bidRequests.find(b => b.bidId === igbid.impid); + const bidId = bidRequest.bidId; + let sellerSignals = body.ext.sellerSignals || {}; if (!sellerSignals.floor && bidRequest.params.bidFloor) { sellerSignals.floor = bidRequest.params.bidFloor; } if (!sellerSignals.sellerCurrency && bidRequest.params.bidFloorCur) { sellerSignals.sellerCurrency = bidRequest.params.bidFloorCur; } - const bidId = bidRequest.bidId; + if (body?.ext?.sellerSignalsPerImp !== undefined) { + const sellerSignalsPerImp = body.ext.sellerSignalsPerImp[bidId]; + if (sellerSignalsPerImp !== undefined) { + sellerSignals = {...sellerSignals, ...sellerSignalsPerImp}; + } + } fledgeAuctionConfigs.push({ bidId, config: { diff --git a/test/spec/modules/criteoBidAdapter_spec.js b/test/spec/modules/criteoBidAdapter_spec.js index 36132fe963b..5dc6d74d90e 100755 --- a/test/spec/modules/criteoBidAdapter_spec.js +++ b/test/spec/modules/criteoBidAdapter_spec.js @@ -2470,12 +2470,30 @@ describe('The Criteo bidding adapter', function () { foo: 'baz', }, }] + }, { + impid: 'test-bidId-2', + igbuyer: [{ + origin: 'https://first-buyer-domain.com', + buyerdata: { + foo: 'bar', + }, + }, { + origin: 'https://second-buyer-domain.com', + buyerdata: { + foo: 'baz', + }, + }] }], seller: 'https://seller-domain.com', sellerTimeout: 500, sellerSignals: { foo: 'bar', }, + sellerSignalsPerImp: { + 'test-bidId': { + foo2: 'bar2', + } + }, }, }, }; @@ -2502,15 +2520,54 @@ describe('The Criteo bidding adapter', function () { bidFloorCur: 'EUR' } }, + { + bidId: 'test-bidId-2', + bidder: 'criteo', + adUnitCode: 'bid-123', + transactionId: 'transaction-123', + mediaTypes: { + banner: { + sizes: [[728, 90]] + } + }, + params: { + bidFloor: 1, + bidFloorCur: 'EUR' + } + }, ]; const request = spec.buildRequests(bidRequests, bidderRequest); const interpretedResponse = spec.interpretResponse(response, request); expect(interpretedResponse).to.have.property('bids'); expect(interpretedResponse).to.have.property('fledgeAuctionConfigs'); expect(interpretedResponse.bids).to.have.lengthOf(0); - expect(interpretedResponse.fledgeAuctionConfigs).to.have.lengthOf(1); + expect(interpretedResponse.fledgeAuctionConfigs).to.have.lengthOf(2); expect(interpretedResponse.fledgeAuctionConfigs[0]).to.deep.equal({ bidId: 'test-bidId', + config: { + auctionSignals: {}, + decisionLogicUrl: 'https://grid-mercury.criteo.com/fledge/decision', + interestGroupBuyers: ['https://first-buyer-domain.com', 'https://second-buyer-domain.com'], + perBuyerSignals: { + 'https://first-buyer-domain.com': { + foo: 'bar', + }, + 'https://second-buyer-domain.com': { + foo: 'baz' + }, + }, + seller: 'https://seller-domain.com', + sellerTimeout: 500, + sellerSignals: { + foo: 'bar', + foo2: 'bar2', + floor: 1, + sellerCurrency: 'EUR', + }, + }, + }); + expect(interpretedResponse.fledgeAuctionConfigs[1]).to.deep.equal({ + bidId: 'test-bidId-2', config: { auctionSignals: {}, decisionLogicUrl: 'https://grid-mercury.criteo.com/fledge/decision', From 5bf8083d5c91a9665230dedee6d3a839494a68c0 Mon Sep 17 00:00:00 2001 From: SmartyAdman <59048845+SmartyAdman@users.noreply.github.com> Date: Tue, 16 Jan 2024 18:49:01 +0200 Subject: [PATCH 147/246] Adman Bid Adapter : add GVLID (#10949) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * 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 * сompatible with prebid v5 * add Lotame Panorama ID * update getUserSyncs * fix * fix tests * remove package-lock.json * update sync url * update test * add idx (UserID Module) * update tests * remove traffic param * handle transactionID param * send transactionID param in imp.ext * rename transactionID to transactionId * update tests * additional content handle * rollback content * content handle via hb integration * update gdprConsent * return old package-lock * add GVLID * rem package-lock.json from changes --------- Co-authored-by: minoru katogi Co-authored-by: minoru katogi Co-authored-by: ADman Media Co-authored-by: SmartyAdman Co-authored-by: SmartyAdman <> --- modules/admanBidAdapter.js | 2 ++ 1 file changed, 2 insertions(+) diff --git a/modules/admanBidAdapter.js b/modules/admanBidAdapter.js index 5eac02b7420..b78737722bd 100644 --- a/modules/admanBidAdapter.js +++ b/modules/admanBidAdapter.js @@ -4,6 +4,7 @@ import { isFn, deepAccess, logMessage } from '../src/utils.js'; import {config} from '../src/config.js'; import { convertOrtbRequestToProprietaryNative } from '../src/native.js'; +const GVLID = 149; const BIDDER_CODE = 'adman'; const AD_URL = 'https://pub.admanmedia.com/?c=o&m=multi'; const URL_SYNC = 'https://sync.admanmedia.com'; @@ -57,6 +58,7 @@ function getUserId(eids, id, source, uidExt) { export const spec = { code: BIDDER_CODE, + gvlid: GVLID, supportedMediaTypes: [BANNER, VIDEO, NATIVE], isBidRequestValid: (bid) => { From d6c39f555c4bd8ac134ec48d08a2c254362f70a4 Mon Sep 17 00:00:00 2001 From: faithnh Date: Wed, 17 Jan 2024 02:22:49 +0900 Subject: [PATCH 148/246] Unicorn Bid Adapter : support id5 (#10947) * support id5 * fix lint change quote --- modules/unicornBidAdapter.js | 23 +++++++++++ test/spec/modules/unicornBidAdapter_spec.js | 45 ++++++++++++++++----- 2 files changed, 57 insertions(+), 11 deletions(-) diff --git a/modules/unicornBidAdapter.js b/modules/unicornBidAdapter.js index 66aaf4a17e5..429ba8f60ba 100644 --- a/modules/unicornBidAdapter.js +++ b/modules/unicornBidAdapter.js @@ -87,10 +87,33 @@ function buildOpenRtbBidRequestPayload(validBidRequests, bidderRequest) { accountId: deepAccess(validBidRequests[0], 'params.accountId') } }; + const eids = initializeEids(validBidRequests[0]); + if (eids.length > 0) { + request.user.eids = eids; + } + logInfo('[UNICORN] OpenRTB Formatted Request:', request); return JSON.stringify(request); } +const initializeEids = (bidRequest) => { + let eids = []; + + let id5 = deepAccess(bidRequest, 'userId.id5id.uid'); + if (id5) { + eids.push({ + source: 'id5-sync.com', + uids: [ + { + id: id5 + } + ] + }); + } + + return eids; +} + const interpretResponse = (serverResponse, request) => { logInfo('[UNICORN] interpretResponse.serverResponse:', serverResponse); logInfo('[UNICORN] interpretResponse.request:', request); diff --git a/test/spec/modules/unicornBidAdapter_spec.js b/test/spec/modules/unicornBidAdapter_spec.js index 0abb09bfb78..bd9175dac1e 100644 --- a/test/spec/modules/unicornBidAdapter_spec.js +++ b/test/spec/modules/unicornBidAdapter_spec.js @@ -1,4 +1,5 @@ import {assert, expect} from 'chai'; +import * as utils from 'src/utils.js'; import {spec} from 'modules/unicornBidAdapter.js'; import * as _ from 'lodash'; @@ -496,6 +497,17 @@ describe('unicornBidAdapterTest', () => { }); describe('buildBidRequest', () => { + 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; + }; before(function () { $$PREBID_GLOBAL$$.bidderSettings = { unicorn: { @@ -508,17 +520,6 @@ describe('unicornBidAdapterTest', () => { }); 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); @@ -527,6 +528,28 @@ describe('unicornBidAdapterTest', () => { const uid2 = JSON.parse(req2.data)['user']['id']; assert.deepStrictEqual(uid, uid2); }); + it('test if contains ID5', () => { + let _validBidRequests = utils.deepClone(validBidRequests); + _validBidRequests[0].userId = { + id5id: { + uid: 'id5_XXXXX' + } + } + const req = spec.buildRequests(_validBidRequests, bidderRequest); + const reqData = removeUntestableAttrs(JSON.parse(req.data)); + const openRTBRequestData = removeUntestableAttrs(utils.deepClone(openRTBRequest)); + openRTBRequestData.user.eids = [ + { + source: 'id5-sync.com', + uids: [ + { + id: 'id5_XXXXX' + } + ] + } + ] + assert.deepStrictEqual(reqData, openRTBRequestData); + }) }); describe('interpretResponse', () => { From a63e74890aa02cc5b2ddf17310595daacce5de2c Mon Sep 17 00:00:00 2001 From: Deepthi Neeladri Date: Wed, 17 Jan 2024 16:18:01 +0530 Subject: [PATCH 149/246] Yahoo SSP Bid Adapter : update maintainer email address (#10954) * change * removing global filtet * reverting page * message * adapter change * remove space * 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 * comment * update maintainer email address --------- Co-authored-by: Deepthi Neeladri Sravana Co-authored-by: Deepthi Neeladri Sravana Co-authored-by: Deepthi Neeladri Sravana Co-authored-by: dsravana --- modules/yahoosspBidAdapter.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/yahoosspBidAdapter.md b/modules/yahoosspBidAdapter.md index e7074e64682..62fe0f22a55 100644 --- a/modules/yahoosspBidAdapter.md +++ b/modules/yahoosspBidAdapter.md @@ -1,7 +1,7 @@ # Overview **Module Name:** Yahoo Advertising Bid Adapter **Module Type:** Bidder Adapter -**Maintainer:** hb-fe-tech@yahooinc.com +**Maintainer:** prebid-tech-team@yahooinc.com # Description The Yahoo Advertising Bid Adapter is an OpenRTB interface that consolidates all previous "Oath.inc" adapters such as: "aol", "oneMobile", "oneDisplay" & "oneVideo" supply-side platforms. From 2e4b98eb78618cc427a7fb12b78fee976b350210 Mon Sep 17 00:00:00 2001 From: Scott Sundahl <37344964+ssundahlTTD@users.noreply.github.com> Date: Wed, 17 Jan 2024 07:39:20 -0700 Subject: [PATCH 150/246] EUID Id Module : add support for client side token generation (#10885) * enable cstg for euid * test added for euid cstg * fixed euid cstg test and updated docs --- modules/euidIdSystem.js | 10 ++++- modules/euidIdSystem.md | 52 +++++++++++++++++++++++++- modules/uid2IdSystem.js | 14 +------ modules/uid2IdSystem_shared.js | 12 ++++++ test/spec/modules/euidIdSystem_spec.js | 34 +++++++++++++++-- test/spec/modules/uid2IdSystem_spec.js | 44 ++++++++++------------ 6 files changed, 124 insertions(+), 42 deletions(-) diff --git a/modules/euidIdSystem.js b/modules/euidIdSystem.js index 6a3a0869c0e..a29da69d6c7 100644 --- a/modules/euidIdSystem.js +++ b/modules/euidIdSystem.js @@ -12,7 +12,7 @@ import {MODULE_TYPE_UID} from '../src/activities/modules.js'; // RE below lint exception: UID2 and EUID are separate modules, but the protocol is the same and shared code makes sense here. // eslint-disable-next-line prebid/validate-imports -import { Uid2GetId, Uid2CodeVersion } from './uid2IdSystem_shared.js'; +import { Uid2GetId, Uid2CodeVersion, extractIdentityFromParams } from './uid2IdSystem_shared.js'; const MODULE_NAME = 'euid'; const MODULE_REVISION = Uid2CodeVersion; @@ -99,6 +99,14 @@ export const euidIdSubmodule = { internalStorage: ADVERTISING_COOKIE }; + if (FEATURES.UID2_CSTG) { + mappedConfig.cstg = { + serverPublicKey: config?.params?.serverPublicKey, + subscriptionId: config?.params?.subscriptionId, + ...extractIdentityFromParams(config?.params ?? {}) + } + } + _logInfo(`EUID configuration loaded and mapped.`, mappedConfig); const result = Uid2GetId(mappedConfig, storage, _logInfo, _logWarn); _logInfo(`EUID getId returned`, result); return result; diff --git a/modules/euidIdSystem.md b/modules/euidIdSystem.md index e3e16bce89d..72e40b8ce7b 100644 --- a/modules/euidIdSystem.md +++ b/modules/euidIdSystem.md @@ -1,9 +1,59 @@ ## EUID User ID Submodule -EUID requires initial tokens to be generated server-side. The EUID module handles storing, providing, and optionally refreshing them. The module can operate in one of two different modes: *Client Refresh* mode or *Server Only* mode. +The EUID module handles storing, providing, and optionally refreshing tokens. While initial tokens traditionally required server-side generation, the introduction of the *Client-Side Token Generation (CSTG)* mode offers publishers the flexibility to generate EUID tokens directly from the module, eliminating this need. Publishers can choose to operate the module in one of three distinct modes: *Client Refresh* mode, *Server Only* mode and *Client-Side Token Generation* mode. *Server Only* mode was originally referred to as *legacy mode*, but it is a popular mode for new integrations where publishers prefer to handle token refresh server-side. +*Client-Side Token Generation* mode is included in EUID module by default. However, it's important to note that this mode is created and made available recently. For publishers who do not intend to use it, you have the option to instruct the build to exclude the code related to this feature: + +``` + $ gulp build --modules=uid2IdSystem --disable UID2_CSTG +``` +If you do plan to use Client-Side Token Generation (CSTG) mode, please consult the EUID Team first as they will provide required configuration values for you to use (see the Client-Side Token Generation (CSTG) mode section below for details) + +**This mode is created and made available recently. Please consult EUID Team first as they will provide required configuration values for you to use.** + +For publishers seeking a purely client-side integration without the complexities of server-side involvement, the CSTG mode is highly recommended. This mode requires the provision of a public key, subscription ID and [directly identifying information (DII)](https://unifiedid.com/docs/ref-info/glossary-uid#gl-dii) - either emails or phone numbers. In the CSTG mode, the module takes on the responsibility of encrypting the DII, generating the EUID token, and handling token refreshes when necessary. + +To configure the module to use this mode, you must: +1. Set `parmas.serverPublicKey` and `params.subscriptionId` (please reach out to the UID2 team to obtain these values) +2. Provide **ONLY ONE DII** by setting **ONLY ONE** of `params.email`/`params.phone`/`params.emailHash`/`params.phoneHash` + +Below is a table that provides guidance on when to use each directly identifying information (DII) parameter, along with information on whether normalization and hashing are required by the publisher for each parameter. + +| DII param | When to use it | Normalization required by publisher? | Hashing required by publisher? | +|------------------|-------------------------------------------------------|--------------------------------------|--------------------------------| +| params.email | When you have users' email address | No | No | +| params.phone | When you have user's phone number | Yes | No | +| params.emailHash | When you have user's hashed, normalized email address | Yes | Yes | +| params.phoneHash | When you have user's hashed, normalized phone number | Yes | Yes | + + +*Note that setting params.email will normalize email addresses, but params.phone requires phone numbers to be normalized.* + +Refer to [Normalization and Encoding](#normalization-and-encoding) for details on email address normalization, SHA-256 hashing and Base64 encoding. + +### CSTG example + +Configuration: +``` +pbjs.setConfig({ + userSync: { + userIds: [{ + name: 'euid', + params: { + serverPublicKey: '...server public key...', + subscriptionId: '...subcription id...', + email: 'user@email.com', + //phone: '+0000000', + //emailHash: '...email hash...', + //phoneHash: '...phone hash ...' + } + }] + } +}); +``` + ## Client Refresh mode This is the recommended mode for most scenarios. In this mode, the full response body from the EUID Token Generate or Token Refresh endpoint must be provided to the module. As long as the refresh token remains valid, the module will refresh the advertising token as needed. diff --git a/modules/uid2IdSystem.js b/modules/uid2IdSystem.js index b9b3dfa2380..13c0303e0b3 100644 --- a/modules/uid2IdSystem.js +++ b/modules/uid2IdSystem.js @@ -13,7 +13,7 @@ import {MODULE_TYPE_UID} from '../src/activities/modules.js'; // RE below lint exception: UID2 and EUID are separate modules, but the protocol is the same and shared code makes sense here. // eslint-disable-next-line prebid/validate-imports -import { Uid2GetId, Uid2CodeVersion } from './uid2IdSystem_shared.js'; +import { Uid2GetId, Uid2CodeVersion, extractIdentityFromParams } from './uid2IdSystem_shared.js'; import {UID2_EIDS} from '../libraries/uid2Eids/uid2Eids.js'; const MODULE_NAME = 'uid2'; @@ -34,18 +34,6 @@ function createLogger(logger, prefix) { } } -function extractIdentityFromParams(params) { - const keysToCheck = ['emailHash', 'phoneHash', 'email', 'phone']; - - for (let key of keysToCheck) { - if (params.hasOwnProperty(key)) { - return { [key]: params[key] }; - } - } - - return {}; -} - const _logInfo = createLogger(logInfo, LOG_PRE_FIX); const _logWarn = createLogger(logWarn, LOG_PRE_FIX); diff --git a/modules/uid2IdSystem_shared.js b/modules/uid2IdSystem_shared.js index 29837c5f012..acc440eafc5 100644 --- a/modules/uid2IdSystem_shared.js +++ b/modules/uid2IdSystem_shared.js @@ -755,3 +755,15 @@ export function Uid2GetId(config, prebidStorageManager, _logInfo, _logWarn) { storageManager.storeValue(tokens); return { id: tokens }; } + +export function extractIdentityFromParams(params) { + const keysToCheck = ['emailHash', 'phoneHash', 'email', 'phone']; + + for (let key of keysToCheck) { + if (params.hasOwnProperty(key)) { + return { [key]: params[key] }; + } + } + + return {}; +} diff --git a/test/spec/modules/euidIdSystem_spec.js b/test/spec/modules/euidIdSystem_spec.js index 9e4598bb5f5..98770fa80bc 100644 --- a/test/spec/modules/euidIdSystem_spec.js +++ b/test/spec/modules/euidIdSystem_spec.js @@ -3,6 +3,7 @@ import {config} from 'src/config.js'; import {euidIdSubmodule} from 'modules/euidIdSystem.js'; import 'modules/consentManagement.js'; import 'src/prebid.js'; +import * as utils from 'src/utils.js'; import {apiHelpers, cookieHelpers, runAuction, setGdprApplies} from './uid2IdSystem_helpers.js'; import {hook} from 'src/hook.js'; import {uninstall as uninstallGdprEnforcement} from 'modules/gdprEnforcement.js'; @@ -22,13 +23,18 @@ const auctionDelayMs = 10; const makeEuidIdentityContainer = (token) => ({euid: {id: token}}); const useLocalStorage = true; + const makePrebidConfig = (params = null, extraSettings = {}, debug = false) => ({ userSync: { auctionDelay: auctionDelayMs, userIds: [{name: 'euid', params: {storage: useLocalStorage ? 'localStorage' : 'cookie', ...params}, ...extraSettings}] }, debug }); +const cstgConfigParams = { serverPublicKey: 'UID2-X-L-24B8a/eLYBmRkXA9yPgRZt+ouKbXewG2OPs23+ov3JC8mtYJBCx6AxGwJ4MlwUcguebhdDp2CvzsCgS9ogwwGA==', subscriptionId: 'subscription-id' } +const clientSideGeneratedToken = 'client-side-generated-advertising-token'; + const apiUrl = 'https://prod.euid.eu/v2/token/refresh'; +const cstgApiUrl = 'https://prod.euid.eu/v2/token/client-generate'; const headers = { 'Content-Type': 'application/json' }; -const makeSuccessResponseBody = () => btoa(JSON.stringify({ status: 'success', body: { ...apiHelpers.makeTokenResponse(initialToken), advertising_token: refreshedToken } })); +const makeSuccessResponseBody = (token) => btoa(JSON.stringify({ status: 'success', body: { ...apiHelpers.makeTokenResponse(initialToken), advertising_token: token } })); const expectToken = (bid, token) => expect(bid?.userId ?? {}).to.deep.include(makeEuidIdentityContainer(token)); const expectNoIdentity = (bid) => expect(bid).to.not.haveOwnProperty('userId'); @@ -36,6 +42,7 @@ describe('EUID module', function() { let suiteSandbox, restoreSubtleToUndefined = false; const configureEuidResponse = (httpStatus, response) => server.respondWith('POST', apiUrl, (xhr) => xhr.respond(httpStatus, headers, response)); + const configureEuidCstgResponse = (httpStatus, response) => server.respondWith('POST', cstgApiUrl, (xhr) => xhr.respond(httpStatus, headers, response)); before(function() { uninstallGdprEnforcement(); @@ -43,10 +50,18 @@ describe('EUID module', function() { suiteSandbox = sinon.sandbox.create(); if (typeof window.crypto.subtle === 'undefined') { restoreSubtleToUndefined = true; - window.crypto.subtle = { importKey: () => {}, decrypt: () => {} }; + window.crypto.subtle = { importKey: () => {}, digest: () => {}, decrypt: () => {}, deriveKey: () => {}, encrypt: () => {}, generateKey: () => {}, exportKey: () => {} }; } suiteSandbox.stub(window.crypto.subtle, 'importKey').callsFake(() => Promise.resolve()); + suiteSandbox.stub(window.crypto.subtle, 'digest').callsFake(() => Promise.resolve('hashed_value')); suiteSandbox.stub(window.crypto.subtle, 'decrypt').callsFake((settings, key, data) => Promise.resolve(new Uint8Array([...settings.iv, ...data]))); + suiteSandbox.stub(window.crypto.subtle, 'deriveKey').callsFake(() => Promise.resolve()); + suiteSandbox.stub(window.crypto.subtle, 'exportKey').callsFake(() => Promise.resolve()); + suiteSandbox.stub(window.crypto.subtle, 'encrypt').callsFake(() => Promise.resolve(new ArrayBuffer())); + suiteSandbox.stub(window.crypto.subtle, 'generateKey').callsFake(() => Promise.resolve({ + privateKey: {}, + publicKey: {} + })); }); after(function() { suiteSandbox.restore(); @@ -113,10 +128,23 @@ describe('EUID module', function() { it('When an expired token is provided and the API responds in time, the refreshed token is provided to the auction.', async function() { setGdprApplies(true); const euidToken = apiHelpers.makeTokenResponse(initialToken, true, true); - configureEuidResponse(200, makeSuccessResponseBody()); + configureEuidResponse(200, makeSuccessResponseBody(refreshedToken)); config.setConfig(makePrebidConfig({euidToken})); apiHelpers.respondAfterDelay(1, server); const bid = await runAuction(); expectToken(bid, refreshedToken); }); + + if (FEATURES.UID2_CSTG) { + it('Should use client side generated EUID token in the auction.', async function() { + setGdprApplies(true); + const euidToken = apiHelpers.makeTokenResponse(initialToken, true, true); + configureEuidCstgResponse(200, makeSuccessResponseBody(clientSideGeneratedToken)); + config.setConfig(makePrebidConfig({ euidToken, ...cstgConfigParams, email: 'test@test.com' })); + apiHelpers.respondAfterDelay(1, server); + + const bid = await runAuction(); + expectToken(bid, clientSideGeneratedToken); + }); + } }); diff --git a/test/spec/modules/uid2IdSystem_spec.js b/test/spec/modules/uid2IdSystem_spec.js index 8e3728704c7..901e0c57e32 100644 --- a/test/spec/modules/uid2IdSystem_spec.js +++ b/test/spec/modules/uid2IdSystem_spec.js @@ -476,37 +476,33 @@ describe(`UID2 module`, function () { }) describe('When the storedToken is expired and can be refreshed ', function() { - it('it should calls refresh API', function() { - testApiSuccessAndFailure(async function(apiSucceeds) { - const refreshedIdentity = apiHelpers.makeTokenResponse(refreshedToken, true, true); - const moduleCookie = {originalIdentity: makeOriginalIdentity('test@test.com'), latestToken: refreshedIdentity}; - coreStorage.setCookie(moduleCookieName, JSON.stringify(moduleCookie), cookieHelpers.getFutureCookieExpiry()); - config.setConfig(makePrebidConfig({ ...cstgConfigParams, email: 'test@test.com' })); - apiHelpers.respondAfterDelay(auctionDelayMs / 10, server); + testApiSuccessAndFailure(async function(apiSucceeds) { + const refreshedIdentity = apiHelpers.makeTokenResponse(refreshedToken, true, true); + const moduleCookie = {originalIdentity: makeOriginalIdentity('test@test.com'), latestToken: refreshedIdentity}; + coreStorage.setCookie(moduleCookieName, JSON.stringify(moduleCookie), cookieHelpers.getFutureCookieExpiry()); + config.setConfig(makePrebidConfig({ ...cstgConfigParams, email: 'test@test.com' })); + apiHelpers.respondAfterDelay(auctionDelayMs / 10, server); - const bid = await runAuction(); + const bid = await runAuction(); - if (apiSucceeds) expectToken(bid, refreshedToken); - else expectNoIdentity(bid); - }, refreshApiUrl, 'it should use refreshed token in the auction', 'the auction should have no uid2'); - }); + if (apiSucceeds) expectToken(bid, refreshedToken); + else expectNoIdentity(bid); + }, refreshApiUrl, 'it should use refreshed token in the auction', 'the auction should have no uid2'); }) describe('When the storedToken is expired for refresh', function() { - it('it should calls CSTG API and not use the stored token', function() { - testApiSuccessAndFailure(async function(apiSucceeds) { - const refreshedIdentity = apiHelpers.makeTokenResponse(refreshedToken, true, true, true); - const moduleCookie = {originalIdentity: makeOriginalIdentity('test@test.com'), latestToken: refreshedIdentity}; - coreStorage.setCookie(moduleCookieName, JSON.stringify(moduleCookie), cookieHelpers.getFutureCookieExpiry()); - config.setConfig(makePrebidConfig({ ...cstgConfigParams, email: 'test@test.com' })); - apiHelpers.respondAfterDelay(auctionDelayMs / 10, server); + testApiSuccessAndFailure(async function(apiSucceeds) { + const refreshedIdentity = apiHelpers.makeTokenResponse(refreshedToken, true, true, true); + const moduleCookie = {originalIdentity: makeOriginalIdentity('test@test.com'), latestToken: refreshedIdentity}; + coreStorage.setCookie(moduleCookieName, JSON.stringify(moduleCookie), cookieHelpers.getFutureCookieExpiry()); + config.setConfig(makePrebidConfig({ ...cstgConfigParams, email: 'test@test.com' })); + apiHelpers.respondAfterDelay(auctionDelayMs / 10, server); - const bid = await runAuction(); + const bid = await runAuction(); - if (apiSucceeds) expectToken(bid, clientSideGeneratedToken); - else expectNoIdentity(bid); - }, cstgApiUrl, 'it should use generated token in the auction', 'the auction should have no uid2', false, clientSideGeneratedToken); - }); + if (apiSucceeds) expectToken(bid, clientSideGeneratedToken); + else expectNoIdentity(bid); + }, cstgApiUrl, 'it should use generated token in the auction', 'the auction should have no uid2', false, clientSideGeneratedToken); }) }) From 34e619221143057f5ed7ae84abd848629db8f87d Mon Sep 17 00:00:00 2001 From: lasloche <62240785+lasloche@users.noreply.github.com> Date: Wed, 17 Jan 2024 18:48:10 +0100 Subject: [PATCH 151/246] add alias support to riseBidAdapter (#10956) --- modules/riseBidAdapter.js | 7 ++++++- test/spec/modules/riseBidAdapter_spec.js | 6 ++++++ 2 files changed, 12 insertions(+), 1 deletion(-) diff --git a/modules/riseBidAdapter.js b/modules/riseBidAdapter.js index 44cc91aa87b..78854129f8a 100644 --- a/modules/riseBidAdapter.js +++ b/modules/riseBidAdapter.js @@ -20,6 +20,7 @@ const BIDDER_CODE = 'rise'; const ADAPTER_VERSION = '6.0.0'; const TTL = 360; const DEFAULT_CURRENCY = 'USD'; +const DEFAULT_GVLID = 1043; const DEFAULT_SELLER_ENDPOINT = 'https://hb.yellowblue.io/'; const MODES = { PRODUCTION: 'hb-multi', @@ -32,7 +33,11 @@ const SUPPORTED_SYNC_METHODS = { export const spec = { code: BIDDER_CODE, - gvlid: 1043, + aliases: [ + { code: 'risexchange', gvlid: DEFAULT_GVLID }, + { code: 'openwebxchange', gvlid: 280 } + ], + gvlid: DEFAULT_GVLID, version: ADAPTER_VERSION, supportedMediaTypes: SUPPORTED_AD_TYPES, isBidRequestValid: function (bidRequest) { diff --git a/test/spec/modules/riseBidAdapter_spec.js b/test/spec/modules/riseBidAdapter_spec.js index 4be56c79913..28bd123cb5d 100644 --- a/test/spec/modules/riseBidAdapter_spec.js +++ b/test/spec/modules/riseBidAdapter_spec.js @@ -22,6 +22,12 @@ describe('riseAdapter', function () { }); }); + describe('bid adapter', function () { + it('should have aliases', function () { + expect(spec.aliases).to.be.an('array').that.is.not.empty; + }); + }); + describe('isBidRequestValid', function () { const bid = { 'bidder': spec.code, From faec96e5747ed20d192b40b4fb49cf269e6be5f1 Mon Sep 17 00:00:00 2001 From: Rich Audience Date: Thu, 18 Jan 2024 19:18:22 +0100 Subject: [PATCH 152/246] Richaudience Bid Adapter): change url tracking (#10963) * RichaudienceBidAdapter add function onTimeout * Add unit test * revert: Revert changes in integrationExamples/creative.html * fix: Remove useless package in richaudiences test module * Change referer with host * Fix(RichaudienceBidAdapter): Change url tracking * deploy * change test --------- Co-authored-by: Sergi Gimenez --- modules/contxtfulRtdProvider.js | 2 +- modules/dsp_genieeBidAdapter.js | 46 +++++++++---------- modules/richaudienceBidAdapter.js | 2 +- package-lock.json | 2 +- .../modules/richaudienceBidAdapter_spec.js | 2 +- 5 files changed, 27 insertions(+), 27 deletions(-) diff --git a/modules/contxtfulRtdProvider.js b/modules/contxtfulRtdProvider.js index 69ff3c85079..6d4b2a2ce29 100644 --- a/modules/contxtfulRtdProvider.js +++ b/modules/contxtfulRtdProvider.js @@ -121,7 +121,7 @@ function getReceptivity() { * @param { [String] } adUnits * @param {*} _config * @param {*} _userConsent -* @return {{ code: { ReceptivityState: String } }} + * @return {{ code: { ReceptivityState: String } }} */ function getTargetingData(adUnits, _config, _userConsent) { logInfo(MODULE, 'getTargetingData'); diff --git a/modules/dsp_genieeBidAdapter.js b/modules/dsp_genieeBidAdapter.js index 517d6a93177..17865e6793a 100644 --- a/modules/dsp_genieeBidAdapter.js +++ b/modules/dsp_genieeBidAdapter.js @@ -42,21 +42,21 @@ export const spec = { code: BIDDER_CODE, supportedMediaTypes: [BANNER], /** - * Determines whether or not the given bid request is valid. - * - * @param {BidRequest} - 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} - The bid params to validate. + * @return boolean True if this is a valid bid, and false otherwise. + */ isBidRequestValid: function (_) { return true; }, /** - * Make a server request from the list of BidRequests. - * - * @param {validBidRequests[]} - an array of bids - * @param {bidderRequest} - the master bidRequest object - * @return ServerRequest Info describing the request to the server. - */ + * Make a server request from the list of BidRequests. + * + * @param {validBidRequests[]} - an array of bids + * @param {bidderRequest} - the master bidRequest object + * @return ServerRequest Info describing the request to the server. + */ buildRequests: function (validBidRequests, bidderRequest) { if (deepAccess(bidderRequest, 'gdprConsent.gdprApplies') || // gdpr USPConsent(bidderRequest.uspConsent) || // usp @@ -84,12 +84,12 @@ export const spec = { }; }, /** - * Unpack the response from the server into a list of bids. - * - * @param {ServerResponse} serverResponse A successful response from the server. - * @param {BidRequest} bidRequest - the master bidRequest object - * @return {bids} - 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 - the master bidRequest object + * @return {bids} - An array of bids which were nested inside the server. + */ interpretResponse: function (serverResponse, bidRequest) { if (!serverResponse.body) { // empty response (no bids) return []; @@ -99,12 +99,12 @@ 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: function (syncOptions, serverResponses, gdprConsent, uspConsent) { const syncs = []; // gdpr & usp diff --git a/modules/richaudienceBidAdapter.js b/modules/richaudienceBidAdapter.js index 9668435e2fe..36513aeda47 100755 --- a/modules/richaudienceBidAdapter.js +++ b/modules/richaudienceBidAdapter.js @@ -347,7 +347,7 @@ function raiGetTimeoutURL(data) { url = url.replace('[timeout_publisher]', timeout) url = url.replace('[placement_hash]', params[0].pid) if (REFERER != null) { - url = url.replace('[domain]', REFERER) + url = url.replace('[domain]', document.location.host) } return url } diff --git a/package-lock.json b/package-lock.json index 7114a39d509..ff5ad5282d9 100644 --- a/package-lock.json +++ b/package-lock.json @@ -6,7 +6,7 @@ "packages": { "": { "name": "prebid.js", - "version": "8.31.0-pre", + "version": "8.32.0-pre", "license": "Apache-2.0", "dependencies": { "@babel/core": "^7.16.7", diff --git a/test/spec/modules/richaudienceBidAdapter_spec.js b/test/spec/modules/richaudienceBidAdapter_spec.js index 52ec6db500e..20c60ca328a 100644 --- a/test/spec/modules/richaudienceBidAdapter_spec.js +++ b/test/spec/modules/richaudienceBidAdapter_spec.js @@ -919,7 +919,7 @@ describe('Richaudience adapter tests', function () { it('should send timeout', function () { spec.onTimeout(DEFAULT_PARAMS_VIDEO_TIMEOUT); expect(utils.triggerPixel.called).to.equal(true); - expect(utils.triggerPixel.firstCall.args[0]).to.equal('https://s.richaudience.com/err/?ec=6&ev=3000&pla=ADb1f40rmi&int=PREBID&pltfm=&node=&dm=http%3A%2F%2Fdomain.com'); + expect(utils.triggerPixel.firstCall.args[0]).to.equal('https://s.richaudience.com/err/?ec=6&ev=3000&pla=ADb1f40rmi&int=PREBID&pltfm=&node=&dm=localhost:9876'); }); }); From 953e38798ef0a4bdc0053b21ca7f57bd23850426 Mon Sep 17 00:00:00 2001 From: "Prebid.js automated release" Date: Thu, 18 Jan 2024 21:21:48 +0000 Subject: [PATCH 153/246] Prebid 8.32.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 ff5ad5282d9..2da7efe90e2 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,6 +1,6 @@ { "name": "prebid.js", - "version": "8.32.0-pre", + "version": "8.32.0", "lockfileVersion": 2, "requires": true, "packages": { diff --git a/package.json b/package.json index 82db5e9f7c2..cba237195dd 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "prebid.js", - "version": "8.32.0-pre", + "version": "8.32.0", "description": "Header Bidding Management Library", "main": "src/prebid.js", "scripts": { From a7e6ce95de5686879a633246c0cfb3073e3f5f25 Mon Sep 17 00:00:00 2001 From: "Prebid.js automated release" Date: Thu, 18 Jan 2024 21:21:48 +0000 Subject: [PATCH 154/246] Increment version to 8.33.0-pre --- package-lock.json | 2 +- package.json | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/package-lock.json b/package-lock.json index 2da7efe90e2..bac5bc0c929 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,6 +1,6 @@ { "name": "prebid.js", - "version": "8.32.0", + "version": "8.33.0-pre", "lockfileVersion": 2, "requires": true, "packages": { diff --git a/package.json b/package.json index cba237195dd..70b0bd4a144 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "prebid.js", - "version": "8.32.0", + "version": "8.33.0-pre", "description": "Header Bidding Management Library", "main": "src/prebid.js", "scripts": { From f95f5203d62f8dfa0b7bdeddea7074b95dbec545 Mon Sep 17 00:00:00 2001 From: JonGoSonobi Date: Thu, 18 Jan 2024 16:30:57 -0500 Subject: [PATCH 155/246] Send experian rtid in the bid request payload (#10961) --- modules/sonobiBidAdapter.js | 7 ++++++- test/spec/modules/sonobiBidAdapter_spec.js | 22 ++++++++++++++++------ 2 files changed, 22 insertions(+), 7 deletions(-) diff --git a/modules/sonobiBidAdapter.js b/modules/sonobiBidAdapter.js index a2d1f385623..2c84854e507 100644 --- a/modules/sonobiBidAdapter.js +++ b/modules/sonobiBidAdapter.js @@ -6,7 +6,7 @@ import { Renderer } from '../src/Renderer.js'; import { userSync } from '../src/userSync.js'; import { bidderSettings } from '../src/bidderSettings.js'; import { getAllOrtbKeywords } from '../libraries/keywords/keywords.js'; -import {getGptSlotInfoForAdUnitCode} from '../libraries/gptUtils/gptUtils.js'; +import { getGptSlotInfoForAdUnitCode } from '../libraries/gptUtils/gptUtils.js'; const BIDDER_CODE = 'sonobi'; const STR_ENDPOINT = 'https://apex.go.sonobi.com/trinity.json'; const PAGEVIEW_ID = generateUUID(); @@ -150,6 +150,11 @@ export const spec = { payload.coppa = 0; } + if (deepAccess(bidderRequest, 'ortb2.experianRtidData') && deepAccess(bidderRequest, 'ortb2.experianRtidKey')) { + payload.expData = deepAccess(bidderRequest, 'ortb2.experianRtidData'); + payload.expKey = deepAccess(bidderRequest, 'ortb2.experianRtidKey'); + } + // 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 164aa06d9b7..83db7c0a812 100644 --- a/test/spec/modules/sonobiBidAdapter_spec.js +++ b/test/spec/modules/sonobiBidAdapter_spec.js @@ -1,8 +1,8 @@ -import {expect} from 'chai'; -import {_getPlatform, spec} from 'modules/sonobiBidAdapter.js'; -import {newBidder} from 'src/adapters/bidderFactory.js'; -import {userSync} from '../../../src/userSync.js'; -import {config} from 'src/config.js'; +import { expect } from 'chai'; +import { _getPlatform, spec } 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 gptUtils from '../../../libraries/gptUtils/gptUtils.js'; describe('SonobiBidAdapter', function () { @@ -359,7 +359,9 @@ describe('SonobiBidAdapter', function () { 'page': 'https://example.com', 'stack': ['https://example.com'] }, - uspConsent: 'someCCPAString' + uspConsent: 'someCCPAString', + ortb2: {} + }; it('should set fpd if there is any data in ortb2', function () { @@ -493,6 +495,14 @@ describe('SonobiBidAdapter', function () { expect(bidRequests.data.hfa).to.equal('hfakey') }) + it('should return a properly formatted request with expData and expKey', function () { + bidderRequests.ortb2.experianRtidData = 'IkhlbGxvLCB3b3JsZC4gSGVsbG8sIHdvcmxkLiBIZWxsbywgd29ybGQuIg=='; + bidderRequests.ortb2.experianRtidKey = 'sovrn-encryption-key-1'; + const bidRequests = spec.buildRequests(bidRequest, bidderRequests) + expect(bidRequests.data.expData).to.equal('IkhlbGxvLCB3b3JsZC4gSGVsbG8sIHdvcmxkLiBIZWxsbywgd29ybGQuIg=='); + expect(bidRequests.data.expKey).to.equal('sovrn-encryption-key-1'); + }) + it('should return null if there is nothing to bid on', function () { const bidRequests = spec.buildRequests([{ params: {} }], bidderRequests) expect(bidRequests).to.equal(null); From bc7aa938b18371d888e33982f29a2a36299f868c Mon Sep 17 00:00:00 2001 From: Saar Amrani Date: Mon, 22 Jan 2024 15:07:42 +0200 Subject: [PATCH 156/246] Tagoras Bid Adapter : Initial release (#10826) * MinuteMediaPlus Bid Adapter: pass gpp consent to userSync server. * tagorasBidAdapter submission * update maintainer * Remove unneeded GVLID. * Adjust syncs condition logic in tagorasBidAdapter. --- modules/tagorasBidAdapter.js | 342 ++++++++++ modules/tagorasBidAdapter.md | 35 ++ test/spec/modules/tagorasBidAdapter_spec.js | 651 ++++++++++++++++++++ 3 files changed, 1028 insertions(+) create mode 100644 modules/tagorasBidAdapter.js create mode 100644 modules/tagorasBidAdapter.md create mode 100644 test/spec/modules/tagorasBidAdapter_spec.js diff --git a/modules/tagorasBidAdapter.js b/modules/tagorasBidAdapter.js new file mode 100644 index 00000000000..0138ba3daf9 --- /dev/null +++ b/modules/tagorasBidAdapter.js @@ -0,0 +1,342 @@ +import {_each, deepAccess, parseSizesInput, parseUrl, uniques, isFn} 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 {config} from '../src/config.js'; + +const DEFAULT_SUB_DOMAIN = 'exchange'; +const BIDDER_CODE = 'tagoras'; +const BIDDER_VERSION = '1.0.0'; +const CURRENCY = 'USD'; +const TTL_SECONDS = 60 * 5; +const UNIQUE_DEAL_ID_EXPIRY = 1000 * 60 * 15; +const storage = getStorageManager({bidderCode: BIDDER_CODE}); + +function getTopWindowQueryParams() { + try { + const parsedUrl = parseUrl(window.top.document.URL, {decodeSearchAsString: true}); + return parsedUrl.search; + } catch (e) { + return ''; + } +} + +export function createDomain(subDomain = DEFAULT_SUB_DOMAIN) { + return `https://${subDomain}.tagoras.io`; +} + +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, bidderTimeout) { + const { + params, + bidId, + userId, + adUnitCode, + schain, + mediaTypes, + ortb2Imp, + bidderRequestId, + bidRequestsCount, + bidderRequestsCount, + bidderWinsCount + } = bid; + let {bidFloor, ext} = params; + const hashUrl = hashCode(topWindowUrl); + const uniqueDealId = getUniqueDealId(hashUrl); + const cId = extractCID(params); + const pId = extractPID(params); + const subDomain = extractSubDomain(params); + + const gpid = deepAccess(bid, 'ortb2Imp.ext.gpid', deepAccess(bid, 'ortb2Imp.ext.data.pbadslot', '')); + + if (isFn(bid.getFloor)) { + const floorInfo = bid.getFloor({ + currency: 'USD', + mediaType: '*', + size: '*' + }); + + if (floorInfo.currency === 'USD') { + bidFloor = floorInfo.floor; + } + } + + let data = { + url: encodeURIComponent(topWindowUrl), + uqs: getTopWindowQueryParams(), + cb: Date.now(), + bidFloor: bidFloor, + bidId: bidId, + referrer: bidderRequest.refererInfo.ref, + adUnitCode: adUnitCode, + publisherId: pId, + sizes: sizes, + uniqueDealId: uniqueDealId, + bidderVersion: BIDDER_VERSION, + prebidVersion: '$prebid.version$', + res: `${screen.width}x${screen.height}`, + schain: schain, + mediaTypes: mediaTypes, + gpid: gpid, + transactionId: ortb2Imp?.ext?.tid, + bidderRequestId: bidderRequestId, + bidRequestsCount: bidRequestsCount, + bidderRequestsCount: bidderRequestsCount, + bidderWinsCount: bidderWinsCount, + bidderTimeout: bidderTimeout + }; + + appendUserIdsToRequestPayload(data, userId); + + const sua = deepAccess(bidderRequest, 'ortb2.device.sua'); + + if (sua) { + data.sua = sua; + } + + 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; + } + + if (bidderRequest.gppConsent) { + data.gppString = bidderRequest.gppConsent.gppString; + data.gppSid = bidderRequest.gppConsent.applicableSections; + } else if (bidderRequest.ortb2?.regs?.gpp) { + data.gppString = bidderRequest.ortb2.regs.gpp; + data.gppSid = bidderRequest.ortb2.regs.gpp_sid; + } + + const dto = { + method: 'POST', + url: `${createDomain(subDomain)}/prebid/multi/${cId}`, + data: data + }; + + _each(ext, (value, key) => { + dto.data['ext.' + key] = value; + }); + + return dto; +} + +function appendUserIdsToRequestPayload(payloadRef, userIds) { + let key; + _each(userIds, (userId, idSystemProviderName) => { + key = `uid.${idSystemProviderName}`; + + switch (idSystemProviderName) { + case 'digitrustid': + payloadRef[key] = 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.page || bidderRequest.refererInfo.topmostLocation; + const bidderTimeout = config.getConfig('bidderTimeout'); + const requests = []; + validBidRequests.forEach(validBidRequest => { + const sizes = parseSizesInput(validBidRequest.sizes); + const request = buildRequest(validBidRequest, topWindowUrl, sizes, bidderRequest, bidderTimeout); + 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, + metaData, + advertiserDomains, + mediaType = BANNER + } = result; + if (!ad || !price) { + return; + } + + const response = { + requestId: bidId, + cpm: price, + width: width, + height: height, + creativeId: creativeId, + currency: currency || CURRENCY, + netRevenue: true, + ttl: exp || TTL_SECONDS, + }; + + if (metaData) { + Object.assign(response, { + meta: metaData + }) + } else { + Object.assign(response, { + meta: { + advertiserDomains: advertiserDomains || [] + } + }) + } + + if (mediaType === BANNER) { + Object.assign(response, { + ad: ad, + }); + } else { + Object.assign(response, { + vastXml: ad, + mediaType: VIDEO + }); + } + output.push(response); + }); + return output; + } catch (e) { + return []; + } +} + +function getUserSyncs(syncOptions, responses, gdprConsent = {}, uspConsent = '', gppConsent = {}) { + let syncs = []; + const {iframeEnabled, pixelEnabled} = syncOptions; + const {gdprApplies, consentString = ''} = gdprConsent; + const {gppString, applicableSections} = gppConsent; + + const cidArr = responses.filter(resp => deepAccess(resp, 'body.cid')).map(resp => resp.body.cid).filter(uniques); + let params = `?cid=${encodeURIComponent(cidArr.join(','))}&gdpr=${gdprApplies ? 1 : 0}&gdpr_consent=${encodeURIComponent(consentString || '')}&us_privacy=${encodeURIComponent(uspConsent || '')}` + + if (gppString && applicableSections?.length) { + params += '&gpp=' + encodeURIComponent(gppString); + params += '&gpp_sid=' + encodeURIComponent(applicableSections.join(',')); + } + + if (iframeEnabled) { + syncs.push({ + type: 'iframe', + url: `https://sync.tagoras.io/api/sync/iframe/${params}` + }); + } else if (pixelEnabled) { + syncs.push({ + type: 'image', + url: `https://sync.tagoras.io/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 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 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, VIDEO], + isBidRequestValid, + buildRequests, + interpretResponse, + getUserSyncs +}; + +registerBidder(spec); diff --git a/modules/tagorasBidAdapter.md b/modules/tagorasBidAdapter.md new file mode 100644 index 00000000000..83290bff525 --- /dev/null +++ b/modules/tagorasBidAdapter.md @@ -0,0 +1,35 @@ +# Overview + +**Module Name:** Tagoras Bidder Adapter + +**Module Type:** Bidder Adapter + +**Maintainer:** prebid@tagoras.io + +# Description + +Module that connects to Tagoras's demand sources. + +# Test Parameters +```js +var adUnits = [ + { + code: 'test-ad', + sizes: [[300, 250]], + bids: [ + { + bidder: 'tagoras', + params: { + cId: '562524b21b1c1f08117fc7f9', + pId: '59ac17c192832d0011283fe3', + bidFloor: 0.0001, + ext: { + param1: 'loremipsum', + param2: 'dolorsitamet' + } + } + } + ] + } +]; +``` diff --git a/test/spec/modules/tagorasBidAdapter_spec.js b/test/spec/modules/tagorasBidAdapter_spec.js new file mode 100644 index 00000000000..7559567dcff --- /dev/null +++ b/test/spec/modules/tagorasBidAdapter_spec.js @@ -0,0 +1,651 @@ +import {expect} from 'chai'; +import { + spec as adapter, + createDomain, + hashCode, + extractPID, + extractCID, + extractSubDomain, + getStorageItem, + setStorageItem, + tryParseJSON, + getUniqueDealId, +} from 'modules/tagorasBidAdapter'; +import * as utils from 'src/utils.js'; +import {version} from 'package.json'; +import {useFakeTimers} from 'sinon'; +import {BANNER, VIDEO} from '../../../src/mediaTypes'; +import {config} from '../../../src/config'; + +export const TEST_ID_SYSTEMS = ['britepoolid', 'criteoId', 'id5id', 'idl_env', 'lipb', 'netId', 'parrableId', 'pubcid', 'tdid', 'pubProvidedId']; + +const SUB_DOMAIN = 'exchange'; + +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', + 'sizes': [[300, 250], [300, 600]], + 'bidderRequestId': '1fdb5ff1b6eaa7', + 'bidRequestsCount': 4, + 'bidderRequestsCount': 3, + 'bidderWinsCount': 1, + 'requestId': 'b0777d85-d061-450e-9bc7-260dd54bbb7a', + 'schain': 'a0819c69-005b-41ed-af06-1be1e0aefefc', + 'mediaTypes': [BANNER], + 'ortb2Imp': { + 'ext': { + 'gpid': '0123456789', + 'tid': 'c881914b-a3b5-4ecf-ad9c-1c2f37c6aabf' + } + } +}; + +const VIDEO_BID = { + 'bidId': '2d52001cabd527', + 'adUnitCode': '63550ad1ff6642d368cba59dh5884270560', + 'bidderRequestId': '12a8ae9ada9c13', + 'transactionId': '56e184c6-bde9-497b-b9b9-cf47a61381ee', + 'bidRequestsCount': 4, + 'bidderRequestsCount': 3, + 'bidderWinsCount': 1, + 'schain': 'a0819c69-005b-41ed-af06-1be1e0aefefc', + 'params': { + 'subDomain': SUB_DOMAIN, + 'cId': '635509f7ff6642d368cb9837', + 'pId': '59ac17c192832d0011283fe3', + 'bidFloor': 0.1 + }, + 'sizes': [[545, 307]], + 'mediaTypes': { + 'video': { + 'playerSize': [[545, 307]], + 'context': 'instream', + 'mimes': [ + 'video/mp4', + 'application/javascript' + ], + 'protocols': [2, 3, 5, 6], + 'maxduration': 60, + 'minduration': 0, + 'startdelay': 0, + 'linearity': 1, + 'api': [2], + 'placement': 1 + } + }, + 'ortb2Imp': { + 'ext': { + 'tid': '56e184c6-bde9-497b-b9b9-cf47a61381ee' + } + } +} + +const BIDDER_REQUEST = { + 'gdprConsent': { + 'consentString': 'consent_string', + 'gdprApplies': true + }, + 'gppString': 'gpp_string', + 'gppSid': [7], + 'uspConsent': 'consent_string', + 'refererInfo': { + 'page': 'https://www.greatsite.com', + 'ref': 'https://www.somereferrer.com' + }, + 'ortb2': { + 'regs': { + 'gpp': 'gpp_string', + 'gpp_sid': [7] + }, + 'device': { + 'sua': { + 'source': 2, + 'platform': { + 'brand': 'Android', + 'version': ['8', '0', '0'] + }, + 'browsers': [ + {'brand': 'Not_A Brand', 'version': ['99', '0', '0', '0']}, + {'brand': 'Google Chrome', 'version': ['109', '0', '5414', '119']}, + {'brand': 'Chromium', 'version': ['109', '0', '5414', '119']} + ], + 'mobile': 1, + 'model': 'SM-G955U', + 'bitness': '64', + 'architecture': '' + } + } + } +}; + +const SERVER_RESPONSE = { + body: { + cid: 'testcid123', + 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 VIDEO_SERVER_RESPONSE = { + body: { + 'cid': '635509f7ff6642d368cb9837', + 'results': [{ + 'ad': '', + 'advertiserDomains': ['tagoras.io'], + 'exp': 60, + 'width': 545, + 'height': 307, + 'mediaType': 'video', + 'creativeId': '12610997325162499419', + 'price': 2, + 'cookies': [] + }] + } +}; + +const REQUEST = { + data: { + width: 300, + height: 250, + bidId: '2d52001cabd527' + } +}; + +function getTopWindowQueryParams() { + try { + const parsedUrl = utils.parseUrl(window.top.document.URL, {decodeSearchAsString: true}); + return parsedUrl.search; + } catch (e) { + return ''; + } +} + +describe('TagorasBidAdapter', 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'); + }); + + it('exists and contains media types', function () { + expect(adapter.supportedMediaTypes).to.exist.and.to.be.an('array').with.length(2); + expect(adapter.supportedMediaTypes).to.contain.members([BANNER, VIDEO]); + }); + }); + + describe('validate bid requests', function () { + it('should require cId', function () { + const isValid = adapter.isBidRequestValid({ + params: { + pId: 'pid' + } + }); + expect(isValid).to.be.false; + }); + + it('should require pId', function () { + const isValid = adapter.isBidRequestValid({ + params: { + cId: 'cid' + } + }); + expect(isValid).to.be.false; + }); + + it('should validate correctly', function () { + const isValid = adapter.isBidRequestValid({ + params: { + cId: 'cid', + pId: 'pid' + } + }); + expect(isValid).to.be.true; + }); + }); + + describe('build requests', function () { + let sandbox; + before(function () { + $$PREBID_GLOBAL$$.bidderSettings = { + tagoras: { + storageAllowed: true + } + }; + sandbox = sinon.sandbox.create(); + sandbox.stub(Date, 'now').returns(1000); + }); + + it('should build video request', function () { + const hashUrl = hashCode(BIDDER_REQUEST.refererInfo.page); + config.setConfig({ + bidderTimeout: 3000 + }); + const requests = adapter.buildRequests([VIDEO_BID], BIDDER_REQUEST); + expect(requests).to.have.length(1); + expect(requests[0]).to.deep.equal({ + method: 'POST', + url: `${createDomain(SUB_DOMAIN)}/prebid/multi/635509f7ff6642d368cb9837`, + data: { + adUnitCode: '63550ad1ff6642d368cba59dh5884270560', + bidFloor: 0.1, + bidId: '2d52001cabd527', + bidderVersion: adapter.version, + bidderRequestId: '12a8ae9ada9c13', + cb: 1000, + gdpr: 1, + gdprConsent: 'consent_string', + usPrivacy: 'consent_string', + gppString: 'gpp_string', + gppSid: [7], + prebidVersion: version, + transactionId: '56e184c6-bde9-497b-b9b9-cf47a61381ee', + bidRequestsCount: 4, + bidderRequestsCount: 3, + bidderWinsCount: 1, + bidderTimeout: 3000, + publisherId: '59ac17c192832d0011283fe3', + url: 'https%3A%2F%2Fwww.greatsite.com', + referrer: 'https://www.somereferrer.com', + res: `${window.top.screen.width}x${window.top.screen.height}`, + schain: VIDEO_BID.schain, + sizes: ['545x307'], + sua: { + 'source': 2, + 'platform': { + 'brand': 'Android', + 'version': ['8', '0', '0'] + }, + 'browsers': [ + {'brand': 'Not_A Brand', 'version': ['99', '0', '0', '0']}, + {'brand': 'Google Chrome', 'version': ['109', '0', '5414', '119']}, + {'brand': 'Chromium', 'version': ['109', '0', '5414', '119']} + ], + 'mobile': 1, + 'model': 'SM-G955U', + 'bitness': '64', + 'architecture': '' + }, + uniqueDealId: `${hashUrl}_${Date.now().toString()}`, + uqs: getTopWindowQueryParams(), + mediaTypes: { + video: { + api: [2], + context: 'instream', + linearity: 1, + maxduration: 60, + mimes: [ + 'video/mp4', + 'application/javascript' + ], + minduration: 0, + placement: 1, + playerSize: [[545, 307]], + protocols: [2, 3, 5, 6], + startdelay: 0 + } + }, + gpid: '' + } + }); + }); + + it('should build banner request for each size', function () { + const hashUrl = hashCode(BIDDER_REQUEST.refererInfo.page); + config.setConfig({ + bidderTimeout: 3000 + }); + 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, + gppString: 'gpp_string', + gppSid: [7], + usPrivacy: 'consent_string', + transactionId: 'c881914b-a3b5-4ecf-ad9c-1c2f37c6aabf', + bidRequestsCount: 4, + bidderRequestsCount: 3, + bidderWinsCount: 1, + bidderTimeout: 3000, + bidderRequestId: '1fdb5ff1b6eaa7', + sizes: ['300x250', '300x600'], + sua: { + 'source': 2, + 'platform': { + 'brand': 'Android', + 'version': ['8', '0', '0'] + }, + 'browsers': [ + {'brand': 'Not_A Brand', 'version': ['99', '0', '0', '0']}, + {'brand': 'Google Chrome', 'version': ['109', '0', '5414', '119']}, + {'brand': 'Chromium', 'version': ['109', '0', '5414', '119']} + ], + 'mobile': 1, + 'model': 'SM-G955U', + 'bitness': '64', + 'architecture': '' + }, + url: 'https%3A%2F%2Fwww.greatsite.com', + referrer: 'https://www.somereferrer.com', + cb: 1000, + bidFloor: 0.1, + bidId: '2d52001cabd527', + adUnitCode: 'div-gpt-ad-12345-0', + publisherId: '59ac17c192832d0011283fe3', + uniqueDealId: `${hashUrl}_${Date.now().toString()}`, + bidderVersion: adapter.version, + prebidVersion: version, + schain: BID.schain, + res: `${window.top.screen.width}x${window.top.screen.height}`, + mediaTypes: [BANNER], + gpid: '0123456789', + uqs: getTopWindowQueryParams(), + 'ext.param1': 'loremipsum', + 'ext.param2': 'dolorsitamet', + } + }); + }); + + after(function () { + $$PREBID_GLOBAL$$.bidderSettings = {}; + sandbox.restore(); + }); + }); + describe('getUserSyncs', function () { + it('should have valid user sync with iframeEnabled', function () { + const result = adapter.getUserSyncs({iframeEnabled: true}, [SERVER_RESPONSE]); + + expect(result).to.deep.equal([{ + type: 'iframe', + url: 'https://sync.tagoras.io/api/sync/iframe/?cid=testcid123&gdpr=0&gdpr_consent=&us_privacy=' + }]); + }); + + it('should have valid user sync with cid on response', function () { + const result = adapter.getUserSyncs({iframeEnabled: true}, [SERVER_RESPONSE]); + expect(result).to.deep.equal([{ + type: 'iframe', + url: 'https://sync.tagoras.io/api/sync/iframe/?cid=testcid123&gdpr=0&gdpr_consent=&us_privacy=' + }]); + }); + + it('should have valid user sync with pixelEnabled', function () { + const result = adapter.getUserSyncs({pixelEnabled: true}, [SERVER_RESPONSE]); + + expect(result).to.deep.equal([{ + 'url': 'https://sync.tagoras.io/api/sync/image/?cid=testcid123&gdpr=0&gdpr_consent=&us_privacy=', + 'type': 'image' + }]); + }); + + it('should generate url with consent data', function () { + const gdprConsent = { + gdprApplies: true, + consentString: 'consent_string' + }; + const uspConsent = 'usp_string'; + const gppConsent = { + gppString: 'gpp_string', + applicableSections: [7] + } + + const result = adapter.getUserSyncs({pixelEnabled: true}, [SERVER_RESPONSE], gdprConsent, uspConsent, gppConsent); + + expect(result).to.deep.equal([{ + 'url': 'https://sync.tagoras.io/api/sync/image/?cid=testcid123&gdpr=1&gdpr_consent=consent_string&us_privacy=usp_string&gpp=gpp_string&gpp_sid=7', + '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 banner 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 get meta from response metaData', function () { + const serverResponse = utils.deepClone(SERVER_RESPONSE); + serverResponse.body.results[0].metaData = { + advertiserDomains: ['tagoras.io'], + agencyName: 'Agency Name', + }; + const responses = adapter.interpretResponse(serverResponse, REQUEST); + expect(responses[0].meta).to.deep.equal({ + advertiserDomains: ['tagoras.io'], + agencyName: 'Agency Name' + }); + }); + + it('should return an array of interpreted video responses', function () { + const responses = adapter.interpretResponse(VIDEO_SERVER_RESPONSE, REQUEST); + expect(responses).to.have.length(1); + expect(responses[0]).to.deep.equal({ + requestId: '2d52001cabd527', + cpm: 2, + width: 545, + height: 307, + mediaType: 'video', + creativeId: '12610997325162499419', + currency: 'USD', + netRevenue: true, + ttl: 60, + vastXml: '', + meta: { + advertiserDomains: ['tagoras.io'] + } + }); + }); + + 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 () { + TEST_ID_SYSTEMS.forEach((idSystemProvider) => { + const id = Date.now().toString(); + const bid = utils.deepClone(BID); + + const userId = (function () { + switch (idSystemProvider) { + case 'lipb': + return {lipbid: id}; + case 'parrableId': + return {eid: id}; + case 'id5id': + return {uid: id}; + default: + return id; + } + })(); + + bid.userId = { + [idSystemProvider]: userId + }; + + it(`should include 'uid.${idSystemProvider}' in request params`, function () { + const requests = adapter.buildRequests([bid], BIDDER_REQUEST); + expect(requests[0].data[`uid.${idSystemProvider}`]).to.equal(id); + }); + }); + }); + + describe('alternate param names extractors', function () { + it('should return undefined when param not supported', function () { + const cid = extractCID({'c_id': '1'}); + const pid = extractPID({'p_id': '1'}); + const subDomain = extractSubDomain({'sub_domain': 'prebid'}); + expect(cid).to.be.undefined; + expect(pid).to.be.undefined; + expect(subDomain).to.be.undefined; + }); + + it('should return value when param supported', function () { + const cid = extractCID({'cID': '1'}); + const pid = extractPID({'Pid': '2'}); + const subDomain = extractSubDomain({'subDOMAIN': 'prebid'}); + expect(cid).to.be.equal('1'); + expect(pid).to.be.equal('2'); + expect(subDomain).to.be.equal('prebid'); + }); + }); + + describe('unique deal id', function () { + before(function () { + $$PREBID_GLOBAL$$.bidderSettings = { + tagoras: { + storageAllowed: true + } + }; + }); + after(function () { + $$PREBID_GLOBAL$$.bidderSettings = {}; + }); + const key = 'myKey'; + let uniqueDealId; + beforeEach(() => { + uniqueDealId = getUniqueDealId(key, 0); + }) + + it('should get current unique deal id', function (done) { + // waiting some time so `now` will become past + setTimeout(() => { + const current = getUniqueDealId(key); + expect(current).to.be.equal(uniqueDealId); + done(); + }, 200); + }); + + it('should get new unique deal id on expiration', function (done) { + setTimeout(() => { + const current = getUniqueDealId(key, 100); + expect(current).to.not.be.equal(uniqueDealId); + done(); + }, 200) + }); + }); + + describe('storage utils', function () { + before(function () { + $$PREBID_GLOBAL$$.bidderSettings = { + tagoras: { + storageAllowed: true + } + }; + }); + after(function () { + $$PREBID_GLOBAL$$.bidderSettings = {}; + }); + it('should get value from storage with create param', function () { + const now = Date.now(); + const clock = useFakeTimers({ + shouldAdvanceTime: true, + now + }); + setStorageItem('myKey', 2020); + const {value, created} = getStorageItem('myKey'); + expect(created).to.be.equal(now); + expect(value).to.be.equal(2020); + expect(typeof value).to.be.equal('number'); + expect(typeof created).to.be.equal('number'); + clock.restore(); + }); + + it('should get external stored value', function () { + const value = 'superman' + window.localStorage.setItem('myExternalKey', value); + const item = getStorageItem('myExternalKey'); + expect(item).to.be.equal(value); + }); + + it('should parse JSON value', function () { + const data = JSON.stringify({event: 'send'}); + const {event} = tryParseJSON(data); + expect(event).to.be.equal('send'); + }); + + it('should get original value on parse fail', function () { + const value = 21; + const parsed = tryParseJSON(value); + expect(typeof parsed).to.be.equal('number'); + expect(parsed).to.be.equal(value); + }); + }); +}); From d93eb4c8ee0e7b079619b6ac45b22329ccc836fd Mon Sep 17 00:00:00 2001 From: Shubham <127132399+shubhamc-ins@users.noreply.github.com> Date: Mon, 22 Jan 2024 23:54:41 +0530 Subject: [PATCH 157/246] Insticator Bid Adapter: support optional video params (#10969) * add support for video params acc to ortb2.5 * update the optionalParams logic for buildVideo * update check for protocols * udpate validation for video params and added test case --- modules/insticatorBidAdapter.js | 60 ++++++++++++++-- .../spec/modules/insticatorBidAdapter_spec.js | 70 +++++++++++++++++++ 2 files changed, 124 insertions(+), 6 deletions(-) diff --git a/modules/insticatorBidAdapter.js b/modules/insticatorBidAdapter.js index 193337d1503..32ec5b482c4 100644 --- a/modules/insticatorBidAdapter.js +++ b/modules/insticatorBidAdapter.js @@ -1,7 +1,7 @@ import {config} from '../src/config.js'; import {BANNER, VIDEO} from '../src/mediaTypes.js'; import {registerBidder} from '../src/adapters/bidderFactory.js'; -import {deepAccess, generateUUID, logError, isArray} from '../src/utils.js'; +import {deepAccess, generateUUID, logError, isArray, isInteger, isArrayOfNums} from '../src/utils.js'; import {getStorageManager} from '../src/storageManager.js'; import {find} from '../src/polyfill.js'; @@ -12,6 +12,35 @@ const USER_ID_COOKIE_EXP = 2592000000; // 30 days const BID_TTL = 300; // 5 minutes const GVLID = 910; +const isSubarray = (arr, target) => { + if (!isArrayOfNums(arr) || arr.length === 0) { + return false; + } + const targetSet = new Set(target); + return arr.every(el => targetSet.has(el)); +}; + +export const OPTIONAL_VIDEO_PARAMS = { + 'minduration': (value) => isInteger(value), + 'maxduration': (value) => isInteger(value), + 'protocols': (value) => isSubarray(value, [2, 3, 5, 6, 7, 8]), // protocols values supported by Inticator, according to the OpenRTB spec + 'startdelay': (value) => isInteger(value), + 'linearity': (value) => isInteger(value) && [1].includes(value), + 'skip': (value) => isInteger(value) && [1, 0].includes(value), + 'skipmin': (value) => isInteger(value), + 'skipafter': (value) => isInteger(value), + 'sequence': (value) => isInteger(value), + 'battr': (value) => isSubarray(value, [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17]), + 'maxextended': (value) => isInteger(value), + 'minbitrate': (value) => isInteger(value), + 'maxbitrate': (value) => isInteger(value), + 'playbackmethod': (value) => isSubarray(value, [1, 2, 3, 4]), + 'playbackend': (value) => isInteger(value) && [1, 2, 3].includes(value), + 'delivery': (value) => isSubarray(value, [1, 2, 3]), + 'pos': (value) => isInteger(value) && [0, 1, 2, 3, 4, 5, 6, 7].includes(value), + 'api': (value) => isSubarray(value, [1, 2, 3, 4, 5, 6, 7]), +}; + export const storage = getStorageManager({bidderCode: BIDDER_CODE}); config.setDefaults({ @@ -90,18 +119,24 @@ function buildVideo(bidRequest) { } } + const bidRequestVideo = deepAccess(bidRequest, 'mediaTypes.video'); + let optionalParams = {}; + for (const param in OPTIONAL_VIDEO_PARAMS) { + if (bidRequestVideo[param]) { + optionalParams[param] = bidRequestVideo[param]; + } + } + let videoObj = { placement, mimes, w, h, + ...optionalParams } if (plcmt) { - videoObj = { - ...videoObj, - plcmt - } + videoObj['plcmt'] = plcmt; } return videoObj } @@ -405,6 +440,20 @@ function validateVideo(bid) { return false; } + for (const param in OPTIONAL_VIDEO_PARAMS) { + if (video[param]) { + if (!OPTIONAL_VIDEO_PARAMS[param](video[param])) { + logError(`insticator: video ${param} is invalid or not supported by insticator`); + return false + } + } + } + + if (video.minduration && video.maxduration && video.minduration > video.maxduration) { + logError('insticator: video minduration is greater than maxduration'); + return false; + } + return true; } @@ -446,7 +495,6 @@ export const spec = { interpretResponse: function (serverResponse, request) { const bidderRequest = request.bidderRequest; const body = serverResponse.body; - if (!body || body.id !== bidderRequest.bidderRequestId) { logError('insticator: response id does not match bidderRequestId'); return []; diff --git a/test/spec/modules/insticatorBidAdapter_spec.js b/test/spec/modules/insticatorBidAdapter_spec.js index 36b6dd8fbf2..f9aa3b913c6 100644 --- a/test/spec/modules/insticatorBidAdapter_spec.js +++ b/test/spec/modules/insticatorBidAdapter_spec.js @@ -216,6 +216,76 @@ describe('InsticatorBidAdapter', function () { } })).to.be.true; }); + + it('should return true if optional video fields are valid', () => { + expect(spec.isBidRequestValid({ + ...bidRequest, + ...{ + mediaTypes: { + video: { + mimes: [ + 'video/mp4', + 'video/mpeg', + ], + playerSize: [250, 300], + placement: 1, + startdelay: 1, + skip: 1, + skipmin: 1, + skipafter: 1, + minduration: 1, + maxduration: 1, + api: [1, 2], + protocols: [2], + battr: [1, 2], + playbackmethod: [1, 2], + playbackend: 1, + delivery: [1, 2], + pos: 1, + }, + } + } + })).to.be.true; + }); + + it('should return false if optional video fields are not valid', () => { + expect(spec.isBidRequestValid({ + ...bidRequest, + ...{ + mediaTypes: { + video: { + mimes: [ + 'video/mp4', + 'video/mpeg', + ], + playerSize: [250, 300], + placement: 1, + startdelay: 'NaN', + }, + } + } + })).to.be.false; + }); + + it('should return false if video min duration > max duration', () => { + expect(spec.isBidRequestValid({ + ...bidRequest, + ...{ + mediaTypes: { + video: { + mimes: [ + 'video/mp4', + 'video/mpeg', + ], + playerSize: [250, 300], + placement: 1, + minduration: 5, + maxduration: 4, + }, + } + } + })).to.be.false; + }); }); describe('buildRequests', function () { From 4092a053f52655523fc7d8e8e4197a5a57dee883 Mon Sep 17 00:00:00 2001 From: olafbuitelaar Date: Mon, 22 Jan 2024 19:35:02 +0100 Subject: [PATCH 158/246] pass document instance to custom renderer (#10959) --- libraries/creativeRender/direct.js | 2 +- src/adRendering.js | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/libraries/creativeRender/direct.js b/libraries/creativeRender/direct.js index 0b4fb2f0a75..19d34e16844 100644 --- a/libraries/creativeRender/direct.js +++ b/libraries/creativeRender/direct.js @@ -52,7 +52,7 @@ export function renderAdDirect(doc, adId, options) { message: `renderAd was prevented from writing to the main document.` }) } else { - handleRender(renderFn, {adId, options: {clickUrl: options?.clickThrough}, bidResponse: bid}); + handleRender(renderFn, {adId, options: {clickUrl: options?.clickThrough}, bidResponse: bid, doc}); } } } catch (e) { diff --git a/src/adRendering.js b/src/adRendering.js index ac2639c4052..f8fe0044f9b 100644 --- a/src/adRendering.js +++ b/src/adRendering.js @@ -43,7 +43,7 @@ export function emitAdRenderSucceeded({ doc, bid, id }) { events.emit(AD_RENDER_SUCCEEDED, data); } -export function handleRender(renderFn, {adId, options, bidResponse}) { +export function handleRender(renderFn, {adId, options, bidResponse, doc}) { if (bidResponse == null) { emitAdRenderFail({ reason: constants.AD_RENDER_FAILED_REASON.CANNOT_FIND_AD, @@ -63,7 +63,7 @@ export function handleRender(renderFn, {adId, options, bidResponse}) { const {adId, ad, adUrl, width, height, renderer, cpm, originalCpm, mediaType} = bidResponse; // rendering for outstream safeframe if (isRendererRequired(renderer)) { - executeRenderer(renderer, bidResponse); + executeRenderer(renderer, bidResponse, doc); } else if (adId) { if (mediaType === VIDEO) { emitAdRenderFail({ From e589c4b6b86f7b854c6644c91640d2d9de2bc497 Mon Sep 17 00:00:00 2001 From: matthieularere-msq <63732822+matthieularere-msq@users.noreply.github.com> Date: Mon, 22 Jan 2024 19:49:45 +0100 Subject: [PATCH 159/246] Oxxion Rtd Module: tracking time to run (#10955) * Oxxion Rtd Module: tracking time to run * Oxxion Rtd Module: tracking time to run * Oxxion Rtd Module: tracking time to run --- modules/oxxionRtdProvider.js | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/modules/oxxionRtdProvider.js b/modules/oxxionRtdProvider.js index c979a10d00c..f968fc10e4b 100644 --- a/modules/oxxionRtdProvider.js +++ b/modules/oxxionRtdProvider.js @@ -23,6 +23,7 @@ function init(config, userConsent) { } function getAdUnits(reqBidsConfigObj, callback, config, userConsent) { + const moduleStarted = new Date(); logInfo(LOG_PREFIX + 'started with ', config); if (typeof config.params.threshold != 'undefined' && typeof config.params.samplingRate == 'number') { let filteredBids; @@ -47,6 +48,11 @@ function getAdUnits(reqBidsConfigObj, callback, config, userConsent) { }); } if (typeof callback == 'function') { callback(); } + const timeToRun = new Date() - moduleStarted; + logInfo(LOG_PREFIX + ' time to run: ' + timeToRun); + if (getRandomNumber(50) == 1) { + ajax('https://' + config.params.domain + '.oxxion.io/ova/time', null, JSON.stringify({'duration': timeToRun, 'auctionId': reqBidsConfigObj.auctionId}), {method: 'POST', withCredentials: true}); + } }).catch(error => logError(LOG_PREFIX, 'bidInterestError', error)); } } From 01ef25ee7328a04a09872ae3c2a7db6042d8b8d8 Mon Sep 17 00:00:00 2001 From: IQzoneIT Date: Mon, 22 Jan 2024 22:51:53 +0200 Subject: [PATCH 160/246] IQX Bid Adapter : initial release (#10952) * new adapter - IQX * chang maintainer --------- Co-authored-by: Chucky-choo --- modules/iqxBidAdapter.js | 207 +++++++++++ modules/iqxBidAdapter.md | 54 +++ test/spec/modules/iqxBidAdapter_spec.js | 455 ++++++++++++++++++++++++ 3 files changed, 716 insertions(+) create mode 100644 modules/iqxBidAdapter.js create mode 100644 modules/iqxBidAdapter.md create mode 100644 test/spec/modules/iqxBidAdapter_spec.js diff --git a/modules/iqxBidAdapter.js b/modules/iqxBidAdapter.js new file mode 100644 index 00000000000..1bef158c4a2 --- /dev/null +++ b/modules/iqxBidAdapter.js @@ -0,0 +1,207 @@ +import {config} from '../src/config.js'; +import {BANNER, VIDEO} from '../src/mediaTypes.js'; +import {registerBidder} from '../src/adapters/bidderFactory.js'; +import {parseSizesInput, isFn, deepAccess, getBidIdParameter, logError, isArray} from '../src/utils.js'; +import {getAdUnitSizes} from '../libraries/sizeUtils/sizeUtils.js'; + +const CUR = 'USD'; +const BIDDER_CODE = 'iqx'; +const ENDPOINT = 'https://pbjs.iqzonertb.live'; + +/** + * 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. + */ +function isBidRequestValid(req) { + if (req && typeof req.params !== 'object') { + logError('Params is not defined or is incorrect in the bidder settings'); + return false; + } + + if (!getBidIdParameter('env', req.params) || !getBidIdParameter('pid', req.params)) { + logError('Env or pid is not present in bidder params'); + return false; + } + + if (deepAccess(req, 'mediaTypes.video') && !isArray(deepAccess(req, 'mediaTypes.video.playerSize'))) { + logError('mediaTypes.video.playerSize is required for video'); + return false; + } + + return true; +} + +/** + * Make a server request from the list of BidRequests. + * + * @param {validBidRequest?pbjs_debug=trues[]} - an array of bids + * @return ServerRequest Info describing the request to the server. + */ +function buildRequests(validBidRequests, bidderRequest) { + const {refererInfo = {}, gdprConsent = {}, uspConsent} = bidderRequest; + const requests = validBidRequests.map(req => { + const request = {}; + request.bidId = req.bidId; + request.banner = deepAccess(req, 'mediaTypes.banner'); + request.auctionId = req.ortb2?.source?.tid; + request.transactionId = req.ortb2Imp?.ext?.tid; + request.sizes = parseSizesInput(getAdUnitSizes(req)); + request.schain = req.schain; + request.location = { + page: refererInfo.page, + location: refererInfo.location, + domain: refererInfo.domain, + whost: window.location.host, + ref: refererInfo.ref, + isAmp: refererInfo.isAmp + }; + request.device = { + ua: navigator.userAgent, + lang: navigator.language + }; + request.env = { + env: req.params.env, + pid: req.params.pid + }; + request.ortb2 = req.ortb2; + request.ortb2Imp = req.ortb2Imp; + request.tz = new Date().getTimezoneOffset(); + request.ext = req.params.ext; + request.bc = req.bidRequestsCount; + request.floor = getBidFloor(req); + + if (req.userIdAsEids && req.userIdAsEids.length !== 0) { + request.userEids = req.userIdAsEids; + } else { + request.userEids = []; + } + if (gdprConsent.gdprApplies) { + request.gdprApplies = Number(gdprConsent.gdprApplies); + request.consentString = gdprConsent.consentString; + } else { + request.gdprApplies = 0; + request.consentString = ''; + } + if (uspConsent) { + request.usPrivacy = uspConsent; + } else { + request.usPrivacy = ''; + } + if (config.getConfig('coppa')) { + request.coppa = 1; + } else { + request.coppa = 0; + } + + const video = deepAccess(req, 'mediaTypes.video'); + if (video) { + request.sizes = parseSizesInput(deepAccess(req, 'mediaTypes.video.playerSize')); + request.video = video; + } + + return request; + }); + + return { + method: 'POST', + url: ENDPOINT + '/bid', + data: JSON.stringify(requests), + withCredentials: true, + bidderRequest, + 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. + */ +function interpretResponse(serverResponse, {bidderRequest}) { + const response = []; + if (!isArray(deepAccess(serverResponse, 'body.data'))) { + return response; + } + + serverResponse.body.data.forEach(serverBid => { + const bid = { + requestId: bidderRequest.bidId, + dealId: bidderRequest.dealId || null, + ...serverBid + }; + response.push(bid); + }); + + return response; +} + +/** + * 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. + */ +function getUserSyncs(syncOptions, serverResponses, gdprConsent = {}, uspConsent = '') { + const syncs = []; + const pixels = deepAccess(serverResponses, '0.body.data.0.ext.pixels'); + + if ((syncOptions.iframeEnabled || syncOptions.pixelEnabled) && isArray(pixels) && pixels.length !== 0) { + const gdprFlag = `&gdpr=${gdprConsent.gdprApplies ? 1 : 0}`; + const gdprString = `&gdpr_consent=${encodeURIComponent((gdprConsent.consentString || ''))}`; + const usPrivacy = `us_privacy=${encodeURIComponent(uspConsent)}`; + + pixels.forEach(pixel => { + const [type, url] = pixel; + const sync = {type, url: `${url}&${usPrivacy}${gdprFlag}${gdprString}`}; + if (type === 'iframe' && syncOptions.iframeEnabled) { + syncs.push(sync) + } else if (type === 'image' && syncOptions.pixelEnabled) { + syncs.push(sync) + } + }); + } + + return syncs; +} + +/** + * Get valid floor value from getFloor fuction. + * + * @param {Object} bid Current bid request. + * @return {null|Number} Returns floor value when bid.getFloor is function and returns valid floor object with USD currency, otherwise returns null. + */ +export function getBidFloor(bid) { + if (!isFn(bid.getFloor)) { + return null; + } + + let floor = bid.getFloor({ + currency: CUR, + mediaType: '*', + size: '*' + }); + + if (typeof floor === 'object' && !isNaN(floor.floor) && floor.currency === CUR) { + return floor.floor; + } + + return null; +} + +export const spec = { + code: BIDDER_CODE, + aliases: ['iqx'], + supportedMediaTypes: [BANNER, VIDEO], + isBidRequestValid, + buildRequests, + interpretResponse, + getUserSyncs +} + +registerBidder(spec); diff --git a/modules/iqxBidAdapter.md b/modules/iqxBidAdapter.md new file mode 100644 index 00000000000..c48864c4306 --- /dev/null +++ b/modules/iqxBidAdapter.md @@ -0,0 +1,54 @@ +# Overview + +``` +Module Name: IQX Bidder Adapter +Module Type: IQX Bidder Adapter +Maintainer: it@iqzone.com +``` + +# Description + +Module that connects to iqx.com demand sources + +# Test Parameters +``` +var adUnits = [ + { + code: 'test-banner', + mediaTypes: { + banner: { + sizes: [[300, 250]], + } + }, + bids: [ + { + bidder: 'iqx', + params: { + env: 'iqx', + pid: '40', + ext: {} + } + } + ] + }, + { + code: 'test-video', + sizes: [ [ 640, 480 ] ], + mediaTypes: { + video: { + playerSize: [640, 480], + context: 'instream', + skipppable: true + } + }, + bids: [{ + bidder: 'iqx', + params: { + env: 'iqx', + pid: '40', + ext: {} + } + }] + } +]; +``` diff --git a/test/spec/modules/iqxBidAdapter_spec.js b/test/spec/modules/iqxBidAdapter_spec.js new file mode 100644 index 00000000000..f5e680c8e0b --- /dev/null +++ b/test/spec/modules/iqxBidAdapter_spec.js @@ -0,0 +1,455 @@ +import {expect} from 'chai'; +import {config} from 'src/config.js'; +import {spec, getBidFloor} from 'modules/iqxBidAdapter.js'; +import {deepClone} from 'src/utils'; + +const ENDPOINT = 'https://pbjs.iqzonertb.live'; + +const defaultRequest = { + adUnitCode: 'test', + bidId: '1', + requestId: 'qwerty', + ortb2: { + source: { + tid: 'auctionId' + } + }, + ortb2Imp: { + ext: { + tid: 'tr1', + } + }, + mediaTypes: { + banner: { + sizes: [ + [300, 250], + [300, 200] + ] + } + }, + bidder: 'iqx', + params: { + env: 'iqx', + pid: '40', + ext: {} + }, + bidRequestsCount: 1 +}; + +const defaultRequestVideo = deepClone(defaultRequest); +defaultRequestVideo.mediaTypes = { + video: { + playerSize: [640, 480], + context: 'instream', + skipppable: true + } +}; +describe('iqxBidAdapter', () => { + describe('isBidRequestValid', function () { + it('should return false when request params is missing', function () { + const invalidRequest = deepClone(defaultRequest); + delete invalidRequest.params; + expect(spec.isBidRequestValid(invalidRequest)).to.equal(false); + }); + + it('should return false when required env param is missing', function () { + const invalidRequest = deepClone(defaultRequest); + delete invalidRequest.params.env; + expect(spec.isBidRequestValid(invalidRequest)).to.equal(false); + }); + + it('should return false when required pid param is missing', function () { + const invalidRequest = deepClone(defaultRequest); + delete invalidRequest.params.pid; + expect(spec.isBidRequestValid(invalidRequest)).to.equal(false); + }); + + it('should return false when video.playerSize is missing', function () { + const invalidRequest = deepClone(defaultRequestVideo); + delete invalidRequest.mediaTypes.video.playerSize; + expect(spec.isBidRequestValid(invalidRequest)).to.equal(false); + }); + + it('should return true when required params found', function () { + expect(spec.isBidRequestValid(defaultRequest)).to.equal(true); + }); + }); + + describe('buildRequests', function () { + beforeEach(function () { + config.resetConfig(); + }); + + it('should send request with correct structure', function () { + const request = spec.buildRequests([defaultRequest], {}); + expect(request.method).to.equal('POST'); + expect(request.url).to.equal(ENDPOINT + '/bid'); + expect(request.options).to.have.property('contentType').and.to.equal('application/json'); + expect(request).to.have.property('data'); + }); + + it('should build basic request structure', function () { + const request = JSON.parse(spec.buildRequests([defaultRequest], {}).data)[0]; + expect(request).to.have.property('bidId').and.to.equal(defaultRequest.bidId); + expect(request).to.have.property('auctionId').and.to.equal(defaultRequest.ortb2.source.tid); + expect(request).to.have.property('transactionId').and.to.equal(defaultRequest.ortb2Imp.ext.tid); + expect(request).to.have.property('tz').and.to.equal(new Date().getTimezoneOffset()); + expect(request).to.have.property('bc').and.to.equal(1); + expect(request).to.have.property('floor').and.to.equal(null); + expect(request).to.have.property('banner').and.to.deep.equal({sizes: [[300, 250], [300, 200]]}); + expect(request).to.have.property('gdprApplies').and.to.equal(0); + expect(request).to.have.property('consentString').and.to.equal(''); + expect(request).to.have.property('userEids').and.to.deep.equal([]); + expect(request).to.have.property('usPrivacy').and.to.equal(''); + expect(request).to.have.property('coppa').and.to.equal(0); + expect(request).to.have.property('sizes').and.to.deep.equal(['300x250', '300x200']); + expect(request).to.have.property('ext').and.to.deep.equal({}); + expect(request).to.have.property('env').and.to.deep.equal({ + env: 'iqx', + pid: '40' + }); + expect(request).to.have.property('device').and.to.deep.equal({ + ua: navigator.userAgent, + lang: navigator.language + }); + }); + + it('should build request with schain', function () { + const schainRequest = deepClone(defaultRequest); + schainRequest.schain = { + validation: 'strict', + config: { + ver: '1.0' + } + }; + const request = JSON.parse(spec.buildRequests([schainRequest], {}).data)[0]; + expect(request).to.have.property('schain').and.to.deep.equal({ + validation: 'strict', + config: { + ver: '1.0' + } + }); + }); + + it('should build request with location', function () { + const bidderRequest = { + refererInfo: { + page: 'page', + location: 'location', + domain: 'domain', + ref: 'ref', + isAmp: false + } + }; + const request = JSON.parse(spec.buildRequests([defaultRequest], bidderRequest).data)[0]; + expect(request).to.have.property('location'); + const location = request.location; + expect(location).to.have.property('page').and.to.equal('page'); + expect(location).to.have.property('location').and.to.equal('location'); + expect(location).to.have.property('domain').and.to.equal('domain'); + expect(location).to.have.property('ref').and.to.equal('ref'); + expect(location).to.have.property('isAmp').and.to.equal(false); + }); + + it('should build request with ortb2 info', function () { + const ortb2Request = deepClone(defaultRequest); + ortb2Request.ortb2 = { + site: { + name: 'name' + } + }; + const request = JSON.parse(spec.buildRequests([ortb2Request], {}).data)[0]; + expect(request).to.have.property('ortb2').and.to.deep.equal({ + site: { + name: 'name' + } + }); + }); + + it('should build request with ortb2Imp info', function () { + const ortb2ImpRequest = deepClone(defaultRequest); + ortb2ImpRequest.ortb2Imp = { + ext: { + data: { + pbadslot: 'home1', + adUnitSpecificAttribute: '1' + } + } + }; + const request = JSON.parse(spec.buildRequests([ortb2ImpRequest], {}).data)[0]; + expect(request).to.have.property('ortb2Imp').and.to.deep.equal({ + ext: { + data: { + pbadslot: 'home1', + adUnitSpecificAttribute: '1' + } + } + }); + }); + + it('should build request with valid bidfloor', function () { + const bfRequest = deepClone(defaultRequest); + bfRequest.getFloor = () => ({floor: 5, currency: 'USD'}); + const request = JSON.parse(spec.buildRequests([bfRequest], {}).data)[0]; + expect(request).to.have.property('floor').and.to.equal(5); + }); + + it('should build request with gdpr consent data if applies', function () { + const bidderRequest = { + gdprConsent: { + gdprApplies: true, + consentString: 'qwerty' + } + }; + const request = JSON.parse(spec.buildRequests([defaultRequest], bidderRequest).data)[0]; + expect(request).to.have.property('gdprApplies').and.equals(1); + expect(request).to.have.property('consentString').and.equals('qwerty'); + }); + + it('should build request with usp consent data if applies', function () { + const bidderRequest = { + uspConsent: '1YA-' + }; + const request = JSON.parse(spec.buildRequests([defaultRequest], bidderRequest).data)[0]; + expect(request).to.have.property('usPrivacy').and.equals('1YA-'); + }); + + it('should build request with coppa 1', function () { + config.setConfig({ + coppa: true + }); + const request = JSON.parse(spec.buildRequests([defaultRequest], {}).data)[0]; + expect(request).to.have.property('coppa').and.equals(1); + }); + + it('should build request with extended ids', function () { + const idRequest = deepClone(defaultRequest); + idRequest.userIdAsEids = [ + {source: 'adserver.org', uids: [{id: 'TTD_ID_FROM_USER_ID_MODULE', atype: 1, ext: {rtiPartner: 'TDID'}}]}, + {source: 'pubcid.org', uids: [{id: 'pubCommonId_FROM_USER_ID_MODULE', atype: 1}]} + ]; + const request = JSON.parse(spec.buildRequests([idRequest], {}).data)[0]; + expect(request).to.have.property('userEids').and.deep.equal(idRequest.userIdAsEids); + }); + + it('should build request with video', function () { + const request = JSON.parse(spec.buildRequests([defaultRequestVideo], {}).data)[0]; + expect(request).to.have.property('video').and.to.deep.equal({ + playerSize: [640, 480], + context: 'instream', + skipppable: true + }); + expect(request).to.have.property('sizes').and.to.deep.equal(['640x480']); + }); + }); + + describe('interpretResponse', function () { + it('should return empty bids', function () { + const serverResponse = { + body: { + data: null + } + }; + + const invalidResponse = spec.interpretResponse(serverResponse, {}); + expect(invalidResponse).to.be.an('array').that.is.empty; + }); + + it('should interpret valid response', function () { + const serverResponse = { + body: { + data: [{ + requestId: 'qwerty', + cpm: 1, + currency: 'USD', + width: 300, + height: 250, + ttl: 600, + meta: { + advertiserDomains: ['iqx'] + }, + ext: { + pixels: [ + ['iframe', 'surl1'], + ['image', 'surl2'], + ] + } + }] + } + }; + + const validResponse = spec.interpretResponse(serverResponse, {bidderRequest: defaultRequest}); + const bid = validResponse[0]; + expect(validResponse).to.be.an('array').that.is.not.empty; + expect(bid.requestId).to.equal('qwerty'); + expect(bid.cpm).to.equal(1); + expect(bid.currency).to.equal('USD'); + expect(bid.width).to.equal(300); + expect(bid.height).to.equal(250); + expect(bid.ttl).to.equal(600); + expect(bid.meta).to.deep.equal({advertiserDomains: ['iqx']}); + }); + + it('should interpret valid banner response', function () { + const serverResponse = { + body: { + data: [{ + requestId: 'qwerty', + cpm: 1, + currency: 'USD', + width: 300, + height: 250, + ttl: 600, + mediaType: 'banner', + creativeId: 'xe-demo-banner', + ad: 'ad', + meta: {} + }] + } + }; + + const validResponseBanner = spec.interpretResponse(serverResponse, {bidderRequest: defaultRequest}); + const bid = validResponseBanner[0]; + expect(validResponseBanner).to.be.an('array').that.is.not.empty; + expect(bid.mediaType).to.equal('banner'); + expect(bid.creativeId).to.equal('xe-demo-banner'); + expect(bid.ad).to.equal('ad'); + }); + + it('should interpret valid video response', function () { + const serverResponse = { + body: { + data: [{ + requestId: 'qwerty', + cpm: 1, + currency: 'USD', + width: 600, + height: 480, + ttl: 600, + mediaType: 'video', + creativeId: 'xe-demo-video', + ad: 'vast-xml', + meta: {} + }] + } + }; + + const validResponseBanner = spec.interpretResponse(serverResponse, {bidderRequest: defaultRequestVideo}); + const bid = validResponseBanner[0]; + expect(validResponseBanner).to.be.an('array').that.is.not.empty; + expect(bid.mediaType).to.equal('video'); + expect(bid.creativeId).to.equal('xe-demo-video'); + expect(bid.ad).to.equal('vast-xml'); + }); + }); + + describe('getUserSyncs', function () { + it('shoukd handle no params', function () { + const opts = spec.getUserSyncs({}, []); + expect(opts).to.be.an('array').that.is.empty; + }); + + it('should return empty if sync is not allowed', function () { + const opts = spec.getUserSyncs({iframeEnabled: false, pixelEnabled: false}); + expect(opts).to.be.an('array').that.is.empty; + }); + + it('should allow iframe sync', function () { + const opts = spec.getUserSyncs({iframeEnabled: true, pixelEnabled: false}, [{ + body: { + data: [{ + requestId: 'qwerty', + ext: { + pixels: [ + ['iframe', 'surl1?a=b'], + ['image', 'surl2?a=b'], + ] + } + }] + } + }]); + expect(opts.length).to.equal(1); + expect(opts[0].type).to.equal('iframe'); + expect(opts[0].url).to.equal('surl1?a=b&us_privacy=&gdpr=0&gdpr_consent='); + }); + + it('should allow pixel sync', function () { + const opts = spec.getUserSyncs({iframeEnabled: false, pixelEnabled: true}, [{ + body: { + data: [{ + requestId: 'qwerty', + ext: { + pixels: [ + ['iframe', 'surl1?a=b'], + ['image', 'surl2?a=b'], + ] + } + }] + } + }]); + expect(opts.length).to.equal(1); + expect(opts[0].type).to.equal('image'); + expect(opts[0].url).to.equal('surl2?a=b&us_privacy=&gdpr=0&gdpr_consent='); + }); + + it('should allow pixel sync and parse consent params', function () { + const opts = spec.getUserSyncs({iframeEnabled: false, pixelEnabled: true}, [{ + body: { + data: [{ + requestId: 'qwerty', + ext: { + pixels: [ + ['iframe', 'surl1?a=b'], + ['image', 'surl2?a=b'], + ] + } + }] + } + }], { + gdprApplies: 1, + consentString: '1YA-' + }); + expect(opts.length).to.equal(1); + expect(opts[0].type).to.equal('image'); + expect(opts[0].url).to.equal('surl2?a=b&us_privacy=&gdpr=1&gdpr_consent=1YA-'); + }); + }); + + describe('getBidFloor', function () { + 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: 'USD'}) + }; + const result = getBidFloor(bid); + expect(result).to.be.null; + }); + + it('should return null when currency is not USD', () => { + const bid = { + getFloor: () => ({floor: 5, currency: 'EUR'}) + }; + const result = getBidFloor(bid); + expect(result).to.be.null; + }); + + it('should return floor value when everything is correct', () => { + const bid = { + getFloor: () => ({floor: 5, currency: 'USD'}) + }; + const result = getBidFloor(bid); + expect(result).to.equal(5); + }); + }); +}) From a8d36a6b0114f72bad81ca5b6821cf65ab42f71f Mon Sep 17 00:00:00 2001 From: Taro FURUKAWA <6879289+0tarof@users.noreply.github.com> Date: Tue, 23 Jan 2024 19:52:56 +0900 Subject: [PATCH 161/246] AJA Bid Adapter: add Global Placement ID support, remove native/video ad support (#10945) * add gpid support, remove native/video ad support * also added cdep support --- modules/ajaBidAdapter.js | 157 +++++++----------------- modules/ajaBidAdapter.md | 60 +-------- test/spec/modules/ajaBidAdapter_spec.js | 145 ++-------------------- 3 files changed, 55 insertions(+), 307 deletions(-) diff --git a/modules/ajaBidAdapter.js b/modules/ajaBidAdapter.js index 9049197e565..9e2d4efb5ff 100644 --- a/modules/ajaBidAdapter.js +++ b/modules/ajaBidAdapter.js @@ -1,7 +1,6 @@ -import {createTrackPixelHtml, logError, logWarn, deepAccess, getBidIdParameter} from '../src/utils.js'; -import { Renderer } from '../src/Renderer.js'; +import {createTrackPixelHtml, logError, getBidIdParameter} from '../src/utils.js'; import { registerBidder } from '../src/adapters/bidderFactory.js'; -import { VIDEO, BANNER, NATIVE } from '../src/mediaTypes.js'; +import { BANNER } from '../src/mediaTypes.js'; import {tryAppendQueryString} from '../libraries/urlUtils/urlUtils.js'; const BidderCode = 'aja'; @@ -25,7 +24,7 @@ const BannerSizeMap = { export const spec = { code: BidderCode, - supportedMediaTypes: [VIDEO, BANNER, NATIVE], + supportedMediaTypes: [BANNER], /** * Determines whether or not the given bid has all the params needed to make a valid request. @@ -51,12 +50,20 @@ export const spec = { for (let i = 0, len = validBidRequests.length; i < len; i++) { const bidRequest = validBidRequests[i]; + if ( + (bidRequest.mediaTypes?.native || bidRequest.mediaTypes?.video) && + bidRequest.mediaTypes?.banner) { + continue + } + let queryString = ''; const asi = getBidIdParameter('asi', bidRequest.params); queryString = tryAppendQueryString(queryString, 'asi', asi); queryString = tryAppendQueryString(queryString, 'skt', SDKType); + queryString = tryAppendQueryString(queryString, 'gpid', bidRequest.ortb2Imp?.ext?.gpid) queryString = tryAppendQueryString(queryString, 'tid', bidRequest.ortb2Imp?.ext?.tid) + queryString = tryAppendQueryString(queryString, 'cdep', bidRequest.ortb2?.device?.ext?.cdep) queryString = tryAppendQueryString(queryString, 'prebid_id', bidRequest.bidId); queryString = tryAppendQueryString(queryString, 'prebid_ver', '$prebid.version$'); @@ -64,19 +71,8 @@ export const spec = { queryString = tryAppendQueryString(queryString, 'page_url', pageUrl); } - const banner = deepAccess(bidRequest, `mediaTypes.${BANNER}`) - if (banner) { - const adFormatIDs = []; - for (const size of banner.sizes || []) { - if (size.length !== 2) { - continue - } - - const adFormatID = BannerSizeMap[`${size[0]}x${size[1]}`]; - if (adFormatID) { - adFormatIDs.push(adFormatID); - } - } + const adFormatIDs = pickAdFormats(bidRequest) + if (adFormatIDs && adFormatIDs.length > 0) { queryString = tryAppendQueryString(queryString, 'ad_format_ids', adFormatIDs.join(',')); } @@ -87,7 +83,7 @@ export const spec = { })); } - const sua = deepAccess(bidRequest, 'ortb2.device.sua'); + const sua = bidRequest.ortb2?.device?.sua if (sua) { queryString = tryAppendQueryString(queryString, 'sua', JSON.stringify(sua)); } @@ -110,9 +106,17 @@ export const spec = { } const ad = bidderResponseBody.ad; + if (AdType.Banner !== ad.ad_type) { + return [] + } + const bannerAd = bidderResponseBody.ad.banner; const bid = { requestId: ad.prebid_id, + mediaType: BANNER, + ad: bannerAd.tag, + width: bannerAd.w, + height: bannerAd.h, cpm: ad.price, creativeId: ad.creative_id, dealId: ad.deal_id, @@ -120,80 +124,16 @@ export const spec = { netRevenue: true, ttl: 300, // 5 minutes meta: { - advertiserDomains: [] + advertiserDomains: bannerAd.adomain, }, } - - if (AdType.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 - }); - - Array.prototype.push.apply(bid.meta.advertiserDomains, videoAd.adomain) - } else if (AdType.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 = createTrackPixelHtml(impTracker); + bid.ad += tracker; }); - try { - bannerAd.imps.forEach(impTracker => { - const tracker = createTrackPixelHtml(impTracker); - bid.ad += tracker; - }); - } catch (error) { - logError('Error appending tracking pixel', error); - } - - Array.prototype.push.apply(bid.meta.advertiserDomains, bannerAd.adomain) - } else if (AdType.Native === ad.ad_type) { - const nativeAds = ad.native.template_and_ads.ads; - if (nativeAds.length === 0) { - return []; - } - - const nativeAd = nativeAds[0]; - 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) - }; - } - - Array.prototype.push.apply(bid.meta.advertiserDomains, nativeAd.adomain) + } catch (error) { + logError('Error appending tracking pixel', error); } return [bid]; @@ -229,34 +169,23 @@ export const spec = { }, } -function newRenderer(bidderResponse) { - const renderer = Renderer.install({ - id: bidderResponse.ad.prebid_id, - url: bidderResponse.ad.video.purl, - loaded: false, - }); +function pickAdFormats(bidRequest) { + let sizes = bidRequest.sizes || [] + sizes.push(...(bidRequest.mediaTypes?.banner?.sizes || [])) - try { - renderer.setRender(outstreamRender); - } catch (err) { - logWarn('Prebid Error calling setRender on newRenderer', err); - } + const adFormatIDs = []; + for (const size of sizes) { + if (size.length !== 2) { + continue + } - return renderer; -} + const adFormatID = BannerSizeMap[`${size[0]}x${size[1]}`]; + if (adFormatID) { + adFormatIDs.push(adFormatID); + } + } -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 - }); - }); + return [...new Set(adFormatIDs)] } registerBidder(spec); diff --git a/modules/ajaBidAdapter.md b/modules/ajaBidAdapter.md index 66155875f4d..92ffecaeb9f 100644 --- a/modules/ajaBidAdapter.md +++ b/modules/ajaBidAdapter.md @@ -8,7 +8,7 @@ Maintainer: ssp_support@aja-kk.co.jp # Description Connects to Aja exchange for bids. -Aja bid adapter supports Banner and Outstream Video. +Aja bid adapter supports Banner. # Test Parameters ```js @@ -29,64 +29,6 @@ var adUnits = [ asi: 'tk82gbLmg' } }] - }, - // Video outstream adUnit - { - code: 'prebid_video', - mediaTypes: { - video: { - context: 'outstream', - playerSize: [300, 250] - } - }, - bids: [{ - bidder: 'aja', - params: { - asi: '1-KwEG_iR' - } - }] - }, - // Native adUnit - { - code: 'prebid_native', - mediaTypes: { - native: { - image: { - required: true, - sendId: false - }, - title: { - required: true, - sendId: true - }, - sponsoredBy: { - required: false, - sendId: true - }, - clickUrl: { - required: false, - sendId: true - }, - body: { - required: false, - sendId: true - }, - icon: { - required: false, - sendId: false - }, - privacyLink: { - required: true, - sendId: true - }, - } - }, - bids: [{ - bidder: 'aja', - params: { - asi: 'qxueUGliR' - } - }] } ]; ``` diff --git a/test/spec/modules/ajaBidAdapter_spec.js b/test/spec/modules/ajaBidAdapter_spec.js index 7cf5698f7d4..3137c9dc24e 100644 --- a/test/spec/modules/ajaBidAdapter_spec.js +++ b/test/spec/modules/ajaBidAdapter_spec.js @@ -62,8 +62,17 @@ describe('AjaAdapter', function () { model: 'SM-G955U', bitness: '64', architecture: '' + }, + ext: { + cdep: 'example_label_1' } } + }, + ortb2Imp: { + ext: { + tid: 'cea1eb09-d970-48dc-8585-634d3a7b0544', + gpid: '/1111/homepage#300x250' + } } } ]; @@ -78,7 +87,7 @@ describe('AjaAdapter', 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&sua=%7B%22source%22%3A2%2C%22platform%22%3A%7B%22brand%22%3A%22Android%22%2C%22version%22%3A%5B%228%22%2C%220%22%2C%220%22%5D%7D%2C%22browsers%22%3A%5B%7B%22brand%22%3A%22Not_A%20Brand%22%2C%22version%22%3A%5B%2299%22%2C%220%22%2C%220%22%2C%220%22%5D%7D%2C%7B%22brand%22%3A%22Google%20Chrome%22%2C%22version%22%3A%5B%22109%22%2C%220%22%2C%225414%22%2C%22119%22%5D%7D%2C%7B%22brand%22%3A%22Chromium%22%2C%22version%22%3A%5B%22109%22%2C%220%22%2C%225414%22%2C%22119%22%5D%7D%5D%2C%22mobile%22%3A1%2C%22model%22%3A%22SM-G955U%22%2C%22bitness%22%3A%2264%22%2C%22architecture%22%3A%22%22%7D&'); + expect(requests[0].data).to.equal('asi=123456&skt=5&gpid=%2F1111%2Fhomepage%23300x250&tid=cea1eb09-d970-48dc-8585-634d3a7b0544&cdep=example_label_1&prebid_id=30b31c1838de1e&prebid_ver=$prebid.version$&page_url=https%3A%2F%2Fhoge.com&ad_format_ids=2&sua=%7B%22source%22%3A2%2C%22platform%22%3A%7B%22brand%22%3A%22Android%22%2C%22version%22%3A%5B%228%22%2C%220%22%2C%220%22%5D%7D%2C%22browsers%22%3A%5B%7B%22brand%22%3A%22Not_A%20Brand%22%2C%22version%22%3A%5B%2299%22%2C%220%22%2C%220%22%2C%220%22%5D%7D%2C%7B%22brand%22%3A%22Google%20Chrome%22%2C%22version%22%3A%5B%22109%22%2C%220%22%2C%225414%22%2C%22119%22%5D%7D%2C%7B%22brand%22%3A%22Chromium%22%2C%22version%22%3A%5B%22109%22%2C%220%22%2C%225414%22%2C%22119%22%5D%7D%5D%2C%22mobile%22%3A1%2C%22model%22%3A%22SM-G955U%22%2C%22bitness%22%3A%2264%22%2C%22architecture%22%3A%22%22%7D&'); }); }); @@ -116,7 +125,7 @@ describe('AjaAdapter', 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&eids=%7B%22eids%22%3A%5B%7B%22source%22%3A%22pubcid.org%22%2C%22uids%22%3A%5B%7B%22id%22%3A%22some-random-id-value%22%2C%22atype%22%3A1%7D%5D%7D%5D%7D&'); + expect(requests[0].data).to.equal('asi=123456&skt=5&prebid_id=30b31c1838de1e&prebid_ver=$prebid.version$&page_url=https%3A%2F%2Fhoge.com&ad_format_ids=2&eids=%7B%22eids%22%3A%5B%7B%22source%22%3A%22pubcid.org%22%2C%22uids%22%3A%5B%7B%22id%22%3A%22some-random-id-value%22%2C%22atype%22%3A1%7D%5D%7D%5D%7D&'); }); }); @@ -173,138 +182,6 @@ describe('AjaAdapter', function () { 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, - 'adomain': [ - 'www.example.com' - ] - } - }, - '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, - 'adomain': [ - 'www.example.com' - ] - } - ] - } - } - }, - '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': [ - 'www.example.com' - ] - } - } - ]; - - 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, From 48d8766a40be2e8183310fc06fb7ce63d5622590 Mon Sep 17 00:00:00 2001 From: AdFusionPrebid <144114711+AdFusionPrebid@users.noreply.github.com> Date: Tue, 23 Jan 2024 12:11:29 +0100 Subject: [PATCH 162/246] AdFusion Bid Adapter : currency support (#10938) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * adfusion bid adapter test * Add adapter and docs * add currency support * kick of integration tests --------- Co-authored-by: Łukasz Co-authored-by: Chris Huie --- modules/adfusionBidAdapter.js | 30 ++++++++++++++++ test/spec/modules/adfusionBidAdapter_spec.js | 37 ++++++++++++++++++-- 2 files changed, 65 insertions(+), 2 deletions(-) diff --git a/modules/adfusionBidAdapter.js b/modules/adfusionBidAdapter.js index b3638159c2a..a206ee5e899 100644 --- a/modules/adfusionBidAdapter.js +++ b/modules/adfusionBidAdapter.js @@ -5,6 +5,7 @@ import * as utils from '../src/utils.js'; const adpterVersion = '1.0'; export const REQUEST_URL = 'https://spicyrtb.com/auction/prebid'; +export const DEFAULT_CURRENCY = 'USD'; export const spec = { code: 'adfusion', @@ -23,6 +24,17 @@ const converter = ortbConverter({ context: { netRevenue: true, ttl: 300, + currency: DEFAULT_CURRENCY, + }, + imp(buildImp, bidRequest, context) { + const imp = buildImp(bidRequest, context); + const floor = getBidFloor(bidRequest); + if (floor) { + imp.bidfloor = floor; + imp.bidfloorcur = DEFAULT_CURRENCY; + } + + return imp; }, request(buildRequest, imps, bidderRequest, context) { const req = buildRequest(imps, bidderRequest, context); @@ -88,3 +100,21 @@ function isBannerBid(bid) { function interpretResponse(resp, req) { return converter.fromORTB({ request: req.data, response: resp.body }); } + +function getBidFloor(bid) { + if (utils.isFn(bid.getFloor)) { + let floor = bid.getFloor({ + currency: DEFAULT_CURRENCY, + mediaType: '*', + size: '*', + }); + if ( + utils.isPlainObject(floor) && + !isNaN(floor.floor) && + floor.currency === DEFAULT_CURRENCY + ) { + return floor.floor; + } + } + return null; +} diff --git a/test/spec/modules/adfusionBidAdapter_spec.js b/test/spec/modules/adfusionBidAdapter_spec.js index 638831c33f3..82705b727b4 100644 --- a/test/spec/modules/adfusionBidAdapter_spec.js +++ b/test/spec/modules/adfusionBidAdapter_spec.js @@ -1,6 +1,7 @@ import { expect } from 'chai'; import { spec } from 'modules/adfusionBidAdapter'; import 'modules/priceFloors.js'; +import 'modules/currency.js'; import { newBidder } from 'src/adapters/bidderFactory'; describe('adfusionBidAdapter', function () { @@ -24,7 +25,7 @@ describe('adfusionBidAdapter', function () { transactionId: 'test-transactionId-1', }; - it('should return true when required params found', function () { + it('should return true when required params are found', function () { expect(spec.isBidRequestValid(bid)).to.equal(true); }); @@ -36,7 +37,7 @@ describe('adfusionBidAdapter', function () { }); describe('buildRequests', function () { - let bidRequests, bidderRequest; + let bidRequests, bannerBidRequest, bidderRequest; beforeEach(function () { bidRequests = [ { @@ -75,6 +76,25 @@ describe('adfusionBidAdapter', function () { transactionId: 'test-transactionId-2', }, ]; + bannerBidRequest = { + bidder: 'adfusion', + params: { + accountId: 1234, + }, + mediaTypes: { + banner: { + sizes: [ + [300, 250], + [300, 600], + ], + }, + }, + adUnitCode: '/adunit-code/test-path', + bidId: 'test-bid-id-1', + bidderRequestId: 'test-bid-request-1', + auctionId: 'test-auction-1', + transactionId: 'test-transactionId-1', + }; bidderRequest = { refererInfo: {} }; }); @@ -89,9 +109,22 @@ describe('adfusionBidAdapter', function () { expect(request).to.be.an('array'); expect(request[0].data).to.be.an('object'); expect(request[0].method).to.equal('POST'); + expect(request[0].currency).to.not.equal('USD'); expect(request[0].url).to.not.equal(''); expect(request[0].url).to.not.equal(undefined); expect(request[0].url).to.not.equal(null); }); + + it('should add bid floor', function () { + let bidRequest = Object.assign({}, bannerBidRequest); + let payload = spec.buildRequests([bidRequest], bidderRequest)[0].data; + expect(payload.imp[0].bidfloorcur).to.not.exist; + + let getFloorResponse = { currency: 'USD', floor: 3 }; + bidRequest.getFloor = () => getFloorResponse; + payload = spec.buildRequests([bidRequest], bidderRequest)[0].data; + expect(payload.imp[0].bidfloor).to.equal(3); + expect(payload.imp[0].bidfloorcur).to.equal('USD'); + }); }); }); From 65fd60588e98c96fd15e537f893f5122420ec30a Mon Sep 17 00:00:00 2001 From: ramyferjaniadot <90328697+ramyferjaniadot@users.noreply.github.com> Date: Tue, 23 Jan 2024 16:06:23 +0100 Subject: [PATCH 163/246] [TECH-6244] fix: add gvlid to adot adapter (#11) (#10975) --- modules/adotBidAdapter.js | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/modules/adotBidAdapter.js b/modules/adotBidAdapter.js index c34af4d3d17..b48a7ec43b0 100644 --- a/modules/adotBidAdapter.js +++ b/modules/adotBidAdapter.js @@ -9,6 +9,7 @@ import { convertOrtbRequestToProprietaryNative } from '../src/native.js'; const BIDDER_CODE = 'adot'; const ADAPTER_VERSION = 'v2.0.0'; +const GVLID = 272; const BID_METHOD = 'POST'; const BIDDER_URL = 'https://dsp.adotmob.com/headerbidding{PUBLISHER_PATH}/bidrequest'; const REQUIRED_VIDEO_PARAMS = ['mimes', 'protocols']; @@ -635,7 +636,8 @@ export const spec = { isBidRequestValid, buildRequests, interpretResponse, - getFloor + getFloor, + gvlid: GVLID }; registerBidder(spec); From 5a73e16f6696b98c731c079bc3138c9e881ee129 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Steffen=20M=C3=BCller?= Date: Tue, 23 Jan 2024 19:56:03 +0100 Subject: [PATCH 164/246] Agma Analytics Module : fix getting global config data (#10968) * Fix getting global config data * Increase internal version number --- modules/agmaAnalyticsAdapter.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/modules/agmaAnalyticsAdapter.js b/modules/agmaAnalyticsAdapter.js index afbc3e771ec..e43dee063c5 100644 --- a/modules/agmaAnalyticsAdapter.js +++ b/modules/agmaAnalyticsAdapter.js @@ -17,7 +17,7 @@ import { config } from '../src/config.js'; const GVLID = 1122; const ModuleCode = 'agma'; const analyticsType = 'endpoint'; -const scriptVersion = '1.7.0'; +const scriptVersion = '1.7.1'; const batchDelayInMs = 1000; const agmaURL = 'https://pbc.agma-analytics.de/v1'; const pageViewId = generateUUID(); @@ -61,7 +61,7 @@ export const getOrtb2Data = (options) => { } } try { - const configData = config.getConfig('agma'); + const configData = config.getConfig(); // try to fallback to global config if (configData.ortb2) { site = site || configData.ortb2.site; From c6a9ebce5dc7db3599ca39c11b511b3916591e64 Mon Sep 17 00:00:00 2001 From: Marco Cosentino <807030+cosenmarco@users.noreply.github.com> Date: Tue, 23 Jan 2024 23:37:44 +0100 Subject: [PATCH 165/246] ID5 ID Module : ID5 will be able to optionally delegate its logic to an external module (#10742) * id-7317 Adding ability to load exernal module by param configuration * id-7317 Fixing bugs with id5 external module * id-7313 Addinf documentation to new externalModuleUrl parameter * id-7317 Typo * id-7317 Fix Lint error * id-7317 Some improvements from PR * id-7317 Some test iprovements * id-7317 Using loadExternalScript() utility instead of loading the script directly * id-7317 Lint error * id-7317 Fixing nb increments * ID5 User Id module - pass gpp consent data to external module --------- Co-authored-by: abazylewicz Co-authored-by: abazylewicz-id5 <106807984+abazylewicz-id5@users.noreply.github.com> --- modules/id5IdSystem.js | 242 ++++-- modules/id5IdSystem.md | 2 + src/adloader.js | 3 +- test/spec/modules/id5IdSystem_spec.js | 1033 +++++++++++++------------ 4 files changed, 713 insertions(+), 567 deletions(-) diff --git a/modules/id5IdSystem.js b/modules/id5IdSystem.js index 5ed12d8def2..96ae5386de6 100644 --- a/modules/id5IdSystem.js +++ b/modules/id5IdSystem.js @@ -15,12 +15,14 @@ import { logWarn, safeJSONParse } from '../src/utils.js'; -import {ajax} from '../src/ajax.js'; +import {fetch} from '../src/ajax.js'; import {submodule} from '../src/hook.js'; import {getRefererInfo} from '../src/refererDetection.js'; import {getStorageManager} from '../src/storageManager.js'; import {uspDataHandler, gppDataHandler} from '../src/adapterManager.js'; import {MODULE_TYPE_UID} from '../src/activities/modules.js'; +import { GreedyPromise } from '../src/utils/promise.js'; +import { loadExternalScript } from '../src/adloader.js'; const MODULE_NAME = 'id5Id'; const GVLID = 131; @@ -37,6 +39,70 @@ const LEGACY_COOKIE_NAMES = ['pbjs-id5id', 'id5id.1st', 'id5id']; export const storage = getStorageManager({moduleType: MODULE_TYPE_UID, moduleName: MODULE_NAME}); +/** + * @typedef {Object} IdResponse + * @property {string} [universal_uid] - The encrypted ID5 ID to pass to bidders + * @property {Object} [ext] - The extensions object to pass to bidders + * @property {Object} [ab_testing] - A/B testing configuration +*/ + +/** + * @typedef {Object} FetchCallConfig + * @property {string} [url] - The URL for the fetch endpoint + * @property {Object} [overrides] - Overrides to apply to fetch parameters +*/ + +/** + * @typedef {Object} ExtensionsCallConfig + * @property {string} [url] - The URL for the extensions endpoint + * @property {string} [method] - Overrides the HTTP method to use to make the call + * @property {Object} [body] - Specifies a body to pass to the extensions endpoint +*/ + +/** + * @typedef {Object} DynamicConfig + * @property {FetchCallConfig} [fetchCall] - The fetch call configuration + * @property {ExtensionsCallConfig} [extensionsCall] - The extensions call configuration +*/ + +/** + * @typedef {Object} ABTestingConfig + * @property {boolean} enabled - Tells whether A/B testing is enabled for this instance + * @property {number} controlGroupPct - A/B testing probability + */ + +/** + * @typedef {Object} Multiplexing + * @property {boolean} [disabled] - Disable multiplexing (instance will work in single mode) + */ + +/** + * @typedef {Object} Diagnostics + * @property {boolean} [publishingDisabled] - Disable diagnostics publishing + * @property {number} [publishAfterLoadInMsec] - Delay in ms after script load after which collected diagnostics are published + * @property {boolean} [publishBeforeWindowUnload] - When true, diagnostics publishing is triggered on Window 'beforeunload' event + * @property {number} [publishingSampleRatio] - Diagnostics publishing sample ratio + */ + +/** + * @typedef {Object} Segment + * @property {string} [destination] - GVL ID or ID5-XX Partner ID. Mandatory + * @property {Array} [ids] - The segment IDs to push. Must contain at least one segment ID. + */ + +/** + * @typedef {Object} Id5PrebidConfig + * @property {number} partner - The ID5 partner ID + * @property {string} pd - The ID5 partner data string + * @property {ABTestingConfig} abTesting - The A/B testing configuration + * @property {boolean} disableExtensions - Disabled extensions call + * @property {string} [externalModuleUrl] - URL for the id5 prebid external module + * @property {Multiplexing} [multiplexing] - Multiplexing options. Only supported when loading the external module. + * @property {Diagnostics} [diagnostics] - Diagnostics options. Supported only in multiplexing + * @property {Array} [segments] - A list of segments to push to partners. Supported only in multiplexing. + * @property {boolean} [disableUaHints] - When true, look up of high entropy values through user agent hints is disabled. + */ + /** @type {Submodule} */ export const id5IdSubmodule = { /** @@ -118,7 +184,8 @@ export const id5IdSubmodule = { } const resp = function (cbFunction) { - new IdFetchFlow(submoduleConfig, consentData, cacheIdObj, uspDataHandler.getConsentData(), gppDataHandler.getConsentData()).execute() + const fetchFlow = new IdFetchFlow(submoduleConfig, consentData, cacheIdObj, uspDataHandler.getConsentData(), gppDataHandler.getConsentData()); + fetchFlow.execute() .then(response => { cbFunction(response) }) @@ -169,7 +236,7 @@ export const id5IdSubmodule = { }, }; -class IdFetchFlow { +export class IdFetchFlow { constructor(submoduleConfig, gdprConsentData, cacheIdObj, usPrivacyData, gppData) { this.submoduleConfig = submoduleConfig this.gdprConsentData = gdprConsentData @@ -178,83 +245,97 @@ class IdFetchFlow { this.gppData = gppData } - execute() { - return this.#callForConfig(this.submoduleConfig) - .then(fetchFlowConfig => { - return this.#callForExtensions(fetchFlowConfig.extensionsCall) - .then(extensionsData => { - return this.#callId5Fetch(fetchFlowConfig.fetchCall, extensionsData) - }) - }) - .then(fetchCallResponse => { - try { - resetNb(this.submoduleConfig.params.partner); - if (fetchCallResponse.privacy) { - storeInLocalStorage(ID5_PRIVACY_STORAGE_NAME, JSON.stringify(fetchCallResponse.privacy), NB_EXP_DAYS); - } - } catch (error) { - logError(LOG_PREFIX + error); - } - return fetchCallResponse; - }) + /** + * Calls the ID5 Servers to fetch an ID5 ID + * @returns {Promise} The result of calling the server side + */ + async execute() { + const configCallPromise = this.#callForConfig(); + if (this.#isExternalModule()) { + try { + return await this.#externalModuleFlow(configCallPromise); + } catch (error) { + logError(LOG_PREFIX + 'Error while performing ID5 external module flow. Continuing with regular flow.', error); + return this.#regularFlow(configCallPromise); + } + } else { + return this.#regularFlow(configCallPromise); + } + } + + #isExternalModule() { + return typeof this.submoduleConfig.params.externalModuleUrl === 'string'; + } + + // eslint-disable-next-line no-dupe-class-members + async #externalModuleFlow(configCallPromise) { + await loadExternalModule(this.submoduleConfig.params.externalModuleUrl); + const fetchFlowConfig = await configCallPromise; + + return this.#getExternalIntegration().fetchId5Id(fetchFlowConfig, this.submoduleConfig.params, getRefererInfo(), this.gdprConsentData, this.usPrivacyData, this.gppData); } - #ajaxPromise(url, data, options) { - return new Promise((resolve, reject) => { - ajax(url, - { - success: function (res) { - resolve(res) - }, - error: function (err) { - reject(err) - } - }, data, options) - }) + // eslint-disable-next-line no-dupe-class-members + #getExternalIntegration() { + return window.id5Prebid && window.id5Prebid.integration; } // eslint-disable-next-line no-dupe-class-members - #callForConfig(submoduleConfig) { - let url = submoduleConfig.params.configUrl || ID5_API_CONFIG_URL; // override for debug/test purposes only - return this.#ajaxPromise(url, JSON.stringify(submoduleConfig), {method: 'POST'}) - .then(response => { - let responseObj = JSON.parse(response); - logInfo(LOG_PREFIX + 'config response received from the server', responseObj); - return responseObj; - }); + async #regularFlow(configCallPromise) { + const fetchFlowConfig = await configCallPromise; + const extensionsData = await this.#callForExtensions(fetchFlowConfig.extensionsCall); + const fetchCallResponse = await this.#callId5Fetch(fetchFlowConfig.fetchCall, extensionsData); + return this.#processFetchCallResponse(fetchCallResponse); } // eslint-disable-next-line no-dupe-class-members - #callForExtensions(extensionsCallConfig) { + async #callForConfig() { + let url = this.submoduleConfig.params.configUrl || ID5_API_CONFIG_URL; // override for debug/test purposes only + const response = await fetch(url, { + method: 'POST', + body: JSON.stringify(this.submoduleConfig) + }); + if (!response.ok) { + throw new Error('Error while calling config endpoint: ', response); + } + const dynamicConfig = await response.json(); + logInfo(LOG_PREFIX + 'config response received from the server', dynamicConfig); + return dynamicConfig; + } + + // eslint-disable-next-line no-dupe-class-members + async #callForExtensions(extensionsCallConfig) { if (extensionsCallConfig === undefined) { - return Promise.resolve(undefined) + return undefined; + } + const extensionsUrl = extensionsCallConfig.url; + const method = extensionsCallConfig.method || 'GET'; + const body = method === 'GET' ? undefined : JSON.stringify(extensionsCallConfig.body || {}); + const response = await fetch(extensionsUrl, { method, body }); + if (!response.ok) { + throw new Error('Error while calling extensions endpoint: ', response); } - let extensionsUrl = extensionsCallConfig.url - let method = extensionsCallConfig.method || 'GET' - let data = method === 'GET' ? undefined : JSON.stringify(extensionsCallConfig.body || {}) - return this.#ajaxPromise(extensionsUrl, data, {'method': method}) - .then(response => { - let responseObj = JSON.parse(response); - logInfo(LOG_PREFIX + 'extensions response received from the server', responseObj); - return responseObj; - }) + const extensions = await response.json(); + logInfo(LOG_PREFIX + 'extensions response received from the server', extensions); + return extensions; } // eslint-disable-next-line no-dupe-class-members - #callId5Fetch(fetchCallConfig, extensionsData) { - let url = fetchCallConfig.url; - let additionalData = fetchCallConfig.overrides || {}; - let data = { + async #callId5Fetch(fetchCallConfig, extensionsData) { + const fetchUrl = fetchCallConfig.url; + const additionalData = fetchCallConfig.overrides || {}; + const body = JSON.stringify({ ...this.#createFetchRequestData(), ...additionalData, extensions: extensionsData - }; - return this.#ajaxPromise(url, JSON.stringify(data), {method: 'POST', withCredentials: true}) - .then(response => { - let responseObj = JSON.parse(response); - logInfo(LOG_PREFIX + 'fetch response received from the server', responseObj); - return responseObj; - }); + }); + const response = await fetch(fetchUrl, { method: 'POST', body, credentials: 'include' }); + if (!response.ok) { + throw new Error('Error while calling fetch endpoint: ', response); + } + const fetchResponse = await response.json(); + logInfo(LOG_PREFIX + 'fetch response received from the server', fetchResponse); + return fetchResponse; } // eslint-disable-next-line no-dupe-class-members @@ -263,7 +344,7 @@ class IdFetchFlow { const hasGdpr = (this.gdprConsentData && typeof this.gdprConsentData.gdprApplies === 'boolean' && this.gdprConsentData.gdprApplies) ? 1 : 0; const referer = getRefererInfo(); const signature = (this.cacheIdObj && this.cacheIdObj.signature) ? this.cacheIdObj.signature : getLegacyCookieSignature(); - const nbPage = incrementNb(params.partner); + const nbPage = incrementAndResetNb(params.partner); const data = { 'partner': params.partner, 'gdpr': hasGdpr, @@ -309,6 +390,33 @@ class IdFetchFlow { } return data; } + + // eslint-disable-next-line no-dupe-class-members + #processFetchCallResponse(fetchCallResponse) { + try { + if (fetchCallResponse.privacy) { + storeInLocalStorage(ID5_PRIVACY_STORAGE_NAME, JSON.stringify(fetchCallResponse.privacy), NB_EXP_DAYS); + } + } catch (error) { + logError(LOG_PREFIX + 'Error while writing privacy info into local storage.', error); + } + return fetchCallResponse; + } +} + +async function loadExternalModule(url) { + return new GreedyPromise((resolve, reject) => { + if (window.id5Prebid) { + // Already loaded + resolve(); + } else { + try { + loadExternalScript(url, 'id5', resolve); + } catch (error) { + reject(error); + } + } + }); } function validateConfig(config) { @@ -371,8 +479,10 @@ function incrementNb(partnerId) { return nb; } -function resetNb(partnerId) { +function incrementAndResetNb(partnerId) { + const result = incrementNb(partnerId); storeNbInCache(partnerId, 0); + return result; } function getLegacyCookieSignature() { diff --git a/modules/id5IdSystem.md b/modules/id5IdSystem.md index 927fa10f87b..592c69056fa 100644 --- a/modules/id5IdSystem.md +++ b/modules/id5IdSystem.md @@ -25,6 +25,7 @@ pbjs.setConfig({ name: 'id5Id', params: { partner: 173, // change to the Partner Number you received from ID5 + externalModuleUrl: "https://cdn.id5-sync.com/api/1.0/id5PrebidModule.js" // optional but recommended pd: 'MT1iNTBjY...', // optional, see table below for a link to how to generate this abTesting: { // optional enabled: true, // false by default @@ -49,6 +50,7 @@ pbjs.setConfig({ | name | Required | String | The name of this module: `"id5Id"` | `"id5Id"` | | params | Required | Object | Details for the ID5 ID. | | | params.partner | Required | Number | This is the ID5 Partner Number obtained from registering with ID5. | `173` | +| params.externalModuleUrl | Optional | String | The URL for the id5-prebid external module. It is recommended to use the latest version at the URL in the example. Source code available [here](https://github.com/id5io/id5-api.js/blob/master/src/id5PrebidModule.js). | https://cdn.id5-sync.com/api/1.0/id5PrebidModule.js | params.pd | Optional | String | Partner-supplied data used for linking ID5 IDs across domains. See [our documentation](https://wiki.id5.io/en/identitycloud/retrieve-id5-ids/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 | diff --git a/src/adloader.js b/src/adloader.js index 87683828aa8..f60955736bd 100644 --- a/src/adloader.js +++ b/src/adloader.js @@ -30,7 +30,8 @@ const _approvedLoadExternalJSList = [ 'mediafilter', 'qortex', 'dynamicAdBoost', - 'contxtful' + 'contxtful', + 'id5' ] /** diff --git a/test/spec/modules/id5IdSystem_spec.js b/test/spec/modules/id5IdSystem_spec.js index d3d91db010d..ecce98d0b8d 100644 --- a/test/spec/modules/id5IdSystem_spec.js +++ b/test/spec/modules/id5IdSystem_spec.js @@ -1,26 +1,18 @@ -import { - expDaysStr, - getFromLocalStorage, - getNbFromCache, - ID5_PRIVACY_STORAGE_NAME, - ID5_STORAGE_NAME, - id5IdSubmodule, - nbCacheName, - storage, - storeInLocalStorage, - storeNbInCache, -} from 'modules/id5IdSystem.js'; -import {coreStorage, getConsentHash, init, requestBidsHook, setSubmoduleRegistry} from 'modules/userId/index.js'; -import {config} from 'src/config.js'; -import * as events from 'src/events.js'; -import CONSTANTS from 'src/constants.json'; -import * as utils from 'src/utils.js'; -import {uspDataHandler, gppDataHandler} from 'src/adapterManager.js'; -import 'src/prebid.js'; +import * as id5System from '../../../modules/id5IdSystem.js'; +import {coreStorage, getConsentHash, init, requestBidsHook, setSubmoduleRegistry} from '../../../modules/userId/index.js'; +import {config} from '../../../src/config.js'; +import * as events from '../../../src/events.js'; +import CONSTANTS from '../../../src/constants.json'; +import * as utils from '../../../src/utils.js'; +import {uspDataHandler, gppDataHandler} from '../../../src/adapterManager.js'; +import '../../../src/prebid.js'; import {hook} from '../../../src/hook.js'; import {mockGdprConsent} from '../../helpers/consentData.js'; import {server} from '../../mocks/xhr.js'; import {expect} from 'chai'; +import { GreedyPromise } from '../../../src/utils/promise.js'; + +const IdFetchFlow = id5System.IdFetchFlow; describe('ID5 ID System', function () { const ID5_MODULE_NAME = 'id5Id'; @@ -35,7 +27,6 @@ describe('ID5 ID System', function () { url: ID5_ENDPOINT } }; - 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; @@ -74,11 +65,11 @@ describe('ID5 ID System', function () { 'Content-Type': 'application/json' } - function getId5FetchConfig(storageName = ID5_STORAGE_NAME, storageType = 'html5') { + function getId5FetchConfig(partner = ID5_TEST_PARTNER_ID, storageName = id5System.ID5_STORAGE_NAME, storageType = 'html5') { return { name: ID5_MODULE_NAME, params: { - partner: ID5_TEST_PARTNER_ID + partner }, storage: { name: storageName, @@ -109,7 +100,7 @@ describe('ID5 ID System', function () { } function getFetchLocalStorageConfig() { - return getUserSyncConfig([getId5FetchConfig(ID5_STORAGE_NAME, 'html5')]); + return getUserSyncConfig([getId5FetchConfig()]); } function getValueConfig(value) { @@ -126,67 +117,66 @@ describe('ID5 ID System', function () { } function callSubmoduleGetId(config, consentData, cacheIdObj) { - return new Promise((resolve) => { - id5IdSubmodule.getId(config, consentData, cacheIdObj).callback((response) => { - resolve(response) - }) + return new GreedyPromise((resolve) => { + id5System.id5IdSubmodule.getId(config, consentData, cacheIdObj).callback((response) => { + resolve(response); + }); }); } class XhrServerMock { + currentRequestIdx = 0; + server; + constructor(server) { - this.currentRequestIdx = 0 - this.server = server + this.currentRequestIdx = 0; + this.server = server; } - expectFirstRequest() { - return this.#expectRequest(0); + async expectFirstRequest() { + return this.#waitOnRequest(0); } - expectNextRequest() { - return this.#expectRequest(++this.currentRequestIdx) + async expectNextRequest() { + return this.#waitOnRequest(++this.currentRequestIdx); } - expectConfigRequest() { - return this.expectFirstRequest() - .then(configRequest => { - expect(configRequest.url).is.eq(ID5_API_CONFIG_URL); - expect(configRequest.method).is.eq('POST'); - return configRequest; - }) + async expectConfigRequest() { + const configRequest = await this.expectFirstRequest(); + expect(configRequest.url).is.eq(ID5_API_CONFIG_URL); + expect(configRequest.method).is.eq('POST'); + return configRequest; } - respondWithConfigAndExpectNext(configRequest, config = ID5_API_CONFIG) { + async respondWithConfigAndExpectNext(configRequest, config = ID5_API_CONFIG) { configRequest.respond(200, HEADERS_CONTENT_TYPE_JSON, JSON.stringify(config)); - return this.expectNextRequest() + return this.expectNextRequest(); } - expectFetchRequest() { - return this.expectConfigRequest() - .then(configRequest => { - return this.respondWithConfigAndExpectNext(configRequest, ID5_API_CONFIG); - }).then(request => { - expect(request.url).is.eq(ID5_API_CONFIG.fetchCall.url); - expect(request.method).is.eq('POST'); - return request; - }) + async expectFetchRequest() { + const configRequest = await this.expectFirstRequest(); + const fetchRequest = await this.respondWithConfigAndExpectNext(configRequest); + expect(fetchRequest.method).is.eq('POST'); + expect(fetchRequest.url).is.eq(ID5_API_CONFIG.fetchCall.url); + return fetchRequest; } - #expectRequest(index) { - let server = this.server - return new Promise(function (resolve) { - (function waitForCondition() { - if (server.requests && server.requests.length > index) return resolve(server.requests[index]); - setTimeout(waitForCondition, 30); - })(); - }) - .then(request => { - return request - }); + async #waitOnRequest(index) { + const server = this.server + return new GreedyPromise((resolve) => { + const waitForCondition = () => { + if (server.requests && server.requests.length > index) { + resolve(server.requests[index]); + } else { + setTimeout(waitForCondition, 30); + } + }; + waitForCondition(); + }); } hasReceivedAnyRequest() { - let requests = this.server.requests; + const requests = this.server.requests; return requests && requests.length > 0; } } @@ -198,29 +188,29 @@ describe('ID5 ID System', function () { describe('Check for valid publisher config', function () { it('should fail with invalid config', function () { // no config - expect(id5IdSubmodule.getId()).is.eq(undefined); - expect(id5IdSubmodule.getId({})).is.eq(undefined); - - // valid params, invalid storage - expect(id5IdSubmodule.getId({ params: { partner: 123 } })).to.be.eq(undefined); - 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); + expect(id5System.id5IdSubmodule.getId()).is.eq(undefined); + expect(id5System.id5IdSubmodule.getId({})).is.eq(undefined); + + // valid params, invalid id5System.storage + expect(id5System.id5IdSubmodule.getId({ params: { partner: 123 } })).to.be.eq(undefined); + expect(id5System.id5IdSubmodule.getId({ params: { partner: 123 }, storage: {} })).to.be.eq(undefined); + expect(id5System.id5IdSubmodule.getId({ params: { partner: 123 }, storage: { name: '' } })).to.be.eq(undefined); + expect(id5System.id5IdSubmodule.getId({ params: { partner: 123 }, storage: { type: '' } })).to.be.eq(undefined); + + // valid id5System.storage, invalid params + expect(id5System.id5IdSubmodule.getId({ storage: { name: 'name', type: 'html5', }, })).to.be.eq(undefined); + expect(id5System.id5IdSubmodule.getId({ storage: { name: 'name', type: 'html5', }, params: { } })).to.be.eq(undefined); + expect(id5System.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'); + it('should warn with non-recommended id5System.storage params', function () { + const logWarnStub = sinon.stub(utils, 'logWarn'); - id5IdSubmodule.getId({ storage: { name: 'name', type: 'html5', }, params: { partner: 123 } }); + id5System.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 } }); + id5System.id5IdSubmodule.getId({ storage: { name: id5System.ID5_STORAGE_NAME, type: 'cookie', }, params: { partner: 123 } }); expect(logWarnStub.calledOnce).to.be.true; logWarnStub.restore(); }); @@ -240,19 +230,19 @@ describe('ID5 ID System', function () { dataConsentVals.forEach(function([purposeConsent, vendorConsent, caseName]) { it('should fail with invalid consent because of ' + caseName, function() { - let dataConsent = { + const dataConsent = { gdprApplies: true, consentString: 'consentString', vendorData: { purposeConsent, vendorConsent } } - expect(id5IdSubmodule.getId(config)).is.eq(undefined); - expect(id5IdSubmodule.getId(config, dataConsent)).is.eq(undefined); + expect(id5System.id5IdSubmodule.getId(config)).is.eq(undefined); + expect(id5System.id5IdSubmodule.getId(config, dataConsent)).is.eq(undefined); - let cacheIdObject = 'cacheIdObject'; - expect(id5IdSubmodule.extendId(config)).is.eq(undefined); - expect(id5IdSubmodule.extendId(config, dataConsent, cacheIdObject)).is.eq(cacheIdObject); + const cacheIdObject = 'cacheIdObject'; + expect(id5System.id5IdSubmodule.extendId(config)).is.eq(undefined); + expect(id5System.id5IdSubmodule.extendId(config, dataConsent, cacheIdObject)).is.eq(cacheIdObject); }); }); }); @@ -269,125 +259,121 @@ describe('ID5 ID System', function () { gppStub?.restore() }); - it('should call the ID5 server and handle a valid response', function () { - let xhrServerMock = new XhrServerMock(server) - let config = getId5FetchConfig(); - let submoduleResponse = callSubmoduleGetId(config, undefined, undefined); + it('should call the ID5 server and handle a valid response', async function () { + const xhrServerMock = new XhrServerMock(server) + const config = getId5FetchConfig(); - return xhrServerMock.expectFetchRequest() - .then(fetchRequest => { - let requestBody = JSON.parse(fetchRequest.requestBody); - expect(fetchRequest.url).to.contain(ID5_ENDPOINT); - expect(fetchRequest.withCredentials).is.true; - expect(requestBody.partner).is.eq(ID5_TEST_PARTNER_ID); - expect(requestBody.o).is.eq('pbjs'); - expect(requestBody.pd).is.undefined; - expect(requestBody.s).is.undefined; - expect(requestBody.provider).is.undefined - expect(requestBody.v).is.eq('$prebid.version$'); - expect(requestBody.gdpr).is.eq(0); - expect(requestBody.gdpr_consent).is.undefined; - expect(requestBody.us_privacy).is.undefined; - expect(requestBody.storage).is.deep.eq(config.storage) + // Trigger the fetch but we await on it later + const submoduleResponsePromise = callSubmoduleGetId(config, undefined, undefined); - fetchRequest.respond(200, responseHeader, JSON.stringify(ID5_JSON_RESPONSE)); - return submoduleResponse - }) - .then(submoduleResponse => { - expect(submoduleResponse).is.deep.equal(ID5_JSON_RESPONSE); - }); + const fetchRequest = await xhrServerMock.expectFetchRequest() + + expect(fetchRequest.url).to.contain(ID5_ENDPOINT); + expect(fetchRequest.withCredentials).is.true; + + const requestBody = JSON.parse(fetchRequest.requestBody); + expect(requestBody.partner).is.eq(ID5_TEST_PARTNER_ID); + expect(requestBody.o).is.eq('pbjs'); + expect(requestBody.pd).is.undefined; + expect(requestBody.s).is.undefined; + expect(requestBody.provider).is.undefined + expect(requestBody.v).is.eq('$prebid.version$'); + expect(requestBody.gdpr).is.eq(0); + expect(requestBody.gdpr_consent).is.undefined; + expect(requestBody.us_privacy).is.undefined; + expect(requestBody.storage).is.deep.eq(config.storage) + + fetchRequest.respond(200, responseHeader, JSON.stringify(ID5_JSON_RESPONSE)); + + const submoduleResponse = await submoduleResponsePromise; + expect(submoduleResponse).is.deep.equal(ID5_JSON_RESPONSE); }); - it('should call the ID5 server with gdpr data ', function () { - let xhrServerMock = new XhrServerMock(server) - let consentData = { + it('should call the ID5 server with gdpr data ', async function () { + const xhrServerMock = new XhrServerMock(server) + const consentData = { gdprApplies: true, consentString: 'consentString', vendorData: ALLOWED_ID5_VENDOR_DATA } - let submoduleResponse = callSubmoduleGetId(getId5FetchConfig(), consentData, undefined); + // Trigger the fetch but we await on it later + const submoduleResponsePromise = callSubmoduleGetId(getId5FetchConfig(), consentData, undefined); - return xhrServerMock.expectFetchRequest() - .then(fetchRequest => { - let requestBody = JSON.parse(fetchRequest.requestBody); - expect(requestBody.partner).is.eq(ID5_TEST_PARTNER_ID); - expect(requestBody.gdpr).to.eq(1); - expect(requestBody.gdpr_consent).is.eq(consentData.consentString); + const fetchRequest = await xhrServerMock.expectFetchRequest() + const requestBody = JSON.parse(fetchRequest.requestBody); + expect(requestBody.partner).is.eq(ID5_TEST_PARTNER_ID); + expect(requestBody.gdpr).to.eq(1); + expect(requestBody.gdpr_consent).is.eq(consentData.consentString); - fetchRequest.respond(200, responseHeader, JSON.stringify(ID5_JSON_RESPONSE)); - return submoduleResponse - }) - .then(submoduleResponse => { - expect(submoduleResponse).is.deep.equal(ID5_JSON_RESPONSE); - }); + fetchRequest.respond(200, responseHeader, JSON.stringify(ID5_JSON_RESPONSE)); + + const submoduleResponse = await submoduleResponsePromise; + expect(submoduleResponse).is.deep.equal(ID5_JSON_RESPONSE); }); - it('should call the ID5 server without gdpr data when gdpr not applies ', function () { - let xhrServerMock = new XhrServerMock(server) - let consentData = { + it('should call the ID5 server without gdpr data when gdpr not applies ', async function () { + const xhrServerMock = new XhrServerMock(server) + const consentData = { gdprApplies: false, consentString: 'consentString' } - let submoduleResponse = callSubmoduleGetId(getId5FetchConfig(), consentData, undefined); + // Trigger the fetch but we await on it later + const submoduleResponsePromise = callSubmoduleGetId(getId5FetchConfig(), consentData, undefined); - return xhrServerMock.expectFetchRequest() - .then(fetchRequest => { - let requestBody = JSON.parse(fetchRequest.requestBody); - expect(requestBody.gdpr).to.eq(0); - expect(requestBody.gdpr_consent).is.undefined + const fetchRequest = await xhrServerMock.expectFetchRequest() + const requestBody = JSON.parse(fetchRequest.requestBody); + expect(requestBody.gdpr).to.eq(0); + expect(requestBody.gdpr_consent).is.undefined - fetchRequest.respond(200, responseHeader, JSON.stringify(ID5_JSON_RESPONSE)); - return submoduleResponse - }) - .then(submoduleResponse => { - expect(submoduleResponse).is.deep.equal(ID5_JSON_RESPONSE); - }); + fetchRequest.respond(200, responseHeader, JSON.stringify(ID5_JSON_RESPONSE)); + + const submoduleResponse = await submoduleResponsePromise; + expect(submoduleResponse).is.deep.equal(ID5_JSON_RESPONSE); }); - it('should call the ID5 server with us privacy consent', function () { - let usPrivacyString = '1YN-'; + it('should call the ID5 server with us privacy consent', async function () { + const usPrivacyString = '1YN-'; uspDataHandler.setConsentData(usPrivacyString) - let xhrServerMock = new XhrServerMock(server) - let consentData = { + const xhrServerMock = new XhrServerMock(server) + const consentData = { gdprApplies: true, consentString: 'consentString', vendorData: ALLOWED_ID5_VENDOR_DATA } - let submoduleResponse = callSubmoduleGetId(getId5FetchConfig(), consentData, undefined); + // Trigger the fetch but we await on it later + const submoduleResponsePromise = callSubmoduleGetId(getId5FetchConfig(), consentData, undefined); - return xhrServerMock.expectFetchRequest() - .then(fetchRequest => { - let requestBody = JSON.parse(fetchRequest.requestBody); - expect(requestBody.partner).is.eq(ID5_TEST_PARTNER_ID); - expect(requestBody.us_privacy).to.eq(usPrivacyString); + const fetchRequest = await xhrServerMock.expectFetchRequest() + const requestBody = JSON.parse(fetchRequest.requestBody); + expect(requestBody.partner).is.eq(ID5_TEST_PARTNER_ID); + expect(requestBody.us_privacy).to.eq(usPrivacyString); - fetchRequest.respond(200, responseHeader, JSON.stringify(ID5_JSON_RESPONSE)); - return submoduleResponse - }) - .then(submoduleResponse => { - expect(submoduleResponse).is.deep.equal(ID5_JSON_RESPONSE); - }); + fetchRequest.respond(200, responseHeader, JSON.stringify(ID5_JSON_RESPONSE)); + + const submoduleResponse = await submoduleResponsePromise; + expect(submoduleResponse).is.deep.equal(ID5_JSON_RESPONSE); }); - it('should call the ID5 server with no signature field when no stored object', function () { - let xhrServerMock = new XhrServerMock(server) - let submoduleResponse = callSubmoduleGetId(getId5FetchConfig(), undefined, undefined); + it('should call the ID5 server with no signature field when no stored object', async function () { + const xhrServerMock = new XhrServerMock(server) - return xhrServerMock.expectFetchRequest() - .then(fetchRequest => { - let requestBody = JSON.parse(fetchRequest.requestBody); - expect(requestBody.s).is.undefined; - fetchRequest.respond(200, responseHeader, JSON.stringify(ID5_JSON_RESPONSE)); - return submoduleResponse - }) + // Trigger the fetch but we await on it later + const submoduleResponsePromise = callSubmoduleGetId(getId5FetchConfig(), undefined, undefined); + + const fetchRequest = await xhrServerMock.expectFetchRequest() + const requestBody = JSON.parse(fetchRequest.requestBody); + expect(requestBody.s).is.undefined; + + fetchRequest.respond(200, responseHeader, JSON.stringify(ID5_JSON_RESPONSE)); + await submoduleResponsePromise; }); - it('should call the ID5 server for config with submodule config object', function () { - let xhrServerMock = new XhrServerMock(server) - let id5FetchConfig = getId5FetchConfig(); + it('should call the ID5 server for config with submodule config object', async function () { + const xhrServerMock = new XhrServerMock(server) + const id5FetchConfig = getId5FetchConfig(); id5FetchConfig.params.extraParam = { x: 'X', y: { @@ -395,341 +381,391 @@ describe('ID5 ID System', function () { b: '3' } } - let submoduleResponse = callSubmoduleGetId(id5FetchConfig, undefined, undefined); - return xhrServerMock.expectConfigRequest() - .then(configRequest => { - let requestBody = JSON.parse(configRequest.requestBody) - expect(requestBody).is.deep.eq(id5FetchConfig) - return xhrServerMock.respondWithConfigAndExpectNext(configRequest) - }) - .then(fetchRequest => { - fetchRequest.respond(200, responseHeader, JSON.stringify(ID5_JSON_RESPONSE)); - return submoduleResponse - }) + // Trigger the fetch but we await on it later + const submoduleResponsePromise = callSubmoduleGetId(id5FetchConfig, undefined, undefined); + + const configRequest = await xhrServerMock.expectConfigRequest(); + const requestBody = JSON.parse(configRequest.requestBody); + expect(requestBody).is.deep.eq(id5FetchConfig) + + const fetchRequest = await xhrServerMock.respondWithConfigAndExpectNext(configRequest) + fetchRequest.respond(200, responseHeader, JSON.stringify(ID5_JSON_RESPONSE)); + await submoduleResponsePromise; }); - it('should call the ID5 server for config with partner id being a string', function () { - let xhrServerMock = new XhrServerMock(server) - let id5FetchConfig = getId5FetchConfig(); + it('should call the ID5 server for config with partner id being a string', async function () { + const xhrServerMock = new XhrServerMock(server) + const id5FetchConfig = getId5FetchConfig(); id5FetchConfig.params.partner = '173'; - let submoduleResponse = callSubmoduleGetId(id5FetchConfig, undefined, undefined); - return xhrServerMock.expectConfigRequest() - .then(configRequest => { - let requestBody = JSON.parse(configRequest.requestBody) - expect(requestBody.params.partner).is.eq(173) - return xhrServerMock.respondWithConfigAndExpectNext(configRequest) - }) - .then(fetchRequest => { - fetchRequest.respond(200, responseHeader, JSON.stringify(ID5_JSON_RESPONSE)); - return submoduleResponse - }) + // Trigger the fetch but we await on it later + const submoduleResponsePromise = callSubmoduleGetId(id5FetchConfig, undefined, undefined); + + const configRequest = await xhrServerMock.expectConfigRequest(); + const requestBody = JSON.parse(configRequest.requestBody) + expect(requestBody.params.partner).is.eq(173) + + const fetchRequest = await xhrServerMock.respondWithConfigAndExpectNext(configRequest) + fetchRequest.respond(200, responseHeader, JSON.stringify(ID5_JSON_RESPONSE)); + await submoduleResponsePromise; }); - it('should call the ID5 server for config with overridden url', function () { - let xhrServerMock = new XhrServerMock(server) - let id5FetchConfig = getId5FetchConfig(); + it('should call the ID5 server for config with overridden url', async function () { + const xhrServerMock = new XhrServerMock(server) + const id5FetchConfig = getId5FetchConfig(); id5FetchConfig.params.configUrl = 'http://localhost/x/y/z' - let submoduleResponse = callSubmoduleGetId(id5FetchConfig, undefined, undefined); + // Trigger the fetch but we await on it later + const submoduleResponsePromise = callSubmoduleGetId(id5FetchConfig, undefined, undefined); - return xhrServerMock.expectFirstRequest() - .then(configRequest => { - expect(configRequest.url).is.eq('http://localhost/x/y/z') - return xhrServerMock.respondWithConfigAndExpectNext(configRequest) - }) - .then(fetchRequest => { - fetchRequest.respond(200, responseHeader, JSON.stringify(ID5_JSON_RESPONSE)); - return submoduleResponse - }) + const configRequest = await xhrServerMock.expectFirstRequest(); + expect(configRequest.url).is.eq('http://localhost/x/y/z'); + + const fetchRequest = await xhrServerMock.respondWithConfigAndExpectNext(configRequest) + fetchRequest.respond(200, responseHeader, JSON.stringify(ID5_JSON_RESPONSE)); + await submoduleResponsePromise; }); - it('should call the ID5 server with additional data when provided', function () { - let xhrServerMock = new XhrServerMock(server) - let submoduleResponse = callSubmoduleGetId(getId5FetchConfig(), undefined, undefined); - - return xhrServerMock.expectConfigRequest() - .then(configRequest => { - return xhrServerMock.respondWithConfigAndExpectNext(configRequest, { - fetchCall: { - url: ID5_ENDPOINT, - overrides: { - arg1: '123', - arg2: { - x: '1', - y: 2 - } - } + it('should call the ID5 server with additional data when provided', async function () { + const xhrServerMock = new XhrServerMock(server) + + // Trigger the fetch but we await on it later + const submoduleResponsePromise = callSubmoduleGetId(getId5FetchConfig(), undefined, undefined); + + const configRequest = await xhrServerMock.expectConfigRequest(); + const fetchRequest = await xhrServerMock.respondWithConfigAndExpectNext(configRequest, { + fetchCall: { + url: ID5_ENDPOINT, + overrides: { + arg1: '123', + arg2: { + x: '1', + y: 2 } - }); - }) - .then(fetchRequest => { - let requestBody = JSON.parse(fetchRequest.requestBody); - expect(requestBody.partner).is.eq(ID5_TEST_PARTNER_ID); - expect(requestBody.o).is.eq('pbjs'); - expect(requestBody.v).is.eq('$prebid.version$'); - expect(requestBody.arg1).is.eq('123') - expect(requestBody.arg2).is.deep.eq({ - x: '1', - y: 2 - }) - fetchRequest.respond(200, responseHeader, JSON.stringify(ID5_JSON_RESPONSE)); - return submoduleResponse - }) + } + } + }); + const requestBody = JSON.parse(fetchRequest.requestBody); + expect(requestBody.partner).is.eq(ID5_TEST_PARTNER_ID); + expect(requestBody.o).is.eq('pbjs'); + expect(requestBody.v).is.eq('$prebid.version$'); + expect(requestBody.arg1).is.eq('123') + expect(requestBody.arg2).is.deep.eq({ + x: '1', + y: 2 + }) + + fetchRequest.respond(200, responseHeader, JSON.stringify(ID5_JSON_RESPONSE)); + await submoduleResponsePromise; }); - it('should call the ID5 server with extensions', function () { - let xhrServerMock = new XhrServerMock(server) - let submoduleResponse = callSubmoduleGetId(getId5FetchConfig(), undefined, undefined); - - return xhrServerMock.expectConfigRequest() - .then(configRequest => { - return xhrServerMock.respondWithConfigAndExpectNext(configRequest, { - fetchCall: { - url: ID5_ENDPOINT - }, - extensionsCall: { - url: ID5_EXTENSIONS_ENDPOINT, - method: 'GET' - } - }); - }) - .then(extensionsRequest => { - expect(extensionsRequest.url).is.eq(ID5_EXTENSIONS_ENDPOINT) - expect(extensionsRequest.method).is.eq('GET') - extensionsRequest.respond(200, responseHeader, JSON.stringify({ - lb: 'ex' - })) - return xhrServerMock.expectNextRequest(); - }) - .then(fetchRequest => { - let requestBody = JSON.parse(fetchRequest.requestBody); - expect(requestBody.partner).is.eq(ID5_TEST_PARTNER_ID); - expect(requestBody.o).is.eq('pbjs'); - expect(requestBody.v).is.eq('$prebid.version$'); - expect(requestBody.extensions).is.deep.eq({ - lb: 'ex' - }) - fetchRequest.respond(200, responseHeader, JSON.stringify(ID5_JSON_RESPONSE)); - return submoduleResponse - }) + it('should call the ID5 server with extensions', async function () { + const xhrServerMock = new XhrServerMock(server) + + // Trigger the fetch but we await on it later + const submoduleResponsePromise = callSubmoduleGetId(getId5FetchConfig(), undefined, undefined); + + const configRequest = await xhrServerMock.expectConfigRequest(); + const extensionsRequest = await xhrServerMock.respondWithConfigAndExpectNext(configRequest, { + fetchCall: { + url: ID5_ENDPOINT + }, + extensionsCall: { + url: ID5_EXTENSIONS_ENDPOINT, + method: 'GET' + } + }); + expect(extensionsRequest.url).is.eq(ID5_EXTENSIONS_ENDPOINT) + expect(extensionsRequest.method).is.eq('GET') + + extensionsRequest.respond(200, responseHeader, JSON.stringify({ + lb: 'ex' + })); + const fetchRequest = await xhrServerMock.expectNextRequest(); + const requestBody = JSON.parse(fetchRequest.requestBody); + expect(requestBody.partner).is.eq(ID5_TEST_PARTNER_ID); + expect(requestBody.o).is.eq('pbjs'); + expect(requestBody.v).is.eq('$prebid.version$'); + expect(requestBody.extensions).is.deep.eq({ + lb: 'ex' + }) + + fetchRequest.respond(200, responseHeader, JSON.stringify(ID5_JSON_RESPONSE)); + await submoduleResponsePromise; }); - it('should call the ID5 server with extensions fetched with POST', function () { - let xhrServerMock = new XhrServerMock(server) - let submoduleResponse = callSubmoduleGetId(getId5FetchConfig(), undefined, undefined); - - return xhrServerMock.expectConfigRequest() - .then(configRequest => { - return xhrServerMock.respondWithConfigAndExpectNext(configRequest, { - fetchCall: { - url: ID5_ENDPOINT - }, - extensionsCall: { - url: ID5_EXTENSIONS_ENDPOINT, - method: 'POST', - body: { - x: '1', - y: 2 - } - } - }); - }) - .then(extensionsRequest => { - expect(extensionsRequest.url).is.eq(ID5_EXTENSIONS_ENDPOINT) - expect(extensionsRequest.method).is.eq('POST') - let requestBody = JSON.parse(extensionsRequest.requestBody) - expect(requestBody).is.deep.eq({ + it('should call the ID5 server with extensions fetched using method POST', async function () { + const xhrServerMock = new XhrServerMock(server) + + // Trigger the fetch but we await on it later + const submoduleResponsePromise = callSubmoduleGetId(getId5FetchConfig(), undefined, undefined); + + const configRequest = await xhrServerMock.expectConfigRequest(); + const extensionsRequest = await xhrServerMock.respondWithConfigAndExpectNext(configRequest, { + fetchCall: { + url: ID5_ENDPOINT + }, + extensionsCall: { + url: ID5_EXTENSIONS_ENDPOINT, + method: 'POST', + body: { x: '1', y: 2 - }) - extensionsRequest.respond(200, responseHeader, JSON.stringify({ - lb: 'post', - })) - return xhrServerMock.expectNextRequest(); - }) - .then(fetchRequest => { - let requestBody = JSON.parse(fetchRequest.requestBody); - expect(requestBody.partner).is.eq(ID5_TEST_PARTNER_ID); - expect(requestBody.o).is.eq('pbjs'); - expect(requestBody.v).is.eq('$prebid.version$'); - expect(requestBody.extensions).is.deep.eq({ - lb: 'post' - }) - fetchRequest.respond(200, responseHeader, JSON.stringify(ID5_JSON_RESPONSE)); - return submoduleResponse - }) + } + } + }); + expect(extensionsRequest.url).is.eq(ID5_EXTENSIONS_ENDPOINT) + expect(extensionsRequest.method).is.eq('POST') + const extRequestBody = JSON.parse(extensionsRequest.requestBody) + expect(extRequestBody).is.deep.eq({ + x: '1', + y: 2 + }) + extensionsRequest.respond(200, responseHeader, JSON.stringify({ + lb: 'post', + })); + + const fetchRequest = await xhrServerMock.expectNextRequest(); + const requestBody = JSON.parse(fetchRequest.requestBody); + expect(requestBody.partner).is.eq(ID5_TEST_PARTNER_ID); + expect(requestBody.o).is.eq('pbjs'); + expect(requestBody.v).is.eq('$prebid.version$'); + expect(requestBody.extensions).is.deep.eq({ + lb: 'post' + }); + + fetchRequest.respond(200, responseHeader, JSON.stringify(ID5_JSON_RESPONSE)); + await submoduleResponsePromise; }); - it('should call the ID5 server with signature field from stored object', function () { - let xhrServerMock = new XhrServerMock(server) - let submoduleResponse = callSubmoduleGetId(getId5FetchConfig(), undefined, ID5_STORED_OBJ); + it('should call the ID5 server with signature field from stored object', async function () { + const xhrServerMock = new XhrServerMock(server) - return xhrServerMock.expectFetchRequest() - .then(fetchRequest => { - let requestBody = JSON.parse(fetchRequest.requestBody); - expect(requestBody.s).is.eq(ID5_STORED_SIGNATURE); - fetchRequest.respond(200, responseHeader, JSON.stringify(ID5_JSON_RESPONSE)); - return submoduleResponse - }) + // Trigger the fetch but we await on it later + const submoduleResponsePromise = callSubmoduleGetId(getId5FetchConfig(), undefined, ID5_STORED_OBJ); + + const fetchRequest = await xhrServerMock.expectFetchRequest() + const requestBody = JSON.parse(fetchRequest.requestBody); + expect(requestBody.s).is.eq(ID5_STORED_SIGNATURE); + + fetchRequest.respond(200, responseHeader, JSON.stringify(ID5_JSON_RESPONSE)); + await submoduleResponsePromise; }); - it('should call the ID5 server with pd field when pd config is set', function () { - let xhrServerMock = new XhrServerMock(server) + it('should call the ID5 server with pd field when pd config is set', async function () { + const xhrServerMock = new XhrServerMock(server) const pubData = 'b50ca08271795a8e7e4012813f23d505193d75c0f2e2bb99baa63aa822f66ed3'; - let id5Config = getId5FetchConfig(); + const id5Config = getId5FetchConfig(); id5Config.params.pd = pubData; - let submoduleResponse = callSubmoduleGetId(id5Config, undefined, ID5_STORED_OBJ); + // Trigger the fetch but we await on it later + const submoduleResponsePromise = callSubmoduleGetId(id5Config, undefined, undefined); - return xhrServerMock.expectFetchRequest() - .then(fetchRequest => { - let requestBody = JSON.parse(fetchRequest.requestBody); - expect(requestBody.pd).is.eq(pubData); - fetchRequest.respond(200, responseHeader, JSON.stringify(ID5_JSON_RESPONSE)); - return submoduleResponse; - }) + const fetchRequest = await xhrServerMock.expectFetchRequest(); + const requestBody = JSON.parse(fetchRequest.requestBody); + expect(requestBody.pd).is.eq(pubData); + + fetchRequest.respond(200, responseHeader, JSON.stringify(ID5_JSON_RESPONSE)); + await submoduleResponsePromise; }); - it('should call the ID5 server with no pd field when pd config is not set', function () { - let xhrServerMock = new XhrServerMock(server) - let id5Config = getId5FetchConfig(); + it('should call the ID5 server with no pd field when pd config is not set', async function () { + const xhrServerMock = new XhrServerMock(server) + const id5Config = getId5FetchConfig(); id5Config.params.pd = undefined; - let submoduleResponse = callSubmoduleGetId(id5Config, undefined, ID5_STORED_OBJ); + // Trigger the fetch but we await on it later + const submoduleResponsePromise = callSubmoduleGetId(id5Config, undefined, ID5_STORED_OBJ); - return xhrServerMock.expectFetchRequest() - .then(fetchRequest => { - let requestBody = JSON.parse(fetchRequest.requestBody); - expect(requestBody.pd).is.undefined; - fetchRequest.respond(200, responseHeader, JSON.stringify(ID5_JSON_RESPONSE)); - return submoduleResponse; - }) + const fetchRequest = await xhrServerMock.expectFetchRequest(); + const requestBody = JSON.parse(fetchRequest.requestBody); + expect(requestBody.pd).is.undefined; + + fetchRequest.respond(200, responseHeader, JSON.stringify(ID5_JSON_RESPONSE)); + await submoduleResponsePromise; }); - it('should call the ID5 server with nb=1 when no stored value exists and reset after', function () { - let xhrServerMock = new XhrServerMock(server) - coreStorage.removeDataFromLocalStorage(ID5_NB_STORAGE_NAME); + it('should call the ID5 server with nb=1 when no stored value exists and reset after', async function () { + const xhrServerMock = new XhrServerMock(server) + const TEST_PARTNER_ID = 189; + coreStorage.removeDataFromLocalStorage(id5System.nbCacheName(TEST_PARTNER_ID)); - let submoduleResponse = callSubmoduleGetId(getId5FetchConfig(), undefined, ID5_STORED_OBJ); + // Trigger the fetch but we await on it later + const submoduleResponsePromise = callSubmoduleGetId(getId5FetchConfig(), undefined, ID5_STORED_OBJ); - return xhrServerMock.expectFetchRequest() - .then(fetchRequest => { - let requestBody = JSON.parse(fetchRequest.requestBody); - expect(requestBody.nbPage).is.eq(1); - fetchRequest.respond(200, responseHeader, JSON.stringify(ID5_JSON_RESPONSE)); - return submoduleResponse - }) - .then(() => { - expect(getNbFromCache(ID5_TEST_PARTNER_ID)).is.eq(0); - }) + const fetchRequest = await xhrServerMock.expectFetchRequest(); + const requestBody = JSON.parse(fetchRequest.requestBody); + expect(requestBody.nbPage).is.eq(1); + + fetchRequest.respond(200, responseHeader, JSON.stringify(ID5_JSON_RESPONSE)); + await submoduleResponsePromise; + + expect(id5System.getNbFromCache(TEST_PARTNER_ID)).is.eq(0); }); - it('should call the ID5 server with incremented nb when stored value exists and reset after', function () { - let xhrServerMock = new XhrServerMock(server) - storeNbInCache(ID5_TEST_PARTNER_ID, 1); + it('should call the ID5 server with incremented nb when stored value exists and reset after', async function () { + const xhrServerMock = new XhrServerMock(server); + const TEST_PARTNER_ID = 189; + const config = getId5FetchConfig(TEST_PARTNER_ID); + id5System.storeNbInCache(TEST_PARTNER_ID, 1); - let submoduleResponse = callSubmoduleGetId(getId5FetchConfig(), undefined, ID5_STORED_OBJ); + // Trigger the fetch but we await on it later + const submoduleResponsePromise = callSubmoduleGetId(config, undefined, ID5_STORED_OBJ); - return xhrServerMock.expectFetchRequest() - .then(fetchRequest => { - let requestBody = JSON.parse(fetchRequest.requestBody); - expect(requestBody.nbPage).is.eq(2); - fetchRequest.respond(200, responseHeader, JSON.stringify(ID5_JSON_RESPONSE)); - return submoduleResponse - }) - .then(() => { - expect(getNbFromCache(ID5_TEST_PARTNER_ID)).is.eq(0); - }) + const fetchRequest = await xhrServerMock.expectFetchRequest(); + const requestBody = JSON.parse(fetchRequest.requestBody); + expect(requestBody.nbPage).is.eq(2); + + fetchRequest.respond(200, responseHeader, JSON.stringify(ID5_JSON_RESPONSE)); + await submoduleResponsePromise; + + expect(id5System.getNbFromCache(TEST_PARTNER_ID)).is.eq(0); }); - it('should call the ID5 server with ab_testing object when abTesting is turned on', function () { - let xhrServerMock = new XhrServerMock(server) - let id5Config = getId5FetchConfig(); + it('should call the ID5 server with ab_testing object when abTesting is turned on', async function () { + const xhrServerMock = new XhrServerMock(server) + const id5Config = getId5FetchConfig(); id5Config.params.abTesting = {enabled: true, controlGroupPct: 0.234} - let submoduleResponse = callSubmoduleGetId(id5Config, undefined, ID5_STORED_OBJ); + // Trigger the fetch but we await on it later + const submoduleResponsePromise = callSubmoduleGetId(id5Config, undefined, ID5_STORED_OBJ); - return xhrServerMock.expectFetchRequest() - .then(fetchRequest => { - let requestBody = JSON.parse(fetchRequest.requestBody); - expect(requestBody.ab_testing.enabled).is.eq(true); - expect(requestBody.ab_testing.control_group_pct).is.eq(0.234); - fetchRequest.respond(200, responseHeader, JSON.stringify(ID5_JSON_RESPONSE)); - return submoduleResponse; - }); + const fetchRequest = await xhrServerMock.expectFetchRequest(); + const requestBody = JSON.parse(fetchRequest.requestBody); + expect(requestBody.ab_testing.enabled).is.eq(true); + expect(requestBody.ab_testing.control_group_pct).is.eq(0.234); + + fetchRequest.respond(200, responseHeader, JSON.stringify(ID5_JSON_RESPONSE)); + await submoduleResponsePromise; }); - it('should call the ID5 server without ab_testing object when abTesting is turned off', function () { - let xhrServerMock = new XhrServerMock(server) - let id5Config = getId5FetchConfig(); + it('should call the ID5 server without ab_testing object when abTesting is turned off', async function () { + const xhrServerMock = new XhrServerMock(server) + const id5Config = getId5FetchConfig(); id5Config.params.abTesting = {enabled: false, controlGroupPct: 0.55} - let submoduleResponse = callSubmoduleGetId(id5Config, undefined, ID5_STORED_OBJ); + // Trigger the fetch but we await on it later + const submoduleResponsePromise = callSubmoduleGetId(id5Config, undefined, ID5_STORED_OBJ); - return xhrServerMock.expectFetchRequest() - .then(fetchRequest => { - let requestBody = JSON.parse(fetchRequest.requestBody); - expect(requestBody.ab_testing).is.undefined; - fetchRequest.respond(200, responseHeader, JSON.stringify(ID5_JSON_RESPONSE)); - return submoduleResponse - }); + const fetchRequest = await xhrServerMock.expectFetchRequest(); + const requestBody = JSON.parse(fetchRequest.requestBody); + expect(requestBody.ab_testing).is.undefined; + + fetchRequest.respond(200, responseHeader, JSON.stringify(ID5_JSON_RESPONSE)); + await submoduleResponsePromise; }); - it('should call the ID5 server without ab_testing when when abTesting is not set', function () { - let xhrServerMock = new XhrServerMock(server) - let id5Config = getId5FetchConfig(); + it('should call the ID5 server without ab_testing when when abTesting is not set', async function () { + const xhrServerMock = new XhrServerMock(server) + const id5Config = getId5FetchConfig(); - let submoduleResponse = callSubmoduleGetId(id5Config, undefined, ID5_STORED_OBJ); + // Trigger the fetch but we await on it later + const submoduleResponsePromise = callSubmoduleGetId(id5Config, undefined, ID5_STORED_OBJ); - return xhrServerMock.expectFetchRequest() - .then(fetchRequest => { - let requestBody = JSON.parse(fetchRequest.requestBody); - expect(requestBody.ab_testing).is.undefined; - fetchRequest.respond(200, responseHeader, JSON.stringify(ID5_JSON_RESPONSE)); - return submoduleResponse - }); + const fetchRequest = await xhrServerMock.expectFetchRequest(); + const requestBody = JSON.parse(fetchRequest.requestBody); + expect(requestBody.ab_testing).is.undefined; + + fetchRequest.respond(200, responseHeader, JSON.stringify(ID5_JSON_RESPONSE)); + await submoduleResponsePromise; }); - it('should store the privacy object from the ID5 server response', function () { - let xhrServerMock = new XhrServerMock(server) - let submoduleResponse = callSubmoduleGetId(getId5FetchConfig(), undefined, ID5_STORED_OBJ); + it('should store the privacy object from the ID5 server response', async function () { + const xhrServerMock = new XhrServerMock(server) + + // Trigger the fetch but we await on it later + const submoduleResponsePromise = callSubmoduleGetId(getId5FetchConfig(), undefined, ID5_STORED_OBJ); const privacy = { jurisdiction: 'gdpr', id5_consent: true }; - return xhrServerMock.expectFetchRequest() - .then(request => { - let responseObject = utils.deepClone(ID5_JSON_RESPONSE); - responseObject.privacy = privacy; - request.respond(200, responseHeader, JSON.stringify(responseObject)); - return submoduleResponse - }) - .then(() => { - expect(getFromLocalStorage(ID5_PRIVACY_STORAGE_NAME)).is.eq(JSON.stringify(privacy)); - coreStorage.removeDataFromLocalStorage(ID5_PRIVACY_STORAGE_NAME); - }) + const fetchRequest = await xhrServerMock.expectFetchRequest(); + const responseObject = utils.deepClone(ID5_JSON_RESPONSE); + responseObject.privacy = privacy; + + fetchRequest.respond(200, responseHeader, JSON.stringify(responseObject)); + await submoduleResponsePromise; + + expect(id5System.getFromLocalStorage(id5System.ID5_PRIVACY_STORAGE_NAME)).is.eq(JSON.stringify(privacy)); + coreStorage.removeDataFromLocalStorage(id5System.ID5_PRIVACY_STORAGE_NAME); }); - it('should not store a privacy object if not part of ID5 server response', function () { - let xhrServerMock = new XhrServerMock(server) - coreStorage.removeDataFromLocalStorage(ID5_PRIVACY_STORAGE_NAME); - let submoduleResponse = callSubmoduleGetId(getId5FetchConfig(), undefined, ID5_STORED_OBJ); + it('should not store a privacy object if not part of ID5 server response', async function () { + const xhrServerMock = new XhrServerMock(server); + coreStorage.removeDataFromLocalStorage(id5System.ID5_PRIVACY_STORAGE_NAME); - return xhrServerMock.expectFetchRequest() - .then(request => { - let responseObject = utils.deepClone(ID5_JSON_RESPONSE); - responseObject.privacy = undefined; - request.respond(200, responseHeader, JSON.stringify(responseObject)); - return submoduleResponse - }) - .then(() => { - expect(getFromLocalStorage(ID5_PRIVACY_STORAGE_NAME)).is.null; - }); + // Trigger the fetch but we await on it later + const submoduleResponsePromise = callSubmoduleGetId(getId5FetchConfig(), undefined, ID5_STORED_OBJ); + + const fetchRequest = await xhrServerMock.expectFetchRequest(); + const responseObject = utils.deepClone(ID5_JSON_RESPONSE); + responseObject.privacy = undefined; + + fetchRequest.respond(200, responseHeader, JSON.stringify(responseObject)); + await submoduleResponsePromise; + + expect(id5System.getFromLocalStorage(id5System.ID5_PRIVACY_STORAGE_NAME)).is.null; + }); + + describe('with successful external module call', function() { + const MOCK_RESPONSE = { + ...ID5_JSON_RESPONSE, + universal_uid: 'my_mock_reponse' + }; + let mockId5ExternalModule; + + beforeEach(() => { + window.id5Prebid = { + integration: { + fetchId5Id: function() {} + } + }; + mockId5ExternalModule = sinon.stub(window.id5Prebid.integration, 'fetchId5Id') + .resolves(MOCK_RESPONSE); + }); + + this.afterEach(() => { + mockId5ExternalModule.restore(); + delete window.id5Prebid; + }); + + it('should retrieve the response from the external module interface', async function() { + const xhrServerMock = new XhrServerMock(server); + const config = getId5FetchConfig(); + config.params.externalModuleUrl = 'https://test-me.test'; + + // Trigger the fetch but we await on it later + const submoduleResponsePromise = callSubmoduleGetId(config, undefined, undefined); + + const configRequest = await xhrServerMock.expectConfigRequest(); + configRequest.respond(200, HEADERS_CONTENT_TYPE_JSON, JSON.stringify(ID5_API_CONFIG)); + + const submoduleResponse = await submoduleResponsePromise; + expect(submoduleResponse).to.deep.equal(MOCK_RESPONSE); + expect(mockId5ExternalModule.calledOnce); + }); + }); + + describe('with failing external module loading', function() { + it('should fallback to regular logic if external module fails to load', async function() { + const xhrServerMock = new XhrServerMock(server); + const config = getId5FetchConfig(); + config.params.externalModuleUrl = 'https://test-me.test'; // Fails by loading this fake URL + + // Trigger the fetch but we await on it later + const submoduleResponsePromise = callSubmoduleGetId(config, undefined, undefined); + + // Still we have a server-side request triggered as fallback + const fetchRequest = await xhrServerMock.expectFetchRequest(); + fetchRequest.respond(200, responseHeader, JSON.stringify(ID5_JSON_RESPONSE)); + + const submoduleResponse = await submoduleResponsePromise; + expect(submoduleResponse).to.deep.equal(ID5_JSON_RESPONSE); + }); }); it('should pass gpp_string and gpp_sid to ID5 server', function () { @@ -756,14 +792,14 @@ describe('ID5 ID System', function () { let sandbox; beforeEach(() => { sandbox = sinon.sandbox.create(); - sandbox.stub(storage, 'getCookie'); + sandbox.stub(id5System.storage, 'getCookie'); }); afterEach(() => { sandbox.restore(); }); it('should not throw if malformed JSON is forced into cookies', () => { - storage.getCookie.callsFake(() => ' Not JSON '); - id5IdSubmodule.getId(getId5FetchConfig()); + id5System.storage.getCookie.callsFake(() => ' Not JSON '); + id5System.id5IdSubmodule.getId(getId5FetchConfig()); }); }) }); @@ -772,7 +808,7 @@ describe('ID5 ID System', function () { let sandbox; beforeEach(() => { sandbox = sinon.sandbox.create(); - sandbox.stub(storage, 'localStorageIsEnabled'); + sandbox.stub(id5System.storage, 'localStorageIsEnabled'); }); afterEach(() => { sandbox.restore(); @@ -780,26 +816,23 @@ describe('ID5 ID System', function () { [ [true, 1], [false, 0] - ].forEach(function ([isEnabled, expectedValue]) { - it(`should check localStorage availability and log in request. Available=${isEnabled}`, () => { - let xhrServerMock = new XhrServerMock(server) - let config = getId5FetchConfig(); - let submoduleResponse = callSubmoduleGetId(config, undefined, undefined); - storage.localStorageIsEnabled.callsFake(() => isEnabled) - - return xhrServerMock.expectFetchRequest() - .then(fetchRequest => { - let requestBody = JSON.parse(fetchRequest.requestBody); - expect(requestBody.localStorage).is.eq(expectedValue); - - fetchRequest.respond(200, HEADERS_CONTENT_TYPE_JSON, JSON.stringify(ID5_JSON_RESPONSE)); - return submoduleResponse - }) - .then(submoduleResponse => { - expect(submoduleResponse).is.deep.equal(ID5_JSON_RESPONSE); - }); - }) - }) + ].forEach(([isEnabled, expectedValue]) => { + it(`should check localStorage availability and log in request. Available=${isEnabled}`, async function() { + const xhrServerMock = new XhrServerMock(server) + id5System.storage.localStorageIsEnabled.callsFake(() => isEnabled) + + // Trigger the fetch but we await on it later + const submoduleResponsePromise = callSubmoduleGetId(getId5FetchConfig(), undefined, ID5_STORED_OBJ); + + const fetchRequest = await xhrServerMock.expectFetchRequest(); + const requestBody = JSON.parse(fetchRequest.requestBody); + expect(requestBody.localStorage).is.eq(expectedValue); + + fetchRequest.respond(200, HEADERS_CONTENT_TYPE_JSON, JSON.stringify(ID5_JSON_RESPONSE)); + const submoduleResponse = await submoduleResponsePromise; + expect(submoduleResponse).is.deep.equal(ID5_JSON_RESPONSE); + }); + }); }); describe('Request Bids Hook', function () { @@ -810,26 +843,26 @@ describe('ID5 ID System', function () { sandbox = sinon.sandbox.create(); mockGdprConsent(sandbox); sinon.stub(events, 'getEvents').returns([]); - coreStorage.removeDataFromLocalStorage(ID5_STORAGE_NAME); - coreStorage.removeDataFromLocalStorage(`${ID5_STORAGE_NAME}_last`); - coreStorage.removeDataFromLocalStorage(ID5_NB_STORAGE_NAME); - coreStorage.setDataInLocalStorage(ID5_STORAGE_NAME + '_cst', getConsentHash()) + coreStorage.removeDataFromLocalStorage(id5System.ID5_STORAGE_NAME); + coreStorage.removeDataFromLocalStorage(`${id5System.ID5_STORAGE_NAME}_last`); + coreStorage.removeDataFromLocalStorage(id5System.nbCacheName(ID5_TEST_PARTNER_ID)); + coreStorage.setDataInLocalStorage(id5System.ID5_STORAGE_NAME + '_cst', getConsentHash()) adUnits = [getAdUnitMock()]; }); afterEach(function () { events.getEvents.restore(); - coreStorage.removeDataFromLocalStorage(ID5_STORAGE_NAME); - coreStorage.removeDataFromLocalStorage(`${ID5_STORAGE_NAME}_last`); - coreStorage.removeDataFromLocalStorage(ID5_NB_STORAGE_NAME); - coreStorage.removeDataFromLocalStorage(ID5_STORAGE_NAME + '_cst') + coreStorage.removeDataFromLocalStorage(id5System.ID5_STORAGE_NAME); + coreStorage.removeDataFromLocalStorage(`${id5System.ID5_STORAGE_NAME}_last`); + coreStorage.removeDataFromLocalStorage(id5System.nbCacheName(ID5_TEST_PARTNER_ID)); + coreStorage.removeDataFromLocalStorage(id5System.ID5_STORAGE_NAME + '_cst') sandbox.restore(); }); it('should add stored ID from cache to bids', function (done) { - storeInLocalStorage(ID5_STORAGE_NAME, JSON.stringify(ID5_STORED_OBJ), 1); + id5System.storeInLocalStorage(id5System.ID5_STORAGE_NAME, JSON.stringify(ID5_STORED_OBJ), 1); init(config); - setSubmoduleRegistry([id5IdSubmodule]); + setSubmoduleRegistry([id5System.id5IdSubmodule]); config.setConfig(getFetchLocalStorageConfig()); requestBidsHook(function () { @@ -855,7 +888,7 @@ describe('ID5 ID System', function () { it('should add config value ID to bids', function (done) { init(config); - setSubmoduleRegistry([id5IdSubmodule]); + setSubmoduleRegistry([id5System.id5IdSubmodule]); config.setConfig(getValueConfig(ID5_STORED_ID)); requestBidsHook(function () { @@ -874,44 +907,44 @@ describe('ID5 ID System', function () { }); it('should set nb=1 in cache when no stored nb value exists and cached ID', function (done) { - storeInLocalStorage(ID5_STORAGE_NAME, JSON.stringify(ID5_STORED_OBJ), 1); - coreStorage.removeDataFromLocalStorage(ID5_NB_STORAGE_NAME); + id5System.storeInLocalStorage(id5System.ID5_STORAGE_NAME, JSON.stringify(ID5_STORED_OBJ), 1); + coreStorage.removeDataFromLocalStorage(id5System.nbCacheName(ID5_TEST_PARTNER_ID)); init(config); - setSubmoduleRegistry([id5IdSubmodule]); + setSubmoduleRegistry([id5System.id5IdSubmodule]); config.setConfig(getFetchLocalStorageConfig()); requestBidsHook((adUnitConfig) => { - expect(getNbFromCache(ID5_TEST_PARTNER_ID)).is.eq(1); + expect(id5System.getNbFromCache(ID5_TEST_PARTNER_ID)).is.eq(1); done() }, {adUnits}); }); it('should increment nb in cache when stored nb value exists and cached ID', function (done) { - storeInLocalStorage(ID5_STORAGE_NAME, JSON.stringify(ID5_STORED_OBJ), 1); - storeNbInCache(ID5_TEST_PARTNER_ID, 1); + id5System.storeInLocalStorage(id5System.ID5_STORAGE_NAME, JSON.stringify(ID5_STORED_OBJ), 1); + id5System.storeNbInCache(ID5_TEST_PARTNER_ID, 1); init(config); - setSubmoduleRegistry([id5IdSubmodule]); + setSubmoduleRegistry([id5System.id5IdSubmodule]); config.setConfig(getFetchLocalStorageConfig()); requestBidsHook(() => { - expect(getNbFromCache(ID5_TEST_PARTNER_ID)).is.eq(2); + expect(id5System.getNbFromCache(ID5_TEST_PARTNER_ID)).is.eq(2); done() }, {adUnits}); }); it('should call ID5 servers with signature and incremented nb post auction if refresh needed', function () { - let xhrServerMock = new XhrServerMock(server) - let initialLocalStorageValue = JSON.stringify(ID5_STORED_OBJ); - storeInLocalStorage(ID5_STORAGE_NAME, initialLocalStorageValue, 1); - storeInLocalStorage(`${ID5_STORAGE_NAME}_last`, expDaysStr(-1), 1); + const xhrServerMock = new XhrServerMock(server) + const initialLocalStorageValue = JSON.stringify(ID5_STORED_OBJ); + id5System.storeInLocalStorage(id5System.ID5_STORAGE_NAME, initialLocalStorageValue, 1); + id5System.storeInLocalStorage(`${id5System.ID5_STORAGE_NAME}_last`, id5System.expDaysStr(-1), 1); - storeNbInCache(ID5_TEST_PARTNER_ID, 1); + id5System.storeNbInCache(ID5_TEST_PARTNER_ID, 1); let id5Config = getFetchLocalStorageConfig(); id5Config.userSync.userIds[0].storage.refreshInSeconds = 2; init(config); - setSubmoduleRegistry([id5IdSubmodule]); + setSubmoduleRegistry([id5System.id5IdSubmodule]); config.setConfig(id5Config); return new Promise((resolve) => { @@ -921,23 +954,23 @@ describe('ID5 ID System', function () { }).then(() => { expect(xhrServerMock.hasReceivedAnyRequest()).is.false; events.emit(CONSTANTS.EVENTS.AUCTION_END, {}); - return xhrServerMock.expectFetchRequest() + return xhrServerMock.expectFetchRequest(); }).then(request => { - let requestBody = JSON.parse(request.requestBody); + const requestBody = JSON.parse(request.requestBody); expect(requestBody.s).is.eq(ID5_STORED_SIGNATURE); expect(requestBody.nbPage).is.eq(2); - expect(getNbFromCache(ID5_TEST_PARTNER_ID)).is.eq(2); + expect(id5System.getNbFromCache(ID5_TEST_PARTNER_ID)).is.eq(0); request.respond(200, HEADERS_CONTENT_TYPE_JSON, JSON.stringify(ID5_JSON_RESPONSE)); return new Promise(function (resolve) { (function waitForCondition() { - if (getFromLocalStorage(ID5_STORAGE_NAME) !== initialLocalStorageValue) return resolve(); + if (id5System.getFromLocalStorage(id5System.ID5_STORAGE_NAME) !== initialLocalStorageValue) return resolve(); setTimeout(waitForCondition, 30); })(); }) }).then(() => { - expect(decodeURIComponent(getFromLocalStorage(ID5_STORAGE_NAME))).is.eq(JSON.stringify(ID5_JSON_RESPONSE)); - expect(getNbFromCache(ID5_TEST_PARTNER_ID)).is.eq(0); + expect(decodeURIComponent(id5System.getFromLocalStorage(id5System.ID5_STORAGE_NAME))).is.eq(JSON.stringify(ID5_JSON_RESPONSE)); + expect(id5System.getNbFromCache(ID5_TEST_PARTNER_ID)).is.eq(0); }) }); }); @@ -946,10 +979,10 @@ describe('ID5 ID System', function () { const expectedDecodedObject = {id5id: {uid: ID5_STORED_ID, ext: {linkType: ID5_STORED_LINK_TYPE}}}; it('should properly decode from a stored object', function () { - expect(id5IdSubmodule.decode(ID5_STORED_OBJ, getId5FetchConfig())).is.deep.equal(expectedDecodedObject); + expect(id5System.id5IdSubmodule.decode(ID5_STORED_OBJ, getId5FetchConfig())).is.deep.equal(expectedDecodedObject); }); it('should return undefined if passed a string', function () { - expect(id5IdSubmodule.decode('somestring', getId5FetchConfig())).is.eq(undefined); + expect(id5System.id5IdSubmodule.decode('somestring', getId5FetchConfig())).is.eq(undefined); }); }); @@ -992,13 +1025,13 @@ describe('ID5 ID System', function () { }); it('should not set abTestingControlGroup extension when A/B testing is off', function () { - let decoded = id5IdSubmodule.decode(storedObject, testConfig); + const decoded = id5System.id5IdSubmodule.decode(storedObject, testConfig); expect(decoded).is.deep.equal(expectedDecodedObjectWithIdAbOff); }); it('should set abTestingControlGroup to false when A/B testing is on but in normal group', function () { storedObject.ab_testing = {result: 'normal'}; - let decoded = id5IdSubmodule.decode(storedObject, testConfig); + const decoded = id5System.id5IdSubmodule.decode(storedObject, testConfig); expect(decoded).is.deep.equal(expectedDecodedObjectWithIdAbOn); }); @@ -1008,13 +1041,13 @@ describe('ID5 ID System', function () { storedObject.ext = { 'linkType': 0 }; - let decoded = id5IdSubmodule.decode(storedObject, testConfig); + const decoded = id5System.id5IdSubmodule.decode(storedObject, testConfig); expect(decoded).is.deep.equal(expectedDecodedObjectWithoutIdAbOn); }); it('should log A/B testing errors', function () { storedObject.ab_testing = {result: 'error'}; - let decoded = id5IdSubmodule.decode(storedObject, testConfig); + const decoded = id5System.id5IdSubmodule.decode(storedObject, testConfig); expect(decoded).is.deep.equal(expectedDecodedObjectWithIdAbOff); sinon.assert.calledOnce(logErrorSpy); }); From a189b322775784c0d101ffaf5b7022299e9d7352 Mon Sep 17 00:00:00 2001 From: Chris Huie Date: Tue, 23 Jan 2024 15:38:35 -0700 Subject: [PATCH 166/246] JSdoc Lint Fixes for Multiple ID Modules (#10972) * fixes jsdoc * fix id jsdocs * jsdoc id fixes * jsdoc id fixes * fix id module lint * id lint fixes * fix lint jsdoc * jslint fixes * id jsdoc lint fix * jsdoc lint fixes --- modules/33acrossIdSystem.js | 6 ++++++ modules/admixerIdSystem.js | 7 +++++++ modules/adqueryIdSystem.js | 6 ++++++ modules/adriverIdSystem.js | 7 +++++++ modules/adtelligentIdSystem.js | 7 +++++++ modules/britepoolIdSystem.js | 7 +++++++ modules/connectIdSystem.js | 7 +++++++ modules/criteoIdSystem.js | 6 ++++++ modules/czechAdIdSystem.js | 5 +++++ modules/deepintentDpesIdSystem.js | 6 ++++++ modules/dmdIdSystem.js | 7 +++++++ modules/euidIdSystem.js | 7 +++++++ modules/fabrickIdSystem.js | 7 +++++++ modules/ftrackIdSystem.js | 7 +++++++ modules/growthCodeIdSystem.js | 6 ++++++ modules/hadronIdSystem.js | 6 ++++++ modules/id5IdSystem.js | 9 ++++++++- modules/identityLinkIdSystem.js | 7 +++++++ modules/idxIdSystem.js | 5 +++++ modules/imuIdSystem.js | 5 +++++ modules/intentIqIdSystem.js | 6 ++++++ modules/justIdSystem.js | 7 +++++++ modules/kinessoIdSystem.js | 6 ++++++ modules/liveIntentIdSystem.js | 6 ++++++ modules/lotamePanoramaIdSystem.js | 7 +++++++ modules/merkleIdSystem.js | 7 +++++++ modules/mwOpenLinkIdSystem.js | 5 +++++ modules/mygaruIdSystem.js | 6 ++++++ modules/naveggIdSystem.js | 5 +++++ modules/netIdSystem.js | 7 +++++++ modules/novatiqIdSystem.js | 5 +++++ modules/oneKeyIdSystem.js | 7 +++++++ modules/operaadsIdSystem.js | 5 +++++ modules/pairIdSystem.js | 4 ++++ modules/parrableIdSystem.js | 6 ++++++ modules/pubProvidedIdSystem.js | 9 +++++++-- modules/publinkIdSystem.js | 7 +++++++ modules/quantcastIdSystem.js | 4 ++++ modules/sharedIdSystem.js | 8 ++++++++ modules/teadsIdSystem.js | 7 +++++++ modules/uid2IdSystem.js | 7 +++++++ modules/unifiedIdSystem.js | 6 ++++++ modules/verizonMediaIdSystem.js | 7 +++++++ modules/zeotapIdPlusIdSystem.js | 5 +++++ src/targeting.js | 8 ++++---- src/userSync.js | 13 ++++++------- 46 files changed, 286 insertions(+), 14 deletions(-) diff --git a/modules/33acrossIdSystem.js b/modules/33acrossIdSystem.js index 0f370237e21..0cb1b1f3382 100644 --- a/modules/33acrossIdSystem.js +++ b/modules/33acrossIdSystem.js @@ -10,6 +10,12 @@ import { ajaxBuilder } from '../src/ajax.js'; import { submodule } from '../src/hook.js'; import { uspDataHandler, coppaDataHandler, gppDataHandler } from '../src/adapterManager.js'; +/** + * @typedef {import('../modules/userId/index.js').Submodule} Submodule + * @typedef {import('../modules/userId/index.js').SubmoduleConfig} SubmoduleConfig + * @typedef {import('../modules/userId/index.js').IdResponse} IdResponse + */ + const MODULE_NAME = '33acrossId'; const API_URL = 'https://lexicon.33across.com/v1/envelope'; const AJAX_TIMEOUT = 10000; diff --git a/modules/admixerIdSystem.js b/modules/admixerIdSystem.js index 0e3a56420a8..cb7248c9537 100644 --- a/modules/admixerIdSystem.js +++ b/modules/admixerIdSystem.js @@ -11,6 +11,13 @@ import { submodule } from '../src/hook.js'; import {getStorageManager} from '../src/storageManager.js'; import {MODULE_TYPE_UID} from '../src/activities/modules.js'; +/** + * @typedef {import('../modules/userId/index.js').Submodule} Submodule + * @typedef {import('../modules/userId/index.js').SubmoduleConfig} SubmoduleConfig + * @typedef {import('../modules/userId/index.js').ConsentData} ConsentData + * @typedef {import('../modules/userId/index.js').IdResponse} IdResponse + */ + const NAME = 'admixerId'; export const storage = getStorageManager({moduleType: MODULE_TYPE_UID, moduleName: NAME}); diff --git a/modules/adqueryIdSystem.js b/modules/adqueryIdSystem.js index c5d01d7fbed..eb00011593d 100644 --- a/modules/adqueryIdSystem.js +++ b/modules/adqueryIdSystem.js @@ -11,6 +11,12 @@ import {submodule} from '../src/hook.js'; import {isFn, isPlainObject, isStr, logError, logInfo} from '../src/utils.js'; import {MODULE_TYPE_UID} from '../src/activities/modules.js'; +/** + * @typedef {import('../modules/userId/index.js').Submodule} Submodule + * @typedef {import('../modules/userId/index.js').SubmoduleConfig} SubmoduleConfig + * @typedef {import('../modules/userId/index.js').IdResponse} IdResponse + */ + const MODULE_NAME = 'qid'; const AU_GVLID = 902; diff --git a/modules/adriverIdSystem.js b/modules/adriverIdSystem.js index c04ebf48028..1da75f2063d 100644 --- a/modules/adriverIdSystem.js +++ b/modules/adriverIdSystem.js @@ -11,6 +11,13 @@ import { submodule } from '../src/hook.js'; import {getStorageManager} from '../src/storageManager.js'; import {MODULE_TYPE_UID} from '../src/activities/modules.js'; +/** + * @typedef {import('../modules/userId/index.js').Submodule} Submodule + * @typedef {import('../modules/userId/index.js').SubmoduleConfig} SubmoduleConfig + * @typedef {import('../modules/userId/index.js').ConsentData} ConsentData + * @typedef {import('../modules/userId/index.js').IdResponse} IdResponse + */ + const MODULE_NAME = 'adriverId'; export const storage = getStorageManager({moduleType: MODULE_TYPE_UID, moduleName: MODULE_NAME}); diff --git a/modules/adtelligentIdSystem.js b/modules/adtelligentIdSystem.js index 440ed9ade75..76713f29775 100644 --- a/modules/adtelligentIdSystem.js +++ b/modules/adtelligentIdSystem.js @@ -8,6 +8,13 @@ import * as ajax from '../src/ajax.js'; import { submodule } from '../src/hook.js'; +/** + * @typedef {import('../modules/userId/index.js').Submodule} Submodule + * @typedef {import('../modules/userId/index.js').SubmoduleConfig} SubmoduleConfig + * @typedef {import('../modules/userId/index.js').ConsentData} ConsentData + * @typedef {import('../modules/userId/index.js').IdResponse} IdResponse + */ + const gvlid = 410; const moduleName = 'adtelligent'; const syncUrl = 'https://idrs.adtelligent.com/get'; diff --git a/modules/britepoolIdSystem.js b/modules/britepoolIdSystem.js index b75fe9424b1..37ace544dc7 100644 --- a/modules/britepoolIdSystem.js +++ b/modules/britepoolIdSystem.js @@ -10,6 +10,13 @@ import {ajax} from '../src/ajax.js'; import {submodule} from '../src/hook.js'; const PIXEL = 'https://px.britepool.com/new?partner_id=t'; +/** + * @typedef {import('../modules/userId/index.js').Submodule} Submodule + * @typedef {import('../modules/userId/index.js').SubmoduleConfig} SubmoduleConfig + * @typedef {import('../modules/userId/index.js').ConsentData} ConsentData + * @typedef {import('../modules/userId/index.js').SubmoduleParams} SubmoduleParams + */ + /** @type {Submodule} */ export const britepoolIdSubmodule = { /** diff --git a/modules/connectIdSystem.js b/modules/connectIdSystem.js index 35a77a9d72d..2ebc68baa84 100644 --- a/modules/connectIdSystem.js +++ b/modules/connectIdSystem.js @@ -14,6 +14,13 @@ import {formatQS, isNumber, isPlainObject, logError, parseUrl} from '../src/util import {uspDataHandler, gppDataHandler} from '../src/adapterManager.js'; import {MODULE_TYPE_UID} from '../src/activities/modules.js'; +/** + * @typedef {import('../modules/userId/index.js').Submodule} Submodule + * @typedef {import('../modules/userId/index.js').SubmoduleConfig} SubmoduleConfig + * @typedef {import('../modules/userId/index.js').ConsentData} ConsentData + * @typedef {import('../modules/userId/index.js').IdResponse} IdResponse + */ + const MODULE_NAME = 'connectId'; const STORAGE_EXPIRY_DAYS = 365; const STORAGE_DURATION = 60 * 60 * 24 * 1000 * STORAGE_EXPIRY_DAYS; diff --git a/modules/criteoIdSystem.js b/modules/criteoIdSystem.js index 6a09ce2c973..0c42858a0fb 100644 --- a/modules/criteoIdSystem.js +++ b/modules/criteoIdSystem.js @@ -13,6 +13,12 @@ import { getStorageManager } from '../src/storageManager.js'; import { MODULE_TYPE_UID } from '../src/activities/modules.js'; import { gdprDataHandler, uspDataHandler, gppDataHandler } from '../src/adapterManager.js'; +/** + * @typedef {import('../modules/userId/index.js').Submodule} Submodule + * @typedef {import('../modules/userId/index.js').SubmoduleConfig} SubmoduleConfig + * @typedef {import('../modules/userId/index.js').ConsentData} ConsentData + */ + const gvlid = 91; const bidderCode = 'criteo'; export const storage = getStorageManager({ moduleType: MODULE_TYPE_UID, moduleName: bidderCode }); diff --git a/modules/czechAdIdSystem.js b/modules/czechAdIdSystem.js index ae958aae198..7fdf462183a 100644 --- a/modules/czechAdIdSystem.js +++ b/modules/czechAdIdSystem.js @@ -9,6 +9,11 @@ import { submodule } from '../src/hook.js' import {getStorageManager} from '../src/storageManager.js'; import {MODULE_TYPE_UID} from '../src/activities/modules.js'; +/** + * @typedef {import('../modules/userId/index.js').Submodule} Submodule + * @typedef {import('../modules/userId/index.js').IdResponse} IdResponse + */ + // Returns StorageManager export const storage = getStorageManager({ moduleType: MODULE_TYPE_UID, moduleName: 'czechAdId' }) diff --git a/modules/deepintentDpesIdSystem.js b/modules/deepintentDpesIdSystem.js index 4d685592c04..2d3eae980cd 100644 --- a/modules/deepintentDpesIdSystem.js +++ b/modules/deepintentDpesIdSystem.js @@ -9,6 +9,12 @@ import { submodule } from '../src/hook.js'; import {getStorageManager} from '../src/storageManager.js'; import {MODULE_TYPE_UID} from '../src/activities/modules.js'; +/** + * @typedef {import('../modules/userId/index.js').Submodule} Submodule + * @typedef {import('../modules/userId/index.js').SubmoduleConfig} SubmoduleConfig + * @typedef {import('../modules/userId/index.js').ConsentData} ConsentData + */ + const MODULE_NAME = 'deepintentId'; export const storage = getStorageManager({moduleType: MODULE_TYPE_UID, moduleName: MODULE_NAME}); diff --git a/modules/dmdIdSystem.js b/modules/dmdIdSystem.js index 2f910a8bd92..3575e658a2a 100644 --- a/modules/dmdIdSystem.js +++ b/modules/dmdIdSystem.js @@ -9,6 +9,13 @@ import { logError, getWindowLocation } from '../src/utils.js'; import { submodule } from '../src/hook.js'; import { ajax } from '../src/ajax.js'; +/** + * @typedef {import('../modules/userId/index.js').Submodule} Submodule + * @typedef {import('../modules/userId/index.js').SubmoduleConfig} SubmoduleConfig + * @typedef {import('../modules/userId/index.js').ConsentData} ConsentData + * @typedef {import('../modules/userId/index.js').IdResponse} IdResponse + */ + const MODULE_NAME = 'dmdId'; /** @type {Submodule} */ diff --git a/modules/euidIdSystem.js b/modules/euidIdSystem.js index a29da69d6c7..fa6113250a8 100644 --- a/modules/euidIdSystem.js +++ b/modules/euidIdSystem.js @@ -14,6 +14,13 @@ import {MODULE_TYPE_UID} from '../src/activities/modules.js'; // eslint-disable-next-line prebid/validate-imports import { Uid2GetId, Uid2CodeVersion, extractIdentityFromParams } from './uid2IdSystem_shared.js'; +/** + * @typedef {import('../modules/userId/index.js').Submodule} Submodule + * @typedef {import('../modules/userId/index.js').SubmoduleConfig} SubmoduleConfig + * @typedef {import('../modules/userId/index.js').ConsentData} ConsentData + * @typedef {import('../modules/userId/index.js').euidId} euidId + */ + const MODULE_NAME = 'euid'; const MODULE_REVISION = Uid2CodeVersion; const PREBID_VERSION = '$prebid.version$'; diff --git a/modules/fabrickIdSystem.js b/modules/fabrickIdSystem.js index bc9c30cb479..f62bafcf637 100644 --- a/modules/fabrickIdSystem.js +++ b/modules/fabrickIdSystem.js @@ -10,6 +10,13 @@ import { ajax } from '../src/ajax.js'; import { submodule } from '../src/hook.js'; import { getRefererInfo } from '../src/refererDetection.js'; +/** + * @typedef {import('../modules/userId/index.js').Submodule} Submodule + * @typedef {import('../modules/userId/index.js').SubmoduleConfig} SubmoduleConfig + * @typedef {import('../modules/userId/index.js').ConsentData} ConsentData + * @typedef {import('../modules/userId/index.js').IdResponse} IdResponse + */ + /** @type {Submodule} */ export const fabrickIdSubmodule = { /** diff --git a/modules/ftrackIdSystem.js b/modules/ftrackIdSystem.js index 809f1311c42..1794c3f76f4 100644 --- a/modules/ftrackIdSystem.js +++ b/modules/ftrackIdSystem.js @@ -12,6 +12,13 @@ import {uspDataHandler} from '../src/adapterManager.js'; import {loadExternalScript} from '../src/adloader.js'; import {MODULE_TYPE_UID} from '../src/activities/modules.js'; +/** + * @typedef {import('../modules/userId/index.js').Submodule} Submodule + * @typedef {import('../modules/userId/index.js').SubmoduleConfig} SubmoduleConfig + * @typedef {import('../modules/userId/index.js').ConsentData} ConsentData + * @typedef {import('../modules/userId/index.js').IdResponse} IdResponse + */ + const MODULE_NAME = 'ftrackId'; const LOG_PREFIX = 'FTRACK - '; const LOCAL_STORAGE_EXP_DAYS = 30; diff --git a/modules/growthCodeIdSystem.js b/modules/growthCodeIdSystem.js index e50a4e73019..539a20a7302 100644 --- a/modules/growthCodeIdSystem.js +++ b/modules/growthCodeIdSystem.js @@ -12,6 +12,12 @@ import {getStorageManager} from '../src/storageManager.js'; import {MODULE_TYPE_UID} from '../src/activities/modules.js'; import {tryAppendQueryString} from '../libraries/urlUtils/urlUtils.js'; +/** + * @typedef {import('../modules/userId/index.js').Submodule} Submodule + * @typedef {import('../modules/userId/index.js').SubmoduleConfig} SubmoduleConfig + * @typedef {import('../modules/userId/index.js').IdResponse} IdResponse + */ + const MODULE_NAME = 'growthCodeId'; const GC_DATA_KEY = '_gc_data'; const GCID_KEY = 'gcid'; diff --git a/modules/hadronIdSystem.js b/modules/hadronIdSystem.js index 4141e9a01f8..01b0283d3d1 100644 --- a/modules/hadronIdSystem.js +++ b/modules/hadronIdSystem.js @@ -13,6 +13,12 @@ import { config } from '../src/config.js'; import {MODULE_TYPE_UID} from '../src/activities/modules.js'; import { gdprDataHandler, uspDataHandler, gppDataHandler } from '../src/adapterManager.js'; +/** + * @typedef {import('../modules/userId/index.js').Submodule} Submodule + * @typedef {import('../modules/userId/index.js').SubmoduleConfig} SubmoduleConfig + * @typedef {import('../modules/userId/index.js').IdResponse} IdResponse + */ + const LOG_PREFIX = '[hadronIdSystem]'; const HADRONID_LOCAL_NAME = 'auHadronId'; const MODULE_NAME = 'hadronId'; diff --git a/modules/id5IdSystem.js b/modules/id5IdSystem.js index 96ae5386de6..b10f3c5d2cf 100644 --- a/modules/id5IdSystem.js +++ b/modules/id5IdSystem.js @@ -24,6 +24,13 @@ import {MODULE_TYPE_UID} from '../src/activities/modules.js'; import { GreedyPromise } from '../src/utils/promise.js'; import { loadExternalScript } from '../src/adloader.js'; +/** + * @typedef {import('../modules/userId/index.js').Submodule} Submodule + * @typedef {import('../modules/userId/index.js').SubmoduleConfig} SubmoduleConfig + * @typedef {import('../modules/userId/index.js').ConsentData} ConsentData + * @typedef {import('../modules/userId/index.js').IdResponse} IdResponse + */ + const MODULE_NAME = 'id5Id'; const GVLID = 131; const NB_EXP_DAYS = 30; @@ -521,7 +528,7 @@ export function getFromLocalStorage(key) { * by default it's not required * @param {string} key * @param {any} value - * @param {integer} expDays + * @param {number} expDays */ export function storeInLocalStorage(key, value, expDays) { storage.setDataInLocalStorage(`${key}_exp`, expDaysStr(expDays)); diff --git a/modules/identityLinkIdSystem.js b/modules/identityLinkIdSystem.js index d70b5680b69..82aa2303e1c 100644 --- a/modules/identityLinkIdSystem.js +++ b/modules/identityLinkIdSystem.js @@ -12,6 +12,13 @@ import {getStorageManager} from '../src/storageManager.js'; import {MODULE_TYPE_UID} from '../src/activities/modules.js'; import { gppDataHandler } from '../src/adapterManager.js'; +/** + * @typedef {import('../modules/userId/index.js').Submodule} Submodule + * @typedef {import('../modules/userId/index.js').SubmoduleConfig} SubmoduleConfig + * @typedef {import('../modules/userId/index.js').ConsentData} ConsentData + * @typedef {import('../modules/userId/index.js').IdResponse} IdResponse + */ + const MODULE_NAME = 'identityLink'; export const storage = getStorageManager({moduleType: MODULE_TYPE_UID, moduleName: MODULE_NAME}); diff --git a/modules/idxIdSystem.js b/modules/idxIdSystem.js index bf807f199a6..db545eecd8c 100644 --- a/modules/idxIdSystem.js +++ b/modules/idxIdSystem.js @@ -9,6 +9,11 @@ import { submodule } from '../src/hook.js'; import {getStorageManager} from '../src/storageManager.js'; import {MODULE_TYPE_UID} from '../src/activities/modules.js'; +/** + * @typedef {import('../modules/userId/index.js').Submodule} Submodule + * @typedef {import('../modules/userId/index.js').SubmoduleConfig} SubmoduleConfig + */ + const IDX_MODULE_NAME = 'idx'; const IDX_COOKIE_NAME = '_idx'; export const storage = getStorageManager({moduleType: MODULE_TYPE_UID, moduleName: IDX_MODULE_NAME}); diff --git a/modules/imuIdSystem.js b/modules/imuIdSystem.js index 38870c9403b..1242ca183ea 100644 --- a/modules/imuIdSystem.js +++ b/modules/imuIdSystem.js @@ -11,6 +11,11 @@ import { submodule } from '../src/hook.js'; import {getStorageManager} from '../src/storageManager.js'; import {MODULE_TYPE_UID} from '../src/activities/modules.js'; +/** + * @typedef {import('../modules/userId/index.js').Submodule} Submodule + * @typedef {import('../modules/userId/index.js').SubmoduleConfig} SubmoduleConfig + */ + const MODULE_NAME = 'imuid'; export const storage = getStorageManager({moduleType: MODULE_TYPE_UID, moduleName: MODULE_NAME}); diff --git a/modules/intentIqIdSystem.js b/modules/intentIqIdSystem.js index 5164080c317..109ea5c39c6 100644 --- a/modules/intentIqIdSystem.js +++ b/modules/intentIqIdSystem.js @@ -11,6 +11,12 @@ import { submodule } from '../src/hook.js' import { getStorageManager } from '../src/storageManager.js'; import { MODULE_TYPE_UID } from '../src/activities/modules.js'; +/** + * @typedef {import('../modules/userId/index.js').Submodule} Submodule + * @typedef {import('../modules/userId/index.js').SubmoduleConfig} SubmoduleConfig + * @typedef {import('../modules/userId/index.js').IdResponse} IdResponse + */ + const PCID_EXPIRY = 365; const MODULE_NAME = 'intentIqId'; diff --git a/modules/justIdSystem.js b/modules/justIdSystem.js index 26e9275bbab..a5698023020 100644 --- a/modules/justIdSystem.js +++ b/modules/justIdSystem.js @@ -10,6 +10,13 @@ import { submodule } from '../src/hook.js' import { loadExternalScript } from '../src/adloader.js' import {includes} from '../src/polyfill.js'; +/** + * @typedef {import('../modules/userId/index.js').Submodule} Submodule + * @typedef {import('../modules/userId/index.js').SubmoduleConfig} SubmoduleConfig + * @typedef {import('../modules/userId/index.js').ConsentData} ConsentData + * @typedef {import('../modules/userId/index.js').IdResponse} IdResponse + */ + const MODULE_NAME = 'justId'; const EXTERNAL_SCRIPT_MODULE_CODE = 'justtag'; const LOG_PREFIX = 'User ID - JustId submodule: '; diff --git a/modules/kinessoIdSystem.js b/modules/kinessoIdSystem.js index c13ed3976d3..35b8dcc182d 100644 --- a/modules/kinessoIdSystem.js +++ b/modules/kinessoIdSystem.js @@ -10,6 +10,12 @@ import {ajax} from '../src/ajax.js'; import {submodule} from '../src/hook.js'; import {coppaDataHandler, uspDataHandler} from '../src/adapterManager.js'; +/** + * @typedef {import('../modules/userId/index.js').Submodule} Submodule + * @typedef {import('../modules/userId/index.js').SubmoduleConfig} SubmoduleConfig + * @typedef {import('../modules/userId/index.js').ConsentData} ConsentData + */ + const MODULE_NAME = 'kpuid'; const ID_SVC = 'https://id.knsso.com/id'; // These values should NEVER change. If diff --git a/modules/liveIntentIdSystem.js b/modules/liveIntentIdSystem.js index 8dc7102091d..ad71a584e42 100644 --- a/modules/liveIntentIdSystem.js +++ b/modules/liveIntentIdSystem.js @@ -13,6 +13,12 @@ import {getStorageManager} from '../src/storageManager.js'; import {MODULE_TYPE_UID} from '../src/activities/modules.js'; import {UID2_EIDS} from '../libraries/uid2Eids/uid2Eids.js'; +/** + * @typedef {import('../modules/userId/index.js').Submodule} Submodule + * @typedef {import('../modules/userId/index.js').SubmoduleConfig} SubmoduleConfig + * @typedef {import('../modules/userId/index.js').IdResponse} IdResponse + */ + const DEFAULT_AJAX_TIMEOUT = 5000 const EVENTS_TOPIC = 'pre_lips' const MODULE_NAME = 'liveIntentId'; diff --git a/modules/lotamePanoramaIdSystem.js b/modules/lotamePanoramaIdSystem.js index 808a67492b0..64d631c2469 100644 --- a/modules/lotamePanoramaIdSystem.js +++ b/modules/lotamePanoramaIdSystem.js @@ -20,6 +20,13 @@ import {getStorageManager} from '../src/storageManager.js'; import { uspDataHandler } from '../src/adapterManager.js'; import {MODULE_TYPE_UID} from '../src/activities/modules.js'; +/** + * @typedef {import('../modules/userId/index.js').Submodule} Submodule + * @typedef {import('../modules/userId/index.js').SubmoduleConfig} SubmoduleConfig + * @typedef {import('../modules/userId/index.js').ConsentData} ConsentData + * @typedef {import('../modules/userId/index.js').IdResponse} IdResponse + */ + const KEY_ID = 'panoramaId'; const KEY_EXPIRY = `${KEY_ID}_expiry`; const KEY_PROFILE = '_cc_id'; diff --git a/modules/merkleIdSystem.js b/modules/merkleIdSystem.js index 20c6f817dca..3f3a90c3c49 100644 --- a/modules/merkleIdSystem.js +++ b/modules/merkleIdSystem.js @@ -11,6 +11,13 @@ import {submodule} from '../src/hook.js' import {getStorageManager} from '../src/storageManager.js'; import {MODULE_TYPE_UID} from '../src/activities/modules.js'; +/** + * @typedef {import('../modules/userId/index.js').Submodule} Submodule + * @typedef {import('../modules/userId/index.js').SubmoduleConfig} SubmoduleConfig + * @typedef {import('../modules/userId/index.js').ConsentData} ConsentData + * @typedef {import('../modules/userId/index.js').IdResponse} IdResponse + */ + const MODULE_NAME = 'merkleId'; const ID_URL = 'https://prebid.sv.rkdms.com/identity/'; const DEFAULT_REFRESH = 7 * 3600; diff --git a/modules/mwOpenLinkIdSystem.js b/modules/mwOpenLinkIdSystem.js index 8cc5cd8bb4a..c06f61ff82f 100644 --- a/modules/mwOpenLinkIdSystem.js +++ b/modules/mwOpenLinkIdSystem.js @@ -11,6 +11,11 @@ import { submodule } from '../src/hook.js'; import {getStorageManager} from '../src/storageManager.js'; import {MODULE_TYPE_UID} from '../src/activities/modules.js'; +/** + * @typedef {import('../modules/userId/index.js').Submodule} Submodule + * @typedef {import('../modules/userId/index.js').SubmoduleParams} SubmoduleParams + */ + const openLinkID = { name: 'mwol', cookie_expiration: (86400 * 1000 * 365 * 1) // 1 year diff --git a/modules/mygaruIdSystem.js b/modules/mygaruIdSystem.js index 4d50de16d48..9133480477b 100644 --- a/modules/mygaruIdSystem.js +++ b/modules/mygaruIdSystem.js @@ -8,6 +8,12 @@ import { ajax } from '../src/ajax.js'; import { submodule } from '../src/hook.js'; +/** + * @typedef {import('../modules/userId/index.js').Submodule} Submodule + * @typedef {import('../modules/userId/index.js').SubmoduleConfig} SubmoduleConfig + * @typedef {import('../modules/userId/index.js').ConsentData} ConsentData + */ + const bidderCode = 'mygaruId'; const syncUrl = 'https://ident.mygaru.com/v2/id'; diff --git a/modules/naveggIdSystem.js b/modules/naveggIdSystem.js index f82bf4002ca..42c6b113566 100644 --- a/modules/naveggIdSystem.js +++ b/modules/naveggIdSystem.js @@ -10,6 +10,11 @@ import { ajax } from '../src/ajax.js'; import {getStorageManager} from '../src/storageManager.js'; import {MODULE_TYPE_UID} from '../src/activities/modules.js'; +/** + * @typedef {import('../modules/userId/index.js').Submodule} Submodule + * @typedef {import('../modules/userId/index.js').SubmoduleConfig} SubmoduleConfig + */ + const MODULE_NAME = 'naveggId'; const OLD_NAVEGG_ID = 'nid'; const NAVEGG_ID = 'nvggid'; diff --git a/modules/netIdSystem.js b/modules/netIdSystem.js index 6f1ffe8b0e7..4765f892a97 100644 --- a/modules/netIdSystem.js +++ b/modules/netIdSystem.js @@ -7,6 +7,13 @@ import {submodule} from '../src/hook.js'; +/** + * @typedef {import('../modules/userId/index.js').Submodule} Submodule + * @typedef {import('../modules/userId/index.js').SubmoduleConfig} SubmoduleConfig + * @typedef {import('../modules/userId/index.js').ConsentData} ConsentData + * @typedef {import('../modules/userId/index.js').IdResponse} IdResponse + */ + /** @type {Submodule} */ export const netIdSubmodule = { /** diff --git a/modules/novatiqIdSystem.js b/modules/novatiqIdSystem.js index d0cc94b513b..b6eab776df2 100644 --- a/modules/novatiqIdSystem.js +++ b/modules/novatiqIdSystem.js @@ -11,6 +11,11 @@ import { submodule } from '../src/hook.js'; import {getStorageManager} from '../src/storageManager.js'; import {MODULE_TYPE_UID} from '../src/activities/modules.js'; +/** + * @typedef {import('../modules/userId/index.js').Submodule} Submodule + * @typedef {import('../modules/userId/index.js').SubmoduleConfig} SubmoduleConfig + */ + const MODULE_NAME = 'novatiq'; /** @type {Submodule} */ diff --git a/modules/oneKeyIdSystem.js b/modules/oneKeyIdSystem.js index 7aa9e7b48ac..8765a72a1af 100644 --- a/modules/oneKeyIdSystem.js +++ b/modules/oneKeyIdSystem.js @@ -8,6 +8,13 @@ import {submodule} from '../src/hook.js'; import { logError, logMessage } from '../src/utils.js'; +/** + * @typedef {import('../modules/userId/index.js').Submodule} Submodule + * @typedef {import('../modules/userId/index.js').SubmoduleConfig} SubmoduleConfig + * @typedef {import('../modules/userId/index.js').ConsentData} ConsentData + * @typedef {import('../modules/userId/index.js').IdResponse} IdResponse + */ + // Pre-init OneKey if it has not load yet. window.OneKey = window.OneKey || {}; window.OneKey.queue = window.OneKey.queue || []; diff --git a/modules/operaadsIdSystem.js b/modules/operaadsIdSystem.js index c16d5a9c009..7cf5e2ce5e1 100644 --- a/modules/operaadsIdSystem.js +++ b/modules/operaadsIdSystem.js @@ -8,6 +8,11 @@ import * as ajax from '../src/ajax.js'; import { submodule } from '../src/hook.js'; import { logMessage, logError } from '../src/utils.js'; +/** + * @typedef {import('../modules/userId/index.js').SubmoduleConfig} SubmoduleConfig + * @typedef {import('../modules/userId/index.js').IdResponse} IdResponse + */ + const MODULE_NAME = 'operaId'; const ID_KEY = MODULE_NAME; const version = '1.0'; diff --git a/modules/pairIdSystem.js b/modules/pairIdSystem.js index 1009846e06a..dbff4c6a402 100644 --- a/modules/pairIdSystem.js +++ b/modules/pairIdSystem.js @@ -10,6 +10,10 @@ import {getStorageManager} from '../src/storageManager.js' import { logInfo } from '../src/utils.js'; import {MODULE_TYPE_UID} from '../src/activities/modules.js'; +/** + * @typedef {import('../modules/userId/index.js').Submodule} Submodule + */ + const MODULE_NAME = 'pairId'; const PAIR_ID_KEY = 'pairId'; const DEFAULT_LIVERAMP_PAIR_ID_KEY = '_lr_pairId'; diff --git a/modules/parrableIdSystem.js b/modules/parrableIdSystem.js index 3e3488f72f3..5651bdf0434 100644 --- a/modules/parrableIdSystem.js +++ b/modules/parrableIdSystem.js @@ -26,6 +26,12 @@ import {uspDataHandler} from '../src/adapterManager.js'; import {getStorageManager} from '../src/storageManager.js'; import {MODULE_TYPE_UID} from '../src/activities/modules.js'; +/** + * @typedef {import('../modules/userId/index.js').Submodule} Submodule + * @typedef {import('../modules/userId/index.js').SubmoduleConfig} SubmoduleConfig + * @typedef {import('../modules/userId/index.js').ConsentData} ConsentData + */ + const PARRABLE_URL = 'https://h.parrable.com/prebid'; const PARRABLE_COOKIE_NAME = '_parrable_id'; const PARRABLE_GVLID = 928; diff --git a/modules/pubProvidedIdSystem.js b/modules/pubProvidedIdSystem.js index baffd997443..d23d992e495 100644 --- a/modules/pubProvidedIdSystem.js +++ b/modules/pubProvidedIdSystem.js @@ -9,6 +9,11 @@ import {submodule} from '../src/hook.js'; import { logInfo, isArray } from '../src/utils.js'; import {VENDORLESS_GVLID} from '../src/consentHandler.js'; +/** + * @typedef {import('../modules/userId/index.js').Submodule} Submodule + * @typedef {import('../modules/userId/index.js').SubmoduleConfig} SubmoduleConfig + */ + const MODULE_NAME = 'pubProvidedId'; /** @type {Submodule} */ @@ -25,7 +30,7 @@ export const pubProvidedIdSubmodule = { * decode the stored id value for passing to bid request * @function * @param {string} value - * @returns {{pubProvidedId: array}} or undefined if value doesn't exists + * @returns {{pubProvidedId: Array}} or undefined if value doesn't exists */ decode(value) { const res = value ? {pubProvidedId: value} : undefined; @@ -37,7 +42,7 @@ export const pubProvidedIdSubmodule = { * performs action to obtain id and return a value. * @function * @param {SubmoduleConfig} [config] - * @returns {{id: array}} + * @returns {{id: Array}} */ getId(config) { const configParams = (config && config.params) || {}; diff --git a/modules/publinkIdSystem.js b/modules/publinkIdSystem.js index 1a993c99b45..e8eb90cd02a 100644 --- a/modules/publinkIdSystem.js +++ b/modules/publinkIdSystem.js @@ -12,6 +12,13 @@ import { parseUrl, buildUrl, logError } from '../src/utils.js'; import {uspDataHandler} from '../src/adapterManager.js'; import {MODULE_TYPE_UID} from '../src/activities/modules.js'; +/** + * @typedef {import('../modules/userId/index.js').Submodule} Submodule + * @typedef {import('../modules/userId/index.js').SubmoduleConfig} SubmoduleConfig + * @typedef {import('../modules/userId/index.js').ConsentData} ConsentData + * @typedef {import('../modules/userId/index.js').IdResponse} IdResponse + */ + const MODULE_NAME = 'publinkId'; const GVLID = 24; const PUBLINK_COOKIE = '_publink'; diff --git a/modules/quantcastIdSystem.js b/modules/quantcastIdSystem.js index 2faf638fc0b..d980f5316e5 100644 --- a/modules/quantcastIdSystem.js +++ b/modules/quantcastIdSystem.js @@ -11,6 +11,10 @@ import { triggerPixel, logInfo } from '../src/utils.js'; import { uspDataHandler, coppaDataHandler, gdprDataHandler } from '../src/adapterManager.js'; import {MODULE_TYPE_UID} from '../src/activities/modules.js'; +/** + * @typedef {import('../modules/userId/index.js').Submodule} Submodule + */ + const QUANTCAST_FPA = '__qca'; const DEFAULT_COOKIE_EXP_DAYS = 392; // (13 months - 2 days) const DAY_MS = 86400000; diff --git a/modules/sharedIdSystem.js b/modules/sharedIdSystem.js index 9046d6a633d..fa8b5e3bfdb 100644 --- a/modules/sharedIdSystem.js +++ b/modules/sharedIdSystem.js @@ -13,6 +13,14 @@ import {VENDORLESS_GVLID} from '../src/consentHandler.js'; import {MODULE_TYPE_UID} from '../src/activities/modules.js'; import {domainOverrideToRootDomain} from '../libraries/domainOverrideToRootDomain/index.js'; +/** + * @typedef {import('../modules/userId/index.js').Submodule} Submodule + * @typedef {import('../modules/userId/index.js').SubmoduleConfig} SubmoduleConfig + * @typedef {import('../modules/userId/index.js').SubmoduleParams} SubmoduleParams + * @typedef {import('../modules/userId/index.js').ConsentData} ConsentData + * @typedef {import('../modules/userId/index.js').IdResponse} IdResponse + */ + export const storage = getStorageManager({moduleType: MODULE_TYPE_UID, moduleName: 'sharedId'}); const COOKIE = 'cookie'; const LOCAL_STORAGE = 'html5'; diff --git a/modules/teadsIdSystem.js b/modules/teadsIdSystem.js index 4744fd5d37b..8026fe77f87 100644 --- a/modules/teadsIdSystem.js +++ b/modules/teadsIdSystem.js @@ -12,6 +12,13 @@ import {getStorageManager} from '../src/storageManager.js'; import {uspDataHandler} from '../src/adapterManager.js'; import {MODULE_TYPE_UID} from '../src/activities/modules.js'; +/** + * @typedef {import('../modules/userId/index.js').Submodule} Submodule + * @typedef {import('../modules/userId/index.js').SubmoduleConfig} SubmoduleConfig + * @typedef {import('../modules/userId/index.js').ConsentData} ConsentData + * @typedef {import('../modules/userId/index.js').IdResponse} IdResponse + */ + const MODULE_NAME = 'teadsId'; const GVL_ID = 132; const FP_TEADS_ID_COOKIE_NAME = '_tfpvi'; diff --git a/modules/uid2IdSystem.js b/modules/uid2IdSystem.js index 13c0303e0b3..32d2322e9bd 100644 --- a/modules/uid2IdSystem.js +++ b/modules/uid2IdSystem.js @@ -16,6 +16,13 @@ import {MODULE_TYPE_UID} from '../src/activities/modules.js'; import { Uid2GetId, Uid2CodeVersion, extractIdentityFromParams } from './uid2IdSystem_shared.js'; import {UID2_EIDS} from '../libraries/uid2Eids/uid2Eids.js'; +/** + * @typedef {import('../modules/userId/index.js').Submodule} Submodule + * @typedef {import('../modules/userId/index.js').SubmoduleConfig} SubmoduleConfig + * @typedef {import('../modules/userId/index.js').ConsentData} ConsentData + * @typedef {import('../modules/userId/index.js').uid2Id} uid2Id + */ + const MODULE_NAME = 'uid2'; const MODULE_REVISION = Uid2CodeVersion; const PREBID_VERSION = '$prebid.version$'; diff --git a/modules/unifiedIdSystem.js b/modules/unifiedIdSystem.js index 5576c40d960..e88aec3a90f 100644 --- a/modules/unifiedIdSystem.js +++ b/modules/unifiedIdSystem.js @@ -9,6 +9,12 @@ import { logError } from '../src/utils.js'; import {ajax} from '../src/ajax.js'; import {submodule} from '../src/hook.js' +/** + * @typedef {import('../modules/userId/index.js').Submodule} Submodule + * @typedef {import('../modules/userId/index.js').SubmoduleConfig} SubmoduleConfig + * @typedef {import('../modules/userId/index.js').IdResponse} IdResponse + */ + const MODULE_NAME = 'unifiedId'; /** @type {Submodule} */ diff --git a/modules/verizonMediaIdSystem.js b/modules/verizonMediaIdSystem.js index 6b22538fdc7..26fa89cfe03 100644 --- a/modules/verizonMediaIdSystem.js +++ b/modules/verizonMediaIdSystem.js @@ -10,6 +10,13 @@ import {submodule} from '../src/hook.js'; import {formatQS, logError} from '../src/utils.js'; import {includes} from '../src/polyfill.js'; +/** + * @typedef {import('../modules/userId/index.js').Submodule} Submodule + * @typedef {import('../modules/userId/index.js').SubmoduleConfig} SubmoduleConfig + * @typedef {import('../modules/userId/index.js').ConsentData} ConsentData + * @typedef {import('../modules/userId/index.js').IdResponse} IdResponse + */ + const MODULE_NAME = 'verizonMediaId'; const VENDOR_ID = 25; const PLACEHOLDER = '__PIXEL_ID__'; diff --git a/modules/zeotapIdPlusIdSystem.js b/modules/zeotapIdPlusIdSystem.js index ab7bf7c237b..32b7a5b77ad 100644 --- a/modules/zeotapIdPlusIdSystem.js +++ b/modules/zeotapIdPlusIdSystem.js @@ -9,6 +9,11 @@ import {submodule} from '../src/hook.js'; import {getStorageManager} from '../src/storageManager.js'; import {MODULE_TYPE_UID} from '../src/activities/modules.js'; +/** + * @typedef {import('../modules/userId/index.js').Submodule} Submodule + * @typedef {import('../modules/userId/index.js').SubmoduleConfig} SubmoduleConfig + */ + const ZEOTAP_COOKIE_NAME = 'IDP'; const ZEOTAP_VENDOR_ID = 301; const ZEOTAP_MODULE_NAME = 'zeotapIdPlus'; diff --git a/src/targeting.js b/src/targeting.js index 2c2ddefa6c2..ddbc3cebaf3 100644 --- a/src/targeting.js +++ b/src/targeting.js @@ -497,7 +497,7 @@ export function newTargeting(auctionManager) { }; /** - * @param {(string|string[])} adUnitCode adUnitCode or array of adUnitCodes + * @param {(string|string[])} adUnitCodes adUnitCode or array of adUnitCodes * Sets targeting for AST */ targeting.setTargetingForAst = function(adUnitCodes) { @@ -530,7 +530,7 @@ export function newTargeting(auctionManager) { /** * Get targeting key value pairs for winning bid. - * @param {string[]} AdUnit code array + * @param {string[]} adUnitCodes code array * @return {targetingArray} winning bids targeting */ function getWinningBidTargeting(adUnitCodes, bidsReceived) { @@ -630,7 +630,7 @@ export function newTargeting(auctionManager) { /** * Get custom targeting key value pairs for bids. - * @param {string[]} AdUnit code array + * @param {string[]} adUnitCodes code array * @return {targetingArray} bids with custom targeting defined in bidderSettings */ function getCustomBidTargeting(adUnitCodes, bidsReceived) { @@ -644,7 +644,7 @@ export function newTargeting(auctionManager) { /** * Get targeting key value pairs for non-winning bids. - * @param {string[]} AdUnit code array + * @param {string[]} adUnitCodes code array * @return {targetingArray} all non-winning bids targeting */ function getBidLandscapeTargeting(adUnitCodes, bidsReceived) { diff --git a/src/userSync.js b/src/userSync.js index 153b36816a4..1b684de6de0 100644 --- a/src/userSync.js +++ b/src/userSync.js @@ -182,8 +182,8 @@ export function newUserSync(deps) { * @function incrementAdapterBids * @summary Increment the count of user syncs queue for the adapter * @private - * @params {object} numAdapterBids The object contain counts for all adapters - * @params {string} bidder The name of the bidder adding a sync + * @param {object} numAdapterBids The object contain counts for all adapters + * @param {string} bidder The name of the bidder adding a sync * @returns {object} The updated version of numAdapterBids */ function incrementAdapterBids(numAdapterBids, bidder) { @@ -199,10 +199,9 @@ export function newUserSync(deps) { * @function registerSync * @summary Add sync for this bidder to a queue to be fired later * @public - * @params {string} type The type of the sync including image, iframe - * @params {string} bidder The name of the adapter. e.g. "rubicon" - * @params {string} url Either the pixel url or iframe url depending on the type - + * @param {string} type The type of the sync including image, iframe + * @param {string} bidder The name of the adapter. e.g. "rubicon" + * @param {string} url Either the pixel url or iframe url depending on the type * @example Using Image Sync * // registerSync(type, adapter, pixelUrl) * userSync.registerSync('image', 'rubicon', 'http://example.com/pixel') @@ -309,7 +308,7 @@ export function newUserSync(deps) { * @function syncUsers * @summary Trigger all the user syncs based on publisher-defined timeout * @public - * @params {number} timeout The delay in ms before syncing data - default 0 + * @param {number} timeout The delay in ms before syncing data - default 0 */ publicApi.syncUsers = (timeout = 0) => { if (timeout) { From aa24e103cefdd7508ad6488ad495fbcba5e14e62 Mon Sep 17 00:00:00 2001 From: Chris Huie Date: Tue, 23 Jan 2024 15:39:03 -0700 Subject: [PATCH 167/246] JSdoc Linting Fixes for Multiiple RTD Modules (#10973) * fix rtd jsdoc * jsdoc lint fixes * import jsdoc types * rtd jsdoc fixes --- modules/a1MediaRtdProvider.js | 4 ++++ modules/adnuntiusRtdProvider.js | 4 ++++ modules/airgridRtdProvider.js | 4 ++++ modules/arcspanRtdProvider.js | 4 ++++ modules/blueconicRtdProvider.js | 4 ++++ modules/brandmetricsRtdProvider.js | 4 ++++ modules/browsiRtdProvider.js | 5 +++++ modules/cleanioRtdProvider.js | 4 ++++ modules/dgkeywordRtdProvider.js | 4 ++++ modules/experianRtdProvider.js | 6 ++++++ modules/geoedgeRtdProvider.js | 4 ++++ modules/goldfishAdsRtdProvider.js | 4 ++++ modules/hadronRtdProvider.js | 4 ++++ modules/idWardRtdProvider.js | 3 +++ modules/imRtdProvider.js | 4 ++++ modules/jwplayerRtdProvider.js | 5 +++++ modules/mgidRtdProvider.js | 4 ++++ modules/oneKeyRtdProvider.js | 4 ++++ modules/optimeraRtdProvider.js | 4 ++++ modules/oxxionRtdProvider.js | 4 ++++ modules/permutiveRtdProvider.js | 4 ++++ modules/reconciliationRtdProvider.js | 4 ++++ modules/timeoutRtdProvider.js | 4 ++++ 23 files changed, 95 insertions(+) diff --git a/modules/a1MediaRtdProvider.js b/modules/a1MediaRtdProvider.js index 9fa6b307b6a..445ed47181d 100644 --- a/modules/a1MediaRtdProvider.js +++ b/modules/a1MediaRtdProvider.js @@ -4,6 +4,10 @@ import { submodule } from '../src/hook.js'; import { getStorageManager } from '../src/storageManager.js'; import { isEmptyStr, mergeDeep } from '../src/utils.js'; +/** + * @typedef {import('../modules/rtdModule/index.js').RtdSubmodule} RtdSubmodule + */ + const REAL_TIME_MODULE = 'realTimeData'; const MODULE_NAME = 'a1Media'; const SCRIPT_URL = 'https://linkback.contentsfeed.com/src'; diff --git a/modules/adnuntiusRtdProvider.js b/modules/adnuntiusRtdProvider.js index 9234a30aa33..1d5d639aa55 100644 --- a/modules/adnuntiusRtdProvider.js +++ b/modules/adnuntiusRtdProvider.js @@ -5,6 +5,10 @@ import { ajax } from '../src/ajax.js'; import { config as sourceConfig } from '../src/config.js'; +/** + * @typedef {import('../modules/rtdModule/index.js').RtdSubmodule} RtdSubmodule + */ + const GVLID = 855; function init(config, userConsent) { diff --git a/modules/airgridRtdProvider.js b/modules/airgridRtdProvider.js index 7c6cf1f5de0..2184544807e 100644 --- a/modules/airgridRtdProvider.js +++ b/modules/airgridRtdProvider.js @@ -11,6 +11,10 @@ import {getStorageManager} from '../src/storageManager.js'; import {loadExternalScript} from '../src/adloader.js'; import {MODULE_TYPE_RTD} from '../src/activities/modules.js'; +/** + * @typedef {import('../modules/rtdModule/index.js').RtdSubmodule} RtdSubmodule + */ + const MODULE_NAME = 'realTimeData'; const SUBMODULE_NAME = 'airgrid'; const AG_TCF_ID = 782; diff --git a/modules/arcspanRtdProvider.js b/modules/arcspanRtdProvider.js index a7ffa059279..8ccf3d160b9 100644 --- a/modules/arcspanRtdProvider.js +++ b/modules/arcspanRtdProvider.js @@ -2,6 +2,10 @@ import { submodule } from '../src/hook.js'; import { mergeDeep } from '../src/utils.js'; import {loadExternalScript} from '../src/adloader.js'; +/** + * @typedef {import('../modules/rtdModule/index.js').RtdSubmodule} RtdSubmodule + */ + /** @type {string} */ const MODULE_NAME = 'realTimeData'; const SUBMODULE_NAME = 'arcspan'; diff --git a/modules/blueconicRtdProvider.js b/modules/blueconicRtdProvider.js index bf0c457bebd..6b10e79b94c 100644 --- a/modules/blueconicRtdProvider.js +++ b/modules/blueconicRtdProvider.js @@ -11,6 +11,10 @@ import {submodule} from '../src/hook.js'; import {mergeDeep, isPlainObject, logMessage, logError} from '../src/utils.js'; import {MODULE_TYPE_RTD} from '../src/activities/modules.js'; +/** + * @typedef {import('../modules/rtdModule/index.js').RtdSubmodule} RtdSubmodule + */ + const MODULE_NAME = 'realTimeData'; const SUBMODULE_NAME = 'blueconic'; diff --git a/modules/brandmetricsRtdProvider.js b/modules/brandmetricsRtdProvider.js index 20b1125cee9..17336baa76c 100644 --- a/modules/brandmetricsRtdProvider.js +++ b/modules/brandmetricsRtdProvider.js @@ -11,6 +11,10 @@ import {loadExternalScript} from '../src/adloader.js'; import * as events from '../src/events.js'; import CONSTANTS from '../src/constants.json'; +/** + * @typedef {import('../modules/rtdModule/index.js').RtdSubmodule} RtdSubmodule + */ + const MODULE_NAME = 'brandmetrics' const MODULE_CODE = MODULE_NAME const RECEIVED_EVENTS = [] diff --git a/modules/browsiRtdProvider.js b/modules/browsiRtdProvider.js index 1f37c7c5bc0..5281274616a 100644 --- a/modules/browsiRtdProvider.js +++ b/modules/browsiRtdProvider.js @@ -25,6 +25,11 @@ import {getGlobal} from '../src/prebidGlobal.js'; import * as events from '../src/events.js'; import CONSTANTS from '../src/constants.json'; import {MODULE_TYPE_RTD} from '../src/activities/modules.js'; + +/** + * @typedef {import('../modules/rtdModule/index.js').RtdSubmodule} RtdSubmodule + */ + const MODULE_NAME = 'browsi'; const storage = getStorageManager({moduleType: MODULE_TYPE_RTD, moduleName: MODULE_NAME}); diff --git a/modules/cleanioRtdProvider.js b/modules/cleanioRtdProvider.js index 7d0f461108b..f9bed5357ee 100644 --- a/modules/cleanioRtdProvider.js +++ b/modules/cleanioRtdProvider.js @@ -12,6 +12,10 @@ import { logError, generateUUID, insertElement } from '../src/utils.js'; import * as events from '../src/events.js'; import CONSTANTS from '../src/constants.json'; +/** + * @typedef {import('../modules/rtdModule/index.js').RtdSubmodule} RtdSubmodule + */ + // ============================ MODULE STATE =============================== /** diff --git a/modules/dgkeywordRtdProvider.js b/modules/dgkeywordRtdProvider.js index 76f5f04ac03..14519ae2713 100644 --- a/modules/dgkeywordRtdProvider.js +++ b/modules/dgkeywordRtdProvider.js @@ -11,6 +11,10 @@ import { ajax } from '../src/ajax.js'; import { submodule } from '../src/hook.js'; import { getGlobal } from '../src/prebidGlobal.js'; +/** + * @typedef {import('../modules/rtdModule/index.js').RtdSubmodule} RtdSubmodule + */ + /** * get keywords from api server. and set keywords. * @param {Object} reqBidsConfigObj diff --git a/modules/experianRtdProvider.js b/modules/experianRtdProvider.js index e18296342de..cd415d4b32c 100644 --- a/modules/experianRtdProvider.js +++ b/modules/experianRtdProvider.js @@ -12,6 +12,12 @@ import { } from '../src/utils.js'; import { ajax } from '../src/ajax.js'; +/** + * @typedef {import('../modules/rtdModule/index.js').RtdSubmodule} RtdSubmodule + * @typedef {import('../modules/rtdModule/index.js').SubmoduleConfig} SubmoduleConfig + * @typedef {import('../modules/rtdModule/index.js').UserConsentData} UserConsentData + */ + export const SUBMODULE_NAME = 'experian_rtid'; export const EXPERIAN_RTID_DATA_KEY = 'experian_rtid_data'; export const EXPERIAN_RTID_EXPIRATION_KEY = 'experian_rtid_expiration'; diff --git a/modules/geoedgeRtdProvider.js b/modules/geoedgeRtdProvider.js index 37db5860001..a2ed71a898c 100644 --- a/modules/geoedgeRtdProvider.js +++ b/modules/geoedgeRtdProvider.js @@ -24,6 +24,10 @@ import { loadExternalScript } from '../src/adloader.js'; import { auctionManager } from '../src/auctionManager.js'; import { getRefererInfo } from '../src/refererDetection.js'; +/** + * @typedef {import('../modules/rtdModule/index.js').RtdSubmodule} RtdSubmodule + */ + /** @type {string} */ const SUBMODULE_NAME = 'geoedge'; /** @type {string} */ diff --git a/modules/goldfishAdsRtdProvider.js b/modules/goldfishAdsRtdProvider.js index c90d3104b56..c595e361968 100755 --- a/modules/goldfishAdsRtdProvider.js +++ b/modules/goldfishAdsRtdProvider.js @@ -3,6 +3,10 @@ import { submodule } from '../src/hook.js'; import { getStorageManager } from '../src/storageManager.js'; import { deepAccess } from '../src/utils.js'; +/** + * @typedef {import('../modules/rtdModule/index.js').RtdSubmodule} RtdSubmodule + */ + export const MODULE_NAME = 'goldfishAdsRtd'; export const MODULE_TYPE = 'realTimeData'; export const ENDPOINT_URL = 'https://prebid.goldfishads.com/iab-segments'; diff --git a/modules/hadronRtdProvider.js b/modules/hadronRtdProvider.js index 6fb982815c1..5c604709b4b 100644 --- a/modules/hadronRtdProvider.js +++ b/modules/hadronRtdProvider.js @@ -14,6 +14,10 @@ import {isFn, isStr, isArray, deepEqual, isPlainObject, logError, logInfo} from import {loadExternalScript} from '../src/adloader.js'; import {MODULE_TYPE_RTD} from '../src/activities/modules.js'; +/** + * @typedef {import('../modules/rtdModule/index.js').RtdSubmodule} RtdSubmodule + */ + const LOG_PREFIX = 'User ID - HadronRtdProvider submodule: '; const MODULE_NAME = 'realTimeData'; const SUBMODULE_NAME = 'hadron'; diff --git a/modules/idWardRtdProvider.js b/modules/idWardRtdProvider.js index 4e51f87cc81..dd08a132b2d 100644 --- a/modules/idWardRtdProvider.js +++ b/modules/idWardRtdProvider.js @@ -9,6 +9,9 @@ import {getStorageManager} from '../src/storageManager.js'; import {submodule} from '../src/hook.js'; import {isPlainObject, mergeDeep, logMessage, logError} from '../src/utils.js'; import {MODULE_TYPE_RTD} from '../src/activities/modules.js'; +/** + * @typedef {import('../modules/rtdModule/index.js').RtdSubmodule} RtdSubmodule + */ const MODULE_NAME = 'realTimeData'; const SUBMODULE_NAME = 'idWard'; diff --git a/modules/imRtdProvider.js b/modules/imRtdProvider.js index 5aa1d4e4dbe..78681c2beda 100644 --- a/modules/imRtdProvider.js +++ b/modules/imRtdProvider.js @@ -20,6 +20,10 @@ import { import {submodule} from '../src/hook.js'; import {MODULE_TYPE_RTD} from '../src/activities/modules.js'; +/** + * @typedef {import('../modules/rtdModule/index.js').RtdSubmodule} RtdSubmodule + */ + export const imUidLocalName = '__im_uid'; export const imVidCookieName = '_im_vid'; export const imRtdLocalName = '__im_sids'; diff --git a/modules/jwplayerRtdProvider.js b/modules/jwplayerRtdProvider.js index 18f89c33ad2..573ff391dae 100644 --- a/modules/jwplayerRtdProvider.js +++ b/modules/jwplayerRtdProvider.js @@ -16,6 +16,11 @@ import {deepAccess, logError} from '../src/utils.js'; import {find} from '../src/polyfill.js'; import {getGlobal} from '../src/prebidGlobal.js'; +/** + * @typedef {import('../modules/rtdModule/index.js').RtdSubmodule} RtdSubmodule + * @typedef {import('../modules/rtdModule/index.js').adUnit} adUnit + */ + const SUBMODULE_NAME = 'jwplayer'; const JWPLAYER_DOMAIN = SUBMODULE_NAME + '.com'; const segCache = {}; diff --git a/modules/mgidRtdProvider.js b/modules/mgidRtdProvider.js index fd2c0bbe6fd..059be4e9103 100644 --- a/modules/mgidRtdProvider.js +++ b/modules/mgidRtdProvider.js @@ -5,6 +5,10 @@ import {getStorageManager} from '../src/storageManager.js'; import {getRefererInfo} from '../src/refererDetection.js'; import {MODULE_TYPE_RTD} from '../src/activities/modules.js'; +/** + * @typedef {import('../modules/rtdModule/index.js').RtdSubmodule} RtdSubmodule + */ + const MODULE_NAME = 'realTimeData'; const SUBMODULE_NAME = 'mgid'; const MGID_RTD_API_URL = 'https://servicer.mgid.com/sda'; diff --git a/modules/oneKeyRtdProvider.js b/modules/oneKeyRtdProvider.js index 27511017676..19915609820 100644 --- a/modules/oneKeyRtdProvider.js +++ b/modules/oneKeyRtdProvider.js @@ -3,6 +3,10 @@ import { submodule } from '../src/hook.js'; import { mergeDeep, logError, logMessage, deepSetValue, generateUUID } from '../src/utils.js'; import { getGlobal } from '../src/prebidGlobal.js'; +/** + * @typedef {import('../modules/rtdModule/index.js').RtdSubmodule} RtdSubmodule + */ + const SUBMODULE_NAME = 'oneKey'; const prefixLog = 'OneKey.RTD-module' diff --git a/modules/optimeraRtdProvider.js b/modules/optimeraRtdProvider.js index 074d8d39bbf..bd564e3a260 100644 --- a/modules/optimeraRtdProvider.js +++ b/modules/optimeraRtdProvider.js @@ -23,6 +23,10 @@ import { logInfo, logError } from '../src/utils.js'; import { submodule } from '../src/hook.js'; import { ajaxBuilder } from '../src/ajax.js'; +/** + * @typedef {import('../modules/rtdModule/index.js').RtdSubmodule} RtdSubmodule + */ + /** @type {ModuleParams} */ let _moduleParams = {}; diff --git a/modules/oxxionRtdProvider.js b/modules/oxxionRtdProvider.js index f968fc10e4b..a0476d8ca0f 100644 --- a/modules/oxxionRtdProvider.js +++ b/modules/oxxionRtdProvider.js @@ -3,6 +3,10 @@ import { logInfo, logError } from '../src/utils.js' import { ajax } from '../src/ajax.js'; import adapterManager from '../src/adapterManager.js'; +/** + * @typedef {import('../modules/rtdModule/index.js').RtdSubmodule} RtdSubmodule + */ + const LOG_PREFIX = 'oxxionRtdProvider submodule: '; const bidderAliasRegistry = adapterManager.aliasRegistry || {}; diff --git a/modules/permutiveRtdProvider.js b/modules/permutiveRtdProvider.js index 697d7721205..5a63990f84f 100644 --- a/modules/permutiveRtdProvider.js +++ b/modules/permutiveRtdProvider.js @@ -12,6 +12,10 @@ import {deepAccess, deepSetValue, isFn, logError, mergeDeep, isPlainObject, safe import {includes} from '../src/polyfill.js'; import {MODULE_TYPE_RTD} from '../src/activities/modules.js'; +/** + * @typedef {import('../modules/rtdModule/index.js').RtdSubmodule} RtdSubmodule + */ + const MODULE_NAME = 'permutive' const logger = prefixLog('[PermutiveRTD]') diff --git a/modules/reconciliationRtdProvider.js b/modules/reconciliationRtdProvider.js index 9b6a3d7aca3..5671b2021d8 100644 --- a/modules/reconciliationRtdProvider.js +++ b/modules/reconciliationRtdProvider.js @@ -21,6 +21,10 @@ import {ajaxBuilder} from '../src/ajax.js'; import {generateUUID, isGptPubadsDefined, logError, timestamp} from '../src/utils.js'; import {find} from '../src/polyfill.js'; +/** + * @typedef {import('../modules/rtdModule/index.js').RtdSubmodule} RtdSubmodule + */ + /** @type {Object} */ const MessageType = { IMPRESSION_REQUEST: 'rsdk:impression:req', diff --git a/modules/timeoutRtdProvider.js b/modules/timeoutRtdProvider.js index fe9597f6f4e..a46a39a2c2b 100644 --- a/modules/timeoutRtdProvider.js +++ b/modules/timeoutRtdProvider.js @@ -4,6 +4,10 @@ import * as ajax from '../src/ajax.js'; import { logInfo, deepAccess, logError } from '../src/utils.js'; import { getGlobal } from '../src/prebidGlobal.js'; +/** + * @typedef {import('../modules/rtdModule/index.js').RtdSubmodule} RtdSubmodule + */ + const SUBMODULE_NAME = 'timeout'; // this allows the stubbing of functions during testing From b7f60d86589288d3afc9c397127ee00e3bdbb627 Mon Sep 17 00:00:00 2001 From: Shubham <127132399+shubhamc-ins@users.noreply.github.com> Date: Wed, 24 Jan 2024 07:15:27 +0530 Subject: [PATCH 168/246] Insticator Bid Adaptor: add support for bidder video params (#10976) * add support for bidder video params * add comment for bidderspecific override * fix video validation empty condition * fix test case --- modules/insticatorBidAdapter.js | 18 ++++-- .../spec/modules/insticatorBidAdapter_spec.js | 64 +++++++++++++++++++ 2 files changed, 77 insertions(+), 5 deletions(-) diff --git a/modules/insticatorBidAdapter.js b/modules/insticatorBidAdapter.js index 32ec5b482c4..3ea95ef8443 100644 --- a/modules/insticatorBidAdapter.js +++ b/modules/insticatorBidAdapter.js @@ -120,6 +120,7 @@ function buildVideo(bidRequest) { } const bidRequestVideo = deepAccess(bidRequest, 'mediaTypes.video'); + const videoBidderParams = deepAccess(bidRequest, 'params.video', {}); let optionalParams = {}; for (const param in OPTIONAL_VIDEO_PARAMS) { if (bidRequestVideo[param]) { @@ -132,7 +133,8 @@ function buildVideo(bidRequest) { mimes, w, h, - ...optionalParams + ...optionalParams, + ...videoBidderParams // bidder specific overrides for video } if (plcmt) { @@ -384,10 +386,16 @@ function validateBanner(bid) { } function validateVideo(bid) { - const video = deepAccess(bid, 'mediaTypes.video'); - - if (video === undefined) { - return true; + const videoParams = deepAccess(bid, 'mediaTypes.video', {}); + const videoBidderParams = deepAccess(bid, 'params.video', {}); + let video = { + ...videoParams, + ...videoBidderParams // bidder specific overrides for video + } + // Check if the merged video object is empty + if (Object.keys(video).length === 0 || video === undefined) { + logError('insticator: video object is empty'); + return false; // or handle the case where video parameters are undefined or empty } let w = deepAccess(bid, 'mediaTypes.video.w'); diff --git a/test/spec/modules/insticatorBidAdapter_spec.js b/test/spec/modules/insticatorBidAdapter_spec.js index f9aa3b913c6..149983552f8 100644 --- a/test/spec/modules/insticatorBidAdapter_spec.js +++ b/test/spec/modules/insticatorBidAdapter_spec.js @@ -286,6 +286,36 @@ describe('InsticatorBidAdapter', function () { } })).to.be.false; }); + + it('should return true when video bidder params override bidRequest video params', () => { + expect(spec.isBidRequestValid({ + ...bidRequest, + ...{ + mediaTypes: { + video: { + mimes: [ + 'video/mp4', + 'video/mpeg', + ], + playerSize: [250, 300], + placement: 1, + }, + } + }, + params: { + ...bidRequest.params, + video: { + mimes: [ + 'video/mp4', + 'video/mpeg', + 'video/x-flv', + 'video/webm', + ], + placement: 2, + }, + } + })).to.be.true; + }); }); describe('buildRequests', function () { @@ -462,6 +492,40 @@ describe('InsticatorBidAdapter', function () { it('should return empty array if no valid requests are passed', function () { expect(spec.buildRequests([], bidderRequest)).to.be.an('array').that.have.lengthOf(0); }); + + it('should have bidder params override bidRequest mediatypes', function () { + const tempBiddRequest = { + ...bidRequest, + params: { + ...bidRequest.params, + video: { + mimes: [ + 'video/mp4', + 'video/mpeg', + 'video/x-flv', + 'video/webm', + 'video/ogg', + ], + plcmt: 4, + w: 640, + h: 480, + } + } + } + const requests = spec.buildRequests([tempBiddRequest], bidderRequest); + const data = JSON.parse(requests[0].data); + expect(data.imp[0].video.mimes).to.deep.equal([ + 'video/mp4', + 'video/mpeg', + 'video/x-flv', + 'video/webm', + 'video/ogg', + ]) + expect(data.imp[0].video.placement).to.equal(2); + expect(data.imp[0].video.plcmt).to.equal(4); + expect(data.imp[0].video.w).to.equal(640); + expect(data.imp[0].video.h).to.equal(480); + }); }); describe('interpretResponse', function () { From 12430d72667c39f97c52d95d44ec8e094a303120 Mon Sep 17 00:00:00 2001 From: johnwier <49074029+johnwier@users.noreply.github.com> Date: Wed, 24 Jan 2024 03:52:07 -0800 Subject: [PATCH 169/246] Conversant adapter: use ortbConverter to handle requests and responses (#10913) Co-authored-by: johwier --- modules/conversantBidAdapter.js | 396 ++++--------- .../spec/modules/conversantBidAdapter_spec.js | 543 ++++++++---------- 2 files changed, 377 insertions(+), 562 deletions(-) diff --git a/modules/conversantBidAdapter.js b/modules/conversantBidAdapter.js index bef65a43616..c338a6633b5 100644 --- a/modules/conversantBidAdapter.js +++ b/modules/conversantBidAdapter.js @@ -1,23 +1,23 @@ import { - logWarn, - isStr, + buildUrl, deepAccess, - isArray, deepSetValue, - isEmpty, - _each, - parseUrl, - mergeDeep, - buildUrl, - _map, - logError, + getBidIdParameter, + isArray, isFn, - isPlainObject, getBidIdParameter, + isPlainObject, + isStr, + logError, + logWarn, + mergeDeep, + parseUrl, } 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 {convertTypes} from '../libraries/transformParamsUtils/convertTypes.js'; +import {ortbConverter} from '../libraries/ortbConverter/converter.js'; +import {ORTB_MTYPES} from '../libraries/ortbConverter/processors/mediaType.js'; // Maintainer: mediapsr@epsilon.com @@ -27,6 +27,92 @@ const BIDDER_CODE = 'conversant'; export const storage = getStorageManager({gvlid: GVLID, bidderCode: BIDDER_CODE}); const URL = 'https://web.hb.ad.cpe.dotomi.com/cvx/client/hb/ortb/25'; +function setSiteId(bidRequest, request) { + if (bidRequest.params.site_id) { + if (request.site) { + request.site.id = bidRequest.params.site_id; + } + if (request.app) { + request.app.id = bidRequest.params.site_id; + } + } +} + +function setPubcid(bidRequest, request) { + // Add common id if available + const pubcid = getPubcid(bidRequest); + if (pubcid) { + deepSetValue(request, 'user.ext.fpc', pubcid); + } +} + +const converter = ortbConverter({ + context: { + netRevenue: true, + ttl: 300 + }, + request: function (buildRequest, imps, bidderRequest, context) { + const request = buildRequest(imps, bidderRequest, context); + request.at = 1; + if (context.bidRequests) { + const bidRequest = context.bidRequests[0]; + setSiteId(bidRequest, request); + setPubcid(bidRequest, request); + } + + return request; + }, + imp(buildImp, bidRequest, context) { + const imp = buildImp(bidRequest, context); + const data = { + secure: 1, + bidfloor: getBidFloor(bidRequest) || 0, + displaymanager: 'Prebid.js', + displaymanagerver: '$prebid.version$' + }; + copyOptProperty(bidRequest.params.tag_id, data, 'tagid'); + mergeDeep(imp, data, imp); + return imp; + }, + bidResponse: function (buildBidResponse, bid, context) { + if (!bid.price) return; + + // ensure that context.mediaType is set to banner or video otherwise + if (!context.mediaType && context.bidRequest.mediaTypes) { + const [type] = Object.keys(context.bidRequest.mediaTypes); + if (Object.values(ORTB_MTYPES).includes(type)) { + context.mediaType = type; + } + } + const bidResponse = buildBidResponse(bid, context); + return bidResponse; + }, + + overrides: { + imp: { + banner(fillBannerImp, imp, bidRequest, context) { + if (bidRequest.mediaTypes && !bidRequest.mediaTypes.banner) return; + if (bidRequest.params.position) { + // fillBannerImp looks for mediaTypes.banner.pos so put it under the right name here + mergeDeep(bidRequest, {mediaTypes: {banner: {pos: bidRequest.params.position}}}); + } + fillBannerImp(imp, bidRequest, context); + }, + video(fillVideoImp, imp, bidRequest, context) { + if (bidRequest.mediaTypes && !bidRequest.mediaTypes.video) return; + const videoData = {}; + copyOptProperty(bidRequest.params?.position, videoData, 'pos'); + copyOptProperty(bidRequest.params?.mimes, videoData, 'mimes'); + copyOptProperty(bidRequest.params?.maxduration, videoData, 'maxduration'); + copyOptProperty(bidRequest.params?.protocols, videoData, 'protocols'); + copyOptProperty(bidRequest.params?.api, videoData, 'api'); + imp.video = mergeDeep(videoData, imp.video); + fillVideoImp(imp, bidRequest, context); + } + }, + } +}); + export const spec = { code: BIDDER_CODE, gvlid: GVLID, @@ -64,148 +150,14 @@ export const spec = { return true; }, - /** - * 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) { - const page = (bidderRequest && bidderRequest.refererInfo) ? bidderRequest.refererInfo.page : ''; - let siteId = ''; - let pubcid = null; - let pubcidName = '_pubcid'; - let bidurl = URL; - - const conversantImps = validBidRequests.map(function(bid) { - const bidfloor = getBidFloor(bid); - - siteId = getBidIdParameter('site_id', bid.params) || siteId; - pubcidName = getBidIdParameter('pubcid_name', bid.params) || pubcidName; - - const imp = { - id: bid.bidId, - secure: 1, - bidfloor: bidfloor || 0, - displaymanager: 'Prebid.js', - displaymanagerver: '$prebid.version$' - }; - if (bid.ortb2Imp) { - mergeDeep(imp, bid.ortb2Imp); - } - - copyOptProperty(bid.params.tag_id, imp, 'tagid'); - - if (isVideoRequest(bid)) { - const videoData = deepAccess(bid, 'mediaTypes.video') || {}; - const format = convertSizes(videoData.playerSize || bid.sizes); - const video = {}; - - if (format && format[0]) { - copyOptProperty(format[0].w, video, 'w'); - copyOptProperty(format[0].h, video, 'h'); - } - - copyOptProperty(bid.params.position || videoData.pos, video, 'pos'); - copyOptProperty(bid.params.mimes || videoData.mimes, video, 'mimes'); - copyOptProperty(bid.params.maxduration || videoData.maxduration, video, 'maxduration'); - copyOptProperty(bid.params.protocols || videoData.protocols, video, 'protocols'); - copyOptProperty(bid.params.api || videoData.api, video, 'api'); - - imp.video = video; - } else { - const bannerData = deepAccess(bid, 'mediaTypes.banner') || {}; - const format = convertSizes(bannerData.sizes || bid.sizes); - const banner = {format: format}; - - copyOptProperty(bid.params.position || bannerData.pos, banner, 'pos'); - - imp.banner = banner; - } - - if (bid.userId && bid.userId.pubcid) { - pubcid = bid.userId.pubcid; - } else if (bid.crumbs && bid.crumbs.pubcid) { - pubcid = bid.crumbs.pubcid; - } - if (bid.params.white_label_url) { - bidurl = bid.params.white_label_url; - } - - return imp; - }); - - const payload = { - id: bidderRequest.bidderRequestId, - imp: conversantImps, - source: { - tid: bidderRequest.ortb2?.source?.tid, - }, - site: { - id: siteId, - mobile: document.querySelector('meta[name="viewport"][content*="width=device-width"]') !== null ? 1 : 0, - page: page - }, - device: getDevice(), - at: 1 - }; - - let userExt = {}; - - // pass schain object if it is present - const schain = deepAccess(validBidRequests, '0.schain'); - if (schain) { - deepSetValue(payload, 'source.ext.schain', schain); - } - - if (bidderRequest) { - if (bidderRequest.timeout) { - deepSetValue(payload, 'tmax', bidderRequest.timeout); - } - - // Add GDPR flag and consent string - if (bidderRequest.gdprConsent) { - userExt.consent = bidderRequest.gdprConsent.consentString; - - if (typeof bidderRequest.gdprConsent.gdprApplies === 'boolean') { - deepSetValue(payload, 'regs.ext.gdpr', bidderRequest.gdprConsent.gdprApplies ? 1 : 0); - } - } - - if (bidderRequest.uspConsent) { - deepSetValue(payload, 'regs.ext.us_privacy', bidderRequest.uspConsent); - } - } - - if (!pubcid) { - pubcid = readStoredValue(pubcidName); - } - - // Add common id if available - if (pubcid) { - userExt.fpc = pubcid; - } - - // Add Eids if available - const eids = collectEids(validBidRequests); - if (eids.length > 0) { - userExt.eids = eids; - } - - // Only add the user object if it's not empty - if (!isEmpty(userExt)) { - payload.user = {ext: userExt}; - } - - const firstPartyData = bidderRequest.ortb2 || {}; - mergeDeep(payload, firstPartyData); - - return { + buildRequests: function(bidRequests, bidderRequest) { + const payload = converter.toORTB({bidderRequest, bidRequests}); + const result = { method: 'POST', - url: bidurl, + url: makeBidUrl(bidRequests[0]), data: payload, }; + return result; }, /** * Unpack the response from the server into a list of bids. @@ -215,59 +167,7 @@ export const spec = { * @return {Bid[]} An array of bids which were nested inside the server. */ interpretResponse: function(serverResponse, bidRequest) { - const bidResponses = []; - const requestMap = {}; - serverResponse = serverResponse.body; - - if (bidRequest && bidRequest.data && bidRequest.data.imp) { - _each(bidRequest.data.imp, imp => requestMap[imp.id] = imp); - } - - if (serverResponse && isArray(serverResponse.seatbid)) { - _each(serverResponse.seatbid, function(bidList) { - _each(bidList.bid, function(conversantBid) { - const responseCPM = parseFloat(conversantBid.price); - if (responseCPM > 0.0 && conversantBid.impid) { - const responseAd = conversantBid.adm || ''; - const responseNurl = conversantBid.nurl || ''; - const request = requestMap[conversantBid.impid]; - - const bid = { - requestId: conversantBid.impid, - currency: serverResponse.cur || 'USD', - cpm: responseCPM, - creativeId: conversantBid.crid || '', - ttl: 300, - netRevenue: true - }; - bid.meta = {}; - if (conversantBid.adomain && conversantBid.adomain.length > 0) { - bid.meta.advertiserDomains = conversantBid.adomain; - } - - if (request.video) { - if (responseAd.charAt(0) === '<') { - bid.vastXml = responseAd; - } else { - bid.vastUrl = responseAd; - } - - bid.mediaType = 'video'; - bid.width = request.video.w; - bid.height = request.video.h; - } else { - bid.ad = responseAd + ''; - bid.width = conversantBid.w; - bid.height = conversantBid.h; - } - - bidResponses.push(bid); - } - }) - }); - } - - return bidResponses; + return converter.fromORTB({request: bidRequest.data, response: serverResponse.body}); }, /** @@ -327,51 +227,18 @@ export const spec = { } }; -/** - * Determine do-not-track state - * - * @returns {boolean} - */ -function getDNT() { - return navigator.doNotTrack === '1' || window.doNotTrack === '1' || navigator.msDoNoTrack === '1' || navigator.doNotTrack === 'yes'; -} - -/** - * Return openrtb device object that includes ua, width, and height. - * - * @returns {Device} Openrtb device object - */ -function getDevice() { - const language = navigator.language ? 'language' : 'userLanguage'; - return { - h: screen.height, - w: screen.width, - dnt: getDNT() ? 1 : 0, - language: navigator[language].split('-')[0], - make: navigator.vendor ? navigator.vendor : '', - ua: navigator.userAgent - }; -} - -/** - * Convert arrays of widths and heights to an array of objects with w and h properties. - * - * [[300, 250], [300, 600]] => [{w: 300, h: 250}, {w: 300, h: 600}] - * - * @param {Array.>} bidSizes - arrays of widths and heights - * @returns {object[]} Array of objects with w and h - */ -function convertSizes(bidSizes) { - let format; - if (Array.isArray(bidSizes)) { - if (bidSizes.length === 2 && typeof bidSizes[0] === 'number' && typeof bidSizes[1] === 'number') { - format = [{w: bidSizes[0], h: bidSizes[1]}]; - } else { - format = _map(bidSizes, d => { return {w: d[0], h: d[1]}; }); - } +function getPubcid(bidRequest) { + let pubcid = null; + if (bidRequest.userId && bidRequest.userId.pubcid) { + pubcid = bidRequest.userId.pubcid; + } else if (bidRequest.crumbs && bidRequest.crumbs.pubcid) { + pubcid = bidRequest.crumbs.pubcid; } - - return format; + if (!pubcid) { + const pubcidName = getBidIdParameter('pubcid_name', bidRequest.params) || '_pubcid'; + pubcid = readStoredValue(pubcidName); + } + return pubcid; } /** @@ -397,33 +264,6 @@ function copyOptProperty(src, dst, dstName) { } } -/** - * Collect IDs from validBidRequests and store them as an extended id array - * @param bidRequests valid bid requests - */ -function collectEids(bidRequests) { - const request = bidRequests[0]; // bidRequests have the same userId object - const eids = []; - if (isArray(request.userIdAsEids) && request.userIdAsEids.length > 0) { - // later following white-list can be converted to block-list if needed - const requiredSourceValues = { - 'epsilon.com': 1, - 'adserver.org': 1, - 'liveramp.com': 1, - 'criteo.com': 1, - 'id5-sync.com': 1, - 'parrable.com': 1, - 'liveintent.com': 1 - }; - request.userIdAsEids.forEach(function(eid) { - if (requiredSourceValues.hasOwnProperty(eid.source)) { - eids.push(eid); - } - }); - } - return eids; -} - /** * Look for a stored value from both cookie and local storage and return the first value found. * @param key Key for the search @@ -479,4 +319,12 @@ function getBidFloor(bid) { return floor } +function makeBidUrl(bid) { + let bidurl = URL; + if (bid.params.white_label_url) { + bidurl = bid.params.white_label_url; + } + return bidurl; +} + registerBidder(spec); diff --git a/test/spec/modules/conversantBidAdapter_spec.js b/test/spec/modules/conversantBidAdapter_spec.js index 59ebefa2d60..73d1978a9d9 100644 --- a/test/spec/modules/conversantBidAdapter_spec.js +++ b/test/spec/modules/conversantBidAdapter_spec.js @@ -2,13 +2,20 @@ 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 { config } from '../../../src/config.js'; import {deepAccess} from 'src/utils'; +// load modules that register ORTB processors +import 'src/prebid.js' +import 'modules/currency.js'; +import 'modules/userId/index.js'; // handles eids +import 'modules/priceFloors.js'; +import 'modules/consentManagement.js'; +import 'modules/consentManagementUsp.js'; +import 'modules/schain.js'; // handles schain +import {hook} from '../../../src/hook.js' describe('Conversant adapter tests', function() { const siteId = '108060'; const versionPattern = /^\d+\.\d+\.\d+(.)*$/; - const bidRequests = [ // banner with single size { @@ -19,13 +26,18 @@ describe('Conversant adapter tests', function() { tag_id: 'tagid-1', bidfloor: 0.5 }, + mediaTypes: { + banner: { + sizes: [[300, 250]], + } + }, placementCode: 'pcode000', transactionId: 'tx000', - sizes: [[300, 250]], bidId: 'bid000', bidderRequestId: '117d765b87bed38', auctionId: 'req000' }, + // banner with sizes in mediaTypes.banner.sizes { bidder: 'conversant', @@ -51,9 +63,13 @@ describe('Conversant adapter tests', function() { position: 2, tag_id: '' }, + mediaTypes: { + banner: { + sizes: [[300, 600], [160, 600]], + } + }, placementCode: 'pcode002', transactionId: 'tx002', - sizes: [[300, 600], [160, 600]], bidId: 'bid002', bidderRequestId: '117d765b87bed38', auctionId: 'req000' @@ -77,7 +93,6 @@ describe('Conversant adapter tests', function() { }, placementCode: 'pcode003', transactionId: 'tx003', - sizes: [640, 480], bidId: 'bid003', bidderRequestId: '117d765b87bed38', auctionId: 'req000' @@ -125,16 +140,15 @@ describe('Conversant adapter tests', function() { bidderRequestId: '117d765b87bed38', auctionId: 'req000' }, - // video with first party data + // banner with first party data { bidder: 'conversant', params: { site_id: siteId }, mediaTypes: { - video: { - context: 'instream', - mimes: ['video/mp4', 'video/x-flv'] + banner: { + sizes: [[300, 600], [160, 600]], } }, ortb2Imp: { @@ -150,23 +164,6 @@ describe('Conversant adapter tests', function() { bidId: 'bid006', bidderRequestId: '117d765b87bed38', auctionId: 'req000' - }, - { - bidder: 'conversant', - params: { - site_id: siteId - }, - mediaTypes: { - banner: { - sizes: [[728, 90], [468, 60]], - pos: 5 - } - }, - placementCode: 'pcode001', - transactionId: 'tx001', - bidId: 'bid007', - bidderRequestId: '117d765b87bed38', - auctionId: 'req000' } ]; @@ -217,7 +214,14 @@ describe('Conversant adapter tests', function() { }] }] }, - headers: {}}; + headers: {} + }; + + before(() => { + // ortbConverter depends on other modules to be setup to work as expected so run hook.ready to register some + // submodules so functions like setOrtbSourceExtSchain and setOrtbUserExtEids are available + hook.ready(); + }); it('Verify basic properties', function() { expect(spec.code).to.equal('conversant'); @@ -232,12 +236,9 @@ describe('Conversant adapter tests', function() { expect(spec.isBidRequestValid({})).to.be.false; expect(spec.isBidRequestValid({params: {}})).to.be.false; expect(spec.isBidRequestValid({params: {site_id: '123'}})).to.be.true; - expect(spec.isBidRequestValid(bidRequests[0])).to.be.true; - expect(spec.isBidRequestValid(bidRequests[1])).to.be.true; - expect(spec.isBidRequestValid(bidRequests[2])).to.be.true; - expect(spec.isBidRequestValid(bidRequests[3])).to.be.true; - expect(spec.isBidRequestValid(bidRequests[4])).to.be.true; - expect(spec.isBidRequestValid(bidRequests[5])).to.be.true; + bidRequests.forEach((bid) => { + expect(spec.isBidRequestValid(bid)).to.be.true; + }); const simpleVideo = JSON.parse(JSON.stringify(bidRequests[3])); simpleVideo.params.site_id = 123; @@ -251,152 +252,171 @@ describe('Conversant adapter tests', function() { expect(spec.isBidRequestValid(simpleVideo)).to.be.true; }); - it('Verify buildRequest', function() { - const page = 'http://test.com?a=b&c=123'; - const bidderRequest = { - refererInfo: { - page: page - }, - ortb2: { - source: { - tid: 'tid000' + describe('Verify buildRequest', function() { + let page, bidderRequest, request, payload; + before(() => { + page = 'http://test.com?a=b&c=123'; + // ortbConverter uses the site/device information from the ortb2 object passed in the bidderRequest object + bidderRequest = { + refererInfo: { + page: page + }, + ortb2: { + source: { + tid: 'tid000' + }, + site: { + mobile: 0, + page: page, + }, + device: { + w: screen.width, + h: screen.height, + dnt: 0, + ua: navigator.userAgent + } } - } - }; - const request = spec.buildRequests(bidRequests, bidderRequest); - expect(request.method).to.equal('POST'); - expect(request.url).to.equal('https://web.hb.ad.cpe.dotomi.com/cvx/client/hb/ortb/25'); - const payload = request.data; + }; + request = spec.buildRequests(bidRequests, bidderRequest); + payload = request.data; + }); + + it('Verify common elements', function() { + expect(request.method).to.equal('POST'); + expect(request.url).to.equal('https://web.hb.ad.cpe.dotomi.com/cvx/client/hb/ortb/25'); + + expect(payload).to.have.property('id'); + expect(payload.source).to.have.property('tid', 'tid000'); + expect(payload).to.have.property('at', 1); + expect(payload).to.have.property('imp'); + expect(payload.imp).to.be.an('array').with.lengthOf(bidRequests.length); + + expect(payload).to.have.property('site'); + expect(payload.site).to.have.property('id', siteId); + expect(payload.site).to.have.property('mobile').that.is.oneOf([0, 1]); + + expect(payload.site).to.have.property('page', page); + + expect(payload).to.have.property('device'); + expect(payload.device).to.have.property('w', screen.width); + expect(payload.device).to.have.property('h', screen.height); + expect(payload.device).to.have.property('dnt').that.is.oneOf([0, 1]); + expect(payload.device).to.have.property('ua', navigator.userAgent); + + expect(payload).to.not.have.property('user'); // there should be no user by default + expect(payload).to.not.have.property('tmax'); // there should be no user by default + }); - expect(payload).to.have.property('id'); - expect(payload.source).to.have.property('tid', 'tid000'); - expect(payload).to.have.property('at', 1); - expect(payload).to.have.property('imp'); - expect(payload.imp).to.be.an('array').with.lengthOf(8); - - expect(payload.imp[0]).to.have.property('id', 'bid000'); - expect(payload.imp[0]).to.have.property('secure', 1); - expect(payload.imp[0]).to.have.property('bidfloor', 0.5); - expect(payload.imp[0]).to.have.property('displaymanager', 'Prebid.js'); - expect(payload.imp[0]).to.have.property('displaymanagerver').that.matches(versionPattern); - expect(payload.imp[0]).to.have.property('tagid', 'tagid-1'); - expect(payload.imp[0]).to.have.property('banner'); - expect(payload.imp[0].banner).to.have.property('pos', 1); - expect(payload.imp[0].banner).to.have.property('format'); - expect(payload.imp[0].banner.format).to.deep.equal([{w: 300, h: 250}]); - expect(payload.imp[0]).to.not.have.property('video'); - - expect(payload.imp[1]).to.have.property('id', 'bid001'); - expect(payload.imp[1]).to.have.property('secure', 1); - expect(payload.imp[1]).to.have.property('bidfloor', 0); - expect(payload.imp[1]).to.have.property('displaymanager', 'Prebid.js'); - expect(payload.imp[1]).to.have.property('displaymanagerver').that.matches(versionPattern); - expect(payload.imp[1]).to.not.have.property('tagid'); - expect(payload.imp[1]).to.have.property('banner'); - expect(payload.imp[1].banner).to.not.have.property('pos'); - expect(payload.imp[1].banner).to.have.property('format'); - expect(payload.imp[1].banner.format).to.deep.equal([{w: 728, h: 90}, {w: 468, h: 60}]); - - expect(payload.imp[2]).to.have.property('id', 'bid002'); - expect(payload.imp[2]).to.have.property('secure', 1); - expect(payload.imp[2]).to.have.property('bidfloor', 0); - expect(payload.imp[2]).to.have.property('displaymanager', 'Prebid.js'); - expect(payload.imp[2]).to.have.property('displaymanagerver').that.matches(versionPattern); - expect(payload.imp[2]).to.have.property('banner'); - expect(payload.imp[2].banner).to.have.property('pos', 2); - expect(payload.imp[2].banner).to.have.property('format'); - expect(payload.imp[2].banner.format).to.deep.equal([{w: 300, h: 600}, {w: 160, h: 600}]); - - expect(payload.imp[3]).to.have.property('id', 'bid003'); - expect(payload.imp[3]).to.have.property('secure', 1); - expect(payload.imp[3]).to.have.property('bidfloor', 0); - expect(payload.imp[3]).to.have.property('displaymanager', 'Prebid.js'); - expect(payload.imp[3]).to.have.property('displaymanagerver').that.matches(versionPattern); - expect(payload.imp[3]).to.not.have.property('tagid'); - expect(payload.imp[3]).to.have.property('video'); - expect(payload.imp[3].video).to.have.property('pos', 3); - expect(payload.imp[3].video).to.have.property('w', 632); - expect(payload.imp[3].video).to.have.property('h', 499); - expect(payload.imp[3].video).to.have.property('mimes'); - expect(payload.imp[3].video.mimes).to.deep.equal(['video/mp4', 'video/x-flv']); - expect(payload.imp[3].video).to.have.property('protocols'); - expect(payload.imp[3].video.protocols).to.deep.equal([1, 2]); - expect(payload.imp[3].video).to.have.property('api'); - expect(payload.imp[3].video.api).to.deep.equal([2]); - expect(payload.imp[3].video).to.have.property('maxduration', 30); - expect(payload.imp[3]).to.not.have.property('banner'); - - expect(payload.imp[4]).to.have.property('id', 'bid004'); - expect(payload.imp[4]).to.have.property('secure', 1); - expect(payload.imp[4]).to.have.property('bidfloor', 0); - expect(payload.imp[4]).to.have.property('displaymanager', 'Prebid.js'); - expect(payload.imp[4]).to.have.property('displaymanagerver').that.matches(versionPattern); - expect(payload.imp[4]).to.not.have.property('tagid'); - expect(payload.imp[4]).to.have.property('video'); - expect(payload.imp[4].video).to.not.have.property('pos'); - expect(payload.imp[4].video).to.have.property('w', 1024); - expect(payload.imp[4].video).to.have.property('h', 768); - expect(payload.imp[4].video).to.have.property('mimes'); - expect(payload.imp[4].video.mimes).to.deep.equal(['video/mp4', 'video/x-flv']); - expect(payload.imp[4].video).to.have.property('protocols'); - expect(payload.imp[4].video.protocols).to.deep.equal([1, 2, 3]); - expect(payload.imp[4].video).to.have.property('api'); - expect(payload.imp[4].video.api).to.deep.equal([2, 3]); - expect(payload.imp[4].video).to.have.property('maxduration', 30); - expect(payload.imp[4]).to.not.have.property('banner'); - - expect(payload.imp[5]).to.have.property('id', 'bid005'); - expect(payload.imp[5]).to.have.property('secure', 1); - expect(payload.imp[5]).to.have.property('bidfloor', 0); - expect(payload.imp[5]).to.have.property('displaymanager', 'Prebid.js'); - expect(payload.imp[5]).to.have.property('displaymanagerver').that.matches(versionPattern); - expect(payload.imp[5]).to.not.have.property('tagid'); - expect(payload.imp[5]).to.have.property('video'); - expect(payload.imp[5].video).to.have.property('pos', 2); - expect(payload.imp[5].video).to.not.have.property('w'); - expect(payload.imp[5].video).to.not.have.property('h'); - expect(payload.imp[5].video).to.have.property('mimes'); - expect(payload.imp[5].video.mimes).to.deep.equal(['video/mp4', 'video/x-flv']); - expect(payload.imp[5].video).to.not.have.property('protocols'); - expect(payload.imp[5].video).to.not.have.property('api'); - expect(payload.imp[5].video).to.not.have.property('maxduration'); - expect(payload.imp[5]).to.not.have.property('banner'); - - expect(payload.imp[6]).to.have.property('id', 'bid006'); - expect(payload.imp[6]).to.have.property('video'); - expect(payload.imp[6].video).to.have.property('mimes'); - expect(payload.imp[6].video.mimes).to.deep.equal(['video/mp4', 'video/x-flv']); - expect(payload.imp[6]).to.not.have.property('banner'); - expect(payload.imp[6]).to.have.property('instl'); - expect(payload.imp[6]).to.have.property('ext'); - expect(payload.imp[6].ext).to.have.property('data'); - expect(payload.imp[6].ext.data).to.have.property('pbadslot'); - - expect(payload.imp[7]).to.have.property('id', 'bid007'); - expect(payload.imp[7]).to.have.property('secure', 1); - expect(payload.imp[7]).to.have.property('bidfloor', 0); - expect(payload.imp[7]).to.have.property('displaymanager', 'Prebid.js'); - expect(payload.imp[7]).to.have.property('displaymanagerver').that.matches(versionPattern); - expect(payload.imp[7]).to.not.have.property('tagid'); - expect(payload.imp[7]).to.have.property('banner'); - expect(payload.imp[7].banner).to.have.property('pos', 5); - expect(payload.imp[7].banner).to.have.property('format'); - expect(payload.imp[7].banner.format).to.deep.equal([{w: 728, h: 90}, {w: 468, h: 60}]); - - expect(payload).to.have.property('site'); - expect(payload.site).to.have.property('id', siteId); - expect(payload.site).to.have.property('mobile').that.is.oneOf([0, 1]); - - expect(payload.site).to.have.property('page', page); - - expect(payload).to.have.property('device'); - expect(payload.device).to.have.property('w', screen.width); - expect(payload.device).to.have.property('h', screen.height); - expect(payload.device).to.have.property('dnt').that.is.oneOf([0, 1]); - expect(payload.device).to.have.property('ua', navigator.userAgent); - - expect(payload).to.not.have.property('user'); // there should be no user by default - expect(payload).to.not.have.property('tmax'); // there should be no user by default + it('Simple banner', () => { + expect(payload.imp[0]).to.have.property('id', 'bid000'); + expect(payload.imp[0]).to.have.property('secure', 1); + expect(payload.imp[0]).to.have.property('bidfloor', 0.5); + expect(payload.imp[0]).to.have.property('displaymanager', 'Prebid.js'); + expect(payload.imp[0]).to.have.property('displaymanagerver').that.matches(versionPattern); + expect(payload.imp[0]).to.have.property('tagid', 'tagid-1'); + expect(payload.imp[0]).to.have.property('banner'); + expect(payload.imp[0].banner).to.have.property('pos', 1); + expect(payload.imp[0].banner).to.have.property('format'); + expect(payload.imp[0].banner.format).to.deep.equal([{w: 300, h: 250}]); + expect(payload.imp[0]).to.not.have.property('video'); + }); + + it('Banner multiple sizes', () => { + expect(payload.imp[1]).to.have.property('id', 'bid001'); + expect(payload.imp[1]).to.have.property('secure', 1); + expect(payload.imp[1]).to.have.property('bidfloor', 0); + expect(payload.imp[1]).to.have.property('displaymanager', 'Prebid.js'); + expect(payload.imp[1]).to.have.property('displaymanagerver').that.matches(versionPattern); + expect(payload.imp[1]).to.not.have.property('tagid'); + expect(payload.imp[1]).to.have.property('banner'); + expect(payload.imp[1].banner).to.not.have.property('pos'); + expect(payload.imp[1].banner).to.have.property('format'); + expect(payload.imp[1].banner.format).to.deep.equal([{w: 728, h: 90}, {w: 468, h: 60}]); + }); + + it('Banner with tagid and position', () => { + expect(payload.imp[2]).to.have.property('id', 'bid002'); + expect(payload.imp[2]).to.have.property('secure', 1); + expect(payload.imp[2]).to.have.property('bidfloor', 0); + expect(payload.imp[2]).to.have.property('displaymanager', 'Prebid.js'); + expect(payload.imp[2]).to.have.property('displaymanagerver').that.matches(versionPattern); + expect(payload.imp[2]).to.have.property('banner'); + expect(payload.imp[2].banner).to.have.property('pos', 2); + expect(payload.imp[2].banner).to.have.property('format'); + expect(payload.imp[2].banner.format).to.deep.equal([{w: 300, h: 600}, {w: 160, h: 600}]); + }); + + if (FEATURES.VIDEO) { + it('Simple video', () => { + expect(payload.imp[3]).to.have.property('id', 'bid003'); + expect(payload.imp[3]).to.have.property('secure', 1); + expect(payload.imp[3]).to.have.property('bidfloor', 0); + expect(payload.imp[3]).to.have.property('displaymanager', 'Prebid.js'); + expect(payload.imp[3]).to.have.property('displaymanagerver').that.matches(versionPattern); + expect(payload.imp[3]).to.not.have.property('tagid'); + expect(payload.imp[3]).to.have.property('video'); + expect(payload.imp[3].video).to.have.property('pos', 3); + expect(payload.imp[3].video).to.have.property('w', 632); + expect(payload.imp[3].video).to.have.property('h', 499); + expect(payload.imp[3].video).to.have.property('mimes'); + expect(payload.imp[3].video.mimes).to.deep.equal(['video/mp4', 'video/x-flv']); + expect(payload.imp[3].video).to.have.property('protocols'); + expect(payload.imp[3].video.protocols).to.deep.equal([1, 2]); + expect(payload.imp[3].video).to.have.property('api'); + expect(payload.imp[3].video.api).to.deep.equal([2]); + expect(payload.imp[3].video).to.have.property('maxduration', 30); + expect(payload.imp[3]).to.not.have.property('banner'); + }); + + it('Video with playerSize', () => { + expect(payload.imp[4]).to.have.property('id', 'bid004'); + expect(payload.imp[4]).to.have.property('secure', 1); + expect(payload.imp[4]).to.have.property('bidfloor', 0); + expect(payload.imp[4]).to.have.property('displaymanager', 'Prebid.js'); + expect(payload.imp[4]).to.have.property('displaymanagerver').that.matches(versionPattern); + expect(payload.imp[4]).to.not.have.property('tagid'); + expect(payload.imp[4]).to.have.property('video'); + expect(payload.imp[4].video).to.not.have.property('pos'); + expect(payload.imp[4].video).to.have.property('w', 1024); + expect(payload.imp[4].video).to.have.property('h', 768); + expect(payload.imp[4].video).to.have.property('mimes'); + expect(payload.imp[4].video.mimes).to.deep.equal(['video/mp4', 'video/x-flv']); + expect(payload.imp[4].video).to.have.property('protocols'); + expect(payload.imp[4].video.protocols).to.deep.equal([1, 2, 3]); + expect(payload.imp[4].video).to.have.property('api'); + expect(payload.imp[4].video.api).to.deep.equal([1, 2, 3]); + expect(payload.imp[4].video).to.have.property('maxduration', 30); + }); + + it('Video without sizes', () => { + expect(payload.imp[5]).to.have.property('id', 'bid005'); + expect(payload.imp[5]).to.have.property('secure', 1); + expect(payload.imp[5]).to.have.property('bidfloor', 0); + expect(payload.imp[5]).to.have.property('displaymanager', 'Prebid.js'); + expect(payload.imp[5]).to.have.property('displaymanagerver').that.matches(versionPattern); + expect(payload.imp[5]).to.not.have.property('tagid'); + expect(payload.imp[5]).to.have.property('video'); + expect(payload.imp[5].video).to.have.property('pos', 2); + expect(payload.imp[5].video).to.not.have.property('w'); + expect(payload.imp[5].video).to.not.have.property('h'); + expect(payload.imp[5].video).to.have.property('mimes'); + expect(payload.imp[5].video.mimes).to.deep.equal(['video/mp4', 'video/x-flv']); + expect(payload.imp[5].video).to.not.have.property('protocols'); + expect(payload.imp[5].video).to.not.have.property('api'); + expect(payload.imp[5].video).to.not.have.property('maxduration'); + expect(payload.imp[5]).to.not.have.property('banner'); + }); + } + + it('With FPD', () => { + expect(payload.imp[6]).to.have.property('id', 'bid006'); + expect(payload.imp[6]).to.have.property('banner'); + expect(payload.imp[6]).to.not.have.property('video'); + expect(payload.imp[6]).to.have.property('instl'); + expect(payload.imp[6]).to.have.property('ext'); + expect(payload.imp[6].ext).to.have.property('data'); + expect(payload.imp[6].ext.data).to.have.property('pbadslot'); + }); }); it('Verify timeout', () => { @@ -440,59 +460,62 @@ describe('Conversant adapter tests', function() { expect(request.url).to.equal(testUrl); }); - it('Verify interpretResponse', function() { - const request = spec.buildRequests(bidRequests, {}); - const response = spec.interpretResponse(bidResponses, request); - expect(response).to.be.an('array').with.lengthOf(4); - - let bid = response[0]; - expect(bid).to.have.property('requestId', 'bid000'); - expect(bid).to.have.property('currency', 'USD'); - expect(bid).to.have.property('cpm', 0.99); - expect(bid).to.have.property('creativeId', '1000'); - expect(bid).to.have.property('width', 300); - expect(bid).to.have.property('height', 250); - expect(bid.meta.advertiserDomains).to.deep.equal(['https://example.com']); - expect(bid).to.have.property('ad', 'markup000'); - expect(bid).to.have.property('ttl', 300); - expect(bid).to.have.property('netRevenue', true); + describe('Verify interpretResponse', function() { + let bid, request, response; + + before(() => { + request = spec.buildRequests(bidRequests, {}); + response = spec.interpretResponse(bidResponses, request).bids; + }); + + it('Banner', function() { + expect(response).to.be.an('array').with.lengthOf(4); + bid = response[0]; + expect(bid).to.have.property('requestId', 'bid000'); + expect(bid).to.have.property('cpm', 0.99); + expect(bid).to.have.property('creativeId', '1000'); + expect(bid).to.have.property('width', 300); + expect(bid).to.have.property('height', 250); + expect(bid.meta.advertiserDomains).to.deep.equal(['https://example.com']); + expect(bid).to.have.property('ad', 'markup000
'); + expect(bid).to.have.property('ttl', 300); + expect(bid).to.have.property('netRevenue', true); + }); // There is no bid001 because cpm is $0 - bid = response[1]; - expect(bid).to.have.property('requestId', 'bid002'); - expect(bid).to.have.property('currency', 'USD'); - expect(bid).to.have.property('cpm', 2.99); - expect(bid).to.have.property('creativeId', '1002'); - expect(bid).to.have.property('width', 300); - expect(bid).to.have.property('height', 600); - expect(bid).to.have.property('ad', 'markup002'); - expect(bid).to.have.property('ttl', 300); - expect(bid).to.have.property('netRevenue', true); - - bid = response[2]; - expect(bid).to.have.property('requestId', 'bid003'); - expect(bid).to.have.property('currency', 'USD'); - expect(bid).to.have.property('cpm', 3.99); - expect(bid).to.have.property('creativeId', '1003'); - expect(bid).to.have.property('width', 632); - expect(bid).to.have.property('height', 499); - expect(bid).to.have.property('vastUrl', 'markup003'); - expect(bid).to.have.property('mediaType', 'video'); - expect(bid).to.have.property('ttl', 300); - expect(bid).to.have.property('netRevenue', true); - - bid = response[3]; - expect(bid).to.have.property('vastXml', ''); - }); + it('Banner multiple sizes', function() { + bid = response[1]; + expect(bid).to.have.property('requestId', 'bid002'); + expect(bid).to.have.property('cpm', 2.99); + expect(bid).to.have.property('creativeId', '1002'); + expect(bid).to.have.property('width', 300); + expect(bid).to.have.property('height', 600); + expect(bid).to.have.property('ad', 'markup002
'); + expect(bid).to.have.property('ttl', 300); + expect(bid).to.have.property('netRevenue', true); + }); + + if (FEATURES.VIDEO) { + it('Video', function () { + bid = response[2]; + expect(bid).to.have.property('requestId', 'bid003'); + expect(bid).to.have.property('cpm', 3.99); + expect(bid).to.have.property('creativeId', '1003'); + expect(bid).to.have.property('playerWidth', 632); + expect(bid).to.have.property('playerHeight', 499); + expect(bid).to.have.property('vastUrl', 'notify003'); + expect(bid).to.have.property('vastXml', 'markup003'); + expect(bid).to.have.property('mediaType', 'video'); + expect(bid).to.have.property('ttl', 300); + expect(bid).to.have.property('netRevenue', true); + }); - it('Verify handling of bad responses', function() { - let response = spec.interpretResponse({}, {}); - expect(response).to.be.an('array').with.lengthOf(0); - response = spec.interpretResponse({id: '123'}, {}); - expect(response).to.be.an('array').with.lengthOf(0); - response = spec.interpretResponse({id: '123', seatbid: []}, {}); - expect(response).to.be.an('array').with.lengthOf(0); + it('Empty Video', function() { + bid = response[3]; + expect(bid).to.have.property('vastXml', ''); + }); + } }); it('Verify publisher commond id support', function() { @@ -524,79 +547,23 @@ describe('Conversant adapter tests', function() { expect(payload).to.not.have.nested.property('user.ext.eids'); }); - it('Verify GDPR bid request', function() { - // add gdpr info - const bidderRequest = { - gdprConsent: { - consentString: 'BOJObISOJObISAABAAENAA4AAAAAoAAA', - gdprApplies: true - } - }; - - const payload = spec.buildRequests(bidRequests, bidderRequest).data; - expect(payload).to.have.deep.nested.property('user.ext.consent', 'BOJObISOJObISAABAAENAA4AAAAAoAAA'); - expect(payload).to.have.deep.nested.property('regs.ext.gdpr', 1); - }); - - it('Verify GDPR bid request without gdprApplies', function() { - // add gdpr info - const bidderRequest = { - gdprConsent: { - consentString: '' - } - }; - - const payload = spec.buildRequests(bidRequests, bidderRequest).data; - expect(payload).to.have.deep.nested.property('user.ext.consent', ''); - expect(payload).to.not.have.deep.nested.property('regs.ext.gdpr'); - }); - - describe('CCPA', function() { - it('should have us_privacy', function() { - const bidderRequest = { - uspConsent: '1NYN' - }; - - const payload = spec.buildRequests(bidRequests, bidderRequest).data; - expect(payload).to.have.deep.nested.property('regs.ext.us_privacy', '1NYN'); - expect(payload).to.not.have.deep.nested.property('regs.ext.gdpr'); - }); - - it('should have no us_privacy', function() { - const payload = spec.buildRequests(bidRequests, {}).data; - expect(payload).to.not.have.deep.nested.property('regs.ext.us_privacy'); - }); - - it('should have both gdpr and us_privacy', function() { - const bidderRequest = { - gdprConsent: { - consentString: 'BOJObISOJObISAABAAENAA4AAAAAoAAA', - gdprApplies: true - }, - uspConsent: '1NYN' - }; - - const payload = spec.buildRequests(bidRequests, bidderRequest).data; - expect(payload).to.have.deep.nested.property('user.ext.consent', 'BOJObISOJObISAABAAENAA4AAAAAoAAA'); - expect(payload).to.have.deep.nested.property('regs.ext.gdpr', 1); - expect(payload).to.have.deep.nested.property('regs.ext.us_privacy', '1NYN'); - }); - }); - describe('Extended ID', function() { it('Verify unifiedid and liveramp', function() { // clone bidRequests let requests = utils.deepClone(bidRequests); + const uid = {pubcid: '112233', idl_env: '334455'}; + const eidArray = [{'source': 'pubcid.org', 'uids': [{'id': '112233', 'atype': 1}]}, {'source': 'liveramp.com', 'uids': [{'id': '334455', 'atype': 3}]}]; + // add pubcid to every entry requests.forEach((unit) => { - Object.assign(unit, {userId: {pubcid: '112233', tdid: '223344', idl_env: '334455'}}); - Object.assign(unit, {userIdAsEids: createEidsArray(unit.userId)}); + Object.assign(unit, {userId: uid}); + Object.assign(unit, {userIdAsEids: eidArray}); }); // construct http post payload const payload = spec.buildRequests(requests, {}).data; expect(payload).to.have.deep.nested.property('user.ext.eids', [ - {source: 'adserver.org', uids: [{id: '223344', atype: 1, ext: {rtiPartner: 'TDID'}}]}, + {source: 'pubcid.org', uids: [{id: '112233', atype: 1}]}, {source: 'liveramp.com', uids: [{id: '334455', atype: 3}]} ]); }); From a40061e42abb9e6fcd2f7858ce2dd591cf4d01d8 Mon Sep 17 00:00:00 2001 From: Petre Damoc Date: Wed, 24 Jan 2024 22:11:02 +0200 Subject: [PATCH 170/246] Missena Bid Adapter: send cookieDeprecationLabel and prebid version (#10979) --- modules/missenaBidAdapter.js | 4 ++++ test/spec/modules/missenaBidAdapter_spec.js | 14 ++++++++++++++ 2 files changed, 18 insertions(+) diff --git a/modules/missenaBidAdapter.js b/modules/missenaBidAdapter.js index 5e68f56ed1c..080ed2a556f 100644 --- a/modules/missenaBidAdapter.js +++ b/modules/missenaBidAdapter.js @@ -99,7 +99,11 @@ export const spec = { if (bidRequest.params.isInternal) { payload.is_internal = bidRequest.params.isInternal; } + if (bidRequest.ortb2?.device?.ext?.cdep) { + payload.cdep = bidRequest.ortb2?.device?.ext?.cdep; + } payload.userEids = bidRequest.userIdAsEids || []; + payload.version = '$prebid.version$'; const bidFloor = getFloor(bidRequest); payload.floor = bidFloor?.floor; diff --git a/test/spec/modules/missenaBidAdapter_spec.js b/test/spec/modules/missenaBidAdapter_spec.js index a51cb8bbac9..f84981352ab 100644 --- a/test/spec/modules/missenaBidAdapter_spec.js +++ b/test/spec/modules/missenaBidAdapter_spec.js @@ -4,6 +4,7 @@ import { BANNER } from '../../../src/mediaTypes.js'; const REFERRER = 'https://referer'; const REFERRER2 = 'https://referer2'; +const COOKIE_DEPRECATION_LABEL = 'test'; describe('Missena Adapter', function () { $$PREBID_GLOBAL$$.bidderSettings = { @@ -18,6 +19,11 @@ describe('Missena Adapter', function () { bidId: bidId, sizes: [[1, 1]], mediaTypes: { banner: { sizes: [[1, 1]] } }, + ortb2: { + device: { + ext: { cdep: COOKIE_DEPRECATION_LABEL }, + }, + }, params: { apiKey: 'PA-34745704', placement: 'sticky', @@ -183,6 +189,14 @@ describe('Missena Adapter', function () { it('should participate if capped on a different page', function () { expect(cappedRequestsOtherPage.length).to.equal(2); }); + + it('should send the prebid version', function () { + expect(payload.version).to.equal('$prebid.version$'); + }); + + it('should send cookie deprecation', function () { + expect(payload.cdep).to.equal(COOKIE_DEPRECATION_LABEL); + }); }); describe('interpretResponse', function () { From ec5880b0f4c6094f2e8f8519976ca5c15fb4ccb2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=9D=8E=E5=90=91=E5=86=9B?= <401154047@qq.com> Date: Fri, 26 Jan 2024 01:49:41 +0800 Subject: [PATCH 171/246] zMaticoo Bid Adapter : Initial Release (#10881) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * feat:zMaticoo Bid Adapter for Prebid.js * feat:zMaticoo Bid Adapter for Prebid.js * feat:fix some code style * feat:fix some code style * feat:update package-lock.json --------- Co-authored-by: adam --- modules/zmaticooBidAdapter.js | 141 +++++++++++++++++++ modules/zmaticooBidAdapter.md | 45 ++++++ test/spec/modules/zmaticooBidAdapter_spec.js | 83 +++++++++++ 3 files changed, 269 insertions(+) create mode 100644 modules/zmaticooBidAdapter.js create mode 100644 modules/zmaticooBidAdapter.md create mode 100644 test/spec/modules/zmaticooBidAdapter_spec.js diff --git a/modules/zmaticooBidAdapter.js b/modules/zmaticooBidAdapter.js new file mode 100644 index 00000000000..8566d58c6de --- /dev/null +++ b/modules/zmaticooBidAdapter.js @@ -0,0 +1,141 @@ +import {deepAccess, logWarn} from '../src/utils.js'; +import {registerBidder} from '../src/adapters/bidderFactory.js'; +import {BANNER} from '../src/mediaTypes.js'; + +const BIDDER_CODE = 'zmaticoo'; +const ENDPOINT_URL = 'https://bid.zmaticoo.com/prebid/bid'; +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 + if (!(bid && bid.bidId && bid.params)) { + logWarn('Invalid bid request - missing required bid data'); + return false; + } + + if (!(bid.params.pubId)) { + logWarn('Invalid bid request - missing required field pubId'); + return false; + } + + if (!(bid.params.device && bid.params.device.ip)) { + logWarn('Invalid bid request - missing required device data'); + return false; + } + return true; + }, + + /** + * 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; + const request = validBidRequests[0]; + const params = request.params; + let impData = { + id: request.bidId, + secure: secure, + banner: buildBanner(request), + ext: { + bidder: { + pubId: params.pubId + } + } + }; + let payload = { + id: bidderRequest.bidderRequestId, + imp: [impData], + site: params.site ? params.site : {}, + app: params.app ? params.app : {}, + device: params.device ? params.device : {}, + user: params.user ? params.user : {}, + at: params.at, + tmax: params.tmax, + wseat: params.wseat, + bseat: params.bseat, + allimps: params.allimps, + cur: [DEFAULT_CUR], + wlang: params.wlang, + bcat: deepAccess(bidderRequest.ortb2Imp, 'bcat') || params.bcat, + 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.page; + payload.site.mobile = /(ios|ipod|ipad|iphone|android)/i.test(navigator.userAgent) ? 1 : 0; + if (params.test) { + payload.test = params.test; + } + if (request.gdprConsent) { + payload.regs.ext = Object.assign(payload.regs.ext, {gdpr: request.gdprConsent.gdprApplies === true ? 1 : 0}); + } + if (request.gdprConsent && request.gdprConsent.gdprApplies) { + payload.user.ext = Object.assign(payload.user.ext, {consent: request.gdprConsent.consentString}); + } + const postUrl = ENDPOINT_URL; + return { + method: 'POST', url: postUrl, 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 zresponse = serverResponse.body; + let zbid = zresponse.seatbid[0].bid[0]; + let bid = { + requestId: zbid.impid, + cpm: zbid.price, + currency: zbid.cur, + width: zbid.w, + height: zbid.h, + ad: zbid.adm, + ttl: TTL, + creativeId: zbid.crid, + netRevenue: NET_REV + }; + bidResponse.push(bid); + } + return bidResponse; + } +} + +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/zmaticooBidAdapter.md b/modules/zmaticooBidAdapter.md new file mode 100644 index 00000000000..95959ef218c --- /dev/null +++ b/modules/zmaticooBidAdapter.md @@ -0,0 +1,45 @@ +# Overview + +``` +Module Name: zMaticoo Bidder Adapter +Module Type: Bidder Adapter +Maintainer: adam.li@eclicktech.com.cn +``` + +# Description + +zMaticoo Bidder Adapter for Prebid.js. + +# Test Parameters + +``` + var adUnits = [ + { + mediaTypes: { + banner: { + sizes: [[320, 50]], // a display size + } + }, + bids: [ + { + bidder: 'zmaticoo', + bidId: '12345', + params: { + user: { + uid: '12345', + buyeruid: '12345' + }, + device: { + ip: '111.222.33.44', + geo: { + country: 'USA' + } + }, + pubId: 'prebid-fgh', + test: 1 + } + } + ] + } + ]; +``` diff --git a/test/spec/modules/zmaticooBidAdapter_spec.js b/test/spec/modules/zmaticooBidAdapter_spec.js new file mode 100644 index 00000000000..898dccf1fde --- /dev/null +++ b/test/spec/modules/zmaticooBidAdapter_spec.js @@ -0,0 +1,83 @@ +import {spec} from '../../../modules/zmaticooBidAdapter.js' + +describe('zMaticoo Bidder Adapter', function () { + const bannerRequest = [{ + bidId: '1234511', + auctionId: '223', + mediaTypes: { + banner: { + sizes: [[320, 50]], + } + }, + refererInfo: { + page: 'testprebid.com' + }, + params: { + user: { + uid: '12345', + buyeruid: '12345' + }, + device: { + ip: '111.222.33.44', + geo: { + country: 'USA' + } + }, + pubId: 'prebid-test', + 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: 50, + w: 320 + } + ] + } + ], + 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 57c18bd43e6db1291a28f03e1df4393e3f17f149 Mon Sep 17 00:00:00 2001 From: Yimilh <34877463+Yimilh@users.noreply.github.com> Date: Fri, 26 Jan 2024 02:17:11 +0800 Subject: [PATCH 172/246] MediaGo Bid Adapter : add pmguid, title, description, keywords and synchronize mguid from third party cookie to first party cookie. (#10923) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * MediaGo Bid Adapter : update buyerId and add pmguid,title, description and keywords. --- modules/mediagoBidAdapter.js | 149 ++++++-- test/spec/modules/mediagoBidAdapter_spec.js | 375 +++++++++++++++++++- 2 files changed, 497 insertions(+), 27 deletions(-) diff --git a/modules/mediagoBidAdapter.js b/modules/mediagoBidAdapter.js index a9c670f35d8..8513fba94c4 100644 --- a/modules/mediagoBidAdapter.js +++ b/modules/mediagoBidAdapter.js @@ -11,40 +11,105 @@ import { registerBidder } from '../src/adapters/bidderFactory.js'; const BIDDER_CODE = 'mediago'; // const PROTOCOL = window.document.location.protocol; const ENDPOINT_URL = 'https://gbid.mediago.io/api/bid?tn='; -const COOKY_SYNC_URL = 'https://gtrace.mediago.io/ju/cs/eplist'; +// const COOKY_SYNC_URL = 'https://gtrace.mediago.io/ju/cs/eplist'; const COOKY_SYNC_IFRAME_URL = 'https://cdn.mediago.io/js/cookieSync.html'; +export const THIRD_PARTY_COOKIE_ORIGIN = 'https://cdn.mediago.io'; const TIME_TO_LIVE = 500; const GVLID = 1020; // const ENDPOINT_URL = '/api/bid?tn='; -const storage = getStorageManager({ bidderCode: BIDDER_CODE }); +export const storage = getStorageManager({bidderCode: BIDDER_CODE}); let globals = {}; let itemMaps = {}; /* ----- mguid:start ------ */ -const COOKIE_KEY_MGUID = '__mguid_'; -const STORE_MAX_AGE = 1000 * 60 * 60 * 24 * 365; +export const COOKIE_KEY_MGUID = '__mguid_'; +const COOKIE_KEY_PMGUID = '__pmguid_'; +const COOKIE_RETENTION_TIME = 365 * 24 * 60 * 60 * 1000; // 1 year let reqTimes = 0; +/** + * get page title + * @returns {string} + */ + +export function getPageTitle(win = window) { + try { + const ogTitle = win.top.document.querySelector('meta[property="og:title"]') + return win.top.document.title || (ogTitle && ogTitle.content) || ''; + } catch (e) { + const ogTitle = document.querySelector('meta[property="og:title"]') + return document.title || (ogTitle && ogTitle.content) || ''; + } +} /** - * 获取用户id - * @return {string} + * get page description + * + * @returns {string} + */ +export function getPageDescription(win = window) { + let element; + + try { + element = win.top.document.querySelector('meta[name="description"]') || + win.top.document.querySelector('meta[property="og:description"]') + } catch (e) { + element = document.querySelector('meta[name="description"]') || + document.querySelector('meta[property="og:description"]') + } + + return (element && element.content) || ''; +} + +/** + * get page keywords + * @returns {string} + */ +export function getPageKeywords(win = window) { + let element; + + try { + element = win.top.document.querySelector('meta[name="keywords"]'); + } catch (e) { + element = document.querySelector('meta[name="keywords"]'); + } + + return (element && element.content) || ''; +} + +/** + * get connection downlink + * @returns {number} */ -const getUserID = () => { - const i = storage.getCookie(COOKIE_KEY_MGUID); +export function getConnectionDownLink(win = window) { + const nav = win.navigator || {}; + return nav && nav.connection && nav.connection.downlink >= 0 ? nav.connection.downlink.toString() : undefined; +} - if (i === null) { - const uuid = utils.generateUUID(); - storage.setCookie(COOKIE_KEY_MGUID, uuid, STORE_MAX_AGE); - return uuid; +/** + * get pmg uid + * 获取并生成用户的id + * + * @return {string} + */ +export const getPmgUID = () => { + if (!storage.cookiesAreEnabled()) return; + + let pmgUid = storage.getCookie(COOKIE_KEY_PMGUID); + if (!pmgUid) { + pmgUid = utils.generateUUID(); + try { + storage.setCookie(COOKIE_KEY_PMGUID, pmgUid, getCurrentTimeToUTCString()); + } catch (e) {} } - return i; + return pmgUid; }; -/* ----- mguid:end ------ */ +/* ----- pmguid:end ------ */ /** * 获取一个对象的某个值,如果没有则返回空字符串 + * * @param {Object} obj 对象 * @param {...string} keys 键名 * @return {any} @@ -283,6 +348,16 @@ function getReferrer(bidRequest = {}, bidderRequest = {}) { return pageUrl; } +/** + * get current time to UTC string + * @returns utc string + */ +export function getCurrentTimeToUTCString() { + const date = new Date(); + date.setTime(date.getTime() + COOKIE_RETENTION_TIME); + return date.toUTCString(); +} + /** * 获取rtb请求参数 * @@ -317,6 +392,10 @@ function getParam(validBidRequests, bidderRequest) { const timeout = bidderRequest.timeout || 2000; const firstPartyData = bidderRequest.ortb2; + const topWindow = window.top; + const title = getPageTitle(); + const desc = getPageDescription(); + const keywords = getPageKeywords(); if (items && items.length) { let c = { @@ -344,11 +423,23 @@ function getParam(validBidRequests, bidderRequest) { firstPartyData, content, cat, - reqTimes + reqTimes, + pmguid: getPmgUID(), + page: { + title: title ? title.slice(0, 100) : undefined, + desc: desc ? desc.slice(0, 300) : undefined, + keywords: keywords ? keywords.slice(0, 100) : undefined, + hLen: topWindow.history?.length || undefined, + }, + device: { + nbw: getConnectionDownLink(), + hc: topWindow.navigator?.hardwareConcurrency || undefined, + dm: topWindow.navigator?.deviceMemory || undefined, + } }, user: { - buyeruid: getUserID(), - id: sharedid || pubcid + buyeruid: storage.getCookie(COOKIE_KEY_MGUID) || undefined, + id: sharedid || pubcid, }, eids, site: { @@ -407,13 +498,12 @@ export const spec = { return { method: 'POST', url: ENDPOINT_URL + globals['token'], - data: payloadString + 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. */ @@ -471,19 +561,26 @@ export const spec = { } if (syncOptions.iframeEnabled) { + window.addEventListener('message', function handler(event) { + if (!event.data || event.origin != THIRD_PARTY_COOKIE_ORIGIN) { + return; + } + + this.removeEventListener('message', handler); + + event.stopImmediatePropagation(); + + const response = event.data; + if (!response.optout && response.mguid) { + storage.setCookie(COOKIE_KEY_MGUID, response.mguid, getCurrentTimeToUTCString()); + } + }, true); return [ { type: 'iframe', url: `${COOKY_SYNC_IFRAME_URL}?${syncParamUrl}` } ]; - } else { - return [ - { - type: 'image', - url: `${COOKY_SYNC_URL}?${syncParamUrl}` - } - ]; } }, diff --git a/test/spec/modules/mediagoBidAdapter_spec.js b/test/spec/modules/mediagoBidAdapter_spec.js index 601bcff527a..6e58217b3d3 100644 --- a/test/spec/modules/mediagoBidAdapter_spec.js +++ b/test/spec/modules/mediagoBidAdapter_spec.js @@ -1,5 +1,17 @@ import { expect } from 'chai'; -import { spec } from 'modules/mediagoBidAdapter.js'; +import { + spec, + getPmgUID, + storage, + getPageTitle, + getPageDescription, + getPageKeywords, + getConnectionDownLink, + THIRD_PARTY_COOKIE_ORIGIN, + COOKIE_KEY_MGUID, + getCurrentTimeToUTCString +} from 'modules/mediagoBidAdapter.js'; +import * as utils from 'src/utils.js'; describe('mediago:BidAdapterTests', function () { let bidRequestData = { @@ -161,6 +173,47 @@ describe('mediago:BidAdapterTests', function () { expect(req_data.imp).to.have.lengthOf(1); }); + describe('mediago: buildRequests', function() { + describe('getPmgUID function', function() { + let sandbox; + + beforeEach(() => { + sandbox = sinon.sandbox.create(); + sandbox.stub(storage, 'getCookie'); + sandbox.stub(storage, 'setCookie'); + sandbox.stub(utils, 'generateUUID').returns('new-uuid'); + sandbox.stub(storage, 'cookiesAreEnabled'); + }) + + afterEach(() => { + sandbox.restore(); + }); + + it('should generate new UUID and set cookie if not exists', () => { + storage.cookiesAreEnabled.callsFake(() => true); + storage.getCookie.callsFake(() => null); + const uid = getPmgUID(); + expect(uid).to.equal('new-uuid'); + expect(storage.setCookie.calledOnce).to.be.true; + }); + + it('should return existing UUID from cookie', () => { + storage.cookiesAreEnabled.callsFake(() => true); + storage.getCookie.callsFake(() => 'existing-uuid'); + const uid = getPmgUID(); + expect(uid).to.equal('existing-uuid'); + expect(storage.setCookie.called).to.be.false; + }); + + it('should not set new UUID when cookies are not enabled', () => { + storage.cookiesAreEnabled.callsFake(() => false); + storage.getCookie.callsFake(() => null); + getPmgUID(); + expect(storage.setCookie.calledOnce).to.be.false; + }); + }) + }); + it('mediago:validate_response_params', function () { let adm = ''; @@ -207,4 +260,324 @@ describe('mediago:BidAdapterTests', function () { expect(bid.height).to.equal(250); expect(bid.currency).to.equal('USD'); }); + + describe('mediago: getUserSyncs', function() { + const COOKY_SYNC_IFRAME_URL = 'https://cdn.mediago.io/js/cookieSync.html'; + const IFRAME_ENABLED = { + iframeEnabled: true, + pixelEnabled: false, + }; + const IFRAME_DISABLED = { + iframeEnabled: false, + pixelEnabled: false, + }; + const GDPR_CONSENT = { + consentString: 'gdprConsentString', + gdprApplies: true + }; + const USP_CONSENT = { + consentString: 'uspConsentString' + } + + let syncParamUrl = `dm=${encodeURIComponent(location.origin || `https://${location.host}`)}`; + syncParamUrl += '&gdpr=1&gdpr_consent=gdprConsentString&ccpa_consent=uspConsentString'; + const expectedIframeSyncs = [ + { + type: 'iframe', + url: `${COOKY_SYNC_IFRAME_URL}?${syncParamUrl}` + } + ]; + + it('should return nothing if iframe is disabled', () => { + const userSyncs = spec.getUserSyncs(IFRAME_DISABLED, undefined, GDPR_CONSENT, USP_CONSENT, undefined); + expect(userSyncs).to.be.undefined; + }); + + it('should do userSyncs if iframe is enabled', () => { + const userSyncs = spec.getUserSyncs(IFRAME_ENABLED, undefined, GDPR_CONSENT, USP_CONSENT, undefined); + expect(userSyncs).to.deep.equal(expectedIframeSyncs); + }); + }); +}); + +describe('mediago Bid Adapter Tests', function () { + describe('buildRequests', () => { + describe('getPageTitle function', function() { + let sandbox; + + beforeEach(() => { + sandbox = sinon.createSandbox(); + }); + + afterEach(() => { + sandbox.restore(); + }); + + it('should return the top document title if available', function() { + const fakeTopDocument = { + title: 'Top Document Title', + querySelector: () => ({ content: 'Top Document Title test' }) + }; + const fakeTopWindow = { + document: fakeTopDocument + }; + const result = getPageTitle({ top: fakeTopWindow }); + expect(result).to.equal('Top Document Title'); + }); + + it('should return the content of top og:title meta tag if title is empty', function() { + const ogTitleContent = 'Top OG Title Content'; + const fakeTopWindow = { + document: { + title: '', + querySelector: sandbox.stub().withArgs('meta[property="og:title"]').returns({ content: ogTitleContent }) + } + }; + + const result = getPageTitle({ top: fakeTopWindow }); + expect(result).to.equal(ogTitleContent); + }); + + it('should return the document title if no og:title meta tag is present', function() { + document.title = 'Test Page Title'; + sandbox.stub(document, 'querySelector').withArgs('meta[property="og:title"]').returns(null); + + const result = getPageTitle({ top: undefined }); + expect(result).to.equal('Test Page Title'); + }); + + it('should return the content of og:title meta tag if present', function() { + document.title = ''; + const ogTitleContent = 'Top OG Title Content'; + sandbox.stub(document, 'querySelector').withArgs('meta[property="og:title"]').returns({ content: ogTitleContent }); + const result = getPageTitle({ top: undefined }); + expect(result).to.equal(ogTitleContent); + }); + + it('should return an empty string if no title or og:title meta tag is found', function() { + document.title = ''; + sandbox.stub(document, 'querySelector').withArgs('meta[property="og:title"]').returns(null); + const result = getPageTitle({ top: undefined }); + expect(result).to.equal(''); + }); + + it('should handle exceptions when accessing top.document and fallback to current document', function() { + const fakeWindow = { + get top() { + throw new Error('Access denied'); + } + }; + const ogTitleContent = 'Current OG Title Content'; + document.title = 'Current Document Title'; + sandbox.stub(document, 'querySelector').withArgs('meta[property="og:title"]').returns({ content: ogTitleContent }); + const result = getPageTitle(fakeWindow); + expect(result).to.equal('Current Document Title'); + }); + }); + + describe('getPageDescription function', function() { + let sandbox; + + beforeEach(() => { + sandbox = sinon.createSandbox(); + }); + + afterEach(() => { + sandbox.restore(); + }); + + it('should return the top document description if available', function() { + const descriptionContent = 'Top Document Description'; + const fakeTopDocument = { + querySelector: sandbox.stub().withArgs('meta[name="description"]').returns({ content: descriptionContent }) + }; + const fakeTopWindow = { document: fakeTopDocument }; + const result = getPageDescription({ top: fakeTopWindow }); + expect(result).to.equal(descriptionContent); + }); + + it('should return the top document og:description if description is not present', function() { + const ogDescriptionContent = 'Top OG Description'; + const fakeTopDocument = { + querySelector: sandbox.stub().withArgs('meta[property="og:description"]').returns({ content: ogDescriptionContent }) + }; + const fakeTopWindow = { document: fakeTopDocument }; + const result = getPageDescription({ top: fakeTopWindow }); + expect(result).to.equal(ogDescriptionContent); + }); + + it('should return the current document description if top document is not accessible', function() { + const descriptionContent = 'Current Document Description'; + sandbox.stub(document, 'querySelector') + .withArgs('meta[name="description"]').returns({ content: descriptionContent }) + const fakeWindow = { + get top() { + throw new Error('Access denied'); + } + }; + const result = getPageDescription(fakeWindow); + expect(result).to.equal(descriptionContent); + }); + + it('should return the current document og:description if description is not present and top document is not accessible', function() { + const ogDescriptionContent = 'Current OG Description'; + sandbox.stub(document, 'querySelector') + .withArgs('meta[property="og:description"]').returns({ content: ogDescriptionContent }); + + const fakeWindow = { + get top() { + throw new Error('Access denied'); + } + }; + const result = getPageDescription(fakeWindow); + expect(result).to.equal(ogDescriptionContent); + }); + }); + + describe('getPageKeywords function', function() { + let sandbox; + + beforeEach(() => { + sandbox = sinon.createSandbox(); + }); + + afterEach(() => { + sandbox.restore(); + }); + + it('should return the top document keywords if available', function() { + const keywordsContent = 'keyword1, keyword2, keyword3'; + const fakeTopDocument = { + querySelector: sandbox.stub() + .withArgs('meta[name="keywords"]').returns({ content: keywordsContent }) + }; + const fakeTopWindow = { document: fakeTopDocument }; + + const result = getPageKeywords({ top: fakeTopWindow }); + expect(result).to.equal(keywordsContent); + }); + + it('should return the current document keywords if top document is not accessible', function() { + const keywordsContent = 'keyword1, keyword2, keyword3'; + sandbox.stub(document, 'querySelector') + .withArgs('meta[name="keywords"]').returns({ content: keywordsContent }); + + // 模拟顶层窗口访问异常 + const fakeWindow = { + get top() { + throw new Error('Access denied'); + } + }; + + const result = getPageKeywords(fakeWindow); + expect(result).to.equal(keywordsContent); + }); + + it('should return an empty string if no keywords meta tag is found', function() { + sandbox.stub(document, 'querySelector').withArgs('meta[name="keywords"]').returns(null); + + const result = getPageKeywords(); + expect(result).to.equal(''); + }); + }); + describe('getConnectionDownLink function', function() { + let sandbox; + + beforeEach(() => { + sandbox = sinon.createSandbox(); + }); + + afterEach(() => { + sandbox.restore(); + }); + + it('should return the downlink value as a string if available', function() { + const downlinkValue = 2.5; + const fakeNavigator = { + connection: { + downlink: downlinkValue + } + }; + + const result = getConnectionDownLink({ navigator: fakeNavigator }); + expect(result).to.equal(downlinkValue.toString()); + }); + + it('should return undefined if downlink is not available', function() { + const fakeNavigator = { + connection: {} + }; + + const result = getConnectionDownLink({ navigator: fakeNavigator }); + expect(result).to.be.undefined; + }); + + it('should return undefined if connection is not available', function() { + const fakeNavigator = {}; + + const result = getConnectionDownLink({ navigator: fakeNavigator }); + expect(result).to.be.undefined; + }); + + it('should handle cases where navigator is not defined', function() { + const result = getConnectionDownLink({}); + expect(result).to.be.undefined; + }); + }); + + describe('getUserSyncs with message event listener', function() { + function messageHandler(event) { + if (!event.data || event.origin !== THIRD_PARTY_COOKIE_ORIGIN) { + return; + } + + window.removeEventListener('message', messageHandler, true); + event.stopImmediatePropagation(); + + const response = event.data; + if (!response.optout && response.mguid) { + storage.setCookie(COOKIE_KEY_MGUID, response.mguid, getCurrentTimeToUTCString()); + } + } + + let sandbox; + + beforeEach(() => { + sandbox = sinon.createSandbox(); + sandbox.stub(storage, 'setCookie'); + sandbox.stub(window, 'removeEventListener'); + }); + + afterEach(() => { + sandbox.restore(); + }); + + it('should set a cookie when a valid message is received', () => { + const fakeEvent = { + data: { optout: '', mguid: '12345' }, + origin: THIRD_PARTY_COOKIE_ORIGIN, + stopImmediatePropagation: sinon.spy() + }; + + messageHandler(fakeEvent); + + expect(fakeEvent.stopImmediatePropagation.calledOnce).to.be.true; + expect(window.removeEventListener.calledWith('message', messageHandler, true)).to.be.true; + expect(storage.setCookie.calledWith(COOKIE_KEY_MGUID, '12345', sinon.match.string)).to.be.true; + }); + it('should not do anything when an invalid message is received', () => { + const fakeEvent = { + data: null, + origin: 'http://invalid-origin.com', + stopImmediatePropagation: sinon.spy() + }; + + messageHandler(fakeEvent); + + expect(fakeEvent.stopImmediatePropagation.notCalled).to.be.true; + expect(window.removeEventListener.notCalled).to.be.true; + expect(storage.setCookie.notCalled).to.be.true; + }); + }); + }); }); From 379b687f49d37a79850aa5e5bc9251950d2e2219 Mon Sep 17 00:00:00 2001 From: ecoeco163 <147788250+ecoeco163@users.noreply.github.com> Date: Fri, 26 Jan 2024 04:37:07 +0800 Subject: [PATCH 173/246] Discovery Bid Adapter : not filter params (#10965) * feat(isBidRequestValid): just filter token once. not filter publisher and tagid * feat(isBidRequestValid): add unit test * feat(spec): fix eslint * feat(spec): fix unit test * feat(spec): fix unit test --------- Co-authored-by: yubei01 --- modules/discoveryBidAdapter.js | 4 +- test/spec/modules/discoveryBidAdapter_spec.js | 80 +++++++++++++++++++ 2 files changed, 83 insertions(+), 1 deletion(-) diff --git a/modules/discoveryBidAdapter.js b/modules/discoveryBidAdapter.js index 551c78c1e24..1576a8dcb08 100644 --- a/modules/discoveryBidAdapter.js +++ b/modules/discoveryBidAdapter.js @@ -511,7 +511,7 @@ export const spec = { if (bid.params.badv) { globals['badv'] = Array.isArray(bid.params.badv) ? bid.params.badv : []; } - return !!(bid.params.token && bid.params.publisher && bid.params.tagid); + return true; }, /** @@ -522,6 +522,8 @@ export const spec = { * @return ServerRequest Info describing the request to the server. */ buildRequests: function (validBidRequests, bidderRequest) { + if (!globals['token']) return; + let payload = getParam(validBidRequests, bidderRequest); const payloadString = JSON.stringify(payload); diff --git a/test/spec/modules/discoveryBidAdapter_spec.js b/test/spec/modules/discoveryBidAdapter_spec.js index 05216ff126c..65fbf33022f 100644 --- a/test/spec/modules/discoveryBidAdapter_spec.js +++ b/test/spec/modules/discoveryBidAdapter_spec.js @@ -80,6 +80,77 @@ describe('discovery:BidAdapterTests', function () { }; let request = []; + let bidRequestDataNoParams = { + bidderCode: 'discovery', + auctionId: 'ff66e39e-4075-4d18-9854-56fde9b879ac', + bidderRequestId: '4fec04e87ad785', + bids: [ + { + bidder: 'discovery', + params: { + referrer: 'https://discovery.popin.cc', + }, + refererInfo: { + page: 'https://discovery.popin.cc', + stack: [ + 'a.com', + 'b.com' + ] + }, + mediaTypes: { + banner: { + sizes: [[300, 250]], + pos: 'left', + }, + }, + ortb2: { + user: { + ext: { + data: { + CxSegments: [] + } + } + }, + site: { + domain: 'discovery.popin.cc', + publisher: { + domain: 'discovery.popin.cc' + }, + page: 'https://discovery.popin.cc', + cat: ['IAB-19', 'IAB-20'], + }, + }, + ortb2Imp: { + ext: { + gpid: 'adslot_gpid', + tid: 'tid_01', + data: { + browsi: { + browsiViewability: 'NA', + }, + adserver: { + name: 'adserver_name', + adslot: 'adslot_name', + }, + keywords: ['travel', 'sport'], + pbadslot: '202309999' + } + } + }, + adUnitCode: 'regular_iframe', + transactionId: 'd163f9e2-7ecd-4c2c-a3bd-28ceb52a60ee', + sizes: [[300, 250]], + bidId: '276092a19e05eb', + bidderRequestId: '1fadae168708b', + auctionId: 'ff66e39e-4075-4d18-9854-56fde9b879ac', + src: 'client', + bidRequestsCount: 1, + bidderRequestsCount: 1, + bidderWinsCount: 0, + }, + ], + }; + it('discovery:validate_pub_params', function () { expect( spec.isBidRequestValid({ @@ -93,6 +164,15 @@ describe('discovery:BidAdapterTests', function () { ).to.equal(true); }); + it('isBidRequestValid:no_params', function () { + expect( + spec.isBidRequestValid({ + bidder: 'discovery', + params: {}, + }) + ).to.equal(true); + }); + it('discovery:validate_generated_params', function () { request = spec.buildRequests(bidRequestData.bids, bidRequestData); let req_data = JSON.parse(request.data); From 4cd9d5009dc72bcd21fe174c723b0ec49fbeaa9f Mon Sep 17 00:00:00 2001 From: jsnellbaker <31102355+jsnellbaker@users.noreply.github.com> Date: Fri, 26 Jan 2024 07:13:04 -0500 Subject: [PATCH 174/246] appnexus Bid Adapter - DSA support (#10971) --- modules/appnexusBidAdapter.js | 34 ++++++++++++ test/spec/modules/appnexusBidAdapter_spec.js | 58 ++++++++++++++++++++ 2 files changed, 92 insertions(+) diff --git a/modules/appnexusBidAdapter.js b/modules/appnexusBidAdapter.js index c6230d9f1e4..6a0b088795f 100644 --- a/modules/appnexusBidAdapter.js +++ b/modules/appnexusBidAdapter.js @@ -346,6 +346,36 @@ export const spec = { } } + if (bidderRequest?.ortb2?.regs?.ext?.dsa) { + const pubDsaObj = bidderRequest.ortb2.regs.ext.dsa; + const dsaObj = {}; + ['required', 'pubrender', 'datatopub'].forEach((dsaKey) => { + if (isNumber(pubDsaObj[dsaKey])) { + if (dsaKey === 'required') { + // our client-side endpoint has a different name for the 'required' field + // ...this is the only exception to the openrtb spec for these fields + dsaObj.dsainfo = pubDsaObj[dsaKey]; + } else { + dsaObj[dsaKey] = pubDsaObj[dsaKey]; + } + } + }); + + if (isArray(pubDsaObj.transparency) && pubDsaObj.transparency.every((v) => isPlainObject(v))) { + const tpData = []; + pubDsaObj.transparency.forEach((tpObj) => { + if (isStr(tpObj.domain) && tpObj.domain != '' && isArray(tpObj.params) && tpObj.params.every((v) => isNumber(v))) { + tpData.push(tpObj); + } + }); + if (tpData.length > 0) { + dsaObj.transparency = tpData; + } + } + + if (!isEmpty(dsaObj)) payload.dsa = dsaObj; + } + if (tags[0].publisher_id) { payload.publisher_id = tags[0].publisher_id; } @@ -585,6 +615,10 @@ function newBid(serverBid, rtbBid, bidderRequest) { bid.meta = Object.assign({}, bid.meta, { advertiserId: rtbBid.advertiser_id }); } + if (rtbBid.dsa) { + bid.meta = Object.assign({}, bid.meta, { dsa: rtbBid.dsa }); + } + // temporary function; may remove at later date if/when adserver fully supports dchain function setupDChain(rtbBid) { let dchain = { diff --git a/test/spec/modules/appnexusBidAdapter_spec.js b/test/spec/modules/appnexusBidAdapter_spec.js index 193e8aa64f8..c44ef475290 100644 --- a/test/spec/modules/appnexusBidAdapter_spec.js +++ b/test/spec/modules/appnexusBidAdapter_spec.js @@ -1204,6 +1204,46 @@ describe('AppNexusAdapter', function () { expect(payload.privacy.gpp_sid).to.deep.equal([7]); }); + it('should add dsa information to the request via bidderRequest.ortb2.regs.ext.dsa', function () { + let bidderRequest = { + 'bidderCode': 'appnexus', + 'auctionId': '1d1a030790a475', + 'bidderRequestId': '22edbae2733bf6', + 'timeout': 3000, + 'ortb2': { + 'regs': { + 'ext': { + 'dsa': { + 'required': 1, + 'pubrender': 0, + 'datatopub': 1, + 'transparency': [{ + 'domain': 'good-domain', + 'params': [1, 2] + }, { + 'domain': 'bad-setup', + 'params': ['1', 3] + }] + } + } + } + } + }; + bidderRequest.bids = bidRequests; + + const request = spec.buildRequests(bidRequests, bidderRequest); + const payload = JSON.parse(request.data); + + expect(payload.dsa).to.exist; + expect(payload.dsa.dsainfo).to.equal(1); + expect(payload.dsa.pubrender).to.equal(0); + expect(payload.dsa.datatopub).to.equal(1); + expect(payload.dsa.transparency).to.deep.equal([{ + 'domain': 'good-domain', + 'params': [1, 2] + }]); + }); + it('supports sending hybrid mobile app parameters', function () { let appRequest = Object.assign({}, bidRequests[0], @@ -1575,6 +1615,15 @@ describe('AppNexusAdapter', function () { 'viewability': { 'config': '' }, + 'dsa': { + 'behalf': 'test-behalf', + 'paid': 'test-paid', + 'transparency': [{ + 'domain': 'good-domain', + 'params': [1, 2, 3] + }], + 'adrender': 1 + }, 'rtb': { 'banner': { 'content': '', @@ -1623,6 +1672,15 @@ describe('AppNexusAdapter', function () { 'nodes': [{ 'bsid': '958' }] + }, + 'dsa': { + 'behalf': 'test-behalf', + 'paid': 'test-paid', + 'transparency': [{ + 'domain': 'good-domain', + 'params': [1, 2, 3] + }], + 'adrender': 1 } } } From 73310f170975cbb61f82920e35e8262a981d0566 Mon Sep 17 00:00:00 2001 From: Malkov Mikhail Date: Fri, 26 Jan 2024 15:21:25 +0300 Subject: [PATCH 175/246] NextMillennium Bid Adapter : currency support in request (#10871) * added support for gpp consent string * changed test for nextMillenniumBidAdapter * added some tests * added site.pagecat, site.content.cat and site.content.language to request * lint fix * formated code * formated code * formated code * pachage-lock with prebid * pachage-lock with prebid * formatted code * added device.sua, user.eids * formatted * fixed tests * fixed bug functio getSua * currency * currency --- modules/nextMillenniumBidAdapter.js | 55 ++++++++++++++++--- .../modules/nextMillenniumBidAdapter_spec.js | 22 +++++++- 2 files changed, 66 insertions(+), 11 deletions(-) diff --git a/modules/nextMillenniumBidAdapter.js b/modules/nextMillenniumBidAdapter.js index 5dea4f9b651..0af2129de61 100644 --- a/modules/nextMillenniumBidAdapter.js +++ b/modules/nextMillenniumBidAdapter.js @@ -31,6 +31,7 @@ const TEST_ENDPOINT = 'https://test.pbs.nextmillmedia.com/openrtb2/auction'; const SYNC_ENDPOINT = 'https://cookies.nextmillmedia.com/sync?gdpr={{.GDPR}}&gdpr_consent={{.GDPRConsent}}&us_privacy={{.USPrivacy}}&gpp={{.GPP}}&gpp_sid={{.GPPSID}}&type={{.TYPE_PIXEL}}'; const REPORT_ENDPOINT = 'https://report2.hb.brainlyads.com/statistics/metric'; const TIME_TO_LIVE = 360; +const DEFAULT_CURRENCY = 'USD'; const VIDEO_PARAMS = [ 'api', @@ -83,9 +84,11 @@ export const spec = { const site = getSiteObj(); const device = getDeviceObj(); + const {cur, mediaTypes} = getCurrency(bid); const postBody = { id: bidderRequest?.bidderRequestId, + cur, ext: { prebid: { storedrequest: { @@ -107,7 +110,7 @@ export const spec = { imp: [], }; - postBody.imp.push(getImp(bid, id)); + postBody.imp.push(getImp(bid, id, mediaTypes)); setConsentStrings(postBody, bidderRequest); setOrtb2Parameters(postBody, bidderRequest?.ortb2); setEids(postBody, bid); @@ -157,7 +160,7 @@ export const spec = { width: bid.w, height: bid.h, creativeId: bid.adid, - currency: response.cur, + currency: response.cur || DEFAULT_CURRENCY, netRevenue: true, ttl: TIME_TO_LIVE, meta: { @@ -244,7 +247,8 @@ export const spec = { }, }; -export function getImp(bid, id) { +export function getImp(bid, id, mediaTypes) { + const {banner, video} = mediaTypes; const imp = { id: bid.adUnitCode, ext: { @@ -256,18 +260,22 @@ export function getImp(bid, id) { }, }; - const banner = deepAccess(bid, 'mediaTypes.banner'); if (banner) { + if (banner.bidfloorcur) imp.bidfloorcur = banner.bidfloorcur; + if (banner.bidfloor) imp.bidfloor = banner.bidfloor; + imp.banner = { - format: (banner?.sizes || []).map(s => { return {w: s[0], h: s[1]} }), + format: (banner.data?.sizes || []).map(s => { return {w: s[0], h: s[1]} }), }; }; - const video = deepAccess(bid, 'mediaTypes.video'); if (video) { + if (video.bidfloorcur) imp.bidfloorcur = video.bidfloorcur; + if (video.bidfloor) imp.bidfloor = video.bidfloor; + imp.video = getDefinedParams(video, VIDEO_PARAMS); - if (video.playerSize) { - imp.video = Object.assign(imp.video, parseGPTSingleSizeArrayToRtbSize(video.playerSize) || {}); + if (video.data.playerSize) { + imp.video = Object.assign(imp.video, parseGPTSingleSizeArrayToRtbSize(video.data.playerSize) || {}); } else if (video.w && video.h) { imp.video.w = video.w; imp.video.h = video.h; @@ -334,7 +342,36 @@ export function replaceUsersyncMacros(url, gdprConsent = {}, uspConsent = '', gp .replace('{{.TYPE_PIXEL}}', type); return url; -}; +} + +function getCurrency(bid = {}) { + const currency = config?.getConfig('currency')?.adServerCurrency || DEFAULT_CURRENCY; + const cur = []; + const types = ['banner', 'video']; + const mediaTypes = {}; + for (const mediaType of types) { + const mediaTypeData = deepAccess(bid, `mediaTypes.${mediaType}`); + if (mediaTypeData) { + mediaTypes[mediaType] = {data: mediaTypeData}; + } else { + continue; + }; + + if (typeof bid.getFloor === 'function') { + let floorInfo = bid.getFloor({currency, mediaType, size: '*'}); + mediaTypes[mediaType].bidfloorcur = floorInfo.currency; + mediaTypes[mediaType].bidfloor = floorInfo.floor; + } else { + mediaTypes[mediaType].bidfloorcur = currency; + }; + + if (cur.includes(mediaTypes[mediaType].bidfloorcur)) cur.push(mediaTypes[mediaType].bidfloorcur); + }; + + if (!cur.length) cur.push(DEFAULT_CURRENCY); + + return {cur, mediaTypes}; +} function getAdEl(bid) { // best way I could think of to get El, is by matching adUnitCode to google slots... diff --git a/test/spec/modules/nextMillenniumBidAdapter_spec.js b/test/spec/modules/nextMillenniumBidAdapter_spec.js index f57db82aedc..169ab7b01e4 100644 --- a/test/spec/modules/nextMillenniumBidAdapter_spec.js +++ b/test/spec/modules/nextMillenniumBidAdapter_spec.js @@ -19,10 +19,20 @@ describe('nextMillenniumBidAdapterTests', () => { mediaTypes: {banner: {sizes: [[300, 250], [320, 250]]}}, adUnitCode: 'test-banner-1', }, + + mediaTypes: { + banner: { + data: {sizes: [[300, 250], [320, 250]]}, + bidfloorcur: 'EUR', + bidfloor: 1.11, + }, + }, }, expected: { id: 'test-banner-1', + bidfloorcur: 'EUR', + bidfloor: 1.11, ext: {prebid: {storedrequest: {id: '123'}}}, banner: {format: [{w: 300, h: 250}, {w: 320, h: 250}]}, }, @@ -36,10 +46,18 @@ describe('nextMillenniumBidAdapterTests', () => { mediaTypes: {video: {playerSize: [400, 300]}}, adUnitCode: 'test-video-1', }, + + mediaTypes: { + video: { + data: {playerSize: [400, 300]}, + bidfloorcur: 'USD', + }, + }, }, expected: { id: 'test-video-1', + bidfloorcur: 'USD', ext: {prebid: {storedrequest: {id: '234'}}}, video: {w: 400, h: 300}, }, @@ -48,8 +66,8 @@ describe('nextMillenniumBidAdapterTests', () => { for (let {title, data, expected} of dataTests) { it(title, () => { - const {bid, id} = data; - const imp = getImp(bid, id); + const {bid, id, mediaTypes} = data; + const imp = getImp(bid, id, mediaTypes); expect(imp).to.deep.equal(expected); }); } From f78996d5de015ec11ae1444b32e485f4abf1ba97 Mon Sep 17 00:00:00 2001 From: Demetrio Girardi Date: Fri, 26 Jan 2024 04:46:55 -0800 Subject: [PATCH 176/246] Core & PBS adapter: fix race condition between network timeout and auction timeout (#10779) * Core: fix race condition between fetch timeout and auction timeout * PBS adapter: fix race condition between ajax timeout and auction timeout --- modules/prebidServerBidAdapter/index.js | 4 +- src/adapterManager.js | 8 +-- src/adapters/bidderFactory.js | 4 +- src/ajax.js | 8 ++- .../modules/prebidServerBidAdapter_spec.js | 26 +++++++++ test/spec/unit/core/adapterManager_spec.js | 53 ++++++++++++------- test/spec/unit/core/ajax_spec.js | 24 +++++++-- test/spec/unit/core/bidderFactory_spec.js | 39 +++++++++++--- 8 files changed, 127 insertions(+), 39 deletions(-) diff --git a/modules/prebidServerBidAdapter/index.js b/modules/prebidServerBidAdapter/index.js index 5ae9a9fc27a..6e4aec8ad92 100644 --- a/modules/prebidServerBidAdapter/index.js +++ b/modules/prebidServerBidAdapter/index.js @@ -478,13 +478,13 @@ export function PrebidServer() { adapterMetrics }); } - done(); + done(false); doClientSideSyncs(requestedBidders, gdprConsent, uspConsent, gppConsent); }, onError(msg, error) { logError(`Prebid server call failed: '${msg}'`, error); bidRequests.forEach(bidderRequest => events.emit(CONSTANTS.EVENTS.BIDDER_ERROR, {error, bidderRequest})); - done(); + done(error.timedOut); }, onBid: function ({adUnit, bid}) { const metrics = bid.metrics = s2sBidRequest.metrics.fork().renameWith(); diff --git a/src/adapterManager.js b/src/adapterManager.js index 575d28b35fa..189fbf31f2e 100644 --- a/src/adapterManager.js +++ b/src/adapterManager.js @@ -413,8 +413,10 @@ adapterManager.callBids = (adUnits, bidRequests, addBidResponse, doneCb, request if (s2sBidRequest.ad_units.length) { let doneCbs = uniqueServerRequests.map(bidRequest => { bidRequest.start = timestamp(); - return function () { - onTimelyResponse(bidRequest.bidderRequestId); + return function (timedOut) { + if (!timedOut) { + onTimelyResponse(bidRequest.bidderRequestId); + } doneCb.apply(bidRequest, arguments); } }); @@ -433,7 +435,7 @@ adapterManager.callBids = (adUnits, bidRequests, addBidResponse, doneCb, request s2sBidRequest, serverBidderRequests, addBidResponse, - () => doneCbs.forEach(done => done()), + (timedOut) => doneCbs.forEach(done => done(timedOut)), s2sAjax ); } diff --git a/src/adapters/bidderFactory.js b/src/adapters/bidderFactory.js index df7b2fb0eea..3d55f2c06af 100644 --- a/src/adapters/bidderFactory.js +++ b/src/adapters/bidderFactory.js @@ -305,7 +305,9 @@ export function newBidder(spec) { }, // If the server responds with an error, there's not much we can do beside logging. onError: (errorMessage, error) => { - onTimelyResponse(spec.code); + if (!error.timedOut) { + onTimelyResponse(spec.code); + } adapterManager.callBidderError(spec.code, error, bidderRequest) events.emit(CONSTANTS.EVENTS.BIDDER_ERROR, { error, bidderRequest }); logError(`Server call for ${spec.code} failed: ${errorMessage} ${error.status}. Continuing without bids.`); diff --git a/src/ajax.js b/src/ajax.js index 6f2f45e935e..ef4c2e4bcb4 100644 --- a/src/ajax.js +++ b/src/ajax.js @@ -114,7 +114,8 @@ function toXHR({status, statusText = '', headers, url}, responseText) { getResponseHeader: (header) => headers?.has(header) ? headers.get(header) : null, toJSON() { return Object.assign({responseXML: getXML()}, this) - } + }, + timedOut: false } } @@ -130,7 +131,10 @@ export function attachCallbacks(fetchPm, callback) { .then(([response, responseText]) => { const xhr = toXHR(response, responseText); response.ok || response.status === 304 ? success(responseText, xhr) : error(response.statusText, xhr); - }, () => error('', toXHR({status: 0}, ''))); + }, (reason) => error('', Object.assign( + toXHR({status: 0}, ''), + {reason, timedOut: reason?.name === 'AbortError'})) + ); } export function ajaxBuilder(timeout = 3000, {request, done} = {}) { diff --git a/test/spec/modules/prebidServerBidAdapter_spec.js b/test/spec/modules/prebidServerBidAdapter_spec.js index 5b591804b95..d88d4411e33 100644 --- a/test/spec/modules/prebidServerBidAdapter_spec.js +++ b/test/spec/modules/prebidServerBidAdapter_spec.js @@ -2882,6 +2882,32 @@ describe('S2S Adapter', function () { }) }) + describe('calls done', () => { + let success, error; + beforeEach(() => { + const mockAjax = function (_, callback) { + ({success, error} = callback); + } + config.setConfig({ s2sConfig: CONFIG }); + adapter.callBids(REQUEST, BID_REQUESTS, addBidResponse, done, mockAjax); + }) + + it('passing timedOut = false on succcess', () => { + success({}); + sinon.assert.calledWith(done, false); + }); + + Object.entries({ + 'timeouts': true, + 'other errors': false + }).forEach(([t, timedOut]) => { + it(`passing timedOut = ${timedOut} on ${t}`, () => { + error('', {timedOut}); + sinon.assert.calledWith(done, timedOut); + }) + }) + }) + // TODO: test dependent on pbjs_api_spec. Needs to be isolated it('does not call addBidResponse and calls done when ad unit not set', function () { config.setConfig({ s2sConfig: CONFIG }); diff --git a/test/spec/unit/core/adapterManager_spec.js b/test/spec/unit/core/adapterManager_spec.js index 98d841d9c7c..dac70696b4b 100644 --- a/test/spec/unit/core/adapterManager_spec.js +++ b/test/spec/unit/core/adapterManager_spec.js @@ -964,25 +964,42 @@ describe('adapterManager tests', function () { 'start': 1462918897460 }]; - it('invokes callBids on the S2S adapter', function () { - const done = sinon.stub(); - const onTimelyResponse = sinon.stub(); - prebidServerAdapterMock.callBids.callsFake((_1, _2, _3, done) => { - done(); + describe('invokes callBids on the S2S adapter', () => { + let onTimelyResponse, timedOut, done; + beforeEach(() => { + done = sinon.stub(); + onTimelyResponse = sinon.stub(); + prebidServerAdapterMock.callBids.callsFake((_1, _2, _3, done) => { + done(timedOut); + }); + }) + + function runTest() { + adapterManager.callBids( + getAdUnits(), + bidRequests, + () => {}, + done, + undefined, + undefined, + onTimelyResponse + ); + sinon.assert.calledTwice(prebidServerAdapterMock.callBids); + sinon.assert.calledTwice(done); + } + + it('and marks requests as timely if the adapter says timedOut = false', function () { + timedOut = false; + runTest(); + bidRequests.forEach(br => sinon.assert.calledWith(onTimelyResponse, br.bidderRequestId)); }); - adapterManager.callBids( - getAdUnits(), - bidRequests, - () => {}, - done, - undefined, - undefined, - onTimelyResponse - ); - sinon.assert.calledTwice(prebidServerAdapterMock.callBids); - sinon.assert.calledTwice(done); - bidRequests.forEach(br => sinon.assert.calledWith(onTimelyResponse, br.bidderRequestId)); - }); + + it('and does NOT mark them as timely if it says timedOut = true', () => { + timedOut = true; + runTest(); + sinon.assert.notCalled(onTimelyResponse); + }) + }) // Enable this test when prebidServer adapter is made 1.0 compliant it('invokes callBids with only s2s bids', function () { diff --git a/test/spec/unit/core/ajax_spec.js b/test/spec/unit/core/ajax_spec.js index a3a0459b980..dd03ad1a761 100644 --- a/test/spec/unit/core/ajax_spec.js +++ b/test/spec/unit/core/ajax_spec.js @@ -232,7 +232,7 @@ describe('attachCallbacks', () => { }; } - function expectNullXHR(response) { + function expectNullXHR(response, reason) { return new Promise((resolve, reject) => { attachCallbacks(Promise.resolve(response), { success: () => { @@ -246,7 +246,8 @@ describe('attachCallbacks', () => { statusText: '', responseText: '', response: '', - responseXML: null + responseXML: null, + reason }); expect(xhr.getResponseHeader('any')).to.be.null; resolve(); @@ -256,9 +257,21 @@ describe('attachCallbacks', () => { } it('runs error callback on rejections', () => { - return expectNullXHR(Promise.reject(new Error())); + const err = new Error(); + return expectNullXHR(Promise.reject(err), err); }); + it('sets timedOut = true on fetch timeout', (done) => { + const ctl = new AbortController(); + ctl.abort(); + attachCallbacks(fetch('/', {signal: ctl.signal}), { + error(_, xhr) { + expect(xhr.timedOut).to.be.true; + done(); + } + }); + }) + Object.entries({ '2xx response': { success: true, @@ -368,8 +381,9 @@ describe('attachCallbacks', () => { }); it(`runs error callback if body cannot be retrieved`, () => { - response.text = () => Promise.reject(new Error()); - return expectNullXHR(response); + const err = new Error(); + response.text = () => Promise.reject(err); + return expectNullXHR(response, err); }); if (success) { diff --git a/test/spec/unit/core/bidderFactory_spec.js b/test/spec/unit/core/bidderFactory_spec.js index 9dcdb627698..16042ca52dd 100644 --- a/test/spec/unit/core/bidderFactory_spec.js +++ b/test/spec/unit/core/bidderFactory_spec.js @@ -38,10 +38,6 @@ const MOCK_BIDS_REQUEST = { ] } -function onTimelyResponseStub() { - -} - before(() => { hook.ready(); }); @@ -49,6 +45,10 @@ before(() => { let wrappedCallback = config.callbackWithBidder(CODE); describe('bidderFactory', () => { + let onTimelyResponseStub; + beforeEach(() => { + onTimelyResponseStub = sinon.stub(); + }) describe('bidders created by newBidder', function () { let spec; let bidder; @@ -582,6 +582,14 @@ describe('bidderFactory', () => { utils.logError.restore(); }); + it('should call onTimelyResponse', () => { + const bidder = newBidder(spec); + spec.isBidRequestValid.returns(true); + spec.buildRequests.returns({method: 'POST', url: 'test', data: {}}); + bidder.callBids(MOCK_BIDS_REQUEST, addBidResponseStub, doneStub, ajaxStub, onTimelyResponseStub, wrappedCallback); + sinon.assert.called(onTimelyResponseStub); + }) + it('should call spec.interpretResponse() with the response content', function () { const bidder = newBidder(spec); @@ -800,12 +808,13 @@ describe('bidderFactory', () => { let ajaxStub; let callBidderErrorStub; let eventEmitterStub; - let xhrErrorMock = { - status: 500, - statusText: 'Internal Server Error' - }; + let xhrErrorMock; beforeEach(function () { + xhrErrorMock = { + status: 500, + statusText: 'Internal Server Error' + }; ajaxStub = sinon.stub(ajax, 'ajax').callsFake(function(url, callbacks) { callbacks.error('ajax call failed.', xhrErrorMock); }); @@ -821,6 +830,20 @@ describe('bidderFactory', () => { eventEmitterStub.restore(); }); + Object.entries({ + 'timeouts': true, + 'other errors': false + }).forEach(([t, timedOut]) => { + it(`should ${timedOut ? 'NOT ' : ''}call onTimelyResponse on ${t}`, () => { + Object.assign(xhrErrorMock, {timedOut}); + const bidder = newBidder(spec); + spec.isBidRequestValid.returns(true); + spec.buildRequests.returns({method: 'POST', url: 'test', data: {}}); + bidder.callBids(MOCK_BIDS_REQUEST, addBidResponseStub, doneStub, ajaxStub, onTimelyResponseStub, wrappedCallback); + sinon.assert[timedOut ? 'notCalled' : 'called'](onTimelyResponseStub); + }) + }) + it('should not spec.interpretResponse()', function () { const bidder = newBidder(spec); From 3ab9a57749f172df25c40294e08e13315bc24b99 Mon Sep 17 00:00:00 2001 From: "Prebid.js automated release" Date: Fri, 26 Jan 2024 14:00:28 +0000 Subject: [PATCH 177/246] Prebid 8.33.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 bac5bc0c929..d575b074b0d 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,6 +1,6 @@ { "name": "prebid.js", - "version": "8.33.0-pre", + "version": "8.33.0", "lockfileVersion": 2, "requires": true, "packages": { diff --git a/package.json b/package.json index 70b0bd4a144..8694938e05e 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "prebid.js", - "version": "8.33.0-pre", + "version": "8.33.0", "description": "Header Bidding Management Library", "main": "src/prebid.js", "scripts": { From 904359e1542a08c4b436e9007cec1b9fdb39389d Mon Sep 17 00:00:00 2001 From: "Prebid.js automated release" Date: Fri, 26 Jan 2024 14:00:28 +0000 Subject: [PATCH 178/246] Increment version to 8.34.0-pre --- package-lock.json | 2 +- package.json | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/package-lock.json b/package-lock.json index d575b074b0d..87110627d26 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,6 +1,6 @@ { "name": "prebid.js", - "version": "8.33.0", + "version": "8.34.0-pre", "lockfileVersion": 2, "requires": true, "packages": { diff --git a/package.json b/package.json index 8694938e05e..38697197d0b 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "prebid.js", - "version": "8.33.0", + "version": "8.34.0-pre", "description": "Header Bidding Management Library", "main": "src/prebid.js", "scripts": { From 0dc3d5972d4239814aae2b256d1fdbdfca858c9f Mon Sep 17 00:00:00 2001 From: tudou <42998776+lhxx121@users.noreply.github.com> Date: Sat, 27 Jan 2024 01:05:26 +0800 Subject: [PATCH 179/246] Discovery Bid Adapter : synchronize mguid from third party cookie to first party cookie (#10927) * Discovery Bid Adapter : add title, desc, keywords, hLen, nbw, hc, dm add unit test resolve conflict * Discovery Bid Adapter : add title, desc, keywords, hLen, nbw, hc, dm add unit test * Discovery Bid Adapter : synchronize mguid from third party cookie to first party cookie --------- Co-authored-by: lvhuixin --- modules/discoveryBidAdapter.js | 31 +++++++-- test/spec/modules/discoveryBidAdapter_spec.js | 68 ++++++++++++++++++- 2 files changed, 94 insertions(+), 5 deletions(-) diff --git a/modules/discoveryBidAdapter.js b/modules/discoveryBidAdapter.js index 1576a8dcb08..b9ba6087d30 100644 --- a/modules/discoveryBidAdapter.js +++ b/modules/discoveryBidAdapter.js @@ -13,10 +13,11 @@ const MEDIATYPE = [BANNER, NATIVE]; /* ----- _ss_pp_id:start ------ */ const COOKIE_KEY_SSPPID = '_ss_pp_id'; -const COOKIE_KEY_MGUID = '__mguid_'; +export const COOKIE_KEY_MGUID = '__mguid_'; const COOKIE_KEY_PMGUID = '__pmguid_'; const COOKIE_RETENTION_TIME = 365 * 24 * 60 * 60 * 1000; // 1 year const COOKY_SYNC_IFRAME_URL = 'https://asset.popin.cc/js/cookieSync.html'; +export const THIRD_PARTY_COOKIE_ORIGIN = 'https://asset.popin.cc'; const NATIVERET = { id: 'id', @@ -126,10 +127,8 @@ export const getPmgUID = () => { let pmgUid = storage.getCookie(COOKIE_KEY_PMGUID); if (!pmgUid) { pmgUid = utils.generateUUID(); - const date = new Date(); - date.setTime(date.getTime() + COOKIE_RETENTION_TIME); try { - storage.setCookie(COOKIE_KEY_PMGUID, pmgUid, date.toUTCString()); + storage.setCookie(COOKIE_KEY_PMGUID, pmgUid, getCurrentTimeToUTCString()); } catch (e) {} } return pmgUid; @@ -295,6 +294,16 @@ function getReferrer(bidRequest = {}, bidderRequest = {}) { return pageUrl; } +/** + * get current time to UTC string + * @returns utc string + */ +export function getCurrentTimeToUTCString() { + const date = new Date(); + date.setTime(date.getTime() + COOKIE_RETENTION_TIME); + return date.toUTCString(); +} + /** * format imp ad test ext params * @@ -642,6 +651,20 @@ export const spec = { } if (syncOptions.iframeEnabled) { + window.addEventListener('message', function handler(event) { + if (!event.data || event.origin != THIRD_PARTY_COOKIE_ORIGIN) { + return; + } + + this.removeEventListener('message', handler); + + event.stopImmediatePropagation(); + + const response = event.data; + if (!response.optout && response.mguid) { + storage.setCookie(COOKIE_KEY_MGUID, response.mguid, getCurrentTimeToUTCString()); + } + }, true); return [ { type: 'iframe', diff --git a/test/spec/modules/discoveryBidAdapter_spec.js b/test/spec/modules/discoveryBidAdapter_spec.js index 65fbf33022f..961ccb33c4f 100644 --- a/test/spec/modules/discoveryBidAdapter_spec.js +++ b/test/spec/modules/discoveryBidAdapter_spec.js @@ -1,5 +1,16 @@ import { expect } from 'chai'; -import { spec, getPmgUID, storage, getPageTitle, getPageDescription, getPageKeywords, getConnectionDownLink } from 'modules/discoveryBidAdapter.js'; +import { + spec, + getPmgUID, + storage, + getPageTitle, + getPageDescription, + getPageKeywords, + getConnectionDownLink, + THIRD_PARTY_COOKIE_ORIGIN, + COOKIE_KEY_MGUID, + getCurrentTimeToUTCString +} from 'modules/discoveryBidAdapter.js'; import * as utils from 'src/utils.js'; describe('discovery:BidAdapterTests', function () { @@ -523,5 +534,60 @@ describe('discovery Bid Adapter Tests', function () { expect(result).to.be.undefined; }); }); + + describe('getUserSyncs with message event listener', function() { + function messageHandler(event) { + if (!event.data || event.origin !== THIRD_PARTY_COOKIE_ORIGIN) { + return; + } + + window.removeEventListener('message', messageHandler, true); + event.stopImmediatePropagation(); + + const response = event.data; + if (!response.optout && response.mguid) { + storage.setCookie(COOKIE_KEY_MGUID, response.mguid, getCurrentTimeToUTCString()); + } + } + + let sandbox; + + beforeEach(() => { + sandbox = sinon.createSandbox(); + sandbox.stub(storage, 'setCookie'); + sandbox.stub(window, 'removeEventListener'); + }); + + afterEach(() => { + sandbox.restore(); + }); + + it('should set a cookie when a valid message is received', () => { + const fakeEvent = { + data: { optout: '', mguid: '12345' }, + origin: THIRD_PARTY_COOKIE_ORIGIN, + stopImmediatePropagation: sinon.spy() + }; + + messageHandler(fakeEvent); + + expect(fakeEvent.stopImmediatePropagation.calledOnce).to.be.true; + expect(window.removeEventListener.calledWith('message', messageHandler, true)).to.be.true; + expect(storage.setCookie.calledWith(COOKIE_KEY_MGUID, '12345', sinon.match.string)).to.be.true; + }); + it('should not do anything when an invalid message is received', () => { + const fakeEvent = { + data: null, + origin: 'http://invalid-origin.com', + stopImmediatePropagation: sinon.spy() + }; + + messageHandler(fakeEvent); + + expect(fakeEvent.stopImmediatePropagation.notCalled).to.be.true; + expect(window.removeEventListener.notCalled).to.be.true; + expect(storage.setCookie.notCalled).to.be.true; + }); + }); }); }); From e37ba4e70fed642b3f57121566073b6e11f23fd4 Mon Sep 17 00:00:00 2001 From: Chris Huie Date: Fri, 26 Jan 2024 13:52:00 -0700 Subject: [PATCH 180/246] JSdoc Lint Fixes for Multiple Adapters (#10967) * fix native * update ablida adgeneration * fix adkernel admatic * update type imports * js lint fixes * jslint appnexus * jslint jsdoc fixes * fix jsdoc warnings * lint fixes * lint fix * jsdoc updates * Update adgenerationBidAdapter.js * fix js doc * jsdoc type imports * jsdoc type updates * add types for jsdoc * jsdoc type updates * add types * jsdoc types added * import types * jsdoc updates * jsdoc type imports * type jsdoc import fixes * jsdoc type imports * jsdoc type import fix * lint fixes * fix jsdoc types * type imports * lint fixes jsdoc * jsdoc fixes * type imports for jsdoc * jsdoc type fixes * fxes for jsdoc types * jsdoc fixes --- modules/ablidaBidAdapter.js | 6 ++++++ modules/adgenerationBidAdapter.js | 8 ++++++++ modules/adkernelBidAdapter.js | 8 ++++++-- modules/admaticBidAdapter.js | 7 +++++++ modules/admediaBidAdapter.js | 6 ++++++ modules/adnowBidAdapter.js | 4 ++++ modules/adqueryBidAdapter.js | 8 ++++++++ modules/adrelevantisBidAdapter.js | 5 +++++ modules/adtelligentBidAdapter.js | 4 ++++ modules/aduptechBidAdapter.js | 5 +++++ modules/adyoulikeBidAdapter.js | 5 +++++ modules/ajaBidAdapter.js | 5 +++++ modules/appierBidAdapter.js | 5 +++++ modules/appnexusBidAdapter.js | 5 +++++ modules/astraoneBidAdapter.js | 6 ++++++ modules/audiencerunBidAdapter.js | 9 +++++++++ modules/betweenBidAdapter.js | 8 ++++++++ modules/biddoBidAdapter.js | 5 +++++ modules/bidglassBidAdapter.js | 7 ++++++- modules/big-richmediaBidAdapter.js | 5 +++++ modules/braveBidAdapter.js | 5 +++++ modules/bridBidAdapter.js | 6 ++++++ modules/bridgewellBidAdapter.js | 5 +++++ modules/browsiBidAdapter.js | 5 +++++ modules/bucksenseBidAdapter.js | 5 +++++ modules/buzzoolaBidAdapter.js | 6 ++++++ modules/c1xBidAdapter.js | 4 ++++ modules/clickforceBidAdapter.js | 6 ++++++ modules/codefuelBidAdapter.js | 8 ++++++++ modules/cointrafficBidAdapter.js | 7 +++++++ modules/coinzillaBidAdapter.js | 6 ++++++ modules/colossussspBidAdapter.js | 5 +++++ modules/concertBidAdapter.js | 7 +++++++ modules/consumableBidAdapter.js | 6 ++++++ modules/conversantBidAdapter.js | 7 +++++++ modules/criteoBidAdapter.js | 8 ++++++++ modules/cwireBidAdapter.js | 6 ++++++ modules/datawrkzBidAdapter.js | 5 +++++ modules/discoveryBidAdapter.js | 6 ++++++ modules/dspxBidAdapter.js | 4 ++++ modules/dxkultureBidAdapter.js | 5 +++++ modules/eskimiBidAdapter.js | 4 ++++ modules/feedadBidAdapter.js | 9 +++++++++ modules/flippBidAdapter.js | 8 ++++++++ modules/fluctBidAdapter.js | 5 +++++ modules/freewheel-sspBidAdapter.js | 5 +++++ modules/gammaBidAdapter.js | 5 +++++ modules/getintentBidAdapter.js | 5 +++++ modules/gjirafaBidAdapter.js | 7 +++++++ modules/gmosspBidAdapter.js | 9 +++++++++ modules/gnetBidAdapter.js | 6 ++++++ modules/goldbachBidAdapter.js | 5 +++++ modules/gothamadsBidAdapter.js | 5 +++++ modules/gridBidAdapter.js | 6 ++++++ modules/gumgumBidAdapter.js | 9 +++++++++ modules/hybridBidAdapter.js | 7 +++++++ modules/impactifyBidAdapter.js | 8 ++++++++ modules/improvedigitalBidAdapter.js | 8 ++++++++ modules/incrxBidAdapter.js | 6 ++++++ modules/instreamTracking.js | 6 ++++++ modules/integr8BidAdapter.js | 7 +++++++ modules/invamiaBidAdapter.js | 5 +++++ modules/invibesBidAdapter.js | 5 +++++ modules/iqmBidAdapter.js | 4 ++++ modules/ivsBidAdapter.js | 7 +++++++ modules/lemmaDigitalBidAdapter.js | 9 +++++++++ modules/lifestreetBidAdapter.js | 4 ++++ modules/limelightDigitalBidAdapter.js | 6 ++++++ modules/livewrappedBidAdapter.js | 5 +++++ modules/lm_kiviadsBidAdapter.js | 8 ++++++++ modules/madvertiseBidAdapter.js | 5 +++++ modules/malltvBidAdapter.js | 7 +++++++ modules/mediaforceBidAdapter.js | 6 ++++++ modules/mediafuseBidAdapter.js | 5 +++++ modules/mediagoBidAdapter.js | 7 +++++++ modules/mediakeysBidAdapter.js | 2 +- modules/medianetBidAdapter.js | 6 ++++++ modules/mediasniperBidAdapter.js | 2 +- modules/mediasquareBidAdapter.js | 9 +++++++++ modules/mgidBidAdapter.js | 6 ++++++ modules/missenaBidAdapter.js | 7 +++++++ modules/nextrollBidAdapter.js | 6 ++++++ modules/nexx360BidAdapter.js | 9 +++++++++ modules/nobidBidAdapter.js | 9 +++++++++ modules/onetagBidAdapter.js | 5 +++++ modules/operaadsBidAdapter.js | 7 +++++++ modules/optidigitalBidAdapter.js | 9 +++++++++ modules/orbidderBidAdapter.js | 4 ++++ modules/pilotxBidAdapter.js | 8 ++++++++ modules/prismaBidAdapter.js | 9 +++++++++ modules/pubmaticBidAdapter.js | 6 ++++++ modules/pubwiseBidAdapter.js | 7 +++++++ modules/pxyzBidAdapter.js | 5 +++++ modules/quantcastBidAdapter.js | 5 +++++ modules/radsBidAdapter.js | 4 ++++ modules/retailspotBidAdapter.js | 7 ++++++- modules/rtbsapeBidAdapter.js | 8 ++++++++ modules/rubiconBidAdapter.js | 4 ++++ modules/seedtagBidAdapter.js | 9 +++++++++ modules/slimcutBidAdapter.js | 7 +++++++ modules/smaatoBidAdapter.js | 8 ++++++++ modules/smartadserverBidAdapter.js | 6 ++++++ modules/smartxBidAdapter.js | 6 ++++++ modules/smilewantedBidAdapter.js | 5 +++++ modules/sonobiBidAdapter.js | 6 ++++++ modules/sovrnBidAdapter.js | 4 ++++ modules/sparteoBidAdapter.js | 4 ++++ modules/spotxBidAdapter.js | 6 ++++++ modules/stvBidAdapter.js | 4 ++++ modules/sublimeBidAdapter.js | 6 ++++++ modules/tappxBidAdapter.js | 4 ++++ modules/targetVideoBidAdapter.js | 5 +++++ modules/teadsBidAdapter.js | 5 +++++ modules/temedyaBidAdapter.js | 8 ++++++++ modules/theAdxBidAdapter.js | 9 +++++++++ modules/tpmnBidAdapter.js | 4 ++++ modules/ttdBidAdapter.js | 9 +++++++++ modules/ucfunnelBidAdapter.js | 6 ++++++ modules/unicornBidAdapter.js | 5 +++++ modules/vdoaiBidAdapter.js | 6 ++++++ modules/vibrantmediaBidAdapter.js | 7 +++++++ modules/videobyteBidAdapter.js | 8 ++++++++ modules/videoheroesBidAdapter.js | 5 +++++ modules/videonowBidAdapter.js | 6 ++++++ modules/viqeoBidAdapter.js | 8 ++++++++ modules/voxBidAdapter.js | 7 +++++++ modules/welectBidAdapter.js | 7 +++++++ modules/winrBidAdapter.js | 5 +++++ modules/xeBidAdapter.js | 8 ++++++++ modules/yieldlabBidAdapter.js | 9 +++++++++ modules/yieldmoBidAdapter.js | 7 +++++++ modules/zetaBidAdapter.js | 8 ++++++++ modules/zeta_global_sspBidAdapter.js | 6 ++++++ src/native.js | 5 +++++ 134 files changed, 822 insertions(+), 6 deletions(-) diff --git a/modules/ablidaBidAdapter.js b/modules/ablidaBidAdapter.js index 805a2020fb4..175d5ff7c72 100644 --- a/modules/ablidaBidAdapter.js +++ b/modules/ablidaBidAdapter.js @@ -3,6 +3,12 @@ import {registerBidder} from '../src/adapters/bidderFactory.js'; import {BANNER, NATIVE, VIDEO} from '../src/mediaTypes.js'; import {convertOrtbRequestToProprietaryNative} from '../src/native.js'; +/** + * @typedef {import('../src/adapters/bidderFactory.js').BidRequest} BidRequest + * @typedef {import('../src/adapters/bidderFactory.js').Bid} Bid + * @typedef {import('../src/adapters/bidderFactory.js').ServerResponse} ServerResponse + */ + const BIDDER_CODE = 'ablida'; const ENDPOINT_URL = 'https://bidder.ablida.net/prebid'; diff --git a/modules/adgenerationBidAdapter.js b/modules/adgenerationBidAdapter.js index b40378c8e35..e0538fe2815 100644 --- a/modules/adgenerationBidAdapter.js +++ b/modules/adgenerationBidAdapter.js @@ -6,6 +6,14 @@ import {convertOrtbRequestToProprietaryNative} from '../src/native.js'; import {tryAppendQueryString} from '../libraries/urlUtils/urlUtils.js'; import {escapeUnsafeChars} from '../libraries/htmlEscape/htmlEscape.js'; +/** + * @typedef {import('../src/adapters/bidderFactory.js').BidRequest} BidRequest + * @typedef {import('../src/adapters/bidderFactory.js').Bid} Bid + * @typedef {import('../src/adapters/bidderFactory.js').ServerResponse} ServerResponse + * @typedef {import('../src/adapters/bidderFactory.js').SyncOptions} SyncOptions + * @typedef {import('../src/adapters/bidderFactory.js').UserSync} UserSync + */ + const ADG_BIDDER_CODE = 'adgeneration'; export const spec = { diff --git a/modules/adkernelBidAdapter.js b/modules/adkernelBidAdapter.js index add24772463..d6a4030057a 100644 --- a/modules/adkernelBidAdapter.js +++ b/modules/adkernelBidAdapter.js @@ -23,12 +23,16 @@ import {config} from '../src/config.js'; import {convertOrtbRequestToProprietaryNative} from '../src/native.js'; import {getAdUnitSizes} from '../libraries/sizeUtils/sizeUtils.js'; -/* +/** * In case you're AdKernel whitelable platform's client who needs branded adapter to * work with Adkernel platform - DO NOT COPY THIS ADAPTER UNDER NEW NAME * - * Please contact prebid@adkernel.com and we'll add your adapter as an alias. + * Please contact prebid@adkernel.com and we'll add your adapter as an alias + * @typedef {import('../src/adapters/bidderFactory.js').Bid} Bid + * @typedef {import('../src/adapters/bidderFactory.js').ServerRequest} ServerRequest + * @typedef {import('../src/adapters/bidderFactory.js').UserSync} UserSync */ + const VIDEO_PARAMS = ['pos', 'context', 'placement', 'plcmt', 'api', 'mimes', 'protocols', 'playbackmethod', 'minduration', 'maxduration', 'startdelay', 'linearity', 'skip', 'skipmin', 'skipafter', 'minbitrate', 'maxbitrate', 'delivery', 'playbackend', 'boxingallowed']; const VIDEO_FPD = ['battr', 'pos']; diff --git a/modules/admaticBidAdapter.js b/modules/admaticBidAdapter.js index 5afe7781d7d..3f87476def7 100644 --- a/modules/admaticBidAdapter.js +++ b/modules/admaticBidAdapter.js @@ -2,6 +2,13 @@ import {getValue, formatQS, logError, deepAccess, isArray, getBidIdParameter} fr import { registerBidder } from '../src/adapters/bidderFactory.js'; import { config } from '../src/config.js'; import { BANNER, VIDEO, NATIVE } from '../src/mediaTypes.js'; + +/** + * @typedef {import('../src/adapters/bidderFactory.js').BidRequest} BidRequest + * @typedef {import('../src/adapters/bidderFactory.js').Bid} Bid + * @typedef {import('../src/adapters/bidderFactory.js').ServerRequest} ServerRequest + */ + export const OPENRTB = { NATIVE: { IMAGE_TYPE: { diff --git a/modules/admediaBidAdapter.js b/modules/admediaBidAdapter.js index 42593a36159..5ea3e27b0d9 100644 --- a/modules/admediaBidAdapter.js +++ b/modules/admediaBidAdapter.js @@ -1,6 +1,12 @@ import {registerBidder} from '../src/adapters/bidderFactory.js'; import {BANNER} from '../src/mediaTypes.js'; +/** + * @typedef {import('../src/adapters/bidderFactory.js').BidRequest} BidRequest + * @typedef {import('../src/adapters/bidderFactory.js').Bid} Bid + * @typedef {import('../src/adapters/bidderFactory.js').ServerResponse} ServerResponse + */ + const BIDDER_CODE = 'admedia'; const ENDPOINT_URL = 'https://prebid.admedia.com/bidder/'; diff --git a/modules/adnowBidAdapter.js b/modules/adnowBidAdapter.js index f83dbf68a1f..99f56df58b2 100644 --- a/modules/adnowBidAdapter.js +++ b/modules/adnowBidAdapter.js @@ -9,6 +9,10 @@ const ENDPOINT = 'https://n.ads3-adnow.com/a'; /** * @typedef {object} CommonBidData + * @typedef {import('../src/adapters/bidderFactory.js').BidRequest} BidRequest + * @typedef {import('../src/adapters/bidderFactory.js').Bid} Bid + * @typedef {import('../src/adapters/bidderFactory.js').ServerRequest} ServerRequest + * @typedef {import('../src/adapters/bidderFactory.js').BidderSpec} BidderSpec * * @property {string} requestId The specific BidRequest which this bid is aimed at. * This should match the BidRequest.bidId which this Bid targets. diff --git a/modules/adqueryBidAdapter.js b/modules/adqueryBidAdapter.js index 6b0d642468b..d5a8ba6da84 100644 --- a/modules/adqueryBidAdapter.js +++ b/modules/adqueryBidAdapter.js @@ -2,6 +2,14 @@ import {registerBidder} from '../src/adapters/bidderFactory.js'; import {BANNER} from '../src/mediaTypes.js'; import {buildUrl, logInfo, parseSizesInput, triggerPixel} from '../src/utils.js'; +/** + * @typedef {import('../src/adapters/bidderFactory.js').BidRequest} BidRequest + * @typedef {import('../src/adapters/bidderFactory.js').Bid} Bid + * @typedef {import('../src/adapters/bidderFactory.js').ServerRequest} ServerRequest + * @typedef {import('../src/adapters/bidderFactory.js').BidderSpec} BidderSpec + * @typedef {import('../src/adapters/bidderFactory.js').TimedOutBid} TimedOutBid + */ + const ADQUERY_GVLID = 902; const ADQUERY_BIDDER_CODE = 'adquery'; const ADQUERY_BIDDER_DOMAIN_PROTOCOL = 'https'; diff --git a/modules/adrelevantisBidAdapter.js b/modules/adrelevantisBidAdapter.js index 3c9c661b09c..68cd859e24e 100644 --- a/modules/adrelevantisBidAdapter.js +++ b/modules/adrelevantisBidAdapter.js @@ -23,6 +23,11 @@ import {convertCamelToUnderscore} from '../libraries/appnexusUtils/anUtils.js'; import {convertTypes} from '../libraries/transformParamsUtils/convertTypes.js'; import {chunk} from '../libraries/chunk/chunk.js'; +/** + * @typedef {import('../src/adapters/bidderFactory.js').BidRequest} BidRequest + * @typedef {import('../src/adapters/bidderFactory.js').Bid} Bid + */ + const BIDDER_CODE = 'adrelevantis'; const URL = 'https://ssp.adrelevantis.com/prebid'; const VIDEO_TARGETING = ['id', 'mimes', 'minduration', 'maxduration', diff --git a/modules/adtelligentBidAdapter.js b/modules/adtelligentBidAdapter.js index 8e6aeecdd75..cadba499b5c 100644 --- a/modules/adtelligentBidAdapter.js +++ b/modules/adtelligentBidAdapter.js @@ -7,6 +7,10 @@ import {find} from '../src/polyfill.js'; import {convertTypes} from '../libraries/transformParamsUtils/convertTypes.js'; import {chunk} from '../libraries/chunk/chunk.js'; +/** + * @typedef {import('../src/adapters/bidderFactory.js').Bid} Bid + */ + const subdomainSuffixes = ['', 1, 2]; const AUCTION_PATH = '/v2/auction/'; const PROTOCOL = 'https://'; diff --git a/modules/aduptechBidAdapter.js b/modules/aduptechBidAdapter.js index 49187da2fe2..fdc1249ded4 100644 --- a/modules/aduptechBidAdapter.js +++ b/modules/aduptechBidAdapter.js @@ -4,6 +4,11 @@ import {BANNER, NATIVE} from '../src/mediaTypes.js'; import { convertOrtbRequestToProprietaryNative } from '../src/native.js'; import {getAdUnitSizes} from '../libraries/sizeUtils/sizeUtils.js'; +/** + * @typedef {import('../src/adapters/bidderFactory.js').BidRequest} BidRequest + * @typedef {import('../src/adapters/bidderFactory.js').BidderRequest} BidderRequest + */ + export const BIDDER_CODE = 'aduptech'; export const GVLID = 647; export const ENDPOINT_URL_PUBLISHER_PLACEHOLDER = '{PUBLISHER}'; diff --git a/modules/adyoulikeBidAdapter.js b/modules/adyoulikeBidAdapter.js index 8952c3ae2b9..9bc24b11ac3 100644 --- a/modules/adyoulikeBidAdapter.js +++ b/modules/adyoulikeBidAdapter.js @@ -5,6 +5,11 @@ import {find} from '../src/polyfill.js'; import {BANNER, NATIVE, VIDEO} from '../src/mediaTypes.js'; import { convertOrtbRequestToProprietaryNative } from '../src/native.js'; +/** + * @typedef {import('../src/adapters/bidderFactory.js').BidRequest} BidRequest + * @typedef {import('../src/adapters/bidderFactory.js').Bid} Bid + */ + const VERSION = '1.0'; const BIDDER_CODE = 'adyoulike'; const DEFAULT_DC = 'hb-api'; diff --git a/modules/ajaBidAdapter.js b/modules/ajaBidAdapter.js index 9e2d4efb5ff..fcbe0c12c6e 100644 --- a/modules/ajaBidAdapter.js +++ b/modules/ajaBidAdapter.js @@ -3,6 +3,11 @@ import { registerBidder } from '../src/adapters/bidderFactory.js'; import { BANNER } from '../src/mediaTypes.js'; import {tryAppendQueryString} from '../libraries/urlUtils/urlUtils.js'; +/** + * @typedef {import('../src/adapters/bidderFactory.js').BidRequest} BidRequest + * @typedef {import('../src/adapters/bidderFactory.js').ServerRequest} ServerRequest + */ + const BidderCode = 'aja'; const URL = 'https://ad.as.amanad.adtdp.com/v2/prebid'; const SDKType = 5; diff --git a/modules/appierBidAdapter.js b/modules/appierBidAdapter.js index 12346d15130..fa314f0bd5f 100644 --- a/modules/appierBidAdapter.js +++ b/modules/appierBidAdapter.js @@ -2,6 +2,11 @@ import { registerBidder } from '../src/adapters/bidderFactory.js'; import { BANNER } from '../src/mediaTypes.js'; import { config } from '../src/config.js'; +/** + * @typedef {import('../src/adapters/bidderFactory.js').BidRequest} BidRequest + * @typedef {import('../src/adapters/bidderFactory.js').Bid} Bid + */ + export const ADAPTER_VERSION = '1.0.0'; const SUPPORTED_AD_TYPES = [BANNER]; diff --git a/modules/appnexusBidAdapter.js b/modules/appnexusBidAdapter.js index 6a0b088795f..e62824cb2aa 100644 --- a/modules/appnexusBidAdapter.js +++ b/modules/appnexusBidAdapter.js @@ -38,6 +38,11 @@ import {convertCamelToUnderscore, fill} from '../libraries/appnexusUtils/anUtils import {convertTypes} from '../libraries/transformParamsUtils/convertTypes.js'; import {chunk} from '../libraries/chunk/chunk.js'; +/** + * @typedef {import('../src/adapters/bidderFactory.js').BidRequest} BidRequest + * @typedef {import('../src/adapters/bidderFactory.js').Bid} Bid + */ + const BIDDER_CODE = 'appnexus'; const URL = 'https://ib.adnxs.com/ut/v3/prebid'; const URL_SIMPLE = 'https://ib.adnxs-simple.com/ut/v3/prebid'; diff --git a/modules/astraoneBidAdapter.js b/modules/astraoneBidAdapter.js index d7f92bb5fac..9645b8e68bd 100644 --- a/modules/astraoneBidAdapter.js +++ b/modules/astraoneBidAdapter.js @@ -2,6 +2,12 @@ import { _map } from '../src/utils.js'; import { registerBidder } from '../src/adapters/bidderFactory.js' import { BANNER } from '../src/mediaTypes.js' +/** + * @typedef {import('../src/adapters/bidderFactory.js').BidRequest} BidRequest + * @typedef {import('../src/adapters/bidderFactory.js').Bid} Bid + * @typedef {import('../src/adapters/bidderFactory.js').ServerResponse} ServerResponse + */ + const BIDDER_CODE = 'astraone'; const SSP_ENDPOINT = 'https://ssp.astraone.io/auction/prebid'; const TTL = 60; diff --git a/modules/audiencerunBidAdapter.js b/modules/audiencerunBidAdapter.js index e716fe94c8b..92a4343b3ed 100644 --- a/modules/audiencerunBidAdapter.js +++ b/modules/audiencerunBidAdapter.js @@ -12,6 +12,15 @@ import {config} from '../src/config.js'; import {registerBidder} from '../src/adapters/bidderFactory.js'; import {BANNER} from '../src/mediaTypes.js'; +/** + * @typedef {import('../src/adapters/bidderFactory.js').BidRequest} BidRequest + * @typedef {import('../src/adapters/bidderFactory.js').Bid} Bid + * @typedef {import('../src/adapters/bidderFactory.js').ServerRequest} ServerRequest + * @typedef {import('../src/adapters/bidderFactory.js').ServerResponse} ServerResponse + * @typedef {import('../src/adapters/bidderFactory.js').SyncOptions} SyncOptions + * @typedef {import('../src/adapters/bidderFactory.js').UserSync} UserSync + */ + const BIDDER_CODE = 'audiencerun'; const BASE_URL = 'https://d.audiencerun.com'; const AUCTION_URL = `${BASE_URL}/prebid`; diff --git a/modules/betweenBidAdapter.js b/modules/betweenBidAdapter.js index 6883b7cce2c..4a953875d99 100644 --- a/modules/betweenBidAdapter.js +++ b/modules/betweenBidAdapter.js @@ -3,6 +3,14 @@ import {parseSizesInput} from '../src/utils.js'; import {includes} from '../src/polyfill.js'; import {getAdUnitSizes} from '../libraries/sizeUtils/sizeUtils.js'; +/** + * @typedef {import('../src/adapters/bidderFactory.js').BidRequest} BidRequest + * @typedef {import('../src/adapters/bidderFactory.js').Bid} Bid + * @typedef {import('../src/adapters/bidderFactory.js').ServerResponse} ServerResponse + * @typedef {import('../src/adapters/bidderFactory.js').SyncOptions} SyncOptions + * @typedef {import('../src/adapters/bidderFactory.js').UserSync} UserSync + */ + const BIDDER_CODE = 'between'; let ENDPOINT = 'https://ads.betweendigital.com/adjson?t=prebid'; const CODE_TYPES = ['inpage', 'preroll', 'midroll', 'postroll']; diff --git a/modules/biddoBidAdapter.js b/modules/biddoBidAdapter.js index 5512ca60f8e..cf39c572629 100644 --- a/modules/biddoBidAdapter.js +++ b/modules/biddoBidAdapter.js @@ -1,6 +1,11 @@ import {registerBidder} from '../src/adapters/bidderFactory.js'; import {BANNER} from '../src/mediaTypes.js'; +/** + * @typedef {import('../src/adapters/bidderFactory.js').BidRequest} BidRequest + * @typedef {import('../src/adapters/bidderFactory.js').ServerResponse} ServerResponse + */ + const BIDDER_CODE = 'biddo'; const ENDPOINT_URL = 'https://ad.adopx.net/delivery/impress'; diff --git a/modules/bidglassBidAdapter.js b/modules/bidglassBidAdapter.js index a29976cfcb7..6074d1a0e4c 100644 --- a/modules/bidglassBidAdapter.js +++ b/modules/bidglassBidAdapter.js @@ -1,7 +1,12 @@ import {_each, isArray, deepClone, getUniqueIdentifierStr, getBidIdParameter} from '../src/utils.js'; -// import {config} from 'src/config.js'; import {registerBidder} from '../src/adapters/bidderFactory.js'; +/** + * @typedef {import('../src/adapters/bidderFactory.js').BidRequest} BidRequest + * @typedef {import('../src/adapters/bidderFactory.js').Bid} Bid + * @typedef {import('../src/adapters/bidderFactory.js').ServerResponse} ServerResponse + */ + const BIDDER_CODE = 'bidglass'; export const spec = { diff --git a/modules/big-richmediaBidAdapter.js b/modules/big-richmediaBidAdapter.js index 8a03aac1ace..ecb1724c2a1 100644 --- a/modules/big-richmediaBidAdapter.js +++ b/modules/big-richmediaBidAdapter.js @@ -3,6 +3,11 @@ import {config} from '../src/config.js'; import {registerBidder} from '../src/adapters/bidderFactory.js'; import {spec as baseAdapter} from './appnexusBidAdapter.js'; // eslint-disable-line prebid/validate-imports +/** + * @typedef {import('../src/adapters/bidderFactory.js').BidRequest} BidRequest + * @typedef {import('../src/adapters/bidderFactory.js').Bid} Bid + */ + const BIDDER_CODE = 'big-richmedia'; const metadataByRequestId = {}; diff --git a/modules/braveBidAdapter.js b/modules/braveBidAdapter.js index d954522ae24..4c5448482db 100644 --- a/modules/braveBidAdapter.js +++ b/modules/braveBidAdapter.js @@ -4,6 +4,11 @@ import {BANNER, NATIVE, VIDEO} from '../src/mediaTypes.js'; import {config} from '../src/config.js'; import {convertOrtbRequestToProprietaryNative} from '../src/native.js'; +/** + * @typedef {import('../src/adapters/bidderFactory.js').BidRequest} BidRequest + * @typedef {import('../src/adapters/bidderFactory.js').Bid} Bid + */ + const BIDDER_CODE = 'brave'; const DEFAULT_CUR = 'USD'; const ENDPOINT_URL = `https://point.bravegroup.tv/?t=2&partner=hash`; diff --git a/modules/bridBidAdapter.js b/modules/bridBidAdapter.js index 8e7c2f166ef..e784ea517ac 100644 --- a/modules/bridBidAdapter.js +++ b/modules/bridBidAdapter.js @@ -3,6 +3,12 @@ import {VIDEO} from '../src/mediaTypes.js'; import {registerBidder} from '../src/adapters/bidderFactory.js'; import {getRefererInfo} from '../src/refererDetection.js'; +/** + * @typedef {import('../src/adapters/bidderFactory.js').BidRequest} BidRequest + * @typedef {import('../src/adapters/bidderFactory.js').Bid} Bid + * @typedef {import('../src/adapters/bidderFactory.js').BidderRequest} BidderRequest + */ + const SOURCE = 'pbjs'; const BIDDER_CODE = 'brid'; const ENDPOINT_URL = 'https://pbs.prebrid.tv/openrtb2/auction'; diff --git a/modules/bridgewellBidAdapter.js b/modules/bridgewellBidAdapter.js index 6088cefaa55..578acf8a358 100644 --- a/modules/bridgewellBidAdapter.js +++ b/modules/bridgewellBidAdapter.js @@ -4,6 +4,11 @@ import {BANNER, NATIVE} from '../src/mediaTypes.js'; import {find} from '../src/polyfill.js'; import { convertOrtbRequestToProprietaryNative } from '../src/native.js'; +/** + * @typedef {import('../src/adapters/bidderFactory.js').BidRequest} BidRequest + * @typedef {import('../src/adapters/bidderFactory.js').Bid} Bid + */ + const BIDDER_CODE = 'bridgewell'; const REQUEST_ENDPOINT = 'https://prebid.scupio.com/recweb/prebid.aspx?cb='; const BIDDER_VERSION = '1.1.0'; diff --git a/modules/browsiBidAdapter.js b/modules/browsiBidAdapter.js index 03b6b2a8f3d..fa1cacaa568 100644 --- a/modules/browsiBidAdapter.js +++ b/modules/browsiBidAdapter.js @@ -3,6 +3,11 @@ import {config} from '../src/config.js'; import {VIDEO} from '../src/mediaTypes.js'; import {logError, logInfo, isArray, isStr} from '../src/utils.js'; +/** + * @typedef {import('../src/adapters/bidderFactory.js').Bid} Bid + * @typedef {import('../src/adapters/bidderFactory.js').UserSync} UserSync + */ + const BIDDER_CODE = 'browsi'; const DATA = 'brwvidtag'; const ADAPTER = '__bad'; diff --git a/modules/bucksenseBidAdapter.js b/modules/bucksenseBidAdapter.js index ecf27de997d..5aa14f2a53b 100644 --- a/modules/bucksenseBidAdapter.js +++ b/modules/bucksenseBidAdapter.js @@ -2,6 +2,11 @@ import { logInfo } from '../src/utils.js'; import { registerBidder } from '../src/adapters/bidderFactory.js'; import { BANNER } from '../src/mediaTypes.js'; +/** + * @typedef {import('../src/adapters/bidderFactory.js').BidRequest} BidRequest + * @typedef {import('../src/adapters/bidderFactory.js').Bid} Bid + */ + const WHO = 'BKSHBID-005'; const BIDDER_CODE = 'bucksense'; const URL = 'https://directo.prebidserving.com/prebidjs/'; diff --git a/modules/buzzoolaBidAdapter.js b/modules/buzzoolaBidAdapter.js index b5ea6227f58..14a26203484 100644 --- a/modules/buzzoolaBidAdapter.js +++ b/modules/buzzoolaBidAdapter.js @@ -5,6 +5,12 @@ import {Renderer} from '../src/Renderer.js'; import {OUTSTREAM} from '../src/video.js'; import { convertOrtbRequestToProprietaryNative } from '../src/native.js'; +/** + * @typedef {import('../src/adapters/bidderFactory.js').BidRequest} BidRequest + * @typedef {import('../src/adapters/bidderFactory.js').Bid} Bid + * @typedef {import('../src/adapters/bidderFactory.js').ServerResponse} ServerResponse + */ + const BIDDER_CODE = 'buzzoola'; const ENDPOINT = 'https://exchange.buzzoola.com/ssp/prebidjs'; const RENDERER_SRC = 'https://tube.buzzoola.com/new/build/buzzlibrary.js'; diff --git a/modules/c1xBidAdapter.js b/modules/c1xBidAdapter.js index 8c9407825ba..79ba8cf499d 100644 --- a/modules/c1xBidAdapter.js +++ b/modules/c1xBidAdapter.js @@ -2,6 +2,10 @@ import { registerBidder } from '../src/adapters/bidderFactory.js'; import { logInfo, logError } from '../src/utils.js'; import { BANNER } from '../src/mediaTypes.js'; +/** + * @typedef {import('../src/adapters/bidderFactory.js').BidRequest} BidRequest + */ + const BIDDER_CODE = 'c1x'; const URL = 'https://hb-stg.c1exchange.com/ht'; // const PIXEL_ENDPOINT = '//px.c1exchange.com/pubpixel/'; diff --git a/modules/clickforceBidAdapter.js b/modules/clickforceBidAdapter.js index 92bc9b1bad2..be81ff1885c 100644 --- a/modules/clickforceBidAdapter.js +++ b/modules/clickforceBidAdapter.js @@ -2,6 +2,12 @@ import { _each } from '../src/utils.js'; import {registerBidder} from '../src/adapters/bidderFactory.js'; import {BANNER, NATIVE} from '../src/mediaTypes.js'; import { convertOrtbRequestToProprietaryNative } from '../src/native.js'; + +/** + * @typedef {import('../src/adapters/bidderFactory.js').BidRequest} BidRequest + * @typedef {import('../src/adapters/bidderFactory.js').Bid} Bid + */ + const BIDDER_CODE = 'clickforce'; const ENDPOINT_URL = 'https://ad.holmesmind.com/adserver/prebid.json?cb=' + new Date().getTime() + '&hb=1&ver=1.21'; diff --git a/modules/codefuelBidAdapter.js b/modules/codefuelBidAdapter.js index 4fef3fb2494..a289e29bd19 100644 --- a/modules/codefuelBidAdapter.js +++ b/modules/codefuelBidAdapter.js @@ -2,6 +2,14 @@ import {deepAccess, isArray} from '../src/utils.js'; import {registerBidder} from '../src/adapters/bidderFactory.js'; import {BANNER} from '../src/mediaTypes.js'; +/** + * @typedef {import('../src/adapters/bidderFactory.js').BidRequest} BidRequest + * @typedef {import('../src/adapters/bidderFactory.js').Bid} Bid + * @typedef {import('../src/adapters/bidderFactory.js').ServerResponse} ServerResponse + * @typedef {import('../src/adapters/bidderFactory.js').SyncOptions} SyncOptions + * @typedef {import('../src/adapters/bidderFactory.js').UserSync} UserSync + */ + const BIDDER_CODE = 'codefuel'; const CURRENCY = 'USD'; diff --git a/modules/cointrafficBidAdapter.js b/modules/cointrafficBidAdapter.js index 380e1f5fc77..3b90529b6cc 100644 --- a/modules/cointrafficBidAdapter.js +++ b/modules/cointrafficBidAdapter.js @@ -3,6 +3,13 @@ import { registerBidder } from '../src/adapters/bidderFactory.js'; import { BANNER } from '../src/mediaTypes.js' import { config } from '../src/config.js' +/** + * @typedef {import('../src/adapters/bidderFactory.js').BidRequest} BidRequest + * @typedef {import('../src/adapters/bidderFactory.js').Bid} Bid + * @typedef {import('../src/adapters/bidderFactory.js').ServerResponse} ServerResponse + * @typedef {import('../src/adapters/bidderFactory.js').BidderSpec} BidderSpec + */ + const BIDDER_CODE = 'cointraffic'; const ENDPOINT_URL = 'https://apps-pbd.ctraffic.io/pb/tmp'; const DEFAULT_CURRENCY = 'EUR'; diff --git a/modules/coinzillaBidAdapter.js b/modules/coinzillaBidAdapter.js index c7d8fa5797c..9ae2c74547d 100644 --- a/modules/coinzillaBidAdapter.js +++ b/modules/coinzillaBidAdapter.js @@ -1,6 +1,12 @@ import { parseSizesInput } from '../src/utils.js'; import {registerBidder} from '../src/adapters/bidderFactory.js'; +/** + * @typedef {import('../src/adapters/bidderFactory.js').BidRequest} BidRequest + * @typedef {import('../src/adapters/bidderFactory.js').Bid} Bid + * @typedef {import('../src/adapters/bidderFactory.js').ServerResponse} ServerResponse + */ + const BIDDER_CODE = 'coinzilla'; const ENDPOINT_URL = 'https://request.czilladx.com/serve/request.php'; diff --git a/modules/colossussspBidAdapter.js b/modules/colossussspBidAdapter.js index b1ee8875422..cc3e452f20c 100644 --- a/modules/colossussspBidAdapter.js +++ b/modules/colossussspBidAdapter.js @@ -5,6 +5,11 @@ import { ajax } from '../src/ajax.js'; import { config } from '../src/config.js'; import { convertOrtbRequestToProprietaryNative } from '../src/native.js'; +/** + * @typedef {import('../src/adapters/bidderFactory.js').BidRequest} BidRequest + * @typedef {import('../src/adapters/bidderFactory.js').Bid} Bid + */ + const BIDDER_CODE = 'colossusssp'; const G_URL = 'https://colossusssp.com/?c=o&m=multi'; const G_URL_SYNC = 'https://sync.colossusssp.com'; diff --git a/modules/concertBidAdapter.js b/modules/concertBidAdapter.js index bc2c4555c54..bd738a39bba 100644 --- a/modules/concertBidAdapter.js +++ b/modules/concertBidAdapter.js @@ -3,6 +3,13 @@ import { registerBidder } from '../src/adapters/bidderFactory.js'; import { getStorageManager } from '../src/storageManager.js'; import { hasPurpose1Consent } from '../src/utils/gpdr.js'; +/** + * @typedef {import('../src/adapters/bidderFactory.js').BidRequest} BidRequest + * @typedef {import('../src/adapters/bidderFactory.js').Bid} Bid + * @typedef {import('../src/adapters/bidderFactory.js').ServerResponse} ServerResponse + * @typedef {import('../src/adapters/bidderFactory.js').validBidRequests} validBidRequests + */ + const BIDDER_CODE = 'concert'; const CONCERT_ENDPOINT = 'https://bids.concert.io'; diff --git a/modules/consumableBidAdapter.js b/modules/consumableBidAdapter.js index 696549a67dc..30b081e53d3 100644 --- a/modules/consumableBidAdapter.js +++ b/modules/consumableBidAdapter.js @@ -3,6 +3,12 @@ import {config} from '../src/config.js'; import { registerBidder } from '../src/adapters/bidderFactory.js'; import { BANNER, VIDEO } from '../src/mediaTypes.js'; +/** + * @typedef {import('../src/adapters/bidderFactory.js').BidRequest} BidRequest + * @typedef {import('../src/adapters/bidderFactory.js').Bid} Bid + * @typedef {import('../src/adapters/bidderFactory.js').validBidRequests} validBidRequests + */ + const BIDDER_CODE = 'consumable'; const BASE_URI = 'https://e.serverbid.com/api/v2'; diff --git a/modules/conversantBidAdapter.js b/modules/conversantBidAdapter.js index c338a6633b5..dffc1a9f33b 100644 --- a/modules/conversantBidAdapter.js +++ b/modules/conversantBidAdapter.js @@ -21,6 +21,13 @@ import {ORTB_MTYPES} from '../libraries/ortbConverter/processors/mediaType.js'; // Maintainer: mediapsr@epsilon.com +/** + * @typedef {import('../src/adapters/bidderFactory.js').BidRequest} BidRequest + * @typedef {import('../src/adapters/bidderFactory.js').Bid} Bid + * @typedef {import('../src/adapters/bidderFactory.js').ServerRequest} ServerRequest + * @typedef {import('../src/adapters/bidderFactory.js').Device} Device + */ + const GVLID = 24; const BIDDER_CODE = 'conversant'; diff --git a/modules/criteoBidAdapter.js b/modules/criteoBidAdapter.js index 2fbf3d9ab9e..a8a1416e1f3 100644 --- a/modules/criteoBidAdapter.js +++ b/modules/criteoBidAdapter.js @@ -11,6 +11,14 @@ import { Renderer } from '../src/Renderer.js'; import { OUTSTREAM } from '../src/video.js'; import { ajax } from '../src/ajax.js'; +/** + * @typedef {import('../src/adapters/bidderFactory.js').BidRequest} BidRequest + * @typedef {import('../src/adapters/bidderFactory.js').Bid} Bid + * @typedef {import('../src/adapters/bidderFactory.js').ServerRequest} ServerRequest + * @typedef {import('../src/adapters/bidderFactory.js').BidderSpec} BidderSpec + * @typedef {import('../src/adapters/bidderFactory.js').TimedOutBid} TimedOutBid + */ + const GVLID = 91; export const ADAPTER_VERSION = 36; const BIDDER_CODE = 'criteo'; diff --git a/modules/cwireBidAdapter.js b/modules/cwireBidAdapter.js index f158e16a64e..d36948d162d 100644 --- a/modules/cwireBidAdapter.js +++ b/modules/cwireBidAdapter.js @@ -4,6 +4,12 @@ import {BANNER} from '../src/mediaTypes.js'; import {generateUUID, getParameterByName, isNumber, logError, logInfo} from '../src/utils.js'; import {hasPurpose1Consent} from '../src/utils/gpdr.js'; +/** + * @typedef {import('../src/adapters/bidderFactory.js').BidRequest} BidRequest + * @typedef {import('../src/adapters/bidderFactory.js').Bid} Bid + * @typedef {import('../src/adapters/bidderFactory.js').ServerResponse} ServerResponse + */ + // ------------------------------------ const BIDDER_CODE = 'cwire'; const CWID_KEY = 'cw_cwid'; diff --git a/modules/datawrkzBidAdapter.js b/modules/datawrkzBidAdapter.js index 127e7893ec5..db795c89155 100644 --- a/modules/datawrkzBidAdapter.js +++ b/modules/datawrkzBidAdapter.js @@ -15,6 +15,11 @@ import { BANNER, NATIVE, VIDEO } from '../src/mediaTypes.js'; import CONSTANTS from '../src/constants.json'; import { OUTSTREAM, INSTREAM } from '../src/video.js'; +/** + * @typedef {import('../src/adapters/bidderFactory.js').BidRequest} BidRequest + * @typedef {import('../src/adapters/bidderFactory.js').Bid} Bid + */ + const BIDDER_CODE = 'datawrkz'; const ALIASES = []; const ENDPOINT_URL = 'https://at.datawrkz.com/exchange/openrtb23/'; diff --git a/modules/discoveryBidAdapter.js b/modules/discoveryBidAdapter.js index b9ba6087d30..ad8f5616d44 100644 --- a/modules/discoveryBidAdapter.js +++ b/modules/discoveryBidAdapter.js @@ -3,6 +3,12 @@ import { getStorageManager } from '../src/storageManager.js'; import { registerBidder } from '../src/adapters/bidderFactory.js'; import { BANNER, NATIVE } from '../src/mediaTypes.js'; +/** + * @typedef {import('../src/adapters/bidderFactory.js').BidRequest} BidRequest + * @typedef {import('../src/adapters/bidderFactory.js').Bid} Bid + * @typedef {import('../src/adapters/bidderFactory.js').ServerResponse} ServerResponse + */ + const BIDDER_CODE = 'discovery'; const ENDPOINT_URL = 'https://rtb-jp.mediago.io/api/bid?tn='; const TIME_TO_LIVE = 500; diff --git a/modules/dspxBidAdapter.js b/modules/dspxBidAdapter.js index b8e812f581a..b4490095894 100644 --- a/modules/dspxBidAdapter.js +++ b/modules/dspxBidAdapter.js @@ -4,6 +4,10 @@ import {BANNER, VIDEO} from '../src/mediaTypes.js'; import {Renderer} from '../src/Renderer.js'; import {includes} from '../src/polyfill.js'; +/** + * @typedef {import('../src/adapters/bidderFactory.js').BidRequest} BidRequest + */ + const BIDDER_CODE = 'dspx'; const ENDPOINT_URL = 'https://buyer.dspx.tv/request/'; const ENDPOINT_URL_DEV = 'https://dcbuyer.dspx.tv/request/'; diff --git a/modules/dxkultureBidAdapter.js b/modules/dxkultureBidAdapter.js index 282b54e0823..c167baef6ea 100644 --- a/modules/dxkultureBidAdapter.js +++ b/modules/dxkultureBidAdapter.js @@ -12,6 +12,11 @@ import {BANNER, VIDEO} from '../src/mediaTypes.js'; import { Renderer } from '../src/Renderer.js'; import {ortbConverter} from '../libraries/ortbConverter/converter.js' +/** + * @typedef {import('../src/adapters/bidderFactory.js').BidRequest} BidRequest + * @typedef {import('../src/adapters/bidderFactory.js').Bid} Bid + */ + const BIDDER_CODE = 'dxkulture'; const DEFAULT_BID_TTL = 300; const DEFAULT_NET_REVENUE = true; diff --git a/modules/eskimiBidAdapter.js b/modules/eskimiBidAdapter.js index 81b8c5d8058..ce01abb9e71 100644 --- a/modules/eskimiBidAdapter.js +++ b/modules/eskimiBidAdapter.js @@ -4,6 +4,10 @@ import {BANNER, VIDEO} from '../src/mediaTypes.js'; import * as utils from '../src/utils.js'; import {getBidIdParameter} from '../src/utils.js'; +/** + * @typedef {import('../src/adapters/bidderFactory.js').Bid} Bid + */ + const BIDDER_CODE = 'eskimi'; // const ENDPOINT = 'https://hb.eskimi.com/bids' const ENDPOINT = 'https://sspback.eskimi.com/bid-request' diff --git a/modules/feedadBidAdapter.js b/modules/feedadBidAdapter.js index 7b41f0fcc03..e68a932b726 100644 --- a/modules/feedadBidAdapter.js +++ b/modules/feedadBidAdapter.js @@ -3,6 +3,15 @@ import {registerBidder} from '../src/adapters/bidderFactory.js'; import {BANNER} from '../src/mediaTypes.js'; import {ajax} from '../src/ajax.js'; +/** + * @typedef {import('../src/adapters/bidderFactory.js').BidRequest} BidRequest + * @typedef {import('../src/adapters/bidderFactory.js').Bid} Bid + * @typedef {import('../src/adapters/bidderFactory.js').ServerRequest} ServerRequest + * @typedef {import('../src/adapters/bidderFactory.js').SyncOptions} SyncOptions + * @typedef {import('../src/adapters/bidderFactory.js').BidderSpec} BidderSpec + * @typedef {import('../src/adapters/bidderFactory.js').MediaTypes} MediaTypes + */ + /** * Version of the FeedAd bid adapter * @type {string} diff --git a/modules/flippBidAdapter.js b/modules/flippBidAdapter.js index ea5e71ad81d..0708c90ac0d 100644 --- a/modules/flippBidAdapter.js +++ b/modules/flippBidAdapter.js @@ -3,6 +3,14 @@ import { registerBidder } from '../src/adapters/bidderFactory.js'; import { BANNER } from '../src/mediaTypes.js'; import {getStorageManager} from '../src/storageManager.js'; +/** + * @typedef {import('../src/adapters/bidderFactory.js').BidRequest} BidRequest + * @typedef {import('../src/adapters/bidderFactory.js').Bid} Bid + * @typedef {import('../src/adapters/bidderFactory.js').ServerResponse} ServerResponse + * @typedef {import('../src/adapters/bidderFactory.js').SyncOptions} SyncOptions + * @typedef {import('../src/adapters/bidderFactory.js').UserSync} UserSync + */ + const NETWORK_ID = 10922; const AD_TYPES = [4309, 641]; const DTX_TYPES = [5061]; diff --git a/modules/fluctBidAdapter.js b/modules/fluctBidAdapter.js index b566769c00e..f6d97fa7cd8 100644 --- a/modules/fluctBidAdapter.js +++ b/modules/fluctBidAdapter.js @@ -2,6 +2,11 @@ import { _each, deepSetValue, isEmpty } from '../src/utils.js'; import { config } from '../src/config.js'; import { registerBidder } from '../src/adapters/bidderFactory.js'; +/** + * @typedef {import('../src/adapters/bidderFactory.js').BidRequest} BidRequest + * @typedef {import('../src/adapters/bidderFactory.js').validBidRequests} validBidRequests + */ + const BIDDER_CODE = 'fluct'; const END_POINT = 'https://hb.adingo.jp/prebid'; const VERSION = '1.2'; diff --git a/modules/freewheel-sspBidAdapter.js b/modules/freewheel-sspBidAdapter.js index 73c56160b00..c4653181fd0 100644 --- a/modules/freewheel-sspBidAdapter.js +++ b/modules/freewheel-sspBidAdapter.js @@ -3,6 +3,11 @@ import { BANNER, VIDEO } from '../src/mediaTypes.js'; import { registerBidder } from '../src/adapters/bidderFactory.js'; import { config } from '../src/config.js'; +/** + * @typedef {import('../src/adapters/bidderFactory.js').BidRequest} BidRequest + * @typedef {import('../src/adapters/bidderFactory.js').Bid} Bid + */ + const BIDDER_CODE = 'freewheel-ssp'; const GVL_ID = 285; diff --git a/modules/gammaBidAdapter.js b/modules/gammaBidAdapter.js index 279eb78812e..40abdd81930 100644 --- a/modules/gammaBidAdapter.js +++ b/modules/gammaBidAdapter.js @@ -1,5 +1,10 @@ import { registerBidder } from '../src/adapters/bidderFactory.js'; +/** + * @typedef {import('../src/adapters/bidderFactory.js').BidRequest} BidRequest + * @typedef {import('../src/adapters/bidderFactory.js').Bid} Bid + */ + const ENDPOINT = 'https://hb.gammaplatform.com'; const ENDPOINT_USERSYNC = 'https://cm-supply-web.gammaplatform.com'; const BIDDER_CODE = 'gamma'; diff --git a/modules/getintentBidAdapter.js b/modules/getintentBidAdapter.js index 8cffccb9f30..7353a1c1f67 100644 --- a/modules/getintentBidAdapter.js +++ b/modules/getintentBidAdapter.js @@ -1,6 +1,11 @@ import {getBidIdParameter, isFn, isInteger} from '../src/utils.js'; import { registerBidder } from '../src/adapters/bidderFactory.js'; +/** + * @typedef {import('../src/adapters/bidderFactory.js').BidRequest} BidRequest + * @typedef {import('../src/adapters/bidderFactory.js').Bid} Bid + */ + const BIDDER_CODE = 'getintent'; const IS_NET_REVENUE = true; const BID_HOST = 'px.adhigh.net'; diff --git a/modules/gjirafaBidAdapter.js b/modules/gjirafaBidAdapter.js index 86cbb1d89de..9259010ac78 100644 --- a/modules/gjirafaBidAdapter.js +++ b/modules/gjirafaBidAdapter.js @@ -2,6 +2,13 @@ import { registerBidder } from '../src/adapters/bidderFactory.js'; import { getStorageManager } from '../src/storageManager.js'; import { BANNER, VIDEO } from '../src/mediaTypes.js'; +/** + * @typedef {import('../src/adapters/bidderFactory.js').BidRequest} BidRequest + * @typedef {import('../src/adapters/bidderFactory.js').Bid} Bid + * @typedef {import('../src/adapters/bidderFactory.js').ServerResponse} ServerResponse + * @typedef {import('../src/adapters/bidderFactory.js').validBidRequests} validBidRequests + */ + const BIDDER_CODE = 'gjirafa'; const ENDPOINT_URL = 'https://central.gjirafa.com/bid'; const DIMENSION_SEPARATOR = 'x'; diff --git a/modules/gmosspBidAdapter.js b/modules/gmosspBidAdapter.js index 559f9f77aaf..c4b8cd819e0 100644 --- a/modules/gmosspBidAdapter.js +++ b/modules/gmosspBidAdapter.js @@ -12,6 +12,15 @@ import {config} from '../src/config.js'; import {BANNER} from '../src/mediaTypes.js'; import {tryAppendQueryString} from '../libraries/urlUtils/urlUtils.js'; +/** + * @typedef {import('../src/adapters/bidderFactory.js').BidRequest} BidRequest + * @typedef {import('../src/adapters/bidderFactory.js').Bid} Bid + * @typedef {import('../src/adapters/bidderFactory.js').ServerResponse} ServerResponse + * @typedef {import('../src/adapters/bidderFactory.js').SyncOptions} SyncOptions + * @typedef {import('../src/adapters/bidderFactory.js').UserSync} UserSync + * @typedef {import('../src/adapters/bidderFactory.js').validBidRequests} validBidRequests + */ + const BIDDER_CODE = 'gmossp'; const ENDPOINT = 'https://sp.gmossp-sp.jp/hb/prebid/query.ad'; diff --git a/modules/gnetBidAdapter.js b/modules/gnetBidAdapter.js index 38e96c183b9..4718438b9bb 100644 --- a/modules/gnetBidAdapter.js +++ b/modules/gnetBidAdapter.js @@ -4,6 +4,12 @@ import { BANNER } from '../src/mediaTypes.js'; import { getStorageManager } from '../src/storageManager.js'; import {ajax} from '../src/ajax.js'; +/** + * @typedef {import('../src/adapters/bidderFactory.js').BidRequest} BidRequest + * @typedef {import('../src/adapters/bidderFactory.js').Bid} Bid + * @typedef {import('../src/adapters/bidderFactory.js').validBidRequests} validBidRequests + */ + const BIDDER_CODE = 'gnet'; const ENDPOINT = 'https://service.gnetrtb.com/api'; const storage = getStorageManager({bidderCode: BIDDER_CODE}); diff --git a/modules/goldbachBidAdapter.js b/modules/goldbachBidAdapter.js index 8892df130df..ff394e69e54 100644 --- a/modules/goldbachBidAdapter.js +++ b/modules/goldbachBidAdapter.js @@ -30,6 +30,11 @@ import {convertCamelToUnderscore, fill} from '../libraries/appnexusUtils/anUtils import {convertTypes} from '../libraries/transformParamsUtils/convertTypes.js'; import {chunk} from '../libraries/chunk/chunk.js'; +/** + * @typedef {import('../src/adapters/bidderFactory.js').BidRequest} BidRequest + * @typedef {import('../src/adapters/bidderFactory.js').Bid} Bid + */ + const BIDDER_CODE = 'goldbach'; const URL = 'https://ib.adnxs.com/ut/v3/prebid'; const PRICING_URL = 'https://templates.da-services.ch/01_universal/burda_prebid/1.0/json/sizeCPMMapping.json'; diff --git a/modules/gothamadsBidAdapter.js b/modules/gothamadsBidAdapter.js index 9f44a54460f..ab59c6febec 100644 --- a/modules/gothamadsBidAdapter.js +++ b/modules/gothamadsBidAdapter.js @@ -4,6 +4,11 @@ import { BANNER, NATIVE, VIDEO } from '../src/mediaTypes.js'; import { config } from '../src/config.js'; import { convertOrtbRequestToProprietaryNative } from '../src/native.js'; +/** + * @typedef {import('../src/adapters/bidderFactory.js').BidRequest} BidRequest + * @typedef {import('../src/adapters/bidderFactory.js').Bid} Bid + */ + const BIDDER_CODE = 'gothamads'; const ACCOUNTID_MACROS = '[account_id]'; const URL_ENDPOINT = `https://us-e-node1.gothamads.com/bid?pass=${ACCOUNTID_MACROS}&integration=prebidjs`; diff --git a/modules/gridBidAdapter.js b/modules/gridBidAdapter.js index 9673633a0fe..b2350c0a61c 100644 --- a/modules/gridBidAdapter.js +++ b/modules/gridBidAdapter.js @@ -16,6 +16,12 @@ import { VIDEO, BANNER } from '../src/mediaTypes.js'; import { config } from '../src/config.js'; import { getStorageManager } from '../src/storageManager.js'; +/** + * @typedef {import('../src/adapters/bidderFactory.js').BidRequest} BidRequest + * @typedef {import('../src/adapters/bidderFactory.js').Bid} Bid + * @typedef {import('../src/adapters/bidderFactory.js').ServerRequest} ServerRequest + */ + const BIDDER_CODE = 'grid'; const ENDPOINT_URL = 'https://grid.bidswitch.net/hbjson'; const USP_DELETE_DATA_HANDLER = 'https://media.grid.bidswitch.net/uspapi_delete_c2s' diff --git a/modules/gumgumBidAdapter.js b/modules/gumgumBidAdapter.js index deee906298e..15af94494a3 100644 --- a/modules/gumgumBidAdapter.js +++ b/modules/gumgumBidAdapter.js @@ -6,6 +6,15 @@ import {getStorageManager} from '../src/storageManager.js'; import {includes} from '../src/polyfill.js'; import {registerBidder} from '../src/adapters/bidderFactory.js'; +/** + * @typedef {import('../src/adapters/bidderFactory.js').BidRequest} BidRequest + * @typedef {import('../src/adapters/bidderFactory.js').Bid} Bid + * @typedef {import('../src/adapters/bidderFactory.js').ServerResponse} ServerResponse + * @typedef {import('../src/adapters/bidderFactory.js').SyncOptions} SyncOptions + * @typedef {import('../src/adapters/bidderFactory.js').UserSync} UserSync + * @typedef {import('../src/adapters/bidderFactory.js').validBidRequests} validBidRequests + */ + const BIDDER_CODE = 'gumgum'; const storage = getStorageManager({bidderCode: BIDDER_CODE}); const ALIAS_BIDDER_CODE = ['gg']; diff --git a/modules/hybridBidAdapter.js b/modules/hybridBidAdapter.js index f746e69cbba..00e109516e5 100644 --- a/modules/hybridBidAdapter.js +++ b/modules/hybridBidAdapter.js @@ -5,6 +5,13 @@ import {BANNER, VIDEO} from '../src/mediaTypes.js'; import {Renderer} from '../src/Renderer.js'; import {find} from '../src/polyfill.js'; +/** + * @typedef {import('../src/adapters/bidderFactory.js').BidRequest} BidRequest + * @typedef {import('../src/adapters/bidderFactory.js').Bid} Bid + * @typedef {import('../src/adapters/bidderFactory.js').ServerResponse} ServerResponse + * @typedef {import('../src/adapters/bidderFactory.js').validBidRequests} validBidRequests + */ + const BIDDER_CODE = 'hybrid'; const DSP_ENDPOINT = 'https://hbe198.hybrid.ai/prebidhb'; const TRAFFIC_TYPE_WEB = 1; diff --git a/modules/impactifyBidAdapter.js b/modules/impactifyBidAdapter.js index 4c0b9801a36..ea446bd150d 100644 --- a/modules/impactifyBidAdapter.js +++ b/modules/impactifyBidAdapter.js @@ -6,6 +6,14 @@ import { config } from '../src/config.js'; import { ajax } from '../src/ajax.js'; import { getStorageManager } from '../src/storageManager.js'; +/** + * @typedef {import('../src/adapters/bidderFactory.js').BidRequest} BidRequest + * @typedef {import('../src/adapters/bidderFactory.js').Bid} Bid + * @typedef {import('../src/adapters/bidderFactory.js').ServerResponse} ServerResponse + * @typedef {import('../src/adapters/bidderFactory.js').SyncOptions} SyncOptions + * @typedef {import('../src/adapters/bidderFactory.js').UserSync} UserSync + */ + const BIDDER_CODE = 'impactify'; const BIDDER_ALIAS = ['imp']; const DEFAULT_CURRENCY = 'USD'; diff --git a/modules/improvedigitalBidAdapter.js b/modules/improvedigitalBidAdapter.js index b563faf52ac..3a258dfa327 100644 --- a/modules/improvedigitalBidAdapter.js +++ b/modules/improvedigitalBidAdapter.js @@ -7,6 +7,14 @@ import {hasPurpose1Consent} from '../src/utils/gpdr.js'; import {ortbConverter} from '../libraries/ortbConverter/converter.js'; import {loadExternalScript} from '../src/adloader.js'; +/** + * @typedef {import('../src/adapters/bidderFactory.js').BidRequest} BidRequest + * @typedef {import('../src/adapters/bidderFactory.js').Bid} Bid + * @typedef {import('../src/adapters/bidderFactory.js').ServerResponse} ServerResponse + * @typedef {import('../src/adapters/bidderFactory.js').SyncOptions} SyncOptions + * @typedef {import('../src/adapters/bidderFactory.js').UserSync} UserSync + */ + const BIDDER_CODE = 'improvedigital'; const CREATIVE_TTL = 300; diff --git a/modules/incrxBidAdapter.js b/modules/incrxBidAdapter.js index 46be7fb75d6..9b939aff11b 100644 --- a/modules/incrxBidAdapter.js +++ b/modules/incrxBidAdapter.js @@ -2,6 +2,12 @@ import { parseSizesInput, isEmpty } from '../src/utils.js'; import { registerBidder } from '../src/adapters/bidderFactory.js'; import { BANNER } from '../src/mediaTypes.js' +/** + * @typedef {import('../src/adapters/bidderFactory.js').BidRequest} BidRequest + * @typedef {import('../src/adapters/bidderFactory.js').Bid} Bid + * @typedef {import('../src/adapters/bidderFactory.js').ServerResponse} ServerResponse + */ + const BIDDER_CODE = 'incrementx'; const ENDPOINT_URL = 'https://hb.incrementxserv.com/vzhbidder/bid'; const DEFAULT_CURRENCY = 'USD'; diff --git a/modules/instreamTracking.js b/modules/instreamTracking.js index ff8305c7fed..ece556d0fd2 100644 --- a/modules/instreamTracking.js +++ b/modules/instreamTracking.js @@ -5,6 +5,12 @@ import { INSTREAM } from '../src/video.js'; import * as events from '../src/events.js'; import CONSTANTS from '../src/constants.json' +/** + * @typedef {import('../src/adapters/bidderFactory.js').BidRequest} BidRequest + * @typedef {import('../src/adapters/bidderFactory.js').Bid} Bid + * @typedef {import('../src/adapters/bidderFactory.js').AdUnit} AdUnit + */ + const {CACHE_ID, UUID} = CONSTANTS.TARGETING_KEYS; const {BID_WON, AUCTION_END} = CONSTANTS.EVENTS; const {RENDERED} = CONSTANTS.BID_STATUS; diff --git a/modules/integr8BidAdapter.js b/modules/integr8BidAdapter.js index 79c6533d122..949483ea7bf 100644 --- a/modules/integr8BidAdapter.js +++ b/modules/integr8BidAdapter.js @@ -3,6 +3,13 @@ import { registerBidder } from '../src/adapters/bidderFactory.js'; import { getStorageManager } from '../src/storageManager.js'; import { BANNER, VIDEO } from '../src/mediaTypes.js'; +/** + * @typedef {import('../src/adapters/bidderFactory.js').BidRequest} BidRequest + * @typedef {import('../src/adapters/bidderFactory.js').Bid} Bid + * @typedef {import('../src/adapters/bidderFactory.js').ServerResponse} ServerResponse + * @typedef {import('../src/adapters/bidderFactory.js').validBidRequests} validBidRequests + */ + const BIDDER_CODE = 'integr8'; const DEFAULT_ENDPOINT_URL = 'https://central.sea.integr8.digital/bid'; const DIMENSION_SEPARATOR = 'x'; diff --git a/modules/invamiaBidAdapter.js b/modules/invamiaBidAdapter.js index 2d36fb77e16..96af163ca4f 100644 --- a/modules/invamiaBidAdapter.js +++ b/modules/invamiaBidAdapter.js @@ -1,6 +1,11 @@ import {registerBidder} from '../src/adapters/bidderFactory.js'; import {BANNER} from '../src/mediaTypes.js'; +/** + * @typedef {import('../src/adapters/bidderFactory.js').BidRequest} BidRequest + * @typedef {import('../src/adapters/bidderFactory.js').ServerResponse} ServerResponse + */ + const BIDDER_CODE = 'invamia'; const ENDPOINT_URL = 'https://ad.invamia.com/delivery/impress'; diff --git a/modules/invibesBidAdapter.js b/modules/invibesBidAdapter.js index 1d608d7136b..2c37c0edad9 100644 --- a/modules/invibesBidAdapter.js +++ b/modules/invibesBidAdapter.js @@ -2,6 +2,11 @@ import {logInfo} from '../src/utils.js'; import {registerBidder} from '../src/adapters/bidderFactory.js'; import {getStorageManager} from '../src/storageManager.js'; +/** + * @typedef {import('../src/adapters/bidderFactory.js').BidRequest} BidRequest + * @typedef {import('../src/adapters/bidderFactory.js').Bid} Bid + */ + const CONSTANTS = { BIDDER_CODE: 'invibes', BID_ENDPOINT: '.videostep.com/Bid/VideoAdContent', diff --git a/modules/iqmBidAdapter.js b/modules/iqmBidAdapter.js index c3808afd225..c94a88748a7 100644 --- a/modules/iqmBidAdapter.js +++ b/modules/iqmBidAdapter.js @@ -3,6 +3,10 @@ import {registerBidder} from '../src/adapters/bidderFactory.js'; import {BANNER, VIDEO} from '../src/mediaTypes.js'; import {INSTREAM} from '../src/video.js'; +/** + * @typedef {import('../src/adapters/bidderFactory.js').BidRequest} BidRequest + */ + const BIDDER_CODE = 'iqm'; const VERSION = 'v.1.0.0'; const VIDEO_ORTB_PARAMS = [ diff --git a/modules/ivsBidAdapter.js b/modules/ivsBidAdapter.js index 6f4c024f09f..3deebf9bff3 100644 --- a/modules/ivsBidAdapter.js +++ b/modules/ivsBidAdapter.js @@ -4,6 +4,13 @@ import { registerBidder } from '../src/adapters/bidderFactory.js'; import { VIDEO } from '../src/mediaTypes.js'; import { INSTREAM } from '../src/video.js'; +/** + * @typedef {import('../src/adapters/bidderFactory.js').BidRequest} BidRequest + * @typedef {import('../src/adapters/bidderFactory.js').Bid} Bid + * @typedef {import('../src/adapters/bidderFactory.js').ServerResponse} ServerResponse + * @typedef {import('../src/adapters/bidderFactory.js').validBidRequests} validBidRequests + */ + const BIDDER_CODE = 'ivs'; const ENDPOINT_URL = 'https://a.ivstracker.net/prod/openrtb/2.5'; diff --git a/modules/lemmaDigitalBidAdapter.js b/modules/lemmaDigitalBidAdapter.js index 8a3b05b7ed3..dde7c25d9b9 100644 --- a/modules/lemmaDigitalBidAdapter.js +++ b/modules/lemmaDigitalBidAdapter.js @@ -3,6 +3,15 @@ import { config } from '../src/config.js'; import { registerBidder } from '../src/adapters/bidderFactory.js'; import { BANNER, VIDEO } from '../src/mediaTypes.js'; +/** + * @typedef {import('../src/adapters/bidderFactory.js').BidRequest} BidRequest + * @typedef {import('../src/adapters/bidderFactory.js').Bid} Bid + * @typedef {import('../src/adapters/bidderFactory.js').ServerResponse} ServerResponse + * @typedef {import('../src/adapters/bidderFactory.js').SyncOptions} SyncOptions + * @typedef {import('../src/adapters/bidderFactory.js').UserSync} UserSync + * @typedef {import('../src/adapters/bidderFactory.js').validBidRequests} validBidRequests + */ + var BIDDER_CODE = 'lemmadigital'; var LOG_WARN_PREFIX = 'LEMMADIGITAL: '; var ENDPOINT = 'https://bid.lemmadigital.com/lemma/servad'; diff --git a/modules/lifestreetBidAdapter.js b/modules/lifestreetBidAdapter.js index 6a8b783ce21..5b5eb639fcf 100644 --- a/modules/lifestreetBidAdapter.js +++ b/modules/lifestreetBidAdapter.js @@ -2,6 +2,10 @@ import { isInteger } from '../src/utils.js'; import { registerBidder } from '../src/adapters/bidderFactory.js'; import { BANNER, VIDEO } from '../src/mediaTypes.js'; +/** + * @typedef {import('../src/adapters/bidderFactory.js').BidRequest} BidRequest + */ + const BIDDER_CODE = 'lifestreet'; const ADAPTER_VERSION = '$prebid.version$'; diff --git a/modules/limelightDigitalBidAdapter.js b/modules/limelightDigitalBidAdapter.js index 0eb9e900160..acc76014abe 100644 --- a/modules/limelightDigitalBidAdapter.js +++ b/modules/limelightDigitalBidAdapter.js @@ -3,6 +3,12 @@ import { registerBidder } from '../src/adapters/bidderFactory.js'; import { BANNER, VIDEO } from '../src/mediaTypes.js'; import { ajax } from '../src/ajax.js'; +/** + * @typedef {import('../src/adapters/bidderFactory.js').BidRequest} BidRequest + * @typedef {import('../src/adapters/bidderFactory.js').Bid} Bid + * @typedef {import('../src/adapters/bidderFactory.js').ServerResponse} ServerResponse + */ + const BIDDER_CODE = 'limelightDigital'; /** diff --git a/modules/livewrappedBidAdapter.js b/modules/livewrappedBidAdapter.js index 82affe40e03..cf1e690d862 100644 --- a/modules/livewrappedBidAdapter.js +++ b/modules/livewrappedBidAdapter.js @@ -6,6 +6,11 @@ import {BANNER, NATIVE, VIDEO} from '../src/mediaTypes.js'; import {getStorageManager} from '../src/storageManager.js'; import { convertOrtbRequestToProprietaryNative } from '../src/native.js'; +/** + * @typedef {import('../src/adapters/bidderFactory.js').BidRequest} BidRequest + * @typedef {import('../src/adapters/bidderFactory.js').Bid} Bid + */ + const BIDDER_CODE = 'livewrapped'; export const storage = getStorageManager({bidderCode: BIDDER_CODE}); export const URL = 'https://lwadm.com/ad'; diff --git a/modules/lm_kiviadsBidAdapter.js b/modules/lm_kiviadsBidAdapter.js index 9ba26052727..7c3085047c4 100644 --- a/modules/lm_kiviadsBidAdapter.js +++ b/modules/lm_kiviadsBidAdapter.js @@ -4,6 +4,14 @@ import {registerBidder} from '../src/adapters/bidderFactory.js'; import {parseSizesInput, isFn, deepAccess, getBidIdParameter, logError, isArray} from '../src/utils.js'; import {getAdUnitSizes} from '../libraries/sizeUtils/sizeUtils.js'; +/** + * @typedef {import('../src/adapters/bidderFactory.js').BidRequest} BidRequest + * @typedef {import('../src/adapters/bidderFactory.js').Bid} Bid + * @typedef {import('../src/adapters/bidderFactory.js').ServerResponse} ServerResponse + * @typedef {import('../src/adapters/bidderFactory.js').SyncOptions} SyncOptions + * @typedef {import('../src/adapters/bidderFactory.js').UserSync} UserSync + */ + const CUR = 'USD'; const BIDDER_CODE = 'lm_kiviads'; const ENDPOINT = 'https://pbjs.kiviads.live'; diff --git a/modules/madvertiseBidAdapter.js b/modules/madvertiseBidAdapter.js index 457ff2409b8..3b031623aef 100644 --- a/modules/madvertiseBidAdapter.js +++ b/modules/madvertiseBidAdapter.js @@ -2,6 +2,11 @@ import { parseSizesInput, _each } from '../src/utils.js'; import {config} from '../src/config.js'; import {registerBidder} from '../src/adapters/bidderFactory.js'; +/** + * @typedef {import('../src/adapters/bidderFactory.js').BidRequest} BidRequest + * @typedef {import('../src/adapters/bidderFactory.js').Bid} Bid + */ + // use protocol relative urls for http or https const MADVERTISE_ENDPOINT = 'https://mobile.mng-ads.com/'; diff --git a/modules/malltvBidAdapter.js b/modules/malltvBidAdapter.js index 1d50ad4e4c1..67c8a4aec07 100644 --- a/modules/malltvBidAdapter.js +++ b/modules/malltvBidAdapter.js @@ -2,6 +2,13 @@ import { registerBidder } from '../src/adapters/bidderFactory.js'; import { getStorageManager } from '../src/storageManager.js'; import { BANNER, VIDEO } from '../src/mediaTypes.js'; +/** + * @typedef {import('../src/adapters/bidderFactory.js').BidRequest} BidRequest + * @typedef {import('../src/adapters/bidderFactory.js').Bid} Bid + * @typedef {import('../src/adapters/bidderFactory.js').ServerResponse} ServerResponse + * @typedef {import('../src/adapters/bidderFactory.js').validBidRequests} validBidRequests + */ + const BIDDER_CODE = 'malltv'; const ENDPOINT_URL = 'https://central.mall.tv/bid'; const DIMENSION_SEPARATOR = 'x'; diff --git a/modules/mediaforceBidAdapter.js b/modules/mediaforceBidAdapter.js index 3d33bbf8c12..9f899974721 100644 --- a/modules/mediaforceBidAdapter.js +++ b/modules/mediaforceBidAdapter.js @@ -3,6 +3,12 @@ import {registerBidder} from '../src/adapters/bidderFactory.js'; import {BANNER, NATIVE} from '../src/mediaTypes.js'; import { convertOrtbRequestToProprietaryNative } from '../src/native.js'; +/** + * @typedef {import('../src/adapters/bidderFactory.js').BidRequest} BidRequest + * @typedef {import('../src/adapters/bidderFactory.js').Bid} Bid + * @typedef {import('../src/adapters/bidderFactory.js').ServerResponse} ServerResponse + */ + 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'; diff --git a/modules/mediafuseBidAdapter.js b/modules/mediafuseBidAdapter.js index 1fdd3530fae..5e7221583a8 100644 --- a/modules/mediafuseBidAdapter.js +++ b/modules/mediafuseBidAdapter.js @@ -37,6 +37,11 @@ import {convertCamelToUnderscore, fill} from '../libraries/appnexusUtils/anUtils import {convertTypes} from '../libraries/transformParamsUtils/convertTypes.js'; import {chunk} from '../libraries/chunk/chunk.js'; +/** + * @typedef {import('../src/adapters/bidderFactory.js').BidRequest} BidRequest + * @typedef {import('../src/adapters/bidderFactory.js').Bid} Bid + */ + const BIDDER_CODE = 'mediafuse'; const URL = 'https://ib.adnxs.com/ut/v3/prebid'; const URL_SIMPLE = 'https://ib.adnxs-simple.com/ut/v3/prebid'; diff --git a/modules/mediagoBidAdapter.js b/modules/mediagoBidAdapter.js index 8513fba94c4..8f687d30ff3 100644 --- a/modules/mediagoBidAdapter.js +++ b/modules/mediagoBidAdapter.js @@ -8,6 +8,13 @@ import { registerBidder } from '../src/adapters/bidderFactory.js'; // import { config } from '../src/config.js'; // import { isPubcidEnabled } from './pubCommonId.js'; +/** + * @typedef {import('../src/adapters/bidderFactory.js').BidRequest} BidRequest + * @typedef {import('../src/adapters/bidderFactory.js').Bid} Bid + * @typedef {import('../src/adapters/bidderFactory.js').ServerResponse} ServerResponse + * @typedef {import('../src/adapters/bidderFactory.js').mediaType} mediaType + */ + const BIDDER_CODE = 'mediago'; // const PROTOCOL = window.document.location.protocol; const ENDPOINT_URL = 'https://gbid.mediago.io/api/bid?tn='; diff --git a/modules/mediakeysBidAdapter.js b/modules/mediakeysBidAdapter.js index 7af43a3c549..f4967fed170 100644 --- a/modules/mediakeysBidAdapter.js +++ b/modules/mediakeysBidAdapter.js @@ -119,7 +119,7 @@ function getOS() { * * @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 '*' + * @param {string|Array} size the size array or the wildcard '*' * @returns {number|boolean} */ function getFloor(bid, mediaType, size = '*') { diff --git a/modules/medianetBidAdapter.js b/modules/medianetBidAdapter.js index 041db71cd34..6a8a35dbfd4 100644 --- a/modules/medianetBidAdapter.js +++ b/modules/medianetBidAdapter.js @@ -19,6 +19,12 @@ import { convertOrtbRequestToProprietaryNative } from '../src/native.js'; import {getGlobal} from '../src/prebidGlobal.js'; import {getGptSlotInfoForAdUnitCode} from '../libraries/gptUtils/gptUtils.js'; +/** + * @typedef {import('../src/adapters/bidderFactory.js').BidRequest} BidRequest + * @typedef {import('../src/adapters/bidderFactory.js').Bid} Bid + * @typedef {import('../src/adapters/bidderFactory.js').TimedOutBid} TimedOutBid + */ + const BIDDER_CODE = 'medianet'; const TRUSTEDSTACK_CODE = 'trustedstack'; const BID_URL = 'https://prebid.media.net/rtb/prebid'; diff --git a/modules/mediasniperBidAdapter.js b/modules/mediasniperBidAdapter.js index aee5f6230b2..5cf0ceaba18 100644 --- a/modules/mediasniperBidAdapter.js +++ b/modules/mediasniperBidAdapter.js @@ -241,7 +241,7 @@ function createImp(bid) { * * @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 '*' + * @param {string|Array} size the size array or the wildcard '*' * @returns {number|boolean} */ function getFloor(bid, mediaType, size = '*') { diff --git a/modules/mediasquareBidAdapter.js b/modules/mediasquareBidAdapter.js index fb580d81b94..550c715e741 100644 --- a/modules/mediasquareBidAdapter.js +++ b/modules/mediasquareBidAdapter.js @@ -6,6 +6,15 @@ import { convertOrtbRequestToProprietaryNative } from '../src/native.js'; import {Renderer} from '../src/Renderer.js'; import { getRefererInfo } from '../src/refererDetection.js'; +/** + * @typedef {import('../src/adapters/bidderFactory.js').BidRequest} BidRequest + * @typedef {import('../src/adapters/bidderFactory.js').Bid} Bid + * @typedef {import('../src/adapters/bidderFactory.js').ServerResponse} ServerResponse + * @typedef {import('../src/adapters/bidderFactory.js').SyncOptions} SyncOptions + * @typedef {import('../src/adapters/bidderFactory.js').UserSync} UserSync + * @typedef {import('../src/adapters/bidderFactory.js').validBidRequests} validBidRequests + */ + const BIDDER_CODE = 'mediasquare'; const BIDDER_URL_PROD = 'https://pbs-front.mediasquare.fr/' const BIDDER_URL_TEST = 'https://bidder-test.mediasquare.fr/' diff --git a/modules/mgidBidAdapter.js b/modules/mgidBidAdapter.js index 1e158236deb..fb3990e97f1 100644 --- a/modules/mgidBidAdapter.js +++ b/modules/mgidBidAdapter.js @@ -21,6 +21,12 @@ import { getStorageManager } from '../src/storageManager.js'; import { convertOrtbRequestToProprietaryNative } from '../src/native.js'; import {USERSYNC_DEFAULT_CONFIG} from '../src/userSync.js'; +/** + * @typedef {import('../src/adapters/bidderFactory.js').BidRequest} BidRequest + * @typedef {import('../src/adapters/bidderFactory.js').Bid} Bid + * @typedef {import('../src/adapters/bidderFactory.js').ServerResponse} ServerResponse + */ + const GVLID = 358; const DEFAULT_CUR = 'USD'; const BIDDER_CODE = 'mgid'; diff --git a/modules/missenaBidAdapter.js b/modules/missenaBidAdapter.js index 080ed2a556f..527b6704146 100644 --- a/modules/missenaBidAdapter.js +++ b/modules/missenaBidAdapter.js @@ -11,6 +11,13 @@ import { BANNER } from '../src/mediaTypes.js'; import { registerBidder } from '../src/adapters/bidderFactory.js'; import { getStorageManager } from '../src/storageManager.js'; +/** + * @typedef {import('../src/adapters/bidderFactory.js').BidRequest} BidRequest + * @typedef {import('../src/adapters/bidderFactory.js').Bid} Bid + * @typedef {import('../src/adapters/bidderFactory.js').ServerResponse} ServerResponse + * @typedef {import('../src/adapters/bidderFactory.js').validBidRequests} validBidRequests + */ + const BIDDER_CODE = 'missena'; const ENDPOINT_URL = 'https://bid.missena.io/'; const EVENTS_DOMAIN = 'events.missena.io'; diff --git a/modules/nextrollBidAdapter.js b/modules/nextrollBidAdapter.js index eab174d22dd..8a41efe4dcc 100644 --- a/modules/nextrollBidAdapter.js +++ b/modules/nextrollBidAdapter.js @@ -14,6 +14,12 @@ import { convertOrtbRequestToProprietaryNative } from '../src/native.js'; import {find} from '../src/polyfill.js'; +/** + * @typedef {import('../src/adapters/bidderFactory.js').BidRequest} BidRequest + * @typedef {import('../src/adapters/bidderFactory.js').Bid} Bid + * @typedef {import('../src/adapters/bidderFactory.js').ServerResponse} ServerResponse + * @typedef {import('../src/adapters/bidderFactory.js').validBidRequests} validBidRequests + */ const BIDDER_CODE = 'nextroll'; const BIDDER_ENDPOINT = 'https://d.adroll.com/bid/prebid/'; const ADAPTER_VERSION = 5; diff --git a/modules/nexx360BidAdapter.js b/modules/nexx360BidAdapter.js index c65544936fa..baadaa272e6 100644 --- a/modules/nexx360BidAdapter.js +++ b/modules/nexx360BidAdapter.js @@ -8,6 +8,15 @@ import {getGlobal} from '../src/prebidGlobal.js'; import {ortbConverter} from '../libraries/ortbConverter/converter.js' import { INSTREAM, OUTSTREAM } from '../src/video.js'; +/** + * @typedef {import('../src/adapters/bidderFactory.js').BidRequest} BidRequest + * @typedef {import('../src/adapters/bidderFactory.js').Bid} Bid + * @typedef {import('../src/adapters/bidderFactory.js').ServerResponse} ServerResponse + * @typedef {import('../src/adapters/bidderFactory.js').SyncOptions} SyncOptions + * @typedef {import('../src/adapters/bidderFactory.js').UserSync} UserSync + * @typedef {import('../src/adapters/bidderFactory.js').validBidRequests} validBidRequests + */ + const OUTSTREAM_RENDERER_URL = 'https://acdn.adnxs.com/video/outstream/ANOutstreamVideo.js'; const BIDDER_CODE = 'nexx360'; diff --git a/modules/nobidBidAdapter.js b/modules/nobidBidAdapter.js index 77de3c6d97b..28fb38e14e5 100644 --- a/modules/nobidBidAdapter.js +++ b/modules/nobidBidAdapter.js @@ -5,6 +5,15 @@ import { BANNER, VIDEO } from '../src/mediaTypes.js'; import { getStorageManager } from '../src/storageManager.js'; import { hasPurpose1Consent } from '../src/utils/gpdr.js'; +/** + * @typedef {import('../src/adapters/bidderFactory.js').BidRequest} BidRequest + * @typedef {import('../src/adapters/bidderFactory.js').Bid} Bid + * @typedef {import('../src/adapters/bidderFactory.js').ServerResponse} ServerResponse + * @typedef {import('../src/adapters/bidderFactory.js').SyncOptions} SyncOptions + * @typedef {import('../src/adapters/bidderFactory.js').UserSync} UserSync + * @typedef {import('../src/adapters/bidderFactory.js').validBidRequests} validBidRequests + */ + const GVLID = 816; const BIDDER_CODE = 'nobid'; const storage = getStorageManager({bidderCode: BIDDER_CODE}); diff --git a/modules/onetagBidAdapter.js b/modules/onetagBidAdapter.js index ee6d2980385..eb9fa2eb536 100644 --- a/modules/onetagBidAdapter.js +++ b/modules/onetagBidAdapter.js @@ -8,6 +8,11 @@ import { getStorageManager } from '../src/storageManager.js'; import { registerBidder } from '../src/adapters/bidderFactory.js'; import { deepClone, logError, deepAccess } from '../src/utils.js'; +/** + * @typedef {import('../src/adapters/bidderFactory.js').BidRequest} BidRequest + * @typedef {import('../src/adapters/bidderFactory.js').validBidRequests} validBidRequests + */ + const ENDPOINT = 'https://onetag-sys.com/prebid-request'; const USER_SYNC_ENDPOINT = 'https://onetag-sys.com/usync/'; const BIDDER_CODE = 'onetag'; diff --git a/modules/operaadsBidAdapter.js b/modules/operaadsBidAdapter.js index 131ba0bc1f2..957192d1bec 100644 --- a/modules/operaadsBidAdapter.js +++ b/modules/operaadsBidAdapter.js @@ -18,6 +18,13 @@ import {Renderer} from '../src/Renderer.js'; import {OUTSTREAM} from '../src/video.js'; import {convertOrtbRequestToProprietaryNative} from '../src/native.js'; +/** + * @typedef {import('../src/adapters/bidderFactory.js').BidRequest} BidRequest + * @typedef {import('../src/adapters/bidderFactory.js').Bid} Bid + * @typedef {import('../src/adapters/bidderFactory.js').ServerResponse} ServerResponse + * @typedef {import('../src/adapters/bidderFactory.js').SyncOptions} SyncOptions + * @typedef {import('../src/adapters/bidderFactory.js').UserSync} UserSync + */ const BIDDER_CODE = 'operaads'; const ENDPOINT = 'https://s.adx.opera.com/ortb/v2/'; diff --git a/modules/optidigitalBidAdapter.js b/modules/optidigitalBidAdapter.js index 152876b8d5d..27b858c84fe 100755 --- a/modules/optidigitalBidAdapter.js +++ b/modules/optidigitalBidAdapter.js @@ -3,6 +3,15 @@ import {BANNER} from '../src/mediaTypes.js'; import {deepAccess, parseSizesInput} from '../src/utils.js'; import {getAdUnitSizes} from '../libraries/sizeUtils/sizeUtils.js'; +/** + * @typedef {import('../src/adapters/bidderFactory.js').BidRequest} BidRequest + * @typedef {import('../src/adapters/bidderFactory.js').Bid} Bid + * @typedef {import('../src/adapters/bidderFactory.js').ServerResponse} ServerResponse + * @typedef {import('../src/adapters/bidderFactory.js').SyncOptions} SyncOptions + * @typedef {import('../src/adapters/bidderFactory.js').UserSync} UserSync + * @typedef {import('../src/adapters/bidderFactory.js').validBidRequests} validBidRequests + */ + const BIDDER_CODE = 'optidigital'; const GVL_ID = 915; const ENDPOINT_URL = 'https://pbs.optidigital.com/bidder'; diff --git a/modules/orbidderBidAdapter.js b/modules/orbidderBidAdapter.js index efc2effdd62..0f912384db7 100644 --- a/modules/orbidderBidAdapter.js +++ b/modules/orbidderBidAdapter.js @@ -5,6 +5,10 @@ import { BANNER, NATIVE } from '../src/mediaTypes.js'; import { convertOrtbRequestToProprietaryNative } from '../src/native.js'; import { getGlobal } from '../src/prebidGlobal.js'; +/** + * @typedef {import('../src/adapters/bidderFactory.js').BidRequest} BidRequest + * @typedef {import('../src/adapters/bidderFactory.js').Bid} Bid + */ const storageManager = getStorageManager({ bidderCode: 'orbidder' }); /** diff --git a/modules/pilotxBidAdapter.js b/modules/pilotxBidAdapter.js index bd3612d6429..0fb39e19076 100644 --- a/modules/pilotxBidAdapter.js +++ b/modules/pilotxBidAdapter.js @@ -1,4 +1,12 @@ import { registerBidder } from '../src/adapters/bidderFactory.js'; + +/** + * @typedef {import('../src/adapters/bidderFactory.js').BidRequest} BidRequest + * @typedef {import('../src/adapters/bidderFactory.js').Bid} Bid + * @typedef {import('../src/adapters/bidderFactory.js').ServerResponse} ServerResponse + * @typedef {import('../src/adapters/bidderFactory.js').validBidRequests} validBidRequests + */ + const BIDDER_CODE = 'pilotx'; const ENDPOINT_URL = '//adn.pilotx.tv/hb' export const spec = { diff --git a/modules/prismaBidAdapter.js b/modules/prismaBidAdapter.js index 931c5e7d7e6..4b4b5677cb3 100644 --- a/modules/prismaBidAdapter.js +++ b/modules/prismaBidAdapter.js @@ -4,6 +4,15 @@ import {registerBidder} from '../src/adapters/bidderFactory.js'; import {BANNER, VIDEO} from '../src/mediaTypes.js'; import {getANKeywordParam} from '../libraries/appnexusUtils/anKeywords.js'; +/** + * @typedef {import('../src/adapters/bidderFactory.js').BidRequest} BidRequest + * @typedef {import('../src/adapters/bidderFactory.js').Bid} Bid + * @typedef {import('../src/adapters/bidderFactory.js').ServerResponse} ServerResponse + * @typedef {import('../src/adapters/bidderFactory.js').SyncOptions} SyncOptions + * @typedef {import('../src/adapters/bidderFactory.js').UserSync} UserSync + * @typedef {import('../src/adapters/bidderFactory.js').validBidRequests} validBidRequests + */ + const BIDDER_CODE = 'prisma'; const BIDDER_URL = 'https://prisma.nexx360.io/prebid'; const CACHE_URL = 'https://prisma.nexx360.io/cache'; diff --git a/modules/pubmaticBidAdapter.js b/modules/pubmaticBidAdapter.js index 192c657d976..d25627a7b90 100644 --- a/modules/pubmaticBidAdapter.js +++ b/modules/pubmaticBidAdapter.js @@ -7,6 +7,12 @@ import { bidderSettings } from '../src/bidderSettings.js'; import CONSTANTS from '../src/constants.json'; import {convertTypes} from '../libraries/transformParamsUtils/convertTypes.js'; +/** + * @typedef {import('../src/adapters/bidderFactory.js').BidRequest} BidRequest + * @typedef {import('../src/adapters/bidderFactory.js').Bid} Bid + * @typedef {import('../src/adapters/bidderFactory.js').validBidRequests} validBidRequests + */ + const BIDDER_CODE = 'pubmatic'; const LOG_WARN_PREFIX = 'PubMatic: '; const ENDPOINT = 'https://hbopenbid.pubmatic.com/translator?source=prebid-client'; diff --git a/modules/pubwiseBidAdapter.js b/modules/pubwiseBidAdapter.js index 6a5d866c76d..507df4a2bb0 100644 --- a/modules/pubwiseBidAdapter.js +++ b/modules/pubwiseBidAdapter.js @@ -6,6 +6,13 @@ import { config } from '../src/config.js'; import { convertOrtbRequestToProprietaryNative } from '../src/native.js'; import { OUTSTREAM, INSTREAM } from '../src/video.js'; +/** + * @typedef {import('../src/adapters/bidderFactory.js').BidRequest} BidRequest + * @typedef {import('../src/adapters/bidderFactory.js').Bid} Bid + * @typedef {import('../src/adapters/bidderFactory.js').ServerResponse} ServerResponse + * @typedef {import('../src/adapters/bidderFactory.js').validBidRequests} validBidRequests + */ + const VERSION = '0.3.0'; const GVLID = 842; const NET_REVENUE = true; diff --git a/modules/pxyzBidAdapter.js b/modules/pxyzBidAdapter.js index 1ab432496a3..8b9dbea339b 100644 --- a/modules/pxyzBidAdapter.js +++ b/modules/pxyzBidAdapter.js @@ -2,6 +2,11 @@ import {registerBidder} from '../src/adapters/bidderFactory.js'; import {BANNER} from '../src/mediaTypes.js'; import {isArray, logError, logInfo} from '../src/utils.js'; +/** + * @typedef {import('../src/adapters/bidderFactory.js').BidRequest} BidRequest + * @typedef {import('../src/adapters/bidderFactory.js').Bid} Bid + */ + const BIDDER_CODE = 'pxyz'; const URL = 'https://ads.playground.xyz/host-config/prebid?v=2'; const DEFAULT_CURRENCY = 'USD'; diff --git a/modules/quantcastBidAdapter.js b/modules/quantcastBidAdapter.js index 2c721a61616..1ba23302367 100644 --- a/modules/quantcastBidAdapter.js +++ b/modules/quantcastBidAdapter.js @@ -6,6 +6,11 @@ import {registerBidder} from '../src/adapters/bidderFactory.js'; import {find} from '../src/polyfill.js'; import {parseDomain} from '../src/refererDetection.js'; +/** + * @typedef {import('../src/adapters/bidderFactory.js').BidRequest} BidRequest + * @typedef {import('../src/adapters/bidderFactory.js').Bid} Bid + */ + const BIDDER_CODE = 'quantcast'; const DEFAULT_BID_FLOOR = 0.0000000001; diff --git a/modules/radsBidAdapter.js b/modules/radsBidAdapter.js index ae16bcf9d83..faa35ee51f7 100644 --- a/modules/radsBidAdapter.js +++ b/modules/radsBidAdapter.js @@ -2,6 +2,10 @@ import {deepAccess} from '../src/utils.js'; import {registerBidder} from '../src/adapters/bidderFactory.js'; import {BANNER, VIDEO} from '../src/mediaTypes.js'; +/** + * @typedef {import('../src/adapters/bidderFactory.js').BidRequest} BidRequest + */ + 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'; diff --git a/modules/retailspotBidAdapter.js b/modules/retailspotBidAdapter.js index 616b638e840..557dd617274 100644 --- a/modules/retailspotBidAdapter.js +++ b/modules/retailspotBidAdapter.js @@ -2,6 +2,11 @@ import {buildUrl, deepAccess, parseSizesInput} from '../src/utils.js'; import {registerBidder} from '../src/adapters/bidderFactory.js'; import {BANNER, VIDEO} from '../src/mediaTypes.js'; +/** + * @typedef {import('../src/adapters/bidderFactory.js').BidRequest} BidRequest + * @typedef {import('../src/adapters/bidderFactory.js').Bid} Bid + */ + const BIDDER_CODE = 'retailspot'; const DEFAULT_SUBDOMAIN = 'ssp'; const PREPROD_SUBDOMAIN = 'ssp-preprod'; @@ -28,7 +33,7 @@ export const spec = { /** * Make a server request from the list of BidRequests. * - * @param {bidRequests} - bidRequests.bids[] is an array of AdUnits and bids + * @param {BidRequests} - bidRequests.bids[] is an array of AdUnits and bids * @return ServerRequest Info describing the request to the server. */ buildRequests: function (bidRequests, bidderRequest) { diff --git a/modules/rtbsapeBidAdapter.js b/modules/rtbsapeBidAdapter.js index 5b1a92b02a0..502b62c8799 100644 --- a/modules/rtbsapeBidAdapter.js +++ b/modules/rtbsapeBidAdapter.js @@ -4,6 +4,14 @@ import {BANNER, VIDEO} from '../src/mediaTypes.js'; import {OUTSTREAM} from '../src/video.js'; import {Renderer} from '../src/Renderer.js'; +/** + * @typedef {import('../src/adapters/bidderFactory.js').BidRequest} BidRequest + * @typedef {import('../src/adapters/bidderFactory.js').Bid} Bid + * @typedef {import('../src/adapters/bidderFactory.js').ServerResponse} ServerResponse + * @typedef {import('../src/adapters/bidderFactory.js').SyncOptions} SyncOptions + * @typedef {import('../src/adapters/bidderFactory.js').UserSync} UserSync + */ + const BIDDER_CODE = 'rtbsape'; const ENDPOINT = 'https://ssp-rtb.sape.ru/prebid'; const RENDERER_SRC = 'https://cdn-rtb.sape.ru/js/player.js'; diff --git a/modules/rubiconBidAdapter.js b/modules/rubiconBidAdapter.js index 9882eb23ac3..daaf9a14b9f 100644 --- a/modules/rubiconBidAdapter.js +++ b/modules/rubiconBidAdapter.js @@ -23,6 +23,10 @@ import { } from '../src/utils.js'; import {getAllOrtbKeywords} from '../libraries/keywords/keywords.js'; +/** + * @typedef {import('../src/adapters/bidderFactory.js').BidRequest} BidRequest + */ + const DEFAULT_INTEGRATION = 'pbjs_lite'; const DEFAULT_PBS_INTEGRATION = 'pbjs'; const DEFAULT_RENDERER_URL = 'https://video-outstream.rubiconproject.com/apex-2.2.1.js'; diff --git a/modules/seedtagBidAdapter.js b/modules/seedtagBidAdapter.js index 51c326c2954..6f36c8a191e 100644 --- a/modules/seedtagBidAdapter.js +++ b/modules/seedtagBidAdapter.js @@ -3,6 +3,15 @@ import { registerBidder } from '../src/adapters/bidderFactory.js'; import { VIDEO, BANNER } from '../src/mediaTypes.js'; import { config } from '../src/config.js'; +/** + * @typedef {import('../src/adapters/bidderFactory.js').BidRequest} BidRequest + * @typedef {import('../src/adapters/bidderFactory.js').Bid} Bid + * @typedef {import('../src/adapters/bidderFactory.js').ServerResponse} ServerResponse + * @typedef {import('../src/adapters/bidderFactory.js').SyncOptions} SyncOptions + * @typedef {import('../src/adapters/bidderFactory.js').UserSync} UserSync + * @typedef {import('../src/adapters/bidderFactory.js').validBidRequests} validBidRequests + */ + const BIDDER_CODE = 'seedtag'; const SEEDTAG_ALIAS = 'st'; const SEEDTAG_SSP_ENDPOINT = 'https://s.seedtag.com/c/hb/bid'; diff --git a/modules/slimcutBidAdapter.js b/modules/slimcutBidAdapter.js index 9cdb85d4f25..250c1ebb19e 100644 --- a/modules/slimcutBidAdapter.js +++ b/modules/slimcutBidAdapter.js @@ -5,6 +5,13 @@ import { import { ajax } from '../src/ajax.js'; + +/** + * @typedef {import('../src/adapters/bidderFactory.js').BidRequest} BidRequest + * @typedef {import('../src/adapters/bidderFactory.js').Bid} Bid + * @typedef {import('../src/adapters/bidderFactory.js').validBidRequests} validBidRequests + */ + const BIDDER_CODE = 'slimcut'; const ENDPOINT_URL = 'https://sb.freeskreen.com/pbr'; export const spec = { diff --git a/modules/smaatoBidAdapter.js b/modules/smaatoBidAdapter.js index b735953d099..ac0422842d5 100644 --- a/modules/smaatoBidAdapter.js +++ b/modules/smaatoBidAdapter.js @@ -8,6 +8,14 @@ import {getAdUnitSizes} from '../libraries/sizeUtils/sizeUtils.js'; import {fill} from '../libraries/appnexusUtils/anUtils.js'; import {chunk} from '../libraries/chunk/chunk.js'; +/** + * @typedef {import('../src/adapters/bidderFactory.js').BidRequest} BidRequest + * @typedef {import('../src/adapters/bidderFactory.js').Bid} Bid + * @typedef {import('../src/adapters/bidderFactory.js').ServerResponse} ServerResponse + * @typedef {import('../src/adapters/bidderFactory.js').SyncOptions} SyncOptions + * @typedef {import('../src/adapters/bidderFactory.js').UserSync} UserSync + */ + const { NATIVE_IMAGE_TYPES } = CONSTANTS; const BIDDER_CODE = 'smaato'; const SMAATO_ENDPOINT = 'https://prebid.ad.smaato.net/oapi/prebid'; diff --git a/modules/smartadserverBidAdapter.js b/modules/smartadserverBidAdapter.js index 313d466bd2e..9146bba6514 100644 --- a/modules/smartadserverBidAdapter.js +++ b/modules/smartadserverBidAdapter.js @@ -3,6 +3,12 @@ import { BANNER, VIDEO } from '../src/mediaTypes.js'; import { config } from '../src/config.js'; import { registerBidder } from '../src/adapters/bidderFactory.js'; +/** + * @typedef {import('../src/adapters/bidderFactory.js').BidRequest} BidRequest + * @typedef {import('../src/adapters/bidderFactory.js').Bid} Bid + * @typedef {import('../src/adapters/bidderFactory.js').ServerRequest} ServerRequest + */ + const BIDDER_CODE = 'smartadserver'; const GVL_ID = 45; const DEFAULT_FLOOR = 0.0; diff --git a/modules/smartxBidAdapter.js b/modules/smartxBidAdapter.js index 45cc45192ef..8394814365c 100644 --- a/modules/smartxBidAdapter.js +++ b/modules/smartxBidAdapter.js @@ -21,6 +21,12 @@ import { import { VIDEO } from '../src/mediaTypes.js'; + +/** + * @typedef {import('../src/adapters/bidderFactory.js').BidRequest} BidRequest + * @typedef {import('../src/adapters/bidderFactory.js').Bid} Bid + */ + const BIDDER_CODE = 'smartx'; const URL = 'https://bid.sxp.smartclip.net/bid/1000'; const GVLID = 115; diff --git a/modules/smilewantedBidAdapter.js b/modules/smilewantedBidAdapter.js index 09c39e52825..46584e54373 100644 --- a/modules/smilewantedBidAdapter.js +++ b/modules/smilewantedBidAdapter.js @@ -4,6 +4,11 @@ import { config } from '../src/config.js'; import { registerBidder } from '../src/adapters/bidderFactory.js'; import { BANNER, VIDEO } from '../src/mediaTypes.js'; +/** + * @typedef {import('../src/adapters/bidderFactory.js').BidRequest} BidRequest + * @typedef {import('../src/adapters/bidderFactory.js').Bid} Bid + */ + const GVL_ID = 639; export const spec = { diff --git a/modules/sonobiBidAdapter.js b/modules/sonobiBidAdapter.js index 2c84854e507..1ce7665ddfc 100644 --- a/modules/sonobiBidAdapter.js +++ b/modules/sonobiBidAdapter.js @@ -7,6 +7,12 @@ import { userSync } from '../src/userSync.js'; import { bidderSettings } from '../src/bidderSettings.js'; import { getAllOrtbKeywords } from '../libraries/keywords/keywords.js'; import { getGptSlotInfoForAdUnitCode } from '../libraries/gptUtils/gptUtils.js'; + +/** + * @typedef {import('../src/adapters/bidderFactory.js').BidRequest} BidRequest + * @typedef {import('../src/adapters/bidderFactory.js').Bid} Bid + */ + const BIDDER_CODE = 'sonobi'; const STR_ENDPOINT = 'https://apex.go.sonobi.com/trinity.json'; const PAGEVIEW_ID = generateUUID(); diff --git a/modules/sovrnBidAdapter.js b/modules/sovrnBidAdapter.js index 0ff5d842135..e786095874e 100644 --- a/modules/sovrnBidAdapter.js +++ b/modules/sovrnBidAdapter.js @@ -15,6 +15,10 @@ import { VIDEO } from '../src/mediaTypes.js' +/** + * @typedef {import('../src/adapters/bidderFactory.js').Bid} Bid + */ + const ORTB_VIDEO_PARAMS = { 'mimes': (value) => Array.isArray(value) && value.length > 0 && value.every(v => typeof v === 'string'), 'minduration': (value) => isInteger(value), diff --git a/modules/sparteoBidAdapter.js b/modules/sparteoBidAdapter.js index bfb527d46f2..0bccc1ec140 100644 --- a/modules/sparteoBidAdapter.js +++ b/modules/sparteoBidAdapter.js @@ -3,6 +3,10 @@ import { registerBidder } from '../src/adapters/bidderFactory.js'; import { BANNER, VIDEO } from '../src/mediaTypes.js'; import {ortbConverter} from '../libraries/ortbConverter/converter.js' +/** + * @typedef {import('../src/adapters/bidderFactory.js').BidRequest} BidRequest + */ + const BIDDER_CODE = 'sparteo'; const GVLID = 1028; const TTL = 60; diff --git a/modules/spotxBidAdapter.js b/modules/spotxBidAdapter.js index 017544cc596..c1f1c5159fc 100644 --- a/modules/spotxBidAdapter.js +++ b/modules/spotxBidAdapter.js @@ -21,6 +21,12 @@ import { registerBidder } from '../src/adapters/bidderFactory.js'; import { VIDEO } from '../src/mediaTypes.js'; import { loadExternalScript } from '../src/adloader.js'; +/** + * @typedef {import('../src/adapters/bidderFactory.js').BidRequest} BidRequest + * @typedef {import('../src/adapters/bidderFactory.js').Bid} Bid + * @typedef {import('../src/adapters/bidderFactory.js').ServerRequest} ServerRequest + */ + const BIDDER_CODE = 'spotx'; const URL = 'https://search.spotxchange.com/openrtb/2.3/dados/'; const ORTB_VERSION = '2.3'; diff --git a/modules/stvBidAdapter.js b/modules/stvBidAdapter.js index 0dacf389955..5cffc5853b5 100644 --- a/modules/stvBidAdapter.js +++ b/modules/stvBidAdapter.js @@ -3,6 +3,10 @@ import {registerBidder} from '../src/adapters/bidderFactory.js'; import {BANNER, VIDEO} from '../src/mediaTypes.js'; import {includes} from '../src/polyfill.js'; +/** + * @typedef {import('../src/adapters/bidderFactory.js').BidRequest} BidRequest + */ + const BIDDER_CODE = 'stv'; const ENDPOINT_URL = 'https://ads.smartstream.tv/r/'; const ENDPOINT_URL_DEV = 'https://ads.smartstream.tv/r/'; diff --git a/modules/sublimeBidAdapter.js b/modules/sublimeBidAdapter.js index db2b02aaef1..a29265ce9cd 100644 --- a/modules/sublimeBidAdapter.js +++ b/modules/sublimeBidAdapter.js @@ -2,6 +2,12 @@ import { logInfo, generateUUID, formatQS, triggerPixel, deepAccess } from '../sr import { registerBidder } from '../src/adapters/bidderFactory.js'; import { config } from '../src/config.js'; +/** + * @typedef {import('../src/adapters/bidderFactory.js').BidRequest} BidRequest + * @typedef {import('../src/adapters/bidderFactory.js').Bid} Bid + * @typedef {import('../src/adapters/bidderFactory.js').ServerRequest} ServerRequest + */ + const BIDDER_CODE = 'sublime'; const BIDDER_GVLID = 114; const DEFAULT_BID_HOST = 'pbjs.sskzlabs.com'; diff --git a/modules/tappxBidAdapter.js b/modules/tappxBidAdapter.js index 898f6a4185d..f0c275acfb6 100644 --- a/modules/tappxBidAdapter.js +++ b/modules/tappxBidAdapter.js @@ -8,6 +8,10 @@ import { Renderer } from '../src/Renderer.js'; import {parseDomain} from '../src/refererDetection.js'; import {getGlobal} from '../src/prebidGlobal.js'; +/** + * @typedef {import('../src/adapters/bidderFactory.js').BidRequest} BidRequest + */ + const BIDDER_CODE = 'tappx'; const GVLID_CODE = 628; const TTL = 360; diff --git a/modules/targetVideoBidAdapter.js b/modules/targetVideoBidAdapter.js index 1977686dd23..282f322c36a 100644 --- a/modules/targetVideoBidAdapter.js +++ b/modules/targetVideoBidAdapter.js @@ -3,6 +3,11 @@ import {getBidRequest} from '../src/utils.js'; import {BANNER, VIDEO} from '../src/mediaTypes.js'; import {registerBidder} from '../src/adapters/bidderFactory.js'; +/** + * @typedef {import('../src/adapters/bidderFactory.js').BidRequest} BidRequest + * @typedef {import('../src/adapters/bidderFactory.js').Bid} Bid + */ + const SOURCE = 'pbjs'; const BIDDER_CODE = 'targetVideo'; const ENDPOINT_URL = 'https://ib.adnxs.com/ut/v3/prebid'; diff --git a/modules/teadsBidAdapter.js b/modules/teadsBidAdapter.js index ba16c6ddf82..1c12b0e3968 100644 --- a/modules/teadsBidAdapter.js +++ b/modules/teadsBidAdapter.js @@ -2,6 +2,11 @@ import {getValue, logError, deepAccess, parseSizesInput, isArray, getBidIdParame import {registerBidder} from '../src/adapters/bidderFactory.js'; import {getStorageManager} from '../src/storageManager.js'; +/** + * @typedef {import('../src/adapters/bidderFactory.js').BidRequest} BidRequest + * @typedef {import('../src/adapters/bidderFactory.js').Bid} Bid + */ + const BIDDER_CODE = 'teads'; const GVL_ID = 132; const ENDPOINT_URL = 'https://a.teads.tv/hb/bid-request'; diff --git a/modules/temedyaBidAdapter.js b/modules/temedyaBidAdapter.js index cb9fe46d21a..0e48768b605 100644 --- a/modules/temedyaBidAdapter.js +++ b/modules/temedyaBidAdapter.js @@ -3,6 +3,14 @@ import { registerBidder } from '../src/adapters/bidderFactory.js'; import { BANNER, NATIVE } from '../src/mediaTypes.js'; import { convertOrtbRequestToProprietaryNative } from '../src/native.js'; +/** + * @typedef {import('../src/adapters/bidderFactory.js').BidRequest} BidRequest + * @typedef {import('../src/adapters/bidderFactory.js').Bid} Bid + * @typedef {import('../src/adapters/bidderFactory.js').ServerResponse} ServerResponse + * @typedef {import('../src/adapters/bidderFactory.js').ServerRequest} ServerRequest + * @typedef {import('../src/adapters/bidderFactory.js').validBidRequests} validBidRequests + */ + const BIDDER_CODE = 'temedya'; const ENDPOINT_URL = 'https://adm.vidyome.com/'; const ENDPOINT_METHOD = 'GET'; diff --git a/modules/theAdxBidAdapter.js b/modules/theAdxBidAdapter.js index def2446635b..f19f7cfe515 100644 --- a/modules/theAdxBidAdapter.js +++ b/modules/theAdxBidAdapter.js @@ -9,6 +9,15 @@ import { } from '../src/adapters/bidderFactory.js'; import { convertOrtbRequestToProprietaryNative } from '../src/native.js'; +/** + * @typedef {import('../src/adapters/bidderFactory.js').BidRequest} BidRequest + * @typedef {import('../src/adapters/bidderFactory.js').Bid} Bid + * @typedef {import('../src/adapters/bidderFactory.js').ServerResponse} ServerResponse + * @typedef {import('../src/adapters/bidderFactory.js').SyncOptions} SyncOptions + * @typedef {import('../src/adapters/bidderFactory.js').UserSync} UserSync + * @typedef {import('../src/adapters/bidderFactory.js').validBidRequests} validBidRequests + */ + const BIDDER_CODE = 'theadx'; const ENDPOINT_URL = 'https://ssp.theadx.com/request'; diff --git a/modules/tpmnBidAdapter.js b/modules/tpmnBidAdapter.js index e3adb80fb2f..3edc89c90ae 100644 --- a/modules/tpmnBidAdapter.js +++ b/modules/tpmnBidAdapter.js @@ -7,6 +7,10 @@ import { Renderer } from '../src/Renderer.js'; import { config } from '../src/config.js'; import * as utils from '../src/utils.js'; +/** + * @typedef {import('../src/adapters/bidderFactory.js').Bid} Bid + */ + const BIDDER_CODE = 'tpmn'; const DEFAULT_BID_TTL = 500; const DEFAULT_CURRENCY = 'USD'; diff --git a/modules/ttdBidAdapter.js b/modules/ttdBidAdapter.js index 17a3cd652e8..d7705f2f5df 100644 --- a/modules/ttdBidAdapter.js +++ b/modules/ttdBidAdapter.js @@ -4,6 +4,15 @@ import { registerBidder } from '../src/adapters/bidderFactory.js'; import { BANNER, VIDEO } from '../src/mediaTypes.js'; import {isNumber} from '../src/utils.js'; +/** + * @typedef {import('../src/adapters/bidderFactory.js').BidRequest} BidRequest + * @typedef {import('../src/adapters/bidderFactory.js').Bid} Bid + * @typedef {import('../src/adapters/bidderFactory.js').ServerResponse} ServerResponse + * @typedef {import('../src/adapters/bidderFactory.js').ServerRequest} ServerRequest + * @typedef {import('../src/adapters/bidderFactory.js').SyncOptions} SyncOptions + * @typedef {import('../src/adapters/bidderFactory.js').UserSync} UserSync + */ + const BIDADAPTERVERSION = 'TTD-PREBID-2023.09.05'; const BIDDER_CODE = 'ttd'; const BIDDER_CODE_LONG = 'thetradedesk'; diff --git a/modules/ucfunnelBidAdapter.js b/modules/ucfunnelBidAdapter.js index 1ef72f293fc..19b933a8666 100644 --- a/modules/ucfunnelBidAdapter.js +++ b/modules/ucfunnelBidAdapter.js @@ -5,6 +5,12 @@ import { getStorageManager } from '../src/storageManager.js'; import { config } from '../src/config.js'; import { convertOrtbRequestToProprietaryNative } from '../src/native.js'; +/** + * @typedef {import('../src/adapters/bidderFactory.js').BidRequest} BidRequest + * @typedef {import('../src/adapters/bidderFactory.js').Bid} Bid + * @typedef {import('../src/adapters/bidderFactory.js').ServerRequest} ServerRequest + */ + const COOKIE_NAME = 'ucf_uid'; const VER = 'ADGENT_PREBID-2018011501'; const BIDDER_CODE = 'ucfunnel'; diff --git a/modules/unicornBidAdapter.js b/modules/unicornBidAdapter.js index 429ba8f60ba..43eb943f6d5 100644 --- a/modules/unicornBidAdapter.js +++ b/modules/unicornBidAdapter.js @@ -3,6 +3,11 @@ import {BANNER} from '../src/mediaTypes.js'; import {registerBidder} from '../src/adapters/bidderFactory.js'; import {getStorageManager} from '../src/storageManager.js'; +/** + * @typedef {import('../src/adapters/bidderFactory.js').BidRequest} BidRequest + * @typedef {import('../src/adapters/bidderFactory.js').ServerRequest} ServerRequest + */ + const BIDDER_CODE = 'unicorn'; const UNICORN_ENDPOINT = 'https://ds.uncn.jp/pb/0/bid.json'; const UNICORN_DEFAULT_CURRENCY = 'JPY'; diff --git a/modules/vdoaiBidAdapter.js b/modules/vdoaiBidAdapter.js index 05960378d23..ada843a6e45 100644 --- a/modules/vdoaiBidAdapter.js +++ b/modules/vdoaiBidAdapter.js @@ -2,6 +2,12 @@ import {registerBidder} from '../src/adapters/bidderFactory.js'; import {BANNER, VIDEO} from '../src/mediaTypes.js'; import {getAdUnitSizes} from '../libraries/sizeUtils/sizeUtils.js'; +/** + * @typedef {import('../src/adapters/bidderFactory.js').BidRequest} BidRequest + * @typedef {import('../src/adapters/bidderFactory.js').Bid} Bid + * @typedef {import('../src/adapters/bidderFactory.js').ServerResponse} ServerResponse + */ + const BIDDER_CODE = 'vdoai'; const ENDPOINT_URL = 'https://prebid.vdo.ai/auction'; diff --git a/modules/vibrantmediaBidAdapter.js b/modules/vibrantmediaBidAdapter.js index 7368967ff3f..8809aae32bd 100644 --- a/modules/vibrantmediaBidAdapter.js +++ b/modules/vibrantmediaBidAdapter.js @@ -12,6 +12,13 @@ import {registerBidder} from '../src/adapters/bidderFactory.js'; import {BANNER, NATIVE, VIDEO} from '../src/mediaTypes.js'; import {OUTSTREAM} from '../src/video.js'; +/** + * @typedef {import('../src/adapters/bidderFactory.js').BidRequest} BidRequest + * @typedef {import('../src/adapters/bidderFactory.js').Bid} Bid + * @typedef {import('../src/adapters/bidderFactory.js').ServerResponse} ServerResponse + * @typedef {import('../src/adapters/bidderFactory.js').BidderSpec} BidderSpec + */ + const BIDDER_CODE = 'vibrantmedia'; const VIBRANT_MEDIA_PREBID_URL = 'https://prebid.intellitxt.com/prebid'; const VALID_PIXEL_URL_REGEX = /^https?:\/\/[a-zA-Z0-9-]+(\.[a-zA-Z0-9-]+)+([/?].*)?$/; diff --git a/modules/videobyteBidAdapter.js b/modules/videobyteBidAdapter.js index c4dae78e862..8cedf9ac16a 100644 --- a/modules/videobyteBidAdapter.js +++ b/modules/videobyteBidAdapter.js @@ -2,6 +2,14 @@ import { logMessage, logError, deepAccess, isFn, isPlainObject, isStr, isNumber, import {registerBidder} from '../src/adapters/bidderFactory.js'; import {VIDEO} from '../src/mediaTypes.js'; +/** + * @typedef {import('../src/adapters/bidderFactory.js').BidRequest} BidRequest + * @typedef {import('../src/adapters/bidderFactory.js').Bid} Bid + * @typedef {import('../src/adapters/bidderFactory.js').ServerResponse} ServerResponse + * @typedef {import('../src/adapters/bidderFactory.js').SyncOptions} SyncOptions + * @typedef {import('../src/adapters/bidderFactory.js').UserSync} UserSync + */ + const BIDDER_CODE = 'videobyte'; const DEFAULT_BID_TTL = 300; const DEFAULT_CURRENCY = 'USD'; diff --git a/modules/videoheroesBidAdapter.js b/modules/videoheroesBidAdapter.js index 2f02734a31f..ee2c2deef8b 100644 --- a/modules/videoheroesBidAdapter.js +++ b/modules/videoheroesBidAdapter.js @@ -4,6 +4,11 @@ import { BANNER, NATIVE, VIDEO } from '../src/mediaTypes.js'; import { config } from '../src/config.js'; import { convertOrtbRequestToProprietaryNative } from '../src/native.js'; +/** + * @typedef {import('../src/adapters/bidderFactory.js').BidRequest} BidRequest + * @typedef {import('../src/adapters/bidderFactory.js').Bid} Bid + */ + const BIDDER_CODE = 'videoheroes'; const DEFAULT_CUR = 'USD'; const ENDPOINT_URL = `https://point.contextualadv.com/?t=2&partner=hash`; diff --git a/modules/videonowBidAdapter.js b/modules/videonowBidAdapter.js index bfbc07fdff1..563f692693a 100644 --- a/modules/videonowBidAdapter.js +++ b/modules/videonowBidAdapter.js @@ -2,6 +2,12 @@ import {registerBidder} from '../src/adapters/bidderFactory.js'; import {BANNER} from '../src/mediaTypes.js'; import {_each, getBidIdParameter, getValue, logError, logInfo} from '../src/utils.js'; +/** + * @typedef {import('../src/adapters/bidderFactory.js').BidRequest} BidRequest + * @typedef {import('../src/adapters/bidderFactory.js').Bid} Bid + * @typedef {import('../src/adapters/bidderFactory.js').ServerResponse} ServerResponse + */ + const BIDDER_CODE = 'videonow'; const RTB_URL = 'https://adx.videonow.ru/yhb' const DEFAULT_CURRENCY = 'RUB' diff --git a/modules/viqeoBidAdapter.js b/modules/viqeoBidAdapter.js index 5762a794c8e..28f4de1fd52 100644 --- a/modules/viqeoBidAdapter.js +++ b/modules/viqeoBidAdapter.js @@ -3,6 +3,14 @@ import {logError, logInfo, _each, mergeDeep, isFn, isNumber, isPlainObject} from import {VIDEO} from '../src/mediaTypes.js'; import {Renderer} from '../src/Renderer.js'; +/** + * @typedef {import('../src/adapters/bidderFactory.js').BidRequest} BidRequest + * @typedef {import('../src/adapters/bidderFactory.js').Bid} Bid + * @typedef {import('../src/adapters/bidderFactory.js').ServerResponse} ServerResponse + * @typedef {import('../src/adapters/bidderFactory.js').ServerRequest} ServerRequest + * @typedef {import('../src/adapters/bidderFactory.js').BidderSpec} BidderSpec + */ + const BIDDER_CODE = 'viqeo'; const DEFAULT_MIMES = ['application/javascript']; const VIQEO_ENDPOINT = 'https://ads.betweendigital.com/openrtb_bid'; diff --git a/modules/voxBidAdapter.js b/modules/voxBidAdapter.js index 431d0887334..f1670de76d0 100644 --- a/modules/voxBidAdapter.js +++ b/modules/voxBidAdapter.js @@ -6,6 +6,13 @@ import {auctionManager} from '../src/auctionManager.js'; import {Renderer} from '../src/Renderer.js'; import {config} from '../src/config.js' +/** + * @typedef {import('../src/adapters/bidderFactory.js').BidRequest} BidRequest + * @typedef {import('../src/adapters/bidderFactory.js').Bid} Bid + * @typedef {import('../src/adapters/bidderFactory.js').ServerResponse} ServerResponse + * @typedef {import('../src/adapters/bidderFactory.js').validBidRequests} validBidRequests + */ + const { getConfig } = config; const BIDDER_CODE = 'vox'; diff --git a/modules/welectBidAdapter.js b/modules/welectBidAdapter.js index d88a3f4c3e2..533e6401cd5 100644 --- a/modules/welectBidAdapter.js +++ b/modules/welectBidAdapter.js @@ -1,6 +1,13 @@ import { deepAccess } from '../src/utils.js'; import { registerBidder } from '../src/adapters/bidderFactory.js'; +/** + * @typedef {import('../src/adapters/bidderFactory.js').BidRequest} BidRequest + * @typedef {import('../src/adapters/bidderFactory.js').Bid} Bid + * @typedef {import('../src/adapters/bidderFactory.js').ServerResponse} ServerResponse + * @typedef {import('../src/adapters/bidderFactory.js').validBidRequests} validBidRequests + */ + const BIDDER_CODE = 'welect'; const DEFAULT_DOMAIN = 'www.welect.de'; diff --git a/modules/winrBidAdapter.js b/modules/winrBidAdapter.js index 41efc432e11..cf1158474b4 100644 --- a/modules/winrBidAdapter.js +++ b/modules/winrBidAdapter.js @@ -18,6 +18,11 @@ import {getANKeywordParam, transformBidderParamKeywords} from '../libraries/appn import {convertCamelToUnderscore} from '../libraries/appnexusUtils/anUtils.js'; import {convertTypes} from '../libraries/transformParamsUtils/convertTypes.js'; +/** + * @typedef {import('../src/adapters/bidderFactory.js').BidRequest} BidRequest + * @typedef {import('../src/adapters/bidderFactory.js').Bid} Bid + */ + const BIDDER_CODE = 'winr'; const URL = 'https://ib.adnxs.com/ut/v3/prebid'; const URL_SIMPLE = 'https://ib.adnxs-simple.com/ut/v3/prebid'; diff --git a/modules/xeBidAdapter.js b/modules/xeBidAdapter.js index bff76aae172..a813b9aa2a3 100644 --- a/modules/xeBidAdapter.js +++ b/modules/xeBidAdapter.js @@ -4,6 +4,14 @@ import { registerBidder } from '../src/adapters/bidderFactory.js'; import {parseSizesInput, isFn, deepAccess, logError, isArray, getBidIdParameter} from '../src/utils.js'; import {getAdUnitSizes} from '../libraries/sizeUtils/sizeUtils.js'; +/** + * @typedef {import('../src/adapters/bidderFactory.js').BidRequest} BidRequest + * @typedef {import('../src/adapters/bidderFactory.js').Bid} Bid + * @typedef {import('../src/adapters/bidderFactory.js').ServerResponse} ServerResponse + * @typedef {import('../src/adapters/bidderFactory.js').SyncOptions} SyncOptions + * @typedef {import('../src/adapters/bidderFactory.js').UserSync} UserSync + */ + const CUR = 'USD'; const BIDDER_CODE = 'xe'; const ENDPOINT = 'https://pbjs.xe.works/bid'; diff --git a/modules/yieldlabBidAdapter.js b/modules/yieldlabBidAdapter.js index b0136cd21ea..e4fba5beb62 100644 --- a/modules/yieldlabBidAdapter.js +++ b/modules/yieldlabBidAdapter.js @@ -5,6 +5,15 @@ import { BANNER, NATIVE, VIDEO } from '../src/mediaTypes.js'; import { Renderer } from '../src/Renderer.js'; import { convertOrtbRequestToProprietaryNative } from '../src/native.js'; +/** + * @typedef {import('../src/adapters/bidderFactory.js').BidRequest} BidRequest + * @typedef {import('../src/adapters/bidderFactory.js').Bid} Bid + * @typedef {import('../src/adapters/bidderFactory.js').ServerResponse} ServerResponse + * @typedef {import('../src/adapters/bidderFactory.js').ServerRequest} ServerRequest + * @typedef {import('../src/adapters/bidderFactory.js').SyncOptions} SyncOptions + * @typedef {import('../src/adapters/bidderFactory.js').UserSync} UserSync + */ + const ENDPOINT = 'https://ad.yieldlab.net'; const BIDDER_CODE = 'yieldlab'; const BID_RESPONSE_TTL_SEC = 300; diff --git a/modules/yieldmoBidAdapter.js b/modules/yieldmoBidAdapter.js index 9109c6e2a80..78c2e7430e7 100644 --- a/modules/yieldmoBidAdapter.js +++ b/modules/yieldmoBidAdapter.js @@ -18,6 +18,13 @@ import {registerBidder} from '../src/adapters/bidderFactory.js'; import {Renderer} from '../src/Renderer.js'; import {find, includes} from '../src/polyfill.js'; +/** + * @typedef {import('../src/adapters/bidderFactory.js').BidRequest} BidRequest + * @typedef {import('../src/adapters/bidderFactory.js').Bid} Bid + * @typedef {import('../src/adapters/bidderFactory.js').ServerResponse} ServerResponse + * @typedef {import('../src/adapters/bidderFactory.js').ServerRequest} ServerRequest + */ + const BIDDER_CODE = 'yieldmo'; const GVLID = 173; const CURRENCY = 'USD'; diff --git a/modules/zetaBidAdapter.js b/modules/zetaBidAdapter.js index 9a58e391a17..cc77d53d30d 100644 --- a/modules/zetaBidAdapter.js +++ b/modules/zetaBidAdapter.js @@ -2,6 +2,14 @@ import {deepAccess, logWarn} from '../src/utils.js'; import {registerBidder} from '../src/adapters/bidderFactory.js'; import {BANNER} from '../src/mediaTypes.js'; +/** + * @typedef {import('../src/adapters/bidderFactory.js').BidRequest} BidRequest + * @typedef {import('../src/adapters/bidderFactory.js').Bid} Bid + * @typedef {import('../src/adapters/bidderFactory.js').ServerResponse} ServerResponse + * @typedef {import('../src/adapters/bidderFactory.js').SyncOptions} SyncOptions + * @typedef {import('../src/adapters/bidderFactory.js').UserSync} UserSync + */ + const BIDDER_CODE = 'zeta_global'; const PREBID_DEFINER_ID = '44253' const ENDPOINT_URL = 'https://prebid.rfihub.com/prebid'; diff --git a/modules/zeta_global_sspBidAdapter.js b/modules/zeta_global_sspBidAdapter.js index 0c5db541286..68cf7c4e4cd 100644 --- a/modules/zeta_global_sspBidAdapter.js +++ b/modules/zeta_global_sspBidAdapter.js @@ -5,6 +5,12 @@ import {config} from '../src/config.js'; import {parseDomain} from '../src/refererDetection.js'; import {ajax} from '../src/ajax.js'; +/** + * @typedef {import('../src/adapters/bidderFactory.js').BidRequest} BidRequest + * @typedef {import('../src/adapters/bidderFactory.js').Bid} Bid + * @typedef {import('../src/adapters/bidderFactory.js').ServerResponse} ServerResponse + */ + const BIDDER_CODE = 'zeta_global_ssp'; const ENDPOINT_URL = 'https://ssp.disqus.com/bid/prebid'; const TIMEOUT_URL = 'https://ssp.disqus.com/timeout/prebid'; diff --git a/src/native.js b/src/native.js index c4709dd77e2..affdc855353 100644 --- a/src/native.js +++ b/src/native.js @@ -16,6 +16,11 @@ import {auctionManager} from './auctionManager.js'; import CONSTANTS from './constants.json'; import {NATIVE} from './mediaTypes.js'; +/** + * @typedef {import('../src/adapters/bidderFactory.js').BidRequest} BidRequest + * @typedef {import('../src/adapters/bidderFactory.js').Bid} Bid + */ + export const nativeAdapters = []; export const NATIVE_TARGETING_KEYS = Object.keys(CONSTANTS.NATIVE_KEYS).map( From 1c0f80f2e89aa96555295183871c66c167c60c24 Mon Sep 17 00:00:00 2001 From: Irakli Gotsiridze Date: Sun, 28 Jan 2024 03:31:50 +0400 Subject: [PATCH 181/246] update mail (#10992) --- modules/sovrnAnalyticsAdapter.md | 2 +- modules/sovrnBidAdapter.md | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/modules/sovrnAnalyticsAdapter.md b/modules/sovrnAnalyticsAdapter.md index 80bc6d7f6b1..b4fe7c971a2 100644 --- a/modules/sovrnAnalyticsAdapter.md +++ b/modules/sovrnAnalyticsAdapter.md @@ -3,7 +3,7 @@ ``` Module Name: Sovrn Analytics Adapter Module Type: Analytics Adapter -Maintainer: jrosendahl@sovrn.com +Maintainer: exchange@sovrn.com ``` # Description diff --git a/modules/sovrnBidAdapter.md b/modules/sovrnBidAdapter.md index 53e3158024d..ce131269eee 100644 --- a/modules/sovrnBidAdapter.md +++ b/modules/sovrnBidAdapter.md @@ -3,7 +3,7 @@ ``` Module Name: Sovrn Bid Adapter Module Type: Bidder Adapter -Maintainer: jrosendahl@sovrn.com +Maintainer: exchange@sovrn.com ``` # Description From ba42d90e3af8a5c6b79da98aa5d3b392d8dc8d51 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?L=C3=A9onard=20Labat?= Date: Mon, 29 Jan 2024 15:50:55 +0100 Subject: [PATCH 182/246] Grid bid adapter : do not send topics along requests to the backend (#10995) --- modules/gridBidAdapter.js | 1 + 1 file changed, 1 insertion(+) diff --git a/modules/gridBidAdapter.js b/modules/gridBidAdapter.js index b2350c0a61c..7147864a2e6 100644 --- a/modules/gridBidAdapter.js +++ b/modules/gridBidAdapter.js @@ -466,6 +466,7 @@ export const spec = { }, ajaxCall: function(url, cb, data, options) { + options.browsingTopics = false; return ajax(url, cb, data, options); }, From 17f346834449b1b4e02852793e6a92cbe3541532 Mon Sep 17 00:00:00 2001 From: Demetrio Girardi Date: Mon, 29 Jan 2024 07:19:23 -0800 Subject: [PATCH 183/246] dfpAdServerVideo: add several parameters do DFP URLs (#10977) --- modules/dfpAdServerVideo.js | 103 ++++- test/spec/modules/dfpAdServerVideo_spec.js | 485 ++++++++++++++------- 2 files changed, 405 insertions(+), 183 deletions(-) diff --git a/modules/dfpAdServerVideo.js b/modules/dfpAdServerVideo.js index a3e26dc7202..7f275992210 100644 --- a/modules/dfpAdServerVideo.js +++ b/modules/dfpAdServerVideo.js @@ -2,17 +2,28 @@ * This module adds [DFP support]{@link https://www.doubleclickbygoogle.com/} for Video to Prebid. */ -import { registerVideoSupport } from '../src/adServerManager.js'; -import { targeting } from '../src/targeting.js'; -import { deepAccess, isEmpty, logError, parseSizesInput, formatQS, parseUrl, buildUrl } from '../src/utils.js'; -import { config } from '../src/config.js'; -import { getHook, submodule } from '../src/hook.js'; -import { auctionManager } from '../src/auctionManager.js'; -import { gdprDataHandler, uspDataHandler, gppDataHandler } from '../src/adapterManager.js'; +import {registerVideoSupport} from '../src/adServerManager.js'; +import {targeting} from '../src/targeting.js'; +import { + isNumber, + buildUrl, + deepAccess, + formatQS, + isEmpty, + logError, + parseSizesInput, + parseUrl, + uniques +} from '../src/utils.js'; +import {config} from '../src/config.js'; +import {getHook, submodule} from '../src/hook.js'; +import {auctionManager} from '../src/auctionManager.js'; +import {gdprDataHandler} from '../src/adapterManager.js'; import * as events from '../src/events.js'; import CONSTANTS from '../src/constants.json'; import {getPPID} from '../src/adserver.js'; import {getRefererInfo} from '../src/refererDetection.js'; +import {CLIENT_SECTIONS} from '../src/fpd/oneClient.js'; /** * @typedef {Object} DfpVideoParams @@ -113,7 +124,6 @@ export function buildDfpVideoUrl(options) { const descriptionUrl = getDescriptionUrl(bid, options, 'params'); if (descriptionUrl) { queryParams.description_url = descriptionUrl; } - const gdprConsent = gdprDataHandler.getConsentData(); if (gdprConsent) { if (typeof gdprConsent.gdprApplies === 'boolean') { queryParams.gdpr = Number(gdprConsent.gdprApplies); } @@ -121,14 +131,6 @@ export function buildDfpVideoUrl(options) { if (gdprConsent.addtlConsent) { queryParams.addtl_consent = gdprConsent.addtlConsent; } } - const uspConsent = uspDataHandler.getConsentData(); - if (uspConsent) { queryParams.us_privacy = uspConsent; } - - const gppConsent = gppDataHandler.getConsentData(); - if (gppConsent) { - // TODO - need to know what to set here for queryParams... - } - if (!queryParams.ppid) { const ppid = getPPID(); if (ppid != null) { @@ -136,6 +138,70 @@ export function buildDfpVideoUrl(options) { } } + const video = options.adUnit?.mediaTypes?.video; + Object.entries({ + plcmt: () => video?.plcmt, + min_ad_duration: () => isNumber(video?.minduration) ? video.minduration * 1000 : null, + max_ad_duration: () => isNumber(video?.maxduration) ? video.maxduration * 1000 : null, + vpos() { + const startdelay = video?.startdelay; + if (isNumber(startdelay)) { + if (startdelay === -2) return 'postroll'; + if (startdelay === -1 || startdelay > 0) return 'midroll'; + return 'preroll'; + } + }, + vconp: () => Array.isArray(video?.playbackmethod) && video.playbackmethod.every(m => m === 7) ? '2' : undefined, + vpa() { + // playbackmethod = 3 is play on click; 1, 2, 4, 5, 6 are autoplay + if (Array.isArray(video?.playbackmethod)) { + const click = video.playbackmethod.some(m => m === 3); + const auto = video.playbackmethod.some(m => [1, 2, 4, 5, 6].includes(m)); + if (click && !auto) return 'click'; + if (auto && !click) return 'auto'; + } + }, + vpmute() { + // playbackmethod = 2, 6 are muted; 1, 3, 4, 5 are not + if (Array.isArray(video?.playbackmethod)) { + const muted = video.playbackmethod.some(m => [2, 6].includes(m)); + const talkie = video.playbackmethod.some(m => [1, 3, 4, 5].includes(m)); + if (muted && !talkie) return '1'; + if (talkie && !muted) return '0'; + } + } + }).forEach(([param, getter]) => { + if (!queryParams.hasOwnProperty(param)) { + const val = getter(); + if (val != null) { + queryParams[param] = val; + } + } + }); + const fpd = auctionManager.index.getBidRequest(options.bid || {})?.ortb2 ?? + auctionManager.index.getAuction(options.bid || {})?.getFPD()?.global; + + function getSegments(sections, segtax) { + return sections + .flatMap(section => deepAccess(fpd, section) || []) + .filter(datum => datum.ext?.segtax === segtax) + .flatMap(datum => datum.segment?.map(seg => seg.id)) + .filter(ob => ob) + .filter(uniques) + } + + const signals = Object.entries({ + IAB_AUDIENCE_1_1: getSegments(['user.data'], 4), + IAB_CONTENT_2_2: getSegments(CLIENT_SECTIONS.map(section => `${section}.content.data`), 6) + }).map(([taxonomy, values]) => values.length ? {taxonomy, values} : null) + .filter(ob => ob); + + if (signals.length) { + queryParams.ppsj = btoa(JSON.stringify({ + PublisherProvidedTaxonomySignals: signals + })) + } + return buildUrl(Object.assign({ protocol: 'https', host: 'securepubads.g.doubleclick.net', @@ -164,6 +230,8 @@ if (config.getConfig('brandCategoryTranslation.translationFile')) { getHook('reg * @returns {string} A URL which calls DFP with custom adpod targeting key values to compete with rest of the demand in DFP */ export function buildAdpodVideoUrl({code, params, callback} = {}) { + // TODO: the public API for this does not take in enough info to fill all DFP params (adUnit/bid), + // and is marked "alpha": https://docs.prebid.org/dev-docs/publisher-api-reference/adServers.dfp.buildAdpodVideoUrl.html if (!params || !callback) { logError(`A params object and a callback is required to use pbjs.adServers.dfp.buildAdpodVideoUrl`); return; @@ -225,9 +293,6 @@ export function buildAdpodVideoUrl({code, params, callback} = {}) { if (gdprConsent.addtlConsent) { queryParams.addtl_consent = gdprConsent.addtlConsent; } } - 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 4c12e9fa211..39713c2b51a 100644 --- a/test/spec/modules/dfpAdServerVideo_spec.js +++ b/test/spec/modules/dfpAdServerVideo_spec.js @@ -1,43 +1,61 @@ -import { expect } from 'chai'; +import {expect} from 'chai'; import parse from 'url-parse'; -import {buildDfpVideoUrl, buildAdpodVideoUrl, dep} from 'modules/dfpAdServerVideo.js'; -import adUnit from 'test/fixtures/video/adUnit.json'; +import {buildAdpodVideoUrl, buildDfpVideoUrl, dep} from 'modules/dfpAdServerVideo.js'; +import AD_UNIT from 'test/fixtures/video/adUnit.json'; 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 { gdprDataHandler, uspDataHandler } from 'src/adapterManager.js'; +import {deepClone} from 'src/utils.js'; +import {config} from 'src/config.js'; +import {targeting} from 'src/targeting.js'; +import {auctionManager} from 'src/auctionManager.js'; +import {gdprDataHandler, uspDataHandler} from 'src/adapterManager.js'; import * as adpod from 'modules/adpod.js'; -import { server } from 'test/mocks/xhr.js'; +import {server} from 'test/mocks/xhr.js'; import * as adServer from 'src/adserver.js'; -import {deepClone} from 'src/utils.js'; import {hook} from '../../../src/hook.js'; -import {getRefererInfo} from '../../../src/refererDetection.js'; - -const bid = { - videoCacheKey: 'abc', - adserverTargeting: { - hb_uuid: 'abc', - hb_cache_id: 'abc', - }, -}; +import {stubAuctionIndex} from '../../helpers/indexStub.js'; +import {AuctionIndex} from '../../../src/auctionIndex.js'; describe('The DFP video support module', function () { before(() => { hook.ready(); }); - let sandbox; + let sandbox, bid, adUnit; beforeEach(() => { sandbox = sinon.sandbox.create(); + bid = { + videoCacheKey: 'abc', + adserverTargeting: { + hb_uuid: 'abc', + hb_cache_id: 'abc', + }, + }; + adUnit = deepClone(AD_UNIT); }); afterEach(() => { sandbox.restore(); }); + function getURL(options) { + return parse(buildDfpVideoUrl(Object.assign({ + adUnit: adUnit, + bid: bid, + params: { + 'iu': 'my/adUnit' + } + }, options))) + } + function getQueryParams(options) { + return utils.parseQS(getURL(options).query); + } + + function getCustomParams(options) { + return utils.parseQS('?' + decodeURIComponent(getQueryParams(options).cust_params)); + } + Object.entries({ params: { params: { @@ -51,37 +69,25 @@ describe('The DFP video support module', function () { describe(`when using ${t}`, () => { it('should use page location as default for description_url', () => { sandbox.stub(dep, 'ri').callsFake(() => ({page: 'example.com'})); - - const url = parse(buildDfpVideoUrl(Object.assign({ - adUnit: adUnit, - bid: bid, - }, options))); - const prm = utils.parseQS(url.query); + const prm = getQueryParams(options); expect(prm.description_url).to.eql('example.com'); }); it('should use a URI encoded page location as default for description_url', () => { sandbox.stub(dep, 'ri').callsFake(() => ({page: 'https://example.com?iu=/99999999/news&cust_params=current_hour%3D12%26newscat%3Dtravel&pbjs_debug=true'})); - const url = parse(buildDfpVideoUrl(Object.assign({ - adUnit: adUnit, - bid: bid, - }, options))); - const prm = utils.parseQS(url.query); + const prm = getQueryParams(options); expect(prm.description_url).to.eql('https%3A%2F%2Fexample.com%3Fiu%3D%2F99999999%2Fnews%26cust_params%3Dcurrent_hour%253D12%2526newscat%253Dtravel%26pbjs_debug%3Dtrue'); }); }); }) it('should make a legal request URL when given the required params', function () { - const url = parse(buildDfpVideoUrl({ - adUnit: adUnit, - bid: bid, + const url = getURL({ params: { 'iu': 'my/adUnit', 'description_url': 'someUrl.com', } - })); - + }) expect(url.protocol).to.equal('https:'); expect(url.host).to.equal('securepubads.g.doubleclick.net'); @@ -98,15 +104,10 @@ describe('The DFP video support module', function () { }); it('can take an adserver url as a parameter', function () { - const bidCopy = utils.deepClone(bid); - bidCopy.vastUrl = 'vastUrl.example'; - - const url = parse(buildDfpVideoUrl({ - adUnit: adUnit, - bid: bidCopy, + bid.vastUrl = 'vastUrl.example'; + const url = getURL({ url: 'https://video.adserver.example/', - })); - + }) expect(url.host).to.equal('video.adserver.example'); }); @@ -120,161 +121,64 @@ describe('The DFP video support module', function () { }); it('overwrites url params when both url and params object are given', function () { - const url = parse(buildDfpVideoUrl({ - adUnit: adUnit, - bid: bid, + const params = getQueryParams({ url: 'https://video.adserver.example/ads?sz=640x480&iu=/123/aduniturl&impl=s', params: { iu: 'my/adUnit' } - })); + }); - const queryObject = utils.parseQS(url.query); - expect(queryObject.iu).to.equal('my/adUnit'); + expect(params.iu).to.equal('my/adUnit'); }); it('should override param defaults with user-provided ones', function () { - const url = parse(buildDfpVideoUrl({ - adUnit: adUnit, - bid: bid, + const params = getQueryParams({ params: { - 'iu': 'my/adUnit', 'output': 'vast', } - })); - - expect(utils.parseQS(url.query)).to.have.property('output', 'vast'); + }); + expect(params.output).to.equal('vast'); }); it('should include the cache key and adserver targeting in cust_params', function () { - const bidCopy = utils.deepClone(bid); - bidCopy.adserverTargeting = Object.assign(bidCopy.adserverTargeting, { + bid.adserverTargeting = Object.assign(bid.adserverTargeting, { hb_adid: 'ad_id', }); - const url = parse(buildDfpVideoUrl({ - adUnit: adUnit, - bid: bidCopy, - params: { - 'iu': 'my/adUnit' - } - })); - const queryObject = utils.parseQS(url.query); - const customParams = utils.parseQS('?' + decodeURIComponent(queryObject.cust_params)); + const customParams = getCustomParams() expect(customParams).to.have.property('hb_adid', 'ad_id'); expect(customParams).to.have.property('hb_uuid', bid.videoCacheKey); 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); - }); - it('should include the GDPR keys when GDPR Consent is available', function () { - let gdprDataHandlerStub = sinon.stub(gdprDataHandler, 'getConsentData'); - gdprDataHandlerStub.returns({ + sandbox.stub(gdprDataHandler, 'getConsentData').returns({ gdprApplies: true, consentString: 'consent', addtlConsent: 'moreConsent' }); - - 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); + const queryObject = getQueryParams(); expect(queryObject.gdpr).to.equal('1'); expect(queryObject.gdpr_consent).to.equal('consent'); expect(queryObject.addtl_consent).to.equal('moreConsent'); - gdprDataHandlerStub.restore(); }); it('should not include the GDPR keys when GDPR 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); + const queryObject = getQueryParams() expect(queryObject.gdpr).to.equal(undefined); expect(queryObject.gdpr_consent).to.equal(undefined); expect(queryObject.addtl_consent).to.equal(undefined); }); it('should only include the GDPR keys for GDPR Consent fields with values', function () { - let gdprDataHandlerStub = sinon.stub(gdprDataHandler, 'getConsentData'); - gdprDataHandlerStub.returns({ + sandbox.stub(gdprDataHandler, 'getConsentData').returns({ gdprApplies: true, consentString: 'consent', }); - - 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); + const queryObject = getQueryParams() expect(queryObject.gdpr).to.equal('1'); expect(queryObject.gdpr_consent).to.equal('consent'); expect(queryObject.addtl_consent).to.equal(undefined); - gdprDataHandlerStub.restore(); }); - describe('GAM PPID', () => { let ppid; let getPPIDStub; @@ -290,29 +194,283 @@ describe('The DFP video support module', function () { 'url': {url: 'https://video.adserver.mock/', params: {'iu': 'mock/unit'}} }).forEach(([t, opts]) => { describe(`when using ${t}`, () => { - function buildUrlAndGetParams() { - const url = parse(buildDfpVideoUrl(Object.assign({ - adUnit: adUnit, - bid: deepClone(bid), - }, opts))); - return utils.parseQS(url.query); - } - it('should be included if available', () => { ppid = 'mockPPID'; - const q = buildUrlAndGetParams(); + const q = getQueryParams(opts); expect(q.ppid).to.equal('mockPPID'); }); it('should not be included if not available', () => { ppid = undefined; - const q = buildUrlAndGetParams(); + const q = getQueryParams(opts); expect(q.hasOwnProperty('ppid')).to.be.false; }) }) }) }) + describe('ORTB video parameters', () => { + Object.entries({ + plcmt: [ + { + video: { + plcmt: 1 + }, + expected: '1' + } + ], + min_ad_duration: [ + { + video: { + minduration: 123 + }, + expected: '123000' + } + ], + max_ad_duration: [ + { + video: { + maxduration: 321 + }, + expected: '321000' + } + ], + vpos: [ + { + video: { + startdelay: 0 + }, + expected: 'preroll' + }, + { + video: { + startdelay: -1 + }, + expected: 'midroll' + }, + { + video: { + startdelay: -2 + }, + expected: 'postroll' + }, + { + video: { + startdelay: 10 + }, + expected: 'midroll' + } + ], + vconp: [ + { + video: { + playbackmethod: [7] + }, + expected: '2' + }, + { + video: { + playbackmethod: [7, 1] + }, + expected: undefined + } + ], + vpa: [ + { + video: { + playbackmethod: [1, 2, 4, 5, 6, 7] + }, + expected: 'auto' + }, + { + video: { + playbackmethod: [3, 7], + }, + expected: 'click' + }, + { + video: { + playbackmethod: [1, 3], + }, + expected: undefined + } + ], + vpmute: [ + { + video: { + playbackmethod: [1, 3, 4, 5, 7] + }, + expected: '0' + }, + { + video: { + playbackmethod: [2, 6, 7], + }, + expected: '1' + }, + { + video: { + playbackmethod: [1, 2] + }, + expected: undefined + } + ] + }).forEach(([param, cases]) => { + describe(param, () => { + cases.forEach(({video, expected}) => { + describe(`when mediaTypes.video has ${JSON.stringify(video)}`, () => { + it(`fills in ${param} = ${expected}`, () => { + Object.assign(adUnit.mediaTypes.video, video); + expect(getQueryParams()[param]).to.eql(expected); + }); + it(`does not override pub-provided params.${param}`, () => { + Object.assign(adUnit.mediaTypes.video, video); + expect(getQueryParams({ + params: { + [param]: 'OG' + } + })[param]).to.eql('OG'); + }); + it('does not fill if param has no value', () => { + expect(getQueryParams().hasOwnProperty(param)).to.be.false; + }) + }) + }) + }) + }) + }); + + describe('ppsj', () => { + let ortb2; + beforeEach(() => { + ortb2 = null; + }) + + function getSignals() { + const ppsj = JSON.parse(atob(getQueryParams().ppsj)); + return Object.fromEntries(ppsj.PublisherProvidedTaxonomySignals.map(sig => [sig.taxonomy, sig.values])); + } + + Object.entries({ + 'FPD from bid request'() { + bid.requestId = 'req-id'; + sandbox.stub(auctionManager, 'index').get(() => stubAuctionIndex({ + bidRequests: [ + { + bidId: 'req-id', + ortb2 + } + ] + })); + }, + 'global FPD from auction'() { + bid.auctionId = 'auid'; + sandbox.stub(auctionManager, 'index').get(() => new AuctionIndex(() => [{ + getAuctionId: () => 'auid', + getFPD: () => ({ + global: ortb2 + }) + }])); + } + }).forEach(([t, setup]) => { + describe(`using ${t}`, () => { + beforeEach(setup); + it('does not fill if there\'s no segments in segtax 4 or 6', () => { + ortb2 = { + site: { + content: { + data: [ + { + segment: [ + {id: '1'}, + {id: '2'} + ] + }, + ] + } + }, + user: { + data: [ + { + ext: { + segtax: 1, + }, + segment: [ + {id: '3'} + ] + } + ] + } + } + expect(getQueryParams().ppsj).to.not.exist; + }); + + const SEGMENTS = [ + { + ext: { + segtax: 4, + }, + segment: [ + {id: '4-1'}, + {id: '4-2'} + ] + }, + { + ext: { + segtax: 4, + }, + segment: [ + {id: '4-2'}, + {id: '4-3'} + ] + }, + { + ext: { + segtax: 6, + }, + segment: [ + {id: '6-1'}, + {id: '6-2'} + ] + }, + { + ext: { + segtax: 6, + }, + segment: [ + {id: '6-2'}, + {id: '6-3'} + ] + }, + ] + + it('collects user.data segments with segtax = 4 into IAB_AUDIENCE_1_1', () => { + ortb2 = { + user: { + data: SEGMENTS + } + } + expect(getSignals()).to.eql({ + IAB_AUDIENCE_1_1: ['4-1', '4-2', '4-3'] + }) + }) + + it('collects site.content.data segments with segtax = 6 into IAB_CONTENT_2_2', () => { + ortb2 = { + site: { + content: { + data: SEGMENTS + } + } + } + expect(getSignals()).to.eql({ + IAB_CONTENT_2_2: ['6-1', '6-2', '6-3'] + }) + }) + }) + }) + }) + describe('special targeting unit test', function () { const allTargetingData = { 'hb_format': 'video', @@ -639,7 +797,6 @@ 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'); expect(queryParams).to.have.property('gdpr', '1'); expect(queryParams).to.have.property('gdpr_consent', 'consent'); expect(queryParams).to.have.property('addtl_consent', 'moreConsent'); From 07962d64170f79ee0945ba1a1e747228fb674bd6 Mon Sep 17 00:00:00 2001 From: Quentin Gallard Date: Tue, 30 Jan 2024 14:16:21 +0100 Subject: [PATCH 184/246] SmileWanted - Add Video Instream, Video Outstream and Native support (#10996) Co-authored-by: QuentinGallard --- modules/smilewantedBidAdapter.js | 116 ++++++++--- .../modules/smilewantedBidAdapter_spec.js | 186 +++++++++++++++++- 2 files changed, 272 insertions(+), 30 deletions(-) diff --git a/modules/smilewantedBidAdapter.js b/modules/smilewantedBidAdapter.js index 46584e54373..515aae0e092 100644 --- a/modules/smilewantedBidAdapter.js +++ b/modules/smilewantedBidAdapter.js @@ -1,8 +1,12 @@ -import { isArray, logError, logWarn, isFn, isPlainObject } from '../src/utils.js'; -import { Renderer } from '../src/Renderer.js'; -import { config } from '../src/config.js'; -import { registerBidder } from '../src/adapters/bidderFactory.js'; -import { BANNER, VIDEO } from '../src/mediaTypes.js'; +import {deepAccess, deepClone, isArray, isFn, isPlainObject, logError, logWarn} from '../src/utils.js'; +import {Renderer} from '../src/Renderer.js'; +import {config} from '../src/config.js'; +import {registerBidder} from '../src/adapters/bidderFactory.js'; +import {BANNER, NATIVE, VIDEO} from '../src/mediaTypes.js'; +import {INSTREAM, OUTSTREAM} from '../src/video.js'; +import {convertOrtbRequestToProprietaryNative, toOrtbNativeRequest, toLegacyResponse} from '../src/native.js'; + +const BIDDER_CODE = 'smilewanted'; /** * @typedef {import('../src/adapters/bidderFactory.js').BidRequest} BidRequest @@ -12,29 +16,50 @@ import { BANNER, VIDEO } from '../src/mediaTypes.js'; const GVL_ID = 639; export const spec = { - code: 'smilewanted', - aliases: ['smile', 'sw'], + code: BIDDER_CODE, gvlid: GVL_ID, - supportedMediaTypes: [BANNER, VIDEO], + aliases: ['smile', 'sw'], + supportedMediaTypes: [BANNER, VIDEO, NATIVE], /** * Determines whether or not the given bid request is valid. * - * @param {object} bid The bid to validate. + * @param {BidRequest} bid The bid to validate. * @return boolean True if this is a valid bid, and false otherwise. */ isBidRequestValid: function(bid) { - return !!(bid.params && bid.params.zoneId); + if (!bid.params || !bid.params.zoneId) { + return false; + } + + if (deepAccess(bid, 'mediaTypes.video')) { + const videoMediaTypesParams = deepAccess(bid, 'mediaTypes.video', {}); + const videoBidderParams = deepAccess(bid, 'params.video', {}); + + const videoParams = { + ...videoMediaTypesParams, + ...videoBidderParams + }; + + if (!videoParams.context || ![INSTREAM, OUTSTREAM].includes(videoParams.context)) { + return false; + } + } + + return true; }, /** * Make a server request from the list of BidRequests. * * @param {BidRequest[]} validBidRequests A non-empty list of valid bid requests that should be sent to the Server. + * @param {BidderRequest} bidderRequest bidder request object. * @return ServerRequest Info describing the request to the server. */ buildRequests: function(validBidRequests, bidderRequest) { + validBidRequests = convertOrtbRequestToProprietaryNative(validBidRequests); + return validBidRequests.map(bid => { - var payload = { + const payload = { zoneId: bid.params.zoneId, currencyCode: config.getConfig('currency.adServerCurrency') || 'EUR', tagId: bid.adUnitCode, @@ -65,20 +90,41 @@ export const spec = { payload.bidfloor = bid.params.bidfloor; } - if (bidderRequest && bidderRequest.refererInfo) { + if (bidderRequest?.refererInfo) { payload.pageDomain = bidderRequest.refererInfo.page || ''; } - if (bidderRequest && bidderRequest.gdprConsent) { + if (bidderRequest?.gdprConsent) { payload.gdpr_consent = bidderRequest.gdprConsent.consentString; payload.gdpr = bidderRequest.gdprConsent.gdprApplies; // we're handling the undefined case server side } - if (bid && bid.userIdAsEids) { - payload.eids = bid.userIdAsEids; + payload.eids = bid?.userIdAsEids; + + const videoMediaType = deepAccess(bid, 'mediaTypes.video'); + const context = deepAccess(bid, 'mediaTypes.video.context'); + + if (bid.mediaType === 'video' || (videoMediaType && context === INSTREAM) || (videoMediaType && context === OUTSTREAM)) { + payload.context = context; + payload.videoParams = deepClone(videoMediaType); } - var payloadString = JSON.stringify(payload); + const nativeMediaType = deepAccess(bid, 'mediaTypes.native'); + + if (nativeMediaType) { + payload.context = 'native'; + payload.nativeParams = nativeMediaType; + let sizes = deepAccess(bid, 'mediaTypes.native.image.sizes', []); + + if (sizes.length > 0) { + const size = Array.isArray(sizes[0]) ? sizes[0] : sizes; + + payload.width = size[0] || payload.width; + payload.height = size[1] || payload.height; + } + } + + const payloadString = JSON.stringify(payload); return { method: 'POST', url: 'https://prebid.smilewanted.com', @@ -90,18 +136,21 @@ export const spec = { /** * Unpack the response from the server into a list of bids. * - * @param {*} serverResponse A successful response from the server. + * @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.body) return []; const bidResponses = []; - var response = serverResponse.body; try { + const response = serverResponse.body; + const bidRequestData = JSON.parse(bidRequest.data); if (response) { const dealId = response.dealId || ''; const bidResponse = { - requestId: JSON.parse(bidRequest.data).bidId, + requestId: bidRequestData.bidId, cpm: response.cpm, width: response.width, height: response.height, @@ -113,14 +162,21 @@ export const spec = { ad: response.ad, }; - if (response.formatTypeSw == 'video_instream' || response.formatTypeSw == 'video_outstream') { + if (response.formatTypeSw === 'video_instream' || response.formatTypeSw === 'video_outstream') { bidResponse['mediaType'] = 'video'; bidResponse['vastUrl'] = response.ad; bidResponse['ad'] = null; + + if (response.formatTypeSw === 'video_outstream') { + bidResponse['renderer'] = newRenderer(bidRequestData, response); + } } - if (response.formatTypeSw == 'video_outstream') { - bidResponse['renderer'] = newRenderer(JSON.parse(bidRequest.data), response); + if (response.formatTypeSw === 'native') { + const nativeAdResponse = JSON.parse(response.ad); + const ortbNativeRequest = toOrtbNativeRequest(bidRequestData.nativeParams); + bidResponse['mediaType'] = 'native'; + bidResponse['native'] = toLegacyResponse(nativeAdResponse, ortbNativeRequest); } if (dealId.length > 0) { @@ -128,7 +184,7 @@ export const spec = { } bidResponse.meta = {}; - if (response.meta && response.meta.advertiserDomains && isArray(response.meta.advertiserDomains)) { + if (response.meta?.advertiserDomains && isArray(response.meta.advertiserDomains)) { bidResponse.meta.advertiserDomains = response.meta.advertiserDomains; } bidResponses.push(bidResponse); @@ -136,15 +192,18 @@ export const spec = { } catch (error) { logError('Error while parsing smilewanted response', error); } + return bidResponses; }, /** - * User syncs. + * Register the user sync pixels which should be dropped after the auction. * - * @param {*} syncOptions Publisher prebid configuration. - * @param {*} serverResponses A successful response from the server. - * @return {Syncs[]} An array of syncs that should be executed. + * @param {SyncOptions} syncOptions Which user syncs are allowed? + * @param {ServerResponse[]} responses List of server's responses. + * @param {Object} gdprConsent The GDPR consent parameters + * @param {Object} uspConsent The USP consent parameters + * @return {UserSync[]} The user syncs which should be dropped. */ getUserSyncs: function(syncOptions, responses, gdprConsent, uspConsent) { let params = ''; @@ -177,7 +236,8 @@ export const spec = { /** * Create SmileWanted renderer - * @param requestId + * @param bidRequest + * @param bidResponse * @returns {*} */ function newRenderer(bidRequest, bidResponse) { diff --git a/test/spec/modules/smilewantedBidAdapter_spec.js b/test/spec/modules/smilewantedBidAdapter_spec.js index 22221dbe1ef..99c4034610f 100644 --- a/test/spec/modules/smilewantedBidAdapter_spec.js +++ b/test/spec/modules/smilewantedBidAdapter_spec.js @@ -93,7 +93,24 @@ const BID_RESPONSE_DISPLAY = { const VIDEO_INSTREAM_REQUEST = [{ code: 'video1', mediaTypes: { - video: {} + video: { + context: 'instream', + mimes: ['video/mp4'], + minduration: 0, + maxduration: 120, + protocols: [1, 2, 3, 4, 5, 6, 7, 8], + startdelay: 0, + placement: 1, + skip: 1, + skipafter: 10, + minbitrate: 10, + maxbitrate: 10, + delivery: [1], + playbackmethod: [2], + api: [1, 2], + linearity: 1, + playerSize: [640, 480] + } }, sizes: [ [640, 480] @@ -163,6 +180,99 @@ const BID_RESPONSE_VIDEO_OUTSTREAM = { } }; +const NATIVE_REQUEST = [{ + adUnitCode: 'native_300x250', + code: '/19968336/prebid_native_example_1', + bidId: '12345', + sizes: [ + [300, 250] + ], + mediaTypes: { + native: { + sendTargetingKeys: false, + title: { + required: true, + len: 140 + }, + image: { + required: true, + sizes: [300, 250] + }, + icon: { + required: false, + sizes: [50, 50] + }, + sponsoredBy: { + required: true + }, + body: { + required: true + }, + clickUrl: { + required: false + }, + privacyLink: { + required: false + }, + cta: { + required: false + }, + rating: { + required: false + }, + likes: { + required: false + }, + downloads: { + required: false + }, + price: { + required: false + }, + salePrice: { + required: false + }, + phone: { + required: false + }, + address: { + required: false + }, + desc2: { + required: false + }, + displayUrl: { + required: false + } + } + }, + bidder: 'smilewanted', + params: { + zoneId: 4, + }, + requestId: 'request_abcd1234', + ortb2Imp: { + ext: { + tid: 'trans_abcd1234', + } + }, +}]; + +const BID_RESPONSE_NATIVE = { + body: { + cpm: 3, + width: 300, + height: 250, + creativeId: 'crea_sw_1', + currency: 'EUR', + isNetCpm: true, + ttl: 300, + ad: '{"link":{"url":"https://www.smilewanted.com"},"assets":[{"id":0,"required":1,"title":{"len":50}},{"id":1,"required":1,"img":{"type":3,"w":150,"h":50,"ext":{"aspectratios":["2:1"]}}},{"id":2,"required":0,"img":{"type":1,"w":50,"h":50,"ext":{"aspectratios":["2:1"]}}},{"id":3,"required":1,"data":{"type":1,"value":"Smilewanted sponsor"}},{"id":4,"required":1,"data":{"type":2,"value":"Smilewanted Description"}}]}', + cSyncUrl: 'https://csync.smilewanted.com', + formatTypeSw: 'native' + } +}; + // Default params with optional ones describe('smilewantedBidAdapterTests', function () { it('SmileWanted - Verify build request', function () { @@ -195,6 +305,23 @@ describe('smilewantedBidAdapterTests', function () { expect(requestVideoInstreamContent.sizes[0]).to.have.property('w').and.to.equal(640); expect(requestVideoInstreamContent.sizes[0]).to.have.property('h').and.to.equal(480); expect(requestVideoInstreamContent).to.have.property('transactionId').and.to.not.equal(null).and.to.not.be.undefined; + expect(requestVideoInstreamContent).to.have.property('videoParams'); + expect(requestVideoInstreamContent.videoParams).to.have.property('context').and.to.equal('instream').and.to.not.be.undefined; + expect(requestVideoInstreamContent.videoParams).to.have.property('mimes').to.be.an('array').that.include('video/mp4').and.to.not.be.undefined; + expect(requestVideoInstreamContent.videoParams).to.have.property('minduration').and.to.equal(0).and.to.not.be.undefined; + expect(requestVideoInstreamContent.videoParams).to.have.property('maxduration').and.to.equal(120).and.to.not.be.undefined; + expect(requestVideoInstreamContent.videoParams).to.have.property('protocols').to.be.an('array').that.include.members([1, 2, 3, 4, 5, 6, 7, 8]).and.to.not.be.undefined; + expect(requestVideoInstreamContent.videoParams).to.have.property('startdelay').and.to.equal(0).and.to.not.be.undefined; + expect(requestVideoInstreamContent.videoParams).to.have.property('placement').and.to.equal(1).and.to.not.be.undefined; + expect(requestVideoInstreamContent.videoParams).to.have.property('skip').and.to.equal(1).and.to.not.be.undefined; + expect(requestVideoInstreamContent.videoParams).to.have.property('skipafter').and.to.equal(10).and.to.not.be.undefined; + expect(requestVideoInstreamContent.videoParams).to.have.property('minbitrate').and.to.equal(10).and.to.not.be.undefined; + expect(requestVideoInstreamContent.videoParams).to.have.property('maxbitrate').and.to.equal(10).and.to.not.be.undefined; + expect(requestVideoInstreamContent.videoParams).to.have.property('delivery').to.be.an('array').that.include(1).and.to.not.be.undefined; + expect(requestVideoInstreamContent.videoParams).to.have.property('playbackmethod').to.be.an('array').that.include(2).and.to.not.be.undefined; + expect(requestVideoInstreamContent.videoParams).to.have.property('api').to.be.an('array').that.include.members([1, 2]).and.to.not.be.undefined; + expect(requestVideoInstreamContent.videoParams).to.have.property('linearity').and.to.equal(1).and.to.not.be.undefined; + expect(requestVideoInstreamContent.videoParams).to.have.property('playerSize').to.be.an('array').that.include.members([640, 480]).and.to.not.be.undefined; const requestVideoOutstream = spec.buildRequests(VIDEO_OUTSTREAM_REQUEST); expect(requestVideoOutstream[0]).to.have.property('url').and.to.equal('https://prebid.smilewanted.com'); @@ -206,6 +333,39 @@ describe('smilewantedBidAdapterTests', function () { expect(requestVideoOutstreamContent.sizes[0]).to.have.property('w').and.to.equal(640); expect(requestVideoOutstreamContent.sizes[0]).to.have.property('h').and.to.equal(480); expect(requestVideoOutstreamContent).to.have.property('transactionId').and.to.not.equal(null).and.to.not.be.undefined; + + const requestNative = spec.buildRequests(NATIVE_REQUEST); + expect(requestNative[0]).to.have.property('url').and.to.equal('https://prebid.smilewanted.com'); + expect(requestNative[0]).to.have.property('method').and.to.equal('POST'); + const requestNativeContent = JSON.parse(requestNative[0].data); + expect(requestNativeContent).to.have.property('zoneId').and.to.equal(4); + expect(requestNativeContent).to.have.property('currencyCode').and.to.equal('EUR'); + expect(requestNativeContent).to.have.property('sizes'); + expect(requestNativeContent.sizes[0]).to.have.property('w').and.to.equal(300); + expect(requestNativeContent.sizes[0]).to.have.property('h').and.to.equal(250); + expect(requestNativeContent).to.have.property('transactionId').and.to.not.equal(null).and.to.not.be.undefined; + expect(requestNativeContent).to.have.property('context').and.to.equal('native').and.to.not.be.undefined; + expect(requestNativeContent).to.have.property('nativeParams'); + expect(requestNativeContent.nativeParams.title).to.have.property('required').and.to.equal(true); + expect(requestNativeContent.nativeParams.title).to.have.property('len').and.to.equal(140); + expect(requestNativeContent.nativeParams.image).to.have.property('required').and.to.equal(true); + expect(requestNativeContent.nativeParams.image).to.have.property('sizes').to.be.an('array').that.include.members([300, 250]).and.to.not.be.undefined; + expect(requestNativeContent.nativeParams.icon).to.have.property('required').and.to.equal(false); + expect(requestNativeContent.nativeParams.icon).to.have.property('sizes').to.be.an('array').that.include.members([50, 50]).and.to.not.be.undefined; + expect(requestNativeContent.nativeParams.sponsoredBy).to.have.property('required').and.to.equal(true); + expect(requestNativeContent.nativeParams.body).to.have.property('required').and.to.equal(true); + expect(requestNativeContent.nativeParams.clickUrl).to.have.property('required').and.to.equal(false); + expect(requestNativeContent.nativeParams.privacyLink).to.have.property('required').and.to.equal(false); + expect(requestNativeContent.nativeParams.cta).to.have.property('required').and.to.equal(false); + expect(requestNativeContent.nativeParams.rating).to.have.property('required').and.to.equal(false); + expect(requestNativeContent.nativeParams.likes).to.have.property('required').and.to.equal(false); + expect(requestNativeContent.nativeParams.downloads).to.have.property('required').and.to.equal(false); + expect(requestNativeContent.nativeParams.price).to.have.property('required').and.to.equal(false); + expect(requestNativeContent.nativeParams.salePrice).to.have.property('required').and.to.equal(false); + expect(requestNativeContent.nativeParams.phone).to.have.property('required').and.to.equal(false); + expect(requestNativeContent.nativeParams.address).to.have.property('required').and.to.equal(false); + expect(requestNativeContent.nativeParams.desc2).to.have.property('required').and.to.equal(false); + expect(requestNativeContent.nativeParams.displayUrl).to.have.property('required').and.to.equal(false); }); it('SmileWanted - Verify build request with referrer', function () { @@ -337,7 +497,7 @@ describe('smilewantedBidAdapterTests', function () { }).to.not.throw(); }); - it('SmileWanted - Verify parse response - Video Oustream', function () { + it('SmileWanted - Verify parse response - Video Outstream', function () { const request = spec.buildRequests(VIDEO_OUTSTREAM_REQUEST); const bids = spec.interpretResponse(BID_RESPONSE_VIDEO_OUTSTREAM, request[0]); expect(bids).to.have.lengthOf(1); @@ -360,6 +520,28 @@ describe('smilewantedBidAdapterTests', function () { }).to.not.throw(); }); + it('SmileWanted - Verify parse response - Native', function () { + const request = spec.buildRequests(NATIVE_REQUEST); + const bids = spec.interpretResponse(BID_RESPONSE_NATIVE, request[0]); + expect(bids).to.have.lengthOf(1); + const bid = bids[0]; + expect(bid.cpm).to.equal(3); + expect(bid.ad).to.equal('{"link":{"url":"https://www.smilewanted.com"},"assets":[{"id":0,"required":1,"title":{"len":50}},{"id":1,"required":1,"img":{"type":3,"w":150,"h":50,"ext":{"aspectratios":["2:1"]}}},{"id":2,"required":0,"img":{"type":1,"w":50,"h":50,"ext":{"aspectratios":["2:1"]}}},{"id":3,"required":1,"data":{"type":1,"value":"Smilewanted sponsor"}},{"id":4,"required":1,"data":{"type":2,"value":"Smilewanted Description"}}]}'); + expect(bid.width).to.equal(300); + expect(bid.height).to.equal(250); + expect(bid.creativeId).to.equal('crea_sw_1'); + expect(bid.currency).to.equal('EUR'); + expect(bid.netRevenue).to.equal(true); + expect(bid.ttl).to.equal(300); + expect(bid.requestId).to.equal(NATIVE_REQUEST[0].bidId); + + expect(function () { + spec.interpretResponse(BID_RESPONSE_NATIVE, { + data: 'invalid Json' + }) + }).to.not.throw(); + }); + it('SmileWanted - Verify bidder code', function () { expect(spec.code).to.equal('smilewanted'); }); From 1ec144fab6b13135990b54b7b6894287115add67 Mon Sep 17 00:00:00 2001 From: Chris Southern <79725079+southern-growthcode@users.noreply.github.com> Date: Tue, 30 Jan 2024 13:39:41 -0500 Subject: [PATCH 185/246] GC-179 Simpliy the userId module, and added feature to allow customers to provide custom EIDs (#11004) --- modules/growthCodeIdSystem.js | 169 +++---------------- modules/growthCodeIdSystem.md | 40 +++-- test/spec/modules/growthCodeIdSystem_spec.js | 66 +++----- 3 files changed, 76 insertions(+), 199 deletions(-) diff --git a/modules/growthCodeIdSystem.js b/modules/growthCodeIdSystem.js index 539a20a7302..cf72e2e5133 100644 --- a/modules/growthCodeIdSystem.js +++ b/modules/growthCodeIdSystem.js @@ -5,88 +5,15 @@ * @requires module:modules/userId */ -import {logError, logInfo, pick} from '../src/utils.js'; -import {ajax} from '../src/ajax.js'; import { submodule } from '../src/hook.js' import {getStorageManager} from '../src/storageManager.js'; import {MODULE_TYPE_UID} from '../src/activities/modules.js'; -import {tryAppendQueryString} from '../libraries/urlUtils/urlUtils.js'; - -/** - * @typedef {import('../modules/userId/index.js').Submodule} Submodule - * @typedef {import('../modules/userId/index.js').SubmoduleConfig} SubmoduleConfig - * @typedef {import('../modules/userId/index.js').IdResponse} IdResponse - */ const MODULE_NAME = 'growthCodeId'; -const GC_DATA_KEY = '_gc_data'; const GCID_KEY = 'gcid'; -const ENDPOINT_URL = 'https://p2.gcprivacy.com/v1/pb?' export const storage = getStorageManager({ moduleType: MODULE_TYPE_UID, moduleName: MODULE_NAME }); -/** - * Read GrowthCode data from cookie or local storage - * @param key - * @return {string} - */ -export function readData(key) { - try { - let payload - if (storage.cookiesAreEnabled(null)) { - payload = tryParse(storage.getCookie(key, null)) - } - if (storage.hasLocalStorage()) { - payload = tryParse(storage.getDataFromLocalStorage(key, null)) - } - if (payload !== undefined) { - if (payload.expire_at > (Date.now() / 1000)) { - return payload - } - } - } catch (error) { - logError(error); - } -} - -/** - * Store GrowthCode data in either cookie or local storage - * expiration date: 45 days - * @param key - * @param {string} value - */ -function storeData(key, value) { - try { - logInfo(MODULE_NAME + ': storing data: key=' + key + ' value=' + value); - - if (value) { - if (storage.hasLocalStorage(null)) { - storage.setDataInLocalStorage(key, value, null); - } - } - } catch (error) { - logError(error); - } -} - -/** - * Parse json if possible, else return null - * @param data - * @param {object|null} - */ -function tryParse(data) { - let payload; - try { - payload = JSON.parse(data); - if (payload == null) { - return undefined - } - return payload - } catch (err) { - return undefined; - } -} - /** @type {Submodule} */ export const growthCodeIdSubmodule = { /** @@ -103,96 +30,40 @@ export const growthCodeIdSubmodule = { decode(value) { return value && value !== '' ? { 'growthCodeId': value } : undefined; }, + /** * performs action to obtain id and return a value in the callback's response argument * @function * @param {SubmoduleConfig} [config] * @returns {IdResponse|undefined} */ - getId(config, consentData) { + getId(config) { const configParams = (config && config.params) || {}; - if (!configParams || typeof configParams.pid !== 'string') { - logError('User ID - GrowthCodeID submodule requires a valid Partner ID to be defined'); - return; - } - const gdpr = (consentData && typeof consentData.gdprApplies === 'boolean' && consentData.gdprApplies) ? 1 : 0; - const consentString = gdpr ? consentData.consentString : ''; - if (gdpr && !consentString) { - logInfo('Consent string is required to call GrowthCode id.'); - return; - } + let ids = []; + let gcid = storage.getDataFromLocalStorage(GCID_KEY, null) - let publisherId = configParams.publisher_id ? configParams.publisher_id : '_sharedID'; + if (gcid !== null) { + const gcEid = { + source: 'growthcode.io', + uids: [{ + id: gcid, + atype: 3, + }] + } - let sharedId; - if (configParams.publisher_id_storage === 'html5') { - sharedId = storage.getDataFromLocalStorage(publisherId, null) ? (storage.getDataFromLocalStorage(publisherId, null)) : null; - } else { - sharedId = storage.getCookie(publisherId, null) ? (storage.getCookie(publisherId, null)) : null; - } - if (!sharedId) { - logError('User ID - Publisher ID is not correctly setup.'); + ids = ids.concat(gcEid) } - const resp = function(callback) { - let gcData = readData(GC_DATA_KEY); - if (gcData) { - callback(gcData); - } else { - let segment = window.location.pathname.substr(1).replace(/\/+$/, ''); - if (segment === '') { - segment = 'home'; - } - - let url = configParams.url ? configParams.url : ENDPOINT_URL; - url = tryAppendQueryString(url, 'pid', configParams.pid); - url = tryAppendQueryString(url, 'uid', sharedId); - url = tryAppendQueryString(url, 'u', window.location.href); - url = tryAppendQueryString(url, 'h', window.location.hostname); - url = tryAppendQueryString(url, 's', segment); - url = tryAppendQueryString(url, 'r', document.referrer); + let additionalEids = storage.getDataFromLocalStorage(configParams.customerEids, null) + if (additionalEids !== null) { + let data = JSON.parse(additionalEids) + ids = ids.concat(data) + } - ajax(url, { - success: response => { - let respJson = tryParse(response); - // If response is a valid json and should save is true - if (respJson) { - storeData(GC_DATA_KEY, JSON.stringify(respJson)) - storeData(GCID_KEY, respJson.gc_id); - callback(respJson); - } else { - callback(); - } - }, - error: error => { - logError(MODULE_NAME + ': ID fetch encountered an error', error); - callback(); - } - }, undefined, {method: 'GET', withCredentials: true}) - } - }; - return { callback: resp }; + return {id: ids} }, - eids: { - 'growthCodeId': { - getValue: function(data) { - return data.gc_id - }, - source: 'growthcode.io', - atype: 1, - getUidExt: function(data) { - const extendedData = pick(data, [ - 'h1', - 'h2', - 'h3', - ]); - if (Object.keys(extendedData).length) { - return extendedData; - } - } - }, - } + }; submodule('userId', growthCodeIdSubmodule); diff --git a/modules/growthCodeIdSystem.md b/modules/growthCodeIdSystem.md index f804686a7a9..de5344e966b 100644 --- a/modules/growthCodeIdSystem.md +++ b/modules/growthCodeIdSystem.md @@ -18,20 +18,38 @@ pbjs.setConfig({ userIds: [{ name: 'growthCodeId', params: { - pid: 'TEST01', // Set your Partner ID here for production (obtained from Growthcode) - publisher_id: '_sharedID', - publisher_id_storage: 'html5' + customerEids: 'customerEids', } }] } }); ``` -| Param under userSync.userIds[] | Scope | Type | Description | Example | -|--------------------------------|----------|--------| --- |-----------------| -| name | Required | String | The name of this module. | `"growthCodeId"` | -| params | Required | Object | Details of module params. | | -| params.pid | Required | String | This is the Parter ID value obtained from GrowthCode | `"TEST01"` | -| params.url | Optional | String | Custom URL for server | | -| params.publisher_id | Optional | String | Name if the variable that holds your publisher ID | `"_sharedID"` | -| params.publisher_id_storage | Optional | String | Publisher ID storage (cookie, html5) | `"html5"` | +### Sample Eids +Below is an example of the EIDs stored in Local Store (customerEids) +```json +[ + { + "source":"domain.com", + "uids":[ + { + "id":"8212212191539393121", + "ext":{ + "stype":"ppuid" + } + } + ] + }, + { + "source":"example.com", + "uids":[ + { + "id":"e06e9e5a-273c-46f8-aace-6f62cf13ea71", + "ext":{ + "stype":"ppuid" + } + } + ] + } +] +``` diff --git a/test/spec/modules/growthCodeIdSystem_spec.js b/test/spec/modules/growthCodeIdSystem_spec.js index 97083047d4e..e3848dc4844 100644 --- a/test/spec/modules/growthCodeIdSystem_spec.js +++ b/test/spec/modules/growthCodeIdSystem_spec.js @@ -6,9 +6,12 @@ import {expect} from 'chai'; import {getStorageManager} from '../../../src/storageManager.js'; import {MODULE_TYPE_UID} from '../../../src/activities/modules.js'; -const GCID_EXPIRY = 45; const MODULE_NAME = 'growthCodeId'; -const SHAREDID = 'fe9c5c89-7d56-4666-976d-e07e73b3b664'; +const EIDS = '[{"source":"domain.com","uids":[{"id":"8212212191539393121","ext":{"stype":"ppuid"}}]}]'; +const GCID = 'e06e9e5a-273c-46f8-aace-6f62cf13ea71' + +const GCID_EID = '{"id": [{"source": "growthcode.io", "uids": [{"atype": 3,"id": "e06e9e5a-273c-46f8-aace-6f62cf13ea71"}]}]}' +const GCID_EID_EID = '{"id": [{"source": "growthcode.io", "uids": [{"atype": 3,"id": "e06e9e5a-273c-46f8-aace-6f62cf13ea71"}]},{"source": "domain.com", "uids": [{"id": "8212212191539393121", "ext": {"stype":"ppuid"}}]}]}' const storage = getStorageManager({ moduleType: MODULE_TYPE_UID, moduleName: MODULE_NAME }); @@ -23,11 +26,8 @@ describe('growthCodeIdSystem', () => { beforeEach(function () { logErrorStub = sinon.stub(utils, 'logError'); - storage.setDataInLocalStorage('_sharedid', SHAREDID); - const expiresStr = (new Date(Date.now() + (GCID_EXPIRY * (60 * 60 * 24 * 1000)))).toUTCString(); - if (storage.cookiesAreEnabled()) { - storage.setCookie('_sharedid', SHAREDID, expiresStr, 'LAX'); - } + storage.setDataInLocalStorage('gcid', GCID, null); + storage.setDataInLocalStorage('customerEids', EIDS, null); }); afterEach(function () { @@ -40,45 +40,33 @@ describe('growthCodeIdSystem', () => { }); }); - it('should NOT call the growthcode id endpoint if gdpr applies but consent string is missing', function () { - let submoduleCallback = growthCodeIdSubmodule.getId(getIdParams, { gdprApplies: true }, undefined); - expect(submoduleCallback).to.be.undefined; - }); - - it('should log an error if pid configParam was not passed when getId', function () { - growthCodeIdSubmodule.getId(); - expect(logErrorStub.callCount).to.be.equal(1); + it('test return of GCID', function () { + let ids; + ids = growthCodeIdSubmodule.getId(); + expect(ids).to.deep.equal(JSON.parse(GCID_EID)); }); - it('should log an error if sharedId (LocalStore) is not setup correctly', function () { - growthCodeIdSubmodule.getId({params: { - pid: 'TEST01', - publisher_id: '_sharedid_bad', - publisher_id_storage: 'html5', + it('test return of the GCID and an additional EID', function () { + let ids; + ids = growthCodeIdSubmodule.getId({params: { + customerEids: 'customerEids', }}); - expect(logErrorStub.callCount).to.be.equal(1); + expect(ids).to.deep.equal(JSON.parse(GCID_EID_EID)); }); - it('should log an error if sharedId (LocalStore) is not setup correctly', function () { - growthCodeIdSubmodule.getId({params: { - pid: 'TEST01', - publisher_id: '_sharedid_bad', - publisher_id_storage: 'cookie', + it('test return of the GCID and an additional EID (bad Local Store name)', function () { + let ids; + ids = growthCodeIdSubmodule.getId({params: { + customerEids: 'customerEidsBad', }}); - expect(logErrorStub.callCount).to.be.equal(1); + expect(ids).to.deep.equal(JSON.parse(GCID_EID)); }); - it('should call the growthcode id endpoint', function () { - let callBackSpy = sinon.spy(); - let submoduleCallback = growthCodeIdSubmodule.getId(getIdParams).callback; - submoduleCallback(callBackSpy); - let request = server.requests[0]; - expect(request.url.substr(0, 85)).to.be.eq('https://p2.gcprivacy.com/v1/pb?pid=TEST01&uid=' + SHAREDID + '&u='); - request.respond( - 200, - {}, - JSON.stringify({}) - ); - expect(callBackSpy.calledOnce).to.be.true; + it('test decode function)', function () { + let ids; + ids = growthCodeIdSubmodule.decode(GCID, {params: { + customerEids: 'customerEids', + }}); + expect(ids).to.deep.equal(JSON.parse('{"growthCodeId":"' + GCID + '"}')); }); }) From c161e0c57b0d87c338b64766fd7b32cf2019c8aa Mon Sep 17 00:00:00 2001 From: Jeremy Marzka Date: Tue, 30 Jan 2024 12:41:26 -0600 Subject: [PATCH 186/246] E2E testing: Remove @wdio/sync and update @wdio/* to the latest (#10990) * Remove @wdio/sync and update the rest of the @wdio packages * Update the e2e tests to use async/await * Add support for local e2e testing * Update circleci to Node 16 * Update the min Node version to 12 and a a node check for e2e tests * Fix the Node version check error --- .circleci/config.yml | 2 +- README.md | 6 + gulpfile.js | 61 +- package-lock.json | 16317 +++++++++++----- package.json | 15 +- test/helpers/testing-utils.js | 25 +- test/spec/e2e/banner/basic_banner_ad.spec.js | 4 +- .../instream/basic_instream_video_ad.spec.js | 4 +- .../longform/basic_w_bidderSettings.spec.js | 32 +- ...asic_w_custom_adserver_translation.spec.js | 32 +- .../e2e/longform/basic_w_priceGran.spec.js | 32 +- .../basic_w_requireExactDuration.spec.js | 32 +- .../basic_wo_brandCategoryExclusion.spec.js | 30 +- .../basic_wo_requireExactDuration.spec.js | 28 +- .../e2e/modules/e2e_bidderSettings.spec.js | 4 +- test/spec/e2e/modules/e2e_currency.spec.js | 4 +- .../multi-bidder/e2e_multiple_bidders.spec.js | 4 +- test/spec/e2e/native/basic_native_ad.spec.js | 4 +- .../basic_outstream_video_ad.spec.js | 12 +- wdio.conf.js | 24 +- wdio.local.conf.js | 13 + wdio.shared.conf.js | 23 + 22 files changed, 11550 insertions(+), 5158 deletions(-) create mode 100644 wdio.local.conf.js create mode 100644 wdio.shared.conf.js diff --git a/.circleci/config.yml b/.circleci/config.yml index 84ddcdf26ea..22539912268 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -7,7 +7,7 @@ aliases: - &environment docker: # specify the version you desire here - - image: circleci/node:12.16.1-browsers + - image: cimg/node:16.20-browsers resource_class: xlarge # Specify service dependencies here if necessary # CircleCI maintains a library of pre-built images diff --git a/README.md b/README.md index 58007519b15..e6d25a5cb5a 100644 --- a/README.md +++ b/README.md @@ -255,6 +255,12 @@ gulp test-coverage gulp view-coverage ``` +Local end-to-end testing can be done with: + +```bash +gulp e2e-test --local +``` + For Prebid.org members with access to BrowserStack, additional end-to-end testing can be done with: ```bash diff --git a/gulpfile.js b/gulpfile.js index 5e16af8b0c1..d8a18ba3df1 100644 --- a/gulpfile.js +++ b/gulpfile.js @@ -52,6 +52,18 @@ function clean() { .pipe(gulpClean()); } +function requireNodeVersion(version) { + return (done) => { + const [major] = process.versions.node.split('.'); + + if (major < version) { + throw new Error(`This task requires Node v${version}`) + } + + done(); + } +} + // Dependant task for building postbid. It escapes postbid-config file. function escapePostbidConfig() { gulp.src('./integrationExamples/postbid/oas/postbid-config.js') @@ -293,7 +305,7 @@ function bundle(dev, moduleArr) { // If --notest is given, it will immediately skip the test task (useful for developing changes with `gulp serve --notest`) function testTaskMaker(options = {}) { - ['watch', 'e2e', 'file', 'browserstack', 'notest'].forEach(opt => { + ['watch', 'file', 'browserstack', 'notest'].forEach(opt => { options[opt] = options.hasOwnProperty(opt) ? options[opt] : argv[opt]; }) @@ -302,22 +314,6 @@ function testTaskMaker(options = {}) { return function test(done) { if (options.notest) { done(); - } else if (options.e2e) { - const integ = startIntegServer(); - startLocalServer(); - runWebdriver(options) - .then(stdout => { - // kill fake server - integ.kill('SIGINT'); - done(); - process.exit(0); - }) - .catch(err => { - // kill fake server - integ.kill('SIGINT'); - done(new Error(`Tests failed with error: ${err}`)); - process.exit(1); - }); } else { runKarma(options, done) } @@ -326,10 +322,34 @@ function testTaskMaker(options = {}) { const test = testTaskMaker(); +function e2eTestTaskMaker() { + return function test(done) { + const integ = startIntegServer(); + startLocalServer(); + runWebdriver({}) + .then(stdout => { + // kill fake server + integ.kill('SIGINT'); + done(); + process.exit(0); + }) + .catch(err => { + // kill fake server + integ.kill('SIGINT'); + done(new Error(`Tests failed with error: ${err}`)); + process.exit(1); + }); + } +} + function runWebdriver({file}) { process.env.TEST_SERVER_HOST = argv.host || 'localhost'; + + let local = argv.local || false; + + let wdioConfFile = local === true ? 'wdio.local.conf.js' : 'wdio.conf.js'; let wdioCmd = path.join(__dirname, 'node_modules/.bin/wdio'); - let wdioConf = path.join(__dirname, 'wdio.conf.js'); + let wdioConf = path.join(__dirname, wdioConfFile); let wdioOpts; if (file) { @@ -486,8 +506,9 @@ gulp.task('serve-e2e-dev', gulp.series(clean, 'build-bundle-dev', gulp.parallel( gulp.task('default', gulp.series(clean, 'build-bundle-prod')); -gulp.task('e2e-test-only', () => runWebdriver({file: argv.file})); -gulp.task('e2e-test', gulp.series(clean, 'build-bundle-prod', testTaskMaker({e2e: true}))); +gulp.task('e2e-test-only', gulp.series(requireNodeVersion(16), () => runWebdriver({file: argv.file}))); +gulp.task('e2e-test', gulp.series(requireNodeVersion(16), clean, 'build-bundle-prod', e2eTestTaskMaker())); + // other tasks gulp.task(bundleToStdout); gulp.task('bundle', gulpBundle.bind(null, false)); // used for just concatenating pre-built files with no build step diff --git a/package-lock.json b/package-lock.json index 87110627d26..692afaa120a 100644 --- a/package-lock.json +++ b/package-lock.json @@ -6,7 +6,7 @@ "packages": { "": { "name": "prebid.js", - "version": "8.32.0-pre", + "version": "8.33.0-pre", "license": "Apache-2.0", "dependencies": { "@babel/core": "^7.16.7", @@ -26,13 +26,12 @@ }, "devDependencies": { "@babel/eslint-parser": "^7.16.5", - "@wdio/browserstack-service": "~7.16.0", - "@wdio/cli": "~7.5.2", - "@wdio/concise-reporter": "~7.5.2", - "@wdio/local-runner": "~7.5.2", - "@wdio/mocha-framework": "~7.5.2", - "@wdio/spec-reporter": "~7.19.0", - "@wdio/sync": "~7.5.2", + "@wdio/browserstack-service": "^8.29.0", + "@wdio/cli": "^8.29.0", + "@wdio/concise-reporter": "^8.29.0", + "@wdio/local-runner": "^8.29.0", + "@wdio/mocha-framework": "^8.29.0", + "@wdio/spec-reporter": "^8.29.0", "ajv": "6.12.3", "assert": "^2.0.0", "babel-loader": "^8.0.5", @@ -110,7 +109,7 @@ "yargs": "^1.3.1" }, "engines": { - "node": ">=8.9.0" + "node": ">=12.0.0" }, "optionalDependencies": { "fsevents": "^2.3.2" @@ -1602,19 +1601,6 @@ "node": ">=6.9.0" } }, - "node_modules/@babel/runtime-corejs3": { - "version": "7.20.1", - "resolved": "https://registry.npmjs.org/@babel/runtime-corejs3/-/runtime-corejs3-7.20.1.tgz", - "integrity": "sha512-CGulbEDcg/ND1Im7fUNRZdGXmX2MTWVVZacQi/6DiKE5HNwZ3aVTm5PV4lO8HHz0B2h8WQyvKKjbX5XgTtydsg==", - "dev": true, - "dependencies": { - "core-js-pure": "^3.25.1", - "regenerator-runtime": "^0.13.10" - }, - "engines": { - "node": ">=6.9.0" - } - }, "node_modules/@babel/template": { "version": "7.22.15", "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.22.15.tgz", @@ -1911,6 +1897,102 @@ "integrity": "sha512-ZnQMnLV4e7hDlUvw8H+U8ASL02SS2Gn6+9Ac3wGGLIe7+je2AeAOxPY+izIPJDfFDb7eDjev0Us8MO1iFRN8hA==", "dev": true }, + "node_modules/@isaacs/cliui": { + "version": "8.0.2", + "resolved": "https://registry.npmjs.org/@isaacs/cliui/-/cliui-8.0.2.tgz", + "integrity": "sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA==", + "dev": true, + "dependencies": { + "string-width": "^5.1.2", + "string-width-cjs": "npm:string-width@^4.2.0", + "strip-ansi": "^7.0.1", + "strip-ansi-cjs": "npm:strip-ansi@^6.0.1", + "wrap-ansi": "^8.1.0", + "wrap-ansi-cjs": "npm:wrap-ansi@^7.0.0" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/@isaacs/cliui/node_modules/ansi-regex": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.0.1.tgz", + "integrity": "sha512-n5M855fKb2SsfMIiFFoVrABHJC8QtHwVx+mHWP3QcEqBHYienj5dHSgjbxtC0WEZXYt4wcD6zrQElDPhFuZgfA==", + "dev": true, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-regex?sponsor=1" + } + }, + "node_modules/@isaacs/cliui/node_modules/ansi-styles": { + "version": "6.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.1.tgz", + "integrity": "sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug==", + "dev": true, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/@isaacs/cliui/node_modules/emoji-regex": { + "version": "9.2.2", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-9.2.2.tgz", + "integrity": "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==", + "dev": true + }, + "node_modules/@isaacs/cliui/node_modules/string-width": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-5.1.2.tgz", + "integrity": "sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA==", + "dev": true, + "dependencies": { + "eastasianwidth": "^0.2.0", + "emoji-regex": "^9.2.2", + "strip-ansi": "^7.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@isaacs/cliui/node_modules/strip-ansi": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.0.tgz", + "integrity": "sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ==", + "dev": true, + "dependencies": { + "ansi-regex": "^6.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/strip-ansi?sponsor=1" + } + }, + "node_modules/@isaacs/cliui/node_modules/wrap-ansi": { + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-8.1.0.tgz", + "integrity": "sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ==", + "dev": true, + "dependencies": { + "ansi-styles": "^6.1.0", + "string-width": "^5.0.1", + "strip-ansi": "^7.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } + }, "node_modules/@istanbuljs/load-nyc-config": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/@istanbuljs/load-nyc-config/-/load-nyc-config-1.1.0.tgz", @@ -1936,20 +2018,45 @@ "node": ">=8" } }, + "node_modules/@jest/expect-utils": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/@jest/expect-utils/-/expect-utils-29.7.0.tgz", + "integrity": "sha512-GlsNBWiFQFCVi9QVSx7f5AgMeLxe9YCCs5PuP2O2LdjDAA8Jh9eX7lA1Jq/xdXw3Wb3hyvlFNfZIfcRetSzYcA==", + "dev": true, + "dependencies": { + "jest-get-type": "^29.6.3" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/@jest/schemas": { + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/@jest/schemas/-/schemas-29.6.3.tgz", + "integrity": "sha512-mo5j5X+jIZmJQveBKeS/clAueipV7KgiX1vMgCxam1RNYiqE1w62n0/tJJnHtjW8ZHcQco5gY85jA3mi0L+nSA==", + "dev": true, + "dependencies": { + "@sinclair/typebox": "^0.27.8" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, "node_modules/@jest/types": { - "version": "26.6.2", - "resolved": "https://registry.npmjs.org/@jest/types/-/types-26.6.2.tgz", - "integrity": "sha512-fC6QCp7Sc5sX6g8Tvbmj4XUTbyrik0akgRy03yjXbQaBWWNWGE7SGtJk98m0N8nzegD/7SggrUlivxo5ax4KWQ==", + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/@jest/types/-/types-29.6.3.tgz", + "integrity": "sha512-u3UPsIilWKOM3F9CXtrG8LEJmNxwoCQC/XVj4IKYXvvpx7QIi/Kg1LI5uDmDpKlac62NUtX7eLjRh+jVZcLOzw==", "dev": true, "dependencies": { + "@jest/schemas": "^29.6.3", "@types/istanbul-lib-coverage": "^2.0.0", "@types/istanbul-reports": "^3.0.0", "@types/node": "*", - "@types/yargs": "^15.0.0", + "@types/yargs": "^17.0.8", "chalk": "^4.0.0" }, "engines": { - "node": ">= 10.14.2" + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" } }, "node_modules/@jest/types/node_modules/ansi-styles": { @@ -2088,6 +2195,18 @@ "@jridgewell/sourcemap-codec": "1.4.14" } }, + "node_modules/@ljharb/through": { + "version": "2.3.12", + "resolved": "https://registry.npmjs.org/@ljharb/through/-/through-2.3.12.tgz", + "integrity": "sha512-ajo/heTlG3QgC8EGP6APIejksVAYt4ayz4tqoP3MolFELzcH1x1fzwEYRJTPO0IELutZ5HQ0c26/GqAYy79u3g==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.5" + }, + "engines": { + "node": ">= 0.4" + } + }, "node_modules/@nicolo-ribaudo/eslint-scope-5-internals": { "version": "5.1.1-v1", "resolved": "https://registry.npmjs.org/@nicolo-ribaudo/eslint-scope-5-internals/-/eslint-scope-5-internals-5.1.1-v1.tgz", @@ -2097,12 +2216,142 @@ "eslint-scope": "5.1.1" } }, + "node_modules/@open-draft/until": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/@open-draft/until/-/until-1.0.3.tgz", + "integrity": "sha512-Aq58f5HiWdyDlFffbbSjAlv596h/cOnt2DO1w3DOC7OJ5EHs0hd/nycJfiu9RJbT6Yk6F1knnRRXNSpxoIVZ9Q==", + "dev": true + }, + "node_modules/@percy/appium-app": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/@percy/appium-app/-/appium-app-2.0.3.tgz", + "integrity": "sha512-6INeUJSyK2LzWV4Cc9bszNqKr3/NLcjFelUC2grjPnm6+jLA29inBF4ZE3PeTfLeCSw/0jyCGWV5fr9AyxtzCA==", + "dev": true, + "dependencies": { + "@percy/sdk-utils": "^1.27.0-beta.0", + "tmp": "^0.2.1" + }, + "engines": { + "node": ">=14" + } + }, + "node_modules/@percy/appium-app/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/@percy/sdk-utils": { + "version": "1.27.7", + "resolved": "https://registry.npmjs.org/@percy/sdk-utils/-/sdk-utils-1.27.7.tgz", + "integrity": "sha512-E21dIEQ9wwGDno41FdMDYf6jJow5scbWGClqKE/ptB+950W4UF5C4hxhVVQoEJxDdLE/Gy/8ZJR7upvPHShWDg==", + "dev": true, + "engines": { + "node": ">=14" + } + }, + "node_modules/@percy/selenium-webdriver": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/@percy/selenium-webdriver/-/selenium-webdriver-2.0.3.tgz", + "integrity": "sha512-JfLJVRkwNfqVofe7iGKtoQbOcKSSj9t4pWFbSUk95JfwAA7b9/c+dlBsxgIRrdrMYzLRjnJkYAFSZkJ4F4A19A==", + "dev": true, + "dependencies": { + "@percy/sdk-utils": "^1.27.2", + "node-request-interceptor": "^0.6.3" + }, + "engines": { + "node": ">=14" + } + }, + "node_modules/@pkgjs/parseargs": { + "version": "0.11.0", + "resolved": "https://registry.npmjs.org/@pkgjs/parseargs/-/parseargs-0.11.0.tgz", + "integrity": "sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg==", + "dev": true, + "optional": true, + "engines": { + "node": ">=14" + } + }, "node_modules/@polka/url": { "version": "1.0.0-next.21", "resolved": "https://registry.npmjs.org/@polka/url/-/url-1.0.0-next.21.tgz", "integrity": "sha512-a5Sab1C4/icpTZVzZc5Ghpz88yQtGOyNqYXcZgOssB2uuAr+wF/MvN6bgtW32q7HHrvBki+BsZ0OuNv6EV3K9g==", "dev": true }, + "node_modules/@puppeteer/browsers": { + "version": "1.9.1", + "resolved": "https://registry.npmjs.org/@puppeteer/browsers/-/browsers-1.9.1.tgz", + "integrity": "sha512-PuvK6xZzGhKPvlx3fpfdM2kYY3P/hB1URtK8wA7XUJ6prn6pp22zvJHu48th0SGcHL9SutbPHrFuQgfXTFobWA==", + "dev": true, + "dependencies": { + "debug": "4.3.4", + "extract-zip": "2.0.1", + "progress": "2.0.3", + "proxy-agent": "6.3.1", + "tar-fs": "3.0.4", + "unbzip2-stream": "1.4.3", + "yargs": "17.7.2" + }, + "bin": { + "browsers": "lib/cjs/main-cli.js" + }, + "engines": { + "node": ">=16.3.0" + } + }, + "node_modules/@puppeteer/browsers/node_modules/tar-fs": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/tar-fs/-/tar-fs-3.0.4.tgz", + "integrity": "sha512-5AFQU8b9qLfZCX9zp2duONhPmZv0hGYiBPJsyUdqMjzq/mqVpy/rEUSeHk1+YitmxugaptgBh5oDGU3VsAJq4w==", + "dev": true, + "dependencies": { + "mkdirp-classic": "^0.5.2", + "pump": "^3.0.0", + "tar-stream": "^3.1.5" + } + }, + "node_modules/@puppeteer/browsers/node_modules/tar-stream": { + "version": "3.1.7", + "resolved": "https://registry.npmjs.org/tar-stream/-/tar-stream-3.1.7.tgz", + "integrity": "sha512-qJj60CXt7IU1Ffyc3NJMjh6EkuCFej46zUqJ4J7pqYlThyd9bO0XBTmcOIhSzZJVWfsLks0+nle/j538YAW9RQ==", + "dev": true, + "dependencies": { + "b4a": "^1.6.4", + "fast-fifo": "^1.2.0", + "streamx": "^2.15.0" + } + }, + "node_modules/@puppeteer/browsers/node_modules/yargs": { + "version": "17.7.2", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.7.2.tgz", + "integrity": "sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==", + "dev": true, + "dependencies": { + "cliui": "^8.0.1", + "escalade": "^3.1.1", + "get-caller-file": "^2.0.5", + "require-directory": "^2.1.1", + "string-width": "^4.2.3", + "y18n": "^5.0.5", + "yargs-parser": "^21.1.1" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/@sinclair/typebox": { + "version": "0.27.8", + "resolved": "https://registry.npmjs.org/@sinclair/typebox/-/typebox-0.27.8.tgz", + "integrity": "sha512-+Fj43pSMwJs4KRrH/938Uf+uAELIgVBmQzg/q1YG10djyfA3TnrU8N8XzqCh/okZdszqBQTZf96idMfE5lnwTA==", + "dev": true + }, "node_modules/@sindresorhus/is": { "version": "4.6.0", "resolved": "https://registry.npmjs.org/@sindresorhus/is/-/is-4.6.0.tgz", @@ -2168,6 +2417,23 @@ "node": ">=10" } }, + "node_modules/@tootallnate/once": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/@tootallnate/once/-/once-2.0.0.tgz", + "integrity": "sha512-XCuKFP5PS55gnMVu3dty8KPatLqUoy/ZYzDzAGCQ8JNFCkLXzmI7vNHCR+XpbZaMWQK/vQubr7PkYq8g470J/A==", + "dev": true, + "optional": true, + "peer": true, + "engines": { + "node": ">= 10" + } + }, + "node_modules/@tootallnate/quickjs-emscripten": { + "version": "0.23.0", + "resolved": "https://registry.npmjs.org/@tootallnate/quickjs-emscripten/-/quickjs-emscripten-0.23.0.tgz", + "integrity": "sha512-C5Mc6rdnsaJDjO3UpGW/CQTHtCKaYlScZTly4JIu97Jxo/odCiH0ITnDXSJPTOrEKk/ycSZ0AOgTmkDtkOsvIA==", + "dev": true + }, "node_modules/@types/aria-query": { "version": "5.0.1", "resolved": "https://registry.npmjs.org/@types/aria-query/-/aria-query-5.0.1.tgz", @@ -2210,24 +2476,6 @@ "@types/ms": "*" } }, - "node_modules/@types/diff": { - "version": "5.0.2", - "resolved": "https://registry.npmjs.org/@types/diff/-/diff-5.0.2.tgz", - "integrity": "sha512-uw8eYMIReOwstQ0QKF0sICefSy8cNO/v7gOTiIy9SbwuHyEecJUm7qlgueOO5S1udZ5I/irVydHVwMchgzbKTg==", - "dev": true - }, - "node_modules/@types/easy-table": { - "version": "0.0.33", - "resolved": "https://registry.npmjs.org/@types/easy-table/-/easy-table-0.0.33.tgz", - "integrity": "sha512-/vvqcJPmZUfQwCgemL0/34G7bIQnCuvgls379ygRlcC1FqNqk3n+VZ15dAO51yl6JNDoWd8vsk+kT8zfZ1VZSw==", - "dev": true - }, - "node_modules/@types/ejs": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/@types/ejs/-/ejs-3.1.1.tgz", - "integrity": "sha512-RQul5wEfY7BjWm0sYY86cmUN/pcXWGyVxWX93DFFJvcrxax5zKlieLwA3T77xJGwNcZW0YW6CYG70p1m8xPFmA==", - "dev": true - }, "node_modules/@types/eslint": { "version": "8.4.9", "resolved": "https://registry.npmjs.org/@types/eslint/-/eslint-8.4.9.tgz", @@ -2266,21 +2514,12 @@ "integrity": "sha512-R1g/VyKFFI2HLC1QGAeTtCBWCo6n75l41OnsVYNbmKG+kempOESaodf6BeJyUM3Q0rKa/NQcTHbB2+66lNnxLw==", "dev": true }, - "node_modules/@types/fibers": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/@types/fibers/-/fibers-3.1.1.tgz", - "integrity": "sha512-yHoUi46uika0snoTpNcVqUSvgbRndaIps4TUCotrXjtc0DHDoPQckmyXEZ2bX3e4mpJmyEW3hRhCwQa/ISCPaA==", + "node_modules/@types/gitconfiglocal": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/@types/gitconfiglocal/-/gitconfiglocal-2.0.3.tgz", + "integrity": "sha512-W6hyZux6TrtKfF2I9XNLVcsFr4xRr0T+S6hrJ9nDkhA2vzsFPIEAbnY4vgb6v2yKXQ9MJVcbLsARNlMfg4EVtQ==", "dev": true }, - "node_modules/@types/fs-extra": { - "version": "9.0.13", - "resolved": "https://registry.npmjs.org/@types/fs-extra/-/fs-extra-9.0.13.tgz", - "integrity": "sha512-nEnwB++1u5lVDM2UI4c1+5R+FYaKfaAzS4OococimjVm3nQw3TuzH5UNsocrcTBbhnerblyHj4A49qXbIiZdpA==", - "dev": true, - "dependencies": { - "@types/node": "*" - } - }, "node_modules/@types/github-slugger": { "version": "1.3.0", "resolved": "https://registry.npmjs.org/@types/github-slugger/-/github-slugger-1.3.0.tgz", @@ -2297,40 +2536,30 @@ } }, "node_modules/@types/http-cache-semantics": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/@types/http-cache-semantics/-/http-cache-semantics-4.0.1.tgz", - "integrity": "sha512-SZs7ekbP8CN0txVG2xVRH6EgKmEm31BOxA07vkFaETzZz1xh+cbt8BcI0slpymvwhx5dlFnQG2rTlPVQn+iRPQ==", + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/@types/http-cache-semantics/-/http-cache-semantics-4.0.4.tgz", + "integrity": "sha512-1m0bIFVc7eJWyve9S0RnuRgcQqF/Xd5QsUZAZeQFr1Q3/p9JWoQQEqmVy+DPTNpGXwhgIetAoYF8JSc33q29QA==", "dev": true }, - "node_modules/@types/inquirer": { - "version": "7.3.3", - "resolved": "https://registry.npmjs.org/@types/inquirer/-/inquirer-7.3.3.tgz", - "integrity": "sha512-HhxyLejTHMfohAuhRun4csWigAMjXTmRyiJTU1Y/I1xmggikFMkOUoMQRlFm+zQcPEGHSs3io/0FAmNZf8EymQ==", - "dev": true, - "dependencies": { - "@types/through": "*", - "rxjs": "^6.4.0" - } - }, "node_modules/@types/istanbul-lib-coverage": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/@types/istanbul-lib-coverage/-/istanbul-lib-coverage-2.0.4.tgz", - "integrity": "sha512-z/QT1XN4K4KYuslS23k62yDIDLwLFkzxOuMplDtObz0+y7VqJCaO2o+SPwHCvLFZh7xazvvoor2tA/hPz9ee7g==", + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/@types/istanbul-lib-coverage/-/istanbul-lib-coverage-2.0.6.tgz", + "integrity": "sha512-2QF/t/auWm0lsy8XtKVPG19v3sSOQlJe/YHZgfjb/KBBHOGSV+J2q/S671rcq9uTBrLAXmZpqJiaQbMT+zNU1w==", "dev": true }, "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==", + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/@types/istanbul-lib-report/-/istanbul-lib-report-3.0.3.tgz", + "integrity": "sha512-NQn7AHQnk/RSLOxrBbGyJM/aVQ+pjj5HCgasFxc0K/KhoATfQ/47AyUl15I2yBUpihjmas+a+VJBOqecrFH+uA==", "dev": true, "dependencies": { "@types/istanbul-lib-coverage": "*" } }, "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==", + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/@types/istanbul-reports/-/istanbul-reports-3.0.4.tgz", + "integrity": "sha512-pk2B1NWalF9toCRu6gjBzR69syFjP4Od8WRAX+0mmf9lAjCRicLOWc+ZrxZHx/0XRjotgkF9t6iaMJ+aXcOdZQ==", "dev": true, "dependencies": { "@types/istanbul-lib-report": "*" @@ -2358,39 +2587,6 @@ "keyv": "*" } }, - "node_modules/@types/lodash": { - "version": "4.14.187", - "resolved": "https://registry.npmjs.org/@types/lodash/-/lodash-4.14.187.tgz", - "integrity": "sha512-MrO/xLXCaUgZy3y96C/iOsaIqZSeupyTImKClHunL5GrmaiII2VwvWmLBu2hwa0Kp0sV19CsyjtrTc/Fx8rg/A==", - "dev": true - }, - "node_modules/@types/lodash.flattendeep": { - "version": "4.4.7", - "resolved": "https://registry.npmjs.org/@types/lodash.flattendeep/-/lodash.flattendeep-4.4.7.tgz", - "integrity": "sha512-1h6GW/AeZw/Wej6uxrqgmdTDZX1yFS39lRsXYkg+3kWvOWWrlGCI6H7lXxlUHOzxDT4QeYGmgPpQ3BX9XevzOg==", - "dev": true, - "dependencies": { - "@types/lodash": "*" - } - }, - "node_modules/@types/lodash.pickby": { - "version": "4.6.7", - "resolved": "https://registry.npmjs.org/@types/lodash.pickby/-/lodash.pickby-4.6.7.tgz", - "integrity": "sha512-4ebXRusuLflfscbD0PUX4eVknDHD9Yf+uMtBIvA/hrnTqeAzbuHuDjvnYriLjUrI9YrhCPVKUf4wkRSXJQ6gig==", - "dev": true, - "dependencies": { - "@types/lodash": "*" - } - }, - "node_modules/@types/lodash.union": { - "version": "4.6.7", - "resolved": "https://registry.npmjs.org/@types/lodash.union/-/lodash.union-4.6.7.tgz", - "integrity": "sha512-6HXM6tsnHJzKgJE0gA/LhTGf/7AbjUk759WZ1MziVm+OBNAATHhdgj+a3KVE8g76GCLAnN4ZEQQG1EGgtBIABA==", - "dev": true, - "dependencies": { - "@types/lodash": "*" - } - }, "node_modules/@types/mdast": { "version": "3.0.10", "resolved": "https://registry.npmjs.org/@types/mdast/-/mdast-3.0.10.tgz", @@ -2401,9 +2597,9 @@ } }, "node_modules/@types/mocha": { - "version": "8.2.3", - "resolved": "https://registry.npmjs.org/@types/mocha/-/mocha-8.2.3.tgz", - "integrity": "sha512-ekGvFhFgrc2zYQoX4JeZPmVzZxw6Dtllga7iGHzfbYIYkAMUx/sAFP2GdFpLff+vdHXu5fl7WX9AT+TtqYcsyw==", + "version": "10.0.6", + "resolved": "https://registry.npmjs.org/@types/mocha/-/mocha-10.0.6.tgz", + "integrity": "sha512-dJvrYWxP/UcXm36Qn36fxhUKu8A/xMRXVT2cliFF1Z7UA9liG5Psj3ezNSZw+5puH2czDXRLcXQxf8JbJt0ejg==", "dev": true }, "node_modules/@types/ms": { @@ -2413,10 +2609,13 @@ "dev": true }, "node_modules/@types/node": { - "version": "17.0.45", - "resolved": "https://registry.npmjs.org/@types/node/-/node-17.0.45.tgz", - "integrity": "sha512-w+tIMs3rq2afQdsPJlODhoUEKzFP1ayaoyl1CcnwtIlsVe7K7bA1NGm4s3PraqTLlXnbIN84zuBlxBWo1u9BLw==", - "dev": true + "version": "20.11.6", + "resolved": "https://registry.npmjs.org/@types/node/-/node-20.11.6.tgz", + "integrity": "sha512-+EOokTnksGVgip2PbYbr3xnR7kZigh4LbybAfBAw5BpnQ+FqBYUsvCEjYd70IXKlbohQ64mzEYmMtlWUY8q//Q==", + "dev": true, + "dependencies": { + "undici-types": "~5.26.4" + } }, "node_modules/@types/normalize-package-data": { "version": "2.4.1", @@ -2424,30 +2623,6 @@ "integrity": "sha512-Gj7cI7z+98M282Tqmp2K5EIsoouUEzbBJhQQzDE3jSIRk6r9gsz0oUokqIUR4u1R3dMHo0pDHM7sNOHyhulypw==", "dev": true }, - "node_modules/@types/object-inspect": { - "version": "1.8.1", - "resolved": "https://registry.npmjs.org/@types/object-inspect/-/object-inspect-1.8.1.tgz", - "integrity": "sha512-0JTdf3CGV0oWzE6Wa40Ayv2e2GhpP3pEJMcrlM74vBSJPuuNkVwfDnl0SZxyFCXETcB4oKA/MpTVfuYSMOelBg==", - "dev": true - }, - "node_modules/@types/puppeteer": { - "version": "5.4.7", - "resolved": "https://registry.npmjs.org/@types/puppeteer/-/puppeteer-5.4.7.tgz", - "integrity": "sha512-JdGWZZYL0vKapXF4oQTC5hLVNfOgdPrqeZ1BiQnGk5cB7HeE91EWUiTdVSdQPobRN8rIcdffjiOgCYJ/S8QrnQ==", - "dev": true, - "dependencies": { - "@types/node": "*" - } - }, - "node_modules/@types/recursive-readdir": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/@types/recursive-readdir/-/recursive-readdir-2.2.1.tgz", - "integrity": "sha512-Xd+Ptc4/F2ueInqy5yK2FI5FxtwwbX2+VZpcg+9oYsFJVen8qQKGapCr+Bi5wQtHU1cTXT8s+07lo/nKPgu8Gg==", - "dev": true, - "dependencies": { - "@types/node": "*" - } - }, "node_modules/@types/responselike": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/@types/responselike/-/responselike-1.0.0.tgz", @@ -2458,39 +2633,21 @@ } }, "node_modules/@types/stack-utils": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/@types/stack-utils/-/stack-utils-2.0.1.tgz", - "integrity": "sha512-Hl219/BT5fLAaz6NDkSuhzasy49dwQS/DSdu4MdggFB8zcXv7vflBI3xp7FEmkmdDkBUI2bPUNeMttp2knYdxw==", + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/@types/stack-utils/-/stack-utils-2.0.3.tgz", + "integrity": "sha512-9aEbYZ3TbYMznPdcdr3SmIrLXwC/AKZXQeCf9Pgao5CKb8CyHuEX5jzWPTkvregvhRJHcpRO6BFoGW9ycaOkYw==", "dev": true }, - "node_modules/@types/stream-buffers": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/@types/stream-buffers/-/stream-buffers-3.0.4.tgz", - "integrity": "sha512-qU/K1tb2yUdhXkLIATzsIPwbtX6BpZk0l3dPW6xqWyhfzzM1ECaQ/8faEnu3CNraLiQ9LHyQQPBGp7N9Fbs25w==", - "dev": true, - "dependencies": { - "@types/node": "*" - } - }, "node_modules/@types/supports-color": { "version": "8.1.1", "resolved": "https://registry.npmjs.org/@types/supports-color/-/supports-color-8.1.1.tgz", "integrity": "sha512-dPWnWsf+kzIG140B8z2w3fr5D03TLWbOAFQl45xUpI3vcizeXriNR5VYkWZ+WTMsUHqZ9Xlt3hrxGNANFyNQfw==", "dev": true }, - "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/tmp": { - "version": "0.2.3", - "resolved": "https://registry.npmjs.org/@types/tmp/-/tmp-0.2.3.tgz", - "integrity": "sha512-dDZH/tXzwjutnuk4UacGgFRwV+JSLaXL1ikvidfJprkb7L9Nx1njcRHHmi3Dsvt7pgqqTEeucQuOrWHPFgzVHA==", + "node_modules/@types/triple-beam": { + "version": "1.3.5", + "resolved": "https://registry.npmjs.org/@types/triple-beam/-/triple-beam-1.3.5.tgz", + "integrity": "sha512-6WaYesThRMCl19iryMYP7/x2OVgCtbIVflDGFpWnb9irXI3UjYE4AzmYuiUKY1AJstGijoY+MgUszMgRxIYTYw==", "dev": true }, "node_modules/@types/ua-parser-js": { @@ -2521,19 +2678,28 @@ "integrity": "sha512-8oDqyLC7eD4HM307boe2QWKyuzdzWBj56xI/imSl2cpL+U3tCMaTAkMJ4ee5JBZ/FsOJlvRGeIShiZDAl1qERA==", "dev": true }, + "node_modules/@types/ws": { + "version": "8.5.10", + "resolved": "https://registry.npmjs.org/@types/ws/-/ws-8.5.10.tgz", + "integrity": "sha512-vmQSUcfalpIq0R9q7uTo2lXs6eGIpt9wtnLdMv9LVpIjCA/+ufZRozlVoVelIYixx1ugCBKDhn89vnsEGOCx9A==", + "dev": true, + "dependencies": { + "@types/node": "*" + } + }, "node_modules/@types/yargs": { - "version": "15.0.14", - "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-15.0.14.tgz", - "integrity": "sha512-yEJzHoxf6SyQGhBhIYGXQDSCkJjB6HohDShto7m8vaKg9Yp0Yn8+71J9eakh2bnPg6BfsH9PRMhiRTZnd4eXGQ==", + "version": "17.0.32", + "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-17.0.32.tgz", + "integrity": "sha512-xQ67Yc/laOG5uMfX/093MRlGGCIBzZMarVa+gfNKJxWAIgykYpVGkBdbqEzGDDfCrVUj6Hiff4mTZ5BA6TmAog==", "dev": true, "dependencies": { "@types/yargs-parser": "*" } }, "node_modules/@types/yargs-parser": { - "version": "21.0.0", - "resolved": "https://registry.npmjs.org/@types/yargs-parser/-/yargs-parser-21.0.0.tgz", - "integrity": "sha512-iO9ZQHkZxHn4mSakYV0vFHAVDyEOIJQrV2uZ06HxEPcx+mt8swXoZHIbaaJ2crJYFfErySgktuTZ3BeLz+XmFA==", + "version": "21.0.3", + "resolved": "https://registry.npmjs.org/@types/yargs-parser/-/yargs-parser-21.0.3.tgz", + "integrity": "sha512-I4q9QU9MQv4oEOz4tAHJtNz1cwuLxn2F3xcc2iV5WdqLPpUnj30aUuxt1mAxYTG+oe8CZMV/+6rU4S4gRDzqtQ==", "dev": true }, "node_modules/@types/yauzl": { @@ -2546,12 +2712,6 @@ "@types/node": "*" } }, - "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/@videojs/http-streaming": { "version": "2.14.3", "resolved": "https://registry.npmjs.org/@videojs/http-streaming/-/http-streaming-2.14.3.tgz", @@ -2601,6 +2761,38 @@ "is-function": "^1.0.1" } }, + "node_modules/@vitest/snapshot": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/@vitest/snapshot/-/snapshot-1.2.1.tgz", + "integrity": "sha512-Tmp/IcYEemKaqAYCS08sh0vORLJkMr0NRV76Gl8sHGxXT5151cITJCET20063wk0Yr/1koQ6dnmP6eEqezmd/Q==", + "dev": true, + "dependencies": { + "magic-string": "^0.30.5", + "pathe": "^1.1.1", + "pretty-format": "^29.7.0" + }, + "funding": { + "url": "https://opencollective.com/vitest" + } + }, + "node_modules/@vitest/snapshot/node_modules/@jridgewell/sourcemap-codec": { + "version": "1.4.15", + "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.15.tgz", + "integrity": "sha512-eF2rxCRulEKXHTRiDrDy6erMYWqNw4LPdQ8UQA4huuxaQsVeRPFl2oM8oDGxMFhJUWZf9McpLtJasDDZb/Bpeg==", + "dev": true + }, + "node_modules/@vitest/snapshot/node_modules/magic-string": { + "version": "0.30.5", + "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.5.tgz", + "integrity": "sha512-7xlpfBaQaP/T6Vh8MO/EqXSW5En6INHEvEXQiuff7Gku0PWjU3uf6w/j9o7O+SpB5fOAkrI5HeoNgwjEO0pFsA==", + "dev": true, + "dependencies": { + "@jridgewell/sourcemap-codec": "^1.4.15" + }, + "engines": { + "node": ">=12" + } + }, "node_modules/@vue/compiler-core": { "version": "3.2.41", "resolved": "https://registry.npmjs.org/@vue/compiler-core/-/compiler-core-3.2.41.tgz", @@ -2697,75 +2889,142 @@ "optional": true }, "node_modules/@wdio/browserstack-service": { - "version": "7.16.16", - "resolved": "https://registry.npmjs.org/@wdio/browserstack-service/-/browserstack-service-7.16.16.tgz", - "integrity": "sha512-q4wUh/j0MR2SwhTkmIFif2DaXgH5yzdgOer6G/fac2n81zLCSpQHWO5aQ9T0An9CAd4L2A+t3dmChpBJPkHWSw==", - "dev": true, - "dependencies": { - "@types/node": "^17.0.4", - "@wdio/logger": "7.16.0", - "@wdio/types": "7.16.14", - "browserstack-local": "^1.4.5", - "got": "^11.0.2", - "webdriverio": "7.16.16" + "version": "8.29.1", + "resolved": "https://registry.npmjs.org/@wdio/browserstack-service/-/browserstack-service-8.29.1.tgz", + "integrity": "sha512-dLEJcdVF0Cu+2REByVOfLUzx9FvMias1VsxSCZpKXeIAGAIWBBdNdooK6Vdc9QdS36S5v/mk0/rTTQhYn4nWjQ==", + "dev": true, + "dependencies": { + "@percy/appium-app": "^2.0.1", + "@percy/selenium-webdriver": "^2.0.3", + "@types/gitconfiglocal": "^2.0.1", + "@wdio/logger": "8.28.0", + "@wdio/reporter": "8.29.1", + "@wdio/types": "8.29.1", + "browserstack-local": "^1.5.1", + "chalk": "^5.3.0", + "csv-writer": "^1.6.0", + "formdata-node": "5.0.1", + "git-repo-info": "^2.1.1", + "gitconfiglocal": "^2.1.0", + "got": "^12.6.1", + "uuid": "^9.0.0", + "webdriverio": "8.29.1", + "winston-transport": "^4.5.0", + "yauzl": "^2.10.0" }, "engines": { - "node": ">=12.0.0" + "node": "^16.13 || >=18" }, "peerDependencies": { - "@wdio/cli": "^7.0.0" + "@wdio/cli": "^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0" } }, - "node_modules/@wdio/browserstack-service/node_modules/@wdio/config": { - "version": "7.16.16", - "resolved": "https://registry.npmjs.org/@wdio/config/-/config-7.16.16.tgz", - "integrity": "sha512-K/ObPuo6Da2liz++OKOIfbdpFwI7UWiFcBylfJkCYbweuXCoW1aUqlKI6rmKPwCH9Uqr/RHWu6p8eo0zWe6xVA==", + "node_modules/@wdio/browserstack-service/node_modules/@puppeteer/browsers": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/@puppeteer/browsers/-/browsers-1.3.0.tgz", + "integrity": "sha512-an3QdbNPkuU6qpxpbssxAbjRLJcF+eP4L8UqIY3+6n0sbaVxw5pz7PiCLy9g32XEZuoamUlV5ZQPnA6FxvkIHA==", "dev": true, + "optional": true, + "peer": true, "dependencies": { - "@wdio/logger": "7.16.0", - "@wdio/types": "7.16.14", - "deepmerge": "^4.0.0", - "glob": "^7.1.2" + "debug": "4.3.4", + "extract-zip": "2.0.1", + "http-proxy-agent": "5.0.0", + "https-proxy-agent": "5.0.1", + "progress": "2.0.3", + "proxy-from-env": "1.1.0", + "tar-fs": "2.1.1", + "unbzip2-stream": "1.4.3", + "yargs": "17.7.1" + }, + "bin": { + "browsers": "lib/cjs/main-cli.js" }, "engines": { - "node": ">=12.0.0" + "node": ">=16.0.0" + }, + "peerDependencies": { + "typescript": ">= 4.7.4" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } } }, - "node_modules/@wdio/browserstack-service/node_modules/@wdio/protocols": { - "version": "7.16.7", - "resolved": "https://registry.npmjs.org/@wdio/protocols/-/protocols-7.16.7.tgz", - "integrity": "sha512-Wv40pNQcLiPzQ3o98Mv4A8T1EBQ6k4khglz/e2r16CTm+F3DDYh8eLMAsU5cgnmuwwDKX1EyOiFwieykBn5MCg==", + "node_modules/@wdio/browserstack-service/node_modules/@sindresorhus/is": { + "version": "5.6.0", + "resolved": "https://registry.npmjs.org/@sindresorhus/is/-/is-5.6.0.tgz", + "integrity": "sha512-TV7t8GKYaJWsn00tFDqBw8+Uqmr8A0fRU1tvTQhyZzGv0sJCGRQL3JGMI3ucuKo3XIZdUP+Lx7/gh2t3lewy7g==", "dev": true, "engines": { - "node": ">=12.0.0" + "node": ">=14.16" + }, + "funding": { + "url": "https://github.com/sindresorhus/is?sponsor=1" } }, - "node_modules/@wdio/browserstack-service/node_modules/@wdio/repl": { - "version": "7.16.14", - "resolved": "https://registry.npmjs.org/@wdio/repl/-/repl-7.16.14.tgz", - "integrity": "sha512-Ezih0Y+lsGkKv3H3U56hdWgZiQGA3VaAYguSLd9+g1xbQq+zMKqSmfqECD9bAy+OgCCiVTRstES6lHZxJVPhAg==", + "node_modules/@wdio/browserstack-service/node_modules/@szmarczak/http-timer": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/@szmarczak/http-timer/-/http-timer-5.0.1.tgz", + "integrity": "sha512-+PmQX0PiAYPMeVYe237LJAYvOMYW1j2rH5YROyS3b4CTVJum34HfRvKvAzozHAQG0TnHNdUfY9nCeUyRAs//cw==", "dev": true, "dependencies": { - "@wdio/utils": "7.16.14" + "defer-to-connect": "^2.0.1" }, "engines": { - "node": ">=12.0.0" + "node": ">=14.16" + } + }, + "node_modules/@wdio/browserstack-service/node_modules/@types/which": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/@types/which/-/which-2.0.2.tgz", + "integrity": "sha512-113D3mDkZDjo+EeUEHCFy0qniNc1ZpecGiAU7WSo7YDoSzolZIQKpYFHrPpjkB2nuyahcKfrmLXeQlh7gqJYdw==", + "dev": true, + "optional": true, + "peer": true + }, + "node_modules/@wdio/browserstack-service/node_modules/archiver": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/archiver/-/archiver-6.0.1.tgz", + "integrity": "sha512-CXGy4poOLBKptiZH//VlWdFuUC1RESbdZjGjILwBuZ73P7WkAUN0htfSfBq/7k6FRFlpu7bg4JOkj1vU9G6jcQ==", + "dev": true, + "dependencies": { + "archiver-utils": "^4.0.1", + "async": "^3.2.4", + "buffer-crc32": "^0.2.1", + "readable-stream": "^3.6.0", + "readdir-glob": "^1.1.2", + "tar-stream": "^3.0.0", + "zip-stream": "^5.0.1" + }, + "engines": { + "node": ">= 12.0.0" } }, - "node_modules/@wdio/browserstack-service/node_modules/@wdio/utils": { - "version": "7.16.14", - "resolved": "https://registry.npmjs.org/@wdio/utils/-/utils-7.16.14.tgz", - "integrity": "sha512-wwin8nVpIlhmXJkq6GJw9aDDzgLOJKgXTcEua0T2sdXjoW78u5Ly/GZrFXTjMGhacFvoZfitTrjyfyy4CxMVvw==", + "node_modules/@wdio/browserstack-service/node_modules/archiver-utils": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/archiver-utils/-/archiver-utils-4.0.1.tgz", + "integrity": "sha512-Q4Q99idbvzmgCTEAAhi32BkOyq8iVI5EwdO0PmBDSGIzzjYNdcFn7Q7k3OzbLy4kLUPXfJtG6fO2RjftXbobBg==", "dev": true, "dependencies": { - "@wdio/logger": "7.16.0", - "@wdio/types": "7.16.14", - "p-iteration": "^1.1.8" + "glob": "^8.0.0", + "graceful-fs": "^4.2.0", + "lazystream": "^1.0.0", + "lodash": "^4.17.15", + "normalize-path": "^3.0.0", + "readable-stream": "^3.6.0" }, "engines": { - "node": ">=12.0.0" + "node": ">= 12.0.0" } }, + "node_modules/@wdio/browserstack-service/node_modules/async": { + "version": "3.2.5", + "resolved": "https://registry.npmjs.org/async/-/async-3.2.5.tgz", + "integrity": "sha512-baNZyqaaLhyLVKm/DlvdW051MSgO6b8eVfIezl9E5PqWxFgzLm/wQntEW4zOytVburDEr0JlALEpdOFwvErLsg==", + "dev": true + }, "node_modules/@wdio/browserstack-service/node_modules/brace-expansion": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", @@ -2775,2767 +3034,4356 @@ "balanced-match": "^1.0.0" } }, - "node_modules/@wdio/browserstack-service/node_modules/devtools": { - "version": "7.16.16", - "resolved": "https://registry.npmjs.org/devtools/-/devtools-7.16.16.tgz", - "integrity": "sha512-M0kzkuSgfEhpqIis3gdtWsNjn/HQ+vRAmEzDnbYx/7FfjFxhSv1d+rOOT20pvd60soItMYpsOova1igACEGkGQ==", + "node_modules/@wdio/browserstack-service/node_modules/cacheable-lookup": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/cacheable-lookup/-/cacheable-lookup-7.0.0.tgz", + "integrity": "sha512-+qJyx4xiKra8mZrcwhjMRMUhD5NR1R8esPkzIYxX96JiecFoxAXFuz/GpR3+ev4PE1WamHip78wV0vcmPQtp8w==", "dev": true, - "dependencies": { - "@types/node": "^17.0.4", - "@types/ua-parser-js": "^0.7.33", - "@wdio/config": "7.16.16", - "@wdio/logger": "7.16.0", - "@wdio/protocols": "7.16.7", - "@wdio/types": "7.16.14", - "@wdio/utils": "7.16.14", - "chrome-launcher": "^0.15.0", - "edge-paths": "^2.1.0", - "puppeteer-core": "^13.1.3", - "query-selector-shadow-dom": "^1.0.0", - "ua-parser-js": "^1.0.1", - "uuid": "^8.0.0" - }, "engines": { - "node": ">=12.0.0" + "node": ">=14.16" } }, - "node_modules/@wdio/browserstack-service/node_modules/devtools-protocol": { - "version": "0.0.973690", - "resolved": "https://registry.npmjs.org/devtools-protocol/-/devtools-protocol-0.0.973690.tgz", - "integrity": "sha512-myh3hSFp0YWa2GED11PmbLhV4dv9RdO7YUz27XJrbQLnP5bMbZL6dfOOILTHO57yH0kX5GfuOZBsg/4NamfPvQ==", - "dev": true - }, - "node_modules/@wdio/browserstack-service/node_modules/minimatch": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-5.1.0.tgz", - "integrity": "sha512-9TPBGGak4nHfGZsPBohm9AWg6NoT7QTCehS3BIJABslyZbzxfV78QM2Y6+i741OPZIafFAaiiEMh5OyIrJPgtg==", + "node_modules/@wdio/browserstack-service/node_modules/cacheable-request": { + "version": "10.2.14", + "resolved": "https://registry.npmjs.org/cacheable-request/-/cacheable-request-10.2.14.tgz", + "integrity": "sha512-zkDT5WAF4hSSoUgyfg5tFIxz8XQK+25W/TLVojJTMKBaxevLBBtLxgqguAuVQB8PVW79FVjHcU+GJ9tVbDZ9mQ==", "dev": true, "dependencies": { - "brace-expansion": "^2.0.1" + "@types/http-cache-semantics": "^4.0.2", + "get-stream": "^6.0.1", + "http-cache-semantics": "^4.1.1", + "keyv": "^4.5.3", + "mimic-response": "^4.0.0", + "normalize-url": "^8.0.0", + "responselike": "^3.0.0" }, "engines": { - "node": ">=10" + "node": ">=14.16" } }, - "node_modules/@wdio/browserstack-service/node_modules/ua-parser-js": { - "version": "1.0.33", - "resolved": "https://registry.npmjs.org/ua-parser-js/-/ua-parser-js-1.0.33.tgz", - "integrity": "sha512-RqshF7TPTE0XLYAqmjlu5cLLuGdKrNu9O1KLA/qp39QtbZwuzwv1dT46DZSopoUMsYgXpB3Cv8a03FI8b74oFQ==", + "node_modules/@wdio/browserstack-service/node_modules/chalk": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-5.3.0.tgz", + "integrity": "sha512-dLitG79d+GV1Nb/VYcCDFivJeK1hiukt9QjRNVOsUtTy1rR1YJsmpGGTZ3qJos+uw7WmWF4wUwBd9jxjocFC2w==", "dev": true, - "funding": [ - { - "type": "opencollective", - "url": "https://opencollective.com/ua-parser-js" - }, - { - "type": "paypal", - "url": "https://paypal.me/faisalman" - } - ], "engines": { - "node": "*" + "node": "^12.17.0 || ^14.13 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" } }, - "node_modules/@wdio/browserstack-service/node_modules/uuid": { - "version": "8.3.2", - "resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz", - "integrity": "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==", + "node_modules/@wdio/browserstack-service/node_modules/chrome-launcher": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/chrome-launcher/-/chrome-launcher-1.1.0.tgz", + "integrity": "sha512-rJYWeEAERwWIr3c3mEVXwNiODPEdMRlRxHc47B1qHPOolHZnkj7rMv1QSUfPoG6MgatWj5AxSpnKKR4QEwEQIQ==", "dev": true, + "optional": true, + "peer": true, + "dependencies": { + "@types/node": "*", + "escape-string-regexp": "^4.0.0", + "is-wsl": "^2.2.0", + "lighthouse-logger": "^2.0.1" + }, "bin": { - "uuid": "dist/bin/uuid" + "print-chrome-path": "bin/print-chrome-path.js" + }, + "engines": { + "node": ">=12.13.0" } }, - "node_modules/@wdio/browserstack-service/node_modules/webdriver": { - "version": "7.16.16", - "resolved": "https://registry.npmjs.org/webdriver/-/webdriver-7.16.16.tgz", - "integrity": "sha512-x8UoG9k/P8KDrfSh1pOyNevt9tns3zexoMxp9cKnyA/7HYSErhZYTLGlgxscAXLtQG41cMH/Ba/oBmOx7Hgd8w==", + "node_modules/@wdio/browserstack-service/node_modules/compress-commons": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/compress-commons/-/compress-commons-5.0.1.tgz", + "integrity": "sha512-MPh//1cERdLtqwO3pOFLeXtpuai0Y2WCd5AhtKxznqM7WtaMYaOEMSgn45d9D10sIHSfIKE603HlOp8OPGrvag==", "dev": true, "dependencies": { - "@types/node": "^17.0.4", - "@wdio/config": "7.16.16", - "@wdio/logger": "7.16.0", - "@wdio/protocols": "7.16.7", - "@wdio/types": "7.16.14", - "@wdio/utils": "7.16.14", - "got": "^11.0.2", - "ky": "^0.29.0", - "lodash.merge": "^4.6.1" + "crc-32": "^1.2.0", + "crc32-stream": "^5.0.0", + "normalize-path": "^3.0.0", + "readable-stream": "^3.6.0" }, "engines": { - "node": ">=12.0.0" + "node": ">= 12.0.0" } }, - "node_modules/@wdio/browserstack-service/node_modules/webdriverio": { - "version": "7.16.16", - "resolved": "https://registry.npmjs.org/webdriverio/-/webdriverio-7.16.16.tgz", - "integrity": "sha512-caPaEWyuD3Qoa7YkW4xCCQA4v9Pa9wmhFGPvNZh3ERtjMCNi8L/XXOdkekWNZmFh3tY0kFguBj7+fAwSY7HAGw==", + "node_modules/@wdio/browserstack-service/node_modules/crc32-stream": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/crc32-stream/-/crc32-stream-5.0.0.tgz", + "integrity": "sha512-B0EPa1UK+qnpBZpG+7FgPCu0J2ETLpXq09o9BkLkEAhdB6Z61Qo4pJ3JYu0c+Qi+/SAL7QThqnzS06pmSSyZaw==", "dev": true, "dependencies": { - "@types/aria-query": "^5.0.0", - "@types/node": "^17.0.4", - "@wdio/config": "7.16.16", - "@wdio/logger": "7.16.0", - "@wdio/protocols": "7.16.7", - "@wdio/repl": "7.16.14", - "@wdio/types": "7.16.14", - "@wdio/utils": "7.16.14", - "archiver": "^5.0.0", - "aria-query": "^5.0.0", - "css-shorthand-properties": "^1.1.1", - "css-value": "^0.0.1", - "devtools": "7.16.16", - "devtools-protocol": "^0.0.973690", - "fs-extra": "^10.0.0", - "get-port": "^5.1.1", - "grapheme-splitter": "^1.0.2", - "lodash.clonedeep": "^4.5.0", - "lodash.isobject": "^3.0.2", - "lodash.isplainobject": "^4.0.6", - "lodash.zip": "^4.2.0", - "minimatch": "^5.0.0", - "puppeteer-core": "^13.1.3", - "query-selector-shadow-dom": "^1.0.0", - "resq": "^1.9.1", - "rgb2hex": "0.2.5", - "serialize-error": "^8.0.0", - "webdriver": "7.16.16" + "crc-32": "^1.2.0", + "readable-stream": "^3.4.0" }, "engines": { - "node": ">=12.0.0" + "node": ">= 12.0.0" } }, - "node_modules/@wdio/cli": { - "version": "7.5.7", - "resolved": "https://registry.npmjs.org/@wdio/cli/-/cli-7.5.7.tgz", - "integrity": "sha512-nOQJLskrY+UECDd3NxE7oBzb6cDA7e7x02YWQugOlOgnZ4a+PJmkFoSsO8C2uNCpdFngy5rJKGUo5vbtAHEF9Q==", - "dev": true, - "dependencies": { - "@types/ejs": "^3.0.5", - "@types/fs-extra": "^9.0.4", - "@types/inquirer": "^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.5.3", - "@wdio/logger": "7.5.3", - "@wdio/types": "7.5.3", - "@wdio/utils": "7.5.3", - "async-exit-hook": "^2.0.1", - "chalk": "^4.0.0", - "chokidar": "^3.0.0", - "cli-spinners": "^2.1.0", - "ejs": "^3.0.1", - "fs-extra": "^10.0.0", - "inquirer": "^8.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.5.7", - "yargs": "^17.0.0", - "yarn-install": "^1.0.0" - }, - "bin": { - "wdio": "bin/wdio.js" - }, - "engines": { - "node": ">=12.0.0" + "node_modules/@wdio/browserstack-service/node_modules/cross-fetch": { + "version": "3.1.6", + "resolved": "https://registry.npmjs.org/cross-fetch/-/cross-fetch-3.1.6.tgz", + "integrity": "sha512-riRvo06crlE8HiqOwIpQhxwdOk4fOeR7FVM/wXoxchFEqMNUjvbs3bfo4OTgMEMHzppd4DxFBDbyySj8Cv781g==", + "dev": true, + "optional": true, + "peer": true, + "dependencies": { + "node-fetch": "^2.6.11" } }, - "node_modules/@wdio/cli/node_modules/@types/aria-query": { - "version": "4.2.2", - "resolved": "https://registry.npmjs.org/@types/aria-query/-/aria-query-4.2.2.tgz", - "integrity": "sha512-HnYpAE1Y6kRyKM/XkEuiRQhTHvkzMBurTHnpFLYLBGPIylZNPs9jJcuOOYWxPLJCSEtmZT0Y8rHDokKN7rRTig==", - "dev": true - }, - "node_modules/@wdio/cli/node_modules/@wdio/logger": { - "version": "7.5.3", - "resolved": "https://registry.npmjs.org/@wdio/logger/-/logger-7.5.3.tgz", - "integrity": "sha512-r9EADpm0ncS1bDQSWi/nhF9C59/WNLbdAAFlo782E9ItFCpDhNit3aQP9vETv1vFxJRjUIM8Fw/HW8zwPadkbw==", + "node_modules/@wdio/browserstack-service/node_modules/devtools": { + "version": "8.29.1", + "resolved": "https://registry.npmjs.org/devtools/-/devtools-8.29.1.tgz", + "integrity": "sha512-fbH0Z7CPK4OZSgUw2QcAppczowxtSyvFztPUmiFyi99cUadjEOwlg0aL3pBVlIDo67olYjGb8GD1M5Z4yI/P6w==", "dev": true, + "optional": true, + "peer": true, "dependencies": { - "chalk": "^4.0.0", - "loglevel": "^1.6.0", - "loglevel-plugin-prefix": "^0.8.4", - "strip-ansi": "^6.0.0" + "@types/node": "^20.1.0", + "@wdio/config": "8.29.1", + "@wdio/logger": "8.28.0", + "@wdio/protocols": "8.24.12", + "@wdio/types": "8.29.1", + "@wdio/utils": "8.29.1", + "chrome-launcher": "^1.0.0", + "edge-paths": "^3.0.5", + "import-meta-resolve": "^4.0.0", + "puppeteer-core": "20.3.0", + "query-selector-shadow-dom": "^1.0.0", + "ua-parser-js": "^1.0.1", + "uuid": "^9.0.0", + "which": "^4.0.0" }, "engines": { - "node": ">=12.0.0" + "node": "^16.13 || >=18" } }, - "node_modules/@wdio/cli/node_modules/@wdio/types": { - "version": "7.5.3", - "resolved": "https://registry.npmjs.org/@wdio/types/-/types-7.5.3.tgz", - "integrity": "sha512-jmumhKBhNDABnpmrshYLEcdE9WoP5tmynsDNbDABlb/W8FFiLySQOejukhYIL9CLys4zXerV3/edks0SCzHOiQ==", + "node_modules/@wdio/browserstack-service/node_modules/devtools-protocol": { + "version": "0.0.1120988", + "resolved": "https://registry.npmjs.org/devtools-protocol/-/devtools-protocol-0.0.1120988.tgz", + "integrity": "sha512-39fCpE3Z78IaIPChJsP6Lhmkbf4dWXOmzLk/KFTdRkNk/0JymRIfUynDVRndV9HoDz8PyalK1UH21ST/ivwW5Q==", + "dev": true, + "optional": true, + "peer": true + }, + "node_modules/@wdio/browserstack-service/node_modules/devtools/node_modules/which": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/which/-/which-4.0.0.tgz", + "integrity": "sha512-GlaYyEb07DPxYCKhKzplCWBJtvxZcZMrL+4UkrTSJHHPyZU4mYYTv3qaOe77H7EODLSSopAUFAc6W8U4yqvscg==", "dev": true, + "optional": true, + "peer": true, "dependencies": { - "got": "^11.8.1" + "isexe": "^3.1.1" + }, + "bin": { + "node-which": "bin/which.js" }, "engines": { - "node": ">=12.0.0" + "node": "^16.13.0 || >=18.0.0" } }, - "node_modules/@wdio/cli/node_modules/ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "node_modules/@wdio/browserstack-service/node_modules/edge-paths": { + "version": "3.0.5", + "resolved": "https://registry.npmjs.org/edge-paths/-/edge-paths-3.0.5.tgz", + "integrity": "sha512-sB7vSrDnFa4ezWQk9nZ/n0FdpdUuC6R1EOrlU3DL+bovcNFK28rqu2emmAUjujYEJTWIgQGqgVVWUZXMnc8iWg==", "dev": true, + "optional": true, + "peer": true, "dependencies": { - "color-convert": "^2.0.1" + "@types/which": "^2.0.1", + "which": "^2.0.2" }, "engines": { - "node": ">=8" + "node": ">=14.0.0" }, "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" + "url": "https://github.com/sponsors/shirshak55" } }, - "node_modules/@wdio/cli/node_modules/aria-query": { - "version": "4.2.2", - "resolved": "https://registry.npmjs.org/aria-query/-/aria-query-4.2.2.tgz", - "integrity": "sha512-o/HelwhuKpTj/frsOsbNLNgnNGVIFsVP/SW2BSF14gVl7kAfMOJ6/8wUAUvG1R1NHKrfG+2sHZTu0yauT1qBrA==", + "node_modules/@wdio/browserstack-service/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, - "dependencies": { - "@babel/runtime": "^7.10.2", - "@babel/runtime-corejs3": "^7.10.2" + "optional": true, + "peer": true, + "engines": { + "node": ">=10" }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@wdio/browserstack-service/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": ">=6.0" + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/@wdio/cli/node_modules/chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "node_modules/@wdio/browserstack-service/node_modules/glob": { + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/glob/-/glob-8.1.0.tgz", + "integrity": "sha512-r8hpEjiQEYlF2QU0df3dS+nxxSIreXQS1qRhMJM0Q5NDdR386C7jb7Hwwod8Fgiuex+k0GFjgft18yvxm5XoCQ==", "dev": true, "dependencies": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^5.0.1", + "once": "^1.3.0" }, "engines": { - "node": ">=10" + "node": ">=12" }, "funding": { - "url": "https://github.com/chalk/chalk?sponsor=1" + "url": "https://github.com/sponsors/isaacs" } }, - "node_modules/@wdio/cli/node_modules/chrome-launcher": { - "version": "0.13.4", - "resolved": "https://registry.npmjs.org/chrome-launcher/-/chrome-launcher-0.13.4.tgz", - "integrity": "sha512-nnzXiDbGKjDSK6t2I+35OAPBy5Pw/39bgkb/ZAFwMhwJbdYBp6aH+vW28ZgtjdU890Q7D+3wN/tB8N66q5Gi2A==", + "node_modules/@wdio/browserstack-service/node_modules/glob/node_modules/minimatch": { + "version": "5.1.6", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-5.1.6.tgz", + "integrity": "sha512-lKwV/1brpG6mBUFHtb7NUmtABCb2WZZmm2wNiOA5hAb8VdCS4B3dtMWyvcoViccwAW/COERjXLt0zP1zXUN26g==", "dev": true, "dependencies": { - "@types/node": "*", - "escape-string-regexp": "^1.0.5", - "is-wsl": "^2.2.0", - "lighthouse-logger": "^1.0.0", - "mkdirp": "^0.5.3", - "rimraf": "^3.0.2" + "brace-expansion": "^2.0.1" + }, + "engines": { + "node": ">=10" } }, - "node_modules/@wdio/cli/node_modules/chrome-launcher/node_modules/mkdirp": { - "version": "0.5.6", - "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.6.tgz", - "integrity": "sha512-FP+p8RB8OWpF3YZBCrP5gtADmtXApB5AMLn+vdyA+PyxCjrCs00mjyUozssO33cwDeT3wNGdLxJ5M//YqtHAJw==", + "node_modules/@wdio/browserstack-service/node_modules/got": { + "version": "12.6.1", + "resolved": "https://registry.npmjs.org/got/-/got-12.6.1.tgz", + "integrity": "sha512-mThBblvlAF1d4O5oqyvN+ZxLAYwIJK7bpMxgYqPD9okW0C3qm5FFn7k811QrcuEBwaogR3ngOFoCfs6mRv7teQ==", "dev": true, "dependencies": { - "minimist": "^1.2.6" + "@sindresorhus/is": "^5.2.0", + "@szmarczak/http-timer": "^5.0.1", + "cacheable-lookup": "^7.0.0", + "cacheable-request": "^10.2.8", + "decompress-response": "^6.0.0", + "form-data-encoder": "^2.1.2", + "get-stream": "^6.0.1", + "http2-wrapper": "^2.1.10", + "lowercase-keys": "^3.0.0", + "p-cancelable": "^3.0.0", + "responselike": "^3.0.0" }, - "bin": { - "mkdirp": "bin/cmd.js" + "engines": { + "node": ">=14.16" + }, + "funding": { + "url": "https://github.com/sindresorhus/got?sponsor=1" } }, - "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==", + "node_modules/@wdio/browserstack-service/node_modules/http-proxy-agent": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/http-proxy-agent/-/http-proxy-agent-5.0.0.tgz", + "integrity": "sha512-n2hY8YdoRE1i7r6M0w9DIw5GgZN0G25P8zLCRQ8rjXtTU3vsNFBI/vWK/UIeE6g5MUUz6avwAPXmL6Fy9D/90w==", "dev": true, + "optional": true, + "peer": true, "dependencies": { - "color-name": "~1.1.4" + "@tootallnate/once": "2", + "agent-base": "6", + "debug": "4" }, "engines": { - "node": ">=7.0.0" + "node": ">= 6" } }, - "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/devtools": { - "version": "7.5.7", - "resolved": "https://registry.npmjs.org/devtools/-/devtools-7.5.7.tgz", - "integrity": "sha512-+kqmvFbceElhYpN35yjm1T4Rz3VbH0QaqrNWKRpeyFp657Y5W0bm1s5FyMUeIv0aTNkAgWcETtqL+EG9X9uvjQ==", + "node_modules/@wdio/browserstack-service/node_modules/http2-wrapper": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/http2-wrapper/-/http2-wrapper-2.2.1.tgz", + "integrity": "sha512-V5nVw1PAOgfI3Lmeaj2Exmeg7fenjhRUgz1lPSezy1CuhPYbgQtbQj4jZfEAEMlaL+vupsvhjqCyjzob0yxsmQ==", "dev": true, "dependencies": { - "@wdio/config": "7.5.3", - "@wdio/logger": "7.5.3", - "@wdio/protocols": "7.5.3", - "@wdio/types": "7.5.3", - "@wdio/utils": "7.5.3", - "chrome-launcher": "^0.13.1", - "edge-paths": "^2.1.0", - "puppeteer-core": "^9.1.0", - "query-selector-shadow-dom": "^1.0.0", - "ua-parser-js": "^0.7.21", - "uuid": "^8.0.0" + "quick-lru": "^5.1.1", + "resolve-alpn": "^1.2.0" }, "engines": { - "node": ">=12.0.0" + "node": ">=10.19.0" } }, - "node_modules/@wdio/cli/node_modules/devtools-protocol": { - "version": "0.0.878340", - "resolved": "https://registry.npmjs.org/devtools-protocol/-/devtools-protocol-0.0.878340.tgz", - "integrity": "sha512-W0q8Y02r1RNwfZtI4Jjh1/MZxRHyrIgy9FvElbJzQelZjmNH197H4mBQs7DZjlUUDA9s6Zz2jl+zUYFgLgEnzw==", - "dev": true - }, - "node_modules/@wdio/cli/node_modules/has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "node_modules/@wdio/browserstack-service/node_modules/isexe": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/isexe/-/isexe-3.1.1.tgz", + "integrity": "sha512-LpB/54B+/2J5hqQ7imZHfdU31OlgQqx7ZicVlkm9kzg9/w8GKLEcFfJl/t7DCEDueOyBAD6zCCwTO6Fzs0NoEQ==", "dev": true, + "optional": true, + "peer": true, "engines": { - "node": ">=8" + "node": ">=16" } }, - "node_modules/@wdio/cli/node_modules/puppeteer-core": { - "version": "9.1.1", - "resolved": "https://registry.npmjs.org/puppeteer-core/-/puppeteer-core-9.1.1.tgz", - "integrity": "sha512-zbedbitVIGhmgz0nt7eIdLsnaoVZSlNJfBivqm2w67T8LR2bU1dvnruDZ8nQO0zn++Iet7zHbAOdnuS5+H2E7A==", + "node_modules/@wdio/browserstack-service/node_modules/lighthouse-logger": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/lighthouse-logger/-/lighthouse-logger-2.0.1.tgz", + "integrity": "sha512-ioBrW3s2i97noEmnXxmUq7cjIcVRjT5HBpAYy8zE11CxU9HqlWHHeRxfeN1tn8F7OEMVPIC9x1f8t3Z7US9ehQ==", "dev": true, + "optional": true, + "peer": 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" + "debug": "^2.6.9", + "marky": "^1.2.2" } }, - "node_modules/@wdio/cli/node_modules/puppeteer-core/node_modules/devtools-protocol": { - "version": "0.0.869402", - "resolved": "https://registry.npmjs.org/devtools-protocol/-/devtools-protocol-0.0.869402.tgz", - "integrity": "sha512-VvlVYY+VDJe639yHs5PHISzdWTLL3Aw8rO4cvUtwvoxFd6FHbE4OpHHcde52M6096uYYazAmd4l0o5VuFRO2WA==", - "dev": true - }, - "node_modules/@wdio/cli/node_modules/supports-color": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "node_modules/@wdio/browserstack-service/node_modules/lighthouse-logger/node_modules/debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", "dev": true, + "optional": true, + "peer": true, "dependencies": { - "has-flag": "^4.0.0" + "ms": "2.0.0" + } + }, + "node_modules/@wdio/browserstack-service/node_modules/lowercase-keys": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/lowercase-keys/-/lowercase-keys-3.0.0.tgz", + "integrity": "sha512-ozCC6gdQ+glXOQsveKD0YsDy8DSQFjDTz4zyzEHNV5+JP5D62LmfDZ6o1cycFx9ouG940M5dE8C8CTewdj2YWQ==", + "dev": true, + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@wdio/browserstack-service/node_modules/lru-cache": { + "version": "7.18.3", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-7.18.3.tgz", + "integrity": "sha512-jumlc0BIUrS3qJGgIkWZsyfAM7NCWiBcCDhnd+3NNM5KbBmLTgHVfWBcg6W+rLUsIpzpERPsvwUP7CckAQSOoA==", + "dev": true, "engines": { - "node": ">=8" + "node": ">=12" } }, - "node_modules/@wdio/cli/node_modules/uuid": { - "version": "8.3.2", - "resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz", - "integrity": "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==", + "node_modules/@wdio/browserstack-service/node_modules/mimic-response": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/mimic-response/-/mimic-response-4.0.0.tgz", + "integrity": "sha512-e5ISH9xMYU0DzrT+jl8q2ze9D6eWBto+I8CNpe+VI+K2J/F/k3PdkdTdz4wvGVH4NTpo+NRYTVIuMQEMMcsLqg==", "dev": true, - "bin": { - "uuid": "dist/bin/uuid" + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/@wdio/cli/node_modules/webdriverio": { - "version": "7.5.7", - "resolved": "https://registry.npmjs.org/webdriverio/-/webdriverio-7.5.7.tgz", - "integrity": "sha512-TLluVPLo6Snn/dxEITvMz7ZuklN4qZOBddDuLb9LO3rhsfKDMNbnhcBk0SLdFsWny0aCuhWNpJ6co93702XC0A==", - "dev": true, - "dependencies": { - "@types/aria-query": "^4.2.1", - "@wdio/config": "7.5.3", - "@wdio/logger": "7.5.3", - "@wdio/protocols": "7.5.3", - "@wdio/repl": "7.5.3", - "@wdio/types": "7.5.3", - "@wdio/utils": "7.5.3", - "archiver": "^5.0.0", - "aria-query": "^4.2.2", - "atob": "^2.1.2", - "css-shorthand-properties": "^1.1.1", - "css-value": "^0.0.1", - "devtools": "7.5.7", - "devtools-protocol": "^0.0.878340", - "fs-extra": "^10.0.0", - "get-port": "^5.1.1", - "grapheme-splitter": "^1.0.2", - "lodash.clonedeep": "^4.5.0", - "lodash.isobject": "^3.0.2", - "lodash.isplainobject": "^4.0.6", - "lodash.zip": "^4.2.0", - "minimatch": "^3.0.4", - "puppeteer-core": "^9.1.0", - "query-selector-shadow-dom": "^1.0.0", - "resq": "^1.9.1", - "rgb2hex": "0.2.5", - "serialize-error": "^8.0.0", - "webdriver": "7.5.3" + "node_modules/@wdio/browserstack-service/node_modules/minimatch": { + "version": "9.0.3", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.3.tgz", + "integrity": "sha512-RHiac9mvaRw0x3AYRgDC1CxAP7HTcNrrECeA8YYJeWnpo+2Q5CegtZjaotWTWxDG3UeGA1coE05iH1mPjT/2mg==", + "dev": true, + "dependencies": { + "brace-expansion": "^2.0.1" }, "engines": { - "node": ">=12.0.0" + "node": ">=16 || 14 >=14.17" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" } }, - "node_modules/@wdio/cli/node_modules/ws": { - "version": "7.5.9", - "resolved": "https://registry.npmjs.org/ws/-/ws-7.5.9.tgz", - "integrity": "sha512-F+P9Jil7UiSKSkppIiD94dN07AwvFixvLIj1Og1Rl9GGMuNipJnV9JzjD6XuqmAeiswGvUmNLjr5cFuXwNS77Q==", + "node_modules/@wdio/browserstack-service/node_modules/ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", + "dev": true, + "optional": true, + "peer": true + }, + "node_modules/@wdio/browserstack-service/node_modules/node-fetch": { + "version": "2.7.0", + "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.7.0.tgz", + "integrity": "sha512-c4FRfUm/dbcWZ7U+1Wq0AwCyFL+3nt2bEw05wfxSz+DWpWsitgmSgYmy2dQdWyKC1694ELPqMs/YzUSNozLt8A==", "dev": true, + "dependencies": { + "whatwg-url": "^5.0.0" + }, "engines": { - "node": ">=8.3.0" + "node": "4.x || >=6.0.0" }, "peerDependencies": { - "bufferutil": "^4.0.1", - "utf-8-validate": "^5.0.2" + "encoding": "^0.1.0" }, "peerDependenciesMeta": { - "bufferutil": { - "optional": true - }, - "utf-8-validate": { + "encoding": { "optional": true } } }, - "node_modules/@wdio/cli/node_modules/yargs": { - "version": "17.6.0", - "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.6.0.tgz", - "integrity": "sha512-8H/wTDqlSwoSnScvV2N/JHfLWOKuh5MVla9hqLjK3nsfyy6Y4kDSYSvkU5YCUEPOSnRXfIyx3Sq+B/IWudTo4g==", + "node_modules/@wdio/browserstack-service/node_modules/normalize-url": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/normalize-url/-/normalize-url-8.0.0.tgz", + "integrity": "sha512-uVFpKhj5MheNBJRTiMZ9pE/7hD1QTeEvugSJW/OmLzAp78PB5O6adfMNTvmfKhXBkvCzC+rqifWcVYpGFwTjnw==", "dev": true, - "dependencies": { - "cliui": "^8.0.1", - "escalade": "^3.1.1", - "get-caller-file": "^2.0.5", - "require-directory": "^2.1.1", - "string-width": "^4.2.3", - "y18n": "^5.0.5", - "yargs-parser": "^21.0.0" + "engines": { + "node": ">=14.16" }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@wdio/browserstack-service/node_modules/p-cancelable": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/p-cancelable/-/p-cancelable-3.0.0.tgz", + "integrity": "sha512-mlVgR3PGuzlo0MmTdk4cXqXWlwQDLnONTAg6sm62XkMJEiRxN3GL3SffkYvqwonbkJBcrI7Uvv5Zh9yjvn2iUw==", + "dev": true, "engines": { - "node": ">=12" + "node": ">=12.20" } }, - "node_modules/@wdio/concise-reporter": { - "version": "7.5.7", - "resolved": "https://registry.npmjs.org/@wdio/concise-reporter/-/concise-reporter-7.5.7.tgz", - "integrity": "sha512-964i7eQ4sboSla2bdR8714Er82QBgS6u39GmDFX8Izy9Ge38xaE75HuF5S7mnOWGzSojCWgqtwy5k7Rfg6GE3g==", + "node_modules/@wdio/browserstack-service/node_modules/proxy-agent": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/proxy-agent/-/proxy-agent-6.3.0.tgz", + "integrity": "sha512-0LdR757eTj/JfuU7TL2YCuAZnxWXu3tkJbg4Oq3geW/qFNT/32T0sp2HnZ9O0lMR4q3vwAt0+xCA8SR0WAD0og==", "dev": true, "dependencies": { - "@wdio/reporter": "7.5.7", - "@wdio/types": "7.5.3", - "chalk": "^4.0.0", - "pretty-ms": "^7.0.0" + "agent-base": "^7.0.2", + "debug": "^4.3.4", + "http-proxy-agent": "^7.0.0", + "https-proxy-agent": "^7.0.0", + "lru-cache": "^7.14.1", + "pac-proxy-agent": "^7.0.0", + "proxy-from-env": "^1.1.0", + "socks-proxy-agent": "^8.0.1" }, "engines": { - "node": ">=12.0.0" - }, - "peerDependencies": { - "@wdio/cli": "^7.0.0" + "node": ">= 14" } }, - "node_modules/@wdio/concise-reporter/node_modules/@wdio/types": { - "version": "7.5.3", - "resolved": "https://registry.npmjs.org/@wdio/types/-/types-7.5.3.tgz", - "integrity": "sha512-jmumhKBhNDABnpmrshYLEcdE9WoP5tmynsDNbDABlb/W8FFiLySQOejukhYIL9CLys4zXerV3/edks0SCzHOiQ==", + "node_modules/@wdio/browserstack-service/node_modules/proxy-agent/node_modules/agent-base": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-7.1.0.tgz", + "integrity": "sha512-o/zjMZRhJxny7OyEF+Op8X+efiELC7k7yOjMzgfzVqOzXqkBkWI79YoTdOtsuWd5BWhAGAuOY/Xa6xpiaWXiNg==", "dev": true, "dependencies": { - "got": "^11.8.1" + "debug": "^4.3.4" }, "engines": { - "node": ">=12.0.0" + "node": ">= 14" } }, - "node_modules/@wdio/concise-reporter/node_modules/ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "node_modules/@wdio/browserstack-service/node_modules/proxy-agent/node_modules/http-proxy-agent": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/http-proxy-agent/-/http-proxy-agent-7.0.0.tgz", + "integrity": "sha512-+ZT+iBxVUQ1asugqnD6oWoRiS25AkjNfG085dKJGtGxkdwLQrMKU5wJr2bOOFAXzKcTuqq+7fZlTMgG3SRfIYQ==", "dev": true, "dependencies": { - "color-convert": "^2.0.1" + "agent-base": "^7.1.0", + "debug": "^4.3.4" }, "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" + "node": ">= 14" } }, - "node_modules/@wdio/concise-reporter/node_modules/chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "node_modules/@wdio/browserstack-service/node_modules/proxy-agent/node_modules/https-proxy-agent": { + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-7.0.2.tgz", + "integrity": "sha512-NmLNjm6ucYwtcUmL7JQC1ZQ57LmHP4lT15FQ8D61nak1rO6DH+fz5qNK2Ap5UN4ZapYICE3/0KodcLYSPsPbaA==", "dev": true, "dependencies": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" + "agent-base": "^7.0.2", + "debug": "4" }, "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/chalk?sponsor=1" + "node": ">= 14" } }, - "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==", + "node_modules/@wdio/browserstack-service/node_modules/puppeteer-core": { + "version": "20.3.0", + "resolved": "https://registry.npmjs.org/puppeteer-core/-/puppeteer-core-20.3.0.tgz", + "integrity": "sha512-264pBrIui5bO6NJeOcbJrLa0OCwmA4+WK00JMrLIKTfRiqe2gx8KWTzLsjyw/bizErp3TKS7vt/I0i5fTC+mAw==", "dev": true, + "optional": true, + "peer": true, "dependencies": { - "color-name": "~1.1.4" + "@puppeteer/browsers": "1.3.0", + "chromium-bidi": "0.4.9", + "cross-fetch": "3.1.6", + "debug": "4.3.4", + "devtools-protocol": "0.0.1120988", + "ws": "8.13.0" }, "engines": { - "node": ">=7.0.0" + "node": ">=16.0.0" + }, + "peerDependencies": { + "typescript": ">= 4.7.4" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } } }, - "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==", + "node_modules/@wdio/browserstack-service/node_modules/readable-stream": { + "version": "3.6.2", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz", + "integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==", "dev": true, + "dependencies": { + "inherits": "^2.0.3", + "string_decoder": "^1.1.1", + "util-deprecate": "^1.0.1" + }, "engines": { - "node": ">=8" + "node": ">= 6" } }, - "node_modules/@wdio/concise-reporter/node_modules/supports-color": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "node_modules/@wdio/browserstack-service/node_modules/responselike": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/responselike/-/responselike-3.0.0.tgz", + "integrity": "sha512-40yHxbNcl2+rzXvZuVkrYohathsSJlMTXKryG5y8uciHv1+xDLHQpgjG64JUO9nrEq2jGLH6IZ8BcZyw3wrweg==", "dev": true, "dependencies": { - "has-flag": "^4.0.0" + "lowercase-keys": "^3.0.0" }, "engines": { - "node": ">=8" + "node": ">=14.16" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/@wdio/config": { - "version": "7.5.3", - "resolved": "https://registry.npmjs.org/@wdio/config/-/config-7.5.3.tgz", - "integrity": "sha512-udvVizYoilOxuWj/BmoN6y7ZCd4wPdYNlSfWznrbCezAdaLZ4/pNDOO0WRWx2C4+q1wdkXZV/VuQPUGfL0lEHQ==", + "node_modules/@wdio/browserstack-service/node_modules/serialize-error": { + "version": "11.0.3", + "resolved": "https://registry.npmjs.org/serialize-error/-/serialize-error-11.0.3.tgz", + "integrity": "sha512-2G2y++21dhj2R7iHAdd0FIzjGwuKZld+7Pl/bTU6YIkrC2ZMbVUjm+luj6A6V34Rv9XfKJDKpTWu9W4Gse1D9g==", "dev": true, "dependencies": { - "@wdio/logger": "7.5.3", - "@wdio/types": "7.5.3", - "deepmerge": "^4.0.0", - "glob": "^7.1.2" + "type-fest": "^2.12.2" }, "engines": { - "node": ">=12.0.0" + "node": ">=14.16" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/@wdio/config/node_modules/@wdio/logger": { - "version": "7.5.3", - "resolved": "https://registry.npmjs.org/@wdio/logger/-/logger-7.5.3.tgz", - "integrity": "sha512-r9EADpm0ncS1bDQSWi/nhF9C59/WNLbdAAFlo782E9ItFCpDhNit3aQP9vETv1vFxJRjUIM8Fw/HW8zwPadkbw==", + "node_modules/@wdio/browserstack-service/node_modules/tar-stream": { + "version": "3.1.7", + "resolved": "https://registry.npmjs.org/tar-stream/-/tar-stream-3.1.7.tgz", + "integrity": "sha512-qJj60CXt7IU1Ffyc3NJMjh6EkuCFej46zUqJ4J7pqYlThyd9bO0XBTmcOIhSzZJVWfsLks0+nle/j538YAW9RQ==", "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" + "b4a": "^1.6.4", + "fast-fifo": "^1.2.0", + "streamx": "^2.15.0" } }, - "node_modules/@wdio/config/node_modules/@wdio/types": { - "version": "7.5.3", - "resolved": "https://registry.npmjs.org/@wdio/types/-/types-7.5.3.tgz", - "integrity": "sha512-jmumhKBhNDABnpmrshYLEcdE9WoP5tmynsDNbDABlb/W8FFiLySQOejukhYIL9CLys4zXerV3/edks0SCzHOiQ==", + "node_modules/@wdio/browserstack-service/node_modules/type-fest": { + "version": "2.19.0", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-2.19.0.tgz", + "integrity": "sha512-RAH822pAdBgcNMAfWnCBU3CFZcfZ/i1eZjwFU/dsLKumyuuP3niueg2UAukXYF0E2AAoc82ZSSf9J0WQBinzHA==", "dev": true, - "dependencies": { - "got": "^11.8.1" + "engines": { + "node": ">=12.20" }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@wdio/browserstack-service/node_modules/ua-parser-js": { + "version": "1.0.37", + "resolved": "https://registry.npmjs.org/ua-parser-js/-/ua-parser-js-1.0.37.tgz", + "integrity": "sha512-bhTyI94tZofjo+Dn8SN6Zv8nBDvyXTymAdM3LDI/0IboIUwTu1rEhW7v2TfiVsoYWgkQ4kOVqnI8APUFbIQIFQ==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/ua-parser-js" + }, + { + "type": "paypal", + "url": "https://paypal.me/faisalman" + }, + { + "type": "github", + "url": "https://github.com/sponsors/faisalman" + } + ], + "optional": true, + "peer": true, "engines": { - "node": ">=12.0.0" + "node": "*" } }, - "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==", + "node_modules/@wdio/browserstack-service/node_modules/uuid": { + "version": "9.0.1", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-9.0.1.tgz", + "integrity": "sha512-b+1eJOlsR9K8HJpow9Ok3fiWOWSIcIzXodvv0rQjVoOVNpWMpxf1wZNpt4y9h10odCNrqnYp1OBzRktckBe3sA==", "dev": true, - "dependencies": { - "color-convert": "^2.0.1" + "funding": [ + "https://github.com/sponsors/broofa", + "https://github.com/sponsors/ctavan" + ], + "bin": { + "uuid": "dist/bin/uuid" + } + }, + "node_modules/@wdio/browserstack-service/node_modules/webdriverio": { + "version": "8.29.1", + "resolved": "https://registry.npmjs.org/webdriverio/-/webdriverio-8.29.1.tgz", + "integrity": "sha512-NZK95ivXCqdPraB3FHMw6ByxnCvtgFXkjzG2l3Oq5z0IuJS2aMow3AKFIyiuG6is/deGCe+Tb8eFTCqak7UV+w==", + "dev": true, + "dependencies": { + "@types/node": "^20.1.0", + "@wdio/config": "8.29.1", + "@wdio/logger": "8.28.0", + "@wdio/protocols": "8.24.12", + "@wdio/repl": "8.24.12", + "@wdio/types": "8.29.1", + "@wdio/utils": "8.29.1", + "archiver": "^6.0.0", + "aria-query": "^5.0.0", + "css-shorthand-properties": "^1.1.1", + "css-value": "^0.0.1", + "devtools-protocol": "^0.0.1249869", + "grapheme-splitter": "^1.0.2", + "import-meta-resolve": "^4.0.0", + "is-plain-obj": "^4.1.0", + "lodash.clonedeep": "^4.5.0", + "lodash.zip": "^4.2.0", + "minimatch": "^9.0.0", + "puppeteer-core": "^20.9.0", + "query-selector-shadow-dom": "^1.0.0", + "resq": "^1.9.1", + "rgb2hex": "0.2.5", + "serialize-error": "^11.0.1", + "webdriver": "8.29.1" }, "engines": { - "node": ">=8" + "node": "^16.13 || >=18" }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" + "peerDependencies": { + "devtools": "^8.14.0" + }, + "peerDependenciesMeta": { + "devtools": { + "optional": true + } } }, - "node_modules/@wdio/config/node_modules/chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "node_modules/@wdio/browserstack-service/node_modules/webdriverio/node_modules/@puppeteer/browsers": { + "version": "1.4.6", + "resolved": "https://registry.npmjs.org/@puppeteer/browsers/-/browsers-1.4.6.tgz", + "integrity": "sha512-x4BEjr2SjOPowNeiguzjozQbsc6h437ovD/wu+JpaenxVLm3jkgzHY2xOslMTp50HoTvQreMjiexiGQw1sqZlQ==", "dev": true, "dependencies": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" + "debug": "4.3.4", + "extract-zip": "2.0.1", + "progress": "2.0.3", + "proxy-agent": "6.3.0", + "tar-fs": "3.0.4", + "unbzip2-stream": "1.4.3", + "yargs": "17.7.1" + }, + "bin": { + "browsers": "lib/cjs/main-cli.js" }, "engines": { - "node": ">=10" + "node": ">=16.3.0" }, - "funding": { - "url": "https://github.com/chalk/chalk?sponsor=1" + "peerDependencies": { + "typescript": ">= 4.7.4" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": 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==", + "node_modules/@wdio/browserstack-service/node_modules/webdriverio/node_modules/chromium-bidi": { + "version": "0.4.16", + "resolved": "https://registry.npmjs.org/chromium-bidi/-/chromium-bidi-0.4.16.tgz", + "integrity": "sha512-7ZbXdWERxRxSwo3txsBjjmc/NLxqb1Bk30mRb0BMS4YIaiV6zvKZqL/UAH+DdqcDYayDWk2n/y8klkBDODrPvA==", "dev": true, "dependencies": { - "color-name": "~1.1.4" + "mitt": "3.0.0" }, - "engines": { - "node": ">=7.0.0" + "peerDependencies": { + "devtools-protocol": "*" } }, - "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": { + "node_modules/@wdio/browserstack-service/node_modules/webdriverio/node_modules/cross-fetch": { "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "resolved": "https://registry.npmjs.org/cross-fetch/-/cross-fetch-4.0.0.tgz", + "integrity": "sha512-e4a5N8lVvuLgAWgnCrLr2PP0YyDOTHa9H/Rj54dirp61qXnNq46m82bRhNqIA5VccJtWBvPTFRV3TtvHUKPB1g==", "dev": true, - "engines": { - "node": ">=8" + "dependencies": { + "node-fetch": "^2.6.12" } }, - "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==", + "node_modules/@wdio/browserstack-service/node_modules/webdriverio/node_modules/devtools-protocol": { + "version": "0.0.1249869", + "resolved": "https://registry.npmjs.org/devtools-protocol/-/devtools-protocol-0.0.1249869.tgz", + "integrity": "sha512-Ctp4hInA0BEavlUoRy9mhGq0i+JSo/AwVyX2EFgZmV1kYB+Zq+EMBAn52QWu6FbRr10hRb6pBl420upbp4++vg==", + "dev": true + }, + "node_modules/@wdio/browserstack-service/node_modules/webdriverio/node_modules/puppeteer-core": { + "version": "20.9.0", + "resolved": "https://registry.npmjs.org/puppeteer-core/-/puppeteer-core-20.9.0.tgz", + "integrity": "sha512-H9fYZQzMTRrkboEfPmf7m3CLDN6JvbxXA3qTtS+dFt27tR+CsFHzPsT6pzp6lYL6bJbAPaR0HaPO6uSi+F94Pg==", "dev": true, "dependencies": { - "has-flag": "^4.0.0" + "@puppeteer/browsers": "1.4.6", + "chromium-bidi": "0.4.16", + "cross-fetch": "4.0.0", + "debug": "4.3.4", + "devtools-protocol": "0.0.1147663", + "ws": "8.13.0" }, "engines": { - "node": ">=8" + "node": ">=16.3.0" + }, + "peerDependencies": { + "typescript": ">= 4.7.4" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } } }, - "node_modules/@wdio/local-runner": { - "version": "7.5.7", - "resolved": "https://registry.npmjs.org/@wdio/local-runner/-/local-runner-7.5.7.tgz", - "integrity": "sha512-aYc0XUV+/e3cg8Fp+CWlC4FbwSSG3mKAv1iuy/+Hwzg2kJE+aa+Rf2p2BQYc7HPRtKNW0bM8o+aCImZLAiPM+A==", + "node_modules/@wdio/browserstack-service/node_modules/webdriverio/node_modules/puppeteer-core/node_modules/devtools-protocol": { + "version": "0.0.1147663", + "resolved": "https://registry.npmjs.org/devtools-protocol/-/devtools-protocol-0.0.1147663.tgz", + "integrity": "sha512-hyWmRrexdhbZ1tcJUGpO95ivbRhWXz++F4Ko+n21AY5PNln2ovoJw+8ZMNDTtip+CNFQfrtLVh/w4009dXO/eQ==", + "dev": true + }, + "node_modules/@wdio/browserstack-service/node_modules/webdriverio/node_modules/tar-fs": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/tar-fs/-/tar-fs-3.0.4.tgz", + "integrity": "sha512-5AFQU8b9qLfZCX9zp2duONhPmZv0hGYiBPJsyUdqMjzq/mqVpy/rEUSeHk1+YitmxugaptgBh5oDGU3VsAJq4w==", "dev": true, "dependencies": { - "@types/stream-buffers": "^3.0.3", - "@wdio/logger": "7.5.3", - "@wdio/repl": "7.5.3", - "@wdio/runner": "7.5.7", - "@wdio/types": "7.5.3", - "async-exit-hook": "^2.0.1", - "split2": "^3.2.2", - "stream-buffers": "^3.0.2" - }, + "mkdirp-classic": "^0.5.2", + "pump": "^3.0.0", + "tar-stream": "^3.1.5" + } + }, + "node_modules/@wdio/browserstack-service/node_modules/ws": { + "version": "8.13.0", + "resolved": "https://registry.npmjs.org/ws/-/ws-8.13.0.tgz", + "integrity": "sha512-x9vcZYTrFPC7aSIbj7sRCYo7L/Xb8Iy+pW0ng0wt2vCJv7M9HOMy0UoN3rr+IFC7hb7vXoqS+P9ktyLLLhO+LA==", + "dev": true, "engines": { - "node": ">=12.0.0" + "node": ">=10.0.0" }, "peerDependencies": { - "@wdio/cli": "^7.0.0" + "bufferutil": "^4.0.1", + "utf-8-validate": ">=5.0.2" + }, + "peerDependenciesMeta": { + "bufferutil": { + "optional": true + }, + "utf-8-validate": { + "optional": true + } } }, - "node_modules/@wdio/local-runner/node_modules/@wdio/logger": { - "version": "7.5.3", - "resolved": "https://registry.npmjs.org/@wdio/logger/-/logger-7.5.3.tgz", - "integrity": "sha512-r9EADpm0ncS1bDQSWi/nhF9C59/WNLbdAAFlo782E9ItFCpDhNit3aQP9vETv1vFxJRjUIM8Fw/HW8zwPadkbw==", + "node_modules/@wdio/browserstack-service/node_modules/yargs": { + "version": "17.7.1", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.7.1.tgz", + "integrity": "sha512-cwiTb08Xuv5fqF4AovYacTFNxk62th7LKJ6BL9IGUpTJrWoU7/7WdQGTP2SjKf1dUNBGzDd28p/Yfs/GI6JrLw==", "dev": true, "dependencies": { - "chalk": "^4.0.0", - "loglevel": "^1.6.0", - "loglevel-plugin-prefix": "^0.8.4", - "strip-ansi": "^6.0.0" + "cliui": "^8.0.1", + "escalade": "^3.1.1", + "get-caller-file": "^2.0.5", + "require-directory": "^2.1.1", + "string-width": "^4.2.3", + "y18n": "^5.0.5", + "yargs-parser": "^21.1.1" }, "engines": { - "node": ">=12.0.0" + "node": ">=12" } }, - "node_modules/@wdio/local-runner/node_modules/@wdio/types": { - "version": "7.5.3", - "resolved": "https://registry.npmjs.org/@wdio/types/-/types-7.5.3.tgz", - "integrity": "sha512-jmumhKBhNDABnpmrshYLEcdE9WoP5tmynsDNbDABlb/W8FFiLySQOejukhYIL9CLys4zXerV3/edks0SCzHOiQ==", + "node_modules/@wdio/browserstack-service/node_modules/zip-stream": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/zip-stream/-/zip-stream-5.0.1.tgz", + "integrity": "sha512-UfZ0oa0C8LI58wJ+moL46BDIMgCQbnsb+2PoiJYtonhBsMh2bq1eRBVkvjfVsqbEHd9/EgKPUuL9saSSsec8OA==", "dev": true, "dependencies": { - "got": "^11.8.1" + "archiver-utils": "^4.0.1", + "compress-commons": "^5.0.1", + "readable-stream": "^3.6.0" }, "engines": { - "node": ">=12.0.0" + "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" + "node_modules/@wdio/cli": { + "version": "8.29.1", + "resolved": "https://registry.npmjs.org/@wdio/cli/-/cli-8.29.1.tgz", + "integrity": "sha512-WWRTf0g0O+ovTTvS1kEhZ/svX32M7jERuuMF1MaldKCi7rZwHsQqOyJD+fO1UDjuxqS96LHSGsZn0auwUfCTXA==", + "dev": true, + "dependencies": { + "@types/node": "^20.1.1", + "@vitest/snapshot": "^1.2.1", + "@wdio/config": "8.29.1", + "@wdio/globals": "8.29.1", + "@wdio/logger": "8.28.0", + "@wdio/protocols": "8.24.12", + "@wdio/types": "8.29.1", + "@wdio/utils": "8.29.1", + "async-exit-hook": "^2.0.1", + "chalk": "^5.2.0", + "chokidar": "^3.5.3", + "cli-spinners": "^2.9.0", + "dotenv": "^16.3.1", + "ejs": "^3.1.9", + "execa": "^8.0.1", + "import-meta-resolve": "^4.0.0", + "inquirer": "9.2.12", + "lodash.flattendeep": "^4.4.0", + "lodash.pickby": "^4.6.0", + "lodash.union": "^4.6.0", + "read-pkg-up": "^10.0.0", + "recursive-readdir": "^2.2.3", + "webdriverio": "8.29.1", + "yargs": "^17.7.2" }, - "engines": { - "node": ">=8" + "bin": { + "wdio": "bin/wdio.js" }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" + "engines": { + "node": "^16.13 || >=18" } }, - "node_modules/@wdio/local-runner/node_modules/chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "node_modules/@wdio/cli/node_modules/@puppeteer/browsers": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/@puppeteer/browsers/-/browsers-1.3.0.tgz", + "integrity": "sha512-an3QdbNPkuU6qpxpbssxAbjRLJcF+eP4L8UqIY3+6n0sbaVxw5pz7PiCLy9g32XEZuoamUlV5ZQPnA6FxvkIHA==", "dev": true, + "optional": true, + "peer": true, "dependencies": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" + "debug": "4.3.4", + "extract-zip": "2.0.1", + "http-proxy-agent": "5.0.0", + "https-proxy-agent": "5.0.1", + "progress": "2.0.3", + "proxy-from-env": "1.1.0", + "tar-fs": "2.1.1", + "unbzip2-stream": "1.4.3", + "yargs": "17.7.1" + }, + "bin": { + "browsers": "lib/cjs/main-cli.js" }, "engines": { - "node": ">=10" + "node": ">=16.0.0" }, - "funding": { - "url": "https://github.com/chalk/chalk?sponsor=1" + "peerDependencies": { + "typescript": ">= 4.7.4" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } } }, - "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==", + "node_modules/@wdio/cli/node_modules/@puppeteer/browsers/node_modules/yargs": { + "version": "17.7.1", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.7.1.tgz", + "integrity": "sha512-cwiTb08Xuv5fqF4AovYacTFNxk62th7LKJ6BL9IGUpTJrWoU7/7WdQGTP2SjKf1dUNBGzDd28p/Yfs/GI6JrLw==", "dev": true, + "optional": true, + "peer": true, "dependencies": { - "color-name": "~1.1.4" + "cliui": "^8.0.1", + "escalade": "^3.1.1", + "get-caller-file": "^2.0.5", + "require-directory": "^2.1.1", + "string-width": "^4.2.3", + "y18n": "^5.0.5", + "yargs-parser": "^21.1.1" }, "engines": { - "node": ">=7.0.0" + "node": ">=12" } }, - "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==", + "node_modules/@wdio/cli/node_modules/@types/which": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/@types/which/-/which-2.0.2.tgz", + "integrity": "sha512-113D3mDkZDjo+EeUEHCFy0qniNc1ZpecGiAU7WSo7YDoSzolZIQKpYFHrPpjkB2nuyahcKfrmLXeQlh7gqJYdw==", "dev": true, - "engines": { - "node": ">=8" - } + "optional": true, + "peer": true }, - "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==", + "node_modules/@wdio/cli/node_modules/archiver": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/archiver/-/archiver-6.0.1.tgz", + "integrity": "sha512-CXGy4poOLBKptiZH//VlWdFuUC1RESbdZjGjILwBuZ73P7WkAUN0htfSfBq/7k6FRFlpu7bg4JOkj1vU9G6jcQ==", "dev": true, "dependencies": { - "has-flag": "^4.0.0" + "archiver-utils": "^4.0.1", + "async": "^3.2.4", + "buffer-crc32": "^0.2.1", + "readable-stream": "^3.6.0", + "readdir-glob": "^1.1.2", + "tar-stream": "^3.0.0", + "zip-stream": "^5.0.1" }, "engines": { - "node": ">=8" + "node": ">= 12.0.0" } }, - "node_modules/@wdio/logger": { - "version": "7.16.0", - "resolved": "https://registry.npmjs.org/@wdio/logger/-/logger-7.16.0.tgz", - "integrity": "sha512-/6lOGb2Iow5eSsy7RJOl1kCwsP4eMlG+/QKro5zUJsuyNJSQXf2ejhpkzyKWLgQbHu83WX6cM1014AZuLkzoQg==", + "node_modules/@wdio/cli/node_modules/archiver-utils": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/archiver-utils/-/archiver-utils-4.0.1.tgz", + "integrity": "sha512-Q4Q99idbvzmgCTEAAhi32BkOyq8iVI5EwdO0PmBDSGIzzjYNdcFn7Q7k3OzbLy4kLUPXfJtG6fO2RjftXbobBg==", "dev": true, "dependencies": { - "chalk": "^4.0.0", - "loglevel": "^1.6.0", - "loglevel-plugin-prefix": "^0.8.4", - "strip-ansi": "^6.0.0" + "glob": "^8.0.0", + "graceful-fs": "^4.2.0", + "lazystream": "^1.0.0", + "lodash": "^4.17.15", + "normalize-path": "^3.0.0", + "readable-stream": "^3.6.0" }, "engines": { - "node": ">=12.0.0" + "node": ">= 12.0.0" } }, - "node_modules/@wdio/logger/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==", + "node_modules/@wdio/cli/node_modules/async": { + "version": "3.2.5", + "resolved": "https://registry.npmjs.org/async/-/async-3.2.5.tgz", + "integrity": "sha512-baNZyqaaLhyLVKm/DlvdW051MSgO6b8eVfIezl9E5PqWxFgzLm/wQntEW4zOytVburDEr0JlALEpdOFwvErLsg==", + "dev": true + }, + "node_modules/@wdio/cli/node_modules/brace-expansion": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", + "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", "dev": true, "dependencies": { - "color-convert": "^2.0.1" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" + "balanced-match": "^1.0.0" } }, - "node_modules/@wdio/logger/node_modules/chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "node_modules/@wdio/cli/node_modules/chalk": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-5.3.0.tgz", + "integrity": "sha512-dLitG79d+GV1Nb/VYcCDFivJeK1hiukt9QjRNVOsUtTy1rR1YJsmpGGTZ3qJos+uw7WmWF4wUwBd9jxjocFC2w==", "dev": true, - "dependencies": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - }, "engines": { - "node": ">=10" + "node": "^12.17.0 || ^14.13 || >=16.0.0" }, "funding": { "url": "https://github.com/chalk/chalk?sponsor=1" } }, - "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==", + "node_modules/@wdio/cli/node_modules/chrome-launcher": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/chrome-launcher/-/chrome-launcher-1.1.0.tgz", + "integrity": "sha512-rJYWeEAERwWIr3c3mEVXwNiODPEdMRlRxHc47B1qHPOolHZnkj7rMv1QSUfPoG6MgatWj5AxSpnKKR4QEwEQIQ==", "dev": true, + "optional": true, + "peer": true, "dependencies": { - "color-name": "~1.1.4" + "@types/node": "*", + "escape-string-regexp": "^4.0.0", + "is-wsl": "^2.2.0", + "lighthouse-logger": "^2.0.1" + }, + "bin": { + "print-chrome-path": "bin/print-chrome-path.js" }, "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": ">=12.13.0" } }, - "node_modules/@wdio/logger/node_modules/supports-color": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "node_modules/@wdio/cli/node_modules/compress-commons": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/compress-commons/-/compress-commons-5.0.1.tgz", + "integrity": "sha512-MPh//1cERdLtqwO3pOFLeXtpuai0Y2WCd5AhtKxznqM7WtaMYaOEMSgn45d9D10sIHSfIKE603HlOp8OPGrvag==", "dev": true, "dependencies": { - "has-flag": "^4.0.0" + "crc-32": "^1.2.0", + "crc32-stream": "^5.0.0", + "normalize-path": "^3.0.0", + "readable-stream": "^3.6.0" }, "engines": { - "node": ">=8" + "node": ">= 12.0.0" } }, - "node_modules/@wdio/mocha-framework": { - "version": "7.5.3", - "resolved": "https://registry.npmjs.org/@wdio/mocha-framework/-/mocha-framework-7.5.3.tgz", - "integrity": "sha512-96QCVWsiyZxEgOZP3oTq2B2T7zne5dCdehLa2n4q/BLjk96Rj0jifidJZfd/1+vdNPKX0gWWAzpy98Znn8MVMw==", + "node_modules/@wdio/cli/node_modules/crc32-stream": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/crc32-stream/-/crc32-stream-5.0.0.tgz", + "integrity": "sha512-B0EPa1UK+qnpBZpG+7FgPCu0J2ETLpXq09o9BkLkEAhdB6Z61Qo4pJ3JYu0c+Qi+/SAL7QThqnzS06pmSSyZaw==", "dev": true, "dependencies": { - "@types/mocha": "^8.0.0", - "@wdio/logger": "7.5.3", - "@wdio/types": "7.5.3", - "@wdio/utils": "7.5.3", - "expect-webdriverio": "^2.0.0", - "mocha": "^8.0.1" + "crc-32": "^1.2.0", + "readable-stream": "^3.4.0" }, "engines": { - "node": ">=12.0.0" + "node": ">= 12.0.0" } }, - "node_modules/@wdio/mocha-framework/node_modules/@wdio/logger": { - "version": "7.5.3", - "resolved": "https://registry.npmjs.org/@wdio/logger/-/logger-7.5.3.tgz", - "integrity": "sha512-r9EADpm0ncS1bDQSWi/nhF9C59/WNLbdAAFlo782E9ItFCpDhNit3aQP9vETv1vFxJRjUIM8Fw/HW8zwPadkbw==", + "node_modules/@wdio/cli/node_modules/cross-fetch": { + "version": "3.1.6", + "resolved": "https://registry.npmjs.org/cross-fetch/-/cross-fetch-3.1.6.tgz", + "integrity": "sha512-riRvo06crlE8HiqOwIpQhxwdOk4fOeR7FVM/wXoxchFEqMNUjvbs3bfo4OTgMEMHzppd4DxFBDbyySj8Cv781g==", "dev": true, + "optional": true, + "peer": 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-fetch": "^2.6.11" } }, - "node_modules/@wdio/mocha-framework/node_modules/@wdio/types": { - "version": "7.5.3", - "resolved": "https://registry.npmjs.org/@wdio/types/-/types-7.5.3.tgz", - "integrity": "sha512-jmumhKBhNDABnpmrshYLEcdE9WoP5tmynsDNbDABlb/W8FFiLySQOejukhYIL9CLys4zXerV3/edks0SCzHOiQ==", + "node_modules/@wdio/cli/node_modules/devtools": { + "version": "8.29.1", + "resolved": "https://registry.npmjs.org/devtools/-/devtools-8.29.1.tgz", + "integrity": "sha512-fbH0Z7CPK4OZSgUw2QcAppczowxtSyvFztPUmiFyi99cUadjEOwlg0aL3pBVlIDo67olYjGb8GD1M5Z4yI/P6w==", "dev": true, + "optional": true, + "peer": true, "dependencies": { - "got": "^11.8.1" + "@types/node": "^20.1.0", + "@wdio/config": "8.29.1", + "@wdio/logger": "8.28.0", + "@wdio/protocols": "8.24.12", + "@wdio/types": "8.29.1", + "@wdio/utils": "8.29.1", + "chrome-launcher": "^1.0.0", + "edge-paths": "^3.0.5", + "import-meta-resolve": "^4.0.0", + "puppeteer-core": "20.3.0", + "query-selector-shadow-dom": "^1.0.0", + "ua-parser-js": "^1.0.1", + "uuid": "^9.0.0", + "which": "^4.0.0" }, "engines": { - "node": ">=12.0.0" + "node": "^16.13 || >=18" } }, - "node_modules/@wdio/mocha-framework/node_modules/ansi-colors": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-4.1.1.tgz", - "integrity": "sha512-JoX0apGbHaUJBNl6yF+p6JAFYZ666/hhCGKN5t9QFjbJQKUU/g8MNbFDbvfrgKXvI1QpZplPOnwIo99lX/AAmA==", + "node_modules/@wdio/cli/node_modules/devtools-protocol": { + "version": "0.0.1120988", + "resolved": "https://registry.npmjs.org/devtools-protocol/-/devtools-protocol-0.0.1120988.tgz", + "integrity": "sha512-39fCpE3Z78IaIPChJsP6Lhmkbf4dWXOmzLk/KFTdRkNk/0JymRIfUynDVRndV9HoDz8PyalK1UH21ST/ivwW5Q==", "dev": true, - "engines": { - "node": ">=6" - } + "optional": true, + "peer": true }, - "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==", + "node_modules/@wdio/cli/node_modules/devtools/node_modules/which": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/which/-/which-4.0.0.tgz", + "integrity": "sha512-GlaYyEb07DPxYCKhKzplCWBJtvxZcZMrL+4UkrTSJHHPyZU4mYYTv3qaOe77H7EODLSSopAUFAc6W8U4yqvscg==", "dev": true, + "optional": true, + "peer": true, "dependencies": { - "color-convert": "^2.0.1" + "isexe": "^3.1.1" }, - "engines": { - "node": ">=8" + "bin": { + "node-which": "bin/which.js" }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" + "engines": { + "node": "^16.13.0 || >=18.0.0" } }, - "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.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "node_modules/@wdio/cli/node_modules/edge-paths": { + "version": "3.0.5", + "resolved": "https://registry.npmjs.org/edge-paths/-/edge-paths-3.0.5.tgz", + "integrity": "sha512-sB7vSrDnFa4ezWQk9nZ/n0FdpdUuC6R1EOrlU3DL+bovcNFK28rqu2emmAUjujYEJTWIgQGqgVVWUZXMnc8iWg==", "dev": true, + "optional": true, + "peer": true, "dependencies": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" + "@types/which": "^2.0.1", + "which": "^2.0.2" }, "engines": { - "node": ">=10" + "node": ">=14.0.0" }, "funding": { - "url": "https://github.com/chalk/chalk?sponsor=1" + "url": "https://github.com/sponsors/shirshak55" } }, - "node_modules/@wdio/mocha-framework/node_modules/chokidar": { - "version": "3.5.1", - "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.5.1.tgz", - "integrity": "sha512-9+s+Od+W0VJJzawDma/gvBNQqkTiqYTWLuZoyAsivsI4AaWTCzHG06/TMjsf1cYe9Cb97UCEhjz7HvnPk2p/tw==", + "node_modules/@wdio/cli/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, - "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" - }, + "optional": true, + "peer": true, "engines": { - "node": ">= 8.10.0" + "node": ">=10" }, - "optionalDependencies": { - "fsevents": "~2.3.1" - } - }, - "node_modules/@wdio/mocha-framework/node_modules/cliui": { - "version": "7.0.4", - "resolved": "https://registry.npmjs.org/cliui/-/cliui-7.0.4.tgz", - "integrity": "sha512-OcRE68cOsVMXp1Yvonl/fzkQOyjLSu/8bhPDfQt0e0/Eb283TKP20Fs2MqoPsr9SwA595rRCA+QMzYc9nBP+JQ==", - "dev": true, - "dependencies": { - "string-width": "^4.2.0", - "strip-ansi": "^6.0.0", - "wrap-ansi": "^7.0.0" + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "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==", + "node_modules/@wdio/cli/node_modules/execa": { + "version": "8.0.1", + "resolved": "https://registry.npmjs.org/execa/-/execa-8.0.1.tgz", + "integrity": "sha512-VyhnebXciFV2DESc+p6B+y0LjSm0krU4OgJN44qFAhBY0TJ+1V61tYD2+wHusZ6F9n5K+vl8k0sTy7PEfV4qpg==", "dev": true, "dependencies": { - "color-name": "~1.1.4" + "cross-spawn": "^7.0.3", + "get-stream": "^8.0.1", + "human-signals": "^5.0.0", + "is-stream": "^3.0.0", + "merge-stream": "^2.0.0", + "npm-run-path": "^5.1.0", + "onetime": "^6.0.0", + "signal-exit": "^4.1.0", + "strip-final-newline": "^3.0.0" }, "engines": { - "node": ">=7.0.0" + "node": ">=16.17" + }, + "funding": { + "url": "https://github.com/sindresorhus/execa?sponsor=1" } }, - "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==", + "node_modules/@wdio/cli/node_modules/find-up": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-6.3.0.tgz", + "integrity": "sha512-v2ZsoEuVHYy8ZIlYqwPe/39Cy+cFDzp4dXPaxNvkEuouymu+2Jbz0PxpKarJHYJTmv2HWT3O382qY8l4jMWthw==", "dev": true, "dependencies": { - "ms": "2.1.2" - }, - "engines": { - "node": ">=6.0" + "locate-path": "^7.1.0", + "path-exists": "^5.0.0" }, - "peerDependenciesMeta": { - "supports-color": { - "optional": true - } - } - }, - "node_modules/@wdio/mocha-framework/node_modules/diff": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/diff/-/diff-5.0.0.tgz", - "integrity": "sha512-/VTCrvm5Z0JGty/BWHljh+BAiw3IK+2j87NGMu8Nwc/f48WoDAC395uomO9ZD117ZOBaHmkX1oyLvkVM/aIT3w==", - "dev": true, - "engines": { - "node": ">=0.3.1" - } - }, - "node_modules/@wdio/mocha-framework/node_modules/escape-string-regexp": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", - "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", - "dev": true, "engines": { - "node": ">=10" + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" }, "funding": { "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/@wdio/mocha-framework/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==", + "node_modules/@wdio/cli/node_modules/get-stream": { + "version": "8.0.1", + "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-8.0.1.tgz", + "integrity": "sha512-VaUJspBffn/LMCJVoMvSAdmscJyS1auj5Zulnn5UoYcY531UWmdwhRWkcGKnGU93m5HSXP9LP2usOryrBtQowA==", "dev": true, - "dependencies": { - "locate-path": "^6.0.0", - "path-exists": "^4.0.0" - }, "engines": { - "node": ">=10" + "node": ">=16" }, "funding": { "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/@wdio/mocha-framework/node_modules/glob": { - "version": "7.1.6", - "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.6.tgz", - "integrity": "sha512-LwaxwyZ72Lk7vZINtNNrywX0ZuLyStrdDtabefZKAY5ZGJhVtgdznluResxNmPitE0SAO+O26sWTHeKSI2wMBA==", + "node_modules/@wdio/cli/node_modules/glob": { + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/glob/-/glob-8.1.0.tgz", + "integrity": "sha512-r8hpEjiQEYlF2QU0df3dS+nxxSIreXQS1qRhMJM0Q5NDdR386C7jb7Hwwod8Fgiuex+k0GFjgft18yvxm5XoCQ==", "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" + "minimatch": "^5.0.1", + "once": "^1.3.0" }, "engines": { - "node": "*" + "node": ">=12" }, "funding": { "url": "https://github.com/sponsors/isaacs" } }, - "node_modules/@wdio/mocha-framework/node_modules/has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "node_modules/@wdio/cli/node_modules/glob/node_modules/minimatch": { + "version": "5.1.6", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-5.1.6.tgz", + "integrity": "sha512-lKwV/1brpG6mBUFHtb7NUmtABCb2WZZmm2wNiOA5hAb8VdCS4B3dtMWyvcoViccwAW/COERjXLt0zP1zXUN26g==", "dev": true, + "dependencies": { + "brace-expansion": "^2.0.1" + }, "engines": { - "node": ">=8" + "node": ">=10" } }, - "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==", + "node_modules/@wdio/cli/node_modules/hosted-git-info": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-7.0.1.tgz", + "integrity": "sha512-+K84LB1DYwMHoHSgaOY/Jfhw3ucPmSET5v98Ke/HdNSw4a0UktWzyW1mjhjpuxxTqOOsfWT/7iVshHmVZ4IpOA==", "dev": true, "dependencies": { - "argparse": "^2.0.1" + "lru-cache": "^10.0.1" }, - "bin": { - "js-yaml": "bin/js-yaml.js" + "engines": { + "node": "^16.14.0 || >=18.0.0" } }, - "node_modules/@wdio/mocha-framework/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==", + "node_modules/@wdio/cli/node_modules/hosted-git-info/node_modules/lru-cache": { + "version": "10.1.0", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.1.0.tgz", + "integrity": "sha512-/1clY/ui8CzjKFyjdvwPWJUYKiFVXG2I2cY0ssG7h4+hwk+XOIX7ZSG9Q7TW8TW3Kp3BUSqgFWBLgL4PJ+Blag==", "dev": true, - "dependencies": { - "p-locate": "^5.0.0" - }, "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "node": "14 || >=16.14" } }, - "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==", + "node_modules/@wdio/cli/node_modules/http-proxy-agent": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/http-proxy-agent/-/http-proxy-agent-5.0.0.tgz", + "integrity": "sha512-n2hY8YdoRE1i7r6M0w9DIw5GgZN0G25P8zLCRQ8rjXtTU3vsNFBI/vWK/UIeE6g5MUUz6avwAPXmL6Fy9D/90w==", "dev": true, + "optional": true, + "peer": true, "dependencies": { - "chalk": "^4.0.0" + "@tootallnate/once": "2", + "agent-base": "6", + "debug": "4" }, "engines": { - "node": ">=10" + "node": ">= 6" } }, - "node_modules/@wdio/mocha-framework/node_modules/minimatch": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", - "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==", + "node_modules/@wdio/cli/node_modules/is-stream": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-3.0.0.tgz", + "integrity": "sha512-LnQR4bZ9IADDRSkvpqMGvt/tEJWclzklNgSw48V5EAaAeDd6qGvN8ei6k5p0tvxSR171VmGyHuTiAOfxAbr8kA==", "dev": true, - "dependencies": { - "brace-expansion": "^1.1.7" - }, "engines": { - "node": "*" + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "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==", + "node_modules/@wdio/cli/node_modules/isexe": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/isexe/-/isexe-3.1.1.tgz", + "integrity": "sha512-LpB/54B+/2J5hqQ7imZHfdU31OlgQqx7ZicVlkm9kzg9/w8GKLEcFfJl/t7DCEDueOyBAD6zCCwTO6Fzs0NoEQ==", + "dev": true, + "optional": true, + "peer": true, + "engines": { + "node": ">=16" + } + }, + "node_modules/@wdio/cli/node_modules/json-parse-even-better-errors": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/json-parse-even-better-errors/-/json-parse-even-better-errors-3.0.1.tgz", + "integrity": "sha512-aatBvbL26wVUCLmbWdCpeu9iF5wOyWpagiKkInA+kfws3sWdBrTnsvN2CKcyCYyUrc7rebNBlK6+kteg7ksecg==", "dev": true, + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + } + }, + "node_modules/@wdio/cli/node_modules/lighthouse-logger": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/lighthouse-logger/-/lighthouse-logger-2.0.1.tgz", + "integrity": "sha512-ioBrW3s2i97noEmnXxmUq7cjIcVRjT5HBpAYy8zE11CxU9HqlWHHeRxfeN1tn8F7OEMVPIC9x1f8t3Z7US9ehQ==", + "dev": true, + "optional": true, + "peer": 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" + "debug": "^2.6.9", + "marky": "^1.2.2" + } + }, + "node_modules/@wdio/cli/node_modules/lighthouse-logger/node_modules/debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "dev": true, + "optional": true, + "peer": true, + "dependencies": { + "ms": "2.0.0" + } + }, + "node_modules/@wdio/cli/node_modules/lines-and-columns": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-2.0.4.tgz", + "integrity": "sha512-wM1+Z03eypVAVUCE7QdSqpVIvelbOakn1M0bPDoA4SGWPx3sNDVUiMo3L6To6WWGClB7VyXnhQ4Sn7gxiJbE6A==", + "dev": true, + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + } + }, + "node_modules/@wdio/cli/node_modules/locate-path": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-7.2.0.tgz", + "integrity": "sha512-gvVijfZvn7R+2qyPX8mAuKcFGDf6Nc61GdvGafQsHL0sBIxfKzA+usWn4GFC/bk+QdwPUD4kWFJLhElipq+0VA==", + "dev": true, + "dependencies": { + "p-locate": "^6.0.0" }, "engines": { - "node": ">= 10.12.0" + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" }, "funding": { - "type": "opencollective", - "url": "https://opencollective.com/mochajs" + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/@wdio/mocha-framework/node_modules/mocha/node_modules/ms": { - "version": "2.1.3", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", - "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", - "dev": true + "node_modules/@wdio/cli/node_modules/mimic-fn": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-4.0.0.tgz", + "integrity": "sha512-vqiC06CuhBTUdZH+RYl8sFrL096vA45Ok5ISO6sE/Mr1jRbGH4Csnhi8f3wKVl7x8mO4Au7Ir9D3Oyv1VYMFJw==", + "dev": true, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } }, - "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==", + "node_modules/@wdio/cli/node_modules/minimatch": { + "version": "9.0.3", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.3.tgz", + "integrity": "sha512-RHiac9mvaRw0x3AYRgDC1CxAP7HTcNrrECeA8YYJeWnpo+2Q5CegtZjaotWTWxDG3UeGA1coE05iH1mPjT/2mg==", "dev": true, "dependencies": { - "has-flag": "^4.0.0" + "brace-expansion": "^2.0.1" }, "engines": { - "node": ">=10" + "node": ">=16 || 14 >=14.17" }, "funding": { - "url": "https://github.com/chalk/supports-color?sponsor=1" + "url": "https://github.com/sponsors/isaacs" } }, - "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==", + "node_modules/@wdio/cli/node_modules/ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", + "dev": true, + "optional": true, + "peer": true + }, + "node_modules/@wdio/cli/node_modules/node-fetch": { + "version": "2.7.0", + "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.7.0.tgz", + "integrity": "sha512-c4FRfUm/dbcWZ7U+1Wq0AwCyFL+3nt2bEw05wfxSz+DWpWsitgmSgYmy2dQdWyKC1694ELPqMs/YzUSNozLt8A==", "dev": true, - "bin": { - "nanoid": "bin/nanoid.cjs" + "dependencies": { + "whatwg-url": "^5.0.0" }, "engines": { - "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1" + "node": "4.x || >=6.0.0" + }, + "peerDependencies": { + "encoding": "^0.1.0" + }, + "peerDependenciesMeta": { + "encoding": { + "optional": true + } } }, - "node_modules/@wdio/mocha-framework/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==", + "node_modules/@wdio/cli/node_modules/normalize-package-data": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-6.0.0.tgz", + "integrity": "sha512-UL7ELRVxYBHBgYEtZCXjxuD5vPxnmvMGq0jp/dGPKKrN7tfsBh2IY7TlJ15WWwdjRWD3RJbnsygUurTK3xkPkg==", "dev": true, "dependencies": { - "yocto-queue": "^0.1.0" + "hosted-git-info": "^7.0.0", + "is-core-module": "^2.8.1", + "semver": "^7.3.5", + "validate-npm-package-license": "^3.0.4" }, "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "node": "^16.14.0 || >=18.0.0" } }, - "node_modules/@wdio/mocha-framework/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==", + "node_modules/@wdio/cli/node_modules/npm-run-path": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-5.2.0.tgz", + "integrity": "sha512-W4/tgAXFqFA0iL7fk0+uQ3g7wkL8xJmx3XdK0VGb4cHW//eZTtKGvFBBoRKVTpY7n6ze4NL9ly7rgXcHufqXKg==", "dev": true, "dependencies": { - "p-limit": "^3.0.2" + "path-key": "^4.0.0" }, "engines": { - "node": ">=10" + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" }, "funding": { "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/@wdio/mocha-framework/node_modules/readdirp": { - "version": "3.5.0", - "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.5.0.tgz", - "integrity": "sha512-cMhu7c/8rdhkHXWsY+osBhfSy0JikwpHK/5+imo+LpeasTF8ouErHrlYkwT0++njiyuDvc7OFY5T3ukvZ8qmFQ==", + "node_modules/@wdio/cli/node_modules/onetime": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/onetime/-/onetime-6.0.0.tgz", + "integrity": "sha512-1FlR+gjXK7X+AsAHso35MnyN5KqGwJRi/31ft6x0M194ht7S+rWAvd7PHss9xSKMzE0asv1pyIHaJYq+BbacAQ==", "dev": true, "dependencies": { - "picomatch": "^2.2.1" + "mimic-fn": "^4.0.0" }, "engines": { - "node": ">=8.10.0" + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/@wdio/mocha-framework/node_modules/serialize-javascript": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-5.0.1.tgz", - "integrity": "sha512-SaaNal9imEO737H2c05Og0/8LUXG7EnsZyMa8MzkmuHoELfT6txuj0cMqRj6zfPKnmQ1yasR4PCJc8x+M4JSPA==", + "node_modules/@wdio/cli/node_modules/p-limit": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-4.0.0.tgz", + "integrity": "sha512-5b0R4txpzjPWVw/cXXUResoD4hb6U/x9BH08L7nw+GN1sezDzPdxeRvpc9c433fZhBan/wusjbCsqwqm4EIBIQ==", "dev": true, "dependencies": { - "randombytes": "^2.1.0" - } - }, - "node_modules/@wdio/mocha-framework/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, + "yocto-queue": "^1.0.0" + }, "engines": { - "node": ">=8" + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" }, "funding": { "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/@wdio/mocha-framework/node_modules/supports-color": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "node_modules/@wdio/cli/node_modules/p-locate": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-6.0.0.tgz", + "integrity": "sha512-wPrq66Llhl7/4AGC6I+cqxT07LhXvWL08LNXz1fENOw0Ap4sRZZ/gZpTTJ5jpurzzzfS2W/Ge9BY3LgLjCShcw==", "dev": true, "dependencies": { - "has-flag": "^4.0.0" + "p-limit": "^4.0.0" }, "engines": { - "node": ">=8" + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/@wdio/mocha-framework/node_modules/workerpool": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/workerpool/-/workerpool-6.1.0.tgz", - "integrity": "sha512-toV7q9rWNYha963Pl/qyeZ6wG+3nnsyvolaNUS8+R5Wtw6qJPTxIlOP1ZSvcGhEJw+l3HMMmtiNo9Gl61G4GVg==", - "dev": true - }, - "node_modules/@wdio/mocha-framework/node_modules/yargs": { - "version": "16.2.0", - "resolved": "https://registry.npmjs.org/yargs/-/yargs-16.2.0.tgz", - "integrity": "sha512-D1mvvtDG0L5ft/jGWkLpG1+m0eQxOfaBvTNELraWj22wSVUMWxZUvYgJYcKh6jGGIkJFhH4IZPQhR4TKpc8mBw==", + "node_modules/@wdio/cli/node_modules/parse-json": { + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-7.1.1.tgz", + "integrity": "sha512-SgOTCX/EZXtZxBE5eJ97P4yGM5n37BwRU+YMsH4vNzFqJV/oWFXXCmwFlgWUM4PrakybVOueJJ6pwHqSVhTFDw==", "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" + "@babel/code-frame": "^7.21.4", + "error-ex": "^1.3.2", + "json-parse-even-better-errors": "^3.0.0", + "lines-and-columns": "^2.0.3", + "type-fest": "^3.8.0" }, "engines": { - "node": ">=10" + "node": ">=16" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "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==", + "node_modules/@wdio/cli/node_modules/parse-json/node_modules/type-fest": { + "version": "3.13.1", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-3.13.1.tgz", + "integrity": "sha512-tLq3bSNx+xSpwvAJnzrK0Ep5CLNWjvFTOp71URMaAEWBfRb9nnJiBoUe0tF8bI4ZFO3omgBR6NvnbzVUT3Ly4g==", "dev": true, "engines": { - "node": ">=10" + "node": ">=14.16" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/@wdio/protocols": { - "version": "7.5.3", - "resolved": "https://registry.npmjs.org/@wdio/protocols/-/protocols-7.5.3.tgz", - "integrity": "sha512-lpNaKwxYhDSL6neDtQQYXvzMAw+u4PXx65ryeMEX82mkARgzSZps5Kyrg9ub7X4T17K1NPfnY6UhZEWg6cKJCg==", + "node_modules/@wdio/cli/node_modules/path-exists": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-5.0.0.tgz", + "integrity": "sha512-RjhtfwJOxzcFmNOi6ltcbcu4Iu+FL3zEj83dk4kAS+fVpTxXLO1b38RvJgT/0QwvV/L3aY9TAnyv0EOqW4GoMQ==", "dev": true, "engines": { - "node": ">=12.0.0" + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" } }, - "node_modules/@wdio/repl": { - "version": "7.5.3", - "resolved": "https://registry.npmjs.org/@wdio/repl/-/repl-7.5.3.tgz", - "integrity": "sha512-jfNJwNoc2nWdnLsFoGHmOJR9zaWfDTBMWM3W1eR5kXIjevD6gAfWsB5ZoA4IdybujCXxdnhlsm4o2jIzp/6f7A==", + "node_modules/@wdio/cli/node_modules/path-key": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-4.0.0.tgz", + "integrity": "sha512-haREypq7xkM7ErfgIyA0z+Bj4AGKlMSdlQE2jvJo6huWD1EdkKYV+G/T4nq0YEF2vgTT8kqMFKo1uHn950r4SQ==", "dev": true, - "dependencies": { - "@wdio/utils": "7.5.3" - }, "engines": { - "node": ">=12.0.0" + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/@wdio/reporter": { - "version": "7.5.7", - "resolved": "https://registry.npmjs.org/@wdio/reporter/-/reporter-7.5.7.tgz", - "integrity": "sha512-9PXqZtCXDtU6UYLNDPu9MZQ8BiABGnRlJTrlbYB3gBfZDibMkJMvwXzPderipBv2+ifDZXmGe3Njf1ao2TkbFA==", + "node_modules/@wdio/cli/node_modules/proxy-agent": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/proxy-agent/-/proxy-agent-6.3.0.tgz", + "integrity": "sha512-0LdR757eTj/JfuU7TL2YCuAZnxWXu3tkJbg4Oq3geW/qFNT/32T0sp2HnZ9O0lMR4q3vwAt0+xCA8SR0WAD0og==", "dev": true, "dependencies": { - "@wdio/types": "7.5.3", - "fs-extra": "^10.0.0" + "agent-base": "^7.0.2", + "debug": "^4.3.4", + "http-proxy-agent": "^7.0.0", + "https-proxy-agent": "^7.0.0", + "lru-cache": "^7.14.1", + "pac-proxy-agent": "^7.0.0", + "proxy-from-env": "^1.1.0", + "socks-proxy-agent": "^8.0.1" }, "engines": { - "node": ">=12.0.0" + "node": ">= 14" } }, - "node_modules/@wdio/reporter/node_modules/@wdio/types": { - "version": "7.5.3", - "resolved": "https://registry.npmjs.org/@wdio/types/-/types-7.5.3.tgz", - "integrity": "sha512-jmumhKBhNDABnpmrshYLEcdE9WoP5tmynsDNbDABlb/W8FFiLySQOejukhYIL9CLys4zXerV3/edks0SCzHOiQ==", + "node_modules/@wdio/cli/node_modules/proxy-agent/node_modules/agent-base": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-7.1.0.tgz", + "integrity": "sha512-o/zjMZRhJxny7OyEF+Op8X+efiELC7k7yOjMzgfzVqOzXqkBkWI79YoTdOtsuWd5BWhAGAuOY/Xa6xpiaWXiNg==", "dev": true, "dependencies": { - "got": "^11.8.1" + "debug": "^4.3.4" }, "engines": { - "node": ">=12.0.0" + "node": ">= 14" } }, - "node_modules/@wdio/runner": { - "version": "7.5.7", - "resolved": "https://registry.npmjs.org/@wdio/runner/-/runner-7.5.7.tgz", - "integrity": "sha512-RzVXd+xnwK/thkx1/xo9K5iscQ0Ofobgsx5dNVtwLDVMn9V7jCW/WX4dSCPAPaVSqnUCmkcQp3P5AoSBPpCZnQ==", + "node_modules/@wdio/cli/node_modules/proxy-agent/node_modules/http-proxy-agent": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/http-proxy-agent/-/http-proxy-agent-7.0.0.tgz", + "integrity": "sha512-+ZT+iBxVUQ1asugqnD6oWoRiS25AkjNfG085dKJGtGxkdwLQrMKU5wJr2bOOFAXzKcTuqq+7fZlTMgG3SRfIYQ==", "dev": true, "dependencies": { - "@wdio/config": "7.5.3", - "@wdio/logger": "7.5.3", - "@wdio/types": "7.5.3", - "@wdio/utils": "7.5.3", - "deepmerge": "^4.0.0", - "gaze": "^1.1.2", - "webdriver": "7.5.3", - "webdriverio": "7.5.7" + "agent-base": "^7.1.0", + "debug": "^4.3.4" }, "engines": { - "node": ">=12.0.0" + "node": ">= 14" } }, - "node_modules/@wdio/runner/node_modules/@types/aria-query": { - "version": "4.2.2", - "resolved": "https://registry.npmjs.org/@types/aria-query/-/aria-query-4.2.2.tgz", - "integrity": "sha512-HnYpAE1Y6kRyKM/XkEuiRQhTHvkzMBurTHnpFLYLBGPIylZNPs9jJcuOOYWxPLJCSEtmZT0Y8rHDokKN7rRTig==", - "dev": true - }, - "node_modules/@wdio/runner/node_modules/@wdio/logger": { - "version": "7.5.3", - "resolved": "https://registry.npmjs.org/@wdio/logger/-/logger-7.5.3.tgz", - "integrity": "sha512-r9EADpm0ncS1bDQSWi/nhF9C59/WNLbdAAFlo782E9ItFCpDhNit3aQP9vETv1vFxJRjUIM8Fw/HW8zwPadkbw==", + "node_modules/@wdio/cli/node_modules/proxy-agent/node_modules/https-proxy-agent": { + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-7.0.2.tgz", + "integrity": "sha512-NmLNjm6ucYwtcUmL7JQC1ZQ57LmHP4lT15FQ8D61nak1rO6DH+fz5qNK2Ap5UN4ZapYICE3/0KodcLYSPsPbaA==", "dev": true, "dependencies": { - "chalk": "^4.0.0", - "loglevel": "^1.6.0", - "loglevel-plugin-prefix": "^0.8.4", - "strip-ansi": "^6.0.0" + "agent-base": "^7.0.2", + "debug": "4" }, "engines": { - "node": ">=12.0.0" + "node": ">= 14" } }, - "node_modules/@wdio/runner/node_modules/@wdio/types": { - "version": "7.5.3", - "resolved": "https://registry.npmjs.org/@wdio/types/-/types-7.5.3.tgz", - "integrity": "sha512-jmumhKBhNDABnpmrshYLEcdE9WoP5tmynsDNbDABlb/W8FFiLySQOejukhYIL9CLys4zXerV3/edks0SCzHOiQ==", + "node_modules/@wdio/cli/node_modules/proxy-agent/node_modules/lru-cache": { + "version": "7.18.3", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-7.18.3.tgz", + "integrity": "sha512-jumlc0BIUrS3qJGgIkWZsyfAM7NCWiBcCDhnd+3NNM5KbBmLTgHVfWBcg6W+rLUsIpzpERPsvwUP7CckAQSOoA==", "dev": true, - "dependencies": { - "got": "^11.8.1" - }, "engines": { - "node": ">=12.0.0" + "node": ">=12" } }, - "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==", + "node_modules/@wdio/cli/node_modules/puppeteer-core": { + "version": "20.3.0", + "resolved": "https://registry.npmjs.org/puppeteer-core/-/puppeteer-core-20.3.0.tgz", + "integrity": "sha512-264pBrIui5bO6NJeOcbJrLa0OCwmA4+WK00JMrLIKTfRiqe2gx8KWTzLsjyw/bizErp3TKS7vt/I0i5fTC+mAw==", "dev": true, + "optional": true, + "peer": true, "dependencies": { - "color-convert": "^2.0.1" + "@puppeteer/browsers": "1.3.0", + "chromium-bidi": "0.4.9", + "cross-fetch": "3.1.6", + "debug": "4.3.4", + "devtools-protocol": "0.0.1120988", + "ws": "8.13.0" }, "engines": { - "node": ">=8" + "node": ">=16.0.0" }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" + "peerDependencies": { + "typescript": ">= 4.7.4" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } } }, - "node_modules/@wdio/runner/node_modules/aria-query": { - "version": "4.2.2", - "resolved": "https://registry.npmjs.org/aria-query/-/aria-query-4.2.2.tgz", - "integrity": "sha512-o/HelwhuKpTj/frsOsbNLNgnNGVIFsVP/SW2BSF14gVl7kAfMOJ6/8wUAUvG1R1NHKrfG+2sHZTu0yauT1qBrA==", + "node_modules/@wdio/cli/node_modules/read-pkg": { + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-8.1.0.tgz", + "integrity": "sha512-PORM8AgzXeskHO/WEv312k9U03B8K9JSiWF/8N9sUuFjBa+9SF2u6K7VClzXwDXab51jCd8Nd36CNM+zR97ScQ==", "dev": true, "dependencies": { - "@babel/runtime": "^7.10.2", - "@babel/runtime-corejs3": "^7.10.2" + "@types/normalize-package-data": "^2.4.1", + "normalize-package-data": "^6.0.0", + "parse-json": "^7.0.0", + "type-fest": "^4.2.0" }, "engines": { - "node": ">=6.0" + "node": ">=16" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/@wdio/runner/node_modules/chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "node_modules/@wdio/cli/node_modules/read-pkg-up": { + "version": "10.1.0", + "resolved": "https://registry.npmjs.org/read-pkg-up/-/read-pkg-up-10.1.0.tgz", + "integrity": "sha512-aNtBq4jR8NawpKJQldrQcSW9y/d+KWH4v24HWkHljOZ7H0av+YTGANBzRh9A5pw7v/bLVsLVPpOhJ7gHNVy8lA==", "dev": true, "dependencies": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" + "find-up": "^6.3.0", + "read-pkg": "^8.1.0", + "type-fest": "^4.2.0" }, "engines": { - "node": ">=10" + "node": ">=16" }, "funding": { - "url": "https://github.com/chalk/chalk?sponsor=1" + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/@wdio/runner/node_modules/chrome-launcher": { - "version": "0.13.4", - "resolved": "https://registry.npmjs.org/chrome-launcher/-/chrome-launcher-0.13.4.tgz", - "integrity": "sha512-nnzXiDbGKjDSK6t2I+35OAPBy5Pw/39bgkb/ZAFwMhwJbdYBp6aH+vW28ZgtjdU890Q7D+3wN/tB8N66q5Gi2A==", + "node_modules/@wdio/cli/node_modules/readable-stream": { + "version": "3.6.2", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz", + "integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==", "dev": true, "dependencies": { - "@types/node": "*", - "escape-string-regexp": "^1.0.5", - "is-wsl": "^2.2.0", - "lighthouse-logger": "^1.0.0", - "mkdirp": "^0.5.3", - "rimraf": "^3.0.2" + "inherits": "^2.0.3", + "string_decoder": "^1.1.1", + "util-deprecate": "^1.0.1" + }, + "engines": { + "node": ">= 6" } }, - "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==", + "node_modules/@wdio/cli/node_modules/semver": { + "version": "7.5.4", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.4.tgz", + "integrity": "sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA==", "dev": true, "dependencies": { - "color-name": "~1.1.4" + "lru-cache": "^6.0.0" + }, + "bin": { + "semver": "bin/semver.js" }, "engines": { - "node": ">=7.0.0" + "node": ">=10" } }, - "node_modules/@wdio/runner/node_modules/color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", - "dev": true - }, - "node_modules/@wdio/runner/node_modules/devtools": { - "version": "7.5.7", - "resolved": "https://registry.npmjs.org/devtools/-/devtools-7.5.7.tgz", - "integrity": "sha512-+kqmvFbceElhYpN35yjm1T4Rz3VbH0QaqrNWKRpeyFp657Y5W0bm1s5FyMUeIv0aTNkAgWcETtqL+EG9X9uvjQ==", + "node_modules/@wdio/cli/node_modules/serialize-error": { + "version": "11.0.3", + "resolved": "https://registry.npmjs.org/serialize-error/-/serialize-error-11.0.3.tgz", + "integrity": "sha512-2G2y++21dhj2R7iHAdd0FIzjGwuKZld+7Pl/bTU6YIkrC2ZMbVUjm+luj6A6V34Rv9XfKJDKpTWu9W4Gse1D9g==", "dev": true, "dependencies": { - "@wdio/config": "7.5.3", - "@wdio/logger": "7.5.3", - "@wdio/protocols": "7.5.3", - "@wdio/types": "7.5.3", - "@wdio/utils": "7.5.3", - "chrome-launcher": "^0.13.1", - "edge-paths": "^2.1.0", - "puppeteer-core": "^9.1.0", - "query-selector-shadow-dom": "^1.0.0", - "ua-parser-js": "^0.7.21", - "uuid": "^8.0.0" + "type-fest": "^2.12.2" }, "engines": { - "node": ">=12.0.0" + "node": ">=14.16" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/@wdio/runner/node_modules/devtools-protocol": { - "version": "0.0.878340", - "resolved": "https://registry.npmjs.org/devtools-protocol/-/devtools-protocol-0.0.878340.tgz", - "integrity": "sha512-W0q8Y02r1RNwfZtI4Jjh1/MZxRHyrIgy9FvElbJzQelZjmNH197H4mBQs7DZjlUUDA9s6Zz2jl+zUYFgLgEnzw==", - "dev": true - }, - "node_modules/@wdio/runner/node_modules/has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "node_modules/@wdio/cli/node_modules/serialize-error/node_modules/type-fest": { + "version": "2.19.0", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-2.19.0.tgz", + "integrity": "sha512-RAH822pAdBgcNMAfWnCBU3CFZcfZ/i1eZjwFU/dsLKumyuuP3niueg2UAukXYF0E2AAoc82ZSSf9J0WQBinzHA==", "dev": true, "engines": { - "node": ">=8" + "node": ">=12.20" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/@wdio/runner/node_modules/mkdirp": { - "version": "0.5.6", - "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.6.tgz", - "integrity": "sha512-FP+p8RB8OWpF3YZBCrP5gtADmtXApB5AMLn+vdyA+PyxCjrCs00mjyUozssO33cwDeT3wNGdLxJ5M//YqtHAJw==", + "node_modules/@wdio/cli/node_modules/signal-exit": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-4.1.0.tgz", + "integrity": "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==", "dev": true, - "dependencies": { - "minimist": "^1.2.6" + "engines": { + "node": ">=14" }, - "bin": { - "mkdirp": "bin/cmd.js" + "funding": { + "url": "https://github.com/sponsors/isaacs" } }, - "node_modules/@wdio/runner/node_modules/puppeteer-core": { - "version": "9.1.1", - "resolved": "https://registry.npmjs.org/puppeteer-core/-/puppeteer-core-9.1.1.tgz", - "integrity": "sha512-zbedbitVIGhmgz0nt7eIdLsnaoVZSlNJfBivqm2w67T8LR2bU1dvnruDZ8nQO0zn++Iet7zHbAOdnuS5+H2E7A==", + "node_modules/@wdio/cli/node_modules/tar-stream": { + "version": "3.1.7", + "resolved": "https://registry.npmjs.org/tar-stream/-/tar-stream-3.1.7.tgz", + "integrity": "sha512-qJj60CXt7IU1Ffyc3NJMjh6EkuCFej46zUqJ4J7pqYlThyd9bO0XBTmcOIhSzZJVWfsLks0+nle/j538YAW9RQ==", "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" + "b4a": "^1.6.4", + "fast-fifo": "^1.2.0", + "streamx": "^2.15.0" } }, - "node_modules/@wdio/runner/node_modules/puppeteer-core/node_modules/devtools-protocol": { - "version": "0.0.869402", - "resolved": "https://registry.npmjs.org/devtools-protocol/-/devtools-protocol-0.0.869402.tgz", - "integrity": "sha512-VvlVYY+VDJe639yHs5PHISzdWTLL3Aw8rO4cvUtwvoxFd6FHbE4OpHHcde52M6096uYYazAmd4l0o5VuFRO2WA==", - "dev": true - }, - "node_modules/@wdio/runner/node_modules/supports-color": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "node_modules/@wdio/cli/node_modules/type-fest": { + "version": "4.10.1", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-4.10.1.tgz", + "integrity": "sha512-7ZnJYTp6uc04uYRISWtiX3DSKB/fxNQT0B5o1OUeCqiQiwF+JC9+rJiZIDrPrNCLLuTqyQmh4VdQqh/ZOkv9MQ==", "dev": true, - "dependencies": { - "has-flag": "^4.0.0" + "engines": { + "node": ">=16" }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@wdio/cli/node_modules/ua-parser-js": { + "version": "1.0.37", + "resolved": "https://registry.npmjs.org/ua-parser-js/-/ua-parser-js-1.0.37.tgz", + "integrity": "sha512-bhTyI94tZofjo+Dn8SN6Zv8nBDvyXTymAdM3LDI/0IboIUwTu1rEhW7v2TfiVsoYWgkQ4kOVqnI8APUFbIQIFQ==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/ua-parser-js" + }, + { + "type": "paypal", + "url": "https://paypal.me/faisalman" + }, + { + "type": "github", + "url": "https://github.com/sponsors/faisalman" + } + ], + "optional": true, + "peer": true, "engines": { - "node": ">=8" + "node": "*" } }, - "node_modules/@wdio/runner/node_modules/uuid": { - "version": "8.3.2", - "resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz", - "integrity": "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==", + "node_modules/@wdio/cli/node_modules/uuid": { + "version": "9.0.1", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-9.0.1.tgz", + "integrity": "sha512-b+1eJOlsR9K8HJpow9Ok3fiWOWSIcIzXodvv0rQjVoOVNpWMpxf1wZNpt4y9h10odCNrqnYp1OBzRktckBe3sA==", "dev": true, + "funding": [ + "https://github.com/sponsors/broofa", + "https://github.com/sponsors/ctavan" + ], + "optional": true, + "peer": true, "bin": { "uuid": "dist/bin/uuid" } }, - "node_modules/@wdio/runner/node_modules/webdriverio": { - "version": "7.5.7", - "resolved": "https://registry.npmjs.org/webdriverio/-/webdriverio-7.5.7.tgz", - "integrity": "sha512-TLluVPLo6Snn/dxEITvMz7ZuklN4qZOBddDuLb9LO3rhsfKDMNbnhcBk0SLdFsWny0aCuhWNpJ6co93702XC0A==", - "dev": true, - "dependencies": { - "@types/aria-query": "^4.2.1", - "@wdio/config": "7.5.3", - "@wdio/logger": "7.5.3", - "@wdio/protocols": "7.5.3", - "@wdio/repl": "7.5.3", - "@wdio/types": "7.5.3", - "@wdio/utils": "7.5.3", - "archiver": "^5.0.0", - "aria-query": "^4.2.2", - "atob": "^2.1.2", + "node_modules/@wdio/cli/node_modules/webdriverio": { + "version": "8.29.1", + "resolved": "https://registry.npmjs.org/webdriverio/-/webdriverio-8.29.1.tgz", + "integrity": "sha512-NZK95ivXCqdPraB3FHMw6ByxnCvtgFXkjzG2l3Oq5z0IuJS2aMow3AKFIyiuG6is/deGCe+Tb8eFTCqak7UV+w==", + "dev": true, + "dependencies": { + "@types/node": "^20.1.0", + "@wdio/config": "8.29.1", + "@wdio/logger": "8.28.0", + "@wdio/protocols": "8.24.12", + "@wdio/repl": "8.24.12", + "@wdio/types": "8.29.1", + "@wdio/utils": "8.29.1", + "archiver": "^6.0.0", + "aria-query": "^5.0.0", "css-shorthand-properties": "^1.1.1", "css-value": "^0.0.1", - "devtools": "7.5.7", - "devtools-protocol": "^0.0.878340", - "fs-extra": "^10.0.0", - "get-port": "^5.1.1", + "devtools-protocol": "^0.0.1249869", "grapheme-splitter": "^1.0.2", + "import-meta-resolve": "^4.0.0", + "is-plain-obj": "^4.1.0", "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", + "minimatch": "^9.0.0", + "puppeteer-core": "^20.9.0", "query-selector-shadow-dom": "^1.0.0", "resq": "^1.9.1", "rgb2hex": "0.2.5", - "serialize-error": "^8.0.0", - "webdriver": "7.5.3" + "serialize-error": "^11.0.1", + "webdriver": "8.29.1" }, "engines": { - "node": ">=12.0.0" + "node": "^16.13 || >=18" + }, + "peerDependencies": { + "devtools": "^8.14.0" + }, + "peerDependenciesMeta": { + "devtools": { + "optional": true + } } }, - "node_modules/@wdio/runner/node_modules/ws": { - "version": "7.5.9", - "resolved": "https://registry.npmjs.org/ws/-/ws-7.5.9.tgz", - "integrity": "sha512-F+P9Jil7UiSKSkppIiD94dN07AwvFixvLIj1Og1Rl9GGMuNipJnV9JzjD6XuqmAeiswGvUmNLjr5cFuXwNS77Q==", + "node_modules/@wdio/cli/node_modules/webdriverio/node_modules/@puppeteer/browsers": { + "version": "1.4.6", + "resolved": "https://registry.npmjs.org/@puppeteer/browsers/-/browsers-1.4.6.tgz", + "integrity": "sha512-x4BEjr2SjOPowNeiguzjozQbsc6h437ovD/wu+JpaenxVLm3jkgzHY2xOslMTp50HoTvQreMjiexiGQw1sqZlQ==", "dev": true, + "dependencies": { + "debug": "4.3.4", + "extract-zip": "2.0.1", + "progress": "2.0.3", + "proxy-agent": "6.3.0", + "tar-fs": "3.0.4", + "unbzip2-stream": "1.4.3", + "yargs": "17.7.1" + }, + "bin": { + "browsers": "lib/cjs/main-cli.js" + }, "engines": { - "node": ">=8.3.0" + "node": ">=16.3.0" }, "peerDependencies": { - "bufferutil": "^4.0.1", - "utf-8-validate": "^5.0.2" + "typescript": ">= 4.7.4" }, "peerDependenciesMeta": { - "bufferutil": { - "optional": true - }, - "utf-8-validate": { + "typescript": { "optional": true } } }, - "node_modules/@wdio/spec-reporter": { - "version": "7.19.7", - "resolved": "https://registry.npmjs.org/@wdio/spec-reporter/-/spec-reporter-7.19.7.tgz", - "integrity": "sha512-BDBZU2EK/GuC9VxtfqPtoW43FmvKxYDsvcDVDi3F7o+9fkcuGSJiWbw1AX251ZzzVQ7YP9ImTitSpdpUKXkilQ==", + "node_modules/@wdio/cli/node_modules/webdriverio/node_modules/chromium-bidi": { + "version": "0.4.16", + "resolved": "https://registry.npmjs.org/chromium-bidi/-/chromium-bidi-0.4.16.tgz", + "integrity": "sha512-7ZbXdWERxRxSwo3txsBjjmc/NLxqb1Bk30mRb0BMS4YIaiV6zvKZqL/UAH+DdqcDYayDWk2n/y8klkBDODrPvA==", "dev": true, "dependencies": { - "@types/easy-table": "^0.0.33", - "@wdio/reporter": "7.19.7", - "@wdio/types": "7.19.5", - "chalk": "^4.0.0", - "easy-table": "^1.1.1", - "pretty-ms": "^7.0.0" - }, - "engines": { - "node": ">=12.0.0" + "mitt": "3.0.0" }, "peerDependencies": { - "@wdio/cli": "^7.0.0" + "devtools-protocol": "*" } }, - "node_modules/@wdio/spec-reporter/node_modules/@wdio/reporter": { - "version": "7.19.7", - "resolved": "https://registry.npmjs.org/@wdio/reporter/-/reporter-7.19.7.tgz", - "integrity": "sha512-Dum19gpfru66FnIq78/4HTuW87B7ceLDp6PJXwQM5kXyN7Gb7zhMgp6FZTM0FCYLyi6U/zXZSvpNUYl77caS6g==", + "node_modules/@wdio/cli/node_modules/webdriverio/node_modules/cross-fetch": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/cross-fetch/-/cross-fetch-4.0.0.tgz", + "integrity": "sha512-e4a5N8lVvuLgAWgnCrLr2PP0YyDOTHa9H/Rj54dirp61qXnNq46m82bRhNqIA5VccJtWBvPTFRV3TtvHUKPB1g==", "dev": true, "dependencies": { - "@types/diff": "^5.0.0", - "@types/node": "^17.0.4", - "@types/object-inspect": "^1.8.0", - "@types/supports-color": "^8.1.0", - "@types/tmp": "^0.2.0", - "@wdio/types": "7.19.5", - "diff": "^5.0.0", - "fs-extra": "^10.0.0", - "object-inspect": "^1.10.3", - "supports-color": "8.1.1" - }, - "engines": { - "node": ">=12.0.0" + "node-fetch": "^2.6.12" } }, - "node_modules/@wdio/spec-reporter/node_modules/@wdio/types": { - "version": "7.19.5", - "resolved": "https://registry.npmjs.org/@wdio/types/-/types-7.19.5.tgz", - "integrity": "sha512-S1lC0pmtEO7NVH/2nM1c7NHbkgxLZH3VVG/z6ym3Bbxdtcqi2LMsEvvawMAU/fmhyiIkMsGZCO8vxG9cRw4z4A==", + "node_modules/@wdio/cli/node_modules/webdriverio/node_modules/devtools-protocol": { + "version": "0.0.1249869", + "resolved": "https://registry.npmjs.org/devtools-protocol/-/devtools-protocol-0.0.1249869.tgz", + "integrity": "sha512-Ctp4hInA0BEavlUoRy9mhGq0i+JSo/AwVyX2EFgZmV1kYB+Zq+EMBAn52QWu6FbRr10hRb6pBl420upbp4++vg==", + "dev": true + }, + "node_modules/@wdio/cli/node_modules/webdriverio/node_modules/puppeteer-core": { + "version": "20.9.0", + "resolved": "https://registry.npmjs.org/puppeteer-core/-/puppeteer-core-20.9.0.tgz", + "integrity": "sha512-H9fYZQzMTRrkboEfPmf7m3CLDN6JvbxXA3qTtS+dFt27tR+CsFHzPsT6pzp6lYL6bJbAPaR0HaPO6uSi+F94Pg==", "dev": true, "dependencies": { - "@types/node": "^17.0.4", - "got": "^11.8.1" + "@puppeteer/browsers": "1.4.6", + "chromium-bidi": "0.4.16", + "cross-fetch": "4.0.0", + "debug": "4.3.4", + "devtools-protocol": "0.0.1147663", + "ws": "8.13.0" }, "engines": { - "node": ">=12.0.0" + "node": ">=16.3.0" }, "peerDependencies": { - "typescript": "^4.6.2" + "typescript": ">= 4.7.4" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } } }, - "node_modules/@wdio/spec-reporter/node_modules/ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "node_modules/@wdio/cli/node_modules/webdriverio/node_modules/puppeteer-core/node_modules/devtools-protocol": { + "version": "0.0.1147663", + "resolved": "https://registry.npmjs.org/devtools-protocol/-/devtools-protocol-0.0.1147663.tgz", + "integrity": "sha512-hyWmRrexdhbZ1tcJUGpO95ivbRhWXz++F4Ko+n21AY5PNln2ovoJw+8ZMNDTtip+CNFQfrtLVh/w4009dXO/eQ==", + "dev": true + }, + "node_modules/@wdio/cli/node_modules/webdriverio/node_modules/tar-fs": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/tar-fs/-/tar-fs-3.0.4.tgz", + "integrity": "sha512-5AFQU8b9qLfZCX9zp2duONhPmZv0hGYiBPJsyUdqMjzq/mqVpy/rEUSeHk1+YitmxugaptgBh5oDGU3VsAJq4w==", "dev": true, "dependencies": { - "color-convert": "^2.0.1" + "mkdirp-classic": "^0.5.2", + "pump": "^3.0.0", + "tar-stream": "^3.1.5" + } + }, + "node_modules/@wdio/cli/node_modules/webdriverio/node_modules/yargs": { + "version": "17.7.1", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.7.1.tgz", + "integrity": "sha512-cwiTb08Xuv5fqF4AovYacTFNxk62th7LKJ6BL9IGUpTJrWoU7/7WdQGTP2SjKf1dUNBGzDd28p/Yfs/GI6JrLw==", + "dev": true, + "dependencies": { + "cliui": "^8.0.1", + "escalade": "^3.1.1", + "get-caller-file": "^2.0.5", + "require-directory": "^2.1.1", + "string-width": "^4.2.3", + "y18n": "^5.0.5", + "yargs-parser": "^21.1.1" }, "engines": { - "node": ">=8" + "node": ">=12" + } + }, + "node_modules/@wdio/cli/node_modules/ws": { + "version": "8.13.0", + "resolved": "https://registry.npmjs.org/ws/-/ws-8.13.0.tgz", + "integrity": "sha512-x9vcZYTrFPC7aSIbj7sRCYo7L/Xb8Iy+pW0ng0wt2vCJv7M9HOMy0UoN3rr+IFC7hb7vXoqS+P9ktyLLLhO+LA==", + "dev": true, + "engines": { + "node": ">=10.0.0" }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" + "peerDependencies": { + "bufferutil": "^4.0.1", + "utf-8-validate": ">=5.0.2" + }, + "peerDependenciesMeta": { + "bufferutil": { + "optional": true + }, + "utf-8-validate": { + "optional": true + } } }, - "node_modules/@wdio/spec-reporter/node_modules/chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "node_modules/@wdio/cli/node_modules/yargs": { + "version": "17.7.2", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.7.2.tgz", + "integrity": "sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==", "dev": true, "dependencies": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" + "cliui": "^8.0.1", + "escalade": "^3.1.1", + "get-caller-file": "^2.0.5", + "require-directory": "^2.1.1", + "string-width": "^4.2.3", + "y18n": "^5.0.5", + "yargs-parser": "^21.1.1" }, "engines": { - "node": ">=10" + "node": ">=12" + } + }, + "node_modules/@wdio/cli/node_modules/yocto-queue": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-1.0.0.tgz", + "integrity": "sha512-9bnSc/HEW2uRy67wc+T8UwauLuPJVn28jb+GtJY16iiKWyvmYJRXVT4UamsAEGQfPohgr2q4Tq0sQbQlxTfi1g==", + "dev": true, + "engines": { + "node": ">=12.20" }, "funding": { - "url": "https://github.com/chalk/chalk?sponsor=1" + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/@wdio/spec-reporter/node_modules/chalk/node_modules/supports-color": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "node_modules/@wdio/cli/node_modules/zip-stream": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/zip-stream/-/zip-stream-5.0.1.tgz", + "integrity": "sha512-UfZ0oa0C8LI58wJ+moL46BDIMgCQbnsb+2PoiJYtonhBsMh2bq1eRBVkvjfVsqbEHd9/EgKPUuL9saSSsec8OA==", "dev": true, "dependencies": { - "has-flag": "^4.0.0" + "archiver-utils": "^4.0.1", + "compress-commons": "^5.0.1", + "readable-stream": "^3.6.0" }, "engines": { - "node": ">=8" + "node": ">= 12.0.0" } }, - "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==", + "node_modules/@wdio/concise-reporter": { + "version": "8.29.1", + "resolved": "https://registry.npmjs.org/@wdio/concise-reporter/-/concise-reporter-8.29.1.tgz", + "integrity": "sha512-dUhClWeq1naL1Qa1nSMDeH8aCVViOKiEzhBhQjgrMOz1Mh3l6O/woqbK2iKDVZDRhfGghtGcV0vpoEUvt8ZKOA==", "dev": true, "dependencies": { - "color-name": "~1.1.4" + "@wdio/reporter": "8.29.1", + "@wdio/types": "8.29.1", + "chalk": "^5.0.1", + "pretty-ms": "^7.0.1" }, "engines": { - "node": ">=7.0.0" + "node": "^16.13 || >=18" } }, - "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==", + "node_modules/@wdio/concise-reporter/node_modules/chalk": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-5.3.0.tgz", + "integrity": "sha512-dLitG79d+GV1Nb/VYcCDFivJeK1hiukt9QjRNVOsUtTy1rR1YJsmpGGTZ3qJos+uw7WmWF4wUwBd9jxjocFC2w==", "dev": true, "engines": { - "node": ">=8" + "node": "^12.17.0 || ^14.13 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" } }, - "node_modules/@wdio/spec-reporter/node_modules/supports-color": { - "version": "8.1.1", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz", - "integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==", + "node_modules/@wdio/config": { + "version": "8.29.1", + "resolved": "https://registry.npmjs.org/@wdio/config/-/config-8.29.1.tgz", + "integrity": "sha512-zNUac4lM429HDKAitO+fdlwUH1ACQU8lww+DNVgUyuEb86xgVdTqHeiJr/3kOMJAq9IATeE7mDtYyyn6HPm1JA==", "dev": true, "dependencies": { - "has-flag": "^4.0.0" + "@wdio/logger": "8.28.0", + "@wdio/types": "8.29.1", + "@wdio/utils": "8.29.1", + "decamelize": "^6.0.0", + "deepmerge-ts": "^5.0.0", + "glob": "^10.2.2", + "import-meta-resolve": "^4.0.0" }, "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/supports-color?sponsor=1" + "node": "^16.13 || >=18" } }, - "node_modules/@wdio/sync": { - "version": "7.5.7", - "resolved": "https://registry.npmjs.org/@wdio/sync/-/sync-7.5.7.tgz", - "integrity": "sha512-Zu/AYLjwqbFSbaOU1US7ownv3ov8JrtoGHq51JfJ4masefJDXNkHix2cZ0qEgl3IvkkWQ0ewL0G8GTXb3KOemA==", + "node_modules/@wdio/config/node_modules/brace-expansion": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", + "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", "dev": true, "dependencies": { - "@types/fibers": "^3.1.0", - "@types/puppeteer": "^5.4.0", - "@wdio/logger": "7.5.3", - "@wdio/types": "7.5.3", - "fibers": "^5.0.0", - "webdriverio": "7.5.7" - }, - "engines": { - "node": ">=12.0.0" + "balanced-match": "^1.0.0" } }, - "node_modules/@wdio/sync/node_modules/@types/aria-query": { - "version": "4.2.2", - "resolved": "https://registry.npmjs.org/@types/aria-query/-/aria-query-4.2.2.tgz", - "integrity": "sha512-HnYpAE1Y6kRyKM/XkEuiRQhTHvkzMBurTHnpFLYLBGPIylZNPs9jJcuOOYWxPLJCSEtmZT0Y8rHDokKN7rRTig==", - "dev": true - }, - "node_modules/@wdio/sync/node_modules/@wdio/logger": { - "version": "7.5.3", - "resolved": "https://registry.npmjs.org/@wdio/logger/-/logger-7.5.3.tgz", - "integrity": "sha512-r9EADpm0ncS1bDQSWi/nhF9C59/WNLbdAAFlo782E9ItFCpDhNit3aQP9vETv1vFxJRjUIM8Fw/HW8zwPadkbw==", + "node_modules/@wdio/config/node_modules/decamelize": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-6.0.0.tgz", + "integrity": "sha512-Fv96DCsdOgB6mdGl67MT5JaTNKRzrzill5OH5s8bjYJXVlcXyPYGyPsUkWyGV5p1TXI5esYIYMMeDJL0hEIwaA==", "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": "^12.20.0 || ^14.13.1 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/@wdio/sync/node_modules/@wdio/types": { - "version": "7.5.3", - "resolved": "https://registry.npmjs.org/@wdio/types/-/types-7.5.3.tgz", - "integrity": "sha512-jmumhKBhNDABnpmrshYLEcdE9WoP5tmynsDNbDABlb/W8FFiLySQOejukhYIL9CLys4zXerV3/edks0SCzHOiQ==", + "node_modules/@wdio/config/node_modules/glob": { + "version": "10.3.10", + "resolved": "https://registry.npmjs.org/glob/-/glob-10.3.10.tgz", + "integrity": "sha512-fa46+tv1Ak0UPK1TOy/pZrIybNNt4HCv7SDzwyfiOZkvZLEbjsZkJBPtDHVshZjbecAoAGSC20MjLDG/qr679g==", "dev": true, "dependencies": { - "got": "^11.8.1" + "foreground-child": "^3.1.0", + "jackspeak": "^2.3.5", + "minimatch": "^9.0.1", + "minipass": "^5.0.0 || ^6.0.2 || ^7.0.0", + "path-scurry": "^1.10.1" + }, + "bin": { + "glob": "dist/esm/bin.mjs" }, "engines": { - "node": ">=12.0.0" + "node": ">=16 || 14 >=14.17" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" } }, - "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==", + "node_modules/@wdio/config/node_modules/minimatch": { + "version": "9.0.3", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.3.tgz", + "integrity": "sha512-RHiac9mvaRw0x3AYRgDC1CxAP7HTcNrrECeA8YYJeWnpo+2Q5CegtZjaotWTWxDG3UeGA1coE05iH1mPjT/2mg==", "dev": true, "dependencies": { - "color-convert": "^2.0.1" + "brace-expansion": "^2.0.1" }, "engines": { - "node": ">=8" + "node": ">=16 || 14 >=14.17" }, "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" + "url": "https://github.com/sponsors/isaacs" } }, - "node_modules/@wdio/sync/node_modules/aria-query": { - "version": "4.2.2", - "resolved": "https://registry.npmjs.org/aria-query/-/aria-query-4.2.2.tgz", - "integrity": "sha512-o/HelwhuKpTj/frsOsbNLNgnNGVIFsVP/SW2BSF14gVl7kAfMOJ6/8wUAUvG1R1NHKrfG+2sHZTu0yauT1qBrA==", + "node_modules/@wdio/globals": { + "version": "8.29.1", + "resolved": "https://registry.npmjs.org/@wdio/globals/-/globals-8.29.1.tgz", + "integrity": "sha512-F+fPnX75f44/crZDfQ2FYSino/IMIdbnQGLIkaH0VnoljVJIHuxnX4y5Zqr4yRgurL9DsZaH22cLHrPXaHUhPg==", "dev": true, - "dependencies": { - "@babel/runtime": "^7.10.2", - "@babel/runtime-corejs3": "^7.10.2" - }, "engines": { - "node": ">=6.0" + "node": "^16.13 || >=18" + }, + "optionalDependencies": { + "expect-webdriverio": "^4.9.3", + "webdriverio": "8.29.1" } }, - "node_modules/@wdio/sync/node_modules/chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "node_modules/@wdio/globals/node_modules/@puppeteer/browsers": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/@puppeteer/browsers/-/browsers-1.3.0.tgz", + "integrity": "sha512-an3QdbNPkuU6qpxpbssxAbjRLJcF+eP4L8UqIY3+6n0sbaVxw5pz7PiCLy9g32XEZuoamUlV5ZQPnA6FxvkIHA==", "dev": true, + "optional": true, + "peer": true, "dependencies": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" + "debug": "4.3.4", + "extract-zip": "2.0.1", + "http-proxy-agent": "5.0.0", + "https-proxy-agent": "5.0.1", + "progress": "2.0.3", + "proxy-from-env": "1.1.0", + "tar-fs": "2.1.1", + "unbzip2-stream": "1.4.3", + "yargs": "17.7.1" + }, + "bin": { + "browsers": "lib/cjs/main-cli.js" }, "engines": { - "node": ">=10" + "node": ">=16.0.0" }, - "funding": { - "url": "https://github.com/chalk/chalk?sponsor=1" + "peerDependencies": { + "typescript": ">= 4.7.4" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } } }, - "node_modules/@wdio/sync/node_modules/chrome-launcher": { - "version": "0.13.4", - "resolved": "https://registry.npmjs.org/chrome-launcher/-/chrome-launcher-0.13.4.tgz", - "integrity": "sha512-nnzXiDbGKjDSK6t2I+35OAPBy5Pw/39bgkb/ZAFwMhwJbdYBp6aH+vW28ZgtjdU890Q7D+3wN/tB8N66q5Gi2A==", + "node_modules/@wdio/globals/node_modules/@types/which": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/@types/which/-/which-2.0.2.tgz", + "integrity": "sha512-113D3mDkZDjo+EeUEHCFy0qniNc1ZpecGiAU7WSo7YDoSzolZIQKpYFHrPpjkB2nuyahcKfrmLXeQlh7gqJYdw==", "dev": true, - "dependencies": { - "@types/node": "*", - "escape-string-regexp": "^1.0.5", - "is-wsl": "^2.2.0", - "lighthouse-logger": "^1.0.0", - "mkdirp": "^0.5.3", - "rimraf": "^3.0.2" - } + "optional": true, + "peer": true }, - "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==", + "node_modules/@wdio/globals/node_modules/archiver": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/archiver/-/archiver-6.0.1.tgz", + "integrity": "sha512-CXGy4poOLBKptiZH//VlWdFuUC1RESbdZjGjILwBuZ73P7WkAUN0htfSfBq/7k6FRFlpu7bg4JOkj1vU9G6jcQ==", "dev": true, + "optional": true, "dependencies": { - "color-name": "~1.1.4" + "archiver-utils": "^4.0.1", + "async": "^3.2.4", + "buffer-crc32": "^0.2.1", + "readable-stream": "^3.6.0", + "readdir-glob": "^1.1.2", + "tar-stream": "^3.0.0", + "zip-stream": "^5.0.1" }, "engines": { - "node": ">=7.0.0" + "node": ">= 12.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/devtools": { - "version": "7.5.7", - "resolved": "https://registry.npmjs.org/devtools/-/devtools-7.5.7.tgz", - "integrity": "sha512-+kqmvFbceElhYpN35yjm1T4Rz3VbH0QaqrNWKRpeyFp657Y5W0bm1s5FyMUeIv0aTNkAgWcETtqL+EG9X9uvjQ==", + "node_modules/@wdio/globals/node_modules/archiver-utils": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/archiver-utils/-/archiver-utils-4.0.1.tgz", + "integrity": "sha512-Q4Q99idbvzmgCTEAAhi32BkOyq8iVI5EwdO0PmBDSGIzzjYNdcFn7Q7k3OzbLy4kLUPXfJtG6fO2RjftXbobBg==", "dev": true, + "optional": true, "dependencies": { - "@wdio/config": "7.5.3", - "@wdio/logger": "7.5.3", - "@wdio/protocols": "7.5.3", - "@wdio/types": "7.5.3", - "@wdio/utils": "7.5.3", - "chrome-launcher": "^0.13.1", - "edge-paths": "^2.1.0", - "puppeteer-core": "^9.1.0", - "query-selector-shadow-dom": "^1.0.0", - "ua-parser-js": "^0.7.21", - "uuid": "^8.0.0" + "glob": "^8.0.0", + "graceful-fs": "^4.2.0", + "lazystream": "^1.0.0", + "lodash": "^4.17.15", + "normalize-path": "^3.0.0", + "readable-stream": "^3.6.0" }, "engines": { - "node": ">=12.0.0" + "node": ">= 12.0.0" } }, - "node_modules/@wdio/sync/node_modules/devtools-protocol": { - "version": "0.0.878340", - "resolved": "https://registry.npmjs.org/devtools-protocol/-/devtools-protocol-0.0.878340.tgz", - "integrity": "sha512-W0q8Y02r1RNwfZtI4Jjh1/MZxRHyrIgy9FvElbJzQelZjmNH197H4mBQs7DZjlUUDA9s6Zz2jl+zUYFgLgEnzw==", - "dev": true + "node_modules/@wdio/globals/node_modules/async": { + "version": "3.2.5", + "resolved": "https://registry.npmjs.org/async/-/async-3.2.5.tgz", + "integrity": "sha512-baNZyqaaLhyLVKm/DlvdW051MSgO6b8eVfIezl9E5PqWxFgzLm/wQntEW4zOytVburDEr0JlALEpdOFwvErLsg==", + "dev": true, + "optional": 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==", + "node_modules/@wdio/globals/node_modules/brace-expansion": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", + "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", "dev": true, - "engines": { - "node": ">=8" + "optional": true, + "dependencies": { + "balanced-match": "^1.0.0" } }, - "node_modules/@wdio/sync/node_modules/mkdirp": { - "version": "0.5.6", - "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.6.tgz", - "integrity": "sha512-FP+p8RB8OWpF3YZBCrP5gtADmtXApB5AMLn+vdyA+PyxCjrCs00mjyUozssO33cwDeT3wNGdLxJ5M//YqtHAJw==", + "node_modules/@wdio/globals/node_modules/chrome-launcher": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/chrome-launcher/-/chrome-launcher-1.1.0.tgz", + "integrity": "sha512-rJYWeEAERwWIr3c3mEVXwNiODPEdMRlRxHc47B1qHPOolHZnkj7rMv1QSUfPoG6MgatWj5AxSpnKKR4QEwEQIQ==", "dev": true, + "optional": true, + "peer": true, "dependencies": { - "minimist": "^1.2.6" + "@types/node": "*", + "escape-string-regexp": "^4.0.0", + "is-wsl": "^2.2.0", + "lighthouse-logger": "^2.0.1" }, "bin": { - "mkdirp": "bin/cmd.js" + "print-chrome-path": "bin/print-chrome-path.js" + }, + "engines": { + "node": ">=12.13.0" } }, - "node_modules/@wdio/sync/node_modules/puppeteer-core": { - "version": "9.1.1", - "resolved": "https://registry.npmjs.org/puppeteer-core/-/puppeteer-core-9.1.1.tgz", - "integrity": "sha512-zbedbitVIGhmgz0nt7eIdLsnaoVZSlNJfBivqm2w67T8LR2bU1dvnruDZ8nQO0zn++Iet7zHbAOdnuS5+H2E7A==", + "node_modules/@wdio/globals/node_modules/compress-commons": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/compress-commons/-/compress-commons-5.0.1.tgz", + "integrity": "sha512-MPh//1cERdLtqwO3pOFLeXtpuai0Y2WCd5AhtKxznqM7WtaMYaOEMSgn45d9D10sIHSfIKE603HlOp8OPGrvag==", "dev": true, + "optional": 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" + "crc-32": "^1.2.0", + "crc32-stream": "^5.0.0", + "normalize-path": "^3.0.0", + "readable-stream": "^3.6.0" }, "engines": { - "node": ">=10.18.1" + "node": ">= 12.0.0" } }, - "node_modules/@wdio/sync/node_modules/puppeteer-core/node_modules/devtools-protocol": { - "version": "0.0.869402", - "resolved": "https://registry.npmjs.org/devtools-protocol/-/devtools-protocol-0.0.869402.tgz", - "integrity": "sha512-VvlVYY+VDJe639yHs5PHISzdWTLL3Aw8rO4cvUtwvoxFd6FHbE4OpHHcde52M6096uYYazAmd4l0o5VuFRO2WA==", - "dev": true - }, - "node_modules/@wdio/sync/node_modules/supports-color": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "node_modules/@wdio/globals/node_modules/crc32-stream": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/crc32-stream/-/crc32-stream-5.0.0.tgz", + "integrity": "sha512-B0EPa1UK+qnpBZpG+7FgPCu0J2ETLpXq09o9BkLkEAhdB6Z61Qo4pJ3JYu0c+Qi+/SAL7QThqnzS06pmSSyZaw==", "dev": true, + "optional": true, "dependencies": { - "has-flag": "^4.0.0" + "crc-32": "^1.2.0", + "readable-stream": "^3.4.0" }, "engines": { - "node": ">=8" + "node": ">= 12.0.0" } }, - "node_modules/@wdio/sync/node_modules/uuid": { - "version": "8.3.2", - "resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz", - "integrity": "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==", + "node_modules/@wdio/globals/node_modules/cross-fetch": { + "version": "3.1.6", + "resolved": "https://registry.npmjs.org/cross-fetch/-/cross-fetch-3.1.6.tgz", + "integrity": "sha512-riRvo06crlE8HiqOwIpQhxwdOk4fOeR7FVM/wXoxchFEqMNUjvbs3bfo4OTgMEMHzppd4DxFBDbyySj8Cv781g==", "dev": true, - "bin": { - "uuid": "dist/bin/uuid" + "optional": true, + "peer": true, + "dependencies": { + "node-fetch": "^2.6.11" } }, - "node_modules/@wdio/sync/node_modules/webdriverio": { - "version": "7.5.7", - "resolved": "https://registry.npmjs.org/webdriverio/-/webdriverio-7.5.7.tgz", - "integrity": "sha512-TLluVPLo6Snn/dxEITvMz7ZuklN4qZOBddDuLb9LO3rhsfKDMNbnhcBk0SLdFsWny0aCuhWNpJ6co93702XC0A==", + "node_modules/@wdio/globals/node_modules/devtools": { + "version": "8.29.1", + "resolved": "https://registry.npmjs.org/devtools/-/devtools-8.29.1.tgz", + "integrity": "sha512-fbH0Z7CPK4OZSgUw2QcAppczowxtSyvFztPUmiFyi99cUadjEOwlg0aL3pBVlIDo67olYjGb8GD1M5Z4yI/P6w==", "dev": true, + "optional": true, + "peer": true, "dependencies": { - "@types/aria-query": "^4.2.1", - "@wdio/config": "7.5.3", - "@wdio/logger": "7.5.3", - "@wdio/protocols": "7.5.3", - "@wdio/repl": "7.5.3", - "@wdio/types": "7.5.3", - "@wdio/utils": "7.5.3", - "archiver": "^5.0.0", - "aria-query": "^4.2.2", - "atob": "^2.1.2", - "css-shorthand-properties": "^1.1.1", - "css-value": "^0.0.1", - "devtools": "7.5.7", - "devtools-protocol": "^0.0.878340", - "fs-extra": "^10.0.0", - "get-port": "^5.1.1", - "grapheme-splitter": "^1.0.2", - "lodash.clonedeep": "^4.5.0", - "lodash.isobject": "^3.0.2", - "lodash.isplainobject": "^4.0.6", - "lodash.zip": "^4.2.0", - "minimatch": "^3.0.4", - "puppeteer-core": "^9.1.0", + "@types/node": "^20.1.0", + "@wdio/config": "8.29.1", + "@wdio/logger": "8.28.0", + "@wdio/protocols": "8.24.12", + "@wdio/types": "8.29.1", + "@wdio/utils": "8.29.1", + "chrome-launcher": "^1.0.0", + "edge-paths": "^3.0.5", + "import-meta-resolve": "^4.0.0", + "puppeteer-core": "20.3.0", "query-selector-shadow-dom": "^1.0.0", - "resq": "^1.9.1", - "rgb2hex": "0.2.5", - "serialize-error": "^8.0.0", - "webdriver": "7.5.3" + "ua-parser-js": "^1.0.1", + "uuid": "^9.0.0", + "which": "^4.0.0" }, "engines": { - "node": ">=12.0.0" + "node": "^16.13 || >=18" } }, - "node_modules/@wdio/sync/node_modules/ws": { - "version": "7.5.9", - "resolved": "https://registry.npmjs.org/ws/-/ws-7.5.9.tgz", - "integrity": "sha512-F+P9Jil7UiSKSkppIiD94dN07AwvFixvLIj1Og1Rl9GGMuNipJnV9JzjD6XuqmAeiswGvUmNLjr5cFuXwNS77Q==", + "node_modules/@wdio/globals/node_modules/devtools-protocol": { + "version": "0.0.1120988", + "resolved": "https://registry.npmjs.org/devtools-protocol/-/devtools-protocol-0.0.1120988.tgz", + "integrity": "sha512-39fCpE3Z78IaIPChJsP6Lhmkbf4dWXOmzLk/KFTdRkNk/0JymRIfUynDVRndV9HoDz8PyalK1UH21ST/ivwW5Q==", "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 - } - } + "optional": true, + "peer": true }, - "node_modules/@wdio/types": { - "version": "7.16.14", - "resolved": "https://registry.npmjs.org/@wdio/types/-/types-7.16.14.tgz", - "integrity": "sha512-AyNI9iBSos9xWBmiFAF3sBs6AJXO/55VppU/eeF4HRdbZMtMarnvMuahM+jlUrA3vJSmDW+ufelG0MT//6vrnw==", + "node_modules/@wdio/globals/node_modules/devtools/node_modules/which": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/which/-/which-4.0.0.tgz", + "integrity": "sha512-GlaYyEb07DPxYCKhKzplCWBJtvxZcZMrL+4UkrTSJHHPyZU4mYYTv3qaOe77H7EODLSSopAUFAc6W8U4yqvscg==", "dev": true, + "optional": true, + "peer": true, "dependencies": { - "@types/node": "^17.0.4", - "got": "^11.8.1" + "isexe": "^3.1.1" }, - "engines": { - "node": ">=12.0.0" - } - }, - "node_modules/@wdio/utils": { - "version": "7.5.3", - "resolved": "https://registry.npmjs.org/@wdio/utils/-/utils-7.5.3.tgz", - "integrity": "sha512-nlLDKr8v8abLOHCKroBwQkGPdCIxjID2MllgWX23xqkYZylM9RdwPBdL8osQt9m3rq2TxiPAT4OlbzNt2WtN6Q==", - "dev": true, - "dependencies": { - "@wdio/logger": "7.5.3", - "@wdio/types": "7.5.3" + "bin": { + "node-which": "bin/which.js" }, "engines": { - "node": ">=12.0.0" + "node": "^16.13.0 || >=18.0.0" } }, - "node_modules/@wdio/utils/node_modules/@wdio/logger": { - "version": "7.5.3", - "resolved": "https://registry.npmjs.org/@wdio/logger/-/logger-7.5.3.tgz", - "integrity": "sha512-r9EADpm0ncS1bDQSWi/nhF9C59/WNLbdAAFlo782E9ItFCpDhNit3aQP9vETv1vFxJRjUIM8Fw/HW8zwPadkbw==", + "node_modules/@wdio/globals/node_modules/edge-paths": { + "version": "3.0.5", + "resolved": "https://registry.npmjs.org/edge-paths/-/edge-paths-3.0.5.tgz", + "integrity": "sha512-sB7vSrDnFa4ezWQk9nZ/n0FdpdUuC6R1EOrlU3DL+bovcNFK28rqu2emmAUjujYEJTWIgQGqgVVWUZXMnc8iWg==", "dev": true, + "optional": true, + "peer": true, "dependencies": { - "chalk": "^4.0.0", - "loglevel": "^1.6.0", - "loglevel-plugin-prefix": "^0.8.4", - "strip-ansi": "^6.0.0" + "@types/which": "^2.0.1", + "which": "^2.0.2" }, "engines": { - "node": ">=12.0.0" + "node": ">=14.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/shirshak55" } }, - "node_modules/@wdio/utils/node_modules/@wdio/types": { - "version": "7.5.3", - "resolved": "https://registry.npmjs.org/@wdio/types/-/types-7.5.3.tgz", - "integrity": "sha512-jmumhKBhNDABnpmrshYLEcdE9WoP5tmynsDNbDABlb/W8FFiLySQOejukhYIL9CLys4zXerV3/edks0SCzHOiQ==", + "node_modules/@wdio/globals/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, - "dependencies": { - "got": "^11.8.1" - }, + "optional": true, + "peer": true, "engines": { - "node": ">=12.0.0" + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "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==", + "node_modules/@wdio/globals/node_modules/glob": { + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/glob/-/glob-8.1.0.tgz", + "integrity": "sha512-r8hpEjiQEYlF2QU0df3dS+nxxSIreXQS1qRhMJM0Q5NDdR386C7jb7Hwwod8Fgiuex+k0GFjgft18yvxm5XoCQ==", "dev": true, + "optional": true, "dependencies": { - "color-convert": "^2.0.1" + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^5.0.1", + "once": "^1.3.0" }, "engines": { - "node": ">=8" + "node": ">=12" }, "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" + "url": "https://github.com/sponsors/isaacs" } }, - "node_modules/@wdio/utils/node_modules/chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "node_modules/@wdio/globals/node_modules/glob/node_modules/minimatch": { + "version": "5.1.6", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-5.1.6.tgz", + "integrity": "sha512-lKwV/1brpG6mBUFHtb7NUmtABCb2WZZmm2wNiOA5hAb8VdCS4B3dtMWyvcoViccwAW/COERjXLt0zP1zXUN26g==", "dev": true, + "optional": true, "dependencies": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" + "brace-expansion": "^2.0.1" }, "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==", + "node_modules/@wdio/globals/node_modules/http-proxy-agent": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/http-proxy-agent/-/http-proxy-agent-5.0.0.tgz", + "integrity": "sha512-n2hY8YdoRE1i7r6M0w9DIw5GgZN0G25P8zLCRQ8rjXtTU3vsNFBI/vWK/UIeE6g5MUUz6avwAPXmL6Fy9D/90w==", "dev": true, + "optional": true, + "peer": true, "dependencies": { - "color-name": "~1.1.4" + "@tootallnate/once": "2", + "agent-base": "6", + "debug": "4" }, "engines": { - "node": ">=7.0.0" + "node": ">= 6" } }, - "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==", + "node_modules/@wdio/globals/node_modules/isexe": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/isexe/-/isexe-3.1.1.tgz", + "integrity": "sha512-LpB/54B+/2J5hqQ7imZHfdU31OlgQqx7ZicVlkm9kzg9/w8GKLEcFfJl/t7DCEDueOyBAD6zCCwTO6Fzs0NoEQ==", "dev": true, + "optional": true, + "peer": true, "engines": { - "node": ">=8" + "node": ">=16" } }, - "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==", + "node_modules/@wdio/globals/node_modules/lighthouse-logger": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/lighthouse-logger/-/lighthouse-logger-2.0.1.tgz", + "integrity": "sha512-ioBrW3s2i97noEmnXxmUq7cjIcVRjT5HBpAYy8zE11CxU9HqlWHHeRxfeN1tn8F7OEMVPIC9x1f8t3Z7US9ehQ==", "dev": true, + "optional": true, + "peer": true, "dependencies": { - "has-flag": "^4.0.0" - }, - "engines": { - "node": ">=8" + "debug": "^2.6.9", + "marky": "^1.2.2" } }, - "node_modules/@webassemblyjs/ast": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/@webassemblyjs/ast/-/ast-1.11.1.tgz", - "integrity": "sha512-ukBh14qFLjxTQNTXocdyksN5QdM28S1CxHt2rdskFyL+xFV7VremuBLVbmCePj+URalXBENx/9Lm7lnhihtCSw==", + "node_modules/@wdio/globals/node_modules/lighthouse-logger/node_modules/debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", "dev": true, + "optional": true, + "peer": true, "dependencies": { - "@webassemblyjs/helper-numbers": "1.11.1", - "@webassemblyjs/helper-wasm-bytecode": "1.11.1" + "ms": "2.0.0" } }, - "node_modules/@webassemblyjs/floating-point-hex-parser": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/@webassemblyjs/floating-point-hex-parser/-/floating-point-hex-parser-1.11.1.tgz", - "integrity": "sha512-iGRfyc5Bq+NnNuX8b5hwBrRjzf0ocrJPI6GWFodBFzmFnyvrQ83SHKhmilCU/8Jv67i4GJZBMhEzltxzcNagtQ==", - "dev": true - }, - "node_modules/@webassemblyjs/helper-api-error": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-api-error/-/helper-api-error-1.11.1.tgz", - "integrity": "sha512-RlhS8CBCXfRUR/cwo2ho9bkheSXG0+NwooXcc3PAILALf2QLdFyj7KGsKRbVc95hZnhnERon4kW/D3SZpp6Tcg==", - "dev": true - }, - "node_modules/@webassemblyjs/helper-buffer": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-buffer/-/helper-buffer-1.11.1.tgz", - "integrity": "sha512-gwikF65aDNeeXa8JxXa2BAk+REjSyhrNC9ZwdT0f8jc4dQQeDQ7G4m0f2QCLPJiMTTO6wfDmRmj/pW0PsUvIcA==", - "dev": true + "node_modules/@wdio/globals/node_modules/lru-cache": { + "version": "7.18.3", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-7.18.3.tgz", + "integrity": "sha512-jumlc0BIUrS3qJGgIkWZsyfAM7NCWiBcCDhnd+3NNM5KbBmLTgHVfWBcg6W+rLUsIpzpERPsvwUP7CckAQSOoA==", + "dev": true, + "optional": true, + "engines": { + "node": ">=12" + } }, - "node_modules/@webassemblyjs/helper-numbers": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-numbers/-/helper-numbers-1.11.1.tgz", - "integrity": "sha512-vDkbxiB8zfnPdNK9Rajcey5C0w+QJugEglN0of+kmO8l7lDb77AnlKYQF7aarZuCrv+l0UvqL+68gSDr3k9LPQ==", + "node_modules/@wdio/globals/node_modules/minimatch": { + "version": "9.0.3", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.3.tgz", + "integrity": "sha512-RHiac9mvaRw0x3AYRgDC1CxAP7HTcNrrECeA8YYJeWnpo+2Q5CegtZjaotWTWxDG3UeGA1coE05iH1mPjT/2mg==", "dev": true, + "optional": true, "dependencies": { - "@webassemblyjs/floating-point-hex-parser": "1.11.1", - "@webassemblyjs/helper-api-error": "1.11.1", - "@xtuc/long": "4.2.2" + "brace-expansion": "^2.0.1" + }, + "engines": { + "node": ">=16 || 14 >=14.17" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" } }, - "node_modules/@webassemblyjs/helper-wasm-bytecode": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-wasm-bytecode/-/helper-wasm-bytecode-1.11.1.tgz", - "integrity": "sha512-PvpoOGiJwXeTrSf/qfudJhwlvDQxFgelbMqtq52WWiXC6Xgg1IREdngmPN3bs4RoO83PnL/nFrxucXj1+BX62Q==", - "dev": true + "node_modules/@wdio/globals/node_modules/ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", + "dev": true, + "optional": true, + "peer": true }, - "node_modules/@webassemblyjs/helper-wasm-section": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-wasm-section/-/helper-wasm-section-1.11.1.tgz", - "integrity": "sha512-10P9No29rYX1j7F3EVPX3JvGPQPae+AomuSTPiF9eBQeChHI6iqjMIwR9JmOJXwpnn/oVGDk7I5IlskuMwU/pg==", + "node_modules/@wdio/globals/node_modules/node-fetch": { + "version": "2.7.0", + "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.7.0.tgz", + "integrity": "sha512-c4FRfUm/dbcWZ7U+1Wq0AwCyFL+3nt2bEw05wfxSz+DWpWsitgmSgYmy2dQdWyKC1694ELPqMs/YzUSNozLt8A==", "dev": true, + "optional": true, "dependencies": { - "@webassemblyjs/ast": "1.11.1", - "@webassemblyjs/helper-buffer": "1.11.1", - "@webassemblyjs/helper-wasm-bytecode": "1.11.1", - "@webassemblyjs/wasm-gen": "1.11.1" + "whatwg-url": "^5.0.0" + }, + "engines": { + "node": "4.x || >=6.0.0" + }, + "peerDependencies": { + "encoding": "^0.1.0" + }, + "peerDependenciesMeta": { + "encoding": { + "optional": true + } } }, - "node_modules/@webassemblyjs/ieee754": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/@webassemblyjs/ieee754/-/ieee754-1.11.1.tgz", - "integrity": "sha512-hJ87QIPtAMKbFq6CGTkZYJivEwZDbQUgYd3qKSadTNOhVY7p+gfP6Sr0lLRVTaG1JjFj+r3YchoqRYxNH3M0GQ==", + "node_modules/@wdio/globals/node_modules/proxy-agent": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/proxy-agent/-/proxy-agent-6.3.0.tgz", + "integrity": "sha512-0LdR757eTj/JfuU7TL2YCuAZnxWXu3tkJbg4Oq3geW/qFNT/32T0sp2HnZ9O0lMR4q3vwAt0+xCA8SR0WAD0og==", "dev": true, + "optional": true, "dependencies": { - "@xtuc/ieee754": "^1.2.0" + "agent-base": "^7.0.2", + "debug": "^4.3.4", + "http-proxy-agent": "^7.0.0", + "https-proxy-agent": "^7.0.0", + "lru-cache": "^7.14.1", + "pac-proxy-agent": "^7.0.0", + "proxy-from-env": "^1.1.0", + "socks-proxy-agent": "^8.0.1" + }, + "engines": { + "node": ">= 14" } }, - "node_modules/@webassemblyjs/leb128": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/@webassemblyjs/leb128/-/leb128-1.11.1.tgz", - "integrity": "sha512-BJ2P0hNZ0u+Th1YZXJpzW6miwqQUGcIHT1G/sf72gLVD9DZ5AdYTqPNbHZh6K1M5VmKvFXwGSWZADz+qBWxeRw==", + "node_modules/@wdio/globals/node_modules/proxy-agent/node_modules/agent-base": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-7.1.0.tgz", + "integrity": "sha512-o/zjMZRhJxny7OyEF+Op8X+efiELC7k7yOjMzgfzVqOzXqkBkWI79YoTdOtsuWd5BWhAGAuOY/Xa6xpiaWXiNg==", "dev": true, + "optional": true, "dependencies": { - "@xtuc/long": "4.2.2" + "debug": "^4.3.4" + }, + "engines": { + "node": ">= 14" } }, - "node_modules/@webassemblyjs/utf8": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/@webassemblyjs/utf8/-/utf8-1.11.1.tgz", - "integrity": "sha512-9kqcxAEdMhiwQkHpkNiorZzqpGrodQQ2IGrHHxCy+Ozng0ofyMA0lTqiLkVs1uzTRejX+/O0EOT7KxqVPuXosQ==", - "dev": true - }, - "node_modules/@webassemblyjs/wasm-edit": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-edit/-/wasm-edit-1.11.1.tgz", - "integrity": "sha512-g+RsupUC1aTHfR8CDgnsVRVZFJqdkFHpsHMfJuWQzWU3tvnLC07UqHICfP+4XyL2tnr1amvl1Sdp06TnYCmVkA==", + "node_modules/@wdio/globals/node_modules/proxy-agent/node_modules/http-proxy-agent": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/http-proxy-agent/-/http-proxy-agent-7.0.0.tgz", + "integrity": "sha512-+ZT+iBxVUQ1asugqnD6oWoRiS25AkjNfG085dKJGtGxkdwLQrMKU5wJr2bOOFAXzKcTuqq+7fZlTMgG3SRfIYQ==", "dev": true, + "optional": true, "dependencies": { - "@webassemblyjs/ast": "1.11.1", - "@webassemblyjs/helper-buffer": "1.11.1", - "@webassemblyjs/helper-wasm-bytecode": "1.11.1", - "@webassemblyjs/helper-wasm-section": "1.11.1", - "@webassemblyjs/wasm-gen": "1.11.1", - "@webassemblyjs/wasm-opt": "1.11.1", - "@webassemblyjs/wasm-parser": "1.11.1", - "@webassemblyjs/wast-printer": "1.11.1" + "agent-base": "^7.1.0", + "debug": "^4.3.4" + }, + "engines": { + "node": ">= 14" } }, - "node_modules/@webassemblyjs/wasm-gen": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-gen/-/wasm-gen-1.11.1.tgz", - "integrity": "sha512-F7QqKXwwNlMmsulj6+O7r4mmtAlCWfO/0HdgOxSklZfQcDu0TpLiD1mRt/zF25Bk59FIjEuGAIyn5ei4yMfLhA==", + "node_modules/@wdio/globals/node_modules/proxy-agent/node_modules/https-proxy-agent": { + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-7.0.2.tgz", + "integrity": "sha512-NmLNjm6ucYwtcUmL7JQC1ZQ57LmHP4lT15FQ8D61nak1rO6DH+fz5qNK2Ap5UN4ZapYICE3/0KodcLYSPsPbaA==", "dev": true, + "optional": true, "dependencies": { - "@webassemblyjs/ast": "1.11.1", - "@webassemblyjs/helper-wasm-bytecode": "1.11.1", - "@webassemblyjs/ieee754": "1.11.1", - "@webassemblyjs/leb128": "1.11.1", - "@webassemblyjs/utf8": "1.11.1" + "agent-base": "^7.0.2", + "debug": "4" + }, + "engines": { + "node": ">= 14" } }, - "node_modules/@webassemblyjs/wasm-opt": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-opt/-/wasm-opt-1.11.1.tgz", - "integrity": "sha512-VqnkNqnZlU5EB64pp1l7hdm3hmQw7Vgqa0KF/KCNO9sIpI6Fk6brDEiX+iCOYrvMuBWDws0NkTOxYEb85XQHHw==", + "node_modules/@wdio/globals/node_modules/puppeteer-core": { + "version": "20.3.0", + "resolved": "https://registry.npmjs.org/puppeteer-core/-/puppeteer-core-20.3.0.tgz", + "integrity": "sha512-264pBrIui5bO6NJeOcbJrLa0OCwmA4+WK00JMrLIKTfRiqe2gx8KWTzLsjyw/bizErp3TKS7vt/I0i5fTC+mAw==", "dev": true, + "optional": true, + "peer": true, "dependencies": { - "@webassemblyjs/ast": "1.11.1", - "@webassemblyjs/helper-buffer": "1.11.1", - "@webassemblyjs/wasm-gen": "1.11.1", - "@webassemblyjs/wasm-parser": "1.11.1" + "@puppeteer/browsers": "1.3.0", + "chromium-bidi": "0.4.9", + "cross-fetch": "3.1.6", + "debug": "4.3.4", + "devtools-protocol": "0.0.1120988", + "ws": "8.13.0" + }, + "engines": { + "node": ">=16.0.0" + }, + "peerDependencies": { + "typescript": ">= 4.7.4" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } } }, - "node_modules/@webassemblyjs/wasm-parser": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-parser/-/wasm-parser-1.11.1.tgz", - "integrity": "sha512-rrBujw+dJu32gYB7/Lup6UhdkPx9S9SnobZzRVL7VcBH9Bt9bCBLEuX/YXOOtBsOZ4NQrRykKhffRWHvigQvOA==", + "node_modules/@wdio/globals/node_modules/readable-stream": { + "version": "3.6.2", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz", + "integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==", "dev": true, + "optional": true, "dependencies": { - "@webassemblyjs/ast": "1.11.1", - "@webassemblyjs/helper-api-error": "1.11.1", - "@webassemblyjs/helper-wasm-bytecode": "1.11.1", - "@webassemblyjs/ieee754": "1.11.1", - "@webassemblyjs/leb128": "1.11.1", - "@webassemblyjs/utf8": "1.11.1" + "inherits": "^2.0.3", + "string_decoder": "^1.1.1", + "util-deprecate": "^1.0.1" + }, + "engines": { + "node": ">= 6" } }, - "node_modules/@webassemblyjs/wast-printer": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/@webassemblyjs/wast-printer/-/wast-printer-1.11.1.tgz", - "integrity": "sha512-IQboUWM4eKzWW+N/jij2sRatKMh99QEelo3Eb2q0qXkvPRISAj8Qxtmw5itwqK+TTkBuUIE45AxYPToqPtL5gg==", + "node_modules/@wdio/globals/node_modules/serialize-error": { + "version": "11.0.3", + "resolved": "https://registry.npmjs.org/serialize-error/-/serialize-error-11.0.3.tgz", + "integrity": "sha512-2G2y++21dhj2R7iHAdd0FIzjGwuKZld+7Pl/bTU6YIkrC2ZMbVUjm+luj6A6V34Rv9XfKJDKpTWu9W4Gse1D9g==", "dev": true, + "optional": true, "dependencies": { - "@webassemblyjs/ast": "1.11.1", - "@xtuc/long": "4.2.2" + "type-fest": "^2.12.2" + }, + "engines": { + "node": ">=14.16" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/@xmldom/xmldom": { - "version": "0.7.8", - "resolved": "https://registry.npmjs.org/@xmldom/xmldom/-/xmldom-0.7.8.tgz", - "integrity": "sha512-PrJx38EfpitFhwmILRl37jAdBlsww6AZ6rRVK4QS7T7RHLhX7mSs647sTmgr9GIxe3qjXdesmomEgbgaokrVFg==", + "node_modules/@wdio/globals/node_modules/tar-stream": { + "version": "3.1.7", + "resolved": "https://registry.npmjs.org/tar-stream/-/tar-stream-3.1.7.tgz", + "integrity": "sha512-qJj60CXt7IU1Ffyc3NJMjh6EkuCFej46zUqJ4J7pqYlThyd9bO0XBTmcOIhSzZJVWfsLks0+nle/j538YAW9RQ==", "dev": true, - "engines": { - "node": ">=10.0.0" + "optional": true, + "dependencies": { + "b4a": "^1.6.4", + "fast-fifo": "^1.2.0", + "streamx": "^2.15.0" } }, - "node_modules/@xtuc/ieee754": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/@xtuc/ieee754/-/ieee754-1.2.0.tgz", - "integrity": "sha512-DX8nKgqcGwsc0eJSqYt5lwP4DH5FlHnmuWWBRy7X0NcaGR0ZtuyeESgMwTYVEtxmsNGY+qit4QYT/MIYTOTPeA==", - "dev": true + "node_modules/@wdio/globals/node_modules/type-fest": { + "version": "2.19.0", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-2.19.0.tgz", + "integrity": "sha512-RAH822pAdBgcNMAfWnCBU3CFZcfZ/i1eZjwFU/dsLKumyuuP3niueg2UAukXYF0E2AAoc82ZSSf9J0WQBinzHA==", + "dev": true, + "optional": true, + "engines": { + "node": ">=12.20" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } }, - "node_modules/@xtuc/long": { - "version": "4.2.2", - "resolved": "https://registry.npmjs.org/@xtuc/long/-/long-4.2.2.tgz", - "integrity": "sha512-NuHqBY1PB/D8xU6s/thBgOAiAP7HOYDQ32+BFZILJ8ivkUkAHQnWfn6WhL79Owj1qmUnoN/YPhktdIoucipkAQ==", - "dev": true + "node_modules/@wdio/globals/node_modules/ua-parser-js": { + "version": "1.0.37", + "resolved": "https://registry.npmjs.org/ua-parser-js/-/ua-parser-js-1.0.37.tgz", + "integrity": "sha512-bhTyI94tZofjo+Dn8SN6Zv8nBDvyXTymAdM3LDI/0IboIUwTu1rEhW7v2TfiVsoYWgkQ4kOVqnI8APUFbIQIFQ==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/ua-parser-js" + }, + { + "type": "paypal", + "url": "https://paypal.me/faisalman" + }, + { + "type": "github", + "url": "https://github.com/sponsors/faisalman" + } + ], + "optional": true, + "peer": true, + "engines": { + "node": "*" + } }, - "node_modules/abbrev": { - "version": "1.0.9", - "resolved": "https://registry.npmjs.org/abbrev/-/abbrev-1.0.9.tgz", - "integrity": "sha512-LEyx4aLEC3x6T0UguF6YILf+ntvmOaWsVfENmIW0E9H09vKlLDGelMjjSm0jkDHALj8A8quZ/HapKNigzwge+Q==", - "dev": true + "node_modules/@wdio/globals/node_modules/uuid": { + "version": "9.0.1", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-9.0.1.tgz", + "integrity": "sha512-b+1eJOlsR9K8HJpow9Ok3fiWOWSIcIzXodvv0rQjVoOVNpWMpxf1wZNpt4y9h10odCNrqnYp1OBzRktckBe3sA==", + "dev": true, + "funding": [ + "https://github.com/sponsors/broofa", + "https://github.com/sponsors/ctavan" + ], + "optional": true, + "peer": true, + "bin": { + "uuid": "dist/bin/uuid" + } }, - "node_modules/accepts": { - "version": "1.3.8", - "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.8.tgz", - "integrity": "sha512-PYAthTa2m2VKxuvSD3DPC/Gy+U+sOA1LAuT8mkmRuvw+NACSaeXEQ+NHcVF7rONl6qcaxV3Uuemwawk+7+SJLw==", + "node_modules/@wdio/globals/node_modules/webdriverio": { + "version": "8.29.1", + "resolved": "https://registry.npmjs.org/webdriverio/-/webdriverio-8.29.1.tgz", + "integrity": "sha512-NZK95ivXCqdPraB3FHMw6ByxnCvtgFXkjzG2l3Oq5z0IuJS2aMow3AKFIyiuG6is/deGCe+Tb8eFTCqak7UV+w==", + "dev": true, + "optional": true, "dependencies": { - "mime-types": "~2.1.34", - "negotiator": "0.6.3" + "@types/node": "^20.1.0", + "@wdio/config": "8.29.1", + "@wdio/logger": "8.28.0", + "@wdio/protocols": "8.24.12", + "@wdio/repl": "8.24.12", + "@wdio/types": "8.29.1", + "@wdio/utils": "8.29.1", + "archiver": "^6.0.0", + "aria-query": "^5.0.0", + "css-shorthand-properties": "^1.1.1", + "css-value": "^0.0.1", + "devtools-protocol": "^0.0.1249869", + "grapheme-splitter": "^1.0.2", + "import-meta-resolve": "^4.0.0", + "is-plain-obj": "^4.1.0", + "lodash.clonedeep": "^4.5.0", + "lodash.zip": "^4.2.0", + "minimatch": "^9.0.0", + "puppeteer-core": "^20.9.0", + "query-selector-shadow-dom": "^1.0.0", + "resq": "^1.9.1", + "rgb2hex": "0.2.5", + "serialize-error": "^11.0.1", + "webdriver": "8.29.1" }, "engines": { - "node": ">= 0.6" + "node": "^16.13 || >=18" + }, + "peerDependencies": { + "devtools": "^8.14.0" + }, + "peerDependenciesMeta": { + "devtools": { + "optional": true + } } }, - "node_modules/acorn": { - "version": "7.4.1", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-7.4.1.tgz", - "integrity": "sha512-nQyp0o1/mNdbTO1PO6kHkwSrmgZ0MT/jCCpNiwbUjGoRN4dlBhqJtoQuCnEOKzgTVwg0ZWiCoQy6SxMebQVh8A==", + "node_modules/@wdio/globals/node_modules/webdriverio/node_modules/@puppeteer/browsers": { + "version": "1.4.6", + "resolved": "https://registry.npmjs.org/@puppeteer/browsers/-/browsers-1.4.6.tgz", + "integrity": "sha512-x4BEjr2SjOPowNeiguzjozQbsc6h437ovD/wu+JpaenxVLm3jkgzHY2xOslMTp50HoTvQreMjiexiGQw1sqZlQ==", "dev": true, + "optional": true, + "dependencies": { + "debug": "4.3.4", + "extract-zip": "2.0.1", + "progress": "2.0.3", + "proxy-agent": "6.3.0", + "tar-fs": "3.0.4", + "unbzip2-stream": "1.4.3", + "yargs": "17.7.1" + }, "bin": { - "acorn": "bin/acorn" + "browsers": "lib/cjs/main-cli.js" }, "engines": { - "node": ">=0.4.0" + "node": ">=16.3.0" + }, + "peerDependencies": { + "typescript": ">= 4.7.4" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } } }, - "node_modules/acorn-jsx": { - "version": "5.3.2", - "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.2.tgz", - "integrity": "sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==", + "node_modules/@wdio/globals/node_modules/webdriverio/node_modules/chromium-bidi": { + "version": "0.4.16", + "resolved": "https://registry.npmjs.org/chromium-bidi/-/chromium-bidi-0.4.16.tgz", + "integrity": "sha512-7ZbXdWERxRxSwo3txsBjjmc/NLxqb1Bk30mRb0BMS4YIaiV6zvKZqL/UAH+DdqcDYayDWk2n/y8klkBDODrPvA==", "dev": true, + "optional": true, + "dependencies": { + "mitt": "3.0.0" + }, "peerDependencies": { - "acorn": "^6.0.0 || ^7.0.0 || ^8.0.0" + "devtools-protocol": "*" } }, - "node_modules/acorn-walk": { - "version": "8.2.0", - "resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-8.2.0.tgz", - "integrity": "sha512-k+iyHEuPgSw6SbuDpGQM+06HQUa04DZ3o+F6CSzXMvvI5KMvnaEqXe+YVe555R9nn6GPt404fos4wcgpw12SDA==", + "node_modules/@wdio/globals/node_modules/webdriverio/node_modules/cross-fetch": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/cross-fetch/-/cross-fetch-4.0.0.tgz", + "integrity": "sha512-e4a5N8lVvuLgAWgnCrLr2PP0YyDOTHa9H/Rj54dirp61qXnNq46m82bRhNqIA5VccJtWBvPTFRV3TtvHUKPB1g==", "dev": true, - "engines": { - "node": ">=0.4.0" + "optional": true, + "dependencies": { + "node-fetch": "^2.6.12" } }, - "node_modules/aes-decrypter": { - "version": "3.1.3", - "resolved": "https://registry.npmjs.org/aes-decrypter/-/aes-decrypter-3.1.3.tgz", - "integrity": "sha512-VkG9g4BbhMBy+N5/XodDeV6F02chEk9IpgRTq/0bS80y4dzy79VH2Gtms02VXomf3HmyRe3yyJYkJ990ns+d6A==", + "node_modules/@wdio/globals/node_modules/webdriverio/node_modules/devtools-protocol": { + "version": "0.0.1249869", + "resolved": "https://registry.npmjs.org/devtools-protocol/-/devtools-protocol-0.0.1249869.tgz", + "integrity": "sha512-Ctp4hInA0BEavlUoRy9mhGq0i+JSo/AwVyX2EFgZmV1kYB+Zq+EMBAn52QWu6FbRr10hRb6pBl420upbp4++vg==", "dev": true, - "dependencies": { - "@babel/runtime": "^7.12.5", - "@videojs/vhs-utils": "^3.0.5", - "global": "^4.4.0", - "pkcs7": "^1.0.4" - } + "optional": true }, - "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==", + "node_modules/@wdio/globals/node_modules/webdriverio/node_modules/puppeteer-core": { + "version": "20.9.0", + "resolved": "https://registry.npmjs.org/puppeteer-core/-/puppeteer-core-20.9.0.tgz", + "integrity": "sha512-H9fYZQzMTRrkboEfPmf7m3CLDN6JvbxXA3qTtS+dFt27tR+CsFHzPsT6pzp6lYL6bJbAPaR0HaPO6uSi+F94Pg==", "dev": true, + "optional": true, "dependencies": { - "debug": "4" + "@puppeteer/browsers": "1.4.6", + "chromium-bidi": "0.4.16", + "cross-fetch": "4.0.0", + "debug": "4.3.4", + "devtools-protocol": "0.0.1147663", + "ws": "8.13.0" }, "engines": { - "node": ">= 6.0.0" + "node": ">=16.3.0" + }, + "peerDependencies": { + "typescript": ">= 4.7.4" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } } }, - "node_modules/ajv": { - "version": "6.12.3", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.3.tgz", - "integrity": "sha512-4K0cK3L1hsqk9xIb2z9vs/XU+PGJZ9PNpJRDS9YLzmNdX6jmVPfamLvTJr0aDAusnHyCHO6MjzlkAsgtqp9teA==", + "node_modules/@wdio/globals/node_modules/webdriverio/node_modules/puppeteer-core/node_modules/devtools-protocol": { + "version": "0.0.1147663", + "resolved": "https://registry.npmjs.org/devtools-protocol/-/devtools-protocol-0.0.1147663.tgz", + "integrity": "sha512-hyWmRrexdhbZ1tcJUGpO95ivbRhWXz++F4Ko+n21AY5PNln2ovoJw+8ZMNDTtip+CNFQfrtLVh/w4009dXO/eQ==", + "dev": true, + "optional": true + }, + "node_modules/@wdio/globals/node_modules/webdriverio/node_modules/tar-fs": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/tar-fs/-/tar-fs-3.0.4.tgz", + "integrity": "sha512-5AFQU8b9qLfZCX9zp2duONhPmZv0hGYiBPJsyUdqMjzq/mqVpy/rEUSeHk1+YitmxugaptgBh5oDGU3VsAJq4w==", "dev": true, + "optional": 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" + "mkdirp-classic": "^0.5.2", + "pump": "^3.0.0", + "tar-stream": "^3.1.5" } }, - "node_modules/ajv-keywords": { - "version": "3.5.2", - "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-3.5.2.tgz", - "integrity": "sha512-5p6WTN0DdTGVQk6VjcEju19IgaHudalcfabD7yhDGeA6bcQnmL+CpveLJq/3hvfwd1aof6L386Ougkx6RfyMIQ==", + "node_modules/@wdio/globals/node_modules/ws": { + "version": "8.13.0", + "resolved": "https://registry.npmjs.org/ws/-/ws-8.13.0.tgz", + "integrity": "sha512-x9vcZYTrFPC7aSIbj7sRCYo7L/Xb8Iy+pW0ng0wt2vCJv7M9HOMy0UoN3rr+IFC7hb7vXoqS+P9ktyLLLhO+LA==", "dev": true, + "optional": true, + "engines": { + "node": ">=10.0.0" + }, "peerDependencies": { - "ajv": "^6.9.1" + "bufferutil": "^4.0.1", + "utf-8-validate": ">=5.0.2" + }, + "peerDependenciesMeta": { + "bufferutil": { + "optional": true + }, + "utf-8-validate": { + "optional": true + } } }, - "node_modules/amdefine": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/amdefine/-/amdefine-1.0.1.tgz", - "integrity": "sha512-S2Hw0TtNkMJhIabBwIojKL9YHO5T0n5eNqWJ7Lrlel/zDbftQpxpapi8tZs3X1HWa+u+QeydGmzzNU0m09+Rcg==", + "node_modules/@wdio/globals/node_modules/yargs": { + "version": "17.7.1", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.7.1.tgz", + "integrity": "sha512-cwiTb08Xuv5fqF4AovYacTFNxk62th7LKJ6BL9IGUpTJrWoU7/7WdQGTP2SjKf1dUNBGzDd28p/Yfs/GI6JrLw==", "dev": true, "optional": true, + "dependencies": { + "cliui": "^8.0.1", + "escalade": "^3.1.1", + "get-caller-file": "^2.0.5", + "require-directory": "^2.1.1", + "string-width": "^4.2.3", + "y18n": "^5.0.5", + "yargs-parser": "^21.1.1" + }, "engines": { - "node": ">=0.4.2" + "node": ">=12" } }, - "node_modules/ansi-colors": { - "version": "4.1.3", - "resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-4.1.3.tgz", - "integrity": "sha512-/6w/C21Pm1A7aZitlI5Ni/2J6FFQN8i1Cvz3kHABAAbw93v/NlvKdVOqz7CCWz/3iv/JplRSEEZ83XION15ovw==", + "node_modules/@wdio/globals/node_modules/zip-stream": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/zip-stream/-/zip-stream-5.0.1.tgz", + "integrity": "sha512-UfZ0oa0C8LI58wJ+moL46BDIMgCQbnsb+2PoiJYtonhBsMh2bq1eRBVkvjfVsqbEHd9/EgKPUuL9saSSsec8OA==", "dev": true, + "optional": true, + "dependencies": { + "archiver-utils": "^4.0.1", + "compress-commons": "^5.0.1", + "readable-stream": "^3.6.0" + }, "engines": { - "node": ">=6" + "node": ">= 12.0.0" } }, - "node_modules/ansi-cyan": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/ansi-cyan/-/ansi-cyan-0.1.1.tgz", - "integrity": "sha512-eCjan3AVo/SxZ0/MyIYRtkpxIu/H3xZN7URr1vXVrISxeyz8fUFz0FJziamK4sS8I+t35y4rHg1b2PklyBe/7A==", + "node_modules/@wdio/local-runner": { + "version": "8.29.1", + "resolved": "https://registry.npmjs.org/@wdio/local-runner/-/local-runner-8.29.1.tgz", + "integrity": "sha512-Z3QAgxe1uQ97C7NS1CdMhgmHaLu/sbb47HTbw1yuuLk+SwsBIQGhNpTSA18QVRSUXq70G3bFvjACwqyap1IEQg==", "dev": true, "dependencies": { - "ansi-wrap": "0.1.0" + "@types/node": "^20.1.0", + "@wdio/logger": "8.28.0", + "@wdio/repl": "8.24.12", + "@wdio/runner": "8.29.1", + "@wdio/types": "8.29.1", + "async-exit-hook": "^2.0.1", + "split2": "^4.1.0", + "stream-buffers": "^3.0.2" }, "engines": { - "node": ">=0.10.0" + "node": "^16.13 || >=18" } }, - "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==", + "node_modules/@wdio/logger": { + "version": "8.28.0", + "resolved": "https://registry.npmjs.org/@wdio/logger/-/logger-8.28.0.tgz", + "integrity": "sha512-/s6zNCqwy1hoc+K4SJypis0Ud0dlJ+urOelJFO1x0G0rwDRWyFiUP6ijTaCcFxAm29jYEcEPWijl2xkVIHwOyA==", "dev": true, "dependencies": { - "type-fest": "^0.21.3" + "chalk": "^5.1.2", + "loglevel": "^1.6.0", + "loglevel-plugin-prefix": "^0.8.4", + "strip-ansi": "^7.1.0" }, "engines": { - "node": ">=8" + "node": "^16.13 || >=18" + } + }, + "node_modules/@wdio/logger/node_modules/ansi-regex": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.0.1.tgz", + "integrity": "sha512-n5M855fKb2SsfMIiFFoVrABHJC8QtHwVx+mHWP3QcEqBHYienj5dHSgjbxtC0WEZXYt4wcD6zrQElDPhFuZgfA==", + "dev": true, + "engines": { + "node": ">=12" }, "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "url": "https://github.com/chalk/ansi-regex?sponsor=1" } }, - "node_modules/ansi-gray": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/ansi-gray/-/ansi-gray-0.1.1.tgz", - "integrity": "sha512-HrgGIZUl8h2EHuZaU9hTR/cU5nhKxpVE1V6kdGsQ8e4zirElJ5fvtfc8N7Q1oq1aatO275i8pUFUCpNWCAnVWw==", + "node_modules/@wdio/logger/node_modules/chalk": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-5.3.0.tgz", + "integrity": "sha512-dLitG79d+GV1Nb/VYcCDFivJeK1hiukt9QjRNVOsUtTy1rR1YJsmpGGTZ3qJos+uw7WmWF4wUwBd9jxjocFC2w==", "dev": true, - "dependencies": { - "ansi-wrap": "0.1.0" - }, "engines": { - "node": ">=0.10.0" + "node": "^12.17.0 || ^14.13 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" } }, - "node_modules/ansi-red": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/ansi-red/-/ansi-red-0.1.1.tgz", - "integrity": "sha512-ewaIr5y+9CUTGFwZfpECUbFlGcC0GCw1oqR9RI6h1gQCd9Aj2GxSckCnPsVJnmfMZbwFYE+leZGASgkWl06Jow==", + "node_modules/@wdio/logger/node_modules/strip-ansi": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.0.tgz", + "integrity": "sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ==", "dev": true, "dependencies": { - "ansi-wrap": "0.1.0" + "ansi-regex": "^6.0.1" }, "engines": { - "node": ">=0.10.0" + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/strip-ansi?sponsor=1" } }, - "node_modules/ansi-regex": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", - "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "node_modules/@wdio/mocha-framework": { + "version": "8.29.1", + "resolved": "https://registry.npmjs.org/@wdio/mocha-framework/-/mocha-framework-8.29.1.tgz", + "integrity": "sha512-R9dKMNqWgtUvZo33ORjUQV8Z/WLX5h/pg9u/xIvZSGXuNSw1h+5DWF6UiNFscxBFblL9UvBi6V9ila2LHgE4ew==", "dev": true, + "dependencies": { + "@types/mocha": "^10.0.0", + "@types/node": "^20.1.0", + "@wdio/logger": "8.28.0", + "@wdio/types": "8.29.1", + "@wdio/utils": "8.29.1", + "mocha": "^10.0.0" + }, "engines": { - "node": ">=8" + "node": "^16.13 || >=18" } }, - "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==", + "node_modules/@wdio/protocols": { + "version": "8.24.12", + "resolved": "https://registry.npmjs.org/@wdio/protocols/-/protocols-8.24.12.tgz", + "integrity": "sha512-QnVj3FkapmVD3h2zoZk+ZQ8gevSj9D9MiIQIy8eOnY4FAneYZ9R9GvoW+mgNcCZO8S8++S/jZHetR8n+8Q808g==", + "dev": true + }, + "node_modules/@wdio/repl": { + "version": "8.24.12", + "resolved": "https://registry.npmjs.org/@wdio/repl/-/repl-8.24.12.tgz", + "integrity": "sha512-321F3sWafnlw93uRTSjEBVuvWCxTkWNDs7ektQS15drrroL3TMeFOynu4rDrIz0jXD9Vas0HCD2Tq/P0uxFLdw==", + "dev": true, "dependencies": { - "color-convert": "^1.9.0" + "@types/node": "^20.1.0" }, "engines": { - "node": ">=4" + "node": "^16.13 || >=18" } }, - "node_modules/ansi-wrap": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/ansi-wrap/-/ansi-wrap-0.1.0.tgz", - "integrity": "sha512-ZyznvL8k/FZeQHr2T6LzcJ/+vBApDnMNZvfVFy3At0knswWd6rJ3/0Hhmpu8oqa6C92npmozs890sX9Dl6q+Qw==", + "node_modules/@wdio/reporter": { + "version": "8.29.1", + "resolved": "https://registry.npmjs.org/@wdio/reporter/-/reporter-8.29.1.tgz", + "integrity": "sha512-LZeYHC+HHJRYiFH9odaotDazZh0zNhu4mTuL/T/e3c/Q3oPSQjLvfQYhB3Ece1QA9PKjP1VPmr+g9CvC0lMixA==", "dev": true, + "dependencies": { + "@types/node": "^20.1.0", + "@wdio/logger": "8.28.0", + "@wdio/types": "8.29.1", + "diff": "^5.0.0", + "object-inspect": "^1.12.0" + }, "engines": { - "node": ">=0.10.0" + "node": "^16.13 || >=18" } }, - "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, - "dependencies": { - "normalize-path": "^3.0.0", - "picomatch": "^2.0.4" + "node_modules/@wdio/runner": { + "version": "8.29.1", + "resolved": "https://registry.npmjs.org/@wdio/runner/-/runner-8.29.1.tgz", + "integrity": "sha512-MvYFf4RgRmzxjAzy6nxvaDG1ycBRvoz772fT06csjxuaVYm57s8mlB8X+U1UQMx/IzujAb53fSeAmNcyU3FNEA==", + "dev": true, + "dependencies": { + "@types/node": "^20.1.0", + "@wdio/config": "8.29.1", + "@wdio/globals": "8.29.1", + "@wdio/logger": "8.28.0", + "@wdio/types": "8.29.1", + "@wdio/utils": "8.29.1", + "deepmerge-ts": "^5.0.0", + "expect-webdriverio": "^4.9.3", + "gaze": "^1.1.2", + "webdriver": "8.29.1", + "webdriverio": "8.29.1" }, "engines": { - "node": ">= 8" + "node": "^16.13 || >=18" } }, - "node_modules/append-buffer": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/append-buffer/-/append-buffer-1.0.2.tgz", - "integrity": "sha512-WLbYiXzD3y/ATLZFufV/rZvWdZOs+Z/+5v1rBZ463Jn398pa6kcde27cvozYnBoxXblGZTFfoPpsaEw0orU5BA==", + "node_modules/@wdio/runner/node_modules/@puppeteer/browsers": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/@puppeteer/browsers/-/browsers-1.3.0.tgz", + "integrity": "sha512-an3QdbNPkuU6qpxpbssxAbjRLJcF+eP4L8UqIY3+6n0sbaVxw5pz7PiCLy9g32XEZuoamUlV5ZQPnA6FxvkIHA==", "dev": true, + "optional": true, + "peer": true, "dependencies": { - "buffer-equal": "^1.0.0" + "debug": "4.3.4", + "extract-zip": "2.0.1", + "http-proxy-agent": "5.0.0", + "https-proxy-agent": "5.0.1", + "progress": "2.0.3", + "proxy-from-env": "1.1.0", + "tar-fs": "2.1.1", + "unbzip2-stream": "1.4.3", + "yargs": "17.7.1" + }, + "bin": { + "browsers": "lib/cjs/main-cli.js" }, "engines": { - "node": ">=0.10.0" + "node": ">=16.0.0" + }, + "peerDependencies": { + "typescript": ">= 4.7.4" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } } }, - "node_modules/archiver": { - "version": "5.3.1", - "resolved": "https://registry.npmjs.org/archiver/-/archiver-5.3.1.tgz", - "integrity": "sha512-8KyabkmbYrH+9ibcTScQ1xCJC/CGcugdVIwB+53f5sZziXgwUh3iXlAlANMxcZyDEfTHMe6+Z5FofV8nopXP7w==", + "node_modules/@wdio/runner/node_modules/@types/which": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/@types/which/-/which-2.0.2.tgz", + "integrity": "sha512-113D3mDkZDjo+EeUEHCFy0qniNc1ZpecGiAU7WSo7YDoSzolZIQKpYFHrPpjkB2nuyahcKfrmLXeQlh7gqJYdw==", + "dev": true, + "optional": true, + "peer": true + }, + "node_modules/@wdio/runner/node_modules/archiver": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/archiver/-/archiver-6.0.1.tgz", + "integrity": "sha512-CXGy4poOLBKptiZH//VlWdFuUC1RESbdZjGjILwBuZ73P7WkAUN0htfSfBq/7k6FRFlpu7bg4JOkj1vU9G6jcQ==", "dev": true, "dependencies": { - "archiver-utils": "^2.1.0", - "async": "^3.2.3", + "archiver-utils": "^4.0.1", + "async": "^3.2.4", "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" + "readdir-glob": "^1.1.2", + "tar-stream": "^3.0.0", + "zip-stream": "^5.0.1" }, "engines": { - "node": ">= 10" + "node": ">= 12.0.0" } }, - "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==", + "node_modules/@wdio/runner/node_modules/archiver-utils": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/archiver-utils/-/archiver-utils-4.0.1.tgz", + "integrity": "sha512-Q4Q99idbvzmgCTEAAhi32BkOyq8iVI5EwdO0PmBDSGIzzjYNdcFn7Q7k3OzbLy4kLUPXfJtG6fO2RjftXbobBg==", "dev": true, "dependencies": { - "glob": "^7.1.4", + "glob": "^8.0.0", "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", + "lodash": "^4.17.15", "normalize-path": "^3.0.0", - "readable-stream": "^2.0.0" + "readable-stream": "^3.6.0" }, "engines": { - "node": ">= 6" + "node": ">= 12.0.0" } }, - "node_modules/archiver/node_modules/async": { - "version": "3.2.4", - "resolved": "https://registry.npmjs.org/async/-/async-3.2.4.tgz", - "integrity": "sha512-iAB+JbDEGXhyIUavoDl9WP/Jj106Kz9DEn1DPgYw5ruDn0e3Wgi3sKFm55sASdGBNOQB8F59d9qQ7deqrHA8wQ==", + "node_modules/@wdio/runner/node_modules/async": { + "version": "3.2.5", + "resolved": "https://registry.npmjs.org/async/-/async-3.2.5.tgz", + "integrity": "sha512-baNZyqaaLhyLVKm/DlvdW051MSgO6b8eVfIezl9E5PqWxFgzLm/wQntEW4zOytVburDEr0JlALEpdOFwvErLsg==", "dev": true }, - "node_modules/archiver/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==", + "node_modules/@wdio/runner/node_modules/brace-expansion": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", + "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", "dev": true, "dependencies": { - "inherits": "^2.0.3", - "string_decoder": "^1.1.1", - "util-deprecate": "^1.0.1" + "balanced-match": "^1.0.0" + } + }, + "node_modules/@wdio/runner/node_modules/chrome-launcher": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/chrome-launcher/-/chrome-launcher-1.1.0.tgz", + "integrity": "sha512-rJYWeEAERwWIr3c3mEVXwNiODPEdMRlRxHc47B1qHPOolHZnkj7rMv1QSUfPoG6MgatWj5AxSpnKKR4QEwEQIQ==", + "dev": true, + "optional": true, + "peer": true, + "dependencies": { + "@types/node": "*", + "escape-string-regexp": "^4.0.0", + "is-wsl": "^2.2.0", + "lighthouse-logger": "^2.0.1" + }, + "bin": { + "print-chrome-path": "bin/print-chrome-path.js" }, "engines": { - "node": ">= 6" + "node": ">=12.13.0" } }, - "node_modules/archy": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/archy/-/archy-1.0.0.tgz", - "integrity": "sha512-Xg+9RwCg/0p32teKdGMPTPnVXKD0w3DfHnFTficozsAgsvq2XenPJq/MYpzzQ/v8zrOyJn6Ds39VA4JIDwFfqw==", - "dev": true + "node_modules/@wdio/runner/node_modules/compress-commons": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/compress-commons/-/compress-commons-5.0.1.tgz", + "integrity": "sha512-MPh//1cERdLtqwO3pOFLeXtpuai0Y2WCd5AhtKxznqM7WtaMYaOEMSgn45d9D10sIHSfIKE603HlOp8OPGrvag==", + "dev": true, + "dependencies": { + "crc-32": "^1.2.0", + "crc32-stream": "^5.0.0", + "normalize-path": "^3.0.0", + "readable-stream": "^3.6.0" + }, + "engines": { + "node": ">= 12.0.0" + } }, - "node_modules/argparse": { - "version": "1.0.10", - "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", - "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==", + "node_modules/@wdio/runner/node_modules/crc32-stream": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/crc32-stream/-/crc32-stream-5.0.0.tgz", + "integrity": "sha512-B0EPa1UK+qnpBZpG+7FgPCu0J2ETLpXq09o9BkLkEAhdB6Z61Qo4pJ3JYu0c+Qi+/SAL7QThqnzS06pmSSyZaw==", "dev": true, "dependencies": { - "sprintf-js": "~1.0.2" + "crc-32": "^1.2.0", + "readable-stream": "^3.4.0" + }, + "engines": { + "node": ">= 12.0.0" } }, - "node_modules/aria-query": { - "version": "5.1.3", - "resolved": "https://registry.npmjs.org/aria-query/-/aria-query-5.1.3.tgz", - "integrity": "sha512-R5iJ5lkuHybztUfuOAznmboyjWq8O6sqNqtK7CLOqdydi54VNbORp49mb14KbWgG1QD3JFO9hJdZ+y4KutfdOQ==", + "node_modules/@wdio/runner/node_modules/cross-fetch": { + "version": "3.1.6", + "resolved": "https://registry.npmjs.org/cross-fetch/-/cross-fetch-3.1.6.tgz", + "integrity": "sha512-riRvo06crlE8HiqOwIpQhxwdOk4fOeR7FVM/wXoxchFEqMNUjvbs3bfo4OTgMEMHzppd4DxFBDbyySj8Cv781g==", "dev": true, + "optional": true, + "peer": true, "dependencies": { - "deep-equal": "^2.0.5" + "node-fetch": "^2.6.11" } }, - "node_modules/arr-diff": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/arr-diff/-/arr-diff-1.1.0.tgz", - "integrity": "sha512-OQwDZUqYaQwyyhDJHThmzId8daf4/RFNLaeh3AevmSeZ5Y7ug4Ga/yKc6l6kTZOBW781rCj103ZuTh8GAsB3+Q==", + "node_modules/@wdio/runner/node_modules/devtools": { + "version": "8.29.1", + "resolved": "https://registry.npmjs.org/devtools/-/devtools-8.29.1.tgz", + "integrity": "sha512-fbH0Z7CPK4OZSgUw2QcAppczowxtSyvFztPUmiFyi99cUadjEOwlg0aL3pBVlIDo67olYjGb8GD1M5Z4yI/P6w==", "dev": true, + "optional": true, + "peer": true, "dependencies": { - "arr-flatten": "^1.0.1", - "array-slice": "^0.2.3" + "@types/node": "^20.1.0", + "@wdio/config": "8.29.1", + "@wdio/logger": "8.28.0", + "@wdio/protocols": "8.24.12", + "@wdio/types": "8.29.1", + "@wdio/utils": "8.29.1", + "chrome-launcher": "^1.0.0", + "edge-paths": "^3.0.5", + "import-meta-resolve": "^4.0.0", + "puppeteer-core": "20.3.0", + "query-selector-shadow-dom": "^1.0.0", + "ua-parser-js": "^1.0.1", + "uuid": "^9.0.0", + "which": "^4.0.0" }, "engines": { - "node": ">=0.10.0" + "node": "^16.13 || >=18" } }, - "node_modules/arr-diff/node_modules/array-slice": { - "version": "0.2.3", - "resolved": "https://registry.npmjs.org/array-slice/-/array-slice-0.2.3.tgz", - "integrity": "sha512-rlVfZW/1Ph2SNySXwR9QYkChp8EkOEiTMO5Vwx60usw04i4nWemkm9RXmQqgkQFaLHsqLuADvjp6IfgL9l2M8Q==", + "node_modules/@wdio/runner/node_modules/devtools-protocol": { + "version": "0.0.1120988", + "resolved": "https://registry.npmjs.org/devtools-protocol/-/devtools-protocol-0.0.1120988.tgz", + "integrity": "sha512-39fCpE3Z78IaIPChJsP6Lhmkbf4dWXOmzLk/KFTdRkNk/0JymRIfUynDVRndV9HoDz8PyalK1UH21ST/ivwW5Q==", + "dev": true, + "optional": true, + "peer": true + }, + "node_modules/@wdio/runner/node_modules/devtools/node_modules/which": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/which/-/which-4.0.0.tgz", + "integrity": "sha512-GlaYyEb07DPxYCKhKzplCWBJtvxZcZMrL+4UkrTSJHHPyZU4mYYTv3qaOe77H7EODLSSopAUFAc6W8U4yqvscg==", "dev": true, + "optional": true, + "peer": true, + "dependencies": { + "isexe": "^3.1.1" + }, + "bin": { + "node-which": "bin/which.js" + }, "engines": { - "node": ">=0.10.0" + "node": "^16.13.0 || >=18.0.0" } }, - "node_modules/arr-filter": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/arr-filter/-/arr-filter-1.1.2.tgz", - "integrity": "sha512-A2BETWCqhsecSvCkWAeVBFLH6sXEUGASuzkpjL3GR1SlL/PWL6M3J8EAAld2Uubmh39tvkJTqC9LeLHCUKmFXA==", + "node_modules/@wdio/runner/node_modules/edge-paths": { + "version": "3.0.5", + "resolved": "https://registry.npmjs.org/edge-paths/-/edge-paths-3.0.5.tgz", + "integrity": "sha512-sB7vSrDnFa4ezWQk9nZ/n0FdpdUuC6R1EOrlU3DL+bovcNFK28rqu2emmAUjujYEJTWIgQGqgVVWUZXMnc8iWg==", "dev": true, + "optional": true, + "peer": true, "dependencies": { - "make-iterator": "^1.0.0" + "@types/which": "^2.0.1", + "which": "^2.0.2" }, "engines": { - "node": ">=0.10.0" + "node": ">=14.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/shirshak55" } }, - "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==", + "node_modules/@wdio/runner/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, + "optional": true, + "peer": true, "engines": { - "node": ">=0.10.0" + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/arr-map": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/arr-map/-/arr-map-2.0.2.tgz", - "integrity": "sha512-tVqVTHt+Q5Xb09qRkbu+DidW1yYzz5izWS2Xm2yFm7qJnmUfz4HPzNxbHkdRJbz2lrqI7S+z17xNYdFcBBO8Hw==", + "node_modules/@wdio/runner/node_modules/glob": { + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/glob/-/glob-8.1.0.tgz", + "integrity": "sha512-r8hpEjiQEYlF2QU0df3dS+nxxSIreXQS1qRhMJM0Q5NDdR386C7jb7Hwwod8Fgiuex+k0GFjgft18yvxm5XoCQ==", "dev": true, "dependencies": { - "make-iterator": "^1.0.0" + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^5.0.1", + "once": "^1.3.0" }, "engines": { - "node": ">=0.10.0" + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" } }, - "node_modules/arr-union": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/arr-union/-/arr-union-2.1.0.tgz", - "integrity": "sha512-t5db90jq+qdgk8aFnxEkjqta0B/GHrM1pxzuuZz2zWsOXc5nKu3t+76s/PQBA8FTcM/ipspIH9jWG4OxCBc2eA==", + "node_modules/@wdio/runner/node_modules/glob/node_modules/minimatch": { + "version": "5.1.6", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-5.1.6.tgz", + "integrity": "sha512-lKwV/1brpG6mBUFHtb7NUmtABCb2WZZmm2wNiOA5hAb8VdCS4B3dtMWyvcoViccwAW/COERjXLt0zP1zXUN26g==", "dev": true, + "dependencies": { + "brace-expansion": "^2.0.1" + }, "engines": { - "node": ">=0.10.0" + "node": ">=10" } }, - "node_modules/array-differ": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/array-differ/-/array-differ-1.0.0.tgz", - "integrity": "sha512-LeZY+DZDRnvP7eMuQ6LHfCzUGxAAIViUBliK24P3hWXL6y4SortgR6Nim6xrkfSLlmH0+k+9NYNwVC2s53ZrYQ==", + "node_modules/@wdio/runner/node_modules/http-proxy-agent": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/http-proxy-agent/-/http-proxy-agent-5.0.0.tgz", + "integrity": "sha512-n2hY8YdoRE1i7r6M0w9DIw5GgZN0G25P8zLCRQ8rjXtTU3vsNFBI/vWK/UIeE6g5MUUz6avwAPXmL6Fy9D/90w==", "dev": true, + "optional": true, + "peer": true, + "dependencies": { + "@tootallnate/once": "2", + "agent-base": "6", + "debug": "4" + }, "engines": { - "node": ">=0.10.0" + "node": ">= 6" } }, - "node_modules/array-each": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/array-each/-/array-each-1.0.1.tgz", - "integrity": "sha512-zHjL5SZa68hkKHBFBK6DJCTtr9sfTCPCaph/L7tMSLcTFgy+zX7E+6q5UArbtOtMBCtxdICpfTCspRse+ywyXA==", + "node_modules/@wdio/runner/node_modules/isexe": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/isexe/-/isexe-3.1.1.tgz", + "integrity": "sha512-LpB/54B+/2J5hqQ7imZHfdU31OlgQqx7ZicVlkm9kzg9/w8GKLEcFfJl/t7DCEDueOyBAD6zCCwTO6Fzs0NoEQ==", + "dev": true, + "optional": true, + "peer": true, + "engines": { + "node": ">=16" + } + }, + "node_modules/@wdio/runner/node_modules/lighthouse-logger": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/lighthouse-logger/-/lighthouse-logger-2.0.1.tgz", + "integrity": "sha512-ioBrW3s2i97noEmnXxmUq7cjIcVRjT5HBpAYy8zE11CxU9HqlWHHeRxfeN1tn8F7OEMVPIC9x1f8t3Z7US9ehQ==", + "dev": true, + "optional": true, + "peer": true, + "dependencies": { + "debug": "^2.6.9", + "marky": "^1.2.2" + } + }, + "node_modules/@wdio/runner/node_modules/lighthouse-logger/node_modules/debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "dev": true, + "optional": true, + "peer": true, + "dependencies": { + "ms": "2.0.0" + } + }, + "node_modules/@wdio/runner/node_modules/lru-cache": { + "version": "7.18.3", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-7.18.3.tgz", + "integrity": "sha512-jumlc0BIUrS3qJGgIkWZsyfAM7NCWiBcCDhnd+3NNM5KbBmLTgHVfWBcg6W+rLUsIpzpERPsvwUP7CckAQSOoA==", + "dev": true, + "engines": { + "node": ">=12" + } + }, + "node_modules/@wdio/runner/node_modules/minimatch": { + "version": "9.0.3", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.3.tgz", + "integrity": "sha512-RHiac9mvaRw0x3AYRgDC1CxAP7HTcNrrECeA8YYJeWnpo+2Q5CegtZjaotWTWxDG3UeGA1coE05iH1mPjT/2mg==", + "dev": true, + "dependencies": { + "brace-expansion": "^2.0.1" + }, + "engines": { + "node": ">=16 || 14 >=14.17" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/@wdio/runner/node_modules/ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", + "dev": true, + "optional": true, + "peer": true + }, + "node_modules/@wdio/runner/node_modules/node-fetch": { + "version": "2.7.0", + "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.7.0.tgz", + "integrity": "sha512-c4FRfUm/dbcWZ7U+1Wq0AwCyFL+3nt2bEw05wfxSz+DWpWsitgmSgYmy2dQdWyKC1694ELPqMs/YzUSNozLt8A==", + "dev": true, + "dependencies": { + "whatwg-url": "^5.0.0" + }, + "engines": { + "node": "4.x || >=6.0.0" + }, + "peerDependencies": { + "encoding": "^0.1.0" + }, + "peerDependenciesMeta": { + "encoding": { + "optional": true + } + } + }, + "node_modules/@wdio/runner/node_modules/proxy-agent": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/proxy-agent/-/proxy-agent-6.3.0.tgz", + "integrity": "sha512-0LdR757eTj/JfuU7TL2YCuAZnxWXu3tkJbg4Oq3geW/qFNT/32T0sp2HnZ9O0lMR4q3vwAt0+xCA8SR0WAD0og==", + "dev": true, + "dependencies": { + "agent-base": "^7.0.2", + "debug": "^4.3.4", + "http-proxy-agent": "^7.0.0", + "https-proxy-agent": "^7.0.0", + "lru-cache": "^7.14.1", + "pac-proxy-agent": "^7.0.0", + "proxy-from-env": "^1.1.0", + "socks-proxy-agent": "^8.0.1" + }, + "engines": { + "node": ">= 14" + } + }, + "node_modules/@wdio/runner/node_modules/proxy-agent/node_modules/agent-base": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-7.1.0.tgz", + "integrity": "sha512-o/zjMZRhJxny7OyEF+Op8X+efiELC7k7yOjMzgfzVqOzXqkBkWI79YoTdOtsuWd5BWhAGAuOY/Xa6xpiaWXiNg==", + "dev": true, + "dependencies": { + "debug": "^4.3.4" + }, + "engines": { + "node": ">= 14" + } + }, + "node_modules/@wdio/runner/node_modules/proxy-agent/node_modules/http-proxy-agent": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/http-proxy-agent/-/http-proxy-agent-7.0.0.tgz", + "integrity": "sha512-+ZT+iBxVUQ1asugqnD6oWoRiS25AkjNfG085dKJGtGxkdwLQrMKU5wJr2bOOFAXzKcTuqq+7fZlTMgG3SRfIYQ==", + "dev": true, + "dependencies": { + "agent-base": "^7.1.0", + "debug": "^4.3.4" + }, + "engines": { + "node": ">= 14" + } + }, + "node_modules/@wdio/runner/node_modules/proxy-agent/node_modules/https-proxy-agent": { + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-7.0.2.tgz", + "integrity": "sha512-NmLNjm6ucYwtcUmL7JQC1ZQ57LmHP4lT15FQ8D61nak1rO6DH+fz5qNK2Ap5UN4ZapYICE3/0KodcLYSPsPbaA==", + "dev": true, + "dependencies": { + "agent-base": "^7.0.2", + "debug": "4" + }, + "engines": { + "node": ">= 14" + } + }, + "node_modules/@wdio/runner/node_modules/puppeteer-core": { + "version": "20.3.0", + "resolved": "https://registry.npmjs.org/puppeteer-core/-/puppeteer-core-20.3.0.tgz", + "integrity": "sha512-264pBrIui5bO6NJeOcbJrLa0OCwmA4+WK00JMrLIKTfRiqe2gx8KWTzLsjyw/bizErp3TKS7vt/I0i5fTC+mAw==", + "dev": true, + "optional": true, + "peer": true, + "dependencies": { + "@puppeteer/browsers": "1.3.0", + "chromium-bidi": "0.4.9", + "cross-fetch": "3.1.6", + "debug": "4.3.4", + "devtools-protocol": "0.0.1120988", + "ws": "8.13.0" + }, + "engines": { + "node": ">=16.0.0" + }, + "peerDependencies": { + "typescript": ">= 4.7.4" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/@wdio/runner/node_modules/readable-stream": { + "version": "3.6.2", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz", + "integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==", + "dev": true, + "dependencies": { + "inherits": "^2.0.3", + "string_decoder": "^1.1.1", + "util-deprecate": "^1.0.1" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/@wdio/runner/node_modules/serialize-error": { + "version": "11.0.3", + "resolved": "https://registry.npmjs.org/serialize-error/-/serialize-error-11.0.3.tgz", + "integrity": "sha512-2G2y++21dhj2R7iHAdd0FIzjGwuKZld+7Pl/bTU6YIkrC2ZMbVUjm+luj6A6V34Rv9XfKJDKpTWu9W4Gse1D9g==", + "dev": true, + "dependencies": { + "type-fest": "^2.12.2" + }, + "engines": { + "node": ">=14.16" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@wdio/runner/node_modules/tar-stream": { + "version": "3.1.7", + "resolved": "https://registry.npmjs.org/tar-stream/-/tar-stream-3.1.7.tgz", + "integrity": "sha512-qJj60CXt7IU1Ffyc3NJMjh6EkuCFej46zUqJ4J7pqYlThyd9bO0XBTmcOIhSzZJVWfsLks0+nle/j538YAW9RQ==", + "dev": true, + "dependencies": { + "b4a": "^1.6.4", + "fast-fifo": "^1.2.0", + "streamx": "^2.15.0" + } + }, + "node_modules/@wdio/runner/node_modules/type-fest": { + "version": "2.19.0", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-2.19.0.tgz", + "integrity": "sha512-RAH822pAdBgcNMAfWnCBU3CFZcfZ/i1eZjwFU/dsLKumyuuP3niueg2UAukXYF0E2AAoc82ZSSf9J0WQBinzHA==", + "dev": true, + "engines": { + "node": ">=12.20" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@wdio/runner/node_modules/ua-parser-js": { + "version": "1.0.37", + "resolved": "https://registry.npmjs.org/ua-parser-js/-/ua-parser-js-1.0.37.tgz", + "integrity": "sha512-bhTyI94tZofjo+Dn8SN6Zv8nBDvyXTymAdM3LDI/0IboIUwTu1rEhW7v2TfiVsoYWgkQ4kOVqnI8APUFbIQIFQ==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/ua-parser-js" + }, + { + "type": "paypal", + "url": "https://paypal.me/faisalman" + }, + { + "type": "github", + "url": "https://github.com/sponsors/faisalman" + } + ], + "optional": true, + "peer": true, + "engines": { + "node": "*" + } + }, + "node_modules/@wdio/runner/node_modules/uuid": { + "version": "9.0.1", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-9.0.1.tgz", + "integrity": "sha512-b+1eJOlsR9K8HJpow9Ok3fiWOWSIcIzXodvv0rQjVoOVNpWMpxf1wZNpt4y9h10odCNrqnYp1OBzRktckBe3sA==", + "dev": true, + "funding": [ + "https://github.com/sponsors/broofa", + "https://github.com/sponsors/ctavan" + ], + "optional": true, + "peer": true, + "bin": { + "uuid": "dist/bin/uuid" + } + }, + "node_modules/@wdio/runner/node_modules/webdriverio": { + "version": "8.29.1", + "resolved": "https://registry.npmjs.org/webdriverio/-/webdriverio-8.29.1.tgz", + "integrity": "sha512-NZK95ivXCqdPraB3FHMw6ByxnCvtgFXkjzG2l3Oq5z0IuJS2aMow3AKFIyiuG6is/deGCe+Tb8eFTCqak7UV+w==", + "dev": true, + "dependencies": { + "@types/node": "^20.1.0", + "@wdio/config": "8.29.1", + "@wdio/logger": "8.28.0", + "@wdio/protocols": "8.24.12", + "@wdio/repl": "8.24.12", + "@wdio/types": "8.29.1", + "@wdio/utils": "8.29.1", + "archiver": "^6.0.0", + "aria-query": "^5.0.0", + "css-shorthand-properties": "^1.1.1", + "css-value": "^0.0.1", + "devtools-protocol": "^0.0.1249869", + "grapheme-splitter": "^1.0.2", + "import-meta-resolve": "^4.0.0", + "is-plain-obj": "^4.1.0", + "lodash.clonedeep": "^4.5.0", + "lodash.zip": "^4.2.0", + "minimatch": "^9.0.0", + "puppeteer-core": "^20.9.0", + "query-selector-shadow-dom": "^1.0.0", + "resq": "^1.9.1", + "rgb2hex": "0.2.5", + "serialize-error": "^11.0.1", + "webdriver": "8.29.1" + }, + "engines": { + "node": "^16.13 || >=18" + }, + "peerDependencies": { + "devtools": "^8.14.0" + }, + "peerDependenciesMeta": { + "devtools": { + "optional": true + } + } + }, + "node_modules/@wdio/runner/node_modules/webdriverio/node_modules/@puppeteer/browsers": { + "version": "1.4.6", + "resolved": "https://registry.npmjs.org/@puppeteer/browsers/-/browsers-1.4.6.tgz", + "integrity": "sha512-x4BEjr2SjOPowNeiguzjozQbsc6h437ovD/wu+JpaenxVLm3jkgzHY2xOslMTp50HoTvQreMjiexiGQw1sqZlQ==", + "dev": true, + "dependencies": { + "debug": "4.3.4", + "extract-zip": "2.0.1", + "progress": "2.0.3", + "proxy-agent": "6.3.0", + "tar-fs": "3.0.4", + "unbzip2-stream": "1.4.3", + "yargs": "17.7.1" + }, + "bin": { + "browsers": "lib/cjs/main-cli.js" + }, + "engines": { + "node": ">=16.3.0" + }, + "peerDependencies": { + "typescript": ">= 4.7.4" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/@wdio/runner/node_modules/webdriverio/node_modules/chromium-bidi": { + "version": "0.4.16", + "resolved": "https://registry.npmjs.org/chromium-bidi/-/chromium-bidi-0.4.16.tgz", + "integrity": "sha512-7ZbXdWERxRxSwo3txsBjjmc/NLxqb1Bk30mRb0BMS4YIaiV6zvKZqL/UAH+DdqcDYayDWk2n/y8klkBDODrPvA==", + "dev": true, + "dependencies": { + "mitt": "3.0.0" + }, + "peerDependencies": { + "devtools-protocol": "*" + } + }, + "node_modules/@wdio/runner/node_modules/webdriverio/node_modules/cross-fetch": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/cross-fetch/-/cross-fetch-4.0.0.tgz", + "integrity": "sha512-e4a5N8lVvuLgAWgnCrLr2PP0YyDOTHa9H/Rj54dirp61qXnNq46m82bRhNqIA5VccJtWBvPTFRV3TtvHUKPB1g==", + "dev": true, + "dependencies": { + "node-fetch": "^2.6.12" + } + }, + "node_modules/@wdio/runner/node_modules/webdriverio/node_modules/devtools-protocol": { + "version": "0.0.1249869", + "resolved": "https://registry.npmjs.org/devtools-protocol/-/devtools-protocol-0.0.1249869.tgz", + "integrity": "sha512-Ctp4hInA0BEavlUoRy9mhGq0i+JSo/AwVyX2EFgZmV1kYB+Zq+EMBAn52QWu6FbRr10hRb6pBl420upbp4++vg==", + "dev": true + }, + "node_modules/@wdio/runner/node_modules/webdriverio/node_modules/puppeteer-core": { + "version": "20.9.0", + "resolved": "https://registry.npmjs.org/puppeteer-core/-/puppeteer-core-20.9.0.tgz", + "integrity": "sha512-H9fYZQzMTRrkboEfPmf7m3CLDN6JvbxXA3qTtS+dFt27tR+CsFHzPsT6pzp6lYL6bJbAPaR0HaPO6uSi+F94Pg==", + "dev": true, + "dependencies": { + "@puppeteer/browsers": "1.4.6", + "chromium-bidi": "0.4.16", + "cross-fetch": "4.0.0", + "debug": "4.3.4", + "devtools-protocol": "0.0.1147663", + "ws": "8.13.0" + }, + "engines": { + "node": ">=16.3.0" + }, + "peerDependencies": { + "typescript": ">= 4.7.4" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/@wdio/runner/node_modules/webdriverio/node_modules/puppeteer-core/node_modules/devtools-protocol": { + "version": "0.0.1147663", + "resolved": "https://registry.npmjs.org/devtools-protocol/-/devtools-protocol-0.0.1147663.tgz", + "integrity": "sha512-hyWmRrexdhbZ1tcJUGpO95ivbRhWXz++F4Ko+n21AY5PNln2ovoJw+8ZMNDTtip+CNFQfrtLVh/w4009dXO/eQ==", + "dev": true + }, + "node_modules/@wdio/runner/node_modules/webdriverio/node_modules/tar-fs": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/tar-fs/-/tar-fs-3.0.4.tgz", + "integrity": "sha512-5AFQU8b9qLfZCX9zp2duONhPmZv0hGYiBPJsyUdqMjzq/mqVpy/rEUSeHk1+YitmxugaptgBh5oDGU3VsAJq4w==", + "dev": true, + "dependencies": { + "mkdirp-classic": "^0.5.2", + "pump": "^3.0.0", + "tar-stream": "^3.1.5" + } + }, + "node_modules/@wdio/runner/node_modules/ws": { + "version": "8.13.0", + "resolved": "https://registry.npmjs.org/ws/-/ws-8.13.0.tgz", + "integrity": "sha512-x9vcZYTrFPC7aSIbj7sRCYo7L/Xb8Iy+pW0ng0wt2vCJv7M9HOMy0UoN3rr+IFC7hb7vXoqS+P9ktyLLLhO+LA==", + "dev": true, + "engines": { + "node": ">=10.0.0" + }, + "peerDependencies": { + "bufferutil": "^4.0.1", + "utf-8-validate": ">=5.0.2" + }, + "peerDependenciesMeta": { + "bufferutil": { + "optional": true + }, + "utf-8-validate": { + "optional": true + } + } + }, + "node_modules/@wdio/runner/node_modules/yargs": { + "version": "17.7.1", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.7.1.tgz", + "integrity": "sha512-cwiTb08Xuv5fqF4AovYacTFNxk62th7LKJ6BL9IGUpTJrWoU7/7WdQGTP2SjKf1dUNBGzDd28p/Yfs/GI6JrLw==", + "dev": true, + "dependencies": { + "cliui": "^8.0.1", + "escalade": "^3.1.1", + "get-caller-file": "^2.0.5", + "require-directory": "^2.1.1", + "string-width": "^4.2.3", + "y18n": "^5.0.5", + "yargs-parser": "^21.1.1" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/@wdio/runner/node_modules/zip-stream": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/zip-stream/-/zip-stream-5.0.1.tgz", + "integrity": "sha512-UfZ0oa0C8LI58wJ+moL46BDIMgCQbnsb+2PoiJYtonhBsMh2bq1eRBVkvjfVsqbEHd9/EgKPUuL9saSSsec8OA==", + "dev": true, + "dependencies": { + "archiver-utils": "^4.0.1", + "compress-commons": "^5.0.1", + "readable-stream": "^3.6.0" + }, + "engines": { + "node": ">= 12.0.0" + } + }, + "node_modules/@wdio/spec-reporter": { + "version": "8.29.1", + "resolved": "https://registry.npmjs.org/@wdio/spec-reporter/-/spec-reporter-8.29.1.tgz", + "integrity": "sha512-tuDHihrTjCxFCbSjT0jMvAarLA1MtatnCnhv0vguu3ZWXELR1uESX2KzBmpJ+chGZz3oCcKszT8HOr6Pg2a1QA==", + "dev": true, + "dependencies": { + "@wdio/reporter": "8.29.1", + "@wdio/types": "8.29.1", + "chalk": "^5.1.2", + "easy-table": "^1.2.0", + "pretty-ms": "^7.0.0" + }, + "engines": { + "node": "^16.13 || >=18" + } + }, + "node_modules/@wdio/spec-reporter/node_modules/chalk": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-5.3.0.tgz", + "integrity": "sha512-dLitG79d+GV1Nb/VYcCDFivJeK1hiukt9QjRNVOsUtTy1rR1YJsmpGGTZ3qJos+uw7WmWF4wUwBd9jxjocFC2w==", + "dev": true, + "engines": { + "node": "^12.17.0 || ^14.13 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/@wdio/types": { + "version": "8.29.1", + "resolved": "https://registry.npmjs.org/@wdio/types/-/types-8.29.1.tgz", + "integrity": "sha512-rZYzu+sK8zY1PjCEWxNu4ELJPYKDZRn7HFcYNgR122ylHygfldwkb5TioI6Pn311hQH/S+663KEeoq//Jb0f8A==", + "dev": true, + "dependencies": { + "@types/node": "^20.1.0" + }, + "engines": { + "node": "^16.13 || >=18" + } + }, + "node_modules/@wdio/utils": { + "version": "8.29.1", + "resolved": "https://registry.npmjs.org/@wdio/utils/-/utils-8.29.1.tgz", + "integrity": "sha512-Dm91DKL/ZKeZ2QogWT8Twv0p+slEgKyB/5x9/kcCG0Q2nNa+tZedTjOhryzrsPiWc+jTSBmjGE4katRXpJRFJg==", + "dev": true, + "dependencies": { + "@puppeteer/browsers": "^1.6.0", + "@wdio/logger": "8.28.0", + "@wdio/types": "8.29.1", + "decamelize": "^6.0.0", + "deepmerge-ts": "^5.1.0", + "edgedriver": "^5.3.5", + "geckodriver": "^4.2.0", + "get-port": "^7.0.0", + "import-meta-resolve": "^4.0.0", + "locate-app": "^2.1.0", + "safaridriver": "^0.1.0", + "split2": "^4.2.0", + "wait-port": "^1.0.4" + }, + "engines": { + "node": "^16.13 || >=18" + } + }, + "node_modules/@wdio/utils/node_modules/decamelize": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-6.0.0.tgz", + "integrity": "sha512-Fv96DCsdOgB6mdGl67MT5JaTNKRzrzill5OH5s8bjYJXVlcXyPYGyPsUkWyGV5p1TXI5esYIYMMeDJL0hEIwaA==", + "dev": true, + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@webassemblyjs/ast": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/ast/-/ast-1.11.1.tgz", + "integrity": "sha512-ukBh14qFLjxTQNTXocdyksN5QdM28S1CxHt2rdskFyL+xFV7VremuBLVbmCePj+URalXBENx/9Lm7lnhihtCSw==", + "dev": true, + "dependencies": { + "@webassemblyjs/helper-numbers": "1.11.1", + "@webassemblyjs/helper-wasm-bytecode": "1.11.1" + } + }, + "node_modules/@webassemblyjs/floating-point-hex-parser": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/floating-point-hex-parser/-/floating-point-hex-parser-1.11.1.tgz", + "integrity": "sha512-iGRfyc5Bq+NnNuX8b5hwBrRjzf0ocrJPI6GWFodBFzmFnyvrQ83SHKhmilCU/8Jv67i4GJZBMhEzltxzcNagtQ==", + "dev": true + }, + "node_modules/@webassemblyjs/helper-api-error": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-api-error/-/helper-api-error-1.11.1.tgz", + "integrity": "sha512-RlhS8CBCXfRUR/cwo2ho9bkheSXG0+NwooXcc3PAILALf2QLdFyj7KGsKRbVc95hZnhnERon4kW/D3SZpp6Tcg==", + "dev": true + }, + "node_modules/@webassemblyjs/helper-buffer": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-buffer/-/helper-buffer-1.11.1.tgz", + "integrity": "sha512-gwikF65aDNeeXa8JxXa2BAk+REjSyhrNC9ZwdT0f8jc4dQQeDQ7G4m0f2QCLPJiMTTO6wfDmRmj/pW0PsUvIcA==", + "dev": true + }, + "node_modules/@webassemblyjs/helper-numbers": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-numbers/-/helper-numbers-1.11.1.tgz", + "integrity": "sha512-vDkbxiB8zfnPdNK9Rajcey5C0w+QJugEglN0of+kmO8l7lDb77AnlKYQF7aarZuCrv+l0UvqL+68gSDr3k9LPQ==", + "dev": true, + "dependencies": { + "@webassemblyjs/floating-point-hex-parser": "1.11.1", + "@webassemblyjs/helper-api-error": "1.11.1", + "@xtuc/long": "4.2.2" + } + }, + "node_modules/@webassemblyjs/helper-wasm-bytecode": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-wasm-bytecode/-/helper-wasm-bytecode-1.11.1.tgz", + "integrity": "sha512-PvpoOGiJwXeTrSf/qfudJhwlvDQxFgelbMqtq52WWiXC6Xgg1IREdngmPN3bs4RoO83PnL/nFrxucXj1+BX62Q==", + "dev": true + }, + "node_modules/@webassemblyjs/helper-wasm-section": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-wasm-section/-/helper-wasm-section-1.11.1.tgz", + "integrity": "sha512-10P9No29rYX1j7F3EVPX3JvGPQPae+AomuSTPiF9eBQeChHI6iqjMIwR9JmOJXwpnn/oVGDk7I5IlskuMwU/pg==", + "dev": true, + "dependencies": { + "@webassemblyjs/ast": "1.11.1", + "@webassemblyjs/helper-buffer": "1.11.1", + "@webassemblyjs/helper-wasm-bytecode": "1.11.1", + "@webassemblyjs/wasm-gen": "1.11.1" + } + }, + "node_modules/@webassemblyjs/ieee754": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/ieee754/-/ieee754-1.11.1.tgz", + "integrity": "sha512-hJ87QIPtAMKbFq6CGTkZYJivEwZDbQUgYd3qKSadTNOhVY7p+gfP6Sr0lLRVTaG1JjFj+r3YchoqRYxNH3M0GQ==", + "dev": true, + "dependencies": { + "@xtuc/ieee754": "^1.2.0" + } + }, + "node_modules/@webassemblyjs/leb128": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/leb128/-/leb128-1.11.1.tgz", + "integrity": "sha512-BJ2P0hNZ0u+Th1YZXJpzW6miwqQUGcIHT1G/sf72gLVD9DZ5AdYTqPNbHZh6K1M5VmKvFXwGSWZADz+qBWxeRw==", + "dev": true, + "dependencies": { + "@xtuc/long": "4.2.2" + } + }, + "node_modules/@webassemblyjs/utf8": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/utf8/-/utf8-1.11.1.tgz", + "integrity": "sha512-9kqcxAEdMhiwQkHpkNiorZzqpGrodQQ2IGrHHxCy+Ozng0ofyMA0lTqiLkVs1uzTRejX+/O0EOT7KxqVPuXosQ==", + "dev": true + }, + "node_modules/@webassemblyjs/wasm-edit": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-edit/-/wasm-edit-1.11.1.tgz", + "integrity": "sha512-g+RsupUC1aTHfR8CDgnsVRVZFJqdkFHpsHMfJuWQzWU3tvnLC07UqHICfP+4XyL2tnr1amvl1Sdp06TnYCmVkA==", + "dev": true, + "dependencies": { + "@webassemblyjs/ast": "1.11.1", + "@webassemblyjs/helper-buffer": "1.11.1", + "@webassemblyjs/helper-wasm-bytecode": "1.11.1", + "@webassemblyjs/helper-wasm-section": "1.11.1", + "@webassemblyjs/wasm-gen": "1.11.1", + "@webassemblyjs/wasm-opt": "1.11.1", + "@webassemblyjs/wasm-parser": "1.11.1", + "@webassemblyjs/wast-printer": "1.11.1" + } + }, + "node_modules/@webassemblyjs/wasm-gen": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-gen/-/wasm-gen-1.11.1.tgz", + "integrity": "sha512-F7QqKXwwNlMmsulj6+O7r4mmtAlCWfO/0HdgOxSklZfQcDu0TpLiD1mRt/zF25Bk59FIjEuGAIyn5ei4yMfLhA==", + "dev": true, + "dependencies": { + "@webassemblyjs/ast": "1.11.1", + "@webassemblyjs/helper-wasm-bytecode": "1.11.1", + "@webassemblyjs/ieee754": "1.11.1", + "@webassemblyjs/leb128": "1.11.1", + "@webassemblyjs/utf8": "1.11.1" + } + }, + "node_modules/@webassemblyjs/wasm-opt": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-opt/-/wasm-opt-1.11.1.tgz", + "integrity": "sha512-VqnkNqnZlU5EB64pp1l7hdm3hmQw7Vgqa0KF/KCNO9sIpI6Fk6brDEiX+iCOYrvMuBWDws0NkTOxYEb85XQHHw==", + "dev": true, + "dependencies": { + "@webassemblyjs/ast": "1.11.1", + "@webassemblyjs/helper-buffer": "1.11.1", + "@webassemblyjs/wasm-gen": "1.11.1", + "@webassemblyjs/wasm-parser": "1.11.1" + } + }, + "node_modules/@webassemblyjs/wasm-parser": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-parser/-/wasm-parser-1.11.1.tgz", + "integrity": "sha512-rrBujw+dJu32gYB7/Lup6UhdkPx9S9SnobZzRVL7VcBH9Bt9bCBLEuX/YXOOtBsOZ4NQrRykKhffRWHvigQvOA==", + "dev": true, + "dependencies": { + "@webassemblyjs/ast": "1.11.1", + "@webassemblyjs/helper-api-error": "1.11.1", + "@webassemblyjs/helper-wasm-bytecode": "1.11.1", + "@webassemblyjs/ieee754": "1.11.1", + "@webassemblyjs/leb128": "1.11.1", + "@webassemblyjs/utf8": "1.11.1" + } + }, + "node_modules/@webassemblyjs/wast-printer": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wast-printer/-/wast-printer-1.11.1.tgz", + "integrity": "sha512-IQboUWM4eKzWW+N/jij2sRatKMh99QEelo3Eb2q0qXkvPRISAj8Qxtmw5itwqK+TTkBuUIE45AxYPToqPtL5gg==", + "dev": true, + "dependencies": { + "@webassemblyjs/ast": "1.11.1", + "@xtuc/long": "4.2.2" + } + }, + "node_modules/@xmldom/xmldom": { + "version": "0.7.8", + "resolved": "https://registry.npmjs.org/@xmldom/xmldom/-/xmldom-0.7.8.tgz", + "integrity": "sha512-PrJx38EfpitFhwmILRl37jAdBlsww6AZ6rRVK4QS7T7RHLhX7mSs647sTmgr9GIxe3qjXdesmomEgbgaokrVFg==", + "dev": true, + "engines": { + "node": ">=10.0.0" + } + }, + "node_modules/@xtuc/ieee754": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/@xtuc/ieee754/-/ieee754-1.2.0.tgz", + "integrity": "sha512-DX8nKgqcGwsc0eJSqYt5lwP4DH5FlHnmuWWBRy7X0NcaGR0ZtuyeESgMwTYVEtxmsNGY+qit4QYT/MIYTOTPeA==", + "dev": true + }, + "node_modules/@xtuc/long": { + "version": "4.2.2", + "resolved": "https://registry.npmjs.org/@xtuc/long/-/long-4.2.2.tgz", + "integrity": "sha512-NuHqBY1PB/D8xU6s/thBgOAiAP7HOYDQ32+BFZILJ8ivkUkAHQnWfn6WhL79Owj1qmUnoN/YPhktdIoucipkAQ==", + "dev": true + }, + "node_modules/abbrev": { + "version": "1.0.9", + "resolved": "https://registry.npmjs.org/abbrev/-/abbrev-1.0.9.tgz", + "integrity": "sha512-LEyx4aLEC3x6T0UguF6YILf+ntvmOaWsVfENmIW0E9H09vKlLDGelMjjSm0jkDHALj8A8quZ/HapKNigzwge+Q==", + "dev": true + }, + "node_modules/accepts": { + "version": "1.3.8", + "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.8.tgz", + "integrity": "sha512-PYAthTa2m2VKxuvSD3DPC/Gy+U+sOA1LAuT8mkmRuvw+NACSaeXEQ+NHcVF7rONl6qcaxV3Uuemwawk+7+SJLw==", + "dependencies": { + "mime-types": "~2.1.34", + "negotiator": "0.6.3" + }, + "engines": { + "node": ">= 0.6" + } + }, + "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/acorn-jsx": { + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.2.tgz", + "integrity": "sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==", + "dev": true, + "peerDependencies": { + "acorn": "^6.0.0 || ^7.0.0 || ^8.0.0" + } + }, + "node_modules/acorn-walk": { + "version": "8.2.0", + "resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-8.2.0.tgz", + "integrity": "sha512-k+iyHEuPgSw6SbuDpGQM+06HQUa04DZ3o+F6CSzXMvvI5KMvnaEqXe+YVe555R9nn6GPt404fos4wcgpw12SDA==", + "dev": true, + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/aes-decrypter": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/aes-decrypter/-/aes-decrypter-3.1.3.tgz", + "integrity": "sha512-VkG9g4BbhMBy+N5/XodDeV6F02chEk9IpgRTq/0bS80y4dzy79VH2Gtms02VXomf3HmyRe3yyJYkJ990ns+d6A==", + "dev": true, + "dependencies": { + "@babel/runtime": "^7.12.5", + "@videojs/vhs-utils": "^3.0.5", + "global": "^4.4.0", + "pkcs7": "^1.0.4" + } + }, + "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/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" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, + "node_modules/ajv-keywords": { + "version": "3.5.2", + "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-3.5.2.tgz", + "integrity": "sha512-5p6WTN0DdTGVQk6VjcEju19IgaHudalcfabD7yhDGeA6bcQnmL+CpveLJq/3hvfwd1aof6L386Ougkx6RfyMIQ==", + "dev": true, + "peerDependencies": { + "ajv": "^6.9.1" + } + }, + "node_modules/amdefine": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/amdefine/-/amdefine-1.0.1.tgz", + "integrity": "sha512-S2Hw0TtNkMJhIabBwIojKL9YHO5T0n5eNqWJ7Lrlel/zDbftQpxpapi8tZs3X1HWa+u+QeydGmzzNU0m09+Rcg==", + "dev": true, + "optional": true, + "engines": { + "node": ">=0.4.2" + } + }, + "node_modules/ansi-colors": { + "version": "4.1.3", + "resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-4.1.3.tgz", + "integrity": "sha512-/6w/C21Pm1A7aZitlI5Ni/2J6FFQN8i1Cvz3kHABAAbw93v/NlvKdVOqz7CCWz/3iv/JplRSEEZ83XION15ovw==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/ansi-cyan": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/ansi-cyan/-/ansi-cyan-0.1.1.tgz", + "integrity": "sha512-eCjan3AVo/SxZ0/MyIYRtkpxIu/H3xZN7URr1vXVrISxeyz8fUFz0FJziamK4sS8I+t35y4rHg1b2PklyBe/7A==", + "dev": true, + "dependencies": { + "ansi-wrap": "0.1.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "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, + "dependencies": { + "type-fest": "^0.21.3" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/ansi-gray": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/ansi-gray/-/ansi-gray-0.1.1.tgz", + "integrity": "sha512-HrgGIZUl8h2EHuZaU9hTR/cU5nhKxpVE1V6kdGsQ8e4zirElJ5fvtfc8N7Q1oq1aatO275i8pUFUCpNWCAnVWw==", + "dev": true, + "dependencies": { + "ansi-wrap": "0.1.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/ansi-red": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/ansi-red/-/ansi-red-0.1.1.tgz", + "integrity": "sha512-ewaIr5y+9CUTGFwZfpECUbFlGcC0GCw1oqR9RI6h1gQCd9Aj2GxSckCnPsVJnmfMZbwFYE+leZGASgkWl06Jow==", + "dev": true, + "dependencies": { + "ansi-wrap": "0.1.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "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==", + "dependencies": { + "color-convert": "^1.9.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/ansi-wrap": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/ansi-wrap/-/ansi-wrap-0.1.0.tgz", + "integrity": "sha512-ZyznvL8k/FZeQHr2T6LzcJ/+vBApDnMNZvfVFy3At0knswWd6rJ3/0Hhmpu8oqa6C92npmozs890sX9Dl6q+Qw==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "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, + "dependencies": { + "normalize-path": "^3.0.0", + "picomatch": "^2.0.4" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/append-buffer": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/append-buffer/-/append-buffer-1.0.2.tgz", + "integrity": "sha512-WLbYiXzD3y/ATLZFufV/rZvWdZOs+Z/+5v1rBZ463Jn398pa6kcde27cvozYnBoxXblGZTFfoPpsaEw0orU5BA==", + "dev": true, + "dependencies": { + "buffer-equal": "^1.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/archiver": { + "version": "5.3.1", + "resolved": "https://registry.npmjs.org/archiver/-/archiver-5.3.1.tgz", + "integrity": "sha512-8KyabkmbYrH+9ibcTScQ1xCJC/CGcugdVIwB+53f5sZziXgwUh3iXlAlANMxcZyDEfTHMe6+Z5FofV8nopXP7w==", + "dev": true, + "dependencies": { + "archiver-utils": "^2.1.0", + "async": "^3.2.3", + "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" + } + }, + "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, + "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" + } + }, + "node_modules/archiver/node_modules/async": { + "version": "3.2.4", + "resolved": "https://registry.npmjs.org/async/-/async-3.2.4.tgz", + "integrity": "sha512-iAB+JbDEGXhyIUavoDl9WP/Jj106Kz9DEn1DPgYw5ruDn0e3Wgi3sKFm55sASdGBNOQB8F59d9qQ7deqrHA8wQ==", + "dev": true + }, + "node_modules/archiver/node_modules/readable-stream": { + "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/archy": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/archy/-/archy-1.0.0.tgz", + "integrity": "sha512-Xg+9RwCg/0p32teKdGMPTPnVXKD0w3DfHnFTficozsAgsvq2XenPJq/MYpzzQ/v8zrOyJn6Ds39VA4JIDwFfqw==", + "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" + } + }, + "node_modules/aria-query": { + "version": "5.1.3", + "resolved": "https://registry.npmjs.org/aria-query/-/aria-query-5.1.3.tgz", + "integrity": "sha512-R5iJ5lkuHybztUfuOAznmboyjWq8O6sqNqtK7CLOqdydi54VNbORp49mb14KbWgG1QD3JFO9hJdZ+y4KutfdOQ==", + "dev": true, + "dependencies": { + "deep-equal": "^2.0.5" + } + }, + "node_modules/arr-diff": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/arr-diff/-/arr-diff-1.1.0.tgz", + "integrity": "sha512-OQwDZUqYaQwyyhDJHThmzId8daf4/RFNLaeh3AevmSeZ5Y7ug4Ga/yKc6l6kTZOBW781rCj103ZuTh8GAsB3+Q==", + "dev": true, + "dependencies": { + "arr-flatten": "^1.0.1", + "array-slice": "^0.2.3" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/arr-diff/node_modules/array-slice": { + "version": "0.2.3", + "resolved": "https://registry.npmjs.org/array-slice/-/array-slice-0.2.3.tgz", + "integrity": "sha512-rlVfZW/1Ph2SNySXwR9QYkChp8EkOEiTMO5Vwx60usw04i4nWemkm9RXmQqgkQFaLHsqLuADvjp6IfgL9l2M8Q==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/arr-filter": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/arr-filter/-/arr-filter-1.1.2.tgz", + "integrity": "sha512-A2BETWCqhsecSvCkWAeVBFLH6sXEUGASuzkpjL3GR1SlL/PWL6M3J8EAAld2Uubmh39tvkJTqC9LeLHCUKmFXA==", + "dev": true, + "dependencies": { + "make-iterator": "^1.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "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, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/arr-map": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/arr-map/-/arr-map-2.0.2.tgz", + "integrity": "sha512-tVqVTHt+Q5Xb09qRkbu+DidW1yYzz5izWS2Xm2yFm7qJnmUfz4HPzNxbHkdRJbz2lrqI7S+z17xNYdFcBBO8Hw==", + "dev": true, + "dependencies": { + "make-iterator": "^1.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/arr-union": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/arr-union/-/arr-union-2.1.0.tgz", + "integrity": "sha512-t5db90jq+qdgk8aFnxEkjqta0B/GHrM1pxzuuZz2zWsOXc5nKu3t+76s/PQBA8FTcM/ipspIH9jWG4OxCBc2eA==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/array-differ": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/array-differ/-/array-differ-1.0.0.tgz", + "integrity": "sha512-LeZY+DZDRnvP7eMuQ6LHfCzUGxAAIViUBliK24P3hWXL6y4SortgR6Nim6xrkfSLlmH0+k+9NYNwVC2s53ZrYQ==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/array-each": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/array-each/-/array-each-1.0.1.tgz", + "integrity": "sha512-zHjL5SZa68hkKHBFBK6DJCTtr9sfTCPCaph/L7tMSLcTFgy+zX7E+6q5UArbtOtMBCtxdICpfTCspRse+ywyXA==", "dev": true, "engines": { "node": ">=0.10.0" @@ -5721,6 +7569,24 @@ "node": ">=0.10.0" } }, + "node_modules/ast-types": { + "version": "0.13.4", + "resolved": "https://registry.npmjs.org/ast-types/-/ast-types-0.13.4.tgz", + "integrity": "sha512-x1FCFnFifvYDDzTaLII71vG5uvDwgtmDTEVWAxrgeiR8VjMONcCXJx7E+USjDtHlwFmt9MysbqgF9b9Vjr6w+w==", + "dev": true, + "dependencies": { + "tslib": "^2.0.1" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/ast-types/node_modules/tslib": { + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.2.tgz", + "integrity": "sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==", + "dev": true + }, "node_modules/astral-regex": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/astral-regex/-/astral-regex-2.0.0.tgz", @@ -5823,6 +7689,12 @@ "integrity": "sha512-xh1Rl34h6Fi1DC2WWKfxUTVqRsNnr6LsKz2+hfwDxQJWmrx8+c7ylaqBMcHfl1U1r2dsifOvKX3LQuLNZ+XSvA==", "dev": true }, + "node_modules/b4a": { + "version": "1.6.4", + "resolved": "https://registry.npmjs.org/b4a/-/b4a-1.6.4.tgz", + "integrity": "sha512-fpWrvyVHEKyeEvbKZTVOeZF3VSKKWtJxFIxX/jaVPf+cLbGUSitjb49pHLqPV2BUNNZ0LcoeEGfE/YCpyDYHIw==", + "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", @@ -6317,6 +8189,15 @@ "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", "dev": true }, + "node_modules/basic-ftp": { + "version": "5.0.4", + "resolved": "https://registry.npmjs.org/basic-ftp/-/basic-ftp-5.0.4.tgz", + "integrity": "sha512-8PzkB0arJFV4jJWSGOYR+OEic6aeKMu/osRhBULN6RY0ykby6LKhbmuQ5ublvaas5BOwboah5D87nrHyuh8PPA==", + "dev": true, + "engines": { + "node": ">=10.0.0" + } + }, "node_modules/batch": { "version": "0.6.1", "resolved": "https://registry.npmjs.org/batch/-/batch-0.6.1.tgz", @@ -6748,158 +8629,6 @@ "node": ">= 0.8" } }, - "node_modules/cac": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/cac/-/cac-3.0.4.tgz", - "integrity": "sha512-hq4rxE3NT5PlaEiVV39Z45d6MoFcQZG5dsgJqtAUeOz3408LEQAElToDkf9i5IYSCOmK0If/81dLg7nKxqPR0w==", - "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": "sha512-TIGnTpdo+E3+pCyAluZvtED5p5wCqLdezCyhPZzKPcxvFplEt4i+W7OONCKgeZFT3+y5NZZfOOS/Bdcanm1MYA==", - "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": "sha512-kmCevFghRiWM7HB5zTPULl4r9bVFSWjz62MhqizDGUrq2NWuNMQyuv4tHHoKJHs69M/MF64lEcHdYIocrdWQYA==", - "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": "sha512-U3lRVLMSlsCfjqYPbLyVv11M9CPW4I728d6TCKMAOJueEeB9/8o+eSsMnxPJD+Q+K909sdESg7C+tIkoH6on1A==", - "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/cac/node_modules/find-up": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/find-up/-/find-up-1.1.2.tgz", - "integrity": "sha512-jvElSjyuo4EMQGoTwo1uJU5pQMwTW5lS1x05zzfJuTIyLR3zwO27LYrxNg+dlvKpGOuGy/MzBdXh80g0ve5+HA==", - "dev": true, - "dependencies": { - "path-exists": "^2.0.0", - "pinkie-promise": "^2.0.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/cac/node_modules/hosted-git-info": { - "version": "2.8.9", - "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-2.8.9.tgz", - "integrity": "sha512-mxIDAb9Lsm6DoOJ7xH+5+X4y1LU/4Hi50L9C5sIswK3JzULS4bwk1FvjdBgvYR4bzT4tuUQiC15FE2f5HbLvYw==", - "dev": true - }, - "node_modules/cac/node_modules/normalize-package-data": { - "version": "2.5.0", - "resolved": "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-2.5.0.tgz", - "integrity": "sha512-/5CMN3T0R4XTj4DcGaexo+roZSdSFW/0AOOTROrjxzCG1wrWXEsGbRKevjlIL+ZDE4sZlJr5ED4YW0yqmkK+eA==", - "dev": true, - "dependencies": { - "hosted-git-info": "^2.1.4", - "resolve": "^1.10.0", - "semver": "2 || 3 || 4 || 5", - "validate-npm-package-license": "^3.0.1" - } - }, - "node_modules/cac/node_modules/path-exists": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-2.1.0.tgz", - "integrity": "sha512-yTltuKuhtNeFJKa1PiRzfLAU5182q1y4Eb4XCJ3PBqyzEDkAZRzBrKKBct682ls9reBVHf9udYLN5Nd+K1B9BQ==", - "dev": true, - "dependencies": { - "pinkie-promise": "^2.0.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/cac/node_modules/read-pkg": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-1.1.0.tgz", - "integrity": "sha512-7BGwRHqt4s/uVbuyoeejRn4YmFnYZiFl4AuaeXHlgZf3sONF0SOGlxs2Pw8g6hCKupo08RafIO5YXFNOKTfwsQ==", - "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/cac/node_modules/read-pkg-up": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/read-pkg-up/-/read-pkg-up-1.0.1.tgz", - "integrity": "sha512-WD9MTlNtI55IwYUS27iHh9tK3YoIVhxis8yKhLpTqWtml739uXc9NWTpxoHkfZf3+DkCCsXox94/VWZniuZm6A==", - "dev": true, - "dependencies": { - "find-up": "^1.0.0", - "read-pkg": "^1.0.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/cac/node_modules/semver": { - "version": "5.7.2", - "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.2.tgz", - "integrity": "sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g==", - "dev": true, - "bin": { - "semver": "bin/semver" - } - }, - "node_modules/cac/node_modules/strip-ansi": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz", - "integrity": "sha512-VhumSSbBqDTP8p2ZLKj40UjBCV4+v8bUSEpUb4KjRgWk9pbqGF4REFj6KEagidb2f/M6AzC0EmFyDNGaw9OCzg==", - "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": "sha512-KKNVtd6pCYgPIKU4cp2733HWYCpplQhddZLBUryaAHou723x+FRzQ5Df824Fj+IyyuiQTRoub4SnIFfIcrp70g==", - "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", @@ -6963,12 +8692,13 @@ } }, "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==", + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.5.tgz", + "integrity": "sha512-C3nQxfFZxFRVoJoGKKI8y3MOEo129NQ+FgQ08iye+Mk4zNZZGdjfs06bVTr+DBSlA66Q2VEcMki/cUCP4SercQ==", "dependencies": { - "function-bind": "^1.1.1", - "get-intrinsic": "^1.0.2" + "function-bind": "^1.1.2", + "get-intrinsic": "^1.2.1", + "set-function-length": "^1.1.1" }, "funding": { "url": "https://github.com/sponsors/ljharb" @@ -6992,28 +8722,6 @@ "node": ">=6" } }, - "node_modules/camelcase-keys": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/camelcase-keys/-/camelcase-keys-3.0.0.tgz", - "integrity": "sha512-U4E6A6aFyYnNW+tDt5/yIUKQURKXe3WMFPfX4FxrQFcwZ/R08AUk1xWcUtlr7oq6CV07Ji+aa69V2g7BSpblnQ==", - "dev": true, - "dependencies": { - "camelcase": "^3.0.0", - "map-obj": "^1.0.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/camelcase-keys/node_modules/camelcase": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-3.0.0.tgz", - "integrity": "sha512-4nhGqUkc4BqbBBB4Q6zLuD7lzzrHYrjKGeYaEji/3tFR5VdJu9v+LilhGIVe8wxEJPPOeWo7eg8dwY13TZ1BNg==", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, "node_modules/can-autoplay": { "version": "3.0.2", "resolved": "https://registry.npmjs.org/can-autoplay/-/can-autoplay-3.0.2.tgz", @@ -7211,6 +8919,35 @@ "node": ">=6.0" } }, + "node_modules/chromium-bidi": { + "version": "0.4.9", + "resolved": "https://registry.npmjs.org/chromium-bidi/-/chromium-bidi-0.4.9.tgz", + "integrity": "sha512-u3DC6XwgLCA9QJ5ak1voPslCmacQdulZNCPsI3qNXxSnEcZS7DFIbww+5RM2bznMEje7cc0oydavRLRvOIZtHw==", + "dev": true, + "optional": true, + "peer": true, + "dependencies": { + "mitt": "3.0.0" + }, + "peerDependencies": { + "devtools-protocol": "*" + } + }, + "node_modules/ci-info": { + "version": "3.9.0", + "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-3.9.0.tgz", + "integrity": "sha512-NIxF55hv4nSqQswkAeiOi1r83xy8JldOFDTWiug55KBu9Jnblncd2U6ViHmYgHf01TPZS77NJBhBMKdWj9HQMQ==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/sibiraj-s" + } + ], + "engines": { + "node": ">=8" + } + }, "node_modules/class-utils": { "version": "0.3.6", "resolved": "https://registry.npmjs.org/class-utils/-/class-utils-0.3.6.tgz", @@ -7328,9 +9065,9 @@ } }, "node_modules/cli-spinners": { - "version": "2.7.0", - "resolved": "https://registry.npmjs.org/cli-spinners/-/cli-spinners-2.7.0.tgz", - "integrity": "sha512-qu3pN8Y3qHNgE2AFweciB1IfMnmZ/fsNTEE+NOFjmGB2F/7rLhnhzppvpCnN4FovtP26k8lHyy9ptEbNwWFLzw==", + "version": "2.9.2", + "resolved": "https://registry.npmjs.org/cli-spinners/-/cli-spinners-2.9.2.tgz", + "integrity": "sha512-ywqV+5MmyL4E7ybXgKys4DugZbX0FC6LnwrhjuykIjnK9k8OQacQ7axGKnjDXWNhns0xot3bZI5h55H8yo9cJg==", "dev": true, "engines": { "node": ">=6" @@ -7935,6 +9672,12 @@ "node": ">=0.10.0" } }, + "node_modules/csv-writer": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/csv-writer/-/csv-writer-1.6.0.tgz", + "integrity": "sha512-NOx7YDFWEsM/fTRAJjRpPp8t+MKRVvniAg9wQlUKx20MFrPs73WLJhFf5iteqrxNYnsy924K3Iroh3yNHeYd2g==", + "dev": true + }, "node_modules/custom-event": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/custom-event/-/custom-event-1.0.1.tgz", @@ -7963,6 +9706,15 @@ "node": ">=0.10" } }, + "node_modules/data-uri-to-buffer": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/data-uri-to-buffer/-/data-uri-to-buffer-6.0.1.tgz", + "integrity": "sha512-MZd3VlchQkp8rdend6vrx7MmVDJzSNTBvghvKjirLkD+WTChA3KUf0jkE68Q4UyctNqI11zZO9/x2Yx+ub5Cvg==", + "dev": true, + "engines": { + "node": ">= 14" + } + }, "node_modules/date-format": { "version": "4.0.14", "resolved": "https://registry.npmjs.org/date-format/-/date-format-4.0.14.tgz", @@ -8138,6 +9890,15 @@ "node": ">=0.10.0" } }, + "node_modules/deepmerge-ts": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/deepmerge-ts/-/deepmerge-ts-5.1.0.tgz", + "integrity": "sha512-eS8dRJOckyo9maw9Tu5O5RUi/4inFLrnoLkBe3cPfDMx3WZioXtmOew4TXQaxq7Rhl4xjDtR7c6x8nNTxOvbFw==", + "dev": true, + "engines": { + "node": ">=16.0.0" + } + }, "node_modules/default-compare": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/default-compare/-/default-compare-1.0.0.tgz", @@ -8189,6 +9950,19 @@ "node": ">=10" } }, + "node_modules/define-data-property": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/define-data-property/-/define-data-property-1.1.1.tgz", + "integrity": "sha512-E7uGkTzkk1d0ByLeSc6ZsFS79Axg+m1P/VsgYsxHgiuc3tFSj+MjMIwe90FC4lOAZzNBdY7kkO2P2wKdsQ1vgQ==", + "dependencies": { + "get-intrinsic": "^1.2.1", + "gopd": "^1.0.1", + "has-property-descriptors": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + } + }, "node_modules/define-properties": { "version": "1.1.4", "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.1.4.tgz", @@ -8218,6 +9992,73 @@ "node": ">=0.10.0" } }, + "node_modules/degenerator": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/degenerator/-/degenerator-5.0.1.tgz", + "integrity": "sha512-TllpMR/t0M5sqCXfj85i4XaAzxmS5tVA16dqvdkMwGmzI+dXLXnw3J+3Vdv7VKw+ThlTMboK6i9rnZ6Nntj5CQ==", + "dev": true, + "dependencies": { + "ast-types": "^0.13.4", + "escodegen": "^2.1.0", + "esprima": "^4.0.1" + }, + "engines": { + "node": ">= 14" + } + }, + "node_modules/degenerator/node_modules/escodegen": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/escodegen/-/escodegen-2.1.0.tgz", + "integrity": "sha512-2NlIDTwUWJN0mRPQOdtQBzbUHvdGY2P1VXSyU83Q3xKxM7WHX2Ql8dKq782Q9TgQUNOLEzEYu9bzLNj1q88I5w==", + "dev": true, + "dependencies": { + "esprima": "^4.0.1", + "estraverse": "^5.2.0", + "esutils": "^2.0.2" + }, + "bin": { + "escodegen": "bin/escodegen.js", + "esgenerate": "bin/esgenerate.js" + }, + "engines": { + "node": ">=6.0" + }, + "optionalDependencies": { + "source-map": "~0.6.1" + } + }, + "node_modules/degenerator/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/degenerator/node_modules/estraverse": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", + "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", + "dev": true, + "engines": { + "node": ">=4.0" + } + }, + "node_modules/degenerator/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/delayed-stream": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", @@ -8274,18 +10115,6 @@ "node": ">=0.10.0" } }, - "node_modules/detect-libc": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-1.0.3.tgz", - "integrity": "sha512-pGjwhsmsp4kL2RTz08wcOlGN83otlqHeD/Z5T8GXZB+/YcpQ/dgo+lbU8ZsGxV0HIvqqxo9l7mqYwyYMD9bKDg==", - "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", @@ -8560,12 +10389,12 @@ } }, "node_modules/diff-sequences": { - "version": "26.6.2", - "resolved": "https://registry.npmjs.org/diff-sequences/-/diff-sequences-26.6.2.tgz", - "integrity": "sha512-Mv/TDa3nZ9sbc5soK+OoA74BsS3mL37yixCvUAQkiuA4Wz6YtwP/K47n2rv2ovzHZvoiQeA5FTQOschKkEwB0Q==", + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/diff-sequences/-/diff-sequences-29.6.3.tgz", + "integrity": "sha512-EjePK1srD3P08o2j4f0ExnylqRs5B9tJjcp9t1krH2qRi8CCdsYfwe9JgSLurFBWwq4uOlipzfk5fHNvwFKr8Q==", "dev": true, "engines": { - "node": ">= 10.14.2" + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" } }, "node_modules/dlv": { @@ -8813,6 +10642,18 @@ "url": "https://github.com/fb55/domutils?sponsor=1" } }, + "node_modules/dotenv": { + "version": "16.4.1", + "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-16.4.1.tgz", + "integrity": "sha512-CjA3y+Dr3FyFDOAMnxZEGtnW9KBR2M0JvvUtXNW+dYJL5ROWxP9DUHCwgFqpMk0OXCc0ljhaNTr2w/kutYIcHQ==", + "dev": true, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/motdotla/dotenv?sponsor=1" + } + }, "node_modules/dset": { "version": "3.1.2", "resolved": "https://registry.npmjs.org/dset/-/dset-3.1.2.tgz", @@ -8941,9 +10782,154 @@ "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" + "dependencies": { + "@types/which": "^1.3.2", + "which": "^2.0.2" + } + }, + "node_modules/edgedriver": { + "version": "5.3.9", + "resolved": "https://registry.npmjs.org/edgedriver/-/edgedriver-5.3.9.tgz", + "integrity": "sha512-G0wNgFMFRDnFfKaXG2R6HiyVHqhKwdQ3EgoxW3wPlns2wKqem7F+HgkWBcevN7Vz0nN4AXtskID7/6jsYDXcKw==", + "dev": true, + "hasInstallScript": true, + "dependencies": { + "@wdio/logger": "^8.16.17", + "decamelize": "^6.0.0", + "edge-paths": "^3.0.5", + "node-fetch": "^3.3.2", + "unzipper": "^0.10.14", + "which": "^4.0.0" + }, + "bin": { + "edgedriver": "bin/edgedriver.js" + } + }, + "node_modules/edgedriver/node_modules/@types/which": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/@types/which/-/which-2.0.2.tgz", + "integrity": "sha512-113D3mDkZDjo+EeUEHCFy0qniNc1ZpecGiAU7WSo7YDoSzolZIQKpYFHrPpjkB2nuyahcKfrmLXeQlh7gqJYdw==", + "dev": true + }, + "node_modules/edgedriver/node_modules/data-uri-to-buffer": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/data-uri-to-buffer/-/data-uri-to-buffer-4.0.1.tgz", + "integrity": "sha512-0R9ikRb668HB7QDxT1vkpuUBtqc53YyAwMwGeUFKRojY/NWKvdZ+9UYtRfGmhqNbRkTSVpMbmyhXipFFv2cb/A==", + "dev": true, + "engines": { + "node": ">= 12" + } + }, + "node_modules/edgedriver/node_modules/decamelize": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-6.0.0.tgz", + "integrity": "sha512-Fv96DCsdOgB6mdGl67MT5JaTNKRzrzill5OH5s8bjYJXVlcXyPYGyPsUkWyGV5p1TXI5esYIYMMeDJL0hEIwaA==", + "dev": true, + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/edgedriver/node_modules/duplexer2": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/duplexer2/-/duplexer2-0.1.4.tgz", + "integrity": "sha512-asLFVfWWtJ90ZyOUHMqk7/S2w2guQKxUI2itj3d92ADHhxUSbCMGi1f1cBcJ7xM1To+pE/Khbwo1yuNbMEPKeA==", + "dev": true, + "dependencies": { + "readable-stream": "^2.0.2" + } + }, + "node_modules/edgedriver/node_modules/edge-paths": { + "version": "3.0.5", + "resolved": "https://registry.npmjs.org/edge-paths/-/edge-paths-3.0.5.tgz", + "integrity": "sha512-sB7vSrDnFa4ezWQk9nZ/n0FdpdUuC6R1EOrlU3DL+bovcNFK28rqu2emmAUjujYEJTWIgQGqgVVWUZXMnc8iWg==", + "dev": true, + "dependencies": { + "@types/which": "^2.0.1", + "which": "^2.0.2" + }, + "engines": { + "node": ">=14.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/shirshak55" + } + }, + "node_modules/edgedriver/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/edgedriver/node_modules/node-fetch": { + "version": "3.3.2", + "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-3.3.2.tgz", + "integrity": "sha512-dRB78srN/l6gqWulah9SrxeYnxeddIG30+GOqK/9OlLVyLg3HPnr6SqOWTWOXKRwC2eGYCkZ59NNuSgvSrpgOA==", + "dev": true, + "dependencies": { + "data-uri-to-buffer": "^4.0.0", + "fetch-blob": "^3.1.4", + "formdata-polyfill": "^4.0.10" + }, + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/node-fetch" + } + }, + "node_modules/edgedriver/node_modules/unzipper": { + "version": "0.10.14", + "resolved": "https://registry.npmjs.org/unzipper/-/unzipper-0.10.14.tgz", + "integrity": "sha512-ti4wZj+0bQTiX2KmKWuwj7lhV+2n//uXEotUmGuQqrbVZSEGFMbI68+c6JCQ8aAmUWYvtHEz2A8K6wXvueR/6g==", + "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", + "graceful-fs": "^4.2.2", + "listenercount": "~1.0.1", + "readable-stream": "~2.3.6", + "setimmediate": "~1.0.4" + } + }, + "node_modules/edgedriver/node_modules/which": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/which/-/which-4.0.0.tgz", + "integrity": "sha512-GlaYyEb07DPxYCKhKzplCWBJtvxZcZMrL+4UkrTSJHHPyZU4mYYTv3qaOe77H7EODLSSopAUFAc6W8U4yqvscg==", + "dev": true, + "dependencies": { + "isexe": "^3.1.1" + }, + "bin": { + "node-which": "bin/which.js" + }, + "engines": { + "node": "^16.13.0 || >=18.0.0" + } + }, + "node_modules/edgedriver/node_modules/which/node_modules/isexe": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/isexe/-/isexe-3.1.1.tgz", + "integrity": "sha512-LpB/54B+/2J5hqQ7imZHfdU31OlgQqx7ZicVlkm9kzg9/w8GKLEcFfJl/t7DCEDueOyBAD6zCCwTO6Fzs0NoEQ==", + "dev": true, + "engines": { + "node": ">=16" } }, "node_modules/ee-first": { @@ -8952,9 +10938,9 @@ "integrity": "sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==" }, "node_modules/ejs": { - "version": "3.1.8", - "resolved": "https://registry.npmjs.org/ejs/-/ejs-3.1.8.tgz", - "integrity": "sha512-/sXZeMlhS0ArkfX2Aw780gJzXSMPnKjtspYZv+f3NiKLlubezAHDU5+9xz6gd3/NhG3txQCo6xlglmTS+oTGEQ==", + "version": "3.1.9", + "resolved": "https://registry.npmjs.org/ejs/-/ejs-3.1.9.tgz", + "integrity": "sha512-rC+QVNMJWv+MtPgkt0y+0rVEIdbtxVADApW9JXrUVlzHetgcyczP/E7DJmWJ4fJCZF2cPcBk0laWO9ZHMG3DmQ==", "dev": true, "dependencies": { "jake": "^10.8.5" @@ -9909,424 +11895,1167 @@ "integrity": "sha512-OarPfz0lFCiW4/AV2Oy1Rp9qu0iusTKqykwTspGCZtPxmF81JR4MmIebvF1F9+UOKth2ZubLQ4XGGaU+hSn99A==", "dev": true, "bin": { - "esparse": "bin/esparse.js", - "esvalidate": "bin/esvalidate.js" + "esparse": "bin/esparse.js", + "esvalidate": "bin/esvalidate.js" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "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.3.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", + "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", + "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.3.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", + "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", + "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==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/etag": { + "version": "1.8.1", + "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz", + "integrity": "sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg==", + "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": "sha512-D9rRn9y7kLPnJ+hMq7S/nhvoKwwvVJahBi2BPmx3bvbsEdK3W9ii8cBSGjP+72/LnM4n6fo3+dkCX5FeTQruXA==", + "dev": true, + "dependencies": { + "d": "1", + "es5-ext": "~0.10.14" + } + }, + "node_modules/event-stream": { + "version": "3.3.4", + "resolved": "https://registry.npmjs.org/event-stream/-/event-stream-3.3.4.tgz", + "integrity": "sha512-QHpkERcGsR0T7Qm3HNJSyXKEEj8AHNxkY3PK8TS2KJvQ7NiSHe3DDpwVKKtoYprL/AreyzFBeIkBIWChAqn60g==", + "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/event-stream/node_modules/map-stream": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/map-stream/-/map-stream-0.1.0.tgz", + "integrity": "sha512-CkYQrPYZfWnu/DAmVCpTSX/xHpKZ80eKh2lAkyA6AJTef6bW+6JpbQZN5rofum7da+SyN1bi5ctTm+lTfcCW3g==", + "dev": true + }, + "node_modules/eventemitter3": { + "version": "4.0.7", + "resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-4.0.7.tgz", + "integrity": "sha512-8guHBZCwKnFhYdHr2ysuRWErTwhoN2X8XELRlrRwpmfeY2jjuUN4taQMsULKUVo1K4DvZl+0pgfyoysHxvmvEw==", + "dev": true + }, + "node_modules/events": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/events/-/events-3.3.0.tgz", + "integrity": "sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q==", + "dev": true, + "engines": { + "node": ">=0.8.x" + } + }, + "node_modules/execa": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/execa/-/execa-1.0.0.tgz", + "integrity": "sha512-adbxcyWV46qiHyvSp50TKt05tB4tK3HcmF7/nxfAdhnox83seTDbwnaqKO4sXRy7roHAIFqJP/Rw/AuEbX61LA==", + "dev": true, + "dependencies": { + "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" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/execa/node_modules/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, + "dependencies": { + "nice-try": "^1.0.4", + "path-key": "^2.0.1", + "semver": "^5.5.0", + "shebang-command": "^1.2.0", + "which": "^1.2.9" + }, + "engines": { + "node": ">=4.8" + } + }, + "node_modules/execa/node_modules/path-key": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-2.0.1.tgz", + "integrity": "sha512-fEHGKCSmUSDPv4uoj8AlD+joPlq3peND+HRYyxFz4KPw4z926S/b8rIuFs2FYJg3BwsxJf6A9/3eIdLaYC+9Dw==", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/execa/node_modules/semver": { + "version": "5.7.2", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.2.tgz", + "integrity": "sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g==", + "dev": true, + "bin": { + "semver": "bin/semver" + } + }, + "node_modules/execa/node_modules/shebang-command": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-1.2.0.tgz", + "integrity": "sha512-EV3L1+UQWGor21OmnvojK36mhg+TyIKDh3iFBKBohr5xeXIhNBcx8oWdgkTEEQ+BEFFYdLRuqMfd5L84N1V5Vg==", + "dev": true, + "dependencies": { + "shebang-regex": "^1.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/execa/node_modules/shebang-regex": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-1.0.0.tgz", + "integrity": "sha512-wpoSFAxys6b2a2wHZ1XpDSgD7N9iVjg29Ph9uV/uaP9Ex/KXlkTZTeddxDPSYQpgvzKLGJke2UU0AzoGCjNIvQ==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/execa/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/expand-brackets": { + "version": "2.1.4", + "resolved": "https://registry.npmjs.org/expand-brackets/-/expand-brackets-2.1.4.tgz", + "integrity": "sha512-w/ozOKR9Obk3qoWeY/WDi6MFta9AoMR+zud60mdnbniMcBxRuFJyDt2LdX/14A1UABeqk+Uk+LDfUpvoGKppZA==", + "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/debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "dev": true, + "dependencies": { + "ms": "2.0.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": "sha512-Rr7ADjQZenceVOAKop6ALkkRAmH1A4Gx9hV/7ZujPUN2rkATqFO0JZLZInbAjpZYoJ1gUx8MRMQVkYemcbMSTA==", + "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": "sha512-zCnTtlxNoAiDc3gqY2aYAWFx7XWWiasuF2K8Me5WbN8otHKTUKBwjPtNpRs/rbUZm7KxWAaNj7P1a/p52GbVug==", + "dev": true, + "dependencies": { + "is-extendable": "^0.1.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/expand-brackets/node_modules/is-accessor-descriptor": { + "version": "0.1.6", + "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-0.1.6.tgz", + "integrity": "sha512-e1BM1qnDbMRG3ll2U9dSK0UMHuWOs3pY3AtcFsmvwPtKL3MML/Q86i+GilLfvqEs4GW+ExB91tQ3Ig9noDIZ+A==", + "dev": true, + "dependencies": { + "kind-of": "^3.0.2" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/expand-brackets/node_modules/is-accessor-descriptor/node_modules/kind-of": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", + "integrity": "sha512-NOW9QQXMoZGg/oqnVNoNTTIFEIid1627WCffUBJEdMxYApq7mNE7CpzucIPc+ZQg25Phej7IJSmX3hO+oblOtQ==", + "dev": true, + "dependencies": { + "is-buffer": "^1.1.5" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/expand-brackets/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-brackets/node_modules/is-data-descriptor": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-0.1.4.tgz", + "integrity": "sha512-+w9D5ulSoBNlmw9OHn3U2v51SyoCd0he+bB3xMl62oijhrspxowjU+AIcDY0N3iEJbUEkB15IlMASQsxYigvXg==", + "dev": true, + "dependencies": { + "kind-of": "^3.0.2" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/expand-brackets/node_modules/is-data-descriptor/node_modules/kind-of": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", + "integrity": "sha512-NOW9QQXMoZGg/oqnVNoNTTIFEIid1627WCffUBJEdMxYApq7mNE7CpzucIPc+ZQg25Phej7IJSmX3hO+oblOtQ==", + "dev": true, + "dependencies": { + "is-buffer": "^1.1.5" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "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/expand-brackets/node_modules/is-extendable": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-0.1.1.tgz", + "integrity": "sha512-5BMULNob1vgFX6EjQw5izWDxrecWK9AM72rugNr0TFldMOi0fj6Jk+zeKIt0xGj4cEfQIJth4w3OKWOJ4f+AFw==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/expand-brackets/node_modules/ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", + "dev": true + }, + "node_modules/expand-tilde": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/expand-tilde/-/expand-tilde-2.0.2.tgz", + "integrity": "sha512-A5EmesHW6rfnZ9ysHQjPdJRni0SRar0tjtG5MNtm9n5TUvsYU8oozprtRD4AqHxcZWWlVuAmQo2nWKfN9oyjTw==", + "dev": true, + "dependencies": { + "homedir-polyfill": "^1.0.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/expect": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/expect/-/expect-29.7.0.tgz", + "integrity": "sha512-2Zks0hf1VLFYI1kbh0I5jP3KHHyCHpkfyHBzsSXRFgl/Bg9mWYfMW8oD+PdMPlEwy5HNsR9JutYy6pMeOh61nw==", + "dev": true, + "dependencies": { + "@jest/expect-utils": "^29.7.0", + "jest-get-type": "^29.6.3", + "jest-matcher-utils": "^29.7.0", + "jest-message-util": "^29.7.0", + "jest-util": "^29.7.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/expect-webdriverio": { + "version": "4.9.3", + "resolved": "https://registry.npmjs.org/expect-webdriverio/-/expect-webdriverio-4.9.3.tgz", + "integrity": "sha512-ASHsFc/QaK5ipF4ct3e8hd3elm8wNXk/Qa3EemtYDmfUQ4uzwqDf75m/QFQpwVNCjEpkNP7Be/6X9kz7bN0P9Q==", + "dev": true, + "dependencies": { + "@vitest/snapshot": "^1.2.1", + "expect": "^29.7.0", + "jest-matcher-utils": "^29.7.0", + "lodash.isequal": "^4.5.0" + }, + "engines": { + "node": ">=16 || >=18 || >=20" + }, + "optionalDependencies": { + "@wdio/globals": "^8.27.0", + "@wdio/logger": "^8.24.12", + "webdriverio": "^8.27.0" + } + }, + "node_modules/expect-webdriverio/node_modules/@puppeteer/browsers": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/@puppeteer/browsers/-/browsers-1.3.0.tgz", + "integrity": "sha512-an3QdbNPkuU6qpxpbssxAbjRLJcF+eP4L8UqIY3+6n0sbaVxw5pz7PiCLy9g32XEZuoamUlV5ZQPnA6FxvkIHA==", + "dev": true, + "optional": true, + "peer": true, + "dependencies": { + "debug": "4.3.4", + "extract-zip": "2.0.1", + "http-proxy-agent": "5.0.0", + "https-proxy-agent": "5.0.1", + "progress": "2.0.3", + "proxy-from-env": "1.1.0", + "tar-fs": "2.1.1", + "unbzip2-stream": "1.4.3", + "yargs": "17.7.1" + }, + "bin": { + "browsers": "lib/cjs/main-cli.js" + }, + "engines": { + "node": ">=16.0.0" + }, + "peerDependencies": { + "typescript": ">= 4.7.4" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/expect-webdriverio/node_modules/@types/which": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/@types/which/-/which-2.0.2.tgz", + "integrity": "sha512-113D3mDkZDjo+EeUEHCFy0qniNc1ZpecGiAU7WSo7YDoSzolZIQKpYFHrPpjkB2nuyahcKfrmLXeQlh7gqJYdw==", + "dev": true, + "optional": true, + "peer": true + }, + "node_modules/expect-webdriverio/node_modules/archiver": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/archiver/-/archiver-6.0.1.tgz", + "integrity": "sha512-CXGy4poOLBKptiZH//VlWdFuUC1RESbdZjGjILwBuZ73P7WkAUN0htfSfBq/7k6FRFlpu7bg4JOkj1vU9G6jcQ==", + "dev": true, + "optional": true, + "dependencies": { + "archiver-utils": "^4.0.1", + "async": "^3.2.4", + "buffer-crc32": "^0.2.1", + "readable-stream": "^3.6.0", + "readdir-glob": "^1.1.2", + "tar-stream": "^3.0.0", + "zip-stream": "^5.0.1" + }, + "engines": { + "node": ">= 12.0.0" + } + }, + "node_modules/expect-webdriverio/node_modules/archiver-utils": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/archiver-utils/-/archiver-utils-4.0.1.tgz", + "integrity": "sha512-Q4Q99idbvzmgCTEAAhi32BkOyq8iVI5EwdO0PmBDSGIzzjYNdcFn7Q7k3OzbLy4kLUPXfJtG6fO2RjftXbobBg==", + "dev": true, + "optional": true, + "dependencies": { + "glob": "^8.0.0", + "graceful-fs": "^4.2.0", + "lazystream": "^1.0.0", + "lodash": "^4.17.15", + "normalize-path": "^3.0.0", + "readable-stream": "^3.6.0" + }, + "engines": { + "node": ">= 12.0.0" + } + }, + "node_modules/expect-webdriverio/node_modules/async": { + "version": "3.2.5", + "resolved": "https://registry.npmjs.org/async/-/async-3.2.5.tgz", + "integrity": "sha512-baNZyqaaLhyLVKm/DlvdW051MSgO6b8eVfIezl9E5PqWxFgzLm/wQntEW4zOytVburDEr0JlALEpdOFwvErLsg==", + "dev": true, + "optional": true + }, + "node_modules/expect-webdriverio/node_modules/brace-expansion": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", + "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", + "dev": true, + "optional": true, + "dependencies": { + "balanced-match": "^1.0.0" + } + }, + "node_modules/expect-webdriverio/node_modules/chrome-launcher": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/chrome-launcher/-/chrome-launcher-1.1.0.tgz", + "integrity": "sha512-rJYWeEAERwWIr3c3mEVXwNiODPEdMRlRxHc47B1qHPOolHZnkj7rMv1QSUfPoG6MgatWj5AxSpnKKR4QEwEQIQ==", + "dev": true, + "optional": true, + "peer": true, + "dependencies": { + "@types/node": "*", + "escape-string-regexp": "^4.0.0", + "is-wsl": "^2.2.0", + "lighthouse-logger": "^2.0.1" + }, + "bin": { + "print-chrome-path": "bin/print-chrome-path.js" + }, + "engines": { + "node": ">=12.13.0" + } + }, + "node_modules/expect-webdriverio/node_modules/compress-commons": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/compress-commons/-/compress-commons-5.0.1.tgz", + "integrity": "sha512-MPh//1cERdLtqwO3pOFLeXtpuai0Y2WCd5AhtKxznqM7WtaMYaOEMSgn45d9D10sIHSfIKE603HlOp8OPGrvag==", + "dev": true, + "optional": true, + "dependencies": { + "crc-32": "^1.2.0", + "crc32-stream": "^5.0.0", + "normalize-path": "^3.0.0", + "readable-stream": "^3.6.0" + }, + "engines": { + "node": ">= 12.0.0" + } + }, + "node_modules/expect-webdriverio/node_modules/crc32-stream": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/crc32-stream/-/crc32-stream-5.0.0.tgz", + "integrity": "sha512-B0EPa1UK+qnpBZpG+7FgPCu0J2ETLpXq09o9BkLkEAhdB6Z61Qo4pJ3JYu0c+Qi+/SAL7QThqnzS06pmSSyZaw==", + "dev": true, + "optional": true, + "dependencies": { + "crc-32": "^1.2.0", + "readable-stream": "^3.4.0" + }, + "engines": { + "node": ">= 12.0.0" + } + }, + "node_modules/expect-webdriverio/node_modules/cross-fetch": { + "version": "3.1.6", + "resolved": "https://registry.npmjs.org/cross-fetch/-/cross-fetch-3.1.6.tgz", + "integrity": "sha512-riRvo06crlE8HiqOwIpQhxwdOk4fOeR7FVM/wXoxchFEqMNUjvbs3bfo4OTgMEMHzppd4DxFBDbyySj8Cv781g==", + "dev": true, + "optional": true, + "peer": true, + "dependencies": { + "node-fetch": "^2.6.11" + } + }, + "node_modules/expect-webdriverio/node_modules/devtools": { + "version": "8.29.1", + "resolved": "https://registry.npmjs.org/devtools/-/devtools-8.29.1.tgz", + "integrity": "sha512-fbH0Z7CPK4OZSgUw2QcAppczowxtSyvFztPUmiFyi99cUadjEOwlg0aL3pBVlIDo67olYjGb8GD1M5Z4yI/P6w==", + "dev": true, + "optional": true, + "peer": true, + "dependencies": { + "@types/node": "^20.1.0", + "@wdio/config": "8.29.1", + "@wdio/logger": "8.28.0", + "@wdio/protocols": "8.24.12", + "@wdio/types": "8.29.1", + "@wdio/utils": "8.29.1", + "chrome-launcher": "^1.0.0", + "edge-paths": "^3.0.5", + "import-meta-resolve": "^4.0.0", + "puppeteer-core": "20.3.0", + "query-selector-shadow-dom": "^1.0.0", + "ua-parser-js": "^1.0.1", + "uuid": "^9.0.0", + "which": "^4.0.0" + }, + "engines": { + "node": "^16.13 || >=18" + } + }, + "node_modules/expect-webdriverio/node_modules/devtools-protocol": { + "version": "0.0.1120988", + "resolved": "https://registry.npmjs.org/devtools-protocol/-/devtools-protocol-0.0.1120988.tgz", + "integrity": "sha512-39fCpE3Z78IaIPChJsP6Lhmkbf4dWXOmzLk/KFTdRkNk/0JymRIfUynDVRndV9HoDz8PyalK1UH21ST/ivwW5Q==", + "dev": true, + "optional": true, + "peer": true + }, + "node_modules/expect-webdriverio/node_modules/devtools/node_modules/which": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/which/-/which-4.0.0.tgz", + "integrity": "sha512-GlaYyEb07DPxYCKhKzplCWBJtvxZcZMrL+4UkrTSJHHPyZU4mYYTv3qaOe77H7EODLSSopAUFAc6W8U4yqvscg==", + "dev": true, + "optional": true, + "peer": true, + "dependencies": { + "isexe": "^3.1.1" + }, + "bin": { + "node-which": "bin/which.js" }, "engines": { - "node": ">=0.10.0" + "node": "^16.13.0 || >=18.0.0" } }, - "node_modules/esquery": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.4.0.tgz", - "integrity": "sha512-cCDispWt5vHHtwMY2YrAQ4ibFkAL8RbH5YGBnZBc90MolvvfkkQcJro/aZiAQUlQ3qgrYS6D6v8Gc5G5CQsc9w==", + "node_modules/expect-webdriverio/node_modules/edge-paths": { + "version": "3.0.5", + "resolved": "https://registry.npmjs.org/edge-paths/-/edge-paths-3.0.5.tgz", + "integrity": "sha512-sB7vSrDnFa4ezWQk9nZ/n0FdpdUuC6R1EOrlU3DL+bovcNFK28rqu2emmAUjujYEJTWIgQGqgVVWUZXMnc8iWg==", "dev": true, + "optional": true, + "peer": true, "dependencies": { - "estraverse": "^5.1.0" + "@types/which": "^2.0.1", + "which": "^2.0.2" }, "engines": { - "node": ">=0.10" + "node": ">=14.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/shirshak55" } }, - "node_modules/esquery/node_modules/estraverse": { - "version": "5.3.0", - "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", - "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", + "node_modules/expect-webdriverio/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, + "optional": true, + "peer": true, "engines": { - "node": ">=4.0" + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "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==", + "node_modules/expect-webdriverio/node_modules/glob": { + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/glob/-/glob-8.1.0.tgz", + "integrity": "sha512-r8hpEjiQEYlF2QU0df3dS+nxxSIreXQS1qRhMJM0Q5NDdR386C7jb7Hwwod8Fgiuex+k0GFjgft18yvxm5XoCQ==", "dev": true, + "optional": true, "dependencies": { - "estraverse": "^5.2.0" + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^5.0.1", + "once": "^1.3.0" }, "engines": { - "node": ">=4.0" + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" } }, - "node_modules/esrecurse/node_modules/estraverse": { - "version": "5.3.0", - "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", - "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", + "node_modules/expect-webdriverio/node_modules/glob/node_modules/minimatch": { + "version": "5.1.6", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-5.1.6.tgz", + "integrity": "sha512-lKwV/1brpG6mBUFHtb7NUmtABCb2WZZmm2wNiOA5hAb8VdCS4B3dtMWyvcoViccwAW/COERjXLt0zP1zXUN26g==", "dev": true, + "optional": true, + "dependencies": { + "brace-expansion": "^2.0.1" + }, "engines": { - "node": ">=4.0" + "node": ">=10" } }, - "node_modules/estraverse": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-4.3.0.tgz", - "integrity": "sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw==", + "node_modules/expect-webdriverio/node_modules/http-proxy-agent": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/http-proxy-agent/-/http-proxy-agent-5.0.0.tgz", + "integrity": "sha512-n2hY8YdoRE1i7r6M0w9DIw5GgZN0G25P8zLCRQ8rjXtTU3vsNFBI/vWK/UIeE6g5MUUz6avwAPXmL6Fy9D/90w==", "dev": true, + "optional": true, + "peer": true, + "dependencies": { + "@tootallnate/once": "2", + "agent-base": "6", + "debug": "4" + }, "engines": { - "node": ">=4.0" + "node": ">= 6" } }, - "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==", + "node_modules/expect-webdriverio/node_modules/isexe": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/isexe/-/isexe-3.1.1.tgz", + "integrity": "sha512-LpB/54B+/2J5hqQ7imZHfdU31OlgQqx7ZicVlkm9kzg9/w8GKLEcFfJl/t7DCEDueOyBAD6zCCwTO6Fzs0NoEQ==", "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==", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/etag": { - "version": "1.8.1", - "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz", - "integrity": "sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg==", + "optional": true, + "peer": true, "engines": { - "node": ">= 0.6" + "node": ">=16" } }, - "node_modules/event-emitter": { - "version": "0.3.5", - "resolved": "https://registry.npmjs.org/event-emitter/-/event-emitter-0.3.5.tgz", - "integrity": "sha512-D9rRn9y7kLPnJ+hMq7S/nhvoKwwvVJahBi2BPmx3bvbsEdK3W9ii8cBSGjP+72/LnM4n6fo3+dkCX5FeTQruXA==", + "node_modules/expect-webdriverio/node_modules/lighthouse-logger": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/lighthouse-logger/-/lighthouse-logger-2.0.1.tgz", + "integrity": "sha512-ioBrW3s2i97noEmnXxmUq7cjIcVRjT5HBpAYy8zE11CxU9HqlWHHeRxfeN1tn8F7OEMVPIC9x1f8t3Z7US9ehQ==", "dev": true, + "optional": true, + "peer": true, "dependencies": { - "d": "1", - "es5-ext": "~0.10.14" + "debug": "^2.6.9", + "marky": "^1.2.2" } }, - "node_modules/event-stream": { - "version": "3.3.4", - "resolved": "https://registry.npmjs.org/event-stream/-/event-stream-3.3.4.tgz", - "integrity": "sha512-QHpkERcGsR0T7Qm3HNJSyXKEEj8AHNxkY3PK8TS2KJvQ7NiSHe3DDpwVKKtoYprL/AreyzFBeIkBIWChAqn60g==", + "node_modules/expect-webdriverio/node_modules/lighthouse-logger/node_modules/debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", "dev": true, + "optional": true, + "peer": 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" + "ms": "2.0.0" } }, - "node_modules/event-stream/node_modules/map-stream": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/map-stream/-/map-stream-0.1.0.tgz", - "integrity": "sha512-CkYQrPYZfWnu/DAmVCpTSX/xHpKZ80eKh2lAkyA6AJTef6bW+6JpbQZN5rofum7da+SyN1bi5ctTm+lTfcCW3g==", - "dev": true - }, - "node_modules/eventemitter3": { - "version": "4.0.7", - "resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-4.0.7.tgz", - "integrity": "sha512-8guHBZCwKnFhYdHr2ysuRWErTwhoN2X8XELRlrRwpmfeY2jjuUN4taQMsULKUVo1K4DvZl+0pgfyoysHxvmvEw==", - "dev": true - }, - "node_modules/events": { - "version": "3.3.0", - "resolved": "https://registry.npmjs.org/events/-/events-3.3.0.tgz", - "integrity": "sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q==", + "node_modules/expect-webdriverio/node_modules/lru-cache": { + "version": "7.18.3", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-7.18.3.tgz", + "integrity": "sha512-jumlc0BIUrS3qJGgIkWZsyfAM7NCWiBcCDhnd+3NNM5KbBmLTgHVfWBcg6W+rLUsIpzpERPsvwUP7CckAQSOoA==", "dev": true, + "optional": true, "engines": { - "node": ">=0.8.x" + "node": ">=12" } }, - "node_modules/execa": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/execa/-/execa-1.0.0.tgz", - "integrity": "sha512-adbxcyWV46qiHyvSp50TKt05tB4tK3HcmF7/nxfAdhnox83seTDbwnaqKO4sXRy7roHAIFqJP/Rw/AuEbX61LA==", + "node_modules/expect-webdriverio/node_modules/minimatch": { + "version": "9.0.3", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.3.tgz", + "integrity": "sha512-RHiac9mvaRw0x3AYRgDC1CxAP7HTcNrrECeA8YYJeWnpo+2Q5CegtZjaotWTWxDG3UeGA1coE05iH1mPjT/2mg==", "dev": true, + "optional": true, "dependencies": { - "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" + "brace-expansion": "^2.0.1" }, "engines": { - "node": ">=6" + "node": ">=16 || 14 >=14.17" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" } }, - "node_modules/execa/node_modules/cross-spawn": { - "version": "6.0.5", - "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-6.0.5.tgz", - "integrity": "sha512-eTVLrBSt7fjbDygz805pMnstIs2VTBNkRm0qxZd+M7A5XDdxVRWO5MxGBXZhjY4cqLYLdtrGqRf8mBPmzwSpWQ==", + "node_modules/expect-webdriverio/node_modules/ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", + "dev": true, + "optional": true, + "peer": true + }, + "node_modules/expect-webdriverio/node_modules/node-fetch": { + "version": "2.7.0", + "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.7.0.tgz", + "integrity": "sha512-c4FRfUm/dbcWZ7U+1Wq0AwCyFL+3nt2bEw05wfxSz+DWpWsitgmSgYmy2dQdWyKC1694ELPqMs/YzUSNozLt8A==", "dev": true, + "optional": true, "dependencies": { - "nice-try": "^1.0.4", - "path-key": "^2.0.1", - "semver": "^5.5.0", - "shebang-command": "^1.2.0", - "which": "^1.2.9" + "whatwg-url": "^5.0.0" }, "engines": { - "node": ">=4.8" + "node": "4.x || >=6.0.0" + }, + "peerDependencies": { + "encoding": "^0.1.0" + }, + "peerDependenciesMeta": { + "encoding": { + "optional": true + } } }, - "node_modules/execa/node_modules/path-key": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/path-key/-/path-key-2.0.1.tgz", - "integrity": "sha512-fEHGKCSmUSDPv4uoj8AlD+joPlq3peND+HRYyxFz4KPw4z926S/b8rIuFs2FYJg3BwsxJf6A9/3eIdLaYC+9Dw==", + "node_modules/expect-webdriverio/node_modules/proxy-agent": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/proxy-agent/-/proxy-agent-6.3.0.tgz", + "integrity": "sha512-0LdR757eTj/JfuU7TL2YCuAZnxWXu3tkJbg4Oq3geW/qFNT/32T0sp2HnZ9O0lMR4q3vwAt0+xCA8SR0WAD0og==", "dev": true, + "optional": true, + "dependencies": { + "agent-base": "^7.0.2", + "debug": "^4.3.4", + "http-proxy-agent": "^7.0.0", + "https-proxy-agent": "^7.0.0", + "lru-cache": "^7.14.1", + "pac-proxy-agent": "^7.0.0", + "proxy-from-env": "^1.1.0", + "socks-proxy-agent": "^8.0.1" + }, "engines": { - "node": ">=4" - } - }, - "node_modules/execa/node_modules/semver": { - "version": "5.7.2", - "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.2.tgz", - "integrity": "sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g==", - "dev": true, - "bin": { - "semver": "bin/semver" + "node": ">= 14" } }, - "node_modules/execa/node_modules/shebang-command": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-1.2.0.tgz", - "integrity": "sha512-EV3L1+UQWGor21OmnvojK36mhg+TyIKDh3iFBKBohr5xeXIhNBcx8oWdgkTEEQ+BEFFYdLRuqMfd5L84N1V5Vg==", + "node_modules/expect-webdriverio/node_modules/proxy-agent/node_modules/agent-base": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-7.1.0.tgz", + "integrity": "sha512-o/zjMZRhJxny7OyEF+Op8X+efiELC7k7yOjMzgfzVqOzXqkBkWI79YoTdOtsuWd5BWhAGAuOY/Xa6xpiaWXiNg==", "dev": true, + "optional": true, "dependencies": { - "shebang-regex": "^1.0.0" + "debug": "^4.3.4" }, "engines": { - "node": ">=0.10.0" + "node": ">= 14" } }, - "node_modules/execa/node_modules/shebang-regex": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-1.0.0.tgz", - "integrity": "sha512-wpoSFAxys6b2a2wHZ1XpDSgD7N9iVjg29Ph9uV/uaP9Ex/KXlkTZTeddxDPSYQpgvzKLGJke2UU0AzoGCjNIvQ==", + "node_modules/expect-webdriverio/node_modules/proxy-agent/node_modules/http-proxy-agent": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/http-proxy-agent/-/http-proxy-agent-7.0.0.tgz", + "integrity": "sha512-+ZT+iBxVUQ1asugqnD6oWoRiS25AkjNfG085dKJGtGxkdwLQrMKU5wJr2bOOFAXzKcTuqq+7fZlTMgG3SRfIYQ==", "dev": true, + "optional": true, + "dependencies": { + "agent-base": "^7.1.0", + "debug": "^4.3.4" + }, "engines": { - "node": ">=0.10.0" + "node": ">= 14" } }, - "node_modules/execa/node_modules/which": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/which/-/which-1.3.1.tgz", - "integrity": "sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ==", + "node_modules/expect-webdriverio/node_modules/proxy-agent/node_modules/https-proxy-agent": { + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-7.0.2.tgz", + "integrity": "sha512-NmLNjm6ucYwtcUmL7JQC1ZQ57LmHP4lT15FQ8D61nak1rO6DH+fz5qNK2Ap5UN4ZapYICE3/0KodcLYSPsPbaA==", "dev": true, + "optional": true, "dependencies": { - "isexe": "^2.0.0" + "agent-base": "^7.0.2", + "debug": "4" }, - "bin": { - "which": "bin/which" + "engines": { + "node": ">= 14" } }, - "node_modules/expand-brackets": { - "version": "2.1.4", - "resolved": "https://registry.npmjs.org/expand-brackets/-/expand-brackets-2.1.4.tgz", - "integrity": "sha512-w/ozOKR9Obk3qoWeY/WDi6MFta9AoMR+zud60mdnbniMcBxRuFJyDt2LdX/14A1UABeqk+Uk+LDfUpvoGKppZA==", + "node_modules/expect-webdriverio/node_modules/puppeteer-core": { + "version": "20.3.0", + "resolved": "https://registry.npmjs.org/puppeteer-core/-/puppeteer-core-20.3.0.tgz", + "integrity": "sha512-264pBrIui5bO6NJeOcbJrLa0OCwmA4+WK00JMrLIKTfRiqe2gx8KWTzLsjyw/bizErp3TKS7vt/I0i5fTC+mAw==", "dev": true, + "optional": true, + "peer": 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" + "@puppeteer/browsers": "1.3.0", + "chromium-bidi": "0.4.9", + "cross-fetch": "3.1.6", + "debug": "4.3.4", + "devtools-protocol": "0.0.1120988", + "ws": "8.13.0" }, "engines": { - "node": ">=0.10.0" + "node": ">=16.0.0" + }, + "peerDependencies": { + "typescript": ">= 4.7.4" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } } }, - "node_modules/expand-brackets/node_modules/debug": { - "version": "2.6.9", - "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", - "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "node_modules/expect-webdriverio/node_modules/readable-stream": { + "version": "3.6.2", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz", + "integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==", "dev": true, + "optional": true, "dependencies": { - "ms": "2.0.0" + "inherits": "^2.0.3", + "string_decoder": "^1.1.1", + "util-deprecate": "^1.0.1" + }, + "engines": { + "node": ">= 6" } }, - "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": "sha512-Rr7ADjQZenceVOAKop6ALkkRAmH1A4Gx9hV/7ZujPUN2rkATqFO0JZLZInbAjpZYoJ1gUx8MRMQVkYemcbMSTA==", + "node_modules/expect-webdriverio/node_modules/serialize-error": { + "version": "11.0.3", + "resolved": "https://registry.npmjs.org/serialize-error/-/serialize-error-11.0.3.tgz", + "integrity": "sha512-2G2y++21dhj2R7iHAdd0FIzjGwuKZld+7Pl/bTU6YIkrC2ZMbVUjm+luj6A6V34Rv9XfKJDKpTWu9W4Gse1D9g==", "dev": true, + "optional": true, "dependencies": { - "is-descriptor": "^0.1.0" + "type-fest": "^2.12.2" }, "engines": { - "node": ">=0.10.0" + "node": ">=14.16" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "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": "sha512-zCnTtlxNoAiDc3gqY2aYAWFx7XWWiasuF2K8Me5WbN8otHKTUKBwjPtNpRs/rbUZm7KxWAaNj7P1a/p52GbVug==", + "node_modules/expect-webdriverio/node_modules/tar-stream": { + "version": "3.1.7", + "resolved": "https://registry.npmjs.org/tar-stream/-/tar-stream-3.1.7.tgz", + "integrity": "sha512-qJj60CXt7IU1Ffyc3NJMjh6EkuCFej46zUqJ4J7pqYlThyd9bO0XBTmcOIhSzZJVWfsLks0+nle/j538YAW9RQ==", "dev": true, + "optional": true, "dependencies": { - "is-extendable": "^0.1.0" - }, - "engines": { - "node": ">=0.10.0" + "b4a": "^1.6.4", + "fast-fifo": "^1.2.0", + "streamx": "^2.15.0" } }, - "node_modules/expand-brackets/node_modules/is-accessor-descriptor": { - "version": "0.1.6", - "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-0.1.6.tgz", - "integrity": "sha512-e1BM1qnDbMRG3ll2U9dSK0UMHuWOs3pY3AtcFsmvwPtKL3MML/Q86i+GilLfvqEs4GW+ExB91tQ3Ig9noDIZ+A==", + "node_modules/expect-webdriverio/node_modules/type-fest": { + "version": "2.19.0", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-2.19.0.tgz", + "integrity": "sha512-RAH822pAdBgcNMAfWnCBU3CFZcfZ/i1eZjwFU/dsLKumyuuP3niueg2UAukXYF0E2AAoc82ZSSf9J0WQBinzHA==", "dev": true, - "dependencies": { - "kind-of": "^3.0.2" - }, + "optional": true, "engines": { - "node": ">=0.10.0" + "node": ">=12.20" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/expand-brackets/node_modules/is-accessor-descriptor/node_modules/kind-of": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", - "integrity": "sha512-NOW9QQXMoZGg/oqnVNoNTTIFEIid1627WCffUBJEdMxYApq7mNE7CpzucIPc+ZQg25Phej7IJSmX3hO+oblOtQ==", + "node_modules/expect-webdriverio/node_modules/ua-parser-js": { + "version": "1.0.37", + "resolved": "https://registry.npmjs.org/ua-parser-js/-/ua-parser-js-1.0.37.tgz", + "integrity": "sha512-bhTyI94tZofjo+Dn8SN6Zv8nBDvyXTymAdM3LDI/0IboIUwTu1rEhW7v2TfiVsoYWgkQ4kOVqnI8APUFbIQIFQ==", "dev": true, - "dependencies": { - "is-buffer": "^1.1.5" - }, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/ua-parser-js" + }, + { + "type": "paypal", + "url": "https://paypal.me/faisalman" + }, + { + "type": "github", + "url": "https://github.com/sponsors/faisalman" + } + ], + "optional": true, + "peer": true, "engines": { - "node": ">=0.10.0" + "node": "*" } }, - "node_modules/expand-brackets/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/expect-webdriverio/node_modules/uuid": { + "version": "9.0.1", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-9.0.1.tgz", + "integrity": "sha512-b+1eJOlsR9K8HJpow9Ok3fiWOWSIcIzXodvv0rQjVoOVNpWMpxf1wZNpt4y9h10odCNrqnYp1OBzRktckBe3sA==", + "dev": true, + "funding": [ + "https://github.com/sponsors/broofa", + "https://github.com/sponsors/ctavan" + ], + "optional": true, + "peer": true, + "bin": { + "uuid": "dist/bin/uuid" + } }, - "node_modules/expand-brackets/node_modules/is-data-descriptor": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-0.1.4.tgz", - "integrity": "sha512-+w9D5ulSoBNlmw9OHn3U2v51SyoCd0he+bB3xMl62oijhrspxowjU+AIcDY0N3iEJbUEkB15IlMASQsxYigvXg==", + "node_modules/expect-webdriverio/node_modules/webdriverio": { + "version": "8.29.1", + "resolved": "https://registry.npmjs.org/webdriverio/-/webdriverio-8.29.1.tgz", + "integrity": "sha512-NZK95ivXCqdPraB3FHMw6ByxnCvtgFXkjzG2l3Oq5z0IuJS2aMow3AKFIyiuG6is/deGCe+Tb8eFTCqak7UV+w==", "dev": true, + "optional": true, "dependencies": { - "kind-of": "^3.0.2" + "@types/node": "^20.1.0", + "@wdio/config": "8.29.1", + "@wdio/logger": "8.28.0", + "@wdio/protocols": "8.24.12", + "@wdio/repl": "8.24.12", + "@wdio/types": "8.29.1", + "@wdio/utils": "8.29.1", + "archiver": "^6.0.0", + "aria-query": "^5.0.0", + "css-shorthand-properties": "^1.1.1", + "css-value": "^0.0.1", + "devtools-protocol": "^0.0.1249869", + "grapheme-splitter": "^1.0.2", + "import-meta-resolve": "^4.0.0", + "is-plain-obj": "^4.1.0", + "lodash.clonedeep": "^4.5.0", + "lodash.zip": "^4.2.0", + "minimatch": "^9.0.0", + "puppeteer-core": "^20.9.0", + "query-selector-shadow-dom": "^1.0.0", + "resq": "^1.9.1", + "rgb2hex": "0.2.5", + "serialize-error": "^11.0.1", + "webdriver": "8.29.1" }, "engines": { - "node": ">=0.10.0" + "node": "^16.13 || >=18" + }, + "peerDependencies": { + "devtools": "^8.14.0" + }, + "peerDependenciesMeta": { + "devtools": { + "optional": true + } } }, - "node_modules/expand-brackets/node_modules/is-data-descriptor/node_modules/kind-of": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", - "integrity": "sha512-NOW9QQXMoZGg/oqnVNoNTTIFEIid1627WCffUBJEdMxYApq7mNE7CpzucIPc+ZQg25Phej7IJSmX3hO+oblOtQ==", + "node_modules/expect-webdriverio/node_modules/webdriverio/node_modules/@puppeteer/browsers": { + "version": "1.4.6", + "resolved": "https://registry.npmjs.org/@puppeteer/browsers/-/browsers-1.4.6.tgz", + "integrity": "sha512-x4BEjr2SjOPowNeiguzjozQbsc6h437ovD/wu+JpaenxVLm3jkgzHY2xOslMTp50HoTvQreMjiexiGQw1sqZlQ==", "dev": true, + "optional": true, "dependencies": { - "is-buffer": "^1.1.5" + "debug": "4.3.4", + "extract-zip": "2.0.1", + "progress": "2.0.3", + "proxy-agent": "6.3.0", + "tar-fs": "3.0.4", + "unbzip2-stream": "1.4.3", + "yargs": "17.7.1" + }, + "bin": { + "browsers": "lib/cjs/main-cli.js" }, "engines": { - "node": ">=0.10.0" + "node": ">=16.3.0" + }, + "peerDependencies": { + "typescript": ">= 4.7.4" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } } }, - "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==", + "node_modules/expect-webdriverio/node_modules/webdriverio/node_modules/chromium-bidi": { + "version": "0.4.16", + "resolved": "https://registry.npmjs.org/chromium-bidi/-/chromium-bidi-0.4.16.tgz", + "integrity": "sha512-7ZbXdWERxRxSwo3txsBjjmc/NLxqb1Bk30mRb0BMS4YIaiV6zvKZqL/UAH+DdqcDYayDWk2n/y8klkBDODrPvA==", "dev": true, + "optional": true, "dependencies": { - "is-accessor-descriptor": "^0.1.6", - "is-data-descriptor": "^0.1.4", - "kind-of": "^5.0.0" + "mitt": "3.0.0" }, - "engines": { - "node": ">=0.10.0" + "peerDependencies": { + "devtools-protocol": "*" } }, - "node_modules/expand-brackets/node_modules/is-extendable": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-0.1.1.tgz", - "integrity": "sha512-5BMULNob1vgFX6EjQw5izWDxrecWK9AM72rugNr0TFldMOi0fj6Jk+zeKIt0xGj4cEfQIJth4w3OKWOJ4f+AFw==", + "node_modules/expect-webdriverio/node_modules/webdriverio/node_modules/cross-fetch": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/cross-fetch/-/cross-fetch-4.0.0.tgz", + "integrity": "sha512-e4a5N8lVvuLgAWgnCrLr2PP0YyDOTHa9H/Rj54dirp61qXnNq46m82bRhNqIA5VccJtWBvPTFRV3TtvHUKPB1g==", "dev": true, - "engines": { - "node": ">=0.10.0" + "optional": true, + "dependencies": { + "node-fetch": "^2.6.12" } }, - "node_modules/expand-brackets/node_modules/ms": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", - "dev": true + "node_modules/expect-webdriverio/node_modules/webdriverio/node_modules/devtools-protocol": { + "version": "0.0.1249869", + "resolved": "https://registry.npmjs.org/devtools-protocol/-/devtools-protocol-0.0.1249869.tgz", + "integrity": "sha512-Ctp4hInA0BEavlUoRy9mhGq0i+JSo/AwVyX2EFgZmV1kYB+Zq+EMBAn52QWu6FbRr10hRb6pBl420upbp4++vg==", + "dev": true, + "optional": true }, - "node_modules/expand-tilde": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/expand-tilde/-/expand-tilde-2.0.2.tgz", - "integrity": "sha512-A5EmesHW6rfnZ9ysHQjPdJRni0SRar0tjtG5MNtm9n5TUvsYU8oozprtRD4AqHxcZWWlVuAmQo2nWKfN9oyjTw==", + "node_modules/expect-webdriverio/node_modules/webdriverio/node_modules/puppeteer-core": { + "version": "20.9.0", + "resolved": "https://registry.npmjs.org/puppeteer-core/-/puppeteer-core-20.9.0.tgz", + "integrity": "sha512-H9fYZQzMTRrkboEfPmf7m3CLDN6JvbxXA3qTtS+dFt27tR+CsFHzPsT6pzp6lYL6bJbAPaR0HaPO6uSi+F94Pg==", "dev": true, + "optional": true, "dependencies": { - "homedir-polyfill": "^1.0.1" + "@puppeteer/browsers": "1.4.6", + "chromium-bidi": "0.4.16", + "cross-fetch": "4.0.0", + "debug": "4.3.4", + "devtools-protocol": "0.0.1147663", + "ws": "8.13.0" }, "engines": { - "node": ">=0.10.0" + "node": ">=16.3.0" + }, + "peerDependencies": { + "typescript": ">= 4.7.4" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } } }, - "node_modules/expect": { - "version": "26.6.2", - "resolved": "https://registry.npmjs.org/expect/-/expect-26.6.2.tgz", - "integrity": "sha512-9/hlOBkQl2l/PLHJx6JjoDF6xPKcJEsUlWKb23rKE7KzeDqUZKXKNMW27KIue5JMdBV9HgmoJPcc8HtO85t9IA==", + "node_modules/expect-webdriverio/node_modules/webdriverio/node_modules/puppeteer-core/node_modules/devtools-protocol": { + "version": "0.0.1147663", + "resolved": "https://registry.npmjs.org/devtools-protocol/-/devtools-protocol-0.0.1147663.tgz", + "integrity": "sha512-hyWmRrexdhbZ1tcJUGpO95ivbRhWXz++F4Ko+n21AY5PNln2ovoJw+8ZMNDTtip+CNFQfrtLVh/w4009dXO/eQ==", + "dev": true, + "optional": true + }, + "node_modules/expect-webdriverio/node_modules/webdriverio/node_modules/tar-fs": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/tar-fs/-/tar-fs-3.0.4.tgz", + "integrity": "sha512-5AFQU8b9qLfZCX9zp2duONhPmZv0hGYiBPJsyUdqMjzq/mqVpy/rEUSeHk1+YitmxugaptgBh5oDGU3VsAJq4w==", "dev": true, + "optional": true, "dependencies": { - "@jest/types": "^26.6.2", - "ansi-styles": "^4.0.0", - "jest-get-type": "^26.3.0", - "jest-matcher-utils": "^26.6.2", - "jest-message-util": "^26.6.2", - "jest-regex-util": "^26.0.0" - }, - "engines": { - "node": ">= 10.14.2" + "mkdirp-classic": "^0.5.2", + "pump": "^3.0.0", + "tar-stream": "^3.1.5" } }, - "node_modules/expect-webdriverio": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/expect-webdriverio/-/expect-webdriverio-2.0.2.tgz", - "integrity": "sha512-dst0tqP1aZ2p7TPmbatqoIQ+7hRTw+IeKNi830XxKhu2DNNe5vQ85i9ttf9rpXgbnUf91HxKcocn4G7A5bQxDA==", + "node_modules/expect-webdriverio/node_modules/ws": { + "version": "8.13.0", + "resolved": "https://registry.npmjs.org/ws/-/ws-8.13.0.tgz", + "integrity": "sha512-x9vcZYTrFPC7aSIbj7sRCYo7L/Xb8Iy+pW0ng0wt2vCJv7M9HOMy0UoN3rr+IFC7hb7vXoqS+P9ktyLLLhO+LA==", "dev": true, - "dependencies": { - "expect": "^26.6.2", - "jest-matcher-utils": "^26.6.2" + "optional": true, + "engines": { + "node": ">=10.0.0" + }, + "peerDependencies": { + "bufferutil": "^4.0.1", + "utf-8-validate": ">=5.0.2" + }, + "peerDependenciesMeta": { + "bufferutil": { + "optional": true + }, + "utf-8-validate": { + "optional": true + } } }, - "node_modules/expect/node_modules/ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "node_modules/expect-webdriverio/node_modules/yargs": { + "version": "17.7.1", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.7.1.tgz", + "integrity": "sha512-cwiTb08Xuv5fqF4AovYacTFNxk62th7LKJ6BL9IGUpTJrWoU7/7WdQGTP2SjKf1dUNBGzDd28p/Yfs/GI6JrLw==", "dev": true, + "optional": true, "dependencies": { - "color-convert": "^2.0.1" + "cliui": "^8.0.1", + "escalade": "^3.1.1", + "get-caller-file": "^2.0.5", + "require-directory": "^2.1.1", + "string-width": "^4.2.3", + "y18n": "^5.0.5", + "yargs-parser": "^21.1.1" }, "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" + "node": ">=12" } }, - "node_modules/expect/node_modules/color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "node_modules/expect-webdriverio/node_modules/zip-stream": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/zip-stream/-/zip-stream-5.0.1.tgz", + "integrity": "sha512-UfZ0oa0C8LI58wJ+moL46BDIMgCQbnsb+2PoiJYtonhBsMh2bq1eRBVkvjfVsqbEHd9/EgKPUuL9saSSsec8OA==", "dev": true, + "optional": true, "dependencies": { - "color-name": "~1.1.4" + "archiver-utils": "^4.0.1", + "compress-commons": "^5.0.1", + "readable-stream": "^3.6.0" }, "engines": { - "node": ">=7.0.0" + "node": ">= 12.0.0" } }, - "node_modules/expect/node_modules/color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", - "dev": true - }, "node_modules/express": { "version": "4.18.2", "resolved": "https://registry.npmjs.org/express/-/express-4.18.2.tgz", @@ -10560,6 +13289,12 @@ "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", "dev": true }, + "node_modules/fast-fifo": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/fast-fifo/-/fast-fifo-1.3.2.tgz", + "integrity": "sha512-/d9sfos4yxzpwkDkuN7k2SqFKtYNmCTzgfEpz82x34IM9/zc8KGxQoXg1liNC/izpRM/MBdt44Nmx41ZWqk+FQ==", + "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", @@ -10593,17 +13328,42 @@ "pend": "~1.2.0" } }, - "node_modules/fibers": { - "version": "5.0.3", - "resolved": "https://registry.npmjs.org/fibers/-/fibers-5.0.3.tgz", - "integrity": "sha512-/qYTSoZydQkM21qZpGLDLuCq8c+B8KhuCQ1kLPvnRNhxhVbvrpmH9l2+Lblf5neDuEsY4bfT7LeO553TXQDvJw==", + "node_modules/fecha": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/fecha/-/fecha-4.2.3.tgz", + "integrity": "sha512-OP2IUU6HeYKJi3i0z4A19kHMQoLVs4Hc+DPqqxI2h/DPZHTm/vjsfC6P0b4jCMy14XizLBqvndQ+UilD7707Jw==", + "dev": true + }, + "node_modules/fetch-blob": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/fetch-blob/-/fetch-blob-3.2.0.tgz", + "integrity": "sha512-7yAQpD2UMJzLi1Dqv7qFYnPbaPx7ZfFK6PiIxQ4PfkGPyNyl2Ugx+a/umUonmKqjhM4DnfbMvdX6otXq83soQQ==", "dev": true, - "hasInstallScript": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/jimmywarting" + }, + { + "type": "paypal", + "url": "https://paypal.me/jimmywarting" + } + ], "dependencies": { - "detect-libc": "^1.0.3" + "node-domexception": "^1.0.0", + "web-streams-polyfill": "^3.0.3" }, "engines": { - "node": ">=10.0.0" + "node": "^12.20 || >= 14.13" + } + }, + "node_modules/fetch-blob/node_modules/web-streams-polyfill": { + "version": "3.3.2", + "resolved": "https://registry.npmjs.org/web-streams-polyfill/-/web-streams-polyfill-3.3.2.tgz", + "integrity": "sha512-3pRGuxRF5gpuZc0W+EpwQRmCD7gRqcDOMt688KmdlDAgAyaB1XlN0zq2njfDNm44XVdIouE7pZ6GzbdyH47uIQ==", + "dev": true, + "engines": { + "node": ">= 8" } }, "node_modules/figures": { @@ -10659,9 +13419,9 @@ } }, "node_modules/filelist/node_modules/minimatch": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-5.1.0.tgz", - "integrity": "sha512-9TPBGGak4nHfGZsPBohm9AWg6NoT7QTCehS3BIJABslyZbzxfV78QM2Y6+i741OPZIafFAaiiEMh5OyIrJPgtg==", + "version": "5.1.6", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-5.1.6.tgz", + "integrity": "sha512-lKwV/1brpG6mBUFHtb7NUmtABCb2WZZmm2wNiOA5hAb8VdCS4B3dtMWyvcoViccwAW/COERjXLt0zP1zXUN26g==", "dev": true, "dependencies": { "brace-expansion": "^2.0.1" @@ -11064,6 +13824,34 @@ "integrity": "sha512-J+ler7Ta54FwwNcx6wQRDhTIbNeyDcARMkOcguEqnEdtm0jKvN3Li3PDAb2Du3ubJYEWfYL83XMROXdsXAXycw==", "dev": true }, + "node_modules/foreground-child": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/foreground-child/-/foreground-child-3.1.1.tgz", + "integrity": "sha512-TMKDUnIte6bfb5nWv7V/caI169OHgvwjb7V4WkeUvbQQdjr5rWKqHFiKWb/fcOwB+CzBT+qbWjvj+DVwRskpIg==", + "dev": true, + "dependencies": { + "cross-spawn": "^7.0.0", + "signal-exit": "^4.0.1" + }, + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/foreground-child/node_modules/signal-exit": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-4.1.0.tgz", + "integrity": "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==", + "dev": true, + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, "node_modules/forever-agent": { "version": "0.6.1", "resolved": "https://registry.npmjs.org/forever-agent/-/forever-agent-0.6.1.tgz", @@ -11093,6 +13881,40 @@ "node": ">= 0.12" } }, + "node_modules/form-data-encoder": { + "version": "2.1.4", + "resolved": "https://registry.npmjs.org/form-data-encoder/-/form-data-encoder-2.1.4.tgz", + "integrity": "sha512-yDYSgNMraqvnxiEXO4hi88+YZxaHC6QKzb5N84iRCTDeRO7ZALpir/lVmf/uXUhnwUr2O4HU8s/n6x+yNjQkHw==", + "dev": true, + "engines": { + "node": ">= 14.17" + } + }, + "node_modules/formdata-node": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/formdata-node/-/formdata-node-5.0.1.tgz", + "integrity": "sha512-8xnIjMYGKPj+rY2BTbAmpqVpi8der/2FT4d9f7J32FlsCpO5EzZPq3C/N56zdv8KweHzVF6TGijsS1JT6r1H2g==", + "dev": true, + "dependencies": { + "node-domexception": "1.0.0", + "web-streams-polyfill": "4.0.0-beta.3" + }, + "engines": { + "node": ">= 14.17" + } + }, + "node_modules/formdata-polyfill": { + "version": "4.0.10", + "resolved": "https://registry.npmjs.org/formdata-polyfill/-/formdata-polyfill-4.0.10.tgz", + "integrity": "sha512-buewHzMvYL29jdeQTVILecSaZKnt/RJWjoZCF5OW60Z67/GmSLBkOFM7qh1PI3zFNtJbaZL5eQu1vLfazOwj4g==", + "dev": true, + "dependencies": { + "fetch-blob": "^3.1.2" + }, + "engines": { + "node": ">=12.20.0" + } + }, "node_modules/forwarded": { "version": "0.2.0", "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.2.0.tgz", @@ -11285,9 +14107,12 @@ } }, "node_modules/function-bind": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz", - "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==" + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz", + "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==", + "funding": { + "url": "https://github.com/sponsors/ljharb" + } }, "node_modules/function.prototype.name": { "version": "1.1.5", @@ -11334,6 +14159,166 @@ "node": ">= 4.0.0" } }, + "node_modules/geckodriver": { + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/geckodriver/-/geckodriver-4.3.1.tgz", + "integrity": "sha512-ol7JLsj55o5k+z7YzeSy2mdJROXMAxIa+uzr3A1yEMr5HISqQOTslE3ZeARcxR4jpAY3fxmHM+sq32qbe/eXfA==", + "dev": true, + "hasInstallScript": true, + "dependencies": { + "@wdio/logger": "^8.24.12", + "decamelize": "^6.0.0", + "http-proxy-agent": "^7.0.0", + "https-proxy-agent": "^7.0.2", + "node-fetch": "^3.3.2", + "tar-fs": "^3.0.4", + "unzipper": "^0.10.14", + "which": "^4.0.0" + }, + "bin": { + "geckodriver": "bin/geckodriver.js" + }, + "engines": { + "node": "^16.13 || >=18 || >=20" + } + }, + "node_modules/geckodriver/node_modules/agent-base": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-7.1.0.tgz", + "integrity": "sha512-o/zjMZRhJxny7OyEF+Op8X+efiELC7k7yOjMzgfzVqOzXqkBkWI79YoTdOtsuWd5BWhAGAuOY/Xa6xpiaWXiNg==", + "dev": true, + "dependencies": { + "debug": "^4.3.4" + }, + "engines": { + "node": ">= 14" + } + }, + "node_modules/geckodriver/node_modules/data-uri-to-buffer": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/data-uri-to-buffer/-/data-uri-to-buffer-4.0.1.tgz", + "integrity": "sha512-0R9ikRb668HB7QDxT1vkpuUBtqc53YyAwMwGeUFKRojY/NWKvdZ+9UYtRfGmhqNbRkTSVpMbmyhXipFFv2cb/A==", + "dev": true, + "engines": { + "node": ">= 12" + } + }, + "node_modules/geckodriver/node_modules/decamelize": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-6.0.0.tgz", + "integrity": "sha512-Fv96DCsdOgB6mdGl67MT5JaTNKRzrzill5OH5s8bjYJXVlcXyPYGyPsUkWyGV5p1TXI5esYIYMMeDJL0hEIwaA==", + "dev": true, + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/geckodriver/node_modules/duplexer2": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/duplexer2/-/duplexer2-0.1.4.tgz", + "integrity": "sha512-asLFVfWWtJ90ZyOUHMqk7/S2w2guQKxUI2itj3d92ADHhxUSbCMGi1f1cBcJ7xM1To+pE/Khbwo1yuNbMEPKeA==", + "dev": true, + "dependencies": { + "readable-stream": "^2.0.2" + } + }, + "node_modules/geckodriver/node_modules/https-proxy-agent": { + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-7.0.2.tgz", + "integrity": "sha512-NmLNjm6ucYwtcUmL7JQC1ZQ57LmHP4lT15FQ8D61nak1rO6DH+fz5qNK2Ap5UN4ZapYICE3/0KodcLYSPsPbaA==", + "dev": true, + "dependencies": { + "agent-base": "^7.0.2", + "debug": "4" + }, + "engines": { + "node": ">= 14" + } + }, + "node_modules/geckodriver/node_modules/isexe": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/isexe/-/isexe-3.1.1.tgz", + "integrity": "sha512-LpB/54B+/2J5hqQ7imZHfdU31OlgQqx7ZicVlkm9kzg9/w8GKLEcFfJl/t7DCEDueOyBAD6zCCwTO6Fzs0NoEQ==", + "dev": true, + "engines": { + "node": ">=16" + } + }, + "node_modules/geckodriver/node_modules/node-fetch": { + "version": "3.3.2", + "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-3.3.2.tgz", + "integrity": "sha512-dRB78srN/l6gqWulah9SrxeYnxeddIG30+GOqK/9OlLVyLg3HPnr6SqOWTWOXKRwC2eGYCkZ59NNuSgvSrpgOA==", + "dev": true, + "dependencies": { + "data-uri-to-buffer": "^4.0.0", + "fetch-blob": "^3.1.4", + "formdata-polyfill": "^4.0.10" + }, + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/node-fetch" + } + }, + "node_modules/geckodriver/node_modules/tar-fs": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/tar-fs/-/tar-fs-3.0.4.tgz", + "integrity": "sha512-5AFQU8b9qLfZCX9zp2duONhPmZv0hGYiBPJsyUdqMjzq/mqVpy/rEUSeHk1+YitmxugaptgBh5oDGU3VsAJq4w==", + "dev": true, + "dependencies": { + "mkdirp-classic": "^0.5.2", + "pump": "^3.0.0", + "tar-stream": "^3.1.5" + } + }, + "node_modules/geckodriver/node_modules/tar-stream": { + "version": "3.1.7", + "resolved": "https://registry.npmjs.org/tar-stream/-/tar-stream-3.1.7.tgz", + "integrity": "sha512-qJj60CXt7IU1Ffyc3NJMjh6EkuCFej46zUqJ4J7pqYlThyd9bO0XBTmcOIhSzZJVWfsLks0+nle/j538YAW9RQ==", + "dev": true, + "dependencies": { + "b4a": "^1.6.4", + "fast-fifo": "^1.2.0", + "streamx": "^2.15.0" + } + }, + "node_modules/geckodriver/node_modules/unzipper": { + "version": "0.10.14", + "resolved": "https://registry.npmjs.org/unzipper/-/unzipper-0.10.14.tgz", + "integrity": "sha512-ti4wZj+0bQTiX2KmKWuwj7lhV+2n//uXEotUmGuQqrbVZSEGFMbI68+c6JCQ8aAmUWYvtHEz2A8K6wXvueR/6g==", + "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", + "graceful-fs": "^4.2.2", + "listenercount": "~1.0.1", + "readable-stream": "~2.3.6", + "setimmediate": "~1.0.4" + } + }, + "node_modules/geckodriver/node_modules/which": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/which/-/which-4.0.0.tgz", + "integrity": "sha512-GlaYyEb07DPxYCKhKzplCWBJtvxZcZMrL+4UkrTSJHHPyZU4mYYTv3qaOe77H7EODLSSopAUFAc6W8U4yqvscg==", + "dev": true, + "dependencies": { + "isexe": "^3.1.1" + }, + "bin": { + "node-which": "bin/which.js" + }, + "engines": { + "node": "^16.13.0 || >=18.0.0" + } + }, "node_modules/gensync": { "version": "1.0.0-beta.2", "resolved": "https://registry.npmjs.org/gensync/-/gensync-1.0.0-beta.2.tgz", @@ -11361,13 +14346,14 @@ } }, "node_modules/get-intrinsic": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.1.3.tgz", - "integrity": "sha512-QJVz1Tj7MS099PevUG5jvnt9tSkXN8K14dxQlikJuPt4uD9hHAHjLyLBiLR5zELelBdD9QNRAXZzsJx0WaDL9A==", + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.2.2.tgz", + "integrity": "sha512-0gSo4ml/0j98Y3lngkFEot/zhiCeWsbYIlZ+uZOVgzLyLaUw7wxUL+nCTP0XJvJg1AXulJRI3UJi8GsbDuxdGA==", "dependencies": { - "function-bind": "^1.1.1", - "has": "^1.0.3", - "has-symbols": "^1.0.3" + "function-bind": "^1.1.2", + "has-proto": "^1.0.1", + "has-symbols": "^1.0.3", + "hasown": "^2.0.0" }, "funding": { "url": "https://github.com/sponsors/ljharb" @@ -11383,12 +14369,12 @@ } }, "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==", + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/get-port/-/get-port-7.0.0.tgz", + "integrity": "sha512-mDHFgApoQd+azgMdwylJrv2DX47ywGq1i5VFJE7fZ0dttNq3iQMfsU4IvEgBHojA3KqEudyu7Vq+oN8kNaNkWw==", "dev": true, "engines": { - "node": ">=8" + "node": ">=16" }, "funding": { "url": "https://github.com/sponsors/sindresorhus" @@ -11422,6 +14408,53 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/get-uri": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/get-uri/-/get-uri-6.0.2.tgz", + "integrity": "sha512-5KLucCJobh8vBY1K07EFV4+cPZH3mrV9YeAruUseCQKHB58SGjjT2l9/eA9LD082IiuMjSlFJEcdJ27TXvbZNw==", + "dev": true, + "dependencies": { + "basic-ftp": "^5.0.2", + "data-uri-to-buffer": "^6.0.0", + "debug": "^4.3.4", + "fs-extra": "^8.1.0" + }, + "engines": { + "node": ">= 14" + } + }, + "node_modules/get-uri/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/get-uri/node_modules/jsonfile": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-4.0.0.tgz", + "integrity": "sha512-m6F1R3z8jjlf2imQHS2Qez5sjKWQzbuuhuJ/FKYFRZvPE3PuHcSMVZzfsLhGVOkfd20obL5SWEBew5ShlquNxg==", + "dev": true, + "optionalDependencies": { + "graceful-fs": "^4.1.6" + } + }, + "node_modules/get-uri/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/get-value": { "version": "2.0.6", "resolved": "https://registry.npmjs.org/get-value/-/get-value-2.0.6.tgz", @@ -11440,6 +14473,15 @@ "assert-plus": "^1.0.0" } }, + "node_modules/git-repo-info": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/git-repo-info/-/git-repo-info-2.1.1.tgz", + "integrity": "sha512-8aCohiDo4jwjOwma4FmYFd3i97urZulL8XL24nIPxuE+GZnfsAyy/g2Shqx6OjUiFKUXZM+Yy+KHnOmmA3FVcg==", + "dev": true, + "engines": { + "node": ">= 4.0" + } + }, "node_modules/git-up": { "version": "7.0.0", "resolved": "https://registry.npmjs.org/git-up/-/git-up-7.0.0.tgz", @@ -11459,6 +14501,21 @@ "git-up": "^7.0.0" } }, + "node_modules/gitconfiglocal": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/gitconfiglocal/-/gitconfiglocal-2.1.0.tgz", + "integrity": "sha512-qoerOEliJn3z+Zyn1HW2F6eoYJqKwS6MgC9cztTLUB/xLWX8gD/6T60pKn4+t/d6tP7JlybI7Z3z+I572CR/Vg==", + "dev": true, + "dependencies": { + "ini": "^1.3.2" + } + }, + "node_modules/gitconfiglocal/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/github-slugger": { "version": "1.4.0", "resolved": "https://registry.npmjs.org/github-slugger/-/github-slugger-1.4.0.tgz", @@ -11994,6 +15051,17 @@ "node": ">= 0.10" } }, + "node_modules/gopd": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.0.1.tgz", + "integrity": "sha512-d65bNlIadxvpb/A2abVdlqKqV563juRnZ1Wtk6s1sIR8uNsXR70xqIzVqxVf1eTqDunwT2MkczEeaezCKTZhwA==", + "dependencies": { + "get-intrinsic": "^1.1.3" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/got": { "version": "11.8.5", "resolved": "https://registry.npmjs.org/got/-/got-11.8.5.tgz", @@ -12031,15 +15099,6 @@ "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", @@ -13761,12 +16820,22 @@ } }, "node_modules/has-property-descriptors": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/has-property-descriptors/-/has-property-descriptors-1.0.0.tgz", - "integrity": "sha512-62DVLZGoiEBDHQyqG4w9xCuZ7eJEwNmJRWw2VY84Oedb7WFcA27fiEVe8oUQx9hAUJ4ekurquucTGwsyO1XGdQ==", - "dev": true, + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/has-property-descriptors/-/has-property-descriptors-1.0.1.tgz", + "integrity": "sha512-VsX8eaIewvas0xnvinAe9bw4WfIeODpGYikiWYLH+dma0Jw6KHYqWiWfhQlgOVK8D6PvjubK5Uc4P0iIhIcNVg==", "dependencies": { - "get-intrinsic": "^1.1.1" + "get-intrinsic": "^1.2.2" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/has-proto": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/has-proto/-/has-proto-1.0.1.tgz", + "integrity": "sha512-7qE+iP+O+bgF9clE5+UoBFzE65mlBiVj3tKCrlNQ0Ogwm0BjpT/gK4SlLYDMybDh5I3TCTKnPPa0oMG7JDYrhg==", + "engines": { + "node": ">= 0.4" }, "funding": { "url": "https://github.com/sponsors/ljharb" @@ -13867,6 +16936,17 @@ "node": ">=0.10.0" } }, + "node_modules/hasown": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.0.tgz", + "integrity": "sha512-vUptKVTpIJhcczKBbgnS+RtcuYMB8+oNzPK2/Hp3hanz8JmpATdmmgLgSaadVREkDm+e2giHwY3ZRkyjSIDDFA==", + "dependencies": { + "function-bind": "^1.1.2" + }, + "engines": { + "node": ">= 0.4" + } + }, "node_modules/hast-util-is-element": { "version": "2.1.2", "resolved": "https://registry.npmjs.org/hast-util-is-element/-/hast-util-is-element-2.1.2.tgz", @@ -13935,6 +17015,12 @@ "he": "bin/he" } }, + "node_modules/headers-utils": { + "version": "1.2.5", + "resolved": "https://registry.npmjs.org/headers-utils/-/headers-utils-1.2.5.tgz", + "integrity": "sha512-DAzV5P/pk3wTU/8TLZN+zFTDv4Xa1QDTU8pRvovPetcOMbmqq8CwsAvZBLPZHH6usxyy31zMp7I4aCYb6XIf6w==", + "dev": true + }, "node_modules/highlight.js": { "version": "11.6.0", "resolved": "https://registry.npmjs.org/highlight.js/-/highlight.js-11.6.0.tgz", @@ -14038,6 +17124,31 @@ "node": ">=8.0.0" } }, + "node_modules/http-proxy-agent": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/http-proxy-agent/-/http-proxy-agent-7.0.0.tgz", + "integrity": "sha512-+ZT+iBxVUQ1asugqnD6oWoRiS25AkjNfG085dKJGtGxkdwLQrMKU5wJr2bOOFAXzKcTuqq+7fZlTMgG3SRfIYQ==", + "dev": true, + "dependencies": { + "agent-base": "^7.1.0", + "debug": "^4.3.4" + }, + "engines": { + "node": ">= 14" + } + }, + "node_modules/http-proxy-agent/node_modules/agent-base": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-7.1.0.tgz", + "integrity": "sha512-o/zjMZRhJxny7OyEF+Op8X+efiELC7k7yOjMzgfzVqOzXqkBkWI79YoTdOtsuWd5BWhAGAuOY/Xa6xpiaWXiNg==", + "dev": true, + "dependencies": { + "debug": "^4.3.4" + }, + "engines": { + "node": ">= 14" + } + }, "node_modules/http-signature": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/http-signature/-/http-signature-1.2.0.tgz", @@ -14079,6 +17190,15 @@ "node": ">= 6" } }, + "node_modules/human-signals": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/human-signals/-/human-signals-5.0.0.tgz", + "integrity": "sha512-AXcZb6vzzrFAUE61HnN4mpLqd/cSIwNQjtNWR0euPm6y0iqx3G4gOXaIDdtdDwZmhwe82LA6+zinmW4UBWVePQ==", + "dev": true, + "engines": { + "node": ">=16.17.0" + } + }, "node_modules/iconv-lite": { "version": "0.4.24", "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz", @@ -14144,6 +17264,16 @@ "node": ">=4" } }, + "node_modules/import-meta-resolve": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/import-meta-resolve/-/import-meta-resolve-4.0.0.tgz", + "integrity": "sha512-okYUR7ZQPH+efeuMJGlq4f8ubUgO50kByRPyt/Cy1Io4PSRsPjxME+YlVaCOx+NIToW7hCsZNFJyTPFFKepRSA==", + "dev": true, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, "node_modules/imurmurhash": { "version": "0.1.4", "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", @@ -14153,15 +17283,6 @@ "node": ">=0.8.19" } }, - "node_modules/indent-string": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/indent-string/-/indent-string-3.2.0.tgz", - "integrity": "sha512-BYqTHXTGUIvg7t1r4sJNKcbDZkL92nkXA8YtRpbjFHRHGDL/NtUeiBJMeE60kIFN/Mg8ESaWQvftaYMGJzQZCQ==", - "dev": true, - "engines": { - "node": ">=4" - } - }, "node_modules/individual": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/individual/-/individual-2.0.0.tgz", @@ -14193,29 +17314,29 @@ } }, "node_modules/inquirer": { - "version": "8.2.5", - "resolved": "https://registry.npmjs.org/inquirer/-/inquirer-8.2.5.tgz", - "integrity": "sha512-QAgPDQMEgrDssk1XiwwHoOGYF9BAbUcc1+j+FhEvaOt8/cKRqyLn0U5qA6F74fGhTMGxf92pOvPBeh29jQJDTQ==", + "version": "9.2.12", + "resolved": "https://registry.npmjs.org/inquirer/-/inquirer-9.2.12.tgz", + "integrity": "sha512-mg3Fh9g2zfuVWJn6lhST0O7x4n03k7G8Tx5nvikJkbq8/CK47WDVm+UznF0G6s5Zi0KcyUisr6DU8T67N5U+1Q==", "dev": true, "dependencies": { - "ansi-escapes": "^4.2.1", - "chalk": "^4.1.1", + "@ljharb/through": "^2.3.11", + "ansi-escapes": "^4.3.2", + "chalk": "^5.3.0", "cli-cursor": "^3.1.0", - "cli-width": "^3.0.0", - "external-editor": "^3.0.3", - "figures": "^3.0.0", + "cli-width": "^4.1.0", + "external-editor": "^3.1.0", + "figures": "^5.0.0", "lodash": "^4.17.21", - "mute-stream": "0.0.8", + "mute-stream": "1.0.0", "ora": "^5.4.1", - "run-async": "^2.4.0", - "rxjs": "^7.5.5", - "string-width": "^4.1.0", - "strip-ansi": "^6.0.0", - "through": "^2.3.6", - "wrap-ansi": "^7.0.0" + "run-async": "^3.0.0", + "rxjs": "^7.8.1", + "string-width": "^4.2.3", + "strip-ansi": "^6.0.1", + "wrap-ansi": "^6.2.0" }, "engines": { - "node": ">=12.0.0" + "node": ">=14.18.0" } }, "node_modules/inquirer/node_modules/ansi-styles": { @@ -14234,21 +17355,26 @@ } }, "node_modules/inquirer/node_modules/chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-5.3.0.tgz", + "integrity": "sha512-dLitG79d+GV1Nb/VYcCDFivJeK1hiukt9QjRNVOsUtTy1rR1YJsmpGGTZ3qJos+uw7WmWF4wUwBd9jxjocFC2w==", "dev": true, - "dependencies": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - }, "engines": { - "node": ">=10" + "node": "^12.17.0 || ^14.13 || >=16.0.0" }, "funding": { "url": "https://github.com/chalk/chalk?sponsor=1" } }, + "node_modules/inquirer/node_modules/cli-width": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/cli-width/-/cli-width-4.1.0.tgz", + "integrity": "sha512-ouuZd4/dm2Sw5Gmqy6bGyNNNe1qt9RpmxveLSO7KcgsTnU7RXfsw+/bukWGo1abgBiMAic068rclZsO4IWmmxQ==", + "dev": true, + "engines": { + "node": ">= 12" + } + }, "node_modules/inquirer/node_modules/color-convert": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", @@ -14267,42 +17393,93 @@ "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==", + "node_modules/inquirer/node_modules/escape-string-regexp": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-5.0.0.tgz", + "integrity": "sha512-/veY75JbMK4j1yjvuUxuVsiS/hr/4iHs9FTT6cgTexxdE0Ly/glccBAkloH/DofkjRbZU3bnoj38mOmhkZ0lHw==", "dev": true, "engines": { - "node": ">=8" + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/inquirer/node_modules/figures": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/figures/-/figures-5.0.0.tgz", + "integrity": "sha512-ej8ksPF4x6e5wvK9yevct0UCXh8TTFlWGVLlgjZuoBH1HwjIfKE/IdL5mq89sFA7zELi1VhKpmtDnrs7zWyeyg==", + "dev": true, + "dependencies": { + "escape-string-regexp": "^5.0.0", + "is-unicode-supported": "^1.2.0" + }, + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/inquirer/node_modules/is-unicode-supported": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/is-unicode-supported/-/is-unicode-supported-1.3.0.tgz", + "integrity": "sha512-43r2mRvz+8JRIKnWJ+3j8JtjRKZ6GmjzfaE/qiBJnikNnYv/6bagRJ1kUhNk8R5EX/GkobD+r+sfxCPJsiKBLQ==", + "dev": true, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/inquirer/node_modules/mute-stream": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/mute-stream/-/mute-stream-1.0.0.tgz", + "integrity": "sha512-avsJQhyd+680gKXyG/sQc0nXaC6rBkPOfyHYcFb9+hdkqQkR9bdnkJ0AMZhke0oesPqIO+mFFJ+IdBc7mst4IA==", + "dev": true, + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + } + }, + "node_modules/inquirer/node_modules/run-async": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/run-async/-/run-async-3.0.0.tgz", + "integrity": "sha512-540WwVDOMxA6dN6We19EcT9sc3hkXPw5mzRNGM3FkdN/vtE9NFvj5lFAPNwUDmJjXidm3v7TC1cTE7t17Ulm1Q==", + "dev": true, + "engines": { + "node": ">=0.12.0" } }, "node_modules/inquirer/node_modules/rxjs": { - "version": "7.5.7", - "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-7.5.7.tgz", - "integrity": "sha512-z9MzKh/UcOqB3i20H6rtrlaE/CgjLOvheWK/9ILrbhROGTweAi1BaFsTT9FbwZi5Trr1qNRs+MXkhmR06awzQA==", + "version": "7.8.1", + "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-7.8.1.tgz", + "integrity": "sha512-AA3TVj+0A2iuIoQkWEK/tqFjBq2j+6PO6Y0zJcvzLAFhEFIO3HL0vls9hWLncZbAAbK0mar7oZ4V079I/qPMxg==", "dev": true, "dependencies": { "tslib": "^2.1.0" } }, - "node_modules/inquirer/node_modules/supports-color": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "node_modules/inquirer/node_modules/tslib": { + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.2.tgz", + "integrity": "sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==", + "dev": true + }, + "node_modules/inquirer/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": { - "has-flag": "^4.0.0" + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" }, "engines": { "node": ">=8" } }, - "node_modules/inquirer/node_modules/tslib": { - "version": "2.4.1", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.4.1.tgz", - "integrity": "sha512-tGyy4dAjRIEwI7BzsB0lynWgOpfqjUdq91XXAlIWD2OwKBH7oCl/GZG/HT4BOHrTlPMOASlMQ7veyTqpmRcrNA==", - "dev": true - }, "node_modules/internal-slot": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/internal-slot/-/internal-slot-1.0.3.tgz", @@ -14344,6 +17521,12 @@ "node": ">=0.10.0" } }, + "node_modules/ip": { + "version": "1.1.8", + "resolved": "https://registry.npmjs.org/ip/-/ip-1.1.8.tgz", + "integrity": "sha512-PuExPYUiu6qMBQb4l06ecm6T6ujzhmh+MeJcW9wa89PoAz5pvd4zPgN5WJV104mb6S2T1AwNIAaB70JNrLQWhg==", + "dev": true + }, "node_modules/ipaddr.js": { "version": "1.9.1", "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.1.tgz", @@ -15227,16 +18410,34 @@ "url": "https://bevry.me/fund" } }, + "node_modules/jackspeak": { + "version": "2.3.6", + "resolved": "https://registry.npmjs.org/jackspeak/-/jackspeak-2.3.6.tgz", + "integrity": "sha512-N3yCS/NegsOBokc8GAdM8UcmfsKiSS8cipheD/nivzr700H+nsMOxJjQnvwOcRYVuFkdH0wGUvW2WbXGmrZGbQ==", + "dev": true, + "dependencies": { + "@isaacs/cliui": "^8.0.2" + }, + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + }, + "optionalDependencies": { + "@pkgjs/parseargs": "^0.11.0" + } + }, "node_modules/jake": { - "version": "10.8.5", - "resolved": "https://registry.npmjs.org/jake/-/jake-10.8.5.tgz", - "integrity": "sha512-sVpxYeuAhWt0OTWITwT98oyV0GsXyMlXCF+3L1SuafBVUIr/uILGRB+NqwkzhgXKvoJpDIpQvqkUALgdmQsQxw==", + "version": "10.8.7", + "resolved": "https://registry.npmjs.org/jake/-/jake-10.8.7.tgz", + "integrity": "sha512-ZDi3aP+fG/LchyBzUM804VjddnwfSfsdeYkwt8NcbKRvo4rFkjhs456iLFn3k2ZUWvNe4i48WACDbza8fhq2+w==", "dev": true, "dependencies": { "async": "^3.2.3", "chalk": "^4.0.2", - "filelist": "^1.0.1", - "minimatch": "^3.0.4" + "filelist": "^1.0.4", + "minimatch": "^3.1.2" }, "bin": { "jake": "bin/cli.js" @@ -15261,9 +18462,9 @@ } }, "node_modules/jake/node_modules/async": { - "version": "3.2.4", - "resolved": "https://registry.npmjs.org/async/-/async-3.2.4.tgz", - "integrity": "sha512-iAB+JbDEGXhyIUavoDl9WP/Jj106Kz9DEn1DPgYw5ruDn0e3Wgi3sKFm55sASdGBNOQB8F59d9qQ7deqrHA8wQ==", + "version": "3.2.5", + "resolved": "https://registry.npmjs.org/async/-/async-3.2.5.tgz", + "integrity": "sha512-baNZyqaaLhyLVKm/DlvdW051MSgO6b8eVfIezl9E5PqWxFgzLm/wQntEW4zOytVburDEr0JlALEpdOFwvErLsg==", "dev": true }, "node_modules/jake/node_modules/chalk": { @@ -15322,18 +18523,18 @@ } }, "node_modules/jest-diff": { - "version": "26.6.2", - "resolved": "https://registry.npmjs.org/jest-diff/-/jest-diff-26.6.2.tgz", - "integrity": "sha512-6m+9Z3Gv9wN0WFVasqjCL/06+EFCMTqDEUl/b87HYK2rAPTyfz4ZIuSlPhY51PIQRWx5TaxeF1qmXKe9gfN3sA==", + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-diff/-/jest-diff-29.7.0.tgz", + "integrity": "sha512-LMIgiIrhigmPrs03JHpxUh2yISK3vLFPkAodPeo0+BuF7wA2FoQbkEg1u8gBYBThncu7e1oEDUfIXVuTqLRUjw==", "dev": true, "dependencies": { "chalk": "^4.0.0", - "diff-sequences": "^26.6.2", - "jest-get-type": "^26.3.0", - "pretty-format": "^26.6.2" + "diff-sequences": "^29.6.3", + "jest-get-type": "^29.6.3", + "pretty-format": "^29.7.0" }, "engines": { - "node": ">= 10.14.2" + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" } }, "node_modules/jest-diff/node_modules/ansi-styles": { @@ -15407,27 +18608,27 @@ } }, "node_modules/jest-get-type": { - "version": "26.3.0", - "resolved": "https://registry.npmjs.org/jest-get-type/-/jest-get-type-26.3.0.tgz", - "integrity": "sha512-TpfaviN1R2pQWkIihlfEanwOXK0zcxrKEE4MlU6Tn7keoXdN6/3gK/xl0yEh8DOunn5pOVGKf8hB4R9gVh04ig==", + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/jest-get-type/-/jest-get-type-29.6.3.tgz", + "integrity": "sha512-zrteXnqYxfQh7l5FHyL38jL39di8H8rHoecLH3JNxH3BwOrBsNeabdap5e0I23lD4HHI8W5VFBZqG4Eaq5LNcw==", "dev": true, "engines": { - "node": ">= 10.14.2" + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" } }, "node_modules/jest-matcher-utils": { - "version": "26.6.2", - "resolved": "https://registry.npmjs.org/jest-matcher-utils/-/jest-matcher-utils-26.6.2.tgz", - "integrity": "sha512-llnc8vQgYcNqDrqRDXWwMr9i7rS5XFiCwvh6DTP7Jqa2mqpcCBBlpCbn+trkG0KNhPu/h8rzyBkriOtBstvWhw==", + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-matcher-utils/-/jest-matcher-utils-29.7.0.tgz", + "integrity": "sha512-sBkD+Xi9DtcChsI3L3u0+N0opgPYnCRPtGcQYrgXmR+hmt/fYfWAL0xRXYU8eWOdfuLgBe0YCW3AFtnRLagq/g==", "dev": true, "dependencies": { "chalk": "^4.0.0", - "jest-diff": "^26.6.2", - "jest-get-type": "^26.3.0", - "pretty-format": "^26.6.2" + "jest-diff": "^29.7.0", + "jest-get-type": "^29.6.3", + "pretty-format": "^29.7.0" }, "engines": { - "node": ">= 10.14.2" + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" } }, "node_modules/jest-matcher-utils/node_modules/ansi-styles": { @@ -15501,23 +18702,23 @@ } }, "node_modules/jest-message-util": { - "version": "26.6.2", - "resolved": "https://registry.npmjs.org/jest-message-util/-/jest-message-util-26.6.2.tgz", - "integrity": "sha512-rGiLePzQ3AzwUshu2+Rn+UMFk0pHN58sOG+IaJbk5Jxuqo3NYO1U2/MIR4S1sKgsoYSXSzdtSa0TgrmtUwEbmA==", + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-message-util/-/jest-message-util-29.7.0.tgz", + "integrity": "sha512-GBEV4GRADeP+qtB2+6u61stea8mGcOT4mCtrYISZwfu9/ISHFJ/5zOMXYbpBE9RsS5+Gb63DW4FgmnKJ79Kf6w==", "dev": true, "dependencies": { - "@babel/code-frame": "^7.0.0", - "@jest/types": "^26.6.2", + "@babel/code-frame": "^7.12.13", + "@jest/types": "^29.6.3", "@types/stack-utils": "^2.0.0", "chalk": "^4.0.0", - "graceful-fs": "^4.2.4", - "micromatch": "^4.0.2", - "pretty-format": "^26.6.2", + "graceful-fs": "^4.2.9", + "micromatch": "^4.0.4", + "pretty-format": "^29.7.0", "slash": "^3.0.0", - "stack-utils": "^2.0.2" + "stack-utils": "^2.0.3" }, "engines": { - "node": ">= 10.14.2" + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" } }, "node_modules/jest-message-util/node_modules/ansi-styles": { @@ -15599,13 +18800,91 @@ "node": ">=8" } }, - "node_modules/jest-regex-util": { - "version": "26.0.0", - "resolved": "https://registry.npmjs.org/jest-regex-util/-/jest-regex-util-26.0.0.tgz", - "integrity": "sha512-Gv3ZIs/nA48/Zvjrl34bf+oD76JHiGDUxNOVgUjh3j890sblXryjY4rss71fPtD/njchl6PSE2hIhvyWa1eT0A==", + "node_modules/jest-util": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-util/-/jest-util-29.7.0.tgz", + "integrity": "sha512-z6EbKajIpqGKU56y5KBUgy1dt1ihhQJgWzUlZHArA/+X2ad7Cb5iF+AK1EWVL/Bo7Rz9uurpqw6SiBCefUbCGA==", + "dev": true, + "dependencies": { + "@jest/types": "^29.6.3", + "@types/node": "*", + "chalk": "^4.0.0", + "ci-info": "^3.2.0", + "graceful-fs": "^4.2.9", + "picomatch": "^2.2.3" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-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-util/node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/jest-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-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-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-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": ">= 10.14.2" + "node": ">=8" } }, "node_modules/jest-worker": { @@ -16230,9 +19509,9 @@ "dev": true }, "node_modules/keyv": { - "version": "4.5.0", - "resolved": "https://registry.npmjs.org/keyv/-/keyv-4.5.0.tgz", - "integrity": "sha512-2YvuMsA+jnFGtBareKqgANOEKe1mk3HKiXu2fRmAfyxG0MJAywNhi5ttWA3PMjl4NmpyjZNbFifR2vNjW1znfA==", + "version": "4.5.4", + "resolved": "https://registry.npmjs.org/keyv/-/keyv-4.5.4.tgz", + "integrity": "sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==", "dev": true, "dependencies": { "json-buffer": "3.0.1" @@ -16267,12 +19546,12 @@ } }, "node_modules/ky": { - "version": "0.29.0", - "resolved": "https://registry.npmjs.org/ky/-/ky-0.29.0.tgz", - "integrity": "sha512-01TBSOqlHmLfcQhHseugGHLxPtU03OyZWaLDWt5MfzCkijG6xWFvAQPhKVn0cR2MMjYvBP9keQ8A3+rQEhLO5g==", + "version": "0.33.3", + "resolved": "https://registry.npmjs.org/ky/-/ky-0.33.3.tgz", + "integrity": "sha512-CasD9OCEQSFIam2U8efFK81Yeg8vNMTBUqtMOHlrcWQHqUX3HeCl9Dr31u4toV7emlH8Mymk5+9p0lL6mKb/Xw==", "dev": true, "engines": { - "node": ">=12" + "node": ">=14.16" }, "funding": { "url": "https://github.com/sindresorhus/ky?sponsor=1" @@ -16515,6 +19794,29 @@ "node": ">=8.9.0" } }, + "node_modules/locate-app": { + "version": "2.2.13", + "resolved": "https://registry.npmjs.org/locate-app/-/locate-app-2.2.13.tgz", + "integrity": "sha512-1jp6iRFrHKBj9vq6Idb0cSjly+KnCIMbxZ2BBKSEzIC4ZJosv47wnLoiJu2EgOAdjhGvNcy/P2fbDCS/WziI8g==", + "dev": true, + "dependencies": { + "n12": "1.8.16", + "type-fest": "2.13.0", + "userhome": "1.0.0" + } + }, + "node_modules/locate-app/node_modules/type-fest": { + "version": "2.13.0", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-2.13.0.tgz", + "integrity": "sha512-lPfAm42MxE4/456+QyIaaVBAwgpJb6xZ8PRu09utnhPdWwcyj9vgy6Sq0Z5yNbJ21EdxB5dRU/Qg8bsyAMtlcw==", + "dev": true, + "engines": { + "node": ">=12.20" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/locate-path": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz", @@ -16655,6 +19957,12 @@ "integrity": "sha512-JwObCrNJuT0Nnbuecmqr5DgtuBppuCvGD9lxjFpAzwnVtdGoDQ1zig+5W8k5/6Gcn0gZ3936HDAlGd28i7sOGQ==", "dev": true }, + "node_modules/lodash.isequal": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/lodash.isequal/-/lodash.isequal-4.5.0.tgz", + "integrity": "sha512-pDo3lu8Jhfjqls6GkMgpahsF9kCyayhgykjyLMNFTKWrpVdAQtYyB4muAMWozBB4ig/dtWAmsMxLEI8wuz+DYQ==", + "dev": true + }, "node_modules/lodash.isobject": { "version": "3.0.2", "resolved": "https://registry.npmjs.org/lodash.isobject/-/lodash.isobject-3.0.2.tgz", @@ -16776,6 +20084,32 @@ "node": ">=8.0" } }, + "node_modules/logform": { + "version": "2.6.0", + "resolved": "https://registry.npmjs.org/logform/-/logform-2.6.0.tgz", + "integrity": "sha512-1ulHeNPp6k/LD8H91o7VYFBng5i1BDE7HoKxVbZiGFidS1Rj65qcywLxX+pVfAPoQJEjRdvKcusKwOupHCVOVQ==", + "dev": true, + "dependencies": { + "@colors/colors": "1.6.0", + "@types/triple-beam": "^1.3.2", + "fecha": "^4.2.0", + "ms": "^2.1.1", + "safe-stable-stringify": "^2.3.1", + "triple-beam": "^1.3.0" + }, + "engines": { + "node": ">= 12.0.0" + } + }, + "node_modules/logform/node_modules/@colors/colors": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/@colors/colors/-/colors-1.6.0.tgz", + "integrity": "sha512-Ir+AOibqzrIsL6ajt3Rz3LskB7OiMVHqltZmspbW/TJuTVuyOMirVqAkjfY6JISiLHgyNqicAC8AyHHGzNd/dA==", + "dev": true, + "engines": { + "node": ">=0.1.90" + } + }, "node_modules/loglevel": { "version": "1.8.0", "resolved": "https://registry.npmjs.org/loglevel/-/loglevel-1.8.0.tgz", @@ -16928,15 +20262,6 @@ "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": "sha512-7N/q3lyZ+LVCp7PzuxrJr4KMbBE2hW7BT7YNia330OFxIf4d3r5zVpicP2650l7CPN6RM9zOJRl3NGpqSiw3Eg==", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, "node_modules/map-stream": { "version": "0.0.7", "resolved": "https://registry.npmjs.org/map-stream/-/map-stream-0.0.7.tgz", @@ -18200,6 +21525,21 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/minipass": { + "version": "7.0.4", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-7.0.4.tgz", + "integrity": "sha512-jYofLM5Dam9279rdkWzqHozUo4ybjdZmCsDHePy5V/PbBcVMiSZR97gmAy45aqi8CK1lG2ECd356FU86avfwUQ==", + "dev": true, + "engines": { + "node": ">=16 || 14 >=14.17" + } + }, + "node_modules/mitt": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/mitt/-/mitt-3.0.0.tgz", + "integrity": "sha512-7dX2/10ITVyqh4aOSVI9gdape+t9l2/8QxHrFmUXu4EEUpdlxl6RudZUPZoc+zuY2hk1j7XxVroIVIan/pD/SQ==", + "dev": true + }, "node_modules/mixin-deep": { "version": "1.3.2", "resolved": "https://registry.npmjs.org/mixin-deep/-/mixin-deep-1.3.2.tgz", @@ -18213,18 +21553,6 @@ "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", @@ -18712,6 +22040,12 @@ "npm": ">=5" } }, + "node_modules/n12": { + "version": "1.8.16", + "resolved": "https://registry.npmjs.org/n12/-/n12-1.8.16.tgz", + "integrity": "sha512-CZqHAqbzS0UsaUGkMsL+lMaYLyFr1+/ea+pD8dMziqSjkcuWVWDtgWx9phyfT7C3llqQ2+LwnStSb5afggBMfA==", + "dev": true + }, "node_modules/nan": { "version": "2.17.0", "resolved": "https://registry.npmjs.org/nan/-/nan-2.17.0.tgz", @@ -18813,6 +22147,15 @@ "integrity": "sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw==", "dev": true }, + "node_modules/netmask": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/netmask/-/netmask-2.0.2.tgz", + "integrity": "sha512-dBpDMdxv9Irdq66304OLfEmQ9tbNRFnFTuZiLo+bD+r332bBmMJ8GBLXklIXXgxd3+v9+KUnZaUR5PJMa75Gsg==", + "dev": true, + "engines": { + "node": ">= 0.4.0" + } + }, "node_modules/next-tick": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/next-tick/-/next-tick-1.1.0.tgz", @@ -18872,6 +22215,25 @@ "isarray": "0.0.1" } }, + "node_modules/node-domexception": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/node-domexception/-/node-domexception-1.0.0.tgz", + "integrity": "sha512-/jKZoMpw0F8GRwl4/eLROPA3cfcXtLApP0QzLmUT/HuPCZWyB7IY9ZrMeKw2O/nFIqPQB3PVM9aYm0F312AXDQ==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/jimmywarting" + }, + { + "type": "github", + "url": "https://paypal.me/jimmywarting" + } + ], + "engines": { + "node": ">=10.5.0" + } + }, "node_modules/node-fetch": { "version": "2.6.7", "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.7.tgz", @@ -18907,6 +22269,18 @@ "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.6.tgz", "integrity": "sha512-PiVXnNuFm5+iYkLBNeq5211hvO38y63T0i2KKh2KnUs3RpzJ+JtODFjkD8yjLwnDkTYF1eKXheUwdssR+NRZdg==" }, + "node_modules/node-request-interceptor": { + "version": "0.6.3", + "resolved": "https://registry.npmjs.org/node-request-interceptor/-/node-request-interceptor-0.6.3.tgz", + "integrity": "sha512-8I2V7H2Ch0NvW7qWcjmS0/9Lhr0T6x7RD6PDirhvWEkUQvy83x8BA4haYMr09r/rig7hcgYSjYh6cd4U7G1vLA==", + "dev": true, + "dependencies": { + "@open-draft/until": "^1.0.3", + "debug": "^4.3.0", + "headers-utils": "^1.2.0", + "strict-event-emitter": "^0.1.0" + } + }, "node_modules/nopt": { "version": "3.0.6", "resolved": "https://registry.npmjs.org/nopt/-/nopt-3.0.6.tgz", @@ -19568,6 +22942,64 @@ "node": ">=6" } }, + "node_modules/pac-proxy-agent": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/pac-proxy-agent/-/pac-proxy-agent-7.0.1.tgz", + "integrity": "sha512-ASV8yU4LLKBAjqIPMbrgtaKIvxQri/yh2OpI+S6hVa9JRkUI3Y3NPFbfngDtY7oFtSMD3w31Xns89mDa3Feo5A==", + "dev": true, + "dependencies": { + "@tootallnate/quickjs-emscripten": "^0.23.0", + "agent-base": "^7.0.2", + "debug": "^4.3.4", + "get-uri": "^6.0.1", + "http-proxy-agent": "^7.0.0", + "https-proxy-agent": "^7.0.2", + "pac-resolver": "^7.0.0", + "socks-proxy-agent": "^8.0.2" + }, + "engines": { + "node": ">= 14" + } + }, + "node_modules/pac-proxy-agent/node_modules/agent-base": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-7.1.0.tgz", + "integrity": "sha512-o/zjMZRhJxny7OyEF+Op8X+efiELC7k7yOjMzgfzVqOzXqkBkWI79YoTdOtsuWd5BWhAGAuOY/Xa6xpiaWXiNg==", + "dev": true, + "dependencies": { + "debug": "^4.3.4" + }, + "engines": { + "node": ">= 14" + } + }, + "node_modules/pac-proxy-agent/node_modules/https-proxy-agent": { + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-7.0.2.tgz", + "integrity": "sha512-NmLNjm6ucYwtcUmL7JQC1ZQ57LmHP4lT15FQ8D61nak1rO6DH+fz5qNK2Ap5UN4ZapYICE3/0KodcLYSPsPbaA==", + "dev": true, + "dependencies": { + "agent-base": "^7.0.2", + "debug": "4" + }, + "engines": { + "node": ">= 14" + } + }, + "node_modules/pac-resolver": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/pac-resolver/-/pac-resolver-7.0.0.tgz", + "integrity": "sha512-Fd9lT9vJbHYRACT8OhCbZBbxr6KRSawSovFpy8nDGshaK99S/EBhVIHp9+crhxrsZOuvLpgL1n23iyPg6Rl2hg==", + "dev": true, + "dependencies": { + "degenerator": "^5.0.0", + "ip": "^1.1.8", + "netmask": "^2.0.2" + }, + "engines": { + "node": ">= 14" + } + }, "node_modules/parent-module": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz", @@ -19733,6 +23165,31 @@ "node": ">=0.10.0" } }, + "node_modules/path-scurry": { + "version": "1.10.1", + "resolved": "https://registry.npmjs.org/path-scurry/-/path-scurry-1.10.1.tgz", + "integrity": "sha512-MkhCqzzBEpPvxxQ71Md0b1Kk51W01lrYvlMzSUaIzNsODdd7mqhiimSZlr+VegAz5Z6Vzt9Xg2ttE//XBhH3EQ==", + "dev": true, + "dependencies": { + "lru-cache": "^9.1.1 || ^10.0.0", + "minipass": "^5.0.0 || ^6.0.2 || ^7.0.0" + }, + "engines": { + "node": ">=16 || 14 >=14.17" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/path-scurry/node_modules/lru-cache": { + "version": "10.1.0", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.1.0.tgz", + "integrity": "sha512-/1clY/ui8CzjKFyjdvwPWJUYKiFVXG2I2cY0ssG7h4+hwk+XOIX7ZSG9Q7TW8TW3Kp3BUSqgFWBLgL4PJ+Blag==", + "dev": true, + "engines": { + "node": "14 || >=16.14" + } + }, "node_modules/path-to-regexp": { "version": "0.1.7", "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.7.tgz", @@ -19761,6 +23218,12 @@ "node": ">=0.10.0" } }, + "node_modules/pathe": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/pathe/-/pathe-1.1.2.tgz", + "integrity": "sha512-whLdWMYL2TwI08hn8/ZqAbrVemu0LNaNNJZX73O6qaIdCTfXutsLhMkjdENX0qhsQ9uIimo4/aQOmXkoon2nDQ==", + "dev": true + }, "node_modules/pathval": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/pathval/-/pathval-1.1.1.tgz", @@ -19938,53 +23401,31 @@ } }, "node_modules/pretty-format": { - "version": "26.6.2", - "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-26.6.2.tgz", - "integrity": "sha512-7AeGuCYNGmycyQbCqd/3PWH4eOoX/OiCa0uphp57NVTeAGdJGaAliecxwBDHYQCIvrW7aDBZCYeNTP/WX69mkg==", + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-29.7.0.tgz", + "integrity": "sha512-Pdlw/oPxN+aXdmM9R00JVC9WVFoCLTKJvDVLgmJ+qAffBMxsV85l/Lu7sNx4zSzPyoL2euImuEwHhOXdEgNFZQ==", "dev": true, "dependencies": { - "@jest/types": "^26.6.2", - "ansi-regex": "^5.0.0", - "ansi-styles": "^4.0.0", - "react-is": "^17.0.1" + "@jest/schemas": "^29.6.3", + "ansi-styles": "^5.0.0", + "react-is": "^18.0.0" }, "engines": { - "node": ">= 10" + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" } }, "node_modules/pretty-format/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==", + "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, - "dependencies": { - "color-convert": "^2.0.1" - }, "engines": { - "node": ">=8" + "node": ">=10" }, "funding": { "url": "https://github.com/chalk/ansi-styles?sponsor=1" } }, - "node_modules/pretty-format/node_modules/color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "dev": true, - "dependencies": { - "color-name": "~1.1.4" - }, - "engines": { - "node": ">=7.0.0" - } - }, - "node_modules/pretty-format/node_modules/color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", - "dev": true - }, "node_modules/pretty-hrtime": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/pretty-hrtime/-/pretty-hrtime-1.0.3.tgz", @@ -20070,6 +23511,59 @@ "node": ">= 0.10" } }, + "node_modules/proxy-agent": { + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/proxy-agent/-/proxy-agent-6.3.1.tgz", + "integrity": "sha512-Rb5RVBy1iyqOtNl15Cw/llpeLH8bsb37gM1FUfKQ+Wck6xHlbAhWGUFiTRHtkjqGTA5pSHz6+0hrPW/oECihPQ==", + "dev": true, + "dependencies": { + "agent-base": "^7.0.2", + "debug": "^4.3.4", + "http-proxy-agent": "^7.0.0", + "https-proxy-agent": "^7.0.2", + "lru-cache": "^7.14.1", + "pac-proxy-agent": "^7.0.1", + "proxy-from-env": "^1.1.0", + "socks-proxy-agent": "^8.0.2" + }, + "engines": { + "node": ">= 14" + } + }, + "node_modules/proxy-agent/node_modules/agent-base": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-7.1.0.tgz", + "integrity": "sha512-o/zjMZRhJxny7OyEF+Op8X+efiELC7k7yOjMzgfzVqOzXqkBkWI79YoTdOtsuWd5BWhAGAuOY/Xa6xpiaWXiNg==", + "dev": true, + "dependencies": { + "debug": "^4.3.4" + }, + "engines": { + "node": ">= 14" + } + }, + "node_modules/proxy-agent/node_modules/https-proxy-agent": { + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-7.0.2.tgz", + "integrity": "sha512-NmLNjm6ucYwtcUmL7JQC1ZQ57LmHP4lT15FQ8D61nak1rO6DH+fz5qNK2Ap5UN4ZapYICE3/0KodcLYSPsPbaA==", + "dev": true, + "dependencies": { + "agent-base": "^7.0.2", + "debug": "4" + }, + "engines": { + "node": ">= 14" + } + }, + "node_modules/proxy-agent/node_modules/lru-cache": { + "version": "7.18.3", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-7.18.3.tgz", + "integrity": "sha512-jumlc0BIUrS3qJGgIkWZsyfAM7NCWiBcCDhnd+3NNM5KbBmLTgHVfWBcg6W+rLUsIpzpERPsvwUP7CckAQSOoA==", + "dev": true, + "engines": { + "node": ">=12" + } + }, "node_modules/proxy-from-env": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.1.0.tgz", @@ -20097,12 +23591,6 @@ "node": ">= 0.10" } }, - "node_modules/pseudomap": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/pseudomap/-/pseudomap-1.0.2.tgz", - "integrity": "sha512-b/YwNhb8lk1Zz2+bXXpS/LK9OisiZZ1SNsSLxN1x2OXVEhW2Ckr/7mWE5vrC1ZTiJlD9g19jWszTmJsB+oEpFQ==", - "dev": true - }, "node_modules/psl": { "version": "1.9.0", "resolved": "https://registry.npmjs.org/psl/-/psl-1.9.0.tgz", @@ -20266,6 +23754,12 @@ "integrity": "sha512-FIqgj2EUvTa7R50u0rGsyTftzjYmv/a3hO345bZNrqabNqjtgiDMgmo4mkUjd+nzU5oF3dClKqFIPUKybUyqoQ==", "dev": true }, + "node_modules/queue-tick": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/queue-tick/-/queue-tick-1.0.1.tgz", + "integrity": "sha512-kJt5qhMxoszgU/62PLP1CJytzd2NKetjSRnyuj31fDd3Rlcz3fzlFdFLD1SItunPwyqEOkca6GbV612BWfaBag==", + "dev": true + }, "node_modules/quick-lru": { "version": "5.1.1", "resolved": "https://registry.npmjs.org/quick-lru/-/quick-lru-5.1.1.tgz", @@ -20310,9 +23804,9 @@ } }, "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==", + "version": "18.2.0", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-18.2.0.tgz", + "integrity": "sha512-xWGDIW6x921xtzPkhiULtthJHoJvBbF3q26fzloPCK0hsvxtPVelvftw3zjbHWSkR2km9Z+4uxbDDK/6Zw9B8w==", "dev": true }, "node_modules/read-pkg": { @@ -21156,6 +24650,12 @@ "node": ">=6" } }, + "node_modules/safaridriver": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/safaridriver/-/safaridriver-0.1.2.tgz", + "integrity": "sha512-4R309+gWflJktzPXBQCobbWEHlzC4aK3a+Ov3tz2Ib2aBxiwd11phkdIBH1l0EO22x24CJMUQkpKFumRriCSRg==", + "dev": true + }, "node_modules/safe-buffer": { "version": "5.2.1", "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", @@ -21204,6 +24704,15 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/safe-stable-stringify": { + "version": "2.4.3", + "resolved": "https://registry.npmjs.org/safe-stable-stringify/-/safe-stable-stringify-2.4.3.tgz", + "integrity": "sha512-e2bDA2WJT0wxseVd4lsDP4+3ONX6HpMXQa1ZhFQ7SU+GjvORCmShbCMltrtIDfkYhVHrOcPtj+KhmDBdPdZD1g==", + "dev": true, + "engines": { + "node": ">=10" + } + }, "node_modules/safer-buffer": { "version": "2.1.2", "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", @@ -21456,6 +24965,21 @@ "integrity": "sha512-KiKBS8AnWGEyLzofFfmvKwpdPzqiy16LvQfK3yv/fVH7Bj13/wl3JSR1J+rfgRE9q7xUJK4qvgS8raSOeLUehw==", "dev": true }, + "node_modules/set-function-length": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/set-function-length/-/set-function-length-1.2.0.tgz", + "integrity": "sha512-4DBHDoyHlM1IRPGYcoxexgh67y4ueR53FKV1yyxwFMY7aCqcN/38M1+SwZ/qJQ8iLv7+ck385ot4CcisOAPT9w==", + "dependencies": { + "define-data-property": "^1.1.1", + "function-bind": "^1.1.2", + "get-intrinsic": "^1.2.2", + "gopd": "^1.0.1", + "has-property-descriptors": "^1.0.1" + }, + "engines": { + "node": ">= 0.4" + } + }, "node_modules/set-value": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/set-value/-/set-value-2.0.1.tgz", @@ -21653,6 +25177,16 @@ "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", "dev": true }, + "node_modules/smart-buffer": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/smart-buffer/-/smart-buffer-4.2.0.tgz", + "integrity": "sha512-94hK0Hh8rPqQl2xXc3HsaBoOXKV20MToPkcXvwbISWLEs+64sBq5kFgn2kJDHb1Pry9yrP0dxrCI9RRci7RXKg==", + "dev": true, + "engines": { + "node": ">= 6.0.0", + "npm": ">= 3.0.0" + } + }, "node_modules/snapdragon": { "version": "0.8.2", "resolved": "https://registry.npmjs.org/snapdragon/-/snapdragon-0.8.2.tgz", @@ -21897,6 +25431,52 @@ "node": ">=10.0.0" } }, + "node_modules/socks": { + "version": "2.7.1", + "resolved": "https://registry.npmjs.org/socks/-/socks-2.7.1.tgz", + "integrity": "sha512-7maUZy1N7uo6+WVEX6psASxtNlKaNVMlGQKkG/63nEDdLOWNbiUMoLK7X4uYoLhQstau72mLgfEWcXcwsaHbYQ==", + "dev": true, + "dependencies": { + "ip": "^2.0.0", + "smart-buffer": "^4.2.0" + }, + "engines": { + "node": ">= 10.13.0", + "npm": ">= 3.0.0" + } + }, + "node_modules/socks-proxy-agent": { + "version": "8.0.2", + "resolved": "https://registry.npmjs.org/socks-proxy-agent/-/socks-proxy-agent-8.0.2.tgz", + "integrity": "sha512-8zuqoLv1aP/66PHF5TqwJ7Czm3Yv32urJQHrVyhD7mmA6d61Zv8cIXQYPTWwmg6qlupnPvs/QKDmfa4P/qct2g==", + "dev": true, + "dependencies": { + "agent-base": "^7.0.2", + "debug": "^4.3.4", + "socks": "^2.7.1" + }, + "engines": { + "node": ">= 14" + } + }, + "node_modules/socks-proxy-agent/node_modules/agent-base": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-7.1.0.tgz", + "integrity": "sha512-o/zjMZRhJxny7OyEF+Op8X+efiELC7k7yOjMzgfzVqOzXqkBkWI79YoTdOtsuWd5BWhAGAuOY/Xa6xpiaWXiNg==", + "dev": true, + "dependencies": { + "debug": "^4.3.4" + }, + "engines": { + "node": ">= 14" + } + }, + "node_modules/socks/node_modules/ip": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ip/-/ip-2.0.0.tgz", + "integrity": "sha512-WKa+XuLG1A1R0UWhl2+1XQSi+fZWMsYKffMZTTYsiZaUD8k2yDAj5atimTUD2TZkyCkNEeYE5NhFZmupOGtjYQ==", + "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", @@ -22045,26 +25625,12 @@ } }, "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/split2/node_modules/readable-stream": { - "version": "3.6.0", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.0.tgz", - "integrity": "sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA==", + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/split2/-/split2-4.2.0.tgz", + "integrity": "sha512-UcjcJOWknrNkF6PLX83qcHM6KHgVKNkV62Y8a5uYDVv9ydGQVwAHMKqHdJje1VTWpljG0WYpCDhrCdAOYH4TWg==", "dev": true, - "dependencies": { - "inherits": "^2.0.3", - "string_decoder": "^1.1.1", - "util-deprecate": "^1.0.1" - }, "engines": { - "node": ">= 6" + "node": ">= 10.x" } }, "node_modules/sprintf-js": { @@ -22108,9 +25674,9 @@ } }, "node_modules/stack-utils": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/stack-utils/-/stack-utils-2.0.5.tgz", - "integrity": "sha512-xrQcmYhOsn/1kX+Vraq+7j4oE2j/6BFscZ0etmYg81xuM8Gq0022Pxb8+IqgOFUIaxHs0KaSb7T1+OegiNrNFA==", + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/stack-utils/-/stack-utils-2.0.6.tgz", + "integrity": "sha512-XlkWvfIm6RmsWtNJx+uqtKLS8eqFbxUg0ZzLXqY0caEy9l7hruX8IpiDnjsLavoBgqCCR71TqWO8MaXYheJ3RQ==", "dev": true, "dependencies": { "escape-string-regexp": "^2.0.0" @@ -22305,6 +25871,22 @@ "node": ">= 4.0.0" } }, + "node_modules/streamx": { + "version": "2.15.6", + "resolved": "https://registry.npmjs.org/streamx/-/streamx-2.15.6.tgz", + "integrity": "sha512-q+vQL4AAz+FdfT137VF69Cc/APqUbxy+MDOImRrMvchJpigHj9GksgDU2LYbO9rx7RX6osWgxJB2WxhYv4SZAw==", + "dev": true, + "dependencies": { + "fast-fifo": "^1.1.0", + "queue-tick": "^1.0.1" + } + }, + "node_modules/strict-event-emitter": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/strict-event-emitter/-/strict-event-emitter-0.1.0.tgz", + "integrity": "sha512-8hSYfU+WKLdNcHVXJ0VxRXiPESalzRe7w1l8dg9+/22Ry+iZQUoQuoJ27R30GMD1TiyYINWsIEGY05WrskhSKw==", + "dev": true + }, "node_modules/string_decoder": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", @@ -22340,6 +25922,21 @@ "node": ">=8" } }, + "node_modules/string-width-cjs": { + "name": "string-width", + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "dev": true, + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, "node_modules/string.prototype.trimend": { "version": "1.0.5", "resolved": "https://registry.npmjs.org/string.prototype.trimend/-/string.prototype.trimend-1.0.5.tgz", @@ -22394,6 +25991,19 @@ "node": ">=8" } }, + "node_modules/strip-ansi-cjs": { + "name": "strip-ansi", + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dev": true, + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, "node_modules/strip-bom": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-3.0.0.tgz", @@ -22421,6 +26031,18 @@ "node": ">=0.10.0" } }, + "node_modules/strip-final-newline": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/strip-final-newline/-/strip-final-newline-3.0.0.tgz", + "integrity": "sha512-dOESqjYr96iWYylGObzd39EuNTa5VJxyvVAEm5Jnh7KGo75V43Hk1odPQkNDyXNmUR6k+gEiDVXnjB8HJ3crXw==", + "dev": true, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/strip-json-comments": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-5.0.0.tgz", @@ -22433,15 +26055,6 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/suffix": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/suffix/-/suffix-0.1.1.tgz", - "integrity": "sha512-j5uf6MJtMCfC4vBe5LFktSe4bGyNTBk7I2Kdri0jeLrcv5B9pWfxVa5JQpoxgtR8vaVB7bVxsWgnfQbX5wkhAA==", - "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", @@ -23043,6 +26656,15 @@ "node": ">=0.10.0" } }, + "node_modules/triple-beam": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/triple-beam/-/triple-beam-1.4.1.tgz", + "integrity": "sha512-aZbgViZrg1QNcG+LULa7nhZpJTZSLm/mXnHXnbAbjmN5aSa0y7V+wvv6+4WaBtpISJzThKy+PIPxc1Nq1EJ9mg==", + "dev": true, + "engines": { + "node": ">= 14.0.0" + } + }, "node_modules/trough": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/trough/-/trough-2.1.0.tgz", @@ -23163,6 +26785,7 @@ "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.8.4.tgz", "integrity": "sha512-QCh+85mCy+h0IGff8r5XWzOVSbBO+KfeYrMQh7NJ58QujwcE22u+NUSmUxqF+un70P9GXKxa2HCNiTTMJknyjQ==", "dev": true, + "optional": true, "peer": true, "bin": { "tsc": "bin/tsc", @@ -23295,6 +26918,12 @@ "integrity": "sha512-Ia0sQNrMPXXkqVFt6w6M1n1oKo3NfKs+mvaV811Jwir7vAk9a6PVV9VPYf6X3BU97QiLEmuW3uXH9u87zDFfdw==", "dev": true }, + "node_modules/undici-types": { + "version": "5.26.5", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-5.26.5.tgz", + "integrity": "sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA==", + "dev": true + }, "node_modules/unicode-canonical-property-names-ecmascript": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/unicode-canonical-property-names-ecmascript/-/unicode-canonical-property-names-ecmascript-2.0.0.tgz", @@ -23670,6 +27299,15 @@ "node": ">=0.10.0" } }, + "node_modules/userhome": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/userhome/-/userhome-1.0.0.tgz", + "integrity": "sha512-ayFKY3H+Pwfy4W98yPdtH1VqH4psDeyW8lYYFzfecR9d6hqLpqhecktvYR3SEEXt7vG0S1JEpciI3g94pMErig==", + "dev": true, + "engines": { + "node": ">= 0.8.0" + } + }, "node_modules/util": { "version": "0.12.5", "resolved": "https://registry.npmjs.org/util/-/util-0.12.5.tgz", @@ -24141,6 +27779,102 @@ "he": "^1.2.0" } }, + "node_modules/wait-port": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/wait-port/-/wait-port-1.1.0.tgz", + "integrity": "sha512-3e04qkoN3LxTMLakdqeWth8nih8usyg+sf1Bgdf9wwUkp05iuK1eSY/QpLvscT/+F/gA89+LpUmmgBtesbqI2Q==", + "dev": true, + "dependencies": { + "chalk": "^4.1.2", + "commander": "^9.3.0", + "debug": "^4.3.4" + }, + "bin": { + "wait-port": "bin/wait-port.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/wait-port/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/wait-port/node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/wait-port/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/wait-port/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/wait-port/node_modules/commander": { + "version": "9.5.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-9.5.0.tgz", + "integrity": "sha512-KRs7WVDKg86PWiuAqhDrAQnTXZKraVcCc6vFdL14qrZ/DcWwuRo7VoiYXalXO7S5GKpqYiVEwCbgFDfxNHKJBQ==", + "dev": true, + "engines": { + "node": "^12.20.0 || >=14" + } + }, + "node_modules/wait-port/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/wait-port/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/walk": { "version": "2.3.15", "resolved": "https://registry.npmjs.org/walk/-/walk-2.3.15.tgz", @@ -24172,119 +27906,196 @@ "defaults": "^1.0.3" } }, + "node_modules/web-streams-polyfill": { + "version": "4.0.0-beta.3", + "resolved": "https://registry.npmjs.org/web-streams-polyfill/-/web-streams-polyfill-4.0.0-beta.3.tgz", + "integrity": "sha512-QW95TCTaHmsYfHDybGMwO5IJIM93I/6vTRk+daHTWFPhwh+C8Cg7j7XyKrwrj8Ib6vYXe0ocYNrmzY4xAAN6ug==", + "dev": true, + "engines": { + "node": ">= 14" + } + }, "node_modules/webdriver": { - "version": "7.5.3", - "resolved": "https://registry.npmjs.org/webdriver/-/webdriver-7.5.3.tgz", - "integrity": "sha512-cDTn/hYj5x8BYwXxVb/WUwqGxrhCMP2rC8ttIWCfzmiVtmOnJGulC7CyxU3+p9Q5R/gIKTzdJOss16dhb+5CoA==", + "version": "8.29.1", + "resolved": "https://registry.npmjs.org/webdriver/-/webdriver-8.29.1.tgz", + "integrity": "sha512-D3gkbDUxFKBJhNHRfMriWclooLbNavVQC1MRvmENAgPNKaHnFn+M+WtP9K2sEr0XczLGNlbOzT7CKR9K5UXKXA==", "dev": true, "dependencies": { - "@wdio/config": "7.5.3", - "@wdio/logger": "7.5.3", - "@wdio/protocols": "7.5.3", - "@wdio/types": "7.5.3", - "@wdio/utils": "7.5.3", - "got": "^11.0.2", - "lodash.merge": "^4.6.1" + "@types/node": "^20.1.0", + "@types/ws": "^8.5.3", + "@wdio/config": "8.29.1", + "@wdio/logger": "8.28.0", + "@wdio/protocols": "8.24.12", + "@wdio/types": "8.29.1", + "@wdio/utils": "8.29.1", + "deepmerge-ts": "^5.1.0", + "got": "^12.6.1", + "ky": "^0.33.0", + "ws": "^8.8.0" }, "engines": { - "node": ">=12.0.0" + "node": "^16.13 || >=18" } }, - "node_modules/webdriver/node_modules/@wdio/logger": { - "version": "7.5.3", - "resolved": "https://registry.npmjs.org/@wdio/logger/-/logger-7.5.3.tgz", - "integrity": "sha512-r9EADpm0ncS1bDQSWi/nhF9C59/WNLbdAAFlo782E9ItFCpDhNit3aQP9vETv1vFxJRjUIM8Fw/HW8zwPadkbw==", + "node_modules/webdriver/node_modules/@sindresorhus/is": { + "version": "5.6.0", + "resolved": "https://registry.npmjs.org/@sindresorhus/is/-/is-5.6.0.tgz", + "integrity": "sha512-TV7t8GKYaJWsn00tFDqBw8+Uqmr8A0fRU1tvTQhyZzGv0sJCGRQL3JGMI3ucuKo3XIZdUP+Lx7/gh2t3lewy7g==", "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": ">=14.16" + }, + "funding": { + "url": "https://github.com/sindresorhus/is?sponsor=1" } }, - "node_modules/webdriver/node_modules/@wdio/types": { - "version": "7.5.3", - "resolved": "https://registry.npmjs.org/@wdio/types/-/types-7.5.3.tgz", - "integrity": "sha512-jmumhKBhNDABnpmrshYLEcdE9WoP5tmynsDNbDABlb/W8FFiLySQOejukhYIL9CLys4zXerV3/edks0SCzHOiQ==", + "node_modules/webdriver/node_modules/@szmarczak/http-timer": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/@szmarczak/http-timer/-/http-timer-5.0.1.tgz", + "integrity": "sha512-+PmQX0PiAYPMeVYe237LJAYvOMYW1j2rH5YROyS3b4CTVJum34HfRvKvAzozHAQG0TnHNdUfY9nCeUyRAs//cw==", "dev": true, "dependencies": { - "got": "^11.8.1" + "defer-to-connect": "^2.0.1" }, "engines": { - "node": ">=12.0.0" + "node": ">=14.16" } }, - "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==", + "node_modules/webdriver/node_modules/cacheable-lookup": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/cacheable-lookup/-/cacheable-lookup-7.0.0.tgz", + "integrity": "sha512-+qJyx4xiKra8mZrcwhjMRMUhD5NR1R8esPkzIYxX96JiecFoxAXFuz/GpR3+ev4PE1WamHip78wV0vcmPQtp8w==", + "dev": true, + "engines": { + "node": ">=14.16" + } + }, + "node_modules/webdriver/node_modules/cacheable-request": { + "version": "10.2.14", + "resolved": "https://registry.npmjs.org/cacheable-request/-/cacheable-request-10.2.14.tgz", + "integrity": "sha512-zkDT5WAF4hSSoUgyfg5tFIxz8XQK+25W/TLVojJTMKBaxevLBBtLxgqguAuVQB8PVW79FVjHcU+GJ9tVbDZ9mQ==", "dev": true, "dependencies": { - "color-convert": "^2.0.1" + "@types/http-cache-semantics": "^4.0.2", + "get-stream": "^6.0.1", + "http-cache-semantics": "^4.1.1", + "keyv": "^4.5.3", + "mimic-response": "^4.0.0", + "normalize-url": "^8.0.0", + "responselike": "^3.0.0" }, "engines": { - "node": ">=8" + "node": ">=14.16" + } + }, + "node_modules/webdriver/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/chalk/ansi-styles?sponsor=1" + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/webdriver/node_modules/chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "node_modules/webdriver/node_modules/got": { + "version": "12.6.1", + "resolved": "https://registry.npmjs.org/got/-/got-12.6.1.tgz", + "integrity": "sha512-mThBblvlAF1d4O5oqyvN+ZxLAYwIJK7bpMxgYqPD9okW0C3qm5FFn7k811QrcuEBwaogR3ngOFoCfs6mRv7teQ==", "dev": true, "dependencies": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" + "@sindresorhus/is": "^5.2.0", + "@szmarczak/http-timer": "^5.0.1", + "cacheable-lookup": "^7.0.0", + "cacheable-request": "^10.2.8", + "decompress-response": "^6.0.0", + "form-data-encoder": "^2.1.2", + "get-stream": "^6.0.1", + "http2-wrapper": "^2.1.10", + "lowercase-keys": "^3.0.0", + "p-cancelable": "^3.0.0", + "responselike": "^3.0.0" }, "engines": { - "node": ">=10" + "node": ">=14.16" }, "funding": { - "url": "https://github.com/chalk/chalk?sponsor=1" + "url": "https://github.com/sindresorhus/got?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==", + "node_modules/webdriver/node_modules/http2-wrapper": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/http2-wrapper/-/http2-wrapper-2.2.1.tgz", + "integrity": "sha512-V5nVw1PAOgfI3Lmeaj2Exmeg7fenjhRUgz1lPSezy1CuhPYbgQtbQj4jZfEAEMlaL+vupsvhjqCyjzob0yxsmQ==", "dev": true, "dependencies": { - "color-name": "~1.1.4" + "quick-lru": "^5.1.1", + "resolve-alpn": "^1.2.0" }, "engines": { - "node": ">=7.0.0" + "node": ">=10.19.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/lowercase-keys": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/lowercase-keys/-/lowercase-keys-3.0.0.tgz", + "integrity": "sha512-ozCC6gdQ+glXOQsveKD0YsDy8DSQFjDTz4zyzEHNV5+JP5D62LmfDZ6o1cycFx9ouG940M5dE8C8CTewdj2YWQ==", + "dev": true, + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } }, - "node_modules/webdriver/node_modules/has-flag": { + "node_modules/webdriver/node_modules/mimic-response": { "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "resolved": "https://registry.npmjs.org/mimic-response/-/mimic-response-4.0.0.tgz", + "integrity": "sha512-e5ISH9xMYU0DzrT+jl8q2ze9D6eWBto+I8CNpe+VI+K2J/F/k3PdkdTdz4wvGVH4NTpo+NRYTVIuMQEMMcsLqg==", "dev": true, "engines": { - "node": ">=8" + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "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==", + "node_modules/webdriver/node_modules/normalize-url": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/normalize-url/-/normalize-url-8.0.0.tgz", + "integrity": "sha512-uVFpKhj5MheNBJRTiMZ9pE/7hD1QTeEvugSJW/OmLzAp78PB5O6adfMNTvmfKhXBkvCzC+rqifWcVYpGFwTjnw==", + "dev": true, + "engines": { + "node": ">=14.16" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/webdriver/node_modules/p-cancelable": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/p-cancelable/-/p-cancelable-3.0.0.tgz", + "integrity": "sha512-mlVgR3PGuzlo0MmTdk4cXqXWlwQDLnONTAg6sm62XkMJEiRxN3GL3SffkYvqwonbkJBcrI7Uvv5Zh9yjvn2iUw==", + "dev": true, + "engines": { + "node": ">=12.20" + } + }, + "node_modules/webdriver/node_modules/responselike": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/responselike/-/responselike-3.0.0.tgz", + "integrity": "sha512-40yHxbNcl2+rzXvZuVkrYohathsSJlMTXKryG5y8uciHv1+xDLHQpgjG64JUO9nrEq2jGLH6IZ8BcZyw3wrweg==", "dev": true, "dependencies": { - "has-flag": "^4.0.0" + "lowercase-keys": "^3.0.0" }, "engines": { - "node": ">=8" + "node": ">=14.16" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, "node_modules/webdriverio": { @@ -25068,56 +28879,32 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/wide-align": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/wide-align/-/wide-align-1.1.3.tgz", - "integrity": "sha512-QGkOQc8XL6Bt5PwnsExKBPuMKBxnGxWWW3fU55Xt4feHozMUhdUMaBCk290qpm/wG5u/RSKzwdAC4i51YigihA==", - "dev": true, - "dependencies": { - "string-width": "^1.0.2 || 2" - } - }, - "node_modules/wide-align/node_modules/ansi-regex": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-3.0.1.tgz", - "integrity": "sha512-+O9Jct8wf++lXxxFc4hc8LsjaSq0HFzzL7cVsw8pRDIPdjKD2mT4ytDZlLuSBZ4cLKZFXIrMGO7DbQCtMJJMKw==", - "dev": true, - "engines": { - "node": ">=4" - } - }, - "node_modules/wide-align/node_modules/is-fullwidth-code-point": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz", - "integrity": "sha512-VHskAKYM8RfSFXwee5t5cbN5PZeq1Wrh6qd5bkyiXIf6UQcN6w/A0eXM9r6t8d+GYOh+o6ZhiEnb88LN/Y8m2w==", - "dev": true, - "engines": { - "node": ">=4" - } - }, - "node_modules/wide-align/node_modules/string-width": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-2.1.1.tgz", - "integrity": "sha512-nOqH59deCq9SRHlxq1Aw85Jnt4w6KvLKqWVik6oA9ZklXLNIOlqg4F2yrT1MVaTjAqvVwdfeZ7w7aCvJD7ugkw==", + "node_modules/winston-transport": { + "version": "4.6.0", + "resolved": "https://registry.npmjs.org/winston-transport/-/winston-transport-4.6.0.tgz", + "integrity": "sha512-wbBA9PbPAHxKiygo7ub7BYRiKxms0tpfU2ljtWzb3SjRjv5yl6Ozuy/TkXf00HTAt+Uylo3gSkNwzc4ME0wiIg==", "dev": true, "dependencies": { - "is-fullwidth-code-point": "^2.0.0", - "strip-ansi": "^4.0.0" + "logform": "^2.3.2", + "readable-stream": "^3.6.0", + "triple-beam": "^1.3.0" }, "engines": { - "node": ">=4" + "node": ">= 12.0.0" } }, - "node_modules/wide-align/node_modules/strip-ansi": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-4.0.0.tgz", - "integrity": "sha512-4XaJ2zQdCzROZDivEVIDPkcQn8LMFSa8kj8Gxb/Lnwzv9A8VctNZ+lfivC/sV3ivW8ElJTERXZoPBRrZKkNKow==", + "node_modules/winston-transport/node_modules/readable-stream": { + "version": "3.6.2", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz", + "integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==", "dev": true, "dependencies": { - "ansi-regex": "^3.0.0" + "inherits": "^2.0.3", + "string_decoder": "^1.1.1", + "util-deprecate": "^1.0.1" }, "engines": { - "node": ">=4" + "node": ">= 6" } }, "node_modules/word-wrap": { @@ -25158,6 +28945,57 @@ "url": "https://github.com/chalk/wrap-ansi?sponsor=1" } }, + "node_modules/wrap-ansi-cjs": { + "name": "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-cjs/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-cjs/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-cjs/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/wrap-ansi/node_modules/ansi-styles": { "version": "4.3.0", "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", @@ -25317,117 +29155,6 @@ "node": ">=8" } }, - "node_modules/yarn-install": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/yarn-install/-/yarn-install-1.0.0.tgz", - "integrity": "sha512-VO1u181msinhPcGvQTVMnHVOae8zjX/NSksR17e6eXHRveDvHCF5mGjh9hkN8mzyfnCqcBe42LdTs7bScuTaeg==", - "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": "sha512-TIGnTpdo+E3+pCyAluZvtED5p5wCqLdezCyhPZzKPcxvFplEt4i+W7OONCKgeZFT3+y5NZZfOOS/Bdcanm1MYA==", - "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": "sha512-kmCevFghRiWM7HB5zTPULl4r9bVFSWjz62MhqizDGUrq2NWuNMQyuv4tHHoKJHs69M/MF64lEcHdYIocrdWQYA==", - "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": "sha512-U3lRVLMSlsCfjqYPbLyVv11M9CPW4I728d6TCKMAOJueEeB9/8o+eSsMnxPJD+Q+K909sdESg7C+tIkoH6on1A==", - "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/cross-spawn": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-4.0.2.tgz", - "integrity": "sha512-yAXz/pA1tD8Gtg2S98Ekf/sewp3Lcp3YoFKJ4Hkp5h5yLWnKVTDU0kwjKJ8NDCYcfTLfyGkzTikst+jWypT1iA==", - "dev": true, - "dependencies": { - "lru-cache": "^4.0.1", - "which": "^1.2.9" - } - }, - "node_modules/yarn-install/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/yarn-install/node_modules/strip-ansi": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz", - "integrity": "sha512-VhumSSbBqDTP8p2ZLKj40UjBCV4+v8bUSEpUb4KjRgWk9pbqGF4REFj6KEagidb2f/M6AzC0EmFyDNGaw9OCzg==", - "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": "sha512-KKNVtd6pCYgPIKU4cp2733HWYCpplQhddZLBUryaAHou723x+FRzQ5Df824Fj+IyyuiQTRoub4SnIFfIcrp70g==", - "dev": true, - "engines": { - "node": ">=0.8.0" - } - }, - "node_modules/yarn-install/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/yarn-install/node_modules/yallist": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/yallist/-/yallist-2.1.2.tgz", - "integrity": "sha512-ncTzHV7NvsQZkYe1DW7cbDLm0YpzHmZF5r/iyP3ZnQtMiJ+pjzisCiMNI+Sj+xQF5pXhSHxSB3uDbsBTzY/c2A==", - "dev": true - }, "node_modules/yauzl": { "version": "2.10.0", "resolved": "https://registry.npmjs.org/yauzl/-/yauzl-2.10.0.tgz", @@ -26490,16 +30217,6 @@ "regenerator-runtime": "^0.13.10" } }, - "@babel/runtime-corejs3": { - "version": "7.20.1", - "resolved": "https://registry.npmjs.org/@babel/runtime-corejs3/-/runtime-corejs3-7.20.1.tgz", - "integrity": "sha512-CGulbEDcg/ND1Im7fUNRZdGXmX2MTWVVZacQi/6DiKE5HNwZ3aVTm5PV4lO8HHz0B2h8WQyvKKjbX5XgTtydsg==", - "dev": true, - "requires": { - "core-js-pure": "^3.25.1", - "regenerator-runtime": "^0.13.10" - } - }, "@babel/template": { "version": "7.22.15", "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.22.15.tgz", @@ -26731,6 +30448,71 @@ "integrity": "sha512-ZnQMnLV4e7hDlUvw8H+U8ASL02SS2Gn6+9Ac3wGGLIe7+je2AeAOxPY+izIPJDfFDb7eDjev0Us8MO1iFRN8hA==", "dev": true }, + "@isaacs/cliui": { + "version": "8.0.2", + "resolved": "https://registry.npmjs.org/@isaacs/cliui/-/cliui-8.0.2.tgz", + "integrity": "sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA==", + "dev": true, + "requires": { + "string-width": "^5.1.2", + "string-width-cjs": "npm:string-width@^4.2.0", + "strip-ansi": "^7.0.1", + "strip-ansi-cjs": "npm:strip-ansi@^6.0.1", + "wrap-ansi": "^8.1.0", + "wrap-ansi-cjs": "npm:wrap-ansi@^7.0.0" + }, + "dependencies": { + "ansi-regex": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.0.1.tgz", + "integrity": "sha512-n5M855fKb2SsfMIiFFoVrABHJC8QtHwVx+mHWP3QcEqBHYienj5dHSgjbxtC0WEZXYt4wcD6zrQElDPhFuZgfA==", + "dev": true + }, + "ansi-styles": { + "version": "6.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.1.tgz", + "integrity": "sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug==", + "dev": true + }, + "emoji-regex": { + "version": "9.2.2", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-9.2.2.tgz", + "integrity": "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==", + "dev": true + }, + "string-width": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-5.1.2.tgz", + "integrity": "sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA==", + "dev": true, + "requires": { + "eastasianwidth": "^0.2.0", + "emoji-regex": "^9.2.2", + "strip-ansi": "^7.0.1" + } + }, + "strip-ansi": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.0.tgz", + "integrity": "sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ==", + "dev": true, + "requires": { + "ansi-regex": "^6.0.1" + } + }, + "wrap-ansi": { + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-8.1.0.tgz", + "integrity": "sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ==", + "dev": true, + "requires": { + "ansi-styles": "^6.1.0", + "string-width": "^5.0.1", + "strip-ansi": "^7.0.1" + } + } + } + }, "@istanbuljs/load-nyc-config": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/@istanbuljs/load-nyc-config/-/load-nyc-config-1.1.0.tgz", @@ -26750,16 +30532,35 @@ "integrity": "sha512-ZXRY4jNvVgSVQ8DL3LTcakaAtXwTVUxE81hslsyD2AtoXW/wVob10HkOJ1X/pAlcI7D+2YoZKg5do8G/w6RYgA==", "dev": true }, + "@jest/expect-utils": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/@jest/expect-utils/-/expect-utils-29.7.0.tgz", + "integrity": "sha512-GlsNBWiFQFCVi9QVSx7f5AgMeLxe9YCCs5PuP2O2LdjDAA8Jh9eX7lA1Jq/xdXw3Wb3hyvlFNfZIfcRetSzYcA==", + "dev": true, + "requires": { + "jest-get-type": "^29.6.3" + } + }, + "@jest/schemas": { + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/@jest/schemas/-/schemas-29.6.3.tgz", + "integrity": "sha512-mo5j5X+jIZmJQveBKeS/clAueipV7KgiX1vMgCxam1RNYiqE1w62n0/tJJnHtjW8ZHcQco5gY85jA3mi0L+nSA==", + "dev": true, + "requires": { + "@sinclair/typebox": "^0.27.8" + } + }, "@jest/types": { - "version": "26.6.2", - "resolved": "https://registry.npmjs.org/@jest/types/-/types-26.6.2.tgz", - "integrity": "sha512-fC6QCp7Sc5sX6g8Tvbmj4XUTbyrik0akgRy03yjXbQaBWWNWGE7SGtJk98m0N8nzegD/7SggrUlivxo5ax4KWQ==", + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/@jest/types/-/types-29.6.3.tgz", + "integrity": "sha512-u3UPsIilWKOM3F9CXtrG8LEJmNxwoCQC/XVj4IKYXvvpx7QIi/Kg1LI5uDmDpKlac62NUtX7eLjRh+jVZcLOzw==", "dev": true, "requires": { + "@jest/schemas": "^29.6.3", "@types/istanbul-lib-coverage": "^2.0.0", "@types/istanbul-reports": "^3.0.0", "@types/node": "*", - "@types/yargs": "^15.0.0", + "@types/yargs": "^17.0.8", "chalk": "^4.0.0" }, "dependencies": { @@ -26870,6 +30671,15 @@ "@jridgewell/sourcemap-codec": "1.4.14" } }, + "@ljharb/through": { + "version": "2.3.12", + "resolved": "https://registry.npmjs.org/@ljharb/through/-/through-2.3.12.tgz", + "integrity": "sha512-ajo/heTlG3QgC8EGP6APIejksVAYt4ayz4tqoP3MolFELzcH1x1fzwEYRJTPO0IELutZ5HQ0c26/GqAYy79u3g==", + "dev": true, + "requires": { + "call-bind": "^1.0.5" + } + }, "@nicolo-ribaudo/eslint-scope-5-internals": { "version": "5.1.1-v1", "resolved": "https://registry.npmjs.org/@nicolo-ribaudo/eslint-scope-5-internals/-/eslint-scope-5-internals-5.1.1-v1.tgz", @@ -26879,12 +30689,122 @@ "eslint-scope": "5.1.1" } }, + "@open-draft/until": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/@open-draft/until/-/until-1.0.3.tgz", + "integrity": "sha512-Aq58f5HiWdyDlFffbbSjAlv596h/cOnt2DO1w3DOC7OJ5EHs0hd/nycJfiu9RJbT6Yk6F1knnRRXNSpxoIVZ9Q==", + "dev": true + }, + "@percy/appium-app": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/@percy/appium-app/-/appium-app-2.0.3.tgz", + "integrity": "sha512-6INeUJSyK2LzWV4Cc9bszNqKr3/NLcjFelUC2grjPnm6+jLA29inBF4ZE3PeTfLeCSw/0jyCGWV5fr9AyxtzCA==", + "dev": true, + "requires": { + "@percy/sdk-utils": "^1.27.0-beta.0", + "tmp": "^0.2.1" + }, + "dependencies": { + "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" + } + } + } + }, + "@percy/sdk-utils": { + "version": "1.27.7", + "resolved": "https://registry.npmjs.org/@percy/sdk-utils/-/sdk-utils-1.27.7.tgz", + "integrity": "sha512-E21dIEQ9wwGDno41FdMDYf6jJow5scbWGClqKE/ptB+950W4UF5C4hxhVVQoEJxDdLE/Gy/8ZJR7upvPHShWDg==", + "dev": true + }, + "@percy/selenium-webdriver": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/@percy/selenium-webdriver/-/selenium-webdriver-2.0.3.tgz", + "integrity": "sha512-JfLJVRkwNfqVofe7iGKtoQbOcKSSj9t4pWFbSUk95JfwAA7b9/c+dlBsxgIRrdrMYzLRjnJkYAFSZkJ4F4A19A==", + "dev": true, + "requires": { + "@percy/sdk-utils": "^1.27.2", + "node-request-interceptor": "^0.6.3" + } + }, + "@pkgjs/parseargs": { + "version": "0.11.0", + "resolved": "https://registry.npmjs.org/@pkgjs/parseargs/-/parseargs-0.11.0.tgz", + "integrity": "sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg==", + "dev": true, + "optional": true + }, "@polka/url": { "version": "1.0.0-next.21", "resolved": "https://registry.npmjs.org/@polka/url/-/url-1.0.0-next.21.tgz", "integrity": "sha512-a5Sab1C4/icpTZVzZc5Ghpz88yQtGOyNqYXcZgOssB2uuAr+wF/MvN6bgtW32q7HHrvBki+BsZ0OuNv6EV3K9g==", "dev": true }, + "@puppeteer/browsers": { + "version": "1.9.1", + "resolved": "https://registry.npmjs.org/@puppeteer/browsers/-/browsers-1.9.1.tgz", + "integrity": "sha512-PuvK6xZzGhKPvlx3fpfdM2kYY3P/hB1URtK8wA7XUJ6prn6pp22zvJHu48th0SGcHL9SutbPHrFuQgfXTFobWA==", + "dev": true, + "requires": { + "debug": "4.3.4", + "extract-zip": "2.0.1", + "progress": "2.0.3", + "proxy-agent": "6.3.1", + "tar-fs": "3.0.4", + "unbzip2-stream": "1.4.3", + "yargs": "17.7.2" + }, + "dependencies": { + "tar-fs": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/tar-fs/-/tar-fs-3.0.4.tgz", + "integrity": "sha512-5AFQU8b9qLfZCX9zp2duONhPmZv0hGYiBPJsyUdqMjzq/mqVpy/rEUSeHk1+YitmxugaptgBh5oDGU3VsAJq4w==", + "dev": true, + "requires": { + "mkdirp-classic": "^0.5.2", + "pump": "^3.0.0", + "tar-stream": "^3.1.5" + } + }, + "tar-stream": { + "version": "3.1.7", + "resolved": "https://registry.npmjs.org/tar-stream/-/tar-stream-3.1.7.tgz", + "integrity": "sha512-qJj60CXt7IU1Ffyc3NJMjh6EkuCFej46zUqJ4J7pqYlThyd9bO0XBTmcOIhSzZJVWfsLks0+nle/j538YAW9RQ==", + "dev": true, + "requires": { + "b4a": "^1.6.4", + "fast-fifo": "^1.2.0", + "streamx": "^2.15.0" + } + }, + "yargs": { + "version": "17.7.2", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.7.2.tgz", + "integrity": "sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==", + "dev": true, + "requires": { + "cliui": "^8.0.1", + "escalade": "^3.1.1", + "get-caller-file": "^2.0.5", + "require-directory": "^2.1.1", + "string-width": "^4.2.3", + "y18n": "^5.0.5", + "yargs-parser": "^21.1.1" + } + } + } + }, + "@sinclair/typebox": { + "version": "0.27.8", + "resolved": "https://registry.npmjs.org/@sinclair/typebox/-/typebox-0.27.8.tgz", + "integrity": "sha512-+Fj43pSMwJs4KRrH/938Uf+uAELIgVBmQzg/q1YG10djyfA3TnrU8N8XzqCh/okZdszqBQTZf96idMfE5lnwTA==", + "dev": true + }, "@sindresorhus/is": { "version": "4.6.0", "resolved": "https://registry.npmjs.org/@sindresorhus/is/-/is-4.6.0.tgz", @@ -26941,6 +30861,20 @@ "defer-to-connect": "^2.0.0" } }, + "@tootallnate/once": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/@tootallnate/once/-/once-2.0.0.tgz", + "integrity": "sha512-XCuKFP5PS55gnMVu3dty8KPatLqUoy/ZYzDzAGCQ8JNFCkLXzmI7vNHCR+XpbZaMWQK/vQubr7PkYq8g470J/A==", + "dev": true, + "optional": true, + "peer": true + }, + "@tootallnate/quickjs-emscripten": { + "version": "0.23.0", + "resolved": "https://registry.npmjs.org/@tootallnate/quickjs-emscripten/-/quickjs-emscripten-0.23.0.tgz", + "integrity": "sha512-C5Mc6rdnsaJDjO3UpGW/CQTHtCKaYlScZTly4JIu97Jxo/odCiH0ITnDXSJPTOrEKk/ycSZ0AOgTmkDtkOsvIA==", + "dev": true + }, "@types/aria-query": { "version": "5.0.1", "resolved": "https://registry.npmjs.org/@types/aria-query/-/aria-query-5.0.1.tgz", @@ -26983,24 +30917,6 @@ "@types/ms": "*" } }, - "@types/diff": { - "version": "5.0.2", - "resolved": "https://registry.npmjs.org/@types/diff/-/diff-5.0.2.tgz", - "integrity": "sha512-uw8eYMIReOwstQ0QKF0sICefSy8cNO/v7gOTiIy9SbwuHyEecJUm7qlgueOO5S1udZ5I/irVydHVwMchgzbKTg==", - "dev": true - }, - "@types/easy-table": { - "version": "0.0.33", - "resolved": "https://registry.npmjs.org/@types/easy-table/-/easy-table-0.0.33.tgz", - "integrity": "sha512-/vvqcJPmZUfQwCgemL0/34G7bIQnCuvgls379ygRlcC1FqNqk3n+VZ15dAO51yl6JNDoWd8vsk+kT8zfZ1VZSw==", - "dev": true - }, - "@types/ejs": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/@types/ejs/-/ejs-3.1.1.tgz", - "integrity": "sha512-RQul5wEfY7BjWm0sYY86cmUN/pcXWGyVxWX93DFFJvcrxax5zKlieLwA3T77xJGwNcZW0YW6CYG70p1m8xPFmA==", - "dev": true - }, "@types/eslint": { "version": "8.4.9", "resolved": "https://registry.npmjs.org/@types/eslint/-/eslint-8.4.9.tgz", @@ -27039,21 +30955,12 @@ "integrity": "sha512-R1g/VyKFFI2HLC1QGAeTtCBWCo6n75l41OnsVYNbmKG+kempOESaodf6BeJyUM3Q0rKa/NQcTHbB2+66lNnxLw==", "dev": true }, - "@types/fibers": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/@types/fibers/-/fibers-3.1.1.tgz", - "integrity": "sha512-yHoUi46uika0snoTpNcVqUSvgbRndaIps4TUCotrXjtc0DHDoPQckmyXEZ2bX3e4mpJmyEW3hRhCwQa/ISCPaA==", + "@types/gitconfiglocal": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/@types/gitconfiglocal/-/gitconfiglocal-2.0.3.tgz", + "integrity": "sha512-W6hyZux6TrtKfF2I9XNLVcsFr4xRr0T+S6hrJ9nDkhA2vzsFPIEAbnY4vgb6v2yKXQ9MJVcbLsARNlMfg4EVtQ==", "dev": true }, - "@types/fs-extra": { - "version": "9.0.13", - "resolved": "https://registry.npmjs.org/@types/fs-extra/-/fs-extra-9.0.13.tgz", - "integrity": "sha512-nEnwB++1u5lVDM2UI4c1+5R+FYaKfaAzS4OococimjVm3nQw3TuzH5UNsocrcTBbhnerblyHj4A49qXbIiZdpA==", - "dev": true, - "requires": { - "@types/node": "*" - } - }, "@types/github-slugger": { "version": "1.3.0", "resolved": "https://registry.npmjs.org/@types/github-slugger/-/github-slugger-1.3.0.tgz", @@ -27070,40 +30977,30 @@ } }, "@types/http-cache-semantics": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/@types/http-cache-semantics/-/http-cache-semantics-4.0.1.tgz", - "integrity": "sha512-SZs7ekbP8CN0txVG2xVRH6EgKmEm31BOxA07vkFaETzZz1xh+cbt8BcI0slpymvwhx5dlFnQG2rTlPVQn+iRPQ==", + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/@types/http-cache-semantics/-/http-cache-semantics-4.0.4.tgz", + "integrity": "sha512-1m0bIFVc7eJWyve9S0RnuRgcQqF/Xd5QsUZAZeQFr1Q3/p9JWoQQEqmVy+DPTNpGXwhgIetAoYF8JSc33q29QA==", "dev": true }, - "@types/inquirer": { - "version": "7.3.3", - "resolved": "https://registry.npmjs.org/@types/inquirer/-/inquirer-7.3.3.tgz", - "integrity": "sha512-HhxyLejTHMfohAuhRun4csWigAMjXTmRyiJTU1Y/I1xmggikFMkOUoMQRlFm+zQcPEGHSs3io/0FAmNZf8EymQ==", - "dev": true, - "requires": { - "@types/through": "*", - "rxjs": "^6.4.0" - } - }, "@types/istanbul-lib-coverage": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/@types/istanbul-lib-coverage/-/istanbul-lib-coverage-2.0.4.tgz", - "integrity": "sha512-z/QT1XN4K4KYuslS23k62yDIDLwLFkzxOuMplDtObz0+y7VqJCaO2o+SPwHCvLFZh7xazvvoor2tA/hPz9ee7g==", + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/@types/istanbul-lib-coverage/-/istanbul-lib-coverage-2.0.6.tgz", + "integrity": "sha512-2QF/t/auWm0lsy8XtKVPG19v3sSOQlJe/YHZgfjb/KBBHOGSV+J2q/S671rcq9uTBrLAXmZpqJiaQbMT+zNU1w==", "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==", + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/@types/istanbul-lib-report/-/istanbul-lib-report-3.0.3.tgz", + "integrity": "sha512-NQn7AHQnk/RSLOxrBbGyJM/aVQ+pjj5HCgasFxc0K/KhoATfQ/47AyUl15I2yBUpihjmas+a+VJBOqecrFH+uA==", "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==", + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/@types/istanbul-reports/-/istanbul-reports-3.0.4.tgz", + "integrity": "sha512-pk2B1NWalF9toCRu6gjBzR69syFjP4Od8WRAX+0mmf9lAjCRicLOWc+ZrxZHx/0XRjotgkF9t6iaMJ+aXcOdZQ==", "dev": true, "requires": { "@types/istanbul-lib-report": "*" @@ -27130,39 +31027,6 @@ "keyv": "*" } }, - "@types/lodash": { - "version": "4.14.187", - "resolved": "https://registry.npmjs.org/@types/lodash/-/lodash-4.14.187.tgz", - "integrity": "sha512-MrO/xLXCaUgZy3y96C/iOsaIqZSeupyTImKClHunL5GrmaiII2VwvWmLBu2hwa0Kp0sV19CsyjtrTc/Fx8rg/A==", - "dev": true - }, - "@types/lodash.flattendeep": { - "version": "4.4.7", - "resolved": "https://registry.npmjs.org/@types/lodash.flattendeep/-/lodash.flattendeep-4.4.7.tgz", - "integrity": "sha512-1h6GW/AeZw/Wej6uxrqgmdTDZX1yFS39lRsXYkg+3kWvOWWrlGCI6H7lXxlUHOzxDT4QeYGmgPpQ3BX9XevzOg==", - "dev": true, - "requires": { - "@types/lodash": "*" - } - }, - "@types/lodash.pickby": { - "version": "4.6.7", - "resolved": "https://registry.npmjs.org/@types/lodash.pickby/-/lodash.pickby-4.6.7.tgz", - "integrity": "sha512-4ebXRusuLflfscbD0PUX4eVknDHD9Yf+uMtBIvA/hrnTqeAzbuHuDjvnYriLjUrI9YrhCPVKUf4wkRSXJQ6gig==", - "dev": true, - "requires": { - "@types/lodash": "*" - } - }, - "@types/lodash.union": { - "version": "4.6.7", - "resolved": "https://registry.npmjs.org/@types/lodash.union/-/lodash.union-4.6.7.tgz", - "integrity": "sha512-6HXM6tsnHJzKgJE0gA/LhTGf/7AbjUk759WZ1MziVm+OBNAATHhdgj+a3KVE8g76GCLAnN4ZEQQG1EGgtBIABA==", - "dev": true, - "requires": { - "@types/lodash": "*" - } - }, "@types/mdast": { "version": "3.0.10", "resolved": "https://registry.npmjs.org/@types/mdast/-/mdast-3.0.10.tgz", @@ -27173,9 +31037,9 @@ } }, "@types/mocha": { - "version": "8.2.3", - "resolved": "https://registry.npmjs.org/@types/mocha/-/mocha-8.2.3.tgz", - "integrity": "sha512-ekGvFhFgrc2zYQoX4JeZPmVzZxw6Dtllga7iGHzfbYIYkAMUx/sAFP2GdFpLff+vdHXu5fl7WX9AT+TtqYcsyw==", + "version": "10.0.6", + "resolved": "https://registry.npmjs.org/@types/mocha/-/mocha-10.0.6.tgz", + "integrity": "sha512-dJvrYWxP/UcXm36Qn36fxhUKu8A/xMRXVT2cliFF1Z7UA9liG5Psj3ezNSZw+5puH2czDXRLcXQxf8JbJt0ejg==", "dev": true }, "@types/ms": { @@ -27185,10 +31049,13 @@ "dev": true }, "@types/node": { - "version": "17.0.45", - "resolved": "https://registry.npmjs.org/@types/node/-/node-17.0.45.tgz", - "integrity": "sha512-w+tIMs3rq2afQdsPJlODhoUEKzFP1ayaoyl1CcnwtIlsVe7K7bA1NGm4s3PraqTLlXnbIN84zuBlxBWo1u9BLw==", - "dev": true + "version": "20.11.6", + "resolved": "https://registry.npmjs.org/@types/node/-/node-20.11.6.tgz", + "integrity": "sha512-+EOokTnksGVgip2PbYbr3xnR7kZigh4LbybAfBAw5BpnQ+FqBYUsvCEjYd70IXKlbohQ64mzEYmMtlWUY8q//Q==", + "dev": true, + "requires": { + "undici-types": "~5.26.4" + } }, "@types/normalize-package-data": { "version": "2.4.1", @@ -27196,30 +31063,6 @@ "integrity": "sha512-Gj7cI7z+98M282Tqmp2K5EIsoouUEzbBJhQQzDE3jSIRk6r9gsz0oUokqIUR4u1R3dMHo0pDHM7sNOHyhulypw==", "dev": true }, - "@types/object-inspect": { - "version": "1.8.1", - "resolved": "https://registry.npmjs.org/@types/object-inspect/-/object-inspect-1.8.1.tgz", - "integrity": "sha512-0JTdf3CGV0oWzE6Wa40Ayv2e2GhpP3pEJMcrlM74vBSJPuuNkVwfDnl0SZxyFCXETcB4oKA/MpTVfuYSMOelBg==", - "dev": true - }, - "@types/puppeteer": { - "version": "5.4.7", - "resolved": "https://registry.npmjs.org/@types/puppeteer/-/puppeteer-5.4.7.tgz", - "integrity": "sha512-JdGWZZYL0vKapXF4oQTC5hLVNfOgdPrqeZ1BiQnGk5cB7HeE91EWUiTdVSdQPobRN8rIcdffjiOgCYJ/S8QrnQ==", - "dev": true, - "requires": { - "@types/node": "*" - } - }, - "@types/recursive-readdir": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/@types/recursive-readdir/-/recursive-readdir-2.2.1.tgz", - "integrity": "sha512-Xd+Ptc4/F2ueInqy5yK2FI5FxtwwbX2+VZpcg+9oYsFJVen8qQKGapCr+Bi5wQtHU1cTXT8s+07lo/nKPgu8Gg==", - "dev": true, - "requires": { - "@types/node": "*" - } - }, "@types/responselike": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/@types/responselike/-/responselike-1.0.0.tgz", @@ -27230,39 +31073,21 @@ } }, "@types/stack-utils": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/@types/stack-utils/-/stack-utils-2.0.1.tgz", - "integrity": "sha512-Hl219/BT5fLAaz6NDkSuhzasy49dwQS/DSdu4MdggFB8zcXv7vflBI3xp7FEmkmdDkBUI2bPUNeMttp2knYdxw==", + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/@types/stack-utils/-/stack-utils-2.0.3.tgz", + "integrity": "sha512-9aEbYZ3TbYMznPdcdr3SmIrLXwC/AKZXQeCf9Pgao5CKb8CyHuEX5jzWPTkvregvhRJHcpRO6BFoGW9ycaOkYw==", "dev": true }, - "@types/stream-buffers": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/@types/stream-buffers/-/stream-buffers-3.0.4.tgz", - "integrity": "sha512-qU/K1tb2yUdhXkLIATzsIPwbtX6BpZk0l3dPW6xqWyhfzzM1ECaQ/8faEnu3CNraLiQ9LHyQQPBGp7N9Fbs25w==", - "dev": true, - "requires": { - "@types/node": "*" - } - }, "@types/supports-color": { "version": "8.1.1", "resolved": "https://registry.npmjs.org/@types/supports-color/-/supports-color-8.1.1.tgz", "integrity": "sha512-dPWnWsf+kzIG140B8z2w3fr5D03TLWbOAFQl45xUpI3vcizeXriNR5VYkWZ+WTMsUHqZ9Xlt3hrxGNANFyNQfw==", "dev": true }, - "@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/tmp": { - "version": "0.2.3", - "resolved": "https://registry.npmjs.org/@types/tmp/-/tmp-0.2.3.tgz", - "integrity": "sha512-dDZH/tXzwjutnuk4UacGgFRwV+JSLaXL1ikvidfJprkb7L9Nx1njcRHHmi3Dsvt7pgqqTEeucQuOrWHPFgzVHA==", + "@types/triple-beam": { + "version": "1.3.5", + "resolved": "https://registry.npmjs.org/@types/triple-beam/-/triple-beam-1.3.5.tgz", + "integrity": "sha512-6WaYesThRMCl19iryMYP7/x2OVgCtbIVflDGFpWnb9irXI3UjYE4AzmYuiUKY1AJstGijoY+MgUszMgRxIYTYw==", "dev": true }, "@types/ua-parser-js": { @@ -27293,19 +31118,28 @@ "integrity": "sha512-8oDqyLC7eD4HM307boe2QWKyuzdzWBj56xI/imSl2cpL+U3tCMaTAkMJ4ee5JBZ/FsOJlvRGeIShiZDAl1qERA==", "dev": true }, + "@types/ws": { + "version": "8.5.10", + "resolved": "https://registry.npmjs.org/@types/ws/-/ws-8.5.10.tgz", + "integrity": "sha512-vmQSUcfalpIq0R9q7uTo2lXs6eGIpt9wtnLdMv9LVpIjCA/+ufZRozlVoVelIYixx1ugCBKDhn89vnsEGOCx9A==", + "dev": true, + "requires": { + "@types/node": "*" + } + }, "@types/yargs": { - "version": "15.0.14", - "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-15.0.14.tgz", - "integrity": "sha512-yEJzHoxf6SyQGhBhIYGXQDSCkJjB6HohDShto7m8vaKg9Yp0Yn8+71J9eakh2bnPg6BfsH9PRMhiRTZnd4eXGQ==", + "version": "17.0.32", + "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-17.0.32.tgz", + "integrity": "sha512-xQ67Yc/laOG5uMfX/093MRlGGCIBzZMarVa+gfNKJxWAIgykYpVGkBdbqEzGDDfCrVUj6Hiff4mTZ5BA6TmAog==", "dev": true, "requires": { "@types/yargs-parser": "*" } }, "@types/yargs-parser": { - "version": "21.0.0", - "resolved": "https://registry.npmjs.org/@types/yargs-parser/-/yargs-parser-21.0.0.tgz", - "integrity": "sha512-iO9ZQHkZxHn4mSakYV0vFHAVDyEOIJQrV2uZ06HxEPcx+mt8swXoZHIbaaJ2crJYFfErySgktuTZ3BeLz+XmFA==", + "version": "21.0.3", + "resolved": "https://registry.npmjs.org/@types/yargs-parser/-/yargs-parser-21.0.3.tgz", + "integrity": "sha512-I4q9QU9MQv4oEOz4tAHJtNz1cwuLxn2F3xcc2iV5WdqLPpUnj30aUuxt1mAxYTG+oe8CZMV/+6rU4S4gRDzqtQ==", "dev": true }, "@types/yauzl": { @@ -27318,12 +31152,6 @@ "@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 - }, "@videojs/http-streaming": { "version": "2.14.3", "resolved": "https://registry.npmjs.org/@videojs/http-streaming/-/http-streaming-2.14.3.tgz", @@ -27362,6 +31190,34 @@ "is-function": "^1.0.1" } }, + "@vitest/snapshot": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/@vitest/snapshot/-/snapshot-1.2.1.tgz", + "integrity": "sha512-Tmp/IcYEemKaqAYCS08sh0vORLJkMr0NRV76Gl8sHGxXT5151cITJCET20063wk0Yr/1koQ6dnmP6eEqezmd/Q==", + "dev": true, + "requires": { + "magic-string": "^0.30.5", + "pathe": "^1.1.1", + "pretty-format": "^29.7.0" + }, + "dependencies": { + "@jridgewell/sourcemap-codec": { + "version": "1.4.15", + "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.15.tgz", + "integrity": "sha512-eF2rxCRulEKXHTRiDrDy6erMYWqNw4LPdQ8UQA4huuxaQsVeRPFl2oM8oDGxMFhJUWZf9McpLtJasDDZb/Bpeg==", + "dev": true + }, + "magic-string": { + "version": "0.30.5", + "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.5.tgz", + "integrity": "sha512-7xlpfBaQaP/T6Vh8MO/EqXSW5En6INHEvEXQiuff7Gku0PWjU3uf6w/j9o7O+SpB5fOAkrI5HeoNgwjEO0pFsA==", + "dev": true, + "requires": { + "@jridgewell/sourcemap-codec": "^1.4.15" + } + } + } + }, "@vue/compiler-core": { "version": "3.2.41", "resolved": "https://registry.npmjs.org/@vue/compiler-core/-/compiler-core-3.2.41.tgz", @@ -27456,1756 +31312,2876 @@ "optional": true }, "@wdio/browserstack-service": { - "version": "7.16.16", - "resolved": "https://registry.npmjs.org/@wdio/browserstack-service/-/browserstack-service-7.16.16.tgz", - "integrity": "sha512-q4wUh/j0MR2SwhTkmIFif2DaXgH5yzdgOer6G/fac2n81zLCSpQHWO5aQ9T0An9CAd4L2A+t3dmChpBJPkHWSw==", - "dev": true, - "requires": { - "@types/node": "^17.0.4", - "@wdio/logger": "7.16.0", - "@wdio/types": "7.16.14", - "browserstack-local": "^1.4.5", - "got": "^11.0.2", - "webdriverio": "7.16.16" + "version": "8.29.1", + "resolved": "https://registry.npmjs.org/@wdio/browserstack-service/-/browserstack-service-8.29.1.tgz", + "integrity": "sha512-dLEJcdVF0Cu+2REByVOfLUzx9FvMias1VsxSCZpKXeIAGAIWBBdNdooK6Vdc9QdS36S5v/mk0/rTTQhYn4nWjQ==", + "dev": true, + "requires": { + "@percy/appium-app": "^2.0.1", + "@percy/selenium-webdriver": "^2.0.3", + "@types/gitconfiglocal": "^2.0.1", + "@wdio/logger": "8.28.0", + "@wdio/reporter": "8.29.1", + "@wdio/types": "8.29.1", + "browserstack-local": "^1.5.1", + "chalk": "^5.3.0", + "csv-writer": "^1.6.0", + "formdata-node": "5.0.1", + "git-repo-info": "^2.1.1", + "gitconfiglocal": "^2.1.0", + "got": "^12.6.1", + "uuid": "^9.0.0", + "webdriverio": "8.29.1", + "winston-transport": "^4.5.0", + "yauzl": "^2.10.0" }, "dependencies": { - "@wdio/config": { - "version": "7.16.16", - "resolved": "https://registry.npmjs.org/@wdio/config/-/config-7.16.16.tgz", - "integrity": "sha512-K/ObPuo6Da2liz++OKOIfbdpFwI7UWiFcBylfJkCYbweuXCoW1aUqlKI6rmKPwCH9Uqr/RHWu6p8eo0zWe6xVA==", + "@puppeteer/browsers": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/@puppeteer/browsers/-/browsers-1.3.0.tgz", + "integrity": "sha512-an3QdbNPkuU6qpxpbssxAbjRLJcF+eP4L8UqIY3+6n0sbaVxw5pz7PiCLy9g32XEZuoamUlV5ZQPnA6FxvkIHA==", + "dev": true, + "optional": true, + "peer": true, + "requires": { + "debug": "4.3.4", + "extract-zip": "2.0.1", + "http-proxy-agent": "5.0.0", + "https-proxy-agent": "5.0.1", + "progress": "2.0.3", + "proxy-from-env": "1.1.0", + "tar-fs": "2.1.1", + "unbzip2-stream": "1.4.3", + "yargs": "17.7.1" + } + }, + "@sindresorhus/is": { + "version": "5.6.0", + "resolved": "https://registry.npmjs.org/@sindresorhus/is/-/is-5.6.0.tgz", + "integrity": "sha512-TV7t8GKYaJWsn00tFDqBw8+Uqmr8A0fRU1tvTQhyZzGv0sJCGRQL3JGMI3ucuKo3XIZdUP+Lx7/gh2t3lewy7g==", + "dev": true + }, + "@szmarczak/http-timer": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/@szmarczak/http-timer/-/http-timer-5.0.1.tgz", + "integrity": "sha512-+PmQX0PiAYPMeVYe237LJAYvOMYW1j2rH5YROyS3b4CTVJum34HfRvKvAzozHAQG0TnHNdUfY9nCeUyRAs//cw==", + "dev": true, + "requires": { + "defer-to-connect": "^2.0.1" + } + }, + "@types/which": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/@types/which/-/which-2.0.2.tgz", + "integrity": "sha512-113D3mDkZDjo+EeUEHCFy0qniNc1ZpecGiAU7WSo7YDoSzolZIQKpYFHrPpjkB2nuyahcKfrmLXeQlh7gqJYdw==", + "dev": true, + "optional": true, + "peer": true + }, + "archiver": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/archiver/-/archiver-6.0.1.tgz", + "integrity": "sha512-CXGy4poOLBKptiZH//VlWdFuUC1RESbdZjGjILwBuZ73P7WkAUN0htfSfBq/7k6FRFlpu7bg4JOkj1vU9G6jcQ==", + "dev": true, + "requires": { + "archiver-utils": "^4.0.1", + "async": "^3.2.4", + "buffer-crc32": "^0.2.1", + "readable-stream": "^3.6.0", + "readdir-glob": "^1.1.2", + "tar-stream": "^3.0.0", + "zip-stream": "^5.0.1" + } + }, + "archiver-utils": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/archiver-utils/-/archiver-utils-4.0.1.tgz", + "integrity": "sha512-Q4Q99idbvzmgCTEAAhi32BkOyq8iVI5EwdO0PmBDSGIzzjYNdcFn7Q7k3OzbLy4kLUPXfJtG6fO2RjftXbobBg==", + "dev": true, + "requires": { + "glob": "^8.0.0", + "graceful-fs": "^4.2.0", + "lazystream": "^1.0.0", + "lodash": "^4.17.15", + "normalize-path": "^3.0.0", + "readable-stream": "^3.6.0" + } + }, + "async": { + "version": "3.2.5", + "resolved": "https://registry.npmjs.org/async/-/async-3.2.5.tgz", + "integrity": "sha512-baNZyqaaLhyLVKm/DlvdW051MSgO6b8eVfIezl9E5PqWxFgzLm/wQntEW4zOytVburDEr0JlALEpdOFwvErLsg==", + "dev": true + }, + "brace-expansion": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", + "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", + "dev": true, + "requires": { + "balanced-match": "^1.0.0" + } + }, + "cacheable-lookup": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/cacheable-lookup/-/cacheable-lookup-7.0.0.tgz", + "integrity": "sha512-+qJyx4xiKra8mZrcwhjMRMUhD5NR1R8esPkzIYxX96JiecFoxAXFuz/GpR3+ev4PE1WamHip78wV0vcmPQtp8w==", + "dev": true + }, + "cacheable-request": { + "version": "10.2.14", + "resolved": "https://registry.npmjs.org/cacheable-request/-/cacheable-request-10.2.14.tgz", + "integrity": "sha512-zkDT5WAF4hSSoUgyfg5tFIxz8XQK+25W/TLVojJTMKBaxevLBBtLxgqguAuVQB8PVW79FVjHcU+GJ9tVbDZ9mQ==", + "dev": true, + "requires": { + "@types/http-cache-semantics": "^4.0.2", + "get-stream": "^6.0.1", + "http-cache-semantics": "^4.1.1", + "keyv": "^4.5.3", + "mimic-response": "^4.0.0", + "normalize-url": "^8.0.0", + "responselike": "^3.0.0" + } + }, + "chalk": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-5.3.0.tgz", + "integrity": "sha512-dLitG79d+GV1Nb/VYcCDFivJeK1hiukt9QjRNVOsUtTy1rR1YJsmpGGTZ3qJos+uw7WmWF4wUwBd9jxjocFC2w==", + "dev": true + }, + "chrome-launcher": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/chrome-launcher/-/chrome-launcher-1.1.0.tgz", + "integrity": "sha512-rJYWeEAERwWIr3c3mEVXwNiODPEdMRlRxHc47B1qHPOolHZnkj7rMv1QSUfPoG6MgatWj5AxSpnKKR4QEwEQIQ==", + "dev": true, + "optional": true, + "peer": true, + "requires": { + "@types/node": "*", + "escape-string-regexp": "^4.0.0", + "is-wsl": "^2.2.0", + "lighthouse-logger": "^2.0.1" + } + }, + "compress-commons": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/compress-commons/-/compress-commons-5.0.1.tgz", + "integrity": "sha512-MPh//1cERdLtqwO3pOFLeXtpuai0Y2WCd5AhtKxznqM7WtaMYaOEMSgn45d9D10sIHSfIKE603HlOp8OPGrvag==", + "dev": true, + "requires": { + "crc-32": "^1.2.0", + "crc32-stream": "^5.0.0", + "normalize-path": "^3.0.0", + "readable-stream": "^3.6.0" + } + }, + "crc32-stream": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/crc32-stream/-/crc32-stream-5.0.0.tgz", + "integrity": "sha512-B0EPa1UK+qnpBZpG+7FgPCu0J2ETLpXq09o9BkLkEAhdB6Z61Qo4pJ3JYu0c+Qi+/SAL7QThqnzS06pmSSyZaw==", + "dev": true, + "requires": { + "crc-32": "^1.2.0", + "readable-stream": "^3.4.0" + } + }, + "cross-fetch": { + "version": "3.1.6", + "resolved": "https://registry.npmjs.org/cross-fetch/-/cross-fetch-3.1.6.tgz", + "integrity": "sha512-riRvo06crlE8HiqOwIpQhxwdOk4fOeR7FVM/wXoxchFEqMNUjvbs3bfo4OTgMEMHzppd4DxFBDbyySj8Cv781g==", + "dev": true, + "optional": true, + "peer": true, + "requires": { + "node-fetch": "^2.6.11" + } + }, + "devtools": { + "version": "8.29.1", + "resolved": "https://registry.npmjs.org/devtools/-/devtools-8.29.1.tgz", + "integrity": "sha512-fbH0Z7CPK4OZSgUw2QcAppczowxtSyvFztPUmiFyi99cUadjEOwlg0aL3pBVlIDo67olYjGb8GD1M5Z4yI/P6w==", + "dev": true, + "optional": true, + "peer": true, + "requires": { + "@types/node": "^20.1.0", + "@wdio/config": "8.29.1", + "@wdio/logger": "8.28.0", + "@wdio/protocols": "8.24.12", + "@wdio/types": "8.29.1", + "@wdio/utils": "8.29.1", + "chrome-launcher": "^1.0.0", + "edge-paths": "^3.0.5", + "import-meta-resolve": "^4.0.0", + "puppeteer-core": "20.3.0", + "query-selector-shadow-dom": "^1.0.0", + "ua-parser-js": "^1.0.1", + "uuid": "^9.0.0", + "which": "^4.0.0" + }, + "dependencies": { + "which": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/which/-/which-4.0.0.tgz", + "integrity": "sha512-GlaYyEb07DPxYCKhKzplCWBJtvxZcZMrL+4UkrTSJHHPyZU4mYYTv3qaOe77H7EODLSSopAUFAc6W8U4yqvscg==", + "dev": true, + "optional": true, + "peer": true, + "requires": { + "isexe": "^3.1.1" + } + } + } + }, + "devtools-protocol": { + "version": "0.0.1120988", + "resolved": "https://registry.npmjs.org/devtools-protocol/-/devtools-protocol-0.0.1120988.tgz", + "integrity": "sha512-39fCpE3Z78IaIPChJsP6Lhmkbf4dWXOmzLk/KFTdRkNk/0JymRIfUynDVRndV9HoDz8PyalK1UH21ST/ivwW5Q==", + "dev": true, + "optional": true, + "peer": true + }, + "edge-paths": { + "version": "3.0.5", + "resolved": "https://registry.npmjs.org/edge-paths/-/edge-paths-3.0.5.tgz", + "integrity": "sha512-sB7vSrDnFa4ezWQk9nZ/n0FdpdUuC6R1EOrlU3DL+bovcNFK28rqu2emmAUjujYEJTWIgQGqgVVWUZXMnc8iWg==", + "dev": true, + "optional": true, + "peer": true, + "requires": { + "@types/which": "^2.0.1", + "which": "^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, + "optional": true, + "peer": true + }, + "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 + }, + "glob": { + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/glob/-/glob-8.1.0.tgz", + "integrity": "sha512-r8hpEjiQEYlF2QU0df3dS+nxxSIreXQS1qRhMJM0Q5NDdR386C7jb7Hwwod8Fgiuex+k0GFjgft18yvxm5XoCQ==", "dev": true, "requires": { - "@wdio/logger": "7.16.0", - "@wdio/types": "7.16.14", - "deepmerge": "^4.0.0", - "glob": "^7.1.2" + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^5.0.1", + "once": "^1.3.0" + }, + "dependencies": { + "minimatch": { + "version": "5.1.6", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-5.1.6.tgz", + "integrity": "sha512-lKwV/1brpG6mBUFHtb7NUmtABCb2WZZmm2wNiOA5hAb8VdCS4B3dtMWyvcoViccwAW/COERjXLt0zP1zXUN26g==", + "dev": true, + "requires": { + "brace-expansion": "^2.0.1" + } + } } }, - "@wdio/protocols": { - "version": "7.16.7", - "resolved": "https://registry.npmjs.org/@wdio/protocols/-/protocols-7.16.7.tgz", - "integrity": "sha512-Wv40pNQcLiPzQ3o98Mv4A8T1EBQ6k4khglz/e2r16CTm+F3DDYh8eLMAsU5cgnmuwwDKX1EyOiFwieykBn5MCg==", - "dev": true + "got": { + "version": "12.6.1", + "resolved": "https://registry.npmjs.org/got/-/got-12.6.1.tgz", + "integrity": "sha512-mThBblvlAF1d4O5oqyvN+ZxLAYwIJK7bpMxgYqPD9okW0C3qm5FFn7k811QrcuEBwaogR3ngOFoCfs6mRv7teQ==", + "dev": true, + "requires": { + "@sindresorhus/is": "^5.2.0", + "@szmarczak/http-timer": "^5.0.1", + "cacheable-lookup": "^7.0.0", + "cacheable-request": "^10.2.8", + "decompress-response": "^6.0.0", + "form-data-encoder": "^2.1.2", + "get-stream": "^6.0.1", + "http2-wrapper": "^2.1.10", + "lowercase-keys": "^3.0.0", + "p-cancelable": "^3.0.0", + "responselike": "^3.0.0" + } }, - "@wdio/repl": { - "version": "7.16.14", - "resolved": "https://registry.npmjs.org/@wdio/repl/-/repl-7.16.14.tgz", - "integrity": "sha512-Ezih0Y+lsGkKv3H3U56hdWgZiQGA3VaAYguSLd9+g1xbQq+zMKqSmfqECD9bAy+OgCCiVTRstES6lHZxJVPhAg==", + "http-proxy-agent": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/http-proxy-agent/-/http-proxy-agent-5.0.0.tgz", + "integrity": "sha512-n2hY8YdoRE1i7r6M0w9DIw5GgZN0G25P8zLCRQ8rjXtTU3vsNFBI/vWK/UIeE6g5MUUz6avwAPXmL6Fy9D/90w==", "dev": true, + "optional": true, + "peer": true, "requires": { - "@wdio/utils": "7.16.14" + "@tootallnate/once": "2", + "agent-base": "6", + "debug": "4" } }, - "@wdio/utils": { - "version": "7.16.14", - "resolved": "https://registry.npmjs.org/@wdio/utils/-/utils-7.16.14.tgz", - "integrity": "sha512-wwin8nVpIlhmXJkq6GJw9aDDzgLOJKgXTcEua0T2sdXjoW78u5Ly/GZrFXTjMGhacFvoZfitTrjyfyy4CxMVvw==", + "http2-wrapper": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/http2-wrapper/-/http2-wrapper-2.2.1.tgz", + "integrity": "sha512-V5nVw1PAOgfI3Lmeaj2Exmeg7fenjhRUgz1lPSezy1CuhPYbgQtbQj4jZfEAEMlaL+vupsvhjqCyjzob0yxsmQ==", "dev": true, "requires": { - "@wdio/logger": "7.16.0", - "@wdio/types": "7.16.14", - "p-iteration": "^1.1.8" + "quick-lru": "^5.1.1", + "resolve-alpn": "^1.2.0" } }, - "brace-expansion": { + "isexe": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/isexe/-/isexe-3.1.1.tgz", + "integrity": "sha512-LpB/54B+/2J5hqQ7imZHfdU31OlgQqx7ZicVlkm9kzg9/w8GKLEcFfJl/t7DCEDueOyBAD6zCCwTO6Fzs0NoEQ==", + "dev": true, + "optional": true, + "peer": true + }, + "lighthouse-logger": { "version": "2.0.1", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", - "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", + "resolved": "https://registry.npmjs.org/lighthouse-logger/-/lighthouse-logger-2.0.1.tgz", + "integrity": "sha512-ioBrW3s2i97noEmnXxmUq7cjIcVRjT5HBpAYy8zE11CxU9HqlWHHeRxfeN1tn8F7OEMVPIC9x1f8t3Z7US9ehQ==", "dev": true, + "optional": true, + "peer": true, "requires": { - "balanced-match": "^1.0.0" + "debug": "^2.6.9", + "marky": "^1.2.2" + }, + "dependencies": { + "debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "dev": true, + "optional": true, + "peer": true, + "requires": { + "ms": "2.0.0" + } + } } }, - "devtools": { - "version": "7.16.16", - "resolved": "https://registry.npmjs.org/devtools/-/devtools-7.16.16.tgz", - "integrity": "sha512-M0kzkuSgfEhpqIis3gdtWsNjn/HQ+vRAmEzDnbYx/7FfjFxhSv1d+rOOT20pvd60soItMYpsOova1igACEGkGQ==", - "dev": true, - "requires": { - "@types/node": "^17.0.4", - "@types/ua-parser-js": "^0.7.33", - "@wdio/config": "7.16.16", - "@wdio/logger": "7.16.0", - "@wdio/protocols": "7.16.7", - "@wdio/types": "7.16.14", - "@wdio/utils": "7.16.14", - "chrome-launcher": "^0.15.0", - "edge-paths": "^2.1.0", - "puppeteer-core": "^13.1.3", - "query-selector-shadow-dom": "^1.0.0", - "ua-parser-js": "^1.0.1", - "uuid": "^8.0.0" - } + "lowercase-keys": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/lowercase-keys/-/lowercase-keys-3.0.0.tgz", + "integrity": "sha512-ozCC6gdQ+glXOQsveKD0YsDy8DSQFjDTz4zyzEHNV5+JP5D62LmfDZ6o1cycFx9ouG940M5dE8C8CTewdj2YWQ==", + "dev": true }, - "devtools-protocol": { - "version": "0.0.973690", - "resolved": "https://registry.npmjs.org/devtools-protocol/-/devtools-protocol-0.0.973690.tgz", - "integrity": "sha512-myh3hSFp0YWa2GED11PmbLhV4dv9RdO7YUz27XJrbQLnP5bMbZL6dfOOILTHO57yH0kX5GfuOZBsg/4NamfPvQ==", + "lru-cache": { + "version": "7.18.3", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-7.18.3.tgz", + "integrity": "sha512-jumlc0BIUrS3qJGgIkWZsyfAM7NCWiBcCDhnd+3NNM5KbBmLTgHVfWBcg6W+rLUsIpzpERPsvwUP7CckAQSOoA==", + "dev": true + }, + "mimic-response": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/mimic-response/-/mimic-response-4.0.0.tgz", + "integrity": "sha512-e5ISH9xMYU0DzrT+jl8q2ze9D6eWBto+I8CNpe+VI+K2J/F/k3PdkdTdz4wvGVH4NTpo+NRYTVIuMQEMMcsLqg==", "dev": true }, "minimatch": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-5.1.0.tgz", - "integrity": "sha512-9TPBGGak4nHfGZsPBohm9AWg6NoT7QTCehS3BIJABslyZbzxfV78QM2Y6+i741OPZIafFAaiiEMh5OyIrJPgtg==", + "version": "9.0.3", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.3.tgz", + "integrity": "sha512-RHiac9mvaRw0x3AYRgDC1CxAP7HTcNrrECeA8YYJeWnpo+2Q5CegtZjaotWTWxDG3UeGA1coE05iH1mPjT/2mg==", "dev": true, "requires": { "brace-expansion": "^2.0.1" } }, - "ua-parser-js": { - "version": "1.0.33", - "resolved": "https://registry.npmjs.org/ua-parser-js/-/ua-parser-js-1.0.33.tgz", - "integrity": "sha512-RqshF7TPTE0XLYAqmjlu5cLLuGdKrNu9O1KLA/qp39QtbZwuzwv1dT46DZSopoUMsYgXpB3Cv8a03FI8b74oFQ==", + "ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", + "dev": true, + "optional": true, + "peer": true + }, + "node-fetch": { + "version": "2.7.0", + "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.7.0.tgz", + "integrity": "sha512-c4FRfUm/dbcWZ7U+1Wq0AwCyFL+3nt2bEw05wfxSz+DWpWsitgmSgYmy2dQdWyKC1694ELPqMs/YzUSNozLt8A==", + "dev": true, + "requires": { + "whatwg-url": "^5.0.0" + } + }, + "normalize-url": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/normalize-url/-/normalize-url-8.0.0.tgz", + "integrity": "sha512-uVFpKhj5MheNBJRTiMZ9pE/7hD1QTeEvugSJW/OmLzAp78PB5O6adfMNTvmfKhXBkvCzC+rqifWcVYpGFwTjnw==", "dev": true }, - "uuid": { - "version": "8.3.2", - "resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz", - "integrity": "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==", + "p-cancelable": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/p-cancelable/-/p-cancelable-3.0.0.tgz", + "integrity": "sha512-mlVgR3PGuzlo0MmTdk4cXqXWlwQDLnONTAg6sm62XkMJEiRxN3GL3SffkYvqwonbkJBcrI7Uvv5Zh9yjvn2iUw==", "dev": true }, - "webdriver": { - "version": "7.16.16", - "resolved": "https://registry.npmjs.org/webdriver/-/webdriver-7.16.16.tgz", - "integrity": "sha512-x8UoG9k/P8KDrfSh1pOyNevt9tns3zexoMxp9cKnyA/7HYSErhZYTLGlgxscAXLtQG41cMH/Ba/oBmOx7Hgd8w==", + "proxy-agent": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/proxy-agent/-/proxy-agent-6.3.0.tgz", + "integrity": "sha512-0LdR757eTj/JfuU7TL2YCuAZnxWXu3tkJbg4Oq3geW/qFNT/32T0sp2HnZ9O0lMR4q3vwAt0+xCA8SR0WAD0og==", "dev": true, "requires": { - "@types/node": "^17.0.4", - "@wdio/config": "7.16.16", - "@wdio/logger": "7.16.0", - "@wdio/protocols": "7.16.7", - "@wdio/types": "7.16.14", - "@wdio/utils": "7.16.14", - "got": "^11.0.2", - "ky": "^0.29.0", - "lodash.merge": "^4.6.1" + "agent-base": "^7.0.2", + "debug": "^4.3.4", + "http-proxy-agent": "^7.0.0", + "https-proxy-agent": "^7.0.0", + "lru-cache": "^7.14.1", + "pac-proxy-agent": "^7.0.0", + "proxy-from-env": "^1.1.0", + "socks-proxy-agent": "^8.0.1" + }, + "dependencies": { + "agent-base": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-7.1.0.tgz", + "integrity": "sha512-o/zjMZRhJxny7OyEF+Op8X+efiELC7k7yOjMzgfzVqOzXqkBkWI79YoTdOtsuWd5BWhAGAuOY/Xa6xpiaWXiNg==", + "dev": true, + "requires": { + "debug": "^4.3.4" + } + }, + "http-proxy-agent": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/http-proxy-agent/-/http-proxy-agent-7.0.0.tgz", + "integrity": "sha512-+ZT+iBxVUQ1asugqnD6oWoRiS25AkjNfG085dKJGtGxkdwLQrMKU5wJr2bOOFAXzKcTuqq+7fZlTMgG3SRfIYQ==", + "dev": true, + "requires": { + "agent-base": "^7.1.0", + "debug": "^4.3.4" + } + }, + "https-proxy-agent": { + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-7.0.2.tgz", + "integrity": "sha512-NmLNjm6ucYwtcUmL7JQC1ZQ57LmHP4lT15FQ8D61nak1rO6DH+fz5qNK2Ap5UN4ZapYICE3/0KodcLYSPsPbaA==", + "dev": true, + "requires": { + "agent-base": "^7.0.2", + "debug": "4" + } + } + } + }, + "puppeteer-core": { + "version": "20.3.0", + "resolved": "https://registry.npmjs.org/puppeteer-core/-/puppeteer-core-20.3.0.tgz", + "integrity": "sha512-264pBrIui5bO6NJeOcbJrLa0OCwmA4+WK00JMrLIKTfRiqe2gx8KWTzLsjyw/bizErp3TKS7vt/I0i5fTC+mAw==", + "dev": true, + "optional": true, + "peer": true, + "requires": { + "@puppeteer/browsers": "1.3.0", + "chromium-bidi": "0.4.9", + "cross-fetch": "3.1.6", + "debug": "4.3.4", + "devtools-protocol": "0.0.1120988", + "ws": "8.13.0" + } + }, + "readable-stream": { + "version": "3.6.2", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz", + "integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==", + "dev": true, + "requires": { + "inherits": "^2.0.3", + "string_decoder": "^1.1.1", + "util-deprecate": "^1.0.1" + } + }, + "responselike": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/responselike/-/responselike-3.0.0.tgz", + "integrity": "sha512-40yHxbNcl2+rzXvZuVkrYohathsSJlMTXKryG5y8uciHv1+xDLHQpgjG64JUO9nrEq2jGLH6IZ8BcZyw3wrweg==", + "dev": true, + "requires": { + "lowercase-keys": "^3.0.0" + } + }, + "serialize-error": { + "version": "11.0.3", + "resolved": "https://registry.npmjs.org/serialize-error/-/serialize-error-11.0.3.tgz", + "integrity": "sha512-2G2y++21dhj2R7iHAdd0FIzjGwuKZld+7Pl/bTU6YIkrC2ZMbVUjm+luj6A6V34Rv9XfKJDKpTWu9W4Gse1D9g==", + "dev": true, + "requires": { + "type-fest": "^2.12.2" + } + }, + "tar-stream": { + "version": "3.1.7", + "resolved": "https://registry.npmjs.org/tar-stream/-/tar-stream-3.1.7.tgz", + "integrity": "sha512-qJj60CXt7IU1Ffyc3NJMjh6EkuCFej46zUqJ4J7pqYlThyd9bO0XBTmcOIhSzZJVWfsLks0+nle/j538YAW9RQ==", + "dev": true, + "requires": { + "b4a": "^1.6.4", + "fast-fifo": "^1.2.0", + "streamx": "^2.15.0" } }, + "type-fest": { + "version": "2.19.0", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-2.19.0.tgz", + "integrity": "sha512-RAH822pAdBgcNMAfWnCBU3CFZcfZ/i1eZjwFU/dsLKumyuuP3niueg2UAukXYF0E2AAoc82ZSSf9J0WQBinzHA==", + "dev": true + }, + "ua-parser-js": { + "version": "1.0.37", + "resolved": "https://registry.npmjs.org/ua-parser-js/-/ua-parser-js-1.0.37.tgz", + "integrity": "sha512-bhTyI94tZofjo+Dn8SN6Zv8nBDvyXTymAdM3LDI/0IboIUwTu1rEhW7v2TfiVsoYWgkQ4kOVqnI8APUFbIQIFQ==", + "dev": true, + "optional": true, + "peer": true + }, + "uuid": { + "version": "9.0.1", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-9.0.1.tgz", + "integrity": "sha512-b+1eJOlsR9K8HJpow9Ok3fiWOWSIcIzXodvv0rQjVoOVNpWMpxf1wZNpt4y9h10odCNrqnYp1OBzRktckBe3sA==", + "dev": true + }, "webdriverio": { - "version": "7.16.16", - "resolved": "https://registry.npmjs.org/webdriverio/-/webdriverio-7.16.16.tgz", - "integrity": "sha512-caPaEWyuD3Qoa7YkW4xCCQA4v9Pa9wmhFGPvNZh3ERtjMCNi8L/XXOdkekWNZmFh3tY0kFguBj7+fAwSY7HAGw==", - "dev": true, - "requires": { - "@types/aria-query": "^5.0.0", - "@types/node": "^17.0.4", - "@wdio/config": "7.16.16", - "@wdio/logger": "7.16.0", - "@wdio/protocols": "7.16.7", - "@wdio/repl": "7.16.14", - "@wdio/types": "7.16.14", - "@wdio/utils": "7.16.14", - "archiver": "^5.0.0", + "version": "8.29.1", + "resolved": "https://registry.npmjs.org/webdriverio/-/webdriverio-8.29.1.tgz", + "integrity": "sha512-NZK95ivXCqdPraB3FHMw6ByxnCvtgFXkjzG2l3Oq5z0IuJS2aMow3AKFIyiuG6is/deGCe+Tb8eFTCqak7UV+w==", + "dev": true, + "requires": { + "@types/node": "^20.1.0", + "@wdio/config": "8.29.1", + "@wdio/logger": "8.28.0", + "@wdio/protocols": "8.24.12", + "@wdio/repl": "8.24.12", + "@wdio/types": "8.29.1", + "@wdio/utils": "8.29.1", + "archiver": "^6.0.0", "aria-query": "^5.0.0", "css-shorthand-properties": "^1.1.1", "css-value": "^0.0.1", - "devtools": "7.16.16", - "devtools-protocol": "^0.0.973690", - "fs-extra": "^10.0.0", - "get-port": "^5.1.1", + "devtools-protocol": "^0.0.1249869", "grapheme-splitter": "^1.0.2", + "import-meta-resolve": "^4.0.0", + "is-plain-obj": "^4.1.0", "lodash.clonedeep": "^4.5.0", - "lodash.isobject": "^3.0.2", - "lodash.isplainobject": "^4.0.6", "lodash.zip": "^4.2.0", - "minimatch": "^5.0.0", - "puppeteer-core": "^13.1.3", + "minimatch": "^9.0.0", + "puppeteer-core": "^20.9.0", "query-selector-shadow-dom": "^1.0.0", "resq": "^1.9.1", "rgb2hex": "0.2.5", - "serialize-error": "^8.0.0", - "webdriver": "7.16.16" + "serialize-error": "^11.0.1", + "webdriver": "8.29.1" + }, + "dependencies": { + "@puppeteer/browsers": { + "version": "1.4.6", + "resolved": "https://registry.npmjs.org/@puppeteer/browsers/-/browsers-1.4.6.tgz", + "integrity": "sha512-x4BEjr2SjOPowNeiguzjozQbsc6h437ovD/wu+JpaenxVLm3jkgzHY2xOslMTp50HoTvQreMjiexiGQw1sqZlQ==", + "dev": true, + "requires": { + "debug": "4.3.4", + "extract-zip": "2.0.1", + "progress": "2.0.3", + "proxy-agent": "6.3.0", + "tar-fs": "3.0.4", + "unbzip2-stream": "1.4.3", + "yargs": "17.7.1" + } + }, + "chromium-bidi": { + "version": "0.4.16", + "resolved": "https://registry.npmjs.org/chromium-bidi/-/chromium-bidi-0.4.16.tgz", + "integrity": "sha512-7ZbXdWERxRxSwo3txsBjjmc/NLxqb1Bk30mRb0BMS4YIaiV6zvKZqL/UAH+DdqcDYayDWk2n/y8klkBDODrPvA==", + "dev": true, + "requires": { + "mitt": "3.0.0" + } + }, + "cross-fetch": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/cross-fetch/-/cross-fetch-4.0.0.tgz", + "integrity": "sha512-e4a5N8lVvuLgAWgnCrLr2PP0YyDOTHa9H/Rj54dirp61qXnNq46m82bRhNqIA5VccJtWBvPTFRV3TtvHUKPB1g==", + "dev": true, + "requires": { + "node-fetch": "^2.6.12" + } + }, + "devtools-protocol": { + "version": "0.0.1249869", + "resolved": "https://registry.npmjs.org/devtools-protocol/-/devtools-protocol-0.0.1249869.tgz", + "integrity": "sha512-Ctp4hInA0BEavlUoRy9mhGq0i+JSo/AwVyX2EFgZmV1kYB+Zq+EMBAn52QWu6FbRr10hRb6pBl420upbp4++vg==", + "dev": true + }, + "puppeteer-core": { + "version": "20.9.0", + "resolved": "https://registry.npmjs.org/puppeteer-core/-/puppeteer-core-20.9.0.tgz", + "integrity": "sha512-H9fYZQzMTRrkboEfPmf7m3CLDN6JvbxXA3qTtS+dFt27tR+CsFHzPsT6pzp6lYL6bJbAPaR0HaPO6uSi+F94Pg==", + "dev": true, + "requires": { + "@puppeteer/browsers": "1.4.6", + "chromium-bidi": "0.4.16", + "cross-fetch": "4.0.0", + "debug": "4.3.4", + "devtools-protocol": "0.0.1147663", + "ws": "8.13.0" + }, + "dependencies": { + "devtools-protocol": { + "version": "0.0.1147663", + "resolved": "https://registry.npmjs.org/devtools-protocol/-/devtools-protocol-0.0.1147663.tgz", + "integrity": "sha512-hyWmRrexdhbZ1tcJUGpO95ivbRhWXz++F4Ko+n21AY5PNln2ovoJw+8ZMNDTtip+CNFQfrtLVh/w4009dXO/eQ==", + "dev": true + } + } + }, + "tar-fs": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/tar-fs/-/tar-fs-3.0.4.tgz", + "integrity": "sha512-5AFQU8b9qLfZCX9zp2duONhPmZv0hGYiBPJsyUdqMjzq/mqVpy/rEUSeHk1+YitmxugaptgBh5oDGU3VsAJq4w==", + "dev": true, + "requires": { + "mkdirp-classic": "^0.5.2", + "pump": "^3.0.0", + "tar-stream": "^3.1.5" + } + } + } + }, + "ws": { + "version": "8.13.0", + "resolved": "https://registry.npmjs.org/ws/-/ws-8.13.0.tgz", + "integrity": "sha512-x9vcZYTrFPC7aSIbj7sRCYo7L/Xb8Iy+pW0ng0wt2vCJv7M9HOMy0UoN3rr+IFC7hb7vXoqS+P9ktyLLLhO+LA==", + "dev": true, + "requires": {} + }, + "yargs": { + "version": "17.7.1", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.7.1.tgz", + "integrity": "sha512-cwiTb08Xuv5fqF4AovYacTFNxk62th7LKJ6BL9IGUpTJrWoU7/7WdQGTP2SjKf1dUNBGzDd28p/Yfs/GI6JrLw==", + "dev": true, + "requires": { + "cliui": "^8.0.1", + "escalade": "^3.1.1", + "get-caller-file": "^2.0.5", + "require-directory": "^2.1.1", + "string-width": "^4.2.3", + "y18n": "^5.0.5", + "yargs-parser": "^21.1.1" + } + }, + "zip-stream": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/zip-stream/-/zip-stream-5.0.1.tgz", + "integrity": "sha512-UfZ0oa0C8LI58wJ+moL46BDIMgCQbnsb+2PoiJYtonhBsMh2bq1eRBVkvjfVsqbEHd9/EgKPUuL9saSSsec8OA==", + "dev": true, + "requires": { + "archiver-utils": "^4.0.1", + "compress-commons": "^5.0.1", + "readable-stream": "^3.6.0" } } } }, "@wdio/cli": { - "version": "7.5.7", - "resolved": "https://registry.npmjs.org/@wdio/cli/-/cli-7.5.7.tgz", - "integrity": "sha512-nOQJLskrY+UECDd3NxE7oBzb6cDA7e7x02YWQugOlOgnZ4a+PJmkFoSsO8C2uNCpdFngy5rJKGUo5vbtAHEF9Q==", - "dev": true, - "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.5.3", - "@wdio/logger": "7.5.3", - "@wdio/types": "7.5.3", - "@wdio/utils": "7.5.3", + "version": "8.29.1", + "resolved": "https://registry.npmjs.org/@wdio/cli/-/cli-8.29.1.tgz", + "integrity": "sha512-WWRTf0g0O+ovTTvS1kEhZ/svX32M7jERuuMF1MaldKCi7rZwHsQqOyJD+fO1UDjuxqS96LHSGsZn0auwUfCTXA==", + "dev": true, + "requires": { + "@types/node": "^20.1.1", + "@vitest/snapshot": "^1.2.1", + "@wdio/config": "8.29.1", + "@wdio/globals": "8.29.1", + "@wdio/logger": "8.28.0", + "@wdio/protocols": "8.24.12", + "@wdio/types": "8.29.1", + "@wdio/utils": "8.29.1", "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", + "chalk": "^5.2.0", + "chokidar": "^3.5.3", + "cli-spinners": "^2.9.0", + "dotenv": "^16.3.1", + "ejs": "^3.1.9", + "execa": "^8.0.1", + "import-meta-resolve": "^4.0.0", + "inquirer": "9.2.12", "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.5.7", - "yargs": "^17.0.0", - "yarn-install": "^1.0.0" + "read-pkg-up": "^10.0.0", + "recursive-readdir": "^2.2.3", + "webdriverio": "8.29.1", + "yargs": "^17.7.2" }, "dependencies": { - "@types/aria-query": { - "version": "4.2.2", - "resolved": "https://registry.npmjs.org/@types/aria-query/-/aria-query-4.2.2.tgz", - "integrity": "sha512-HnYpAE1Y6kRyKM/XkEuiRQhTHvkzMBurTHnpFLYLBGPIylZNPs9jJcuOOYWxPLJCSEtmZT0Y8rHDokKN7rRTig==", - "dev": true + "@puppeteer/browsers": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/@puppeteer/browsers/-/browsers-1.3.0.tgz", + "integrity": "sha512-an3QdbNPkuU6qpxpbssxAbjRLJcF+eP4L8UqIY3+6n0sbaVxw5pz7PiCLy9g32XEZuoamUlV5ZQPnA6FxvkIHA==", + "dev": true, + "optional": true, + "peer": true, + "requires": { + "debug": "4.3.4", + "extract-zip": "2.0.1", + "http-proxy-agent": "5.0.0", + "https-proxy-agent": "5.0.1", + "progress": "2.0.3", + "proxy-from-env": "1.1.0", + "tar-fs": "2.1.1", + "unbzip2-stream": "1.4.3", + "yargs": "17.7.1" + }, + "dependencies": { + "yargs": { + "version": "17.7.1", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.7.1.tgz", + "integrity": "sha512-cwiTb08Xuv5fqF4AovYacTFNxk62th7LKJ6BL9IGUpTJrWoU7/7WdQGTP2SjKf1dUNBGzDd28p/Yfs/GI6JrLw==", + "dev": true, + "optional": true, + "peer": true, + "requires": { + "cliui": "^8.0.1", + "escalade": "^3.1.1", + "get-caller-file": "^2.0.5", + "require-directory": "^2.1.1", + "string-width": "^4.2.3", + "y18n": "^5.0.5", + "yargs-parser": "^21.1.1" + } + } + } }, - "@wdio/logger": { - "version": "7.5.3", - "resolved": "https://registry.npmjs.org/@wdio/logger/-/logger-7.5.3.tgz", - "integrity": "sha512-r9EADpm0ncS1bDQSWi/nhF9C59/WNLbdAAFlo782E9ItFCpDhNit3aQP9vETv1vFxJRjUIM8Fw/HW8zwPadkbw==", + "@types/which": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/@types/which/-/which-2.0.2.tgz", + "integrity": "sha512-113D3mDkZDjo+EeUEHCFy0qniNc1ZpecGiAU7WSo7YDoSzolZIQKpYFHrPpjkB2nuyahcKfrmLXeQlh7gqJYdw==", + "dev": true, + "optional": true, + "peer": true + }, + "archiver": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/archiver/-/archiver-6.0.1.tgz", + "integrity": "sha512-CXGy4poOLBKptiZH//VlWdFuUC1RESbdZjGjILwBuZ73P7WkAUN0htfSfBq/7k6FRFlpu7bg4JOkj1vU9G6jcQ==", "dev": true, "requires": { - "chalk": "^4.0.0", - "loglevel": "^1.6.0", - "loglevel-plugin-prefix": "^0.8.4", - "strip-ansi": "^6.0.0" + "archiver-utils": "^4.0.1", + "async": "^3.2.4", + "buffer-crc32": "^0.2.1", + "readable-stream": "^3.6.0", + "readdir-glob": "^1.1.2", + "tar-stream": "^3.0.0", + "zip-stream": "^5.0.1" } }, - "@wdio/types": { - "version": "7.5.3", - "resolved": "https://registry.npmjs.org/@wdio/types/-/types-7.5.3.tgz", - "integrity": "sha512-jmumhKBhNDABnpmrshYLEcdE9WoP5tmynsDNbDABlb/W8FFiLySQOejukhYIL9CLys4zXerV3/edks0SCzHOiQ==", + "archiver-utils": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/archiver-utils/-/archiver-utils-4.0.1.tgz", + "integrity": "sha512-Q4Q99idbvzmgCTEAAhi32BkOyq8iVI5EwdO0PmBDSGIzzjYNdcFn7Q7k3OzbLy4kLUPXfJtG6fO2RjftXbobBg==", "dev": true, "requires": { - "got": "^11.8.1" + "glob": "^8.0.0", + "graceful-fs": "^4.2.0", + "lazystream": "^1.0.0", + "lodash": "^4.17.15", + "normalize-path": "^3.0.0", + "readable-stream": "^3.6.0" } }, - "ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "async": { + "version": "3.2.5", + "resolved": "https://registry.npmjs.org/async/-/async-3.2.5.tgz", + "integrity": "sha512-baNZyqaaLhyLVKm/DlvdW051MSgO6b8eVfIezl9E5PqWxFgzLm/wQntEW4zOytVburDEr0JlALEpdOFwvErLsg==", + "dev": true + }, + "brace-expansion": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", + "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", "dev": true, "requires": { - "color-convert": "^2.0.1" + "balanced-match": "^1.0.0" } }, - "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==", + "chalk": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-5.3.0.tgz", + "integrity": "sha512-dLitG79d+GV1Nb/VYcCDFivJeK1hiukt9QjRNVOsUtTy1rR1YJsmpGGTZ3qJos+uw7WmWF4wUwBd9jxjocFC2w==", + "dev": true + }, + "chrome-launcher": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/chrome-launcher/-/chrome-launcher-1.1.0.tgz", + "integrity": "sha512-rJYWeEAERwWIr3c3mEVXwNiODPEdMRlRxHc47B1qHPOolHZnkj7rMv1QSUfPoG6MgatWj5AxSpnKKR4QEwEQIQ==", "dev": true, + "optional": true, + "peer": true, "requires": { - "@babel/runtime": "^7.10.2", - "@babel/runtime-corejs3": "^7.10.2" + "@types/node": "*", + "escape-string-regexp": "^4.0.0", + "is-wsl": "^2.2.0", + "lighthouse-logger": "^2.0.1" } }, - "chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "compress-commons": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/compress-commons/-/compress-commons-5.0.1.tgz", + "integrity": "sha512-MPh//1cERdLtqwO3pOFLeXtpuai0Y2WCd5AhtKxznqM7WtaMYaOEMSgn45d9D10sIHSfIKE603HlOp8OPGrvag==", "dev": true, "requires": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" + "crc-32": "^1.2.0", + "crc32-stream": "^5.0.0", + "normalize-path": "^3.0.0", + "readable-stream": "^3.6.0" } }, - "chrome-launcher": { - "version": "0.13.4", - "resolved": "https://registry.npmjs.org/chrome-launcher/-/chrome-launcher-0.13.4.tgz", - "integrity": "sha512-nnzXiDbGKjDSK6t2I+35OAPBy5Pw/39bgkb/ZAFwMhwJbdYBp6aH+vW28ZgtjdU890Q7D+3wN/tB8N66q5Gi2A==", + "crc32-stream": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/crc32-stream/-/crc32-stream-5.0.0.tgz", + "integrity": "sha512-B0EPa1UK+qnpBZpG+7FgPCu0J2ETLpXq09o9BkLkEAhdB6Z61Qo4pJ3JYu0c+Qi+/SAL7QThqnzS06pmSSyZaw==", "dev": true, "requires": { - "@types/node": "*", - "escape-string-regexp": "^1.0.5", - "is-wsl": "^2.2.0", - "lighthouse-logger": "^1.0.0", - "mkdirp": "^0.5.3", - "rimraf": "^3.0.2" + "crc-32": "^1.2.0", + "readable-stream": "^3.4.0" + } + }, + "cross-fetch": { + "version": "3.1.6", + "resolved": "https://registry.npmjs.org/cross-fetch/-/cross-fetch-3.1.6.tgz", + "integrity": "sha512-riRvo06crlE8HiqOwIpQhxwdOk4fOeR7FVM/wXoxchFEqMNUjvbs3bfo4OTgMEMHzppd4DxFBDbyySj8Cv781g==", + "dev": true, + "optional": true, + "peer": true, + "requires": { + "node-fetch": "^2.6.11" + } + }, + "devtools": { + "version": "8.29.1", + "resolved": "https://registry.npmjs.org/devtools/-/devtools-8.29.1.tgz", + "integrity": "sha512-fbH0Z7CPK4OZSgUw2QcAppczowxtSyvFztPUmiFyi99cUadjEOwlg0aL3pBVlIDo67olYjGb8GD1M5Z4yI/P6w==", + "dev": true, + "optional": true, + "peer": true, + "requires": { + "@types/node": "^20.1.0", + "@wdio/config": "8.29.1", + "@wdio/logger": "8.28.0", + "@wdio/protocols": "8.24.12", + "@wdio/types": "8.29.1", + "@wdio/utils": "8.29.1", + "chrome-launcher": "^1.0.0", + "edge-paths": "^3.0.5", + "import-meta-resolve": "^4.0.0", + "puppeteer-core": "20.3.0", + "query-selector-shadow-dom": "^1.0.0", + "ua-parser-js": "^1.0.1", + "uuid": "^9.0.0", + "which": "^4.0.0" }, "dependencies": { - "mkdirp": { - "version": "0.5.6", - "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.6.tgz", - "integrity": "sha512-FP+p8RB8OWpF3YZBCrP5gtADmtXApB5AMLn+vdyA+PyxCjrCs00mjyUozssO33cwDeT3wNGdLxJ5M//YqtHAJw==", + "which": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/which/-/which-4.0.0.tgz", + "integrity": "sha512-GlaYyEb07DPxYCKhKzplCWBJtvxZcZMrL+4UkrTSJHHPyZU4mYYTv3qaOe77H7EODLSSopAUFAc6W8U4yqvscg==", "dev": true, + "optional": true, + "peer": true, "requires": { - "minimist": "^1.2.6" + "isexe": "^3.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==", + "devtools-protocol": { + "version": "0.0.1120988", + "resolved": "https://registry.npmjs.org/devtools-protocol/-/devtools-protocol-0.0.1120988.tgz", + "integrity": "sha512-39fCpE3Z78IaIPChJsP6Lhmkbf4dWXOmzLk/KFTdRkNk/0JymRIfUynDVRndV9HoDz8PyalK1UH21ST/ivwW5Q==", + "dev": true, + "optional": true, + "peer": true + }, + "edge-paths": { + "version": "3.0.5", + "resolved": "https://registry.npmjs.org/edge-paths/-/edge-paths-3.0.5.tgz", + "integrity": "sha512-sB7vSrDnFa4ezWQk9nZ/n0FdpdUuC6R1EOrlU3DL+bovcNFK28rqu2emmAUjujYEJTWIgQGqgVVWUZXMnc8iWg==", "dev": true, + "optional": true, + "peer": true, "requires": { - "color-name": "~1.1.4" + "@types/which": "^2.0.1", + "which": "^2.0.2" } }, - "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==", + "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, + "optional": true, + "peer": true + }, + "execa": { + "version": "8.0.1", + "resolved": "https://registry.npmjs.org/execa/-/execa-8.0.1.tgz", + "integrity": "sha512-VyhnebXciFV2DESc+p6B+y0LjSm0krU4OgJN44qFAhBY0TJ+1V61tYD2+wHusZ6F9n5K+vl8k0sTy7PEfV4qpg==", + "dev": true, + "requires": { + "cross-spawn": "^7.0.3", + "get-stream": "^8.0.1", + "human-signals": "^5.0.0", + "is-stream": "^3.0.0", + "merge-stream": "^2.0.0", + "npm-run-path": "^5.1.0", + "onetime": "^6.0.0", + "signal-exit": "^4.1.0", + "strip-final-newline": "^3.0.0" + } + }, + "find-up": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-6.3.0.tgz", + "integrity": "sha512-v2ZsoEuVHYy8ZIlYqwPe/39Cy+cFDzp4dXPaxNvkEuouymu+2Jbz0PxpKarJHYJTmv2HWT3O382qY8l4jMWthw==", + "dev": true, + "requires": { + "locate-path": "^7.1.0", + "path-exists": "^5.0.0" + } + }, + "get-stream": { + "version": "8.0.1", + "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-8.0.1.tgz", + "integrity": "sha512-VaUJspBffn/LMCJVoMvSAdmscJyS1auj5Zulnn5UoYcY531UWmdwhRWkcGKnGU93m5HSXP9LP2usOryrBtQowA==", "dev": true }, - "devtools": { - "version": "7.5.7", - "resolved": "https://registry.npmjs.org/devtools/-/devtools-7.5.7.tgz", - "integrity": "sha512-+kqmvFbceElhYpN35yjm1T4Rz3VbH0QaqrNWKRpeyFp657Y5W0bm1s5FyMUeIv0aTNkAgWcETtqL+EG9X9uvjQ==", - "dev": true, - "requires": { - "@wdio/config": "7.5.3", - "@wdio/logger": "7.5.3", - "@wdio/protocols": "7.5.3", - "@wdio/types": "7.5.3", - "@wdio/utils": "7.5.3", - "chrome-launcher": "^0.13.1", - "edge-paths": "^2.1.0", - "puppeteer-core": "^9.1.0", - "query-selector-shadow-dom": "^1.0.0", - "ua-parser-js": "^0.7.21", - "uuid": "^8.0.0" + "glob": { + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/glob/-/glob-8.1.0.tgz", + "integrity": "sha512-r8hpEjiQEYlF2QU0df3dS+nxxSIreXQS1qRhMJM0Q5NDdR386C7jb7Hwwod8Fgiuex+k0GFjgft18yvxm5XoCQ==", + "dev": true, + "requires": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^5.0.1", + "once": "^1.3.0" + }, + "dependencies": { + "minimatch": { + "version": "5.1.6", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-5.1.6.tgz", + "integrity": "sha512-lKwV/1brpG6mBUFHtb7NUmtABCb2WZZmm2wNiOA5hAb8VdCS4B3dtMWyvcoViccwAW/COERjXLt0zP1zXUN26g==", + "dev": true, + "requires": { + "brace-expansion": "^2.0.1" + } + } } }, - "devtools-protocol": { - "version": "0.0.878340", - "resolved": "https://registry.npmjs.org/devtools-protocol/-/devtools-protocol-0.0.878340.tgz", - "integrity": "sha512-W0q8Y02r1RNwfZtI4Jjh1/MZxRHyrIgy9FvElbJzQelZjmNH197H4mBQs7DZjlUUDA9s6Zz2jl+zUYFgLgEnzw==", + "hosted-git-info": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-7.0.1.tgz", + "integrity": "sha512-+K84LB1DYwMHoHSgaOY/Jfhw3ucPmSET5v98Ke/HdNSw4a0UktWzyW1mjhjpuxxTqOOsfWT/7iVshHmVZ4IpOA==", + "dev": true, + "requires": { + "lru-cache": "^10.0.1" + }, + "dependencies": { + "lru-cache": { + "version": "10.1.0", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.1.0.tgz", + "integrity": "sha512-/1clY/ui8CzjKFyjdvwPWJUYKiFVXG2I2cY0ssG7h4+hwk+XOIX7ZSG9Q7TW8TW3Kp3BUSqgFWBLgL4PJ+Blag==", + "dev": true + } + } + }, + "http-proxy-agent": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/http-proxy-agent/-/http-proxy-agent-5.0.0.tgz", + "integrity": "sha512-n2hY8YdoRE1i7r6M0w9DIw5GgZN0G25P8zLCRQ8rjXtTU3vsNFBI/vWK/UIeE6g5MUUz6avwAPXmL6Fy9D/90w==", + "dev": true, + "optional": true, + "peer": true, + "requires": { + "@tootallnate/once": "2", + "agent-base": "6", + "debug": "4" + } + }, + "is-stream": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-3.0.0.tgz", + "integrity": "sha512-LnQR4bZ9IADDRSkvpqMGvt/tEJWclzklNgSw48V5EAaAeDd6qGvN8ei6k5p0tvxSR171VmGyHuTiAOfxAbr8kA==", "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==", + "isexe": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/isexe/-/isexe-3.1.1.tgz", + "integrity": "sha512-LpB/54B+/2J5hqQ7imZHfdU31OlgQqx7ZicVlkm9kzg9/w8GKLEcFfJl/t7DCEDueOyBAD6zCCwTO6Fzs0NoEQ==", + "dev": true, + "optional": true, + "peer": true + }, + "json-parse-even-better-errors": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/json-parse-even-better-errors/-/json-parse-even-better-errors-3.0.1.tgz", + "integrity": "sha512-aatBvbL26wVUCLmbWdCpeu9iF5wOyWpagiKkInA+kfws3sWdBrTnsvN2CKcyCYyUrc7rebNBlK6+kteg7ksecg==", "dev": true }, - "puppeteer-core": { - "version": "9.1.1", - "resolved": "https://registry.npmjs.org/puppeteer-core/-/puppeteer-core-9.1.1.tgz", - "integrity": "sha512-zbedbitVIGhmgz0nt7eIdLsnaoVZSlNJfBivqm2w67T8LR2bU1dvnruDZ8nQO0zn++Iet7zHbAOdnuS5+H2E7A==", - "dev": true, - "requires": { - "debug": "^4.1.0", - "devtools-protocol": "0.0.869402", - "extract-zip": "^2.0.0", - "https-proxy-agent": "^5.0.0", - "node-fetch": "^2.6.1", - "pkg-dir": "^4.2.0", - "progress": "^2.0.1", - "proxy-from-env": "^1.1.0", - "rimraf": "^3.0.2", - "tar-fs": "^2.0.0", - "unbzip2-stream": "^1.3.3", - "ws": "^7.2.3" + "lighthouse-logger": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/lighthouse-logger/-/lighthouse-logger-2.0.1.tgz", + "integrity": "sha512-ioBrW3s2i97noEmnXxmUq7cjIcVRjT5HBpAYy8zE11CxU9HqlWHHeRxfeN1tn8F7OEMVPIC9x1f8t3Z7US9ehQ==", + "dev": true, + "optional": true, + "peer": true, + "requires": { + "debug": "^2.6.9", + "marky": "^1.2.2" }, "dependencies": { - "devtools-protocol": { - "version": "0.0.869402", - "resolved": "https://registry.npmjs.org/devtools-protocol/-/devtools-protocol-0.0.869402.tgz", - "integrity": "sha512-VvlVYY+VDJe639yHs5PHISzdWTLL3Aw8rO4cvUtwvoxFd6FHbE4OpHHcde52M6096uYYazAmd4l0o5VuFRO2WA==", - "dev": true + "debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "dev": true, + "optional": true, + "peer": true, + "requires": { + "ms": "2.0.0" + } } } }, - "supports-color": { + "lines-and-columns": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-2.0.4.tgz", + "integrity": "sha512-wM1+Z03eypVAVUCE7QdSqpVIvelbOakn1M0bPDoA4SGWPx3sNDVUiMo3L6To6WWGClB7VyXnhQ4Sn7gxiJbE6A==", + "dev": true + }, + "locate-path": { "version": "7.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-7.2.0.tgz", + "integrity": "sha512-gvVijfZvn7R+2qyPX8mAuKcFGDf6Nc61GdvGafQsHL0sBIxfKzA+usWn4GFC/bk+QdwPUD4kWFJLhElipq+0VA==", "dev": true, "requires": { - "has-flag": "^4.0.0" + "p-locate": "^6.0.0" } }, - "uuid": { - "version": "8.3.2", - "resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz", - "integrity": "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==", + "mimic-fn": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-4.0.0.tgz", + "integrity": "sha512-vqiC06CuhBTUdZH+RYl8sFrL096vA45Ok5ISO6sE/Mr1jRbGH4Csnhi8f3wKVl7x8mO4Au7Ir9D3Oyv1VYMFJw==", "dev": true }, - "webdriverio": { - "version": "7.5.7", - "resolved": "https://registry.npmjs.org/webdriverio/-/webdriverio-7.5.7.tgz", - "integrity": "sha512-TLluVPLo6Snn/dxEITvMz7ZuklN4qZOBddDuLb9LO3rhsfKDMNbnhcBk0SLdFsWny0aCuhWNpJ6co93702XC0A==", - "dev": true, - "requires": { - "@types/aria-query": "^4.2.1", - "@wdio/config": "7.5.3", - "@wdio/logger": "7.5.3", - "@wdio/protocols": "7.5.3", - "@wdio/repl": "7.5.3", - "@wdio/types": "7.5.3", - "@wdio/utils": "7.5.3", - "archiver": "^5.0.0", - "aria-query": "^4.2.2", - "atob": "^2.1.2", - "css-shorthand-properties": "^1.1.1", - "css-value": "^0.0.1", - "devtools": "7.5.7", - "devtools-protocol": "^0.0.878340", - "fs-extra": "^10.0.0", - "get-port": "^5.1.1", - "grapheme-splitter": "^1.0.2", - "lodash.clonedeep": "^4.5.0", - "lodash.isobject": "^3.0.2", - "lodash.isplainobject": "^4.0.6", - "lodash.zip": "^4.2.0", - "minimatch": "^3.0.4", - "puppeteer-core": "^9.1.0", - "query-selector-shadow-dom": "^1.0.0", - "resq": "^1.9.1", - "rgb2hex": "0.2.5", - "serialize-error": "^8.0.0", - "webdriver": "7.5.3" + "minimatch": { + "version": "9.0.3", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.3.tgz", + "integrity": "sha512-RHiac9mvaRw0x3AYRgDC1CxAP7HTcNrrECeA8YYJeWnpo+2Q5CegtZjaotWTWxDG3UeGA1coE05iH1mPjT/2mg==", + "dev": true, + "requires": { + "brace-expansion": "^2.0.1" } }, - "ws": { - "version": "7.5.9", - "resolved": "https://registry.npmjs.org/ws/-/ws-7.5.9.tgz", - "integrity": "sha512-F+P9Jil7UiSKSkppIiD94dN07AwvFixvLIj1Og1Rl9GGMuNipJnV9JzjD6XuqmAeiswGvUmNLjr5cFuXwNS77Q==", + "ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", "dev": true, - "requires": {} + "optional": true, + "peer": true }, - "yargs": { - "version": "17.6.0", - "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.6.0.tgz", - "integrity": "sha512-8H/wTDqlSwoSnScvV2N/JHfLWOKuh5MVla9hqLjK3nsfyy6Y4kDSYSvkU5YCUEPOSnRXfIyx3Sq+B/IWudTo4g==", + "node-fetch": { + "version": "2.7.0", + "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.7.0.tgz", + "integrity": "sha512-c4FRfUm/dbcWZ7U+1Wq0AwCyFL+3nt2bEw05wfxSz+DWpWsitgmSgYmy2dQdWyKC1694ELPqMs/YzUSNozLt8A==", "dev": true, "requires": { - "cliui": "^8.0.1", - "escalade": "^3.1.1", - "get-caller-file": "^2.0.5", - "require-directory": "^2.1.1", - "string-width": "^4.2.3", - "y18n": "^5.0.5", - "yargs-parser": "^21.0.0" + "whatwg-url": "^5.0.0" } - } - } - }, - "@wdio/concise-reporter": { - "version": "7.5.7", - "resolved": "https://registry.npmjs.org/@wdio/concise-reporter/-/concise-reporter-7.5.7.tgz", - "integrity": "sha512-964i7eQ4sboSla2bdR8714Er82QBgS6u39GmDFX8Izy9Ge38xaE75HuF5S7mnOWGzSojCWgqtwy5k7Rfg6GE3g==", - "dev": true, - "requires": { - "@wdio/reporter": "7.5.7", - "@wdio/types": "7.5.3", - "chalk": "^4.0.0", - "pretty-ms": "^7.0.0" - }, - "dependencies": { - "@wdio/types": { - "version": "7.5.3", - "resolved": "https://registry.npmjs.org/@wdio/types/-/types-7.5.3.tgz", - "integrity": "sha512-jmumhKBhNDABnpmrshYLEcdE9WoP5tmynsDNbDABlb/W8FFiLySQOejukhYIL9CLys4zXerV3/edks0SCzHOiQ==", + }, + "normalize-package-data": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-6.0.0.tgz", + "integrity": "sha512-UL7ELRVxYBHBgYEtZCXjxuD5vPxnmvMGq0jp/dGPKKrN7tfsBh2IY7TlJ15WWwdjRWD3RJbnsygUurTK3xkPkg==", "dev": true, "requires": { - "got": "^11.8.1" + "hosted-git-info": "^7.0.0", + "is-core-module": "^2.8.1", + "semver": "^7.3.5", + "validate-npm-package-license": "^3.0.4" } }, - "ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "npm-run-path": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-5.2.0.tgz", + "integrity": "sha512-W4/tgAXFqFA0iL7fk0+uQ3g7wkL8xJmx3XdK0VGb4cHW//eZTtKGvFBBoRKVTpY7n6ze4NL9ly7rgXcHufqXKg==", "dev": true, "requires": { - "color-convert": "^2.0.1" + "path-key": "^4.0.0" } }, - "chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "onetime": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/onetime/-/onetime-6.0.0.tgz", + "integrity": "sha512-1FlR+gjXK7X+AsAHso35MnyN5KqGwJRi/31ft6x0M194ht7S+rWAvd7PHss9xSKMzE0asv1pyIHaJYq+BbacAQ==", "dev": true, "requires": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" + "mimic-fn": "^4.0.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==", + "p-limit": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-4.0.0.tgz", + "integrity": "sha512-5b0R4txpzjPWVw/cXXUResoD4hb6U/x9BH08L7nw+GN1sezDzPdxeRvpc9c433fZhBan/wusjbCsqwqm4EIBIQ==", "dev": true, "requires": { - "color-name": "~1.1.4" + "yocto-queue": "^1.0.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==", + "p-locate": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-6.0.0.tgz", + "integrity": "sha512-wPrq66Llhl7/4AGC6I+cqxT07LhXvWL08LNXz1fENOw0Ap4sRZZ/gZpTTJ5jpurzzzfS2W/Ge9BY3LgLjCShcw==", + "dev": true, + "requires": { + "p-limit": "^4.0.0" + } + }, + "parse-json": { + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-7.1.1.tgz", + "integrity": "sha512-SgOTCX/EZXtZxBE5eJ97P4yGM5n37BwRU+YMsH4vNzFqJV/oWFXXCmwFlgWUM4PrakybVOueJJ6pwHqSVhTFDw==", + "dev": true, + "requires": { + "@babel/code-frame": "^7.21.4", + "error-ex": "^1.3.2", + "json-parse-even-better-errors": "^3.0.0", + "lines-and-columns": "^2.0.3", + "type-fest": "^3.8.0" + }, + "dependencies": { + "type-fest": { + "version": "3.13.1", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-3.13.1.tgz", + "integrity": "sha512-tLq3bSNx+xSpwvAJnzrK0Ep5CLNWjvFTOp71URMaAEWBfRb9nnJiBoUe0tF8bI4ZFO3omgBR6NvnbzVUT3Ly4g==", + "dev": true + } + } + }, + "path-exists": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-5.0.0.tgz", + "integrity": "sha512-RjhtfwJOxzcFmNOi6ltcbcu4Iu+FL3zEj83dk4kAS+fVpTxXLO1b38RvJgT/0QwvV/L3aY9TAnyv0EOqW4GoMQ==", "dev": true }, - "has-flag": { + "path-key": { "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-4.0.0.tgz", + "integrity": "sha512-haREypq7xkM7ErfgIyA0z+Bj4AGKlMSdlQE2jvJo6huWD1EdkKYV+G/T4nq0YEF2vgTT8kqMFKo1uHn950r4SQ==", "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/config": { - "version": "7.5.3", - "resolved": "https://registry.npmjs.org/@wdio/config/-/config-7.5.3.tgz", - "integrity": "sha512-udvVizYoilOxuWj/BmoN6y7ZCd4wPdYNlSfWznrbCezAdaLZ4/pNDOO0WRWx2C4+q1wdkXZV/VuQPUGfL0lEHQ==", - "dev": true, - "requires": { - "@wdio/logger": "7.5.3", - "@wdio/types": "7.5.3", - "deepmerge": "^4.0.0", - "glob": "^7.1.2" - }, - "dependencies": { - "@wdio/logger": { - "version": "7.5.3", - "resolved": "https://registry.npmjs.org/@wdio/logger/-/logger-7.5.3.tgz", - "integrity": "sha512-r9EADpm0ncS1bDQSWi/nhF9C59/WNLbdAAFlo782E9ItFCpDhNit3aQP9vETv1vFxJRjUIM8Fw/HW8zwPadkbw==", + "proxy-agent": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/proxy-agent/-/proxy-agent-6.3.0.tgz", + "integrity": "sha512-0LdR757eTj/JfuU7TL2YCuAZnxWXu3tkJbg4Oq3geW/qFNT/32T0sp2HnZ9O0lMR4q3vwAt0+xCA8SR0WAD0og==", "dev": true, "requires": { - "chalk": "^4.0.0", - "loglevel": "^1.6.0", - "loglevel-plugin-prefix": "^0.8.4", - "strip-ansi": "^6.0.0" + "agent-base": "^7.0.2", + "debug": "^4.3.4", + "http-proxy-agent": "^7.0.0", + "https-proxy-agent": "^7.0.0", + "lru-cache": "^7.14.1", + "pac-proxy-agent": "^7.0.0", + "proxy-from-env": "^1.1.0", + "socks-proxy-agent": "^8.0.1" + }, + "dependencies": { + "agent-base": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-7.1.0.tgz", + "integrity": "sha512-o/zjMZRhJxny7OyEF+Op8X+efiELC7k7yOjMzgfzVqOzXqkBkWI79YoTdOtsuWd5BWhAGAuOY/Xa6xpiaWXiNg==", + "dev": true, + "requires": { + "debug": "^4.3.4" + } + }, + "http-proxy-agent": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/http-proxy-agent/-/http-proxy-agent-7.0.0.tgz", + "integrity": "sha512-+ZT+iBxVUQ1asugqnD6oWoRiS25AkjNfG085dKJGtGxkdwLQrMKU5wJr2bOOFAXzKcTuqq+7fZlTMgG3SRfIYQ==", + "dev": true, + "requires": { + "agent-base": "^7.1.0", + "debug": "^4.3.4" + } + }, + "https-proxy-agent": { + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-7.0.2.tgz", + "integrity": "sha512-NmLNjm6ucYwtcUmL7JQC1ZQ57LmHP4lT15FQ8D61nak1rO6DH+fz5qNK2Ap5UN4ZapYICE3/0KodcLYSPsPbaA==", + "dev": true, + "requires": { + "agent-base": "^7.0.2", + "debug": "4" + } + }, + "lru-cache": { + "version": "7.18.3", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-7.18.3.tgz", + "integrity": "sha512-jumlc0BIUrS3qJGgIkWZsyfAM7NCWiBcCDhnd+3NNM5KbBmLTgHVfWBcg6W+rLUsIpzpERPsvwUP7CckAQSOoA==", + "dev": true + } } }, - "@wdio/types": { - "version": "7.5.3", - "resolved": "https://registry.npmjs.org/@wdio/types/-/types-7.5.3.tgz", - "integrity": "sha512-jmumhKBhNDABnpmrshYLEcdE9WoP5tmynsDNbDABlb/W8FFiLySQOejukhYIL9CLys4zXerV3/edks0SCzHOiQ==", + "puppeteer-core": { + "version": "20.3.0", + "resolved": "https://registry.npmjs.org/puppeteer-core/-/puppeteer-core-20.3.0.tgz", + "integrity": "sha512-264pBrIui5bO6NJeOcbJrLa0OCwmA4+WK00JMrLIKTfRiqe2gx8KWTzLsjyw/bizErp3TKS7vt/I0i5fTC+mAw==", "dev": true, + "optional": true, + "peer": true, "requires": { - "got": "^11.8.1" + "@puppeteer/browsers": "1.3.0", + "chromium-bidi": "0.4.9", + "cross-fetch": "3.1.6", + "debug": "4.3.4", + "devtools-protocol": "0.0.1120988", + "ws": "8.13.0" } }, - "ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "read-pkg": { + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-8.1.0.tgz", + "integrity": "sha512-PORM8AgzXeskHO/WEv312k9U03B8K9JSiWF/8N9sUuFjBa+9SF2u6K7VClzXwDXab51jCd8Nd36CNM+zR97ScQ==", "dev": true, "requires": { - "color-convert": "^2.0.1" + "@types/normalize-package-data": "^2.4.1", + "normalize-package-data": "^6.0.0", + "parse-json": "^7.0.0", + "type-fest": "^4.2.0" } }, - "chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "read-pkg-up": { + "version": "10.1.0", + "resolved": "https://registry.npmjs.org/read-pkg-up/-/read-pkg-up-10.1.0.tgz", + "integrity": "sha512-aNtBq4jR8NawpKJQldrQcSW9y/d+KWH4v24HWkHljOZ7H0av+YTGANBzRh9A5pw7v/bLVsLVPpOhJ7gHNVy8lA==", "dev": true, "requires": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" + "find-up": "^6.3.0", + "read-pkg": "^8.1.0", + "type-fest": "^4.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==", + "readable-stream": { + "version": "3.6.2", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz", + "integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==", "dev": true, "requires": { - "color-name": "~1.1.4" + "inherits": "^2.0.3", + "string_decoder": "^1.1.1", + "util-deprecate": "^1.0.1" } }, - "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==", + "semver": { + "version": "7.5.4", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.4.tgz", + "integrity": "sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA==", "dev": true, "requires": { - "has-flag": "^4.0.0" + "lru-cache": "^6.0.0" } - } - } - }, - "@wdio/local-runner": { - "version": "7.5.7", - "resolved": "https://registry.npmjs.org/@wdio/local-runner/-/local-runner-7.5.7.tgz", - "integrity": "sha512-aYc0XUV+/e3cg8Fp+CWlC4FbwSSG3mKAv1iuy/+Hwzg2kJE+aa+Rf2p2BQYc7HPRtKNW0bM8o+aCImZLAiPM+A==", - "dev": true, - "requires": { - "@types/stream-buffers": "^3.0.3", - "@wdio/logger": "7.5.3", - "@wdio/repl": "7.5.3", - "@wdio/runner": "7.5.7", - "@wdio/types": "7.5.3", - "async-exit-hook": "^2.0.1", - "split2": "^3.2.2", - "stream-buffers": "^3.0.2" - }, - "dependencies": { - "@wdio/logger": { - "version": "7.5.3", - "resolved": "https://registry.npmjs.org/@wdio/logger/-/logger-7.5.3.tgz", - "integrity": "sha512-r9EADpm0ncS1bDQSWi/nhF9C59/WNLbdAAFlo782E9ItFCpDhNit3aQP9vETv1vFxJRjUIM8Fw/HW8zwPadkbw==", + }, + "serialize-error": { + "version": "11.0.3", + "resolved": "https://registry.npmjs.org/serialize-error/-/serialize-error-11.0.3.tgz", + "integrity": "sha512-2G2y++21dhj2R7iHAdd0FIzjGwuKZld+7Pl/bTU6YIkrC2ZMbVUjm+luj6A6V34Rv9XfKJDKpTWu9W4Gse1D9g==", "dev": true, "requires": { - "chalk": "^4.0.0", - "loglevel": "^1.6.0", - "loglevel-plugin-prefix": "^0.8.4", - "strip-ansi": "^6.0.0" + "type-fest": "^2.12.2" + }, + "dependencies": { + "type-fest": { + "version": "2.19.0", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-2.19.0.tgz", + "integrity": "sha512-RAH822pAdBgcNMAfWnCBU3CFZcfZ/i1eZjwFU/dsLKumyuuP3niueg2UAukXYF0E2AAoc82ZSSf9J0WQBinzHA==", + "dev": true + } } }, - "@wdio/types": { - "version": "7.5.3", - "resolved": "https://registry.npmjs.org/@wdio/types/-/types-7.5.3.tgz", - "integrity": "sha512-jmumhKBhNDABnpmrshYLEcdE9WoP5tmynsDNbDABlb/W8FFiLySQOejukhYIL9CLys4zXerV3/edks0SCzHOiQ==", + "signal-exit": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-4.1.0.tgz", + "integrity": "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==", + "dev": true + }, + "tar-stream": { + "version": "3.1.7", + "resolved": "https://registry.npmjs.org/tar-stream/-/tar-stream-3.1.7.tgz", + "integrity": "sha512-qJj60CXt7IU1Ffyc3NJMjh6EkuCFej46zUqJ4J7pqYlThyd9bO0XBTmcOIhSzZJVWfsLks0+nle/j538YAW9RQ==", "dev": true, "requires": { - "got": "^11.8.1" + "b4a": "^1.6.4", + "fast-fifo": "^1.2.0", + "streamx": "^2.15.0" } }, - "ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "type-fest": { + "version": "4.10.1", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-4.10.1.tgz", + "integrity": "sha512-7ZnJYTp6uc04uYRISWtiX3DSKB/fxNQT0B5o1OUeCqiQiwF+JC9+rJiZIDrPrNCLLuTqyQmh4VdQqh/ZOkv9MQ==", + "dev": true + }, + "ua-parser-js": { + "version": "1.0.37", + "resolved": "https://registry.npmjs.org/ua-parser-js/-/ua-parser-js-1.0.37.tgz", + "integrity": "sha512-bhTyI94tZofjo+Dn8SN6Zv8nBDvyXTymAdM3LDI/0IboIUwTu1rEhW7v2TfiVsoYWgkQ4kOVqnI8APUFbIQIFQ==", "dev": true, - "requires": { - "color-convert": "^2.0.1" - } + "optional": true, + "peer": true }, - "chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "uuid": { + "version": "9.0.1", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-9.0.1.tgz", + "integrity": "sha512-b+1eJOlsR9K8HJpow9Ok3fiWOWSIcIzXodvv0rQjVoOVNpWMpxf1wZNpt4y9h10odCNrqnYp1OBzRktckBe3sA==", "dev": true, - "requires": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" + "optional": true, + "peer": true + }, + "webdriverio": { + "version": "8.29.1", + "resolved": "https://registry.npmjs.org/webdriverio/-/webdriverio-8.29.1.tgz", + "integrity": "sha512-NZK95ivXCqdPraB3FHMw6ByxnCvtgFXkjzG2l3Oq5z0IuJS2aMow3AKFIyiuG6is/deGCe+Tb8eFTCqak7UV+w==", + "dev": true, + "requires": { + "@types/node": "^20.1.0", + "@wdio/config": "8.29.1", + "@wdio/logger": "8.28.0", + "@wdio/protocols": "8.24.12", + "@wdio/repl": "8.24.12", + "@wdio/types": "8.29.1", + "@wdio/utils": "8.29.1", + "archiver": "^6.0.0", + "aria-query": "^5.0.0", + "css-shorthand-properties": "^1.1.1", + "css-value": "^0.0.1", + "devtools-protocol": "^0.0.1249869", + "grapheme-splitter": "^1.0.2", + "import-meta-resolve": "^4.0.0", + "is-plain-obj": "^4.1.0", + "lodash.clonedeep": "^4.5.0", + "lodash.zip": "^4.2.0", + "minimatch": "^9.0.0", + "puppeteer-core": "^20.9.0", + "query-selector-shadow-dom": "^1.0.0", + "resq": "^1.9.1", + "rgb2hex": "0.2.5", + "serialize-error": "^11.0.1", + "webdriver": "8.29.1" + }, + "dependencies": { + "@puppeteer/browsers": { + "version": "1.4.6", + "resolved": "https://registry.npmjs.org/@puppeteer/browsers/-/browsers-1.4.6.tgz", + "integrity": "sha512-x4BEjr2SjOPowNeiguzjozQbsc6h437ovD/wu+JpaenxVLm3jkgzHY2xOslMTp50HoTvQreMjiexiGQw1sqZlQ==", + "dev": true, + "requires": { + "debug": "4.3.4", + "extract-zip": "2.0.1", + "progress": "2.0.3", + "proxy-agent": "6.3.0", + "tar-fs": "3.0.4", + "unbzip2-stream": "1.4.3", + "yargs": "17.7.1" + } + }, + "chromium-bidi": { + "version": "0.4.16", + "resolved": "https://registry.npmjs.org/chromium-bidi/-/chromium-bidi-0.4.16.tgz", + "integrity": "sha512-7ZbXdWERxRxSwo3txsBjjmc/NLxqb1Bk30mRb0BMS4YIaiV6zvKZqL/UAH+DdqcDYayDWk2n/y8klkBDODrPvA==", + "dev": true, + "requires": { + "mitt": "3.0.0" + } + }, + "cross-fetch": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/cross-fetch/-/cross-fetch-4.0.0.tgz", + "integrity": "sha512-e4a5N8lVvuLgAWgnCrLr2PP0YyDOTHa9H/Rj54dirp61qXnNq46m82bRhNqIA5VccJtWBvPTFRV3TtvHUKPB1g==", + "dev": true, + "requires": { + "node-fetch": "^2.6.12" + } + }, + "devtools-protocol": { + "version": "0.0.1249869", + "resolved": "https://registry.npmjs.org/devtools-protocol/-/devtools-protocol-0.0.1249869.tgz", + "integrity": "sha512-Ctp4hInA0BEavlUoRy9mhGq0i+JSo/AwVyX2EFgZmV1kYB+Zq+EMBAn52QWu6FbRr10hRb6pBl420upbp4++vg==", + "dev": true + }, + "puppeteer-core": { + "version": "20.9.0", + "resolved": "https://registry.npmjs.org/puppeteer-core/-/puppeteer-core-20.9.0.tgz", + "integrity": "sha512-H9fYZQzMTRrkboEfPmf7m3CLDN6JvbxXA3qTtS+dFt27tR+CsFHzPsT6pzp6lYL6bJbAPaR0HaPO6uSi+F94Pg==", + "dev": true, + "requires": { + "@puppeteer/browsers": "1.4.6", + "chromium-bidi": "0.4.16", + "cross-fetch": "4.0.0", + "debug": "4.3.4", + "devtools-protocol": "0.0.1147663", + "ws": "8.13.0" + }, + "dependencies": { + "devtools-protocol": { + "version": "0.0.1147663", + "resolved": "https://registry.npmjs.org/devtools-protocol/-/devtools-protocol-0.0.1147663.tgz", + "integrity": "sha512-hyWmRrexdhbZ1tcJUGpO95ivbRhWXz++F4Ko+n21AY5PNln2ovoJw+8ZMNDTtip+CNFQfrtLVh/w4009dXO/eQ==", + "dev": true + } + } + }, + "tar-fs": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/tar-fs/-/tar-fs-3.0.4.tgz", + "integrity": "sha512-5AFQU8b9qLfZCX9zp2duONhPmZv0hGYiBPJsyUdqMjzq/mqVpy/rEUSeHk1+YitmxugaptgBh5oDGU3VsAJq4w==", + "dev": true, + "requires": { + "mkdirp-classic": "^0.5.2", + "pump": "^3.0.0", + "tar-stream": "^3.1.5" + } + }, + "yargs": { + "version": "17.7.1", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.7.1.tgz", + "integrity": "sha512-cwiTb08Xuv5fqF4AovYacTFNxk62th7LKJ6BL9IGUpTJrWoU7/7WdQGTP2SjKf1dUNBGzDd28p/Yfs/GI6JrLw==", + "dev": true, + "requires": { + "cliui": "^8.0.1", + "escalade": "^3.1.1", + "get-caller-file": "^2.0.5", + "require-directory": "^2.1.1", + "string-width": "^4.2.3", + "y18n": "^5.0.5", + "yargs-parser": "^21.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==", + "ws": { + "version": "8.13.0", + "resolved": "https://registry.npmjs.org/ws/-/ws-8.13.0.tgz", + "integrity": "sha512-x9vcZYTrFPC7aSIbj7sRCYo7L/Xb8Iy+pW0ng0wt2vCJv7M9HOMy0UoN3rr+IFC7hb7vXoqS+P9ktyLLLhO+LA==", + "dev": true, + "requires": {} + }, + "yargs": { + "version": "17.7.2", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.7.2.tgz", + "integrity": "sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==", "dev": true, "requires": { - "color-name": "~1.1.4" + "cliui": "^8.0.1", + "escalade": "^3.1.1", + "get-caller-file": "^2.0.5", + "require-directory": "^2.1.1", + "string-width": "^4.2.3", + "y18n": "^5.0.5", + "yargs-parser": "^21.1.1" } }, - "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==", + "yocto-queue": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-1.0.0.tgz", + "integrity": "sha512-9bnSc/HEW2uRy67wc+T8UwauLuPJVn28jb+GtJY16iiKWyvmYJRXVT4UamsAEGQfPohgr2q4Tq0sQbQlxTfi1g==", "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==", + "zip-stream": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/zip-stream/-/zip-stream-5.0.1.tgz", + "integrity": "sha512-UfZ0oa0C8LI58wJ+moL46BDIMgCQbnsb+2PoiJYtonhBsMh2bq1eRBVkvjfVsqbEHd9/EgKPUuL9saSSsec8OA==", "dev": true, "requires": { - "has-flag": "^4.0.0" + "archiver-utils": "^4.0.1", + "compress-commons": "^5.0.1", + "readable-stream": "^3.6.0" } } } }, - "@wdio/logger": { - "version": "7.16.0", - "resolved": "https://registry.npmjs.org/@wdio/logger/-/logger-7.16.0.tgz", - "integrity": "sha512-/6lOGb2Iow5eSsy7RJOl1kCwsP4eMlG+/QKro5zUJsuyNJSQXf2ejhpkzyKWLgQbHu83WX6cM1014AZuLkzoQg==", + "@wdio/concise-reporter": { + "version": "8.29.1", + "resolved": "https://registry.npmjs.org/@wdio/concise-reporter/-/concise-reporter-8.29.1.tgz", + "integrity": "sha512-dUhClWeq1naL1Qa1nSMDeH8aCVViOKiEzhBhQjgrMOz1Mh3l6O/woqbK2iKDVZDRhfGghtGcV0vpoEUvt8ZKOA==", "dev": true, "requires": { - "chalk": "^4.0.0", - "loglevel": "^1.6.0", - "loglevel-plugin-prefix": "^0.8.4", - "strip-ansi": "^6.0.0" + "@wdio/reporter": "8.29.1", + "@wdio/types": "8.29.1", + "chalk": "^5.0.1", + "pretty-ms": "^7.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.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", - "dev": true, - "requires": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - } - }, - "color-convert": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-5.3.0.tgz", + "integrity": "sha512-dLitG79d+GV1Nb/VYcCDFivJeK1hiukt9QjRNVOsUtTy1rR1YJsmpGGTZ3qJos+uw7WmWF4wUwBd9jxjocFC2w==", + "dev": true + } + } + }, + "@wdio/config": { + "version": "8.29.1", + "resolved": "https://registry.npmjs.org/@wdio/config/-/config-8.29.1.tgz", + "integrity": "sha512-zNUac4lM429HDKAitO+fdlwUH1ACQU8lww+DNVgUyuEb86xgVdTqHeiJr/3kOMJAq9IATeE7mDtYyyn6HPm1JA==", + "dev": true, + "requires": { + "@wdio/logger": "8.28.0", + "@wdio/types": "8.29.1", + "@wdio/utils": "8.29.1", + "decamelize": "^6.0.0", + "deepmerge-ts": "^5.0.0", + "glob": "^10.2.2", + "import-meta-resolve": "^4.0.0" + }, + "dependencies": { + "brace-expansion": { "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", + "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", "dev": true, "requires": { - "color-name": "~1.1.4" + "balanced-match": "^1.0.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==", + "decamelize": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-6.0.0.tgz", + "integrity": "sha512-Fv96DCsdOgB6mdGl67MT5JaTNKRzrzill5OH5s8bjYJXVlcXyPYGyPsUkWyGV5p1TXI5esYIYMMeDJL0hEIwaA==", "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 + "glob": { + "version": "10.3.10", + "resolved": "https://registry.npmjs.org/glob/-/glob-10.3.10.tgz", + "integrity": "sha512-fa46+tv1Ak0UPK1TOy/pZrIybNNt4HCv7SDzwyfiOZkvZLEbjsZkJBPtDHVshZjbecAoAGSC20MjLDG/qr679g==", + "dev": true, + "requires": { + "foreground-child": "^3.1.0", + "jackspeak": "^2.3.5", + "minimatch": "^9.0.1", + "minipass": "^5.0.0 || ^6.0.2 || ^7.0.0", + "path-scurry": "^1.10.1" + } }, - "supports-color": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "minimatch": { + "version": "9.0.3", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.3.tgz", + "integrity": "sha512-RHiac9mvaRw0x3AYRgDC1CxAP7HTcNrrECeA8YYJeWnpo+2Q5CegtZjaotWTWxDG3UeGA1coE05iH1mPjT/2mg==", "dev": true, "requires": { - "has-flag": "^4.0.0" + "brace-expansion": "^2.0.1" } } } }, - "@wdio/mocha-framework": { - "version": "7.5.3", - "resolved": "https://registry.npmjs.org/@wdio/mocha-framework/-/mocha-framework-7.5.3.tgz", - "integrity": "sha512-96QCVWsiyZxEgOZP3oTq2B2T7zne5dCdehLa2n4q/BLjk96Rj0jifidJZfd/1+vdNPKX0gWWAzpy98Znn8MVMw==", + "@wdio/globals": { + "version": "8.29.1", + "resolved": "https://registry.npmjs.org/@wdio/globals/-/globals-8.29.1.tgz", + "integrity": "sha512-F+fPnX75f44/crZDfQ2FYSino/IMIdbnQGLIkaH0VnoljVJIHuxnX4y5Zqr4yRgurL9DsZaH22cLHrPXaHUhPg==", "dev": true, "requires": { - "@types/mocha": "^8.0.0", - "@wdio/logger": "7.5.3", - "@wdio/types": "7.5.3", - "@wdio/utils": "7.5.3", - "expect-webdriverio": "^2.0.0", - "mocha": "^8.0.1" + "expect-webdriverio": "^4.9.3", + "webdriverio": "8.29.1" }, "dependencies": { - "@wdio/logger": { - "version": "7.5.3", - "resolved": "https://registry.npmjs.org/@wdio/logger/-/logger-7.5.3.tgz", - "integrity": "sha512-r9EADpm0ncS1bDQSWi/nhF9C59/WNLbdAAFlo782E9ItFCpDhNit3aQP9vETv1vFxJRjUIM8Fw/HW8zwPadkbw==", + "@puppeteer/browsers": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/@puppeteer/browsers/-/browsers-1.3.0.tgz", + "integrity": "sha512-an3QdbNPkuU6qpxpbssxAbjRLJcF+eP4L8UqIY3+6n0sbaVxw5pz7PiCLy9g32XEZuoamUlV5ZQPnA6FxvkIHA==", "dev": true, + "optional": true, + "peer": true, "requires": { - "chalk": "^4.0.0", - "loglevel": "^1.6.0", - "loglevel-plugin-prefix": "^0.8.4", - "strip-ansi": "^6.0.0" + "debug": "4.3.4", + "extract-zip": "2.0.1", + "http-proxy-agent": "5.0.0", + "https-proxy-agent": "5.0.1", + "progress": "2.0.3", + "proxy-from-env": "1.1.0", + "tar-fs": "2.1.1", + "unbzip2-stream": "1.4.3", + "yargs": "17.7.1" } }, - "@wdio/types": { - "version": "7.5.3", - "resolved": "https://registry.npmjs.org/@wdio/types/-/types-7.5.3.tgz", - "integrity": "sha512-jmumhKBhNDABnpmrshYLEcdE9WoP5tmynsDNbDABlb/W8FFiLySQOejukhYIL9CLys4zXerV3/edks0SCzHOiQ==", + "@types/which": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/@types/which/-/which-2.0.2.tgz", + "integrity": "sha512-113D3mDkZDjo+EeUEHCFy0qniNc1ZpecGiAU7WSo7YDoSzolZIQKpYFHrPpjkB2nuyahcKfrmLXeQlh7gqJYdw==", + "dev": true, + "optional": true, + "peer": true + }, + "archiver": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/archiver/-/archiver-6.0.1.tgz", + "integrity": "sha512-CXGy4poOLBKptiZH//VlWdFuUC1RESbdZjGjILwBuZ73P7WkAUN0htfSfBq/7k6FRFlpu7bg4JOkj1vU9G6jcQ==", "dev": true, + "optional": true, "requires": { - "got": "^11.8.1" + "archiver-utils": "^4.0.1", + "async": "^3.2.4", + "buffer-crc32": "^0.2.1", + "readable-stream": "^3.6.0", + "readdir-glob": "^1.1.2", + "tar-stream": "^3.0.0", + "zip-stream": "^5.0.1" } }, - "ansi-colors": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-4.1.1.tgz", - "integrity": "sha512-JoX0apGbHaUJBNl6yF+p6JAFYZ666/hhCGKN5t9QFjbJQKUU/g8MNbFDbvfrgKXvI1QpZplPOnwIo99lX/AAmA==", - "dev": true - }, - "ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "archiver-utils": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/archiver-utils/-/archiver-utils-4.0.1.tgz", + "integrity": "sha512-Q4Q99idbvzmgCTEAAhi32BkOyq8iVI5EwdO0PmBDSGIzzjYNdcFn7Q7k3OzbLy4kLUPXfJtG6fO2RjftXbobBg==", "dev": true, + "optional": true, "requires": { - "color-convert": "^2.0.1" + "glob": "^8.0.0", + "graceful-fs": "^4.2.0", + "lazystream": "^1.0.0", + "lodash": "^4.17.15", + "normalize-path": "^3.0.0", + "readable-stream": "^3.6.0" } }, - "argparse": { + "async": { + "version": "3.2.5", + "resolved": "https://registry.npmjs.org/async/-/async-3.2.5.tgz", + "integrity": "sha512-baNZyqaaLhyLVKm/DlvdW051MSgO6b8eVfIezl9E5PqWxFgzLm/wQntEW4zOytVburDEr0JlALEpdOFwvErLsg==", + "dev": true, + "optional": true + }, + "brace-expansion": { "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 + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", + "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", + "dev": true, + "optional": true, + "requires": { + "balanced-match": "^1.0.0" + } }, - "chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "chrome-launcher": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/chrome-launcher/-/chrome-launcher-1.1.0.tgz", + "integrity": "sha512-rJYWeEAERwWIr3c3mEVXwNiODPEdMRlRxHc47B1qHPOolHZnkj7rMv1QSUfPoG6MgatWj5AxSpnKKR4QEwEQIQ==", "dev": true, + "optional": true, + "peer": true, "requires": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" + "@types/node": "*", + "escape-string-regexp": "^4.0.0", + "is-wsl": "^2.2.0", + "lighthouse-logger": "^2.0.1" } }, - "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==", + "compress-commons": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/compress-commons/-/compress-commons-5.0.1.tgz", + "integrity": "sha512-MPh//1cERdLtqwO3pOFLeXtpuai0Y2WCd5AhtKxznqM7WtaMYaOEMSgn45d9D10sIHSfIKE603HlOp8OPGrvag==", "dev": true, + "optional": 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" + "crc-32": "^1.2.0", + "crc32-stream": "^5.0.0", + "normalize-path": "^3.0.0", + "readable-stream": "^3.6.0" } }, - "cliui": { - "version": "7.0.4", - "resolved": "https://registry.npmjs.org/cliui/-/cliui-7.0.4.tgz", - "integrity": "sha512-OcRE68cOsVMXp1Yvonl/fzkQOyjLSu/8bhPDfQt0e0/Eb283TKP20Fs2MqoPsr9SwA595rRCA+QMzYc9nBP+JQ==", + "crc32-stream": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/crc32-stream/-/crc32-stream-5.0.0.tgz", + "integrity": "sha512-B0EPa1UK+qnpBZpG+7FgPCu0J2ETLpXq09o9BkLkEAhdB6Z61Qo4pJ3JYu0c+Qi+/SAL7QThqnzS06pmSSyZaw==", "dev": true, + "optional": true, "requires": { - "string-width": "^4.2.0", - "strip-ansi": "^6.0.0", - "wrap-ansi": "^7.0.0" + "crc-32": "^1.2.0", + "readable-stream": "^3.4.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==", + "cross-fetch": { + "version": "3.1.6", + "resolved": "https://registry.npmjs.org/cross-fetch/-/cross-fetch-3.1.6.tgz", + "integrity": "sha512-riRvo06crlE8HiqOwIpQhxwdOk4fOeR7FVM/wXoxchFEqMNUjvbs3bfo4OTgMEMHzppd4DxFBDbyySj8Cv781g==", "dev": true, + "optional": true, + "peer": true, "requires": { - "color-name": "~1.1.4" + "node-fetch": "^2.6.11" } }, - "color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", - "dev": true + "devtools": { + "version": "8.29.1", + "resolved": "https://registry.npmjs.org/devtools/-/devtools-8.29.1.tgz", + "integrity": "sha512-fbH0Z7CPK4OZSgUw2QcAppczowxtSyvFztPUmiFyi99cUadjEOwlg0aL3pBVlIDo67olYjGb8GD1M5Z4yI/P6w==", + "dev": true, + "optional": true, + "peer": true, + "requires": { + "@types/node": "^20.1.0", + "@wdio/config": "8.29.1", + "@wdio/logger": "8.28.0", + "@wdio/protocols": "8.24.12", + "@wdio/types": "8.29.1", + "@wdio/utils": "8.29.1", + "chrome-launcher": "^1.0.0", + "edge-paths": "^3.0.5", + "import-meta-resolve": "^4.0.0", + "puppeteer-core": "20.3.0", + "query-selector-shadow-dom": "^1.0.0", + "ua-parser-js": "^1.0.1", + "uuid": "^9.0.0", + "which": "^4.0.0" + }, + "dependencies": { + "which": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/which/-/which-4.0.0.tgz", + "integrity": "sha512-GlaYyEb07DPxYCKhKzplCWBJtvxZcZMrL+4UkrTSJHHPyZU4mYYTv3qaOe77H7EODLSSopAUFAc6W8U4yqvscg==", + "dev": true, + "optional": true, + "peer": true, + "requires": { + "isexe": "^3.1.1" + } + } + } }, - "debug": { - "version": "4.3.1", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.1.tgz", - "integrity": "sha512-doEwdvm4PCeK4K3RQN2ZC2BYUBaxwLARCqZmMjtF8a51J2Rb0xpVloFRnCODwqjpwnAoao4pelN8l3RJdv3gRQ==", + "devtools-protocol": { + "version": "0.0.1120988", + "resolved": "https://registry.npmjs.org/devtools-protocol/-/devtools-protocol-0.0.1120988.tgz", + "integrity": "sha512-39fCpE3Z78IaIPChJsP6Lhmkbf4dWXOmzLk/KFTdRkNk/0JymRIfUynDVRndV9HoDz8PyalK1UH21ST/ivwW5Q==", + "dev": true, + "optional": true, + "peer": true + }, + "edge-paths": { + "version": "3.0.5", + "resolved": "https://registry.npmjs.org/edge-paths/-/edge-paths-3.0.5.tgz", + "integrity": "sha512-sB7vSrDnFa4ezWQk9nZ/n0FdpdUuC6R1EOrlU3DL+bovcNFK28rqu2emmAUjujYEJTWIgQGqgVVWUZXMnc8iWg==", "dev": true, + "optional": true, + "peer": true, "requires": { - "ms": "2.1.2" + "@types/which": "^2.0.1", + "which": "^2.0.2" } }, - "diff": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/diff/-/diff-5.0.0.tgz", - "integrity": "sha512-/VTCrvm5Z0JGty/BWHljh+BAiw3IK+2j87NGMu8Nwc/f48WoDAC395uomO9ZD117ZOBaHmkX1oyLvkVM/aIT3w==", - "dev": true - }, "escape-string-regexp": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", "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" - } + "optional": true, + "peer": true }, "glob": { - "version": "7.1.6", - "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.6.tgz", - "integrity": "sha512-LwaxwyZ72Lk7vZINtNNrywX0ZuLyStrdDtabefZKAY5ZGJhVtgdznluResxNmPitE0SAO+O26sWTHeKSI2wMBA==", + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/glob/-/glob-8.1.0.tgz", + "integrity": "sha512-r8hpEjiQEYlF2QU0df3dS+nxxSIreXQS1qRhMJM0Q5NDdR386C7jb7Hwwod8Fgiuex+k0GFjgft18yvxm5XoCQ==", "dev": true, + "optional": 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" + "minimatch": "^5.0.1", + "once": "^1.3.0" + }, + "dependencies": { + "minimatch": { + "version": "5.1.6", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-5.1.6.tgz", + "integrity": "sha512-lKwV/1brpG6mBUFHtb7NUmtABCb2WZZmm2wNiOA5hAb8VdCS4B3dtMWyvcoViccwAW/COERjXLt0zP1zXUN26g==", + "dev": true, + "optional": true, + "requires": { + "brace-expansion": "^2.0.1" + } + } } }, - "has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", - "dev": true - }, - "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==", + "http-proxy-agent": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/http-proxy-agent/-/http-proxy-agent-5.0.0.tgz", + "integrity": "sha512-n2hY8YdoRE1i7r6M0w9DIw5GgZN0G25P8zLCRQ8rjXtTU3vsNFBI/vWK/UIeE6g5MUUz6avwAPXmL6Fy9D/90w==", "dev": true, + "optional": true, + "peer": true, "requires": { - "argparse": "^2.0.1" + "@tootallnate/once": "2", + "agent-base": "6", + "debug": "4" } }, - "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==", + "isexe": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/isexe/-/isexe-3.1.1.tgz", + "integrity": "sha512-LpB/54B+/2J5hqQ7imZHfdU31OlgQqx7ZicVlkm9kzg9/w8GKLEcFfJl/t7DCEDueOyBAD6zCCwTO6Fzs0NoEQ==", + "dev": true, + "optional": true, + "peer": true + }, + "lighthouse-logger": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/lighthouse-logger/-/lighthouse-logger-2.0.1.tgz", + "integrity": "sha512-ioBrW3s2i97noEmnXxmUq7cjIcVRjT5HBpAYy8zE11CxU9HqlWHHeRxfeN1tn8F7OEMVPIC9x1f8t3Z7US9ehQ==", "dev": true, + "optional": true, + "peer": true, "requires": { - "p-locate": "^5.0.0" + "debug": "^2.6.9", + "marky": "^1.2.2" + }, + "dependencies": { + "debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "dev": true, + "optional": true, + "peer": true, + "requires": { + "ms": "2.0.0" + } + } } }, - "log-symbols": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-4.0.0.tgz", - "integrity": "sha512-FN8JBzLx6CzeMrB0tg6pqlGU1wCrXW+ZXGH481kfsBqer0hToTIiHdjH4Mq8xJUbvATujKCvaREGWpGUionraA==", + "lru-cache": { + "version": "7.18.3", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-7.18.3.tgz", + "integrity": "sha512-jumlc0BIUrS3qJGgIkWZsyfAM7NCWiBcCDhnd+3NNM5KbBmLTgHVfWBcg6W+rLUsIpzpERPsvwUP7CckAQSOoA==", + "dev": true, + "optional": true + }, + "minimatch": { + "version": "9.0.3", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.3.tgz", + "integrity": "sha512-RHiac9mvaRw0x3AYRgDC1CxAP7HTcNrrECeA8YYJeWnpo+2Q5CegtZjaotWTWxDG3UeGA1coE05iH1mPjT/2mg==", "dev": true, + "optional": true, "requires": { - "chalk": "^4.0.0" + "brace-expansion": "^2.0.1" } }, - "minimatch": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", - "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==", + "ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", "dev": true, + "optional": true, + "peer": true + }, + "node-fetch": { + "version": "2.7.0", + "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.7.0.tgz", + "integrity": "sha512-c4FRfUm/dbcWZ7U+1Wq0AwCyFL+3nt2bEw05wfxSz+DWpWsitgmSgYmy2dQdWyKC1694ELPqMs/YzUSNozLt8A==", + "dev": true, + "optional": true, "requires": { - "brace-expansion": "^1.1.7" + "whatwg-url": "^5.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" + "proxy-agent": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/proxy-agent/-/proxy-agent-6.3.0.tgz", + "integrity": "sha512-0LdR757eTj/JfuU7TL2YCuAZnxWXu3tkJbg4Oq3geW/qFNT/32T0sp2HnZ9O0lMR4q3vwAt0+xCA8SR0WAD0og==", + "dev": true, + "optional": true, + "requires": { + "agent-base": "^7.0.2", + "debug": "^4.3.4", + "http-proxy-agent": "^7.0.0", + "https-proxy-agent": "^7.0.0", + "lru-cache": "^7.14.1", + "pac-proxy-agent": "^7.0.0", + "proxy-from-env": "^1.1.0", + "socks-proxy-agent": "^8.0.1" }, "dependencies": { - "ms": { - "version": "2.1.3", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", - "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", - "dev": true + "agent-base": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-7.1.0.tgz", + "integrity": "sha512-o/zjMZRhJxny7OyEF+Op8X+efiELC7k7yOjMzgfzVqOzXqkBkWI79YoTdOtsuWd5BWhAGAuOY/Xa6xpiaWXiNg==", + "dev": true, + "optional": true, + "requires": { + "debug": "^4.3.4" + } }, - "supports-color": { - "version": "8.1.1", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz", - "integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==", + "http-proxy-agent": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/http-proxy-agent/-/http-proxy-agent-7.0.0.tgz", + "integrity": "sha512-+ZT+iBxVUQ1asugqnD6oWoRiS25AkjNfG085dKJGtGxkdwLQrMKU5wJr2bOOFAXzKcTuqq+7fZlTMgG3SRfIYQ==", "dev": true, + "optional": true, "requires": { - "has-flag": "^4.0.0" + "agent-base": "^7.1.0", + "debug": "^4.3.4" + } + }, + "https-proxy-agent": { + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-7.0.2.tgz", + "integrity": "sha512-NmLNjm6ucYwtcUmL7JQC1ZQ57LmHP4lT15FQ8D61nak1rO6DH+fz5qNK2Ap5UN4ZapYICE3/0KodcLYSPsPbaA==", + "dev": true, + "optional": true, + "requires": { + "agent-base": "^7.0.2", + "debug": "4" } } } }, - "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 - }, - "p-limit": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", - "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==", + "puppeteer-core": { + "version": "20.3.0", + "resolved": "https://registry.npmjs.org/puppeteer-core/-/puppeteer-core-20.3.0.tgz", + "integrity": "sha512-264pBrIui5bO6NJeOcbJrLa0OCwmA4+WK00JMrLIKTfRiqe2gx8KWTzLsjyw/bizErp3TKS7vt/I0i5fTC+mAw==", "dev": true, + "optional": true, + "peer": true, "requires": { - "yocto-queue": "^0.1.0" + "@puppeteer/browsers": "1.3.0", + "chromium-bidi": "0.4.9", + "cross-fetch": "3.1.6", + "debug": "4.3.4", + "devtools-protocol": "0.0.1120988", + "ws": "8.13.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==", + "readable-stream": { + "version": "3.6.2", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz", + "integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==", "dev": true, + "optional": true, "requires": { - "p-limit": "^3.0.2" + "inherits": "^2.0.3", + "string_decoder": "^1.1.1", + "util-deprecate": "^1.0.1" } }, - "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==", + "serialize-error": { + "version": "11.0.3", + "resolved": "https://registry.npmjs.org/serialize-error/-/serialize-error-11.0.3.tgz", + "integrity": "sha512-2G2y++21dhj2R7iHAdd0FIzjGwuKZld+7Pl/bTU6YIkrC2ZMbVUjm+luj6A6V34Rv9XfKJDKpTWu9W4Gse1D9g==", "dev": true, + "optional": true, "requires": { - "picomatch": "^2.2.1" + "type-fest": "^2.12.2" } }, - "serialize-javascript": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-5.0.1.tgz", - "integrity": "sha512-SaaNal9imEO737H2c05Og0/8LUXG7EnsZyMa8MzkmuHoELfT6txuj0cMqRj6zfPKnmQ1yasR4PCJc8x+M4JSPA==", + "tar-stream": { + "version": "3.1.7", + "resolved": "https://registry.npmjs.org/tar-stream/-/tar-stream-3.1.7.tgz", + "integrity": "sha512-qJj60CXt7IU1Ffyc3NJMjh6EkuCFej46zUqJ4J7pqYlThyd9bO0XBTmcOIhSzZJVWfsLks0+nle/j538YAW9RQ==", "dev": true, + "optional": true, "requires": { - "randombytes": "^2.1.0" + "b4a": "^1.6.4", + "fast-fifo": "^1.2.0", + "streamx": "^2.15.0" } }, - "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 + "type-fest": { + "version": "2.19.0", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-2.19.0.tgz", + "integrity": "sha512-RAH822pAdBgcNMAfWnCBU3CFZcfZ/i1eZjwFU/dsLKumyuuP3niueg2UAukXYF0E2AAoc82ZSSf9J0WQBinzHA==", + "dev": true, + "optional": 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==", + "ua-parser-js": { + "version": "1.0.37", + "resolved": "https://registry.npmjs.org/ua-parser-js/-/ua-parser-js-1.0.37.tgz", + "integrity": "sha512-bhTyI94tZofjo+Dn8SN6Zv8nBDvyXTymAdM3LDI/0IboIUwTu1rEhW7v2TfiVsoYWgkQ4kOVqnI8APUFbIQIFQ==", + "dev": true, + "optional": true, + "peer": true + }, + "uuid": { + "version": "9.0.1", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-9.0.1.tgz", + "integrity": "sha512-b+1eJOlsR9K8HJpow9Ok3fiWOWSIcIzXodvv0rQjVoOVNpWMpxf1wZNpt4y9h10odCNrqnYp1OBzRktckBe3sA==", + "dev": true, + "optional": true, + "peer": true + }, + "webdriverio": { + "version": "8.29.1", + "resolved": "https://registry.npmjs.org/webdriverio/-/webdriverio-8.29.1.tgz", + "integrity": "sha512-NZK95ivXCqdPraB3FHMw6ByxnCvtgFXkjzG2l3Oq5z0IuJS2aMow3AKFIyiuG6is/deGCe+Tb8eFTCqak7UV+w==", "dev": true, + "optional": true, "requires": { - "has-flag": "^4.0.0" + "@types/node": "^20.1.0", + "@wdio/config": "8.29.1", + "@wdio/logger": "8.28.0", + "@wdio/protocols": "8.24.12", + "@wdio/repl": "8.24.12", + "@wdio/types": "8.29.1", + "@wdio/utils": "8.29.1", + "archiver": "^6.0.0", + "aria-query": "^5.0.0", + "css-shorthand-properties": "^1.1.1", + "css-value": "^0.0.1", + "devtools-protocol": "^0.0.1249869", + "grapheme-splitter": "^1.0.2", + "import-meta-resolve": "^4.0.0", + "is-plain-obj": "^4.1.0", + "lodash.clonedeep": "^4.5.0", + "lodash.zip": "^4.2.0", + "minimatch": "^9.0.0", + "puppeteer-core": "^20.9.0", + "query-selector-shadow-dom": "^1.0.0", + "resq": "^1.9.1", + "rgb2hex": "0.2.5", + "serialize-error": "^11.0.1", + "webdriver": "8.29.1" + }, + "dependencies": { + "@puppeteer/browsers": { + "version": "1.4.6", + "resolved": "https://registry.npmjs.org/@puppeteer/browsers/-/browsers-1.4.6.tgz", + "integrity": "sha512-x4BEjr2SjOPowNeiguzjozQbsc6h437ovD/wu+JpaenxVLm3jkgzHY2xOslMTp50HoTvQreMjiexiGQw1sqZlQ==", + "dev": true, + "optional": true, + "requires": { + "debug": "4.3.4", + "extract-zip": "2.0.1", + "progress": "2.0.3", + "proxy-agent": "6.3.0", + "tar-fs": "3.0.4", + "unbzip2-stream": "1.4.3", + "yargs": "17.7.1" + } + }, + "chromium-bidi": { + "version": "0.4.16", + "resolved": "https://registry.npmjs.org/chromium-bidi/-/chromium-bidi-0.4.16.tgz", + "integrity": "sha512-7ZbXdWERxRxSwo3txsBjjmc/NLxqb1Bk30mRb0BMS4YIaiV6zvKZqL/UAH+DdqcDYayDWk2n/y8klkBDODrPvA==", + "dev": true, + "optional": true, + "requires": { + "mitt": "3.0.0" + } + }, + "cross-fetch": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/cross-fetch/-/cross-fetch-4.0.0.tgz", + "integrity": "sha512-e4a5N8lVvuLgAWgnCrLr2PP0YyDOTHa9H/Rj54dirp61qXnNq46m82bRhNqIA5VccJtWBvPTFRV3TtvHUKPB1g==", + "dev": true, + "optional": true, + "requires": { + "node-fetch": "^2.6.12" + } + }, + "devtools-protocol": { + "version": "0.0.1249869", + "resolved": "https://registry.npmjs.org/devtools-protocol/-/devtools-protocol-0.0.1249869.tgz", + "integrity": "sha512-Ctp4hInA0BEavlUoRy9mhGq0i+JSo/AwVyX2EFgZmV1kYB+Zq+EMBAn52QWu6FbRr10hRb6pBl420upbp4++vg==", + "dev": true, + "optional": true + }, + "puppeteer-core": { + "version": "20.9.0", + "resolved": "https://registry.npmjs.org/puppeteer-core/-/puppeteer-core-20.9.0.tgz", + "integrity": "sha512-H9fYZQzMTRrkboEfPmf7m3CLDN6JvbxXA3qTtS+dFt27tR+CsFHzPsT6pzp6lYL6bJbAPaR0HaPO6uSi+F94Pg==", + "dev": true, + "optional": true, + "requires": { + "@puppeteer/browsers": "1.4.6", + "chromium-bidi": "0.4.16", + "cross-fetch": "4.0.0", + "debug": "4.3.4", + "devtools-protocol": "0.0.1147663", + "ws": "8.13.0" + }, + "dependencies": { + "devtools-protocol": { + "version": "0.0.1147663", + "resolved": "https://registry.npmjs.org/devtools-protocol/-/devtools-protocol-0.0.1147663.tgz", + "integrity": "sha512-hyWmRrexdhbZ1tcJUGpO95ivbRhWXz++F4Ko+n21AY5PNln2ovoJw+8ZMNDTtip+CNFQfrtLVh/w4009dXO/eQ==", + "dev": true, + "optional": true + } + } + }, + "tar-fs": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/tar-fs/-/tar-fs-3.0.4.tgz", + "integrity": "sha512-5AFQU8b9qLfZCX9zp2duONhPmZv0hGYiBPJsyUdqMjzq/mqVpy/rEUSeHk1+YitmxugaptgBh5oDGU3VsAJq4w==", + "dev": true, + "optional": true, + "requires": { + "mkdirp-classic": "^0.5.2", + "pump": "^3.0.0", + "tar-stream": "^3.1.5" + } + } } }, - "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 + "ws": { + "version": "8.13.0", + "resolved": "https://registry.npmjs.org/ws/-/ws-8.13.0.tgz", + "integrity": "sha512-x9vcZYTrFPC7aSIbj7sRCYo7L/Xb8Iy+pW0ng0wt2vCJv7M9HOMy0UoN3rr+IFC7hb7vXoqS+P9ktyLLLhO+LA==", + "dev": true, + "optional": true, + "requires": {} }, "yargs": { - "version": "16.2.0", - "resolved": "https://registry.npmjs.org/yargs/-/yargs-16.2.0.tgz", - "integrity": "sha512-D1mvvtDG0L5ft/jGWkLpG1+m0eQxOfaBvTNELraWj22wSVUMWxZUvYgJYcKh6jGGIkJFhH4IZPQhR4TKpc8mBw==", + "version": "17.7.1", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.7.1.tgz", + "integrity": "sha512-cwiTb08Xuv5fqF4AovYacTFNxk62th7LKJ6BL9IGUpTJrWoU7/7WdQGTP2SjKf1dUNBGzDd28p/Yfs/GI6JrLw==", "dev": true, + "optional": true, "requires": { - "cliui": "^7.0.2", + "cliui": "^8.0.1", "escalade": "^3.1.1", "get-caller-file": "^2.0.5", "require-directory": "^2.1.1", - "string-width": "^4.2.0", + "string-width": "^4.2.3", "y18n": "^5.0.5", - "yargs-parser": "^20.2.2" + "yargs-parser": "^21.1.1" } }, - "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 + "zip-stream": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/zip-stream/-/zip-stream-5.0.1.tgz", + "integrity": "sha512-UfZ0oa0C8LI58wJ+moL46BDIMgCQbnsb+2PoiJYtonhBsMh2bq1eRBVkvjfVsqbEHd9/EgKPUuL9saSSsec8OA==", + "dev": true, + "optional": true, + "requires": { + "archiver-utils": "^4.0.1", + "compress-commons": "^5.0.1", + "readable-stream": "^3.6.0" + } } } }, - "@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.5.3", - "resolved": "https://registry.npmjs.org/@wdio/repl/-/repl-7.5.3.tgz", - "integrity": "sha512-jfNJwNoc2nWdnLsFoGHmOJR9zaWfDTBMWM3W1eR5kXIjevD6gAfWsB5ZoA4IdybujCXxdnhlsm4o2jIzp/6f7A==", + "@wdio/local-runner": { + "version": "8.29.1", + "resolved": "https://registry.npmjs.org/@wdio/local-runner/-/local-runner-8.29.1.tgz", + "integrity": "sha512-Z3QAgxe1uQ97C7NS1CdMhgmHaLu/sbb47HTbw1yuuLk+SwsBIQGhNpTSA18QVRSUXq70G3bFvjACwqyap1IEQg==", "dev": true, "requires": { - "@wdio/utils": "7.5.3" + "@types/node": "^20.1.0", + "@wdio/logger": "8.28.0", + "@wdio/repl": "8.24.12", + "@wdio/runner": "8.29.1", + "@wdio/types": "8.29.1", + "async-exit-hook": "^2.0.1", + "split2": "^4.1.0", + "stream-buffers": "^3.0.2" } }, - "@wdio/reporter": { - "version": "7.5.7", - "resolved": "https://registry.npmjs.org/@wdio/reporter/-/reporter-7.5.7.tgz", - "integrity": "sha512-9PXqZtCXDtU6UYLNDPu9MZQ8BiABGnRlJTrlbYB3gBfZDibMkJMvwXzPderipBv2+ifDZXmGe3Njf1ao2TkbFA==", + "@wdio/logger": { + "version": "8.28.0", + "resolved": "https://registry.npmjs.org/@wdio/logger/-/logger-8.28.0.tgz", + "integrity": "sha512-/s6zNCqwy1hoc+K4SJypis0Ud0dlJ+urOelJFO1x0G0rwDRWyFiUP6ijTaCcFxAm29jYEcEPWijl2xkVIHwOyA==", "dev": true, "requires": { - "@wdio/types": "7.5.3", - "fs-extra": "^10.0.0" + "chalk": "^5.1.2", + "loglevel": "^1.6.0", + "loglevel-plugin-prefix": "^0.8.4", + "strip-ansi": "^7.1.0" }, "dependencies": { - "@wdio/types": { - "version": "7.5.3", - "resolved": "https://registry.npmjs.org/@wdio/types/-/types-7.5.3.tgz", - "integrity": "sha512-jmumhKBhNDABnpmrshYLEcdE9WoP5tmynsDNbDABlb/W8FFiLySQOejukhYIL9CLys4zXerV3/edks0SCzHOiQ==", + "ansi-regex": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.0.1.tgz", + "integrity": "sha512-n5M855fKb2SsfMIiFFoVrABHJC8QtHwVx+mHWP3QcEqBHYienj5dHSgjbxtC0WEZXYt4wcD6zrQElDPhFuZgfA==", + "dev": true + }, + "chalk": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-5.3.0.tgz", + "integrity": "sha512-dLitG79d+GV1Nb/VYcCDFivJeK1hiukt9QjRNVOsUtTy1rR1YJsmpGGTZ3qJos+uw7WmWF4wUwBd9jxjocFC2w==", + "dev": true + }, + "strip-ansi": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.0.tgz", + "integrity": "sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ==", "dev": true, "requires": { - "got": "^11.8.1" + "ansi-regex": "^6.0.1" } } } }, - "@wdio/runner": { - "version": "7.5.7", - "resolved": "https://registry.npmjs.org/@wdio/runner/-/runner-7.5.7.tgz", - "integrity": "sha512-RzVXd+xnwK/thkx1/xo9K5iscQ0Ofobgsx5dNVtwLDVMn9V7jCW/WX4dSCPAPaVSqnUCmkcQp3P5AoSBPpCZnQ==", + "@wdio/mocha-framework": { + "version": "8.29.1", + "resolved": "https://registry.npmjs.org/@wdio/mocha-framework/-/mocha-framework-8.29.1.tgz", + "integrity": "sha512-R9dKMNqWgtUvZo33ORjUQV8Z/WLX5h/pg9u/xIvZSGXuNSw1h+5DWF6UiNFscxBFblL9UvBi6V9ila2LHgE4ew==", "dev": true, "requires": { - "@wdio/config": "7.5.3", - "@wdio/logger": "7.5.3", - "@wdio/types": "7.5.3", - "@wdio/utils": "7.5.3", - "deepmerge": "^4.0.0", + "@types/mocha": "^10.0.0", + "@types/node": "^20.1.0", + "@wdio/logger": "8.28.0", + "@wdio/types": "8.29.1", + "@wdio/utils": "8.29.1", + "mocha": "^10.0.0" + } + }, + "@wdio/protocols": { + "version": "8.24.12", + "resolved": "https://registry.npmjs.org/@wdio/protocols/-/protocols-8.24.12.tgz", + "integrity": "sha512-QnVj3FkapmVD3h2zoZk+ZQ8gevSj9D9MiIQIy8eOnY4FAneYZ9R9GvoW+mgNcCZO8S8++S/jZHetR8n+8Q808g==", + "dev": true + }, + "@wdio/repl": { + "version": "8.24.12", + "resolved": "https://registry.npmjs.org/@wdio/repl/-/repl-8.24.12.tgz", + "integrity": "sha512-321F3sWafnlw93uRTSjEBVuvWCxTkWNDs7ektQS15drrroL3TMeFOynu4rDrIz0jXD9Vas0HCD2Tq/P0uxFLdw==", + "dev": true, + "requires": { + "@types/node": "^20.1.0" + } + }, + "@wdio/reporter": { + "version": "8.29.1", + "resolved": "https://registry.npmjs.org/@wdio/reporter/-/reporter-8.29.1.tgz", + "integrity": "sha512-LZeYHC+HHJRYiFH9odaotDazZh0zNhu4mTuL/T/e3c/Q3oPSQjLvfQYhB3Ece1QA9PKjP1VPmr+g9CvC0lMixA==", + "dev": true, + "requires": { + "@types/node": "^20.1.0", + "@wdio/logger": "8.28.0", + "@wdio/types": "8.29.1", + "diff": "^5.0.0", + "object-inspect": "^1.12.0" + } + }, + "@wdio/runner": { + "version": "8.29.1", + "resolved": "https://registry.npmjs.org/@wdio/runner/-/runner-8.29.1.tgz", + "integrity": "sha512-MvYFf4RgRmzxjAzy6nxvaDG1ycBRvoz772fT06csjxuaVYm57s8mlB8X+U1UQMx/IzujAb53fSeAmNcyU3FNEA==", + "dev": true, + "requires": { + "@types/node": "^20.1.0", + "@wdio/config": "8.29.1", + "@wdio/globals": "8.29.1", + "@wdio/logger": "8.28.0", + "@wdio/types": "8.29.1", + "@wdio/utils": "8.29.1", + "deepmerge-ts": "^5.0.0", + "expect-webdriverio": "^4.9.3", "gaze": "^1.1.2", - "webdriver": "7.5.3", - "webdriverio": "7.5.7" + "webdriver": "8.29.1", + "webdriverio": "8.29.1" }, "dependencies": { - "@types/aria-query": { - "version": "4.2.2", - "resolved": "https://registry.npmjs.org/@types/aria-query/-/aria-query-4.2.2.tgz", - "integrity": "sha512-HnYpAE1Y6kRyKM/XkEuiRQhTHvkzMBurTHnpFLYLBGPIylZNPs9jJcuOOYWxPLJCSEtmZT0Y8rHDokKN7rRTig==", - "dev": true - }, - "@wdio/logger": { - "version": "7.5.3", - "resolved": "https://registry.npmjs.org/@wdio/logger/-/logger-7.5.3.tgz", - "integrity": "sha512-r9EADpm0ncS1bDQSWi/nhF9C59/WNLbdAAFlo782E9ItFCpDhNit3aQP9vETv1vFxJRjUIM8Fw/HW8zwPadkbw==", + "@puppeteer/browsers": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/@puppeteer/browsers/-/browsers-1.3.0.tgz", + "integrity": "sha512-an3QdbNPkuU6qpxpbssxAbjRLJcF+eP4L8UqIY3+6n0sbaVxw5pz7PiCLy9g32XEZuoamUlV5ZQPnA6FxvkIHA==", "dev": true, + "optional": true, + "peer": true, "requires": { - "chalk": "^4.0.0", - "loglevel": "^1.6.0", - "loglevel-plugin-prefix": "^0.8.4", - "strip-ansi": "^6.0.0" + "debug": "4.3.4", + "extract-zip": "2.0.1", + "http-proxy-agent": "5.0.0", + "https-proxy-agent": "5.0.1", + "progress": "2.0.3", + "proxy-from-env": "1.1.0", + "tar-fs": "2.1.1", + "unbzip2-stream": "1.4.3", + "yargs": "17.7.1" } }, - "@wdio/types": { - "version": "7.5.3", - "resolved": "https://registry.npmjs.org/@wdio/types/-/types-7.5.3.tgz", - "integrity": "sha512-jmumhKBhNDABnpmrshYLEcdE9WoP5tmynsDNbDABlb/W8FFiLySQOejukhYIL9CLys4zXerV3/edks0SCzHOiQ==", + "@types/which": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/@types/which/-/which-2.0.2.tgz", + "integrity": "sha512-113D3mDkZDjo+EeUEHCFy0qniNc1ZpecGiAU7WSo7YDoSzolZIQKpYFHrPpjkB2nuyahcKfrmLXeQlh7gqJYdw==", "dev": true, - "requires": { - "got": "^11.8.1" - } + "optional": true, + "peer": true }, - "ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "archiver": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/archiver/-/archiver-6.0.1.tgz", + "integrity": "sha512-CXGy4poOLBKptiZH//VlWdFuUC1RESbdZjGjILwBuZ73P7WkAUN0htfSfBq/7k6FRFlpu7bg4JOkj1vU9G6jcQ==", "dev": true, "requires": { - "color-convert": "^2.0.1" + "archiver-utils": "^4.0.1", + "async": "^3.2.4", + "buffer-crc32": "^0.2.1", + "readable-stream": "^3.6.0", + "readdir-glob": "^1.1.2", + "tar-stream": "^3.0.0", + "zip-stream": "^5.0.1" } }, - "aria-query": { - "version": "4.2.2", - "resolved": "https://registry.npmjs.org/aria-query/-/aria-query-4.2.2.tgz", - "integrity": "sha512-o/HelwhuKpTj/frsOsbNLNgnNGVIFsVP/SW2BSF14gVl7kAfMOJ6/8wUAUvG1R1NHKrfG+2sHZTu0yauT1qBrA==", + "archiver-utils": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/archiver-utils/-/archiver-utils-4.0.1.tgz", + "integrity": "sha512-Q4Q99idbvzmgCTEAAhi32BkOyq8iVI5EwdO0PmBDSGIzzjYNdcFn7Q7k3OzbLy4kLUPXfJtG6fO2RjftXbobBg==", "dev": true, "requires": { - "@babel/runtime": "^7.10.2", - "@babel/runtime-corejs3": "^7.10.2" + "glob": "^8.0.0", + "graceful-fs": "^4.2.0", + "lazystream": "^1.0.0", + "lodash": "^4.17.15", + "normalize-path": "^3.0.0", + "readable-stream": "^3.6.0" } }, - "chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "async": { + "version": "3.2.5", + "resolved": "https://registry.npmjs.org/async/-/async-3.2.5.tgz", + "integrity": "sha512-baNZyqaaLhyLVKm/DlvdW051MSgO6b8eVfIezl9E5PqWxFgzLm/wQntEW4zOytVburDEr0JlALEpdOFwvErLsg==", + "dev": true + }, + "brace-expansion": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", + "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", "dev": true, "requires": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" + "balanced-match": "^1.0.0" } }, "chrome-launcher": { - "version": "0.13.4", - "resolved": "https://registry.npmjs.org/chrome-launcher/-/chrome-launcher-0.13.4.tgz", - "integrity": "sha512-nnzXiDbGKjDSK6t2I+35OAPBy5Pw/39bgkb/ZAFwMhwJbdYBp6aH+vW28ZgtjdU890Q7D+3wN/tB8N66q5Gi2A==", + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/chrome-launcher/-/chrome-launcher-1.1.0.tgz", + "integrity": "sha512-rJYWeEAERwWIr3c3mEVXwNiODPEdMRlRxHc47B1qHPOolHZnkj7rMv1QSUfPoG6MgatWj5AxSpnKKR4QEwEQIQ==", "dev": true, + "optional": true, + "peer": true, "requires": { "@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": "^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==", + "compress-commons": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/compress-commons/-/compress-commons-5.0.1.tgz", + "integrity": "sha512-MPh//1cERdLtqwO3pOFLeXtpuai0Y2WCd5AhtKxznqM7WtaMYaOEMSgn45d9D10sIHSfIKE603HlOp8OPGrvag==", "dev": true, "requires": { - "color-name": "~1.1.4" - } - }, - "color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", - "dev": true - }, - "devtools": { - "version": "7.5.7", - "resolved": "https://registry.npmjs.org/devtools/-/devtools-7.5.7.tgz", - "integrity": "sha512-+kqmvFbceElhYpN35yjm1T4Rz3VbH0QaqrNWKRpeyFp657Y5W0bm1s5FyMUeIv0aTNkAgWcETtqL+EG9X9uvjQ==", - "dev": true, - "requires": { - "@wdio/config": "7.5.3", - "@wdio/logger": "7.5.3", - "@wdio/protocols": "7.5.3", - "@wdio/types": "7.5.3", - "@wdio/utils": "7.5.3", - "chrome-launcher": "^0.13.1", - "edge-paths": "^2.1.0", - "puppeteer-core": "^9.1.0", - "query-selector-shadow-dom": "^1.0.0", - "ua-parser-js": "^0.7.21", - "uuid": "^8.0.0" + "crc-32": "^1.2.0", + "crc32-stream": "^5.0.0", + "normalize-path": "^3.0.0", + "readable-stream": "^3.6.0" } }, - "devtools-protocol": { - "version": "0.0.878340", - "resolved": "https://registry.npmjs.org/devtools-protocol/-/devtools-protocol-0.0.878340.tgz", - "integrity": "sha512-W0q8Y02r1RNwfZtI4Jjh1/MZxRHyrIgy9FvElbJzQelZjmNH197H4mBQs7DZjlUUDA9s6Zz2jl+zUYFgLgEnzw==", - "dev": true - }, - "has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", - "dev": true - }, - "mkdirp": { - "version": "0.5.6", - "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.6.tgz", - "integrity": "sha512-FP+p8RB8OWpF3YZBCrP5gtADmtXApB5AMLn+vdyA+PyxCjrCs00mjyUozssO33cwDeT3wNGdLxJ5M//YqtHAJw==", + "crc32-stream": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/crc32-stream/-/crc32-stream-5.0.0.tgz", + "integrity": "sha512-B0EPa1UK+qnpBZpG+7FgPCu0J2ETLpXq09o9BkLkEAhdB6Z61Qo4pJ3JYu0c+Qi+/SAL7QThqnzS06pmSSyZaw==", "dev": true, "requires": { - "minimist": "^1.2.6" + "crc-32": "^1.2.0", + "readable-stream": "^3.4.0" } }, - "puppeteer-core": { - "version": "9.1.1", - "resolved": "https://registry.npmjs.org/puppeteer-core/-/puppeteer-core-9.1.1.tgz", - "integrity": "sha512-zbedbitVIGhmgz0nt7eIdLsnaoVZSlNJfBivqm2w67T8LR2bU1dvnruDZ8nQO0zn++Iet7zHbAOdnuS5+H2E7A==", - "dev": true, - "requires": { - "debug": "^4.1.0", - "devtools-protocol": "0.0.869402", - "extract-zip": "^2.0.0", - "https-proxy-agent": "^5.0.0", - "node-fetch": "^2.6.1", - "pkg-dir": "^4.2.0", - "progress": "^2.0.1", - "proxy-from-env": "^1.1.0", - "rimraf": "^3.0.2", - "tar-fs": "^2.0.0", - "unbzip2-stream": "^1.3.3", - "ws": "^7.2.3" - }, - "dependencies": { - "devtools-protocol": { - "version": "0.0.869402", - "resolved": "https://registry.npmjs.org/devtools-protocol/-/devtools-protocol-0.0.869402.tgz", - "integrity": "sha512-VvlVYY+VDJe639yHs5PHISzdWTLL3Aw8rO4cvUtwvoxFd6FHbE4OpHHcde52M6096uYYazAmd4l0o5VuFRO2WA==", - "dev": true - } - } - }, - "supports-color": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "cross-fetch": { + "version": "3.1.6", + "resolved": "https://registry.npmjs.org/cross-fetch/-/cross-fetch-3.1.6.tgz", + "integrity": "sha512-riRvo06crlE8HiqOwIpQhxwdOk4fOeR7FVM/wXoxchFEqMNUjvbs3bfo4OTgMEMHzppd4DxFBDbyySj8Cv781g==", "dev": true, + "optional": true, + "peer": true, "requires": { - "has-flag": "^4.0.0" + "node-fetch": "^2.6.11" } }, - "uuid": { - "version": "8.3.2", - "resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz", - "integrity": "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==", - "dev": true - }, - "webdriverio": { - "version": "7.5.7", - "resolved": "https://registry.npmjs.org/webdriverio/-/webdriverio-7.5.7.tgz", - "integrity": "sha512-TLluVPLo6Snn/dxEITvMz7ZuklN4qZOBddDuLb9LO3rhsfKDMNbnhcBk0SLdFsWny0aCuhWNpJ6co93702XC0A==", - "dev": true, - "requires": { - "@types/aria-query": "^4.2.1", - "@wdio/config": "7.5.3", - "@wdio/logger": "7.5.3", - "@wdio/protocols": "7.5.3", - "@wdio/repl": "7.5.3", - "@wdio/types": "7.5.3", - "@wdio/utils": "7.5.3", - "archiver": "^5.0.0", - "aria-query": "^4.2.2", - "atob": "^2.1.2", - "css-shorthand-properties": "^1.1.1", - "css-value": "^0.0.1", - "devtools": "7.5.7", - "devtools-protocol": "^0.0.878340", - "fs-extra": "^10.0.0", - "get-port": "^5.1.1", - "grapheme-splitter": "^1.0.2", - "lodash.clonedeep": "^4.5.0", - "lodash.isobject": "^3.0.2", - "lodash.isplainobject": "^4.0.6", - "lodash.zip": "^4.2.0", - "minimatch": "^3.0.4", - "puppeteer-core": "^9.1.0", + "devtools": { + "version": "8.29.1", + "resolved": "https://registry.npmjs.org/devtools/-/devtools-8.29.1.tgz", + "integrity": "sha512-fbH0Z7CPK4OZSgUw2QcAppczowxtSyvFztPUmiFyi99cUadjEOwlg0aL3pBVlIDo67olYjGb8GD1M5Z4yI/P6w==", + "dev": true, + "optional": true, + "peer": true, + "requires": { + "@types/node": "^20.1.0", + "@wdio/config": "8.29.1", + "@wdio/logger": "8.28.0", + "@wdio/protocols": "8.24.12", + "@wdio/types": "8.29.1", + "@wdio/utils": "8.29.1", + "chrome-launcher": "^1.0.0", + "edge-paths": "^3.0.5", + "import-meta-resolve": "^4.0.0", + "puppeteer-core": "20.3.0", "query-selector-shadow-dom": "^1.0.0", - "resq": "^1.9.1", - "rgb2hex": "0.2.5", - "serialize-error": "^8.0.0", - "webdriver": "7.5.3" + "ua-parser-js": "^1.0.1", + "uuid": "^9.0.0", + "which": "^4.0.0" + }, + "dependencies": { + "which": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/which/-/which-4.0.0.tgz", + "integrity": "sha512-GlaYyEb07DPxYCKhKzplCWBJtvxZcZMrL+4UkrTSJHHPyZU4mYYTv3qaOe77H7EODLSSopAUFAc6W8U4yqvscg==", + "dev": true, + "optional": true, + "peer": true, + "requires": { + "isexe": "^3.1.1" + } + } } }, - "ws": { - "version": "7.5.9", - "resolved": "https://registry.npmjs.org/ws/-/ws-7.5.9.tgz", - "integrity": "sha512-F+P9Jil7UiSKSkppIiD94dN07AwvFixvLIj1Og1Rl9GGMuNipJnV9JzjD6XuqmAeiswGvUmNLjr5cFuXwNS77Q==", - "dev": true, - "requires": {} - } - } - }, - "@wdio/spec-reporter": { - "version": "7.19.7", - "resolved": "https://registry.npmjs.org/@wdio/spec-reporter/-/spec-reporter-7.19.7.tgz", - "integrity": "sha512-BDBZU2EK/GuC9VxtfqPtoW43FmvKxYDsvcDVDi3F7o+9fkcuGSJiWbw1AX251ZzzVQ7YP9ImTitSpdpUKXkilQ==", - "dev": true, - "requires": { - "@types/easy-table": "^0.0.33", - "@wdio/reporter": "7.19.7", - "@wdio/types": "7.19.5", - "chalk": "^4.0.0", - "easy-table": "^1.1.1", - "pretty-ms": "^7.0.0" - }, - "dependencies": { - "@wdio/reporter": { - "version": "7.19.7", - "resolved": "https://registry.npmjs.org/@wdio/reporter/-/reporter-7.19.7.tgz", - "integrity": "sha512-Dum19gpfru66FnIq78/4HTuW87B7ceLDp6PJXwQM5kXyN7Gb7zhMgp6FZTM0FCYLyi6U/zXZSvpNUYl77caS6g==", + "devtools-protocol": { + "version": "0.0.1120988", + "resolved": "https://registry.npmjs.org/devtools-protocol/-/devtools-protocol-0.0.1120988.tgz", + "integrity": "sha512-39fCpE3Z78IaIPChJsP6Lhmkbf4dWXOmzLk/KFTdRkNk/0JymRIfUynDVRndV9HoDz8PyalK1UH21ST/ivwW5Q==", "dev": true, - "requires": { - "@types/diff": "^5.0.0", - "@types/node": "^17.0.4", - "@types/object-inspect": "^1.8.0", - "@types/supports-color": "^8.1.0", - "@types/tmp": "^0.2.0", - "@wdio/types": "7.19.5", - "diff": "^5.0.0", - "fs-extra": "^10.0.0", - "object-inspect": "^1.10.3", - "supports-color": "8.1.1" - } + "optional": true, + "peer": true }, - "@wdio/types": { - "version": "7.19.5", - "resolved": "https://registry.npmjs.org/@wdio/types/-/types-7.19.5.tgz", - "integrity": "sha512-S1lC0pmtEO7NVH/2nM1c7NHbkgxLZH3VVG/z6ym3Bbxdtcqi2LMsEvvawMAU/fmhyiIkMsGZCO8vxG9cRw4z4A==", + "edge-paths": { + "version": "3.0.5", + "resolved": "https://registry.npmjs.org/edge-paths/-/edge-paths-3.0.5.tgz", + "integrity": "sha512-sB7vSrDnFa4ezWQk9nZ/n0FdpdUuC6R1EOrlU3DL+bovcNFK28rqu2emmAUjujYEJTWIgQGqgVVWUZXMnc8iWg==", "dev": true, + "optional": true, + "peer": true, "requires": { - "@types/node": "^17.0.4", - "got": "^11.8.1" + "@types/which": "^2.0.1", + "which": "^2.0.2" } }, - "ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "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, - "requires": { - "color-convert": "^2.0.1" - } + "optional": true, + "peer": true }, - "chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "glob": { + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/glob/-/glob-8.1.0.tgz", + "integrity": "sha512-r8hpEjiQEYlF2QU0df3dS+nxxSIreXQS1qRhMJM0Q5NDdR386C7jb7Hwwod8Fgiuex+k0GFjgft18yvxm5XoCQ==", "dev": true, "requires": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^5.0.1", + "once": "^1.3.0" }, "dependencies": { - "supports-color": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "minimatch": { + "version": "5.1.6", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-5.1.6.tgz", + "integrity": "sha512-lKwV/1brpG6mBUFHtb7NUmtABCb2WZZmm2wNiOA5hAb8VdCS4B3dtMWyvcoViccwAW/COERjXLt0zP1zXUN26g==", "dev": true, "requires": { - "has-flag": "^4.0.0" + "brace-expansion": "^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==", + "http-proxy-agent": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/http-proxy-agent/-/http-proxy-agent-5.0.0.tgz", + "integrity": "sha512-n2hY8YdoRE1i7r6M0w9DIw5GgZN0G25P8zLCRQ8rjXtTU3vsNFBI/vWK/UIeE6g5MUUz6avwAPXmL6Fy9D/90w==", "dev": true, + "optional": true, + "peer": true, "requires": { - "color-name": "~1.1.4" + "@tootallnate/once": "2", + "agent-base": "6", + "debug": "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 + "isexe": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/isexe/-/isexe-3.1.1.tgz", + "integrity": "sha512-LpB/54B+/2J5hqQ7imZHfdU31OlgQqx7ZicVlkm9kzg9/w8GKLEcFfJl/t7DCEDueOyBAD6zCCwTO6Fzs0NoEQ==", + "dev": true, + "optional": true, + "peer": true }, - "supports-color": { - "version": "8.1.1", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz", - "integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==", + "lighthouse-logger": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/lighthouse-logger/-/lighthouse-logger-2.0.1.tgz", + "integrity": "sha512-ioBrW3s2i97noEmnXxmUq7cjIcVRjT5HBpAYy8zE11CxU9HqlWHHeRxfeN1tn8F7OEMVPIC9x1f8t3Z7US9ehQ==", "dev": true, + "optional": true, + "peer": true, "requires": { - "has-flag": "^4.0.0" + "debug": "^2.6.9", + "marky": "^1.2.2" + }, + "dependencies": { + "debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "dev": true, + "optional": true, + "peer": true, + "requires": { + "ms": "2.0.0" + } + } } - } - } - }, - "@wdio/sync": { - "version": "7.5.7", - "resolved": "https://registry.npmjs.org/@wdio/sync/-/sync-7.5.7.tgz", - "integrity": "sha512-Zu/AYLjwqbFSbaOU1US7ownv3ov8JrtoGHq51JfJ4masefJDXNkHix2cZ0qEgl3IvkkWQ0ewL0G8GTXb3KOemA==", - "dev": true, - "requires": { - "@types/fibers": "^3.1.0", - "@types/puppeteer": "^5.4.0", - "@wdio/logger": "7.5.3", - "@wdio/types": "7.5.3", - "fibers": "^5.0.0", - "webdriverio": "7.5.7" - }, - "dependencies": { - "@types/aria-query": { - "version": "4.2.2", - "resolved": "https://registry.npmjs.org/@types/aria-query/-/aria-query-4.2.2.tgz", - "integrity": "sha512-HnYpAE1Y6kRyKM/XkEuiRQhTHvkzMBurTHnpFLYLBGPIylZNPs9jJcuOOYWxPLJCSEtmZT0Y8rHDokKN7rRTig==", + }, + "lru-cache": { + "version": "7.18.3", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-7.18.3.tgz", + "integrity": "sha512-jumlc0BIUrS3qJGgIkWZsyfAM7NCWiBcCDhnd+3NNM5KbBmLTgHVfWBcg6W+rLUsIpzpERPsvwUP7CckAQSOoA==", "dev": true }, - "@wdio/logger": { - "version": "7.5.3", - "resolved": "https://registry.npmjs.org/@wdio/logger/-/logger-7.5.3.tgz", - "integrity": "sha512-r9EADpm0ncS1bDQSWi/nhF9C59/WNLbdAAFlo782E9ItFCpDhNit3aQP9vETv1vFxJRjUIM8Fw/HW8zwPadkbw==", + "minimatch": { + "version": "9.0.3", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.3.tgz", + "integrity": "sha512-RHiac9mvaRw0x3AYRgDC1CxAP7HTcNrrECeA8YYJeWnpo+2Q5CegtZjaotWTWxDG3UeGA1coE05iH1mPjT/2mg==", "dev": true, "requires": { - "chalk": "^4.0.0", - "loglevel": "^1.6.0", - "loglevel-plugin-prefix": "^0.8.4", - "strip-ansi": "^6.0.0" + "brace-expansion": "^2.0.1" } }, - "@wdio/types": { - "version": "7.5.3", - "resolved": "https://registry.npmjs.org/@wdio/types/-/types-7.5.3.tgz", - "integrity": "sha512-jmumhKBhNDABnpmrshYLEcdE9WoP5tmynsDNbDABlb/W8FFiLySQOejukhYIL9CLys4zXerV3/edks0SCzHOiQ==", + "ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", "dev": true, - "requires": { - "got": "^11.8.1" - } + "optional": true, + "peer": true }, - "ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "node-fetch": { + "version": "2.7.0", + "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.7.0.tgz", + "integrity": "sha512-c4FRfUm/dbcWZ7U+1Wq0AwCyFL+3nt2bEw05wfxSz+DWpWsitgmSgYmy2dQdWyKC1694ELPqMs/YzUSNozLt8A==", "dev": true, "requires": { - "color-convert": "^2.0.1" + "whatwg-url": "^5.0.0" } }, - "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==", + "proxy-agent": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/proxy-agent/-/proxy-agent-6.3.0.tgz", + "integrity": "sha512-0LdR757eTj/JfuU7TL2YCuAZnxWXu3tkJbg4Oq3geW/qFNT/32T0sp2HnZ9O0lMR4q3vwAt0+xCA8SR0WAD0og==", "dev": true, "requires": { - "@babel/runtime": "^7.10.2", - "@babel/runtime-corejs3": "^7.10.2" + "agent-base": "^7.0.2", + "debug": "^4.3.4", + "http-proxy-agent": "^7.0.0", + "https-proxy-agent": "^7.0.0", + "lru-cache": "^7.14.1", + "pac-proxy-agent": "^7.0.0", + "proxy-from-env": "^1.1.0", + "socks-proxy-agent": "^8.0.1" + }, + "dependencies": { + "agent-base": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-7.1.0.tgz", + "integrity": "sha512-o/zjMZRhJxny7OyEF+Op8X+efiELC7k7yOjMzgfzVqOzXqkBkWI79YoTdOtsuWd5BWhAGAuOY/Xa6xpiaWXiNg==", + "dev": true, + "requires": { + "debug": "^4.3.4" + } + }, + "http-proxy-agent": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/http-proxy-agent/-/http-proxy-agent-7.0.0.tgz", + "integrity": "sha512-+ZT+iBxVUQ1asugqnD6oWoRiS25AkjNfG085dKJGtGxkdwLQrMKU5wJr2bOOFAXzKcTuqq+7fZlTMgG3SRfIYQ==", + "dev": true, + "requires": { + "agent-base": "^7.1.0", + "debug": "^4.3.4" + } + }, + "https-proxy-agent": { + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-7.0.2.tgz", + "integrity": "sha512-NmLNjm6ucYwtcUmL7JQC1ZQ57LmHP4lT15FQ8D61nak1rO6DH+fz5qNK2Ap5UN4ZapYICE3/0KodcLYSPsPbaA==", + "dev": true, + "requires": { + "agent-base": "^7.0.2", + "debug": "4" + } + } } }, - "chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "puppeteer-core": { + "version": "20.3.0", + "resolved": "https://registry.npmjs.org/puppeteer-core/-/puppeteer-core-20.3.0.tgz", + "integrity": "sha512-264pBrIui5bO6NJeOcbJrLa0OCwmA4+WK00JMrLIKTfRiqe2gx8KWTzLsjyw/bizErp3TKS7vt/I0i5fTC+mAw==", "dev": true, + "optional": true, + "peer": true, "requires": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" + "@puppeteer/browsers": "1.3.0", + "chromium-bidi": "0.4.9", + "cross-fetch": "3.1.6", + "debug": "4.3.4", + "devtools-protocol": "0.0.1120988", + "ws": "8.13.0" } }, - "chrome-launcher": { - "version": "0.13.4", - "resolved": "https://registry.npmjs.org/chrome-launcher/-/chrome-launcher-0.13.4.tgz", - "integrity": "sha512-nnzXiDbGKjDSK6t2I+35OAPBy5Pw/39bgkb/ZAFwMhwJbdYBp6aH+vW28ZgtjdU890Q7D+3wN/tB8N66q5Gi2A==", + "readable-stream": { + "version": "3.6.2", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz", + "integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==", "dev": true, "requires": { - "@types/node": "*", - "escape-string-regexp": "^1.0.5", - "is-wsl": "^2.2.0", - "lighthouse-logger": "^1.0.0", - "mkdirp": "^0.5.3", - "rimraf": "^3.0.2" + "inherits": "^2.0.3", + "string_decoder": "^1.1.1", + "util-deprecate": "^1.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==", + "serialize-error": { + "version": "11.0.3", + "resolved": "https://registry.npmjs.org/serialize-error/-/serialize-error-11.0.3.tgz", + "integrity": "sha512-2G2y++21dhj2R7iHAdd0FIzjGwuKZld+7Pl/bTU6YIkrC2ZMbVUjm+luj6A6V34Rv9XfKJDKpTWu9W4Gse1D9g==", "dev": true, "requires": { - "color-name": "~1.1.4" - } - }, - "color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", - "dev": true - }, - "devtools": { - "version": "7.5.7", - "resolved": "https://registry.npmjs.org/devtools/-/devtools-7.5.7.tgz", - "integrity": "sha512-+kqmvFbceElhYpN35yjm1T4Rz3VbH0QaqrNWKRpeyFp657Y5W0bm1s5FyMUeIv0aTNkAgWcETtqL+EG9X9uvjQ==", - "dev": true, - "requires": { - "@wdio/config": "7.5.3", - "@wdio/logger": "7.5.3", - "@wdio/protocols": "7.5.3", - "@wdio/types": "7.5.3", - "@wdio/utils": "7.5.3", - "chrome-launcher": "^0.13.1", - "edge-paths": "^2.1.0", - "puppeteer-core": "^9.1.0", - "query-selector-shadow-dom": "^1.0.0", - "ua-parser-js": "^0.7.21", - "uuid": "^8.0.0" + "type-fest": "^2.12.2" } }, - "devtools-protocol": { - "version": "0.0.878340", - "resolved": "https://registry.npmjs.org/devtools-protocol/-/devtools-protocol-0.0.878340.tgz", - "integrity": "sha512-W0q8Y02r1RNwfZtI4Jjh1/MZxRHyrIgy9FvElbJzQelZjmNH197H4mBQs7DZjlUUDA9s6Zz2jl+zUYFgLgEnzw==", - "dev": true - }, - "has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", - "dev": true - }, - "mkdirp": { - "version": "0.5.6", - "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.6.tgz", - "integrity": "sha512-FP+p8RB8OWpF3YZBCrP5gtADmtXApB5AMLn+vdyA+PyxCjrCs00mjyUozssO33cwDeT3wNGdLxJ5M//YqtHAJw==", + "tar-stream": { + "version": "3.1.7", + "resolved": "https://registry.npmjs.org/tar-stream/-/tar-stream-3.1.7.tgz", + "integrity": "sha512-qJj60CXt7IU1Ffyc3NJMjh6EkuCFej46zUqJ4J7pqYlThyd9bO0XBTmcOIhSzZJVWfsLks0+nle/j538YAW9RQ==", "dev": true, "requires": { - "minimist": "^1.2.6" + "b4a": "^1.6.4", + "fast-fifo": "^1.2.0", + "streamx": "^2.15.0" } }, - "puppeteer-core": { - "version": "9.1.1", - "resolved": "https://registry.npmjs.org/puppeteer-core/-/puppeteer-core-9.1.1.tgz", - "integrity": "sha512-zbedbitVIGhmgz0nt7eIdLsnaoVZSlNJfBivqm2w67T8LR2bU1dvnruDZ8nQO0zn++Iet7zHbAOdnuS5+H2E7A==", - "dev": true, - "requires": { - "debug": "^4.1.0", - "devtools-protocol": "0.0.869402", - "extract-zip": "^2.0.0", - "https-proxy-agent": "^5.0.0", - "node-fetch": "^2.6.1", - "pkg-dir": "^4.2.0", - "progress": "^2.0.1", - "proxy-from-env": "^1.1.0", - "rimraf": "^3.0.2", - "tar-fs": "^2.0.0", - "unbzip2-stream": "^1.3.3", - "ws": "^7.2.3" - }, - "dependencies": { - "devtools-protocol": { - "version": "0.0.869402", - "resolved": "https://registry.npmjs.org/devtools-protocol/-/devtools-protocol-0.0.869402.tgz", - "integrity": "sha512-VvlVYY+VDJe639yHs5PHISzdWTLL3Aw8rO4cvUtwvoxFd6FHbE4OpHHcde52M6096uYYazAmd4l0o5VuFRO2WA==", - "dev": true - } - } + "type-fest": { + "version": "2.19.0", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-2.19.0.tgz", + "integrity": "sha512-RAH822pAdBgcNMAfWnCBU3CFZcfZ/i1eZjwFU/dsLKumyuuP3niueg2UAukXYF0E2AAoc82ZSSf9J0WQBinzHA==", + "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==", + "ua-parser-js": { + "version": "1.0.37", + "resolved": "https://registry.npmjs.org/ua-parser-js/-/ua-parser-js-1.0.37.tgz", + "integrity": "sha512-bhTyI94tZofjo+Dn8SN6Zv8nBDvyXTymAdM3LDI/0IboIUwTu1rEhW7v2TfiVsoYWgkQ4kOVqnI8APUFbIQIFQ==", "dev": true, - "requires": { - "has-flag": "^4.0.0" - } + "optional": true, + "peer": true }, "uuid": { - "version": "8.3.2", - "resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz", - "integrity": "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==", - "dev": true + "version": "9.0.1", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-9.0.1.tgz", + "integrity": "sha512-b+1eJOlsR9K8HJpow9Ok3fiWOWSIcIzXodvv0rQjVoOVNpWMpxf1wZNpt4y9h10odCNrqnYp1OBzRktckBe3sA==", + "dev": true, + "optional": true, + "peer": true }, "webdriverio": { - "version": "7.5.7", - "resolved": "https://registry.npmjs.org/webdriverio/-/webdriverio-7.5.7.tgz", - "integrity": "sha512-TLluVPLo6Snn/dxEITvMz7ZuklN4qZOBddDuLb9LO3rhsfKDMNbnhcBk0SLdFsWny0aCuhWNpJ6co93702XC0A==", - "dev": true, - "requires": { - "@types/aria-query": "^4.2.1", - "@wdio/config": "7.5.3", - "@wdio/logger": "7.5.3", - "@wdio/protocols": "7.5.3", - "@wdio/repl": "7.5.3", - "@wdio/types": "7.5.3", - "@wdio/utils": "7.5.3", - "archiver": "^5.0.0", - "aria-query": "^4.2.2", - "atob": "^2.1.2", + "version": "8.29.1", + "resolved": "https://registry.npmjs.org/webdriverio/-/webdriverio-8.29.1.tgz", + "integrity": "sha512-NZK95ivXCqdPraB3FHMw6ByxnCvtgFXkjzG2l3Oq5z0IuJS2aMow3AKFIyiuG6is/deGCe+Tb8eFTCqak7UV+w==", + "dev": true, + "requires": { + "@types/node": "^20.1.0", + "@wdio/config": "8.29.1", + "@wdio/logger": "8.28.0", + "@wdio/protocols": "8.24.12", + "@wdio/repl": "8.24.12", + "@wdio/types": "8.29.1", + "@wdio/utils": "8.29.1", + "archiver": "^6.0.0", + "aria-query": "^5.0.0", "css-shorthand-properties": "^1.1.1", "css-value": "^0.0.1", - "devtools": "7.5.7", - "devtools-protocol": "^0.0.878340", - "fs-extra": "^10.0.0", - "get-port": "^5.1.1", + "devtools-protocol": "^0.0.1249869", "grapheme-splitter": "^1.0.2", + "import-meta-resolve": "^4.0.0", + "is-plain-obj": "^4.1.0", "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", + "minimatch": "^9.0.0", + "puppeteer-core": "^20.9.0", "query-selector-shadow-dom": "^1.0.0", "resq": "^1.9.1", "rgb2hex": "0.2.5", - "serialize-error": "^8.0.0", - "webdriver": "7.5.3" + "serialize-error": "^11.0.1", + "webdriver": "8.29.1" + }, + "dependencies": { + "@puppeteer/browsers": { + "version": "1.4.6", + "resolved": "https://registry.npmjs.org/@puppeteer/browsers/-/browsers-1.4.6.tgz", + "integrity": "sha512-x4BEjr2SjOPowNeiguzjozQbsc6h437ovD/wu+JpaenxVLm3jkgzHY2xOslMTp50HoTvQreMjiexiGQw1sqZlQ==", + "dev": true, + "requires": { + "debug": "4.3.4", + "extract-zip": "2.0.1", + "progress": "2.0.3", + "proxy-agent": "6.3.0", + "tar-fs": "3.0.4", + "unbzip2-stream": "1.4.3", + "yargs": "17.7.1" + } + }, + "chromium-bidi": { + "version": "0.4.16", + "resolved": "https://registry.npmjs.org/chromium-bidi/-/chromium-bidi-0.4.16.tgz", + "integrity": "sha512-7ZbXdWERxRxSwo3txsBjjmc/NLxqb1Bk30mRb0BMS4YIaiV6zvKZqL/UAH+DdqcDYayDWk2n/y8klkBDODrPvA==", + "dev": true, + "requires": { + "mitt": "3.0.0" + } + }, + "cross-fetch": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/cross-fetch/-/cross-fetch-4.0.0.tgz", + "integrity": "sha512-e4a5N8lVvuLgAWgnCrLr2PP0YyDOTHa9H/Rj54dirp61qXnNq46m82bRhNqIA5VccJtWBvPTFRV3TtvHUKPB1g==", + "dev": true, + "requires": { + "node-fetch": "^2.6.12" + } + }, + "devtools-protocol": { + "version": "0.0.1249869", + "resolved": "https://registry.npmjs.org/devtools-protocol/-/devtools-protocol-0.0.1249869.tgz", + "integrity": "sha512-Ctp4hInA0BEavlUoRy9mhGq0i+JSo/AwVyX2EFgZmV1kYB+Zq+EMBAn52QWu6FbRr10hRb6pBl420upbp4++vg==", + "dev": true + }, + "puppeteer-core": { + "version": "20.9.0", + "resolved": "https://registry.npmjs.org/puppeteer-core/-/puppeteer-core-20.9.0.tgz", + "integrity": "sha512-H9fYZQzMTRrkboEfPmf7m3CLDN6JvbxXA3qTtS+dFt27tR+CsFHzPsT6pzp6lYL6bJbAPaR0HaPO6uSi+F94Pg==", + "dev": true, + "requires": { + "@puppeteer/browsers": "1.4.6", + "chromium-bidi": "0.4.16", + "cross-fetch": "4.0.0", + "debug": "4.3.4", + "devtools-protocol": "0.0.1147663", + "ws": "8.13.0" + }, + "dependencies": { + "devtools-protocol": { + "version": "0.0.1147663", + "resolved": "https://registry.npmjs.org/devtools-protocol/-/devtools-protocol-0.0.1147663.tgz", + "integrity": "sha512-hyWmRrexdhbZ1tcJUGpO95ivbRhWXz++F4Ko+n21AY5PNln2ovoJw+8ZMNDTtip+CNFQfrtLVh/w4009dXO/eQ==", + "dev": true + } + } + }, + "tar-fs": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/tar-fs/-/tar-fs-3.0.4.tgz", + "integrity": "sha512-5AFQU8b9qLfZCX9zp2duONhPmZv0hGYiBPJsyUdqMjzq/mqVpy/rEUSeHk1+YitmxugaptgBh5oDGU3VsAJq4w==", + "dev": true, + "requires": { + "mkdirp-classic": "^0.5.2", + "pump": "^3.0.0", + "tar-stream": "^3.1.5" + } + } } }, "ws": { - "version": "7.5.9", - "resolved": "https://registry.npmjs.org/ws/-/ws-7.5.9.tgz", - "integrity": "sha512-F+P9Jil7UiSKSkppIiD94dN07AwvFixvLIj1Og1Rl9GGMuNipJnV9JzjD6XuqmAeiswGvUmNLjr5cFuXwNS77Q==", + "version": "8.13.0", + "resolved": "https://registry.npmjs.org/ws/-/ws-8.13.0.tgz", + "integrity": "sha512-x9vcZYTrFPC7aSIbj7sRCYo7L/Xb8Iy+pW0ng0wt2vCJv7M9HOMy0UoN3rr+IFC7hb7vXoqS+P9ktyLLLhO+LA==", "dev": true, "requires": {} + }, + "yargs": { + "version": "17.7.1", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.7.1.tgz", + "integrity": "sha512-cwiTb08Xuv5fqF4AovYacTFNxk62th7LKJ6BL9IGUpTJrWoU7/7WdQGTP2SjKf1dUNBGzDd28p/Yfs/GI6JrLw==", + "dev": true, + "requires": { + "cliui": "^8.0.1", + "escalade": "^3.1.1", + "get-caller-file": "^2.0.5", + "require-directory": "^2.1.1", + "string-width": "^4.2.3", + "y18n": "^5.0.5", + "yargs-parser": "^21.1.1" + } + }, + "zip-stream": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/zip-stream/-/zip-stream-5.0.1.tgz", + "integrity": "sha512-UfZ0oa0C8LI58wJ+moL46BDIMgCQbnsb+2PoiJYtonhBsMh2bq1eRBVkvjfVsqbEHd9/EgKPUuL9saSSsec8OA==", + "dev": true, + "requires": { + "archiver-utils": "^4.0.1", + "compress-commons": "^5.0.1", + "readable-stream": "^3.6.0" + } } } }, - "@wdio/types": { - "version": "7.16.14", - "resolved": "https://registry.npmjs.org/@wdio/types/-/types-7.16.14.tgz", - "integrity": "sha512-AyNI9iBSos9xWBmiFAF3sBs6AJXO/55VppU/eeF4HRdbZMtMarnvMuahM+jlUrA3vJSmDW+ufelG0MT//6vrnw==", + "@wdio/spec-reporter": { + "version": "8.29.1", + "resolved": "https://registry.npmjs.org/@wdio/spec-reporter/-/spec-reporter-8.29.1.tgz", + "integrity": "sha512-tuDHihrTjCxFCbSjT0jMvAarLA1MtatnCnhv0vguu3ZWXELR1uESX2KzBmpJ+chGZz3oCcKszT8HOr6Pg2a1QA==", "dev": true, "requires": { - "@types/node": "^17.0.4", - "got": "^11.8.1" + "@wdio/reporter": "8.29.1", + "@wdio/types": "8.29.1", + "chalk": "^5.1.2", + "easy-table": "^1.2.0", + "pretty-ms": "^7.0.0" + }, + "dependencies": { + "chalk": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-5.3.0.tgz", + "integrity": "sha512-dLitG79d+GV1Nb/VYcCDFivJeK1hiukt9QjRNVOsUtTy1rR1YJsmpGGTZ3qJos+uw7WmWF4wUwBd9jxjocFC2w==", + "dev": true + } } }, - "@wdio/utils": { - "version": "7.5.3", - "resolved": "https://registry.npmjs.org/@wdio/utils/-/utils-7.5.3.tgz", - "integrity": "sha512-nlLDKr8v8abLOHCKroBwQkGPdCIxjID2MllgWX23xqkYZylM9RdwPBdL8osQt9m3rq2TxiPAT4OlbzNt2WtN6Q==", + "@wdio/types": { + "version": "8.29.1", + "resolved": "https://registry.npmjs.org/@wdio/types/-/types-8.29.1.tgz", + "integrity": "sha512-rZYzu+sK8zY1PjCEWxNu4ELJPYKDZRn7HFcYNgR122ylHygfldwkb5TioI6Pn311hQH/S+663KEeoq//Jb0f8A==", "dev": true, "requires": { - "@wdio/logger": "7.5.3", - "@wdio/types": "7.5.3" + "@types/node": "^20.1.0" + } + }, + "@wdio/utils": { + "version": "8.29.1", + "resolved": "https://registry.npmjs.org/@wdio/utils/-/utils-8.29.1.tgz", + "integrity": "sha512-Dm91DKL/ZKeZ2QogWT8Twv0p+slEgKyB/5x9/kcCG0Q2nNa+tZedTjOhryzrsPiWc+jTSBmjGE4katRXpJRFJg==", + "dev": true, + "requires": { + "@puppeteer/browsers": "^1.6.0", + "@wdio/logger": "8.28.0", + "@wdio/types": "8.29.1", + "decamelize": "^6.0.0", + "deepmerge-ts": "^5.1.0", + "edgedriver": "^5.3.5", + "geckodriver": "^4.2.0", + "get-port": "^7.0.0", + "import-meta-resolve": "^4.0.0", + "locate-app": "^2.1.0", + "safaridriver": "^0.1.0", + "split2": "^4.2.0", + "wait-port": "^1.0.4" }, "dependencies": { - "@wdio/logger": { - "version": "7.5.3", - "resolved": "https://registry.npmjs.org/@wdio/logger/-/logger-7.5.3.tgz", - "integrity": "sha512-r9EADpm0ncS1bDQSWi/nhF9C59/WNLbdAAFlo782E9ItFCpDhNit3aQP9vETv1vFxJRjUIM8Fw/HW8zwPadkbw==", - "dev": true, - "requires": { - "chalk": "^4.0.0", - "loglevel": "^1.6.0", - "loglevel-plugin-prefix": "^0.8.4", - "strip-ansi": "^6.0.0" - } - }, - "@wdio/types": { - "version": "7.5.3", - "resolved": "https://registry.npmjs.org/@wdio/types/-/types-7.5.3.tgz", - "integrity": "sha512-jmumhKBhNDABnpmrshYLEcdE9WoP5tmynsDNbDABlb/W8FFiLySQOejukhYIL9CLys4zXerV3/edks0SCzHOiQ==", - "dev": true, - "requires": { - "got": "^11.8.1" - } - }, - "ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "dev": true, - "requires": { - "color-convert": "^2.0.1" - } - }, - "chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", - "dev": true, - "requires": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - } - }, - "color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "dev": true, - "requires": { - "color-name": "~1.1.4" - } - }, - "color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", - "dev": true - }, - "has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "decamelize": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-6.0.0.tgz", + "integrity": "sha512-Fv96DCsdOgB6mdGl67MT5JaTNKRzrzill5OH5s8bjYJXVlcXyPYGyPsUkWyGV5p1TXI5esYIYMMeDJL0hEIwaA==", "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" - } } } }, @@ -29810,6 +34786,23 @@ "integrity": "sha512-Q+JC7Whu8HhmTdBph/Tq59IoRtoy6KAm5zzPv00WdujX82lbAL8K7WVjne7vdCsAmbF4AYaDOPyO3k0kl8qIrw==", "dev": true }, + "ast-types": { + "version": "0.13.4", + "resolved": "https://registry.npmjs.org/ast-types/-/ast-types-0.13.4.tgz", + "integrity": "sha512-x1FCFnFifvYDDzTaLII71vG5uvDwgtmDTEVWAxrgeiR8VjMONcCXJx7E+USjDtHlwFmt9MysbqgF9b9Vjr6w+w==", + "dev": true, + "requires": { + "tslib": "^2.0.1" + }, + "dependencies": { + "tslib": { + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.2.tgz", + "integrity": "sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==", + "dev": true + } + } + }, "astral-regex": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/astral-regex/-/astral-regex-2.0.0.tgz", @@ -29885,6 +34878,12 @@ "integrity": "sha512-xh1Rl34h6Fi1DC2WWKfxUTVqRsNnr6LsKz2+hfwDxQJWmrx8+c7ylaqBMcHfl1U1r2dsifOvKX3LQuLNZ+XSvA==", "dev": true }, + "b4a": { + "version": "1.6.4", + "resolved": "https://registry.npmjs.org/b4a/-/b4a-1.6.4.tgz", + "integrity": "sha512-fpWrvyVHEKyeEvbKZTVOeZF3VSKKWtJxFIxX/jaVPf+cLbGUSitjb49pHLqPV2BUNNZ0LcoeEGfE/YCpyDYHIw==", + "dev": true + }, "babel-code-frame": { "version": "6.26.0", "resolved": "https://registry.npmjs.org/babel-code-frame/-/babel-code-frame-6.26.0.tgz", @@ -30308,6 +35307,12 @@ } } }, + "basic-ftp": { + "version": "5.0.4", + "resolved": "https://registry.npmjs.org/basic-ftp/-/basic-ftp-5.0.4.tgz", + "integrity": "sha512-8PzkB0arJFV4jJWSGOYR+OEic6aeKMu/osRhBULN6RY0ykby6LKhbmuQ5ublvaas5BOwboah5D87nrHyuh8PPA==", + "dev": true + }, "batch": { "version": "0.6.1", "resolved": "https://registry.npmjs.org/batch/-/batch-0.6.1.tgz", @@ -30652,127 +35657,6 @@ "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz", "integrity": "sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==" }, - "cac": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/cac/-/cac-3.0.4.tgz", - "integrity": "sha512-hq4rxE3NT5PlaEiVV39Z45d6MoFcQZG5dsgJqtAUeOz3408LEQAElToDkf9i5IYSCOmK0If/81dLg7nKxqPR0w==", - "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": "sha512-TIGnTpdo+E3+pCyAluZvtED5p5wCqLdezCyhPZzKPcxvFplEt4i+W7OONCKgeZFT3+y5NZZfOOS/Bdcanm1MYA==", - "dev": true - }, - "ansi-styles": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-2.2.1.tgz", - "integrity": "sha512-kmCevFghRiWM7HB5zTPULl4r9bVFSWjz62MhqizDGUrq2NWuNMQyuv4tHHoKJHs69M/MF64lEcHdYIocrdWQYA==", - "dev": true - }, - "chalk": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz", - "integrity": "sha512-U3lRVLMSlsCfjqYPbLyVv11M9CPW4I728d6TCKMAOJueEeB9/8o+eSsMnxPJD+Q+K909sdESg7C+tIkoH6on1A==", - "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" - } - }, - "find-up": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/find-up/-/find-up-1.1.2.tgz", - "integrity": "sha512-jvElSjyuo4EMQGoTwo1uJU5pQMwTW5lS1x05zzfJuTIyLR3zwO27LYrxNg+dlvKpGOuGy/MzBdXh80g0ve5+HA==", - "dev": true, - "requires": { - "path-exists": "^2.0.0", - "pinkie-promise": "^2.0.0" - } - }, - "hosted-git-info": { - "version": "2.8.9", - "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-2.8.9.tgz", - "integrity": "sha512-mxIDAb9Lsm6DoOJ7xH+5+X4y1LU/4Hi50L9C5sIswK3JzULS4bwk1FvjdBgvYR4bzT4tuUQiC15FE2f5HbLvYw==", - "dev": true - }, - "normalize-package-data": { - "version": "2.5.0", - "resolved": "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-2.5.0.tgz", - "integrity": "sha512-/5CMN3T0R4XTj4DcGaexo+roZSdSFW/0AOOTROrjxzCG1wrWXEsGbRKevjlIL+ZDE4sZlJr5ED4YW0yqmkK+eA==", - "dev": true, - "requires": { - "hosted-git-info": "^2.1.4", - "resolve": "^1.10.0", - "semver": "2 || 3 || 4 || 5", - "validate-npm-package-license": "^3.0.1" - } - }, - "path-exists": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-2.1.0.tgz", - "integrity": "sha512-yTltuKuhtNeFJKa1PiRzfLAU5182q1y4Eb4XCJ3PBqyzEDkAZRzBrKKBct682ls9reBVHf9udYLN5Nd+K1B9BQ==", - "dev": true, - "requires": { - "pinkie-promise": "^2.0.0" - } - }, - "read-pkg": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-1.1.0.tgz", - "integrity": "sha512-7BGwRHqt4s/uVbuyoeejRn4YmFnYZiFl4AuaeXHlgZf3sONF0SOGlxs2Pw8g6hCKupo08RafIO5YXFNOKTfwsQ==", - "dev": true, - "requires": { - "load-json-file": "^1.0.0", - "normalize-package-data": "^2.3.2", - "path-type": "^1.0.0" - } - }, - "read-pkg-up": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/read-pkg-up/-/read-pkg-up-1.0.1.tgz", - "integrity": "sha512-WD9MTlNtI55IwYUS27iHh9tK3YoIVhxis8yKhLpTqWtml739uXc9NWTpxoHkfZf3+DkCCsXox94/VWZniuZm6A==", - "dev": true, - "requires": { - "find-up": "^1.0.0", - "read-pkg": "^1.0.0" - } - }, - "semver": { - "version": "5.7.2", - "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.2.tgz", - "integrity": "sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g==", - "dev": true - }, - "strip-ansi": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz", - "integrity": "sha512-VhumSSbBqDTP8p2ZLKj40UjBCV4+v8bUSEpUb4KjRgWk9pbqGF4REFj6KEagidb2f/M6AzC0EmFyDNGaw9OCzg==", - "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": "sha512-KKNVtd6pCYgPIKU4cp2733HWYCpplQhddZLBUryaAHou723x+FRzQ5Df824Fj+IyyuiQTRoub4SnIFfIcrp70g==", - "dev": true - } - } - }, "cache-base": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/cache-base/-/cache-base-1.0.1.tgz", @@ -30823,12 +35707,13 @@ } }, "call-bind": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.2.tgz", - "integrity": "sha512-7O+FbCihrB5WGbFYesctwmTKae6rOiIzmz1icreWJ+0aA7LJfuqhEso2T9ncpcFtzMQtzXf2QGGueWJGTYsqrA==", + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.5.tgz", + "integrity": "sha512-C3nQxfFZxFRVoJoGKKI8y3MOEo129NQ+FgQ08iye+Mk4zNZZGdjfs06bVTr+DBSlA66Q2VEcMki/cUCP4SercQ==", "requires": { - "function-bind": "^1.1.1", - "get-intrinsic": "^1.0.2" + "function-bind": "^1.1.2", + "get-intrinsic": "^1.2.1", + "set-function-length": "^1.1.1" } }, "callsites": { @@ -30843,24 +35728,6 @@ "integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==", "dev": true }, - "camelcase-keys": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/camelcase-keys/-/camelcase-keys-3.0.0.tgz", - "integrity": "sha512-U4E6A6aFyYnNW+tDt5/yIUKQURKXe3WMFPfX4FxrQFcwZ/R08AUk1xWcUtlr7oq6CV07Ji+aa69V2g7BSpblnQ==", - "dev": true, - "requires": { - "camelcase": "^3.0.0", - "map-obj": "^1.0.0" - }, - "dependencies": { - "camelcase": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-3.0.0.tgz", - "integrity": "sha512-4nhGqUkc4BqbBBB4Q6zLuD7lzzrHYrjKGeYaEji/3tFR5VdJu9v+LilhGIVe8wxEJPPOeWo7eg8dwY13TZ1BNg==", - "dev": true - } - } - }, "can-autoplay": { "version": "3.0.2", "resolved": "https://registry.npmjs.org/can-autoplay/-/can-autoplay-3.0.2.tgz", @@ -30996,6 +35863,23 @@ "integrity": "sha512-p3KULyQg4S7NIHixdwbGX+nFHkoBiA4YQmyWtjb8XngSKV124nJmRysgAeujbUVb15vh+RvFUfCPqU7rXk+hZg==", "dev": true }, + "chromium-bidi": { + "version": "0.4.9", + "resolved": "https://registry.npmjs.org/chromium-bidi/-/chromium-bidi-0.4.9.tgz", + "integrity": "sha512-u3DC6XwgLCA9QJ5ak1voPslCmacQdulZNCPsI3qNXxSnEcZS7DFIbww+5RM2bznMEje7cc0oydavRLRvOIZtHw==", + "dev": true, + "optional": true, + "peer": true, + "requires": { + "mitt": "3.0.0" + } + }, + "ci-info": { + "version": "3.9.0", + "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-3.9.0.tgz", + "integrity": "sha512-NIxF55hv4nSqQswkAeiOi1r83xy8JldOFDTWiug55KBu9Jnblncd2U6ViHmYgHf01TPZS77NJBhBMKdWj9HQMQ==", + "dev": true + }, "class-utils": { "version": "0.3.6", "resolved": "https://registry.npmjs.org/class-utils/-/class-utils-0.3.6.tgz", @@ -31092,9 +35976,9 @@ } }, "cli-spinners": { - "version": "2.7.0", - "resolved": "https://registry.npmjs.org/cli-spinners/-/cli-spinners-2.7.0.tgz", - "integrity": "sha512-qu3pN8Y3qHNgE2AFweciB1IfMnmZ/fsNTEE+NOFjmGB2F/7rLhnhzppvpCnN4FovtP26k8lHyy9ptEbNwWFLzw==", + "version": "2.9.2", + "resolved": "https://registry.npmjs.org/cli-spinners/-/cli-spinners-2.9.2.tgz", + "integrity": "sha512-ywqV+5MmyL4E7ybXgKys4DugZbX0FC6LnwrhjuykIjnK9k8OQacQ7axGKnjDXWNhns0xot3bZI5h55H8yo9cJg==", "dev": true }, "cli-width": { @@ -31574,6 +36458,12 @@ "integrity": "sha512-HTUrgRJ7r4dsZKU6GjmpfRK1O76h97Z8MfS1G0FozR+oF2kG6Vfe8JE6zwrkbxigziPHinCJ+gCPjA9EaBDtRw==", "dev": true }, + "csv-writer": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/csv-writer/-/csv-writer-1.6.0.tgz", + "integrity": "sha512-NOx7YDFWEsM/fTRAJjRpPp8t+MKRVvniAg9wQlUKx20MFrPs73WLJhFf5iteqrxNYnsy924K3Iroh3yNHeYd2g==", + "dev": true + }, "custom-event": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/custom-event/-/custom-event-1.0.1.tgz", @@ -31599,6 +36489,12 @@ "assert-plus": "^1.0.0" } }, + "data-uri-to-buffer": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/data-uri-to-buffer/-/data-uri-to-buffer-6.0.1.tgz", + "integrity": "sha512-MZd3VlchQkp8rdend6vrx7MmVDJzSNTBvghvKjirLkD+WTChA3KUf0jkE68Q4UyctNqI11zZO9/x2Yx+ub5Cvg==", + "dev": true + }, "date-format": { "version": "4.0.14", "resolved": "https://registry.npmjs.org/date-format/-/date-format-4.0.14.tgz", @@ -31730,6 +36626,12 @@ "integrity": "sha512-FJ3UgI4gIl+PHZm53knsuSFpE+nESMr7M4v9QcgB7S63Kj/6WqMiFQJpBBYz1Pt+66bZpP3Q7Lye0Oo9MPKEdg==", "dev": true }, + "deepmerge-ts": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/deepmerge-ts/-/deepmerge-ts-5.1.0.tgz", + "integrity": "sha512-eS8dRJOckyo9maw9Tu5O5RUi/4inFLrnoLkBe3cPfDMx3WZioXtmOew4TXQaxq7Rhl4xjDtR7c6x8nNTxOvbFw==", + "dev": true + }, "default-compare": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/default-compare/-/default-compare-1.0.0.tgz", @@ -31768,6 +36670,16 @@ "integrity": "sha512-4tvttepXG1VaYGrRibk5EwJd1t4udunSOVMdLSAL6mId1ix438oPwPZMALY41FCijukO1L0twNcGsdzS7dHgDg==", "dev": true }, + "define-data-property": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/define-data-property/-/define-data-property-1.1.1.tgz", + "integrity": "sha512-E7uGkTzkk1d0ByLeSc6ZsFS79Axg+m1P/VsgYsxHgiuc3tFSj+MjMIwe90FC4lOAZzNBdY7kkO2P2wKdsQ1vgQ==", + "requires": { + "get-intrinsic": "^1.2.1", + "gopd": "^1.0.1", + "has-property-descriptors": "^1.0.0" + } + }, "define-properties": { "version": "1.1.4", "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.1.4.tgz", @@ -31788,6 +36700,50 @@ "isobject": "^3.0.1" } }, + "degenerator": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/degenerator/-/degenerator-5.0.1.tgz", + "integrity": "sha512-TllpMR/t0M5sqCXfj85i4XaAzxmS5tVA16dqvdkMwGmzI+dXLXnw3J+3Vdv7VKw+ThlTMboK6i9rnZ6Nntj5CQ==", + "dev": true, + "requires": { + "ast-types": "^0.13.4", + "escodegen": "^2.1.0", + "esprima": "^4.0.1" + }, + "dependencies": { + "escodegen": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/escodegen/-/escodegen-2.1.0.tgz", + "integrity": "sha512-2NlIDTwUWJN0mRPQOdtQBzbUHvdGY2P1VXSyU83Q3xKxM7WHX2Ql8dKq782Q9TgQUNOLEzEYu9bzLNj1q88I5w==", + "dev": true, + "requires": { + "esprima": "^4.0.1", + "estraverse": "^5.2.0", + "esutils": "^2.0.2", + "source-map": "~0.6.1" + } + }, + "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 + }, + "estraverse": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", + "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", + "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, + "optional": true + } + } + }, "delayed-stream": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", @@ -31825,12 +36781,6 @@ "repeating": "^2.0.0" } }, - "detect-libc": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-1.0.3.tgz", - "integrity": "sha512-pGjwhsmsp4kL2RTz08wcOlGN83otlqHeD/Z5T8GXZB+/YcpQ/dgo+lbU8ZsGxV0HIvqqxo9l7mqYwyYMD9bKDg==", - "dev": true - }, "detect-newline": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/detect-newline/-/detect-newline-2.1.0.tgz", @@ -32029,9 +36979,9 @@ "dev": true }, "diff-sequences": { - "version": "26.6.2", - "resolved": "https://registry.npmjs.org/diff-sequences/-/diff-sequences-26.6.2.tgz", - "integrity": "sha512-Mv/TDa3nZ9sbc5soK+OoA74BsS3mL37yixCvUAQkiuA4Wz6YtwP/K47n2rv2ovzHZvoiQeA5FTQOschKkEwB0Q==", + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/diff-sequences/-/diff-sequences-29.6.3.tgz", + "integrity": "sha512-EjePK1srD3P08o2j4f0ExnylqRs5B9tJjcp9t1krH2qRi8CCdsYfwe9JgSLurFBWwq4uOlipzfk5fHNvwFKr8Q==", "dev": true }, "dlv": { @@ -32228,6 +37178,12 @@ "domhandler": "^5.0.3" } }, + "dotenv": { + "version": "16.4.1", + "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-16.4.1.tgz", + "integrity": "sha512-CjA3y+Dr3FyFDOAMnxZEGtnW9KBR2M0JvvUtXNW+dYJL5ROWxP9DUHCwgFqpMk0OXCc0ljhaNTr2w/kutYIcHQ==", + "dev": true + }, "dset": { "version": "3.1.2", "resolved": "https://registry.npmjs.org/dset/-/dset-3.1.2.tgz", @@ -32356,15 +37312,125 @@ "which": "^2.0.2" } }, + "edgedriver": { + "version": "5.3.9", + "resolved": "https://registry.npmjs.org/edgedriver/-/edgedriver-5.3.9.tgz", + "integrity": "sha512-G0wNgFMFRDnFfKaXG2R6HiyVHqhKwdQ3EgoxW3wPlns2wKqem7F+HgkWBcevN7Vz0nN4AXtskID7/6jsYDXcKw==", + "dev": true, + "requires": { + "@wdio/logger": "^8.16.17", + "decamelize": "^6.0.0", + "edge-paths": "^3.0.5", + "node-fetch": "^3.3.2", + "unzipper": "^0.10.14", + "which": "^4.0.0" + }, + "dependencies": { + "@types/which": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/@types/which/-/which-2.0.2.tgz", + "integrity": "sha512-113D3mDkZDjo+EeUEHCFy0qniNc1ZpecGiAU7WSo7YDoSzolZIQKpYFHrPpjkB2nuyahcKfrmLXeQlh7gqJYdw==", + "dev": true + }, + "data-uri-to-buffer": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/data-uri-to-buffer/-/data-uri-to-buffer-4.0.1.tgz", + "integrity": "sha512-0R9ikRb668HB7QDxT1vkpuUBtqc53YyAwMwGeUFKRojY/NWKvdZ+9UYtRfGmhqNbRkTSVpMbmyhXipFFv2cb/A==", + "dev": true + }, + "decamelize": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-6.0.0.tgz", + "integrity": "sha512-Fv96DCsdOgB6mdGl67MT5JaTNKRzrzill5OH5s8bjYJXVlcXyPYGyPsUkWyGV5p1TXI5esYIYMMeDJL0hEIwaA==", + "dev": true + }, + "duplexer2": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/duplexer2/-/duplexer2-0.1.4.tgz", + "integrity": "sha512-asLFVfWWtJ90ZyOUHMqk7/S2w2guQKxUI2itj3d92ADHhxUSbCMGi1f1cBcJ7xM1To+pE/Khbwo1yuNbMEPKeA==", + "dev": true, + "requires": { + "readable-stream": "^2.0.2" + } + }, + "edge-paths": { + "version": "3.0.5", + "resolved": "https://registry.npmjs.org/edge-paths/-/edge-paths-3.0.5.tgz", + "integrity": "sha512-sB7vSrDnFa4ezWQk9nZ/n0FdpdUuC6R1EOrlU3DL+bovcNFK28rqu2emmAUjujYEJTWIgQGqgVVWUZXMnc8iWg==", + "dev": true, + "requires": { + "@types/which": "^2.0.1", + "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" + } + } + } + }, + "node-fetch": { + "version": "3.3.2", + "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-3.3.2.tgz", + "integrity": "sha512-dRB78srN/l6gqWulah9SrxeYnxeddIG30+GOqK/9OlLVyLg3HPnr6SqOWTWOXKRwC2eGYCkZ59NNuSgvSrpgOA==", + "dev": true, + "requires": { + "data-uri-to-buffer": "^4.0.0", + "fetch-blob": "^3.1.4", + "formdata-polyfill": "^4.0.10" + } + }, + "unzipper": { + "version": "0.10.14", + "resolved": "https://registry.npmjs.org/unzipper/-/unzipper-0.10.14.tgz", + "integrity": "sha512-ti4wZj+0bQTiX2KmKWuwj7lhV+2n//uXEotUmGuQqrbVZSEGFMbI68+c6JCQ8aAmUWYvtHEz2A8K6wXvueR/6g==", + "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", + "graceful-fs": "^4.2.2", + "listenercount": "~1.0.1", + "readable-stream": "~2.3.6", + "setimmediate": "~1.0.4" + } + }, + "which": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/which/-/which-4.0.0.tgz", + "integrity": "sha512-GlaYyEb07DPxYCKhKzplCWBJtvxZcZMrL+4UkrTSJHHPyZU4mYYTv3qaOe77H7EODLSSopAUFAc6W8U4yqvscg==", + "dev": true, + "requires": { + "isexe": "^3.1.1" + }, + "dependencies": { + "isexe": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/isexe/-/isexe-3.1.1.tgz", + "integrity": "sha512-LpB/54B+/2J5hqQ7imZHfdU31OlgQqx7ZicVlkm9kzg9/w8GKLEcFfJl/t7DCEDueOyBAD6zCCwTO6Fzs0NoEQ==", + "dev": true + } + } + } + } + }, "ee-first": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz", "integrity": "sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==" }, "ejs": { - "version": "3.1.8", - "resolved": "https://registry.npmjs.org/ejs/-/ejs-3.1.8.tgz", - "integrity": "sha512-/sXZeMlhS0ArkfX2Aw780gJzXSMPnKjtspYZv+f3NiKLlubezAHDU5+9xz6gd3/NhG3txQCo6xlglmTS+oTGEQ==", + "version": "3.1.9", + "resolved": "https://registry.npmjs.org/ejs/-/ejs-3.1.9.tgz", + "integrity": "sha512-rC+QVNMJWv+MtPgkt0y+0rVEIdbtxVADApW9JXrUVlzHetgcyczP/E7DJmWJ4fJCZF2cPcBk0laWO9ZHMG3DmQ==", "dev": true, "requires": { "jake": "^10.8.5" @@ -33391,55 +38457,605 @@ } }, "expect": { - "version": "26.6.2", - "resolved": "https://registry.npmjs.org/expect/-/expect-26.6.2.tgz", - "integrity": "sha512-9/hlOBkQl2l/PLHJx6JjoDF6xPKcJEsUlWKb23rKE7KzeDqUZKXKNMW27KIue5JMdBV9HgmoJPcc8HtO85t9IA==", + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/expect/-/expect-29.7.0.tgz", + "integrity": "sha512-2Zks0hf1VLFYI1kbh0I5jP3KHHyCHpkfyHBzsSXRFgl/Bg9mWYfMW8oD+PdMPlEwy5HNsR9JutYy6pMeOh61nw==", "dev": true, "requires": { - "@jest/types": "^26.6.2", - "ansi-styles": "^4.0.0", - "jest-get-type": "^26.3.0", - "jest-matcher-utils": "^26.6.2", - "jest-message-util": "^26.6.2", - "jest-regex-util": "^26.0.0" + "@jest/expect-utils": "^29.7.0", + "jest-get-type": "^29.6.3", + "jest-matcher-utils": "^29.7.0", + "jest-message-util": "^29.7.0", + "jest-util": "^29.7.0" + } + }, + "expect-webdriverio": { + "version": "4.9.3", + "resolved": "https://registry.npmjs.org/expect-webdriverio/-/expect-webdriverio-4.9.3.tgz", + "integrity": "sha512-ASHsFc/QaK5ipF4ct3e8hd3elm8wNXk/Qa3EemtYDmfUQ4uzwqDf75m/QFQpwVNCjEpkNP7Be/6X9kz7bN0P9Q==", + "dev": true, + "requires": { + "@vitest/snapshot": "^1.2.1", + "@wdio/globals": "^8.27.0", + "@wdio/logger": "^8.24.12", + "expect": "^29.7.0", + "jest-matcher-utils": "^29.7.0", + "lodash.isequal": "^4.5.0", + "webdriverio": "^8.27.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==", + "@puppeteer/browsers": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/@puppeteer/browsers/-/browsers-1.3.0.tgz", + "integrity": "sha512-an3QdbNPkuU6qpxpbssxAbjRLJcF+eP4L8UqIY3+6n0sbaVxw5pz7PiCLy9g32XEZuoamUlV5ZQPnA6FxvkIHA==", "dev": true, + "optional": true, + "peer": true, "requires": { - "color-convert": "^2.0.1" + "debug": "4.3.4", + "extract-zip": "2.0.1", + "http-proxy-agent": "5.0.0", + "https-proxy-agent": "5.0.1", + "progress": "2.0.3", + "proxy-from-env": "1.1.0", + "tar-fs": "2.1.1", + "unbzip2-stream": "1.4.3", + "yargs": "17.7.1" } }, - "color-convert": { + "@types/which": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/@types/which/-/which-2.0.2.tgz", + "integrity": "sha512-113D3mDkZDjo+EeUEHCFy0qniNc1ZpecGiAU7WSo7YDoSzolZIQKpYFHrPpjkB2nuyahcKfrmLXeQlh7gqJYdw==", + "dev": true, + "optional": true, + "peer": true + }, + "archiver": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/archiver/-/archiver-6.0.1.tgz", + "integrity": "sha512-CXGy4poOLBKptiZH//VlWdFuUC1RESbdZjGjILwBuZ73P7WkAUN0htfSfBq/7k6FRFlpu7bg4JOkj1vU9G6jcQ==", + "dev": true, + "optional": true, + "requires": { + "archiver-utils": "^4.0.1", + "async": "^3.2.4", + "buffer-crc32": "^0.2.1", + "readable-stream": "^3.6.0", + "readdir-glob": "^1.1.2", + "tar-stream": "^3.0.0", + "zip-stream": "^5.0.1" + } + }, + "archiver-utils": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/archiver-utils/-/archiver-utils-4.0.1.tgz", + "integrity": "sha512-Q4Q99idbvzmgCTEAAhi32BkOyq8iVI5EwdO0PmBDSGIzzjYNdcFn7Q7k3OzbLy4kLUPXfJtG6fO2RjftXbobBg==", + "dev": true, + "optional": true, + "requires": { + "glob": "^8.0.0", + "graceful-fs": "^4.2.0", + "lazystream": "^1.0.0", + "lodash": "^4.17.15", + "normalize-path": "^3.0.0", + "readable-stream": "^3.6.0" + } + }, + "async": { + "version": "3.2.5", + "resolved": "https://registry.npmjs.org/async/-/async-3.2.5.tgz", + "integrity": "sha512-baNZyqaaLhyLVKm/DlvdW051MSgO6b8eVfIezl9E5PqWxFgzLm/wQntEW4zOytVburDEr0JlALEpdOFwvErLsg==", + "dev": true, + "optional": true + }, + "brace-expansion": { "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", + "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", "dev": true, + "optional": true, "requires": { - "color-name": "~1.1.4" + "balanced-match": "^1.0.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 + "chrome-launcher": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/chrome-launcher/-/chrome-launcher-1.1.0.tgz", + "integrity": "sha512-rJYWeEAERwWIr3c3mEVXwNiODPEdMRlRxHc47B1qHPOolHZnkj7rMv1QSUfPoG6MgatWj5AxSpnKKR4QEwEQIQ==", + "dev": true, + "optional": true, + "peer": true, + "requires": { + "@types/node": "*", + "escape-string-regexp": "^4.0.0", + "is-wsl": "^2.2.0", + "lighthouse-logger": "^2.0.1" + } + }, + "compress-commons": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/compress-commons/-/compress-commons-5.0.1.tgz", + "integrity": "sha512-MPh//1cERdLtqwO3pOFLeXtpuai0Y2WCd5AhtKxznqM7WtaMYaOEMSgn45d9D10sIHSfIKE603HlOp8OPGrvag==", + "dev": true, + "optional": true, + "requires": { + "crc-32": "^1.2.0", + "crc32-stream": "^5.0.0", + "normalize-path": "^3.0.0", + "readable-stream": "^3.6.0" + } + }, + "crc32-stream": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/crc32-stream/-/crc32-stream-5.0.0.tgz", + "integrity": "sha512-B0EPa1UK+qnpBZpG+7FgPCu0J2ETLpXq09o9BkLkEAhdB6Z61Qo4pJ3JYu0c+Qi+/SAL7QThqnzS06pmSSyZaw==", + "dev": true, + "optional": true, + "requires": { + "crc-32": "^1.2.0", + "readable-stream": "^3.4.0" + } + }, + "cross-fetch": { + "version": "3.1.6", + "resolved": "https://registry.npmjs.org/cross-fetch/-/cross-fetch-3.1.6.tgz", + "integrity": "sha512-riRvo06crlE8HiqOwIpQhxwdOk4fOeR7FVM/wXoxchFEqMNUjvbs3bfo4OTgMEMHzppd4DxFBDbyySj8Cv781g==", + "dev": true, + "optional": true, + "peer": true, + "requires": { + "node-fetch": "^2.6.11" + } + }, + "devtools": { + "version": "8.29.1", + "resolved": "https://registry.npmjs.org/devtools/-/devtools-8.29.1.tgz", + "integrity": "sha512-fbH0Z7CPK4OZSgUw2QcAppczowxtSyvFztPUmiFyi99cUadjEOwlg0aL3pBVlIDo67olYjGb8GD1M5Z4yI/P6w==", + "dev": true, + "optional": true, + "peer": true, + "requires": { + "@types/node": "^20.1.0", + "@wdio/config": "8.29.1", + "@wdio/logger": "8.28.0", + "@wdio/protocols": "8.24.12", + "@wdio/types": "8.29.1", + "@wdio/utils": "8.29.1", + "chrome-launcher": "^1.0.0", + "edge-paths": "^3.0.5", + "import-meta-resolve": "^4.0.0", + "puppeteer-core": "20.3.0", + "query-selector-shadow-dom": "^1.0.0", + "ua-parser-js": "^1.0.1", + "uuid": "^9.0.0", + "which": "^4.0.0" + }, + "dependencies": { + "which": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/which/-/which-4.0.0.tgz", + "integrity": "sha512-GlaYyEb07DPxYCKhKzplCWBJtvxZcZMrL+4UkrTSJHHPyZU4mYYTv3qaOe77H7EODLSSopAUFAc6W8U4yqvscg==", + "dev": true, + "optional": true, + "peer": true, + "requires": { + "isexe": "^3.1.1" + } + } + } + }, + "devtools-protocol": { + "version": "0.0.1120988", + "resolved": "https://registry.npmjs.org/devtools-protocol/-/devtools-protocol-0.0.1120988.tgz", + "integrity": "sha512-39fCpE3Z78IaIPChJsP6Lhmkbf4dWXOmzLk/KFTdRkNk/0JymRIfUynDVRndV9HoDz8PyalK1UH21ST/ivwW5Q==", + "dev": true, + "optional": true, + "peer": true + }, + "edge-paths": { + "version": "3.0.5", + "resolved": "https://registry.npmjs.org/edge-paths/-/edge-paths-3.0.5.tgz", + "integrity": "sha512-sB7vSrDnFa4ezWQk9nZ/n0FdpdUuC6R1EOrlU3DL+bovcNFK28rqu2emmAUjujYEJTWIgQGqgVVWUZXMnc8iWg==", + "dev": true, + "optional": true, + "peer": true, + "requires": { + "@types/which": "^2.0.1", + "which": "^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, + "optional": true, + "peer": true + }, + "glob": { + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/glob/-/glob-8.1.0.tgz", + "integrity": "sha512-r8hpEjiQEYlF2QU0df3dS+nxxSIreXQS1qRhMJM0Q5NDdR386C7jb7Hwwod8Fgiuex+k0GFjgft18yvxm5XoCQ==", + "dev": true, + "optional": true, + "requires": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^5.0.1", + "once": "^1.3.0" + }, + "dependencies": { + "minimatch": { + "version": "5.1.6", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-5.1.6.tgz", + "integrity": "sha512-lKwV/1brpG6mBUFHtb7NUmtABCb2WZZmm2wNiOA5hAb8VdCS4B3dtMWyvcoViccwAW/COERjXLt0zP1zXUN26g==", + "dev": true, + "optional": true, + "requires": { + "brace-expansion": "^2.0.1" + } + } + } + }, + "http-proxy-agent": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/http-proxy-agent/-/http-proxy-agent-5.0.0.tgz", + "integrity": "sha512-n2hY8YdoRE1i7r6M0w9DIw5GgZN0G25P8zLCRQ8rjXtTU3vsNFBI/vWK/UIeE6g5MUUz6avwAPXmL6Fy9D/90w==", + "dev": true, + "optional": true, + "peer": true, + "requires": { + "@tootallnate/once": "2", + "agent-base": "6", + "debug": "4" + } + }, + "isexe": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/isexe/-/isexe-3.1.1.tgz", + "integrity": "sha512-LpB/54B+/2J5hqQ7imZHfdU31OlgQqx7ZicVlkm9kzg9/w8GKLEcFfJl/t7DCEDueOyBAD6zCCwTO6Fzs0NoEQ==", + "dev": true, + "optional": true, + "peer": true + }, + "lighthouse-logger": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/lighthouse-logger/-/lighthouse-logger-2.0.1.tgz", + "integrity": "sha512-ioBrW3s2i97noEmnXxmUq7cjIcVRjT5HBpAYy8zE11CxU9HqlWHHeRxfeN1tn8F7OEMVPIC9x1f8t3Z7US9ehQ==", + "dev": true, + "optional": true, + "peer": true, + "requires": { + "debug": "^2.6.9", + "marky": "^1.2.2" + }, + "dependencies": { + "debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "dev": true, + "optional": true, + "peer": true, + "requires": { + "ms": "2.0.0" + } + } + } + }, + "lru-cache": { + "version": "7.18.3", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-7.18.3.tgz", + "integrity": "sha512-jumlc0BIUrS3qJGgIkWZsyfAM7NCWiBcCDhnd+3NNM5KbBmLTgHVfWBcg6W+rLUsIpzpERPsvwUP7CckAQSOoA==", + "dev": true, + "optional": true + }, + "minimatch": { + "version": "9.0.3", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.3.tgz", + "integrity": "sha512-RHiac9mvaRw0x3AYRgDC1CxAP7HTcNrrECeA8YYJeWnpo+2Q5CegtZjaotWTWxDG3UeGA1coE05iH1mPjT/2mg==", + "dev": true, + "optional": true, + "requires": { + "brace-expansion": "^2.0.1" + } + }, + "ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", + "dev": true, + "optional": true, + "peer": true + }, + "node-fetch": { + "version": "2.7.0", + "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.7.0.tgz", + "integrity": "sha512-c4FRfUm/dbcWZ7U+1Wq0AwCyFL+3nt2bEw05wfxSz+DWpWsitgmSgYmy2dQdWyKC1694ELPqMs/YzUSNozLt8A==", + "dev": true, + "optional": true, + "requires": { + "whatwg-url": "^5.0.0" + } + }, + "proxy-agent": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/proxy-agent/-/proxy-agent-6.3.0.tgz", + "integrity": "sha512-0LdR757eTj/JfuU7TL2YCuAZnxWXu3tkJbg4Oq3geW/qFNT/32T0sp2HnZ9O0lMR4q3vwAt0+xCA8SR0WAD0og==", + "dev": true, + "optional": true, + "requires": { + "agent-base": "^7.0.2", + "debug": "^4.3.4", + "http-proxy-agent": "^7.0.0", + "https-proxy-agent": "^7.0.0", + "lru-cache": "^7.14.1", + "pac-proxy-agent": "^7.0.0", + "proxy-from-env": "^1.1.0", + "socks-proxy-agent": "^8.0.1" + }, + "dependencies": { + "agent-base": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-7.1.0.tgz", + "integrity": "sha512-o/zjMZRhJxny7OyEF+Op8X+efiELC7k7yOjMzgfzVqOzXqkBkWI79YoTdOtsuWd5BWhAGAuOY/Xa6xpiaWXiNg==", + "dev": true, + "optional": true, + "requires": { + "debug": "^4.3.4" + } + }, + "http-proxy-agent": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/http-proxy-agent/-/http-proxy-agent-7.0.0.tgz", + "integrity": "sha512-+ZT+iBxVUQ1asugqnD6oWoRiS25AkjNfG085dKJGtGxkdwLQrMKU5wJr2bOOFAXzKcTuqq+7fZlTMgG3SRfIYQ==", + "dev": true, + "optional": true, + "requires": { + "agent-base": "^7.1.0", + "debug": "^4.3.4" + } + }, + "https-proxy-agent": { + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-7.0.2.tgz", + "integrity": "sha512-NmLNjm6ucYwtcUmL7JQC1ZQ57LmHP4lT15FQ8D61nak1rO6DH+fz5qNK2Ap5UN4ZapYICE3/0KodcLYSPsPbaA==", + "dev": true, + "optional": true, + "requires": { + "agent-base": "^7.0.2", + "debug": "4" + } + } + } + }, + "puppeteer-core": { + "version": "20.3.0", + "resolved": "https://registry.npmjs.org/puppeteer-core/-/puppeteer-core-20.3.0.tgz", + "integrity": "sha512-264pBrIui5bO6NJeOcbJrLa0OCwmA4+WK00JMrLIKTfRiqe2gx8KWTzLsjyw/bizErp3TKS7vt/I0i5fTC+mAw==", + "dev": true, + "optional": true, + "peer": true, + "requires": { + "@puppeteer/browsers": "1.3.0", + "chromium-bidi": "0.4.9", + "cross-fetch": "3.1.6", + "debug": "4.3.4", + "devtools-protocol": "0.0.1120988", + "ws": "8.13.0" + } + }, + "readable-stream": { + "version": "3.6.2", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz", + "integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==", + "dev": true, + "optional": true, + "requires": { + "inherits": "^2.0.3", + "string_decoder": "^1.1.1", + "util-deprecate": "^1.0.1" + } + }, + "serialize-error": { + "version": "11.0.3", + "resolved": "https://registry.npmjs.org/serialize-error/-/serialize-error-11.0.3.tgz", + "integrity": "sha512-2G2y++21dhj2R7iHAdd0FIzjGwuKZld+7Pl/bTU6YIkrC2ZMbVUjm+luj6A6V34Rv9XfKJDKpTWu9W4Gse1D9g==", + "dev": true, + "optional": true, + "requires": { + "type-fest": "^2.12.2" + } + }, + "tar-stream": { + "version": "3.1.7", + "resolved": "https://registry.npmjs.org/tar-stream/-/tar-stream-3.1.7.tgz", + "integrity": "sha512-qJj60CXt7IU1Ffyc3NJMjh6EkuCFej46zUqJ4J7pqYlThyd9bO0XBTmcOIhSzZJVWfsLks0+nle/j538YAW9RQ==", + "dev": true, + "optional": true, + "requires": { + "b4a": "^1.6.4", + "fast-fifo": "^1.2.0", + "streamx": "^2.15.0" + } + }, + "type-fest": { + "version": "2.19.0", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-2.19.0.tgz", + "integrity": "sha512-RAH822pAdBgcNMAfWnCBU3CFZcfZ/i1eZjwFU/dsLKumyuuP3niueg2UAukXYF0E2AAoc82ZSSf9J0WQBinzHA==", + "dev": true, + "optional": true + }, + "ua-parser-js": { + "version": "1.0.37", + "resolved": "https://registry.npmjs.org/ua-parser-js/-/ua-parser-js-1.0.37.tgz", + "integrity": "sha512-bhTyI94tZofjo+Dn8SN6Zv8nBDvyXTymAdM3LDI/0IboIUwTu1rEhW7v2TfiVsoYWgkQ4kOVqnI8APUFbIQIFQ==", + "dev": true, + "optional": true, + "peer": true + }, + "uuid": { + "version": "9.0.1", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-9.0.1.tgz", + "integrity": "sha512-b+1eJOlsR9K8HJpow9Ok3fiWOWSIcIzXodvv0rQjVoOVNpWMpxf1wZNpt4y9h10odCNrqnYp1OBzRktckBe3sA==", + "dev": true, + "optional": true, + "peer": true + }, + "webdriverio": { + "version": "8.29.1", + "resolved": "https://registry.npmjs.org/webdriverio/-/webdriverio-8.29.1.tgz", + "integrity": "sha512-NZK95ivXCqdPraB3FHMw6ByxnCvtgFXkjzG2l3Oq5z0IuJS2aMow3AKFIyiuG6is/deGCe+Tb8eFTCqak7UV+w==", + "dev": true, + "optional": true, + "requires": { + "@types/node": "^20.1.0", + "@wdio/config": "8.29.1", + "@wdio/logger": "8.28.0", + "@wdio/protocols": "8.24.12", + "@wdio/repl": "8.24.12", + "@wdio/types": "8.29.1", + "@wdio/utils": "8.29.1", + "archiver": "^6.0.0", + "aria-query": "^5.0.0", + "css-shorthand-properties": "^1.1.1", + "css-value": "^0.0.1", + "devtools-protocol": "^0.0.1249869", + "grapheme-splitter": "^1.0.2", + "import-meta-resolve": "^4.0.0", + "is-plain-obj": "^4.1.0", + "lodash.clonedeep": "^4.5.0", + "lodash.zip": "^4.2.0", + "minimatch": "^9.0.0", + "puppeteer-core": "^20.9.0", + "query-selector-shadow-dom": "^1.0.0", + "resq": "^1.9.1", + "rgb2hex": "0.2.5", + "serialize-error": "^11.0.1", + "webdriver": "8.29.1" + }, + "dependencies": { + "@puppeteer/browsers": { + "version": "1.4.6", + "resolved": "https://registry.npmjs.org/@puppeteer/browsers/-/browsers-1.4.6.tgz", + "integrity": "sha512-x4BEjr2SjOPowNeiguzjozQbsc6h437ovD/wu+JpaenxVLm3jkgzHY2xOslMTp50HoTvQreMjiexiGQw1sqZlQ==", + "dev": true, + "optional": true, + "requires": { + "debug": "4.3.4", + "extract-zip": "2.0.1", + "progress": "2.0.3", + "proxy-agent": "6.3.0", + "tar-fs": "3.0.4", + "unbzip2-stream": "1.4.3", + "yargs": "17.7.1" + } + }, + "chromium-bidi": { + "version": "0.4.16", + "resolved": "https://registry.npmjs.org/chromium-bidi/-/chromium-bidi-0.4.16.tgz", + "integrity": "sha512-7ZbXdWERxRxSwo3txsBjjmc/NLxqb1Bk30mRb0BMS4YIaiV6zvKZqL/UAH+DdqcDYayDWk2n/y8klkBDODrPvA==", + "dev": true, + "optional": true, + "requires": { + "mitt": "3.0.0" + } + }, + "cross-fetch": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/cross-fetch/-/cross-fetch-4.0.0.tgz", + "integrity": "sha512-e4a5N8lVvuLgAWgnCrLr2PP0YyDOTHa9H/Rj54dirp61qXnNq46m82bRhNqIA5VccJtWBvPTFRV3TtvHUKPB1g==", + "dev": true, + "optional": true, + "requires": { + "node-fetch": "^2.6.12" + } + }, + "devtools-protocol": { + "version": "0.0.1249869", + "resolved": "https://registry.npmjs.org/devtools-protocol/-/devtools-protocol-0.0.1249869.tgz", + "integrity": "sha512-Ctp4hInA0BEavlUoRy9mhGq0i+JSo/AwVyX2EFgZmV1kYB+Zq+EMBAn52QWu6FbRr10hRb6pBl420upbp4++vg==", + "dev": true, + "optional": true + }, + "puppeteer-core": { + "version": "20.9.0", + "resolved": "https://registry.npmjs.org/puppeteer-core/-/puppeteer-core-20.9.0.tgz", + "integrity": "sha512-H9fYZQzMTRrkboEfPmf7m3CLDN6JvbxXA3qTtS+dFt27tR+CsFHzPsT6pzp6lYL6bJbAPaR0HaPO6uSi+F94Pg==", + "dev": true, + "optional": true, + "requires": { + "@puppeteer/browsers": "1.4.6", + "chromium-bidi": "0.4.16", + "cross-fetch": "4.0.0", + "debug": "4.3.4", + "devtools-protocol": "0.0.1147663", + "ws": "8.13.0" + }, + "dependencies": { + "devtools-protocol": { + "version": "0.0.1147663", + "resolved": "https://registry.npmjs.org/devtools-protocol/-/devtools-protocol-0.0.1147663.tgz", + "integrity": "sha512-hyWmRrexdhbZ1tcJUGpO95ivbRhWXz++F4Ko+n21AY5PNln2ovoJw+8ZMNDTtip+CNFQfrtLVh/w4009dXO/eQ==", + "dev": true, + "optional": true + } + } + }, + "tar-fs": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/tar-fs/-/tar-fs-3.0.4.tgz", + "integrity": "sha512-5AFQU8b9qLfZCX9zp2duONhPmZv0hGYiBPJsyUdqMjzq/mqVpy/rEUSeHk1+YitmxugaptgBh5oDGU3VsAJq4w==", + "dev": true, + "optional": true, + "requires": { + "mkdirp-classic": "^0.5.2", + "pump": "^3.0.0", + "tar-stream": "^3.1.5" + } + } + } + }, + "ws": { + "version": "8.13.0", + "resolved": "https://registry.npmjs.org/ws/-/ws-8.13.0.tgz", + "integrity": "sha512-x9vcZYTrFPC7aSIbj7sRCYo7L/Xb8Iy+pW0ng0wt2vCJv7M9HOMy0UoN3rr+IFC7hb7vXoqS+P9ktyLLLhO+LA==", + "dev": true, + "optional": true, + "requires": {} + }, + "yargs": { + "version": "17.7.1", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.7.1.tgz", + "integrity": "sha512-cwiTb08Xuv5fqF4AovYacTFNxk62th7LKJ6BL9IGUpTJrWoU7/7WdQGTP2SjKf1dUNBGzDd28p/Yfs/GI6JrLw==", + "dev": true, + "optional": true, + "requires": { + "cliui": "^8.0.1", + "escalade": "^3.1.1", + "get-caller-file": "^2.0.5", + "require-directory": "^2.1.1", + "string-width": "^4.2.3", + "y18n": "^5.0.5", + "yargs-parser": "^21.1.1" + } + }, + "zip-stream": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/zip-stream/-/zip-stream-5.0.1.tgz", + "integrity": "sha512-UfZ0oa0C8LI58wJ+moL46BDIMgCQbnsb+2PoiJYtonhBsMh2bq1eRBVkvjfVsqbEHd9/EgKPUuL9saSSsec8OA==", + "dev": true, + "optional": true, + "requires": { + "archiver-utils": "^4.0.1", + "compress-commons": "^5.0.1", + "readable-stream": "^3.6.0" + } } } }, - "expect-webdriverio": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/expect-webdriverio/-/expect-webdriverio-2.0.2.tgz", - "integrity": "sha512-dst0tqP1aZ2p7TPmbatqoIQ+7hRTw+IeKNi830XxKhu2DNNe5vQ85i9ttf9rpXgbnUf91HxKcocn4G7A5bQxDA==", - "dev": true, - "requires": { - "expect": "^26.6.2", - "jest-matcher-utils": "^26.6.2" - } - }, "express": { "version": "4.18.2", "resolved": "https://registry.npmjs.org/express/-/express-4.18.2.tgz", @@ -33639,6 +39255,12 @@ "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", "dev": true }, + "fast-fifo": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/fast-fifo/-/fast-fifo-1.3.2.tgz", + "integrity": "sha512-/d9sfos4yxzpwkDkuN7k2SqFKtYNmCTzgfEpz82x34IM9/zc8KGxQoXg1liNC/izpRM/MBdt44Nmx41ZWqk+FQ==", + "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", @@ -33669,13 +39291,28 @@ "pend": "~1.2.0" } }, - "fibers": { - "version": "5.0.3", - "resolved": "https://registry.npmjs.org/fibers/-/fibers-5.0.3.tgz", - "integrity": "sha512-/qYTSoZydQkM21qZpGLDLuCq8c+B8KhuCQ1kLPvnRNhxhVbvrpmH9l2+Lblf5neDuEsY4bfT7LeO553TXQDvJw==", + "fecha": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/fecha/-/fecha-4.2.3.tgz", + "integrity": "sha512-OP2IUU6HeYKJi3i0z4A19kHMQoLVs4Hc+DPqqxI2h/DPZHTm/vjsfC6P0b4jCMy14XizLBqvndQ+UilD7707Jw==", + "dev": true + }, + "fetch-blob": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/fetch-blob/-/fetch-blob-3.2.0.tgz", + "integrity": "sha512-7yAQpD2UMJzLi1Dqv7qFYnPbaPx7ZfFK6PiIxQ4PfkGPyNyl2Ugx+a/umUonmKqjhM4DnfbMvdX6otXq83soQQ==", "dev": true, "requires": { - "detect-libc": "^1.0.3" + "node-domexception": "^1.0.0", + "web-streams-polyfill": "^3.0.3" + }, + "dependencies": { + "web-streams-polyfill": { + "version": "3.3.2", + "resolved": "https://registry.npmjs.org/web-streams-polyfill/-/web-streams-polyfill-3.3.2.tgz", + "integrity": "sha512-3pRGuxRF5gpuZc0W+EpwQRmCD7gRqcDOMt688KmdlDAgAyaB1XlN0zq2njfDNm44XVdIouE7pZ6GzbdyH47uIQ==", + "dev": true + } } }, "figures": { @@ -33722,9 +39359,9 @@ } }, "minimatch": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-5.1.0.tgz", - "integrity": "sha512-9TPBGGak4nHfGZsPBohm9AWg6NoT7QTCehS3BIJABslyZbzxfV78QM2Y6+i741OPZIafFAaiiEMh5OyIrJPgtg==", + "version": "5.1.6", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-5.1.6.tgz", + "integrity": "sha512-lKwV/1brpG6mBUFHtb7NUmtABCb2WZZmm2wNiOA5hAb8VdCS4B3dtMWyvcoViccwAW/COERjXLt0zP1zXUN26g==", "dev": true, "requires": { "brace-expansion": "^2.0.1" @@ -34046,6 +39683,24 @@ "integrity": "sha512-J+ler7Ta54FwwNcx6wQRDhTIbNeyDcARMkOcguEqnEdtm0jKvN3Li3PDAb2Du3ubJYEWfYL83XMROXdsXAXycw==", "dev": true }, + "foreground-child": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/foreground-child/-/foreground-child-3.1.1.tgz", + "integrity": "sha512-TMKDUnIte6bfb5nWv7V/caI169OHgvwjb7V4WkeUvbQQdjr5rWKqHFiKWb/fcOwB+CzBT+qbWjvj+DVwRskpIg==", + "dev": true, + "requires": { + "cross-spawn": "^7.0.0", + "signal-exit": "^4.0.1" + }, + "dependencies": { + "signal-exit": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-4.1.0.tgz", + "integrity": "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==", + "dev": true + } + } + }, "forever-agent": { "version": "0.6.1", "resolved": "https://registry.npmjs.org/forever-agent/-/forever-agent-0.6.1.tgz", @@ -34069,6 +39724,31 @@ "mime-types": "^2.1.12" } }, + "form-data-encoder": { + "version": "2.1.4", + "resolved": "https://registry.npmjs.org/form-data-encoder/-/form-data-encoder-2.1.4.tgz", + "integrity": "sha512-yDYSgNMraqvnxiEXO4hi88+YZxaHC6QKzb5N84iRCTDeRO7ZALpir/lVmf/uXUhnwUr2O4HU8s/n6x+yNjQkHw==", + "dev": true + }, + "formdata-node": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/formdata-node/-/formdata-node-5.0.1.tgz", + "integrity": "sha512-8xnIjMYGKPj+rY2BTbAmpqVpi8der/2FT4d9f7J32FlsCpO5EzZPq3C/N56zdv8KweHzVF6TGijsS1JT6r1H2g==", + "dev": true, + "requires": { + "node-domexception": "1.0.0", + "web-streams-polyfill": "4.0.0-beta.3" + } + }, + "formdata-polyfill": { + "version": "4.0.10", + "resolved": "https://registry.npmjs.org/formdata-polyfill/-/formdata-polyfill-4.0.10.tgz", + "integrity": "sha512-buewHzMvYL29jdeQTVILecSaZKnt/RJWjoZCF5OW60Z67/GmSLBkOFM7qh1PI3zFNtJbaZL5eQu1vLfazOwj4g==", + "dev": true, + "requires": { + "fetch-blob": "^3.1.2" + } + }, "forwarded": { "version": "0.2.0", "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.2.0.tgz", @@ -34229,9 +39909,9 @@ } }, "function-bind": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz", - "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==" + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz", + "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==" }, "function.prototype.name": { "version": "1.1.5", @@ -34266,6 +39946,130 @@ "globule": "^1.0.0" } }, + "geckodriver": { + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/geckodriver/-/geckodriver-4.3.1.tgz", + "integrity": "sha512-ol7JLsj55o5k+z7YzeSy2mdJROXMAxIa+uzr3A1yEMr5HISqQOTslE3ZeARcxR4jpAY3fxmHM+sq32qbe/eXfA==", + "dev": true, + "requires": { + "@wdio/logger": "^8.24.12", + "decamelize": "^6.0.0", + "http-proxy-agent": "^7.0.0", + "https-proxy-agent": "^7.0.2", + "node-fetch": "^3.3.2", + "tar-fs": "^3.0.4", + "unzipper": "^0.10.14", + "which": "^4.0.0" + }, + "dependencies": { + "agent-base": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-7.1.0.tgz", + "integrity": "sha512-o/zjMZRhJxny7OyEF+Op8X+efiELC7k7yOjMzgfzVqOzXqkBkWI79YoTdOtsuWd5BWhAGAuOY/Xa6xpiaWXiNg==", + "dev": true, + "requires": { + "debug": "^4.3.4" + } + }, + "data-uri-to-buffer": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/data-uri-to-buffer/-/data-uri-to-buffer-4.0.1.tgz", + "integrity": "sha512-0R9ikRb668HB7QDxT1vkpuUBtqc53YyAwMwGeUFKRojY/NWKvdZ+9UYtRfGmhqNbRkTSVpMbmyhXipFFv2cb/A==", + "dev": true + }, + "decamelize": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-6.0.0.tgz", + "integrity": "sha512-Fv96DCsdOgB6mdGl67MT5JaTNKRzrzill5OH5s8bjYJXVlcXyPYGyPsUkWyGV5p1TXI5esYIYMMeDJL0hEIwaA==", + "dev": true + }, + "duplexer2": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/duplexer2/-/duplexer2-0.1.4.tgz", + "integrity": "sha512-asLFVfWWtJ90ZyOUHMqk7/S2w2guQKxUI2itj3d92ADHhxUSbCMGi1f1cBcJ7xM1To+pE/Khbwo1yuNbMEPKeA==", + "dev": true, + "requires": { + "readable-stream": "^2.0.2" + } + }, + "https-proxy-agent": { + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-7.0.2.tgz", + "integrity": "sha512-NmLNjm6ucYwtcUmL7JQC1ZQ57LmHP4lT15FQ8D61nak1rO6DH+fz5qNK2Ap5UN4ZapYICE3/0KodcLYSPsPbaA==", + "dev": true, + "requires": { + "agent-base": "^7.0.2", + "debug": "4" + } + }, + "isexe": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/isexe/-/isexe-3.1.1.tgz", + "integrity": "sha512-LpB/54B+/2J5hqQ7imZHfdU31OlgQqx7ZicVlkm9kzg9/w8GKLEcFfJl/t7DCEDueOyBAD6zCCwTO6Fzs0NoEQ==", + "dev": true + }, + "node-fetch": { + "version": "3.3.2", + "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-3.3.2.tgz", + "integrity": "sha512-dRB78srN/l6gqWulah9SrxeYnxeddIG30+GOqK/9OlLVyLg3HPnr6SqOWTWOXKRwC2eGYCkZ59NNuSgvSrpgOA==", + "dev": true, + "requires": { + "data-uri-to-buffer": "^4.0.0", + "fetch-blob": "^3.1.4", + "formdata-polyfill": "^4.0.10" + } + }, + "tar-fs": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/tar-fs/-/tar-fs-3.0.4.tgz", + "integrity": "sha512-5AFQU8b9qLfZCX9zp2duONhPmZv0hGYiBPJsyUdqMjzq/mqVpy/rEUSeHk1+YitmxugaptgBh5oDGU3VsAJq4w==", + "dev": true, + "requires": { + "mkdirp-classic": "^0.5.2", + "pump": "^3.0.0", + "tar-stream": "^3.1.5" + } + }, + "tar-stream": { + "version": "3.1.7", + "resolved": "https://registry.npmjs.org/tar-stream/-/tar-stream-3.1.7.tgz", + "integrity": "sha512-qJj60CXt7IU1Ffyc3NJMjh6EkuCFej46zUqJ4J7pqYlThyd9bO0XBTmcOIhSzZJVWfsLks0+nle/j538YAW9RQ==", + "dev": true, + "requires": { + "b4a": "^1.6.4", + "fast-fifo": "^1.2.0", + "streamx": "^2.15.0" + } + }, + "unzipper": { + "version": "0.10.14", + "resolved": "https://registry.npmjs.org/unzipper/-/unzipper-0.10.14.tgz", + "integrity": "sha512-ti4wZj+0bQTiX2KmKWuwj7lhV+2n//uXEotUmGuQqrbVZSEGFMbI68+c6JCQ8aAmUWYvtHEz2A8K6wXvueR/6g==", + "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", + "graceful-fs": "^4.2.2", + "listenercount": "~1.0.1", + "readable-stream": "~2.3.6", + "setimmediate": "~1.0.4" + } + }, + "which": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/which/-/which-4.0.0.tgz", + "integrity": "sha512-GlaYyEb07DPxYCKhKzplCWBJtvxZcZMrL+4UkrTSJHHPyZU4mYYTv3qaOe77H7EODLSSopAUFAc6W8U4yqvscg==", + "dev": true, + "requires": { + "isexe": "^3.1.1" + } + } + } + }, "gensync": { "version": "1.0.0-beta.2", "resolved": "https://registry.npmjs.org/gensync/-/gensync-1.0.0-beta.2.tgz", @@ -34284,13 +40088,14 @@ "dev": true }, "get-intrinsic": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.1.3.tgz", - "integrity": "sha512-QJVz1Tj7MS099PevUG5jvnt9tSkXN8K14dxQlikJuPt4uD9hHAHjLyLBiLR5zELelBdD9QNRAXZzsJx0WaDL9A==", + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.2.2.tgz", + "integrity": "sha512-0gSo4ml/0j98Y3lngkFEot/zhiCeWsbYIlZ+uZOVgzLyLaUw7wxUL+nCTP0XJvJg1AXulJRI3UJi8GsbDuxdGA==", "requires": { - "function-bind": "^1.1.1", - "has": "^1.0.3", - "has-symbols": "^1.0.3" + "function-bind": "^1.1.2", + "has-proto": "^1.0.1", + "has-symbols": "^1.0.3", + "hasown": "^2.0.0" } }, "get-package-type": { @@ -34300,9 +40105,9 @@ "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==", + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/get-port/-/get-port-7.0.0.tgz", + "integrity": "sha512-mDHFgApoQd+azgMdwylJrv2DX47ywGq1i5VFJE7fZ0dttNq3iQMfsU4IvEgBHojA3KqEudyu7Vq+oN8kNaNkWw==", "dev": true }, "get-stream": { @@ -34324,6 +40129,46 @@ "get-intrinsic": "^1.1.1" } }, + "get-uri": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/get-uri/-/get-uri-6.0.2.tgz", + "integrity": "sha512-5KLucCJobh8vBY1K07EFV4+cPZH3mrV9YeAruUseCQKHB58SGjjT2l9/eA9LD082IiuMjSlFJEcdJ27TXvbZNw==", + "dev": true, + "requires": { + "basic-ftp": "^5.0.2", + "data-uri-to-buffer": "^6.0.0", + "debug": "^4.3.4", + "fs-extra": "^8.1.0" + }, + "dependencies": { + "fs-extra": { + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-8.1.0.tgz", + "integrity": "sha512-yhlQgA6mnOJUKOsRUFsgJdQCvkKhcz8tlZG5HBQfReYZy46OwLcY+Zia0mtdHsOo9y/hP+CxMN0TU9QxoOtG4g==", + "dev": true, + "requires": { + "graceful-fs": "^4.2.0", + "jsonfile": "^4.0.0", + "universalify": "^0.1.0" + } + }, + "jsonfile": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-4.0.0.tgz", + "integrity": "sha512-m6F1R3z8jjlf2imQHS2Qez5sjKWQzbuuhuJ/FKYFRZvPE3PuHcSMVZzfsLhGVOkfd20obL5SWEBew5ShlquNxg==", + "dev": true, + "requires": { + "graceful-fs": "^4.1.6" + } + }, + "universalify": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/universalify/-/universalify-0.1.2.tgz", + "integrity": "sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg==", + "dev": true + } + } + }, "get-value": { "version": "2.0.6", "resolved": "https://registry.npmjs.org/get-value/-/get-value-2.0.6.tgz", @@ -34339,6 +40184,12 @@ "assert-plus": "^1.0.0" } }, + "git-repo-info": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/git-repo-info/-/git-repo-info-2.1.1.tgz", + "integrity": "sha512-8aCohiDo4jwjOwma4FmYFd3i97urZulL8XL24nIPxuE+GZnfsAyy/g2Shqx6OjUiFKUXZM+Yy+KHnOmmA3FVcg==", + "dev": true + }, "git-up": { "version": "7.0.0", "resolved": "https://registry.npmjs.org/git-up/-/git-up-7.0.0.tgz", @@ -34358,6 +40209,23 @@ "git-up": "^7.0.0" } }, + "gitconfiglocal": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/gitconfiglocal/-/gitconfiglocal-2.1.0.tgz", + "integrity": "sha512-qoerOEliJn3z+Zyn1HW2F6eoYJqKwS6MgC9cztTLUB/xLWX8gD/6T60pKn4+t/d6tP7JlybI7Z3z+I572CR/Vg==", + "dev": true, + "requires": { + "ini": "^1.3.2" + }, + "dependencies": { + "ini": { + "version": "1.3.8", + "resolved": "https://registry.npmjs.org/ini/-/ini-1.3.8.tgz", + "integrity": "sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew==", + "dev": true + } + } + }, "github-slugger": { "version": "1.4.0", "resolved": "https://registry.npmjs.org/github-slugger/-/github-slugger-1.4.0.tgz", @@ -34797,6 +40665,14 @@ "sparkles": "^1.0.0" } }, + "gopd": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.0.1.tgz", + "integrity": "sha512-d65bNlIadxvpb/A2abVdlqKqV563juRnZ1Wtk6s1sIR8uNsXR70xqIzVqxVf1eTqDunwT2MkczEeaezCKTZhwA==", + "requires": { + "get-intrinsic": "^1.1.3" + } + }, "got": { "version": "11.8.5", "resolved": "https://registry.npmjs.org/got/-/got-11.8.5.tgz", @@ -34828,12 +40704,6 @@ "integrity": "sha512-bzh50DW9kTPM00T8y4o8vQg89Di9oLJVLW/KaOGIXJWP/iqCN6WKYkbNOF04vFLJhwcpYUh9ydh/+5vpOqV4YQ==", "dev": true }, - "growl": { - "version": "1.10.5", - "resolved": "https://registry.npmjs.org/growl/-/growl-1.10.5.tgz", - "integrity": "sha512-qBr4OuELkhPenW6goKVXiv47US3clb3/IbuWF9KNKEijAy9oeHxU9IgzjvJhHkUzhaj7rOUD7+YGWqUjLp5oSA==", - "dev": true - }, "gulp": { "version": "4.0.2", "resolved": "https://registry.npmjs.org/gulp/-/gulp-4.0.2.tgz", @@ -36224,14 +42094,18 @@ } }, "has-property-descriptors": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/has-property-descriptors/-/has-property-descriptors-1.0.0.tgz", - "integrity": "sha512-62DVLZGoiEBDHQyqG4w9xCuZ7eJEwNmJRWw2VY84Oedb7WFcA27fiEVe8oUQx9hAUJ4ekurquucTGwsyO1XGdQ==", - "dev": true, + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/has-property-descriptors/-/has-property-descriptors-1.0.1.tgz", + "integrity": "sha512-VsX8eaIewvas0xnvinAe9bw4WfIeODpGYikiWYLH+dma0Jw6KHYqWiWfhQlgOVK8D6PvjubK5Uc4P0iIhIcNVg==", "requires": { - "get-intrinsic": "^1.1.1" + "get-intrinsic": "^1.2.2" } }, + "has-proto": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/has-proto/-/has-proto-1.0.1.tgz", + "integrity": "sha512-7qE+iP+O+bgF9clE5+UoBFzE65mlBiVj3tKCrlNQ0Ogwm0BjpT/gK4SlLYDMybDh5I3TCTKnPPa0oMG7JDYrhg==" + }, "has-symbols": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.3.tgz", @@ -36304,6 +42178,14 @@ } } }, + "hasown": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.0.tgz", + "integrity": "sha512-vUptKVTpIJhcczKBbgnS+RtcuYMB8+oNzPK2/Hp3hanz8JmpATdmmgLgSaadVREkDm+e2giHwY3ZRkyjSIDDFA==", + "requires": { + "function-bind": "^1.1.2" + } + }, "hast-util-is-element": { "version": "2.1.2", "resolved": "https://registry.npmjs.org/hast-util-is-element/-/hast-util-is-element-2.1.2.tgz", @@ -36353,6 +42235,12 @@ "integrity": "sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw==", "dev": true }, + "headers-utils": { + "version": "1.2.5", + "resolved": "https://registry.npmjs.org/headers-utils/-/headers-utils-1.2.5.tgz", + "integrity": "sha512-DAzV5P/pk3wTU/8TLZN+zFTDv4Xa1QDTU8pRvovPetcOMbmqq8CwsAvZBLPZHH6usxyy31zMp7I4aCYb6XIf6w==", + "dev": true + }, "highlight.js": { "version": "11.6.0", "resolved": "https://registry.npmjs.org/highlight.js/-/highlight.js-11.6.0.tgz", @@ -36434,6 +42322,27 @@ "requires-port": "^1.0.0" } }, + "http-proxy-agent": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/http-proxy-agent/-/http-proxy-agent-7.0.0.tgz", + "integrity": "sha512-+ZT+iBxVUQ1asugqnD6oWoRiS25AkjNfG085dKJGtGxkdwLQrMKU5wJr2bOOFAXzKcTuqq+7fZlTMgG3SRfIYQ==", + "dev": true, + "requires": { + "agent-base": "^7.1.0", + "debug": "^4.3.4" + }, + "dependencies": { + "agent-base": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-7.1.0.tgz", + "integrity": "sha512-o/zjMZRhJxny7OyEF+Op8X+efiELC7k7yOjMzgfzVqOzXqkBkWI79YoTdOtsuWd5BWhAGAuOY/Xa6xpiaWXiNg==", + "dev": true, + "requires": { + "debug": "^4.3.4" + } + } + } + }, "http-signature": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/http-signature/-/http-signature-1.2.0.tgz", @@ -36465,6 +42374,12 @@ "debug": "4" } }, + "human-signals": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/human-signals/-/human-signals-5.0.0.tgz", + "integrity": "sha512-AXcZb6vzzrFAUE61HnN4mpLqd/cSIwNQjtNWR0euPm6y0iqx3G4gOXaIDdtdDwZmhwe82LA6+zinmW4UBWVePQ==", + "dev": true + }, "iconv-lite": { "version": "0.4.24", "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz", @@ -36503,18 +42418,18 @@ } } }, + "import-meta-resolve": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/import-meta-resolve/-/import-meta-resolve-4.0.0.tgz", + "integrity": "sha512-okYUR7ZQPH+efeuMJGlq4f8ubUgO50kByRPyt/Cy1Io4PSRsPjxME+YlVaCOx+NIToW7hCsZNFJyTPFFKepRSA==", + "dev": true + }, "imurmurhash": { "version": "0.1.4", "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", "integrity": "sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==", "dev": true }, - "indent-string": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/indent-string/-/indent-string-3.2.0.tgz", - "integrity": "sha512-BYqTHXTGUIvg7t1r4sJNKcbDZkL92nkXA8YtRpbjFHRHGDL/NtUeiBJMeE60kIFN/Mg8ESaWQvftaYMGJzQZCQ==", - "dev": true - }, "individual": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/individual/-/individual-2.0.0.tgz", @@ -36543,26 +42458,26 @@ "dev": true }, "inquirer": { - "version": "8.2.5", - "resolved": "https://registry.npmjs.org/inquirer/-/inquirer-8.2.5.tgz", - "integrity": "sha512-QAgPDQMEgrDssk1XiwwHoOGYF9BAbUcc1+j+FhEvaOt8/cKRqyLn0U5qA6F74fGhTMGxf92pOvPBeh29jQJDTQ==", + "version": "9.2.12", + "resolved": "https://registry.npmjs.org/inquirer/-/inquirer-9.2.12.tgz", + "integrity": "sha512-mg3Fh9g2zfuVWJn6lhST0O7x4n03k7G8Tx5nvikJkbq8/CK47WDVm+UznF0G6s5Zi0KcyUisr6DU8T67N5U+1Q==", "dev": true, "requires": { - "ansi-escapes": "^4.2.1", - "chalk": "^4.1.1", + "@ljharb/through": "^2.3.11", + "ansi-escapes": "^4.3.2", + "chalk": "^5.3.0", "cli-cursor": "^3.1.0", - "cli-width": "^3.0.0", - "external-editor": "^3.0.3", - "figures": "^3.0.0", + "cli-width": "^4.1.0", + "external-editor": "^3.1.0", + "figures": "^5.0.0", "lodash": "^4.17.21", - "mute-stream": "0.0.8", + "mute-stream": "1.0.0", "ora": "^5.4.1", - "run-async": "^2.4.0", - "rxjs": "^7.5.5", - "string-width": "^4.1.0", - "strip-ansi": "^6.0.0", - "through": "^2.3.6", - "wrap-ansi": "^7.0.0" + "run-async": "^3.0.0", + "rxjs": "^7.8.1", + "string-width": "^4.2.3", + "strip-ansi": "^6.0.1", + "wrap-ansi": "^6.2.0" }, "dependencies": { "ansi-styles": { @@ -36575,14 +42490,16 @@ } }, "chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", - "dev": true, - "requires": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - } + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-5.3.0.tgz", + "integrity": "sha512-dLitG79d+GV1Nb/VYcCDFivJeK1hiukt9QjRNVOsUtTy1rR1YJsmpGGTZ3qJos+uw7WmWF4wUwBd9jxjocFC2w==", + "dev": true + }, + "cli-width": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/cli-width/-/cli-width-4.1.0.tgz", + "integrity": "sha512-ouuZd4/dm2Sw5Gmqy6bGyNNNe1qt9RpmxveLSO7KcgsTnU7RXfsw+/bukWGo1abgBiMAic068rclZsO4IWmmxQ==", + "dev": true }, "color-convert": { "version": "2.0.1", @@ -36599,35 +42516,65 @@ "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==", + "escape-string-regexp": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-5.0.0.tgz", + "integrity": "sha512-/veY75JbMK4j1yjvuUxuVsiS/hr/4iHs9FTT6cgTexxdE0Ly/glccBAkloH/DofkjRbZU3bnoj38mOmhkZ0lHw==", "dev": true }, - "rxjs": { - "version": "7.5.7", - "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-7.5.7.tgz", - "integrity": "sha512-z9MzKh/UcOqB3i20H6rtrlaE/CgjLOvheWK/9ILrbhROGTweAi1BaFsTT9FbwZi5Trr1qNRs+MXkhmR06awzQA==", + "figures": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/figures/-/figures-5.0.0.tgz", + "integrity": "sha512-ej8ksPF4x6e5wvK9yevct0UCXh8TTFlWGVLlgjZuoBH1HwjIfKE/IdL5mq89sFA7zELi1VhKpmtDnrs7zWyeyg==", "dev": true, "requires": { - "tslib": "^2.1.0" + "escape-string-regexp": "^5.0.0", + "is-unicode-supported": "^1.2.0" } }, - "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==", + "is-unicode-supported": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/is-unicode-supported/-/is-unicode-supported-1.3.0.tgz", + "integrity": "sha512-43r2mRvz+8JRIKnWJ+3j8JtjRKZ6GmjzfaE/qiBJnikNnYv/6bagRJ1kUhNk8R5EX/GkobD+r+sfxCPJsiKBLQ==", + "dev": true + }, + "mute-stream": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/mute-stream/-/mute-stream-1.0.0.tgz", + "integrity": "sha512-avsJQhyd+680gKXyG/sQc0nXaC6rBkPOfyHYcFb9+hdkqQkR9bdnkJ0AMZhke0oesPqIO+mFFJ+IdBc7mst4IA==", + "dev": true + }, + "run-async": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/run-async/-/run-async-3.0.0.tgz", + "integrity": "sha512-540WwVDOMxA6dN6We19EcT9sc3hkXPw5mzRNGM3FkdN/vtE9NFvj5lFAPNwUDmJjXidm3v7TC1cTE7t17Ulm1Q==", + "dev": true + }, + "rxjs": { + "version": "7.8.1", + "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-7.8.1.tgz", + "integrity": "sha512-AA3TVj+0A2iuIoQkWEK/tqFjBq2j+6PO6Y0zJcvzLAFhEFIO3HL0vls9hWLncZbAAbK0mar7oZ4V079I/qPMxg==", "dev": true, "requires": { - "has-flag": "^4.0.0" + "tslib": "^2.1.0" } }, "tslib": { - "version": "2.4.1", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.4.1.tgz", - "integrity": "sha512-tGyy4dAjRIEwI7BzsB0lynWgOpfqjUdq91XXAlIWD2OwKBH7oCl/GZG/HT4BOHrTlPMOASlMQ7veyTqpmRcrNA==", + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.2.tgz", + "integrity": "sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==", "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": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + } } } }, @@ -36663,6 +42610,12 @@ "integrity": "sha512-xgs2NH9AE66ucSq4cNG1nhSFghr5l6tdL15Pk+jl46bmmBapgoaY/AacXyaDznAqmGL99TiLSQgO/XazFSKYeQ==", "dev": true }, + "ip": { + "version": "1.1.8", + "resolved": "https://registry.npmjs.org/ip/-/ip-1.1.8.tgz", + "integrity": "sha512-PuExPYUiu6qMBQb4l06ecm6T6ujzhmh+MeJcW9wa89PoAz5pvd4zPgN5WJV104mb6S2T1AwNIAaB70JNrLQWhg==", + "dev": true + }, "ipaddr.js": { "version": "1.9.1", "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.1.tgz", @@ -37290,16 +43243,26 @@ "textextensions": "^3.2.0" } }, + "jackspeak": { + "version": "2.3.6", + "resolved": "https://registry.npmjs.org/jackspeak/-/jackspeak-2.3.6.tgz", + "integrity": "sha512-N3yCS/NegsOBokc8GAdM8UcmfsKiSS8cipheD/nivzr700H+nsMOxJjQnvwOcRYVuFkdH0wGUvW2WbXGmrZGbQ==", + "dev": true, + "requires": { + "@isaacs/cliui": "^8.0.2", + "@pkgjs/parseargs": "^0.11.0" + } + }, "jake": { - "version": "10.8.5", - "resolved": "https://registry.npmjs.org/jake/-/jake-10.8.5.tgz", - "integrity": "sha512-sVpxYeuAhWt0OTWITwT98oyV0GsXyMlXCF+3L1SuafBVUIr/uILGRB+NqwkzhgXKvoJpDIpQvqkUALgdmQsQxw==", + "version": "10.8.7", + "resolved": "https://registry.npmjs.org/jake/-/jake-10.8.7.tgz", + "integrity": "sha512-ZDi3aP+fG/LchyBzUM804VjddnwfSfsdeYkwt8NcbKRvo4rFkjhs456iLFn3k2ZUWvNe4i48WACDbza8fhq2+w==", "dev": true, "requires": { "async": "^3.2.3", "chalk": "^4.0.2", - "filelist": "^1.0.1", - "minimatch": "^3.0.4" + "filelist": "^1.0.4", + "minimatch": "^3.1.2" }, "dependencies": { "ansi-styles": { @@ -37312,9 +43275,9 @@ } }, "async": { - "version": "3.2.4", - "resolved": "https://registry.npmjs.org/async/-/async-3.2.4.tgz", - "integrity": "sha512-iAB+JbDEGXhyIUavoDl9WP/Jj106Kz9DEn1DPgYw5ruDn0e3Wgi3sKFm55sASdGBNOQB8F59d9qQ7deqrHA8wQ==", + "version": "3.2.5", + "resolved": "https://registry.npmjs.org/async/-/async-3.2.5.tgz", + "integrity": "sha512-baNZyqaaLhyLVKm/DlvdW051MSgO6b8eVfIezl9E5PqWxFgzLm/wQntEW4zOytVburDEr0JlALEpdOFwvErLsg==", "dev": true }, "chalk": { @@ -37359,16 +43322,85 @@ } } }, - "jest-diff": { - "version": "26.6.2", - "resolved": "https://registry.npmjs.org/jest-diff/-/jest-diff-26.6.2.tgz", - "integrity": "sha512-6m+9Z3Gv9wN0WFVasqjCL/06+EFCMTqDEUl/b87HYK2rAPTyfz4ZIuSlPhY51PIQRWx5TaxeF1qmXKe9gfN3sA==", + "jest-diff": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-diff/-/jest-diff-29.7.0.tgz", + "integrity": "sha512-LMIgiIrhigmPrs03JHpxUh2yISK3vLFPkAodPeo0+BuF7wA2FoQbkEg1u8gBYBThncu7e1oEDUfIXVuTqLRUjw==", + "dev": true, + "requires": { + "chalk": "^4.0.0", + "diff-sequences": "^29.6.3", + "jest-get-type": "^29.6.3", + "pretty-format": "^29.7.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.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, + "requires": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + } + }, + "color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "requires": { + "color-name": "~1.1.4" + } + }, + "color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true + }, + "has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true + }, + "supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "requires": { + "has-flag": "^4.0.0" + } + } + } + }, + "jest-get-type": { + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/jest-get-type/-/jest-get-type-29.6.3.tgz", + "integrity": "sha512-zrteXnqYxfQh7l5FHyL38jL39di8H8rHoecLH3JNxH3BwOrBsNeabdap5e0I23lD4HHI8W5VFBZqG4Eaq5LNcw==", + "dev": true + }, + "jest-matcher-utils": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-matcher-utils/-/jest-matcher-utils-29.7.0.tgz", + "integrity": "sha512-sBkD+Xi9DtcChsI3L3u0+N0opgPYnCRPtGcQYrgXmR+hmt/fYfWAL0xRXYU8eWOdfuLgBe0YCW3AFtnRLagq/g==", "dev": true, "requires": { "chalk": "^4.0.0", - "diff-sequences": "^26.6.2", - "jest-get-type": "^26.3.0", - "pretty-format": "^26.6.2" + "jest-diff": "^29.7.0", + "jest-get-type": "^29.6.3", + "pretty-format": "^29.7.0" }, "dependencies": { "ansi-styles": { @@ -37422,22 +43454,21 @@ } } }, - "jest-get-type": { - "version": "26.3.0", - "resolved": "https://registry.npmjs.org/jest-get-type/-/jest-get-type-26.3.0.tgz", - "integrity": "sha512-TpfaviN1R2pQWkIihlfEanwOXK0zcxrKEE4MlU6Tn7keoXdN6/3gK/xl0yEh8DOunn5pOVGKf8hB4R9gVh04ig==", - "dev": true - }, - "jest-matcher-utils": { - "version": "26.6.2", - "resolved": "https://registry.npmjs.org/jest-matcher-utils/-/jest-matcher-utils-26.6.2.tgz", - "integrity": "sha512-llnc8vQgYcNqDrqRDXWwMr9i7rS5XFiCwvh6DTP7Jqa2mqpcCBBlpCbn+trkG0KNhPu/h8rzyBkriOtBstvWhw==", + "jest-message-util": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-message-util/-/jest-message-util-29.7.0.tgz", + "integrity": "sha512-GBEV4GRADeP+qtB2+6u61stea8mGcOT4mCtrYISZwfu9/ISHFJ/5zOMXYbpBE9RsS5+Gb63DW4FgmnKJ79Kf6w==", "dev": true, "requires": { + "@babel/code-frame": "^7.12.13", + "@jest/types": "^29.6.3", + "@types/stack-utils": "^2.0.0", "chalk": "^4.0.0", - "jest-diff": "^26.6.2", - "jest-get-type": "^26.3.0", - "pretty-format": "^26.6.2" + "graceful-fs": "^4.2.9", + "micromatch": "^4.0.4", + "pretty-format": "^29.7.0", + "slash": "^3.0.0", + "stack-utils": "^2.0.3" }, "dependencies": { "ansi-styles": { @@ -37480,6 +43511,12 @@ "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", "dev": true }, + "slash": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz", + "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==", + "dev": true + }, "supports-color": { "version": "7.2.0", "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", @@ -37491,21 +43528,18 @@ } } }, - "jest-message-util": { - "version": "26.6.2", - "resolved": "https://registry.npmjs.org/jest-message-util/-/jest-message-util-26.6.2.tgz", - "integrity": "sha512-rGiLePzQ3AzwUshu2+Rn+UMFk0pHN58sOG+IaJbk5Jxuqo3NYO1U2/MIR4S1sKgsoYSXSzdtSa0TgrmtUwEbmA==", + "jest-util": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-util/-/jest-util-29.7.0.tgz", + "integrity": "sha512-z6EbKajIpqGKU56y5KBUgy1dt1ihhQJgWzUlZHArA/+X2ad7Cb5iF+AK1EWVL/Bo7Rz9uurpqw6SiBCefUbCGA==", "dev": true, "requires": { - "@babel/code-frame": "^7.0.0", - "@jest/types": "^26.6.2", - "@types/stack-utils": "^2.0.0", + "@jest/types": "^29.6.3", + "@types/node": "*", "chalk": "^4.0.0", - "graceful-fs": "^4.2.4", - "micromatch": "^4.0.2", - "pretty-format": "^26.6.2", - "slash": "^3.0.0", - "stack-utils": "^2.0.2" + "ci-info": "^3.2.0", + "graceful-fs": "^4.2.9", + "picomatch": "^2.2.3" }, "dependencies": { "ansi-styles": { @@ -37548,12 +43582,6 @@ "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", "dev": true }, - "slash": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz", - "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==", - "dev": true - }, "supports-color": { "version": "7.2.0", "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", @@ -37565,12 +43593,6 @@ } } }, - "jest-regex-util": { - "version": "26.0.0", - "resolved": "https://registry.npmjs.org/jest-regex-util/-/jest-regex-util-26.0.0.tgz", - "integrity": "sha512-Gv3ZIs/nA48/Zvjrl34bf+oD76JHiGDUxNOVgUjh3j890sblXryjY4rss71fPtD/njchl6PSE2hIhvyWa1eT0A==", - "dev": true - }, "jest-worker": { "version": "27.5.1", "resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-27.5.1.tgz", @@ -38070,9 +44092,9 @@ "dev": true }, "keyv": { - "version": "4.5.0", - "resolved": "https://registry.npmjs.org/keyv/-/keyv-4.5.0.tgz", - "integrity": "sha512-2YvuMsA+jnFGtBareKqgANOEKe1mk3HKiXu2fRmAfyxG0MJAywNhi5ttWA3PMjl4NmpyjZNbFifR2vNjW1znfA==", + "version": "4.5.4", + "resolved": "https://registry.npmjs.org/keyv/-/keyv-4.5.4.tgz", + "integrity": "sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==", "dev": true, "requires": { "json-buffer": "3.0.1" @@ -38101,9 +44123,9 @@ } }, "ky": { - "version": "0.29.0", - "resolved": "https://registry.npmjs.org/ky/-/ky-0.29.0.tgz", - "integrity": "sha512-01TBSOqlHmLfcQhHseugGHLxPtU03OyZWaLDWt5MfzCkijG6xWFvAQPhKVn0cR2MMjYvBP9keQ8A3+rQEhLO5g==", + "version": "0.33.3", + "resolved": "https://registry.npmjs.org/ky/-/ky-0.33.3.tgz", + "integrity": "sha512-CasD9OCEQSFIam2U8efFK81Yeg8vNMTBUqtMOHlrcWQHqUX3HeCl9Dr31u4toV7emlH8Mymk5+9p0lL6mKb/Xw==", "dev": true }, "last-run": { @@ -38301,6 +44323,25 @@ "json5": "^2.1.2" } }, + "locate-app": { + "version": "2.2.13", + "resolved": "https://registry.npmjs.org/locate-app/-/locate-app-2.2.13.tgz", + "integrity": "sha512-1jp6iRFrHKBj9vq6Idb0cSjly+KnCIMbxZ2BBKSEzIC4ZJosv47wnLoiJu2EgOAdjhGvNcy/P2fbDCS/WziI8g==", + "dev": true, + "requires": { + "n12": "1.8.16", + "type-fest": "2.13.0", + "userhome": "1.0.0" + }, + "dependencies": { + "type-fest": { + "version": "2.13.0", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-2.13.0.tgz", + "integrity": "sha512-lPfAm42MxE4/456+QyIaaVBAwgpJb6xZ8PRu09utnhPdWwcyj9vgy6Sq0Z5yNbJ21EdxB5dRU/Qg8bsyAMtlcw==", + "dev": true + } + } + }, "locate-path": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz", @@ -38438,6 +44479,12 @@ "integrity": "sha512-JwObCrNJuT0Nnbuecmqr5DgtuBppuCvGD9lxjFpAzwnVtdGoDQ1zig+5W8k5/6Gcn0gZ3936HDAlGd28i7sOGQ==", "dev": true }, + "lodash.isequal": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/lodash.isequal/-/lodash.isequal-4.5.0.tgz", + "integrity": "sha512-pDo3lu8Jhfjqls6GkMgpahsF9kCyayhgykjyLMNFTKWrpVdAQtYyB4muAMWozBB4ig/dtWAmsMxLEI8wuz+DYQ==", + "dev": true + }, "lodash.isobject": { "version": "3.0.2", "resolved": "https://registry.npmjs.org/lodash.isobject/-/lodash.isobject-3.0.2.tgz", @@ -38550,6 +44597,28 @@ "streamroller": "^3.1.3" } }, + "logform": { + "version": "2.6.0", + "resolved": "https://registry.npmjs.org/logform/-/logform-2.6.0.tgz", + "integrity": "sha512-1ulHeNPp6k/LD8H91o7VYFBng5i1BDE7HoKxVbZiGFidS1Rj65qcywLxX+pVfAPoQJEjRdvKcusKwOupHCVOVQ==", + "dev": true, + "requires": { + "@colors/colors": "1.6.0", + "@types/triple-beam": "^1.3.2", + "fecha": "^4.2.0", + "ms": "^2.1.1", + "safe-stable-stringify": "^2.3.1", + "triple-beam": "^1.3.0" + }, + "dependencies": { + "@colors/colors": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/@colors/colors/-/colors-1.6.0.tgz", + "integrity": "sha512-Ir+AOibqzrIsL6ajt3Rz3LskB7OiMVHqltZmspbW/TJuTVuyOMirVqAkjfY6JISiLHgyNqicAC8AyHHGzNd/dA==", + "dev": true + } + } + }, "loglevel": { "version": "1.8.0", "resolved": "https://registry.npmjs.org/loglevel/-/loglevel-1.8.0.tgz", @@ -38669,12 +44738,6 @@ "integrity": "sha512-8y/eV9QQZCiyn1SprXSrCmqJN0yNRATe+PO8ztwqrvrbdRLA3eYJF0yaR0YayLWkMbsQSKWS9N2gPcGEc4UsZg==", "dev": true }, - "map-obj": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/map-obj/-/map-obj-1.0.1.tgz", - "integrity": "sha512-7N/q3lyZ+LVCp7PzuxrJr4KMbBE2hW7BT7YNia330OFxIf4d3r5zVpicP2650l7CPN6RM9zOJRl3NGpqSiw3Eg==", - "dev": true - }, "map-stream": { "version": "0.0.7", "resolved": "https://registry.npmjs.org/map-stream/-/map-stream-0.0.7.tgz", @@ -39547,6 +45610,18 @@ "integrity": "sha512-bzfL1YUZsP41gmu/qjrEk0Q6i2ix/cVeAhbCbqH9u3zYutS1cLg00qhrD0M2MVdCcx4Sc0UpP2eBWo9rotpq6g==", "dev": true }, + "minipass": { + "version": "7.0.4", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-7.0.4.tgz", + "integrity": "sha512-jYofLM5Dam9279rdkWzqHozUo4ybjdZmCsDHePy5V/PbBcVMiSZR97gmAy45aqi8CK1lG2ECd356FU86avfwUQ==", + "dev": true + }, + "mitt": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/mitt/-/mitt-3.0.0.tgz", + "integrity": "sha512-7dX2/10ITVyqh4aOSVI9gdape+t9l2/8QxHrFmUXu4EEUpdlxl6RudZUPZoc+zuY2hk1j7XxVroIVIan/pD/SQ==", + "dev": true + }, "mixin-deep": { "version": "1.3.2", "resolved": "https://registry.npmjs.org/mixin-deep/-/mixin-deep-1.3.2.tgz", @@ -39557,12 +45632,6 @@ "is-extendable": "^1.0.1" } }, - "mkdirp": { - "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", "resolved": "https://registry.npmjs.org/mkdirp-classic/-/mkdirp-classic-0.5.3.tgz", @@ -39928,6 +45997,12 @@ "global": "^4.4.0" } }, + "n12": { + "version": "1.8.16", + "resolved": "https://registry.npmjs.org/n12/-/n12-1.8.16.tgz", + "integrity": "sha512-CZqHAqbzS0UsaUGkMsL+lMaYLyFr1+/ea+pD8dMziqSjkcuWVWDtgWx9phyfT7C3llqQ2+LwnStSb5afggBMfA==", + "dev": true + }, "nan": { "version": "2.17.0", "resolved": "https://registry.npmjs.org/nan/-/nan-2.17.0.tgz", @@ -40007,6 +46082,12 @@ "integrity": "sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw==", "dev": true }, + "netmask": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/netmask/-/netmask-2.0.2.tgz", + "integrity": "sha512-dBpDMdxv9Irdq66304OLfEmQ9tbNRFnFTuZiLo+bD+r332bBmMJ8GBLXklIXXgxd3+v9+KUnZaUR5PJMa75Gsg==", + "dev": true + }, "next-tick": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/next-tick/-/next-tick-1.1.0.tgz", @@ -40068,6 +46149,12 @@ } } }, + "node-domexception": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/node-domexception/-/node-domexception-1.0.0.tgz", + "integrity": "sha512-/jKZoMpw0F8GRwl4/eLROPA3cfcXtLApP0QzLmUT/HuPCZWyB7IY9ZrMeKw2O/nFIqPQB3PVM9aYm0F312AXDQ==", + "dev": true + }, "node-fetch": { "version": "2.6.7", "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.7.tgz", @@ -40092,6 +46179,18 @@ "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.6.tgz", "integrity": "sha512-PiVXnNuFm5+iYkLBNeq5211hvO38y63T0i2KKh2KnUs3RpzJ+JtODFjkD8yjLwnDkTYF1eKXheUwdssR+NRZdg==" }, + "node-request-interceptor": { + "version": "0.6.3", + "resolved": "https://registry.npmjs.org/node-request-interceptor/-/node-request-interceptor-0.6.3.tgz", + "integrity": "sha512-8I2V7H2Ch0NvW7qWcjmS0/9Lhr0T6x7RD6PDirhvWEkUQvy83x8BA4haYMr09r/rig7hcgYSjYh6cd4U7G1vLA==", + "dev": true, + "requires": { + "@open-draft/until": "^1.0.3", + "debug": "^4.3.0", + "headers-utils": "^1.2.0", + "strict-event-emitter": "^0.1.0" + } + }, "nopt": { "version": "3.0.6", "resolved": "https://registry.npmjs.org/nopt/-/nopt-3.0.6.tgz", @@ -40576,6 +46675,54 @@ "integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==", "dev": true }, + "pac-proxy-agent": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/pac-proxy-agent/-/pac-proxy-agent-7.0.1.tgz", + "integrity": "sha512-ASV8yU4LLKBAjqIPMbrgtaKIvxQri/yh2OpI+S6hVa9JRkUI3Y3NPFbfngDtY7oFtSMD3w31Xns89mDa3Feo5A==", + "dev": true, + "requires": { + "@tootallnate/quickjs-emscripten": "^0.23.0", + "agent-base": "^7.0.2", + "debug": "^4.3.4", + "get-uri": "^6.0.1", + "http-proxy-agent": "^7.0.0", + "https-proxy-agent": "^7.0.2", + "pac-resolver": "^7.0.0", + "socks-proxy-agent": "^8.0.2" + }, + "dependencies": { + "agent-base": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-7.1.0.tgz", + "integrity": "sha512-o/zjMZRhJxny7OyEF+Op8X+efiELC7k7yOjMzgfzVqOzXqkBkWI79YoTdOtsuWd5BWhAGAuOY/Xa6xpiaWXiNg==", + "dev": true, + "requires": { + "debug": "^4.3.4" + } + }, + "https-proxy-agent": { + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-7.0.2.tgz", + "integrity": "sha512-NmLNjm6ucYwtcUmL7JQC1ZQ57LmHP4lT15FQ8D61nak1rO6DH+fz5qNK2Ap5UN4ZapYICE3/0KodcLYSPsPbaA==", + "dev": true, + "requires": { + "agent-base": "^7.0.2", + "debug": "4" + } + } + } + }, + "pac-resolver": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/pac-resolver/-/pac-resolver-7.0.0.tgz", + "integrity": "sha512-Fd9lT9vJbHYRACT8OhCbZBbxr6KRSawSovFpy8nDGshaK99S/EBhVIHp9+crhxrsZOuvLpgL1n23iyPg6Rl2hg==", + "dev": true, + "requires": { + "degenerator": "^5.0.0", + "ip": "^1.1.8", + "netmask": "^2.0.2" + } + }, "parent-module": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz", @@ -40699,6 +46846,24 @@ "integrity": "sha512-4GlJ6rZDhQZFE0DPVKh0e9jmZ5egZfxTkp7bcRDuPlJXbAwhxcl2dINPUAsjLdejqaLsCeg8axcLjIbvBjN4pQ==", "dev": true }, + "path-scurry": { + "version": "1.10.1", + "resolved": "https://registry.npmjs.org/path-scurry/-/path-scurry-1.10.1.tgz", + "integrity": "sha512-MkhCqzzBEpPvxxQ71Md0b1Kk51W01lrYvlMzSUaIzNsODdd7mqhiimSZlr+VegAz5Z6Vzt9Xg2ttE//XBhH3EQ==", + "dev": true, + "requires": { + "lru-cache": "^9.1.1 || ^10.0.0", + "minipass": "^5.0.0 || ^6.0.2 || ^7.0.0" + }, + "dependencies": { + "lru-cache": { + "version": "10.1.0", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.1.0.tgz", + "integrity": "sha512-/1clY/ui8CzjKFyjdvwPWJUYKiFVXG2I2cY0ssG7h4+hwk+XOIX7ZSG9Q7TW8TW3Kp3BUSqgFWBLgL4PJ+Blag==", + "dev": true + } + } + }, "path-to-regexp": { "version": "0.1.7", "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.7.tgz", @@ -40723,6 +46888,12 @@ } } }, + "pathe": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/pathe/-/pathe-1.1.2.tgz", + "integrity": "sha512-whLdWMYL2TwI08hn8/ZqAbrVemu0LNaNNJZX73O6qaIdCTfXutsLhMkjdENX0qhsQ9uIimo4/aQOmXkoon2nDQ==", + "dev": true + }, "pathval": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/pathval/-/pathval-1.1.1.tgz", @@ -40847,39 +47018,20 @@ "dev": true }, "pretty-format": { - "version": "26.6.2", - "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-26.6.2.tgz", - "integrity": "sha512-7AeGuCYNGmycyQbCqd/3PWH4eOoX/OiCa0uphp57NVTeAGdJGaAliecxwBDHYQCIvrW7aDBZCYeNTP/WX69mkg==", + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-29.7.0.tgz", + "integrity": "sha512-Pdlw/oPxN+aXdmM9R00JVC9WVFoCLTKJvDVLgmJ+qAffBMxsV85l/Lu7sNx4zSzPyoL2euImuEwHhOXdEgNFZQ==", "dev": true, "requires": { - "@jest/types": "^26.6.2", - "ansi-regex": "^5.0.0", - "ansi-styles": "^4.0.0", - "react-is": "^17.0.1" + "@jest/schemas": "^29.6.3", + "ansi-styles": "^5.0.0", + "react-is": "^18.0.0" }, "dependencies": { "ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "dev": true, - "requires": { - "color-convert": "^2.0.1" - } - }, - "color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "dev": true, - "requires": { - "color-name": "~1.1.4" - } - }, - "color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "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 } } @@ -40944,6 +47096,49 @@ "ipaddr.js": "1.9.1" } }, + "proxy-agent": { + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/proxy-agent/-/proxy-agent-6.3.1.tgz", + "integrity": "sha512-Rb5RVBy1iyqOtNl15Cw/llpeLH8bsb37gM1FUfKQ+Wck6xHlbAhWGUFiTRHtkjqGTA5pSHz6+0hrPW/oECihPQ==", + "dev": true, + "requires": { + "agent-base": "^7.0.2", + "debug": "^4.3.4", + "http-proxy-agent": "^7.0.0", + "https-proxy-agent": "^7.0.2", + "lru-cache": "^7.14.1", + "pac-proxy-agent": "^7.0.1", + "proxy-from-env": "^1.1.0", + "socks-proxy-agent": "^8.0.2" + }, + "dependencies": { + "agent-base": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-7.1.0.tgz", + "integrity": "sha512-o/zjMZRhJxny7OyEF+Op8X+efiELC7k7yOjMzgfzVqOzXqkBkWI79YoTdOtsuWd5BWhAGAuOY/Xa6xpiaWXiNg==", + "dev": true, + "requires": { + "debug": "^4.3.4" + } + }, + "https-proxy-agent": { + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-7.0.2.tgz", + "integrity": "sha512-NmLNjm6ucYwtcUmL7JQC1ZQ57LmHP4lT15FQ8D61nak1rO6DH+fz5qNK2Ap5UN4ZapYICE3/0KodcLYSPsPbaA==", + "dev": true, + "requires": { + "agent-base": "^7.0.2", + "debug": "4" + } + }, + "lru-cache": { + "version": "7.18.3", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-7.18.3.tgz", + "integrity": "sha512-jumlc0BIUrS3qJGgIkWZsyfAM7NCWiBcCDhnd+3NNM5KbBmLTgHVfWBcg6W+rLUsIpzpERPsvwUP7CckAQSOoA==", + "dev": true + } + } + }, "proxy-from-env": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.1.0.tgz", @@ -40965,12 +47160,6 @@ "event-stream": "=3.3.4" } }, - "pseudomap": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/pseudomap/-/pseudomap-1.0.2.tgz", - "integrity": "sha512-b/YwNhb8lk1Zz2+bXXpS/LK9OisiZZ1SNsSLxN1x2OXVEhW2Ckr/7mWE5vrC1ZTiJlD9g19jWszTmJsB+oEpFQ==", - "dev": true - }, "psl": { "version": "1.9.0", "resolved": "https://registry.npmjs.org/psl/-/psl-1.9.0.tgz", @@ -41101,6 +47290,12 @@ "integrity": "sha512-FIqgj2EUvTa7R50u0rGsyTftzjYmv/a3hO345bZNrqabNqjtgiDMgmo4mkUjd+nzU5oF3dClKqFIPUKybUyqoQ==", "dev": true }, + "queue-tick": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/queue-tick/-/queue-tick-1.0.1.tgz", + "integrity": "sha512-kJt5qhMxoszgU/62PLP1CJytzd2NKetjSRnyuj31fDd3Rlcz3fzlFdFLD1SItunPwyqEOkca6GbV612BWfaBag==", + "dev": true + }, "quick-lru": { "version": "5.1.1", "resolved": "https://registry.npmjs.org/quick-lru/-/quick-lru-5.1.1.tgz", @@ -41133,9 +47328,9 @@ } }, "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==", + "version": "18.2.0", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-18.2.0.tgz", + "integrity": "sha512-xWGDIW6x921xtzPkhiULtthJHoJvBbF3q26fzloPCK0hsvxtPVelvftw3zjbHWSkR2km9Z+4uxbDDK/6Zw9B8w==", "dev": true }, "read-pkg": { @@ -41795,6 +47990,12 @@ "mri": "^1.1.0" } }, + "safaridriver": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/safaridriver/-/safaridriver-0.1.2.tgz", + "integrity": "sha512-4R309+gWflJktzPXBQCobbWEHlzC4aK3a+Ov3tz2Ib2aBxiwd11phkdIBH1l0EO22x24CJMUQkpKFumRriCSRg==", + "dev": true + }, "safe-buffer": { "version": "5.2.1", "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", @@ -41826,6 +48027,12 @@ "is-regex": "^1.1.4" } }, + "safe-stable-stringify": { + "version": "2.4.3", + "resolved": "https://registry.npmjs.org/safe-stable-stringify/-/safe-stable-stringify-2.4.3.tgz", + "integrity": "sha512-e2bDA2WJT0wxseVd4lsDP4+3ONX6HpMXQa1ZhFQ7SU+GjvORCmShbCMltrtIDfkYhVHrOcPtj+KhmDBdPdZD1g==", + "dev": true + }, "safer-buffer": { "version": "2.1.2", "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", @@ -42034,6 +48241,18 @@ "integrity": "sha512-KiKBS8AnWGEyLzofFfmvKwpdPzqiy16LvQfK3yv/fVH7Bj13/wl3JSR1J+rfgRE9q7xUJK4qvgS8raSOeLUehw==", "dev": true }, + "set-function-length": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/set-function-length/-/set-function-length-1.2.0.tgz", + "integrity": "sha512-4DBHDoyHlM1IRPGYcoxexgh67y4ueR53FKV1yyxwFMY7aCqcN/38M1+SwZ/qJQ8iLv7+ck385ot4CcisOAPT9w==", + "requires": { + "define-data-property": "^1.1.1", + "function-bind": "^1.1.2", + "get-intrinsic": "^1.2.2", + "gopd": "^1.0.1", + "has-property-descriptors": "^1.0.1" + } + }, "set-value": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/set-value/-/set-value-2.0.1.tgz", @@ -42191,6 +48410,12 @@ } } }, + "smart-buffer": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/smart-buffer/-/smart-buffer-4.2.0.tgz", + "integrity": "sha512-94hK0Hh8rPqQl2xXc3HsaBoOXKV20MToPkcXvwbISWLEs+64sBq5kFgn2kJDHb1Pry9yrP0dxrCI9RRci7RXKg==", + "dev": true + }, "snapdragon": { "version": "0.8.2", "resolved": "https://registry.npmjs.org/snapdragon/-/snapdragon-0.8.2.tgz", @@ -42399,6 +48624,46 @@ "debug": "~4.3.1" } }, + "socks": { + "version": "2.7.1", + "resolved": "https://registry.npmjs.org/socks/-/socks-2.7.1.tgz", + "integrity": "sha512-7maUZy1N7uo6+WVEX6psASxtNlKaNVMlGQKkG/63nEDdLOWNbiUMoLK7X4uYoLhQstau72mLgfEWcXcwsaHbYQ==", + "dev": true, + "requires": { + "ip": "^2.0.0", + "smart-buffer": "^4.2.0" + }, + "dependencies": { + "ip": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ip/-/ip-2.0.0.tgz", + "integrity": "sha512-WKa+XuLG1A1R0UWhl2+1XQSi+fZWMsYKffMZTTYsiZaUD8k2yDAj5atimTUD2TZkyCkNEeYE5NhFZmupOGtjYQ==", + "dev": true + } + } + }, + "socks-proxy-agent": { + "version": "8.0.2", + "resolved": "https://registry.npmjs.org/socks-proxy-agent/-/socks-proxy-agent-8.0.2.tgz", + "integrity": "sha512-8zuqoLv1aP/66PHF5TqwJ7Czm3Yv32urJQHrVyhD7mmA6d61Zv8cIXQYPTWwmg6qlupnPvs/QKDmfa4P/qct2g==", + "dev": true, + "requires": { + "agent-base": "^7.0.2", + "debug": "^4.3.4", + "socks": "^2.7.1" + }, + "dependencies": { + "agent-base": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-7.1.0.tgz", + "integrity": "sha512-o/zjMZRhJxny7OyEF+Op8X+efiELC7k7yOjMzgfzVqOzXqkBkWI79YoTdOtsuWd5BWhAGAuOY/Xa6xpiaWXiNg==", + "dev": true, + "requires": { + "debug": "^4.3.4" + } + } + } + }, "source-list-map": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/source-list-map/-/source-list-map-2.0.1.tgz", @@ -42525,26 +48790,10 @@ } }, "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" - }, - "dependencies": { - "readable-stream": { - "version": "3.6.0", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.0.tgz", - "integrity": "sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA==", - "dev": true, - "requires": { - "inherits": "^2.0.3", - "string_decoder": "^1.1.1", - "util-deprecate": "^1.0.1" - } - } - } + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/split2/-/split2-4.2.0.tgz", + "integrity": "sha512-UcjcJOWknrNkF6PLX83qcHM6KHgVKNkV62Y8a5uYDVv9ydGQVwAHMKqHdJje1VTWpljG0WYpCDhrCdAOYH4TWg==", + "dev": true }, "sprintf-js": { "version": "1.0.3", @@ -42576,9 +48825,9 @@ "dev": true }, "stack-utils": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/stack-utils/-/stack-utils-2.0.5.tgz", - "integrity": "sha512-xrQcmYhOsn/1kX+Vraq+7j4oE2j/6BFscZ0etmYg81xuM8Gq0022Pxb8+IqgOFUIaxHs0KaSb7T1+OegiNrNFA==", + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/stack-utils/-/stack-utils-2.0.6.tgz", + "integrity": "sha512-XlkWvfIm6RmsWtNJx+uqtKLS8eqFbxUg0ZzLXqY0caEy9l7hruX8IpiDnjsLavoBgqCCR71TqWO8MaXYheJ3RQ==", "dev": true, "requires": { "escape-string-regexp": "^2.0.0" @@ -42741,6 +48990,22 @@ } } }, + "streamx": { + "version": "2.15.6", + "resolved": "https://registry.npmjs.org/streamx/-/streamx-2.15.6.tgz", + "integrity": "sha512-q+vQL4AAz+FdfT137VF69Cc/APqUbxy+MDOImRrMvchJpigHj9GksgDU2LYbO9rx7RX6osWgxJB2WxhYv4SZAw==", + "dev": true, + "requires": { + "fast-fifo": "^1.1.0", + "queue-tick": "^1.0.1" + } + }, + "strict-event-emitter": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/strict-event-emitter/-/strict-event-emitter-0.1.0.tgz", + "integrity": "sha512-8hSYfU+WKLdNcHVXJ0VxRXiPESalzRe7w1l8dg9+/22Ry+iZQUoQuoJ27R30GMD1TiyYINWsIEGY05WrskhSKw==", + "dev": true + }, "string_decoder": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", @@ -42775,6 +49040,17 @@ "strip-ansi": "^6.0.1" } }, + "string-width-cjs": { + "version": "npm:string-width@4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "dev": true, + "requires": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + } + }, "string.prototype.trimend": { "version": "1.0.5", "resolved": "https://registry.npmjs.org/string.prototype.trimend/-/string.prototype.trimend-1.0.5.tgz", @@ -42816,6 +49092,15 @@ "ansi-regex": "^5.0.1" } }, + "strip-ansi-cjs": { + "version": "npm:strip-ansi@6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dev": true, + "requires": { + "ansi-regex": "^5.0.1" + } + }, "strip-bom": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-3.0.0.tgz", @@ -42834,18 +49119,18 @@ "integrity": "sha512-7FCwGGmx8mD5xQd3RPUvnSpUXHM3BWuzjtpD4TXsfcZ9EL4azvVVUscFYwD9nx8Kh+uCBC00XBtAykoMHwTh8Q==", "dev": true }, + "strip-final-newline": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/strip-final-newline/-/strip-final-newline-3.0.0.tgz", + "integrity": "sha512-dOESqjYr96iWYylGObzd39EuNTa5VJxyvVAEm5Jnh7KGo75V43Hk1odPQkNDyXNmUR6k+gEiDVXnjB8HJ3crXw==", + "dev": true + }, "strip-json-comments": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-5.0.0.tgz", "integrity": "sha512-V1LGY4UUo0jgwC+ELQ2BNWfPa17TIuwBLg+j1AA/9RPzKINl1lhxVEu2r+ZTTO8aetIsUzE5Qj6LMSBkoGYKKw==", "dev": true }, - "suffix": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/suffix/-/suffix-0.1.1.tgz", - "integrity": "sha512-j5uf6MJtMCfC4vBe5LFktSe4bGyNTBk7I2Kdri0jeLrcv5B9pWfxVa5JQpoxgtR8vaVB7bVxsWgnfQbX5wkhAA==", - "dev": true - }, "supports-color": { "version": "5.5.0", "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", @@ -43332,6 +49617,12 @@ "integrity": "sha512-WZGXGstmCWgeevgTL54hrCuw1dyMQIzWy7ZfqRJfSmJZBwklI15egmQytFP6bPidmw3M8d5yEowl1niq4vmqZw==", "dev": true }, + "triple-beam": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/triple-beam/-/triple-beam-1.4.1.tgz", + "integrity": "sha512-aZbgViZrg1QNcG+LULa7nhZpJTZSLm/mXnHXnbAbjmN5aSa0y7V+wvv6+4WaBtpISJzThKy+PIPxc1Nq1EJ9mg==", + "dev": true + }, "trough": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/trough/-/trough-2.1.0.tgz", @@ -43429,6 +49720,7 @@ "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.8.4.tgz", "integrity": "sha512-QCh+85mCy+h0IGff8r5XWzOVSbBO+KfeYrMQh7NJ58QujwcE22u+NUSmUxqF+un70P9GXKxa2HCNiTTMJknyjQ==", "dev": true, + "optional": true, "peer": true }, "typescript-compare": { @@ -43525,6 +49817,12 @@ "integrity": "sha512-UR1khWeAjugW3548EfQmL9Z7pGMlBgXteQpr1IZeZBtnkCJQJIJ1Scj0mb9wQaPvUZ9Q17XqW6TIaPchJkyfqw==", "dev": true }, + "undici-types": { + "version": "5.26.5", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-5.26.5.tgz", + "integrity": "sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA==", + "dev": true + }, "unicode-canonical-property-names-ecmascript": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/unicode-canonical-property-names-ecmascript/-/unicode-canonical-property-names-ecmascript-2.0.0.tgz", @@ -43815,6 +50113,12 @@ "integrity": "sha512-cwESVXlO3url9YWlFW/TA9cshCEhtu7IKJ/p5soJ/gGpj7vbvFrAY/eIioQ6Dw23KjZhYgiIo8HOs1nQ2vr/oQ==", "dev": true }, + "userhome": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/userhome/-/userhome-1.0.0.tgz", + "integrity": "sha512-ayFKY3H+Pwfy4W98yPdtH1VqH4psDeyW8lYYFzfecR9d6hqLpqhecktvYR3SEEXt7vG0S1JEpciI3g94pMErig==", + "dev": true + }, "util": { "version": "0.12.5", "resolved": "https://registry.npmjs.org/util/-/util-0.12.5.tgz", @@ -44198,6 +50502,74 @@ "he": "^1.2.0" } }, + "wait-port": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/wait-port/-/wait-port-1.1.0.tgz", + "integrity": "sha512-3e04qkoN3LxTMLakdqeWth8nih8usyg+sf1Bgdf9wwUkp05iuK1eSY/QpLvscT/+F/gA89+LpUmmgBtesbqI2Q==", + "dev": true, + "requires": { + "chalk": "^4.1.2", + "commander": "^9.3.0", + "debug": "^4.3.4" + }, + "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.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, + "requires": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + } + }, + "color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "requires": { + "color-name": "~1.1.4" + } + }, + "color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true + }, + "commander": { + "version": "9.5.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-9.5.0.tgz", + "integrity": "sha512-KRs7WVDKg86PWiuAqhDrAQnTXZKraVcCc6vFdL14qrZ/DcWwuRo7VoiYXalXO7S5GKpqYiVEwCbgFDfxNHKJBQ==", + "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" + } + } + } + }, "walk": { "version": "2.3.15", "resolved": "https://registry.npmjs.org/walk/-/walk-2.3.15.tgz", @@ -44226,89 +50598,133 @@ "defaults": "^1.0.3" } }, + "web-streams-polyfill": { + "version": "4.0.0-beta.3", + "resolved": "https://registry.npmjs.org/web-streams-polyfill/-/web-streams-polyfill-4.0.0-beta.3.tgz", + "integrity": "sha512-QW95TCTaHmsYfHDybGMwO5IJIM93I/6vTRk+daHTWFPhwh+C8Cg7j7XyKrwrj8Ib6vYXe0ocYNrmzY4xAAN6ug==", + "dev": true + }, "webdriver": { - "version": "7.5.3", - "resolved": "https://registry.npmjs.org/webdriver/-/webdriver-7.5.3.tgz", - "integrity": "sha512-cDTn/hYj5x8BYwXxVb/WUwqGxrhCMP2rC8ttIWCfzmiVtmOnJGulC7CyxU3+p9Q5R/gIKTzdJOss16dhb+5CoA==", + "version": "8.29.1", + "resolved": "https://registry.npmjs.org/webdriver/-/webdriver-8.29.1.tgz", + "integrity": "sha512-D3gkbDUxFKBJhNHRfMriWclooLbNavVQC1MRvmENAgPNKaHnFn+M+WtP9K2sEr0XczLGNlbOzT7CKR9K5UXKXA==", "dev": true, "requires": { - "@wdio/config": "7.5.3", - "@wdio/logger": "7.5.3", - "@wdio/protocols": "7.5.3", - "@wdio/types": "7.5.3", - "@wdio/utils": "7.5.3", - "got": "^11.0.2", - "lodash.merge": "^4.6.1" + "@types/node": "^20.1.0", + "@types/ws": "^8.5.3", + "@wdio/config": "8.29.1", + "@wdio/logger": "8.28.0", + "@wdio/protocols": "8.24.12", + "@wdio/types": "8.29.1", + "@wdio/utils": "8.29.1", + "deepmerge-ts": "^5.1.0", + "got": "^12.6.1", + "ky": "^0.33.0", + "ws": "^8.8.0" }, "dependencies": { - "@wdio/logger": { - "version": "7.5.3", - "resolved": "https://registry.npmjs.org/@wdio/logger/-/logger-7.5.3.tgz", - "integrity": "sha512-r9EADpm0ncS1bDQSWi/nhF9C59/WNLbdAAFlo782E9ItFCpDhNit3aQP9vETv1vFxJRjUIM8Fw/HW8zwPadkbw==", - "dev": true, - "requires": { - "chalk": "^4.0.0", - "loglevel": "^1.6.0", - "loglevel-plugin-prefix": "^0.8.4", - "strip-ansi": "^6.0.0" - } + "@sindresorhus/is": { + "version": "5.6.0", + "resolved": "https://registry.npmjs.org/@sindresorhus/is/-/is-5.6.0.tgz", + "integrity": "sha512-TV7t8GKYaJWsn00tFDqBw8+Uqmr8A0fRU1tvTQhyZzGv0sJCGRQL3JGMI3ucuKo3XIZdUP+Lx7/gh2t3lewy7g==", + "dev": true }, - "@wdio/types": { - "version": "7.5.3", - "resolved": "https://registry.npmjs.org/@wdio/types/-/types-7.5.3.tgz", - "integrity": "sha512-jmumhKBhNDABnpmrshYLEcdE9WoP5tmynsDNbDABlb/W8FFiLySQOejukhYIL9CLys4zXerV3/edks0SCzHOiQ==", + "@szmarczak/http-timer": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/@szmarczak/http-timer/-/http-timer-5.0.1.tgz", + "integrity": "sha512-+PmQX0PiAYPMeVYe237LJAYvOMYW1j2rH5YROyS3b4CTVJum34HfRvKvAzozHAQG0TnHNdUfY9nCeUyRAs//cw==", "dev": true, "requires": { - "got": "^11.8.1" + "defer-to-connect": "^2.0.1" } }, - "ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "cacheable-lookup": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/cacheable-lookup/-/cacheable-lookup-7.0.0.tgz", + "integrity": "sha512-+qJyx4xiKra8mZrcwhjMRMUhD5NR1R8esPkzIYxX96JiecFoxAXFuz/GpR3+ev4PE1WamHip78wV0vcmPQtp8w==", + "dev": true + }, + "cacheable-request": { + "version": "10.2.14", + "resolved": "https://registry.npmjs.org/cacheable-request/-/cacheable-request-10.2.14.tgz", + "integrity": "sha512-zkDT5WAF4hSSoUgyfg5tFIxz8XQK+25W/TLVojJTMKBaxevLBBtLxgqguAuVQB8PVW79FVjHcU+GJ9tVbDZ9mQ==", "dev": true, "requires": { - "color-convert": "^2.0.1" + "@types/http-cache-semantics": "^4.0.2", + "get-stream": "^6.0.1", + "http-cache-semantics": "^4.1.1", + "keyv": "^4.5.3", + "mimic-response": "^4.0.0", + "normalize-url": "^8.0.0", + "responselike": "^3.0.0" } }, - "chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "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 + }, + "got": { + "version": "12.6.1", + "resolved": "https://registry.npmjs.org/got/-/got-12.6.1.tgz", + "integrity": "sha512-mThBblvlAF1d4O5oqyvN+ZxLAYwIJK7bpMxgYqPD9okW0C3qm5FFn7k811QrcuEBwaogR3ngOFoCfs6mRv7teQ==", "dev": true, "requires": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" + "@sindresorhus/is": "^5.2.0", + "@szmarczak/http-timer": "^5.0.1", + "cacheable-lookup": "^7.0.0", + "cacheable-request": "^10.2.8", + "decompress-response": "^6.0.0", + "form-data-encoder": "^2.1.2", + "get-stream": "^6.0.1", + "http2-wrapper": "^2.1.10", + "lowercase-keys": "^3.0.0", + "p-cancelable": "^3.0.0", + "responselike": "^3.0.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==", + "http2-wrapper": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/http2-wrapper/-/http2-wrapper-2.2.1.tgz", + "integrity": "sha512-V5nVw1PAOgfI3Lmeaj2Exmeg7fenjhRUgz1lPSezy1CuhPYbgQtbQj4jZfEAEMlaL+vupsvhjqCyjzob0yxsmQ==", "dev": true, "requires": { - "color-name": "~1.1.4" + "quick-lru": "^5.1.1", + "resolve-alpn": "^1.2.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==", + "lowercase-keys": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/lowercase-keys/-/lowercase-keys-3.0.0.tgz", + "integrity": "sha512-ozCC6gdQ+glXOQsveKD0YsDy8DSQFjDTz4zyzEHNV5+JP5D62LmfDZ6o1cycFx9ouG940M5dE8C8CTewdj2YWQ==", "dev": true }, - "has-flag": { + "mimic-response": { "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "resolved": "https://registry.npmjs.org/mimic-response/-/mimic-response-4.0.0.tgz", + "integrity": "sha512-e5ISH9xMYU0DzrT+jl8q2ze9D6eWBto+I8CNpe+VI+K2J/F/k3PdkdTdz4wvGVH4NTpo+NRYTVIuMQEMMcsLqg==", "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==", + "normalize-url": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/normalize-url/-/normalize-url-8.0.0.tgz", + "integrity": "sha512-uVFpKhj5MheNBJRTiMZ9pE/7hD1QTeEvugSJW/OmLzAp78PB5O6adfMNTvmfKhXBkvCzC+rqifWcVYpGFwTjnw==", + "dev": true + }, + "p-cancelable": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/p-cancelable/-/p-cancelable-3.0.0.tgz", + "integrity": "sha512-mlVgR3PGuzlo0MmTdk4cXqXWlwQDLnONTAg6sm62XkMJEiRxN3GL3SffkYvqwonbkJBcrI7Uvv5Zh9yjvn2iUw==", + "dev": true + }, + "responselike": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/responselike/-/responselike-3.0.0.tgz", + "integrity": "sha512-40yHxbNcl2+rzXvZuVkrYohathsSJlMTXKryG5y8uciHv1+xDLHQpgjG64JUO9nrEq2jGLH6IZ8BcZyw3wrweg==", "dev": true, "requires": { - "has-flag": "^4.0.0" + "lowercase-keys": "^3.0.0" } } } @@ -44883,44 +51299,26 @@ "is-typed-array": "^1.1.9" } }, - "wide-align": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/wide-align/-/wide-align-1.1.3.tgz", - "integrity": "sha512-QGkOQc8XL6Bt5PwnsExKBPuMKBxnGxWWW3fU55Xt4feHozMUhdUMaBCk290qpm/wG5u/RSKzwdAC4i51YigihA==", + "winston-transport": { + "version": "4.6.0", + "resolved": "https://registry.npmjs.org/winston-transport/-/winston-transport-4.6.0.tgz", + "integrity": "sha512-wbBA9PbPAHxKiygo7ub7BYRiKxms0tpfU2ljtWzb3SjRjv5yl6Ozuy/TkXf00HTAt+Uylo3gSkNwzc4ME0wiIg==", "dev": true, "requires": { - "string-width": "^1.0.2 || 2" + "logform": "^2.3.2", + "readable-stream": "^3.6.0", + "triple-beam": "^1.3.0" }, "dependencies": { - "ansi-regex": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-3.0.1.tgz", - "integrity": "sha512-+O9Jct8wf++lXxxFc4hc8LsjaSq0HFzzL7cVsw8pRDIPdjKD2mT4ytDZlLuSBZ4cLKZFXIrMGO7DbQCtMJJMKw==", - "dev": true - }, - "is-fullwidth-code-point": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz", - "integrity": "sha512-VHskAKYM8RfSFXwee5t5cbN5PZeq1Wrh6qd5bkyiXIf6UQcN6w/A0eXM9r6t8d+GYOh+o6ZhiEnb88LN/Y8m2w==", - "dev": true - }, - "string-width": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-2.1.1.tgz", - "integrity": "sha512-nOqH59deCq9SRHlxq1Aw85Jnt4w6KvLKqWVik6oA9ZklXLNIOlqg4F2yrT1MVaTjAqvVwdfeZ7w7aCvJD7ugkw==", - "dev": true, - "requires": { - "is-fullwidth-code-point": "^2.0.0", - "strip-ansi": "^4.0.0" - } - }, - "strip-ansi": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-4.0.0.tgz", - "integrity": "sha512-4XaJ2zQdCzROZDivEVIDPkcQn8LMFSa8kj8Gxb/Lnwzv9A8VctNZ+lfivC/sV3ivW8ElJTERXZoPBRrZKkNKow==", + "readable-stream": { + "version": "3.6.2", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz", + "integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==", "dev": true, "requires": { - "ansi-regex": "^3.0.0" + "inherits": "^2.0.3", + "string_decoder": "^1.1.1", + "util-deprecate": "^1.0.1" } } } @@ -44980,6 +51378,43 @@ } } }, + "wrap-ansi-cjs": { + "version": "npm:wrap-ansi@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", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + }, + "dependencies": { + "ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "requires": { + "color-convert": "^2.0.1" + } + }, + "color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "requires": { + "color-name": "~1.1.4" + } + }, + "color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true + } + } + }, "wrappy": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", @@ -45069,94 +51504,6 @@ } } }, - "yarn-install": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/yarn-install/-/yarn-install-1.0.0.tgz", - "integrity": "sha512-VO1u181msinhPcGvQTVMnHVOae8zjX/NSksR17e6eXHRveDvHCF5mGjh9hkN8mzyfnCqcBe42LdTs7bScuTaeg==", - "dev": true, - "requires": { - "cac": "^3.0.3", - "chalk": "^1.1.3", - "cross-spawn": "^4.0.2" - }, - "dependencies": { - "ansi-regex": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz", - "integrity": "sha512-TIGnTpdo+E3+pCyAluZvtED5p5wCqLdezCyhPZzKPcxvFplEt4i+W7OONCKgeZFT3+y5NZZfOOS/Bdcanm1MYA==", - "dev": true - }, - "ansi-styles": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-2.2.1.tgz", - "integrity": "sha512-kmCevFghRiWM7HB5zTPULl4r9bVFSWjz62MhqizDGUrq2NWuNMQyuv4tHHoKJHs69M/MF64lEcHdYIocrdWQYA==", - "dev": true - }, - "chalk": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz", - "integrity": "sha512-U3lRVLMSlsCfjqYPbLyVv11M9CPW4I728d6TCKMAOJueEeB9/8o+eSsMnxPJD+Q+K909sdESg7C+tIkoH6on1A==", - "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" - } - }, - "cross-spawn": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-4.0.2.tgz", - "integrity": "sha512-yAXz/pA1tD8Gtg2S98Ekf/sewp3Lcp3YoFKJ4Hkp5h5yLWnKVTDU0kwjKJ8NDCYcfTLfyGkzTikst+jWypT1iA==", - "dev": true, - "requires": { - "lru-cache": "^4.0.1", - "which": "^1.2.9" - } - }, - "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, - "requires": { - "pseudomap": "^1.0.2", - "yallist": "^2.1.2" - } - }, - "strip-ansi": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz", - "integrity": "sha512-VhumSSbBqDTP8p2ZLKj40UjBCV4+v8bUSEpUb4KjRgWk9pbqGF4REFj6KEagidb2f/M6AzC0EmFyDNGaw9OCzg==", - "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": "sha512-KKNVtd6pCYgPIKU4cp2733HWYCpplQhddZLBUryaAHou723x+FRzQ5Df824Fj+IyyuiQTRoub4SnIFfIcrp70g==", - "dev": true - }, - "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" - } - }, - "yallist": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/yallist/-/yallist-2.1.2.tgz", - "integrity": "sha512-ncTzHV7NvsQZkYe1DW7cbDLm0YpzHmZF5r/iyP3ZnQtMiJ+pjzisCiMNI+Sj+xQF5pXhSHxSB3uDbsBTzY/c2A==", - "dev": true - } - } - }, "yauzl": { "version": "2.10.0", "resolved": "https://registry.npmjs.org/yauzl/-/yauzl-2.10.0.tgz", diff --git a/package.json b/package.json index 38697197d0b..9d4347eeab1 100644 --- a/package.json +++ b/package.json @@ -33,17 +33,16 @@ "author": "the prebid.js contributors", "license": "Apache-2.0", "engines": { - "node": ">=8.9.0" + "node": ">=12.0.0" }, "devDependencies": { "@babel/eslint-parser": "^7.16.5", - "@wdio/browserstack-service": "~7.16.0", - "@wdio/cli": "~7.5.2", - "@wdio/concise-reporter": "~7.5.2", - "@wdio/local-runner": "~7.5.2", - "@wdio/mocha-framework": "~7.5.2", - "@wdio/spec-reporter": "~7.19.0", - "@wdio/sync": "~7.5.2", + "@wdio/browserstack-service": "^8.29.0", + "@wdio/cli": "^8.29.0", + "@wdio/concise-reporter": "^8.29.0", + "@wdio/local-runner": "^8.29.0", + "@wdio/mocha-framework": "^8.29.0", + "@wdio/spec-reporter": "^8.29.0", "ajv": "6.12.3", "assert": "^2.0.0", "babel-loader": "^8.0.5", diff --git a/test/helpers/testing-utils.js b/test/helpers/testing-utils.js index 1336a90ecbf..3f59411ff6c 100644 --- a/test/helpers/testing-utils.js +++ b/test/helpers/testing-utils.js @@ -7,23 +7,23 @@ const utils = { testPageURL: function(name) { return `${utils.protocol}://${utils.host}:9999/test/pages/${name}` }, - waitForElement: function(elementRef, time = DEFAULT_TIMEOUT) { + waitForElement: async function(elementRef, time = DEFAULT_TIMEOUT) { let element = $(elementRef); - element.waitForExist({timeout: time}); + await element.waitForExist({timeout: time}); }, - switchFrame: function(frameRef) { - let iframe = $(frameRef); + switchFrame: async function(frameRef) { + let iframe = await $(frameRef); browser.switchToFrame(iframe); }, - loadAndWaitForElement(url, selector, pause = 3000, timeout = DEFAULT_TIMEOUT, retries = 3, attempt = 1) { - browser.url(url); - browser.pause(pause); + async loadAndWaitForElement(url, selector, pause = 3000, timeout = DEFAULT_TIMEOUT, retries = 3, attempt = 1) { + await browser.url(url); + await browser.pause(pause); if (selector != null) { try { - utils.waitForElement(selector, timeout); + await utils.waitForElement(selector, timeout); } catch (e) { if (attempt < retries) { - utils.loadAndWaitForElement(url, selector, pause, timeout, retries, attempt + 1); + await utils.loadAndWaitForElement(url, selector, pause, timeout, retries, attempt + 1); } } } @@ -35,14 +35,15 @@ const utils = { fn.call(this); if (expectGAMCreative) { expectGAMCreative = expectGAMCreative === true ? waitFor : expectGAMCreative; - it(`should render GAM creative`, () => { - utils.switchFrame(expectGAMCreative); + it(`should render GAM creative`, async () => { + await utils.switchFrame(expectGAMCreative); const creative = [ '> a > img', // banner '> div[class="card"]' // native ].map((child) => `body > div[class="GoogleActiveViewElement"] ${child}`) .join(', '); - expect($(creative).isExisting()).to.be.true; + const existing = await $(creative).isExisting(); + expect(existing).to.be.true; }); } }); diff --git a/test/spec/e2e/banner/basic_banner_ad.spec.js b/test/spec/e2e/banner/basic_banner_ad.spec.js index e8103581d9d..511b1002d80 100644 --- a/test/spec/e2e/banner/basic_banner_ad.spec.js +++ b/test/spec/e2e/banner/basic_banner_ad.spec.js @@ -19,8 +19,8 @@ setupTest({ waitFor: CREATIVE_IFRAME_CSS_SELECTOR, expectGAMCreative: true }, 'Prebid.js Banner Ad Unit Test', function () { - it('should load the targeting keys with correct values', function () { - const result = browser.execute(function () { + it('should load the targeting keys with correct values', async function () { + const result = await browser.execute(function () { return window.pbjs.getAdserverTargeting('div-gpt-ad-1460505748561-1'); }); const targetingKeys = result['div-gpt-ad-1460505748561-1']; diff --git a/test/spec/e2e/instream/basic_instream_video_ad.spec.js b/test/spec/e2e/instream/basic_instream_video_ad.spec.js index 02d218f9175..ca5296f050c 100644 --- a/test/spec/e2e/instream/basic_instream_video_ad.spec.js +++ b/test/spec/e2e/instream/basic_instream_video_ad.spec.js @@ -18,8 +18,8 @@ setupTest({ url: TEST_PAGE_URL, waitFor: ALERT_BOX_CSS_SELECTOR, }, 'Prebid.js Instream Video Ad Test', function () { - it('should load the targeting keys with correct values', function () { - const result = browser.execute(function () { + it('should load the targeting keys with correct values', async function () { + const result = await browser.execute(function () { return window.top.pbjs.getAdserverTargeting('video1'); }); diff --git a/test/spec/e2e/longform/basic_w_bidderSettings.spec.js b/test/spec/e2e/longform/basic_w_bidderSettings.spec.js index e8bdbcf3b4f..1b884aeca1b 100644 --- a/test/spec/e2e/longform/basic_w_bidderSettings.spec.js +++ b/test/spec/e2e/longform/basic_w_bidderSettings.spec.js @@ -10,25 +10,25 @@ const uuidRegex = /(\d|\w){8}-((\d|\w){4}-){3}(\d|\w){12}/; describe('longform ads not using requireExactDuration field', function() { this.retries(3); - it('process the bids successfully', function() { - browser.url(protocol + '://' + host + ':9999/integrationExamples/longform/basic_w_bidderSettings.html?pbjs_debug=true'); - browser.pause(7000); + it('process the bids successfully', async function() { + await browser.url(protocol + '://' + host + ':9999/integrationExamples/longform/basic_w_bidderSettings.html?pbjs_debug=true'); + await browser.pause(7000); const loadPrebidBtnXpath = '//*[@id="loadPrebidRequestBtn"]'; - waitForElement(loadPrebidBtnXpath, 3000); - const prebidBtn = $(loadPrebidBtnXpath); - prebidBtn.click(); - browser.pause(5000); + await waitForElement(loadPrebidBtnXpath, 3000); + const prebidBtn = await $(loadPrebidBtnXpath); + await prebidBtn.click(); + await browser.pause(5000); const listOfCpmsXpath = '/html/body/div[1]/div/div/div/div[1]/div[2]/div/table/tbody/tr/td[2]'; const listOfCategoriesXpath = '/html/body/div[1]/div/div/div/div[1]/div[2]/div/table/tbody/tr/td[3]'; const listOfDurationsXpath = '/html/body/div[1]/div/div/div/div[1]/div[2]/div/table/tbody/tr/td[4]'; - waitForElement(listOfCpmsXpath, 3000); + await waitForElement(listOfCpmsXpath, 3000); - let listOfCpms = $$(listOfCpmsXpath); - let listOfCats = $$(listOfCategoriesXpath); - let listOfDuras = $$(listOfDurationsXpath); + let listOfCpms = await $$(listOfCpmsXpath); + let listOfCats = await $$(listOfCategoriesXpath); + let listOfDuras = await $$(listOfDurationsXpath); expect(listOfCpms.length).to.equal(listOfCats.length).and.to.equal(listOfDuras.length); for (let i = 0; i < listOfCpms.length; i++) { @@ -41,14 +41,14 @@ describe('longform ads not using requireExactDuration field', function() { } }); - it('formats the targeting keys properly', function () { + it('formats the targeting keys properly', async function () { const listOfKeyElementsXpath = '/html/body/div[1]/div/div/div/div[2]/div[2]/div/table/tbody/tr/td[1]'; const listOfKeyValuesXpath = '/html/body/div[1]/div/div/div/div[2]/div[2]/div/table/tbody/tr/td[2]'; - waitForElement(listOfKeyElementsXpath); - waitForElement(listOfKeyValuesXpath); + await waitForElement(listOfKeyElementsXpath); + await waitForElement(listOfKeyValuesXpath); - let listOfKeyElements = $$(listOfKeyElementsXpath); - let listOfKeyValues = $$(listOfKeyValuesXpath); + let listOfKeyElements = await $$(listOfKeyElementsXpath); + let listOfKeyValues = await $$(listOfKeyValuesXpath); let firstKey = listOfKeyElements[0].getText(); expect(firstKey).to.equal('hb_pb_cat_dur'); diff --git a/test/spec/e2e/longform/basic_w_custom_adserver_translation.spec.js b/test/spec/e2e/longform/basic_w_custom_adserver_translation.spec.js index e4ea87bab1a..e66c9eb0cd5 100644 --- a/test/spec/e2e/longform/basic_w_custom_adserver_translation.spec.js +++ b/test/spec/e2e/longform/basic_w_custom_adserver_translation.spec.js @@ -10,25 +10,25 @@ const uuidRegex = /(\d|\w){8}-((\d|\w){4}-){3}(\d|\w){12}/; describe('longform ads using custom adserver translation file', function() { this.retries(3); - it('process the bids successfully', function() { - browser.url(protocol + '://' + host + ':9999/integrationExamples/longform/basic_w_custom_adserver_translation.html?pbjs_debug=true'); - browser.pause(7000); + it('process the bids successfully', async function() { + await browser.url(protocol + '://' + host + ':9999/integrationExamples/longform/basic_w_custom_adserver_translation.html?pbjs_debug=true'); + await browser.pause(7000); const loadPrebidBtnXpath = '//*[@id="loadPrebidRequestBtn"]'; - waitForElement(loadPrebidBtnXpath, 3000); - const prebidBtn = $(loadPrebidBtnXpath); - prebidBtn.click(); - browser.pause(5000); + await waitForElement(loadPrebidBtnXpath, 3000); + const prebidBtn = await $(loadPrebidBtnXpath); + await prebidBtn.click(); + await browser.pause(5000); const listOfCpmsXpath = '/html/body/div[1]/div/div/div/div[1]/div[2]/div/table/tbody/tr/td[2]'; const listOfCategoriesXpath = '/html/body/div[1]/div/div/div/div[1]/div[2]/div/table/tbody/tr/td[3]'; const listOfDurationsXpath = '/html/body/div[1]/div/div/div/div[1]/div[2]/div/table/tbody/tr/td[4]'; - waitForElement(listOfCpmsXpath); + await waitForElement(listOfCpmsXpath); - let listOfCpms = $$(listOfCpmsXpath); - let listOfCats = $$(listOfCategoriesXpath); - let listOfDuras = $$(listOfDurationsXpath); + let listOfCpms = await $$(listOfCpmsXpath); + let listOfCats = await $$(listOfCategoriesXpath); + let listOfDuras = await $$(listOfDurationsXpath); expect(listOfCpms.length).to.equal(listOfCats.length).and.to.equal(listOfDuras.length); for (let i = 0; i < listOfCpms.length; i++) { @@ -41,14 +41,14 @@ describe('longform ads using custom adserver translation file', function() { } }); - it('formats the targeting keys properly', function () { + it('formats the targeting keys properly', async function () { const listOfKeyElementsXpath = '/html/body/div[1]/div/div/div/div[2]/div[2]/div/table/tbody/tr/td[1]'; const listOfKeyValuesXpath = '/html/body/div[1]/div/div/div/div[2]/div[2]/div/table/tbody/tr/td[2]'; - waitForElement(listOfKeyElementsXpath); - waitForElement(listOfKeyValuesXpath); + await waitForElement(listOfKeyElementsXpath); + await waitForElement(listOfKeyValuesXpath); - let listOfKeyElements = $$(listOfKeyElementsXpath); - let listOfKeyValues = $$(listOfKeyValuesXpath); + let listOfKeyElements = await $$(listOfKeyElementsXpath); + let listOfKeyValues = await $$(listOfKeyValuesXpath); let firstKey = listOfKeyElements[0].getText(); expect(firstKey).to.equal('hb_pb_cat_dur'); diff --git a/test/spec/e2e/longform/basic_w_priceGran.spec.js b/test/spec/e2e/longform/basic_w_priceGran.spec.js index b4a5272a69c..df375fb1d39 100644 --- a/test/spec/e2e/longform/basic_w_priceGran.spec.js +++ b/test/spec/e2e/longform/basic_w_priceGran.spec.js @@ -10,25 +10,25 @@ const uuidRegex = /(\d|\w){8}-((\d|\w){4}-){3}(\d|\w){12}/; describe('longform ads not using requireExactDuration field', function() { this.retries(3); - it('process the bids successfully', function() { - browser.url(protocol + '://' + host + ':9999/integrationExamples/longform/basic_w_priceGran.html?pbjs_debug=true'); - browser.pause(7000); + it('process the bids successfully', async function() { + await browser.url(protocol + '://' + host + ':9999/integrationExamples/longform/basic_w_priceGran.html?pbjs_debug=true'); + await browser.pause(7000); const loadPrebidBtnXpath = '//*[@id="loadPrebidRequestBtn"]'; - waitForElement(loadPrebidBtnXpath); - const prebidBtn = $(loadPrebidBtnXpath); - prebidBtn.click(); - browser.pause(5000); + await waitForElement(loadPrebidBtnXpath); + const prebidBtn = await $(loadPrebidBtnXpath); + await prebidBtn.click(); + await browser.pause(5000); const listOfCpmsXpath = '/html/body/div[1]/div/div/div/div[1]/div[2]/div/table/tbody/tr/td[2]'; const listOfCategoriesXpath = '/html/body/div[1]/div/div/div/div[1]/div[2]/div/table/tbody/tr/td[3]'; const listOfDurationsXpath = '/html/body/div[1]/div/div/div/div[1]/div[2]/div/table/tbody/tr/td[4]'; - waitForElement(listOfCpmsXpath); + await waitForElement(listOfCpmsXpath); - let listOfCpms = $$(listOfCpmsXpath); - let listOfCats = $$(listOfCategoriesXpath); - let listOfDuras = $$(listOfDurationsXpath); + let listOfCpms = await $$(listOfCpmsXpath); + let listOfCats = await $$(listOfCategoriesXpath); + let listOfDuras = await $$(listOfDurationsXpath); expect(listOfCpms.length).to.equal(listOfCats.length).and.to.equal(listOfDuras.length); for (let i = 0; i < listOfCpms.length; i++) { @@ -41,14 +41,14 @@ describe('longform ads not using requireExactDuration field', function() { } }); - it('formats the targeting keys properly', function () { + it('formats the targeting keys properly', async function () { const listOfKeyElementsXpath = '/html/body/div[1]/div/div/div/div[2]/div[2]/div/table/tbody/tr/td[1]'; const listOfKeyValuesXpath = '/html/body/div[1]/div/div/div/div[2]/div[2]/div/table/tbody/tr/td[2]'; - waitForElement(listOfKeyElementsXpath); - waitForElement(listOfKeyValuesXpath); + await waitForElement(listOfKeyElementsXpath); + await waitForElement(listOfKeyValuesXpath); - let listOfKeyElements = $$(listOfKeyElementsXpath); - let listOfKeyValues = $$(listOfKeyValuesXpath); + let listOfKeyElements = await $$(listOfKeyElementsXpath); + let listOfKeyValues = await $$(listOfKeyValuesXpath); let firstKey = listOfKeyElements[0].getText(); expect(firstKey).to.equal('hb_pb_cat_dur'); diff --git a/test/spec/e2e/longform/basic_w_requireExactDuration.spec.js b/test/spec/e2e/longform/basic_w_requireExactDuration.spec.js index 6f9acf33061..f36c5815750 100644 --- a/test/spec/e2e/longform/basic_w_requireExactDuration.spec.js +++ b/test/spec/e2e/longform/basic_w_requireExactDuration.spec.js @@ -10,25 +10,25 @@ const uuidRegex = /(\d|\w){8}-((\d|\w){4}-){3}(\d|\w){12}/; describe('longform ads using requireExactDuration field', function() { this.retries(3); - it('process the bids successfully', function() { - browser.url(protocol + '://' + host + ':9999/integrationExamples/longform/basic_w_requireExactDuration.html?pbjs_debug=true'); - browser.pause(7000); + it('process the bids successfully', async function() { + await browser.url(protocol + '://' + host + ':9999/integrationExamples/longform/basic_w_requireExactDuration.html?pbjs_debug=true'); + await browser.pause(7000); const loadPrebidBtnXpath = '//*[@id="loadPrebidRequestBtn"]'; - waitForElement(loadPrebidBtnXpath); - const prebidBtn = $(loadPrebidBtnXpath); - prebidBtn.click(); - browser.pause(5000); + await waitForElement(loadPrebidBtnXpath); + const prebidBtn = await $(loadPrebidBtnXpath); + await prebidBtn.click(); + await browser.pause(5000); const listOfCpmsXpath = '/html/body/div[1]/div/div/div/div[1]/div[2]/div/table/tbody/tr/td[2]'; const listOfCategoriesXpath = '/html/body/div[1]/div/div/div/div[1]/div[2]/div/table/tbody/tr/td[3]'; const listOfDurationsXpath = '/html/body/div[1]/div/div/div/div[1]/div[2]/div/table/tbody/tr/td[4]'; - waitForElement(listOfCpmsXpath); + await waitForElement(listOfCpmsXpath); - let listOfCpms = $$(listOfCpmsXpath); - let listOfCats = $$(listOfCategoriesXpath); - let listOfDuras = $$(listOfDurationsXpath); + let listOfCpms = await $$(listOfCpmsXpath); + let listOfCats = await $$(listOfCategoriesXpath); + let listOfDuras = await $$(listOfDurationsXpath); expect(listOfCpms.length).to.equal(listOfCats.length).and.to.equal(listOfDuras.length); for (let i = 0; i < listOfCpms.length; i++) { @@ -41,14 +41,14 @@ describe('longform ads using requireExactDuration field', function() { } }); - it('formats the targeting keys properly', function () { + it('formats the targeting keys properly', async function () { const listOfKeyElementsXpath = '/html/body/div[1]/div/div/div/div[2]/div[2]/div/table/tbody/tr/td[1]'; const listOfKeyValuesXpath = '/html/body/div[1]/div/div/div/div[2]/div[2]/div/table/tbody/tr/td[2]'; - waitForElement(listOfKeyElementsXpath); - waitForElement(listOfKeyValuesXpath); + await waitForElement(listOfKeyElementsXpath); + await waitForElement(listOfKeyValuesXpath); - let listOfKeyElements = $$(listOfKeyElementsXpath); - let listOfKeyValues = $$(listOfKeyValuesXpath); + let listOfKeyElements = await $$(listOfKeyElementsXpath); + let listOfKeyValues = await $$(listOfKeyValuesXpath); let firstKey = listOfKeyElements[0].getText(); expect(firstKey).to.equal('hb_pb_cat_dur'); diff --git a/test/spec/e2e/longform/basic_wo_brandCategoryExclusion.spec.js b/test/spec/e2e/longform/basic_wo_brandCategoryExclusion.spec.js index 1775bfafa77..2a10e46fc6d 100644 --- a/test/spec/e2e/longform/basic_wo_brandCategoryExclusion.spec.js +++ b/test/spec/e2e/longform/basic_wo_brandCategoryExclusion.spec.js @@ -9,23 +9,23 @@ const uuidRegex = /(\d|\w){8}-((\d|\w){4}-){3}(\d|\w){12}/; describe('longform ads without using brandCategoryExclusion', function() { this.retries(3); - it('process the bids successfully', function() { - browser.url(protocol + '://' + host + ':9999/integrationExamples/longform/basic_wo_brandCategoryExclusion.html?pbjs_debug=true'); - browser.pause(7000); + it('process the bids successfully', async function() { + await browser.url(protocol + '://' + host + ':9999/integrationExamples/longform/basic_wo_brandCategoryExclusion.html?pbjs_debug=true'); + await browser.pause(7000); const loadPrebidBtnXpath = '//*[@id="loadPrebidRequestBtn"]'; - waitForElement(loadPrebidBtnXpath); - const prebidBtn = $(loadPrebidBtnXpath); - prebidBtn.click(); - browser.pause(5000); + await waitForElement(loadPrebidBtnXpath); + const prebidBtn = await $(loadPrebidBtnXpath); + await prebidBtn.click(); + await browser.pause(5000); const listOfCpmsXpath = '/html/body/div[1]/div/div/div/div[1]/div[2]/div/table/tbody/tr/td[2]'; const listOfDurationsXpath = '/html/body/div[1]/div/div/div/div[1]/div[2]/div/table/tbody/tr/td[4]'; - waitForElement(listOfCpmsXpath); + await waitForElement(listOfCpmsXpath); - let listOfCpms = $$(listOfCpmsXpath); - let listOfDuras = $$(listOfDurationsXpath); + let listOfCpms = await $$(listOfCpmsXpath); + let listOfDuras = await $$(listOfDurationsXpath); expect(listOfCpms.length).to.equal(listOfDuras.length); for (let i = 0; i < listOfCpms.length; i++) { @@ -36,14 +36,14 @@ describe('longform ads without using brandCategoryExclusion', function() { } }); - it('formats the targeting keys properly', function () { + it('formats the targeting keys properly', async function () { const listOfKeyElementsXpath = '/html/body/div[1]/div/div/div/div[2]/div[2]/div/table/tbody/tr/td[1]'; const listOfKeyValuesXpath = '/html/body/div[1]/div/div/div/div[2]/div[2]/div/table/tbody/tr/td[2]'; - waitForElement(listOfKeyElementsXpath); - waitForElement(listOfKeyValuesXpath); + await waitForElement(listOfKeyElementsXpath); + await waitForElement(listOfKeyValuesXpath); - let listOfKeyElements = $$(listOfKeyElementsXpath); - let listOfKeyValues = $$(listOfKeyValuesXpath); + let listOfKeyElements = await $$(listOfKeyElementsXpath); + let listOfKeyValues = await $$(listOfKeyValuesXpath); let firstKey = listOfKeyElements[0].getText(); expect(firstKey).to.equal('hb_pb_cat_dur'); diff --git a/test/spec/e2e/longform/basic_wo_requireExactDuration.spec.js b/test/spec/e2e/longform/basic_wo_requireExactDuration.spec.js index 9e92c15e5f5..a2974edca11 100644 --- a/test/spec/e2e/longform/basic_wo_requireExactDuration.spec.js +++ b/test/spec/e2e/longform/basic_wo_requireExactDuration.spec.js @@ -10,25 +10,25 @@ const uuidRegex = /(\d|\w){8}-((\d|\w){4}-){3}(\d|\w){12}/; describe('longform ads not using requireExactDuration field', function() { this.retries(3); - it('process the bids successfully', function() { + it('process the bids successfully', async function() { browser.url(protocol + '://' + host + ':9999/integrationExamples/longform/basic_wo_requireExactDuration.html?pbjs_debug=true'); browser.pause(7000); const loadPrebidBtnXpath = '//*[@id="loadPrebidRequestBtn"]'; - waitForElement(loadPrebidBtnXpath); - const prebidBtn = $(loadPrebidBtnXpath); - prebidBtn.click(); - browser.pause(5000); + await waitForElement(loadPrebidBtnXpath); + const prebidBtn = await $(loadPrebidBtnXpath); + await prebidBtn.click(); + await browser.pause(5000); const listOfCpmsXpath = '/html/body/div[1]/div/div/div/div[1]/div[2]/div/table/tbody/tr/td[2]'; const listOfCategoriesXpath = '/html/body/div[1]/div/div/div/div[1]/div[2]/div/table/tbody/tr/td[3]'; const listOfDurationsXpath = '/html/body/div[1]/div/div/div/div[1]/div[2]/div/table/tbody/tr/td[4]'; - waitForElement(listOfCpmsXpath); + await waitForElement(listOfCpmsXpath); - let listOfCpms = $$(listOfCpmsXpath); - let listOfCats = $$(listOfCategoriesXpath); - let listOfDuras = $$(listOfDurationsXpath); + let listOfCpms = await $$(listOfCpmsXpath); + let listOfCats = await $$(listOfCategoriesXpath); + let listOfDuras = await $$(listOfDurationsXpath); expect(listOfCpms.length).to.equal(listOfCats.length).and.to.equal(listOfDuras.length); for (let i = 0; i < listOfCpms.length; i++) { @@ -41,14 +41,14 @@ describe('longform ads not using requireExactDuration field', function() { } }); - it('formats the targeting keys properly', function () { + it('formats the targeting keys properly', async function () { const listOfKeyElementsXpath = '/html/body/div[1]/div/div/div/div[2]/div[2]/div/table/tbody/tr/td[1]'; const listOfKeyValuesXpath = '/html/body/div[1]/div/div/div/div[2]/div[2]/div/table/tbody/tr/td[2]'; - waitForElement(listOfKeyElementsXpath); - waitForElement(listOfKeyValuesXpath); + await waitForElement(listOfKeyElementsXpath); + await waitForElement(listOfKeyValuesXpath); - let listOfKeyElements = $$(listOfKeyElementsXpath); - let listOfKeyValues = $$(listOfKeyValuesXpath); + let listOfKeyElements = await $$(listOfKeyElementsXpath); + let listOfKeyValues = await $$(listOfKeyValuesXpath); let firstKey = listOfKeyElements[0].getText(); expect(firstKey).to.equal('hb_pb_cat_dur'); diff --git a/test/spec/e2e/modules/e2e_bidderSettings.spec.js b/test/spec/e2e/modules/e2e_bidderSettings.spec.js index f8aedfea652..46251d39be3 100644 --- a/test/spec/e2e/modules/e2e_bidderSettings.spec.js +++ b/test/spec/e2e/modules/e2e_bidderSettings.spec.js @@ -26,8 +26,8 @@ setupTest({ waitFor: CREATIVE_IFRAME_CSS_SELECTOR, expectGAMCreative: true }, 'Prebid.js Bidder Settings Ad Unit Test', function () { - it('should load the targeting keys with correct values', function () { - const result = browser.execute(function () { + it('should load the targeting keys with correct values', async function () { + const result = await browser.execute(function () { return window.pbjs.getAdserverTargeting('/19968336/prebid_native_example_2'); }); diff --git a/test/spec/e2e/modules/e2e_currency.spec.js b/test/spec/e2e/modules/e2e_currency.spec.js index e4eeeab4f5e..865c24cbeb1 100644 --- a/test/spec/e2e/modules/e2e_currency.spec.js +++ b/test/spec/e2e/modules/e2e_currency.spec.js @@ -13,8 +13,8 @@ setupTest({ waitFor: CREATIVE_IFRAME_CSS_SELECTOR, expectGAMCreative: true }, 'Prebid.js Currency Ad Unit Test', function () { - it('should load the targeting keys with correct values', function () { - const result = browser.execute(function () { + it('should load the targeting keys with correct values', async function () { + const result = await browser.execute(function () { return window.pbjs.getAdserverTargeting('/19968336/prebid_native_example_2'); }); diff --git a/test/spec/e2e/multi-bidder/e2e_multiple_bidders.spec.js b/test/spec/e2e/multi-bidder/e2e_multiple_bidders.spec.js index ef34cdc98f1..098dee3647d 100644 --- a/test/spec/e2e/multi-bidder/e2e_multiple_bidders.spec.js +++ b/test/spec/e2e/multi-bidder/e2e_multiple_bidders.spec.js @@ -26,8 +26,8 @@ setupTest({ waitFor: CREATIVE_BANNER_CSS_SELECTOR, expectGAMCreative: true, }, 'Prebid.js Multiple Bidder Ad Unit Test', function () { - it('should load the targeting keys with correct values', function () { - const result = browser.execute(function () { + it('should load the targeting keys with correct values', async function () { + const result = await browser.execute(function () { return window.pbjs.getAdserverTargeting('div-banner-native-2'); }); diff --git a/test/spec/e2e/native/basic_native_ad.spec.js b/test/spec/e2e/native/basic_native_ad.spec.js index 4167046b553..ded7ba610f2 100644 --- a/test/spec/e2e/native/basic_native_ad.spec.js +++ b/test/spec/e2e/native/basic_native_ad.spec.js @@ -26,8 +26,8 @@ setupTest({ waitFor: CREATIVE_IFRAME_CSS_SELECTOR, expectGAMCreative: true }, 'Prebid.js Native Ad Unit Test', function () { - it('should load the targeting keys with correct values', function () { - const result = browser.execute(function () { + it('should load the targeting keys with correct values', async function () { + const result = await browser.execute(function () { return window.pbjs.getAdserverTargeting('/19968336/prebid_native_example_2'); }); diff --git a/test/spec/e2e/outstream/basic_outstream_video_ad.spec.js b/test/spec/e2e/outstream/basic_outstream_video_ad.spec.js index 427839fa92a..4b5c8566f28 100644 --- a/test/spec/e2e/outstream/basic_outstream_video_ad.spec.js +++ b/test/spec/e2e/outstream/basic_outstream_video_ad.spec.js @@ -19,8 +19,8 @@ setupTest({ url: TEST_PAGE_URL, waitFor: CREATIVE_IFRAME_CSS_SELECTOR, }, 'Prebid.js Outstream Video Ad Test', function () { - it('should load the targeting keys with correct values', function () { - const result = browser.execute(function () { + it('should load the targeting keys with correct values', async function () { + const result = await browser.execute(function () { return window.pbjs.getAdserverTargeting('video_ad_unit_2'); }); @@ -30,13 +30,13 @@ setupTest({ expect(targetingKeys.hb_adid_appnexus).to.be.a('string'); }); - it('should render the video ad on the page', function() { + it('should render the video ad on the page', async function() { // skipping test in Edge due to wdio bug: https://github.com/webdriverio/webdriverio/issues/3880 // the iframe for the video does not have a name property and id is generated automatically... if (browser.capabilities.browserName !== 'edge') { - switchFrame(CREATIVE_IFRAME_CSS_SELECTOR); - const ele = $('body > div[id*="an_video_ad_player"] > video'); - expect(ele.isExisting()).to.be.true; + await switchFrame(CREATIVE_IFRAME_CSS_SELECTOR); + const existing = await $('body > div[id*="an_video_ad_player"] > video').isExisting(); + expect(existing).to.be.true; } }); }); diff --git a/wdio.conf.js b/wdio.conf.js index 3d93f909971..d23fecd0b15 100644 --- a/wdio.conf.js +++ b/wdio.conf.js @@ -1,3 +1,5 @@ +const shared = require('./wdio.shared.conf.js'); + const browsers = Object.fromEntries( Object.entries(require('./browsers.json')) .filter(([k, v]) => { @@ -35,14 +37,7 @@ function getCapabilities() { } exports.config = { - specs: [ - './test/spec/e2e/**/*.spec.js', - ], - exclude: [ - // TODO: decipher original intent for "longform" tests - // they all appear to be almost exact copies - './test/spec/e2e/longform/**/*' - ], + ...shared.config, services: [ ['browserstack', { browserstackLocal: true @@ -53,17 +48,4 @@ exports.config = { maxInstances: 5, // Do not increase this, since we have only 5 parallel tests in browserstack account maxInstancesPerCapability: 1, capabilities: getCapabilities(), - logLevel: 'info', // put option here: info | trace | debug | warn| error | silent - bail: 0, - waitforTimeout: 60000, // Default timeout for all waitFor* commands. - connectionRetryTimeout: 60000, // Default timeout in milliseconds for request if Selenium Grid doesn't send response - connectionRetryCount: 3, // Default request retries count - framework: 'mocha', - mochaOpts: { - ui: 'bdd', - timeout: 60000, - compilers: ['js:babel-register'], - }, - // if you see error, update this to spec reporter and logLevel above to get detailed report. - reporters: ['spec'] } diff --git a/wdio.local.conf.js b/wdio.local.conf.js new file mode 100644 index 00000000000..772448472bf --- /dev/null +++ b/wdio.local.conf.js @@ -0,0 +1,13 @@ +const shared = require('./wdio.shared.conf.js'); + +exports.config = { + ...shared.config, + capabilities: [ + { + browserName: 'chrome', + 'goog:chromeOptions': { + args: ['headless', 'disable-gpu'], + }, + }, + ], +}; diff --git a/wdio.shared.conf.js b/wdio.shared.conf.js new file mode 100644 index 00000000000..34e1ee9c675 --- /dev/null +++ b/wdio.shared.conf.js @@ -0,0 +1,23 @@ +exports.config = { + specs: [ + './test/spec/e2e/**/*.spec.js', + ], + exclude: [ + // TODO: decipher original intent for "longform" tests + // they all appear to be almost exact copies + './test/spec/e2e/longform/**/*' + ], + logLevel: 'info', // put option here: info | trace | debug | warn| error | silent + bail: 0, + waitforTimeout: 60000, // Default timeout for all waitFor* commands. + connectionRetryTimeout: 60000, // Default timeout in milliseconds for request if Selenium Grid doesn't send response + connectionRetryCount: 3, // Default request retries count + framework: 'mocha', + mochaOpts: { + ui: 'bdd', + timeout: 60000, + compilers: ['js:babel-register'], + }, + // if you see error, update this to spec reporter and logLevel above to get detailed report. + reporters: ['spec'] +} From eee58b0adce674e4da3891ec967bad88a4a30540 Mon Sep 17 00:00:00 2001 From: Evgenii Novikov Date: Tue, 30 Jan 2024 20:05:00 +0100 Subject: [PATCH 187/246] Yandex Analytics Adapter: initial release (#10876) * Yandex Analytics Adapter: Initial release * Release preparations * Updated trackable events * Updated trackable events * tag URL * Added tests and chanded init logic * Fixed already loaded script scenario * One level of object destruction * Global domain, yandex.com * Removed script insertion logic * Update yandexAnalyticsAdapter.md --------- Co-authored-by: Stanislavsky34200 --- modules/yandexAnalyticsAdapter.js | 154 ++++++++++++++++++ modules/yandexAnalyticsAdapter.md | 36 ++++ .../modules/yandexAnalyticsAdapter_spec.js | 147 +++++++++++++++++ 3 files changed, 337 insertions(+) create mode 100644 modules/yandexAnalyticsAdapter.js create mode 100644 modules/yandexAnalyticsAdapter.md create mode 100644 test/spec/modules/yandexAnalyticsAdapter_spec.js diff --git a/modules/yandexAnalyticsAdapter.js b/modules/yandexAnalyticsAdapter.js new file mode 100644 index 00000000000..045de18e21b --- /dev/null +++ b/modules/yandexAnalyticsAdapter.js @@ -0,0 +1,154 @@ +import buildAdapter from '../libraries/analyticsAdapter/AnalyticsAdapter.js'; +import adapterManager from '../src/adapterManager.js'; +import { logError, logInfo } from '../src/utils.js'; +import CONSTANTS from '../src/constants.json'; +import * as events from '../src/events.js'; + +const timeoutIds = {}; +const tryUntil = (operationId, conditionCb, cb) => { + if (!conditionCb()) { + cb(); + timeoutIds[operationId] = setTimeout( + () => tryUntil(conditionCb, conditionCb, cb), + 100 + ); + } +}; + +const clearTryUntilTimeouts = (timeouts) => { + timeouts.forEach((timeoutID) => { + if (timeoutIds[timeoutID]) { + clearTimeout(timeoutIds[timeoutID]); + } + }); +}; + +const SEND_EVENTS_BUNDLE_TIMEOUT = 1500; +const { + BID_REQUESTED, + BID_RESPONSE, + BID_ADJUSTMENT, + BID_WON, + BIDDER_DONE, + AUCTION_END, + BID_TIMEOUT, +} = CONSTANTS.EVENTS; + +export const EVENTS_TO_TRACK = [ + BID_REQUESTED, + BID_RESPONSE, + BID_ADJUSTMENT, + BID_WON, + BIDDER_DONE, + AUCTION_END, + BID_TIMEOUT, +]; + +const yandexAnalytics = Object.assign(buildAdapter({ analyticsType: 'endpoint' }), { + bufferedEvents: [], + initTimeoutId: 0, + counters: {}, + counterInitTimeouts: {}, + oneCounterInited: false, + + onEvent: (eventName, eventData) => { + const innerEvent = { + event: eventName, + data: eventData, + }; + yandexAnalytics.bufferedEvents.push(innerEvent); + }, + + sendEvents: () => { + if (yandexAnalytics.bufferedEvents.length) { + const data = yandexAnalytics.bufferedEvents.splice( + 0, + yandexAnalytics.bufferedEvents.length + ); + + Object.keys(yandexAnalytics.counters).forEach((counterId) => { + yandexAnalytics.counters[counterId].pbjs(data); + }); + } + setTimeout(yandexAnalytics.sendEvents, SEND_EVENTS_BUNDLE_TIMEOUT); + }, + + onCounterInit: (counterId) => { + yandexAnalytics.counters[counterId] = window[`yaCounter${counterId}`]; + logInfo(`Found metrika counter ${counterId}`); + if (!yandexAnalytics.oneCounterInited) { + yandexAnalytics.oneCounterInited = true; + setTimeout(() => { + yandexAnalytics.sendEvents(); + }, SEND_EVENTS_BUNDLE_TIMEOUT); + clearTimeout(yandexAnalytics.initTimeoutId); + } + }, + + enableAnalytics: (config) => { + yandexAnalytics.options = (config && config.options) || {}; + const { counters } = yandexAnalytics.options || {}; + const validCounters = counters.filter((counterId) => { + if (!counterId) { + return false; + } + + if (isNaN(counterId)) { + return false; + } + + return true; + }); + + if (!validCounters.length) { + logError('options.counters contains no valid counter ids'); + return; + } + + const unsubscribeCallbacks = [ + () => clearTryUntilTimeouts(['countersInit']), + ]; + + yandexAnalytics.initTimeoutId = setTimeout(() => { + yandexAnalytics.bufferedEvents = []; + unsubscribeCallbacks.forEach((cb) => cb()); + logError(`Can't find metrika counter after 25 seconds.`); + logError('Aborting yandex analytics provider initialization.'); + }, 25000); + + events.getEvents().forEach((event) => { + if (event && EVENTS_TO_TRACK.indexOf(event.eventType) >= 0) { + yandexAnalytics.onEvent(event.eventType, event); + } + }); + + EVENTS_TO_TRACK.forEach((eventName) => { + const eventCallback = yandexAnalytics.onEvent.bind(null, eventName); + unsubscribeCallbacks.push(() => events.off(eventName, eventCallback)); + events.on(eventName, eventCallback); + }); + + let allCountersInited = false; + tryUntil('countersInit', () => allCountersInited, () => { + allCountersInited = validCounters.reduce((result, counterId) => { + if (yandexAnalytics.counters[counterId]) { + return result && true; + } + + if (window[`yaCounter${counterId}`]) { + yandexAnalytics.onCounterInit(counterId); + return result && true; + } + + return false; + }, true); + }); + }, +}); + +adapterManager.registerAnalyticsAdapter({ + adapter: yandexAnalytics, + code: 'yandexAnalytics' +}); + +export default yandexAnalytics; diff --git a/modules/yandexAnalyticsAdapter.md b/modules/yandexAnalyticsAdapter.md new file mode 100644 index 00000000000..96d2b08320e --- /dev/null +++ b/modules/yandexAnalyticsAdapter.md @@ -0,0 +1,36 @@ +# Overview + +``` +Module Name: Yandex Analytics Adapter +Module Type: Analytics Adapter +Maintainer: prebid@yandex-team.com +``` + +# Description + +This adapter is designed to work with [Yandex Metrica](https://metrica.yandex.com/about) - Top-5 worldwide web analytics tool. + +Disclosure: provider use Metrica Tag build based on https://github.com/yandex/metrica-tag, ~60 kB gzipped. + +## How to setup provider + +Register your application on https://metrica.yandex.com/ and get counter id. +Insert counter initialization code obtained from the page https://metrica.yandex.com/settings?id={counterId} into your html code. +Init provider like this, where `123` is your counter id. + +Note: If you have Single Page Application (SPA), [configure your tag](https://yandex.com/support/metrica/code/counter-spa-setup.html). + +```javascript +pbjs.enableAnalytics({ + provider: 'yandexAnalytics', + options: { + counters: [ + 123, + ], + }, +}); +``` + +## Where to find data + +Go to https://metrika.yandex.com/dashboard -> Prebid Analytics diff --git a/test/spec/modules/yandexAnalyticsAdapter_spec.js b/test/spec/modules/yandexAnalyticsAdapter_spec.js new file mode 100644 index 00000000000..ca9b29d13a5 --- /dev/null +++ b/test/spec/modules/yandexAnalyticsAdapter_spec.js @@ -0,0 +1,147 @@ +import * as sinon from 'sinon'; +import yandexAnalytics, { EVENTS_TO_TRACK } from 'modules/yandexAnalyticsAdapter.js'; +import * as log from '../../../src/utils.js' +import * as events from '../../../src/events.js'; + +describe('Yandex analytics adapter testing', () => { + const sandbox = sinon.createSandbox(); + let clock; + let logError; + let getEvents; + let onEvent; + const counterId = 123; + const counterWindowKey = 'yaCounter123'; + + beforeEach(() => { + yandexAnalytics.counters = {}; + yandexAnalytics.counterInitTimeouts = {}; + yandexAnalytics.bufferedEvents = []; + yandexAnalytics.oneCounterInited = false; + clock = sinon.useFakeTimers(); + logError = sandbox.stub(log, 'logError'); + sandbox.stub(log, 'logInfo'); + getEvents = sandbox.stub(events, 'getEvents').returns([]); + onEvent = sandbox.stub(events, 'on'); + sandbox.stub(window.document, 'createElement').callsFake((tag) => { + const element = { + tag, + events: {}, + attributes: {}, + addEventListener: (event, cb) => { + element.events[event] = cb; + }, + removeEventListener: (event, cb) => { + chai.expect(element.events[event]).to.equal(cb); + }, + setAttribute: (attr, val) => { + element.attributes[attr] = val; + }, + }; + return element; + }); + }); + + afterEach(() => { + window.Ya = null; + window[counterWindowKey] = null; + sandbox.restore(); + clock.restore(); + }); + + it('fails if timeout for counter insertion is exceeded', () => { + yandexAnalytics.enableAnalytics({ + options: { + counters: [ + 123, + ], + }, + }); + clock.tick(25001); + chai.expect(yandexAnalytics.bufferedEvents).to.deep.equal([]); + sinon.assert.calledWith(logError, `Can't find metrika counter after 25 seconds.`); + sinon.assert.calledWith(logError, `Aborting yandex analytics provider initialization.`); + }); + + it('fails if no valid counters provided', () => { + yandexAnalytics.enableAnalytics({ + options: { + counters: [ + 'abc', + ], + }, + }); + sinon.assert.calledWith(logError, 'options.counters contains no valid counter ids'); + }); + + it('subscribes to events if counter is already present', () => { + window[counterWindowKey] = { + pbjs: sandbox.stub(), + }; + + getEvents.returns([ + { + eventType: EVENTS_TO_TRACK[0], + }, + { + eventType: 'Some_untracked_event', + } + ]); + const eventsToSend = [{ + event: EVENTS_TO_TRACK[0], + data: { + eventType: EVENTS_TO_TRACK[0], + } + }]; + + yandexAnalytics.enableAnalytics({ + options: { + counters: [ + counterId, + ], + }, + }); + + EVENTS_TO_TRACK.forEach((eventName, i) => { + const [event, callback] = onEvent.getCall(i).args; + chai.expect(event).to.equal(eventName); + callback(i); + eventsToSend.push({ + event: eventName, + data: i, + }); + }); + + clock.tick(1501); + + const [ sentEvents ] = window[counterWindowKey].pbjs.getCall(0).args; + chai.expect(sentEvents).to.deep.equal(eventsToSend); + }); + + it('waits for counter initialization', () => { + window.Ya = {}; + // Simulatin metrika script initialization + yandexAnalytics.enableAnalytics({ + options: { + counters: [ + counterId, + ], + }, + }); + + // Sending event + const [event, eventCallback] = onEvent.getCall(0).args; + eventCallback({}); + + const counterPbjsMethod = sandbox.stub(); + window[`yaCounter${counterId}`] = { + pbjs: counterPbjsMethod, + }; + clock.tick(2001); + + const [ sentEvents ] = counterPbjsMethod.getCall(0).args; + chai.expect(sentEvents).to.deep.equal([{ + event, + data: {}, + }]); + }); +}); From 25927069cb77b297d655f02875d353a420d24ae7 Mon Sep 17 00:00:00 2001 From: xmgiddev <133856186+xmgiddev@users.noreply.github.com> Date: Tue, 30 Jan 2024 23:32:52 +0200 Subject: [PATCH 188/246] MgidX Bid Adapter: add optional region param (#10612) * new adapter - MgidX * add new required param host * rem host, add region --------- Co-authored-by: Evgeny Nagorny Co-authored-by: xmgiddev <> --- modules/mgidXBidAdapter.js | 13 +++++++++++-- test/spec/modules/mgidXBidAdapter_spec.js | 21 ++++++++++++--------- 2 files changed, 23 insertions(+), 11 deletions(-) diff --git a/modules/mgidXBidAdapter.js b/modules/mgidXBidAdapter.js index 7e084977cf1..ac25a419de1 100644 --- a/modules/mgidXBidAdapter.js +++ b/modules/mgidXBidAdapter.js @@ -17,7 +17,7 @@ import { USERSYNC_DEFAULT_CONFIG } from '../src/userSync.js'; const BIDDER_CODE = 'mgidX'; const GVLID = 358; -const AD_URL = 'https://us-east-x.mgid.com/pbjs'; +const AD_URL = 'https://#{REGION}#.mgid.com/pbjs'; const PIXEL_SYNC_URL = 'https://cm.mgid.com/i.gif'; const IFRAME_SYNC_URL = 'https://cm.mgid.com/i.html'; @@ -181,9 +181,18 @@ export const spec = { placements.push(getPlacementReqData(bid)); } + const region = validBidRequests[0].params?.region; + + let url; + if (region === 'eu') { + url = AD_URL.replace('#{REGION}#', 'eu'); + } else { + url = AD_URL.replace('#{REGION}#', 'us-east-x'); + } + return { method: 'POST', - url: AD_URL, + url: url, data: request }; }, diff --git a/test/spec/modules/mgidXBidAdapter_spec.js b/test/spec/modules/mgidXBidAdapter_spec.js index 978ca3de036..e0b1e1a84e9 100644 --- a/test/spec/modules/mgidXBidAdapter_spec.js +++ b/test/spec/modules/mgidXBidAdapter_spec.js @@ -6,7 +6,6 @@ import { config } from '../../../src/config'; import { USERSYNC_DEFAULT_CONFIG } from '../../../src/userSync'; const bidder = 'mgidX' -const adUrl = 'https://us-east-x.mgid.com/pbjs'; describe('MGIDXBidAdapter', function () { const bids = [ @@ -19,6 +18,7 @@ describe('MGIDXBidAdapter', function () { } }, params: { + region: 'eu', placementId: 'testBanner', } }, @@ -56,6 +56,7 @@ describe('MGIDXBidAdapter', function () { } }, params: { + region: 'eu', placementId: 'testNative', } } @@ -108,8 +109,16 @@ describe('MGIDXBidAdapter', function () { expect(serverRequest.method).to.equal('POST'); }); - it('Returns valid URL', function () { - expect(serverRequest.url).to.equal(adUrl); + it('Returns valid EU URL', function () { + bids[0].params.region = 'eu'; + serverRequest = spec.buildRequests(bids, bidderRequest); + expect(serverRequest.url).to.equal('https://eu.mgid.com/pbjs'); + }); + + it('Returns valid EAST URL', function () { + bids[0].params.region = 'other'; + serverRequest = spec.buildRequests(bids, bidderRequest); + expect(serverRequest.url).to.equal('https://us-east-x.mgid.com/pbjs'); }); it('Returns general data valid', function () { @@ -193,12 +202,6 @@ describe('MGIDXBidAdapter', function () { 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 () { From ad85b03f7fc3fef0d86011608b809558fb9bcd64 Mon Sep 17 00:00:00 2001 From: Demetrio Girardi Date: Tue, 30 Jan 2024 13:33:57 -0800 Subject: [PATCH 189/246] Video module: log error when adUnit provides unknown player div ID (#10664) * Video module: log error when adUnit provides unknown player div ID * moves error logging to separate function * remove trailing spaces * Update modules/videoModule/coreVideo.js --------- Co-authored-by: Karim Mourra --- modules/videoModule/coreVideo.js | 5 ++++- modules/videoModule/index.js | 19 ++++++++++++++++++- test/spec/modules/videoModule/pbVideo_spec.js | 4 +++- 3 files changed, 25 insertions(+), 3 deletions(-) diff --git a/modules/videoModule/coreVideo.js b/modules/videoModule/coreVideo.js index ce66acc2b02..fc54d0d0b98 100644 --- a/modules/videoModule/coreVideo.js +++ b/modules/videoModule/coreVideo.js @@ -218,7 +218,10 @@ export function VideoCore(parentModule_) { getOrtbContent, setAdTagUrl, onEvents, - offEvents + offEvents, + hasProviderFor(divId) { + return !!parentModule.getSubmodule(divId); + } }; } diff --git a/modules/videoModule/index.js b/modules/videoModule/index.js index b9cba60594d..bfb239210db 100644 --- a/modules/videoModule/index.js +++ b/modules/videoModule/index.js @@ -1,7 +1,7 @@ import { config } from '../../src/config.js'; import { find } from '../../src/polyfill.js'; import * as events from '../../src/events.js'; -import { mergeDeep, logWarn } from '../../src/utils.js'; +import {mergeDeep, logWarn, logError} from '../../src/utils.js'; import { getGlobal } from '../../src/prebidGlobal.js'; import CONSTANTS from '../../src/constants.json'; import { @@ -99,6 +99,7 @@ export function PbVideo(videoCore_, getConfig_, pbGlobal_, pbEvents_, videoEvent return { init, renderBid, getOrtbVideo, getOrtbContent }; function beforeBidsRequested(nextFn, bidderRequest) { + logErrorForInvalidDivIds(bidderRequest); enrichAuction(bidderRequest); const bidsBackHandler = bidderRequest.bidsBackHandler; @@ -109,6 +110,22 @@ export function PbVideo(videoCore_, getConfig_, pbGlobal_, pbEvents_, videoEvent return nextFn.call(this, bidderRequest); } + function logErrorForInvalidDivIds(bidderRequest) { + const adUnits = bidderRequest.adUnits || pbGlobal.adUnits || []; + adUnits.forEach(adUnit => { + const video = adUnit.video; + if (!video) { + return; + } + if (!video.divId) { + logError(`Missing Video player div ID for ad unit '${adUnit.code}'`); + } + if (!videoCore.hasProviderFor(video.divId)) { + logError(`Video player div ID '${video.divId}' for ad unit '${adUnit.code}' does not match any registered player`); + } + }); + } + function enrichAuction(bidderRequest) { if (mainContentDivId) { enrichOrtb2(mainContentDivId, bidderRequest); diff --git a/test/spec/modules/videoModule/pbVideo_spec.js b/test/spec/modules/videoModule/pbVideo_spec.js index 2e26737da40..1ccd9766eab 100644 --- a/test/spec/modules/videoModule/pbVideo_spec.js +++ b/test/spec/modules/videoModule/pbVideo_spec.js @@ -1,3 +1,4 @@ +import 'src/prebid.js'; import { expect } from 'chai'; import { PbVideo } from 'modules/videoModule'; import CONSTANTS from 'src/constants.json'; @@ -26,7 +27,8 @@ function resetTestVars() { onEvents: sinon.spy(), getOrtbVideo: () => ortbVideoMock, getOrtbContent: () => ortbContentMock, - setAdTagUrl: sinon.spy() + setAdTagUrl: sinon.spy(), + hasProviderFor: sinon.spy(), }; getConfigMock = () => {}; requestBidsMock = { From 081541463fa8a7e403921f9689e172608aa649b6 Mon Sep 17 00:00:00 2001 From: asurovenko-zeta <80847074+asurovenko-zeta@users.noreply.github.com> Date: Tue, 30 Jan 2024 22:45:31 +0100 Subject: [PATCH 190/246] ZetaGlobalSsp: bugfix (#10882) Co-authored-by: Surovenko Alexey Co-authored-by: Alexey Surovenko --- modules/zeta_global_sspBidAdapter.js | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/modules/zeta_global_sspBidAdapter.js b/modules/zeta_global_sspBidAdapter.js index 68cf7c4e4cd..4cdda3a1105 100644 --- a/modules/zeta_global_sspBidAdapter.js +++ b/modules/zeta_global_sspBidAdapter.js @@ -83,8 +83,9 @@ export const spec = { id: request.bidId, secure: secure }; - if (params.tagid) { - impData.tagid = params.tagid; + const tagid = request.params?.tagid; + if (tagid) { + impData.tagid = tagid; } if (request.mediaTypes) { for (const mediaType in request.mediaTypes) { @@ -112,8 +113,11 @@ export const spec = { impData.bidfloor = floorInfo.floor; } } - if (!impData.bidfloor && params.bidfloor) { - impData.bidfloor = params.bidfloor; + if (!impData.bidfloor) { + const bidfloor = request.params?.bidfloor; + if (bidfloor) { + impData.bidfloor = bidfloor; + } } return impData; From 951d7d0c6243ef1b81aaf57b668e0417e10b852e Mon Sep 17 00:00:00 2001 From: Nayan Savla Date: Wed, 31 Jan 2024 00:46:28 +0300 Subject: [PATCH 191/246] Yieldmo Bid Adapter : send topics in the Bid Request (#10892) * Adding topics to bid request Getting topics and adding them to bid request. * converting topics to numbers * Adding unit tests --- modules/yieldmoBidAdapter.js | 28 +++++++++++- test/spec/modules/yieldmoBidAdapter_spec.js | 47 +++++++++++++++++++++ 2 files changed, 73 insertions(+), 2 deletions(-) diff --git a/modules/yieldmoBidAdapter.js b/modules/yieldmoBidAdapter.js index 78c2e7430e7..3dd5baf771a 100644 --- a/modules/yieldmoBidAdapter.js +++ b/modules/yieldmoBidAdapter.js @@ -75,7 +75,7 @@ export const spec = { const videoBidRequests = bidRequests.filter(request => hasVideoMediaType(request)); let serverRequests = []; const eids = getEids(bidRequests[0]) || []; - + const topicsData = getTopics(bidderRequest); if (bannerBidRequests.length > 0) { let serverRequest = { pbav: '$prebid.version$', @@ -97,6 +97,9 @@ export const spec = { }), us_privacy: deepAccess(bidderRequest, 'uspConsent') || '', }; + if (topicsData) { + serverRequest.topics = topicsData; + } if (canAccessTopWindow()) { serverRequest.pr = (LOCAL_WINDOW.document && LOCAL_WINDOW.document.referrer) || ''; @@ -161,6 +164,9 @@ export const spec = { if (videoBidRequests.length > 0) { const serverRequest = openRtbRequest(videoBidRequests, bidderRequest); + if (topicsData) { + serverRequest.topics = topicsData; + } if (eids.length) { serverRequest.user = { eids }; }; @@ -416,10 +422,28 @@ function openRtbRequest(bidRequests, bidderRequest) { openRtbRequest.auctionId = bidRequests[0].auctionId; } populateOpenRtbGdpr(openRtbRequest, bidderRequest); - return openRtbRequest; } +function getTopics(bidderRequest) { + const userData = deepAccess(bidderRequest, 'ortb2.user.data') || []; + const topicsData = userData.filter((dataObj) => { + const segtax = dataObj.ext?.segtax; + return segtax >= 600 && segtax <= 609; + })[0]; + + if (topicsData) { + let topicsObject = { + taxonomy: topicsData.ext.segtax, + classifier: topicsData.ext.segclass, + // topics needs to be array of numbers + topics: Object.values(topicsData.segment).map(i => Number(i)), + }; + return topicsObject; + } + return null; +} + /** * @param {BidRequest} bidRequest bidder request object. * @return Object OpenRTB's 'imp' (impression) object diff --git a/test/spec/modules/yieldmoBidAdapter_spec.js b/test/spec/modules/yieldmoBidAdapter_spec.js index edb3ef3af27..6d68ee79170 100644 --- a/test/spec/modules/yieldmoBidAdapter_spec.js +++ b/test/spec/modules/yieldmoBidAdapter_spec.js @@ -394,6 +394,25 @@ describe('YieldmoAdapter', function () { expect(placementInfo).to.include('"gpid":"/6355419/Travel/Europe/France/Paris"'); }); + it('should add topics to the banner bid request', function () { + const biddata = build([mockBannerBid()], mockBidderRequest({ortb2: { user: { + data: [ + { + ext: { + segtax: 600, + segclass: '2206021246', + }, + segment: ['7', '8', '9'], + }, + ], + }}})); + expect(biddata[0].data.topics).to.deep.equal({ + taxonomy: 600, + classifier: '2206021246', + topics: [7, 8, 9], + }); + }); + it('should add eids to the banner bid request', function () { const params = { userIdAsEids: [{ @@ -620,6 +639,34 @@ describe('YieldmoAdapter', function () { }; expect(buildAndGetData([mockVideoBid({...params})]).user.eids).to.eql(params.fakeUserIdAsEids); }); + + it('should add topics to the bid request', function () { + let videoBidder = mockBidderRequest( + { + ortb2: { + user: { + data: [ + { + ext: { + segtax: 600, + segclass: '2206021246', + }, + segment: ['7', '8', '9'], + }, + ], + }, + }, + }, + [mockVideoBid()] + ); + let payload = buildAndGetData([mockVideoBid()], 0, videoBidder); + expect(payload.topics).to.deep.equal({ + taxonomy: 600, + classifier: '2206021246', + topics: [7, 8, 9], + }); + }); + it('should add device info to payload if available', function () { let videoBidder = mockBidderRequest({ ortb2: { device: { From 2171ca4bde6e71b0889f5ba882d6b941dcdbef72 Mon Sep 17 00:00:00 2001 From: freemmy Date: Wed, 31 Jan 2024 03:20:02 +0530 Subject: [PATCH 192/246] SilverMob adapter initial commit (#10896) --- modules/silvermobBidAdapter.js | 76 +++++ modules/silvermobBidAdapter.md | 70 ++++ test/spec/modules/silvermobBidAdapter_spec.js | 301 ++++++++++++++++++ 3 files changed, 447 insertions(+) create mode 100644 modules/silvermobBidAdapter.js create mode 100644 modules/silvermobBidAdapter.md create mode 100644 test/spec/modules/silvermobBidAdapter_spec.js diff --git a/modules/silvermobBidAdapter.js b/modules/silvermobBidAdapter.js new file mode 100644 index 00000000000..340dc9c70ac --- /dev/null +++ b/modules/silvermobBidAdapter.js @@ -0,0 +1,76 @@ +// import { logMessage } from '../src/utils.js'; +import {registerBidder} from '../src/adapters/bidderFactory.js'; +import { BANNER, NATIVE, VIDEO } from '../src/mediaTypes.js'; + +import {ortbConverter} from '../libraries/ortbConverter/converter.js' +import { config } from '../src/config.js'; + +const BIDDER_CODE = 'silvermob'; +const AD_URL = 'https://{HOST}.silvermob.com/marketplace/api/dsp/prebidjs/{ZONEID}'; +const GVLID = 1058; + +const converter = ortbConverter({ + context: { + netRevenue: true, + ttl: 30 + }, + imp(buildImp, bidRequest, context) { + const imp = buildImp(bidRequest, context); + if (!imp.bidfloor) imp.bidfloor = bidRequest.params.bidfloor || 0; + imp.ext = { + [BIDDER_CODE]: { + zoneid: bidRequest.params.zoneid, + host: bidRequest.params.host || 'us', + } + } + return imp; + }, + request(buildRequest, imps, bidderRequest, context) { + const request = buildRequest(imps, bidderRequest, context); + const bid = context.bidRequests[0]; + request.test = config.getConfig('debug') ? 1 : 0; + if (!request.cur) request.cur = [bid.params.currency || 'USD']; + return request; + }, + bidResponse(buildBidResponse, bid, context) { + const bidResponse = buildBidResponse(bid, context); + bidResponse.cur = bid.cur || 'USD'; + return bidResponse; + } +}); + +export const spec = { + code: BIDDER_CODE, + gvlid: GVLID, + supportedMediaTypes: [BANNER, VIDEO, NATIVE], + + isBidRequestValid: (bid) => { + return Boolean(bid.bidId && bid.params && !isNaN(bid.params.zoneid)); + }, + + buildRequests: (validBidRequests, bidderRequest) => { + if (validBidRequests && validBidRequests.length === 0) return []; + + const host = validBidRequests[0].params.host || 'us'; + const zoneid = validBidRequests[0].params.zoneid; + + const data = converter.toORTB({ bidRequests: validBidRequests, bidderRequest }); + + return { + method: 'POST', + url: AD_URL.replace('{HOST}', host).replace('{ZONEID}', zoneid), + data: data + }; + }, + + interpretResponse: (response, request) => { + if (response?.body) { + const bids = converter.fromORTB({ response: response.body, request: request.data }).bids; + return bids; + } + return []; + } + +}; + +registerBidder(spec); diff --git a/modules/silvermobBidAdapter.md b/modules/silvermobBidAdapter.md new file mode 100644 index 00000000000..ba080ec105e --- /dev/null +++ b/modules/silvermobBidAdapter.md @@ -0,0 +1,70 @@ +# Overview + +``` +Module Name: SilverMob Bidder Adapter +Module Type: Bidder Adapter +Maintainer: prebid@silvermob.com +``` + +# Description + +Module that connects to SilverMob platform + +# Test Parameters +``` + var adUnits = [ + // Will return static native ad. Assets are stored through user UI for each placement separetly + { + code: 'placementId_0', + mediaTypes: { + native: {} + }, + bids: [ + { + bidder: 'silvermob', + params: { + host: 'us', + zoneid: '0' + } + } + ] + }, + // Will return static test banner + { + code: 'placementId_0', + mediaTypes: { + banner: { + sizes: [[300, 250]], + } + }, + bids: [ + { + bidder: 'silvermob', + params: { + host: 'us', + zoneid: '0' + } + } + ] + }, + // 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: 'silvermob', + params: { + host: 'us', + zoneid: '0' + } + } + ] + } + ]; +``` diff --git a/test/spec/modules/silvermobBidAdapter_spec.js b/test/spec/modules/silvermobBidAdapter_spec.js new file mode 100644 index 00000000000..7d7fbacc04e --- /dev/null +++ b/test/spec/modules/silvermobBidAdapter_spec.js @@ -0,0 +1,301 @@ +import { expect } from 'chai'; +import {spec} from '../../../modules/silvermobBidAdapter.js'; +import 'modules/priceFloors.js'; +import { newBidder } from 'src/adapters/bidderFactory'; +import { config } from '../../../src/config.js'; +import { syncAddFPDToBidderRequest } from '../../helpers/fpd.js'; + +// load modules that register ORTB processors +import 'src/prebid.js'; +import 'modules/currency.js'; +import 'modules/userId/index.js'; +import 'modules/multibid/index.js'; +import 'modules/priceFloors.js'; +import 'modules/consentManagement.js'; +import 'modules/consentManagementUsp.js'; +import 'modules/schain.js'; + +const SIMPLE_BID_REQUEST = { + bidder: 'silvermob', + params: { + zoneid: '0', + host: 'us', + }, + mediaTypes: { + banner: { + sizes: [ + [320, 250], + [300, 600], + ], + }, + }, + adUnitCode: 'div-gpt-ad-1499748733608-0', + transactionId: 'f183e871-fbed-45f0-a427-c8a63c4c01eb', + bidId: '33e9500b21129f', + bidderRequestId: '2772c1e566670b', + auctionId: '192721e36a0239', + sizes: [[300, 250], [160, 600]], + gdprConsent: { + apiVersion: 2, + consentString: 'CONSENT', + vendorData: { purpose: { consents: { 1: true } } }, + gdprApplies: true, + addtlConsent: '1~1.35.41.101', + }, +} + +const BANNER_BID_REQUEST = { + bidder: 'silvermob', + params: { + zoneid: '0', + host: 'us', + }, + mediaTypes: { + banner: { + sizes: [ + [300, 250], + [300, 600], + ], + }, + }, + adUnitCode: '/adunit-code/test-path', + bidId: 'test-bid-id-1', + bidderRequestId: 'test-bid-request-1', + auctionId: 'test-auction-1', + transactionId: 'test-transactionId-1', + code: 'banner_example', + timeout: 1000, +} + +const VIDEO_BID_REQUEST = { + placementCode: '/DfpAccount1/slotVideo', + bidId: 'test-bid-id-2', + mediaTypes: { + video: { + playerSize: [400, 300], + w: 400, + h: 300, + minduration: 5, + maxduration: 10, + startdelay: 0, + skip: 1, + minbitrate: 200, + protocols: [1, 2, 4] + } + }, + bidder: 'silvermob', + params: { + zoneid: '0', + host: 'us', + }, + adUnitCode: '/adunit-code/test-path', + bidderRequestId: 'test-bid-request-1', + auctionId: 'test-auction-1', + transactionId: 'test-transactionId-1', + timeout: 1000, +} + +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: 'silvermob', + params: { + zoneid: '0', + host: 'us', + }, + adUnitCode: '/adunit-code/test-path', + bidId: 'test-bid-id-1', + bidderRequestId: 'test-bid-request-1', + auctionId: 'test-auction-1', + transactionId: 'test-transactionId-1', + timeout: 1000, + uspConsent: 'uspConsent' +}; + +const bidderRequest = { + refererInfo: { + page: 'https://publisher.com/home', + ref: 'https://referrer' + } +}; + +const gdprConsent = { + apiVersion: 2, + consentString: 'CONSENT', + vendorData: { purpose: { consents: { 1: true } } }, + gdprApplies: true, + addtlConsent: '1~1.35.41.101', +} + +describe('silvermobAdapter', 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('with user privacy regulations', function () { + it('should send the Coppa "required" flag set to "1" in the request', function () { + sinon.stub(config, 'getConfig') + .withArgs('coppa') + .returns(true); + const serverRequest = spec.buildRequests([SIMPLE_BID_REQUEST], syncAddFPDToBidderRequest(bidderRequest)); + expect(serverRequest.data.regs.coppa).to.equal(1); + config.getConfig.restore(); + }); + + it('should send the GDPR Consent data in the request', function () { + const serverRequest = spec.buildRequests([SIMPLE_BID_REQUEST], syncAddFPDToBidderRequest({ ...bidderRequest, gdprConsent })); + expect(serverRequest.data.regs.ext.gdpr).to.exist.and.to.equal(1); + expect(serverRequest.data.user.ext.consent).to.equal('CONSENT'); + }); + + it('should send the CCPA data in the request', function () { + const serverRequest = spec.buildRequests([SIMPLE_BID_REQUEST], syncAddFPDToBidderRequest({...bidderRequest, ...{ uspConsent: '1YYY' }})); + expect(serverRequest.data.regs.ext.us_privacy).to.equal('1YYY'); + }); + }); + + describe('isBidRequestValid', function () { + it('should return true when required params found', function () { + expect(spec.isBidRequestValid(BANNER_BID_REQUEST)).to.equal(true); + }); + + it('should return false when zoneid is missing', function () { + let localbid = Object.assign({}, BANNER_BID_REQUEST); + delete localbid.params.zoneid; + expect(spec.isBidRequestValid(BANNER_BID_REQUEST)).to.equal(false); + }); + }); + + describe('build request', function () { + it('should return an empty array when no bid requests', function () { + const bidRequest = spec.buildRequests([], syncAddFPDToBidderRequest(bidderRequest)); + expect(bidRequest).to.be.an('array'); + expect(bidRequest.length).to.equal(0); + }); + + it('should return a valid bid request object', function () { + const request = spec.buildRequests([SIMPLE_BID_REQUEST], syncAddFPDToBidderRequest(bidderRequest)); + expect(request).to.not.equal('array'); + expect(request.data).to.be.an('object'); + expect(request.method).to.equal('POST'); + expect(request.url).to.equal('https://us.silvermob.com/marketplace/api/dsp/prebidjs/0'); + + expect(request.data.site).to.have.property('page'); + expect(request.data.site).to.have.property('domain'); + expect(request.data).to.have.property('id'); + expect(request.data).to.have.property('imp'); + expect(request.data).to.have.property('device'); + }); + + it('should return a valid bid BANNER request object', function () { + const request = spec.buildRequests([BANNER_BID_REQUEST], syncAddFPDToBidderRequest(bidderRequest)); + expect(request.data.imp[0].banner).to.exist; + expect(request.data.imp[0].banner.format[0].w).to.be.an('number'); + expect(request.data.imp[0].banner.format[0].h).to.be.an('number'); + }); + + if (FEATURES.VIDEO) { + it('should return a valid bid VIDEO request object', function () { + const request = spec.buildRequests([VIDEO_BID_REQUEST], syncAddFPDToBidderRequest(bidderRequest)); + expect(request.data.imp[0].video).to.exist; + expect(request.data.imp[0].video.w).to.be.an('number'); + expect(request.data.imp[0].video.h).to.be.an('number'); + }); + } + + it('should return a valid bid NATIVE request object', function () { + const request = spec.buildRequests([NATIVE_BID_REQUEST], syncAddFPDToBidderRequest(bidderRequest)); + expect(request.data.imp[0]).to.be.an('object'); + }); + }) + + describe('interpretResponse', function () { + let bidRequests, bidderRequest; + beforeEach(function () { + bidRequests = [{ + 'bidId': '28ffdk2B952532', + 'bidder': 'silvermob', + 'userId': { + 'freepassId': { + 'userIp': '172.21.0.1', + 'userId': '123', + 'commonId': 'commonIdValue' + } + }, + 'adUnitCode': 'adunit-code', + 'params': { + 'publisherId': 'publisherIdValue' + } + }]; + bidderRequest = {}; + }); + + it('Empty response must return empty array', function () { + const emptyResponse = null; + let response = spec.interpretResponse(emptyResponse, BANNER_BID_REQUEST); + + expect(response).to.be.an('array').that.is.empty; + }) + + it('Should interpret banner response', function () { + const serverResponse = { + body: { + 'cur': 'USD', + 'seatbid': [{ + 'bid': [{ + 'impid': '28ffdk2B952532', + 'price': 97, + 'adm': '', + 'w': 300, + 'h': 250, + 'crid': 'creative0' + }] + }] + } + }; + it('should interpret server response', function () { + const bidRequest = spec.buildRequests(bidRequests, syncAddFPDToBidderRequest(bidderRequest)); + const bids = spec.interpretResponse(serverResponse, bidRequest); + expect(bids).to.be.an('array'); + const bid = bids[0]; + expect(bid).to.be.an('object'); + expect(bid.currency).to.equal('USD'); + expect(bid.cpm).to.equal(97); + expect(bid.ad).to.equal(ad) + expect(bid.width).to.equal(300); + expect(bid.height).to.equal(250); + expect(bid.creativeId).to.equal('creative0'); + }); + }) + }); +}); From ffd57060e9a98f3d1e5c1c7ead59041982f0b3d8 Mon Sep 17 00:00:00 2001 From: Patrick McCann Date: Wed, 31 Jan 2024 11:43:11 -0500 Subject: [PATCH 193/246] Update appnexusBidAdapter.js (#11009) --- modules/appnexusBidAdapter.js | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/modules/appnexusBidAdapter.js b/modules/appnexusBidAdapter.js index e62824cb2aa..c7d6fbd95a5 100644 --- a/modules/appnexusBidAdapter.js +++ b/modules/appnexusBidAdapter.js @@ -439,11 +439,7 @@ export const spec = { getUserSyncs: function (syncOptions, responses, gdprConsent, uspConsent, gppConsent) { function checkGppStatus(gppConsent) { - // this is a temporary measure to supress usersync in US-based GPP regions - // this logic will be revised when proper signals (akin to purpose1 from TCF2) can be determined for US GPP - if (gppConsent && Array.isArray(gppConsent.applicableSections)) { - return gppConsent.applicableSections.every(sec => typeof sec === 'number' && sec <= 5); - } + // user sync suppression for adapters is handled in activity controls and not needed in adapters return true; } From 4fbb1c7434af4172c5ebad5f85e6a807e5fa0733 Mon Sep 17 00:00:00 2001 From: Jason Quaccia Date: Wed, 31 Jan 2024 11:06:48 -0800 Subject: [PATCH 194/246] Topics fpd module : fix for intermitent failing test (#11013) * test pr for circleci * fix for failing topicsFpdModule tests * formatting fix * minor refactor * triggered another circle ci build --- modules/topicsFpdModule.js | 4 +++- test/spec/modules/topicsFpdModule_spec.js | 23 +++++++++++++++++++++++ 2 files changed, 26 insertions(+), 1 deletion(-) diff --git a/modules/topicsFpdModule.js b/modules/topicsFpdModule.js index 617ce0ad6f4..a31d72fc179 100644 --- a/modules/topicsFpdModule.js +++ b/modules/topicsFpdModule.js @@ -14,9 +14,11 @@ const MODULE_NAME = 'topicsFpd'; const DEFAULT_EXPIRATION_DAYS = 21; const DEFAULT_FETCH_RATE_IN_DAYS = 1; let LOAD_TOPICS_INITIALISE = false; +let iframeLoadedURL = []; export function reset() { LOAD_TOPICS_INITIALISE = false; + iframeLoadedURL = []; } const bidderIframeList = { @@ -29,11 +31,11 @@ const bidderIframeList = { iframeURL: 'https://topics.authorizedvault.com/topicsapi.html' }] } + export const coreStorage = getCoreStorageManager(MODULE_NAME); export const topicStorageName = 'prebid:topics'; export const lastUpdated = 'lastUpdated'; -const iframeLoadedURL = []; const TAXONOMIES = { // map from topic taxonomyVersion to IAB segment taxonomy '1': 600, diff --git a/test/spec/modules/topicsFpdModule_spec.js b/test/spec/modules/topicsFpdModule_spec.js index bc7df85db0d..4a79e7f77fd 100644 --- a/test/spec/modules/topicsFpdModule_spec.js +++ b/test/spec/modules/topicsFpdModule_spec.js @@ -384,6 +384,22 @@ describe('topics', () => { }); describe('cross-frame messages', () => { + before(() => { + config.setConfig({ + userSync: { + topics: { + maxTopicCaller: 3, + bidders: [ + { + bidder: 'pubmatic', + iframeURL: 'https://ads.pubmatic.com/AdServer/js/topics/topics_frame.html' + } + ], + }, + } + }); + }); + beforeEach(() => { // init iframe logic so that the receiveMessage origin check passes loadTopicsForBidders({ @@ -398,6 +414,10 @@ describe('topics', () => { }); }); + after(() => { + config.resetConfig(); + }) + it('should store segments if receiveMessage event is triggered with segment data', () => { receiveMessage(evt); let segments = new Map(safeJSONParse(storage.getDataFromLocalStorage(topicStorageName))); @@ -421,11 +441,13 @@ describe('handles fetch request for topics api headers', () => { beforeEach(() => { stubbedFetch = sinon.stub(window, 'fetch'); + reset(); }); afterEach(() => { stubbedFetch.restore(); storage.removeDataFromLocalStorage(topicStorageName); + config.resetConfig(); }); it('should make a fetch call when a fetchUrl is present for a selected bidder', () => { @@ -444,6 +466,7 @@ describe('handles fetch request for topics api headers', () => { }); stubbedFetch.returns(Promise.resolve(true)); + loadTopicsForBidders({ browsingTopics: true, featurePolicy: { From 1b4a1ce2121b8a267899b59323b220d228f88bfc Mon Sep 17 00:00:00 2001 From: jsnellbaker <31102355+jsnellbaker@users.noreply.github.com> Date: Wed, 31 Jan 2024 15:45:00 -0500 Subject: [PATCH 195/246] Appnexus Bid Adapter: Update DSA field names (#11027) --- modules/appnexusBidAdapter.js | 12 +++--------- test/spec/modules/appnexusBidAdapter_spec.js | 10 +++++----- 2 files changed, 8 insertions(+), 14 deletions(-) diff --git a/modules/appnexusBidAdapter.js b/modules/appnexusBidAdapter.js index c7d6fbd95a5..a6dc05a101f 100644 --- a/modules/appnexusBidAdapter.js +++ b/modules/appnexusBidAdapter.js @@ -354,22 +354,16 @@ export const spec = { if (bidderRequest?.ortb2?.regs?.ext?.dsa) { const pubDsaObj = bidderRequest.ortb2.regs.ext.dsa; const dsaObj = {}; - ['required', 'pubrender', 'datatopub'].forEach((dsaKey) => { + ['dsarequired', 'pubrender', 'datatopub'].forEach((dsaKey) => { if (isNumber(pubDsaObj[dsaKey])) { - if (dsaKey === 'required') { - // our client-side endpoint has a different name for the 'required' field - // ...this is the only exception to the openrtb spec for these fields - dsaObj.dsainfo = pubDsaObj[dsaKey]; - } else { - dsaObj[dsaKey] = pubDsaObj[dsaKey]; - } + dsaObj[dsaKey] = pubDsaObj[dsaKey]; } }); if (isArray(pubDsaObj.transparency) && pubDsaObj.transparency.every((v) => isPlainObject(v))) { const tpData = []; pubDsaObj.transparency.forEach((tpObj) => { - if (isStr(tpObj.domain) && tpObj.domain != '' && isArray(tpObj.params) && tpObj.params.every((v) => isNumber(v))) { + if (isStr(tpObj.domain) && tpObj.domain != '' && isArray(tpObj.dsaparams) && tpObj.dsaparams.every((v) => isNumber(v))) { tpData.push(tpObj); } }); diff --git a/test/spec/modules/appnexusBidAdapter_spec.js b/test/spec/modules/appnexusBidAdapter_spec.js index c44ef475290..cf6a1704bde 100644 --- a/test/spec/modules/appnexusBidAdapter_spec.js +++ b/test/spec/modules/appnexusBidAdapter_spec.js @@ -1214,15 +1214,15 @@ describe('AppNexusAdapter', function () { 'regs': { 'ext': { 'dsa': { - 'required': 1, + 'dsarequired': 1, 'pubrender': 0, 'datatopub': 1, 'transparency': [{ 'domain': 'good-domain', - 'params': [1, 2] + 'dsaparams': [1, 2] }, { 'domain': 'bad-setup', - 'params': ['1', 3] + 'dsaparams': ['1', 3] }] } } @@ -1235,12 +1235,12 @@ describe('AppNexusAdapter', function () { const payload = JSON.parse(request.data); expect(payload.dsa).to.exist; - expect(payload.dsa.dsainfo).to.equal(1); + expect(payload.dsa.dsarequired).to.equal(1); expect(payload.dsa.pubrender).to.equal(0); expect(payload.dsa.datatopub).to.equal(1); expect(payload.dsa.transparency).to.deep.equal([{ 'domain': 'good-domain', - 'params': [1, 2] + 'dsaparams': [1, 2] }]); }); From d000ec89b7f284e75ca7b678884608b24021ea6c Mon Sep 17 00:00:00 2001 From: Nayan Savla Date: Thu, 1 Feb 2024 16:34:17 +0300 Subject: [PATCH 196/246] Adding gpc in the bid request (#11028) --- modules/yieldmoBidAdapter.js | 14 ++++++++- test/spec/modules/yieldmoBidAdapter_spec.js | 33 +++++++++++++++++++++ 2 files changed, 46 insertions(+), 1 deletion(-) diff --git a/modules/yieldmoBidAdapter.js b/modules/yieldmoBidAdapter.js index 3dd5baf771a..7fee4af422c 100644 --- a/modules/yieldmoBidAdapter.js +++ b/modules/yieldmoBidAdapter.js @@ -100,6 +100,10 @@ export const spec = { if (topicsData) { serverRequest.topics = topicsData; } + const gpc = getGPCSignal(bidderRequest); + if (gpc) { + serverRequest.gpc = gpc; + } if (canAccessTopWindow()) { serverRequest.pr = (LOCAL_WINDOW.document && LOCAL_WINDOW.document.referrer) || ''; @@ -417,7 +421,10 @@ function openRtbRequest(bidRequests, bidderRequest) { if (schain) { openRtbRequest.schain = schain; } - + const gpc = getGPCSignal(bidderRequest); + if (gpc) { + deepSetValue(openRtbRequest, 'regs.ext.gpc', gpc); + } if (bidRequests[0].auctionId) { openRtbRequest.auctionId = bidRequests[0].auctionId; } @@ -425,6 +432,11 @@ function openRtbRequest(bidRequests, bidderRequest) { return openRtbRequest; } +function getGPCSignal(bidderRequest) { + const gpc = deepAccess(bidderRequest, 'ortb2.regs.ext.gpc'); + return gpc; +} + function getTopics(bidderRequest) { const userData = deepAccess(bidderRequest, 'ortb2.user.data') || []; const topicsData = userData.filter((dataObj) => { diff --git a/test/spec/modules/yieldmoBidAdapter_spec.js b/test/spec/modules/yieldmoBidAdapter_spec.js index 6d68ee79170..43daa9aca41 100644 --- a/test/spec/modules/yieldmoBidAdapter_spec.js +++ b/test/spec/modules/yieldmoBidAdapter_spec.js @@ -413,6 +413,22 @@ describe('YieldmoAdapter', function () { }); }); + it('should send gpc in the banner bid request', function () { + const biddata = build( + [mockBannerBid()], + mockBidderRequest({ + ortb2: { + regs: { + ext: { + gpc: '1' + }, + }, + }, + }) + ); + expect(biddata[0].data.gpc).to.equal('1'); + }); + it('should add eids to the banner bid request', function () { const params = { userIdAsEids: [{ @@ -667,6 +683,23 @@ describe('YieldmoAdapter', function () { }); }); + it('should send gpc in the bid request', function () { + let videoBidder = mockBidderRequest( + { + ortb2: { + regs: { + ext: { + gpc: '1', + }, + }, + }, + }, + [mockVideoBid()] + ); + let payload = buildAndGetData([mockVideoBid()], 0, videoBidder); + expect(payload.regs.ext.gpc).to.equal('1'); + }); + it('should add device info to payload if available', function () { let videoBidder = mockBidderRequest({ ortb2: { device: { From 0ad32448b86d3fbe2eb39a9e98290cf6e89cfe3f Mon Sep 17 00:00:00 2001 From: bjorn-lw <32431346+bjorn-lw@users.noreply.github.com> Date: Thu, 1 Feb 2024 14:34:24 +0100 Subject: [PATCH 197/246] Livewrapped Bidder: add support for ortb2imp (#11026) * Added support for the Price Floors Module * Use the ad server's currency when getting prices from the floors module * Default to USD if the ad server's currency can't be retrieved * Set the default currency at the right place * Added tests and made a minor change in how device width and height are calculated * Only include flrCur when ad requests contain floors * Use ortb native * Read ortb2imp --------- Co-authored-by: msmeza --- modules/livewrappedBidAdapter.js | 6 +- .../modules/livewrappedBidAdapter_spec.js | 222 +++++++++++++++--- 2 files changed, 190 insertions(+), 38 deletions(-) diff --git a/modules/livewrappedBidAdapter.js b/modules/livewrappedBidAdapter.js index cf1e690d862..cfbd2b5b3b5 100644 --- a/modules/livewrappedBidAdapter.js +++ b/modules/livewrappedBidAdapter.js @@ -4,7 +4,6 @@ import {config} from '../src/config.js'; import {find} from '../src/polyfill.js'; import {BANNER, NATIVE, VIDEO} from '../src/mediaTypes.js'; import {getStorageManager} from '../src/storageManager.js'; -import { convertOrtbRequestToProprietaryNative } from '../src/native.js'; /** * @typedef {import('../src/adapters/bidderFactory.js').BidRequest} BidRequest @@ -52,9 +51,6 @@ export const spec = { * @return ServerRequest Info describing the request to the server. */ buildRequests: function(bidRequests, bidderRequest) { - // convert Native ORTB definition to old-style prebid native definition - bidRequests = convertOrtbRequestToProprietaryNative(bidRequests); - const userId = find(bidRequests, hasUserId); const pubcid = find(bidRequests, hasPubcid); const publisherId = find(bidRequests, hasPublisherId); @@ -236,9 +232,9 @@ function bidToAdRequest(bid, currency) { adUnitId: bid.params.adUnitId, callerAdUnitId: bid.params.adUnitName || bid.adUnitCode || bid.placementCode, bidId: bid.bidId, - transactionId: bid.ortb2Imp?.ext?.tid, formats: getSizes(bid).map(sizeToFormat), flr: getBidFloor(bid, currency), + rtbData: bid.ortb2Imp, options: bid.params.options }; diff --git a/test/spec/modules/livewrappedBidAdapter_spec.js b/test/spec/modules/livewrappedBidAdapter_spec.js index 52eaf8d7d76..5ab00859d81 100644 --- a/test/spec/modules/livewrappedBidAdapter_spec.js +++ b/test/spec/modules/livewrappedBidAdapter_spec.js @@ -38,10 +38,9 @@ describe('Livewrapped adapter tests', function () { auctionId: 'F7557995-65F5-4682-8782-7D5D34D82A8C', ortb2Imp: { ext: { - tid: '3D1C8CF7-D288-4D7F-8ADD-97C553056C3D' + tid: '3D1C8CF7-D288-4D7F-8ADD-97C553056C3D', } }, - transactionId: '3D1C8CF7-D288-4D7F-8ADD-97C553056C3D' } ], start: 1472239426002, @@ -120,8 +119,49 @@ describe('Livewrapped adapter tests', function () { adUnitId: '9E153CED-61BC-479E-98DF-24DC0D01BA37', callerAdUnitId: 'panorama_d_1', bidId: '2ffb201a808da7', - transactionId: '3D1C8CF7-D288-4D7F-8ADD-97C553056C3D', - formats: [{width: 980, height: 240}, {width: 980, height: 120}] + formats: [{width: 980, height: 240}, {width: 980, height: 120}], + rtbData: { + ext: { + tid: '3D1C8CF7-D288-4D7F-8ADD-97C553056C3D' + }, + } + }] + }; + + expect(data).to.deep.equal(expectedQuery); + }); + + it('should send ortb2Imp', function() { + sandbox.stub(utils, 'isSafariBrowser').callsFake(() => false); + sandbox.stub(storage, 'cookiesAreEnabled').callsFake(() => true); + let ortb2ImpRequest = clone(bidderRequest); + ortb2ImpRequest.bids[0].ortb2Imp.ext.data = {key: 'value'}; + let result = spec.buildRequests(ortb2ImpRequest.bids, ortb2ImpRequest); + let data = JSON.parse(result.data); + + expect(result.url).to.equal('https://lwadm.com/ad'); + + let expectedQuery = { + auctionId: 'F7557995-65F5-4682-8782-7D5D34D82A8C', + publisherId: '26947112-2289-405D-88C1-A7340C57E63E', + userId: 'user id', + url: 'https://www.domain.com', + seats: {'dsp': ['seat 1']}, + version: '1.4', + width: 100, + height: 100, + cookieSupport: true, + adRequests: [{ + adUnitId: '9E153CED-61BC-479E-98DF-24DC0D01BA37', + callerAdUnitId: 'panorama_d_1', + bidId: '2ffb201a808da7', + formats: [{width: 980, height: 240}, {width: 980, height: 120}], + rtbData: { + ext: { + data: {key: 'value'}, + tid: '3D1C8CF7-D288-4D7F-8ADD-97C553056C3D' + }, + } }] }; @@ -157,12 +197,20 @@ describe('Livewrapped adapter tests', function () { adUnitId: '9E153CED-61BC-479E-98DF-24DC0D01BA37', callerAdUnitId: 'panorama_d_1', bidId: '2ffb201a808da7', - transactionId: '3D1C8CF7-D288-4D7F-8ADD-97C553056C3D', + rtbData: { + ext: { + tid: '3D1C8CF7-D288-4D7F-8ADD-97C553056C3D' + }, + }, formats: [{width: 980, height: 240}, {width: 980, height: 120}] }, { callerAdUnitId: 'box_d_1', bidId: '3ffb201a808da7', - transactionId: '3D1C8CF7-D288-4D7F-8ADD-97C553056C3D', + rtbData: { + ext: { + tid: '3D1C8CF7-D288-4D7F-8ADD-97C553056C3D' + }, + }, formats: [{width: 300, height: 250}] }] }; @@ -194,7 +242,11 @@ describe('Livewrapped adapter tests', function () { adRequests: [{ callerAdUnitId: 'caller id 1', bidId: '2ffb201a808da7', - transactionId: '3D1C8CF7-D288-4D7F-8ADD-97C553056C3D', + rtbData: { + ext: { + tid: '3D1C8CF7-D288-4D7F-8ADD-97C553056C3D' + }, + }, formats: [{width: 980, height: 240}, {width: 980, height: 120}] }] }; @@ -225,7 +277,11 @@ describe('Livewrapped adapter tests', function () { adRequests: [{ callerAdUnitId: 'panorama_d_1', bidId: '2ffb201a808da7', - transactionId: '3D1C8CF7-D288-4D7F-8ADD-97C553056C3D', + rtbData: { + ext: { + tid: '3D1C8CF7-D288-4D7F-8ADD-97C553056C3D' + }, + }, formats: [{width: 980, height: 240}, {width: 980, height: 120}] }] }; @@ -256,7 +312,11 @@ describe('Livewrapped adapter tests', function () { adUnitId: '9E153CED-61BC-479E-98DF-24DC0D01BA37', callerAdUnitId: 'panorama_d_1', bidId: '2ffb201a808da7', - transactionId: '3D1C8CF7-D288-4D7F-8ADD-97C553056C3D', + rtbData: { + ext: { + tid: '3D1C8CF7-D288-4D7F-8ADD-97C553056C3D' + }, + }, formats: [{width: 980, height: 240}, {width: 980, height: 120}] }] }; @@ -289,7 +349,11 @@ describe('Livewrapped adapter tests', function () { adRequests: [{ callerAdUnitId: 'panorama_d_1', bidId: '2ffb201a808da7', - transactionId: '3D1C8CF7-D288-4D7F-8ADD-97C553056C3D', + rtbData: { + ext: { + tid: '3D1C8CF7-D288-4D7F-8ADD-97C553056C3D' + }, + }, formats: [{width: 980, height: 240}, {width: 980, height: 120}] }] }; @@ -322,7 +386,11 @@ describe('Livewrapped adapter tests', function () { adRequests: [{ callerAdUnitId: 'panorama_d_1', bidId: '2ffb201a808da7', - transactionId: '3D1C8CF7-D288-4D7F-8ADD-97C553056C3D', + rtbData: { + ext: { + tid: '3D1C8CF7-D288-4D7F-8ADD-97C553056C3D' + }, + }, formats: [{width: 980, height: 240}, {width: 980, height: 120}] }] }; @@ -352,7 +420,11 @@ describe('Livewrapped adapter tests', function () { adRequests: [{ callerAdUnitId: 'panorama_d_1', bidId: '2ffb201a808da7', - transactionId: '3D1C8CF7-D288-4D7F-8ADD-97C553056C3D', + rtbData: { + ext: { + tid: '3D1C8CF7-D288-4D7F-8ADD-97C553056C3D' + }, + }, formats: [{width: 980, height: 240}, {width: 980, height: 120}], options: {keyvalues: [{key: 'key', value: 'value'}]} }] @@ -384,7 +456,11 @@ describe('Livewrapped adapter tests', function () { adRequests: [{ callerAdUnitId: 'panorama_d_1', bidId: '2ffb201a808da7', - transactionId: '3D1C8CF7-D288-4D7F-8ADD-97C553056C3D', + rtbData: { + ext: { + tid: '3D1C8CF7-D288-4D7F-8ADD-97C553056C3D' + }, + }, formats: [{width: 980, height: 240}, {width: 980, height: 120}] }] }; @@ -414,7 +490,11 @@ describe('Livewrapped adapter tests', function () { adRequests: [{ callerAdUnitId: 'panorama_d_1', bidId: '2ffb201a808da7', - transactionId: '3D1C8CF7-D288-4D7F-8ADD-97C553056C3D', + rtbData: { + ext: { + tid: '3D1C8CF7-D288-4D7F-8ADD-97C553056C3D' + }, + }, formats: [{width: 980, height: 240}, {width: 980, height: 120}], native: {'nativedata': 'content parsed serverside only'} }] @@ -445,7 +525,11 @@ describe('Livewrapped adapter tests', function () { adRequests: [{ callerAdUnitId: 'panorama_d_1', bidId: '2ffb201a808da7', - transactionId: '3D1C8CF7-D288-4D7F-8ADD-97C553056C3D', + rtbData: { + ext: { + tid: '3D1C8CF7-D288-4D7F-8ADD-97C553056C3D' + }, + }, formats: [{width: 980, height: 240}, {width: 980, height: 120}], native: {'nativedata': 'content parsed serverside only'}, banner: true @@ -477,7 +561,11 @@ describe('Livewrapped adapter tests', function () { adRequests: [{ callerAdUnitId: 'panorama_d_1', bidId: '2ffb201a808da7', - transactionId: '3D1C8CF7-D288-4D7F-8ADD-97C553056C3D', + rtbData: { + ext: { + tid: '3D1C8CF7-D288-4D7F-8ADD-97C553056C3D' + }, + }, formats: [{width: 980, height: 240}, {width: 980, height: 120}], video: {'videodata': 'content parsed serverside only'} }] @@ -525,7 +613,11 @@ describe('Livewrapped adapter tests', function () { adUnitId: '9E153CED-61BC-479E-98DF-24DC0D01BA37', callerAdUnitId: 'panorama_d_1', bidId: '2ffb201a808da7', - transactionId: '3D1C8CF7-D288-4D7F-8ADD-97C553056C3D', + rtbData: { + ext: { + tid: '3D1C8CF7-D288-4D7F-8ADD-97C553056C3D' + }, + }, formats: [{width: 980, height: 240}, {width: 980, height: 120}] }] }; @@ -555,7 +647,11 @@ describe('Livewrapped adapter tests', function () { adRequests: [{ callerAdUnitId: 'panorama_d_1', bidId: '2ffb201a808da7', - transactionId: '3D1C8CF7-D288-4D7F-8ADD-97C553056C3D', + rtbData: { + ext: { + tid: '3D1C8CF7-D288-4D7F-8ADD-97C553056C3D' + }, + }, formats: [{width: 728, height: 90}] }] }; @@ -592,7 +688,11 @@ describe('Livewrapped adapter tests', function () { adUnitId: '9E153CED-61BC-479E-98DF-24DC0D01BA37', callerAdUnitId: 'panorama_d_1', bidId: '2ffb201a808da7', - transactionId: '3D1C8CF7-D288-4D7F-8ADD-97C553056C3D', + rtbData: { + ext: { + tid: '3D1C8CF7-D288-4D7F-8ADD-97C553056C3D' + }, + }, formats: [{width: 980, height: 240}, {width: 980, height: 120}] }] }; @@ -627,7 +727,11 @@ describe('Livewrapped adapter tests', function () { adUnitId: '9E153CED-61BC-479E-98DF-24DC0D01BA37', callerAdUnitId: 'panorama_d_1', bidId: '2ffb201a808da7', - transactionId: '3D1C8CF7-D288-4D7F-8ADD-97C553056C3D', + rtbData: { + ext: { + tid: '3D1C8CF7-D288-4D7F-8ADD-97C553056C3D' + }, + }, formats: [{width: 980, height: 240}, {width: 980, height: 120}] }] }; @@ -660,7 +764,11 @@ describe('Livewrapped adapter tests', function () { adUnitId: '9E153CED-61BC-479E-98DF-24DC0D01BA37', callerAdUnitId: 'panorama_d_1', bidId: '2ffb201a808da7', - transactionId: '3D1C8CF7-D288-4D7F-8ADD-97C553056C3D', + rtbData: { + ext: { + tid: '3D1C8CF7-D288-4D7F-8ADD-97C553056C3D' + }, + }, formats: [{width: 980, height: 240}, {width: 980, height: 120}] }] }; @@ -700,7 +808,11 @@ describe('Livewrapped adapter tests', function () { adUnitId: '9E153CED-61BC-479E-98DF-24DC0D01BA37', callerAdUnitId: 'panorama_d_1', bidId: '2ffb201a808da7', - transactionId: '3D1C8CF7-D288-4D7F-8ADD-97C553056C3D', + rtbData: { + ext: { + tid: '3D1C8CF7-D288-4D7F-8ADD-97C553056C3D' + }, + }, formats: [{width: 980, height: 240}, {width: 980, height: 120}] }] }; @@ -730,7 +842,11 @@ describe('Livewrapped adapter tests', function () { adUnitId: '9E153CED-61BC-479E-98DF-24DC0D01BA37', callerAdUnitId: 'panorama_d_1', bidId: '2ffb201a808da7', - transactionId: '3D1C8CF7-D288-4D7F-8ADD-97C553056C3D', + rtbData: { + ext: { + tid: '3D1C8CF7-D288-4D7F-8ADD-97C553056C3D' + }, + }, formats: [{width: 980, height: 240}, {width: 980, height: 120}] }] }; @@ -760,7 +876,11 @@ describe('Livewrapped adapter tests', function () { adUnitId: '9E153CED-61BC-479E-98DF-24DC0D01BA37', callerAdUnitId: 'panorama_d_1', bidId: '2ffb201a808da7', - transactionId: '3D1C8CF7-D288-4D7F-8ADD-97C553056C3D', + rtbData: { + ext: { + tid: '3D1C8CF7-D288-4D7F-8ADD-97C553056C3D' + }, + }, formats: [{width: 980, height: 240}, {width: 980, height: 120}] }] }; @@ -810,7 +930,11 @@ describe('Livewrapped adapter tests', function () { adUnitId: '9E153CED-61BC-479E-98DF-24DC0D01BA37', callerAdUnitId: 'panorama_d_1', bidId: '2ffb201a808da7', - transactionId: '3D1C8CF7-D288-4D7F-8ADD-97C553056C3D', + rtbData: { + ext: { + tid: '3D1C8CF7-D288-4D7F-8ADD-97C553056C3D' + }, + }, formats: [{width: 980, height: 240}, {width: 980, height: 120}] }] }; @@ -842,7 +966,11 @@ describe('Livewrapped adapter tests', function () { adUnitId: '9E153CED-61BC-479E-98DF-24DC0D01BA37', callerAdUnitId: 'panorama_d_1', bidId: '2ffb201a808da7', - transactionId: '3D1C8CF7-D288-4D7F-8ADD-97C553056C3D', + rtbData: { + ext: { + tid: '3D1C8CF7-D288-4D7F-8ADD-97C553056C3D' + }, + }, formats: [{width: 980, height: 240}, {width: 980, height: 120}] }] }; @@ -876,7 +1004,11 @@ describe('Livewrapped adapter tests', function () { adUnitId: '9E153CED-61BC-479E-98DF-24DC0D01BA37', callerAdUnitId: 'panorama_d_1', bidId: '2ffb201a808da7', - transactionId: '3D1C8CF7-D288-4D7F-8ADD-97C553056C3D', + rtbData: { + ext: { + tid: '3D1C8CF7-D288-4D7F-8ADD-97C553056C3D' + }, + }, formats: [{width: 980, height: 240}, {width: 980, height: 120}] }] }; @@ -910,7 +1042,11 @@ describe('Livewrapped adapter tests', function () { adUnitId: '9E153CED-61BC-479E-98DF-24DC0D01BA37', callerAdUnitId: 'panorama_d_1', bidId: '2ffb201a808da7', - transactionId: '3D1C8CF7-D288-4D7F-8ADD-97C553056C3D', + rtbData: { + ext: { + tid: '3D1C8CF7-D288-4D7F-8ADD-97C553056C3D' + }, + }, formats: [{width: 980, height: 240}, {width: 980, height: 120}] }] }; @@ -946,7 +1082,11 @@ describe('Livewrapped adapter tests', function () { adUnitId: '9E153CED-61BC-479E-98DF-24DC0D01BA37', callerAdUnitId: 'panorama_d_1', bidId: '2ffb201a808da7', - transactionId: '3D1C8CF7-D288-4D7F-8ADD-97C553056C3D', + rtbData: { + ext: { + tid: '3D1C8CF7-D288-4D7F-8ADD-97C553056C3D' + }, + }, formats: [{width: 980, height: 240}, {width: 980, height: 120}] }] }; @@ -982,7 +1122,11 @@ describe('Livewrapped adapter tests', function () { adUnitId: '9E153CED-61BC-479E-98DF-24DC0D01BA37', callerAdUnitId: 'panorama_d_1', bidId: '2ffb201a808da7', - transactionId: '3D1C8CF7-D288-4D7F-8ADD-97C553056C3D', + rtbData: { + ext: { + tid: '3D1C8CF7-D288-4D7F-8ADD-97C553056C3D' + }, + }, formats: [{width: 980, height: 240}, {width: 980, height: 120}] }] }; @@ -1018,7 +1162,11 @@ describe('Livewrapped adapter tests', function () { adUnitId: '9E153CED-61BC-479E-98DF-24DC0D01BA37', callerAdUnitId: 'panorama_d_1', bidId: '2ffb201a808da7', - transactionId: '3D1C8CF7-D288-4D7F-8ADD-97C553056C3D', + rtbData: { + ext: { + tid: '3D1C8CF7-D288-4D7F-8ADD-97C553056C3D' + }, + }, formats: [{width: 980, height: 240}, {width: 980, height: 120}] }] }; @@ -1063,7 +1211,11 @@ describe('Livewrapped adapter tests', function () { adUnitId: '9E153CED-61BC-479E-98DF-24DC0D01BA37', callerAdUnitId: 'panorama_d_1', bidId: '2ffb201a808da7', - transactionId: '3D1C8CF7-D288-4D7F-8ADD-97C553056C3D', + rtbData: { + ext: { + tid: '3D1C8CF7-D288-4D7F-8ADD-97C553056C3D' + }, + }, formats: [{width: 980, height: 240}, {width: 980, height: 120}], flr: 10 }] @@ -1101,7 +1253,11 @@ describe('Livewrapped adapter tests', function () { adUnitId: '9E153CED-61BC-479E-98DF-24DC0D01BA37', callerAdUnitId: 'panorama_d_1', bidId: '2ffb201a808da7', - transactionId: '3D1C8CF7-D288-4D7F-8ADD-97C553056C3D', + rtbData: { + ext: { + tid: '3D1C8CF7-D288-4D7F-8ADD-97C553056C3D' + }, + }, formats: [{width: 980, height: 240}, {width: 980, height: 120}], flr: 10 }] From 7f0732f619e5c969f7749c995e2cf9f3d2bb94f6 Mon Sep 17 00:00:00 2001 From: SebRobert Date: Thu, 1 Feb 2024 14:39:45 +0100 Subject: [PATCH 198/246] BeOp Bid Adapter: add eids support (#11025) * BeOp Bid Adapter: Add eids support (#14) * Fix tests --- modules/beopBidAdapter.js | 2 ++ test/spec/modules/beopBidAdapter_spec.js | 18 ++++++++++++++++++ 2 files changed, 20 insertions(+) diff --git a/modules/beopBidAdapter.js b/modules/beopBidAdapter.js index ad52af824ee..89289654b32 100644 --- a/modules/beopBidAdapter.js +++ b/modules/beopBidAdapter.js @@ -72,6 +72,7 @@ export const spec = { is_amp: deepAccess(bidderRequest, 'referrerInfo.isAmp'), gdpr_applies: gdpr ? gdpr.gdprApplies : false, tc_string: (gdpr && gdpr.gdprApplies) ? gdpr.consentString : null, + eids: firstSlot.eids, }; const payloadString = JSON.stringify(payloadObject); @@ -159,6 +160,7 @@ function beOpRequestSlotsMaker(bid) { brc: getBidIdParameter('bidRequestsCount', bid), bdrc: getBidIdParameter('bidderRequestCount', bid), bwc: getBidIdParameter('bidderWinsCount', bid), + eids: bid.userIdAsEids, } } diff --git a/test/spec/modules/beopBidAdapter_spec.js b/test/spec/modules/beopBidAdapter_spec.js index c77e304e539..663d622e505 100644 --- a/test/spec/modules/beopBidAdapter_spec.js +++ b/test/spec/modules/beopBidAdapter_spec.js @@ -312,4 +312,22 @@ describe('BeOp Bid Adapter tests', () => { expect(payload.kwds).to.include('keywords'); }) }) + + describe('Ensure eids are get', function() { + let bidRequests = []; + afterEach(function () { + bidRequests = []; + }); + + it(`should get eids from bid`, function () { + let bid = Object.assign({}, validBid); + bid.userIdAsEids = [{source: 'provider.com', uids: [{id: 'someid', atype: 1, ext: {whatever: true}}]}]; + bidRequests.push(bid); + + const request = spec.buildRequests(bidRequests, {}); + const payload = JSON.parse(request.data); + expect(payload.eids).to.exist; + expect(payload.eids[0].source).to.equal('provider.com'); + }); + }) }); From 30513cfe1d0afbb83e7c25d497216affd3aa8050 Mon Sep 17 00:00:00 2001 From: Olivier Date: Thu, 1 Feb 2024 14:55:18 +0100 Subject: [PATCH 199/246] AdagioBidAdapter: don't try to compute slot position if element is hidden (#11033) --- modules/adagioBidAdapter.js | 10 +++------- test/spec/modules/adagioBidAdapter_spec.js | 7 +++---- 2 files changed, 6 insertions(+), 11 deletions(-) diff --git a/modules/adagioBidAdapter.js b/modules/adagioBidAdapter.js index 112722383a9..efa6777a0dd 100644 --- a/modules/adagioBidAdapter.js +++ b/modules/adagioBidAdapter.js @@ -795,16 +795,12 @@ function getSlotPosition(adUnitElementId) { const scrollLeft = wt.pageXOffset || docEl.scrollLeft || body.scrollLeft; const elComputedStyle = wt.getComputedStyle(domElement, null); - const elComputedDisplay = elComputedStyle.display || 'block'; - const mustDisplayElement = elComputedDisplay === 'none'; + const mustDisplayElement = elComputedStyle.display === 'none'; if (mustDisplayElement) { - domElement.style = domElement.style || {}; - const originalDisplay = domElement.style.display; - domElement.style.display = 'block'; - box = domElement.getBoundingClientRect(); - domElement.style.display = originalDisplay || null; + logWarn(LOG_PREFIX, 'The element is hidden. The slot position cannot be computed.'); } + position.x = Math.round(box.left + scrollLeft - clientLeft); position.y = Math.round(box.top + scrollTop - clientTop); } catch (err) { diff --git a/test/spec/modules/adagioBidAdapter_spec.js b/test/spec/modules/adagioBidAdapter_spec.js index 744f3c69e83..38dbaf348fd 100644 --- a/test/spec/modules/adagioBidAdapter_spec.js +++ b/test/spec/modules/adagioBidAdapter_spec.js @@ -1497,8 +1497,8 @@ describe('Adagio bid adapter', () => { expect(result.user_timestamp).to.be.a('String'); }); - it('should return `adunit_position` feature when the slot is hidden', function () { - const elem = fixtures.getElementById(); + it('should return `adunit_position` feature when the slot is hidden with value 0x0', function () { + const elem = fixtures.getElementById('0', '0', '0', '0'); sandbox.stub(window.top.document, 'getElementById').returns(elem); sandbox.stub(window.top, 'getComputedStyle').returns({ display: 'none' }); sandbox.stub(utils, 'inIframe').returns(false); @@ -1516,8 +1516,7 @@ describe('Adagio bid adapter', () => { const requests = spec.buildRequests([bidRequest], bidderRequest); const result = requests[0].data.adUnits[0].features; - expect(result.adunit_position).to.match(/^[\d]+x[\d]+$/); - expect(elem.style.display).to.equal(null); // set null to reset style + expect(result.adunit_position).to.equal('0x0'); }); }); From 7d3ff582753179b6fb8272122a2d6dea8b0cbab2 Mon Sep 17 00:00:00 2001 From: Demetrio Girardi Date: Thu, 1 Feb 2024 11:05:05 -0800 Subject: [PATCH 200/246] priceFloors: do not log error on missing floor definitions (#11037) --- modules/id5IdSystem.js | 8 ++++---- modules/priceFloors.js | 9 ++++++--- 2 files changed, 10 insertions(+), 7 deletions(-) diff --git a/modules/id5IdSystem.js b/modules/id5IdSystem.js index b10f3c5d2cf..c585f2fdae0 100644 --- a/modules/id5IdSystem.js +++ b/modules/id5IdSystem.js @@ -51,26 +51,26 @@ export const storage = getStorageManager({moduleType: MODULE_TYPE_UID, moduleNam * @property {string} [universal_uid] - The encrypted ID5 ID to pass to bidders * @property {Object} [ext] - The extensions object to pass to bidders * @property {Object} [ab_testing] - A/B testing configuration -*/ + */ /** * @typedef {Object} FetchCallConfig * @property {string} [url] - The URL for the fetch endpoint * @property {Object} [overrides] - Overrides to apply to fetch parameters -*/ + */ /** * @typedef {Object} ExtensionsCallConfig * @property {string} [url] - The URL for the extensions endpoint * @property {string} [method] - Overrides the HTTP method to use to make the call * @property {Object} [body] - Specifies a body to pass to the extensions endpoint -*/ + */ /** * @typedef {Object} DynamicConfig * @property {FetchCallConfig} [fetchCall] - The fetch call configuration * @property {ExtensionsCallConfig} [extensionsCall] - The extensions call configuration -*/ + */ /** * @typedef {Object} ABTestingConfig diff --git a/modules/priceFloors.js b/modules/priceFloors.js index 9c3869c480a..c2d7db97047 100644 --- a/modules/priceFloors.js +++ b/modules/priceFloors.js @@ -445,10 +445,13 @@ export function continueAuction(hookConfig) { } function validateSchemaFields(fields) { - if (Array.isArray(fields) && fields.length > 0 && fields.every(field => allowedFields.indexOf(field) !== -1)) { - return true; + if (Array.isArray(fields) && fields.length > 0) { + if (fields.every(field => allowedFields.includes(field))) { + return true; + } else { + logError(`${MODULE_NAME}: Fields received do not match allowed fields`); + } } - logError(`${MODULE_NAME}: Fields received do not match allowed fields`); return false; } From 804d078ca1fe0036cae5fdff598d8bb46261c10c Mon Sep 17 00:00:00 2001 From: "Prebid.js automated release" Date: Fri, 2 Feb 2024 13:46:21 +0000 Subject: [PATCH 201/246] Prebid 8.34.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 692afaa120a..df8078e4596 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,6 +1,6 @@ { "name": "prebid.js", - "version": "8.34.0-pre", + "version": "8.34.0", "lockfileVersion": 2, "requires": true, "packages": { diff --git a/package.json b/package.json index 9d4347eeab1..9f56e033c1b 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "prebid.js", - "version": "8.34.0-pre", + "version": "8.34.0", "description": "Header Bidding Management Library", "main": "src/prebid.js", "scripts": { From d53dae228b4f6565fe479ef60671478fa9e977a8 Mon Sep 17 00:00:00 2001 From: "Prebid.js automated release" Date: Fri, 2 Feb 2024 13:46:21 +0000 Subject: [PATCH 202/246] Increment version to 8.35.0-pre --- package-lock.json | 2 +- package.json | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/package-lock.json b/package-lock.json index df8078e4596..9d7bc733e29 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,6 +1,6 @@ { "name": "prebid.js", - "version": "8.34.0", + "version": "8.35.0-pre", "lockfileVersion": 2, "requires": true, "packages": { diff --git a/package.json b/package.json index 9f56e033c1b..6741b36d415 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "prebid.js", - "version": "8.34.0", + "version": "8.35.0-pre", "description": "Header Bidding Management Library", "main": "src/prebid.js", "scripts": { From 60b6adaf5fd88d70e9f1e122706384d8eb6da237 Mon Sep 17 00:00:00 2001 From: Laurentiu Badea Date: Sat, 3 Feb 2024 07:56:42 -0800 Subject: [PATCH 203/246] OpenX Bid Adapter: add ortb2Imp to PAAPI auctionSignals (#11012) * OpenX Bid Adapter: add ortb2Imp to PAAPI auctionSignals * Use imp instead of searching ortbRequest --- modules/openxBidAdapter.js | 8 +++++--- test/spec/modules/openxBidAdapter_spec.js | 19 +++++++++++++++++++ 2 files changed, 24 insertions(+), 3 deletions(-) diff --git a/modules/openxBidAdapter.js b/modules/openxBidAdapter.js index 0f8bee213f7..a99bd1c5325 100644 --- a/modules/openxBidAdapter.js +++ b/modules/openxBidAdapter.js @@ -107,9 +107,11 @@ const converter = ortbConverter({ fledgeAuctionConfigs = Object.entries(fledgeAuctionConfigs).map(([bidId, cfg]) => { return { bidId, - config: Object.assign({ - auctionSignals: {}, - }, cfg) + config: mergeDeep(Object.assign({}, cfg), { + auctionSignals: { + ortb2Imp: context.impContext[bidId]?.imp, + }, + }), } }); return { diff --git a/test/spec/modules/openxBidAdapter_spec.js b/test/spec/modules/openxBidAdapter_spec.js index 1af0fce103d..7c504bca50b 100644 --- a/test/spec/modules/openxBidAdapter_spec.js +++ b/test/spec/modules/openxBidAdapter_spec.js @@ -1508,6 +1508,25 @@ describe('OpenxRtbAdapter', function () { expect(response.fledgeAuctionConfigs.length).to.equal(1); expect(response.fledgeAuctionConfigs[0].bidId).to.equal('test-bid-id'); }); + + it('should inject ortb2Imp in auctionSignals', function () { + const auctionConfig = response.fledgeAuctionConfigs[0].config; + expect(auctionConfig).to.deep.include({ + auctionSignals: { + ortb2Imp: { + id: 'test-bid-id', + tagid: '12345678', + banner: { + topframe: 0, + format: bidRequestConfigs[0].mediaTypes.banner.sizes.map(([w, h]) => ({w, h})) + }, + ext: { + divid: 'adunit-code', + } + } + } + }); + }) }); }); From 9961236f8dc3e6a6d25d2aa5dc96e76bd93c801b Mon Sep 17 00:00:00 2001 From: asurovenko-zeta <80847074+asurovenko-zeta@users.noreply.github.com> Date: Sun, 4 Feb 2024 22:16:19 +0100 Subject: [PATCH 204/246] ZetaGlobalSsp Bid Adapter : cleanup object (#11049) * ZetaGlobalSsp adapter: cleanup object * Update zeta_global_sspBidAdapter.js --------- Co-authored-by: Surovenko Alexey Co-authored-by: Alexey Surovenko Co-authored-by: Chris Huie --- modules/zeta_global_sspBidAdapter.js | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/modules/zeta_global_sspBidAdapter.js b/modules/zeta_global_sspBidAdapter.js index 4cdda3a1105..80856462fe1 100644 --- a/modules/zeta_global_sspBidAdapter.js +++ b/modules/zeta_global_sspBidAdapter.js @@ -178,7 +178,7 @@ export const spec = { return { method: 'POST', url: url, - data: JSON.stringify(payload), + data: JSON.stringify(clearEmpties(payload)), }; }, @@ -373,4 +373,17 @@ function provideMediaType(zetaBid, bid, bidRequest) { } } +function clearEmpties(o) { + for (let k in o) { + if (!o[k] || typeof o[k] !== 'object') { + continue; + } + clearEmpties(o[k]); + if (Object.keys(o[k]).length === 0) { + delete o[k]; + } + } + return o; +} + registerBidder(spec); From 3dc8deab02336c338ea0ab905a44b9a655d9234f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=9D=8E=E5=90=91=E5=86=9B?= <401154047@qq.com> Date: Mon, 5 Feb 2024 15:59:02 +0800 Subject: [PATCH 205/246] zmaticoo Bid Adapter : add support for video (#11016) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * feat:zmaticoo adapter for video Signed-off-by: adam * feat:just test ci/circleci:build failed Signed-off-by: adam * feat:add unit test Signed-off-by: adam * feat:fix response.seatbid and advertiserDomains empty Signed-off-by: adam * feat:add unit test Signed-off-by: adam --------- Signed-off-by: adam Co-authored-by: adam --- modules/zmaticooBidAdapter.js | 201 +++++++++--- modules/zmaticooBidAdapter.md | 37 ++- test/spec/modules/zmaticooBidAdapter_spec.js | 302 ++++++++++++++++--- 3 files changed, 454 insertions(+), 86 deletions(-) diff --git a/modules/zmaticooBidAdapter.js b/modules/zmaticooBidAdapter.js index 8566d58c6de..eeb130af7d5 100644 --- a/modules/zmaticooBidAdapter.js +++ b/modules/zmaticooBidAdapter.js @@ -1,6 +1,6 @@ -import {deepAccess, logWarn} from '../src/utils.js'; +import {deepAccess, isArray, isBoolean, isNumber, isStr, logWarn} from '../src/utils.js'; import {registerBidder} from '../src/adapters/bidderFactory.js'; -import {BANNER} from '../src/mediaTypes.js'; +import {BANNER, VIDEO} from '../src/mediaTypes.js'; const BIDDER_CODE = 'zmaticoo'; const ENDPOINT_URL = 'https://bid.zmaticoo.com/prebid/bid'; @@ -8,9 +8,31 @@ const DEFAULT_CUR = 'USD'; const TTL = 200; const NET_REV = true; +const DATA_TYPES = { + 'NUMBER': 'number', 'STRING': 'string', 'BOOLEAN': 'boolean', 'ARRAY': 'array', 'OBJECT': 'object' +}; +const VIDEO_CUSTOM_PARAMS = { + 'mimes': DATA_TYPES.ARRAY, + '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, + 'plcmt': DATA_TYPES.NUMBER, + 'minbitrate': DATA_TYPES.NUMBER, + 'maxbitrate': DATA_TYPES.NUMBER, + 'skip': DATA_TYPES.NUMBER +} + export const spec = { code: BIDDER_CODE, - supportedMediaTypes: [BANNER], + supportedMediaTypes: [BANNER, VIDEO], /** * Determines whether or not the given bid request is valid. @@ -20,6 +42,10 @@ export const spec = { */ isBidRequestValid: function (bid) { // check for all required bid fields + if (!(hasBannerMediaType(bid) || hasVideoMediaType(bid))) { + logWarn('Invalid bid request - missing required mediaTypes'); + return false; + } if (!(bid && bid.bidId && bid.params)) { logWarn('Invalid bid request - missing required bid data'); return false; @@ -30,7 +56,7 @@ export const spec = { return false; } - if (!(bid.params.device && bid.params.device.ip)) { + if (!(bid.params.device)) { logWarn('Invalid bid request - missing required device data'); return false; } @@ -48,19 +74,49 @@ export const spec = { const secure = 1; const request = validBidRequests[0]; const params = request.params; - let impData = { - id: request.bidId, - secure: secure, - banner: buildBanner(request), - ext: { - bidder: { - pubId: params.pubId + const imps = validBidRequests.map(request => { + const impData = { + id: request.bidId, + secure: secure, + ext: { + bidder: { + pubId: params.pubId + } } + }; + if (params.tagid) { + impData.tagid = params.tagid; } - }; + if (request.mediaTypes) { + for (const mediaType in request.mediaTypes) { + switch (mediaType) { + case BANNER: + impData.banner = buildBanner(request); + break; + case VIDEO: + impData.video = buildVideo(request); + break; + } + } + } + if (typeof bidderRequest.getFloor === 'function') { + const floorInfo = bidderRequest.getFloor({ + currency: 'USD', + mediaType: impData.video ? 'video' : 'banner', + size: [impData.video ? impData.video.w : impData.banner.w, impData.video ? impData.video.h : impData.banner.h] + }); + if (floorInfo && floorInfo.floor) { + impData.bidfloor = floorInfo.floor; + } + } + if (!impData.bidfloor && params.bidfloor) { + impData.bidfloor = params.bidfloor; + } + return impData; + }); let payload = { id: bidderRequest.bidderRequestId, - imp: [impData], + imp: imps, site: params.site ? params.site : {}, app: params.app ? params.app : {}, device: params.device ? params.device : {}, @@ -79,19 +135,21 @@ export const spec = { regs: params.regs ? params.regs : {}, ext: params.ext ? params.ext : {} }; - + payload.regs.ext = {} + payload.user.ext = {} payload.device.ua = navigator.userAgent; payload.device.ip = navigator.ip; - payload.site.page = bidderRequest.refererInfo.page; + payload.site.page = bidderRequest?.refererInfo?.page || 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) { payload.test = params.test; } - if (request.gdprConsent) { - payload.regs.ext = Object.assign(payload.regs.ext, {gdpr: request.gdprConsent.gdprApplies === true ? 1 : 0}); + if (bidderRequest.gdprConsent) { + payload.regs.ext = Object.assign(payload.regs.ext, {gdpr: bidderRequest.gdprConsent.gdprApplies == true ? 1 : 0}); } - if (request.gdprConsent && request.gdprConsent.gdprApplies) { - payload.user.ext = Object.assign(payload.user.ext, {consent: request.gdprConsent.consentString}); + if (bidderRequest.gdprConsent && bidderRequest.gdprConsent.gdprApplies) { + payload.user.ext = Object.assign(payload.user.ext, {consent: bidderRequest.gdprConsent.consentString}); } const postUrl = ENDPOINT_URL; return { @@ -103,28 +161,37 @@ export const spec = { * Unpack the response from the server into a list of bids. * * @param {ServerResponse} serverResponse A successful response from the server. - * @param bidRequest The payload from the server's response. + * @param {BidRequest} 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 zresponse = serverResponse.body; - let zbid = zresponse.seatbid[0].bid[0]; - let bid = { - requestId: zbid.impid, - cpm: zbid.price, - currency: zbid.cur, - width: zbid.w, - height: zbid.h, - ad: zbid.adm, - ttl: TTL, - creativeId: zbid.crid, - netRevenue: NET_REV - }; - bidResponse.push(bid); + let bidResponses = []; + const response = (serverResponse || {}).body; + if (response && response.seatbid && response.seatbid.length && response.seatbid[0].bid && response.seatbid[0].bid.length) { + response.seatbid.forEach(zmSeatbid => { + zmSeatbid.bid.forEach(zmBid => { + let bid = { + requestId: zmBid.impid, + cpm: zmBid.price, + currency: response.cur, + width: zmBid.w, + height: zmBid.h, + ad: zmBid.adm, + ttl: TTL, + creativeId: zmBid.crid, + netRevenue: NET_REV, + }; + bid.meta = { + advertiserDomains: (zmBid.adomain && zmBid.adomain.length) ? zmBid.adomain : [] + }; + if (zmBid.ext && zmBid.ext.vast_url) { + bid.vastXml = zmBid.ext.vast_url; + } + bidResponses.push(bid); + }) + }) } - return bidResponse; + return bidResponses; } } @@ -138,4 +205,64 @@ function buildBanner(request) { }; } +function buildVideo(request) { + let video = {}; + const videoParams = deepAccess(request, 'mediaTypes.video', {}); + for (const key in VIDEO_CUSTOM_PARAMS) { + if (videoParams.hasOwnProperty(key)) { + video[key] = checkParamDataType(key, videoParams[key], VIDEO_CUSTOM_PARAMS[key]); + } + } + if (videoParams.playerSize) { + if (isArray(videoParams.playerSize[0])) { + video.w = parseInt(videoParams.playerSize[0][0], 10); + video.h = parseInt(videoParams.playerSize[0][1], 10); + } else if (isNumber(videoParams.playerSize[0])) { + video.w = parseInt(videoParams.playerSize[0], 10); + video.h = parseInt(videoParams.playerSize[1], 10); + } + } + return video; +} + +export function checkParamDataType(key, value, datatype) { + let functionToExecute; + switch (datatype) { + case DATA_TYPES.BOOLEAN: + functionToExecute = isBoolean; + break; + case DATA_TYPES.NUMBER: + functionToExecute = isNumber; + break; + case DATA_TYPES.STRING: + functionToExecute = isStr; + break; + case DATA_TYPES.ARRAY: + functionToExecute = isArray; + break; + } + if (functionToExecute(value)) { + return value; + } + logWarn('Ignoring param key: ' + key + ', expects ' + datatype + ', found ' + typeof value); + return undefined; +} + +function hasBannerMediaType(bidRequest) { + return !!deepAccess(bidRequest, 'mediaTypes.banner'); +} + +/** + * @param {BidRequest} bidRequest bid request + */ +function hasVideoMediaType(bidRequest) { + return !!deepAccess(bidRequest, 'mediaTypes.video'); +} + +export function _getDomainFromURL(url) { + let anchor = document.createElement('a'); + anchor.href = url; + return anchor.hostname; +} + registerBidder(spec); diff --git a/modules/zmaticooBidAdapter.md b/modules/zmaticooBidAdapter.md index 95959ef218c..06c54b10b11 100644 --- a/modules/zmaticooBidAdapter.md +++ b/modules/zmaticooBidAdapter.md @@ -12,6 +12,8 @@ zMaticoo Bidder Adapter for Prebid.js. # Test Parameters +## banner + ``` var adUnits = [ { @@ -23,7 +25,7 @@ zMaticoo Bidder Adapter for Prebid.js. bids: [ { bidder: 'zmaticoo', - bidId: '12345', + bidId: '123456', params: { user: { uid: '12345', @@ -43,3 +45,36 @@ zMaticoo Bidder Adapter for Prebid.js. } ]; ``` + +## video + +``` + var adUnits = [{ + code: 'test1', + mediaTypes: { + video: { + playerSize: [480, 320], + mimes: ['video/mp4'], + context: 'instream', + 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 + } + }, + bids: [{ + bidder: "zmaticoo", + bidId: '123456', + params: { + pubId: 'prebid-test', + site: {domain: "test.com"} + } + }] +}]; +``` diff --git a/test/spec/modules/zmaticooBidAdapter_spec.js b/test/spec/modules/zmaticooBidAdapter_spec.js index 898dccf1fde..1634340ac6a 100644 --- a/test/spec/modules/zmaticooBidAdapter_spec.js +++ b/test/spec/modules/zmaticooBidAdapter_spec.js @@ -1,4 +1,5 @@ -import {spec} from '../../../modules/zmaticooBidAdapter.js' +import {checkParamDataType, spec} from '../../../modules/zmaticooBidAdapter.js' +import {deepClone} from '../../../src/utils'; describe('zMaticoo Bidder Adapter', function () { const bannerRequest = [{ @@ -24,60 +25,265 @@ describe('zMaticoo Bidder Adapter', function () { } }, pubId: 'prebid-test', - test: 1 + test: 1, + bidfloor: 1, + tagid: 'test' + } + }]; + const bannerRequest1 = [{ + bidId: '1234511', + auctionId: '223', + mediaTypes: { + banner: { + sizes: [[320, 50]], + } + }, + refererInfo: { + page: 'testprebid.com' + }, + params: { + user: { + uid: '12345', + buyeruid: '12345' + }, + device: { + ip: '111.222.33.44', + geo: { + country: 'USA' + } + }, + pubId: 'prebid-test', + test: 1, + tagid: 'test' + }, + gdprConsent: { + gdprApplies: 1, + consentString: 'consentString' + }, + getFloor: function () { + return { + currency: 'USD', + floor: 0.5, + } + }, + }]; + const videoRequest = [{ + bidId: '1234511', + auctionId: '223', + mediaTypes: { + video: { + playerSize: [480, 320], + mimes: ['video/mp4'], + context: 'instream', + placement: 1, + maxduration: 30, + minduration: 15, + pos: 1, + startdelay: 10, + protocols: [2, 3], + api: [2, 3], + playbackmethod: [2, 6], + skip: 10, + } + }, + refererInfo: { + page: 'testprebid.com' + }, + params: { + user: { + uid: '12345', + buyeruid: '12345' + }, + device: { + ip: '111.222.33.44', + geo: { + country: 'USA' + } + }, + pubId: 'prebid-test', + test: 1, + tagid: 'test', + bidfloor: 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 videoRequest1 = [{ + bidId: '1234511', + auctionId: '223', + mediaTypes: { + video: { + playerSize: [[480, 320]], + mimes: ['video/mp4'], + context: 'instream', + placement: 1, + maxduration: 30, + minduration: 15, + pos: 1, + startdelay: 10, + protocols: [2, 3], + api: [2, 3], + playbackmethod: [2, 6], + skip: 10, + } + }, + params: { + user: { + uid: '12345', + buyeruid: '12345' + }, + device: { + ip: '111.222.33.44', + geo: { + country: 'USA' + } + }, + pubId: 'prebid-test', + test: 1, + tagid: 'test', + bidfloor: 1 + } + }]; - const payload = request.data; - expect(payload).to.not.be.empty; - }); + describe('isBidRequestValid', function () { + it('this is valid bidrequest', function () { + const validBid = spec.isBidRequestValid(videoRequest[0]); + expect(validBid).to.be.true; + }); + it('missing required bid data {bid}', function () { + const invalidBid = spec.isBidRequestValid(null); + expect(invalidBid).to.be.false; + }); + it('missing required bid.bidId', function () { + const request = deepClone(videoRequest[0]) + delete request.bidId + const invalidBid = spec.isBidRequestValid(request); + expect(invalidBid).to.be.false; + }); + it('missing required params.pubId', function () { + const request = deepClone(videoRequest[0]) + delete request.params.pubId + const invalidBid = spec.isBidRequestValid(request); + expect(invalidBid).to.be.false; + }); + it('missing required device data', function () { + const request = deepClone(videoRequest[0]) + delete request.params.device + const invalidBid = spec.isBidRequestValid(request); + expect(invalidBid).to.be.false; + }); + }) + describe('buildRequests', function () { + it('Test the banner 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; + }); + it('Test the video request processing function', function () { + const request = spec.buildRequests(videoRequest, videoRequest[0]); + expect(request).to.not.be.empty; + const payload = request.data; + expect(payload).to.not.be.empty; + }); + it('Test the param', function () { + const request = spec.buildRequests(bannerRequest, bannerRequest[0]); + const payload = JSON.parse(request.data); + expect(payload.imp[0].tagid).to.eql(videoRequest[0].params.tagid); + expect(payload.imp[0].bidfloor).to.eql(videoRequest[0].params.bidfloor); + }); + it('Test video object', function () { + const request = spec.buildRequests(videoRequest, videoRequest[0]); + const payload = JSON.parse(request.data); + expect(payload.imp[0].video).to.exist; + expect(payload.imp[0].video.minduration).to.eql(videoRequest[0].mediaTypes.video.minduration); + expect(payload.imp[0].video.maxduration).to.eql(videoRequest[0].mediaTypes.video.maxduration); + expect(payload.imp[0].video.protocols).to.eql(videoRequest[0].mediaTypes.video.protocols); + expect(payload.imp[0].video.mimes).to.eql(videoRequest[0].mediaTypes.video.mimes); + expect(payload.imp[0].video.w).to.eql(480); + expect(payload.imp[0].video.h).to.eql(320); + expect(payload.imp[0].banner).to.be.undefined; + }); - const responseBody = { - id: '12345', - seatbid: [ - { - bid: [ - { - id: 'auctionId', - impid: 'impId', - price: 0.0, - adm: 'adMarkup', - crid: 'creativeId', - h: 50, - w: 320 - } - ] - } - ], - cur: 'USD' - }; + it('Test video isArray size', function () { + const request = spec.buildRequests(videoRequest1, videoRequest1[0]); + const payload = JSON.parse(request.data); + expect(payload.imp[0].video.w).to.eql(480); + expect(payload.imp[0].video.h).to.eql(320); + }); + it('Test banner object', function () { + const request = spec.buildRequests(bannerRequest, bannerRequest[0]); + const payload = JSON.parse(request.data); + expect(payload.imp[0].video).to.be.undefined; + expect(payload.imp[0].banner).to.exist; + }); - it('Test the response parsing function', function () { - const receivedBid = responseBody.seatbid[0].bid[0]; - const response = {}; - response.body = responseBody; + it('Test provide gdpr and ccpa values in payload', function () { + const request = spec.buildRequests(bannerRequest1, bannerRequest1[0]); + const payload = JSON.parse(request.data); + expect(payload.user.ext.consent).to.eql('consentString'); + expect(payload.regs.ext.gdpr).to.eql(1); + }); - const bidResponse = spec.interpretResponse(response, null); - expect(bidResponse).to.not.be.empty; + it('Test bidfloor is function', function () { + const request = spec.buildRequests(bannerRequest1, bannerRequest1[0]); + const payload = JSON.parse(request.data); + expect(payload.imp[0].bidfloor).to.eql(0.5); + }); + }); + describe('checkParamDataType tests', function () { + it('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')); + }); - 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); + it('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; + }); + }) + describe('interpretResponse', function () { + const responseBody = { + id: '12345', + seatbid: [ + { + bid: [ + { + id: 'auctionId', + impid: 'impId', + price: 0.0, + adm: 'adMarkup', + crid: 'creativeId', + adomain: ['test.com'], + h: 50, + w: 320, + ext: { + vast_url: '' + } + } + ] + } + ], + 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.vastXml).to.equal(receivedBid.ext.vast_url); + expect(bid.meta.advertiserDomains).to.equal(receivedBid.adomain); + }); }); }); From 6f93a5b9a3c52926aa8907a997e0b416a46c6d2f Mon Sep 17 00:00:00 2001 From: asurovenko-zeta <80847074+asurovenko-zeta@users.noreply.github.com> Date: Mon, 5 Feb 2024 15:07:22 +0100 Subject: [PATCH 206/246] ZetaGlobalSsp: provide device.sua object (#11050) Co-authored-by: Surovenko Alexey Co-authored-by: Alexey Surovenko --- modules/zeta_global_sspBidAdapter.js | 6 +++++ .../modules/zeta_global_sspBidAdapter_spec.js | 25 +++++++++++++++++++ 2 files changed, 31 insertions(+) diff --git a/modules/zeta_global_sspBidAdapter.js b/modules/zeta_global_sspBidAdapter.js index 80856462fe1..295108f31b4 100644 --- a/modules/zeta_global_sspBidAdapter.js +++ b/modules/zeta_global_sspBidAdapter.js @@ -143,6 +143,12 @@ export const spec = { payload.device.ua = navigator.userAgent; payload.device.language = navigator.language; + payload.device.w = screen.width; + payload.device.h = screen.height; + + if (bidderRequest?.ortb2?.device?.sua) { + payload.device.sua = bidderRequest.ortb2.device.sua; + } if (params.test) { payload.test = params.test; diff --git a/test/spec/modules/zeta_global_sspBidAdapter_spec.js b/test/spec/modules/zeta_global_sspBidAdapter_spec.js index 9ef97019a98..b06a6700ebf 100644 --- a/test/spec/modules/zeta_global_sspBidAdapter_spec.js +++ b/test/spec/modules/zeta_global_sspBidAdapter_spec.js @@ -126,6 +126,16 @@ describe('Zeta Ssp Bid Adapter', function () { userIdAsEids: eids, timeout: 500, ortb2: { + device: { + sua: { + mobile: 1, + architecture: 'arm', + platform: { + brand: 'Chrome', + version: ['102'] + } + } + }, user: { data: [ { @@ -632,4 +642,19 @@ describe('Zeta Ssp Bid Adapter', function () { expect(payload.user.data[0].segment[1].id).to.eql('44'); expect(payload.user.data[0].segment[2].id).to.eql('59'); }); + + it('Test provide device params', function () { + const request = spec.buildRequests(bannerRequest, bannerRequest[0]); + const payload = JSON.parse(request.data); + + expect(payload.device.sua.mobile).to.eql(1); + expect(payload.device.sua.architecture).to.eql('arm'); + expect(payload.device.sua.platform.brand).to.eql('Chrome'); + expect(payload.device.sua.platform.version[0]).to.eql('102'); + + expect(payload.device.ua).to.not.be.undefined; + expect(payload.device.language).to.not.be.undefined; + expect(payload.device.w).to.not.be.undefined; + expect(payload.device.h).to.not.be.undefined; + }); }); From 16ee425a594e005325287fe6a08fd5338651b820 Mon Sep 17 00:00:00 2001 From: inna Date: Mon, 5 Feb 2024 16:10:48 +0200 Subject: [PATCH 207/246] Rise BidAdapter : add support for gpp (#11051) * support gpp in rise and minutemedia * modified tests * removed mm gpp support * updated rise test * fix for gpp section --------- Co-authored-by: Inna Yaretsky <> --- modules/riseBidAdapter.js | 8 ++++++++ test/spec/modules/riseBidAdapter_spec.js | 18 ++++++++++++++++++ 2 files changed, 26 insertions(+) diff --git a/modules/riseBidAdapter.js b/modules/riseBidAdapter.js index 78854129f8a..82790805303 100644 --- a/modules/riseBidAdapter.js +++ b/modules/riseBidAdapter.js @@ -470,6 +470,14 @@ function generateGeneralParams(generalObject, bidderRequest) { generalParams.gdpr_consent = bidderRequest.gdprConsent.consentString; } + if (bidderRequest && bidderRequest.gppConsent) { + generalParams.gpp = bidderRequest.gppConsent.gppString; + generalParams.gpp_sid = bidderRequest.gppConsent.applicableSections; + } else if (bidderRequest.ortb2?.regs?.gpp) { + generalParams.gpp = bidderRequest.ortb2.regs.gpp; + generalParams.gpp_sid = bidderRequest.ortb2.regs.gpp_sid; + } + if (generalBidParams.ifa) { generalParams.ifa = generalBidParams.ifa; } diff --git a/test/spec/modules/riseBidAdapter_spec.js b/test/spec/modules/riseBidAdapter_spec.js index 28bd123cb5d..ec9309fd4ae 100644 --- a/test/spec/modules/riseBidAdapter_spec.js +++ b/test/spec/modules/riseBidAdapter_spec.js @@ -324,6 +324,24 @@ describe('riseAdapter', function () { expect(request.data.params).to.have.property('gdpr_consent', 'test-consent-string'); }); + it('should not send the gpp param if gppConsent is false in the bidRequest', function () { + const bidderRequestWithoutGPP = Object.assign({gppConsent: false}, bidderRequest); + const request = spec.buildRequests(bidRequests, bidderRequestWithoutGPP); + expect(request.data.params).to.be.an('object'); + expect(request.data.params).to.not.have.property('gpp'); + expect(request.data.params).to.not.have.property('gpp_sid'); + }); + + it('should send the gpp param if gppConsent is true in the bidRequest', function () { + const bidderRequestWithGPP = Object.assign({gppConsent: {gppString: 'gpp-consent', applicableSections: [7]}}, bidderRequest); + const request = spec.buildRequests(bidRequests, bidderRequestWithGPP); + console.log('request.data.params'); + console.log(request.data.params); + expect(request.data.params).to.be.an('object'); + expect(request.data.params).to.have.property('gpp', 'gpp-consent'); + expect(request.data.params.gpp_sid[0]).to.be.equal(7); + }); + it('should have schain param if it is available in the bidRequest', () => { const schain = { ver: '1.0', From 6e13ea008558dc780a56dbcac27229f97b076e0a Mon Sep 17 00:00:00 2001 From: inna Date: Mon, 5 Feb 2024 16:36:04 +0200 Subject: [PATCH 208/246] MinuteMedia BidAdapter : add support for gpp (#11052) * add Rise adapter * fixes * change param isOrg to org * Rise adapter * change email for rise * fix circle failed * bump * bump * bump * remove space * Upgrade Rise adapter to 5.0 * added isWrapper param * addes is_wrapper parameter to documentation * added is_wrapper to test * removed isWrapper * Rise Bid Adapter: support Coppa param (#24) * MinuteMedia Bid Adapter: support Coppa param (#25) * Revert "MinuteMedia Bid Adapter: support Coppa param (#25)" (#26) This reverts commit 66c4e7b46121afc5331c8bca6e2fc972fc55f090. * bump * update coppa fetch * setting coppa param update * update Coppa tests * update test naming * Rise Bid Adapter: support plcmt and sua (#27) * update minuteMediaBidAdapter - support missing params (#29) * support gpp for minutemedia adapter * removed spaces * removed extra character --------- Co-authored-by: Noam Tzuberi Co-authored-by: noamtzu Co-authored-by: Noam Tzuberi Co-authored-by: Laslo Chechur Co-authored-by: OronW <41260031+OronW@users.noreply.github.com> Co-authored-by: lasloche <62240785+lasloche@users.noreply.github.com> Co-authored-by: YakirLavi <73641910+YakirLavi@users.noreply.github.com> Co-authored-by: YakirLavi Co-authored-by: Inna Yaretsky <> --- modules/minutemediaBidAdapter.js | 8 ++++++++ test/spec/modules/minutemediaBidAdapter_spec.js | 16 ++++++++++++++++ 2 files changed, 24 insertions(+) diff --git a/modules/minutemediaBidAdapter.js b/modules/minutemediaBidAdapter.js index a4158e2abfa..81200f28a6f 100644 --- a/modules/minutemediaBidAdapter.js +++ b/modules/minutemediaBidAdapter.js @@ -460,6 +460,14 @@ function generateGeneralParams(generalObject, bidderRequest) { generalParams.gdpr_consent = bidderRequest.gdprConsent.consentString; } + if (bidderRequest.gppConsent) { + generalParams.gpp = bidderRequest.gppConsent.gppString; + generalParams.gpp_sid = bidderRequest.gppConsent.applicableSections; + } else if (bidderRequest.ortb2?.regs?.gpp) { + generalParams.gpp = bidderRequest.ortb2.regs.gpp; + generalParams.gpp_sid = bidderRequest.ortb2.regs.gpp_sid; + } + if (generalBidParams.ifa) { generalParams.ifa = generalBidParams.ifa; } diff --git a/test/spec/modules/minutemediaBidAdapter_spec.js b/test/spec/modules/minutemediaBidAdapter_spec.js index 7e22114aeed..d5d6cdc5449 100644 --- a/test/spec/modules/minutemediaBidAdapter_spec.js +++ b/test/spec/modules/minutemediaBidAdapter_spec.js @@ -301,6 +301,22 @@ describe('minutemediaAdapter', function () { expect(request.data.params).to.have.property('gdpr_consent', 'test-consent-string'); }); + it('should not send the gpp param if gppConsent is false in the bidRequest', function () { + const bidderRequestWithGPP = Object.assign({gppConsent: false}, bidderRequest); + const request = spec.buildRequests(bidRequests, bidderRequestWithGPP); + expect(request.data.params).to.be.an('object'); + expect(request.data.params).to.not.have.property('gpp'); + expect(request.data.params).to.not.have.property('gpp_sid'); + }); + + it('should send the gpp param if gppConsent is true in the bidRequest', function () { + const bidderRequestWithGPP = Object.assign({gppConsent: {gppString: 'test-consent-string', applicableSections: [7]}}, bidderRequest); + const request = spec.buildRequests(bidRequests, bidderRequestWithGPP); + expect(request.data.params).to.be.an('object'); + expect(request.data.params).to.have.property('gpp', 'test-consent-string'); + expect(request.data.params.gpp_sid[0]).to.be.equal(7); + }); + it('should have schain param if it is available in the bidRequest', () => { const schain = { ver: '1.0', From 860924f0b31ba62d5b5236d698f52fcd694cb04b Mon Sep 17 00:00:00 2001 From: Sajid Mahmood Date: Mon, 5 Feb 2024 16:00:52 -0500 Subject: [PATCH 209/246] feat: remove dependency on EID Allowlist [ADDR-2801] (#10988) Co-authored-by: Sajid Mahmood --- modules/ixBidAdapter.js | 25 ++++-- test/spec/modules/ixBidAdapter_spec.js | 119 +++++++++++++++++++++++++ 2 files changed, 139 insertions(+), 5 deletions(-) diff --git a/modules/ixBidAdapter.js b/modules/ixBidAdapter.js index 4cc6807b670..54cf3596dbb 100644 --- a/modules/ixBidAdapter.js +++ b/modules/ixBidAdapter.js @@ -38,6 +38,7 @@ const VIDEO_TIME_TO_LIVE = 3600; // 1hr const NATIVE_TIME_TO_LIVE = 3600; // Since native can have video, use ttl same as video const NET_REVENUE = true; const MAX_REQUEST_LIMIT = 4; +const MAX_EID_SOURCES = 50; const OUTSTREAM_MINIMUM_PLAYER_SIZE = [144, 144]; const PRICE_TO_DOLLAR_FACTOR = { JPY: 1 @@ -111,7 +112,8 @@ export const storage = getStorageManager({ bidderCode: BIDDER_CODE }); export const FEATURE_TOGGLES = { // Update with list of CFTs to be requested from Exchange REQUESTED_FEATURE_TOGGLES: [ - 'pbjs_enable_multiformat' + 'pbjs_enable_multiformat', + 'pbjs_allow_all_eids' ], featureToggles: {}, @@ -657,15 +659,23 @@ function getEidInfo(allEids) { let seenSources = {}; if (isArray(allEids)) { for (const eid of allEids) { - if (SOURCE_RTI_MAPPING.hasOwnProperty(eid.source) && deepAccess(eid, 'uids.0')) { + const isSourceMapped = SOURCE_RTI_MAPPING.hasOwnProperty(eid.source); + const allowAllEidsFeatureEnabled = FEATURE_TOGGLES.isFeatureEnabled('pbjs_allow_all_eids'); + const hasUids = deepAccess(eid, 'uids.0'); + + if ((isSourceMapped || allowAllEidsFeatureEnabled) && hasUids) { seenSources[eid.source] = true; - if (SOURCE_RTI_MAPPING[eid.source] != '') { + + if (isSourceMapped && SOURCE_RTI_MAPPING[eid.source] !== '') { eid.uids[0].ext = { rtiPartner: SOURCE_RTI_MAPPING[eid.source] }; } delete eid.uids[0].atype; toSend.push(eid); + if (toSend.length >= MAX_EID_SOURCES) { + break; + } } } } @@ -710,7 +720,7 @@ function buildRequest(validBidRequests, bidderRequest, impressions, version) { // getting ixdiags for adunits of the video, outstream & multi format (MF) style const fledgeEnabled = deepAccess(bidderRequest, 'fledgeEnabled') let ixdiag = buildIXDiag(validBidRequests, fledgeEnabled); - for (var key in ixdiag) { + for (let key in ixdiag) { r.ext.ixdiag[key] = ixdiag[key]; } @@ -796,6 +806,9 @@ function addRTI(userEids, eidInfo) { let identityInfo = window.headertag.getIdentityInfo(); if (identityInfo && typeof identityInfo === 'object') { for (const partnerName in identityInfo) { + if (userEids.length >= MAX_EID_SOURCES) { + return + } if (identityInfo.hasOwnProperty(partnerName)) { let response = identityInfo[partnerName]; if (!response.responsePending && response.data && typeof response.data === 'object' && @@ -1274,6 +1287,7 @@ function buildIXDiag(validBidRequests, fledgeEnabled) { .map(bidRequest => bidRequest.adUnitCode) .filter((value, index, arr) => arr.indexOf(value) === index); + let allEids = deepAccess(validBidRequests, '0.userIdAsEids', []) let ixdiag = { mfu: 0, bu: 0, @@ -1286,7 +1300,8 @@ function buildIXDiag(validBidRequests, fledgeEnabled) { userIds: _getUserIds(validBidRequests[0]), url: window.location.href.split('?')[0], vpd: defaultVideoPlacement, - ae: fledgeEnabled + ae: fledgeEnabled, + eidLength: allEids.length }; // create ad unit map and collect the required diag properties diff --git a/test/spec/modules/ixBidAdapter_spec.js b/test/spec/modules/ixBidAdapter_spec.js index 500f2239e55..05b401fe4db 100644 --- a/test/spec/modules/ixBidAdapter_spec.js +++ b/test/spec/modules/ixBidAdapter_spec.js @@ -919,6 +919,23 @@ describe('IndexexchangeAdapter', function () { const extractPayload = function (bidRequest) { return bidRequest.data } + const generateEid = function (numEid) { + const eids = []; + + for (let i = 1; i <= numEid; i++) { + const newEid = { + source: `eid_source_${i}.com`, + uids: [{ + id: `uid_id_${i}`, + }] + }; + + eids.push(newEid); + } + + return eids; + } + describe('inherited functions', function () { it('should exists and is a function', function () { const adapter = newBidder(spec); @@ -1485,6 +1502,17 @@ describe('IndexexchangeAdapter', function () { describe('buildRequestsUserId', function () { let validIdentityResponse; let validUserIdPayload; + const serverResponse = { + body: { + ext: { + pbjs_allow_all_eids: { + test: { + activated: false + } + } + } + } + }; beforeEach(function () { window.headertag = {}; @@ -1495,6 +1523,12 @@ describe('IndexexchangeAdapter', function () { afterEach(function () { delete window.headertag; + serverResponse.body.ext.features = { + pbjs_allow_all_eids: { + activated: false + } + }; + validIdentityResponse = {} }); it('IX adapter reads supported user modules from Prebid and adds it to Video', function () { @@ -1506,6 +1540,91 @@ describe('IndexexchangeAdapter', function () { expect(payload.user.eids).to.have.deep.members(DEFAULT_USERID_PAYLOAD); }); + it('IX adapter filters eids from prebid past the maximum eid limit', function () { + serverResponse.body.ext.features = { + pbjs_allow_all_eids: { + activated: true + } + }; + FEATURE_TOGGLES.setFeatureToggles(serverResponse); + const cloneValidBid = utils.deepClone(DEFAULT_VIDEO_VALID_BID); + let eid_sent_from_prebid = generateEid(55); + cloneValidBid[0].userIdAsEids = utils.deepClone(eid_sent_from_prebid); + const request = spec.buildRequests(cloneValidBid, DEFAULT_OPTION)[0]; + const payload = extractPayload(request); + expect(payload.user.eids).to.have.lengthOf(50); + let eid_accepted = eid_sent_from_prebid.slice(0, 50); + expect(payload.user.eids).to.have.deep.members(eid_accepted); + expect(payload.ext.ixdiag.eidLength).to.equal(55); + }); + + it('IX adapter filters eids from IXL past the maximum eid limit', function () { + validIdentityResponse = { + MerkleIp: { + responsePending: false, + data: { + source: 'merkle.com', + uids: [{ + id: '1234-5678-9012-3456', + ext: { + keyID: '1234-5678', + enc: 1 + } + }] + } + }, + LiveIntentIp: { + responsePending: false, + data: { + source: 'liveintent.com', + uids: [{ + id: '1234-5678-9012-3456', + ext: { + keyID: '1234-5678', + rtiPartner: 'LDID', + enc: 1 + } + }] + } + } + }; + serverResponse.body.ext.features = { + pbjs_allow_all_eids: { + activated: true + } + }; + FEATURE_TOGGLES.setFeatureToggles(serverResponse); + const cloneValidBid = utils.deepClone(DEFAULT_VIDEO_VALID_BID); + let eid_sent_from_prebid = generateEid(49); + cloneValidBid[0].userIdAsEids = utils.deepClone(eid_sent_from_prebid); + const request = spec.buildRequests(cloneValidBid, DEFAULT_OPTION)[0]; + const payload = extractPayload(request); + expect(payload.user.eids).to.have.lengthOf(50); + eid_sent_from_prebid.push({ + source: 'merkle.com', + uids: [{ + id: '1234-5678-9012-3456', + ext: { + keyID: '1234-5678', + enc: 1 + } + }] + }) + expect(payload.user.eids).to.have.deep.members(eid_sent_from_prebid); + expect(payload.ext.ixdiag.eidLength).to.equal(49); + }); + + it('All incoming eids are from unsupported source with feature toggle off', function () { + FEATURE_TOGGLES.setFeatureToggles(serverResponse); + const cloneValidBid = utils.deepClone(DEFAULT_VIDEO_VALID_BID); + let eid_sent_from_prebid = generateEid(20); + cloneValidBid[0].userIdAsEids = utils.deepClone(eid_sent_from_prebid); + const request = spec.buildRequests(cloneValidBid, DEFAULT_OPTION)[0]; + const payload = extractPayload(request); + expect(payload.user.eids).to.be.undefined + expect(payload.ext.ixdiag.eidLength).to.equal(20); + }); + it('We continue to send in IXL identity info and Prebid takes precedence over IXL', function () { validIdentityResponse = { AdserverOrgIp: { From 1dfd7eb53e0fbd5f8e8ea70c5170b9a71c1c991d Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 6 Feb 2024 04:47:21 -0700 Subject: [PATCH 210/246] Bump release-drafter/release-drafter from 5 to 6 (#11053) Bumps [release-drafter/release-drafter](https://github.com/release-drafter/release-drafter) from 5 to 6. - [Release notes](https://github.com/release-drafter/release-drafter/releases) - [Commits](https://github.com/release-drafter/release-drafter/compare/v5...v6) --- updated-dependencies: - dependency-name: release-drafter/release-drafter dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/workflows/release-drafter.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/release-drafter.yml b/.github/workflows/release-drafter.yml index a13237f1290..a14e12664b6 100644 --- a/.github/workflows/release-drafter.yml +++ b/.github/workflows/release-drafter.yml @@ -17,7 +17,7 @@ jobs: runs-on: ubuntu-latest steps: # Drafts your next Release notes as Pull Requests are merged into "master" - - uses: release-drafter/release-drafter@v5 + - uses: release-drafter/release-drafter@v6 with: config-name: release-drafter.yml env: From 643930f7d2943689da7df5985b2a2c98fc3734f4 Mon Sep 17 00:00:00 2001 From: Vincent Date: Tue, 6 Feb 2024 13:50:50 +0100 Subject: [PATCH 211/246] Criteo bid adatper: add logic to handle dsa in bid request/response (#11043) Co-authored-by: v.raybaud --- modules/criteoBidAdapter.js | 12 +-- test/spec/modules/criteoBidAdapter_spec.js | 86 +++++++++++++++++++++- 2 files changed, 92 insertions(+), 6 deletions(-) diff --git a/modules/criteoBidAdapter.js b/modules/criteoBidAdapter.js index a8a1416e1f3..fcf8d2ad953 100644 --- a/modules/criteoBidAdapter.js +++ b/modules/criteoBidAdapter.js @@ -258,6 +258,9 @@ export const spec = { if (slot.ext?.meta?.networkName) { bid.meta = Object.assign({}, bid.meta, { networkName: slot.ext.meta.networkName }) } + if (slot.ext?.dsa?.adrender) { + bid.meta = Object.assign({}, bid.meta, { adrender: slot.ext.dsa.adrender }) + } if (slot.native) { if (bidRequest.params.nativeCallback) { bid.ad = createNativeAd(bidId, slot.native, bidRequest.params.nativeCallback); @@ -503,17 +506,16 @@ function buildCdbRequest(context, bidRequests, bidderRequest) { let networkId; let schain; let userIdAsEids; + let regs = Object.assign({}, { + coppa: bidderRequest.coppa === true ? 1 : (bidderRequest.coppa === false ? 0 : undefined) + }, bidderRequest.ortb2?.regs); const request = { id: generateUUID(), publisher: { url: context.url, ext: bidderRequest.publisherExt, }, - regs: { - coppa: bidderRequest.coppa === true ? 1 : (bidderRequest.coppa === false ? 0 : undefined), - gpp: bidderRequest.ortb2?.regs?.gpp, - gpp_sid: bidderRequest.ortb2?.regs?.gpp_sid - }, + regs: regs, slots: bidRequests.map(bidRequest => { if (!userIdAsEids) { userIdAsEids = bidRequest.userIdAsEids; diff --git a/test/spec/modules/criteoBidAdapter_spec.js b/test/spec/modules/criteoBidAdapter_spec.js index 5dc6d74d90e..ce07c6e49bc 100755 --- a/test/spec/modules/criteoBidAdapter_spec.js +++ b/test/spec/modules/criteoBidAdapter_spec.js @@ -1110,7 +1110,7 @@ describe('The Criteo bidding adapter', function () { const ortb2 = { regs: { gpp: 'gpp_consent_string', - gpp_sid: [0, 1, 2] + gpp_sid: [0, 1, 2], } }; @@ -1120,6 +1120,48 @@ describe('The Criteo bidding adapter', function () { expect(request.data.regs.gpp_sid).to.deep.equal([0, 1, 2]); }); + it('should properly build a request with dsa object', function () { + const bidRequests = [ + { + bidder: 'criteo', + adUnitCode: 'bid-123', + transactionId: 'transaction-123', + mediaTypes: { + banner: { + sizes: [[728, 90]] + } + }, + params: { + zoneId: 123, + }, + }, + ]; + let dsa = { + required: 3, + pubrender: 0, + datatopub: 2, + transparency: [{ + domain: 'platform1domain.com', + params: [1] + }, { + domain: 'SSP2domain.com', + params: [1, 2] + }] + }; + const ortb2 = { + regs: { + ext: { + dsa: dsa + } + } + }; + + const request = spec.buildRequests(bidRequests, { ...bidderRequest, ortb2 }); + expect(request.data.regs).to.not.be.null; + expect(request.data.regs.ext).to.not.be.null; + expect(request.data.regs.ext.dsa).to.deep.equal(dsa); + }); + it('should properly build a request with schain object', function () { const expectedSchain = { someProperty: 'someValue' @@ -1979,6 +2021,48 @@ describe('The Criteo bidding adapter', function () { expect(bids[0].meta.networkName).to.equal('Criteo'); }); + it('should properly parse a bid response with dsa', function () { + const response = { + body: { + slots: [{ + impid: 'test-requestId', + cpm: 1.23, + creative: 'test-ad', + creativecode: 'test-crId', + width: 728, + height: 90, + deal: 'myDealCode', + adomain: ['criteo.com'], + ext: { + dsa: { + adrender: 1 + }, + meta: { + networkName: 'Criteo' + } + } + }], + }, + }; + const request = { + bidRequests: [{ + adUnitCode: 'test-requestId', + bidId: 'test-bidId', + mediaTypes: { + banner: { + sizes: [[728, 90]] + } + }, + params: { + networkId: 456, + } + }] + }; + const bids = spec.interpretResponse(response, request); + expect(bids).to.have.lengthOf(1); + expect(bids[0].meta.adrender).to.equal(1); + }); + it('should properly parse a bid response with a networkId with twin ad unit banner win', function () { const response = { body: { From 6733fe8bf0640fc9237a1051ae9729840cb95594 Mon Sep 17 00:00:00 2001 From: onetag-dev <38786435+onetag-dev@users.noreply.github.com> Date: Tue, 6 Feb 2024 13:57:15 +0100 Subject: [PATCH 212/246] Onetag Bid Adapter: add DSA support (#11036) Co-authored-by: federico --- modules/onetagBidAdapter.js | 3 + test/spec/modules/onetagBidAdapter_spec.js | 119 ++++++++++++++------- 2 files changed, 84 insertions(+), 38 deletions(-) diff --git a/modules/onetagBidAdapter.js b/modules/onetagBidAdapter.js index eb9fa2eb536..7a7cbbadd82 100644 --- a/modules/onetagBidAdapter.js +++ b/modules/onetagBidAdapter.js @@ -127,6 +127,9 @@ function interpretResponse(serverResponse, bidderRequest) { }, ttl: bid.ttl || 300 }; + if (bid.dsa) { + responseBid.meta.dsa = bid.dsa; + } if (bid.mediaType === BANNER) { responseBid.ad = bid.ad; } else if (bid.mediaType === VIDEO) { diff --git a/test/spec/modules/onetagBidAdapter_spec.js b/test/spec/modules/onetagBidAdapter_spec.js index 7960574c1a4..c3d8a4ee0e1 100644 --- a/test/spec/modules/onetagBidAdapter_spec.js +++ b/test/spec/modules/onetagBidAdapter_spec.js @@ -396,47 +396,75 @@ describe('onetag', function () { expect(payload.ortb2).to.exist; expect(payload.ortb2).to.exist.and.to.deep.equal(firtPartyData); }); - }); - it('Should send FLEDGE eligibility flag when FLEDGE is enabled', function () { - let bidderRequest = { - 'bidderCode': 'onetag', - 'auctionId': '1d1a030790a475', - 'bidderRequestId': '22edbae2733bf6', - 'timeout': 3000, - 'fledgeEnabled': true - }; - let serverRequest = spec.buildRequests([bannerBid], bidderRequest); - const payload = JSON.parse(serverRequest.data); + it('Should send DSA (ortb2 field)', function () { + const dsa = { + 'regs': { + 'ext': { + 'dsa': { + 'required': 1, + 'pubrender': 0, + 'datatopub': 1, + 'transparency': [{ + 'domain': 'dsa-domain', + 'params': [1, 2] + }] + } + } + } + }; + let bidderRequest = { + 'bidderCode': 'onetag', + 'auctionId': '1d1a030790a475', + 'bidderRequestId': '22edbae2733bf6', + 'timeout': 3000, + 'ortb2': dsa + } + let serverRequest = spec.buildRequests([bannerBid], bidderRequest); + const payload = JSON.parse(serverRequest.data); + expect(payload.ortb2).to.exist; + expect(payload.ortb2).to.exist.and.to.deep.equal(dsa); + }); + it('Should send FLEDGE eligibility flag when FLEDGE is enabled', function () { + let bidderRequest = { + 'bidderCode': 'onetag', + 'auctionId': '1d1a030790a475', + 'bidderRequestId': '22edbae2733bf6', + 'timeout': 3000, + 'fledgeEnabled': true + }; + let serverRequest = spec.buildRequests([bannerBid], bidderRequest); + const payload = JSON.parse(serverRequest.data); - expect(payload.fledgeEnabled).to.exist; - expect(payload.fledgeEnabled).to.exist.and.to.equal(bidderRequest.fledgeEnabled); - }); - it('Should send FLEDGE eligibility flag when FLEDGE is not enabled', function () { - let bidderRequest = { - 'bidderCode': 'onetag', - 'auctionId': '1d1a030790a475', - 'bidderRequestId': '22edbae2733bf6', - 'timeout': 3000, - 'fledgeEnabled': false - }; - let serverRequest = spec.buildRequests([bannerBid], bidderRequest); - const payload = JSON.parse(serverRequest.data); + expect(payload.fledgeEnabled).to.exist; + expect(payload.fledgeEnabled).to.exist.and.to.equal(bidderRequest.fledgeEnabled); + }); + it('Should send FLEDGE eligibility flag when FLEDGE is not enabled', function () { + let bidderRequest = { + 'bidderCode': 'onetag', + 'auctionId': '1d1a030790a475', + 'bidderRequestId': '22edbae2733bf6', + 'timeout': 3000, + 'fledgeEnabled': false + }; + let serverRequest = spec.buildRequests([bannerBid], bidderRequest); + const payload = JSON.parse(serverRequest.data); - expect(payload.fledgeEnabled).to.exist; - expect(payload.fledgeEnabled).to.exist.and.to.equal(bidderRequest.fledgeEnabled); - }); - it('Should send FLEDGE eligibility flag set to false when fledgeEnabled is not defined', function () { - let bidderRequest = { - 'bidderCode': 'onetag', - 'auctionId': '1d1a030790a475', - 'bidderRequestId': '22edbae2733bf6', - 'timeout': 3000, - }; - let serverRequest = spec.buildRequests([bannerBid], bidderRequest); - const payload = JSON.parse(serverRequest.data); + expect(payload.fledgeEnabled).to.exist; + expect(payload.fledgeEnabled).to.exist.and.to.equal(bidderRequest.fledgeEnabled); + }); + it('Should send FLEDGE eligibility flag set to false when fledgeEnabled is not defined', function () { + let bidderRequest = { + 'bidderCode': 'onetag', + 'auctionId': '1d1a030790a475', + 'bidderRequestId': '22edbae2733bf6', + 'timeout': 3000, + }; + let serverRequest = spec.buildRequests([bannerBid], bidderRequest); + const payload = JSON.parse(serverRequest.data); - expect(payload.fledgeEnabled).to.exist; - expect(payload.fledgeEnabled).to.exist.and.to.equal(false); + expect(payload.fledgeEnabled).to.exist; + expect(payload.fledgeEnabled).to.exist.and.to.equal(false); + }); }); describe('interpretResponse', function () { const request = getBannerVideoRequest(); @@ -486,6 +514,21 @@ describe('onetag', function () { const serverResponses = spec.interpretResponse('invalid_response', { data: '{}' }); expect(serverResponses).to.be.an('array').that.is.empty; }); + it('Returns meta dsa field if dsa field is present in response', function () { + const dsaResponseObj = { + 'behalf': 'Advertiser', + 'paid': 'Advertiser', + 'transparency': { + 'domain': 'dsp1domain.com', + 'params': [1, 2] + }, + 'adrender': 1 + }; + const responseWithDsa = {...response}; + responseWithDsa.body.bids.forEach(bid => bid.dsa = {...dsaResponseObj}); + const serverResponse = spec.interpretResponse(responseWithDsa, request); + serverResponse.forEach(bid => expect(bid.meta.dsa).to.deep.equals(dsaResponseObj)); + }); }); describe('getUserSyncs', function () { const sync_endpoint = 'https://onetag-sys.com/usync/'; From 00fd7c18bff32e42312a1bf883f2de2dc708bdf7 Mon Sep 17 00:00:00 2001 From: Saar Amrani Date: Tue, 6 Feb 2024 17:29:40 +0200 Subject: [PATCH 213/246] Add GVLID to illuminBidAdapter module. (#11060) --- modules/illuminBidAdapter.js | 2 ++ 1 file changed, 2 insertions(+) diff --git a/modules/illuminBidAdapter.js b/modules/illuminBidAdapter.js index 36ff7bea0a3..45b74bec5c9 100644 --- a/modules/illuminBidAdapter.js +++ b/modules/illuminBidAdapter.js @@ -7,6 +7,7 @@ import {config} from '../src/config.js'; const DEFAULT_SUB_DOMAIN = 'exchange'; const BIDDER_CODE = 'illumin'; const BIDDER_VERSION = '1.0.0'; +const GVLID = 149; const CURRENCY = 'USD'; const TTL_SECONDS = 60 * 5; const UNIQUE_DEAL_ID_EXPIRY = 1000 * 60 * 15; @@ -327,6 +328,7 @@ export const spec = { code: BIDDER_CODE, version: BIDDER_VERSION, supportedMediaTypes: [BANNER, VIDEO], + gvlid: GVLID, isBidRequestValid, buildRequests, interpretResponse, From 7260a2d9ccd63dab347e679bbbdb286ba0540991 Mon Sep 17 00:00:00 2001 From: PGAMSSP <142323401+PGAMSSP@users.noreply.github.com> Date: Tue, 6 Feb 2024 17:52:21 +0200 Subject: [PATCH 214/246] PGAMSSP Bidder Adapter: add id5id (#11024) * new adapter PGAMSSP * upd * support UID 2.0 * del obj * Add id5id --- modules/pgamsspBidAdapter.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/modules/pgamsspBidAdapter.js b/modules/pgamsspBidAdapter.js index b38131c1c18..f3062fa4ff0 100644 --- a/modules/pgamsspBidAdapter.js +++ b/modules/pgamsspBidAdapter.js @@ -40,7 +40,8 @@ function getPlacementReqData(bid) { }; if (bid.userId) { - getUserId(placement.eids, bid.userId.uid2 && bid.userId.uid2.id, 'uidapi.com'); + getUserId(placement.eids, bid.userId.uid2?.id, 'uidapi.com'); + getUserId(placement.eids, bid.userId.id5id?.uid, 'id5-sync.com'); } if (placementId) { From f8858ae9f4553928fbece2239e299c58be1d28a8 Mon Sep 17 00:00:00 2001 From: Petre Damoc Date: Tue, 6 Feb 2024 21:12:15 +0200 Subject: [PATCH 215/246] Missena Bid Adapter: add session identifier (#11058) --- modules/missenaBidAdapter.js | 3 +++ test/spec/modules/missenaBidAdapter_spec.js | 4 ++++ 2 files changed, 7 insertions(+) diff --git a/modules/missenaBidAdapter.js b/modules/missenaBidAdapter.js index 527b6704146..99cad1c7bc6 100644 --- a/modules/missenaBidAdapter.js +++ b/modules/missenaBidAdapter.js @@ -1,6 +1,7 @@ import { buildUrl, formatQS, + generateUUID, isFn, logInfo, safeJSONParse, @@ -24,6 +25,7 @@ const EVENTS_DOMAIN = 'events.missena.io'; const EVENTS_DOMAIN_DEV = 'events.staging.missena.xyz'; export const storage = getStorageManager({ bidderCode: BIDDER_CODE }); +window.msna_ik = window.msna_ik || generateUUID(); /* Get Floor price information */ function getFloor(bidRequest) { @@ -79,6 +81,7 @@ export const spec = { return validBidRequests.map((bidRequest) => { const payload = { adunit: bidRequest.adUnitCode, + ik: window.msna_ik, request_id: bidRequest.bidId, timeout: bidderRequest.timeout, }; diff --git a/test/spec/modules/missenaBidAdapter_spec.js b/test/spec/modules/missenaBidAdapter_spec.js index f84981352ab..ab1fbdcc074 100644 --- a/test/spec/modules/missenaBidAdapter_spec.js +++ b/test/spec/modules/missenaBidAdapter_spec.js @@ -137,6 +137,10 @@ describe('Missena Adapter', function () { expect(payloadNoFloor.floor).to.equal(undefined); expect(payloadNoFloor.floor_currency).to.equal(undefined); }); + it('should send the idempotency key', function () { + expect(window.msna_ik).to.not.equal(undefined); + expect(payload.ik).to.equal(window.msna_ik); + }); getDataFromLocalStorageStub.restore(); getDataFromLocalStorageStub = sinon.stub( From 88b7477f4c7359522c86c8fd690189b7797d1c61 Mon Sep 17 00:00:00 2001 From: Vincent Date: Tue, 6 Feb 2024 20:45:07 +0100 Subject: [PATCH 216/246] Grid bid adapter: add logic to handle dsa in bid request/response (#11042) Co-authored-by: v.raybaud --- modules/gridBidAdapter.js | 16 +++++++++++++++- test/spec/modules/gridBidAdapter_spec.js | 3 ++- 2 files changed, 17 insertions(+), 2 deletions(-) diff --git a/modules/gridBidAdapter.js b/modules/gridBidAdapter.js index 7147864a2e6..d56639ed714 100644 --- a/modules/gridBidAdapter.js +++ b/modules/gridBidAdapter.js @@ -361,6 +361,16 @@ export const spec = { request.regs.coppa = 1; } + if (ortb2Regs?.ext?.dsa) { + if (!request.regs) { + request.regs = {ext: {}}; + } + if (!request.regs.ext) { + request.regs.ext = {}; + } + request.regs.ext.dsa = ortb2Regs.ext.dsa; + } + const site = deepAccess(bidderRequest, 'ortb2.site'); if (site) { const pageCategory = [...(site.cat || []), ...(site.pagecat || [])].filter((category) => { @@ -532,7 +542,7 @@ function _addBidResponse(serverBid, bidRequest, bidResponses, RendererConst, bid netRevenue: true, ttl: TIME_TO_LIVE, meta: { - advertiserDomains: serverBid.adomain ? serverBid.adomain : [] + advertiserDomains: serverBid.adomain ? serverBid.adomain : [], }, dealId: serverBid.dealid }; @@ -544,6 +554,10 @@ function _addBidResponse(serverBid, bidRequest, bidResponses, RendererConst, bid bidResponse.meta.demandSource = serverBid.ext.bidder.grid.demandSource; } + if (serverBid.ext && serverBid.ext.dsa && serverBid.ext.dsa.adrender) { + bidResponse.meta.adrender = serverBid.ext.dsa.adrender; + } + if (serverBid.content_type === 'video') { if (serverBid.adm) { bidResponse.vastXml = serverBid.adm; diff --git a/test/spec/modules/gridBidAdapter_spec.js b/test/spec/modules/gridBidAdapter_spec.js index 0611fa68bf8..abaa4b37fcd 100644 --- a/test/spec/modules/gridBidAdapter_spec.js +++ b/test/spec/modules/gridBidAdapter_spec.js @@ -1352,7 +1352,7 @@ describe('TheMediaGrid Adapter', function () { it('complicated case', function () { const fullResponse = [ - {'bid': [{'impid': '2164be6358b9', 'adid': '32_52_7543', 'price': 1.15, 'adm': '
test content 1
', 'auid': 1, 'h': 250, 'w': 300, dealid: 11}], 'seat': '1'}, + {'bid': [{'impid': '2164be6358b9', 'adid': '32_52_7543', 'price': 1.15, 'adm': '
test content 1
', 'auid': 1, 'h': 250, 'w': 300, dealid: 11, 'ext': {'dsa': {'adrender': 1}}}], 'seat': '1'}, {'bid': [{'impid': '4e111f1b66e4', 'adid': '32_54_4535', 'price': 0.5, 'adm': '
test content 2
', 'auid': 2, 'h': 600, 'w': 300, dealid: 12}], 'seat': '1'}, {'bid': [{'impid': '26d6f897b516', 'adid': '32_53_75467', 'price': 0.15, 'adm': '
test content 3
', 'auid': 1, 'h': 90, 'w': 728}], 'seat': '1'}, {'bid': [{'impid': '326bde7fbf69', 'adid': '32_54_12342', 'price': 0.15, 'adm': '
test content 4
', 'auid': 1, 'h': 600, 'w': 300}], 'seat': '1'}, @@ -1430,6 +1430,7 @@ describe('TheMediaGrid Adapter', function () { 'netRevenue': true, 'ttl': 360, 'meta': { + adrender: 1, advertiserDomains: [] }, }, From 36906f77daa6a723e95a3897224e045ca11c503e Mon Sep 17 00:00:00 2001 From: Jeff Mahoney Date: Tue, 6 Feb 2024 21:03:24 -0500 Subject: [PATCH 217/246] Sharethrough Bid Adapter: support Fledge + refine video-placement req logic (#11048) * Modify value-setting logic for video-placement reqs * Updating value-setting logic in `buildRequests()` method so that `placement` property in video requests does not possibly get overridden to 1 as a value if `plcmt` is also present as a property. (Current logic sets `placement` at 1 if `context` is "instream".) * Support for Fledge * Adding updates to our unit tests and the relevant methods in our bid adapter to make it ready, eventually, for Fledge auctions. --- modules/sharethroughBidAdapter.js | 30 +++++++++++++-- .../modules/sharethroughBidAdapter_spec.js | 37 +++++++++++++++++++ 2 files changed, 64 insertions(+), 3 deletions(-) diff --git a/modules/sharethroughBidAdapter.js b/modules/sharethroughBidAdapter.js index 53cb67c4e6d..ae1fb131966 100644 --- a/modules/sharethroughBidAdapter.js +++ b/modules/sharethroughBidAdapter.js @@ -1,7 +1,7 @@ import { registerBidder } from '../src/adapters/bidderFactory.js'; import { config } from '../src/config.js'; import { BANNER, VIDEO } from '../src/mediaTypes.js'; -import { deepAccess, generateUUID, inIframe } from '../src/utils.js'; +import { deepAccess, generateUUID, inIframe, mergeDeep } from '../src/utils.js'; const VERSION = '4.3.0'; const BIDDER_CODE = 'sharethrough'; @@ -104,6 +104,10 @@ export const sharethroughAdapterSpec = { const videoRequest = deepAccess(bidReq, 'mediaTypes.video'); + if (bidderRequest.fledgeEnabled && bidReq.mediaTypes.banner) { + mergeDeep(impression, { ext: { ae: 1 } }); // ae = auction environment; if this is 1, ad server knows we have a fledge auction + } + if (videoRequest) { // default playerSize, only change this if we know width and height are properly defined in the request let [w, h] = [640, 360]; @@ -116,6 +120,14 @@ export const sharethroughAdapterSpec = { [w, h] = videoRequest.playerSize[0]; } + const getVideoPlacementValue = (vidReq) => { + if (vidReq.plcmt) { + return vidReq.placement; + } else { + return vidReq.context === 'instream' ? 1 : +deepAccess(vidReq, 'placement', 4); + } + }; + impression.video = { pos: nullish(videoRequest.pos, 0), topframe: inIframe() ? 0 : 1, @@ -132,7 +144,8 @@ export const sharethroughAdapterSpec = { startdelay: nullish(videoRequest.startdelay, 0), skipmin: nullish(videoRequest.skipmin, 0), skipafter: nullish(videoRequest.skipafter, 0), - placement: videoRequest.context === 'instream' ? 1 : +deepAccess(videoRequest, 'placement', 4), + placement: getVideoPlacementValue(videoRequest), + plcmt: videoRequest.plcmt ? videoRequest.plcmt : null, }; if (videoRequest.delivery) impression.video.delivery = videoRequest.delivery; @@ -179,7 +192,9 @@ export const sharethroughAdapterSpec = { return []; } - return body.seatbid[0].bid.map((bid) => { + const fledgeAuctionEnabled = body.ext?.auctionConfigs; + + const bidsFromExchange = body.seatbid[0].bid.map((bid) => { // Spec: https://docs.prebid.org/dev-docs/bidder-adaptor.html#interpreting-the-response const response = { requestId: bid.impid, @@ -219,6 +234,15 @@ export const sharethroughAdapterSpec = { return response; }); + + if (fledgeAuctionEnabled) { + return { + bids: bidsFromExchange, + fledgeAuctionConfigs: body.ext?.auctionConfigs || {}, + }; + } else { + return bidsFromExchange; + } }, getUserSyncs: (syncOptions, serverResponses) => { diff --git a/test/spec/modules/sharethroughBidAdapter_spec.js b/test/spec/modules/sharethroughBidAdapter_spec.js index 6a63ae681e7..1bb6f898b81 100644 --- a/test/spec/modules/sharethroughBidAdapter_spec.js +++ b/test/spec/modules/sharethroughBidAdapter_spec.js @@ -585,6 +585,27 @@ describe('sharethrough adapter spec', function () { expect(videoImp.placement).to.equal(4); }); + + it('should not override "placement" value if "plcmt" prop is present', () => { + // ASSEMBLE + const ARBITRARY_PLACEMENT_VALUE = 99; + const ARBITRARY_PLCMT_VALUE = 100; + + bidRequests[1].mediaTypes.video.context = 'instream'; + bidRequests[1].mediaTypes.video.placement = ARBITRARY_PLACEMENT_VALUE; + + // adding "plcmt" property - this should prevent "placement" prop + // from getting overridden to 1 + bidRequests[1].mediaTypes.video['plcmt'] = ARBITRARY_PLCMT_VALUE; + + // ACT + const builtRequest = spec.buildRequests(bidRequests, bidderRequest)[1]; + const videoImp = builtRequest.data.imp[0].video; + + // ASSERT + expect(videoImp.placement).to.equal(ARBITRARY_PLACEMENT_VALUE); + expect(videoImp.plcmt).to.equal(ARBITRARY_PLCMT_VALUE); + }); }); }); @@ -692,6 +713,22 @@ describe('sharethrough adapter spec', function () { expect(openRtbReq.regs.ext.gpp_sid).to.equal(firstPartyData.regs.gpp_sid); }); }); + + describe('fledge', () => { + it('should attach "ae" as a property to the request if 1) fledge auctions are enabled, and 2) request is display (only supporting display for now)', () => { + // ASSEMBLE + const EXPECTED_AE_VALUE = 1; + + // ACT + bidderRequest['fledgeEnabled'] = true; + const builtRequests = spec.buildRequests(bidRequests, bidderRequest); + const ACTUAL_AE_VALUE = builtRequests[0].data.imp[0].ext.ae; + + // ASSERT + expect(ACTUAL_AE_VALUE).to.equal(EXPECTED_AE_VALUE); + expect(builtRequests[1].data.imp[0].ext.ae).to.be.undefined; + }); + }); }); describe('interpretResponse', function () { From 8fbd6a346737111377b11899aa6ad3eae9a9d281 Mon Sep 17 00:00:00 2001 From: khonghau-cha <68848958+khonghau-cha@users.noreply.github.com> Date: Wed, 7 Feb 2024 20:46:10 +0800 Subject: [PATCH 218/246] Innity Bid Adapter: handle empty response from server (#10960) * Handle empty response from server * Add space before blocks and add unit test for result with no bids --- modules/innityBidAdapter.js | 3 +++ test/spec/modules/innityBidAdapter_spec.js | 6 ++++++ 2 files changed, 9 insertions(+) diff --git a/modules/innityBidAdapter.js b/modules/innityBidAdapter.js index 99eec210193..9bd0538ff0a 100644 --- a/modules/innityBidAdapter.js +++ b/modules/innityBidAdapter.js @@ -38,6 +38,9 @@ export const spec = { }, interpretResponse: function(serverResponse, request) { const res = serverResponse.body; + if (Object.keys(res).length === 0) { + return []; + } const bidResponse = { requestId: res.callback_uid, cpm: parseFloat(res.cpm) / 100, diff --git a/test/spec/modules/innityBidAdapter_spec.js b/test/spec/modules/innityBidAdapter_spec.js index 192ab4911ee..820f535ba72 100644 --- a/test/spec/modules/innityBidAdapter_spec.js +++ b/test/spec/modules/innityBidAdapter_spec.js @@ -120,5 +120,11 @@ describe('innityAdapterTest', () => { expect(result[0].meta.advertiserDomains.length).to.equal(0); expect(result[0].meta.advertiserDomains).to.deep.equal([]); }); + + it('result with no bids', () => { + bidResponse.body = {}; + const result = spec.interpretResponse(bidResponse, bidRequest); + expect(result).to.deep.equal([]); + }); }); }); From 66bab278f45526cb3339afa24234fc428d195022 Mon Sep 17 00:00:00 2001 From: bbaresic <153560835+bbaresic@users.noreply.github.com> Date: Wed, 7 Feb 2024 06:51:22 -0600 Subject: [PATCH 219/246] Add skippedReason property to floorData. (#11040) --- modules/priceFloors.js | 3 +++ src/constants.json | 4 ++++ test/spec/modules/priceFloors_spec.js | 17 +++++++++++++++++ 3 files changed, 24 insertions(+) diff --git a/modules/priceFloors.js b/modules/priceFloors.js index c2d7db97047..277b6839c9b 100644 --- a/modules/priceFloors.js +++ b/modules/priceFloors.js @@ -365,6 +365,7 @@ export function updateAdUnitsForAuction(adUnits, floorData, auctionId) { noFloorSignaled: isNoFloorSignaled, skipped: floorData.skipped, skipRate: deepAccess(floorData, 'data.skipRate') ?? floorData.skipRate, + skippedReason: floorData.skippedReason, floorMin: floorData.floorMin, modelVersion: deepAccess(floorData, 'data.modelVersion'), modelWeight: deepAccess(floorData, 'data.modelWeight'), @@ -411,11 +412,13 @@ export function createFloorsDataForAuction(adUnits, auctionId) { // if we still do not have a valid floor data then floors is not on for this auction, so skip if (Object.keys(deepAccess(resolvedFloorsData, 'data.values') || {}).length === 0) { resolvedFloorsData.skipped = true; + resolvedFloorsData.skippedReason = CONSTANTS.FLOOR_SKIPPED_REASON.NOT_FOUND } else { // determine the skip rate now const auctionSkipRate = getParameterByName('pbjs_skipRate') || (deepAccess(resolvedFloorsData, 'data.skipRate') ?? resolvedFloorsData.skipRate); const isSkipped = Math.random() * 100 < parseFloat(auctionSkipRate); resolvedFloorsData.skipped = isSkipped; + if (isSkipped) resolvedFloorsData.skippedReason = CONSTANTS.FLOOR_SKIPPED_REASON.RANDOM } // copy FloorMin to floorData.data if (resolvedFloorsData.hasOwnProperty('floorMin')) resolvedFloorsData.data.floorMin = resolvedFloorsData.floorMin; diff --git a/src/constants.json b/src/constants.json index 24882c160f8..53ee407b772 100644 --- a/src/constants.json +++ b/src/constants.json @@ -9,6 +9,10 @@ "ADSERVER_TARGETING": "adserverTargeting", "BD_SETTING_STANDARD": "standard" }, + "FLOOR_SKIPPED_REASON": { + "NOT_FOUND": "not_found", + "RANDOM": "random" + }, "DEBUG_MODE": "pbjs_debug", "STATUS": { "GOOD": 1 diff --git a/test/spec/modules/priceFloors_spec.js b/test/spec/modules/priceFloors_spec.js index a31f07fecef..fa31ae0ff81 100644 --- a/test/spec/modules/priceFloors_spec.js +++ b/test/spec/modules/priceFloors_spec.js @@ -730,6 +730,23 @@ describe('the price floors module', function () { expect(floorData.skipRate).to.equal(101); expect(floorData.skipped).to.equal(true); }); + + it('should have skippedReason set to "not_found" if there is no valid floor data', function() { + floorConfig.data = {} + handleSetFloorsConfig(floorConfig); + + const floorData = createFloorsDataForAuction(adUnits, 'id'); + expect(floorData.skippedReason).to.equal(CONSTANTS.FLOOR_SKIPPED_REASON.NOT_FOUND); + }); + + it('should have skippedReason set to "random" if there is floor data and skipped is true', function() { + // this will force skipped to be true + floorConfig.skipRate = 101; + handleSetFloorsConfig(floorConfig); + + const floorData = createFloorsDataForAuction(adUnits, 'id'); + expect(floorData.skippedReason).to.equal(CONSTANTS.FLOOR_SKIPPED_REASON.RANDOM); + }); }); describe('pre-auction tests', function () { From bfbce73f22a17ad5a3732680cb63ce9b83de9594 Mon Sep 17 00:00:00 2001 From: MartinGumGum <109325501+MartinGumGum@users.noreply.github.com> Date: Wed, 7 Feb 2024 05:17:41 -0800 Subject: [PATCH 220/246] GumGum Bid Adapter: fix size in the bid response for multi size slot sizes (#11064) * Fix for the multi-size in- slot * added comment --- modules/gumgumBidAdapter.js | 5 +++-- test/spec/modules/gumgumBidAdapter_spec.js | 13 +++++++++++++ 2 files changed, 16 insertions(+), 2 deletions(-) diff --git a/modules/gumgumBidAdapter.js b/modules/gumgumBidAdapter.js index 15af94494a3..7f8627ec5f7 100644 --- a/modules/gumgumBidAdapter.js +++ b/modules/gumgumBidAdapter.js @@ -553,10 +553,11 @@ function interpretResponse(serverResponse, bidRequest) { sizes = [`${maxw}x${maxh}`]; } else if (product === 5 && includes(sizes, '1x1')) { sizes = ['1x1']; - } else if (product === 2 && includes(sizes, '1x1')) { + // added logic for in-slot multi-szie + } else if ((product === 2 && includes(sizes, '1x1')) || product === 3) { const requestSizesThatMatchResponse = (bidRequest.sizes && bidRequest.sizes.reduce((result, current) => { const [ width, height ] = current; - if (responseWidth === width || responseHeight === height) result.push(current.join('x')); + if (responseWidth === width && responseHeight === height) result.push(current.join('x')); return result }, [])) || []; sizes = requestSizesThatMatchResponse.length ? requestSizesThatMatchResponse : parseSizesInput(bidRequest.sizes) diff --git a/test/spec/modules/gumgumBidAdapter_spec.js b/test/spec/modules/gumgumBidAdapter_spec.js index e0529a895f5..52cfd0294e7 100644 --- a/test/spec/modules/gumgumBidAdapter_spec.js +++ b/test/spec/modules/gumgumBidAdapter_spec.js @@ -880,6 +880,19 @@ describe('gumgumAdapter', function () { expect(result.height = expectedSize[1]); }) + it('request size that matches response size for in-slot', function () { + const request = { ...bidRequest }; + const body = { ...serverResponse }; + const expectedSize = [[ 320, 50 ], [300, 600], [300, 250]]; + let result; + request.pi = 3; + body.ad.width = 300; + body.ad.height = 600; + result = spec.interpretResponse({ body }, request)[0]; + expect(result.width = expectedSize[1][0]); + expect(result.height = expectedSize[1][1]); + }) + it('defaults to use bidRequest sizes', function () { const { ad, jcsi, pag, thms, meta } = serverResponse const noAdSizes = { ...ad } From f3809120c258094c85c486e1c57b2a763e75c8d0 Mon Sep 17 00:00:00 2001 From: Catalin Ciocov Date: Wed, 7 Feb 2024 15:18:15 +0200 Subject: [PATCH 221/246] Addition of ImproveDigital's Topics API iframe (#10986) --- modules/topicsFpdModule.js | 3 +++ 1 file changed, 3 insertions(+) diff --git a/modules/topicsFpdModule.js b/modules/topicsFpdModule.js index a31d72fc179..dd240a929a7 100644 --- a/modules/topicsFpdModule.js +++ b/modules/topicsFpdModule.js @@ -29,6 +29,9 @@ const bidderIframeList = { }, { bidder: 'rtbhouse', iframeURL: 'https://topics.authorizedvault.com/topicsapi.html' + }, { + bidder: 'improvedigital', + iframeURL: 'https://hb.360yield.com/privacy-sandbox/topics.html' }] } From 8931850d3d63700b933fe02aeced0c79d77dda3a Mon Sep 17 00:00:00 2001 From: Demetrio Girardi Date: Wed, 7 Feb 2024 05:27:41 -0800 Subject: [PATCH 222/246] Core: use same transaction ID for twin ad units (#10962) * swith transactionId to adUnitId * use same TID for ad units with the same code * fix appnexus clones --- modules/craftBidAdapter.js | 6 +- modules/goldbachBidAdapter.js | 5 +- modules/hybridBidAdapter.js | 14 +-- modules/mediafuseBidAdapter.js | 5 +- .../prebidServerBidAdapter/ortbConverter.js | 1 + modules/priceFloors.js | 6 +- modules/voxBidAdapter.js | 17 +-- package-lock.json | 2 +- src/adapterManager.js | 1 + src/auction.js | 4 +- src/auctionIndex.js | 18 +-- src/bidfactory.js | 26 ++-- src/prebid.js | 26 ++-- test/spec/auctionmanager_spec.js | 24 ++-- .../modules/prebidServerBidAdapter_spec.js | 1 + test/spec/modules/priceFloors_spec.js | 15 +-- test/spec/native_spec.js | 32 ++--- test/spec/unit/core/auctionIndex_spec.js | 30 ++--- test/spec/unit/core/bidderFactory_spec.js | 4 +- test/spec/unit/pbjs_api_spec.js | 113 +++++++++++++++++- test/spec/video_spec.js | 24 ++-- 21 files changed, 242 insertions(+), 132 deletions(-) diff --git a/modules/craftBidAdapter.js b/modules/craftBidAdapter.js index a2a054d7659..74e732d313f 100644 --- a/modules/craftBidAdapter.js +++ b/modules/craftBidAdapter.js @@ -1,7 +1,6 @@ import {getBidRequest, logError} from '../src/utils.js'; import {registerBidder} from '../src/adapters/bidderFactory.js'; import {BANNER, NATIVE, VIDEO} from '../src/mediaTypes.js'; -import {auctionManager} from '../src/auctionManager.js'; import {find, includes} from '../src/polyfill.js'; import {getStorageManager} from '../src/storageManager.js'; import {ajax} from '../src/ajax.js'; @@ -186,12 +185,9 @@ function bidToTag(bid) { if (keywords.length) { tag.keywords = keywords; } - // TODO: why does this need to iterate through every ad unit? - let adUnit = find(auctionManager.getAdUnits(), au => bid.transactionId === au.transactionId); - if (adUnit && adUnit.mediaTypes && adUnit.mediaTypes.banner) { + if (bid.mediaTypes?.banner) { tag.ad_types.push(BANNER); } - if (tag.ad_types.length === 0) { delete tag.ad_types; } diff --git a/modules/goldbachBidAdapter.js b/modules/goldbachBidAdapter.js index ff394e69e54..9f9913b7023 100644 --- a/modules/goldbachBidAdapter.js +++ b/modules/goldbachBidAdapter.js @@ -19,7 +19,6 @@ import { import {config} from '../src/config.js'; import {registerBidder} from '../src/adapters/bidderFactory.js'; import {ADPOD, BANNER, NATIVE, VIDEO} from '../src/mediaTypes.js'; -import {auctionManager} from '../src/auctionManager.js'; import {find, includes} from '../src/polyfill.js'; import {INSTREAM, OUTSTREAM} from '../src/video.js'; import {hasPurpose1Consent} from '../src/utils/gpdr.js'; @@ -882,9 +881,7 @@ function bidToTag(bid) { tag['banner_frameworks'] = bid.params.frameworks; } - // TODO: why does this need to iterate through every ad unit? - let adUnit = find(auctionManager.getAdUnits(), au => bid.transactionId === au.transactionId); - if (adUnit && adUnit.mediaTypes && adUnit.mediaTypes.banner) { + if (bid.mediaTypes?.banner) { tag.ad_types.push(BANNER); } diff --git a/modules/hybridBidAdapter.js b/modules/hybridBidAdapter.js index 00e109516e5..01d29ee0126 100644 --- a/modules/hybridBidAdapter.js +++ b/modules/hybridBidAdapter.js @@ -1,6 +1,5 @@ import {_map, deepAccess, isArray, logWarn} from '../src/utils.js'; import {registerBidder} from '../src/adapters/bidderFactory.js'; -import {auctionManager} from '../src/auctionManager.js'; import {BANNER, VIDEO} from '../src/mediaTypes.js'; import {Renderer} from '../src/Renderer.js'; import {find} from '../src/polyfill.js'; @@ -95,16 +94,13 @@ function buildBid(bidData) { bid.vastXml = bidData.content; bid.mediaType = VIDEO; - // TODO: why does this need to iterate through every ad unit? - let adUnit = find(auctionManager.getAdUnits(), function (unit) { - return unit.transactionId === bidData.transactionId; - }); + const video = bidData.mediaTypes?.video; - if (adUnit) { - bid.width = adUnit.mediaTypes.video.playerSize[0][0]; - bid.height = adUnit.mediaTypes.video.playerSize[0][1]; + if (video) { + bid.width = video.playerSize[0][0]; + bid.height = video.playerSize[0][1]; - if (adUnit.mediaTypes.video.context === 'outstream') { + if (video.context === 'outstream') { bid.renderer = createRenderer(bid); } } diff --git a/modules/mediafuseBidAdapter.js b/modules/mediafuseBidAdapter.js index 5e7221583a8..5e31f60d3b5 100644 --- a/modules/mediafuseBidAdapter.js +++ b/modules/mediafuseBidAdapter.js @@ -20,7 +20,6 @@ import {Renderer} from '../src/Renderer.js'; import {config} from '../src/config.js'; import {registerBidder} from '../src/adapters/bidderFactory.js'; import {ADPOD, BANNER, NATIVE, VIDEO} from '../src/mediaTypes.js'; -import {auctionManager} from '../src/auctionManager.js'; import {find, includes} from '../src/polyfill.js'; import {INSTREAM, OUTSTREAM} from '../src/video.js'; import {getStorageManager} from '../src/storageManager.js'; @@ -870,9 +869,7 @@ function bidToTag(bid) { tag['banner_frameworks'] = bid.params.frameworks; } - // TODO: why does this need to iterate through every ad unit? - let adUnit = find(auctionManager.getAdUnits(), au => bid.transactionId === au.transactionId); - if (adUnit && adUnit.mediaTypes && adUnit.mediaTypes.banner) { + if (bid.mediaTypes?.banner) { tag.ad_types.push(BANNER); } diff --git a/modules/prebidServerBidAdapter/ortbConverter.js b/modules/prebidServerBidAdapter/ortbConverter.js index 7aeb4302280..1dd1532f423 100644 --- a/modules/prebidServerBidAdapter/ortbConverter.js +++ b/modules/prebidServerBidAdapter/ortbConverter.js @@ -118,6 +118,7 @@ const PBS_CONVERTER = ortbConverter({ src: CONSTANTS.S2S.SRC, bidId: bidRequest ? (bidRequest.bidId || bidRequest.bid_Id) : null, transactionId: context.adUnit.transactionId, + adUnitId: context.adUnit.adUnitId, auctionId: context.bidderRequest.auctionId, }), bidResponse), adUnit: context.adUnit.code diff --git a/modules/priceFloors.js b/modules/priceFloors.js index 277b6839c9b..70a0f9b9a14 100644 --- a/modules/priceFloors.js +++ b/modules/priceFloors.js @@ -94,8 +94,8 @@ const getHostname = (() => { })(); // First look into bidRequest! -function getGptSlotFromAdUnit(transactionId, {index = auctionManager.index} = {}) { - const adUnit = index.getAdUnit({transactionId}); +function getGptSlotFromAdUnit(adUnitId, {index = auctionManager.index} = {}) { + const adUnit = index.getAdUnit({adUnitId}); const isGam = deepAccess(adUnit, 'ortb2Imp.ext.data.adserver.name') === 'gam'; return isGam && adUnit.ortb2Imp.ext.data.adserver.adslot; } @@ -111,7 +111,7 @@ export let fieldMatchingFunctions = { [SYN_FIELD]: () => '*', 'size': (bidRequest, bidResponse) => parseGPTSingleSizeArray(bidResponse.size) || '*', 'mediaType': (bidRequest, bidResponse) => bidResponse.mediaType || 'banner', - 'gptSlot': (bidRequest, bidResponse) => getGptSlotFromAdUnit((bidRequest || bidResponse).transactionId) || getGptSlotInfoForAdUnitCode(getAdUnitCode(bidRequest, bidResponse)).gptSlot, + 'gptSlot': (bidRequest, bidResponse) => getGptSlotFromAdUnit((bidRequest || bidResponse).adUnitId) || getGptSlotInfoForAdUnitCode(getAdUnitCode(bidRequest, bidResponse)).gptSlot, 'domain': getHostname, 'adUnitCode': (bidRequest, bidResponse) => getAdUnitCode(bidRequest, bidResponse) } diff --git a/modules/voxBidAdapter.js b/modules/voxBidAdapter.js index f1670de76d0..da72b975717 100644 --- a/modules/voxBidAdapter.js +++ b/modules/voxBidAdapter.js @@ -2,9 +2,8 @@ import {_map, deepAccess, isArray, logWarn} from '../src/utils.js'; import {registerBidder} from '../src/adapters/bidderFactory.js'; import {BANNER, VIDEO} from '../src/mediaTypes.js'; import {find} from '../src/polyfill.js'; -import {auctionManager} from '../src/auctionManager.js'; import {Renderer} from '../src/Renderer.js'; -import {config} from '../src/config.js' +import {config} from '../src/config.js'; /** * @typedef {import('../src/adapters/bidderFactory.js').BidRequest} BidRequest @@ -99,17 +98,13 @@ function buildBid(bidData) { if (bidData.placement === 'video') { bid.vastXml = bidData.content; bid.mediaType = VIDEO; + const video = bidData.mediaTypes?.video; - // TODO: why does this need to iterate through every ad unit? - 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 (video) { + bid.width = video.playerSize[0][0]; + bid.height = video.playerSize[0][1]; - if (adUnit.mediaTypes.video.context === 'outstream') { + if (video.context === 'outstream') { bid.renderer = createRenderer(bid); } } diff --git a/package-lock.json b/package-lock.json index 9d7bc733e29..4821d711887 100644 --- a/package-lock.json +++ b/package-lock.json @@ -6,7 +6,7 @@ "packages": { "": { "name": "prebid.js", - "version": "8.33.0-pre", + "version": "8.35.0-pre", "license": "Apache-2.0", "dependencies": { "@babel/core": "^7.16.7", diff --git a/src/adapterManager.js b/src/adapterManager.js index 189fbf31f2e..72695be0946 100644 --- a/src/adapterManager.js +++ b/src/adapterManager.js @@ -115,6 +115,7 @@ function getBids({bidderCode, auctionId, bidderRequestId, adUnits, src, metrics} bids.push(Object.assign({}, bid, { adUnitCode: adUnit.code, transactionId: adUnit.transactionId, + adUnitId: adUnit.adUnitId, sizes: deepAccess(mediaTypes, 'banner.sizes') || deepAccess(mediaTypes, 'video.playerSize') || [], bidId: bid.bid_id || getUniqueIdentifierStr(), bidderRequestId, diff --git a/src/auction.js b/src/auction.js index 37bb2604167..cc42e54a2b1 100644 --- a/src/auction.js +++ b/src/auction.js @@ -369,7 +369,7 @@ export function newAuction({adUnits, adUnitCodes, callback, cbTimeout, labels, a } function addWinningBid(winningBid) { - const winningAd = adUnits.find(adUnit => adUnit.transactionId === winningBid.transactionId); + const winningAd = adUnits.find(adUnit => adUnit.adUnitId === winningBid.adUnitId); _winningBids = _winningBids.concat(winningBid); callBurl(winningBid); adapterManager.callBidWonBidder(winningBid.adapterCode || winningBid.bidder, winningBid, adUnits); @@ -557,7 +557,7 @@ function tryAddVideoBid(auctionInstance, bidResponse, afterBidAdded, {index = au const videoMediaType = deepAccess( index.getMediaTypes({ requestId: bidResponse.originalRequestId || bidResponse.requestId, - transactionId: bidResponse.transactionId + adUnitId: bidResponse.adUnitId }), 'video'); const context = videoMediaType && deepAccess(videoMediaType, 'context'); const useCacheKey = videoMediaType && deepAccess(videoMediaType, 'useCacheKey'); diff --git a/src/auctionIndex.js b/src/auctionIndex.js index bb3f535b41a..afae2089518 100644 --- a/src/auctionIndex.js +++ b/src/auctionIndex.js @@ -2,9 +2,9 @@ * @typedef {Object} AuctionIndex * * @property {function({ auctionId: * }): *} getAuction Returns auction instance for `auctionId` - * @property {function({ transactionId: * }): *} getAdUnit Returns `adUnit` object for `transactionId`. + * @property {function({ adUnitId: * }): *} getAdUnit Returns `adUnit` object for `transactionId`. * You should prefer `getMediaTypes` for looking up bid media types. - * @property {function({ transactionId: *, requestId: * }): *} getMediaTypes Returns mediaTypes object from bidRequest (through `requestId`) falling back to the adUnit (through `transactionId`). + * @property {function({ adUnitId: *, requestId: * }): *} getMediaTypes Returns mediaTypes object from bidRequest (through `requestId`) falling back to the adUnit (through `transactionId`). * The bidRequest is given precedence because its mediaTypes can differ from the adUnit's (if bidder-specific labels are in use). * Bids that have no associated request do not have labels either, and use the adUnit's mediaTypes. * @property {function({ requestId: *, bidderRequestId: * }): *} getBidderRequest Returns bidderRequest that matches both requestId and bidderRequestId (if either or both are provided). @@ -25,21 +25,21 @@ export function AuctionIndex(getAuctions) { .find(auction => auction.getAuctionId() === auctionId); } }, - getAdUnit({transactionId}) { - if (transactionId != null) { + getAdUnit({adUnitId}) { + if (adUnitId != null) { return getAuctions() .flatMap(a => a.getAdUnits()) - .find(au => au.transactionId === transactionId); + .find(au => au.adUnitId === adUnitId); } }, - getMediaTypes({transactionId, requestId}) { + getMediaTypes({adUnitId, requestId}) { if (requestId != null) { const req = this.getBidRequest({requestId}); - if (req != null && (transactionId == null || req.transactionId === transactionId)) { + if (req != null && (adUnitId == null || req.adUnitId === adUnitId)) { return req.mediaTypes; } - } else if (transactionId != null) { - const au = this.getAdUnit({transactionId}); + } else if (adUnitId != null) { + const au = this.getAdUnit({adUnitId}); if (au != null) { return au.mediaTypes; } diff --git a/src/bidfactory.js b/src/bidfactory.js index 4c2e4cf3ffb..d3bac4a0030 100644 --- a/src/bidfactory.js +++ b/src/bidfactory.js @@ -14,20 +14,23 @@ import { getUniqueIdentifierStr } from './utils.js'; dealId, priceKeyString; */ -function Bid(statusCode, {src = 'client', bidder = '', bidId, transactionId, auctionId} = {}) { +function Bid(statusCode, {src = 'client', bidder = '', bidId, transactionId, adUnitId, auctionId} = {}) { var _bidSrc = src; var _statusCode = statusCode || 0; - this.bidderCode = bidder; - this.width = 0; - this.height = 0; - this.statusMessage = _getStatus(); - this.adId = getUniqueIdentifierStr(); - this.requestId = bidId; - this.transactionId = transactionId; - this.auctionId = auctionId; - this.mediaType = 'banner'; - this.source = _bidSrc; + Object.assign(this, { + bidderCode: bidder, + width: 0, + height: 0, + statusMessage: _getStatus(), + adId: getUniqueIdentifierStr(), + requestId: bidId, + transactionId, + adUnitId, + auctionId, + mediaType: 'banner', + source: _bidSrc + }) function _getStatus() { switch (_statusCode) { @@ -57,6 +60,7 @@ function Bid(statusCode, {src = 'client', bidder = '', bidId, transactionId, auc bidder: this.bidderCode, bidId: this.requestId, transactionId: this.transactionId, + adUnitId: this.adUnitId, auctionId: this.auctionId } }; diff --git a/src/prebid.js b/src/prebid.js index df224ab83cf..c06fb6c1979 100644 --- a/src/prebid.js +++ b/src/prebid.js @@ -552,6 +552,8 @@ export const startAuction = hook('async', function ({ bidsBackHandler, timeout: defer.resolve({bids, timedOut, auctionId}) } + const tids = {}; + /* * for a given adunit which supports a set of mediaTypes * and a given bidder which supports a set of mediaTypes @@ -567,15 +569,18 @@ export const startAuction = hook('async', function ({ bidsBackHandler, timeout: const bidderRegistry = adapterManager.bidderRegistry; const bidders = allBidders.filter(bidder => !s2sBidders.has(bidder)); - - const tid = adUnit.ortb2Imp?.ext?.tid || generateUUID(); - adUnit.transactionId = tid; + adUnit.adUnitId = generateUUID(); + const tid = adUnit.ortb2Imp?.ext?.tid; + if (tid) { + if (tids.hasOwnProperty(adUnit.code)) { + logWarn(`Multiple distinct ortb2Imp.ext.tid were provided for twin ad units '${adUnit.code}'`) + } else { + tids[adUnit.code] = tid; + } + } if (ttlBuffer != null && !adUnit.hasOwnProperty('ttlBuffer')) { adUnit.ttlBuffer = ttlBuffer; } - // Populate ortb2Imp.ext.tid with transactionId. Specifying a transaction ID per item in the ortb impression array, lets multiple transaction IDs be transmitted in a single bid request. - deepSetValue(adUnit, 'ortb2Imp.ext.tid', tid); - bidders.forEach(bidder => { const adapter = bidderRegistry[bidder]; const spec = adapter && adapter.getSpec && adapter.getSpec(); @@ -594,11 +599,18 @@ export const startAuction = hook('async', function ({ bidsBackHandler, timeout: }); adunitCounter.incrementRequestsCounter(adUnit.code); }); - if (!adUnits || adUnits.length === 0) { logMessage('No adUnits configured. No bids requested.'); auctionDone(); } else { + adUnits.forEach(au => { + const tid = au.ortb2Imp?.ext?.tid || tids[au.code] || generateUUID(); + if (!tids.hasOwnProperty(au.code)) { + tids[au.code] = tid; + } + au.transactionId = tid; + deepSetValue(au, 'ortb2Imp.ext.tid', tid); + }); const auction = auctionManager.createAuction({ adUnits, adUnitCodes, diff --git a/test/spec/auctionmanager_spec.js b/test/spec/auctionmanager_spec.js index 137d009bd18..c78a5e51f17 100644 --- a/test/spec/auctionmanager_spec.js +++ b/test/spec/auctionmanager_spec.js @@ -57,6 +57,7 @@ function mockBid(opts) { 'bidderCode': bidderCode || BIDDER_CODE, 'requestId': utils.getUniqueIdentifierStr(), 'transactionId': (opts && opts.transactionId) || ADUNIT_CODE, + adUnitId: (opts && opts.adUnitId) || ADUNIT_CODE, 'creativeId': 'id', 'currency': 'USD', 'netRevenue': true, @@ -114,6 +115,7 @@ function mockBidRequest(bid, opts) { }, 'adUnitCode': adUnitCode || ADUNIT_CODE, 'transactionId': bid.transactionId, + adUnitId: bid.adUnitId, 'sizes': [[300, 250], [300, 600]], 'bidId': bid.requestId, 'bidderRequestId': requestId, @@ -791,7 +793,7 @@ describe('auctionmanager.js', function () { }); adUnits = [{ code: ADUNIT_CODE, - transactionId: ADUNIT_CODE, + adUnitId: ADUNIT_CODE, bids: [ {bidder: BIDDER_CODE}, ] @@ -849,11 +851,11 @@ describe('auctionmanager.js', function () { bids = [ { adUnitCode: ADUNIT_CODE, - transactionId: ADUNIT_CODE, + adUnitId: ADUNIT_CODE, ttl: 10 }, { adUnitCode: ADUNIT_CODE, - transactionId: ADUNIT_CODE, + adUnitId: ADUNIT_CODE, ttl: 100 } ]; @@ -882,7 +884,7 @@ describe('auctionmanager.js', function () { 'bids': { bd: [{ adUnitCode: ADUNIT_CODE, - transactionId: ADUNIT_CODE, + adUnitId: ADUNIT_CODE, ttl: 10 }], entries: () => auctionManager.getBidsReceived() @@ -933,7 +935,7 @@ describe('auctionmanager.js', function () { } }, code: ADUNIT_CODE, - transactionId: ADUNIT_CODE, + adUnitId: ADUNIT_CODE, bids: [ {bidder: BIDDER_CODE, params: {placementId: 'id'}}, ] @@ -1134,7 +1136,7 @@ describe('auctionmanager.js', function () { }, bidRequests[0].bids[0]); Object.assign(bidRequests[0].bids[0], { adUnitCode: ADUNIT_CODE1, - transactionId: ADUNIT_CODE1, + adUnitId: ADUNIT_CODE1, }); makeRequestsStub.returns(bidRequests); @@ -1186,6 +1188,7 @@ describe('auctionmanager.js', function () { adUnits = [{ code: ADUNIT_CODE, transactionId: ADUNIT_CODE, + adUnitId: ADUNIT_CODE, bids: [ {bidder: BIDDER_CODE, params: {placementId: 'id'}}, {bidder: BIDDER_CODE1, params: {placementId: 'id'}}, @@ -1357,12 +1360,14 @@ describe('auctionmanager.js', function () { adUnits = [{ code: ADUNIT_CODE, transactionId: ADUNIT_CODE, + adUnitId: ADUNIT_CODE, bids: [ {bidder: BIDDER_CODE, params: {placementId: 'id'}}, ] }, { code: ADUNIT_CODE1, transactionId: ADUNIT_CODE1, + adUnitId: ADUNIT_CODE1, bids: [ {bidder: BIDDER_CODE1, params: {placementId: 'id'}}, ] @@ -1570,6 +1575,7 @@ describe('auctionmanager.js', function () { const adUnits = [{ code: ADUNIT_CODE, transactionId: ADUNIT_CODE, + adUnitId: ADUNIT_CODE, bids: [ {bidder: BIDDER_CODE, params: {placementId: 'id'}}, ], @@ -1702,7 +1708,7 @@ describe('auctionmanager.js', function () { function mockAuction(getBidRequests, start = 1) { return { getBidRequests: getBidRequests, - getAdUnits: () => getBidRequests().flatMap(br => br.bids).map(br => ({ code: br.adUnitCode, transactionId: br.transactionId, mediaTypes: br.mediaTypes })), + getAdUnits: () => getBidRequests().flatMap(br => br.bids).map(br => ({ code: br.adUnitCode, transactionId: br.transactionId, adUnitId: br.adUnitId, mediaTypes: br.mediaTypes })), getAuctionId: () => '1', addBidReceived: () => true, addBidRejected: () => true, @@ -1773,8 +1779,8 @@ describe('auctionmanager.js', function () { let ADUNIT_CODE2 = 'adUnitCode2'; let BIDDER_CODE2 = 'sampleBidder2'; - let bids1 = [mockBid({ bidderCode: BIDDER_CODE1, transactionId: ADUNIT_CODE1 })]; - let bids2 = [mockBid({ bidderCode: BIDDER_CODE2, transactionId: ADUNIT_CODE2 })]; + let bids1 = [mockBid({ bidderCode: BIDDER_CODE1, adUnitId: ADUNIT_CODE1 })]; + let bids2 = [mockBid({ bidderCode: BIDDER_CODE2, adUnitId: ADUNIT_CODE2 })]; bidRequests = [ mockBidRequest(bids[0]), mockBidRequest(bids1[0], { adUnitCode: ADUNIT_CODE1 }), diff --git a/test/spec/modules/prebidServerBidAdapter_spec.js b/test/spec/modules/prebidServerBidAdapter_spec.js index d88d4411e33..2bab144dae7 100644 --- a/test/spec/modules/prebidServerBidAdapter_spec.js +++ b/test/spec/modules/prebidServerBidAdapter_spec.js @@ -91,6 +91,7 @@ const REQUEST = { } }, 'transactionId': '4ef956ad-fd83-406d-bd35-e4bb786ab86c', + 'adUnitId': 'au-id-1', 'bids': [ { 'bid_id': '123', diff --git a/test/spec/modules/priceFloors_spec.js b/test/spec/modules/priceFloors_spec.js index fa31ae0ff81..7ea7722b12a 100644 --- a/test/spec/modules/priceFloors_spec.js +++ b/test/spec/modules/priceFloors_spec.js @@ -116,7 +116,8 @@ describe('the price floors module', function () { bidder: 'rubicon', adUnitCode: 'test_div_1', auctionId: '1234-56-789', - transactionId: 'tr_test_div_1' + transactionId: 'tr_test_div_1', + adUnitId: 'tr_test_div_1', }; function getAdUnitMock(code = 'adUnit-code') { @@ -618,8 +619,8 @@ describe('the price floors module', function () { }); }); it('picks the gptSlot from the adUnit and does not call the slotMatching', function () { - const newBidRequest1 = { ...basicBidRequest, transactionId: 'au1' }; - adUnits = [{code: newBidRequest1.code, transactionId: 'au1'}]; + const newBidRequest1 = { ...basicBidRequest, adUnitId: 'au1' }; + adUnits = [{code: newBidRequest1.adUnitCode, adUnitId: 'au1'}]; utils.deepSetValue(adUnits[0], 'ortb2Imp.ext.data.adserver', { name: 'gam', adslot: '/12345/news/politics' @@ -632,8 +633,8 @@ describe('the price floors module', function () { matchingRule: '/12345/news/politics' }); - const newBidRequest2 = { ...basicBidRequest, adUnitCode: 'test_div_2', transactionId: 'au2' }; - adUnits = [{code: newBidRequest2.adUnitCode, transactionId: newBidRequest2.transactionId}]; + const newBidRequest2 = { ...basicBidRequest, adUnitCode: 'test_div_2', adUnitId: 'au2' }; + adUnits = [{code: newBidRequest2.adUnitCode, adUnitId: newBidRequest2.adUnitId}]; utils.deepSetValue(adUnits[0], 'ortb2Imp.ext.data.adserver', { name: 'gam', adslot: '/12345/news/weather' @@ -2362,7 +2363,7 @@ describe('the price floors module', function () { } const resp = { - transactionId: req.transactionId, + adUnitId: req.adUnitId, size: [100, 100], mediaType: 'banner', } @@ -2373,7 +2374,7 @@ describe('the price floors module', function () { adUnits: [ { code: req.adUnitCode, - transactionId: req.transactionId, + adUnitId: req.adUnitId, ortb2Imp: {ext: {data: {adserver: {name: 'gam', adslot: 'slot'}}}} } ] diff --git a/test/spec/native_spec.js b/test/spec/native_spec.js index 9cfee6f5cd8..bf1c745d8cc 100644 --- a/test/spec/native_spec.js +++ b/test/spec/native_spec.js @@ -19,7 +19,7 @@ const utils = require('src/utils'); const bid = { adId: '123', - transactionId: 'au', + adUnitId: 'au', native: { title: 'Native Creative', body: 'Cool description great stuff', @@ -49,7 +49,7 @@ const bid = { const ortbBid = { adId: '123', - transactionId: 'au', + adUnitId: 'au', native: { ortb: { assets: [ @@ -106,7 +106,7 @@ const ortbBid = { const completeNativeBid = { adId: '123', - transactionId: 'au', + adUnitId: 'au', native: { ...bid.native, ...ortbBid.native @@ -157,7 +157,7 @@ const ortbRequest = { } const bidWithUndefinedFields = { - transactionId: 'au', + adUnitId: 'au', native: { title: 'Native Creative', body: undefined, @@ -209,7 +209,7 @@ describe('native.js', function () { it('sends placeholders for configured assets', function () { const adUnit = { - transactionId: 'au', + adUnitId: 'au', nativeParams: { body: { sendId: true }, clickUrl: { sendId: true }, @@ -246,7 +246,7 @@ describe('native.js', function () { it('should only include native targeting keys with values', function () { const adUnit = { - transactionId: 'au', + adUnitId: 'au', nativeParams: { body: { sendId: true }, clickUrl: { sendId: true }, @@ -273,7 +273,7 @@ describe('native.js', function () { it('should only include targeting that has sendTargetingKeys set to true', function () { const adUnit = { - transactionId: 'au', + adUnitId: 'au', nativeParams: { image: { required: true, @@ -294,7 +294,7 @@ describe('native.js', function () { it('should only include targeting if sendTargetingKeys not set to false', function () { const adUnit = { - transactionId: 'au', + adUnitId: 'au', nativeParams: { image: { required: true, @@ -347,7 +347,7 @@ describe('native.js', function () { it('should copy over rendererUrl to bid object and include it in targeting', function () { const adUnit = { - transactionId: 'au', + adUnitId: 'au', nativeParams: { image: { required: true, @@ -382,7 +382,7 @@ describe('native.js', function () { it('should copy over adTemplate to bid object and include it in targeting', function () { const adUnit = { - transactionId: 'au', + adUnitId: 'au', nativeParams: { image: { required: true, @@ -724,7 +724,7 @@ describe('validate native openRTB', function () { describe('validate native', function () { const adUnit = { - transactionId: 'test_adunit', + adUnitId: 'test_adunit', mediaTypes: { native: { title: { @@ -749,7 +749,7 @@ describe('validate native', function () { let validBid = { adId: 'abc123', requestId: 'test_bid_id', - transactionId: 'test_adunit', + adUnitId: 'test_adunit', adUnitCode: '123/prebid_native_adunit', bidder: 'test_bidder', native: { @@ -776,7 +776,7 @@ describe('validate native', function () { let noIconDimBid = { adId: 'abc234', requestId: 'test_bid_id', - transactionId: 'test_adunit', + adUnitId: 'test_adunit', adUnitCode: '123/prebid_native_adunit', bidder: 'test_bidder', native: { @@ -799,7 +799,7 @@ describe('validate native', function () { let noImgDimBid = { adId: 'abc345', requestId: 'test_bid_id', - transactionId: 'test_adunit', + adUnitId: 'test_adunit', adUnitCode: '123/prebid_native_adunit', bidder: 'test_bidder', native: { @@ -836,7 +836,7 @@ describe('validate native', function () { it('should convert from old-style native to OpenRTB request', () => { const adUnit = { - transactionId: 'test_adunit', + adUnitId: 'test_adunit', mediaTypes: { native: { title: { @@ -1043,7 +1043,7 @@ describe('validate native', function () { const validBidRequests = [{ bidId: 'bidId3', adUnitCode: 'adUnitCode3', - transactionId: 'transactionId3', + adUnitId: 'transactionId3', mediaTypes: { banner: {} }, diff --git a/test/spec/unit/core/auctionIndex_spec.js b/test/spec/unit/core/auctionIndex_spec.js index f00e2cd281f..df29ed1a6cb 100644 --- a/test/spec/unit/core/auctionIndex_spec.js +++ b/test/spec/unit/core/auctionIndex_spec.js @@ -38,22 +38,22 @@ describe('auction index', () => { let adUnits; beforeEach(() => { - adUnits = [{transactionId: 'au1'}, {transactionId: 'au2'}]; + adUnits = [{adUnitId: 'au1'}, {adUnitId: 'au2'}]; auctions = [ mockAuction('a1', [adUnits[0], {}]), mockAuction('a2', [adUnits[1]]) ]; }); - it('should find adUnits by transactionId', () => { - expect(index.getAdUnit({transactionId: 'au2'})).to.equal(adUnits[1]); + it('should find adUnits by adUnitId', () => { + expect(index.getAdUnit({adUnitId: 'au2'})).to.equal(adUnits[1]); }); it('should return undefined if adunit is missing', () => { - expect(index.getAdUnit({transactionId: 'missing'})).to.be.undefined; + expect(index.getAdUnit({adUnitId: 'missing'})).to.be.undefined; }); - it('should return undefined if no transactionId is provided', () => { + it('should return undefined if no adUnitId is provided', () => { expect(index.getAdUnit({})).to.be.undefined; }); }); @@ -87,12 +87,12 @@ describe('auction index', () => { beforeEach(() => { mediaTypes = [{mockMT: '1'}, {mockMT: '2'}, {mockMT: '3'}, {mockMT: '4'}] adUnits = [ - {transactionId: 'au1', mediaTypes: mediaTypes[0]}, - {transactionId: 'au2', mediaTypes: mediaTypes[1]} + {adUnitId: 'au1', mediaTypes: mediaTypes[0]}, + {adUnitId: 'au2', mediaTypes: mediaTypes[1]} ] bidderRequests = [ - {bidderRequestId: 'ber1', bids: [{bidId: 'b1', mediaTypes: mediaTypes[2], transactionId: 'au1'}, {}]}, - {bidderRequestId: 'ber2', bids: [{bidId: 'b2', mediaTypes: mediaTypes[3], transactionId: 'au2'}]} + {bidderRequestId: 'ber1', bids: [{bidId: 'b1', mediaTypes: mediaTypes[2], adUnitId: 'au1'}, {}]}, + {bidderRequestId: 'ber2', bids: [{bidId: 'b2', mediaTypes: mediaTypes[3], adUnitId: 'au2'}]} ] auctions = [ mockAuction('a1', [adUnits[0]], [bidderRequests[0], {}]), @@ -100,8 +100,8 @@ describe('auction index', () => { ] }); - it('should find mediaTypes by transactionId', () => { - expect(index.getMediaTypes({transactionId: 'au2'})).to.equal(mediaTypes[1]); + it('should find mediaTypes by adUnitId', () => { + expect(index.getMediaTypes({adUnitId: 'au2'})).to.equal(mediaTypes[1]); }); it('should find mediaTypes by requestId', () => { @@ -109,18 +109,18 @@ describe('auction index', () => { }); it('should give precedence to request.mediaTypes over adUnit.mediaTypes', () => { - expect(index.getMediaTypes({requestId: 'b2', transactionId: 'au2'})).to.equal(mediaTypes[3]); + expect(index.getMediaTypes({requestId: 'b2', adUnitId: 'au2'})).to.equal(mediaTypes[3]); }); - it('should return undef if requestId and transactionId do not match', () => { - expect(index.getMediaTypes({requestId: 'b1', transactionId: 'au2'})).to.be.undefined; + it('should return undef if requestId and adUnitId do not match', () => { + expect(index.getMediaTypes({requestId: 'b1', adUnitId: 'au2'})).to.be.undefined; }); it('should return undef if no params are provided', () => { expect(index.getMediaTypes({})).to.be.undefined; }); - ['requestId', 'transactionId'].forEach(param => { + ['requestId', 'adUnitId'].forEach(param => { it(`should return undef if ${param} is missing`, () => { expect(index.getMediaTypes({[param]: 'missing'})).to.be.undefined; }); diff --git a/test/spec/unit/core/bidderFactory_spec.js b/test/spec/unit/core/bidderFactory_spec.js index 16042ca52dd..aba64733f90 100644 --- a/test/spec/unit/core/bidderFactory_spec.js +++ b/test/spec/unit/core/bidderFactory_spec.js @@ -1109,7 +1109,7 @@ describe('bidderFactory', () => { if (FEATURES.NATIVE) { it('should add native bids that do have required assets', function () { adUnits = [{ - transactionId: 'au', + adUnitId: 'au', nativeParams: { title: {'required': true}, } @@ -1120,7 +1120,7 @@ describe('bidderFactory', () => { bidId: '1', auctionId: 'first-bid-id', adUnitCode: 'mock/placement', - transactionId: 'au', + adUnitId: 'au', params: { param: 5 }, diff --git a/test/spec/unit/pbjs_api_spec.js b/test/spec/unit/pbjs_api_spec.js index 39123d4aa41..e61171c3a22 100644 --- a/test/spec/unit/pbjs_api_spec.js +++ b/test/spec/unit/pbjs_api_spec.js @@ -556,6 +556,7 @@ describe('Unit: Prebid Module', function () { 'bidderRequestId': '331f3cf3f1d9c8', 'auctionId': '20882439e3238c', 'transactionId': 'trdiv-gpt-ad-1460505748561-0', + 'adUnitId': 'audiv-gpt-ad-1460505748561-0', } ], 'auctionStart': 1505250713622, @@ -573,6 +574,7 @@ describe('Unit: Prebid Module', function () { let auctionManagerInstance = newAuctionManager(); targeting = newTargeting(auctionManagerInstance); let adUnits = [{ + adUnitId: 'audiv-gpt-ad-1460505748561-0', transactionId: 'trdiv-gpt-ad-1460505748561-0', code: 'div-gpt-ad-1460505748561-0', sizes: [[300, 250], [300, 600]], @@ -718,6 +720,7 @@ describe('Unit: Prebid Module', function () { const adUnit = { transactionId: `tr${code}`, + adUnitId: `au${code}`, code: code, sizes: [[300, 250], [300, 600]], bids: [{ @@ -820,6 +823,7 @@ describe('Unit: Prebid Module', function () { }, 'adUnitCode': 'div-gpt-ad-1460505748561-0', 'transactionId': 'trdiv-gpt-ad-1460505748561-0', + 'adUnitId': 'audiv-gpt-ad-1460505748561-0', 'sizes': [ [ 300, @@ -1532,6 +1536,7 @@ describe('Unit: Prebid Module', function () { } }, transactionId: 'mock-tid', + adUnitId: 'mock-au', bids: [ {bidder: BIDDER_CODE, params: {placementId: 'id'}}, ] @@ -1606,6 +1611,7 @@ describe('Unit: Prebid Module', function () { height: 250, adUnitCode: bidRequests[0].bids[0].adUnitCode, transactionId: 'mock-tid', + adUnitId: 'mock-au', adserverTargeting: { 'hb_bidder': BIDDER_CODE, 'hb_adid': bidId, @@ -1684,7 +1690,8 @@ describe('Unit: Prebid Module', function () { const bid = { bidder: 'mock-bidder', adUnitCode: adUnits[0].code, - transactionId: adUnits[0].transactionId + transactionId: adUnits[0].transactionId, + adUnitId: adUnits[0].adUnitId, } requestBids({ adUnits, @@ -1970,6 +1977,102 @@ describe('Unit: Prebid Module', function () { .and.to.match(/[a-f0-9\-]{36}/i); }); + it('should use the same transactionID for ad units with the same code', () => { + $$PREBID_GLOBAL$$.requestBids({ + adUnits: [ + { + code: 'twin', + mediaTypes: { banner: { sizes: [] } }, + bids: [] + }, { + code: 'twin', + mediaTypes: { banner: { sizes: [] } }, + bids: [] + } + ] + }); + const tid = auctionArgs.adUnits[0].transactionId; + expect(tid).to.exist; + expect(auctionArgs.adUnits[1].transactionId).to.eql(tid); + }); + + it('should re-use pub-provided transaction ID for ad units with the same code', () => { + $$PREBID_GLOBAL$$.requestBids({ + adUnits: [ + { + code: 'twin', + mediaTypes: { banner: { sizes: [] } }, + bids: [], + }, { + code: 'twin', + mediaTypes: { banner: { sizes: [] } }, + bids: [], + ortb2Imp: { + ext: { + tid: 'pub-tid' + } + } + } + ] + }); + expect(auctionArgs.adUnits.map(au => au.transactionId)).to.eql(['pub-tid', 'pub-tid']); + }); + + it('should use pub-provided TIDs when they conflict for ad units with the same code', () => { + $$PREBID_GLOBAL$$.requestBids({ + adUnits: [ + { + code: 'twin', + mediaTypes: { banner: { sizes: [] } }, + bids: [], + ortb2Imp: { + ext: { + tid: 't1' + } + } + }, { + code: 'twin', + mediaTypes: { banner: { sizes: [] } }, + bids: [], + ortb2Imp: { + ext: { + tid: 't2' + } + } + } + ] + }); + expect(auctionArgs.adUnits.map(au => au.transactionId)).to.eql(['t1', 't2']); + }); + + it('should generate unique adUnitId', () => { + $$PREBID_GLOBAL$$.requestBids({ + adUnits: [ + { + code: 'single', + mediaTypes: { banner: { sizes: [] } }, + bids: [] + }, { + code: 'twin', + mediaTypes: { banner: { sizes: [] } }, + bids: [] + }, + { + code: 'twin', + mediaTypes: { banner: { sizes: [] } }, + bids: [] + } + ] + }); + + const ids = new Set(); + auctionArgs.adUnits.forEach(au => { + expect(au.adUnitId).to.exist; + ids.add(au.adUnitId); + }); + expect(ids.size).to.eql(3); + }); + describe('transactionId', () => { let adUnit; beforeEach(() => { @@ -3546,7 +3649,7 @@ describe('Unit: Prebid Module', function () { { code: 'adUnit-code-1', mediaTypes: { banner: { sizes: [[300, 250], [300, 600]] } }, - transactionId: '1234567890', + adUnitId: '1234567890', bids: [ { bidder: 'pubmatic', params: {placementId: '10433394'}, adUnitCode: 'adUnit-code-1' } ] @@ -3555,15 +3658,15 @@ describe('Unit: Prebid Module', function () { code: 'adUnit-code-2', deferBilling: true, mediaTypes: { banner: { sizes: [[300, 250], [300, 600]] } }, - transactionId: '0987654321', + adUnitId: '0987654321', bids: [ { bidder: 'pubmatic', params: {placementId: '10433394'}, adUnitCode: 'adUnit-code-2' } ] } ]; - let winningBid1 = { adapterCode: 'pubmatic', bidder: 'pubmatic', params: {placementId: '10433394'}, adUnitCode: 'adUnit-code-1', transactionId: '1234567890', adId: 'abcdefg' } - let winningBid2 = { adapterCode: 'pubmatic', bidder: 'pubmatic', params: {placementId: '10433394'}, adUnitCode: 'adUnit-code-2', transactionId: '0987654321' } + let winningBid1 = { adapterCode: 'pubmatic', bidder: 'pubmatic', params: {placementId: '10433394'}, adUnitCode: 'adUnit-code-1', adUnitId: '1234567890', adId: 'abcdefg' } + let winningBid2 = { adapterCode: 'pubmatic', bidder: 'pubmatic', params: {placementId: '10433394'}, adUnitCode: 'adUnit-code-2', adUnitId: '0987654321' } let adUnitCodes = ['adUnit-code-1', 'adUnit-code-2']; let auction = auctionModule.newAuction({adUnits, adUnitCodes, callback: function() {}, cbTimeout: 2000}); diff --git a/test/spec/video_spec.js b/test/spec/video_spec.js index 35d0a4fef24..3252c58c687 100644 --- a/test/spec/video_spec.js +++ b/test/spec/video_spec.js @@ -82,10 +82,10 @@ describe('video.js', function () { const bid = { adId: '456xyz', vastUrl: 'http://www.example.com/vastUrl', - transactionId: 'au' + adUnitId: 'au' }; const adUnits = [{ - transactionId: 'au', + adUnitId: 'au', mediaTypes: { video: {context: 'instream'} } @@ -96,10 +96,10 @@ describe('video.js', function () { it('catches invalid instream bids', function () { const bid = { - transactionId: 'au' + adUnitId: 'au' }; const adUnits = [{ - transactionId: 'au', + adUnitId: 'au', mediaTypes: { video: {context: 'instream'} } @@ -110,26 +110,26 @@ describe('video.js', function () { it('catches invalid bids when prebid-cache is disabled', function () { const adUnits = [{ - transactionId: 'au', + adUnitId: 'au', bidder: 'vastOnlyVideoBidder', mediaTypes: {video: {}}, }]; - const valid = isValidVideoBid({ transactionId: 'au', vastXml: 'vast' }, {index: stubAuctionIndex({adUnits})}); + const valid = isValidVideoBid({ adUnitId: 'au', vastXml: 'vast' }, {index: stubAuctionIndex({adUnits})}); expect(valid).to.equal(false); }); it('validates valid outstream bids', function () { const bid = { - transactionId: 'au', + adUnitId: 'au', renderer: { url: 'render.url', render: () => true, } }; const adUnits = [{ - transactionId: 'au', + adUnitId: 'au', mediaTypes: { video: {context: 'outstream'} } @@ -140,10 +140,10 @@ describe('video.js', function () { it('validates valid outstream bids with a publisher defined renderer', function () { const bid = { - transactionId: 'au', + adUnitId: 'au', }; const adUnits = [{ - transactionId: 'au', + adUnitId: 'au', mediaTypes: { video: { context: 'outstream', @@ -160,10 +160,10 @@ describe('video.js', function () { it('catches invalid outstream bids', function () { const bid = { - transactionId: 'au', + adUnitId: 'au', }; const adUnits = [{ - transactionId: 'au', + adUnitId: 'au', mediaTypes: { video: {context: 'outstream'} } From 63429427dfae6f8aec221ac962282578f57bff67 Mon Sep 17 00:00:00 2001 From: Snigel <108489367+snigelweb@users.noreply.github.com> Date: Wed, 7 Feb 2024 14:52:10 +0100 Subject: [PATCH 223/246] snigel Bid Adapter : pass more information to backend (#10987) * snigelBidAdapter: pass more information to backend * snigelBidAdapter: add unit tests for the new functionality --- modules/snigelBidAdapter.js | 47 ++++++++++++++-- test/spec/modules/snigelBidAdapter_spec.js | 64 ++++++++++++++++++++++ 2 files changed, 107 insertions(+), 4 deletions(-) diff --git a/modules/snigelBidAdapter.js b/modules/snigelBidAdapter.js index 6d32d8f97a2..5a327b05cd0 100644 --- a/modules/snigelBidAdapter.js +++ b/modules/snigelBidAdapter.js @@ -1,9 +1,10 @@ import {config} from '../src/config.js'; import {registerBidder} from '../src/adapters/bidderFactory.js'; import {BANNER} from '../src/mediaTypes.js'; -import {deepAccess, isArray, isFn, isPlainObject, inIframe, getDNT} from '../src/utils.js'; +import {deepAccess, isArray, isFn, isPlainObject, inIframe, getDNT, generateUUID} from '../src/utils.js'; import {hasPurpose1Consent} from '../src/utils/gpdr.js'; import {getGlobal} from '../src/prebidGlobal.js'; +import {getStorageManager} from '../src/storageManager.js'; const BIDDER_CODE = 'snigel'; const GVLID = 1076; @@ -11,9 +12,14 @@ const DEFAULT_URL = 'https://adserv.snigelweb.com/bp/v1/prebid'; const DEFAULT_TTL = 60; const DEFAULT_CURRENCIES = ['USD']; const FLOOR_MATCH_ALL_SIZES = '*'; +const SESSION_ID_KEY = '_sn_session_pba'; const getConfig = config.getConfig; +const storageManager = getStorageManager({bidderCode: BIDDER_CODE}); const refreshes = {}; +const pageViewId = generateUUID(); +const pageViewStart = new Date().getTime(); +let auctionCounter = 0; export const spec = { code: BIDDER_CODE, @@ -33,6 +39,11 @@ export const spec = { id: bidderRequest.auctionId, accountId: deepAccess(bidRequests, '0.params.accountId'), site: deepAccess(bidRequests, '0.params.site'), + sessionId: getSessionId(), + counter: auctionCounter++, + pageViewId: pageViewId, + pageViewStart: pageViewStart, + gdprConsent: gdprApplies === true ? hasFullGdprConsent(deepAccess(bidderRequest, 'gdprConsent')) : false, cur: getCurrencies(), test: getTestFlag(), version: getGlobal().version, @@ -104,9 +115,7 @@ export const spec = { registerBidder(spec); function getPage(bidderRequest) { - return ( - getConfig(`${BIDDER_CODE}.page`) || deepAccess(bidderRequest, 'refererInfo.page') || window.location.href - ); + return getConfig(`${BIDDER_CODE}.page`) || deepAccess(bidderRequest, 'refererInfo.page') || window.location.href; } function getEndpoint() { @@ -193,6 +202,19 @@ function hasSyncConsent(gdprConsent, uspConsent, gppConsent) { return hasPurpose1Consent(gdprConsent) && hasUspConsent(uspConsent) && hasGppConsent(gppConsent); } +function hasFullGdprConsent(gdprConsent) { + try { + const purposeConsents = Object.values(gdprConsent.vendorData.purpose.consents); + return ( + purposeConsents.length > 0 && + purposeConsents.every((value) => value === true) && + gdprConsent.vendorData.vendor.consents[GVLID] === true + ); + } catch (e) { + return false; + } +} + function getSyncUrl(responses) { return getConfig(`${BIDDER_CODE}.syncUrl`) || deepAccess(responses[0], 'body.syncUrl'); } @@ -202,3 +224,20 @@ function getSyncEndpoint(url, gdprConsent) { gdprConsent?.consentString || '' )}`; } + +function getSessionId() { + try { + if (storageManager.localStorageIsEnabled()) { + let sessionId = storageManager.getDataFromLocalStorage(SESSION_ID_KEY); + if (sessionId == null) { + sessionId = generateUUID(); + storageManager.setDataInLocalStorage(SESSION_ID_KEY, sessionId); + } + return sessionId; + } else { + return undefined; + } + } catch (e) { + return undefined; + } +} diff --git a/test/spec/modules/snigelBidAdapter_spec.js b/test/spec/modules/snigelBidAdapter_spec.js index 3ba84228872..828aec9491c 100644 --- a/test/spec/modules/snigelBidAdapter_spec.js +++ b/test/spec/modules/snigelBidAdapter_spec.js @@ -2,6 +2,8 @@ import {expect} from 'chai'; import {spec} from 'modules/snigelBidAdapter.js'; import {config} from 'src/config.js'; import {isValid} from 'src/adapters/bidderFactory.js'; +import {registerActivityControl} from 'src/activities/rules.js'; +import {ACTIVITY_ACCESS_DEVICE} from 'src/activities/activities.js'; const BASE_BID_REQUEST = { adUnitCode: 'top_leaderboard', @@ -344,5 +346,67 @@ describe('snigelBidAdapter', function () { expect(sync).to.have.property('url'); expect(sync.url).to.equal(`https://somesyncurl?gdpr=1&gdpr_consent=${DUMMY_GDPR_CONSENT_STRING}`); }); + + it('should omit session ID if no device access', function() { + const bidderRequest = makeBidderRequest(); + const unregisterRule = registerActivityControl(ACTIVITY_ACCESS_DEVICE, 'denyAccess', () => { + return {allow: false, reason: 'no consent'}; + }); + + try { + const request = spec.buildRequests([], bidderRequest); + expect(request).to.have.property('data'); + const data = JSON.parse(request.data); + expect(data.sessionId).to.be.undefined; + } finally { + unregisterRule(); + } + }); + + it('should determine full GDPR consent correctly', function () { + const baseBidderRequest = makeBidderRequest({ + gdprConsent: { + gdprApplies: true, + vendorData: { + purpose: { + consents: {1: true, 2: true, 3: true, 4: true, 5: true}, + }, + vendor: { + consents: {[spec.gvlid]: true}, + } + }, + } + }); + let request = spec.buildRequests([], baseBidderRequest); + expect(request).to.have.property('data'); + let data = JSON.parse(request.data); + expect(data.gdprConsent).to.be.true; + + let bidderRequest = {...baseBidderRequest, ...{gdprConsent: {vendorData: {purpose: {consents: {1: false}}}}}}; + request = spec.buildRequests([], bidderRequest); + expect(request).to.have.property('data'); + data = JSON.parse(request.data); + expect(data.gdprConsent).to.be.false; + + bidderRequest = {...baseBidderRequest, ...{gdprConsent: {vendorData: {vendor: {consents: {[spec.gvlid]: false}}}}}}; + request = spec.buildRequests([], bidderRequest); + expect(request).to.have.property('data'); + data = JSON.parse(request.data); + expect(data.gdprConsent).to.be.false; + }); + + it('should increment auction counter upon every request', function() { + const bidderRequest = makeBidderRequest({}); + + let request = spec.buildRequests([], bidderRequest); + expect(request).to.have.property('data'); + let data = JSON.parse(request.data); + const previousCounter = data.counter; + + request = spec.buildRequests([], bidderRequest); + expect(request).to.have.property('data'); + data = JSON.parse(request.data); + expect(data.counter).to.equal(previousCounter + 1); + }); }); }); From 35eb3d4c589074c25fecb4623274d50f5ca793b3 Mon Sep 17 00:00:00 2001 From: aleksandar-rayn <155530509+aleksandar-rayn@users.noreply.github.com> Date: Wed, 7 Feb 2024 17:24:45 +0100 Subject: [PATCH 224/246] rayn rtd provider module (#11054) Co-authored-by: Berislav Kovacki --- .../gpt/raynRtdProvider_example.html | 167 ++++++++++ modules/raynRtdProvider.js | 198 +++++++++++ modules/raynRtdProvider.md | 118 +++++++ test/spec/modules/raynRtdProvider_spec.js | 308 ++++++++++++++++++ 4 files changed, 791 insertions(+) create mode 100644 integrationExamples/gpt/raynRtdProvider_example.html create mode 100644 modules/raynRtdProvider.js create mode 100644 modules/raynRtdProvider.md create mode 100644 test/spec/modules/raynRtdProvider_spec.js diff --git a/integrationExamples/gpt/raynRtdProvider_example.html b/integrationExamples/gpt/raynRtdProvider_example.html new file mode 100644 index 00000000000..2d43c37513a --- /dev/null +++ b/integrationExamples/gpt/raynRtdProvider_example.html @@ -0,0 +1,167 @@ + + + + + + + + + + + + + + +

Rayn RTD Prebid

+ +
+ +
+ + Rayn Segments: +
+ + diff --git a/modules/raynRtdProvider.js b/modules/raynRtdProvider.js new file mode 100644 index 00000000000..d558c360c4a --- /dev/null +++ b/modules/raynRtdProvider.js @@ -0,0 +1,198 @@ +/** + * This module adds the Rayn provider to the real time data module + * The {@link module:modules/realTimeData} module is required + * The module will fetch real-time audience and context data from Rayn + * @module modules/raynRtdProvider + * @requires module:modules/realTimeData + */ + +import { MODULE_TYPE_RTD } from '../src/activities/modules.js'; +import { submodule } from '../src/hook.js'; +import { getStorageManager } from '../src/storageManager.js'; +import { deepAccess, deepSetValue, logError, logMessage, mergeDeep } from '../src/utils.js'; + +const MODULE_NAME = 'realTimeData'; +const SUBMODULE_NAME = 'rayn'; +const RAYN_TCF_ID = 1220; +const LOG_PREFIX = 'RaynJS: '; +export const SEGMENTS_RESOLVER = 'rayn.io'; +export const RAYN_LOCAL_STORAGE_KEY = 'rayn-segtax'; + +const defaultIntegration = { + iabAudienceCategories: { + v1_1: { + tier: 6, + enabled: true, + }, + }, + iabContentCategories: { + v3_0: { + tier: 4, + enabled: true, + }, + v2_2: { + tier: 4, + enabled: true, + }, + }, +}; + +export const storage = getStorageManager({ + moduleType: MODULE_TYPE_RTD, + moduleName: SUBMODULE_NAME, +}); + +function init(moduleConfig, userConsent) { + return true; +} + +/** + * Create and return ORTB2 object with segtax and segments + * @param {number} segtax + * @param {Array} segmentIds + * @param {number} maxTier + * @return {Array} + */ +export function generateOrtbDataObject(segtax, segment, maxTier) { + const segmentIds = []; + + try { + Object.keys(segment).forEach(tier => { + if (tier <= maxTier) { + segmentIds.push(...segment[tier].map((id) => { + return { id }; + })) + } + }); + } catch (error) { + logError(LOG_PREFIX, error); + } + + return { + name: SEGMENTS_RESOLVER, + ext: { + segtax, + }, + segment: segmentIds, + }; +} + +/** + * Generates checksum + * @param {string} url + * @returns {string} + */ +export function generateChecksum(stringValue) { + const l = stringValue.length; + let i = 0; + let h = 0; + if (l > 0) while (i < l) h = ((h << 5) - h + stringValue.charCodeAt(i++)) | 0; + return h.toString(); +}; + +/** + * Gets an object of segtax and segment IDs from LocalStorage + * or return the default value provided. + * @param {string} key + * @return {Object} + */ +export function readSegments(key) { + try { + return JSON.parse(storage.getDataFromLocalStorage(key)); + } catch (error) { + logError(LOG_PREFIX, error); + return null; + } +} + +/** + * Pass segments to configured bidders, using ORTB2 + * @param {Object} bidConfig + * @param {Array} bidders + * @param {Object} integrationConfig + * @param {Array} segments + * @return {void} + */ +export function setSegmentsAsBidderOrtb2(bidConfig, bidders, integrationConfig, segments, checksum) { + const raynOrtb2 = {}; + + const raynContentData = []; + if (integrationConfig.iabContentCategories.v2_2.enabled && segments[checksum] && segments[checksum][6]) { + raynContentData.push(generateOrtbDataObject(6, segments[checksum][6], integrationConfig.iabContentCategories.v2_2.tier)); + } + if (integrationConfig.iabContentCategories.v3_0.enabled && segments[checksum] && segments[checksum][7]) { + raynContentData.push(generateOrtbDataObject(7, segments[checksum][7], integrationConfig.iabContentCategories.v3_0.tier)); + } + if (raynContentData.length > 0) { + deepSetValue(raynOrtb2, 'site.content.data', raynContentData); + } + + if (integrationConfig.iabAudienceCategories.v1_1.enabled && segments[4]) { + const raynUserData = [generateOrtbDataObject(4, segments[4], integrationConfig.iabAudienceCategories.v1_1.tier)]; + deepSetValue(raynOrtb2, 'user.data', raynUserData); + } + + if (!bidders || bidders.length === 0 || !segments || Object.keys(segments).length <= 0) { + mergeDeep(bidConfig?.ortb2Fragments?.global, raynOrtb2); + } else { + const bidderConfig = Object.fromEntries( + bidders.map((bidder) => [bidder, raynOrtb2]), + ); + mergeDeep(bidConfig?.ortb2Fragments?.bidder, bidderConfig); + } +} + +/** + * Real-time data retrieval from Rayn + * @param {Object} reqBidsConfigObj + * @param {function} callback + * @param {Object} config + * @param {Object} userConsent + * @return {void} + */ +function alterBidRequests(reqBidsConfigObj, callback, config, userConsent) { + try { + const checksum = generateChecksum(window.location.href); + + const segments = readSegments(RAYN_LOCAL_STORAGE_KEY); + + const bidders = deepAccess(config, 'params.bidders'); + const integrationConfig = mergeDeep(defaultIntegration, deepAccess(config, 'params.integration')); + + if (segments && Object.keys(segments).length > 0 && ( + segments[checksum] || (segments[4] && + integrationConfig.iabAudienceCategories.v1_1.enabled && + !integrationConfig.iabContentCategories.v2_2.enabled && + !integrationConfig.iabContentCategories.v3_0.enabled + ) + )) { + logMessage(LOG_PREFIX, `Segtax data from localStorage: ${JSON.stringify(segments)}`); + setSegmentsAsBidderOrtb2(reqBidsConfigObj, bidders, integrationConfig, segments, checksum); + callback(); + } else if (window.raynJS && typeof window.raynJS.getSegtax === 'function') { + window.raynJS.getSegtax().then((segtaxData) => { + logMessage(LOG_PREFIX, `Segtax data from RaynJS: ${JSON.stringify(segtaxData)}`); + setSegmentsAsBidderOrtb2(reqBidsConfigObj, bidders, integrationConfig, segtaxData, checksum); + callback(); + }).catch((error) => { + logError(LOG_PREFIX, error); + callback(); + }); + } else { + logMessage(LOG_PREFIX, 'No segtax data'); + callback(); + } + } catch (error) { + logError(LOG_PREFIX, error); + callback(); + } +} + +export const raynSubmodule = { + name: SUBMODULE_NAME, + init: init, + getBidRequestData: alterBidRequests, + gvlid: RAYN_TCF_ID, +}; + +submodule(MODULE_NAME, raynSubmodule); diff --git a/modules/raynRtdProvider.md b/modules/raynRtdProvider.md new file mode 100644 index 00000000000..8d888a18d1f --- /dev/null +++ b/modules/raynRtdProvider.md @@ -0,0 +1,118 @@ +--- +layout: page_v2 +title: Rayn RTD Provider +display_name: Rayn Real Time Data Module +description: Rayn Real Time Data module appends privacy preserving enhanced contextual categories and audiences. Moments matter. +page_type: module +module_type: rtd +module_code: raynRtdProvider +enable_download: true +vendor_specific: true +sidebarType: 1 +--- + +# Rayn Real-time Data Submodule + +Rayn is a privacy preserving, data platform. We turn content into context, into audiences. For Personalisation, Monetisation and Insights. This module reads contextual categories and audience cohorts from RaynJS (via localStorage) and passes them to the bid-stream. + +## Integration + +To install the module, follow these instructions: + +Step 1: Prepare the base Prebid file +Compile the Rayn RTD module (`raynRtdProvider`) into your Prebid build along with the parent RTD Module (`rtdModule`). From the command line, run gulp build `gulp build --modules=rtdModule,raynRtdProvider` + +Step 2: Set configuration +Enable Rayn RTD Module using pbjs.setConfig. Example is provided in the Configuration section. See the **Parameter Description** for more detailed information of the configuration parameters. + +### Configuration + +This module is configured as part of the realTimeData.dataProviders object. + +Example format: + +```js +pbjs.setConfig( + // ... + realTimeData: { + auctionDelay: 1000, + dataProviders: [ + { + name: "rayn", + waitForIt: true, + params: { + bidders: ["appnexus", "pubmatic"], + integration: { + iabAudienceCategories: { + v1_1: { + tier: 6, + enabled: true, + }, + }, + iabContentCategories: { + v3_0: { + tier: 4, + enabled: true, + }, + v2_2: { + tier: 4, + enabled: true, + }, + }, + } + } + } + ] + } + // ... +} +``` + +## Parameter Description + +The parameters below provide configurability for general behaviours of the RTD submodule, as well as enabling settings for specific use cases mentioned above (e.g. tiers and bidders). + +### Parameters + +{: .table .table-bordered .table-striped } +| Name | Type | Description | Notes | +| :---------------------------------------------------- | :-------- | :----------------------------------------------------------------------------------- | :---- | +| name | `String` | RTD sub module name | Always "rayn" | +| waitForIt | `Boolean` | Required to ensure that the auction is delayed for the module to respond | Optional. Defaults to false but recommended to true | +| params | `Object` | || +| params.bidders | `Array` | Bidders with which to share context and segment information | Optional. In case no bidder is specified Rayn will append data for all bidders | +| params.integration | `Object` | Controls which IAB taxonomy should be used and up to which category tier | Optional. In case it's not defined, all supported IAB taxonomies and all category tiers will be used | +| params.integration.iabAudienceCategories | `Object` | || +| params.integration.iabAudienceCategories.v1_1 | `Object` | || +| params.integration.iabAudienceCategories.v1_1.enabled | `Boolean` | Controls if IAB Audience Taxonomy v1.1 will be used | Optional. Enabled by default | +| params.integration.iabAudienceCategories.v1_1.tier | `Number` | Controls up to which IAB Audience Taxonomy v1.1 Category tier will be used | Optional. Tier 6 by default | +| params.integration.iabContentCategories | `Object` | || +| params.integration.iabContentCategories.v3_0 | `Object` | || +| params.integration.iabContentCategories.v3_0.enabled | `Boolean` | Controls if IAB Content Taxonomy v3.0 will be used | Optional. Enabled by default | +| params.integration.iabContentCategories.v3_0.tier | `Number` | Controls up to which IAB Content Taxonomy v3.0 Category tier will be used | Optional. Tier 4 by default | +| params.integration.iabContentCategories.v2_2 | `Object` | || +| params.integration.iabContentCategories.v2_2.enabled | `Boolean` | Controls if IAB Content Taxonomy v2.2 will be used | Optional. Enabled by default | +| params.integration.iabContentCategories.v2_2.tier | `Number` | Controls up to which IAB Content Taxonomy v2.2 Category tier will be used | Optional. Tier 4 by default | + +Please note that raynRtdProvider should be integrated into the website along with RaynJS. + +## Testing + +To view an example of the on page setup: + +```bash +gulp serve-fast --modules=rtdModule,raynRtdProvider,appnexusBidAdapter +``` + +Then in your browser access: [http://localhost:9999/integrationExamples/gpt/raynRtdProvider_example.html](http://localhost:9999/integrationExamples/gpt/raynRtdProvider_example.html) + +Run the unit tests, just on the Rayn RTD module test file: + +```bash +gulp test --file "test/spec/modules/raynRtdProvider_spec.js" +``` + +## Support + +If you require further assistance or are interested in discussing the module functionality please reach out to [support@rayn.io](mailto:support@rayn.io). +You are also able to find more examples and other integration routes on the Rayn documentation site. diff --git a/test/spec/modules/raynRtdProvider_spec.js b/test/spec/modules/raynRtdProvider_spec.js new file mode 100644 index 00000000000..69ea316e8b5 --- /dev/null +++ b/test/spec/modules/raynRtdProvider_spec.js @@ -0,0 +1,308 @@ +import * as raynRTD from 'modules/raynRtdProvider.js'; +import { config } from 'src/config.js'; +import * as utils from 'src/utils.js'; + +const TEST_CHECKSUM = '-1135402174'; +const TEST_URL = 'http://localhost:9876/context.html'; +const TEST_SEGMENTS = { + [TEST_CHECKSUM]: { + 7: { + 2: ['51', '246', '652', '48', '324'] + } + } +}; + +const RTD_CONFIG = { + auctionDelay: 250, + dataProviders: [ + { + name: 'rayn', + waitForIt: true, + params: { + bidders: [], + integration: { + iabAudienceCategories: { + v1_1: { + tier: 6, + enabled: true, + }, + }, + iabContentCategories: { + v3_0: { + tier: 4, + enabled: true, + }, + v2_2: { + tier: 4, + enabled: true, + }, + }, + } + }, + }, + ], +}; + +describe('rayn RTD Submodule', function () { + let getDataFromLocalStorageStub; + + beforeEach(function () { + config.resetConfig(); + getDataFromLocalStorageStub = sinon.stub( + raynRTD.storage, + 'getDataFromLocalStorage', + ); + }); + + afterEach(function () { + getDataFromLocalStorageStub.restore(); + }); + + describe('Initialize module', function () { + it('should initialize and return true', function () { + expect(raynRTD.raynSubmodule.init(RTD_CONFIG.dataProviders[0])).to.equal( + true, + ); + }); + }); + + describe('Generate ortb data object', function () { + it('should set empty segment array', function () { + expect(raynRTD.generateOrtbDataObject(7, 'invalid', 2).segment).to.be.instanceOf(Array).and.lengthOf(0); + }); + + it('should set segment array', function () { + const expectedSegmentIdsMap = TEST_SEGMENTS[TEST_CHECKSUM][7][2].map((id) => { + return { id }; + }); + expect(raynRTD.generateOrtbDataObject(7, TEST_SEGMENTS[TEST_CHECKSUM][7], 4)).to.deep.equal({ + name: raynRTD.SEGMENTS_RESOLVER, + ext: { + segtax: 7, + }, + segment: expectedSegmentIdsMap, + }); + }); + }); + + describe('Generate checksum', function () { + it('should generate checksum', function () { + expect(raynRTD.generateChecksum(TEST_URL)).to.equal(TEST_CHECKSUM); + }); + }); + + describe('Get segments', function () { + it('should get segments from local storage', function () { + getDataFromLocalStorageStub + .withArgs(raynRTD.RAYN_LOCAL_STORAGE_KEY) + .returns(JSON.stringify(TEST_SEGMENTS)); + + const segments = raynRTD.readSegments(raynRTD.RAYN_LOCAL_STORAGE_KEY); + + expect(segments).to.deep.equal(TEST_SEGMENTS); + }); + + it('should return null if unable to read and parse data from local storage', function () { + const testString = 'test'; + getDataFromLocalStorageStub + .withArgs(raynRTD.RAYN_LOCAL_STORAGE_KEY) + .returns(testString); + + const segments = raynRTD.readSegments(raynRTD.RAYN_LOCAL_STORAGE_KEY); + + expect(segments).to.equal(null); + }); + }); + + describe('Set segments as bidder ortb2', function () { + it('should set global ortb2 config', function () { + const globalOrtb2 = {}; + const bidders = RTD_CONFIG.dataProviders[0].params.bidders; + const integrationConfig = RTD_CONFIG.dataProviders[0].params.integration; + + raynRTD.setSegmentsAsBidderOrtb2({ ortb2Fragments: { global: globalOrtb2 } }, bidders, integrationConfig, TEST_SEGMENTS, TEST_CHECKSUM); + + TEST_SEGMENTS[TEST_CHECKSUM]['7']['2'].forEach((id) => { + expect(globalOrtb2.site.content.data[0].segment.find(segment => segment.id === id)).to.exist; + }) + }); + + it('should set bidder specific ortb2 config', function () { + RTD_CONFIG.dataProviders[0].params.bidders = ['appnexus']; + + const bidderOrtb2 = {}; + const bidders = RTD_CONFIG.dataProviders[0].params.bidders; + const integrationConfig = RTD_CONFIG.dataProviders[0].params.integration; + + raynRTD.setSegmentsAsBidderOrtb2({ ortb2Fragments: { bidder: bidderOrtb2 } }, bidders, integrationConfig, TEST_SEGMENTS, TEST_CHECKSUM); + + bidders.forEach((bidder) => { + const ortb2 = bidderOrtb2[bidder]; + TEST_SEGMENTS[TEST_CHECKSUM]['7']['2'].forEach((id) => { + expect(ortb2.site.content.data[0].segment.find(segment => segment.id === id)).to.exist; + }) + }); + }); + + it('should set bidder specific ortb2 config with all segments', function () { + TEST_SEGMENTS['4'] = { + 3: ['4', '17', '72', '612'] + }; + TEST_SEGMENTS[TEST_CHECKSUM]['6'] = { + 2: ['71', '313'], + 4: ['33', '145', '712'] + }; + + const bidderOrtb2 = {}; + const bidders = RTD_CONFIG.dataProviders[0].params.bidders; + const integrationConfig = RTD_CONFIG.dataProviders[0].params.integration; + + raynRTD.setSegmentsAsBidderOrtb2({ ortb2Fragments: { bidder: bidderOrtb2 } }, bidders, integrationConfig, TEST_SEGMENTS, TEST_CHECKSUM); + + bidders.forEach((bidder) => { + const ortb2 = bidderOrtb2[bidder]; + + TEST_SEGMENTS[TEST_CHECKSUM]['6']['2'].forEach((id) => { + expect(ortb2.site.content.data[0].segment.find(segment => segment.id === id)).to.exist; + }); + TEST_SEGMENTS[TEST_CHECKSUM]['6']['4'].forEach((id) => { + expect(ortb2.site.content.data[0].segment.find(segment => segment.id === id)).to.exist; + }); + TEST_SEGMENTS[TEST_CHECKSUM]['7']['2'].forEach((id) => { + expect(ortb2.site.content.data[1].segment.find(segment => segment.id === id)).to.exist; + }); + TEST_SEGMENTS['4']['3'].forEach((id) => { + expect(ortb2.user.data[0].segment.find(segment => segment.id === id)).to.exist; + }); + }); + }); + }); + + describe('Alter Bid Requests', function () { + it('should update reqBidsConfigObj and execute callback', function () { + const callbackSpy = sinon.spy(); + const logMessageSpy = sinon.spy(utils, 'logMessage'); + + getDataFromLocalStorageStub + .withArgs(raynRTD.RAYN_LOCAL_STORAGE_KEY) + .returns(JSON.stringify(TEST_SEGMENTS)); + + const reqBidsConfigObj = { ortb2Fragments: { bidder: {} } }; + + raynRTD.raynSubmodule.getBidRequestData(reqBidsConfigObj, callbackSpy, RTD_CONFIG); + + expect(callbackSpy.calledOnce).to.be.true; + expect(logMessageSpy.lastCall.lastArg).to.equal(`Segtax data from localStorage: ${JSON.stringify(TEST_SEGMENTS)}`); + + logMessageSpy.restore(); + }); + + it('should update reqBidsConfigObj and execute callback using user segments from localStorage', function () { + const callbackSpy = sinon.spy(); + const logMessageSpy = sinon.spy(utils, 'logMessage'); + const testSegments = { + 4: { + 3: ['4', '17', '72', '612'] + } + }; + + getDataFromLocalStorageStub + .withArgs(raynRTD.RAYN_LOCAL_STORAGE_KEY) + .returns(JSON.stringify(testSegments)); + + RTD_CONFIG.dataProviders[0].params.integration.iabContentCategories = { + v3_0: { + enabled: false, + }, + v2_2: { + enabled: false, + }, + }; + + const reqBidsConfigObj = { ortb2Fragments: { bidder: {} } }; + + raynRTD.raynSubmodule.getBidRequestData(reqBidsConfigObj, callbackSpy, RTD_CONFIG.dataProviders[0]); + + expect(callbackSpy.calledOnce).to.be.true; + expect(logMessageSpy.lastCall.lastArg).to.equal(`Segtax data from localStorage: ${JSON.stringify(testSegments)}`); + + logMessageSpy.restore(); + }); + + it('should update reqBidsConfigObj and execute callback using segments from raynJS', function () { + const callbackSpy = sinon.spy(); + const logMessageSpy = sinon.spy(utils, 'logMessage'); + + getDataFromLocalStorageStub + .withArgs(raynRTD.RAYN_LOCAL_STORAGE_KEY) + .returns(null); + + const reqBidsConfigObj = { ortb2Fragments: { bidder: {} } }; + + raynRTD.raynSubmodule.getBidRequestData(reqBidsConfigObj, callbackSpy, RTD_CONFIG.dataProviders[0]); + + expect(callbackSpy.calledOnce).to.be.true; + expect(logMessageSpy.lastCall.lastArg).to.equal(`No segtax data`); + + logMessageSpy.restore(); + }); + + it('should update reqBidsConfigObj and execute callback using audience from localStorage', function (done) { + const callbackSpy = sinon.spy(); + const logMessageSpy = sinon.spy(utils, 'logMessage'); + const testSegments = { + 6: { + 4: ['3', '27', '177'] + } + }; + + global.window.raynJS = { + getSegtax: function () { + return Promise.resolve(testSegments); + } + }; + + getDataFromLocalStorageStub + .withArgs(raynRTD.RAYN_LOCAL_STORAGE_KEY) + .returns(null); + + const reqBidsConfigObj = { ortb2Fragments: { bidder: {} } }; + + raynRTD.raynSubmodule.getBidRequestData(reqBidsConfigObj, callbackSpy, RTD_CONFIG.dataProviders[0]); + + setTimeout(() => { + expect(callbackSpy.calledOnce).to.be.true; + expect(logMessageSpy.lastCall.lastArg).to.equal(`Segtax data from RaynJS: ${JSON.stringify(testSegments)}`); + logMessageSpy.restore(); + done(); + }, 0) + }); + + it('should execute callback if log error', function (done) { + const callbackSpy = sinon.spy(); + const logErrorSpy = sinon.spy(utils, 'logError'); + const rejectError = 'Error'; + + global.window.raynJS = { + getSegtax: function () { + return Promise.reject(rejectError); + } + }; + + getDataFromLocalStorageStub + .withArgs(raynRTD.RAYN_LOCAL_STORAGE_KEY) + .returns(null); + + const reqBidsConfigObj = { ortb2Fragments: { bidder: {} } }; + + raynRTD.raynSubmodule.getBidRequestData(reqBidsConfigObj, callbackSpy, RTD_CONFIG.dataProviders[0]); + + setTimeout(() => { + expect(callbackSpy.calledOnce).to.be.true; + expect(logErrorSpy.lastCall.lastArg).to.equal(rejectError); + logErrorSpy.restore(); + done(); + }, 0) + }); + }); +}); From d8c2ffde7d84229aff3e62ccc1c59d650cfc22d9 Mon Sep 17 00:00:00 2001 From: matthieularere-msq <63732822+matthieularere-msq@users.noreply.github.com> Date: Wed, 7 Feb 2024 17:30:00 +0100 Subject: [PATCH 225/246] mediasquare Bid Adapter: add DSA support (#11070) * mediasquare Bid Adapter: add DSA support * mediasquare Bid Adapter: add DSA support --- modules/mediasquareBidAdapter.js | 2 ++ .../modules/mediasquareBidAdapter_spec.js | 26 +++++++++++++++++++ 2 files changed, 28 insertions(+) diff --git a/modules/mediasquareBidAdapter.js b/modules/mediasquareBidAdapter.js index 550c715e741..1ec05d17eef 100644 --- a/modules/mediasquareBidAdapter.js +++ b/modules/mediasquareBidAdapter.js @@ -95,6 +95,7 @@ export const spec = { } else if (bidderRequest.hasOwnProperty('bids') && typeof bidderRequest.bids == 'object' && bidderRequest.bids.length > 0 && bidderRequest.bids[0].hasOwnProperty('userId')) { payload.userId = bidderRequest.bids[0].userId; } + if (bidderRequest.ortb2?.regs?.ext?.dsa) { payload.dsa = bidderRequest.ortb2.regs.ext.dsa } }; if (test) { payload.debug = true; } const payloadString = JSON.stringify(payload); @@ -134,6 +135,7 @@ export const spec = { 'advertiserDomains': value['adomain'] } }; + if ('dsa' in value) { bidResponse.meta.dsa = value['dsa']; } let paramsToSearchFor = ['bidder', 'code', 'match', 'hasConsent', 'context', 'increment', 'ova']; paramsToSearchFor.forEach(param => { if (param in value) { diff --git a/test/spec/modules/mediasquareBidAdapter_spec.js b/test/spec/modules/mediasquareBidAdapter_spec.js index d7984c05967..6082ef65055 100644 --- a/test/spec/modules/mediasquareBidAdapter_spec.js +++ b/test/spec/modules/mediasquareBidAdapter_spec.js @@ -102,10 +102,34 @@ describe('MediaSquare bid adapter tests', function () { 'context': 'instream', 'increment': 1.0, 'ova': 'cleared', + 'dsa': { + 'behalf': 'some-behalf', + 'paid': 'some-paid', + 'transparency': [{ + 'domain': 'test.com', + 'dsaparams': [1, 2, 3] + }], + 'adrender': 1 + } }], }}; const DEFAULT_OPTIONS = { + ortb2: { + regs: { + ext: { + dsa: { + dsarequired: '1', + pubrender: '2', + datatopub: '3', + transparency: [{ + domain: 'test.com', + dsaparams: [1, 2, 3] + }] + } + } + } + }, gdprConsent: { gdprApplies: true, consentString: 'BOzZdA0OzZdA0AGABBENDJ-AAAAvh7_______9______9uz_Ov_v_f__33e8__9v_l_7_-___u_-33d4-_1vf99yfm1-7ftr3tp_87ues2_Xur__79__3z3_9pxP78k89r7337Mw_v-_v-b7JCPN_Y3v-8Kg', @@ -144,6 +168,7 @@ describe('MediaSquare bid adapter tests', function () { expect(requestContent.codes[0]).to.have.property('mediatypes').exist; expect(requestContent.codes[0]).to.have.property('floor').exist; expect(requestContent.codes[0].floor).to.deep.equal({}); + expect(requestContent).to.have.property('dsa'); const requestfloor = spec.buildRequests(FLOORS_PARAMS, DEFAULT_OPTIONS); const responsefloor = JSON.parse(requestfloor.data); expect(responsefloor.codes[0]).to.have.property('floor').exist; @@ -176,6 +201,7 @@ describe('MediaSquare bid adapter tests', function () { expect(bid.meta).to.exist; expect(bid.meta.advertiserDomains).to.exist; expect(bid.meta.advertiserDomains).to.have.lengthOf(1); + expect(bid.meta.dsa).to.exist; }); it('Verifies match', function () { const request = spec.buildRequests(DEFAULT_PARAMS, DEFAULT_OPTIONS); From 9f5e205a0ba50408b1682858fb906930e6771725 Mon Sep 17 00:00:00 2001 From: matthieularere-msq <63732822+matthieularere-msq@users.noreply.github.com> Date: Wed, 7 Feb 2024 17:32:06 +0100 Subject: [PATCH 226/246] PBjs Core : add ability to inject tracking in video (#10191) * add vast impression tracking * support additional context macro * fix spaces and singlequotes * remove 2494945CONTEXT2494945 macro * remove CONTEXT macro * do not update vastImpUrl anymore * add impression trackers in video cache * insert ony unique trackers * rename registerVastTrackers * rename arrayVastTrackers * trackers object change * check modules are allowed to add trackers based on isActivityAllowed * rename validVastTracker and add line breaks * removes duplicates verification in isValidVastTracker * changes in wrapURI + typo fix * requested changes * update function trackersToMap * using Set in trackers map * changes suggested by dgirardi * changes suggested by dgirardi * Update test/spec/video_spec.js Co-authored-by: Karim Mourra * add spaces --------- Co-authored-by: Karim Mourra --- libraries/vastTrackers/vastTrackers.js | 95 ++++++++++++++++++++++++ src/videoCache.js | 7 +- test/spec/libraries/vastTrackers_spec.js | 33 ++++++++ test/spec/videoCache_spec.js | 24 ++++-- 4 files changed, 151 insertions(+), 8 deletions(-) create mode 100644 libraries/vastTrackers/vastTrackers.js create mode 100644 test/spec/libraries/vastTrackers_spec.js diff --git a/libraries/vastTrackers/vastTrackers.js b/libraries/vastTrackers/vastTrackers.js new file mode 100644 index 00000000000..b4ae98aba57 --- /dev/null +++ b/libraries/vastTrackers/vastTrackers.js @@ -0,0 +1,95 @@ +import {addBidResponse} from '../../src/auction.js'; +import {VIDEO} from '../../src/mediaTypes.js'; +import {logError} from '../../src/utils.js'; +import {isActivityAllowed} from '../../src/activities/rules.js'; +import {ACTIVITY_REPORT_ANALYTICS} from '../../src/activities/activities.js'; +import {activityParams} from '../../src/activities/activityParams.js'; + +const vastTrackers = []; + +addBidResponse.before(function (next, adUnitcode, bidResponse, reject) { + if (FEATURES.VIDEO && bidResponse.mediaType === VIDEO) { + const vastTrackers = getVastTrackers(bidResponse); + if (vastTrackers) { + bidResponse.vastXml = insertVastTrackers(vastTrackers, bidResponse.vastXml); + const impTrackers = vastTrackers.get('impressions'); + if (impTrackers) { + bidResponse.vastImpUrl = [].concat(impTrackers).concat(bidResponse.vastImpUrl).filter(t => t); + } + } + } + next(adUnitcode, bidResponse, reject); +}); + +export function registerVastTrackers(moduleType, moduleName, trackerFn) { + if (typeof trackerFn === 'function') { + vastTrackers.push({'moduleType': moduleType, 'moduleName': moduleName, 'trackerFn': trackerFn}); + } +} + +export function insertVastTrackers(trackers, vastXml) { + const doc = new DOMParser().parseFromString(vastXml, 'text/xml'); + const wrappers = doc.querySelectorAll('VAST Ad Wrapper, VAST Ad InLine'); + try { + if (wrappers.length) { + wrappers.forEach(wrapper => { + if (trackers.get('impressions')) { + trackers.get('impressions').forEach(trackingUrl => { + const impression = doc.createElement('Impression'); + impression.appendChild(doc.createCDATASection(trackingUrl)); + wrapper.appendChild(impression); + }); + } + }); + vastXml = new XMLSerializer().serializeToString(doc); + } + } catch (error) { + logError('an error happened trying to insert trackers in vastXml'); + } + return vastXml; +} + +export function getVastTrackers(bid) { + let trackers = []; + vastTrackers.filter( + ({ + moduleType, + moduleName, + trackerFn + }) => isActivityAllowed(ACTIVITY_REPORT_ANALYTICS, activityParams(moduleType, moduleName)) + ).forEach(({trackerFn}) => { + let trackersToAdd = trackerFn(bid); + trackersToAdd.forEach(trackerToAdd => { + if (isValidVastTracker(trackers, trackerToAdd)) { + trackers.push(trackerToAdd); + } + }); + }); + const trackersMap = trackersToMap(trackers); + return (trackersMap.size ? trackersMap : null); +}; + +function isValidVastTracker(trackers, trackerToAdd) { + return trackerToAdd.hasOwnProperty('event') && trackerToAdd.hasOwnProperty('url'); +} + +function trackersToMap(trackers) { + return trackers.reduce((map, {url, event}) => { + !map.has(event) && map.set(event, new Set()); + map.get(event).add(url); + return map; + }, new Map()); +} + +export function addImpUrlToTrackers(bid, trackersMap) { + if (bid.vastImpUrl) { + if (!trackersMap) { + trackersMap = new Map(); + } + if (!trackersMap.get('impressions')) { + trackersMap.set('impressions', new Set()); + } + trackersMap.get('impressions').add(bid.vastImpUrl); + } + return trackersMap; +} diff --git a/src/videoCache.js b/src/videoCache.js index 88fc27625fd..ce03f2f624e 100644 --- a/src/videoCache.js +++ b/src/videoCache.js @@ -42,17 +42,18 @@ const ttlBufferInSeconds = 15; * @param {string} impUrl An impression tracker URL for the delivery of the video ad * @return A VAST URL which loads XML from the given URI. */ -function wrapURI(uri, impUrl) { +function wrapURI(uri, impTrackerURLs) { + impTrackerURLs = impTrackerURLs && (Array.isArray(impTrackerURLs) ? impTrackerURLs : [impTrackerURLs]); // Technically, this is vulnerable to cross-script injection by sketchy vastUrl bids. // We could make sure it's a valid URI... but since we're loading VAST XML from the // URL they provide anyway, that's probably not a big deal. - let vastImp = (impUrl) ? `` : ``; + let impressions = impTrackerURLs ? impTrackerURLs.map(trk => ``).join('') : ''; return ` prebid.org wrapper - ${vastImp} + ${impressions} diff --git a/test/spec/libraries/vastTrackers_spec.js b/test/spec/libraries/vastTrackers_spec.js new file mode 100644 index 00000000000..3849ea75b02 --- /dev/null +++ b/test/spec/libraries/vastTrackers_spec.js @@ -0,0 +1,33 @@ +import {addImpUrlToTrackers, getVastTrackers, insertVastTrackers, registerVastTrackers} from 'libraries/vastTrackers/vastTrackers.js'; +import {MODULE_TYPE_ANALYTICS} from '../../../src/activities/modules.js'; + +describe('vast trackers', () => { + it('insert into tracker list', function() { + let trackers = getVastTrackers({'cpm': 1.0}); + if (!trackers || !trackers.get('impressions')) { + registerVastTrackers(MODULE_TYPE_ANALYTICS, 'test', function(bidResponse) { + return [ + {'event': 'impressions', 'url': `https://vasttracking.mydomain.com/vast?cpm=${bidResponse.cpm}`} + ]; + }); + } + trackers = getVastTrackers({'cpm': 1.0}); + expect(trackers).to.be.a('map'); + expect(trackers.get('impressions')).to.exists; + expect(trackers.get('impressions').has('https://vasttracking.mydomain.com/vast?cpm=1')).to.be.true; + }); + + it('insert trackers in vastXml', function() { + const trackers = getVastTrackers({'cpm': 1.0}); + let vastXml = ''; + vastXml = insertVastTrackers(trackers, vastXml); + expect(vastXml).to.equal(''); + }); + + it('test addImpUrlToTrackers', function() { + const trackers = addImpUrlToTrackers({'vastImpUrl': 'imptracker.com'}, getVastTrackers({'cpm': 1.0})); + expect(trackers).to.be.a('map'); + expect(trackers.get('impressions')).to.exists; + expect(trackers.get('impressions').has('imptracker.com')).to.be.true; + }); +}) diff --git a/test/spec/videoCache_spec.js b/test/spec/videoCache_spec.js index c746fdd2afd..fc6e71779cb 100644 --- a/test/spec/videoCache_spec.js +++ b/test/spec/videoCache_spec.js @@ -1,10 +1,10 @@ import chai from 'chai'; -import { getCacheUrl, store } from 'src/videoCache.js'; -import { config } from 'src/config.js'; -import { server } from 'test/mocks/xhr.js'; +import {getCacheUrl, store} from 'src/videoCache.js'; +import {config} from 'src/config.js'; +import {server} from 'test/mocks/xhr.js'; import {auctionManager} from '../../src/auctionManager.js'; import {AuctionIndex} from '../../src/auctionIndex.js'; -import { batchingCache } from '../../src/auction.js'; +import {batchingCache} from '../../src/auction.js'; const should = chai.should(); @@ -127,7 +127,7 @@ describe('The video cache', function () { prebid.org wrapper - + @@ -149,6 +149,20 @@ describe('The video cache', function () { assertRequestMade({ vastUrl: 'my-mock-url.com', vastImpUrl: 'imptracker.com', ttl: 25 }, expectedValue) }); + it('should include multiple vastImpUrl when it\'s an array', function() { + const expectedValue = ` + + + prebid.org wrapper + + + + + + `; + assertRequestMade({ vastUrl: 'my-mock-url.com', vastImpUrl: ['https://vasttracking.mydomain.com/vast?cpm=1.2', 'imptracker.com'], ttl: 25, cpm: 1.2 }, expectedValue) + }); + it('should make the expected request when store() is called on an ad with vastXml', function () { const vastXml = ''; assertRequestMade({ vastXml: vastXml, ttl: 25 }, vastXml); From 92703399478d918db90ed26a6772e8e4e4cfbeac Mon Sep 17 00:00:00 2001 From: Jurij Sinickij Date: Wed, 7 Feb 2024 18:34:18 +0200 Subject: [PATCH 227/246] Adform Bid Adapter: add DSA support (#11066) --- modules/adfBidAdapter.js | 9 +++- test/spec/modules/adfBidAdapter_spec.js | 63 ++++++++++++++++++++++++- 2 files changed, 70 insertions(+), 2 deletions(-) diff --git a/modules/adfBidAdapter.js b/modules/adfBidAdapter.js index e5b40f66176..0484c383762 100644 --- a/modules/adfBidAdapter.js +++ b/modules/adfBidAdapter.js @@ -64,6 +64,7 @@ export const spec = { const cur = currency && [ currency ]; const eids = setOnAny(validBidRequests, 'userIdAsEids'); const schain = setOnAny(validBidRequests, 'schain'); + const dsa = commonFpd.regs?.ext?.dsa; const imp = validBidRequests.map((bid, id) => { bid.netRevenue = pt; @@ -179,6 +180,10 @@ export const spec = { deepSetValue(request, 'source.ext.schain', schain); } + if (dsa) { + deepSetValue(request, 'regs.ext.dsa', dsa); + } + return { method: 'POST', url: 'https://' + adxDomain + '/adx/openrtb', @@ -201,6 +206,7 @@ export const spec = { const bidResponse = bidResponses[id]; if (bidResponse) { const mediaType = deepAccess(bidResponse, 'ext.prebid.type'); + const dsa = deepAccess(bidResponse, 'ext.dsa'); const result = { requestId: bid.bidId, cpm: bidResponse.price, @@ -214,7 +220,8 @@ export const spec = { dealId: bidResponse.dealid, meta: { mediaType, - advertiserDomains: bidResponse.adomain + advertiserDomains: bidResponse.adomain, + dsa } }; diff --git a/test/spec/modules/adfBidAdapter_spec.js b/test/spec/modules/adfBidAdapter_spec.js index c1acff522c0..5612af8094c 100644 --- a/test/spec/modules/adfBidAdapter_spec.js +++ b/test/spec/modules/adfBidAdapter_spec.js @@ -142,6 +142,49 @@ describe('Adf adapter', function () { assert.equal(request.user, undefined); assert.equal(request.regs, undefined); }); + + it('should transfer DSA info', function () { + let validBidRequests = [ { bidId: 'bidId', params: { siteId: 'siteId' } } ]; + + let request = JSON.parse( + spec.buildRequests(validBidRequests, { + refererInfo: { page: 'page' }, + ortb2: { + regs: { + ext: { + dsa: { + dsarequired: '1', + pubrender: '2', + datatopub: '3', + transparency: [ + { + domain: 'test.com', + dsaparams: [1, 2, 3] + } + ] + } + } + } + } + }).data + ); + + assert.deepEqual(request.regs, { + ext: { + dsa: { + dsarequired: '1', + pubrender: '2', + datatopub: '3', + transparency: [ + { + domain: 'test.com', + dsaparams: [1, 2, 3] + } + ] + } + } + }); + }); }); it('should add test and is_debug to request, if test is set in parameters', function () { @@ -1007,7 +1050,16 @@ describe('Adf adapter', function () { adomain: [ 'demo.com' ], ext: { prebid: { - type: 'native' + type: 'native', + }, + dsa: { + behalf: 'some-behalf', + paid: 'some-paid', + transparency: [{ + domain: 'test.com', + dsaparams: [1, 2, 3] + }], + adrender: 1 } } } @@ -1070,6 +1122,15 @@ describe('Adf adapter', function () { 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].meta.dsa, { + behalf: 'some-behalf', + paid: 'some-paid', + transparency: [{ + domain: 'test.com', + dsaparams: [1, 2, 3] + }], + adrender: 1 + }); assert.deepEqual(bids[0].dealId, 'deal-id'); }); it('should set correct native params', function () { From 796ad95946941d6f76d32bc55cbb94d9439aebc6 Mon Sep 17 00:00:00 2001 From: Saar Amrani Date: Wed, 7 Feb 2024 19:53:02 +0200 Subject: [PATCH 228/246] Vidazoo Bid Adapter: Implement onBidWon (#11057) * Add 'onBidWon' function and 'nurl' handling to vidazooBidAdapter * Add 'onBidWon' function and 'nurl' handling to vidazooBidAdapter * This update introduces the 'onBidWon' function to the vidazooBidAdapter module, and enhances handling for the 'nurl' property. --- modules/vidazooBidAdapter.js | 47 +++++++++++++- test/spec/modules/vidazooBidAdapter_spec.js | 70 +++++++++++++++++++++ 2 files changed, 115 insertions(+), 2 deletions(-) diff --git a/modules/vidazooBidAdapter.js b/modules/vidazooBidAdapter.js index b5323181c6c..59f3fe97969 100644 --- a/modules/vidazooBidAdapter.js +++ b/modules/vidazooBidAdapter.js @@ -1,4 +1,14 @@ -import {_each, deepAccess, isFn, parseSizesInput, parseUrl, uniques, isArray} from '../src/utils.js'; +import { + _each, + deepAccess, + isFn, + parseSizesInput, + parseUrl, + uniques, + isArray, + formatQS, + triggerPixel +} from '../src/utils.js'; import {registerBidder} from '../src/adapters/bidderFactory.js'; import {BANNER, VIDEO} from '../src/mediaTypes.js'; import {getStorageManager} from '../src/storageManager.js'; @@ -271,6 +281,7 @@ function interpretResponse(serverResponse, request) { height, currency, bidId, + nurl, advertiserDomains, metaData, mediaType = BANNER @@ -290,6 +301,10 @@ function interpretResponse(serverResponse, request) { ttl: exp || TTL_SECONDS, }; + if (nurl) { + response.nurl = nurl; + } + if (metaData) { Object.assign(response, { meta: metaData @@ -350,6 +365,33 @@ function getUserSyncs(syncOptions, responses, gdprConsent = {}, uspConsent = '', return syncs; } +/** + * @param {Bid} bid + */ +function onBidWon(bid) { + if (!bid.nurl) { + return; + } + const wonBid = { + adId: bid.adId, + creativeId: bid.creativeId, + auctionId: bid.auctionId, + transactionId: bid.transactionId, + adUnitCode: bid.adUnitCode, + cpm: bid.cpm, + currency: bid.currency, + originalCpm: bid.originalCpm, + originalCurrency: bid.originalCurrency, + netRevenue: bid.netRevenue, + mediaType: bid.mediaType, + timeToRespond: bid.timeToRespond, + status: bid.status, + }; + const qs = formatQS(wonBid); + const url = bid.nurl + (bid.nurl.indexOf('?') === -1 ? '?' : '&') + qs; + triggerPixel(url); +} + export function hashCode(s, prefix = '_') { const l = s.length; let h = 0 @@ -445,7 +487,8 @@ export const spec = { isBidRequestValid, buildRequests, interpretResponse, - getUserSyncs + getUserSyncs, + onBidWon }; registerBidder(spec); diff --git a/test/spec/modules/vidazooBidAdapter_spec.js b/test/spec/modules/vidazooBidAdapter_spec.js index 864f2b8551c..bc5165c8d54 100644 --- a/test/spec/modules/vidazooBidAdapter_spec.js +++ b/test/spec/modules/vidazooBidAdapter_spec.js @@ -648,6 +648,14 @@ describe('VidazooBidAdapter', function () { expect(responses).to.have.length(1); expect(responses[0].ttl).to.equal(300); }); + + it('should add nurl if exists on response', function () { + const serverResponse = utils.deepClone(SERVER_RESPONSE); + serverResponse.body.results[0].nurl = 'https://test.com/win-notice?test=123'; + const responses = adapter.interpretResponse(serverResponse, REQUEST); + expect(responses).to.have.length(1); + expect(responses[0].nurl).to.equal('https://test.com/win-notice?test=123'); + }); }); describe('user id system', function () { @@ -833,4 +841,66 @@ describe('VidazooBidAdapter', function () { expect(parsed).to.be.equal(value); }); }); + + describe('validate onBidWon', function () { + beforeEach(function () { + sinon.stub(utils, 'triggerPixel'); + }); + afterEach(function () { + utils.triggerPixel.restore(); + }); + + it('should call triggerPixel if nurl exists', function () { + const bid = { + adUnitCode: 'div-gpt-ad-12345-0', + adId: '2d52001cabd527', + auctionId: '1fdb5ff1b6eaa7', + transactionId: 'c881914b-a3b5-4ecf-ad9c-1c2f37c6aabf', + status: 'rendered', + timeToRespond: 100, + cpm: 0.8, + originalCpm: 0.8, + creativeId: '12610997325162499419', + currency: 'USD', + originalCurrency: 'USD', + height: 250, + mediaType: 'banner', + nurl: 'https://test.com/win-notice?test=123', + netRevenue: true, + requestId: '2d52001cabd527', + ttl: 30, + width: 300 + }; + adapter.onBidWon(bid); + expect(utils.triggerPixel.called).to.be.true; + + const url = utils.triggerPixel.args[0]; + + expect(url[0]).to.be.equal('https://test.com/win-notice?test=123&adId=2d52001cabd527&creativeId=12610997325162499419&auctionId=1fdb5ff1b6eaa7&transactionId=c881914b-a3b5-4ecf-ad9c-1c2f37c6aabf&adUnitCode=div-gpt-ad-12345-0&cpm=0.8¤cy=USD&originalCpm=0.8&originalCurrency=USD&netRevenue=true&mediaType=banner&timeToRespond=100&status=rendered'); + }); + + it('should not call triggerPixel if nurl does not exist', function () { + const bid = { + adUnitCode: 'div-gpt-ad-12345-0', + adId: '2d52001cabd527', + auctionId: '1fdb5ff1b6eaa7', + transactionId: 'c881914b-a3b5-4ecf-ad9c-1c2f37c6aabf', + status: 'rendered', + timeToRespond: 100, + cpm: 0.8, + originalCpm: 0.8, + creativeId: '12610997325162499419', + currency: 'USD', + originalCurrency: 'USD', + height: 250, + mediaType: 'banner', + netRevenue: true, + requestId: '2d52001cabd527', + ttl: 30, + width: 300 + }; + adapter.onBidWon(bid); + expect(utils.triggerPixel.called).to.be.false; + }); + }); }); From e410b28c3db64067d3282d6d179e832b6d315984 Mon Sep 17 00:00:00 2001 From: adquery <89853721+adquery@users.noreply.github.com> Date: Thu, 8 Feb 2024 15:00:29 +0100 Subject: [PATCH 229/246] Adquery Bid Adapter : IdSystem fix getId and decode method, added userSync with iframe type (#11019) * adquery/prebid_qid_work5 * adquery/prebid_qid_work4 --- modules/adqueryBidAdapter.js | 71 +++++++++++++++------ modules/adqueryIdSystem.js | 15 ++++- test/spec/modules/adqueryBidAdapter_spec.js | 30 ++++++++- test/spec/modules/adqueryIdSystem_spec.js | 12 ++-- 4 files changed, 101 insertions(+), 27 deletions(-) diff --git a/modules/adqueryBidAdapter.js b/modules/adqueryBidAdapter.js index d5a8ba6da84..bfcc56050fb 100644 --- a/modules/adqueryBidAdapter.js +++ b/modules/adqueryBidAdapter.js @@ -1,6 +1,6 @@ import {registerBidder} from '../src/adapters/bidderFactory.js'; import {BANNER} from '../src/mediaTypes.js'; -import {buildUrl, logInfo, parseSizesInput, triggerPixel} from '../src/utils.js'; +import {buildUrl, logInfo, logMessage, parseSizesInput, triggerPixel} from '../src/utils.js'; /** * @typedef {import('../src/adapters/bidderFactory.js').BidRequest} BidRequest @@ -14,7 +14,9 @@ const ADQUERY_GVLID = 902; const ADQUERY_BIDDER_CODE = 'adquery'; const ADQUERY_BIDDER_DOMAIN_PROTOCOL = 'https'; const ADQUERY_BIDDER_DOMAIN = 'bidder.adquery.io'; -const ADQUERY_USER_SYNC_DOMAIN = ADQUERY_BIDDER_DOMAIN_PROTOCOL + '://' + ADQUERY_BIDDER_DOMAIN + '/prebid/userSync?1=1'; +const ADQUERY_STATIC_DOMAIN_PROTOCOL = 'https'; +const ADQUERY_STATIC_DOMAIN = 'api.adquery.io'; +const ADQUERY_USER_SYNC_DOMAIN = ADQUERY_BIDDER_DOMAIN; const ADQUERY_DEFAULT_CURRENCY = 'PLN'; const ADQUERY_NET_REVENUE = true; const ADQUERY_TTL = 360; @@ -26,7 +28,6 @@ export const spec = { supportedMediaTypes: [BANNER], /** - * f * @param {object} bid * @return {boolean} */ @@ -41,10 +42,18 @@ export const spec = { */ buildRequests: (bidRequests, bidderRequest) => { const requests = []; + + let adqueryRequestUrl = buildUrl({ + protocol: ADQUERY_BIDDER_DOMAIN_PROTOCOL, + hostname: ADQUERY_BIDDER_DOMAIN, + pathname: '/prebid/bid', + // search: params + }); + for (let i = 0, len = bidRequests.length; i < len; i++) { const request = { method: 'POST', - url: ADQUERY_BIDDER_DOMAIN_PROTOCOL + '://' + ADQUERY_BIDDER_DOMAIN + '/prebid/bid', + url: adqueryRequestUrl, // ADQUERY_BIDDER_DOMAIN_PROTOCOL + '://' + ADQUERY_BIDDER_DOMAIN + '/prebid/bid', data: buildRequest(bidRequests[i], bidderRequest), options: { withCredentials: false, @@ -62,8 +71,8 @@ export const spec = { * @return {Bid[]} */ interpretResponse: (response, request) => { - logInfo(request); - logInfo(response); + logMessage(request); + logMessage(response); const res = response && response.body && response.body.data; let bidResponses = []; @@ -125,7 +134,6 @@ export const spec = { */ onBidWon: (bid) => { logInfo('onBidWon', bid); - const bidString = JSON.stringify(bid); let copyOfBid = JSON.parse(bidString); delete copyOfBid.ad; @@ -169,21 +177,48 @@ export const spec = { }); triggerPixel(adqueryRequestUrl); }, + /** + * Retrieves user synchronization URLs based on provided options and consents. + * + * @param {object} syncOptions - Options for synchronization. + * @param {object[]} serverResponses - Array of server responses. + * @param {object} gdprConsent - GDPR consent object. + * @param {object} uspConsent - USP consent object. + * @returns {object[]} - Array of synchronization URLs. + */ getUserSyncs: (syncOptions, serverResponses, gdprConsent, uspConsent) => { - let syncUrl = ADQUERY_USER_SYNC_DOMAIN; - 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}`; - } + logMessage('getUserSyncs', syncOptions, serverResponses, gdprConsent, uspConsent); + let syncData = { + 'gdpr': gdprConsent && gdprConsent.gdprApplies ? 1 : 0, + 'gdpr_consent': gdprConsent && gdprConsent.consentString ? gdprConsent.consentString : '', + 'ccpa_consent': uspConsent && uspConsent.uspConsent ? uspConsent.uspConsent : '', + }; + + if (window.qid) { // only for new users (new qid) + syncData.qid = window.qid; } - if (uspConsent && uspConsent.consentString) { - syncUrl += `&ccpa_consent=${uspConsent.consentString}`; + + let syncUrlObject = { + protocol: ADQUERY_BIDDER_DOMAIN_PROTOCOL, + hostname: ADQUERY_USER_SYNC_DOMAIN, + pathname: '/prebid/userSync', + search: syncData + }; + + if (syncOptions.iframeEnabled) { + syncUrlObject.protocol = ADQUERY_STATIC_DOMAIN_PROTOCOL; + syncUrlObject.hostname = ADQUERY_STATIC_DOMAIN; + syncUrlObject.pathname = '/user-sync-iframe.html'; + + return [{ + type: 'iframe', + url: buildUrl(syncUrlObject) + }]; } + return [{ type: 'image', - url: syncUrl + url: buildUrl(syncUrlObject) }]; } }; @@ -205,7 +240,7 @@ function buildRequest(validBidRequests, bidderRequest) { // onetime User ID const ramdomValues = Array.from(window.crypto.getRandomValues(new Uint32Array(4))); userId = ramdomValues.map(val => val.toString(36)).join('').substring(0, 20); - logInfo('generated onetime User ID: ', userId); + logMessage('generated onetime User ID: ', userId); window.qid = userId; } diff --git a/modules/adqueryIdSystem.js b/modules/adqueryIdSystem.js index eb00011593d..43795b3caba 100644 --- a/modules/adqueryIdSystem.js +++ b/modules/adqueryIdSystem.js @@ -8,7 +8,7 @@ import {ajax} from '../src/ajax.js'; import {getStorageManager} from '../src/storageManager.js'; import {submodule} from '../src/hook.js'; -import {isFn, isPlainObject, isStr, logError, logInfo} from '../src/utils.js'; +import {isFn, isPlainObject, isStr, logError, logInfo, logMessage} from '../src/utils.js'; import {MODULE_TYPE_UID} from '../src/activities/modules.js'; /** @@ -66,7 +66,18 @@ export const adqueryIdSubmodule = { * @returns {IdResponse|undefined} */ getId(config) { - logInfo('adqueryIdSubmodule getId'); + logMessage('adqueryIdSubmodule getId'); + + let qid = storage.getDataFromLocalStorage('qid'); + + if (qid) { + return { + callback: function (callback) { + callback(qid); + } + } + } + if (!isPlainObject(config.params)) { config.params = {}; } diff --git a/test/spec/modules/adqueryBidAdapter_spec.js b/test/spec/modules/adqueryBidAdapter_spec.js index e9286329d57..b4aa0992732 100644 --- a/test/spec/modules/adqueryBidAdapter_spec.js +++ b/test/spec/modules/adqueryBidAdapter_spec.js @@ -155,11 +155,39 @@ describe('adqueryBidAdapter', function () { describe('getUserSyncs', function () { it('should return iframe sync', function () { - let sync = spec.getUserSyncs() + let sync = spec.getUserSyncs( + { + iframeEnabled: true, + pixelEnabled: true, + }, + {}, + { + consentString: 'ALL', + gdprApplies: true, + }, + {} + ) expect(sync.length).to.equal(1) expect(sync[0].type === 'iframe') expect(typeof sync[0].url === 'string') }) + it('should return image sync', function () { + let sync = spec.getUserSyncs( + { + iframeEnabled: false, + pixelEnabled: true, + }, + {}, + { + consentString: 'ALL', + gdprApplies: true, + }, + {} + ) + expect(sync.length).to.equal(1) + expect(sync[0].type === 'image') + expect(typeof sync[0].url === 'string') + }) it('Should return array of objects with proper sync config , include GDPR', function() { const syncData = spec.getUserSyncs({}, {}, { diff --git a/test/spec/modules/adqueryIdSystem_spec.js b/test/spec/modules/adqueryIdSystem_spec.js index 0a2cd60d89e..7952f23189e 100644 --- a/test/spec/modules/adqueryIdSystem_spec.js +++ b/test/spec/modules/adqueryIdSystem_spec.js @@ -38,23 +38,23 @@ describe('AdqueryIdSystem', function () { const callback = adqueryIdSubmodule.getId(config).callback; callback(callbackSpy); const request = server.requests[0]; - expect(request.url).to.contains(`https://bidder.adquery.io/prebid/qid?qid=`); - request.respond(200, {'Content-Type': 'application/json'}, JSON.stringify({qid: '6dd9eab7dfeab7df6dd9ea'})); - expect(callbackSpy.lastCall.lastArg).to.deep.equal('6dd9eab7dfeab7df6dd9ea'); + expect(request.url).to.contain(`https://bidder.adquery.io/prebid/qid`); + request.respond(200, { 'Content-Type': 'application/json' }, JSON.stringify({ qid: 'qid_string' })); + expect(callbackSpy.lastCall.lastArg).to.deep.equal('qid_string'); }); it('allows configurable id url', function () { const config = { params: { - url: 'https://another_bidder.adquery.io/qid' + url: 'https://bidder2.adquery.io' } }; const callbackSpy = sinon.spy(); const callback = adqueryIdSubmodule.getId(config).callback; callback(callbackSpy); const request = server.requests[0]; - expect(request.url).to.contains('https://another_bidder.adquery.io/qid'); - request.respond(200, {'Content-Type': 'application/json'}, JSON.stringify({qid: 'testqid'})); + expect(request.url).to.contains('https://bidder2.adquery.io'); + request.respond(200, { 'Content-Type': 'application/json' }, JSON.stringify({ qid: 'testqid' })); expect(callbackSpy.lastCall.lastArg).to.deep.equal('testqid'); }); }); From cd4717c5ea98bd4a24597417937109f93500a4c0 Mon Sep 17 00:00:00 2001 From: Malkov Mikhail Date: Thu, 8 Feb 2024 17:10:09 +0300 Subject: [PATCH 230/246] NextMillenium Bid Adapter : added support for the keywords parameter openrtb (#11018) * added support for gpp consent string * changed test for nextMillenniumBidAdapter * added some tests * added site.pagecat, site.content.cat and site.content.language to request * lint fix * formated code * formated code * formated code * pachage-lock with prebid * pachage-lock with prebid * formatted code * added device.sua, user.eids * formatted * fixed tests * fixed bug functio getSua * added support keywords * added support keywords - code style * changed test for otrb parameters --- modules/nextMillenniumBidAdapter.js | 5 ++++- .../modules/nextMillenniumBidAdapter_spec.js | 22 +++++++++++++++++++ 2 files changed, 26 insertions(+), 1 deletion(-) diff --git a/modules/nextMillenniumBidAdapter.js b/modules/nextMillenniumBidAdapter.js index 0af2129de61..d151523b265 100644 --- a/modules/nextMillenniumBidAdapter.js +++ b/modules/nextMillenniumBidAdapter.js @@ -50,6 +50,9 @@ const ALLOWED_ORTB2_PARAMETERS = [ 'site.content.cat', 'site.content.language', 'device.sua', + 'site.keywords', + 'site.content.keywords', + 'user.keywords', ]; const sendingDataStatistic = initSendingDataStatistic(); @@ -200,7 +203,7 @@ export const spec = { responses.forEach(response => { if (syncOptions.pixelEnabled) setPixelImages(response); if (syncOptions.iframeEnabled) setPixelIframes(response); - }) + }); } if (!pixels.length) { diff --git a/test/spec/modules/nextMillenniumBidAdapter_spec.js b/test/spec/modules/nextMillenniumBidAdapter_spec.js index 169ab7b01e4..b9871bbbe71 100644 --- a/test/spec/modules/nextMillenniumBidAdapter_spec.js +++ b/test/spec/modules/nextMillenniumBidAdapter_spec.js @@ -391,6 +391,28 @@ describe('nextMillenniumBidAdapterTests', () => { }}, }, + { + title: 'site.keywords, site.content.keywords and user.keywords', + data: { + postBody: {}, + ortb2: { + user: {keywords: 'key7,key8,key9'}, + site: { + keywords: 'key1,key2,key3', + content: {keywords: 'key4,key5,key6'}, + }, + }, + }, + + expected: { + user: {keywords: 'key7,key8,key9'}, + site: { + keywords: 'key1,key2,key3', + content: {keywords: 'key4,key5,key6'}, + }, + }, + }, + { title: 'only site.content.language', data: { From e41a1749fe03c5293ab7e41d2def3f4f782d404b Mon Sep 17 00:00:00 2001 From: contentexchange <87769951+contentexchange@users.noreply.github.com> Date: Thu, 8 Feb 2024 17:11:57 +0200 Subject: [PATCH 231/246] Contentexchange Bid Adapter: add gvlid (#11079) * add contentexchange bid adapter * fixes * fix * fix test * validate meta * fix * add GVLID --- modules/contentexchangeBidAdapter.js | 2 ++ 1 file changed, 2 insertions(+) diff --git a/modules/contentexchangeBidAdapter.js b/modules/contentexchangeBidAdapter.js index be5900407ea..a6aa9262061 100644 --- a/modules/contentexchangeBidAdapter.js +++ b/modules/contentexchangeBidAdapter.js @@ -7,6 +7,7 @@ import { convertOrtbRequestToProprietaryNative } from '../src/native.js'; const BIDDER_CODE = 'contentexchange'; const AD_URL = 'https://eu2.adnetwork.agency/pbjs'; const SYNC_URL = 'https://sync2.adnetwork.agency'; +const GVLID = 864; function isBidResponseValid (bid) { if (!bid.requestId || !bid.cpm || !bid.creativeId || @@ -88,6 +89,7 @@ function getBidFloor(bid) { export const spec = { code: BIDDER_CODE, + gvlid: GVLID, supportedMediaTypes: [BANNER, VIDEO, NATIVE], isBidRequestValid: (bid = {}) => { From fe4a2f65a130e7c3b50e02c99373ba15757cfa35 Mon Sep 17 00:00:00 2001 From: "Prebid.js automated release" Date: Thu, 8 Feb 2024 17:06:23 +0000 Subject: [PATCH 232/246] Prebid 8.35.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 4821d711887..fbf536395b5 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,6 +1,6 @@ { "name": "prebid.js", - "version": "8.35.0-pre", + "version": "8.35.0", "lockfileVersion": 2, "requires": true, "packages": { diff --git a/package.json b/package.json index 6741b36d415..e082bb64952 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "prebid.js", - "version": "8.35.0-pre", + "version": "8.35.0", "description": "Header Bidding Management Library", "main": "src/prebid.js", "scripts": { From 61ba3fe6e2a7ff68a890546bd001f489a959f87a Mon Sep 17 00:00:00 2001 From: "Prebid.js automated release" Date: Thu, 8 Feb 2024 17:06:24 +0000 Subject: [PATCH 233/246] Increment version to 8.36.0-pre --- package-lock.json | 2 +- package.json | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/package-lock.json b/package-lock.json index fbf536395b5..4717c12f7c4 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,6 +1,6 @@ { "name": "prebid.js", - "version": "8.35.0", + "version": "8.36.0-pre", "lockfileVersion": 2, "requires": true, "packages": { diff --git a/package.json b/package.json index e082bb64952..78e27a2a44a 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "prebid.js", - "version": "8.35.0", + "version": "8.36.0-pre", "description": "Header Bidding Management Library", "main": "src/prebid.js", "scripts": { From 35487453e78304ef1656f29d36f9764de4dfe98e Mon Sep 17 00:00:00 2001 From: Demetrio Girardi Date: Thu, 8 Feb 2024 10:55:36 -0800 Subject: [PATCH 234/246] Core: fix missing AD_RENDER_SUCCEDED for outstream renderers (#11073) --- src/adRendering.js | 6 ++- test/spec/unit/secureCreatives_spec.js | 56 ++++++++++++++++++++++---- 2 files changed, 53 insertions(+), 9 deletions(-) diff --git a/src/adRendering.js b/src/adRendering.js index f8fe0044f9b..37bbf20d432 100644 --- a/src/adRendering.js +++ b/src/adRendering.js @@ -19,7 +19,10 @@ const {AD_RENDER_FAILED, AD_RENDER_SUCCEEDED, STALE_RENDER, BID_WON} = constants */ export function emitAdRenderFail({ reason, message, bid, id }) { const data = { reason, message }; - if (bid) data.bid = bid; + if (bid) { + data.bid = bid; + data.adId = bid.adId; + } if (id) data.adId = id; logError(`Error rendering ad (id: ${id}): ${message}`); @@ -64,6 +67,7 @@ export function handleRender(renderFn, {adId, options, bidResponse, doc}) { // rendering for outstream safeframe if (isRendererRequired(renderer)) { executeRenderer(renderer, bidResponse, doc); + emitAdRenderSucceeded({doc, bid: bidResponse, id: adId}) } else if (adId) { if (mediaType === VIDEO) { emitAdRenderFail({ diff --git a/test/spec/unit/secureCreatives_spec.js b/test/spec/unit/secureCreatives_spec.js index 895bf03165a..75813245298 100644 --- a/test/spec/unit/secureCreatives_spec.js +++ b/test/spec/unit/secureCreatives_spec.js @@ -15,6 +15,16 @@ import {handleRender} from '../../../src/adRendering.js'; var CONSTANTS = require('src/constants.json'); describe('secureCreatives', () => { + let sandbox; + + beforeEach(() => { + sandbox = sinon.sandbox.create(); + }); + + afterEach(() => { + sandbox.restore(); + }); + function makeEvent(ev) { return Object.assign({origin: 'mock-origin', ports: []}, ev) } @@ -63,14 +73,44 @@ describe('secureCreatives', () => { } }); - it('does not invoke renderFn, but the renderer instead, if the ad has one', () => { - const renderer = { - url: 'some-custom-renderer', - render: sinon.spy() - } - handleRender(renderFn, {bidResponse: {renderer}}); - sinon.assert.notCalled(renderFn); - sinon.assert.called(renderer.render); + describe('when the ad has a renderer', () => { + let bidResponse; + beforeEach(() => { + sandbox.stub(events, 'emit'); + bidResponse = { + adId: 'mock-ad-id', + renderer: { + url: 'some-custom-renderer', + render: sinon.stub() + } + } + }); + + it('does not invoke renderFn, but the renderer instead', () => { + handleRender(renderFn, {bidResponse}); + sinon.assert.notCalled(renderFn); + sinon.assert.called(bidResponse.renderer.render); + }); + + it('emits AD_RENDER_SUCCEDED', () => { + handleRender(renderFn, {bidResponse}); + sinon.assert.calledWith(events.emit, CONSTANTS.EVENTS.AD_RENDER_SUCCEEDED, sinon.match({ + bid: bidResponse, + adId: bidResponse.adId + })); + }); + + it('emits AD_RENDER_FAILED', () => { + const err = new Error('error message'); + bidResponse.renderer.render.throws(err); + handleRender(renderFn, {bidResponse}); + sinon.assert.calledWith(events.emit, CONSTANTS.EVENTS.AD_RENDER_FAILED, sinon.match({ + bid: bidResponse, + adId: bidResponse.adId, + reason: CONSTANTS.AD_RENDER_FAILED_REASON.EXCEPTION, + message: err.message + })); + }) }); ['ad', 'adUrl'].forEach((prop) => { From f31058c131a3b8a6813e3b7fc494e9ebab29b12c Mon Sep 17 00:00:00 2001 From: Sajid Mahmood Date: Thu, 8 Feb 2024 13:56:19 -0500 Subject: [PATCH 235/246] IX Bid Adapter: support DSA fields [ADDR-2990] (#11069) Co-authored-by: Sajid Mahmood --- modules/ixBidAdapter.js | 31 ++++++ test/spec/modules/ixBidAdapter_spec.js | 141 ++++++++++++++++++++++++- 2 files changed, 171 insertions(+), 1 deletion(-) diff --git a/modules/ixBidAdapter.js b/modules/ixBidAdapter.js index 54cf3596dbb..a29c1a39bff 100644 --- a/modules/ixBidAdapter.js +++ b/modules/ixBidAdapter.js @@ -8,6 +8,9 @@ import { isEmpty, isFn, isInteger, + isNumber, + isStr, + isPlainObject, logError, logWarn, mergeDeep, @@ -520,6 +523,9 @@ function parseBid(rawBid, currency, bidRequest) { if (rawBid.adomain && rawBid.adomain.length > 0) { bid.meta.advertiserDomains = rawBid.adomain; } + if (rawBid.ext?.dsa) { + bid.meta.dsa = rawBid.ext.dsa + } return bid; } @@ -1212,6 +1218,7 @@ function addFPD(bidderRequest, r, fpd, site, user) { } } + // regulations from ortb2 if (fpd.hasOwnProperty('regs') && !bidderRequest.gppConsent) { if (fpd.regs.hasOwnProperty('gpp') && typeof fpd.regs.gpp == 'string') { deepSetValue(r, 'regs.gpp', fpd.regs.gpp) @@ -1220,6 +1227,30 @@ function addFPD(bidderRequest, r, fpd, site, user) { if (fpd.regs.hasOwnProperty('gpp_sid') && Array.isArray(fpd.regs.gpp_sid)) { deepSetValue(r, 'regs.gpp_sid', fpd.regs.gpp_sid) } + + if (fpd.regs.ext?.dsa) { + const pubDsaObj = fpd.regs.ext.dsa; + const dsaObj = {}; + ['dsarequired', 'pubrender', 'datatopub'].forEach((dsaKey) => { + if (isNumber(pubDsaObj[dsaKey])) { + dsaObj[dsaKey] = pubDsaObj[dsaKey]; + } + }); + + if (isArray(pubDsaObj.transparency)) { + const tpData = []; + pubDsaObj.transparency.forEach((tpObj) => { + if (isPlainObject(tpObj) && isStr(tpObj.domain) && tpObj.domain != '' && isArray(tpObj.dsaparams) && tpObj.dsaparams.every((v) => isNumber(v))) { + tpData.push(tpObj); + } + }); + if (tpData.length > 0) { + dsaObj.transparency = tpData; + } + } + + if (!isEmpty(dsaObj)) deepSetValue(r, 'regs.ext.dsa', dsaObj); + } } return r; diff --git a/test/spec/modules/ixBidAdapter_spec.js b/test/spec/modules/ixBidAdapter_spec.js index 05b401fe4db..7655868ffc3 100644 --- a/test/spec/modules/ixBidAdapter_spec.js +++ b/test/spec/modules/ixBidAdapter_spec.js @@ -625,6 +625,45 @@ describe('IndexexchangeAdapter', function () { ] }; + const DEFAULT_BANNER_BID_RESPONSE_WITH_DSA = { + cur: 'USD', + id: '11a22b33c44d', + seatbid: [ + { + bid: [ + { + crid: '12345', + adomain: ['www.abc.com'], + adid: '14851455', + impid: '1a2b3c4d', + cid: '3051266', + price: 100, + w: 300, + h: 250, + id: '1', + ext: { + dspid: 50, + pricelevel: '_100', + advbrandid: 303325, + advbrand: 'OECTA', + dsa: { + behalf: 'Advertiser', + paid: 'Advertiser', + transparency: [{ + domain: 'dsp1domain.com', + dsaparams: [1, 2] + }], + 'adrender': 1 + } + }, + adm: '' + } + ], + seat: '3970' + } + ] + }; + const DEFAULT_BANNER_BID_RESPONSE_WITHOUT_ADOMAIN = { cur: 'USD', id: '11a22b33c44d', @@ -1903,6 +1942,72 @@ describe('IndexexchangeAdapter', function () { expect(r.user.testProperty).to.be.undefined; }); + it('should set dsa field when defined', function () { + const dsa = { + dsarequired: 3, + pubrender: 0, + datatopub: 2, + transparency: [{ + domain: 'domain.com', + dsaparams: [1] + }] + } + const request = spec.buildRequests(DEFAULT_BANNER_VALID_BID, { ortb2: {regs: { + ext: { + dsa: deepClone(dsa) + } + } + }})[0]; + const r = extractPayload(request); + + expect(r.regs.ext.dsa.dsarequired).to.equal(dsa.dsarequired); + expect(r.regs.ext.dsa.pubrender).to.equal(dsa.pubrender); + expect(r.regs.ext.dsa.datatopub).to.equal(dsa.datatopub); + expect(r.regs.ext.dsa.transparency).to.be.an('array'); + expect(r.regs.ext.dsa.transparency).to.have.deep.members(dsa.transparency); + }); + it('should not set dsa fields when fields arent appropriately defined', function () { + const dsa = { + dsarequired: '3', + pubrender: '0', + datatopub: '2', + transparency: 20 + } + const request = spec.buildRequests(DEFAULT_BANNER_VALID_BID, { ortb2: {regs: { + ext: { + dsa: deepClone(dsa) + } + } + }})[0]; + const r = extractPayload(request); + + expect(r.regs).to.be.undefined; + }); + it('should not set dsa transparency when fields arent appropriately defined', function () { + const dsa = { + transparency: [{ + domain: 3, + dsaparams: [1] + }, + { + domain: 'domain.com', + dsaparams: 'params' + }, + { + domain: 'domain.com', + dsaparams: ['1'] + }] + } + const request = spec.buildRequests(DEFAULT_BANNER_VALID_BID, { ortb2: {regs: { + ext: { + dsa: deepClone(dsa) + } + } + }})[0]; + const r = extractPayload(request); + + expect(r.regs).to.be.undefined; + }); it('should set gpp and gpp_sid field when defined', function () { const request = spec.buildRequests(DEFAULT_BANNER_VALID_BID, { ortb2: {regs: {gpp: 'gpp', gpp_sid: [1]}} })[0]; const r = extractPayload(request); @@ -1911,7 +2016,7 @@ describe('IndexexchangeAdapter', function () { expect(r.regs.gpp_sid).to.be.an('array'); expect(r.regs.gpp_sid).to.include(1); }); - it('should not set gpp and gpp_sid field when not defined', function () { + it('should not set gpp, gpp_sid and dsa field when not defined', function () { const request = spec.buildRequests(DEFAULT_BANNER_VALID_BID, { ortb2: {regs: {}} })[0]; const r = extractPayload(request); @@ -3529,6 +3634,40 @@ describe('IndexexchangeAdapter', function () { expect(result[0]).to.deep.equal(expectedParse[0]); }); + it('should get correct bid response for banner ad with dsa signals', function () { + const expectedParse = [ + { + requestId: '1a2b3c4d', + cpm: 1, + creativeId: '12345', + width: 300, + height: 250, + mediaType: 'banner', + ad: '', + currency: 'USD', + ttl: 300, + netRevenue: true, + meta: { + networkId: 50, + brandId: 303325, + brandName: 'OECTA', + advertiserDomains: ['www.abc.com'], + dsa: { + behalf: 'Advertiser', + paid: 'Advertiser', + transparency: [{ + domain: 'dsp1domain.com', + dsaparams: [1, 2] + }], + 'adrender': 1 + } + } + } + ]; + const result = spec.interpretResponse({ body: DEFAULT_BANNER_BID_RESPONSE_WITH_DSA }, bannerBidderRequest); + expect(result[0]).to.deep.equal(expectedParse[0]); + }); + it('should get correct bid response for banner ad with missing adomain', function () { const expectedParse = [ { From 6eaae9d1189741b3f1f1caf3e33e773dd48acd83 Mon Sep 17 00:00:00 2001 From: samuel-palmer-relevant-digital <77437973+samuel-palmer-relevant-digital@users.noreply.github.com> Date: Thu, 8 Feb 2024 19:57:27 +0100 Subject: [PATCH 236/246] PBjs Core Utils: fix deepEqual() to work correctly on sites where Array.prototype has been extended (#11077) * Make utils.deepEqual() work correctly on sites where Array.prototype has been extended * Removed blank line * Marginal improvement --------- Co-authored-by: Demetrio Girardi --- src/utils.js | 5 +++-- test/spec/utils_spec.js | 9 +++++++++ 2 files changed, 12 insertions(+), 2 deletions(-) diff --git a/src/utils.js b/src/utils.js index 67ed7339675..7f71ff299fb 100644 --- a/src/utils.js +++ b/src/utils.js @@ -898,8 +898,9 @@ export function deepEqual(obj1, obj2, {checkTypes = false} = {}) { (typeof obj2 === 'object' && obj2 !== null) && (!checkTypes || (obj1.constructor === obj2.constructor)) ) { - if (Object.keys(obj1).length !== Object.keys(obj2).length) return false; - for (let prop in obj1) { + const props1 = Object.keys(obj1); + if (props1.length !== Object.keys(obj2).length) return false; + for (let prop of props1) { if (obj2.hasOwnProperty(prop)) { if (!deepEqual(obj1[prop], obj2[prop], {checkTypes})) { return false; diff --git a/test/spec/utils_spec.js b/test/spec/utils_spec.js index 098582c0af6..c84fe124db6 100644 --- a/test/spec/utils_spec.js +++ b/test/spec/utils_spec.js @@ -1002,6 +1002,15 @@ describe('Utils', function () { const obj = {key: 'value'}; expect(deepEqual({outer: obj}, {outer: new Typed(obj)}, {checkTypes: true})).to.be.false; }); + it('should work when adding properties to the prototype of Array', () => { + after(function () { + // eslint-disable-next-line no-extend-native + delete Array.prototype.unitTestTempProp; + }); + // eslint-disable-next-line no-extend-native + Array.prototype.unitTestTempProp = 'testing'; + expect(deepEqual([], [])).to.be.true; + }); describe('cyrb53Hash', function() { it('should return the same hash for the same string', function() { From e8426c7913b912844f63d5c092375b235cd19296 Mon Sep 17 00:00:00 2001 From: nkloeber <100145701+nkloeber@users.noreply.github.com> Date: Thu, 8 Feb 2024 19:59:49 +0100 Subject: [PATCH 237/246] Yieldlab Bid Adapter: Add Digital Services Act (DSA) handling (#10981) * YieldlabBidAdapter add Digital Services Act (DSA) handling for bid request and responses * YieldlabBidAdapter - read dsa from bidderRequest - put dsa response under meta.dsa not ext.dsa - handle multiple transparency objects under new parameter dsatransparency - only add query params if they are not undefined --- modules/yieldlabBidAdapter.js | 70 +++++++++++- test/spec/modules/yieldlabBidAdapter_spec.js | 110 +++++++++++++++++++ 2 files changed, 176 insertions(+), 4 deletions(-) diff --git a/modules/yieldlabBidAdapter.js b/modules/yieldlabBidAdapter.js index e4fba5beb62..13910d688e8 100644 --- a/modules/yieldlabBidAdapter.js +++ b/modules/yieldlabBidAdapter.js @@ -34,10 +34,7 @@ export const spec = { * @returns {boolean} */ isBidRequestValid(bid) { - if (bid && bid.params && bid.params.adslotId && bid.params.supplyId) { - return true; - } - return false; + return !!(bid && bid.params && bid.params.adslotId && bid.params.supplyId); }, /** @@ -106,6 +103,33 @@ export const spec = { query.consent = bidderRequest.gdprConsent.consentString; } } + + if (bidderRequest.ortb2?.regs?.ext?.dsa !== undefined) { + const dsa = bidderRequest.ortb2.regs.ext.dsa; + + assignIfNotUndefined(query, 'dsarequired', dsa.dsarequired); + assignIfNotUndefined(query, 'dsapubrender', dsa.pubrender); + assignIfNotUndefined(query, 'dsadatatopub', dsa.datatopub); + + if (Array.isArray(dsa.transparency)) { + const filteredTransparencies = dsa.transparency.filter(({ domain, dsaparams }) => { + return domain && !domain.includes('~') && Array.isArray(dsaparams) && dsaparams.length > 0 && dsaparams.every(param => typeof param === 'number'); + }); + + if (filteredTransparencies.length === 1) { + const { domain, dsaparams } = filteredTransparencies[0]; + assignIfNotUndefined(query, 'dsadomain', domain); + assignIfNotUndefined(query, 'dsaparams', dsaparams.join(',')); + } else if (filteredTransparencies.length > 1) { + const dsatransparency = filteredTransparencies.map(({ domain, dsaparams }) => + `${domain}~${dsaparams.join('_')}` + ).join('~~'); + if (dsatransparency) { + query.dsatransparency = dsatransparency; + } + } + } + } } const adslots = adslotIds.join(','); @@ -174,6 +198,11 @@ export const spec = { }, }; + const dsa = getDigitalServicesActObjectFromMatchedBid(matchedBid) + if (dsa !== undefined) { + bidResponse.meta = { ...bidResponse.meta, dsa: dsa }; + } + if (isVideo(bidRequest, adType)) { const playersize = getPlayerSize(bidRequest); if (playersize) { @@ -545,4 +574,37 @@ function isImageAssetOfType(type) { return asset => asset?.img?.type === type; } +/** + * Retrieves the Digital Services Act (DSA) object from a matched bid. + * Only includes specific attributes (behalf, paid, transparency, adrender) from the DSA object. + * + * @param {Object} matchedBid - The server response body to inspect for the DSA information. + * @returns {Object|undefined} A copy of the DSA object if it exists, or undefined if not. + */ +function getDigitalServicesActObjectFromMatchedBid(matchedBid) { + if (matchedBid.dsa) { + const { behalf, paid, transparency, adrender } = matchedBid.dsa; + return { + ...(behalf !== undefined && { behalf }), + ...(paid !== undefined && { paid }), + ...(transparency !== undefined && { transparency }), + ...(adrender !== undefined && { adrender }) + }; + } + return undefined; +} + +/** + * Conditionally assigns a value to a specified key on an object if the value is not undefined. + * + * @param {Object} obj - The object to which the value will be assigned. + * @param {string} key - The key under which the value should be assigned. + * @param {*} value - The value to be assigned, if it is not undefined. + */ +function assignIfNotUndefined(obj, key, value) { + if (value !== undefined) { + obj[key] = value; + } +} + registerBidder(spec); diff --git a/test/spec/modules/yieldlabBidAdapter_spec.js b/test/spec/modules/yieldlabBidAdapter_spec.js index 93c231c816b..751dff4fe33 100644 --- a/test/spec/modules/yieldlabBidAdapter_spec.js +++ b/test/spec/modules/yieldlabBidAdapter_spec.js @@ -226,6 +226,36 @@ const PVID_RESPONSE = Object.assign({}, VIDEO_RESPONSE, { pvid: '43513f11-55a0-4a83-94e5-0ebc08f54a2c', }); +const DIGITAL_SERVICES_ACT_RESPONSE = Object.assign({}, RESPONSE, { + dsa: { + behalf: 'some-behalf', + paid: 'some-paid', + transparency: [{ + domain: 'test.com', + dsaparams: [1, 2, 3] + }], + adrender: 1 + } +}); + +const DIGITAL_SERVICES_ACT_CONFIG = { + ortb2: { + regs: { + ext: { + dsa: { + dsarequired: '1', + pubrender: '2', + datatopub: '3', + transparency: [{ + domain: 'test.com', + dsaparams: [1, 2, 3] + }] + }, + } + }, + } +} + const REQPARAMS = { json: true, ts: 1234567890, @@ -486,6 +516,75 @@ describe('yieldlabBidAdapter', () => { expect(request.url).to.not.include('sizes'); }); }); + + describe('Digital Services Act handling', () => { + beforeEach(() => { + config.setConfig(DIGITAL_SERVICES_ACT_CONFIG); + }); + + afterEach(() => { + config.resetConfig(); + }); + + it('does pass dsarequired parameter', () => { + let request = spec.buildRequests([DEFAULT_REQUEST()], { ...REQPARAMS, ...DIGITAL_SERVICES_ACT_CONFIG }); + expect(request.url).to.include('dsarequired=1'); + }); + + it('does pass dsapubrender parameter', () => { + let request = spec.buildRequests([DEFAULT_REQUEST()], { ...REQPARAMS, ...DIGITAL_SERVICES_ACT_CONFIG }); + expect(request.url).to.include('dsapubrender=2'); + }); + + it('does pass dsadatatopub parameter', () => { + let request = spec.buildRequests([DEFAULT_REQUEST()], { ...REQPARAMS, ...DIGITAL_SERVICES_ACT_CONFIG }); + expect(request.url).to.include('dsadatatopub=3'); + }); + + it('does pass dsadomain parameter', () => { + let request = spec.buildRequests([DEFAULT_REQUEST()], { ...REQPARAMS, ...DIGITAL_SERVICES_ACT_CONFIG }); + expect(request.url).to.include('dsadomain=test.com'); + }); + + it('does pass encoded dsaparams parameter', () => { + let request = spec.buildRequests([DEFAULT_REQUEST()], { ...REQPARAMS, ...DIGITAL_SERVICES_ACT_CONFIG }); + expect(request.url).to.include('dsaparams=1%2C2%2C3'); + }); + + it('does pass multiple transparencies in dsatransparency param', () => { + const DSA_CONFIG_WITH_MULTIPLE_TRANSPARENCIES = { + ortb2: { + regs: { + ext: { + dsa: { + dsarequired: '1', + pubrender: '2', + datatopub: '3', + transparency: [ + { + domain: 'test.com', + dsaparams: [1, 2, 3] + }, + { + domain: 'example.com', + dsaparams: [4, 5, 6] + } + ] + } + } + } + } + }; + + config.setConfig(DSA_CONFIG_WITH_MULTIPLE_TRANSPARENCIES); + + let request = spec.buildRequests([DEFAULT_REQUEST()], { ...REQPARAMS, ...DSA_CONFIG_WITH_MULTIPLE_TRANSPARENCIES }); + + expect(request.url).to.include('dsatransparency=test.com~1_2_3~~example.com~4_5_6'); + expect(request.url).to.not.include('dsadomain'); + expect(request.url).to.not.include('dsaparams'); + }); + }); }); describe('interpretResponse', () => { @@ -676,6 +775,17 @@ describe('yieldlabBidAdapter', () => { const result = spec.interpretResponse({body: [VIDEO_RESPONSE]}, {validBidRequests: [VIDEO_REQUEST()], queryParams: REQPARAMS_IAB_CONTENT}); expect(result[0].vastUrl).to.include('&iab_content=id%3Afoo_id%2Cepisode%3A99%2Ctitle%3Afoo_title%252Cbar_title%2Cseries%3Afoo_series%2Cseason%3As1%2Cartist%3Afoo%2520bar%2Cgenre%3Abaz%2Cisrc%3ACC-XXX-YY-NNNNN%2Curl%3Ahttp%253A%252F%252Ffoo_url.de%2Ccat%3Acat1%7Ccat2%252Cppp%7Ccat3%257C%257C%257C%252F%252F%2Ccontext%3A7%2Ckeywords%3Ak1%252C%7Ck2..%2Clive%3A0'); }); + + it('should get digital services act object in matched bid response', () => { + const result = spec.interpretResponse({body: [DIGITAL_SERVICES_ACT_RESPONSE]}, {validBidRequests: [{...DEFAULT_REQUEST(), ...DIGITAL_SERVICES_ACT_CONFIG}], queryParams: REQPARAMS}); + + expect(result[0].requestId).to.equal('2d925f27f5079f'); + expect(result[0].meta.dsa.behalf).to.equal('some-behalf'); + expect(result[0].meta.dsa.paid).to.equal('some-paid'); + expect(result[0].meta.dsa.transparency[0].domain).to.equal('test.com'); + expect(result[0].meta.dsa.transparency[0].dsaparams).to.deep.equal([1, 2, 3]); + expect(result[0].meta.dsa.adrender).to.equal(1); + }); }); describe('getUserSyncs', () => { From 6fddf80dc428e8d784a69946dc53873641245c48 Mon Sep 17 00:00:00 2001 From: Evgenii Novikov Date: Thu, 8 Feb 2024 20:01:34 +0100 Subject: [PATCH 238/246] Fixed build and improved docs (#11078) --- modules/yandexAnalyticsAdapter.js | 2 +- modules/yandexAnalyticsAdapter.md | 52 ++++++++++++++++++------------- modules/yandexBidAdapter.md | 4 ++- 3 files changed, 35 insertions(+), 23 deletions(-) diff --git a/modules/yandexAnalyticsAdapter.js b/modules/yandexAnalyticsAdapter.js index 045de18e21b..ba000db6162 100644 --- a/modules/yandexAnalyticsAdapter.js +++ b/modules/yandexAnalyticsAdapter.js @@ -148,7 +148,7 @@ const yandexAnalytics = Object.assign(buildAdapter({ analyticsType: 'endpoint' } adapterManager.registerAnalyticsAdapter({ adapter: yandexAnalytics, - code: 'yandexAnalytics' + code: 'yandex' }); export default yandexAnalytics; diff --git a/modules/yandexAnalyticsAdapter.md b/modules/yandexAnalyticsAdapter.md index 96d2b08320e..43460550471 100644 --- a/modules/yandexAnalyticsAdapter.md +++ b/modules/yandexAnalyticsAdapter.md @@ -1,4 +1,4 @@ -# Overview +## Overview ``` Module Name: Yandex Analytics Adapter @@ -6,31 +6,41 @@ Module Type: Analytics Adapter Maintainer: prebid@yandex-team.com ``` -# Description +## Description -This adapter is designed to work with [Yandex Metrica](https://metrica.yandex.com/about) - Top-5 worldwide web analytics tool. +The Yandex Analytics Adapter integrates Prebid.js with [Yandex Metrica](https://metrica.yandex.com/about), a top-5 worldwide web analytics tool. It offers detailed insights into auction performance and user behavior, enabling publishers to make data-driven decisions to optimize their ad revenue. -Disclosure: provider use Metrica Tag build based on https://github.com/yandex/metrica-tag, ~60 kB gzipped. +Disclosure: The adapter utilizes the Metrica Tag build based on [github.com/yandex/metrica-tag](https://github.com/yandex/metrica-tag), approximately 60 kB gzipped. -## How to setup provider +## Setup Instructions -Register your application on https://metrica.yandex.com/ and get counter id. -Insert counter initialization code obtained from the page https://metrica.yandex.com/settings?id={counterId} into your html code. -Init provider like this, where `123` is your counter id. +1. **Register Your Website:** -Note: If you have Single Page Application (SPA), [configure your tag](https://yandex.com/support/metrica/code/counter-spa-setup.html). + Visit [Yandex Metrica](https://metrica.yandex.com/) and register your website to obtain a counter ID. -```javascript -pbjs.enableAnalytics({ - provider: 'yandexAnalytics', - options: { - counters: [ - 123, - ], - }, -}); -``` +2. **Insert Counter Initialization Code:** + + Retrieve the counter initialization code from the Yandex Metrica settings page at `https://metrica.yandex.com/settings?id={counterId}`, where `{counterId}` is your counter ID, and embed it into your website's HTML. + +3. **Initialize the Adapter in Prebid.js:** + + Configure the Yandex Analytics Adapter in your Prebid.js setup. For optimal performance and ease of management, it is preferred to use a single counter. Add the following JavaScript snippet, replacing `123` with your actual counter ID: + + ```javascript + pbjs.enableAnalytics({ + provider: "yandex", + options: { + // Replace 123 with your actual counter ID + // It's preferred to use a single counter for optimal performance and ease of management + counters: [123] + } + }); + ``` + +4. **Special Instructions for Single Page Applications (SPAs):** + + If your website is an SPA, make sure to [configure your Metrica tag accordingly](https://yandex.com/support/metrica/code/counter-spa-setup.html). -## Where to find data +## Accessing Analytics Data -Go to https://metrika.yandex.com/dashboard -> Prebid Analytics +You can view the collected analytics data in the Yandex Metrica dashboard. Navigate to [metrika.yandex.com/dashboard](https://metrika.yandex.com/dashboard) and look for the Prebid Analytics section to analyze your data. diff --git a/modules/yandexBidAdapter.md b/modules/yandexBidAdapter.md index 55a658cc25c..f16d0ec0b33 100644 --- a/modules/yandexBidAdapter.md +++ b/modules/yandexBidAdapter.md @@ -8,7 +8,9 @@ Maintainer: prebid@yandex-team.com # Description -Yandex Bidder Adapter for Prebid.js. +The Yandex Prebid Adapter is designed for seamless integration with Yandex's advertising services. It facilitates effective bidding by leveraging Yandex's robust ad-serving technology, ensuring publishers can maximize their ad revenue through efficient and targeted ad placements. + +For comprehensive auction analytics, consider using the [Yandex Analytics Adapter](https://docs.prebid.org/dev-docs/analytics/yandex.html). This tool provides essential insights into auction dynamics and user interactions, empowering publishers to fine-tune their strategies for optimal ad performance. # Parameters From dbbbccd4aaafcd5a005013e47f620d4cbb1cf1aa Mon Sep 17 00:00:00 2001 From: Demetrio Girardi Date: Thu, 8 Feb 2024 12:16:36 -0800 Subject: [PATCH 239/246] Build system: add --no-lint-warnings option (#11082) --- gulpfile.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gulpfile.js b/gulpfile.js index d8a18ba3df1..125dec93402 100644 --- a/gulpfile.js +++ b/gulpfile.js @@ -88,7 +88,7 @@ function lint(done) { '!plugins/**/node_modules/**', './*.js' ], { base: './' }) - .pipe(gulpif(argv.nolintfix, eslint(), eslint({ fix: true }))) + .pipe(eslint({ fix: !argv.nolintfix, quiet: !(argv.lintWarnings ?? true) })) .pipe(eslint.format('stylish')) .pipe(eslint.failAfterError()) .pipe(gulpif(isFixed, gulp.dest('./'))); From 43e3980a03b88b186892f5ab7258b8f5e2f057aa Mon Sep 17 00:00:00 2001 From: Demetrio Girardi Date: Thu, 8 Feb 2024 13:07:57 -0800 Subject: [PATCH 240/246] dsaControl module: Reject bids without meta.dsa when required (#10982) * dsaControl - reject bids without meta.dsa when required * ortbConverter: always set meta.dsa * dsaControl: reject bids whose DSA rendering method disagrees with the request --- libraries/ortbConverter/processors/default.js | 3 + modules/dsaControl.js | 67 +++++++++++ src/auctionManager.js | 4 +- src/constants.json | 4 +- src/utils/ttlCollection.js | 25 +++- test/spec/modules/dsaControl_spec.js | 113 ++++++++++++++++++ test/spec/ortbConverter/common_spec.js | 29 +++++ test/spec/unit/utils/ttlCollection_spec.js | 27 +++++ 8 files changed, 269 insertions(+), 3 deletions(-) create mode 100644 modules/dsaControl.js create mode 100644 test/spec/modules/dsaControl_spec.js create mode 100644 test/spec/ortbConverter/common_spec.js diff --git a/libraries/ortbConverter/processors/default.js b/libraries/ortbConverter/processors/default.js index 8db2c1c461e..d92a51daba2 100644 --- a/libraries/ortbConverter/processors/default.js +++ b/libraries/ortbConverter/processors/default.js @@ -97,6 +97,9 @@ export const DEFAULT_PROCESSORS = { if (bid.adomain) { bidResponse.meta.advertiserDomains = bid.adomain; } + if (bid.ext?.dsa) { + bidResponse.meta.dsa = bid.ext.dsa; + } } } } diff --git a/modules/dsaControl.js b/modules/dsaControl.js new file mode 100644 index 00000000000..b08a6ea1f4e --- /dev/null +++ b/modules/dsaControl.js @@ -0,0 +1,67 @@ +import {config} from '../src/config.js'; +import {auctionManager} from '../src/auctionManager.js'; +import {timedBidResponseHook} from '../src/utils/perfMetrics.js'; +import CONSTANTS from '../src/constants.json'; +import {getHook} from '../src/hook.js'; +import {logInfo, logWarn} from '../src/utils.js'; + +let expiryHandle; +let dsaAuctions = {}; + +export const addBidResponseHook = timedBidResponseHook('dsa', function (fn, adUnitCode, bid, reject) { + if (!dsaAuctions.hasOwnProperty(bid.auctionId)) { + dsaAuctions[bid.auctionId] = auctionManager.index.getAuction(bid)?.getFPD?.()?.global?.regs?.ext?.dsa; + } + const dsaRequest = dsaAuctions[bid.auctionId]; + let rejectReason; + if (dsaRequest) { + if (!bid.meta?.dsa) { + if (dsaRequest.dsarequired === 1) { + // request says dsa is supported; response does not have dsa info; warn about it + logWarn(`dsaControl: ${CONSTANTS.REJECTION_REASON.DSA_REQUIRED}; will still be accepted as regs.ext.dsa.dsarequired = 1`, bid); + } else if ([2, 3].includes(dsaRequest.dsarequired)) { + // request says dsa is required; response does not have dsa info; reject it + rejectReason = CONSTANTS.REJECTION_REASON.DSA_REQUIRED; + } + } else { + if (dsaRequest.pubrender === 0 && bid.meta.dsa.adrender === 0) { + // request says publisher can't render; response says advertiser won't; reject it + rejectReason = CONSTANTS.REJECTION_REASON.DSA_MISMATCH; + } else if (dsaRequest.pubrender === 2 && bid.meta.dsa.adrender === 1) { + // request says publisher will render; response says advertiser will; reject it + rejectReason = CONSTANTS.REJECTION_REASON.DSA_MISMATCH; + } + } + } + if (rejectReason) { + reject(rejectReason); + } else { + return fn.call(this, adUnitCode, bid, reject); + } +}); + +function toggleHooks(enabled) { + if (enabled && expiryHandle == null) { + getHook('addBidResponse').before(addBidResponseHook); + expiryHandle = auctionManager.onExpiry(auction => { + delete dsaAuctions[auction.getAuctionId()]; + }); + logInfo('dsaControl: DSA bid validation is enabled') + } else if (!enabled && expiryHandle != null) { + getHook('addBidResponse').getHooks({hook: addBidResponseHook}).remove(); + expiryHandle(); + expiryHandle = null; + logInfo('dsaControl: DSA bid validation is disabled') + } +} + +export function reset() { + toggleHooks(false); + dsaAuctions = {}; +} + +toggleHooks(true); + +config.getConfig('consentManagement', (cfg) => { + toggleHooks(cfg.consentManagement?.dsa?.validateBids ?? true); +}); diff --git a/src/auctionManager.js b/src/auctionManager.js index 8f3cbb56333..2d6e0ffbfd9 100644 --- a/src/auctionManager.js +++ b/src/auctionManager.js @@ -60,7 +60,9 @@ export function newAuctionManager() { } }) - const auctionManager = {}; + const auctionManager = { + onExpiry: _auctions.onExpiry + }; function getAuction(auctionId) { for (const auction of _auctions) { diff --git a/src/constants.json b/src/constants.json index 53ee407b772..af7e80ec0b4 100644 --- a/src/constants.json +++ b/src/constants.json @@ -134,7 +134,9 @@ "INVALID_REQUEST_ID": "Invalid request ID", "BIDDER_DISALLOWED": "Bidder code is not allowed by allowedAlternateBidderCodes / allowUnknownBidderCodes", "FLOOR_NOT_MET": "Bid does not meet price floor", - "CANNOT_CONVERT_CURRENCY": "Unable to convert currency" + "CANNOT_CONVERT_CURRENCY": "Unable to convert currency", + "DSA_REQUIRED": "Bid does not provide required DSA transparency info", + "DSA_MISMATCH": "Bid indicates inappropriate DSA rendering method" }, "PREBID_NATIVE_DATA_KEYS_TO_ORTB": { "body": "desc", diff --git a/src/utils/ttlCollection.js b/src/utils/ttlCollection.js index 392ed1c9ad7..0972d175848 100644 --- a/src/utils/ttlCollection.js +++ b/src/utils/ttlCollection.js @@ -1,5 +1,5 @@ import {GreedyPromise} from './promise.js'; -import {binarySearch, timestamp} from '../utils.js'; +import {binarySearch, logError, timestamp} from '../utils.js'; /** * Create a set-like collection that automatically forgets items after a certain time. @@ -27,6 +27,7 @@ export function ttlCollection( } = {} ) { const items = new Map(); + const callbacks = []; const pendingPurge = []; const markForPurge = monotonic ? (entry) => pendingPurge.push(entry) @@ -43,6 +44,13 @@ export function ttlCollection( let cnt = 0; for (const entry of pendingPurge) { if (entry.expiry > now) break; + callbacks.forEach(cb => { + try { + cb(entry.item) + } catch (e) { + logError(e); + } + }); items.delete(entry.item) cnt++; } @@ -135,5 +143,20 @@ export function ttlCollection( entry.refresh(); } }, + /** + * Register a callback to be run when an item has expired and is about to be + * removed the from the collection. + * @param cb a callback that takes the expired item as argument + * @return an unregistration function. + */ + onExpiry(cb) { + callbacks.push(cb); + return () => { + const idx = callbacks.indexOf(cb); + if (idx >= 0) { + callbacks.splice(idx, 1); + } + } + } }; } diff --git a/test/spec/modules/dsaControl_spec.js b/test/spec/modules/dsaControl_spec.js new file mode 100644 index 00000000000..0d7c52b5efd --- /dev/null +++ b/test/spec/modules/dsaControl_spec.js @@ -0,0 +1,113 @@ +import {addBidResponseHook, setMetaDsa, reset} from '../../../modules/dsaControl.js'; +import CONSTANTS from 'src/constants.json'; +import {auctionManager} from '../../../src/auctionManager.js'; +import {AuctionIndex} from '../../../src/auctionIndex.js'; + +describe('DSA transparency', () => { + let sandbox; + beforeEach(() => { + sandbox = sinon.sandbox.create(); + }); + afterEach(() => { + sandbox.restore(); + reset(); + }); + + describe('addBidResponseHook', () => { + const auctionId = 'auction-id'; + let bid, auction, fpd, next, reject; + beforeEach(() => { + next = sinon.stub(); + reject = sinon.stub(); + fpd = {}; + bid = { + auctionId + } + auction = { + getAuctionId: () => auctionId, + getFPD: () => ({global: fpd}) + } + sandbox.stub(auctionManager, 'index').get(() => new AuctionIndex(() => [auction])); + }); + + function expectRejection(reason) { + addBidResponseHook(next, 'adUnit', bid, reject); + sinon.assert.calledWith(reject, reason); + sinon.assert.notCalled(next); + } + + function expectAcceptance() { + addBidResponseHook(next, 'adUnit', bid, reject); + sinon.assert.notCalled(reject); + sinon.assert.calledWith(next, 'adUnit', bid, reject); + } + + [2, 3].forEach(required => { + describe(`when regs.ext.dsa.dsarequired is ${required} (required)`, () => { + beforeEach(() => { + fpd = { + regs: {ext: {dsa: {dsarequired: required}}} + }; + }); + + it('should reject bids that have no meta.dsa', () => { + expectRejection(CONSTANTS.REJECTION_REASON.DSA_REQUIRED); + }); + + it('should accept bids that do', () => { + bid.meta = {dsa: {}}; + expectAcceptance(); + }); + + describe('and pubrender = 0 (rendering by publisher not supported)', () => { + beforeEach(() => { + fpd.regs.ext.dsa.pubrender = 0; + }); + + it('should reject bids with adrender = 0 (advertiser will not render)', () => { + bid.meta = {dsa: {adrender: 0}}; + expectRejection(CONSTANTS.REJECTION_REASON.DSA_MISMATCH); + }); + + it('should accept bids with adrender = 1 (advertiser will render)', () => { + bid.meta = {dsa: {adrender: 1}}; + expectAcceptance(); + }); + }); + describe('and pubrender = 2 (publisher will render)', () => { + beforeEach(() => { + fpd.regs.ext.dsa.pubrender = 2; + }); + + it('should reject bids with adrender = 1 (advertiser will render)', () => { + bid.meta = {dsa: {adrender: 1}}; + expectRejection(CONSTANTS.REJECTION_REASON.DSA_MISMATCH); + }); + + it('should accept bids with adrender = 0 (advertiser will not render)', () => { + bid.meta = {dsa: {adrender: 0}}; + expectAcceptance(); + }) + }) + }); + }); + [undefined, 'garbage', 0, 1].forEach(required => { + describe(`when regs.ext.dsa.dsarequired is ${required}`, () => { + beforeEach(() => { + if (required != null) { + fpd = { + regs: {ext: {dsa: {dsarequired: required}}} + } + } + }); + + it('should accept bids regardless of their meta.dsa', () => { + addBidResponseHook(next, 'adUnit', bid, reject); + sinon.assert.notCalled(reject); + sinon.assert.calledWith(next, 'adUnit', bid, reject); + }) + }) + }) + it('should accept bids regardless of dsa when "required" any other value') + }); +}); diff --git a/test/spec/ortbConverter/common_spec.js b/test/spec/ortbConverter/common_spec.js new file mode 100644 index 00000000000..d2d61e6778c --- /dev/null +++ b/test/spec/ortbConverter/common_spec.js @@ -0,0 +1,29 @@ +import {DEFAULT_PROCESSORS} from '../../../libraries/ortbConverter/processors/default.js'; +import {BID_RESPONSE} from '../../../src/pbjsORTB.js'; + +describe('common processors', () => { + describe('bid response properties', () => { + const responseProps = DEFAULT_PROCESSORS[BID_RESPONSE].props.fn; + let context; + + beforeEach(() => { + context = { + ortbResponse: {} + } + }) + + describe('meta.dsa', () => { + const MOCK_DSA = {transparency: 'info'}; + it('is not set if bid has no meta.dsa', () => { + const resp = {}; + responseProps(resp, {}, context); + expect(resp.meta?.dsa).to.not.exist; + }); + it('is set to ext.dsa otherwise', () => { + const resp = {}; + responseProps(resp, {ext: {dsa: MOCK_DSA}}, context); + expect(resp.meta.dsa).to.eql(MOCK_DSA); + }) + }) + }) +}) diff --git a/test/spec/unit/utils/ttlCollection_spec.js b/test/spec/unit/utils/ttlCollection_spec.js index 29c6c438855..76cfa32d955 100644 --- a/test/spec/unit/utils/ttlCollection_spec.js +++ b/test/spec/unit/utils/ttlCollection_spec.js @@ -67,6 +67,33 @@ describe('ttlCollection', () => { }); }); + it('should run onExpiry when items are cleared', () => { + const i1 = {ttl: 1000, some: 'data'}; + const i2 = {ttl: 2000, some: 'data'}; + coll.add(i1); + coll.add(i2); + const cb = sinon.stub(); + coll.onExpiry(cb); + return waitForPromises().then(() => { + clock.tick(500); + sinon.assert.notCalled(cb); + clock.tick(SLACK + 500); + sinon.assert.calledWith(cb, i1); + clock.tick(3000); + sinon.assert.calledWith(cb, i2); + }) + }); + + it('should allow unregistration of onExpiry callbacks', () => { + const cb = sinon.stub(); + coll.add({ttl: 500}); + coll.onExpiry(cb)(); + return waitForPromises().then(() => { + clock.tick(500 + SLACK); + sinon.assert.notCalled(cb); + }) + }) + it('should not wait too long if a shorter ttl shows up', () => { coll.add({ttl: 4000}); coll.add({ttl: 1000}); From 40474958a89607ca09b483c59765a22fe18abaaa Mon Sep 17 00:00:00 2001 From: "Prebid.js automated release" Date: Thu, 8 Feb 2024 21:45:32 +0000 Subject: [PATCH 241/246] Prebid 8.36.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 4717c12f7c4..48c1fabb125 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,6 +1,6 @@ { "name": "prebid.js", - "version": "8.36.0-pre", + "version": "8.36.0", "lockfileVersion": 2, "requires": true, "packages": { diff --git a/package.json b/package.json index 78e27a2a44a..ee895df69e3 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "prebid.js", - "version": "8.36.0-pre", + "version": "8.36.0", "description": "Header Bidding Management Library", "main": "src/prebid.js", "scripts": { From 8bda2cee499152160e7b9784473ddf206df7b697 Mon Sep 17 00:00:00 2001 From: "Prebid.js automated release" Date: Thu, 8 Feb 2024 21:45:33 +0000 Subject: [PATCH 242/246] Increment version to 8.37.0-pre --- package-lock.json | 2 +- package.json | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/package-lock.json b/package-lock.json index 48c1fabb125..4686462599c 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,6 +1,6 @@ { "name": "prebid.js", - "version": "8.36.0", + "version": "8.37.0-pre", "lockfileVersion": 2, "requires": true, "packages": { diff --git a/package.json b/package.json index ee895df69e3..1954a8c1294 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "prebid.js", - "version": "8.36.0", + "version": "8.37.0-pre", "description": "Header Bidding Management Library", "main": "src/prebid.js", "scripts": { From f7163de30684028ed8480de20190cdcc840917d6 Mon Sep 17 00:00:00 2001 From: redaguermas Date: Sat, 10 Feb 2024 03:16:40 -0800 Subject: [PATCH 243/246] NoBid Analytics Adapter: added support for flag to control bidWon and auctionEnd independently. (#11087) * Enable supplyChain support * Added support for COPPA * rebuilt * Added support for Extended User IDs. * Added support for the "meta" attribute in bid response. * Delete nobidBidAdapter.js.orig * Delete a * Delete .jsdtscope * Delete org.eclipse.wst.jsdt.ui.superType.container * Delete org.eclipse.wst.jsdt.ui.superType.name * Delete .project * Added support for counting blocked requests for the Optimizer. * Added missing function for testing. * Added unit tests * Added support for Analytics adapter flag to control bidWon and auctionEnd independently. --------- Co-authored-by: Reda Guermas --- modules/nobidAnalyticsAdapter.js | 26 +++- .../modules/nobidAnalyticsAdapter_spec.js | 127 ++++++++++++++++-- 2 files changed, 135 insertions(+), 18 deletions(-) diff --git a/modules/nobidAnalyticsAdapter.js b/modules/nobidAnalyticsAdapter.js index 3a7912c37e1..2c119e28610 100644 --- a/modules/nobidAnalyticsAdapter.js +++ b/modules/nobidAnalyticsAdapter.js @@ -6,7 +6,7 @@ import CONSTANTS from '../src/constants.json'; import adapterManager from '../src/adapterManager.js'; import {MODULE_TYPE_ANALYTICS} from '../src/activities/modules.js'; -const VERSION = '1.1.0'; +const VERSION = '2.0.0'; const MODULE_NAME = 'nobidAnalyticsAdapter'; const ANALYTICS_OPT_FLUSH_TIMEOUT_SECONDS = 5 * 1000; const RETENTION_SECONDS = 1 * 24 * 3600; @@ -49,7 +49,7 @@ function sendEvent (event, eventType) { return ret; } if (!nobidAnalytics.initOptions || !nobidAnalytics.initOptions.siteId || !event) return; - if (nobidAnalytics.isAnalyticsDisabled()) { + if (nobidAnalytics.isAnalyticsDisabled(eventType)) { log('NoBid Analytics is Disabled'); return; } @@ -97,8 +97,16 @@ function sendAuctionEndEvent (event, eventType) { if (data) cleanupObjectAttributes(data.bidderRequests, ['bidderCode', 'bidderRequestId', 'bids', 'refererInfo']); if (data) cleanupObjectAttributes(data.bidsReceived, ['bidderCode', 'width', 'height', 'adUnitCode', 'statusMessage', 'requestId', 'mediaType', 'cpm']); if (data) cleanupObjectAttributes(data.noBids, ['bidder', 'sizes', 'bidId']); - if (data.bidderRequests) cleanupObjectAttributes(data.bidderRequests.bids, ['mediaTypes', 'adUnitCode', 'sizes', 'bidId']); - if (data.bidderRequests) cleanupObjectAttributes(data.bidderRequests.refererInfo, ['topmostLocation']); + if (data.bidderRequests) { + data.bidderRequests.forEach(bidderRequest => { + cleanupObjectAttributes(bidderRequest.bids, ['mediaTypes', 'adUnitCode', 'sizes', 'bidId']); + }); + } + if (data.bidderRequests) { + data.bidderRequests.forEach(bidderRequest => { + cleanupObjectAttributes(bidderRequest.refererInfo, ['topmostLocation']); + }); + } sendEvent(data, eventType); } function auctionInit (event) { @@ -147,12 +155,18 @@ nobidAnalytics = { isExpired (data) { return isExpired(data, this.retentionSeconds); }, - isAnalyticsDisabled () { + isAnalyticsDisabled (eventType) { let stored = storage.getDataFromLocalStorage(this.ANALYTICS_DATA_NAME); if (!isJson(stored)) return false; stored = JSON.parse(stored); if (this.isExpired(stored)) return false; - return stored.disabled; + if (stored.disabled === 1) return true; + else if (stored.disabled === 0) return false; + if (eventType) { + if (stored[`disabled_${eventType}`] === 1) return true; + else if (stored[`disabled_${eventType}`] === 0) return false; + } + return false; }, processServerResponse (response) { if (!isJson(response)) return; diff --git a/test/spec/modules/nobidAnalyticsAdapter_spec.js b/test/spec/modules/nobidAnalyticsAdapter_spec.js index 06a39ffd020..3da334eea97 100644 --- a/test/spec/modules/nobidAnalyticsAdapter_spec.js +++ b/test/spec/modules/nobidAnalyticsAdapter_spec.js @@ -344,7 +344,6 @@ describe('NoBid Prebid Analytic', function () { bidderRequestId: '7c1940bb285731', bids: [ { - bidder: 'nobid', params: { siteId: SITE_ID }, mediaTypes: { banner: { sizes: [[728, 90]] } }, adUnitCode: 'leaderboard', @@ -390,19 +389,28 @@ describe('NoBid Prebid Analytic', function () { const auctionEndRequest = JSON.parse(server.requests[0].requestBody); expect(auctionEndRequest).to.have.property('auctionId', requestOutgoing.auctionId); expect(auctionEndRequest.bidderRequests).to.have.length(1); - expect(auctionEndRequest.bidderRequests[0]).to.have.property('bidderCode', requestOutgoing.bidderRequests[0].bidderCode); + expect(auctionEndRequest.bidderRequests[0].bidderCode).to.equal(requestOutgoing.bidderRequests[0].bidderCode); expect(auctionEndRequest.bidderRequests[0].bids).to.have.length(1); - expect(auctionEndRequest.bidderRequests[0].bids[0]).to.have.property('bidder', requestOutgoing.bidderRequests[0].bids[0].bidder); - expect(auctionEndRequest.bidderRequests[0].bids[0]).to.have.property('adUnitCode', requestOutgoing.bidderRequests[0].bids[0].adUnitCode); - expect(auctionEndRequest.bidderRequests[0].bids[0].params).to.have.property('siteId', requestOutgoing.bidderRequests[0].bids[0].params.siteId); - expect(auctionEndRequest.bidderRequests[0].refererInfo).to.have.property('topmostLocation', requestOutgoing.bidderRequests[0].refererInfo.topmostLocation); + expect(typeof auctionEndRequest.bidderRequests[0].bids[0].bidder).to.equal('undefined'); + expect(auctionEndRequest.bidderRequests[0].bids[0].adUnitCode).to.equal(requestOutgoing.bidderRequests[0].bids[0].adUnitCode); + expect(typeof auctionEndRequest.bidderRequests[0].bids[0].params).to.equal('undefined'); + expect(typeof auctionEndRequest.bidderRequests[0].bids[0].src).to.equal('undefined'); + expect(auctionEndRequest.bidderRequests[0].refererInfo.topmostLocation).to.equal(requestOutgoing.bidderRequests[0].refererInfo.topmostLocation); + expect(auctionEndRequest.bidsReceived).to.have.length(1); + expect(auctionEndRequest.bidsReceived[0].bidderCode).to.equal(requestOutgoing.bidsReceived[0].bidderCode); + expect(auctionEndRequest.bidsReceived[0].width).to.equal(requestOutgoing.bidsReceived[0].width); + expect(auctionEndRequest.bidsReceived[0].height).to.equal(requestOutgoing.bidsReceived[0].height); + expect(auctionEndRequest.bidsReceived[0].mediaType).to.equal(requestOutgoing.bidsReceived[0].mediaType); + expect(auctionEndRequest.bidsReceived[0].cpm).to.equal(requestOutgoing.bidsReceived[0].cpm); + expect(auctionEndRequest.bidsReceived[0].adUnitCode).to.equal(requestOutgoing.bidsReceived[0].adUnitCode); + expect(typeof auctionEndRequest.bidsReceived[0].source).to.equal('undefined'); done(); }); it('Analytics disabled test', function (done) { let disabled; - nobidAnalytics.processServerResponse(JSON.stringify({disabled: false})); + nobidAnalytics.processServerResponse(JSON.stringify({disabled: 0})); disabled = nobidAnalytics.isAnalyticsDisabled(); expect(disabled).to.equal(false); events.emit(constants.EVENTS.AUCTION_END, {auctionId: '1234567890'}); @@ -417,7 +425,7 @@ describe('NoBid Prebid Analytic', function () { clock.tick(1000); expect(server.requests).to.have.length(3); - nobidAnalytics.processServerResponse(JSON.stringify({disabled: true})); + nobidAnalytics.processServerResponse(JSON.stringify({disabled: 1})); disabled = nobidAnalytics.isAnalyticsDisabled(); expect(disabled).to.equal(true); events.emit(constants.EVENTS.AUCTION_END, {auctionId: '12345678902'}); @@ -425,7 +433,7 @@ describe('NoBid Prebid Analytic', function () { expect(server.requests).to.have.length(3); nobidAnalytics.retentionSeconds = 5; - nobidAnalytics.processServerResponse(JSON.stringify({disabled: true})); + nobidAnalytics.processServerResponse(JSON.stringify({disabled: 1})); clock.tick(1000); disabled = nobidAnalytics.isAnalyticsDisabled(); expect(disabled).to.equal(true); @@ -437,6 +445,100 @@ describe('NoBid Prebid Analytic', function () { }); }); + describe('Analytics disabled event type test', function () { + beforeEach(function () { + sinon.stub(events, 'getEvents').returns([]); + clock = sinon.useFakeTimers(Date.now()); + }); + + afterEach(function () { + events.getEvents.restore(); + clock.restore(); + }); + + after(function () { + nobidAnalytics.disableAnalytics(); + }); + + it('Analytics disabled event type test', function (done) { + // Initialize adapter + const initOptions = { options: { siteId: SITE_ID } }; + nobidAnalytics.enableAnalytics(initOptions); + adapterManager.enableAnalytics({ provider: 'nobid', options: initOptions }); + + let eventType = constants.EVENTS.AUCTION_END; + let disabled; + nobidAnalytics.processServerResponse(JSON.stringify({disabled: 0})); + disabled = nobidAnalytics.isAnalyticsDisabled(); + expect(disabled).to.equal(false); + events.emit(eventType, {auctionId: '1234567890'}); + clock.tick(1000); + expect(server.requests).to.have.length(1); + events.emit(eventType, {auctionId: '12345678901'}); + clock.tick(1000); + expect(server.requests).to.have.length(2); + + server.requests.length = 0; + expect(server.requests).to.have.length(0); + + nobidAnalytics.processServerResponse(JSON.stringify({disabled_auctionEnd: 1})); + disabled = nobidAnalytics.isAnalyticsDisabled(eventType); + expect(disabled).to.equal(true); + events.emit(eventType, {auctionId: '1234567890'}); + clock.tick(1000); + expect(server.requests).to.have.length(0); + + server.requests.length = 0; + + nobidAnalytics.processServerResponse(JSON.stringify({disabled_auctionEnd: 0})); + disabled = nobidAnalytics.isAnalyticsDisabled(eventType); + expect(disabled).to.equal(false); + events.emit(constants.EVENTS.AUCTION_END, {auctionId: '1234567890'}); + clock.tick(1000); + expect(server.requests).to.have.length(1); + + server.requests.length = 0; + expect(server.requests).to.have.length(0); + + eventType = constants.EVENTS.BID_WON; + nobidAnalytics.processServerResponse(JSON.stringify({disabled_bidWon: 1})); + disabled = nobidAnalytics.isAnalyticsDisabled(eventType); + expect(disabled).to.equal(true); + events.emit(eventType, {bidderCode: 'nobid'}); + clock.tick(1000); + expect(server.requests).to.have.length(0); + + server.requests.length = 0; + expect(server.requests).to.have.length(0); + + eventType = constants.EVENTS.AUCTION_END; + nobidAnalytics.processServerResponse(JSON.stringify({disabled: 1})); + disabled = nobidAnalytics.isAnalyticsDisabled(eventType); + expect(disabled).to.equal(true); + events.emit(eventType, {auctionId: '1234567890'}); + clock.tick(1000); + expect(server.requests).to.have.length(0); + + server.requests.length = 0; + expect(server.requests).to.have.length(0); + + eventType = constants.EVENTS.AUCTION_END; + nobidAnalytics.processServerResponse(JSON.stringify({disabled_auctionEnd: 1, disabled_bidWon: 0})); + disabled = nobidAnalytics.isAnalyticsDisabled(eventType); + expect(disabled).to.equal(true); + events.emit(eventType, {auctionId: '1234567890'}); + clock.tick(1000); + expect(server.requests).to.have.length(0); + disabled = nobidAnalytics.isAnalyticsDisabled(constants.EVENTS.BID_WON); + expect(disabled).to.equal(false); + events.emit(constants.EVENTS.BID_WON, {bidderCode: 'nobid'}); + clock.tick(1000); + expect(server.requests).to.have.length(1); + + done(); + }); + }); + describe('NoBid Carbonizer', function () { beforeEach(function () { sinon.stub(events, 'getEvents').returns([]); @@ -456,7 +558,8 @@ describe('NoBid Prebid Analytic', function () { let active = nobidCarbonizer.isActive(); expect(active).to.equal(false); - active = nobidCarbonizer.isActive(JSON.stringify({carbonizer_active: false})); + nobidAnalytics.processServerResponse(JSON.stringify({carbonizer_active: false})); + active = nobidCarbonizer.isActive(); expect(active).to.equal(false); nobidAnalytics.processServerResponse(JSON.stringify({carbonizer_active: true})); @@ -469,12 +572,12 @@ describe('NoBid Prebid Analytic', function () { let stored = nobidCarbonizer.getStoredLocalData(); expect(stored[nobidAnalytics.ANALYTICS_DATA_NAME]).to.contain(`{"carbonizer_active":true,"ts":`); clock.tick(5000); - active = nobidCarbonizer.isActive(adunits, true); + active = nobidCarbonizer.isActive(); expect(active).to.equal(false); nobidAnalytics.retentionSeconds = previousRetention; nobidAnalytics.processServerResponse(JSON.stringify({carbonizer_active: true})); - active = nobidCarbonizer.isActive(adunits, true); + active = nobidCarbonizer.isActive(); expect(active).to.equal(true); let adunits = [ From e13bd53ab5df06723c36eca8d20b5b7aa544dfd4 Mon Sep 17 00:00:00 2001 From: kidayu <154117971+kida-yuga@users.noreply.github.com> Date: Tue, 13 Feb 2024 02:00:31 +0900 Subject: [PATCH 244/246] Microad Bid Adapter: send gpid and other to our request. (#11076) * Microad Bid Adapter: add gpid and other * Microad Bid Adapter: use deepAccess * Microad Bid Adapter: To simple existing check --------- Co-authored-by: kida-yuga --- modules/microadBidAdapter.js | 20 +++ test/spec/modules/microadBidAdapter_spec.js | 190 ++++++++++++++++++++ 2 files changed, 210 insertions(+) diff --git a/modules/microadBidAdapter.js b/modules/microadBidAdapter.js index ed88dce757c..61aa9b795de 100644 --- a/modules/microadBidAdapter.js +++ b/modules/microadBidAdapter.js @@ -115,6 +115,26 @@ export const spec = { params['aids'] = JSON.stringify(aidsParams) } + const pbadslot = deepAccess(bid, 'ortb2Imp.ext.data.pbadslot'); + const gpid = deepAccess(bid, 'ortb2Imp.ext.gpid') || pbadslot; + if (gpid) { + params['gpid'] = gpid; + } + + if (pbadslot) { + params['pbadslot'] = pbadslot; + } + + const adservname = deepAccess(bid, 'ortb2Imp.ext.data.adserver.name'); + if (adservname) { + params['adservname'] = adservname; + } + + const adservadslot = deepAccess(bid, 'ortb2Imp.ext.data.adserver.adslot'); + if (adservadslot) { + params['adservadslot'] = adservadslot; + } + requests.push({ method: 'GET', url: ENDPOINT_URLS[ENVIRONMENT], diff --git a/test/spec/modules/microadBidAdapter_spec.js b/test/spec/modules/microadBidAdapter_spec.js index bd6d04a6312..9eb36d2fa6c 100644 --- a/test/spec/modules/microadBidAdapter_spec.js +++ b/test/spec/modules/microadBidAdapter_spec.js @@ -382,6 +382,196 @@ describe('microadBidAdapter', () => { }) }); }) + + describe('should send gpid', () => { + it('from gpid', () => { + const bidRequest = Object.assign({}, bidRequestTemplate, { + ortb2Imp: { + ext: { + tid: 'transaction-id', + gpid: '1111/2222', + data: { + pbadslot: '3333/4444' + } + } + } + }); + const requests = spec.buildRequests([bidRequest], bidderRequest) + requests.forEach(request => { + expect(request.data).to.deep.equal( + Object.assign({}, expectedResultTemplate, { + cbt: request.data.cbt, + gpid: '1111/2222', + pbadslot: '3333/4444' + }) + ); + }) + }) + + it('from pbadslot', () => { + const bidRequest = Object.assign({}, bidRequestTemplate, { + ortb2Imp: { + ext: { + tid: 'transaction-id', + data: { + pbadslot: '3333/4444' + } + } + } + }); + const requests = spec.buildRequests([bidRequest], bidderRequest) + requests.forEach(request => { + expect(request.data).to.deep.equal( + Object.assign({}, expectedResultTemplate, { + cbt: request.data.cbt, + gpid: '3333/4444', + pbadslot: '3333/4444' + }) + ); + }) + }) + }) + + const notGettingGpids = { + 'they are not existing': bidRequestTemplate, + 'they are blank': { + ortb2Imp: { + ext: { + tid: 'transaction-id', + gpid: '', + data: { + pbadslot: '' + } + } + } + } + } + + Object.entries(notGettingGpids).forEach(([testTitle, param]) => { + it(`should not send gpid because ${testTitle}`, () => { + const bidRequest = Object.assign({}, bidRequestTemplate, param); + const requests = spec.buildRequests([bidRequest], bidderRequest) + requests.forEach(request => { + expect(request.data).to.deep.equal( + Object.assign({}, expectedResultTemplate, { + cbt: request.data.cbt, + }) + ); + expect(request.data.gpid).to.be.undefined; + expect(request.data.pbadslot).to.be.undefined; + }) + }) + }) + + it('should send adservname', () => { + const bidRequest = Object.assign({}, bidRequestTemplate, { + ortb2Imp: { + ext: { + tid: 'transaction-id', + data: { + adserver: { + name: 'gam' + } + } + } + } + }); + const requests = spec.buildRequests([bidRequest], bidderRequest) + requests.forEach(request => { + expect(request.data).to.deep.equal( + Object.assign({}, expectedResultTemplate, { + cbt: request.data.cbt, + adservname: 'gam' + }) + ); + }) + }) + + const notGettingAdservnames = { + 'it is not existing': bidRequestTemplate, + 'it is blank': { + ortb2Imp: { + ext: { + tid: 'transaction-id', + data: { + adserver: { + name: '' + } + } + } + } + } + } + + Object.entries(notGettingAdservnames).forEach(([testTitle, param]) => { + it(`should not send adservname because ${testTitle}`, () => { + const bidRequest = Object.assign({}, bidRequestTemplate, param); + const requests = spec.buildRequests([bidRequest], bidderRequest) + requests.forEach(request => { + expect(request.data).to.deep.equal( + Object.assign({}, expectedResultTemplate, { + cbt: request.data.cbt, + }) + ); + expect(request.data.adservname).to.be.undefined; + }) + }) + }) + + it('should send adservadslot', () => { + const bidRequest = Object.assign({}, bidRequestTemplate, { + ortb2Imp: { + ext: { + tid: 'transaction-id', + data: { + adserver: { + adslot: '/1111/home' + } + } + } + } + }); + const requests = spec.buildRequests([bidRequest], bidderRequest) + requests.forEach(request => { + expect(request.data).to.deep.equal( + Object.assign({}, expectedResultTemplate, { + cbt: request.data.cbt, + adservadslot: '/1111/home' + }) + ); + }) + }) + + const notGettingAdservadslots = { + 'it is not existing': bidRequestTemplate, + 'it is blank': { + ortb2Imp: { + ext: { + tid: 'transaction-id', + data: { + adserver: { + adslot: '' + } + } + } + } + } + } + + Object.entries(notGettingAdservadslots).forEach(([testTitle, param]) => { + it(`should not send adservadslot because ${testTitle}`, () => { + const bidRequest = Object.assign({}, bidRequestTemplate, param); + const requests = spec.buildRequests([bidRequest], bidderRequest) + requests.forEach(request => { + expect(request.data).to.deep.equal( + Object.assign({}, expectedResultTemplate, { + cbt: request.data.cbt, + }) + ); + expect(request.data.adservadslot).to.be.undefined; + }) + }) + }) }); describe('interpretResponse', () => { From 55fa1a5c894593779b368d5877fb1583cf176e96 Mon Sep 17 00:00:00 2001 From: Philip Watson Date: Tue, 13 Feb 2024 06:30:22 +1300 Subject: [PATCH 245/246] StroeerCore Bid Adapter: add DSA support (#11083) --- modules/stroeerCoreBidAdapter.js | 11 +++- .../modules/stroeerCoreBidAdapter_spec.js | 60 +++++++++++++++++-- 2 files changed, 65 insertions(+), 6 deletions(-) diff --git a/modules/stroeerCoreBidAdapter.js b/modules/stroeerCoreBidAdapter.js index 307a50c7f78..89ed6995a7e 100644 --- a/modules/stroeerCoreBidAdapter.js +++ b/modules/stroeerCoreBidAdapter.js @@ -1,4 +1,4 @@ -import { buildUrl, deepAccess, generateUUID, getWindowSelf, getWindowTop, isEmpty, isStr, logWarn } from '../src/utils.js'; +import { buildUrl, deepAccess, deepSetValue, generateUUID, getWindowSelf, getWindowTop, isEmpty, isStr, logWarn } from '../src/utils.js'; import {registerBidder} from '../src/adapters/bidderFactory.js'; import {BANNER, VIDEO} from '../src/mediaTypes.js'; import {find} from '../src/polyfill.js'; @@ -76,6 +76,12 @@ export const spec = { }; } + const DSA_KEY = 'ortb2.regs.ext.dsa'; + const dsa = deepAccess(bidderRequest, DSA_KEY); + if (dsa) { + deepSetValue(basePayload, DSA_KEY, dsa); + } + const bannerBids = validBidRequests .filter(hasBanner) .map(mapToPayloadBannerBid); @@ -108,7 +114,8 @@ export const spec = { netRevenue: true, creativeId: '', meta: { - advertiserDomains: bidResponse.adomain + advertiserDomains: bidResponse.adomain, + dsa: bidResponse.dsa }, mediaType, }; diff --git a/test/spec/modules/stroeerCoreBidAdapter_spec.js b/test/spec/modules/stroeerCoreBidAdapter_spec.js index 2ed5f80f152..66e0da6ddf8 100644 --- a/test/spec/modules/stroeerCoreBidAdapter_spec.js +++ b/test/spec/modules/stroeerCoreBidAdapter_spec.js @@ -844,6 +844,39 @@ describe('stroeerCore bid adapter', function () { assert.nestedPropertyVal(bid, 'ban.fp.cur', 'EUR'); assert.deepNestedPropertyVal(bid, 'ban.fp.siz', [{w: 160, h: 60, p: 2.7}]); }); + + it('should add the DSA signals', () => { + const bidReq = buildBidderRequest(); + const dsa = { + dsarequired: 3, + pubrender: 0, + datatopub: 2, + transparency: [ + { + domain: 'testplatform.com', + dsaparams: [1], + }, + { + domain: 'testdomain.com', + dsaparams: [1, 2] + } + ] + } + const ortb2 = { + regs: { + ext: { + dsa + } + } + } + + bidReq.ortb2 = utils.deepClone(ortb2); + + const serverRequestInfo = spec.buildRequests(bidReq.bids, bidReq); + const sentOrtb2 = serverRequestInfo.data.ortb2; + + assert.deepEqual(sentOrtb2, ortb2); + }); }); }); }); @@ -882,13 +915,32 @@ describe('stroeerCore bid adapter', function () { assertStandardFieldsOnVideoBid(videoBidResponse, 'bid1', 'video', 800, 250, 4); }) - it('should add data to meta object', () => { + it('should add advertiser domains to meta object', () => { const response = buildBidderResponse(); response.bids[0] = Object.assign(response.bids[0], {adomain: ['website.org', 'domain.com']}); const result = spec.interpretResponse({body: response}); - assert.deepPropertyVal(result[0], 'meta', {advertiserDomains: ['website.org', 'domain.com']}); - // nothing provided for the second bid - assert.deepPropertyVal(result[1], 'meta', {advertiserDomains: undefined}); + assert.deepPropertyVal(result[0].meta, 'advertiserDomains', ['website.org', 'domain.com']); + assert.propertyVal(result[1].meta, 'advertiserDomains', undefined); + }); + + it('should add dsa info to meta object', () => { + const dsaResponse = { + behalf: 'AdvertiserA', + paid: 'AdvertiserB', + transparency: [{ + domain: 'dspexample.com', + dsaparams: [1, 2], + }], + adrender: 1 + }; + + const response = buildBidderResponse(); + response.bids[0] = Object.assign(response.bids[0], {dsa: utils.deepClone(dsaResponse)}); + + const result = spec.interpretResponse({body: response}); + + assert.deepPropertyVal(result[0].meta, 'dsa', dsaResponse); + assert.propertyVal(result[1].meta, 'dsa', undefined); }); }); From aa092c03b57ea5fff5c5a06c2f5d7cc97d371858 Mon Sep 17 00:00:00 2001 From: Christophe Oudar <90898980+github-christophe-oudar@users.noreply.github.com> Date: Tue, 13 Feb 2024 12:00:56 +0100 Subject: [PATCH 246/246] Teads adapter: add dsa info support in bid request & response (#11080) --- modules/teadsBidAdapter.js | 8 +++ test/spec/modules/teadsBidAdapter_spec.js | 63 ++++++++++++++++++++++- 2 files changed, 69 insertions(+), 2 deletions(-) diff --git a/modules/teadsBidAdapter.js b/modules/teadsBidAdapter.js index 1c12b0e3968..d03782611e4 100644 --- a/modules/teadsBidAdapter.js +++ b/modules/teadsBidAdapter.js @@ -101,6 +101,11 @@ export const spec = { payload.userAgentClientHints = userAgentClientHints; } + const dsa = deepAccess(bidderRequest, 'ortb2.regs.ext.dsa'); + if (dsa) { + payload.dsa = dsa; + } + const payloadString = JSON.stringify(payload); return { method: 'POST', @@ -138,6 +143,9 @@ export const spec = { if (bid.dealId) { bidResponse.dealId = bid.dealId } + if (bid?.ext?.dsa) { + bidResponse.meta.dsa = bid.ext.dsa; + } bidResponses.push(bidResponse); }); } diff --git a/test/spec/modules/teadsBidAdapter_spec.js b/test/spec/modules/teadsBidAdapter_spec.js index 98f706f5193..f26081b0cef 100644 --- a/test/spec/modules/teadsBidAdapter_spec.js +++ b/test/spec/modules/teadsBidAdapter_spec.js @@ -1005,6 +1005,45 @@ describe('teadsBidAdapter', () => { } }); } + + it('should add dsa info to payload if available', function () { + const bidRequestWithDsa = Object.assign({}, bidderRequestDefault, { + ortb2: { + regs: { + ext: { + dsa: { + dsarequired: '1', + pubrender: '2', + datatopub: '3', + transparency: [{ + domain: 'test.com', + dsaparams: [1, 2, 3] + }] + } + } + } + } + }); + + const requestWithDsa = spec.buildRequests(bidRequests, bidRequestWithDsa); + const payload = JSON.parse(requestWithDsa.data); + + expect(payload.dsa).to.exist; + expect(payload.dsa).to.deep.equal( + { + dsarequired: '1', + pubrender: '2', + datatopub: '3', + transparency: [{ + domain: 'test.com', + dsaparams: [1, 2, 3] + }] + } + ); + + const defaultRequest = spec.buildRequests(bidRequests, bidderRequestDefault); + expect(JSON.parse(defaultRequest.data).dsa).to.not.exist; + }); }); describe('interpretResponse', function() { @@ -1031,7 +1070,18 @@ describe('teadsBidAdapter', () => { 'width': 350, 'creativeId': 'fs3ff', 'placementId': 34, - 'dealId': 'ABC_123' + 'dealId': 'ABC_123', + 'ext': { + 'dsa': { + 'behalf': 'some-behalf', + 'paid': 'some-paid', + 'transparency': [{ + 'domain': 'test.com', + 'dsaparams': [1, 2, 3] + }], + 'adrender': 1 + } + } }] } }; @@ -1057,7 +1107,16 @@ describe('teadsBidAdapter', () => { 'currency': 'USD', 'netRevenue': true, 'meta': { - advertiserDomains: [] + advertiserDomains: [], + dsa: { + behalf: 'some-behalf', + paid: 'some-paid', + transparency: [{ + domain: 'test.com', + dsaparams: [1, 2, 3] + }], + adrender: 1 + } }, 'ttl': 360, 'ad': AD_SCRIPT,