From 6d107821514079ba4c545e228cd00854efa09177 Mon Sep 17 00:00:00 2001 From: Patrick McCann Date: Tue, 11 Mar 2025 16:20:22 -0400 Subject: [PATCH 01/92] Prebid 10 : Delete telaria --- test/spec/modules/telariaBidAdapter_spec.js | 315 -------------------- 1 file changed, 315 deletions(-) delete mode 100644 test/spec/modules/telariaBidAdapter_spec.js diff --git a/test/spec/modules/telariaBidAdapter_spec.js b/test/spec/modules/telariaBidAdapter_spec.js deleted file mode 100644 index 457dd568764..00000000000 --- a/test/spec/modules/telariaBidAdapter_spec.js +++ /dev/null @@ -1,315 +0,0 @@ -import {expect} from 'chai'; -import {newBidder} from 'src/adapters/bidderFactory.js'; -import {spec, getTimeoutUrl} from 'modules/telariaBidAdapter.js'; -import * as utils from 'src/utils.js'; - -const ENDPOINT = '.ads.tremorhub.com/ad/tag'; -const AD_CODE = 'ssp-!demo!-lufip'; -const SUPPLY_CODE = 'ssp-demo-rm6rh'; -const SIZES = [640, 480]; -const REQUEST = { - 'code': 'video1', - 'mediaTypes': { - 'video': { - 'playerSize': [[640, 480]], - 'context': 'instream' - } - }, - 'mediaType': 'video', - 'bids': [{ - 'bidder': 'telaria', - 'params': { - 'videoId': 'MyCoolVideo', - 'inclSync': true - } - }] -}; - -const REQUEST_WITH_SCHAIN = [{ - 'bidder': 'telaria', - 'params': { - 'videoId': 'MyCoolVideo', - 'inclSync': true, - 'schain': { - 'ver': '1.0', - 'complete': 1, - 'nodes': [ - { - 'asi': 'exchange1.com', - 'sid': '1234', - 'hp': 1, - 'rid': 'bid-request-1', - 'name': 'publisher', - 'domain': 'publisher.com' - }, - { - 'asi': 'exchange2.com', - 'sid': 'abcd', - 'hp': 1, - 'rid': 'bid-request-2', - 'name': 'intermediary', - 'domain': 'intermediary.com' - } - ] - } - } -}]; - -const BIDDER_REQUEST = { - 'refererInfo': { - 'referer': 'www.test.com' - }, - 'gdprConsent': { - 'consentString': 'BOJ/P2HOJ/P2HABABMAAAAAZ+A==', - 'gdprApplies': true - } -}; - -const RESPONSE = { - 'cur': 'USD', - 'id': '3dba13e35f3d42f998bc7e65fd871889', - 'seatbid': [{ - 'seat': 'TremorVideo', - 'bid': [{ - 'adomain': [], - 'price': 0.50000, - 'id': '3dba13e35f3d42f998bc7e65fd871889', - 'adm': '\n Tremor Video Test MP4 Creative \n\n \n\n\n\n\n\n \n\n \n\n', - 'impid': '1' - }] - }] -}; - -describe('TelariaAdapter', () => { - const adapter = newBidder(spec); - - describe('inherited functions', () => { - it('exists and is a function', () => { - expect(adapter.callBids).to.exist.and.to.be.a('function'); - }); - }); - - describe('isBidRequestValid', () => { - let bid = REQUEST.bids[0]; - - it('should return true when required params found', () => { - let tempBid = bid; - tempBid.params.adCode = 'ssp-!demo!-lufip'; - tempBid.params.supplyCode = 'ssp-demo-rm6rh'; - expect(spec.isBidRequestValid(bid)).to.equal(true); - }); - - it('should return true when required params found', () => { - let tempBid = bid; - delete tempBid.params; - tempBid.params = { - supplyCode: 'ssp-demo-rm6rh', - adCode: 'ssp-!demo!-lufip', - }; - - expect(spec.isBidRequestValid(tempBid)).to.equal(true); - }); - - it('should return false when required params are not passed', () => { - let tempBid = bid; - tempBid.params = {}; - expect(spec.isBidRequestValid(tempBid)).to.equal(false); - }); - }); - - describe('buildRequests', () => { - const stub = () => ([{ - mediaTypes: { - video: { - playerSize: [[640, 480]], - context: 'instream' - } - }, - bidder: 'tremor', - params: { - supplyCode: 'ssp-demo-rm6rh', - adCode: 'ssp-!demo!-lufip', - videoId: 'MyCoolVideo' - } - }]); - - const schainStub = REQUEST_WITH_SCHAIN; - - it('exists and is a function', () => { - expect(spec.buildRequests).to.exist.and.to.be.a('function'); - }); - - it('requires supply code & ad code to make a request', () => { - const tempRequest = spec.buildRequests(stub(), BIDDER_REQUEST); - expect(tempRequest.length).to.equal(1); - }); - - it('generates an array of requests with 4 params, method, url, bidId and vastUrl', () => { - const tempRequest = spec.buildRequests(stub(), BIDDER_REQUEST); - - expect(tempRequest.length).to.equal(1); - expect(tempRequest[0].method).to.equal('GET'); - expect(tempRequest[0].url).to.exist; - expect(tempRequest[0].bidId).to.equal(undefined); - expect(tempRequest[0].vastUrl).to.exist; - }); - - it('doesn\'t require player size but is highly recommended', () => { - let tempBid = stub(); - tempBid[0].mediaTypes.video.playerSize = null; - const tempRequest = spec.buildRequests(tempBid, BIDDER_REQUEST); - - expect(tempRequest.length).to.equal(1); - }); - - it('generates a valid request with sizes as an array of two elements', () => { - let tempBid = stub(); - tempBid[0].mediaTypes.video.playerSize = [640, 480]; - tempBid[0].params.adCode = 'ssp-!demo!-lufip'; - tempBid[0].params.supplyCode = 'ssp-demo-rm6rh'; - let builtRequests = spec.buildRequests(tempBid, BIDDER_REQUEST); - expect(builtRequests.length).to.equal(1); - }); - - it('requires ad code and supply code to make a request', () => { - let tempBid = stub(); - tempBid[0].params.adCode = null; - tempBid[0].params.supplyCode = null; - - const tempRequest = spec.buildRequests(tempBid, BIDDER_REQUEST); - - expect(tempRequest.length).to.equal(0); - }); - - it('converts the schain object into a tag param', () => { - let tempBid = schainStub; - tempBid[0].params.adCode = 'ssp-!demo!-lufip'; - tempBid[0].params.supplyCode = 'ssp-demo-rm6rh'; - let builtRequests = spec.buildRequests(tempBid, BIDDER_REQUEST); - expect(builtRequests.length).to.equal(1); - }); - - it('adds adUnitCode to the request url', () => { - const builtRequests = spec.buildRequests(stub(), BIDDER_REQUEST); - - expect(builtRequests.length).to.equal(1); - const parts = builtRequests[0].url.split('adCode='); - expect(parts.length).to.equal(2); - }); - - it('adds srcPageUrl to the request url', () => { - const builtRequests = spec.buildRequests(stub(), BIDDER_REQUEST); - - expect(builtRequests.length).to.equal(1); - const parts = builtRequests[0].url.split('srcPageUrl='); - expect(parts.length).to.equal(2); - }); - - it('adds srcPageUrl from params to the request only once', () => { - const tempBid = stub(); - tempBid[0].params.srcPageUrl = 'http://www.test.com'; - const builtRequests = spec.buildRequests(tempBid, BIDDER_REQUEST); - - expect(builtRequests.length).to.equal(1); - const parts = builtRequests[0].url.split('srcPageUrl='); - expect(parts.length).to.equal(2); - }); - }); - - describe('interpretResponse', () => { - const responseStub = RESPONSE; - const stub = [{ - mediaTypes: { - video: { - playerSize: [[640, 480]], - context: 'instream' - } - }, - bidder: 'tremor', - params: { - supplyCode: 'ssp-demo-rm6rh', - adCode: 'ssp-!demo!-lufip', - videoId: 'MyCoolVideo' - } - }]; - - it('should get correct bid response', () => { - let expectedResponseKeys = ['requestId', 'cpm', 'creativeId', 'vastXml', 'vastUrl', 'mediaType', 'width', 'height', 'currency', 'netRevenue', 'ttl', 'ad', 'meta']; - - let bidRequest = spec.buildRequests(stub, BIDDER_REQUEST)[0]; - bidRequest.bidId = '1234'; - let result = spec.interpretResponse({body: responseStub}, bidRequest); - expect(Object.keys(result[0])).to.have.members(expectedResponseKeys); - }); - - it('handles nobid responses', () => { - let tempResponse = responseStub; - tempResponse.seatbid = []; - - let bidRequest = spec.buildRequests(stub, BIDDER_REQUEST)[0]; - bidRequest.bidId = '1234'; - - let result = spec.interpretResponse({body: tempResponse}, bidRequest); - expect(result.length).to.equal(0); - }); - - it('handles invalid responses', () => { - let result = spec.interpretResponse(null, {bbidderCode: 'telaria'}); - expect(result.length).to.equal(0); - }); - - it('handles error responses', () => { - let result = spec.interpretResponse({body: {error: 'Invalid request'}}, {bbidderCode: 'telaria'}); - expect(result.length).to.equal(0); - }); - }); - - describe('getUserSyncs', () => { - const responses = [{body: RESPONSE}]; - responses[0].body.ext = { - telaria: { - userSync: [ - 'https://url.com', - 'https://url2.com' - ] - } - }; - - it('should get the correct number of sync urls', () => { - let urls = spec.getUserSyncs({pixelEnabled: true}, responses); - expect(urls.length).to.equal(2); - }); - }); - - describe('onTimeout', () => { - const timeoutData = [{ - adUnitCode: 'video1', - auctionId: 'd8d239f4-303a-4798-8c8c-dd3151ced4e7', - bidId: '2c749c0101ea92', - bidder: 'telaria', - params: [{ - adCode: 'ssp-!demo!-lufip', - supplyCode: 'ssp-demo-rm6rh', - mediaId: 'MyCoolVideo' - }] - }]; - - beforeEach(function() { - sinon.stub(utils, 'triggerPixel'); - }); - - afterEach(function() { - utils.triggerPixel.restore(); - }); - - it('should return a pixel url', () => { - let url = getTimeoutUrl(timeoutData); - assert(url); - }); - - it('should fire a pixel', () => { - expect(spec.onTimeout(timeoutData)).to.be.undefined; - expect(utils.triggerPixel.called).to.equal(true); - }); - }); -}); From 9ab17e099f7f7a3531f44f7de9facbcde32fcd33 Mon Sep 17 00:00:00 2001 From: CMDezz <53512796+CMDezz@users.noreply.github.com> Date: Wed, 12 Mar 2025 03:25:36 +0700 Subject: [PATCH 02/92] EClickAds: rename bidder from EClickAds to eClick (#12145) * EClickAds: rename bidder from EClickAds to eClick * Update eclickBidAdapter.js resuming tests --------- Co-authored-by: LeViet Co-authored-by: Patrick McCann --- ...ickadsBidAdapter.js => eclickBidAdapter.js} | 6 +++--- ...ickadsBidAdapter.md => eclickBidAdapter.md} | 10 +++++----- ...dapter_spec.js => eclickBidAdapter_spec.js} | 18 +++++++++--------- 3 files changed, 17 insertions(+), 17 deletions(-) rename modules/{eclickadsBidAdapter.js => eclickBidAdapter.js} (95%) rename modules/{eclickadsBidAdapter.md => eclickBidAdapter.md} (69%) rename test/spec/modules/{eclickadsBidAdapter_spec.js => eclickBidAdapter_spec.js} (94%) diff --git a/modules/eclickadsBidAdapter.js b/modules/eclickBidAdapter.js similarity index 95% rename from modules/eclickadsBidAdapter.js rename to modules/eclickBidAdapter.js index 27e3926afe3..151936c9847 100644 --- a/modules/eclickadsBidAdapter.js +++ b/modules/eclickBidAdapter.js @@ -3,8 +3,8 @@ import { convertOrtbRequestToProprietaryNative } from '../src/native.js'; import { registerBidder } from '../src/adapters/bidderFactory.js'; import { getDevice } from '../libraries/fpdUtils/deviceInfo.js'; -// ***** ECLICKADS ADAPTER ***** -export const BIDDER_CODE = 'eclickads'; +// **** ECLICK ADAPTER **** +export const BIDDER_CODE = 'eclick'; const DEFAULT_CURRENCY = ['USD']; const DEFAULT_TTL = 1000; export const ENDPOINT = 'https://g.eclick.vn/rtb_hb_request?fosp_uid='; @@ -70,7 +70,7 @@ export const spec = { netRevenue: bid.netRevenue, currency: bid.currency || DEFAULT_CURRENCY, adserverTargeting: { - hb_ad_eclickads: bid.ad, + hb_ad_eclick: bid.ad, }, }, ]; diff --git a/modules/eclickadsBidAdapter.md b/modules/eclickBidAdapter.md similarity index 69% rename from modules/eclickadsBidAdapter.md rename to modules/eclickBidAdapter.md index 39ba19d5249..17aa80fede8 100644 --- a/modules/eclickadsBidAdapter.md +++ b/modules/eclickBidAdapter.md @@ -1,12 +1,12 @@ # Overview -Module Name: EClickAds Bid Adapter +Module Name: eClick Bid Adapter Type: Bidder Adapter Maintainer: vietlv14@fpt.com # Description -This module connects to EClickAds exchange for bidding NATIVE ADS via prebid.js +This module connects to eClick exchange for bidding NATIVE ADS via prebid.js # Test Parameters @@ -36,7 +36,7 @@ var adUnits = [{ } }, bids: [{ - bidder: 'eclickads', + bidder: 'eclick', params: { zid:"7096" } @@ -46,10 +46,10 @@ var adUnits = [{ # Notes: -- EClickAdsBidAdapter need serveral params inside bidder config as following +- eClickBidAdapter need serveral params inside bidder config as following - user.myvne_id - site.orig_aid - site.fosp_aid - site.id - site.orig_aid -- EClickAdsBidAdapter will set bid.adserverTargeting.hb_ad_eclickads targeting key while submitting bid to AdServer +- eClickBidAdapter will set bid.adserverTargeting.hb_ad_eclick targeting key while submitting bid to AdServer diff --git a/test/spec/modules/eclickadsBidAdapter_spec.js b/test/spec/modules/eclickBidAdapter_spec.js similarity index 94% rename from test/spec/modules/eclickadsBidAdapter_spec.js rename to test/spec/modules/eclickBidAdapter_spec.js index aadb8f41a64..6c253d981a5 100644 --- a/test/spec/modules/eclickadsBidAdapter_spec.js +++ b/test/spec/modules/eclickBidAdapter_spec.js @@ -3,19 +3,19 @@ import { spec, ENDPOINT, BIDDER_CODE, -} from '../../../modules/eclickadsBidAdapter.js'; +} from '../../../modules/eclickBidAdapter.js'; import { NATIVE, BANNER, VIDEO } from '../../../src/mediaTypes.js'; import { deepClone } from '../../../src/utils.js'; import { config } from '../../../src/config.js'; -describe('eclickadsBidAdapter', () => { +describe('eclickBidAdapter', () => { const bidItem = { bidder: BIDDER_CODE, params: { zid: '7096', }, }; - const eclickadsBidderConfigData = { + const eclickBidderConfigData = { orig_aid: 'xqf7zdmg7the65ac.1718271138.des', fosp_aid: '1013000403', fosp_uid: '7aab24a4663258a2c1d76a08b20f7e6e', @@ -63,7 +63,7 @@ describe('eclickadsBidAdapter', () => { page: 'https://page.example.com/here.html', ref: 'https://ref.example.com', ext: { - data: eclickadsBidderConfigData, + data: eclickBidderConfigData, }, }, }, @@ -99,7 +99,7 @@ describe('eclickadsBidAdapter', () => { expect(request.method).to.be.exist; expect(request.method).equal('POST'); expect(request.url).to.be.exist; - expect(request.url).equal(ENDPOINT + eclickadsBidderConfigData.fosp_uid); + expect(request.url).equal(ENDPOINT + eclickBidderConfigData.fosp_uid); }); it('should return valid data format if bid array is valid', () => { expect(_data).to.be.an('object'); @@ -147,7 +147,7 @@ describe('eclickadsBidAdapter', () => { expect(_data.imp).to.have.lengthOf(bidList.length); }); - it('have to contain required params and correct format for sending to EClickAds', () => { + it('have to contain required params and correct format for sending to eClick', () => { const item = _data.imp[0]; expect(item.zid).to.be.an('string'); }); @@ -167,7 +167,7 @@ describe('eclickadsBidAdapter', () => { netRevenue: true, currency: ['VND'], cpm: 0.1844, - ad: 'eclickads_ad_p', + ad: 'eclick_ad_p', }, ], }; @@ -207,8 +207,8 @@ describe('eclickadsBidAdapter', () => { expect(offer.ttl).to.be.an('number'); expect(offer.cpm).to.be.an('number').greaterThan(0); expect(offer.adserverTargeting).to.be.an('object'); - expect(offer.adserverTargeting['hb_ad_eclickads']).to.be.an('string').that - .is.not.empty; + expect(offer.adserverTargeting['hb_ad_eclick']).to.be.an('string').that.is + .not.empty; }); }); }); From 460bdc9d77bfdde5ca20ee21abf7a0807e589e50 Mon Sep 17 00:00:00 2001 From: pratik-chavan-advertising-dot-com Date: Thu, 13 Mar 2025 08:09:00 -0400 Subject: [PATCH 03/92] Advertising.com Bid Adapter: Rename IMDS adapter (#12878) --- ...BidAdapter.js => advertisingBidAdapter.js} | 13 ++++---- ...BidAdapter.md => advertisingBidAdapter.md} | 8 ++--- ..._spec.js => advertisingBidAdapter_spec.js} | 32 +++++++++---------- 3 files changed, 27 insertions(+), 26 deletions(-) rename modules/{imdsBidAdapter.js => advertisingBidAdapter.js} (95%) rename modules/{imdsBidAdapter.md => advertisingBidAdapter.md} (86%) rename test/spec/modules/{imdsBidAdapter_spec.js => advertisingBidAdapter_spec.js} (98%) diff --git a/modules/imdsBidAdapter.js b/modules/advertisingBidAdapter.js similarity index 95% rename from modules/imdsBidAdapter.js rename to modules/advertisingBidAdapter.js index 0a0514df205..3f9bd6bcf04 100644 --- a/modules/imdsBidAdapter.js +++ b/modules/advertisingBidAdapter.js @@ -18,9 +18,10 @@ const BLOCKED_AD_SIZES = [ ]; const DEFAULT_MAX_TTL = 420; // 7 minutes export const spec = { - code: 'imds', + code: 'advertising', aliases: [ - { code: 'synacormedia' } + { code: 'synacormedia' }, + { code: 'imds' } ], supportedMediaTypes: [ BANNER, VIDEO ], sizeMap: {}, @@ -68,7 +69,7 @@ export const spec = { validBidReqs.forEach((bid, i) => { if (seatId && seatId !== bid.params.seatId) { - logWarn(`IMDS: there is an inconsistent seatId: ${bid.params.seatId} but only sending bid requests for ${seatId}, you should double check your configuration`); + logWarn(`Advertising.com: there is an inconsistent seatId: ${bid.params.seatId} but only sending bid requests for ${seatId}, you should double check your configuration`); return; } else { seatId = bid.params.seatId; @@ -76,7 +77,7 @@ export const spec = { const tagIdOrPlacementId = bid.params.tagId || bid.params.placementId; let pos = parseInt(bid.params.pos || deepAccess(bid.mediaTypes, 'video.pos'), 10); if (isNaN(pos)) { - logWarn(`IMDS: there is an invalid POS: ${bid.params.pos}`); + logWarn(`Advertising.com: there is an invalid POS: ${bid.params.pos}`); pos = 0; } const videoOrBannerKey = this.isVideoBid(bid) ? 'video' : 'banner'; @@ -162,7 +163,7 @@ export const spec = { }; const bidFloor = getBidFloor(bid, 'banner', '*'); if (isNaN(bidFloor)) { - logWarn(`IMDS: there is an invalid bid floor: ${bid.params.bidfloor}`); + logWarn(`Advertising.com: there is an invalid bid floor: ${bid.params.bidfloor}`); } if (bidFloor !== null && !isNaN(bidFloor)) { imp.bidfloor = bidFloor; @@ -186,7 +187,7 @@ export const spec = { }; const bidFloor = getBidFloor(bid, 'video', size); if (isNaN(bidFloor)) { - logWarn(`IMDS: there is an invalid bid floor: ${bid.params.bidfloor}`); + logWarn(`Advertising.com: there is an invalid bid floor: ${bid.params.bidfloor}`); } if (bidFloor !== null && !isNaN(bidFloor)) { diff --git a/modules/imdsBidAdapter.md b/modules/advertisingBidAdapter.md similarity index 86% rename from modules/imdsBidAdapter.md rename to modules/advertisingBidAdapter.md index 2a50868d726..bc4c7d8b2e1 100644 --- a/modules/imdsBidAdapter.md +++ b/modules/advertisingBidAdapter.md @@ -1,14 +1,14 @@ # Overview ``` -Module Name: iMedia Digital Services Bidder Adapter +Module Name: Advertising.com Bidder Adapter Module Type: Bidder Adapter Maintainer: eng-demand@imds.tv ``` # Description -The iMedia Digital Services adapter requires setup and approval from iMedia Digital Services. +The Advertising.com adapter requires setup and approval from Advertising.com. Please reach out to your account manager for more information. ### Google Ad Manager Video Creative @@ -30,7 +30,7 @@ https://track.technoratimedia.com/openrtb/tags?ID=%%PATTERN:hb_uuid_imds%%&AUCTI } }, bids: [{ - bidder: "imds", + bidder: "advertising", params: { seatId: "prebid", tagId: "demo1", @@ -49,7 +49,7 @@ https://track.technoratimedia.com/openrtb/tags?ID=%%PATTERN:hb_uuid_imds%%&AUCTI } }, bids: [{ - bidder: "imds", + bidder: "advertising", params: { seatId: "prebid", tagId: "demo1", diff --git a/test/spec/modules/imdsBidAdapter_spec.js b/test/spec/modules/advertisingBidAdapter_spec.js similarity index 98% rename from test/spec/modules/imdsBidAdapter_spec.js rename to test/spec/modules/advertisingBidAdapter_spec.js index 2911ee588c0..bb4edc9ab05 100644 --- a/test/spec/modules/imdsBidAdapter_spec.js +++ b/test/spec/modules/advertisingBidAdapter_spec.js @@ -1,10 +1,10 @@ import { assert, expect } from 'chai'; import { BANNER } from 'src/mediaTypes.js'; import { config } from 'src/config.js'; -import { spec } from 'modules/imdsBidAdapter.js'; +import { spec } from 'modules/advertisingBidAdapter.js'; import * as utils from 'src/utils.js'; -describe('imdsBidAdapter ', function () { +describe('advertisingBidAdapter ', function () { describe('isBidRequestValid', function () { let bid; beforeEach(function () { @@ -116,7 +116,7 @@ describe('imdsBidAdapter ', function () { }); describe('buildRequests', function () { let validBidRequestVideo = { - bidder: 'imds', + bidder: 'advertising', params: { seatId: 'prebid', tagId: '1234', @@ -141,7 +141,7 @@ describe('imdsBidAdapter ', function () { }; let bidderRequestVideo = { - bidderCode: 'imds', + bidderCode: 'advertising', auctionId: 'VideoAuctionId124', bidderRequestId: '117954d20d7c9c', auctionStart: 1553624929697, @@ -576,7 +576,7 @@ describe('imdsBidAdapter ', function () { }); it('should use all the video params in the impression request', function () { let validBidRequestVideo = { - bidder: 'imds', + bidder: 'advertising', params: { seatId: 'prebid', tagId: '1234', @@ -634,7 +634,7 @@ describe('imdsBidAdapter ', function () { }); it('should move any video params in the mediaTypes object to params.video object', function () { let validBidRequestVideo = { - bidder: 'imds', + bidder: 'advertising', params: { seatId: 'prebid', tagId: '1234', @@ -692,7 +692,7 @@ describe('imdsBidAdapter ', function () { }); it('should create params.video object if not present on bid request and move any video params in the mediaTypes object to it', function () { let validBidRequestVideo = { - bidder: 'imds', + bidder: 'advertising', params: { seatId: 'prebid', tagId: '1234' @@ -790,7 +790,7 @@ describe('imdsBidAdapter ', function () { describe('Bid Requests with placementId should be backward compatible ', function () { let validVideoBidReq = { - bidder: 'imds', + bidder: 'advertising', params: { seatId: 'prebid', placementId: 'demo1', @@ -831,7 +831,7 @@ describe('imdsBidAdapter ', function () { refererInfo: { referer: 'http://localhost:9999/' }, - bidderCode: 'imds', + bidderCode: 'advertising', auctionId: 'f8a75621-d672-4cbb-9275-3db7d74fb110' }; @@ -852,7 +852,7 @@ describe('imdsBidAdapter ', function () { describe('Bid Requests with schain object ', function () { let validBidReq = { - bidder: 'imds', + bidder: 'advertising', params: { seatId: 'prebid', tagId: 'demo1', @@ -894,12 +894,12 @@ describe('imdsBidAdapter ', function () { refererInfo: { referer: 'http://localhost:9999/' }, - bidderCode: 'imds', + bidderCode: 'advertising', auctionId: 'f8a75621-d672-4cbb-9275-3db7d74fb110', bidderRequestId: '16d438671bfbec', bids: [ { - bidder: 'imds', + bidder: 'advertising', params: { seatId: 'prebid', tagId: 'demo1', @@ -1394,7 +1394,7 @@ describe('imdsBidAdapter ', function () { describe('Bid Requests with price module should use if available', function () { let validVideoBidRequest = { - bidder: 'imds', + bidder: 'advertising', params: { bidfloor: '0.50', seatId: 'prebid', @@ -1437,7 +1437,7 @@ describe('imdsBidAdapter ', function () { refererInfo: { referer: 'http://localhost:9999/' }, - bidderCode: 'imds', + bidderCode: 'advertising', auctionId: 'f8a75621-d672-4cbb-9275-3db7d74fb110' }; @@ -1464,7 +1464,7 @@ describe('imdsBidAdapter ', function () { describe('Bid Requests with gpid or anything in bid.ext should use if available', function () { let validVideoBidRequest = { - bidder: 'imds', + bidder: 'advertising', params: { seatId: 'prebid', placementId: 'demo1', @@ -1521,7 +1521,7 @@ describe('imdsBidAdapter ', function () { refererInfo: { referer: 'http://localhost:9999/' }, - bidderCode: 'imds', + bidderCode: 'advertising', auctionId: 'f8a75621-d672-4cbb-9275-3db7d74fb110' }; From 8820f358213832093cf43087b73b4d24c2e1fe85 Mon Sep 17 00:00:00 2001 From: Patrick McCann Date: Thu, 13 Mar 2025 08:23:45 -0400 Subject: [PATCH 04/92] Prebid 10: Delete modules/cleanmedianetBidAdapter.js (#12870) * Prebid 10: Delete modules/cleanmedianetBidAdapter.js * Delete modules/cleanmedianetBidAdapter.md * Delete test/spec/modules/cleanmedianetBidAdapter_spec.js --- modules/cleanmedianetBidAdapter.js | 379 ----------- modules/cleanmedianetBidAdapter.md | 112 ---- .../modules/cleanmedianetBidAdapter_spec.js | 632 ------------------ 3 files changed, 1123 deletions(-) delete mode 100644 modules/cleanmedianetBidAdapter.js delete mode 100644 modules/cleanmedianetBidAdapter.md delete mode 100644 test/spec/modules/cleanmedianetBidAdapter_spec.js diff --git a/modules/cleanmedianetBidAdapter.js b/modules/cleanmedianetBidAdapter.js deleted file mode 100644 index 01178d63872..00000000000 --- a/modules/cleanmedianetBidAdapter.js +++ /dev/null @@ -1,379 +0,0 @@ -import { - deepAccess, - deepSetValue, - getDNT, - inIframe, - isArray, - isFn, - isNumber, - isPlainObject, - isStr, - logError, - logWarn -} from '../src/utils.js'; -import {registerBidder} from '../src/adapters/bidderFactory.js'; -import {Renderer} from '../src/Renderer.js'; -import {BANNER, VIDEO} from '../src/mediaTypes.js'; -import {includes} from '../src/polyfill.js'; - -const ENDPOINTS = { - 'cleanmedianet': 'https://bidder.cleanmediaads.com' -}; - -const DEFAULT_TTL = 360; - -export const helper = { - getTopFrame: function () { - try { - return window.top === window ? 1 : 0; - } catch (e) { - } - return 0; - }, - startsWith: function (str, search) { - return str.substr(0, search.length) === search; - }, - getMediaType: function (bid) { - if (bid.ext) { - if (bid.ext.media_type) { - return bid.ext.media_type.toLowerCase(); - } else if (bid.ext.vast_url) { - return VIDEO; - } else { - return BANNER; - } - } - return BANNER; - }, - getBidFloor(bid) { - if (!isFn(bid.getFloor)) { - return bid.params.bidfloor ? bid.params.bidfloor : null; - } - - let bidFloor = bid.getFloor({ - mediaType: '*', - size: '*', - currency: 'USD' - }); - - if (isPlainObject(bidFloor) && !isNaN(bidFloor.floor) && bidFloor.currency === 'USD') { - return bidFloor.floor; - } - - return null; - } -}; - -export const spec = { - code: 'cleanmedianet', - aliases: [], - supportedMediaTypes: ['banner', 'video'], - - isBidRequestValid: function (bid) { - return !!bid.params.supplyPartnerId && isStr(bid.params.supplyPartnerId) && - (!bid.params['rtbEndpoint'] || isStr(bid.params['rtbEndpoint'])) && - (!bid.params.bidfloor || isNumber(bid.params.bidfloor)) && - (!bid.params['adpos'] || isNumber(bid.params['adpos'])) && - (!bid.params['protocols'] || Array.isArray(bid.params['protocols'])) && - (!bid.params.instl || bid.params.instl === 0 || bid.params.instl === 1); - }, - - buildRequests: function (validBidRequests, bidderRequest) { - return validBidRequests.map(bidRequest => { - const {adUnitCode, bidId, mediaTypes, params, sizes} = bidRequest; - const baseEndpoint = (params['rtbEndpoint'] || ENDPOINTS['cleanmedianet']).replace(/^http:/, 'https:'); - const rtbEndpoint = `${baseEndpoint}/r/${params.supplyPartnerId}/bidr?rformat=open_rtb&reqformat=rtb_json&bidder=prebid` + (params.query ? '&' + params.query : ''); - const rtbBidRequest = { - id: bidId, - site: { - domain: bidderRequest.refererInfo.domain, - page: bidderRequest.refererInfo.page, - ref: bidderRequest.refererInfo.ref - }, - device: { - ua: navigator.userAgent, - dnt: getDNT() ? 1 : 0, - h: screen.height, - w: screen.width, - language: navigator.language - }, - imp: [], - ext: {}, - user: {ext: {}}, - source: {ext: {}}, - regs: {ext: {}} - }; - - const gdprConsent = getGdprConsent(bidderRequest); - rtbBidRequest.ext.gdpr_consent = gdprConsent; - deepSetValue(rtbBidRequest, 'regs.ext.gdpr', gdprConsent.consent_required === true ? 1 : 0); - deepSetValue(rtbBidRequest, 'user.ext.consent', gdprConsent.consent_string); - - if (validBidRequests[0].schain) { - deepSetValue(rtbBidRequest, 'source.ext.schain', validBidRequests[0].schain); - } - - if (bidderRequest && bidderRequest.uspConsent) { - deepSetValue(rtbBidRequest, 'regs.ext.us_privacy', bidderRequest.uspConsent); - } - - const imp = { - id: bidId, - instl: deepAccess(bidderRequest.ortb2Imp, 'instl') === 1 || params.instl === 1 ? 1 : 0, - tagid: adUnitCode, - bidfloor: helper.getBidFloor(bidRequest) || 0, - bidfloorcur: 'USD', - secure: 1 - }; - - const hasFavoredMediaType = - params.favoredMediaType && includes(this.supportedMediaTypes, params.favoredMediaType); - - if (!mediaTypes || mediaTypes.banner) { - if (!hasFavoredMediaType || params.favoredMediaType === BANNER) { - const bannerImp = Object.assign({}, imp, { - banner: { - w: sizes.length ? sizes[0][0] : 300, - h: sizes.length ? sizes[0][1] : 250, - pos: deepAccess(bidderRequest, 'mediaTypes.banner.pos') || params.pos || 0, - topframe: inIframe() ? 0 : 1 - } - }); - rtbBidRequest.imp.push(bannerImp); - } - } - - if (mediaTypes && mediaTypes.video) { - if (!hasFavoredMediaType || params.favoredMediaType === VIDEO) { - const playerSize = mediaTypes.video.playerSize || sizes; - const videoImp = Object.assign({}, imp, { - video: { - protocols: bidRequest.mediaTypes.video.protocols || params.protocols || [1, 2, 3, 4, 5, 6], - pos: deepAccess(bidRequest, 'mediaTypes.video.pos') || params.pos || 0, - ext: { - context: mediaTypes.video.context - }, - mimes: bidRequest.mediaTypes.video.mimes, - maxduration: bidRequest.mediaTypes.video.maxduration, - api: bidRequest.mediaTypes.video.api, - skip: bidRequest.mediaTypes.video.skip || bidRequest.params.video.skip, - plcmt: bidRequest.mediaTypes.video.plcmt || bidRequest.params.video.plcmt, - minduration: bidRequest.mediaTypes.video.minduration || bidRequest.params.video.minduration, - playbackmethod: bidRequest.mediaTypes.video.playbackmethod || bidRequest.params.video.playbackmethod, - startdelay: bidRequest.mediaTypes.video.startdelay || bidRequest.params.video.startdelay - } - }); - - if (isArray(playerSize[0])) { - videoImp.video.w = playerSize[0][0]; - videoImp.video.h = playerSize[0][1]; - } else if (isNumber(playerSize[0])) { - videoImp.video.w = playerSize[0]; - videoImp.video.h = playerSize[1]; - } else { - videoImp.video.w = 300; - videoImp.video.h = 250; - } - - rtbBidRequest.imp.push(videoImp); - } - } - - let eids = []; - if (bidRequest && bidRequest.userId) { - addExternalUserId(eids, deepAccess(bidRequest, `userId.id5id.uid`), 'id5-sync.com', 'ID5ID'); - addExternalUserId(eids, deepAccess(bidRequest, `userId.tdid`), 'adserver.org', 'TDID'); - addExternalUserId(eids, deepAccess(bidRequest, `userId.idl_env`), 'liveramp.com', 'idl'); - } - if (eids.length > 0) { - rtbBidRequest.user.ext.eids = eids; - } - - if (rtbBidRequest.imp.length === 0) { - return; - } - - return { - method: 'POST', - url: rtbEndpoint, - data: rtbBidRequest, - bidRequest - }; - }); - }, - - interpretResponse: function (serverResponse, bidRequest) { - const response = serverResponse && serverResponse.body; - if (!response) { - logError('empty response'); - return []; - } - - const bids = response.seatbid.reduce((acc, seatBid) => acc.concat(seatBid.bid), []); - let outBids = []; - - bids.forEach(bid => { - const outBid = { - requestId: bidRequest.bidRequest.bidId, - cpm: bid.price, - width: bid.w, - height: bid.h, - ttl: DEFAULT_TTL, - creativeId: bid.crid || bid.adid, - netRevenue: true, - currency: bid.cur || response.cur, - mediaType: helper.getMediaType(bid), - }; - - if (bid.adomain && bid.adomain.length) { - outBid.meta = { - advertiserDomains: bid.adomain - } - } - - if (deepAccess(bidRequest.bidRequest, 'mediaTypes.' + outBid.mediaType)) { - if (outBid.mediaType === BANNER) { - outBids.push(Object.assign({}, outBid, {ad: bid.adm})); - } else if (outBid.mediaType === VIDEO) { - const context = deepAccess(bidRequest.bidRequest, 'mediaTypes.video.context'); - outBids.push(Object.assign({}, outBid, { - vastUrl: bid.ext.vast_url, - vastXml: bid.adm, - renderer: context === 'outstream' ? newRenderer(bidRequest.bidRequest, bid) : undefined - })); - } - } - }); - return outBids; - }, - - getUserSyncs: function (syncOptions, serverResponses, gdprConsent, uspConsent) { - const syncs = []; - let gdprApplies = false; - let consentString = ''; - let uspConsentString = ''; - - if (gdprConsent && (typeof gdprConsent.gdprApplies === 'boolean')) { - gdprApplies = gdprConsent.gdprApplies; - } - let gdpr = gdprApplies ? 1 : 0; - - if (gdprApplies && gdprConsent.consentString) { - consentString = encodeURIComponent(gdprConsent.consentString); - } - - if (uspConsent) { - uspConsentString = encodeURIComponent(uspConsent); - } - - const macroValues = { - gdpr: gdpr, - consent: consentString, - uspConsent: uspConsentString - }; - - serverResponses.forEach(resp => { - if (resp.body) { - const bidResponse = resp.body; - if (bidResponse.ext && Array.isArray(bidResponse.ext['utrk'])) { - bidResponse.ext['utrk'] - .forEach(pixel => { - const url = replaceMacros(pixel.url, macroValues); - syncs.push({type: pixel.type, url}); - }); - } - - if (Array.isArray(bidResponse.seatbid)) { - bidResponse.seatbid.forEach(seatBid => { - if (Array.isArray(seatBid.bid)) { - seatBid.bid.forEach(bid => { - if (bid.ext && Array.isArray(bid.ext['utrk'])) { - bid.ext['utrk'] - .forEach(pixel => { - const url = replaceMacros(pixel.url, macroValues); - syncs.push({type: pixel.type, url}); - }); - } - }); - } - }); - } - } - }); - - return syncs; - } -}; - -function newRenderer(bidRequest, bid, rendererOptions = {}) { - const renderer = Renderer.install({ - url: (bidRequest.params && bidRequest.params.rendererUrl) || (bid.ext && bid.ext.renderer_url) || 'https://s.gamoshi.io/video/latest/renderer.js', - config: rendererOptions, - loaded: false, - }); - try { - renderer.setRender(renderOutstream); - } catch (err) { - logWarn('Prebid Error calling setRender on renderer', err); - } - return renderer; -} - -function renderOutstream(bid) { - bid.renderer.push(() => { - const unitId = bid.adUnitCode + '/' + bid.adId; - window['GamoshiPlayer'].renderAd({ - id: unitId, - debug: window.location.href.indexOf('pbjsDebug') >= 0, - placement: document.getElementById(bid.adUnitCode), - width: bid.width, - height: bid.height, - events: { - ALL_ADS_COMPLETED: () => window.setTimeout(() => { - window['GamoshiPlayer'].removeAd(unitId); - }, 300) - }, - vastUrl: bid.vastUrl, - vastXml: bid.vastXml - }); - }); -} - -function addExternalUserId(eids, value, source, rtiPartner) { - if (isStr(value)) { - eids.push({ - source, - uids: [{ - id: value, - ext: { - rtiPartner - } - }] - }); - } -} - -function replaceMacros(url, macros) { - return url - .replace('[GDPR]', macros.gdpr) - .replace('[CONSENT]', macros.consent) - .replace('[US_PRIVACY]', macros.uspConsent); -} - -function getGdprConsent(bidderRequest) { - const gdprConsent = bidderRequest.gdprConsent; - - if (gdprConsent && gdprConsent.consentString && gdprConsent.gdprApplies) { - return { - consent_string: gdprConsent.consentString, - consent_required: gdprConsent.gdprApplies - }; - } - - return { - consent_required: false, - consent_string: '', - }; -} - -registerBidder(spec); diff --git a/modules/cleanmedianetBidAdapter.md b/modules/cleanmedianetBidAdapter.md deleted file mode 100644 index ee4e049e8d6..00000000000 --- a/modules/cleanmedianetBidAdapter.md +++ /dev/null @@ -1,112 +0,0 @@ -# Overview - -``` -Module Name: CleanMedia Bid Adapter -Module Type: Bidder Adapter -Maintainer: dev@CleanMedia.net -``` - -# Description - -Connects to CleanMedia's Programmatic advertising platform as a service. - -CleanMedia bid adapter supports Banner & Outstream Video. The *only* required parameter (in the `params` section) is the `supplyPartnerId` parameter. - -# Test Parameters -``` -var adUnits = [ - - // Banner adUnit - { - code: 'banner-div', - sizes: [[300, 250]], - bids: [{ - bidder: 'cleanmedianet', - params: { - - // ID of the supply partner you created in the CleanMedia dashboard - supplyPartnerId: '1253', - - // OPTIONAL: custom bid floor - bidfloor: 0.01, - - // OPTIONAL: if you know the ad position on the page, specify it here - // (this corresponds to "Ad Position" in OpenRTB 2.3, section 5.4) - //adpos: 1, - - // OPTIONAL: whether this is an interstitial placement (0 or 1) - // (see "instl" property in "Imp" object in the OpenRTB 2.3, section 3.2.2) - //instl: 0 - } - }] - }, - - // Video outstream adUnit - { - code: 'video-outstream', - mediaTypes: { - video: { - context: 'outstream', - playerSize: [300, 250] - } - }, - bids: [ { - bidder: 'CleanMedia', - params: { - - // ID of the supply partner you created in the dashboard - supplyPartnerId: '1254', - - // OPTIONAL: custom bid floor - bidfloor: 0.01, - - // OPTIONAL: if you know the ad position on the page, specify it here - // (this corresponds to "Ad Position" in OpenRTB 2.3, section 5.4) - //adpos: 1, - - // OPTIONAL: whether this is an interstitial placement (0 or 1) - // (see "instl" property in "Imp" object in the OpenRTB 2.3, section 3.2.2) - //instl: 0 - } - }] - }, - - // Multi-Format adUnit - { - code: 'banner-div', - mediaTypes: { - video: { - context: 'outstream', - playerSize: [300, 250] - }, - banner: { - sizes: [[300, 250]] - } - }, - bids: [{ - bidder: 'CleanMedia', - params: { - - // ID of the supply partner you created in the CleanMedia dashboard - supplyPartnerId: '1253', - - // OPTIONAL: custom bid floor - bidfloor: 0.01, - - // OPTIONAL: if you know the ad position on the page, specify it here - // (this corresponds to "Ad Position" in OpenRTB 2.3, section 5.4) - //adpos: 1, - - // OPTIONAL: whether this is an interstitial placement (0 or 1) - // (see "instl" property in "Imp" object in the OpenRTB 2.3, section 3.2.2) - //instl: 0, - - // OPTIONAL: enable enforcement bids of a specific media type (video, banner) - // in this ad placement - // query: 'key1=value1&k2=value2', - // favoredMediaType: 'video', - } - }] - }, -]; -``` diff --git a/test/spec/modules/cleanmedianetBidAdapter_spec.js b/test/spec/modules/cleanmedianetBidAdapter_spec.js deleted file mode 100644 index 58270438772..00000000000 --- a/test/spec/modules/cleanmedianetBidAdapter_spec.js +++ /dev/null @@ -1,632 +0,0 @@ -import {expect} from 'chai'; -import {spec, helper} from 'modules/cleanmedianetBidAdapter.js'; -import * as utils from 'src/utils.js'; -import {newBidder} from '../../../src/adapters/bidderFactory.js'; -import {deepClone} from 'src/utils'; - -const supplyPartnerId = '123'; -const adapter = newBidder(spec); -const TTL = 360; - -describe('CleanmedianetAdapter', () => { - let schainConfig, - bidRequest, - bannerBidRequest, - videoBidRequest, - rtbResponse, - videoResponse, - gdprConsent; - - beforeEach(() => { - schainConfig = { - 'ver': '1.0', - 'complete': 1, - 'nodes': [ - { - 'asi': 'indirectseller.com', - 'sid': '00001', - 'hp': 1 - }, - - { - 'asi': 'indirectseller-2.com', - 'sid': '00002', - 'hp': 2 - } - ] - }; - - bidRequest = { - 'adUnitCode': 'adunit-code', - 'auctionId': '1d1a030790a475', - 'mediaTypes': { - banner: {} - }, - 'params': { - 'supplyPartnerId': supplyPartnerId - }, - 'sizes': [[300, 250], [300, 600]], - 'transactionId': 'a123456789', - refererInfo: {referer: 'http://examplereferer.com'}, - gdprConsent: { - consentString: 'some string', - gdprApplies: true - }, - schain: schainConfig, - uspConsent: 'cleanmediaCCPA' - }; - - bannerBidRequest = { - 'adUnitCode': 'adunit-code', - 'auctionId': '1d1a030790a475', - 'mediaTypes': { - banner: {} - }, - 'params': { - 'supplyPartnerId': supplyPartnerId - }, - 'sizes': [[300, 250], [300, 600]], - 'transactionId': 'a123456789', - 'bidId': '111', - refererInfo: {referer: 'http://examplereferer.com'} - }; - - videoBidRequest = { - 'adUnitCode': 'adunit-code', - 'auctionId': '1d1a030790a475', - 'mediaTypes': { - video: {} - }, - 'params': { - 'supplyPartnerId': supplyPartnerId - }, - 'sizes': [[300, 250], [300, 600]], - 'transactionId': 'a123456789', - 'bidId': '111', - refererInfo: {referer: 'http://examplereferer.com'} - }; - - rtbResponse = { - 'id': 'imp_5b05b9fde4b09084267a556f', - 'bidid': 'imp_5b05b9fde4b09084267a556f', - 'cur': 'USD', - 'ext': { - 'utrk': [ - {'type': 'iframe', 'url': '//bidder.cleanmediaads.com/user/sync/1?gdpr=[GDPR]&consent=[CONSENT]&usp=[US_PRIVACY]'}, - {'type': 'image', 'url': '//bidder.cleanmediaads.com/user/sync/2'} - ] - }, - 'seatbid': [ - { - 'seat': 'seat1', - 'group': 0, - 'bid': [ - { - 'id': '0', - 'impid': '1', - 'price': 2.016, - 'adid': '579ef31bfa788b9d2000d562', - 'nurl': 'https://bidder.cleanmediaads.com/pix/monitoring/win_notice/imp_5b05b9fde4b09084267a556f/im.gif?r=imp_5b05b9fde4b09084267a556f&i=1&a=579ef31bfa788b9d2000d562&b=0', - 'adm': '', - 'adomain': ['aaa.com'], - 'cid': '579ef268fa788b9d2000d55c', - 'crid': '579ef31bfa788b9d2000d562', - 'attr': [], - 'h': 600, - 'w': 120, - 'ext': { - 'vast_url': 'http://my.vast.com', - 'utrk': [ - {'type': 'iframe', 'url': '//p.partner1.io/user/sync/1'} - ] - } - } - ] - }, - { - 'seat': 'seat2', - 'group': 0, - 'bid': [ - { - 'id': '1', - 'impid': '1', - 'price': 3, - 'adid': '542jlhdfd2112jnjf3x', - 'nurl': 'https://bidder.cleanmediaads.com/pix/monitoring/win_notice/imp_5b05b9fde4b09084267a556f/im.gif?r=imp_5b05b9fde4b09084267a556f&i=1&a=579ef31bfa788b9d2000d562&b=0', - 'adm': ' ', - 'adomain': ['bbb.com'], - 'cid': 'fgdlwjh2498ydjhg1', - 'crid': 'kjh34297ydh2133d', - 'attr': [], - 'h': 250, - 'w': 300, - 'ext': { - 'utrk': [ - {'type': 'image', 'url': '//p.partner2.io/user/sync/1'} - ] - } - } - ] - } - ] - }; - - videoResponse = { - 'id': '64f32497-b2f7-48ec-9205-35fc39894d44', - 'bidid': 'imp_5c24924de4b0d106447af333', - 'cur': 'USD', - 'seatbid': [ - { - 'seat': '3668', - 'group': 0, - 'bid': [ - { - 'id': 'gb_1', - 'impid': 'afbb5852-7cea-4a81-aa9a-a41aab505c23', - 'price': 5.0, - 'adid': '1274', - 'nurl': 'https://bidder.cleanmediaads.com/pix/1275/win_notice/imp_5c24924de4b0d106447af333/im.gif?r=imp_5c24924de4b0d106447af333&i=afbb5852-7cea-4a81-aa9a-a41aab505c23&a=1274&b=gb_1', - 'adomain': [], - 'adm': '\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n', - 'cid': '3668', - 'crid': '1274', - 'cat': [], - 'attr': [], - 'h': 250, - 'w': 300, - 'ext': { - 'vast_url': 'https://bidder.cleanmediaads.com/pix/1275/vast_o/imp_5c24924de4b0d106447af333/im.xml?r=imp_5c24924de4b0d106447af333&i=afbb5852-7cea-4a81-aa9a-a41aab505c23&a=1274&b=gb_1&w=300&h=250&vatu=aHR0cHM6Ly9zdGF0aWMuZ2FtYmlkLmlvL2RlbW8vdmFzdC54bWw&vwarv', - 'imptrackers': [ - 'https://bidder.cleanmediaads.com/pix/1275/imp/imp_5c24924de4b0d106447af333/im.gif?r=imp_5c24924de4b0d106447af333&i=afbb5852-7cea-4a81-aa9a-a41aab505c23&a=1274&b=gb_1'] - } - } - ] - } - ], - 'ext': { - 'utrk': [{ - 'type': 'image', - 'url': 'https://bidder.cleanmediaads.com/pix/1275/scm?cb=1545900621675&gdpr=[GDPR]&consent=[CONSENT]&us_privacy=[US_PRIVACY]' - }] - } - }; - - gdprConsent = { - gdprApplies: true, - consentString: 'consent string' - }; - }); - - describe('Get top Frame', () => { - it('check if you are in the top frame', () => { - expect(helper.getTopFrame()).to.equal(0); - }); - }); - - describe('Is String start with search', () => { - it('check if a string started with', () => { - expect(helper.startsWith('cleanmedia.net', 'clea')).to.equal(true); - }); - }); - - describe('inherited functions', () => { - it('exists and is a function', () => { - expect(adapter.callBids).to.exist.and.to.be.a('function'); - }); - }); - - describe('isBidRequestValid', () => { - it('should validate supply-partner ID', () => { - expect(spec.isBidRequestValid({params: {}})).to.equal(false); - expect(spec.isBidRequestValid({params: {supplyPartnerId: 123}})).to.equal(false); - expect(spec.isBidRequestValid({params: {supplyPartnerId: '123'}})).to.equal(true); - }); - - it('should validate RTB endpoint', () => { - expect(spec.isBidRequestValid({params: {supplyPartnerId: '123'}})).to.equal(true); // RTB endpoint has a default - expect(spec.isBidRequestValid({params: {supplyPartnerId: '123', rtbEndpoint: 123}})).to.equal(false); - expect(spec.isBidRequestValid({ - params: { - supplyPartnerId: '123', - rtbEndpoint: 'https://some.url.com' - } - })).to.equal(true); - }); - - it('should validate bid floor', () => { - expect(spec.isBidRequestValid({params: {supplyPartnerId: '123'}})).to.equal(true); // bidfloor has a default - expect(spec.isBidRequestValid({params: {supplyPartnerId: '123', bidfloor: '123'}})).to.equal(false); - expect(spec.isBidRequestValid({params: {supplyPartnerId: '123', bidfloor: 0.1}})).to.equal(true); - - const getFloorResponse = {currency: 'USD', floor: 5}; - let testBidRequest = deepClone(bidRequest); - let request = spec.buildRequests([testBidRequest], bidRequest)[0]; - - // 1. getBidFloor not exist AND bidfloor not exist - return 0 - let payload = request.data; - expect(payload.imp[0].bidfloor).to.exist.and.equal(0); - - // 2. getBidFloor not exist AND bidfloor exist - use bidfloor property - testBidRequest = deepClone(bidRequest); - testBidRequest.params = { - 'bidfloor': 0.3 - }; - request = spec.buildRequests([testBidRequest], bidRequest)[0]; - payload = request.data; - expect(payload.imp[0].bidfloor).to.exist.and.to.equal(0.3) - - // 3. getBidFloor exist AND bidfloor not exist - use getFloor method - testBidRequest = deepClone(bidRequest); - testBidRequest.getFloor = () => getFloorResponse; - request = spec.buildRequests([testBidRequest], bidRequest)[0]; - payload = request.data; - expect(payload.imp[0].bidfloor).to.exist.and.to.equal(5) - - // 4. getBidFloor exist AND bidfloor exist -> use getFloor method - testBidRequest = deepClone(bidRequest); - testBidRequest.getFloor = () => getFloorResponse; - testBidRequest.params = { - 'bidfloor': 0.3 - }; - request = spec.buildRequests([testBidRequest], bidRequest)[0]; - payload = request.data; - expect(payload.imp[0].bidfloor).to.exist.and.to.equal(5) - }); - - it('should validate adpos', () => { - expect(spec.isBidRequestValid({params: {supplyPartnerId: '123'}})).to.equal(true); // adpos has a default - expect(spec.isBidRequestValid({params: {supplyPartnerId: '123', adpos: '123'}})).to.equal(false); - expect(spec.isBidRequestValid({params: {supplyPartnerId: '123', adpos: 0.1}})).to.equal(true); - }); - - it('should validate instl', () => { - expect(spec.isBidRequestValid({params: {supplyPartnerId: '123'}})).to.equal(true); // adpos has a default - expect(spec.isBidRequestValid({params: {supplyPartnerId: '123', instl: '123'}})).to.equal(false); - expect(spec.isBidRequestValid({params: {supplyPartnerId: '123', instl: -1}})).to.equal(false); - expect(spec.isBidRequestValid({params: {supplyPartnerId: '123', instl: 0}})).to.equal(true); - expect(spec.isBidRequestValid({params: {supplyPartnerId: '123', instl: 1}})).to.equal(true); - expect(spec.isBidRequestValid({params: {supplyPartnerId: '123', instl: 2}})).to.equal(false); - }); - }); - - describe('buildRequests', () => { - it('returns an array', () => { - let response; - response = spec.buildRequests([]); - expect(Array.isArray(response)).to.equal(true); - expect(response.length).to.equal(0); - response = spec.buildRequests([bidRequest], bidRequest); - expect(Array.isArray(response)).to.equal(true); - expect(response.length).to.equal(1); - const adUnit1 = Object.assign({}, utils.deepClone(bidRequest), {auctionId: '1', adUnitCode: 'a'}); - const adUnit2 = Object.assign({}, utils.deepClone(bidRequest), {auctionId: '1', adUnitCode: 'b'}); - response = spec.buildRequests([adUnit1, adUnit2], bidRequest); - expect(Array.isArray(response)).to.equal(true); - expect(response.length).to.equal(2); - }); - - it('targets correct endpoint', () => { - let response; - response = spec.buildRequests([bidRequest], bidRequest)[0]; - expect(response.method).to.equal('POST'); - expect(response.url).to.match(new RegExp(`^https://bidder\\.cleanmediaads\\.com/r/${supplyPartnerId}/bidr\\?rformat=open_rtb&reqformat=rtb_json&bidder=prebid$`, 'g')); - expect(response.data.id).to.equal(bidRequest.bidId); - const bidRequestWithEndpoint = utils.deepClone(bidRequest); - bidRequestWithEndpoint.params.rtbEndpoint = 'https://bidder.cleanmediaads.com/a12'; - response = spec.buildRequests([bidRequestWithEndpoint], bidRequest)[0]; - expect(response.url).to.match(new RegExp(`^https://bidder\\.cleanmediaads\\.com/a12/r/${supplyPartnerId}/bidr\\?rformat=open_rtb&reqformat=rtb_json&bidder=prebid$`, 'g')); - }); - - it('builds request correctly', () => { - let bidRequest2 = utils.deepClone(bidRequest); - Object.assign(bidRequest2.refererInfo, { - page: 'http://www.test.com/page.html', - domain: 'www.test.com', - ref: 'http://referrer.com' - }) - let response = spec.buildRequests([bidRequest], bidRequest2)[0]; - - expect(response.data.site.domain).to.equal('www.test.com'); - expect(response.data.site.page).to.equal('http://www.test.com/page.html'); - expect(response.data.site.ref).to.equal('http://referrer.com'); - expect(response.data.imp.length).to.equal(1); - expect(response.data.imp[0].id).to.equal(bidRequest.bidId); - expect(response.data.imp[0].instl).to.equal(0); - expect(response.data.imp[0].tagid).to.equal(bidRequest.adUnitCode); - expect(response.data.imp[0].bidfloor).to.equal(0); - expect(response.data.imp[0].bidfloorcur).to.equal('USD'); - expect(response.data.regs.ext.us_privacy).to.equal('cleanmediaCCPA');// USP/CCPAs - expect(response.data.source.ext.schain).to.deep.equal(bidRequest2.schain); - - const bidRequestWithInstlEquals1 = utils.deepClone(bidRequest); - bidRequestWithInstlEquals1.params.instl = 1; - response = spec.buildRequests([bidRequestWithInstlEquals1], bidRequest2)[0]; - expect(response.data.imp[0].instl).to.equal(bidRequestWithInstlEquals1.params.instl); - const bidRequestWithInstlEquals0 = utils.deepClone(bidRequest); - bidRequestWithInstlEquals0.params.instl = 1; - response = spec.buildRequests([bidRequestWithInstlEquals0], bidRequest2)[0]; - expect(response.data.imp[0].instl).to.equal(bidRequestWithInstlEquals0.params.instl); - const bidRequestWithBidfloorEquals1 = utils.deepClone(bidRequest); - bidRequestWithBidfloorEquals1.params.bidfloor = 1; - response = spec.buildRequests([bidRequestWithBidfloorEquals1], bidRequest2)[0]; - expect(response.data.imp[0].bidfloor).to.equal(bidRequestWithBidfloorEquals1.params.bidfloor); - }); - - it('builds request banner object correctly', () => { - let response; - const bidRequestWithBanner = utils.deepClone(bidRequest); - bidRequestWithBanner.mediaTypes = { - banner: { - sizes: [[300, 250], [120, 600]] - } - }; - response = spec.buildRequests([bidRequestWithBanner], bidRequest)[0]; - expect(response.data.imp[0].banner.w).to.equal(bidRequestWithBanner.mediaTypes.banner.sizes[0][0]); - expect(response.data.imp[0].banner.h).to.equal(bidRequestWithBanner.mediaTypes.banner.sizes[0][1]); - expect(response.data.imp[0].banner.pos).to.equal(0); - expect(response.data.imp[0].banner.topframe).to.equal(0); - const bidRequestWithPosEquals1 = utils.deepClone(bidRequestWithBanner); - bidRequestWithPosEquals1.params.pos = 1; - response = spec.buildRequests([bidRequestWithPosEquals1], bidRequest)[0]; - expect(response.data.imp[0].banner.pos).to.equal(bidRequestWithPosEquals1.params.pos); - }); - - it('builds request video object correctly', () => { - let response; - const bidRequestWithVideo = utils.deepClone(bidRequest); - - bidRequestWithVideo.params.video = { - plcmt: 1, - minduration: 1, - } - - bidRequestWithVideo.mediaTypes = { - video: { - playerSize: [[302, 252]], - mimes: ['video/mpeg'], - playbackmethod: 1, - startdelay: 1, - } - }; - response = spec.buildRequests([bidRequestWithVideo], bidRequest)[0]; - expect(response.data.imp[0].video.w).to.equal(bidRequestWithVideo.mediaTypes.video.playerSize[0][0]); - expect(response.data.imp[0].video.h).to.equal(bidRequestWithVideo.mediaTypes.video.playerSize[0][1]); - expect(response.data.imp[0].video.pos).to.equal(0); - - expect(response.data.imp[0].video.mimes).to.equal(bidRequestWithVideo.mediaTypes.video.mimes); - expect(response.data.imp[0].video.skip).to.not.exist; - expect(response.data.imp[0].video.plcmt).to.equal(1); - expect(response.data.imp[0].video.minduration).to.equal(1); - expect(response.data.imp[0].video.playbackmethod).to.equal(1); - expect(response.data.imp[0].video.startdelay).to.equal(1); - - bidRequestWithVideo.mediaTypes = { - video: { - playerSize: [302, 252], - mimes: ['video/mpeg'], - skip: 1, - plcmt: 1, - minduration: 1, - playbackmethod: 1, - startdelay: 1, - }, - }; - - const bidRequestWithPosEquals1 = utils.deepClone(bidRequestWithVideo); - expect(response.data.imp[0].video.w).to.equal(bidRequestWithVideo.mediaTypes.video.playerSize[0]); - expect(response.data.imp[0].video.h).to.equal(bidRequestWithVideo.mediaTypes.video.playerSize[1]); - - bidRequestWithPosEquals1.params.pos = 1; - response = spec.buildRequests([bidRequestWithPosEquals1], bidRequest)[0]; - expect(response.data.imp[0].video.pos).to.equal(bidRequestWithPosEquals1.params.pos); - }); - - it('builds request video object correctly with context', () => { - const bidRequestWithVideo = utils.deepClone(bidRequest); - bidRequestWithVideo.mediaTypes = { - video: { - context: 'instream', - mimes: ['video/mpeg'], - skip: 1, - plcmt: 1, - minduration: 1, - playbackmethod: 1, - startdelay: 1, - } - }; - let response = spec.buildRequests([bidRequestWithVideo], bidRequest)[0]; - expect(response.data.imp[0].video.ext.context).to.equal('instream'); - bidRequestWithVideo.mediaTypes.video.context = 'outstream'; - - const bidRequestWithPosEquals1 = utils.deepClone(bidRequestWithVideo); - bidRequestWithPosEquals1.mediaTypes.video.context = 'outstream'; - response = spec.buildRequests([bidRequestWithPosEquals1], bidRequest)[0]; - expect(response.data.imp[0].video.ext.context).to.equal('outstream'); - - const bidRequestWithPosEquals2 = utils.deepClone(bidRequestWithVideo); - bidRequestWithPosEquals2.mediaTypes.video.context = null; - response = spec.buildRequests([bidRequestWithPosEquals2], bidRequest)[0]; - expect(response.data.imp[0].video.ext.context).to.equal(null); - }); - - it('builds request video object correctly with multi-dimensions size array', () => { - let response; - const bidRequestWithVideo = utils.deepClone(bidRequest); - bidRequestWithVideo.mediaTypes.video = { - playerSize: [[304, 254], [305, 255]], - context: 'instream', - mimes: ['video/mpeg'], - skip: 1, - plcmt: 1, - minduration: 1, - playbackmethod: 1, - startdelay: 1, - }; - - response = spec.buildRequests([bidRequestWithVideo], bidRequest)[0]; - expect(response.data.imp[1].video.ext.context).to.equal('instream'); - bidRequestWithVideo.mediaTypes.video.context = 'outstream'; - - const bidRequestWithPosEquals1 = utils.deepClone(bidRequestWithVideo); - bidRequestWithPosEquals1.mediaTypes.video.context = 'outstream'; - response = spec.buildRequests([bidRequestWithPosEquals1], bidRequest)[0]; - expect(response.data.imp[1].video.ext.context).to.equal('outstream'); - - const bidRequestWithPosEquals2 = utils.deepClone(bidRequestWithVideo); - bidRequestWithPosEquals2.mediaTypes.video.context = null; - response = spec.buildRequests([bidRequestWithPosEquals2], bidRequest)[0]; - expect(response.data.imp[1].video.ext.context).to.equal(null); - }); - - it('builds request with gdpr consent', () => { - let response = spec.buildRequests([bidRequest], bidRequest)[0]; - - expect(response.data.ext.gdpr_consent).to.not.equal(null).and.not.equal(undefined); - expect(response.data.ext).to.have.property('gdpr_consent'); - expect(response.data.ext.gdpr_consent.consent_string).to.equal('some string'); - expect(response.data.ext.gdpr_consent.consent_required).to.equal(true); - - expect(response.data.regs.ext.gdpr).to.not.equal(null).and.not.equal(undefined); - expect(response.data.user.ext.consent).to.equal('some string'); - }); - - it('build request with ID5 Id', () => { - const bidRequestClone = utils.deepClone(bidRequest); - bidRequestClone.userId = {}; - bidRequestClone.userId.id5id = { uid: 'id5-user-id' }; - let request = spec.buildRequests([bidRequestClone], bidRequestClone)[0]; - expect(request.data.user.ext.eids).to.deep.equal([{ - 'source': 'id5-sync.com', - 'uids': [{ - 'id': 'id5-user-id', - 'ext': { - 'rtiPartner': 'ID5ID' - } - }] - }]); - }); - - it('build request with unified Id', () => { - const bidRequestClone = utils.deepClone(bidRequest); - bidRequestClone.userId = {}; - bidRequestClone.userId.tdid = 'tdid-user-id'; - let request = spec.buildRequests([bidRequestClone], bidRequestClone)[0]; - expect(request.data.user.ext.eids).to.deep.equal([{ - 'source': 'adserver.org', - 'uids': [{ - 'id': 'tdid-user-id', - 'ext': { - 'rtiPartner': 'TDID' - } - }] - }]); - }); - }); - - describe('interpretResponse', () => { - it('returns an empty array on missing response', () => { - let response = spec.interpretResponse(undefined, {bidRequest: bannerBidRequest}); - expect(Array.isArray(response)).to.equal(true); - expect(response.length).to.equal(0); - - response = spec.interpretResponse({}, {bidRequest: bannerBidRequest}); - expect(Array.isArray(response)).to.equal(true); - expect(response.length).to.equal(0); - }); - - it('aggregates banner bids from all seat bids', () => { - const response = spec.interpretResponse({body: rtbResponse}, {bidRequest: bannerBidRequest}); - expect(Array.isArray(response)).to.equal(true); - expect(response.length).to.equal(1); - const ad0 = response[0]; - expect(ad0.requestId).to.equal(bannerBidRequest.bidId); - expect(ad0.cpm).to.equal(rtbResponse.seatbid[1].bid[0].price); - expect(ad0.width).to.equal(rtbResponse.seatbid[1].bid[0].w); - expect(ad0.height).to.equal(rtbResponse.seatbid[1].bid[0].h); - expect(ad0.ttl).to.equal(TTL); - expect(ad0.creativeId).to.equal(rtbResponse.seatbid[1].bid[0].crid); - expect(ad0.netRevenue).to.equal(true); - expect(ad0.currency).to.equal(rtbResponse.seatbid[1].bid[0].cur || rtbResponse.cur || 'USD'); - expect(ad0.ad).to.equal(rtbResponse.seatbid[1].bid[0].adm); - expect(ad0.vastXml).to.be.an('undefined'); - expect(ad0.vastUrl).to.be.an('undefined'); - expect(ad0.meta.advertiserDomains).to.be.equal(rtbResponse.seatbid[1].bid[0].adomain); - }); - - it('aggregates video bids from all seat bids', () => { - const response = spec.interpretResponse({body: rtbResponse}, {bidRequest: videoBidRequest}); - expect(Array.isArray(response)).to.equal(true); - expect(response.length).to.equal(1); - const ad0 = response[0]; - expect(ad0.requestId).to.equal(videoBidRequest.bidId); - expect(ad0.cpm).to.equal(rtbResponse.seatbid[0].bid[0].price); - expect(ad0.width).to.equal(rtbResponse.seatbid[0].bid[0].w); - expect(ad0.height).to.equal(rtbResponse.seatbid[0].bid[0].h); - expect(ad0.ttl).to.equal(TTL); - expect(ad0.creativeId).to.equal(rtbResponse.seatbid[0].bid[0].crid); - expect(ad0.netRevenue).to.equal(true); - expect(ad0.currency).to.equal(rtbResponse.seatbid[0].bid[0].cur || rtbResponse.cur || 'USD'); - expect(ad0.ad).to.be.an('undefined'); - expect(ad0.vastXml).to.equal(rtbResponse.seatbid[0].bid[0].adm); - expect(ad0.vastUrl).to.equal(rtbResponse.seatbid[0].bid[0].ext.vast_url); - }); - - it('aggregates user-sync pixels', () => { - const response = spec.getUserSyncs({}, [{body: rtbResponse}]); - expect(Array.isArray(response)).to.equal(true); - expect(response.length).to.equal(4); - expect(response[0].type).to.equal(rtbResponse.ext.utrk[0].type); - expect(response[0].url).to.equal('//bidder.cleanmediaads.com/user/sync/1?gdpr=0&consent=&usp='); - expect(response[1].type).to.equal(rtbResponse.ext.utrk[1].type); - expect(response[1].url).to.equal('//bidder.cleanmediaads.com/user/sync/2'); - expect(response[2].type).to.equal(rtbResponse.seatbid[0].bid[0].ext.utrk[0].type); - expect(response[2].url).to.equal('//p.partner1.io/user/sync/1'); - expect(response[3].type).to.equal(rtbResponse.seatbid[1].bid[0].ext.utrk[0].type); - expect(response[3].url).to.equal('//p.partner2.io/user/sync/1'); - }); - - it('supports configuring outstream renderers', () => { - const videoRequest = utils.deepClone(videoBidRequest); - videoRequest.mediaTypes.video.context = 'outstream'; - const result = spec.interpretResponse({body: videoResponse}, {bidRequest: videoRequest}); - expect(result[0].renderer).to.not.equal(undefined); - }); - - it('validates in/existing of gdpr consent', () => { - let result = spec.getUserSyncs({}, [{body: videoResponse}], gdprConsent, 'cleanmediaCCPA'); - expect(result).to.be.an('array'); - expect(result.length).to.equal(1); - expect(result[0].type).to.equal('image'); - expect(result[0].url).to.equal('https://bidder.cleanmediaads.com/pix/1275/scm?cb=1545900621675&gdpr=1&consent=consent%20string&us_privacy=cleanmediaCCPA'); - - gdprConsent.gdprApplies = false; - result = spec.getUserSyncs({}, [{body: videoResponse}], gdprConsent, 'cleanmediaCCPA'); - expect(result).to.be.an('array'); - expect(result.length).to.equal(1); - expect(result[0].type).to.equal('image'); - expect(result[0].url).to.equal('https://bidder.cleanmediaads.com/pix/1275/scm?cb=1545900621675&gdpr=0&consent=&us_privacy=cleanmediaCCPA'); - - videoResponse.ext.utrk[0].url = 'https://bidder.cleanmediaads.com/pix/1275/scm'; - result = spec.getUserSyncs({}, [{body: videoResponse}], gdprConsent); - expect(result).to.be.an('array'); - expect(result.length).to.equal(1); - expect(result[0].type).to.equal('image'); - expect(result[0].url).to.equal('https://bidder.cleanmediaads.com/pix/1275/scm'); - }); - - it('validates existence of gdpr, gdpr consent and usp consent', () => { - let result = spec.getUserSyncs({}, [{body: videoResponse}], gdprConsent, 'cleanmediaCCPA'); - expect(result).to.be.an('array'); - expect(result.length).to.equal(1); - expect(result[0].type).to.equal('image'); - expect(result[0].url).to.equal('https://bidder.cleanmediaads.com/pix/1275/scm?cb=1545900621675&gdpr=1&consent=consent%20string&us_privacy=cleanmediaCCPA'); - - gdprConsent.gdprApplies = false; - result = spec.getUserSyncs({}, [{body: videoResponse}], gdprConsent, ''); - expect(result).to.be.an('array'); - expect(result.length).to.equal(1); - expect(result[0].type).to.equal('image'); - expect(result[0].url).to.equal('https://bidder.cleanmediaads.com/pix/1275/scm?cb=1545900621675&gdpr=0&consent=&us_privacy='); - }); - }); -}); From df30a6b0d7b47777dcc35a778bdd185728b957b8 Mon Sep 17 00:00:00 2001 From: Patrick McCann Date: Thu, 13 Mar 2025 08:23:58 -0400 Subject: [PATCH 05/92] Prebid 10: Delete modules/telariaBidAdapter.js (#12868) * Prebid 10: Delete modules/telariaBidAdapter.js partial to #12001 * Delete modules/telariaBidAdapter.md --- modules/telariaBidAdapter.js | 262 ----------------------------------- modules/telariaBidAdapter.md | 37 ----- 2 files changed, 299 deletions(-) delete mode 100644 modules/telariaBidAdapter.js delete mode 100644 modules/telariaBidAdapter.md diff --git a/modules/telariaBidAdapter.js b/modules/telariaBidAdapter.js deleted file mode 100644 index 4ad544aaa50..00000000000 --- a/modules/telariaBidAdapter.js +++ /dev/null @@ -1,262 +0,0 @@ -import { logError, isEmpty, deepAccess, triggerPixel, logWarn, isArray } from '../src/utils.js'; -import {registerBidder} from '../src/adapters/bidderFactory.js'; -import {VIDEO} from '../src/mediaTypes.js'; -import {getSupplyChain} from '../libraries/riseUtils/index.js'; - -const BIDDER_CODE = 'telaria'; -const DOMAIN = 'tremorhub.com'; -const TAG_ENDPOINT = `ads.${DOMAIN}/ad/tag`; -const EVENTS_ENDPOINT = `events.${DOMAIN}/diag`; - -export const spec = { - code: BIDDER_CODE, - gvlid: 202, - aliases: [ - { code: 'tremor', gvlid: 202 }, - { code: 'tremorvideo', gvlid: 202 } - ], - supportedMediaTypes: [VIDEO], - /** - * Determines if the request is valid - * @param bid - * @returns {*|string} - */ - isBidRequestValid: function (bid) { - return !!(bid && bid.params && bid.params.adCode && bid.params.supplyCode); - }, - - /** - * Make a server request from the list of BidRequests. - * @param validBidRequests list of valid bid requests that have passed isBidRequestValid check - * @param bidderRequest - * @returns {Array} of url objects - */ - buildRequests: function (validBidRequests, bidderRequest) { - let requests = []; - - validBidRequests.forEach(bid => { - let url = generateUrl(bid, bidderRequest); - if (url) { - requests.push({ - method: 'GET', - url: url, - bidId: bid.bidId, - vastUrl: url.split('&fmt=json')[0] - }); - } - }); - - return requests; - }, - - /** - * convert the server response into a list of BidObjects that prebid accepts - * http://prebid.org/dev-docs/bidder-adaptor.html#interpreting-the-response - * @param serverResponse - * @param bidderRequest - * @returns {Array} - */ - interpretResponse: function (serverResponse, bidderRequest) { - let bidResult; - let width, height; - - let bids = []; - - try { - bidResult = serverResponse.body; - - bidderRequest.url.split('&').forEach(param => { - let lower = param.toLowerCase(); - if (lower.indexOf('player') > -1) { - if (lower.indexOf('width') > -1) { - width = param.split('=')[1]; - } else if (lower.indexOf('height') > -1) { - height = param.split('=')[1]; - } - } - }); - } catch (error) { - logError(error); - width = 0; - height = 0; - } - - if (!bidResult || bidResult.error) { - let errorMessage = `in response for ${bidderRequest.bidderCode} adapter`; - if (bidResult && bidResult.error) { - errorMessage += `: ${bidResult.error}`; - } - logError(errorMessage); - } else if (!isEmpty(bidResult.seatbid)) { - bidResult.seatbid[0].bid.forEach(tag => { - if (tag) { - bids.push(createBid(bidderRequest, tag, width, height)); - } - }); - } - - return bids; - }, - /** - * We support pixel syncing only at the moment. Telaria ad server returns 'ext' - * as an optional parameter if the tag has 'incIdSync' parameter set to true - * @param syncOptions - * @param serverResponses - * @returns {Array} - */ - getUserSyncs: function (syncOptions, serverResponses) { - const syncs = []; - if (syncOptions.pixelEnabled && serverResponses.length) { - (deepAccess(serverResponses, '0.body.ext.telaria.userSync') || []).forEach(url => syncs.push({type: 'image', url: url})); - } - return syncs; - }, - - /** - * See http://prebid.org/dev-docs/bidder-adaptor.html#registering-on-timeout for detailed semantic. - * @param timeoutData bidRequest - */ - onTimeout: function (timeoutData) { - let url = getTimeoutUrl(timeoutData); - if (url) { - triggerPixel(url); - } - } -}; - -function getDefaultSrcPageUrl() { - return encodeURIComponent(document.location.href); -} - -function getUrlParams(params, schainFromBidRequest) { - let urlSuffix = ''; - - if (!isEmpty(params)) { - for (let key in params) { - if (key !== 'schain' && params.hasOwnProperty(key) && !isEmpty(params[key])) { - urlSuffix += `&${key}=${params[key]}`; - } - } - urlSuffix += getSupplyChain(!isEmpty(schainFromBidRequest) ? schainFromBidRequest : params['schain']); - } - - return urlSuffix; -} - -export const getTimeoutUrl = function(timeoutData) { - let params = deepAccess(timeoutData, '0.params.0'); - - if (!isEmpty(params)) { - let url = `https://${EVENTS_ENDPOINT}`; - - params = Object.assign({ - srcPageUrl: getDefaultSrcPageUrl() - }, params); - - url += `${getUrlParams(params)}`; - - url += '&hb=1&evt=TO'; - - return url; - } -}; - -/** - * Generates the url based on the parameters given. Sizes, supplyCode & adCode are required. - * The format is: [L,W] or [[L1,W1],...] - * @param bid - * @param bidderRequest - * @returns {string} - */ -function generateUrl(bid, bidderRequest) { - let playerSize = deepAccess(bid, 'mediaTypes.video.playerSize'); - if (!playerSize) { - logWarn(`Although player size isn't required it is highly recommended`); - } - - let width, height; - if (playerSize) { - if (isArray(playerSize) && (playerSize.length === 2) && (!isNaN(playerSize[0]) && !isNaN(playerSize[1]))) { - width = playerSize[0]; - height = playerSize[1]; - } else if (typeof playerSize === 'object') { - width = playerSize[0][0]; - height = playerSize[0][1]; - } - } - - let supplyCode = deepAccess(bid, 'params.supplyCode'); - let adCode = deepAccess(bid, 'params.adCode'); - - if (supplyCode && adCode) { - let url = `https://${supplyCode}.${TAG_ENDPOINT}?adCode=${adCode}`; - - if (width) { - url += (`&playerWidth=${width}`); - } - if (height) { - url += (`&playerHeight=${height}`); - } - - const params = Object.assign({ - srcPageUrl: getDefaultSrcPageUrl() - }, bid.params); - delete params.adCode; - - url += `${getUrlParams(params, bid.schain)}`; - - url += (`&transactionId=${bid.ortb2Imp?.ext?.tid}`); - - if (bidderRequest) { - if (bidderRequest.gdprConsent) { - if (typeof bidderRequest.gdprConsent.gdprApplies === 'boolean') { - url += (`&gdpr=${(bidderRequest.gdprConsent.gdprApplies ? 1 : 0)}`); - } - if (bidderRequest.gdprConsent.consentString) { - url += (`&gdpr_consent=${bidderRequest.gdprConsent.consentString}`); - } - } - - if (bidderRequest.refererInfo && bidderRequest.refererInfo.page) { - // TODO: is 'page' the right value here? - url += (`&referrer=${encodeURIComponent(bidderRequest.refererInfo.page)}`); - } - } - - return (url + '&hb=1&fmt=json'); - } -} - -/** - * Create and return a bid response - * @param reqBid - * @param response - * @param width - * @param height - */ -function createBid(reqBid, response, width, height) { - // TTL 5 mins by default, future support for extended imp wait time - const bid = { - requestId: reqBid.bidId, - cpm: response.price, - creativeId: response.crid || '-1', - vastXml: response.adm, - vastUrl: reqBid.vastUrl, - mediaType: 'video', - width: width, - height: height, - currency: 'USD', - netRevenue: true, - ttl: 300, - ad: response.adm, - meta: {} - }; - - if (response.adomain && response.adomain.length > 0) { - bid.meta.advertiserDomains = response.adomain; - } - - return bid; -} - -registerBidder(spec); diff --git a/modules/telariaBidAdapter.md b/modules/telariaBidAdapter.md deleted file mode 100644 index 3a6c86ec2f6..00000000000 --- a/modules/telariaBidAdapter.md +++ /dev/null @@ -1,37 +0,0 @@ -# Overview - -``` -Module Name: Telaria Bid Adapter -Module Type: Bidder Adapter -Maintainer: github@telaria.com -``` - -# Description - -Connects to Telaria's exchange. - -Telaria bid adapter supports instream Video. - -# Test Parameters -``` -{ - code: 'video1', - mediaTypes: { - 'video': { - playerSize: [640, 480], - context: 'instream' - } - }, - bids: [{ - bidder: 'telaria', - params: { - supplyCode: 'ssp-demo-rm6rh', - adCode: 'ssp-!demo!-lufip', - videoId: 'MyCoolVideo' - } - }] -} -``` - -# Example: -https://console.telaria.com/examples/hb/headerbidding.jsp From eea63f637dc00a7987842f497b3779f7f298b6ef Mon Sep 17 00:00:00 2001 From: Patrick McCann Date: Thu, 13 Mar 2025 08:24:20 -0400 Subject: [PATCH 06/92] Prebid 10: EMX Digital, alias of Cadent (#12864) * EMX Digital: alias of Cadent * Update cadentApertureMXBidAdapter.js (#12865) --- libraries/appnexusUtils/anUtils.js | 1 - modules/cadentApertureMXBidAdapter.js | 1 + 2 files changed, 1 insertion(+), 1 deletion(-) diff --git a/libraries/appnexusUtils/anUtils.js b/libraries/appnexusUtils/anUtils.js index 1d04711bd0f..adf91bcfe32 100644 --- a/libraries/appnexusUtils/anUtils.js +++ b/libraries/appnexusUtils/anUtils.js @@ -12,7 +12,6 @@ export function convertCamelToUnderscore(value) { export const appnexusAliases = [ { code: 'appnexusAst', gvlid: 32 }, - { code: 'emxdigital', gvlid: 183 }, { code: 'emetriq', gvlid: 213 }, { code: 'pagescience', gvlid: 32 }, { code: 'gourmetads', gvlid: 32 }, diff --git a/modules/cadentApertureMXBidAdapter.js b/modules/cadentApertureMXBidAdapter.js index fa441a4f4fa..d75f1eade2b 100644 --- a/modules/cadentApertureMXBidAdapter.js +++ b/modules/cadentApertureMXBidAdapter.js @@ -22,6 +22,7 @@ const DEFAULT_CUR = 'USD'; const ALIASES = [ { code: 'emx_digital', gvlid: 183 }, { code: 'cadent', gvlid: 183 }, + { code: 'emxdigital', gvlid: 183 }, ]; const EIDS_SUPPORTED = [ From 267810481c01c521258455fda99ce3f09ad75d58 Mon Sep 17 00:00:00 2001 From: Patrick McCann Date: Thu, 13 Mar 2025 08:24:38 -0400 Subject: [PATCH 07/92] Prebid 10: Delete adsinteractiveBidAdapter_spec.js (#12874) * Prebid 10: Delete adsinteractiveBidAdapter_spec.js * Delete modules/adsinteractiveBidAdapter.md * Delete modules/adsinteractiveBidAdapter.js --- modules/adsinteractiveBidAdapter.js | 146 ------------ modules/adsinteractiveBidAdapter.md | 31 --- .../modules/adsinteractiveBidAdapter_spec.js | 207 ------------------ 3 files changed, 384 deletions(-) delete mode 100644 modules/adsinteractiveBidAdapter.js delete mode 100644 modules/adsinteractiveBidAdapter.md delete mode 100644 test/spec/modules/adsinteractiveBidAdapter_spec.js diff --git a/modules/adsinteractiveBidAdapter.js b/modules/adsinteractiveBidAdapter.js deleted file mode 100644 index ad6bdfeb299..00000000000 --- a/modules/adsinteractiveBidAdapter.js +++ /dev/null @@ -1,146 +0,0 @@ -import { - deepAccess, -} from '../src/utils.js'; - -import { registerBidder } from '../src/adapters/bidderFactory.js'; -import { BANNER } from '../src/mediaTypes.js'; - -const ADSINTERACTIVE_CODE = 'adsinteractive'; -const USER_SYNC_URL_IMAGE = 'https://sync.adsinteractive.com/img'; -const USER_SYNC_URL_IFRAME = 'https://sync.adsinteractive.com/sync'; -const GVLID = 1212; - -export const spec = { - code: ADSINTERACTIVE_CODE, - supportedMediaTypes: [BANNER], - gvlid: GVLID, - - isBidRequestValid: (bid) => { - return ( - !!bid.params.adUnit && !!bid.bidId && bid.bidder === 'adsinteractive' - ); - }, - - buildRequests: (bidRequests, bidderRequest) => { - return bidRequests.map((bid) => { - var gdprConsent; - if (bidderRequest && bidderRequest.gdprConsent) { - gdprConsent = { - consent_string: bidderRequest.gdprConsent.consentString, - consent_required: bidderRequest.gdprConsent.gdprApplies, - }; - - if ( - bidderRequest.gdprConsent.addtlConsent && - bidderRequest.gdprConsent.addtlConsent.indexOf('~') !== -1 - ) { - let ac = bidderRequest.gdprConsent.addtlConsent; - let acStr = ac.substring(ac.indexOf('~') + 1); - gdprConsent.addtl_consent = acStr - .split('.') - .map((id) => parseInt(id, 10)); - } - } - - let url = 'https://pb.adsinteractive.com/prebid'; - const data = { - id: bid.bidId, - at: 1, - source: { fd: 0 }, - gdprConsent: gdprConsent, - site: { - page: bid.ortb2.site.page, - keywords: bid.ortb2.site.keywords, - domain: bid.ortb2.site.domain, - publisher: { - domain: bid.ortb2.site.domain, - }, - ext: { - amp: Number(bidderRequest.refererInfo.isAmp), - }, - }, - regs: bid.ortb2.regs, - device: bid.ortb2.device, - user: bid.ortb2.user, - imp: [ - { - id: bid.params.adUnit, - banner: { - format: bid.sizes.map((size) => ({ - w: size[0], - h: size[1], - })), - }, - ext: { - bidder: { - adUnit: bid.params.adUnit, - }, - }, - }, - ], - tmax: bidderRequest.timeout, - }; - const options = { - withCredentials: true, - }; - return { - method: 'POST', - url, - data, - options, - }; - }); - }, - - interpretResponse: (serverResponse, bidRequest) => { - let answer = []; - if (serverResponse && serverResponse.body && serverResponse.body.seatbid) { - serverResponse.body.seatbid.forEach((seatbid) => { - if (seatbid.bid.length) { - answer = [ - ...answer, - ...seatbid.bid - .filter((bid) => bid.price > 0) - .map((adsinteractiveBid) => { - const bid = { - id: adsinteractiveBid.id, - requestId: bidRequest.data.id, - cpm: adsinteractiveBid.price, - netRevenue: true, - ttl: 1000, - ad: adsinteractiveBid.adm, - meta: {advertiserDomains: adsinteractiveBid && adsinteractiveBid.adomain ? adsinteractiveBid.adomain : []}, - width: adsinteractiveBid.w, - height: adsinteractiveBid.h, - currency: serverResponse.body.cur || 'USD', - creativeId: adsinteractiveBid.crid || 0, - }; - return bid; - }), - ]; - } - }); - } - return answer; - }, - getUserSyncs: (syncOptions, serverResponse, gdprConsent, uspConsent) => { - if (syncOptions.iframeEnabled) { - const auid = serverResponse.filter(resp => deepAccess(resp, 'body.ext.auid')) - .map(resp => resp.body.ext.auid); - return [ - { - type: 'iframe', - url: USER_SYNC_URL_IFRAME + '?consent=' + gdprConsent.consentString + '&auid=' + auid, - }, - ]; - } else { - return [ - { - type: 'image', - url: USER_SYNC_URL_IMAGE, - }, - ]; - } - }, -}; -registerBidder(spec); diff --git a/modules/adsinteractiveBidAdapter.md b/modules/adsinteractiveBidAdapter.md deleted file mode 100644 index 81afcd18200..00000000000 --- a/modules/adsinteractiveBidAdapter.md +++ /dev/null @@ -1,31 +0,0 @@ -# Overview - -Module Name: AdsInteractive Bidder Adapter - -Module Type: Bidder Adapter - -Maintainer: it@adsinteractive.com - -# Description - -You can use this adapter to get a bid from adsinteractive.com. - -About us : https://www.adsinteractive.com - - -# Test Parameters -```javascript - var adUnits = [ - { - sizes: [[300, 250]], - bids: [ - { - bidder: "adsinteractive", - params: { - adUnit: "example_adunit_1" - } - } - ] - } - ]; -``` diff --git a/test/spec/modules/adsinteractiveBidAdapter_spec.js b/test/spec/modules/adsinteractiveBidAdapter_spec.js deleted file mode 100644 index d0f90bd71de..00000000000 --- a/test/spec/modules/adsinteractiveBidAdapter_spec.js +++ /dev/null @@ -1,207 +0,0 @@ -import { expect } from 'chai'; -import { spec } from 'modules/adsinteractiveBidAdapter.js'; -import { newBidder } from 'src/adapters/bidderFactory.js'; - -describe('adsinteractiveBidAdapter', function () { - let bid = { - ortb2: { - site: { - page: 'http://test.com', - domain: 'test.com', - publisher: { - domain: 'test.com', - }, - }, - }, - bidder: 'adsinteractive', - sizes: [[300, 250]], - bidId: '32469kja92389', - params: { - adUnit: 'example_adunit_1', - }, - } - - const bidderRequest = { - refererInfo: { - isAmp: 0 - } } - - describe('build requests', () => { - it('sends bid request to ENDPOINT via POST', function () { - const request = spec.buildRequests([ - { - ortb2: { - site: { - page: 'http://test.com', - domain: 'test.com', - publisher: { - domain: 'test.com', - }, - }, - }, - bidder: 'adsinteractive', - sizes: [[300, 250]], - bidId: '32469kja92389', - params: { - adUnit: 'example_adunit_1', - }, - }, - ], bidderRequest); - expect(request[0].method).to.equal('POST'); - }); - it('sends bid request to adsinteractive endpoint', function () { - const request = spec.buildRequests([ - { - ortb2: { - site: { - page: 'http://test.com', - domain: 'test.com', - publisher: { - domain: 'test.com', - }, - }, - }, - bidder: 'adsinteractive', - sizes: [[300, 250]], - bidId: '32469kja92389', - params: { - adUnit: 'example_adunit_1', - }, - }, - ], bidderRequest); - expect(request[0].url).to.equal('https://pb.adsinteractive.com/prebid'); - }); - }); - - describe('inherited functions', () => { - const adapter = newBidder(spec); - it('exists and is a function', () => { - expect(adapter.callBids).to.exist.and.to.be.a('function'); - }); - }); - - describe('isBidRequestValid', function () { - it('should return true when necessary information is found', function () { - expect(spec.isBidRequestValid(bid)).to.be.true; - }); - - it('should return false when necessary information is not found', function () { - // empty bid - expect(spec.isBidRequestValid({ bidId: '', params: {} })).to.be.false; - - // empty bidId - bid.bidId = ''; - expect(spec.isBidRequestValid(bid)).to.be.false; - - // empty adUnit - bid.bidId = '32469kja92389'; - bid.params.adUnit = ''; - expect(spec.isBidRequestValid(bid)).to.be.false; - }); - - it('returns false when bidder not set to "adsinteractive"', function () { - const invalidBid = { - bidder: 'newyork', - sizes: [[300, 250]], - bidId: '32469kja92389', - params: { - adUnit: 'example_adunit_1', - }, - }; - expect(spec.isBidRequestValid(invalidBid)).to.be.false; - }); - - it('returns false when adUnit is not set in params', function () { - const invalidBid = { - bidder: 'adsinteractive', - sizes: [[300, 250]], - bidId: '32469kja92389', - params: {}, - }; - - expect(spec.isBidRequestValid(invalidBid)).to.be.false; - }); - }); - describe('interpretResponse', function () { - let serverResponse; - let bidRequest = { data: { id: 'adsinteractiverequest-9320' } }; - - beforeEach(function () { - serverResponse = { - body: { - id: '239823rhaldf822', - seatbid: [ - { - bid: [ - { - id: 'fae50ca1-3f69-4b34-bf6d-b2eb0ae3376b', - impid: 'example_adunit_1', - price: 0.49, - netRevenue: true, - ttl: 1000, - meta: {advertiserDomains: []}, - adm: '', - crid: '932048jda99cr', - h: 250, - w: 300, - }, - ], - seat: 'adsinteractive', - }, - ], - cur: 'USD', - }, - }; - }); - - it('validate_response_params', function () { - const newResponse = spec.interpretResponse(serverResponse, bidRequest); - expect(newResponse[0].id).to.be.equal( - 'fae50ca1-3f69-4b34-bf6d-b2eb0ae3376b' - ); - expect(newResponse[0].requestId).to.be.equal( - 'adsinteractiverequest-9320' - ); - expect(newResponse[0].cpm).to.be.equal(0.49); - expect(newResponse[0].width).to.be.equal(300); - expect(newResponse[0].height).to.be.equal(250); - expect(newResponse[0].currency).to.be.equal('USD'); - expect(newResponse[0].ad).to.be.equal( - '' - ); - }); - - it('should correctly reorder the server response', function () { - const newResponse = spec.interpretResponse(serverResponse, bidRequest); - expect(newResponse.length).to.be.equal(1); - expect(newResponse[0]).to.deep.equal({ - id: 'fae50ca1-3f69-4b34-bf6d-b2eb0ae3376b', - requestId: 'adsinteractiverequest-9320', - cpm: 0.49, - netRevenue: true, - ttl: 1000, - width: 300, - height: 250, - meta: {advertiserDomains: []}, - creativeId: '932048jda99cr', - currency: 'USD', - ad: '', - }); - }); - - it('should not add responses if the cpm is 0 or null', function () { - serverResponse.body.seatbid[0].bid[0].price = 0; - let response = spec.interpretResponse(serverResponse, bidRequest); - expect(response).to.deep.equal([]); - - serverResponse.body.seatbid[0].bid[0].price = null; - response = spec.interpretResponse(serverResponse, bidRequest); - expect(response).to.deep.equal([]); - }); - it('should add responses if the cpm is valid', function () { - serverResponse.body.seatbid[0].bid[0].price = 0.5; - let response = spec.interpretResponse(serverResponse, bidRequest); - expect(response).to.not.deep.equal([]); - }); - }); -}); From 5de5fae7c7c2b9f0007afcefe4de79bcda3bd9d7 Mon Sep 17 00:00:00 2001 From: Patrick McCann Date: Thu, 13 Mar 2025 08:24:50 -0400 Subject: [PATCH 08/92] Prebid 10: Delete modules/kueezBidAdapter.js (#12869) * Prebid 10: Delete modules/kueezBidAdapter.js #12001 * Delete modules/kueezBidAdapter.md * Delete test/spec/modules/kueezBidAdapter_spec.js --- modules/kueezBidAdapter.js | 486 ---------------------- modules/kueezBidAdapter.md | 73 ---- test/spec/modules/kueezBidAdapter_spec.js | 482 --------------------- 3 files changed, 1041 deletions(-) delete mode 100644 modules/kueezBidAdapter.js delete mode 100644 modules/kueezBidAdapter.md delete mode 100644 test/spec/modules/kueezBidAdapter_spec.js diff --git a/modules/kueezBidAdapter.js b/modules/kueezBidAdapter.js deleted file mode 100644 index f11d71f3318..00000000000 --- a/modules/kueezBidAdapter.js +++ /dev/null @@ -1,486 +0,0 @@ -import { - logWarn, - logInfo, - isArray, - isFn, - deepAccess, - isEmpty, - contains, - timestamp, - triggerPixel, - isInteger, - getBidIdParameter -} from '../src/utils.js'; -import { registerBidder } from '../src/adapters/bidderFactory.js'; -import { BANNER, VIDEO } from '../src/mediaTypes.js'; -import { config } from '../src/config.js'; - -const BIDDER_ENDPOINT = 'https://hb.kueezssp.com/hb-kz-multi'; -const BIDDER_TEST_ENDPOINT = 'https://hb.kueezssp.com/hb-multi-kz-test' -const BIDDER_CODE = 'kueez'; -const MAIN_CURRENCY = 'USD'; -const MEDIA_TYPES = [BANNER, VIDEO]; -const TTL = 420; -const VERSION = '1.0.0'; -const SUPPORTED_SYNC_METHODS = { - IFRAME: 'iframe', - PIXEL: 'pixel' -} - -export const spec = { - code: BIDDER_CODE, - version: VERSION, - supportedMediaTypes: MEDIA_TYPES, - isBidRequestValid: function (bidRequest) { - return validateParams(bidRequest); - }, - buildRequests: function (validBidRequests, bidderRequest) { - const [ sharedParams ] = validBidRequests; - const testMode = sharedParams.params.testMode; - const bidsToSend = prepareBids(validBidRequests, sharedParams, bidderRequest); - - return { - method: 'POST', - url: getBidderEndpoint(testMode), - data: bidsToSend - } - }, - interpretResponse: function ({body}) { - const bidResponses = body?.bids; - - if (!bidResponses || !bidResponses.length) { - return []; - } - - return parseBidResponses(bidResponses); - }, - getUserSyncs: function (syncOptions, serverResponses) { - const syncs = []; - for (const response of serverResponses) { - if (syncOptions.pixelEnabled && isArray(response.body.params.userSyncPixels)) { - const pixels = response.body.params.userSyncPixels.map(pixel => { - return { - type: 'image', - url: pixel - } - }) - syncs.push(...pixels) - } - if (syncOptions.iframeEnabled && response.body.params.userSyncURL) { - syncs.push({ - type: 'iframe', - url: response.body.params.userSyncURL - }); - } - } - return syncs; - }, - onBidWon: function (bid) { - if (bid == null) { - return; - } - - logInfo('onBidWon:', bid); - if (bid.hasOwnProperty('nurl') && bid.nurl.length > 0) { - triggerPixel(bid.nurl); - } - } -}; - -registerBidder(spec); - -/** - * Get schain string value - * @param schainObject {Object} - * @returns {string} - */ -function getSupplyChain(schainObject) { - if (isEmpty(schainObject)) { - return ''; - } - let scStr = `${schainObject.ver},${schainObject.complete}`; - schainObject.nodes.forEach((node) => { - scStr += '!'; - scStr += `${getEncodedValIfNotEmpty(node.asi)},`; - scStr += `${getEncodedValIfNotEmpty(node.sid)},`; - scStr += `${node.hp ? encodeURIComponent(node.hp) : ''},`; - scStr += `${getEncodedValIfNotEmpty(node.rid)},`; - scStr += `${getEncodedValIfNotEmpty(node.name)},`; - scStr += `${getEncodedValIfNotEmpty(node.domain)}`; - }); - return scStr; -} - -/** - * Get the encoded value - * @param val {string} - * @returns {string} - */ -function getEncodedValIfNotEmpty(val) { - return !isEmpty(val) ? encodeURIComponent(val) : ''; -} - -/** - * get device type - * @returns {string} - */ -function getDeviceType() { - const ua = navigator.userAgent; - if (/ipad|android 3.0|xoom|sch-i800|playbook|tablet|kindle/i - .test(ua.toLowerCase())) { - return '5'; - } - if (/iphone|ipod|android|blackberry|opera|mini|windows\sce|palm|smartphone|iemobile/i - .test(ua.toLowerCase())) { - return '4'; - } - if (/smart[-_\s]?tv|hbbtv|appletv|googletv|hdmi|netcast|viera|nettv|roku|\bdtv\b|sonydtv|inettvbrowser|\btv\b/i - .test(ua.toLowerCase())) { - return '3'; - } - return '1'; -} - -/** - * Get floor price - * @param bid {bid} - * @param mediaType {string} - * @returns {Number} - */ -function getFloorPrice(bid, mediaType) { - let floor = 0; - - if (isFn(bid.getFloor)) { - let floorResult = bid.getFloor({ - currency: MAIN_CURRENCY, - mediaType: mediaType, - size: '*' - }) || {}; - floor = floorResult.currency === MAIN_CURRENCY && floorResult.floor ? floorResult.floor : 0; - } - - return floor; -} - -/** - * Get the ad sizes array from the bid - * @param bid {bid} - * @param mediaType {string} - * @returns {Array} - */ -function getSizesArray(bid, mediaType) { - let sizes = [] - - if (deepAccess(bid, `mediaTypes.${mediaType}.sizes`)) { - sizes = bid.mediaTypes[mediaType].sizes; - } else if (Array.isArray(bid.sizes) && bid.sizes.length > 0) { - sizes = bid.sizes; - } - - return sizes; -} - -/** - * Get the preferred user-sync method - * @param filterSettings {filterSettings} - * @param bidderCode {string} - * @returns {string} - */ -function getSyncMethod(filterSettings, bidderCode) { - const iframeConfigs = ['all', 'iframe']; - const pixelConfig = 'image'; - if (filterSettings && iframeConfigs.some(config => isSyncMethodAllowed(filterSettings[config], bidderCode))) { - return SUPPORTED_SYNC_METHODS.IFRAME; - } - if (!filterSettings || !filterSettings[pixelConfig] || isSyncMethodAllowed(filterSettings[pixelConfig], bidderCode)) { - return SUPPORTED_SYNC_METHODS.PIXEL; - } -} - -/** - * Check sync rule support - * @param filterSetting {Object} - * @param bidderCode {string} - * @returns {boolean} - */ -function isSyncMethodAllowed(filterSetting, bidderCode) { - if (!filterSetting) { - return false; - } - const bidders = isArray(filterSetting.bidders) ? filterSetting.bidders : [bidderCode]; - return filterSetting.filter === 'include' && contains(bidders, bidderCode); -} - -/** - * Get the bidder endpoint - * @param testMode {boolean} - * @returns {string} - */ -function getBidderEndpoint(testMode) { - return testMode ? BIDDER_TEST_ENDPOINT : BIDDER_ENDPOINT; -} - -/** - * Generates the bidder parameters - * @param validBidRequests {Array} - * @param bidderRequest {bidderRequest} - * @returns {Array} - */ -function generateBidParams(validBidRequests, bidderRequest) { - const bidsArray = []; - - if (validBidRequests.length) { - validBidRequests.forEach(bid => { - bidsArray.push(generateBidParameters(bid, bidderRequest)); - }); - } - - return bidsArray; -} - -/** - * Generate bid specific parameters - * @param bid {bid} - * @param bidderRequest {bidderRequest} - * @returns {Object} bid specific params object - */ -function generateBidParameters(bid, bidderRequest) { - const {params} = bid; - const mediaType = isBanner(bid) ? BANNER : VIDEO; - const sizesArray = getSizesArray(bid, mediaType); - const gpid = deepAccess(bid, `ortb2Imp.ext.gpid`); - const pos = deepAccess(bid, `mediaTypes.${mediaType}.pos`); - const placementId = params.placementId || deepAccess(bid, `mediaTypes.${mediaType}.name`); - const paramsFloorPrice = isNaN(params.floorPrice) ? 0 : params.floorPrice; - - const bidObject = { - adUnitCode: getBidIdParameter('adUnitCode', bid), - bidId: getBidIdParameter('bidId', bid), - loop: getBidIdParameter('bidderRequestsCount', bid), - bidderRequestId: getBidIdParameter('bidderRequestId', bid), - floorPrice: Math.max(getFloorPrice(bid, mediaType), paramsFloorPrice), - mediaType, - sizes: sizesArray, - transactionId: bid.ortb2Imp?.ext?.tid || '' - }; - - if (pos) { - bidObject.pos = pos; - } - - if (gpid) { - bidObject.gpid = gpid; - } - - if (placementId) { - bidObject.placementId = placementId; - } - - if (mediaType === VIDEO) { - populateVideoParams(bidObject, bid); - } - - return bidObject; -} - -/** - * Checks if the media type is a banner - * @param bid {bid} - * @returns {boolean} - */ -function isBanner(bid) { - return bid.mediaTypes && bid.mediaTypes.banner; -} - -/** - * Generate params that are common between all bids - * @param sharedParams {sharedParams} - * @param bidderRequest {bidderRequest} - * @returns {object} the common params object - */ -function generateSharedParams(sharedParams, bidderRequest) { - const {bidderCode} = bidderRequest; - const {syncEnabled, filterSettings} = config.getConfig('userSync') || {}; - const domain = window.location.hostname; - const generalBidParams = getBidIdParameter('params', sharedParams); - const userIds = getBidIdParameter('userId', sharedParams); - const ortb2Metadata = bidderRequest.ortb2 || {}; - const timeout = bidderRequest.timeout; - - const params = { - adapter_version: VERSION, - auction_start: timestamp(), - device_type: getDeviceType(), - dnt: (navigator.doNotTrack === 'yes' || navigator.doNotTrack === '1' || navigator.msDoNotTrack === '1') ? 1 : 0, - publisher_id: generalBidParams.org, - publisher_name: domain, - session_id: getBidIdParameter('auctionId', sharedParams), - site_domain: domain, - tmax: timeout, - ua: navigator.userAgent, - wrapper_type: 'prebidjs', - wrapper_vendor: '$$PREBID_GLOBAL$$', - wrapper_version: '$prebid.version$' - }; - - if (syncEnabled) { - const allowedSyncMethod = getSyncMethod(filterSettings, bidderCode); - if (allowedSyncMethod) { - params.cs_method = allowedSyncMethod; - } - } - - if (bidderRequest && bidderRequest.gdprConsent && bidderRequest.gdprConsent.gdprApplies) { - params.gdpr = bidderRequest.gdprConsent.gdprApplies; - params.gdpr_consent = bidderRequest.gdprConsent.consentString; - } - - if (bidderRequest.uspConsent) { - params.us_privacy = bidderRequest.uspConsent; - } - - if (generalBidParams.ifa) { - params.ifa = generalBidParams.ifa; - } - - if (ortb2Metadata.site) { - params.site_metadata = JSON.stringify(ortb2Metadata.site); - } - - if (ortb2Metadata.user) { - params.user_metadata = JSON.stringify(ortb2Metadata.user); - } - - if (bidderRequest && bidderRequest.refererInfo) { - params.referrer = deepAccess(bidderRequest, 'refererInfo.ref'); - params.page_url = deepAccess(bidderRequest, 'refererInfo.page') || deepAccess(window, 'location.href'); - } - - if (sharedParams.schain) { - params.schain = getSupplyChain(sharedParams.schain); - } - - if (userIds) { - params.userIds = JSON.stringify(userIds); - } - - return params; -} - -/** - * Validates the bidder params - * @param bidRequest {bidRequest} - * @returns {boolean} - */ -function validateParams(bidRequest) { - let isValid = true; - - if (!bidRequest.params) { - logWarn('Kueez adapter - missing params'); - isValid = false; - } - - if (!bidRequest.params.org) { - logWarn('Kueez adapter - org is a required param'); - isValid = false; - } - - return isValid; -} - -/** - * Validates the bidder params - * @param validBidRequests {Array} - * @param sharedParams {sharedParams} - * @param bidderRequest {bidderRequest} - * @returns {Object} - */ -function prepareBids(validBidRequests, sharedParams, bidderRequest) { - return { - params: generateSharedParams(sharedParams, bidderRequest), - bids: generateBidParams(validBidRequests, bidderRequest) - } -} - -function getPlaybackMethod(bid) { - const playbackMethod = deepAccess(bid, `mediaTypes.video.playbackmethod`); - - if (Array.isArray(playbackMethod) && isInteger(playbackMethod[0])) { - return playbackMethod[0]; - } else if (isInteger(playbackMethod)) { - return playbackMethod; - } -} - -function populateVideoParams(params, bid) { - const linearity = deepAccess(bid, `mediaTypes.video.linearity`); - const maxDuration = deepAccess(bid, `mediaTypes.video.maxduration`); - const minDuration = deepAccess(bid, `mediaTypes.video.minduration`); - const placement = deepAccess(bid, `mediaTypes.video.placement`); - const plcmt = deepAccess(bid, `mediaTypes.video.plcmt`); - const playbackMethod = getPlaybackMethod(bid); - const skip = deepAccess(bid, `mediaTypes.video.skip`); - - if (linearity) { - params.linearity = linearity; - } - - if (maxDuration) { - params.maxDuration = maxDuration; - } - - if (minDuration) { - params.minDuration = minDuration; - } - - if (placement) { - params.placement = placement; - } - if (plcmt) { - params.plcmt = plcmt; - } - if (playbackMethod) { - params.playbackMethod = playbackMethod; - } - - if (skip) { - params.skip = skip; - } -} - -/** - * Processes the bid responses - * @param bids {Array} - * @returns {Array} - */ -function parseBidResponses(bids) { - return bids.map(bid => { - const bidResponse = { - cpm: bid.cpm, - creativeId: bid.requestId, - currency: bid.currency || MAIN_CURRENCY, - height: bid.height, - mediaType: bid.mediaType, - meta: { - mediaType: bid.mediaType - }, - netRevenue: bid.netRevenue || true, - nurl: bid.nurl, - requestId: bid.requestId, - ttl: bid.ttl || TTL, - width: bid.width - }; - - if (bid.adomain && bid.adomain.length) { - bidResponse.meta.advertiserDomains = bid.adomain; - } - - if (bid.mediaType === VIDEO) { - bidResponse.vastXml = bid.vastXml; - } else if (bid.mediaType === BANNER) { - bidResponse.ad = bid.ad; - } - - return bidResponse; - }); -} diff --git a/modules/kueezBidAdapter.md b/modules/kueezBidAdapter.md deleted file mode 100644 index 8b17e40f503..00000000000 --- a/modules/kueezBidAdapter.md +++ /dev/null @@ -1,73 +0,0 @@ -#Overview - -Module Name: Kueez Bidder Adapter - -Module Type: Bidder Adapter - -Maintainer: prebid@kueez.com - -# Description - -The Kueez adapter requires setup and approval from the Kueez team. Please reach out to prebid@kueez.com for more information. - -The adapter supports Banner and Video(instream) media types. - -# Bid Parameters - -## Video - -| Name | Scope | Type | Description | Example -|---------------| ----- | ---- |-------------------------------------------------------------------| ------- -| `org` | required | String | the organization Id provided by your Kueez representative | "test-publisher-id" -| `floorPrice` | optional | Number | Minimum price in USD. Misuse of this parameter can impact revenue | 1.50 -| `placementId` | optional | String | A unique placement identifier | "12345678" -| `testMode` | optional | Boolean | This activates the test mode | false - -# Test Parameters - -```javascript -var adUnits = [{ - code: 'banner-div', - mediaTypes: { - banner: { - sizes: [ - [300, 250], - [728, 90] - ] - } - }, - bids: [{ - bidder: 'kueez', - params: { - org: 'test-org-id', // Required - floorPrice: 0.2, // Optional - placementId: '12345678', // Optional - testMode: true // Optional - } - }] -}, - { - code: 'dfp-video-div', - sizes: [ - [640, 480] - ], - mediaTypes: { - video: { - playerSize: [ - [640, 480] - ], - context: 'instream' - } - }, - bids: [{ - bidder: 'kueez', - params: { - org: 'test-org-id', // Required - floorPrice: 1.50, // Optional - placementId: '12345678', // Optional - testMode: true // Optional - } - }] - } -]; -``` diff --git a/test/spec/modules/kueezBidAdapter_spec.js b/test/spec/modules/kueezBidAdapter_spec.js deleted file mode 100644 index cd95a9ebdc6..00000000000 --- a/test/spec/modules/kueezBidAdapter_spec.js +++ /dev/null @@ -1,482 +0,0 @@ -import { expect } from 'chai'; -import { spec } from 'modules/kueezBidAdapter.js'; -import { newBidder } from 'src/adapters/bidderFactory.js'; -import { config } from 'src/config.js'; -import { BANNER, VIDEO } from '../../../src/mediaTypes.js'; -import * as utils from 'src/utils.js'; - -const ENDPOINT = 'https://hb.kueezssp.com/hb-kz-multi'; -const TEST_ENDPOINT = 'https://hb.kueezssp.com/hb-multi-kz-test'; -const TTL = 360; -/* eslint no-console: ["error", { allow: ["log", "warn", "error"] }] */ - -describe('kueezBidAdapter', function () { - const adapter = newBidder(spec); - - describe('inherited functions', function () { - it('exists and is a function', function () { - expect(adapter.callBids).to.exist.and.to.be.a('function'); - }); - }); - - describe('isBidRequestValid', function () { - const bid = { - 'bidder': spec.code, - 'adUnitCode': 'adunit-code', - 'sizes': [['640', '480']], - 'params': { - 'org': 'test-publisher-id' - } - }; - - it('should return true when required params are passed', function () { - expect(spec.isBidRequestValid(bid)).to.equal(true); - }); - - it('should return false when required params are not found', function () { - const newBid = Object.assign({}, bid); - delete newBid.params; - newBid.params = { - 'org': null - }; - expect(spec.isBidRequestValid(newBid)).to.equal(false); - }); - }); - - describe('buildRequests', function () { - const bidRequests = [ - { - 'bidder': spec.code, - 'adUnitCode': 'adunit-code', - 'sizes': [[640, 480]], - 'params': { - 'org': 'test-publisher-id' - }, - 'bidId': '5wfg9887sd5478', - 'loop': 1, - 'bidderRequestId': 'op87952ewq8567', - 'auctionId': '87se98rt-5789-8735-2546-t98yh5678231', - 'mediaTypes': { - 'video': { - 'playerSize': [[640, 480]], - 'context': 'instream' - } - }, - 'vastXml': '"..."' - }, - { - 'bidder': spec.code, - 'adUnitCode': 'adunit-code', - 'sizes': [[300, 250]], - 'params': { - 'org': 'test-publisher-id' - }, - 'bidId': '5wfg9887sd5478', - 'loop': 1, - 'bidderRequestId': 'op87952ewq8567', - 'auctionId': '87se98rt-5789-8735-2546-t98yh5678231', - 'mediaTypes': { - 'banner': { - } - }, - 'ad': '""' - } - ]; - - const testModeBidRequests = [ - { - 'bidder': spec.code, - 'adUnitCode': 'adunit-code', - 'sizes': [[640, 480]], - 'params': { - 'org': 'test-publisher-id', - 'testMode': true - }, - 'bidId': '5wfg9887sd5478', - 'loop': 2, - 'bidderRequestId': 'op87952ewq8567', - 'auctionId': '87se98rt-5789-8735-2546-t98yh5678231', - } - ]; - - const bidderRequest = { - bidderCode: 'kueez', - } - const placementId = '12345678'; - - it('sends the placementId to ENDPOINT via POST', function () { - bidRequests[0].params.placementId = placementId; - const request = spec.buildRequests(bidRequests, bidderRequest); - expect(request.data.bids[0].placementId).to.equal(placementId); - }); - - it('sends bid request to ENDPOINT via POST', function () { - const request = spec.buildRequests(bidRequests, bidderRequest); - expect(request.url).to.equal(ENDPOINT); - expect(request.method).to.equal('POST'); - }); - - it('sends bid request to TEST ENDPOINT via POST', function () { - const request = spec.buildRequests(testModeBidRequests, bidderRequest); - expect(request.url).to.equal(TEST_ENDPOINT); - expect(request.method).to.equal('POST'); - }); - - it('should send the correct bid Id', function () { - const request = spec.buildRequests(bidRequests, bidderRequest); - expect(request.data.bids[0].bidId).to.equal('5wfg9887sd5478'); - }); - - it('should send the correct sizes array', function () { - const request = spec.buildRequests(bidRequests, bidderRequest); - expect(request.data.bids[0].sizes).to.be.an('array'); - expect(request.data.bids[0].sizes).to.equal(bidRequests[0].sizes) - expect(request.data.bids[1].sizes).to.be.an('array'); - expect(request.data.bids[1].sizes).to.equal(bidRequests[1].sizes) - }); - - it('should send the correct media type', function () { - const request = spec.buildRequests(bidRequests, bidderRequest); - expect(request.data.bids[0].mediaType).to.equal(VIDEO) - expect(request.data.bids[1].mediaType).to.equal(BANNER) - }); - - it('should respect syncEnabled option', function() { - config.setConfig({ - userSync: { - syncEnabled: false, - filterSettings: { - all: { - bidders: '*', - filter: 'include' - } - } - } - }); - const request = spec.buildRequests(bidRequests, bidderRequest); - expect(request.data.params).to.be.an('object'); - expect(request.data.params).to.not.have.property('cs_method'); - }); - - it('should respect "iframe" filter settings', function () { - config.setConfig({ - userSync: { - syncEnabled: true, - filterSettings: { - iframe: { - bidders: [spec.code], - filter: 'include' - } - } - } - }); - const request = spec.buildRequests(bidRequests, bidderRequest); - expect(request.data.params).to.be.an('object'); - expect(request.data.params).to.have.property('cs_method', 'iframe'); - }); - - it('should respect "all" filter settings', function () { - config.setConfig({ - userSync: { - syncEnabled: true, - filterSettings: { - all: { - bidders: [spec.code], - filter: 'include' - } - } - } - }); - const request = spec.buildRequests(bidRequests, bidderRequest); - expect(request.data.params).to.be.an('object'); - expect(request.data.params).to.have.property('cs_method', 'iframe'); - }); - - it('should send the pixel user sync param if userSync is enabled and no "iframe" or "all" configs are present', function () { - config.resetConfig(); - config.setConfig({ - userSync: { - syncEnabled: true, - } - }); - const request = spec.buildRequests(bidRequests, bidderRequest); - expect(request.data.params).to.be.an('object'); - expect(request.data.params).to.have.property('cs_method', 'pixel'); - }); - - it('should respect total exclusion', function() { - config.setConfig({ - userSync: { - syncEnabled: true, - filterSettings: { - image: { - bidders: [spec.code], - filter: 'exclude' - }, - iframe: { - bidders: [spec.code], - filter: 'exclude' - } - } - } - }); - const request = spec.buildRequests(bidRequests, bidderRequest); - expect(request.data.params).to.be.an('object'); - expect(request.data.params).to.not.have.property('cs_method'); - }); - - it('should have us_privacy param if usPrivacy is available in the bidRequest', function () { - const bidderRequestWithUSP = Object.assign({uspConsent: '1YNN'}, bidderRequest); - const request = spec.buildRequests(bidRequests, bidderRequestWithUSP); - expect(request.data.params).to.be.an('object'); - expect(request.data.params).to.have.property('us_privacy', '1YNN'); - }); - - it('should have an empty us_privacy param if usPrivacy is missing in the bidRequest', function () { - const request = spec.buildRequests(bidRequests, bidderRequest); - expect(request.data.params).to.be.an('object'); - expect(request.data.params).to.not.have.property('us_privacy'); - }); - - it('should not send the gdpr param if gdprApplies is false in the bidRequest', function () { - const bidderRequestWithGDPR = Object.assign({gdprConsent: {gdprApplies: false}}, bidderRequest); - const request = spec.buildRequests(bidRequests, bidderRequestWithGDPR); - expect(request.data.params).to.be.an('object'); - expect(request.data.params).to.not.have.property('gdpr'); - expect(request.data.params).to.not.have.property('gdpr_consent'); - }); - - it('should send the gdpr param if gdprApplies is true in the bidRequest', function () { - const bidderRequestWithGDPR = Object.assign({gdprConsent: {gdprApplies: true, consentString: 'test-consent-string'}}, bidderRequest); - const request = spec.buildRequests(bidRequests, bidderRequestWithGDPR); - expect(request.data.params).to.be.an('object'); - expect(request.data.params).to.have.property('gdpr', true); - expect(request.data.params).to.have.property('gdpr_consent', 'test-consent-string'); - }); - - it('should have schain param if it is available in the bidRequest', () => { - const schain = { - ver: '1.0', - complete: 1, - nodes: [{ asi: 'indirectseller.com', sid: '00001', hp: 1 }], - }; - bidRequests[0].schain = schain; - const request = spec.buildRequests(bidRequests, bidderRequest); - expect(request.data.params).to.be.an('object'); - expect(request.data.params).to.have.property('schain', '1.0,1!indirectseller.com,00001,1,,,'); - }); - - it('should set flooPrice to getFloor.floor value if it is greater than params.floorPrice', function() { - const bid = utils.deepClone(bidRequests[0]); - bid.getFloor = () => { - return { - currency: 'USD', - floor: 3.32 - } - } - bid.params.floorPrice = 0.64; - const request = spec.buildRequests([bid], bidderRequest); - expect(request.data.bids[0]).to.be.an('object'); - expect(request.data.bids[0]).to.have.property('floorPrice', 3.32); - }); - - it('should set floorPrice to params.floorPrice value if it is greater than getFloor.floor', function() { - const bid = utils.deepClone(bidRequests[0]); - bid.getFloor = () => { - return { - currency: 'USD', - floor: 0.8 - } - } - bid.params.floorPrice = 1.5; - const request = spec.buildRequests([bid], bidderRequest); - expect(request.data.bids[0]).to.be.an('object'); - expect(request.data.bids[0]).to.have.property('floorPrice', 1.5); - }); - }); - - describe('interpretResponse', function () { - const response = { - params: { - currency: 'USD', - netRevenue: true, - }, - bids: [{ - cpm: 12.5, - vastXml: '', - width: 640, - height: 480, - requestId: '21e12606d47ba7', - adomain: ['abc.com'], - mediaType: VIDEO - }, - { - cpm: 12.5, - ad: '""', - width: 300, - height: 250, - requestId: '21e12606d47ba7', - adomain: ['abc.com'], - mediaType: BANNER - }] - }; - - const expectedVideoResponse = { - cpm: 12.5, - creativeId: '21e12606d47ba7', - currency: 'USD', - height: 480, - mediaType: VIDEO, - meta: { - mediaType: VIDEO, - advertiserDomains: ['abc.com'] - }, - netRevenue: true, - nurl: 'http://example.com/win/1234', - requestId: '21e12606d47ba7', - ttl: TTL, - width: 640, - vastXml: '' - }; - - const expectedBannerResponse = { - cpm: 12.5, - creativeId: '21e12606d47ba7', - currency: 'USD', - height: 480, - mediaType: BANNER, - meta: { - mediaType: BANNER, - advertiserDomains: ['abc.com'] - }, - netRevenue: true, - nurl: 'http://example.com/win/1234', - requestId: '21e12606d47ba7', - ttl: TTL, - width: 640, - ad: '""' - }; - - it('should get correct bid response', function () { - const result = spec.interpretResponse({ body: response }); - expect(Object.keys(result[0])).to.deep.equal(Object.keys(expectedVideoResponse)); - expect(Object.keys(result[1])).to.deep.equal(Object.keys(expectedBannerResponse)); - }); - - it('video type should have vastXml key', function () { - const result = spec.interpretResponse({ body: response }); - expect(result[0].vastXml).to.equal(expectedVideoResponse.vastXml) - }); - - it('banner type should have ad key', function () { - const result = spec.interpretResponse({ body: response }); - expect(result[1].ad).to.equal(expectedBannerResponse.ad) - }); - }) - - describe('getUserSyncs', function() { - const imageSyncResponse = { - body: { - params: { - userSyncPixels: [ - 'https://image-sync-url.test/1', - 'https://image-sync-url.test/2', - 'https://image-sync-url.test/3' - ] - } - } - }; - - const iframeSyncResponse = { - body: { - params: { - userSyncURL: 'https://iframe-sync-url.test' - } - } - }; - - it('should register all img urls from the response', function() { - const syncs = spec.getUserSyncs({ pixelEnabled: true }, [imageSyncResponse]); - expect(syncs).to.deep.equal([ - { - type: 'image', - url: 'https://image-sync-url.test/1' - }, - { - type: 'image', - url: 'https://image-sync-url.test/2' - }, - { - type: 'image', - url: 'https://image-sync-url.test/3' - } - ]); - }); - - it('should register the iframe url from the response', function() { - const syncs = spec.getUserSyncs({ iframeEnabled: true }, [iframeSyncResponse]); - expect(syncs).to.deep.equal([ - { - type: 'iframe', - url: 'https://iframe-sync-url.test' - } - ]); - }); - - it('should register both image and iframe urls from the responses', function() { - const syncs = spec.getUserSyncs({ pixelEnabled: true, iframeEnabled: true }, [iframeSyncResponse, imageSyncResponse]); - expect(syncs).to.deep.equal([ - { - type: 'iframe', - url: 'https://iframe-sync-url.test' - }, - { - type: 'image', - url: 'https://image-sync-url.test/1' - }, - { - type: 'image', - url: 'https://image-sync-url.test/2' - }, - { - type: 'image', - url: 'https://image-sync-url.test/3' - } - ]); - }); - - it('should handle an empty response', function() { - const syncs = spec.getUserSyncs({ iframeEnabled: true }, []); - expect(syncs).to.deep.equal([]); - }); - - it('should handle when user syncs are disabled', function() { - const syncs = spec.getUserSyncs({ pixelEnabled: false }, [imageSyncResponse]); - expect(syncs).to.deep.equal([]); - }); - }) - - describe('onBidWon', function() { - beforeEach(function() { - sinon.stub(utils, 'triggerPixel'); - }); - afterEach(function() { - utils.triggerPixel.restore(); - }); - - it('Should trigger pixel if bid nurl', function() { - const bid = { - 'bidder': spec.code, - 'adUnitCode': 'adunit-code', - 'sizes': [['640', '480']], - 'nurl': 'http://example.com/win/1234', - 'params': { - 'org': 'test-publisher-id' - } - }; - - spec.onBidWon(bid); - expect(utils.triggerPixel.callCount).to.equal(1) - }) - }) -}); From affbb620fa3f8b100446340a7fa3dc55d9db3cce Mon Sep 17 00:00:00 2001 From: Patrick McCann Date: Thu, 13 Mar 2025 08:25:02 -0400 Subject: [PATCH 09/92] Prebid 10: Delete modules/akamaiDapRtdProvider (#12862) * Prebid 10: Delete modules/akamaiDapRtdProvider.md * Delete modules/akamaiDapRtdProvider.js * Delete test/spec/modules/akamaiDapRtdProvider_spec.js * Delete integrationExamples/gpt/akamaidap_segments_example.html * Update adloader.js * Update .submodules.json * Update yahooAdsBidAdapter.js --- .../gpt/akamaidap_segments_example.html | 134 ---- modules/.submodules.json | 1 - modules/akamaiDapRtdProvider.js | 27 - modules/akamaiDapRtdProvider.md | 49 -- modules/yahooAdsBidAdapter.js | 1 - src/adloader.js | 1 - .../spec/modules/akamaiDapRtdProvider_spec.js | 600 ------------------ 7 files changed, 813 deletions(-) delete mode 100644 integrationExamples/gpt/akamaidap_segments_example.html delete mode 100644 modules/akamaiDapRtdProvider.js delete mode 100644 modules/akamaiDapRtdProvider.md delete mode 100644 test/spec/modules/akamaiDapRtdProvider_spec.js diff --git a/integrationExamples/gpt/akamaidap_segments_example.html b/integrationExamples/gpt/akamaidap_segments_example.html deleted file mode 100644 index f44c60292ce..00000000000 --- a/integrationExamples/gpt/akamaidap_segments_example.html +++ /dev/null @@ -1,134 +0,0 @@ - - - - - - - - - - - - - -

Prebid.js Test

-
Div-1
-
- -
-
Segments Sent to Bidding Adapter
-
- - diff --git a/modules/.submodules.json b/modules/.submodules.json index 92932beb445..7993fb0d36c 100644 --- a/modules/.submodules.json +++ b/modules/.submodules.json @@ -69,7 +69,6 @@ "adlooxRtdProvider", "adnuntiusRtdProvider", "airgridRtdProvider", - "akamaiDapRtdProvider", "arcspanRtdProvider", "azerionedgeRtdProvider", "blueconicRtdProvider", diff --git a/modules/akamaiDapRtdProvider.js b/modules/akamaiDapRtdProvider.js deleted file mode 100644 index e5a647a90ef..00000000000 --- a/modules/akamaiDapRtdProvider.js +++ /dev/null @@ -1,27 +0,0 @@ -/** - * This module adds the Akamai DAP RTD provider to the real time data module - * The {@link module:modules/realTimeData} module is required - * The module will fetch real-time data from DAP - * @module modules/akamaiDapRtdProvider - * @requires module:modules/realTimeData - */ - -import { - createRtdProvider -} from './symitriDapRtdProvider.js'/* eslint prebid/validate-imports: "off" */ - -export const { - addRealTimeData, - getRealTimeData, - generateRealTimeData, - rtdSubmodule: akamaiDapRtdSubmodule, - storage, - dapUtils, - DAP_TOKEN, - DAP_MEMBERSHIP, - DAP_ENCRYPTED_MEMBERSHIP, - DAP_SS_ID, - DAP_DEFAULT_TOKEN_TTL, - DAP_MAX_RETRY_TOKENIZE, - DAP_CLIENT_ENTROPY -} = createRtdProvider('dap', 'akamaidap', 'Akamai'); diff --git a/modules/akamaiDapRtdProvider.md b/modules/akamaiDapRtdProvider.md deleted file mode 100644 index efd93db3a51..00000000000 --- a/modules/akamaiDapRtdProvider.md +++ /dev/null @@ -1,49 +0,0 @@ -### Overview - - Akamai DAP Real time data Provider automatically invokes the DAP APIs and submit audience segments and the SAID to the bid-stream. - -### Integration - - 1) Build the akamaiDapRTD module into the Prebid.js package with: - - ``` - gulp build --modules=akamaiDapRtdProvider,... - ``` - - 2) Use `setConfig` to instruct Prebid.js to initilaize the akamaiDapRtdProvider module, as specified below. - -### Configuration - -``` - pbjs.setConfig({ - realTimeData: { - auctionDelay: 2000, - dataProviders: [ - { - name: "dap", - waitForIt: true, - params: { - apiHostname: '', - apiVersion: "x1", - domain: 'your-domain.com', - identityType: 'email' | 'mobile' | ... | 'dap-signature:1.3.0', - segtax: 504, - dapEntropyUrl: 'https://dap-dist.akamaized.net/dapentropy.js', - dapEntropyTimeout: 1500 // Maximum time for dapentropy to run - } - } - ] - } - }); - ``` - -Please reach out to your Akamai account representative(Prebid@akamai.com) to get provisioned on the DAP platform. - - -### Testing -To view an example of available segments returned by dap: -``` -‘gulp serve --modules=rtdModule,akamaiDapRtdProvider,appnexusBidAdapter,sovrnBidAdapter’ -``` -and then point your browser at: -"http://localhost:9999/integrationExamples/gpt/akamaidap_segments_example.html" diff --git a/modules/yahooAdsBidAdapter.js b/modules/yahooAdsBidAdapter.js index 7622a5b7587..6e67df84b2f 100644 --- a/modules/yahooAdsBidAdapter.js +++ b/modules/yahooAdsBidAdapter.js @@ -27,7 +27,6 @@ const SUPPORTED_USER_ID_SOURCES = [ 'admixer.net', 'adserver.org', 'adtelligent.com', - 'akamai.com', 'amxdt.net', 'audigent.com', 'britepool.com', diff --git a/src/adloader.js b/src/adloader.js index 7a24cfc2954..b9de42ce8db 100644 --- a/src/adloader.js +++ b/src/adloader.js @@ -16,7 +16,6 @@ const _approvedLoadExternalJSList = [ 'aaxBlockmeter', 'adagio', 'adloox', - 'akamaidap', 'arcspan', 'airgrid', 'browsi', diff --git a/test/spec/modules/akamaiDapRtdProvider_spec.js b/test/spec/modules/akamaiDapRtdProvider_spec.js deleted file mode 100644 index 0787f911591..00000000000 --- a/test/spec/modules/akamaiDapRtdProvider_spec.js +++ /dev/null @@ -1,600 +0,0 @@ -import {config} from 'src/config.js'; -import { - dapUtils, - generateRealTimeData, - akamaiDapRtdSubmodule, - storage, DAP_MAX_RETRY_TOKENIZE, DAP_SS_ID, DAP_TOKEN, DAP_MEMBERSHIP, DAP_ENCRYPTED_MEMBERSHIP -} from 'modules/akamaiDapRtdProvider.js'; -import {server} from 'test/mocks/xhr.js'; -import {hook} from '../../../src/hook.js'; -const responseHeader = {'Content-Type': 'application/json'}; - -describe('akamaiDapRtdProvider', function() { - const testReqBidsConfigObj = { - adUnits: [ - { - bids: ['bid1', 'bid2'] - } - ] - }; - - const onDone = function() { return true }; - - const sampleGdprConsentConfig = { - 'gdpr': { - 'consentString': null, - 'vendorData': {}, - 'gdprApplies': true - } - }; - - const sampleUspConsentConfig = { - 'usp': '1YYY' - }; - - const sampleIdentity = { - type: 'dap-signature:1.0.0' - }; - - const cmoduleConfig = { - 'name': 'dap', - 'waitForIt': true, - 'params': { - 'apiHostname': 'prebid.dap.akadns.net', - 'apiVersion': 'x1', - 'domain': 'prebid.org', - 'identityType': 'dap-signature:1.0.0', - 'segtax': 708 - } - } - - const emoduleConfig = { - 'name': 'dap', - 'waitForIt': true, - 'params': { - 'apiHostname': 'prebid.dap.akadns.net', - 'apiVersion': 'x1', - 'domain': 'prebid.org', - 'identityType': 'dap-signature:1.0.0', - 'segtax': 710 - } - } - - const sampleConfig = { - 'api_hostname': 'prebid.dap.akadns.net', - 'api_version': 'x1', - 'domain': 'prebid.org', - 'segtax': 708, - 'identity': sampleIdentity - } - - const esampleConfig = { - 'api_hostname': 'prebid.dap.akadns.net', - 'api_version': 'x1', - 'domain': 'prebid.org', - 'segtax': 710, - 'identity': sampleIdentity - } - let cacheExpiry = Math.round(Date.now() / 1000.0) + 300; // in seconds - const sampleCachedToken = {'expires_at': cacheExpiry, 'token': 'eyJhbGciOiJkaXIiLCJlbmMiOiJBMTI4Q0JDLUhTMjU2Iiwia2lkIjoicGFzc3dvcmQxIn0..6buzBd2BjtgoyaNbHN8YnQ.l38avCfm3sYNy798-ETYOugz0cOx1cCkjACkAhYszxzrZ0sUJ0AiF-NdDXVTiTyp2Ih3vCWKzS0rKJ8lbS1zhyEVWVu91QwtwseM2fBbwA5ggAgBEo5wV-IXqDLPxVnxsPF0D3hP6cNCiH9Q2c-vULfsLhMhG5zvvZDPBbn4hUY5fKB8LoCBTF9rbuuWGYK1nramnb4AlS5UK82wBsHQea1Ou_Kp5wWCMNZ6TZk5qKIuRBfPIAhQblWvHECaHXkg1wyoM9VASs_yNhne7RR-qkwzbFiPFiMJibNOt9hF3_vPDJO5-06ZBjRTP1BllYGWxI-uQX6InzN18Wtun2WHqg.63sH0SNlIRcsK57v0pMujfB_nhU8Y5CuQbsHqH5MGoM'}; - const cachedEncryptedMembership = {'expires_at': cacheExpiry, 'encryptedSegments': 'eyJhbGciOiJkaXIiLCJlbmMiOiJBMTI4Q0JDLUhTMjU2Iiwia2lkIjoic29tZXNlY3JldGludmF1bHQifQ..IvnIUQDqWBVYIS0gbcE9bw.Z4NZGvtogWaWlGH4e-GdYKe_PUc15M2x3Bj85rMWsN1A17mIxQIMOfg2hsQ2tgieLu5LggWPmsFu1Wbph6P0k3kOu1dVReoIhOHzxw50rP0DLHKaEZ5mLMJ7Lcosvwh4miIfFuCHlsX7J0sFgOTAp0zGo1S_UsHLtev1JflhjoSB0AoX95ALbAnyctirPuLJM8gZ1vXTiZ01jpvucGyR1lM4cWjPOeD8jPtgwaPGgSRZXE-3X2Cqy7z4Giam5Uqu74LPWTBuKtUQTGyAXA5QJoP7xwTbsU4O1f69lu3fWNqC92GijeTH1A4Zd_C-WXxWuQlDEURjlkWQoaqTHka2OqlnwukEQIf_v0r5KQQX64CTLhEUH91jeD0-E9ClcIP7pwOLxxqiKoaBmx8Mrnm_6Agj5DtTA1rusy3AL63sI_rsUxrmLrVt0Wft4aCfRkW8QpQxu8clFdOmce0NNCGeBCyCPVw9d9izrILlXJ6rItU2cpFrcbz8uw2otamF5eOFCOY3IzHedWVNNuKHFIUVC_xYSlsYvQ8f2QIP1eiMbmukcuPzmTzjw1h1_7IKaj-jJkXrnrY-TdDgX_4-_Z3rmbpXK2yTR7dBrsg-ubqFbgbKic1b4zlQEO_LbBlgPl3DYdWEuJ8CY2NUt1GfpATQGsufS2FTY1YGw_gkPe3q04l_cgLafDoxHvHh_t_0ZgPjciW82gThB_kN4RP7Mc3krVcXl_P6N1VbV07xyx0hCyVsrrxbLslI8q9wYDiLGci7mNmByM5j7SXV9jPwwPkHtn0HfMJlw2PFbIDPjgG3h7sOyLcBIJTTvuUIgpHPIkRWLIl_4FlIucXbJ7orW2nt5BWleBVHgumzGcnl9ZNcZb3W-dsdYPSOmuj0CY28MRTP2oJ1rzLInbDDpIRffJBtR7SS4nYyy7Vi09PtBigod5YNz1Q0WDSJxr8zeH_aKFaXInw7Bfo_U0IAcLiRgcT0ogsMLeQRjRFy27mr4XNJv3NtHhbdjDAwF2aClCktXyXbQaVdsPH2W71v6m2Q9rB5GQWOktw2s5f-4N1-_EBPGq6TgjF-aJZP22MJVwp1pimT50DfOzoeEqDwi862NNwNNoHmcObH0ZfwAXlhRxsgupNBe20-MNNABj2Phlfv4DUrtQbMdfCnNiypzNCmoTb7G7c_o5_JUwoV_GVkwUtvmi_IUm05P4GeMASSUw8zDKVRAj9h31C2cabM8RjMHGhkbCWpUP2pcz9zlJ7Y76Dh3RLnctfTw7DG9U4w4UlaxNZOgLUiSrGwfyapuSiuGUpuOJkBBLiHmEqAGI5C8oJpcVRccNlHxJAYowgXyFopD5Fr-FkXmv8KMkS0h5C9F6KihmDt5sqDD0qnjM0hHJgq01l7wjVnhEmPpyD-6auFQ-xDnbh1uBOJ_0gCVbRad--FSa5p-dXenggegRxOvZXJ0iAtM6Fal5Og-RCjexIHa9WhVbXhQBJpkSTWwAajZJ64eQ.yih49XB51wE-Xob7COT9OYqBrzBmIMVCQbLFx2UdzkI'}; - const cachedMembership = {'expires_at': cacheExpiry, 'said': 'eyJhbGciOiJkaXIiLCJlbmMiOiJBMTI4Q0JDLUhTMjU2Iiwia2lkIjoicGFzc3dvcmQxIn0..QwvU5h0NVJYaJbs5EqWCKA.XNaJHSlnsH8P-yBIr3gIEqavLONWDIFyj7QCHFwJVkwXH_EYkxrk0_26b0uMPzfJp5URnqxKZusMH9DzEJsmj8EMrKQv1y3IYYMsW5_0BdP5bcAWfG6fzOqtMOwLiYRkYiQOqn1ZVGzhovheHWEmNr2_oCY0LvAr3iN1eG_K-l-bBKvBWnwvuuGKquUfCqO8NMMq6wtkecEXM9blqFRZ7oNYmW2aIG7qcHUsrUW7HMr9Ev2Ik0sIeEUsOYrgf_X_VA64RgKSTRugS9FupMv1p54JkHokwduF9pOFmW8QLQi8itFogKGbbgvOTNnmahxQUX5FcrjjYLqHwKqC8htLdlHnO5LWU9l4A7vLXrRurvoSnh0cAJy0GsdoyEwTqR9bwVFHoPquxlJjQ4buEd7PIxpBj9Qg9oOPH3b2upbMTu5CQ9oj526eXPhP5G54nwGklm2AZ3Vggd7jCQJn45Jjiq0iIfsXAtpqS2BssCLBN8WhmUTnStK8m5sux6WUBdrpDESQjPj-EEHVS-DB5rA7icRUh6EzRxzen2rndvHvnwVhSG_l6cwPYuJ0HE0KBmYHOoqNpKwzoGiKFHrf4ReA06iWB3V2TEGJucGujhtQ9_18WwHCeJ1XtQiiO1eqa3tp5MwAbFXawVFl3FFOBgadrPyvGmkmUJ6FCLU2MSwHiYZmANMnJsokFX_6DwoAgO3U_QnvEHIVSvefc7ReeJ8fBDdmrH3LtuLrUpXsvLvEIMQdWQ_SXhjKIi7tOODR8CfrhUcdIjsp3PZs1DpuOcDB6YJKbGnKZTluLUJi3TyHgyi-DHXdTm-jSE5i_DYJGW-t2Gf23FoQhexv4q7gdrfsKfcRJNrZLp6Gd6jl4zHhUtY.nprKBsy9taQBk6dCPbA7BFF0CiGhQOEF_MazZ2bedqk', 'cohorts': ['9', '11', '13']}; - const rtdUserObj = { - name: 'www.dataprovider3.com', - ext: { - taxonomyname: 'iab_audience_taxonomy' - }, - segment: [ - { - id: '1918' - }, - { - id: '1939' - } - ] - }; - - const encRtdUserObj = { - name: 'www.dataprovider3.com', - ext: { - segtax: 710, - taxonomyname: 'iab_audience_taxonomy' - }, - segment: [] - }; - - const cachedRtd = { - rtd: { - ortb2: { - user: { - data: [rtdUserObj] - } - } - } - }; - - let membership = { - said: cachedMembership.said, - cohorts: cachedMembership.cohorts, - attributes: null - }; - let encMembership = { - encryptedSegments: cachedEncryptedMembership.encryptedSegments - }; - encRtdUserObj.segment.push({ id: encMembership.encryptedSegments }); - const cachedEncRtd = { - rtd: { - ortb2: { - user: { - data: [encRtdUserObj] - } - } - } - }; - - before(() => { - hook.ready(); - }); - - let ortb2, bidConfig; - - beforeEach(function() { - bidConfig = {ortb2Fragments: {}}; - ortb2 = bidConfig.ortb2Fragments.global = {}; - config.resetConfig(); - storage.removeDataFromLocalStorage(DAP_TOKEN); - storage.removeDataFromLocalStorage(DAP_MEMBERSHIP); - storage.removeDataFromLocalStorage(DAP_ENCRYPTED_MEMBERSHIP); - storage.removeDataFromLocalStorage(DAP_SS_ID); - }); - - afterEach(function () { - }); - - describe('akamaiDapRtdSubmodule', function() { - it('successfully instantiates', function () { - expect(akamaiDapRtdSubmodule.init()).to.equal(true); - }); - }); - - describe('Get Real-Time Data', function() { - it('gets rtd from local storage cache', function() { - let dapGetMembershipFromLocalStorageStub = sinon.stub(dapUtils, 'dapGetMembershipFromLocalStorage').returns(membership) - let dapGetRtdObjStub = sinon.stub(dapUtils, 'dapGetRtdObj').returns(cachedRtd) - let dapGetEncryptedMembershipFromLocalStorageStub = sinon.stub(dapUtils, 'dapGetEncryptedMembershipFromLocalStorage').returns(encMembership) - let dapGetEncryptedRtdObjStub = sinon.stub(dapUtils, 'dapGetEncryptedRtdObj').returns(cachedEncRtd) - let callDapApisStub = sinon.stub(dapUtils, 'callDapAPIs') - try { - storage.setDataInLocalStorage(DAP_TOKEN, JSON.stringify(sampleCachedToken)); - expect(ortb2).to.eql({}); - generateRealTimeData(bidConfig, () => {}, emoduleConfig, {}); - - expect(ortb2.user.data).to.deep.include.members([encRtdUserObj]); - generateRealTimeData(bidConfig, () => {}, cmoduleConfig, {}); - expect(ortb2.user.data).to.deep.include.members([rtdUserObj]); - } finally { - dapGetRtdObjStub.restore() - dapGetMembershipFromLocalStorageStub.restore() - dapGetEncryptedRtdObjStub.restore() - dapGetEncryptedMembershipFromLocalStorageStub.restore() - callDapApisStub.restore() - } - }); - }); - - describe('calling DAP APIs', function() { - it('Calls callDapAPIs for unencrypted segments flow', function() { - storage.setDataInLocalStorage(DAP_TOKEN, JSON.stringify(sampleCachedToken)); - let dapExtractExpiryFromTokenStub = sinon.stub(dapUtils, 'dapExtractExpiryFromToken').returns(cacheExpiry) - try { - expect(ortb2).to.eql({}); - dapUtils.callDapAPIs(bidConfig, () => {}, cmoduleConfig, {}); - let membership = {'cohorts': ['9', '11', '13'], 'said': 'sample-said'} - let membershipRequest = server.requests[0]; - membershipRequest.respond(200, responseHeader, JSON.stringify(membership)); - let tokenWithExpiry = 'Sample-token-with-exp' - let tokenizeRequest = server.requests[1]; - responseHeader['Akamai-DAP-Token'] = tokenWithExpiry; - tokenizeRequest.respond(200, responseHeader, JSON.stringify(tokenWithExpiry)); - let data = dapUtils.dapGetRtdObj(membership, cmoduleConfig.params.segtax); - expect(ortb2.user.data).to.deep.include.members(data.rtd.ortb2.user.data); - } finally { - dapExtractExpiryFromTokenStub.restore(); - } - }); - - it('Calls callDapAPIs for encrypted segments flow', function() { - storage.setDataInLocalStorage(DAP_TOKEN, JSON.stringify(sampleCachedToken)); - let dapExtractExpiryFromTokenStub = sinon.stub(dapUtils, 'dapExtractExpiryFromToken').returns(cacheExpiry) - try { - expect(ortb2).to.eql({}); - dapUtils.callDapAPIs(bidConfig, () => {}, emoduleConfig, {}); - let encMembership = 'Sample-enc-token'; - let membershipRequest = server.requests[0]; - responseHeader['Akamai-DAP-Token'] = encMembership; - membershipRequest.respond(200, responseHeader, JSON.stringify(encMembership)); - let tokenWithExpiry = 'Sample-token-with-exp' - let tokenizeRequest = server.requests[1]; - responseHeader['Akamai-DAP-Token'] = tokenWithExpiry; - tokenizeRequest.respond(200, responseHeader, JSON.stringify(tokenWithExpiry)); - let data = dapUtils.dapGetEncryptedRtdObj({'encryptedSegments': encMembership}, emoduleConfig.params.segtax); - expect(ortb2.user.data).to.deep.include.members(data.rtd.ortb2.user.data); - } finally { - dapExtractExpiryFromTokenStub.restore(); - } - }); - }); - - describe('dapTokenize', function () { - it('dapTokenize error callback', function () { - let configAsync = JSON.parse(JSON.stringify(sampleConfig)); - let submoduleCallback = dapUtils.dapTokenize(configAsync, sampleIdentity, onDone, - function(token, status, xhr, onDone) { - }, - function(xhr, status, error, onDone) { - } - ); - let request = server.requests[0]; - request.respond(400, responseHeader, JSON.stringify('error')); - expect(submoduleCallback).to.equal(undefined); - }); - - it('dapTokenize success callback', function () { - let configAsync = JSON.parse(JSON.stringify(sampleConfig)); - let submoduleCallback = dapUtils.dapTokenize(configAsync, sampleIdentity, onDone, - function(token, status, xhr, onDone) { - }, - function(xhr, status, error, onDone) { - } - ); - let request = server.requests[0]; - request.respond(200, responseHeader, JSON.stringify('success')); - expect(submoduleCallback).to.equal(undefined); - }); - }); - - describe('dapTokenize and dapMembership incorrect params', function () { - it('Onerror and config are null', function () { - expect(dapUtils.dapTokenize(null, 'identity', onDone, null, null)).to.be.equal(undefined); - expect(dapUtils.dapMembership(null, 'identity', onDone, null, null)).to.be.equal(undefined); - expect(dapUtils.dapEncryptedMembership(null, 'identity', onDone, null, null)).to.be.equal(undefined); - const config = { - 'api_hostname': 'prebid.dap.akadns.net', - 'api_version': 1, - 'domain': '', - 'segtax': 708 - }; - const encConfig = { - 'api_hostname': 'prebid.dap.akadns.net', - 'api_version': 1, - 'domain': '', - 'segtax': 710 - }; - let identity = { - type: 'dap-signature:1.0.0' - }; - expect(dapUtils.dapTokenize(config, identity, onDone, null, null)).to.be.equal(undefined); - expect(dapUtils.dapMembership(config, 'token', onDone, null, null)).to.be.equal(undefined); - expect(dapUtils.dapEncryptedMembership(encConfig, 'token', onDone, null, null)).to.be.equal(undefined); - }); - }); - - describe('Getting dapTokenize, dapMembership and dapEncryptedMembership from localstorage', function () { - it('dapGetTokenFromLocalStorage success', function () { - storage.setDataInLocalStorage(DAP_TOKEN, JSON.stringify(sampleCachedToken)); - expect(dapUtils.dapGetTokenFromLocalStorage(60)).to.be.equal(sampleCachedToken.token); - }); - - it('dapGetMembershipFromLocalStorage success', function () { - storage.setDataInLocalStorage(DAP_MEMBERSHIP, JSON.stringify(cachedMembership)); - expect(JSON.stringify(dapUtils.dapGetMembershipFromLocalStorage())).to.be.equal(JSON.stringify(membership)); - }); - - it('dapGetEncryptedMembershipFromLocalStorage success', function () { - storage.setDataInLocalStorage(DAP_ENCRYPTED_MEMBERSHIP, JSON.stringify(cachedEncryptedMembership)); - expect(JSON.stringify(dapUtils.dapGetEncryptedMembershipFromLocalStorage())).to.be.equal(JSON.stringify(encMembership)); - }); - }); - - describe('dapMembership', function () { - it('dapMembership success callback', function () { - let configAsync = JSON.parse(JSON.stringify(sampleConfig)); - let submoduleCallback = dapUtils.dapMembership(configAsync, 'token', onDone, - function(token, status, xhr, onDone) { - }, - function(xhr, status, error, onDone) { - } - ); - let request = server.requests[0]; - request.respond(200, responseHeader, JSON.stringify('success')); - expect(submoduleCallback).to.equal(undefined); - }); - - it('dapMembership error callback', function () { - let configAsync = JSON.parse(JSON.stringify(sampleConfig)); - let submoduleCallback = dapUtils.dapMembership(configAsync, 'token', onDone, - function(token, status, xhr, onDone) { - }, - function(xhr, status, error, onDone) { - } - ); - let request = server.requests[0]; - request.respond(400, responseHeader, JSON.stringify('error')); - expect(submoduleCallback).to.equal(undefined); - }); - }); - - describe('dapEncMembership', function () { - it('dapEncMembership success callback', function () { - let configAsync = JSON.parse(JSON.stringify(esampleConfig)); - let submoduleCallback = dapUtils.dapEncryptedMembership(configAsync, 'token', onDone, - function(token, status, xhr, onDone) { - }, - function(xhr, status, error, onDone) { - } - ); - let request = server.requests[0]; - request.respond(200, responseHeader, JSON.stringify('success')); - expect(submoduleCallback).to.equal(undefined); - }); - - it('dapEncMembership error callback', function () { - let configAsync = JSON.parse(JSON.stringify(esampleConfig)); - let submoduleCallback = dapUtils.dapEncryptedMembership(configAsync, 'token', onDone, - function(token, status, xhr, onDone) { - }, - function(xhr, status, error, onDone) { - } - ); - let request = server.requests[0]; - request.respond(400, responseHeader, JSON.stringify('error')); - expect(submoduleCallback).to.equal(undefined); - }); - }); - - describe('dapMembership', function () { - it('should invoke the getDapToken and getDapMembership', function () { - let membership = { - said: 'item.said1', - cohorts: 'item.cohorts', - attributes: null - }; - - let getDapMembershipStub = sinon.stub(dapUtils, 'dapGetMembershipFromLocalStorage').returns(membership); - let callDapApisStub = sinon.stub(dapUtils, 'callDapAPIs'); - try { - generateRealTimeData(testReqBidsConfigObj, onDone, cmoduleConfig); - expect(getDapMembershipStub.calledOnce).to.be.equal(true); - } finally { - getDapMembershipStub.restore(); - callDapApisStub.restore(); - } - }); - }); - - describe('dapEncMembership test', function () { - it('should invoke the getDapToken and getEncDapMembership', function () { - let encMembership = { - encryptedSegments: 'enc.seg', - }; - - let getDapEncMembershipStub = sinon.stub(dapUtils, 'dapGetEncryptedMembershipFromLocalStorage').returns(encMembership); - let callDapApisStub = sinon.stub(dapUtils, 'callDapAPIs'); - try { - generateRealTimeData(testReqBidsConfigObj, onDone, emoduleConfig); - expect(getDapEncMembershipStub.calledOnce).to.be.equal(true); - } finally { - getDapEncMembershipStub.restore(); - callDapApisStub.restore(); - } - }); - }); - - describe('dapGetRtdObj test', function () { - it('dapGetRtdObj', function () { - const config = { - apiHostname: 'prebid.dap.akadns.net', - apiVersion: 'x1', - domain: 'prebid.org', - segtax: 708 - }; - expect(dapUtils.dapRefreshMembership(ortb2, config, 'token', onDone)).to.equal(undefined) - const membership = {cohorts: ['1', '5', '7']} - expect(dapUtils.dapGetRtdObj(membership, config.segtax)).to.not.equal(undefined); - }); - }); - - describe('checkAndAddRealtimeData test', function () { - it('add realtime data for segtax 708 and 710', function () { - dapUtils.checkAndAddRealtimeData(ortb2, cachedEncRtd, 710); - dapUtils.checkAndAddRealtimeData(ortb2, cachedEncRtd, 710); - expect(ortb2.user.data).to.deep.include.members([encRtdUserObj]); - dapUtils.checkAndAddRealtimeData(ortb2, cachedRtd, 708); - expect(ortb2.user.data).to.deep.include.members([rtdUserObj]); - }); - }); - - describe('dapExtractExpiryFromToken test', function () { - it('test dapExtractExpiryFromToken function', function () { - let tokenWithoutExpiry = 'eyJhbGciOiJkaXIiLCJlbmMiOiJBMTI4Q0JDLUhTMjU2Iiwia2lkIjoicGFzc3dvcmQxIn0..6buzBd2BjtgoyaNbHN8YnQ.l38avCfm3sYNy798-ETYOugz0cOx1cCkjACkAhYszxzrZ0sUJ0AiF-NdDXVTiTyp2Ih3vCWKzS0rKJ8lbS1zhyEVWVu91QwtwseM2fBbwA5ggAgBEo5wV-IXqDLPxVnxsPF0D3hP6cNCiH9Q2c-vULfsLhMhG5zvvZDPBbn4hUY5fKB8LoCBTF9rbuuWGYK1nramnb4AlS5UK82wBsHQea1Ou_Kp5wWCMNZ6TZk5qKIuRBfPIAhQblWvHECaHXkg1wyoM9VASs_yNhne7RR-qkwzbFiPFiMJibNOt9hF3_vPDJO5-06ZBjRTP1BllYGWxI-uQX6InzN18Wtun2WHqg.63sH0SNlIRcsK57v0pMujfB_nhU8Y5CuQbsHqH5MGoM' - expect(dapUtils.dapExtractExpiryFromToken(tokenWithoutExpiry)).to.equal(undefined); - let tokenWithExpiry = 'eyJhbGciOiJkaXIiLCJlbmMiOiJBMTI4Q0JDLUhTMjU2Iiwia2lkIjoicGFzc3dvcmQxIiwiZXhwIjoxNjQzODMwMzY5fQ..hTbcSQgmmO0HUJJrQ5fRHw.7zjrQXNNVkb-GD0ZhIVhEPcWbyaDBilHTWv-bp1lFZ9mdkSC0QbcAvUbYteiTD7ya23GUwcL2WOW8WgRSHaWHOJe0B5NDqfdUGTzElWfu7fFodRxRgGmwG8Rq5xxteFKLLGHLf1mFYRJKDtjtgajGNUKIDfn9AEt-c5Qz4KU8VolG_KzrLROx-f6Z7MnoPTcwRCj0WjXD6j2D6RAZ80-mKTNIsMIELdj6xiabHcjDJ1WzwtwCZSE2y2nMs451pSYp8W-bFPfZmDDwrkjN4s9ASLlIXcXgxK-H0GsiEbckQOZ49zsIKyFtasBvZW8339rrXi1js-aBh99M7aS5w9DmXPpUDmppSPpwkeTfKiqF0cQiAUq8tpeEQrGDJuw3Qt2.XI8h9Xw-VZj_NOmKtV19wLM63S4snos7rzkoHf9FXCw' - expect(dapUtils.dapExtractExpiryFromToken(tokenWithExpiry)).to.equal(1643830369); - }); - }); - - describe('dapRefreshToken test', function () { - it('test dapRefreshToken success response', function () { - dapUtils.dapRefreshToken(ortb2, sampleConfig, true, onDone) - let request = server.requests[0]; - responseHeader['Akamai-DAP-Token'] = sampleCachedToken.token; - request.respond(200, responseHeader, JSON.stringify(sampleCachedToken.token)); - expect(JSON.parse(storage.getDataFromLocalStorage(DAP_TOKEN)).token).to.be.equal(sampleCachedToken.token); - }); - - it('test dapRefreshToken success response with deviceid 100', function () { - dapUtils.dapRefreshToken(ortb2, esampleConfig, true, onDone) - let request = server.requests[0]; - responseHeader['Akamai-DAP-100'] = sampleCachedToken.token; - request.respond(200, responseHeader, ''); - expect(storage.getDataFromLocalStorage('dap_deviceId100')).to.be.equal(sampleCachedToken.token); - }); - - it('test dapRefreshToken success response with exp claim', function () { - dapUtils.dapRefreshToken(ortb2, sampleConfig, true, onDone) - let request = server.requests[0]; - let tokenWithExpiry = 'eyJhbGciOiJkaXIiLCJlbmMiOiJBMTI4Q0JDLUhTMjU2Iiwia2lkIjoicGFzc3dvcmQxIiwiZXhwIjoxNjQzODMwMzY5fQ..hTbcSQgmmO0HUJJrQ5fRHw.7zjrQXNNVkb-GD0ZhIVhEPcWbyaDBilHTWv-bp1lFZ9mdkSC0QbcAvUbYteiTD7ya23GUwcL2WOW8WgRSHaWHOJe0B5NDqfdUGTzElWfu7fFodRxRgGmwG8Rq5xxteFKLLGHLf1mFYRJKDtjtgajGNUKIDfn9AEt-c5Qz4KU8VolG_KzrLROx-f6Z7MnoPTcwRCj0WjXD6j2D6RAZ80-mKTNIsMIELdj6xiabHcjDJ1WzwtwCZSE2y2nMs451pSYp8W-bFPfZmDDwrkjN4s9ASLlIXcXgxK-H0GsiEbckQOZ49zsIKyFtasBvZW8339rrXi1js-aBh99M7aS5w9DmXPpUDmppSPpwkeTfKiqF0cQiAUq8tpeEQrGDJuw3Qt2.XI8h9Xw-VZj_NOmKtV19wLM63S4snos7rzkoHf9FXCw' - responseHeader['Akamai-DAP-Token'] = tokenWithExpiry; - request.respond(200, responseHeader, JSON.stringify(tokenWithExpiry)); - expect(JSON.parse(storage.getDataFromLocalStorage(DAP_TOKEN)).expires_at).to.be.equal(1643830359); - }); - - it('test dapRefreshToken error response', function () { - storage.setDataInLocalStorage(DAP_TOKEN, JSON.stringify(sampleCachedToken)); - dapUtils.dapRefreshToken(ortb2, sampleConfig, false, onDone) - let request = server.requests[0]; - request.respond(400, responseHeader, 'error'); - expect(JSON.parse(storage.getDataFromLocalStorage(DAP_TOKEN)).expires_at).to.be.equal(cacheExpiry);// Since the expiry is same, the token is not updated in the cache - }); - }); - - describe('dapRefreshEncryptedMembership test', function () { - it('test dapRefreshEncryptedMembership success response', function () { - let expiry = Math.round(Date.now() / 1000.0) + 3600; // in seconds - let encMembership = 'eyJhbGciOiJkaXIiLCJlbmMiOiJBMTI4Q0JDLUhTMjU2Iiwia2lkIjoic29tZXNlY3JldGludmF1bHQifQ..f8_At4OqeQXyQcSwThOJ_w.69ImVQ3bEZ6QP7ROCRpAJjNcKY49SEPYR6qTp_8l7L8kQdPbpi4wmuOzt78j7iBrX64k2wltzmQFjDmVKSxDhrEguxpgx6t-L1tT8ZA0UosMWpVsgmKEZxOn2e9ES3jw8RNCS4WSWocSPQX33xSb51evXjm9E1s0tGoLnwXl0GsUvzRsSU86wQG6RZnAQTi7s-r-M2TKibdDjUqgIt62vJ-aBZ7RWw91MINgOdmDNs1bFfbBX5Cy1kd4-kjvRDz_aJ6zHX4sK_7EmQhGEY3tW-A3_l2I88mw-RSJaPkb_IWg0QpVwXDaE2F2g8NpY1PzCRvG_NIE8r28eK5q44OMVitykHmKmBXGDj7z2JVgoXkfo5u0I-dypZARn4GP_7niK932avB-9JD7Mz3TrlU4GZ7IpYfJ91PMsRhrs5xNPQwLZbpuhF76A7Dp7iss71UjkGCiPTU6udfRb4foyf_7xEF66m1eQVcVaMdxEbMuu9GBfdr-d04TbtJhPfUV8JfxTenvRYoi13n0j5kH0M5OgaSQD9kQ3Mrd9u-Cms-BGtT0vf-N8AaFZY_wn0Y4rkpv5HEaH7z3iT4RCHINWrXb_D0WtjLTKQi2YmF8zMlzUOewNJGwZRwbRwxc7JoDIKEc5RZkJYevfJXOEEOPGXZ7AGZxOEsJawPqFqd_nOUosCZS4akHhcDPcVowoecVAV0hhhoS6JEY66PhPp1snbt6yqA-fQhch7z8Y-DZT3Scibvffww3Scg_KFANWp0KeEvHG0vyv9R2F4o66viSS8y21MDnM7Yjk8C-j7aNMldUQbjN_7Yq1nkfe0jiBX_hsINBRPgJHUY4zCaXuyXs-JZZfU92nwG0RT3A_3RP2rpY8-fXp9d3C2QJjEpnmHvTMsuAZCQSBe5DVrJwN_UKedxcJEoOt0wLz6MaCMyYZPd8tnQeqYK1cd3RgQDXtzKC0HDw1En489DqJXEst4eSSkaaW1lImLeaF8XCOaIqPqoyGk4_6KVLw5Q7OnpczuXqYKMd9UTMovGeuTuo1k0ddfEqTq9QwxkwZL51AiDRnwTCAeYBU1krV8FCJQx-mH_WPB5ftZj-o_3pbvANeRk27QBVmjcS-tgDllJkWBxX-4axRXzLw8pUUUZUT_NOL0OiqUCWVm0qMBEpgRQ57Se42-hkLMTzLhhGJOnVcaXU1j4ep-N7faNvbgREBjf_LgzvaWS90a2NJ9bB_J9FyXelhCN_AMLfdOS3fHkeWlZ0u0PMbn5DxXRMe0l9jB-2VJZhcPQRlWoYyoCO3l4F5ZmuQP5Xh9CU4tvSWih6jlwMDgdVWuTpdfPD5bx8ccog3JDq87enx-QtPzLU3gMgouNARJGgNwKS_GJSE1uPrt2oiqgZ3Z0u_I5MKvPdQPV3o-4rsaE730eB4OwAOF-mkGWpzy8Pbl-Qe5PR9mHBhuyJgZ-WDSCHl5yvet2kfO9mPXZlqBQ26fzTcUYH94MULAZn36og6w.3iKGv-Le-AvRmi26W1v6ibRLGbwKbCR92vs-a9t55hw'; - dapUtils.dapRefreshEncryptedMembership(ortb2, esampleConfig, sampleCachedToken.token, onDone) - let request = server.requests[0]; - responseHeader['Akamai-DAP-Token'] = encMembership; - request.respond(200, responseHeader, encMembership); - let rtdObj = dapUtils.dapGetEncryptedRtdObj({'encryptedSegments': encMembership}, 710) - expect(ortb2.user.data).to.deep.include.members(rtdObj.rtd.ortb2.user.data); - expect(JSON.parse(storage.getDataFromLocalStorage(DAP_ENCRYPTED_MEMBERSHIP)).expires_at).to.equal(expiry); - }); - - it('test dapRefreshEncryptedMembership success response with exp claim', function () { - let encMembership = 'eyJhbGciOiJkaXIiLCJlbmMiOiJBMTI4Q0JDLUhTMjU2Iiwia2lkIjoic29tZXNlY3JldGludmF1bHQiLCJleHAiOjE2NDM4MzA2NDB9..inYoxwht_aqTIWqGhEm_Gw.wDcCUOCwtqgnNUouaD723gKfm7X7bgkHgtiX4mr07P3tWk25PUQunmwTLhWBB5CYzzGIfIvveG_u4glNRLi_eRSQV4ihKKk1AN-BSSJ3d0CLAdY9I1WG5vX1VmopXyKnV90bl9SLNqnhg4Vxe6YU4ogTYxsKHuIN1EeIH4hpl-HbCQWQ1DQt4mB-MQF8V9AWTfU0D7sFMSK8f9qj6NGmf1__oHdHUlws0t5V2UAn_dhJexsuREK_gh65pczCuly5eEcziZ82LeP-nOhKWSRHB_tS_mKXrRU6_At_EVDgtfA3PSBJ6eQylCii6bTL42vZzz4jZhJv_3eLfRdKqpVT5CWNBzcDoQ2VcQgKgIBtPJ45KFfAYTQ6kdl21QMSjqtu8GTsv1lEZtrqHY6zRiG8_Mu28-PmjEw4LDdZmBDOeroue_MJD6wuE_jlE7J2iVdo8CkVnoRgzFwNbKBo7CK4z0WahV9rhuOm0LKAN5H0jF_gj696U-3fVTDTIb8ndNKNI2_xAhvWs00BFGtUtWgr8QGDGRTDCNGsDgnb_Vva9xCqVOyAE9O3Fq1QYl-tMA-KkBt3zzvmFFpOxpOyH-lUubKLKlsrxKc3GSyVEQ9DDLhrXXJgR5H5BSE4tjlK7p3ODF5qz0FHtIj7oDcgLazFO7z2MuFy2LjJmd3hKl6ujcfYEDiQ4D3pMIo7oiU33aFBD1YpzI4-WzNfJlUt1FoK0-DAXpbbV95s8p08GOD4q81rPw5hRADKJEr0QzrbDwplTWCzT2fKXMg_dIIc5AGqGKnVRUS6UyF1DnHpudNIJWxyWZjWIEw_QNjU0cDFmyPSyKxNrnfq9w8WE2bfbS5KTicxei5QHnC-cnL7Nh7IXp7WOW6R1YHbNPT7Ad4OhnlV-jjrXwkSv4wMAbfwAWoSCchGh7uvENNAeJymuponlJbOgw_GcYM73hMs8Z8W9qxRfbyF4WX5fDKXg61mMlaieHkc0EnoC5q7uKyXuZUehHZ76JLDFmewslLkQq5SkVCttzJePBnY1ouPEHw5ZTzUnG5f01QQOVcjIN-AqXNDbG5IOwq0heyS6vVfq7lZKJdLDVQ21qRjazGPaqYwLzugkWkzCOzPTgyFdbXzgjfmJwylHSOM5Jpnul84GzxEQF-1mHP2A8wtIT-M7_iX24It2wwWvc8qLA6GEqruWCtNyoug8CXo44mKdSSCGeEZHtfMbzXdLIBHCy2jSHz5i8S7DU_R7rE_5Ssrb81CqIYbgsAQBHtOYoyvzduTOruWcci4De0QcULloqImIEHUuIe2lnYO889_LIx5p7nE3UlSvLBo0sPexavFUtHqI6jdG6ye9tdseUEoNBDXW0aWD4D-KXX1JLtAgToPVUtEaXCJI7QavwO9ZG6UZM6jbfuJ5co0fvUXp6qYrFxPQo2dYHkar0nT6s1Zg5l2g8yWlLUJrHdHAzAw_NScUp71OpM4TmNsLnYaPVPcOxMvtJXTanbNWr0VKc8gy9q3k_1XxAnQwiduNs7f5bA-6qCVpayHv5dE7mUhFEwyh1_w95jEaURsQF_hnnd2OqRkADfiok4ZiPU2b38kFW1LXjpI39XXES3JU0e08Rq2uuelyLbCLWuJWq_axuKSZbZvpYeqWtIAde8FjCiO7RPlEc0nyzWBst8RBxQ-Bekg9UXPhxBRcm0HwA.Q2cBSFOQAC-QKDwmjrQXnVQd3jNOppMl9oZfd2yuKeY'; - dapUtils.dapRefreshEncryptedMembership(ortb2, esampleConfig, sampleCachedToken.token, onDone) - let request = server.requests[0]; - responseHeader['Akamai-DAP-Token'] = encMembership; - request.respond(200, responseHeader, encMembership); - let rtdObj = dapUtils.dapGetEncryptedRtdObj({'encryptedSegments': encMembership}, 710) - expect(ortb2.user.data).to.deep.include.members(rtdObj.rtd.ortb2.user.data); - expect(JSON.parse(storage.getDataFromLocalStorage(DAP_ENCRYPTED_MEMBERSHIP)).expires_at).to.equal(1643830630); - }); - - it('test dapRefreshEncryptedMembership error response', function () { - dapUtils.dapRefreshEncryptedMembership(ortb2, esampleConfig, sampleCachedToken.token, onDone) - let request = server.requests[0]; - request.respond(400, responseHeader, 'error'); - expect(ortb2).to.eql({}); - }); - - it('test dapRefreshEncryptedMembership 403 error response', function () { - dapUtils.dapRefreshEncryptedMembership(ortb2, esampleConfig, sampleCachedToken.token, onDone) - let request = server.requests[0]; - request.respond(403, responseHeader, 'error'); - let requestTokenize = server.requests[1]; - responseHeader['Akamai-DAP-Token'] = sampleCachedToken.token; - requestTokenize.respond(200, responseHeader, ''); - let requestMembership = server.requests[2]; - requestMembership.respond(403, responseHeader, 'error'); - expect(server.requests.length).to.be.equal(DAP_MAX_RETRY_TOKENIZE + 2); - }); - }); - - describe('dapRefreshMembership test', function () { - it('test dapRefreshMembership success response', function () { - let membership = {'cohorts': ['9', '11', '13'], 'said': 'eyJhbGciOiJkaXIiLCJlbmMiOiJBMTI4Q0JDLUhTMjU2Iiwia2lkIjoicGFzc3dvcmQxIn0..17wnrhz6FbWx0Cf6LXpm1A.m9PKVCradk3CZokNKzVHzE06TOqiXYeijgxTQUiQy5Syx-yicnO8DyYX6zQ6rgPcNgUNRt4R4XE5MXuK0laUVQJr9yc9g3vUfQfw69OMYGW_vRlLMPzoNOhF2c4gSyfkRrLr7C0qgALmZO1D11sPflaCTNmO7pmZtRaCOB5buHoWcQhp1bUSJ09DNDb31dX3llimPwjNGSrUhyq_EZl4HopnnjxbM4qVNMY2G_43C_idlVOvbFoTxcDRATd-6MplJoIOIHQLDZEetpIOVcbEYN9gQ_ndBISITwuu5YEgs5C_WPHA25nm6e4BT5R-tawSA8yPyQAupqE8gk4ZWq_2-T0cqyTstIHrMQnZ_vysYN7h6bkzE-KeZRk7GMtySN87_fiu904hLD9QentGegamX6UAbVqQh7Htj7SnMHXkEenjxXAM5mRqQvNCTlw8k-9-VPXs-vTcKLYP8VFf8gMOmuYykgWac1gX-svyAg-24mo8cUbqcsj9relx4Qj5HiXUVyDMBZxK-mHZi-Xz6uv9GlggcsjE13DSszar-j2OetigpdibnJIxRZ-4ew3-vlvZ0Dul3j0LjeWURVBWYWfMjuZ193G7lwR3ohh_NzlNfwOPBK_SYurdAnLh7jJgTW-lVLjH2Dipmi9JwX9s03IQq9opexAn7hlM9oBI6x5asByH8JF8WwZ5GhzDjpDwpSmHPQNGFRSyrx_Sh2CPWNK6C1NJmLkyqAtJ5iw0_al7vPDQyZrKXaLTjBCUnbpJhUZ8dUKtWLzGPjzFXp10muoDIutd1NfyKxk1aWGhx5aerYuLdywv6cT_M8RZTi8924NGj5VA30V5OvEwLLyX93eDhntXZSCbkPHpAfiRZNGXrPY.GhCbWGQz11mIRD4uPKmoAuFXDH7hGnils54zg7N7-TU'} - dapUtils.dapRefreshMembership(ortb2, sampleConfig, sampleCachedToken.token, onDone); - let request = server.requests[0]; - request.respond(200, responseHeader, JSON.stringify(membership)); - let rtdObj = dapUtils.dapGetRtdObj(membership, 708); - expect(ortb2.user.data).to.deep.include.members(rtdObj.rtd.ortb2.user.data); - }); - - it('test dapRefreshMembership success response with exp claim', function () { - let membership = {'cohorts': ['9', '11', '13'], 'said': 'eyJhbGciOiJkaXIiLCJlbmMiOiJBMTI4Q0JDLUhTMjU2Iiwia2lkIjoicGFzc3dvcmQxIiwiZXhwIjoxNjQ3OTcxNTU4fQ..ptdM5WO-62ypXlKxFXD4FQ.waEo9MHS2NYQCi-zh_p6HgT9BdqGyQbBq4GfGLfsay4nRBgICsTS-VkV6e7xx5U1T8BgpKkRJIZBwTOY5Pkxk9FpK5nnffDSEljRrp1LXLCkNP4qwrlqHInFbZsonNWW4_mW-7aUPlTwIsTbfjTuyHdXHeQa1ALrwFFFWE7QUmPNd2RsHjDwUsxlJPEb5TnHn5W0Mgo_PQZaxvhJInMbxPgtJLoqnJvOqCBEoQY7au7ALZL_nWK8XIwPMF19J7Z3cBg9vQInhr_E3rMdQcAFHEzYfgoNcIYCCR0t1UOqUE3HNtX-E64kZAYKWdlsBb9eW5Gj9hHYyPNL_4Hntjg5eLXGpsocMg0An-qQKGC6hkrxKzeM-GrjpvSaQLNs4iqDpHUtzA02LW_vkLkMNRUiyXVJ3FUZwfyq6uHSRKWZ6UFdAfL0rfJ8q8x8Ll-qJO2Jfyvidlsi9FIs7x1WJrvDCKepfAQM1UXRTonrQljFBAk83PcL2bmWuJDgJZ0lWS4VnZbIf6A7fDourmkDxdVRptvQq5nSjtzCA6whRw0-wGz8ehNJsaJw9H_nG9k4lRKs7A5Lqsyy7TVFrAPjnA_Q1a2H6xF2ULxrtIqoNqdX7k9RjowEZSQlZgZUOAmI4wzjckdcSyC_pUlYBMcBwmlld34mmOJe9EBHAxjdci7Q_9lvj1HTcwGDcQITXnkW9Ux5Jkt9Naw-IGGrnEIADaT2guUAto8W_Gb05TmwHSd6DCmh4zepQCbqeVe6AvPILtVkTgsTTo27Q-NvS7h-XtthJy8425j5kqwxxpZFJ0l0ytc6DUyNCLJXuxi0JFU6-LoSXcROEMVrHa_Achufr9vHIELwacSAIHuwseEvg_OOu1c1WYEwZH8ynBLSjqzy8AnDj24hYgA0YanPAvDqacrYrTUFqURbHmvcQqLBTcYa_gs7uDx4a1EjtP_NvHRlvCgGAaASrjGMhTX8oJxlTqahhQ.pXm-7KqnNK8sbyyczwkVYhcjgiwkpO8LjBBVw4lcyZE'}; - dapUtils.dapRefreshMembership(ortb2, sampleConfig, sampleCachedToken.token, onDone); - let request = server.requests[0]; - request.respond(200, responseHeader, JSON.stringify(membership)); - let rtdObj = dapUtils.dapGetRtdObj(membership, 708) - expect(ortb2.user.data).to.deep.include.members(rtdObj.rtd.ortb2.user.data); - expect(JSON.parse(storage.getDataFromLocalStorage(DAP_MEMBERSHIP)).expires_at).to.be.equal(1647971548); - }); - - it('test dapRefreshMembership 400 error response', function () { - dapUtils.dapRefreshMembership(ortb2, sampleConfig, sampleCachedToken.token, onDone) - let request = server.requests[0]; - request.respond(400, responseHeader, 'error'); - expect(ortb2).to.eql({}); - }); - - it('test dapRefreshMembership 403 error response', function () { - dapUtils.dapRefreshMembership(ortb2, sampleConfig, sampleCachedToken.token, onDone) - let request = server.requests[0]; - request.respond(403, responseHeader, 'error'); - expect(server.requests.length).to.be.equal(DAP_MAX_RETRY_TOKENIZE); - }); - }); - - describe('dapGetEncryptedMembershipFromLocalStorage test', function () { - it('test dapGetEncryptedMembershipFromLocalStorage function with valid cache', function () { - storage.setDataInLocalStorage(DAP_ENCRYPTED_MEMBERSHIP, JSON.stringify(cachedEncryptedMembership)) - expect(JSON.stringify(dapUtils.dapGetEncryptedMembershipFromLocalStorage())).to.equal(JSON.stringify(encMembership)); - }); - - it('test dapGetEncryptedMembershipFromLocalStorage function with invalid cache', function () { - let expiry = Math.round(Date.now() / 1000.0) - 100; // in seconds - let encMembership = {'expiry': expiry, 'encryptedSegments': cachedEncryptedMembership.encryptedSegments} - storage.setDataInLocalStorage(DAP_ENCRYPTED_MEMBERSHIP, JSON.stringify(encMembership)) - expect(dapUtils.dapGetEncryptedMembershipFromLocalStorage()).to.equal(null); - }); - }); - - describe('Akamai-DAP-SS-ID test', function () { - it('Akamai-DAP-SS-ID present in response header', function () { - let expiry = Math.round(Date.now() / 1000.0) + 300; // in seconds - dapUtils.dapRefreshToken(ortb2, sampleConfig, false, onDone) - let request = server.requests[0]; - let sampleSSID = 'Test_SSID_Spec'; - responseHeader['Akamai-DAP-Token'] = sampleCachedToken.token; - responseHeader['Akamai-DAP-SS-ID'] = sampleSSID; - request.respond(200, responseHeader, ''); - expect(storage.getDataFromLocalStorage(DAP_SS_ID)).to.be.equal(JSON.stringify(sampleSSID)); - }); - - it('Test if Akamai-DAP-SS-ID is present in request header', function () { - let expiry = Math.round(Date.now() / 1000.0) + 100; // in seconds - storage.setDataInLocalStorage(DAP_SS_ID, JSON.stringify('Test_SSID_Spec')) - dapUtils.dapRefreshToken(ortb2, sampleConfig, false, onDone) - let request = server.requests[0]; - let ssidHeader = request.requestHeaders['Akamai-DAP-SS-ID']; - responseHeader['Akamai-DAP-Token'] = sampleCachedToken.token; - request.respond(200, responseHeader, ''); - expect(ssidHeader).to.be.equal('Test_SSID_Spec'); - }); - }); - - describe('Test gdpr and usp consent handling', function () { - it('Gdpr applies and gdpr consent string not present', function () { - expect(akamaiDapRtdSubmodule.init(null, sampleGdprConsentConfig)).to.equal(false); - }); - - it('Gdpr applies and gdpr consent string is present', function () { - sampleGdprConsentConfig.gdpr.consentString = 'BOJ/P2HOJ/P2HABABMAAAAAZ+A=='; - expect(akamaiDapRtdSubmodule.init(null, sampleGdprConsentConfig)).to.equal(true); - }); - - it('USP consent present and user have opted out', function () { - expect(akamaiDapRtdSubmodule.init(null, sampleUspConsentConfig)).to.equal(false); - }); - - it('USP consent present and user have not been provided with option to opt out', function () { - expect(akamaiDapRtdSubmodule.init(null, {'usp': '1NYY'})).to.equal(false); - }); - - it('USP consent present and user have not opted out', function () { - expect(akamaiDapRtdSubmodule.init(null, {'usp': '1YNY'})).to.equal(true); - }); - }); -}); From f577da4620a716d8f731c929b0f101aa5e507ac2 Mon Sep 17 00:00:00 2001 From: Patrick McCann Date: Thu, 13 Mar 2025 08:25:16 -0400 Subject: [PATCH 10/92] Delete modules/saambaaBidAdapter.js (#12863) follow up to #11992 , partial solution to #12001 --- modules/saambaaBidAdapter.js | 3 --- 1 file changed, 3 deletions(-) delete mode 100644 modules/saambaaBidAdapter.js diff --git a/modules/saambaaBidAdapter.js b/modules/saambaaBidAdapter.js deleted file mode 100644 index 3e33496b7d9..00000000000 --- a/modules/saambaaBidAdapter.js +++ /dev/null @@ -1,3 +0,0 @@ -import { spec } from './advangelistsBidAdapter.js'; // eslint-disable-line prebid/validate-imports -import { registerBidder } from '../src/adapters/bidderFactory.js'; -registerBidder(spec); From 02195602760483f231ab8cd308275df8498160e3 Mon Sep 17 00:00:00 2001 From: Patrick McCann Date: Thu, 13 Mar 2025 08:26:57 -0400 Subject: [PATCH 11/92] Prebid 10: Delete modules/bidwatchAnalyticsAdapter.js (#12873) * Prebid 10: Delete modules/bidwatchAnalyticsAdapter.js replaced by oxxion * Delete test/spec/modules/bidwatchAnalyticsAdapter_spec.js * Delete modules/bidwatchAnalyticsAdapter.md --- modules/bidwatchAnalyticsAdapter.js | 239 ------------ modules/bidwatchAnalyticsAdapter.md | 21 -- .../modules/bidwatchAnalyticsAdapter_spec.js | 343 ------------------ 3 files changed, 603 deletions(-) delete mode 100644 modules/bidwatchAnalyticsAdapter.js delete mode 100644 modules/bidwatchAnalyticsAdapter.md delete mode 100644 test/spec/modules/bidwatchAnalyticsAdapter_spec.js diff --git a/modules/bidwatchAnalyticsAdapter.js b/modules/bidwatchAnalyticsAdapter.js deleted file mode 100644 index e385b02fe5f..00000000000 --- a/modules/bidwatchAnalyticsAdapter.js +++ /dev/null @@ -1,239 +0,0 @@ -import adapter from '../libraries/analyticsAdapter/AnalyticsAdapter.js'; -import adapterManager from '../src/adapterManager.js'; -import { EVENTS } from '../src/constants.js'; -import { ajax } from '../src/ajax.js'; -import { getRefererInfo } from '../src/refererDetection.js'; -import { deepClone } from '../src/utils.js'; - -const analyticsType = 'endpoint'; -const url = 'URL_TO_SERVER_ENDPOINT'; - -const { - AUCTION_END, - BID_WON, - BID_RESPONSE, - BID_REQUESTED, - BID_TIMEOUT, -} = EVENTS; - -let saveEvents = {} -let allEvents = {} -let auctionEnd = {} -let initOptions = {} -let endpoint = 'https://default' -let requestsAttributes = ['adUnitCode', 'auctionId', 'bidder', 'bidderCode', 'bidId', 'cpm', 'creativeId', 'currency', 'width', 'height', 'mediaType', 'netRevenue', 'originalCpm', 'originalCurrency', 'requestId', 'size', 'source', 'status', 'timeToRespond', 'transactionId', 'ttl', 'sizes', 'mediaTypes', 'src', 'params', 'userId', 'labelAny', 'bids', 'adId']; - -function getAdapterNameForAlias(aliasName) { - return adapterManager.aliasRegistry[aliasName] || aliasName; -} - -function filterAttributes(arg, removead) { - let response = {}; - if (typeof arg == 'object') { - if (typeof arg['bidderCode'] == 'string') { - response['originalBidder'] = getAdapterNameForAlias(arg['bidderCode']); - } else if (typeof arg['bidder'] == 'string') { - response['originalBidder'] = getAdapterNameForAlias(arg['bidder']); - } - if (!removead && typeof arg['ad'] != 'undefined') { - response['ad'] = arg['ad']; - } - if (typeof arg['gdprConsent'] != 'undefined') { - response['gdprConsent'] = {}; - if (typeof arg['gdprConsent']['consentString'] != 'undefined') { response['gdprConsent']['consentString'] = arg['gdprConsent']['consentString']; } - } - if (typeof arg['meta'] == 'object' && typeof arg['meta']['advertiserDomains'] != 'undefined') { - response['meta'] = {'advertiserDomains': arg['meta']['advertiserDomains']}; - } - requestsAttributes.forEach((attr) => { - if (typeof arg[attr] != 'undefined') { response[attr] = arg[attr]; } - }); - if (typeof response['creativeId'] == 'number') { response['creativeId'] = response['creativeId'].toString(); } - } - return response; -} - -function cleanAuctionEnd(args) { - let response = {}; - let filteredObj; - let objects = ['bidderRequests', 'bidsReceived', 'noBids', 'adUnits']; - objects.forEach((attr) => { - if (Array.isArray(args[attr])) { - response[attr] = []; - args[attr].forEach((obj) => { - filteredObj = filterAttributes(obj, true); - if (typeof obj['bids'] == 'object') { - filteredObj['bids'] = []; - obj['bids'].forEach((bid) => { - filteredObj['bids'].push(filterAttributes(bid, true)); - }); - } - response[attr].push(filteredObj); - }); - } - }); - return response; -} - -function cleanCreatives(args) { - let stringArgs = JSON.parse(dereferenceWithoutRenderer(args)); - return filterAttributes(stringArgs, false); -} - -function enhanceMediaType(arg) { - saveEvents['bidRequested'].forEach((bidRequested) => { - if (bidRequested['auctionId'] == arg['auctionId'] && Array.isArray(bidRequested['bids'])) { - bidRequested['bids'].forEach((bid) => { - if (bid['transactionId'] == arg['transactionId'] && bid['bidId'] == arg['requestId']) { arg['mediaTypes'] = bid['mediaTypes']; } - }); - } - }); - return arg; -} - -function addBidResponse(args) { - let eventType = BID_RESPONSE; - let argsCleaned = cleanCreatives(args); ; - if (allEvents[eventType] == undefined) { allEvents[eventType] = [] } - allEvents[eventType].push(argsCleaned); -} - -function addBidRequested(args) { - let eventType = BID_REQUESTED; - let argsCleaned = filterAttributes(args, true); - if (saveEvents[eventType] == undefined) { saveEvents[eventType] = [] } - saveEvents[eventType].push(argsCleaned); -} - -function addTimeout(args) { - let eventType = BID_TIMEOUT; - if (saveEvents[eventType] == undefined) { saveEvents[eventType] = [] } - saveEvents[eventType].push(args); - let argsCleaned = []; - let argsDereferenced = {} - let stringArgs = JSON.parse(dereferenceWithoutRenderer(args)); - argsDereferenced = stringArgs; - argsDereferenced.forEach((attr) => { - argsCleaned.push(filterAttributes(deepClone(attr), false)); - }); - if (auctionEnd[eventType] == undefined) { auctionEnd[eventType] = [] } - auctionEnd[eventType].push(argsCleaned); -} - -export const dereferenceWithoutRenderer = function(args) { - if (args.renderer) { - let tmp = args.renderer; - delete args.renderer; - let stringified = JSON.stringify(args); - args['renderer'] = tmp; - return stringified; - } - if (args.bidsReceived) { - let tmp = {} - for (let key in args.bidsReceived) { - if (args.bidsReceived[key].renderer) { - tmp[key] = args.bidsReceived[key].renderer; - delete args.bidsReceived[key].renderer; - } - } - let stringified = JSON.stringify(args); - for (let key in tmp) { - args.bidsReceived[key].renderer = tmp[key]; - } - return stringified; - } - return JSON.stringify(args); -} - -function addAuctionEnd(args) { - let eventType = AUCTION_END; - if (saveEvents[eventType] == undefined) { saveEvents[eventType] = [] } - saveEvents[eventType].push(args); - let argsCleaned = cleanAuctionEnd(JSON.parse(dereferenceWithoutRenderer(args))); - if (auctionEnd[eventType] == undefined) { auctionEnd[eventType] = [] } - auctionEnd[eventType].push(argsCleaned); -} - -function handleBidWon(args) { - args = enhanceMediaType(filterAttributes(JSON.parse(dereferenceWithoutRenderer(args)), true)); - let increment = args['cpm']; - if (typeof saveEvents['auctionEnd'] == 'object') { - saveEvents['auctionEnd'].forEach((auction) => { - if (auction['auctionId'] == args['auctionId'] && typeof auction['bidsReceived'] == 'object') { - auction['bidsReceived'].forEach((bid) => { - if (bid['transactionId'] == args['transactionId'] && bid['adId'] != args['adId']) { - if (args['cpm'] < bid['cpm']) { - increment = 0; - } else if (increment > args['cpm'] - bid['cpm']) { - increment = args['cpm'] - bid['cpm']; - } - } - }); - } - }); - } - args['cpmIncrement'] = increment; - args['referer'] = encodeURIComponent(getRefererInfo().page || getRefererInfo().topmostLocation); - if (typeof saveEvents.bidRequested == 'object' && saveEvents.bidRequested.length > 0 && saveEvents.bidRequested[0].gdprConsent) { args.gdpr = saveEvents.bidRequested[0].gdprConsent; } - ajax(endpoint + '.bidwatch.io/analytics/bid_won', null, JSON.stringify(args), {method: 'POST', withCredentials: true}); -} - -function handleAuctionEnd() { - ajax(endpoint + '.bidwatch.io/analytics/auctions', function (data) { - let list = JSON.parse(data); - if (Array.isArray(list) && typeof allEvents['bidResponse'] != 'undefined') { - let alreadyCalled = []; - allEvents['bidResponse'].forEach((bidResponse) => { - let tmpId = bidResponse['originalBidder'] + '_' + bidResponse['creativeId']; - if (list.includes(tmpId) && !alreadyCalled.includes(tmpId)) { - alreadyCalled.push(tmpId); - ajax(endpoint + '.bidwatch.io/analytics/creatives', null, JSON.stringify(bidResponse), {method: 'POST', withCredentials: true}); - } - }); - } - allEvents = {}; - }, JSON.stringify(auctionEnd), {method: 'POST', withCredentials: true}); - auctionEnd = {}; -} - -let bidwatchAnalytics = Object.assign(adapter({url, analyticsType}), { - track({ - eventType, - args - }) { - switch (eventType) { - case AUCTION_END: - addAuctionEnd(args); - handleAuctionEnd(); - break; - case BID_WON: - handleBidWon(args); - break; - case BID_RESPONSE: - addBidResponse(args); - break; - case BID_REQUESTED: - addBidRequested(args); - break; - case BID_TIMEOUT: - addTimeout(args); - break; - } - }}); - -// save the base class function -bidwatchAnalytics.originEnableAnalytics = bidwatchAnalytics.enableAnalytics; - -// override enableAnalytics so we can get access to the config passed in from the page -bidwatchAnalytics.enableAnalytics = function (config) { - bidwatchAnalytics.originEnableAnalytics(config); // call the base class function - initOptions = config.options; - if (initOptions.domain) { endpoint = 'https://' + initOptions.domain; } -}; - -adapterManager.registerAnalyticsAdapter({ - adapter: bidwatchAnalytics, - code: 'bidwatch' -}); - -export default bidwatchAnalytics; diff --git a/modules/bidwatchAnalyticsAdapter.md b/modules/bidwatchAnalyticsAdapter.md deleted file mode 100644 index bfa453640b8..00000000000 --- a/modules/bidwatchAnalyticsAdapter.md +++ /dev/null @@ -1,21 +0,0 @@ -# Overview -Module Name: bidwatch Analytics Adapter - -Module Type: Analytics Adapter - -Maintainer: tech@bidwatch.io - -# Description - -Analytics adapter for bidwatch.io. - -# Test Parameters - -``` -{ - provider: 'bidwatch', - options : { - domain: 'test.endpoint' - } -} -``` diff --git a/test/spec/modules/bidwatchAnalyticsAdapter_spec.js b/test/spec/modules/bidwatchAnalyticsAdapter_spec.js deleted file mode 100644 index d934a6c611b..00000000000 --- a/test/spec/modules/bidwatchAnalyticsAdapter_spec.js +++ /dev/null @@ -1,343 +0,0 @@ -import bidwatchAnalytics from 'modules/bidwatchAnalyticsAdapter.js'; -import {dereferenceWithoutRenderer} from 'modules/bidwatchAnalyticsAdapter.js'; -import { expect } from 'chai'; -import { server } from 'test/mocks/xhr.js'; -import { EVENTS } from 'src/constants.js'; - -let adapterManager = require('src/adapterManager').default; -let events = require('src/events'); - -describe('BidWatch Analytics', function () { - let timestamp = new Date() - 256; - let auctionId = '5018eb39-f900-4370-b71e-3bb5b48d324f'; - let timeout = 1500; - - let bidTimeout = [ - { - 'bidId': '5fe418f2d70364', - 'bidder': 'appnexusAst', - 'adUnitCode': 'tag_200124_banner', - 'auctionId': '1e8b993d-8f0a-4232-83eb-3639ddf3a44b' - } - ]; - - const auctionEnd = { - 'auctionId': '1e8b993d-8f0a-4232-83eb-3639ddf3a44b', - 'timestamp': 1647424261187, - 'auctionEnd': 1647424261714, - 'auctionStatus': 'completed', - 'adUnits': [ - { - 'code': 'tag_200124_banner', - 'mediaTypes': { - 'banner': { - 'sizes': [ - [ - 300, - 600 - ] - ] - } - }, - 'bids': [ - { - 'bidder': 'appnexus', - 'params': { - 'placementId': 123456 - } - }, - { - 'bidder': 'appnexusAst', - 'params': { - 'placementId': 234567 - } - } - ], - 'sizes': [ - [ - 300, - 600 - ] - ], - 'transactionId': 'de664ccb-e18b-4436-aeb0-362382eb1b40' - } - ], - 'adUnitCodes': [ - 'tag_200124_banner' - ], - 'bidderRequests': [ - { - 'bidderCode': 'appnexus', - 'auctionId': '1e8b993d-8f0a-4232-83eb-3639ddf3a44b', - 'bidderRequestId': '11dc6ff6378de7', - 'bids': [ - { - 'bidder': 'appnexus', - 'params': { - 'placementId': 123456 - }, - 'mediaTypes': { - 'banner': { - 'sizes': [ - [ - 300, - 600 - ] - ] - } - }, - 'adUnitCode': 'tag_200124_banner', - 'transactionId': 'de664ccb-e18b-4436-aeb0-362382eb1b40', - 'sizes': [ - [ - 300, - 600 - ] - ], - 'bidId': '34a63e5d5378a3', - 'bidderRequestId': '11dc6ff6378de7', - 'auctionId': '1e8b993d-8f0a-4232-83eb-3639ddf3a44b', - 'src': 'client', - 'bidRequestsCount': 1, - 'bidderRequestsCount': 1, - 'bidderWinsCount': 0 - } - ], - 'auctionStart': 1647424261187, - 'timeout': 1000, - 'gdprConsent': { - 'consentString': 'CONSENT', - 'gdprApplies': true, - 'apiVersion': 2, - 'vendorData': 'a lot of borring stuff', - }, - 'start': 1647424261189 - }, - ], - 'noBids': [ - { - 'bidder': 'appnexusAst', - 'params': { - 'placementId': 10471298 - }, - 'mediaTypes': { - 'banner': { - 'sizes': [ - [ - 300, - 600 - ] - ] - } - }, - 'adUnitCode': 'tag_200124_banner', - 'transactionId': 'de664ccb-e18b-4436-aeb0-362382eb1b40', - 'sizes': [ - [ - 300, - 600 - ] - ], - 'bidId': '5fe418f2d70364', - 'bidderRequestId': '4229a45ab8ea87', - 'auctionId': '1e8b993d-8f0a-4232-83eb-3639ddf3a44b', - 'src': 'client', - 'bidRequestsCount': 1, - 'bidderRequestsCount': 1, - 'bidderWinsCount': 0 - } - ], - 'bidsReceived': [ - { - 'bidderCode': 'appnexus', - 'width': 300, - 'height': 600, - 'statusMessage': 'Bid available', - 'adId': '7a4ced80f33d33', - 'requestId': '34a63e5d5378a3', - 'transactionId': 'de664ccb-e18b-4436-aeb0-362382eb1b40', - 'auctionId': '1e8b993d-8f0a-4232-83eb-3639ddf3a44b', - 'mediaType': 'video', - 'source': 'client', - 'cpm': 27.4276, - 'creativeId': '158534630', - 'currency': 'USD', - 'netRevenue': true, - 'ttl': 2000, - 'ad': 'some html', - 'meta': { - 'advertiserDomains': [ - 'example.com' - ] - }, - 'renderer': 'something', - 'originalCpm': 25.02521, - 'originalCurrency': 'EUR', - 'responseTimestamp': 1647424261559, - 'requestTimestamp': 1647424261189, - 'bidder': 'appnexus', - 'adUnitCode': 'tag_200124_banner', - 'timeToRespond': 370, - 'pbLg': '5.00', - 'pbMg': '20.00', - 'pbHg': '20.00', - 'pbAg': '20.00', - 'pbDg': '20.00', - 'pbCg': '20.000000', - 'size': '300x600', - 'adserverTargeting': { - 'hb_bidder': 'appnexus', - 'hb_adid': '7a4ced80f33d33', - 'hb_pb': '20.000000', - 'hb_size': '300x600', - 'hb_source': 'client', - 'hb_format': 'banner', - 'hb_adomain': 'example.com' - } - } - ], - 'winningBids': [ - - ], - 'timeout': 1000 - }; - - let bidWon = { - 'bidderCode': 'appnexus', - 'width': 970, - 'height': 250, - 'statusMessage': 'Bid available', - 'adId': '65d16ef039a97a', - 'requestId': '2bd3e8ff8a113f', - 'transactionId': '8b2a8629-d1ea-4bb1-aff0-e335b96dd002', - 'auctionId': '1e8b993d-8f0a-4232-83eb-3639ddf3a44b', - 'mediaType': 'banner', - 'source': 'client', - 'cpm': 27.4276, - 'creativeId': '158533702', - 'currency': 'USD', - 'netRevenue': true, - 'ttl': 2000, - 'ad': 'some html', - 'meta': { - 'advertiserDomains': [ - 'example.com' - ] - }, - 'renderer': 'something', - 'originalCpm': 25.02521, - 'originalCurrency': 'EUR', - 'responseTimestamp': 1647424261558, - 'requestTimestamp': 1647424261189, - 'bidder': 'appnexus', - 'adUnitCode': 'tag_200123_banner', - 'timeToRespond': 369, - 'originalBidder': 'appnexus', - 'pbLg': '5.00', - 'pbMg': '20.00', - 'pbHg': '20.00', - 'pbAg': '20.00', - 'pbDg': '20.00', - 'pbCg': '20.000000', - 'size': '970x250', - 'adserverTargeting': { - 'hb_bidder': 'appnexus', - 'hb_adid': '65d16ef039a97a', - 'hb_pb': '20.000000', - 'hb_size': '970x250', - 'hb_source': 'client', - 'hb_format': 'banner', - 'hb_adomain': 'example.com' - }, - 'status': 'rendered', - 'params': [ - { - 'placementId': 123456 - } - ] - }; - - after(function () { - bidwatchAnalytics.disableAnalytics(); - }); - - describe('main test flow', function () { - beforeEach(function () { - sinon.stub(events, 'getEvents').returns([]); - sinon.spy(bidwatchAnalytics, 'track'); - }); - afterEach(function () { - events.getEvents.restore(); - bidwatchAnalytics.disableAnalytics(); - bidwatchAnalytics.track.restore(); - }); - it('test dereferenceWithoutRenderer', function () { - adapterManager.registerAnalyticsAdapter({ - code: 'bidwatch', - adapter: bidwatchAnalytics - }); - - adapterManager.enableAnalytics({ - provider: 'bidwatch', - options: { - domain: 'test' - } - }); - let resultBidWon = JSON.parse(dereferenceWithoutRenderer(bidWon)); - expect(resultBidWon).not.to.have.property('renderer'); - let resultBid = JSON.parse(dereferenceWithoutRenderer(auctionEnd)); - expect(resultBid).to.have.property('bidsReceived').and.to.have.lengthOf(1); - expect(resultBid.bidsReceived[0]).not.to.have.property('renderer'); - }); - it('test auctionEnd', function () { - adapterManager.registerAnalyticsAdapter({ - code: 'bidwatch', - adapter: bidwatchAnalytics - }); - - adapterManager.enableAnalytics({ - provider: 'bidwatch', - options: { - domain: 'test' - } - }); - - events.emit(EVENTS.BID_REQUESTED, auctionEnd['bidderRequests'][0]); - events.emit(EVENTS.BID_RESPONSE, auctionEnd['bidsReceived'][0]); - events.emit(EVENTS.BID_TIMEOUT, bidTimeout); - events.emit(EVENTS.AUCTION_END, auctionEnd); - expect(server.requests.length).to.equal(1); - let message = JSON.parse(server.requests[0].requestBody); - expect(message).to.have.property('auctionEnd').exist; - expect(message.auctionEnd).to.have.lengthOf(1); - expect(message.auctionEnd[0]).to.have.property('bidsReceived').and.to.have.lengthOf(1); - expect(message.auctionEnd[0].bidsReceived[0]).not.to.have.property('ad'); - expect(message.auctionEnd[0].bidsReceived[0]).to.have.property('meta'); - expect(message.auctionEnd[0].bidsReceived[0].meta).to.have.property('advertiserDomains'); - expect(message.auctionEnd[0].bidsReceived[0]).to.have.property('adId'); - expect(message.auctionEnd[0]).to.have.property('bidderRequests').and.to.have.lengthOf(1); - expect(message.auctionEnd[0].bidderRequests[0]).to.have.property('gdprConsent'); - expect(message.auctionEnd[0].bidderRequests[0].gdprConsent).not.to.have.property('vendorData'); - }); - - it('test bidWon', function() { - adapterManager.registerAnalyticsAdapter({ - code: 'bidwatch', - adapter: bidwatchAnalytics - }); - - adapterManager.enableAnalytics({ - provider: 'bidwatch', - options: { - domain: 'test' - } - }); - events.emit(EVENTS.BID_WON, bidWon); - expect(server.requests.length).to.equal(1); - let message = JSON.parse(server.requests[0].requestBody); - expect(message).not.to.have.property('ad'); - expect(message).to.have.property('adId') - expect(message).to.have.property('cpmIncrement').and.to.equal(27.4276); - }); - }); -}); From f5057bed7bf37f3370b53a8f37ac47bf25165937 Mon Sep 17 00:00:00 2001 From: Patrick McCann Date: Thu, 27 Mar 2025 09:48:45 -0400 Subject: [PATCH 12/92] Delete modules/konduitWrapper.md --- modules/konduitWrapper.md | 162 -------------------------------------- 1 file changed, 162 deletions(-) delete mode 100644 modules/konduitWrapper.md diff --git a/modules/konduitWrapper.md b/modules/konduitWrapper.md deleted file mode 100644 index a5718d9848e..00000000000 --- a/modules/konduitWrapper.md +++ /dev/null @@ -1,162 +0,0 @@ -# Overview - -``` -Module Name: Konduit Accelerate -Module Type: Video Module -Maintainer: support@konduit.me -``` - -# Description - -Konduit Wrapper is a prebid module that allows -- wrapping a bid response so that it is processed through Konduit platform -- obtaining a historical performance indicator for a bid - - -# Configuration - -## Building Prebid with the Konduit wrapper function - -Your Prebid build must include the **konduitWrapper** module. Follow the build instructions for Prebid as explained in the top level README.md file of the Prebid source tree. - -ex: $ gulp build --modules=konduitWrapper - - -## Prebid related configuration - -Konduit module should be used with a valid Konduit identifier. - -```javascript -pbjs.setConfig({ - konduit: { - konduitId: your_konduit_id, - } -}); -``` - -Konduit module respects the Prebid `enableSendAllBids` flag and supports both ‘Send All Bids’ and ‘Use only a winner bid’ scenarios. - -Please contact support@konduit.me for assistance. - -## GAM related configuration - -It is important to configure your GAM line items. -Please contact support@konduit.me for assistance. - -In most cases it would require only Creative VAST URL update with the following URL: - -Konduit platform supports ‘Send all bids’ scenario and depending on whether this feature is used or not GAM configuration could be slightly different. - -- Send all bids is off (a single winner bid is used) -GAM line item creative URL should be updated as: -``` -https://p.konduit.me/api/vastProxy?konduit_hb=1&konduit_hb_awarded=1&konduit_cache_key=%%PATTERN:k_cache_key%%&konduit_id=%%PATTERN:k_id%% -``` - -- Send all bids is on -GAM line item creative URL should be updated as: -``` -https://p.konduit.me/api/vastProxy?konduit_hb=1&konduit_hb_awarded=1&konduit_cache_key=%%PATTERN:k_cache_key_BIDDERCODE%%&konduit_id=%%PATTERN:k_id%% -``` - -k_cache_key_BIDDERCODE is a bidder specific macro and ‘BIDDERCODE’ should be replaced with a bidder code. For instance, k_cache_key_appnexus. - -# Usage - -Konduit module contains a single function that accepts an `options` parameter. - -The `options` parameter can include: -* `bid` - prebid object with VAST url that should be cached (if not passed first winning bid from `auctionManager.getWinningBids()` will be used) -* `bids` - array of prebid objects with VAST url that should be cached (if not passed and `enableSendAllBids: true` bids from `auctionManager.getBidsReceived()` will be used) -* `adUnitCode` - adUnitCode where a winner bid can be found -* `timeout` - max time to wait for Konduit response with cache key and kCpm data -* `callback` - callback function is called once Konduit cache data for the bid. Arguments of this function are - `error` and `bids` (error should be `null` if Konduit request is successful) - -The function adds two parameters into the passed bid - kCpm and konduitCacheKey. Additionally `processBids` updates bid's `adserverTargeting` with `k_cpm`, `konduti_cache_key` and `konduit_id` fields. - - -```javascript -pbjs.requestBids({ - bidsBackHandler: function (bids) { - pbjs.adServers.konduit.processBids({ - callback: function (error, processedBids) { - var videoUrl = pbjs.adServers.dfp.buildVideoUrl({ - ... - }); - } - }); - } -}) -``` - - -# Sample code - -```javascript -var videoAdUnit = [{ - code: 'videoAd', - mediaTypes: { - video: { - playerSize: [640, 480], - context: 'instream' - } - }, - bids: [{ - bidder: 'appnexus', - params: { - placementId: 13232361, - video: { - skippable: true, - playback_method: ['auto_play_sound_off'] - } - } - }] -}]; - -pbjs.que.push(function(){ - pbjs.addAdUnits(videoAdUnit); - pbjs.setConfig({ - konduit: { - konduitId: 'your_konduit_id', - }, - }); - - pbjs.requestBids({ - bidsBackHandler : function(bids) { - var winnerBid = pbjs.getHighestCpmBids('videoAd')[0]; - pbjs.adServers.konduit.processBids({ - bid: winnerBid, - adUnitCode: videoAdUnit[0].code, - timeout: 2000, - callback: function (error, processedBids) { - var vastTagUrl = pbjs.adServers.dfp.buildVideoUrl({ - adUnit: videoAdUnit, - params: { - iu: '', - output: 'vast', - }, - }); - - invokeVideoPlayer(vastTagUrl); - } - }); - } - }); -}); - -function invokeVideoPlayer(vastTagUrl) { - videojs("video_player_id").ready(function() { - this.vastClient({ - adTagUrl: vastTagUrl, - playAdAlways: true, - verbosity: 4, - autoplay: true - }); - - this.play(); - }); -} -``` - - - From 09bc0b2748b51824d0cc4e393d0a6df5f5267906 Mon Sep 17 00:00:00 2001 From: Patrick McCann Date: Thu, 27 Mar 2025 09:49:07 -0400 Subject: [PATCH 13/92] Delete modules/konduitWrapper.js --- modules/konduitWrapper.js | 256 -------------------------------------- 1 file changed, 256 deletions(-) delete mode 100644 modules/konduitWrapper.js diff --git a/modules/konduitWrapper.js b/modules/konduitWrapper.js deleted file mode 100644 index f19318e3128..00000000000 --- a/modules/konduitWrapper.js +++ /dev/null @@ -1,256 +0,0 @@ -import { logInfo, logError, isNumber, isStr, isEmpty } from '../src/utils.js'; -import { registerVideoSupport } from '../src/adServerManager.js'; -import { targeting } from '../src/targeting.js'; -import { config } from '../src/config.js'; -import { ajaxBuilder } from '../src/ajax.js'; -import { getPriceBucketString } from '../src/cpmBucketManager.js'; -import { getPriceByGranularity } from '../src/auction.js'; -import { auctionManager } from '../src/auctionManager.js'; - -const SERVER_PROTOCOL = 'https'; -const SERVER_HOST = 'p.konduit.me'; - -const KONDUIT_PREBID_MODULE_VERSION = '1.0.0'; -const MODULE_NAME = 'Konduit'; - -const KONDUIT_ID_CONFIG = 'konduit.konduitId'; -const SEND_ALL_BIDS_CONFIG = 'enableSendAllBids'; - -export const errorMessages = { - NO_KONDUIT_ID: 'A konduitId param is required to be in configs', - NO_BIDS: 'No bids received in the auction', - NO_BID: 'A bid was not found', - CACHE_FAILURE: 'A bid was not cached', -}; - -// This function is copy from prebid core -function formatQS(query) { - return Object - .keys(query) - .map(k => Array.isArray(query[k]) - ? query[k].map(v => `${k}[]=${v}`).join('&') - : `${k}=${query[k]}`) - .join('&'); -} - -// This function is copy from prebid core -function buildUrl(obj) { - return (obj.protocol || 'http') + '://' + - (obj.host || - obj.hostname + (obj.port ? `:${obj.port}` : '')) + - (obj.pathname || '') + - (obj.search ? `?${formatQS(obj.search || '')}` : '') + - (obj.hash ? `#${obj.hash}` : ''); -} - -function addLogLabel(args) { - args = [].slice.call(args); - args.unshift(`${MODULE_NAME}: `); - return args; -} - -function _logInfo() { - logInfo(...addLogLabel(arguments)); -} - -function _logError() { - logError(...addLogLabel(arguments)); -} - -function sendRequest ({ host = SERVER_HOST, protocol = SERVER_PROTOCOL, method = 'GET', path, payload, callbacks, timeout }) { - const formattedUrlOptions = { - protocol: protocol, - hostname: host, - pathname: path, - }; - if (method === 'GET') { - formattedUrlOptions.search = payload; - } - - let konduitAnalyticsRequestUrl = buildUrl(formattedUrlOptions); - const ajax = ajaxBuilder(timeout); - - ajax( - konduitAnalyticsRequestUrl, - callbacks, - method === 'POST' ? JSON.stringify(payload) : null, - { - contentType: 'application/json', - method, - withCredentials: true - } - ); -} - -function composeBidsProcessorRequestPayload(bid) { - return { - auctionId: bid.auctionId, - vastUrl: bid.vastUrl, - bidderCode: bid.bidderCode, - creativeId: bid.creativeId, - adUnitCode: bid.adUnitCode, - cpm: bid.cpm, - currency: bid.currency, - }; -} - -function setDefaultKCpmToBid(bid, winnerBid, priceGranularity) { - bid.kCpm = bid.cpm; - - if (!bid.adserverTargeting) { - bid.adserverTargeting = {}; - } - - const kCpm = getPriceByGranularity(priceGranularity)(bid); - - if (config.getConfig(SEND_ALL_BIDS_CONFIG)) { - bid.adserverTargeting[`k_cpm_${bid.bidderCode}`] = kCpm; - } - - if ((winnerBid.bidderCode === bid.bidderCode) && (winnerBid.creativeId === bid.creativeId)) { - bid.adserverTargeting.k_cpm = kCpm; - } -} - -function addKCpmToBid(kCpm, bid, winnerBid, priceGranularity) { - if (isNumber(kCpm)) { - bid.kCpm = kCpm; - const priceStringsObj = getPriceBucketString( - kCpm, - config.getConfig('customPriceBucket'), - config.getConfig('currency.granularityMultiplier') - ); - - const calculatedKCpm = priceStringsObj.custom || priceStringsObj[priceGranularity] || priceStringsObj.med; - - if (config.getConfig(SEND_ALL_BIDS_CONFIG)) { - bid.adserverTargeting[`k_cpm_${bid.bidderCode}`] = calculatedKCpm; - } - - if ((winnerBid.bidderCode === bid.bidderCode) && (winnerBid.creativeId === bid.creativeId)) { - bid.adserverTargeting.k_cpm = calculatedKCpm; - } - } -} - -function addKonduitCacheKeyToBid(cacheKey, bid, winnerBid) { - if (isStr(cacheKey)) { - bid.konduitCacheKey = cacheKey; - - if (config.getConfig(SEND_ALL_BIDS_CONFIG)) { - bid.adserverTargeting[`k_cache_key_${bid.bidderCode}`] = cacheKey; - } - - if ((winnerBid.bidderCode === bid.bidderCode) && (winnerBid.creativeId === bid.creativeId)) { - bid.adserverTargeting.k_cache_key = cacheKey; - bid.adserverTargeting.konduit_cache_key = cacheKey; - } - } -} - -/** - * This function accepts an object with bid and tries to cache it while generating k_cache_key for it. - * In addition, it returns a list with updated bid objects where k_cpm key is added - * @param {Object} options - * @param {Object} [options.bid] - a winner bid provided by a client - * @param {Object} [options.bids] - bids array provided by a client for "Send All Bids" scenario - * @param {string} [options.adUnitCode] - ad unit code that is used to get winning bids - * @param {string} [options.timeout] - timeout for Konduit bids processor HTTP request - * @param {function} [options.callback] - callback function to be executed on HTTP request end; the function is invoked with two parameters - error and bids - */ -export function processBids(options = {}) { - const konduitId = config.getConfig(KONDUIT_ID_CONFIG); - options = options || {}; - - if (!konduitId) { - _logError(errorMessages.NO_KONDUIT_ID); - - if (options.callback) { - options.callback(new Error(errorMessages.NO_KONDUIT_ID), []); - } - - return null; - } - - const publisherBids = options.bids || auctionManager.getBidsReceived(); - - const winnerBid = options.bid || targeting.getWinningBids(options.adUnitCode, publisherBids)[0]; - const bids = []; - - if (config.getConfig(SEND_ALL_BIDS_CONFIG)) { - bids.push(...publisherBids); - } else if (winnerBid) { - bids.push(winnerBid); - } - - if (!bids.length) { - _logError(errorMessages.NO_BIDS); - - if (options.callback) { - options.callback(new Error(errorMessages.NO_BIDS), []); - } - - return null; - } - - const priceGranularity = config.getConfig('priceGranularity'); - - const bidsToProcess = []; - - bids.forEach((bid) => { - setDefaultKCpmToBid(bid, winnerBid, priceGranularity); - bidsToProcess.push(composeBidsProcessorRequestPayload(bid)); - }); - - sendRequest({ - method: 'POST', - path: '/api/bidsProcessor', - timeout: options.timeout || 1000, - payload: { - clientId: konduitId, - konduitPrebidModuleVersion: KONDUIT_PREBID_MODULE_VERSION, - enableSendAllBids: config.getConfig(SEND_ALL_BIDS_CONFIG), - bids: bidsToProcess, - bidResponsesCount: auctionManager.getBidsReceived().length, - }, - callbacks: { - success: (data) => { - let error = null; - _logInfo('Bids processed successfully ', data); - try { - const { kCpmData, cacheData } = JSON.parse(data); - - if (isEmpty(cacheData)) { - throw new Error(errorMessages.CACHE_FAILURE); - } - - winnerBid.adserverTargeting.konduit_id = konduitId; - winnerBid.adserverTargeting.k_id = konduitId; - - bids.forEach((bid) => { - const processedBidKey = `${bid.bidderCode}:${bid.creativeId}`; - addKCpmToBid(kCpmData[processedBidKey], bid, winnerBid, priceGranularity); - addKonduitCacheKeyToBid(cacheData[processedBidKey], bid, winnerBid); - }) - } catch (err) { - error = err; - _logError('Error parsing JSON response for bidsProcessor data: ', err) - } - - if (options.callback) { - options.callback(error, bids); - } - }, - error: (error) => { - _logError('Bids were not processed successfully ', error); - if (options.callback) { - options.callback(isStr(error) ? new Error(error) : error, bids); - } - } - } - }); -} - -registerVideoSupport('konduit', { - processBids: processBids, -}); From 9b536abe8f04bd73df03d7e182b5b85e054241fc Mon Sep 17 00:00:00 2001 From: Patrick McCann Date: Thu, 27 Mar 2025 09:49:37 -0400 Subject: [PATCH 14/92] Delete modules/konduitAnalyticsAdapter.js --- modules/konduitAnalyticsAdapter.js | 227 ----------------------------- 1 file changed, 227 deletions(-) delete mode 100644 modules/konduitAnalyticsAdapter.js diff --git a/modules/konduitAnalyticsAdapter.js b/modules/konduitAnalyticsAdapter.js deleted file mode 100644 index 5316d5b22a4..00000000000 --- a/modules/konduitAnalyticsAdapter.js +++ /dev/null @@ -1,227 +0,0 @@ -import { parseSizesInput, logError, uniques } from '../src/utils.js'; -import { ajax } from '../src/ajax.js'; -import adapter from '../libraries/analyticsAdapter/AnalyticsAdapter.js'; -import adapterManager from '../src/adapterManager.js'; -import { targeting } from '../src/targeting.js'; -import { config } from '../src/config.js'; -import {EVENTS} from '../src/constants.js'; - -const TRACKER_HOST = 'tracker.konduit.me'; -const KONDUIT_PREBID_MODULE_VERSION = '1.0.0'; - -const analyticsType = 'endpoint'; - -const eventDataComposerMap = { - [EVENTS.AUCTION_INIT]: obtainAuctionInfo, - [EVENTS.AUCTION_END]: obtainAuctionInfo, - [EVENTS.BID_REQUESTED]: obtainBidRequestsInfo, - [EVENTS.BID_TIMEOUT]: obtainBidTimeoutInfo, - [EVENTS.BID_RESPONSE]: obtainBidResponseInfo, - [EVENTS.BID_WON]: obtainWinnerBidInfo, - [EVENTS.NO_BID]: obtainNoBidInfo, -}; - -// This function is copy from prebid core -function formatQS(query) { - return Object - .keys(query) - .map(k => Array.isArray(query[k]) - ? query[k].map(v => `${k}[]=${v}`).join('&') - : `${k}=${query[k]}`) - .join('&'); -} - -// This function is copy from prebid core -function buildUrl(obj) { - return (obj.protocol || 'http') + '://' + - (obj.host || - obj.hostname + (obj.port ? `:${obj.port}` : '')) + - (obj.pathname || '') + - (obj.search ? `?${formatQS(obj.search || '')}` : '') + - (obj.hash ? `#${obj.hash}` : ''); -} - -const getWinnerBidFromAggregatedEvents = () => { - return konduitAnalyticsAdapter.context.aggregatedEvents - .filter(evt => evt.eventType === EVENTS.BID_WON)[0]; -}; - -const isWinnerBidDetected = () => { - return !!getWinnerBidFromAggregatedEvents(); -}; -const isWinnerBidExist = () => { - return !!targeting.getWinningBids()[0]; -}; - -const konduitAnalyticsAdapter = Object.assign( - adapter({ analyticsType }), - { - track ({ eventType, args }) { - if (EVENTS.AUCTION_INIT === eventType) { - konduitAnalyticsAdapter.context.aggregatedEvents.splice(0); - } - - if (eventDataComposerMap[eventType]) { - konduitAnalyticsAdapter.context.aggregatedEvents.push({ - eventType, - data: eventDataComposerMap[eventType](args), - }); - } - - if (eventType === EVENTS.AUCTION_END) { - if (!isWinnerBidDetected() && isWinnerBidExist()) { - const bidWonData = eventDataComposerMap[EVENTS.BID_WON](targeting.getWinningBids()[0]); - - konduitAnalyticsAdapter.context.aggregatedEvents.push({ - eventType: EVENTS.BID_WON, - data: bidWonData, - }); - } - sendRequest({ method: 'POST', path: '/analytics-initial-event', payload: composeRequestPayload() }); - } - } - } -); - -function obtainBidTimeoutInfo (args) { - return args.map(item => item.bidder).filter(uniques); -} - -function obtainAuctionInfo (auction) { - return { - auctionId: auction.auctionId, - timestamp: auction.timestamp, - auctionEnd: auction.auctionEnd, - auctionStatus: auction.auctionStatus, - adUnitCodes: auction.adUnitCodes, - labels: auction.labels, - timeout: auction.timeout - }; -} - -function obtainBidRequestsInfo (bidRequests) { - return { - bidderCode: bidRequests.bidderCode, - time: bidRequests.start, - bids: bidRequests.bids.map(function (bid) { - return { - transactionId: bid.transactionId, - adUnitCode: bid.adUnitCode, - bidId: bid.bidId, - startTime: bid.startTime, - sizes: parseSizesInput(bid.sizes).toString(), - params: bid.params - }; - }), - }; -} - -function obtainBidResponseInfo (bidResponse) { - return { - bidderCode: bidResponse.bidder, - transactionId: bidResponse.transactionId, - adUnitCode: bidResponse.adUnitCode, - statusMessage: bidResponse.statusMessage, - mediaType: bidResponse.mediaType, - renderedSize: bidResponse.size, - cpm: bidResponse.cpm, - currency: bidResponse.currency, - netRevenue: bidResponse.netRevenue, - timeToRespond: bidResponse.timeToRespond, - bidId: bidResponse.bidId, - requestId: bidResponse.requestId, - creativeId: bidResponse.creativeId - }; -} - -function obtainNoBidInfo (bidResponse) { - return { - bidderCode: bidResponse.bidder, - transactionId: bidResponse.transactionId, - adUnitCode: bidResponse.adUnitCode, - bidId: bidResponse.bidId, - }; -} - -function obtainWinnerBidInfo (bidResponse) { - return { - adId: bidResponse.adId, - bidderCode: bidResponse.bidder, - adUnitCode: bidResponse.adUnitCode, - statusMessage: bidResponse.statusMessage, - mediaType: bidResponse.mediaType, - renderedSize: bidResponse.size, - cpm: bidResponse.cpm, - currency: bidResponse.currency, - netRevenue: bidResponse.netRevenue, - timeToRespond: bidResponse.timeToRespond, - bidId: bidResponse.requestId, - dealId: bidResponse.dealId, - status: bidResponse.status, - creativeId: bidResponse.creativeId - }; -} - -function composeRequestPayload () { - const konduitId = config.getConfig('konduit.konduitId'); - const { width, height } = window.screen; - - return { - konduitId, - prebidVersion: '$prebid.version$', - konduitPrebidModuleVersion: KONDUIT_PREBID_MODULE_VERSION, - environment: { - screen: { width, height }, - language: navigator.language, - }, - events: konduitAnalyticsAdapter.context.aggregatedEvents, - }; -} - -function sendRequest ({ host = TRACKER_HOST, method, path, payload }) { - const formattedUrlOptions = { - protocol: 'https', - hostname: host, - pathname: path, - }; - if (method === 'GET') { - formattedUrlOptions.search = payload; - } - - let konduitAnalyticsRequestUrl = buildUrl(formattedUrlOptions); - - ajax( - konduitAnalyticsRequestUrl, - undefined, - method === 'POST' ? JSON.stringify(payload) : null, - { - contentType: 'application/json', - method, - withCredentials: true - } - ); -} - -konduitAnalyticsAdapter.originEnableAnalytics = konduitAnalyticsAdapter.enableAnalytics; - -konduitAnalyticsAdapter.enableAnalytics = function (analyticsConfig) { - const konduitId = config.getConfig('konduit.konduitId'); - - if (!konduitId) { - logError('A konduitId in config is required to use konduitAnalyticsAdapter'); - return; - } - - konduitAnalyticsAdapter.context = { - aggregatedEvents: [], - }; - - konduitAnalyticsAdapter.originEnableAnalytics(analyticsConfig); -}; - -adapterManager.registerAnalyticsAdapter({ - adapter: konduitAnalyticsAdapter, - code: 'konduit' -}); - -export default konduitAnalyticsAdapter; From 017cdf7d73410ba0bc6754cf9114816ca3a5a743 Mon Sep 17 00:00:00 2001 From: Patrick McCann Date: Thu, 27 Mar 2025 09:49:51 -0400 Subject: [PATCH 15/92] Delete modules/konduitAnalyticsAdapter.md --- modules/konduitAnalyticsAdapter.md | 32 ------------------------------ 1 file changed, 32 deletions(-) delete mode 100644 modules/konduitAnalyticsAdapter.md diff --git a/modules/konduitAnalyticsAdapter.md b/modules/konduitAnalyticsAdapter.md deleted file mode 100644 index c5854b77ccd..00000000000 --- a/modules/konduitAnalyticsAdapter.md +++ /dev/null @@ -1,32 +0,0 @@ -# Overview -​ -``` -Module Name: Konduit Analytics Adapter -Module Type: Analytics Adapter -Maintainer: support@konduit.me -``` -​ -​ -# Description -​ -Konduit Analytics adapter pushes Prebid events into Konduit platform, which is then organizes the data and presents it to a client in different insightful views. -​ -For more information, visit the [official Konduit website](https://konduitvideo.com/). -​ -​ -# Usage -​ -Konduit Analytics can be enabled with a standard `enableAnalytics` call. -Note it is also important to provide a valid Konduit identifier as a config parameter. -​ -```javascript -pbjs.setConfig({ - konduit: { - konduitId: your_konduit_id, - } -}); -​ -pbjs.enableAnalytics({ - provider: 'konduit' -}) -``` From b52fce043c20a4c4852550173bd96506985ff625 Mon Sep 17 00:00:00 2001 From: Patrick McCann Date: Thu, 27 Mar 2025 09:50:06 -0400 Subject: [PATCH 16/92] Delete test/spec/modules/konduitWrapper_spec.js --- test/spec/modules/konduitWrapper_spec.js | 291 ----------------------- 1 file changed, 291 deletions(-) delete mode 100644 test/spec/modules/konduitWrapper_spec.js diff --git a/test/spec/modules/konduitWrapper_spec.js b/test/spec/modules/konduitWrapper_spec.js deleted file mode 100644 index 506d2189049..00000000000 --- a/test/spec/modules/konduitWrapper_spec.js +++ /dev/null @@ -1,291 +0,0 @@ -import { expect } from 'chai'; - -import { processBids, errorMessages } from 'modules/konduitWrapper.js'; -import { config } from 'src/config.js'; -import { server } from 'test/mocks/xhr.js'; - -describe('The Konduit vast wrapper module', function () { - const konduitId = 'test'; - beforeEach(function() { - config.setConfig({ konduit: { konduitId } }); - }); - - describe('processBids function (send one bid)', () => { - beforeEach(function() { - config.setConfig({ enableSendAllBids: false }); - }); - - it(`should make a correct processBids request and add kCpm and konduitCacheKey - to the passed bids and to the adserverTargeting object`, function () { - const bid = createBid(10, 'video1', 15, '10.00_15s', '123', '395'); - - server.respondWith(JSON.stringify({ - kCpmData: { [`${bid.bidderCode}:${bid.creativeId}`]: bid.cpm }, - cacheData: { [`${bid.bidderCode}:${bid.creativeId}`]: 'test_cache_key' }, - })); - - processBids({ bid }); - server.respond(); - - expect(server.requests.length).to.equal(1); - - const requestBody = JSON.parse(server.requests[0].requestBody); - - expect(requestBody.clientId).to.equal(konduitId); - - expect(bid.konduitCacheKey).to.equal('test_cache_key'); - expect(bid.kCpm).to.equal(bid.cpm); - - expect(bid.adserverTargeting).to.be.an('object'); - - expect(bid.adserverTargeting.k_cpm).to.equal(bid.pbCg || bid.pbAg); - expect(bid.adserverTargeting.k_cache_key).to.equal('test_cache_key'); - expect(bid.adserverTargeting.konduit_id).to.equal(konduitId); - }); - - it(`should call callback with error object in arguments if cacheData is empty in the response`, function () { - const bid = createBid(10, 'video1', 15, '10.00_15s', '123', '395'); - - server.respondWith(JSON.stringify({ - kCpmData: { [`${bid.bidderCode}:${bid.creativeId}`]: bid.cpm }, - cacheData: {}, - })); - const callback = sinon.spy(); - processBids({ bid, callback }); - server.respond(); - expect(server.requests.length).to.equal(1); - - const requestBody = JSON.parse(server.requests[0].requestBody); - - expect(requestBody.clientId).to.equal(konduitId); - - expect(bid.konduitCacheKey).to.be.undefined; - expect(bid.kCpm).to.equal(bid.cpm); - - expect(bid.adserverTargeting.k_cpm).to.equal(bid.pbCg || bid.pbAg); - expect(bid.adserverTargeting.k_cache_key).to.be.undefined; - expect(bid.adserverTargeting.konduit_id).to.be.undefined; - - expect(callback.firstCall.args[0]).to.be.an('error'); - }); - - it('should call callback if processBids request is sent successfully', function () { - const bid = createBid(10, 'video1', 15, '10.00_15s', '123', '395'); - server.respondWith(JSON.stringify({ key: 'test' })); - const callback = sinon.spy(); - processBids({ - bid, - callback - }); - server.respond(); - - expect(callback.calledOnce).to.be.true; - }); - - it('should call callback with error object in arguments if processBids request is failed', function () { - const bid = createBid(10, 'video1', 15, '10.00_15s', '123', '395'); - const callback = sinon.spy(); - processBids({ - bid, - callback - }); - server.respond(); - - expect(callback.calledOnce).to.be.true; - expect(callback.firstCall.args[0]).to.be.an('error'); - }); - - it('should call callback with error object in arguments if no konduitId in configs', function () { - config.setConfig({ konduit: { konduitId: null } }); - - const bid = createBid(10, 'video1', 15, '10.00_15s', '123', '395'); - const callback = sinon.spy(); - processBids({ - bid, - callback - }); - - expect(callback.calledOnce).to.be.true; - expect(callback.firstCall.args[0]).to.be.an('error'); - expect(callback.firstCall.args[0].message).to.equal(errorMessages.NO_KONDUIT_ID); - }); - - it('should call callback with error object in arguments if no bids found', function () { - const callback = sinon.spy(); - processBids({ - bid: null, - bids: [], - callback - }); - - expect(callback.calledOnce).to.be.true; - expect(callback.firstCall.args[0]).to.be.an('error'); - expect(callback.firstCall.args[0].message).to.equal(errorMessages.NO_BIDS); - }); - }); - describe('processBids function (send all bids)', () => { - beforeEach(function() { - config.setConfig({ enableSendAllBids: true }); - }); - - it(`should make a correct processBids request and add kCpm and konduitCacheKey - to the passed bids and to the adserverTargeting object`, function () { - const bid = createBid(10, 'video1', 15, '10.00_15s', '123', '395'); - - server.respondWith(JSON.stringify({ - kCpmData: { [`${bid.bidderCode}:${bid.creativeId}`]: bid.cpm }, - cacheData: { [`${bid.bidderCode}:${bid.creativeId}`]: 'test_cache_key' }, - })); - - processBids({ adUnitCode: 'video1', bids: [bid] }); - server.respond(); - - expect(server.requests.length).to.equal(1); - - const requestBody = JSON.parse(server.requests[0].requestBody); - - expect(requestBody.clientId).to.equal(konduitId); - - expect(bid.konduitCacheKey).to.equal('test_cache_key'); - expect(bid.kCpm).to.equal(bid.cpm); - - expect(bid.adserverTargeting).to.be.an('object'); - - expect(bid.adserverTargeting.k_cpm).to.equal(bid.pbCg || bid.pbAg); - expect(bid.adserverTargeting[`k_cpm_${bid.bidderCode}`]).to.equal(bid.pbCg || bid.pbAg); - expect(bid.adserverTargeting.k_cache_key).to.equal('test_cache_key'); - expect(bid.adserverTargeting[`k_cache_key_${bid.bidderCode}`]).to.equal('test_cache_key'); - expect(bid.adserverTargeting.konduit_id).to.equal(konduitId); - }); - - it(`should call callback with error object in arguments if cacheData is empty in the response`, function () { - const bid = createBid(10, 'video1', 15, '10.00_15s', '123', '395'); - - server.respondWith(JSON.stringify({ - kCpmData: { [`${bid.bidderCode}:${bid.creativeId}`]: bid.cpm }, - cacheData: {}, - })); - const callback = sinon.spy(); - processBids({ adUnitCode: 'video1', bids: [bid], callback }); - server.respond(); - - expect(server.requests.length).to.equal(1); - - const requestBody = JSON.parse(server.requests[0].requestBody); - - expect(requestBody.clientId).to.equal(konduitId); - - expect(bid.konduitCacheKey).to.be.undefined; - expect(bid.kCpm).to.equal(bid.cpm); - - expect(bid.adserverTargeting.k_cpm).to.equal(bid.pbCg || bid.pbAg); - expect(bid.adserverTargeting[`k_cpm_${bid.bidderCode}`]).to.equal(bid.pbCg || bid.pbAg); - expect(bid.adserverTargeting.k_cache_key).to.be.undefined; - expect(bid.adserverTargeting[`k_cache_key_${bid.bidderCode}`]).to.be.undefined; - expect(bid.adserverTargeting.konduit_id).to.be.undefined; - - expect(callback.firstCall.args[0]).to.be.an('error'); - }); - - it('should call callback if processBids request is sent successfully', function () { - const bid = createBid(10, 'video1', 15, '10.00_15s', '123', '395'); - server.respondWith(JSON.stringify({ key: 'test' })); - const callback = sinon.spy(); - processBids({ adUnitCode: 'video1', bid: [bid], callback }); - server.respond(); - - expect(callback.calledOnce).to.be.true; - }); - - it('should call callback with error object in arguments if processBids request is failed', function () { - const bid = createBid(10, 'video1', 15, '10.00_15s', '123', '395'); - const callback = sinon.spy(); - processBids({ adUnitCode: 'video1', bid: [bid], callback }); - server.respond(); - - expect(callback.calledOnce).to.be.true; - expect(callback.firstCall.args[0]).to.be.an('error'); - }); - - it('should call callback with error object in arguments if no konduitId in configs', function () { - config.setConfig({ konduit: { konduitId: null } }); - - const bid = createBid(10, 'video1', 15, '10.00_15s', '123', '395'); - const callback = sinon.spy(); - processBids({ adUnitCode: 'video1', bid: [bid], callback }); - - expect(callback.calledOnce).to.be.true; - expect(callback.firstCall.args[0]).to.be.an('error'); - expect(callback.firstCall.args[0].message).to.equal(errorMessages.NO_KONDUIT_ID); - }); - - it('should call callback with error object in arguments if no bids found', function () { - const callback = sinon.spy(); - processBids({ - bid: null, - bids: [], - callback - }); - - expect(callback.calledOnce).to.be.true; - expect(callback.firstCall.args[0]).to.be.an('error'); - expect(callback.firstCall.args[0].message).to.equal(errorMessages.NO_BIDS); - }); - }); -}); - -function createBid(cpm, adUnitCode, durationBucket, priceIndustryDuration, uuid, label) { - return { - 'bidderCode': 'appnexus', - 'width': 640, - 'height': 360, - 'statusMessage': 'Bid available', - 'adId': '28f24ced14586c', - 'mediaType': 'video', - 'source': 'client', - 'requestId': '28f24ced14586c', - 'cpm': cpm, - 'creativeId': 97517771, - 'currency': 'USD', - 'netRevenue': true, - 'ttl': 3600, - 'adUnitCode': adUnitCode, - 'video': { - 'context': 'adpod', - 'durationBucket': durationBucket - }, - 'appnexus': { - 'buyerMemberId': 9325 - }, - 'vastUrl': 'http://some-vast-url.com', - 'vastImpUrl': 'http://some-vast-imp-url.com', - 'auctionId': 'ec266b31-d652-49c5-8295-e83fafe5532b', - 'responseTimestamp': 1548442460888, - 'requestTimestamp': 1548442460827, - 'bidder': 'appnexus', - 'timeToRespond': 61, - 'pbLg': '5.00', - 'pbMg': `${cpm}.00`, - 'pbHg': '5.00', - 'pbAg': `${cpm}.00`, - 'pbDg': '5.00', - 'pbCg': '', - 'size': '640x360', - 'adserverTargeting': { - 'hb_bidder': 'appnexus', - 'hb_adid': '28f24ced14586c', - 'hb_pb': '5.00', - 'hb_size': '640x360', - 'hb_source': 'client', - 'hb_format': 'video', - 'hb_pb_cat_dur': priceIndustryDuration, - 'hb_cache_id': uuid - }, - 'customCacheKey': `${priceIndustryDuration}_${uuid}`, - 'meta': { - 'primaryCatId': 'iab-1', - 'adServerCatId': label - }, - 'videoCacheKey': '4cf395af-8fee-4960-af0e-88d44e399f14' - } -} From 64c19364cbc568fc1bf55acb06edf361e004ddf2 Mon Sep 17 00:00:00 2001 From: Patrick McCann Date: Thu, 27 Mar 2025 09:50:24 -0400 Subject: [PATCH 17/92] Delete test/spec/modules/konduitAnalyticsAdapter_spec.js --- .../modules/konduitAnalyticsAdapter_spec.js | 125 ------------------ 1 file changed, 125 deletions(-) delete mode 100644 test/spec/modules/konduitAnalyticsAdapter_spec.js diff --git a/test/spec/modules/konduitAnalyticsAdapter_spec.js b/test/spec/modules/konduitAnalyticsAdapter_spec.js deleted file mode 100644 index 496dd171afa..00000000000 --- a/test/spec/modules/konduitAnalyticsAdapter_spec.js +++ /dev/null @@ -1,125 +0,0 @@ -import konduitAnalyticsAdapter from 'modules/konduitAnalyticsAdapter'; -import { expect } from 'chai'; -import { config } from '../../../src/config.js'; -import { server } from 'test/mocks/xhr.js'; -import { EVENTS } from 'src/constants.js'; -let events = require('src/events'); -let adapterManager = require('src/adapterManager').default; - -const eventsData = { - [EVENTS.AUCTION_INIT]: { - 'auctionId': 'test_auction_id', - 'timestamp': Date.now(), - 'auctionStatus': 'inProgress', - 'adUnitCodes': ['video-test'], - 'timeout': 700 - }, - [EVENTS.BID_REQUESTED]: { - 'bidderCode': 'test_bidder_code', - 'time': Date.now(), - 'bids': [{ - 'transactionId': 'test_transaction_id', - 'adUnitCode': 'video-test', - 'bidId': 'test_bid_id', - 'sizes': '640x480', - 'params': { 'testParam': 'test_param' } - }] - }, - [EVENTS.NO_BID]: { - 'bidderCode': 'test_bidder_code2', - 'transactionId': 'test_transaction_id', - 'adUnitCode': 'video-test', - 'bidId': 'test_bid_id' - }, - [EVENTS.BID_RESPONSE]: { - 'bidderCode': 'test_bidder_code', - 'adUnitCode': 'video-test', - 'statusMessage': 'Bid available', - 'mediaType': 'video', - 'renderedSize': '640x480', - 'cpm': 0.5, - 'currency': 'USD', - 'netRevenue': true, - 'timeToRespond': 124, - 'requestId': 'test_request_id', - 'creativeId': 144876543 - }, - [EVENTS.AUCTION_END]: { - 'auctionId': 'test_auction_id', - 'timestamp': Date.now(), - 'auctionEnd': Date.now() + 400, - 'auctionStatus': 'completed', - 'adUnitCodes': ['video-test'], - 'timeout': 700 - }, - [EVENTS.BID_WON]: { - 'bidderCode': 'test_bidder_code', - 'adUnitCode': 'video-test', - 'statusMessage': 'Bid available', - 'mediaType': 'video', - 'renderedSize': '640x480', - 'cpm': 0.5, - 'currency': 'USD', - 'netRevenue': true, - 'timeToRespond': 124, - 'requestId': 'test_request_id', - 'creativeId': 144876543 - }, -}; - -describe(`Konduit Analytics Adapter`, () => { - const konduitId = 'test'; - - beforeEach(function () { - sinon.spy(konduitAnalyticsAdapter, 'track'); - sinon.stub(events, 'getEvents').returns([]); - config.setConfig({ konduit: { konduitId } }); - }); - - afterEach(function () { - events.getEvents.restore(); - konduitAnalyticsAdapter.track.restore(); - konduitAnalyticsAdapter.disableAnalytics(); - }); - - it(`should add all events to an aggregatedEvents queue - inside konduitAnalyticsAdapter.context and send a request with correct data`, function () { - server.respondWith(JSON.stringify({ key: 'test' })); - - adapterManager.registerAnalyticsAdapter({ - code: 'konduit', - adapter: konduitAnalyticsAdapter - }); - - adapterManager.enableAnalytics({ - provider: 'konduit', - }); - - expect(konduitAnalyticsAdapter.context).to.be.an('object'); - expect(konduitAnalyticsAdapter.context.aggregatedEvents).to.be.an('array'); - - const eventTypes = [ - EVENTS.AUCTION_INIT, - EVENTS.BID_REQUESTED, - EVENTS.NO_BID, - EVENTS.BID_RESPONSE, - EVENTS.BID_WON, - EVENTS.AUCTION_END, - ]; - const args = eventTypes.map(eventType => eventsData[eventType]); - - eventTypes.forEach((eventType, i) => { - events.emit(eventType, args[i]); - }); - - server.respond(); - - expect(konduitAnalyticsAdapter.context.aggregatedEvents.length).to.be.equal(6); - expect(server.requests[0].url).to.match(/http(s):\/\/\w*\.konduit\.me\/analytics-initial-event/); - - const requestBody = JSON.parse(server.requests[0].requestBody); - expect(requestBody.konduitId).to.be.equal(konduitId); - expect(requestBody.prebidVersion).to.be.equal('$prebid.version$'); - expect(requestBody.environment).to.be.an('object'); - }); -}); From 38b5dd06d2cdc5332c276067cc3ff036d6ba390c Mon Sep 17 00:00:00 2001 From: Patrick McCann Date: Thu, 27 Mar 2025 09:54:06 -0400 Subject: [PATCH 18/92] Delete modules/globalsunBidAdapter.js --- modules/globalsunBidAdapter.js | 19 ------------------- 1 file changed, 19 deletions(-) delete mode 100644 modules/globalsunBidAdapter.js diff --git a/modules/globalsunBidAdapter.js b/modules/globalsunBidAdapter.js deleted file mode 100644 index 1684509b7b9..00000000000 --- a/modules/globalsunBidAdapter.js +++ /dev/null @@ -1,19 +0,0 @@ -import { registerBidder } from '../src/adapters/bidderFactory.js'; -import { BANNER, NATIVE, VIDEO } from '../src/mediaTypes.js'; -import { isBidRequestValid, buildRequests, interpretResponse, getUserSyncs } from '../libraries/teqblazeUtils/bidderUtils.js'; - -const BIDDER_CODE = 'globalsun'; -const AD_URL = 'https://endpoint.globalsun.io/pbjs'; -const SYNC_URL = 'https://cs.globalsun.io'; - -export const spec = { - code: BIDDER_CODE, - supportedMediaTypes: [BANNER, VIDEO, NATIVE], - - isBidRequestValid: isBidRequestValid(), - buildRequests: buildRequests(AD_URL), - interpretResponse, - getUserSyncs: getUserSyncs(SYNC_URL) -}; - -registerBidder(spec); From 847ad5b52ceb7b7afc8859c11436003e0685a1f5 Mon Sep 17 00:00:00 2001 From: Patrick McCann Date: Thu, 27 Mar 2025 09:54:19 -0400 Subject: [PATCH 19/92] Delete modules/globalsunBidAdapter.md --- modules/globalsunBidAdapter.md | 79 ---------------------------------- 1 file changed, 79 deletions(-) delete mode 100644 modules/globalsunBidAdapter.md diff --git a/modules/globalsunBidAdapter.md b/modules/globalsunBidAdapter.md deleted file mode 100644 index 07c3ce32155..00000000000 --- a/modules/globalsunBidAdapter.md +++ /dev/null @@ -1,79 +0,0 @@ -# Overview - -``` -Module Name: Globalsun Bidder Adapter -Module Type: Globalsun Bidder Adapter -Maintainer: prebid@globalsun.io -``` - -# Description - -Connects to Globalsun exchange for bids. -Globalsun bid adapter supports Banner, Video (instream and outstream) and Native. - -# Test Parameters -``` - var adUnits = [ - // Will return static test banner - { - code: 'adunit1', - mediaTypes: { - banner: { - sizes: [ [300, 250], [320, 50] ], - } - }, - bids: [ - { - bidder: 'globalsun', - params: { - placementId: 'testBanner', - } - } - ] - }, - { - code: 'addunit2', - mediaTypes: { - video: { - playerSize: [ [640, 480] ], - context: 'instream', - minduration: 5, - maxduration: 60, - } - }, - bids: [ - { - bidder: 'globalsun', - params: { - placementId: 'testVideo', - } - } - ] - }, - { - code: 'addunit3', - mediaTypes: { - native: { - title: { - required: true - }, - body: { - required: true - }, - icon: { - required: true, - size: [64, 64] - } - } - }, - bids: [ - { - bidder: 'globalsun', - params: { - placementId: 'testNative', - } - } - ] - } - ]; -``` From bf060f39730c1093a6b83cabb2b9622dd86ae966 Mon Sep 17 00:00:00 2001 From: Patrick McCann Date: Thu, 27 Mar 2025 09:54:32 -0400 Subject: [PATCH 20/92] Delete test/spec/modules/globalsunBidAdapter_spec.js --- test/spec/modules/globalsunBidAdapter_spec.js | 518 ------------------ 1 file changed, 518 deletions(-) delete mode 100644 test/spec/modules/globalsunBidAdapter_spec.js diff --git a/test/spec/modules/globalsunBidAdapter_spec.js b/test/spec/modules/globalsunBidAdapter_spec.js deleted file mode 100644 index f8d6e2b710d..00000000000 --- a/test/spec/modules/globalsunBidAdapter_spec.js +++ /dev/null @@ -1,518 +0,0 @@ -import { expect } from 'chai'; -import { spec } from '../../../modules/globalsunBidAdapter.js'; -import { BANNER, VIDEO, NATIVE } from '../../../src/mediaTypes.js'; -import { getUniqueIdentifierStr } from '../../../src/utils.js'; - -const bidder = 'globalsun'; - -describe('GlobalsunBidAdapter', function () { - const userIdAsEids = [{ - source: 'test.org', - uids: [{ - id: '01**********', - atype: 1, - ext: { - third: '01***********' - } - }] - }]; - const bids = [ - { - bidId: getUniqueIdentifierStr(), - bidder: bidder, - mediaTypes: { - [BANNER]: { - sizes: [[300, 250]] - } - }, - params: { - placementId: 'testBanner' - }, - userIdAsEids - }, - { - bidId: getUniqueIdentifierStr(), - bidder: bidder, - mediaTypes: { - [VIDEO]: { - playerSize: [[300, 300]], - minduration: 5, - maxduration: 60 - } - }, - params: { - placementId: 'testVideo' - }, - userIdAsEids - }, - { - bidId: getUniqueIdentifierStr(), - bidder: bidder, - mediaTypes: { - [NATIVE]: { - native: { - title: { - required: true - }, - body: { - required: true - }, - icon: { - required: true, - size: [64, 64] - } - } - } - }, - params: { - placementId: 'testNative' - }, - userIdAsEids - } - ]; - - const invalidBid = { - bidId: getUniqueIdentifierStr(), - bidder: bidder, - mediaTypes: { - [BANNER]: { - sizes: [[300, 250]] - } - }, - params: { - - } - } - - const bidderRequest = { - uspConsent: '1---', - gdprConsent: { - consentString: 'COvFyGBOvFyGBAbAAAENAPCAAOAAAAAAAAAAAEEUACCKAAA.IFoEUQQgAIQwgIwQABAEAAAAOIAACAIAAAAQAIAgEAACEAAAAAgAQBAAAAAAAGBAAgAAAAAAAFAAECAAAgAAQARAEQAAAAAJAAIAAgAAAYQEAAAQmAgBC3ZAYzUw', - vendorData: {} - }, - refererInfo: { - referer: 'https://test.com', - page: 'https://test.com' - }, - ortb2: { - device: { - w: 1512, - h: 982, - language: 'en-UK' - } - }, - timeout: 500 - }; - - describe('isBidRequestValid', function () { - it('Should return true if there are bidId, params and key parameters present', function () { - expect(spec.isBidRequestValid(bids[0])).to.be.true; - }); - it('Should return false if at least one of parameters is not present', function () { - expect(spec.isBidRequestValid(invalidBid)).to.be.false; - }); - }); - - describe('buildRequests', function () { - let serverRequest = spec.buildRequests(bids, bidderRequest); - - it('Creates a ServerRequest object with method, URL and data', function () { - expect(serverRequest).to.exist; - expect(serverRequest.method).to.exist; - expect(serverRequest.url).to.exist; - expect(serverRequest.data).to.exist; - }); - - it('Returns POST method', function () { - expect(serverRequest.method).to.equal('POST'); - }); - - it('Returns valid URL', function () { - expect(serverRequest.url).to.equal('https://endpoint.globalsun.io/pbjs'); - }); - - it('Returns general data valid', function () { - let data = serverRequest.data; - expect(data).to.be.an('object'); - expect(data).to.have.all.keys('deviceWidth', - 'deviceHeight', - 'device', - 'language', - 'secure', - 'host', - 'page', - 'placements', - 'coppa', - 'ccpa', - 'gdpr', - 'tmax', - 'bcat', - 'badv', - 'bapp', - 'battr' - ); - expect(data.deviceWidth).to.be.a('number'); - expect(data.deviceHeight).to.be.a('number'); - expect(data.language).to.be.a('string'); - expect(data.secure).to.be.within(0, 1); - expect(data.host).to.be.a('string'); - expect(data.page).to.be.a('string'); - expect(data.coppa).to.be.a('number'); - expect(data.gdpr).to.be.a('object'); - expect(data.ccpa).to.be.a('string'); - expect(data.tmax).to.be.a('number'); - expect(data.placements).to.have.lengthOf(3); - }); - - it('Returns valid placements', function () { - const { placements } = serverRequest.data; - for (let i = 0, len = placements.length; i < len; i++) { - const placement = placements[i]; - expect(placement.placementId).to.be.oneOf(['testBanner', 'testVideo', 'testNative']); - expect(placement.adFormat).to.be.oneOf([BANNER, VIDEO, NATIVE]); - expect(placement.bidId).to.be.a('string'); - expect(placement.schain).to.be.an('object'); - expect(placement.bidfloor).to.exist.and.to.equal(0); - expect(placement.type).to.exist.and.to.equal('publisher'); - expect(placement.eids).to.exist.and.to.be.deep.equal(userIdAsEids); - - if (placement.adFormat === BANNER) { - expect(placement.sizes).to.be.an('array'); - } - switch (placement.adFormat) { - case BANNER: - expect(placement.sizes).to.be.an('array'); - break; - case VIDEO: - expect(placement.playerSize).to.be.an('array'); - expect(placement.minduration).to.be.an('number'); - expect(placement.maxduration).to.be.an('number'); - break; - case NATIVE: - expect(placement.native).to.be.an('object'); - break; - } - } - }); - - it('Returns valid endpoints', function () { - const bids = [ - { - bidId: getUniqueIdentifierStr(), - bidder: bidder, - mediaTypes: { - [BANNER]: { - sizes: [[300, 250]] - } - }, - params: { - endpointId: 'testBanner', - }, - userIdAsEids - } - ]; - - let serverRequest = spec.buildRequests(bids, bidderRequest); - - const { placements } = serverRequest.data; - for (let i = 0, len = placements.length; i < len; i++) { - const placement = placements[i]; - expect(placement.endpointId).to.be.oneOf(['testBanner', 'testVideo', 'testNative']); - expect(placement.adFormat).to.be.oneOf([BANNER, VIDEO, NATIVE]); - expect(placement.bidId).to.be.a('string'); - expect(placement.schain).to.be.an('object'); - expect(placement.bidfloor).to.exist.and.to.equal(0); - expect(placement.type).to.exist.and.to.equal('network'); - expect(placement.eids).to.exist.and.to.be.deep.equal(userIdAsEids); - - if (placement.adFormat === BANNER) { - expect(placement.sizes).to.be.an('array'); - } - switch (placement.adFormat) { - case BANNER: - expect(placement.sizes).to.be.an('array'); - break; - case VIDEO: - expect(placement.playerSize).to.be.an('array'); - expect(placement.minduration).to.be.an('number'); - expect(placement.maxduration).to.be.an('number'); - break; - case NATIVE: - expect(placement.native).to.be.an('object'); - break; - } - } - }); - - it('Returns data with gdprConsent and without uspConsent', function () { - delete bidderRequest.uspConsent; - serverRequest = spec.buildRequests(bids, bidderRequest); - let data = serverRequest.data; - expect(data.gdpr).to.exist; - expect(data.gdpr).to.be.a('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; - }); - - it('Returns data with uspConsent and without gdprConsent', function () { - bidderRequest.uspConsent = '1---'; - delete bidderRequest.gdprConsent; - serverRequest = spec.buildRequests(bids, bidderRequest); - let data = serverRequest.data; - expect(data.ccpa).to.exist; - expect(data.ccpa).to.be.a('string'); - expect(data.ccpa).to.equal(bidderRequest.uspConsent); - expect(data.gdpr).to.not.exist; - }); - }); - - describe('gpp consent', function () { - it('bidderRequest.gppConsent', () => { - bidderRequest.gppConsent = { - gppString: 'abc123', - applicableSections: [8] - }; - - let serverRequest = spec.buildRequests(bids, bidderRequest); - let data = serverRequest.data; - expect(data).to.be.an('object'); - expect(data).to.have.property('gpp'); - expect(data).to.have.property('gpp_sid'); - - delete bidderRequest.gppConsent; - }) - - it('bidderRequest.ortb2.regs.gpp', () => { - bidderRequest.ortb2 = bidderRequest.ortb2 || {}; - bidderRequest.ortb2.regs = bidderRequest.ortb2.regs || {}; - bidderRequest.ortb2.regs.gpp = 'abc123'; - bidderRequest.ortb2.regs.gpp_sid = [8]; - - let serverRequest = spec.buildRequests(bids, bidderRequest); - let data = serverRequest.data; - expect(data).to.be.an('object'); - expect(data).to.have.property('gpp'); - expect(data).to.have.property('gpp_sid'); - - bidderRequest.ortb2; - }) - }); - - describe('interpretResponse', function () { - it('Should interpret banner response', function () { - const banner = { - body: [{ - mediaType: 'banner', - width: 300, - height: 250, - cpm: 0.4, - ad: 'Test', - requestId: '23fhj33i987f', - ttl: 120, - creativeId: '2', - netRevenue: true, - currency: 'USD', - dealId: '1', - meta: { - advertiserDomains: ['google.com'], - advertiserId: 1234 - } - }] - }; - let bannerResponses = spec.interpretResponse(banner); - expect(bannerResponses).to.be.an('array').that.is.not.empty; - let dataItem = bannerResponses[0]; - expect(dataItem).to.have.all.keys('requestId', 'cpm', 'width', 'height', 'ad', 'ttl', 'creativeId', - 'netRevenue', 'currency', 'dealId', 'mediaType', 'meta'); - expect(dataItem.requestId).to.equal(banner.body[0].requestId); - expect(dataItem.cpm).to.equal(banner.body[0].cpm); - expect(dataItem.width).to.equal(banner.body[0].width); - expect(dataItem.height).to.equal(banner.body[0].height); - expect(dataItem.ad).to.equal(banner.body[0].ad); - expect(dataItem.ttl).to.equal(banner.body[0].ttl); - expect(dataItem.creativeId).to.equal(banner.body[0].creativeId); - expect(dataItem.netRevenue).to.be.true; - expect(dataItem.currency).to.equal(banner.body[0].currency); - expect(dataItem.meta).to.be.an('object').that.has.any.key('advertiserDomains'); - }); - it('Should interpret video response', function () { - const video = { - body: [{ - vastUrl: 'test.com', - mediaType: 'video', - cpm: 0.5, - requestId: '23fhj33i987f', - ttl: 120, - creativeId: '2', - netRevenue: true, - currency: 'USD', - dealId: '1', - meta: { - advertiserDomains: ['google.com'], - advertiserId: 1234 - } - }] - }; - let videoResponses = spec.interpretResponse(video); - expect(videoResponses).to.be.an('array').that.is.not.empty; - - let dataItem = videoResponses[0]; - expect(dataItem).to.have.all.keys('requestId', 'cpm', 'vastUrl', 'ttl', 'creativeId', - 'netRevenue', 'currency', 'dealId', 'mediaType', 'meta'); - expect(dataItem.requestId).to.equal('23fhj33i987f'); - expect(dataItem.cpm).to.equal(0.5); - expect(dataItem.vastUrl).to.equal('test.com'); - expect(dataItem.ttl).to.equal(120); - expect(dataItem.creativeId).to.equal('2'); - expect(dataItem.netRevenue).to.be.true; - expect(dataItem.currency).to.equal('USD'); - expect(dataItem.meta).to.be.an('object').that.has.any.key('advertiserDomains'); - }); - it('Should interpret native response', function () { - const native = { - body: [{ - mediaType: 'native', - native: { - clickUrl: 'test.com', - title: 'Test', - image: 'test.com', - impressionTrackers: ['test.com'], - }, - ttl: 120, - cpm: 0.4, - requestId: '23fhj33i987f', - creativeId: '2', - netRevenue: true, - currency: 'USD', - meta: { - advertiserDomains: ['google.com'], - advertiserId: 1234 - } - }] - }; - let nativeResponses = spec.interpretResponse(native); - expect(nativeResponses).to.be.an('array').that.is.not.empty; - - let dataItem = nativeResponses[0]; - expect(dataItem).to.have.keys('requestId', 'cpm', 'ttl', 'creativeId', 'netRevenue', 'currency', 'mediaType', 'native', 'meta'); - expect(dataItem.native).to.have.keys('clickUrl', 'impressionTrackers', 'title', 'image') - expect(dataItem.requestId).to.equal('23fhj33i987f'); - expect(dataItem.cpm).to.equal(0.4); - expect(dataItem.native.clickUrl).to.equal('test.com'); - expect(dataItem.native.title).to.equal('Test'); - expect(dataItem.native.image).to.equal('test.com'); - expect(dataItem.native.impressionTrackers).to.be.an('array').that.is.not.empty; - expect(dataItem.native.impressionTrackers[0]).to.equal('test.com'); - expect(dataItem.ttl).to.equal(120); - expect(dataItem.creativeId).to.equal('2'); - expect(dataItem.netRevenue).to.be.true; - expect(dataItem.currency).to.equal('USD'); - expect(dataItem.meta).to.be.an('object').that.has.any.key('advertiserDomains'); - }); - it('Should return an empty array if invalid banner response is passed', function () { - const invBanner = { - body: [{ - width: 300, - cpm: 0.4, - ad: 'Test', - requestId: '23fhj33i987f', - ttl: 120, - creativeId: '2', - netRevenue: true, - currency: 'USD', - dealId: '1' - }] - }; - - let serverResponses = spec.interpretResponse(invBanner); - expect(serverResponses).to.be.an('array').that.is.empty; - }); - it('Should return an empty array if invalid video response is passed', function () { - const invVideo = { - body: [{ - mediaType: 'video', - cpm: 0.5, - requestId: '23fhj33i987f', - ttl: 120, - creativeId: '2', - netRevenue: true, - currency: 'USD', - dealId: '1' - }] - }; - let serverResponses = spec.interpretResponse(invVideo); - expect(serverResponses).to.be.an('array').that.is.empty; - }); - it('Should return an empty array if invalid native response is passed', function () { - const invNative = { - body: [{ - mediaType: 'native', - clickUrl: 'test.com', - title: 'Test', - impressionTrackers: ['test.com'], - ttl: 120, - requestId: '23fhj33i987f', - creativeId: '2', - netRevenue: true, - currency: 'USD', - }] - }; - let serverResponses = spec.interpretResponse(invNative); - expect(serverResponses).to.be.an('array').that.is.empty; - }); - it('Should return an empty array if invalid response is passed', function () { - const invalid = { - body: [{ - ttl: 120, - creativeId: '2', - netRevenue: true, - currency: 'USD', - dealId: '1' - }] - }; - let serverResponses = spec.interpretResponse(invalid); - expect(serverResponses).to.be.an('array').that.is.empty; - }); - }); - - describe('getUserSyncs', function() { - it('Should return array of objects with proper sync config , include GDPR', function() { - const syncData = spec.getUserSyncs({}, {}, { - consentString: 'ALL', - gdprApplies: true, - }, {}); - expect(syncData).to.be.an('array').which.is.not.empty; - expect(syncData[0]).to.be.an('object') - expect(syncData[0].type).to.be.a('string') - expect(syncData[0].type).to.equal('image') - expect(syncData[0].url).to.be.a('string') - expect(syncData[0].url).to.equal('https://cs.globalsun.io/image?pbjs=1&gdpr=1&gdpr_consent=ALL&coppa=0') - }); - it('Should return array of objects with proper sync config , include CCPA', function() { - const syncData = spec.getUserSyncs({}, {}, {}, { - consentString: '1---' - }); - expect(syncData).to.be.an('array').which.is.not.empty; - expect(syncData[0]).to.be.an('object') - expect(syncData[0].type).to.be.a('string') - expect(syncData[0].type).to.equal('image') - expect(syncData[0].url).to.be.a('string') - expect(syncData[0].url).to.equal('https://cs.globalsun.io/image?pbjs=1&ccpa_consent=1---&coppa=0') - }); - it('Should return array of objects with proper sync config , include GPP', function() { - const syncData = spec.getUserSyncs({}, {}, {}, {}, { - gppString: 'abc123', - applicableSections: [8] - }); - expect(syncData).to.be.an('array').which.is.not.empty; - expect(syncData[0]).to.be.an('object') - expect(syncData[0].type).to.be.a('string') - expect(syncData[0].type).to.equal('image') - expect(syncData[0].url).to.be.a('string') - expect(syncData[0].url).to.equal('https://cs.globalsun.io/image?pbjs=1&gpp=abc123&gpp_sid=8&coppa=0') - }); - }); -}); From 63d9df30b9d0ea21d9a9e0e45f59f42fac85f218 Mon Sep 17 00:00:00 2001 From: Patrick McCann Date: Thu, 27 Mar 2025 09:55:13 -0400 Subject: [PATCH 21/92] Delete modules/verizonMediaIdSystem.js --- modules/verizonMediaIdSystem.js | 118 -------------------------------- 1 file changed, 118 deletions(-) delete mode 100644 modules/verizonMediaIdSystem.js diff --git a/modules/verizonMediaIdSystem.js b/modules/verizonMediaIdSystem.js deleted file mode 100644 index f2ab448aec8..00000000000 --- a/modules/verizonMediaIdSystem.js +++ /dev/null @@ -1,118 +0,0 @@ -/** - * This module adds verizonMediaId to the User ID module - * The {@link module:modules/userId} module is required - * @module modules/verizonMediaIdSystem - * @requires module:modules/userId - */ - -import {ajax} from '../src/ajax.js'; -import {submodule} from '../src/hook.js'; -import {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__'; -const VMCID_ENDPOINT = `https://ups.analytics.yahoo.com/ups/${PLACEHOLDER}/fed`; - -function isEUConsentRequired(consentData) { - return !!(consentData && consentData.gdpr && consentData.gdpr.gdprApplies); -} - -/** @type {Submodule} */ -export const verizonMediaIdSubmodule = { - /** - * used to link submodule with config - * @type {string} - */ - name: MODULE_NAME, - /** - * Vendor id of Verizon Media EMEA Limited - * @type {Number} - */ - gvlid: VENDOR_ID, - /** - * decode the stored id value for passing to bid requests - * @function - * @returns {{connectid: string} | undefined} - */ - decode(value) { - return (typeof value === 'object' && (value.connectid || value.vmuid)) - ? {connectid: value.connectid || value.vmuid} : undefined; - }, - /** - * Gets the Verizon Media Connect ID - * @function - * @param {SubmoduleConfig} [config] - * @param {ConsentData} [consentData] - * @returns {IdResponse|undefined} - */ - getId(config, consentData) { - const params = config.params || {}; - if (!params || typeof params.he !== 'string' || - (typeof params.pixelId === 'undefined' && typeof params.endpoint === 'undefined')) { - logError('The verizonMediaId submodule requires the \'he\' and \'pixelId\' parameters to be defined.'); - return; - } - - const data = { - '1p': includes([1, '1', true], params['1p']) ? '1' : '0', - he: params.he, - gdpr: isEUConsentRequired(consentData) ? '1' : '0', - gdpr_consent: isEUConsentRequired(consentData) ? consentData.gdpr.consentString : '', - us_privacy: consentData && consentData.usp ? consentData.usp : '' - }; - - if (params.pixelId) { - data.pixelId = params.pixelId - } - - const resp = function (callback) { - const callbacks = { - success: response => { - let responseObj; - if (response) { - try { - responseObj = JSON.parse(response); - } catch (error) { - logError(error); - } - } - callback(responseObj); - }, - error: error => { - logError(`${MODULE_NAME}: ID fetch encountered an error`, error); - callback(); - } - }; - const endpoint = VMCID_ENDPOINT.replace(PLACEHOLDER, params.pixelId); - let url = `${params.endpoint || endpoint}?${formatQS(data)}`; - verizonMediaIdSubmodule.getAjaxFn()(url, callbacks, null, {method: 'GET', withCredentials: true}); - }; - return {callback: resp}; - }, - - /** - * Return the function used to perform XHR calls. - * Utilised for each of testing. - * @returns {Function} - */ - getAjaxFn() { - return ajax; - }, - eids: { - 'connectid': { - source: 'verizonmedia.com', - atype: 3 - }, - } -}; - -submodule('userId', verizonMediaIdSubmodule); From 5818151bc9fc38e1acdfed9c4b3b23f2521890d4 Mon Sep 17 00:00:00 2001 From: Patrick McCann Date: Thu, 27 Mar 2025 09:55:28 -0400 Subject: [PATCH 22/92] Delete modules/verizonMediaSystemId.md --- modules/verizonMediaSystemId.md | 33 --------------------------------- 1 file changed, 33 deletions(-) delete mode 100644 modules/verizonMediaSystemId.md diff --git a/modules/verizonMediaSystemId.md b/modules/verizonMediaSystemId.md deleted file mode 100644 index c0d315dc754..00000000000 --- a/modules/verizonMediaSystemId.md +++ /dev/null @@ -1,33 +0,0 @@ -## Verizon Media User ID Submodule - -Verizon Media User ID Module. - -### Prebid Params - -``` -pbjs.setConfig({ - userSync: { - userIds: [{ - name: 'verizonMediaId', - storage: { - name: 'vmcid', - type: 'html5', - expires: 15 - }, - params: { - pixelId: 58776, - he: '0bef996248d63cea1529cb86de31e9547a712d9f380146e98bbd39beec70355a' - } - }] - } -}); -``` -## Parameter Descriptions for the `usersync` Configuration Section -The below parameters apply only to the Verizon Media User ID Module integration. - -| Param under usersync.userIds[] | Scope | Type | Description | Example | -| --- | --- | --- | --- | --- | -| name | Required | String | ID value for the Verizon Media module - `"verizonMediaId"` | `"verizonMediaId"` | -| params | Required | Object | Data for Verizon Media ID initialization. | | -| params.pixelId | Required | Number | The Verizon Media supplied publisher specific pixel Id | `8976` | -| params.he | Required | String | The SHA-256 hashed user email address | `"529cb86de31e9547a712d9f380146e98bbd39beec"` | From 2e66555284a8d3dbff293e4f07c79e426e4870b3 Mon Sep 17 00:00:00 2001 From: Patrick McCann Date: Thu, 27 Mar 2025 09:55:40 -0400 Subject: [PATCH 23/92] Delete test/spec/modules/verizonMediaIdSystem_spec.js --- .../spec/modules/verizonMediaIdSystem_spec.js | 204 ------------------ 1 file changed, 204 deletions(-) delete mode 100644 test/spec/modules/verizonMediaIdSystem_spec.js diff --git a/test/spec/modules/verizonMediaIdSystem_spec.js b/test/spec/modules/verizonMediaIdSystem_spec.js deleted file mode 100644 index 623097b48ce..00000000000 --- a/test/spec/modules/verizonMediaIdSystem_spec.js +++ /dev/null @@ -1,204 +0,0 @@ -import {expect} from 'chai'; -import * as utils from 'src/utils.js'; -import {verizonMediaIdSubmodule} from 'modules/verizonMediaIdSystem.js'; - -describe('Verizon Media ID Submodule', () => { - const HASHED_EMAIL = '6bda6f2fa268bf0438b5423a9861a2cedaa5dec163c03f743cfe05c08a8397b2'; - const PIXEL_ID = '1234'; - const PROD_ENDPOINT = `https://ups.analytics.yahoo.com/ups/${PIXEL_ID}/fed`; - const OVERRIDE_ENDPOINT = 'https://foo/bar'; - - it('should have the correct module name declared', () => { - expect(verizonMediaIdSubmodule.name).to.equal('verizonMediaId'); - }); - - it('should have the correct TCFv2 Vendor ID declared', () => { - expect(verizonMediaIdSubmodule.gvlid).to.equal(25); - }); - - describe('getId()', () => { - let ajaxStub; - let getAjaxFnStub; - let consentData; - beforeEach(() => { - ajaxStub = sinon.stub(); - getAjaxFnStub = sinon.stub(verizonMediaIdSubmodule, 'getAjaxFn'); - getAjaxFnStub.returns(ajaxStub); - - consentData = { - gdpr: { - gdprApplies: 1, - consentString: 'GDPR_CONSENT_STRING' - }, - usp: 'USP_CONSENT_STRING' - }; - }); - - afterEach(() => { - getAjaxFnStub.restore(); - }); - - function invokeGetIdAPI(configParams, consentData) { - let result = verizonMediaIdSubmodule.getId({ - params: configParams - }, consentData); - if (typeof result === 'object') { - result.callback(sinon.stub()); - } - return result; - } - - it('returns undefined if he and pixelId params are not passed', () => { - expect(invokeGetIdAPI({}, consentData)).to.be.undefined; - expect(ajaxStub.callCount).to.equal(0); - }); - - it('returns undefined if the pixelId param is not passed', () => { - expect(invokeGetIdAPI({ - he: HASHED_EMAIL - }, consentData)).to.be.undefined; - expect(ajaxStub.callCount).to.equal(0); - }); - - it('returns undefined if the he param is not passed', () => { - expect(invokeGetIdAPI({ - pixelId: PIXEL_ID - }, consentData)).to.be.undefined; - expect(ajaxStub.callCount).to.equal(0); - }); - - it('returns an object with the callback function if the correct params are passed', () => { - let result = invokeGetIdAPI({ - he: HASHED_EMAIL, - pixelId: PIXEL_ID - }, consentData); - expect(result).to.be.an('object').that.has.all.keys('callback'); - expect(result.callback).to.be.a('function'); - }); - - it('Makes an ajax GET request to the production API endpoint with query params', () => { - invokeGetIdAPI({ - he: HASHED_EMAIL, - pixelId: PIXEL_ID - }, consentData); - - const expectedParams = { - he: HASHED_EMAIL, - pixelId: PIXEL_ID, - '1p': '0', - gdpr: '1', - gdpr_consent: consentData.gdpr.consentString, - us_privacy: consentData.usp - }; - const requestQueryParams = utils.parseQS(ajaxStub.firstCall.args[0].split('?')[1]); - - expect(ajaxStub.firstCall.args[0].indexOf(`${PROD_ENDPOINT}?`)).to.equal(0); - expect(requestQueryParams).to.deep.equal(expectedParams); - expect(ajaxStub.firstCall.args[3]).to.deep.equal({method: 'GET', withCredentials: true}); - }); - - it('Makes an ajax GET request to the specified override API endpoint with query params', () => { - invokeGetIdAPI({ - he: HASHED_EMAIL, - endpoint: OVERRIDE_ENDPOINT - }, consentData); - - const expectedParams = { - he: HASHED_EMAIL, - '1p': '0', - gdpr: '1', - gdpr_consent: consentData.gdpr.consentString, - us_privacy: consentData.usp - }; - const requestQueryParams = utils.parseQS(ajaxStub.firstCall.args[0].split('?')[1]); - - expect(ajaxStub.firstCall.args[0].indexOf(`${OVERRIDE_ENDPOINT}?`)).to.equal(0); - expect(requestQueryParams).to.deep.equal(expectedParams); - expect(ajaxStub.firstCall.args[3]).to.deep.equal({method: 'GET', withCredentials: true}); - }); - - it('sets the callbacks param of the ajax function call correctly', () => { - invokeGetIdAPI({ - he: HASHED_EMAIL, - pixelId: PIXEL_ID, - }, consentData); - - expect(ajaxStub.firstCall.args[1]).to.be.an('object').that.has.all.keys(['success', 'error']); - }); - - it('sets GDPR consent data flag correctly when call is under GDPR jurisdiction.', () => { - invokeGetIdAPI({ - he: HASHED_EMAIL, - pixelId: PIXEL_ID, - }, consentData); - - const requestQueryParams = utils.parseQS(ajaxStub.firstCall.args[0].split('?')[1]); - expect(requestQueryParams.gdpr).to.equal('1'); - expect(requestQueryParams.gdpr_consent).to.equal(consentData.gdpr.consentString); - }); - - it('sets GDPR consent data flag correctly when call is NOT under GDPR jurisdiction.', () => { - consentData.gdpr.gdprApplies = false; - - invokeGetIdAPI({ - he: HASHED_EMAIL, - pixelId: PIXEL_ID, - }, consentData); - - const requestQueryParams = utils.parseQS(ajaxStub.firstCall.args[0].split('?')[1]); - expect(requestQueryParams.gdpr).to.equal('0'); - expect(requestQueryParams.gdpr_consent).to.equal(''); - }); - - [1, '1', true].forEach(firstPartyParamValue => { - it(`sets 1p payload property to '1' for a config value of ${firstPartyParamValue}`, () => { - invokeGetIdAPI({ - '1p': firstPartyParamValue, - he: HASHED_EMAIL, - pixelId: PIXEL_ID, - }, consentData); - - const requestQueryParams = utils.parseQS(ajaxStub.firstCall.args[0].split('?')[1]); - expect(requestQueryParams['1p']).to.equal('1'); - }); - }); - }); - - describe('decode()', () => { - const VALID_API_RESPONSES = [{ - key: 'vmiud', - expected: '1234', - payload: { - vmuid: '1234' - } - }, - { - key: 'connectid', - expected: '4567', - payload: { - connectid: '4567' - } - }, - { - key: 'both', - expected: '4567', - payload: { - vmuid: '1234', - connectid: '4567' - } - }]; - VALID_API_RESPONSES.forEach(responseData => { - it('should return a newly constructed object with the connectid for a payload with ${responseData.key} key(s)', () => { - expect(verizonMediaIdSubmodule.decode(responseData.payload)).to.deep.equal( - {connectid: responseData.expected} - ); - }); - }); - - [{}, '', {foo: 'bar'}].forEach((response) => { - it(`should return undefined for an invalid response "${JSON.stringify(response)}"`, () => { - expect(verizonMediaIdSubmodule.decode(response)).to.be.undefined; - }); - }); - }); -}); From eb082e6c54c231a34a562e695d5fcb1939f9c634 Mon Sep 17 00:00:00 2001 From: Patrick McCann Date: Thu, 27 Mar 2025 10:08:21 -0400 Subject: [PATCH 24/92] Delete test/spec/modules/vubleAnalyticsAdapter_spec.js --- test/spec/modules/vubleAnalyticsAdapter_spec.js | 0 1 file changed, 0 insertions(+), 0 deletions(-) delete mode 100644 test/spec/modules/vubleAnalyticsAdapter_spec.js diff --git a/test/spec/modules/vubleAnalyticsAdapter_spec.js b/test/spec/modules/vubleAnalyticsAdapter_spec.js deleted file mode 100644 index e69de29bb2d..00000000000 From 4e446c350b67fad6489e2a3ce5c6cb027e21608b Mon Sep 17 00:00:00 2001 From: Patrick McCann Date: Thu, 27 Mar 2025 10:09:44 -0400 Subject: [PATCH 25/92] Delete test/spec/modules/serverbidServerBidAdapter_spec.js --- test/spec/modules/serverbidServerBidAdapter_spec.js | 0 1 file changed, 0 insertions(+), 0 deletions(-) delete mode 100644 test/spec/modules/serverbidServerBidAdapter_spec.js diff --git a/test/spec/modules/serverbidServerBidAdapter_spec.js b/test/spec/modules/serverbidServerBidAdapter_spec.js deleted file mode 100644 index e69de29bb2d..00000000000 From 395ab61fa4cf9ed30154fc0e1654284d3431aefd Mon Sep 17 00:00:00 2001 From: Patrick McCann Date: Thu, 27 Mar 2025 10:10:41 -0400 Subject: [PATCH 26/92] Delete integrationExamples/gpt/serverbidServer_example.html --- .../gpt/serverbidServer_example.html | 101 ------------------ 1 file changed, 101 deletions(-) delete mode 100644 integrationExamples/gpt/serverbidServer_example.html diff --git a/integrationExamples/gpt/serverbidServer_example.html b/integrationExamples/gpt/serverbidServer_example.html deleted file mode 100644 index 1bd9b39d999..00000000000 --- a/integrationExamples/gpt/serverbidServer_example.html +++ /dev/null @@ -1,101 +0,0 @@ - - - - - - - - -

Prebid.js S2S Example

- -
Div-1
-
- -
- - From bccb0a2ed466ab384ee5f6edaeded087569a03e5 Mon Sep 17 00:00:00 2001 From: suzuki <70867995+logly-suzuki@users.noreply.github.com> Date: Mon, 31 Mar 2025 22:40:09 +0900 Subject: [PATCH 27/92] remove loglylift adapter (#12897) --- modules/loglyliftBidAdapter.js | 85 ------ modules/loglyliftBidAdapter.md | 71 ----- test/spec/modules/loglyliftBidAdapter_spec.js | 260 ------------------ 3 files changed, 416 deletions(-) delete mode 100644 modules/loglyliftBidAdapter.js delete mode 100644 modules/loglyliftBidAdapter.md delete mode 100644 test/spec/modules/loglyliftBidAdapter_spec.js diff --git a/modules/loglyliftBidAdapter.js b/modules/loglyliftBidAdapter.js deleted file mode 100644 index 7cd76bb719d..00000000000 --- a/modules/loglyliftBidAdapter.js +++ /dev/null @@ -1,85 +0,0 @@ -import { config } from '../src/config.js'; -import { registerBidder } from '../src/adapters/bidderFactory.js'; -import { BANNER, NATIVE } from '../src/mediaTypes.js'; -import { convertOrtbRequestToProprietaryNative } from '../src/native.js'; - -const BIDDER_CODE = 'loglylift'; -const ENDPOINT_URL = 'https://bid.logly.co.jp/prebid/client/v1'; - -export const spec = { - code: BIDDER_CODE, - supportedMediaTypes: [BANNER, NATIVE], - - isBidRequestValid: function (bid) { - return !!(bid.params && bid.params.adspotId); - }, - - buildRequests: function (bidRequests, bidderRequest) { - // convert Native ORTB definition to old-style prebid native definition - bidRequests = convertOrtbRequestToProprietaryNative(bidRequests); - - const requests = []; - for (let i = 0, len = bidRequests.length; i < len; i++) { - const request = { - method: 'POST', - url: ENDPOINT_URL + '?adspot_id=' + bidRequests[i].params.adspotId, - data: JSON.stringify(newBidRequest(bidRequests[i], bidderRequest)), - options: {}, - bidderRequest - }; - requests.push(request); - } - return requests; - }, - - interpretResponse: function (serverResponse, { bidderRequest }) { - serverResponse = serverResponse.body; - const bidResponses = []; - if (!serverResponse || serverResponse.error) { - return bidResponses; - } - serverResponse.bids.forEach(function (bid) { - bidResponses.push(bid); - }) - return bidResponses; - }, - - getUserSyncs: function (syncOptions, serverResponses) { - const syncs = []; - - // sync if mediaType is native because not native ad itself has a function for sync - if (syncOptions.iframeEnabled && serverResponses.length > 0 && serverResponses[0].body.bids[0].native) { - syncs.push({ - type: 'iframe', - url: 'https://sync.logly.co.jp/sync/sync.html' - }); - } - return syncs; - } - -}; - -function newBidRequest(bid, bidderRequest) { - const currencyObj = config.getConfig('currency'); - const currency = (currencyObj && currencyObj.adServerCurrency) || 'USD'; - - return { - // TODO: fix auctionId leak: https://github.com/prebid/Prebid.js/issues/9781 - auctionId: bid.auctionId, - bidderRequestId: bid.bidderRequestId, - transactionId: bid.ortb2Imp?.ext?.tid, - adUnitCode: bid.adUnitCode, - bidId: bid.bidId, - mediaTypes: bid.mediaTypes, - params: bid.params, - prebidJsVersion: '$prebid.version$', - url: window.location.href, - domain: bidderRequest.refererInfo.domain, - referer: bidderRequest.refererInfo.page, - auctionStartTime: bidderRequest.auctionStart, - currency: currency, - timeout: config.getConfig('bidderTimeout') - }; -} - -registerBidder(spec); diff --git a/modules/loglyliftBidAdapter.md b/modules/loglyliftBidAdapter.md deleted file mode 100644 index 5505d66957d..00000000000 --- a/modules/loglyliftBidAdapter.md +++ /dev/null @@ -1,71 +0,0 @@ -# Overview -``` -Module Name: LOGLY lift for Publisher -Module Type: Bidder Adapter -Maintainer: dev@logly.co.jp -``` - -# Description -Module that connects to Logly's demand sources. -Currently module supports only native mediaType. - -# Test Parameters -``` -var adUnits = [ - // Banner adUnit - { - code: 'test-banner-code', - sizes: [[300, 250], [300, 600]], - mediaTypes: { - banner: { - sizes: [[300, 250], [300, 600]] - } - }, - bids: [{ - bidder: 'loglylift', - params: { - adspotId: 1302078 - } - }] - }, - // Native adUnit - { - code: 'test-native-code', - sizes: [[1, 1]], - mediaTypes: { - native: { - title: { - required: true - }, - image: { - required: true - }, - sponsoredBy: { - required: true - } - } - }, - bids: [{ - bidder: 'loglylift', - params: { - adspotId: 4302078 - } - }] - } -]; -``` - -# UserSync example - -``` -pbjs.setConfig({ - userSync: { - filterSettings: { - iframe: { - bidders: '*', // '*' represents all bidders - filter: 'include' - } - } - } -}); -``` diff --git a/test/spec/modules/loglyliftBidAdapter_spec.js b/test/spec/modules/loglyliftBidAdapter_spec.js deleted file mode 100644 index 9805561442a..00000000000 --- a/test/spec/modules/loglyliftBidAdapter_spec.js +++ /dev/null @@ -1,260 +0,0 @@ -import { expect } from 'chai'; -import { spec } from '../../../modules/loglyliftBidAdapter'; -import * as utils from 'src/utils.js'; - -describe('loglyliftBidAdapter', function () { - const bannerBidRequests = [{ - bidder: 'loglylift', - bidId: '51ef8751f9aead', - params: { - adspotId: 16 - }, - adUnitCode: '/19968336/prebid_native_example_1', - transactionId: '10aee457-617c-4572-ab5b-99df1d73ccb4', - ortb2Imp: { - ext: { - tid: '10aee457-617c-4572-ab5b-99df1d73ccb4', - } - }, - sizes: [[300, 250], [300, 600]], - bidderRequestId: '15da3afd9632d7', - auctionId: 'f890b7d9-e787-4237-ac21-6d8554abac9f', - mediaTypes: { - banner: { - sizes: [[300, 250], [300, 600]] - } - } - }]; - - const nativeBidRequests = [{ - bidder: 'loglylift', - bidId: '254304ac29e265', - params: { - adspotId: 16 - }, - adUnitCode: '/19968336/prebid_native_example_1', - transactionId: '10aee457-617c-4572-ab5b-99df1d73ccb4', - ortb2Imp: { - ext: { - tid: '10aee457-617c-4572-ab5b-99df1d73ccb4', - } - }, - sizes: [ - [] - ], - bidderRequestId: '15da3afd9632d7', - auctionId: 'f890b7d9-e787-4237-ac21-6d8554abac9f', - mediaTypes: { - native: { - body: { - required: true - }, - icon: { - required: false - }, - title: { - required: true - }, - image: { - required: true - }, - sponsoredBy: { - required: true - }, - cta: { - required: true - }, - privacyLink: { - required: true - } - } - } - }]; - - const bidderRequest = { - refererInfo: { - domain: 'domain', - page: 'fakeReferer', - reachedTop: true, - numIframes: 1, - stack: [] - }, - auctionStart: 1632194172781, - bidderCode: 'loglylift', - bidderRequestId: '15da3afd9632d7', - auctionId: 'f890b7d9-e787-4237-ac21-6d8554abac9f', - timeout: 3000 - }; - - const bannerServerResponse = { - body: { - bids: [{ - requestId: '51ef8751f9aead', - cpm: 101.0234, - width: 300, - height: 250, - creativeId: '16', - currency: 'JPY', - netRevenue: true, - ttl: 60, - meta: { - advertiserDomains: ['advertiserexample.com'] - }, - ad: '
TEST
', - }] - } - }; - - const nativeServerResponse = { - body: { - bids: [{ - requestId: '254304ac29e265', - cpm: 10.123, - width: 360, - height: 360, - creativeId: '123456789', - currency: 'JPY', - netRevenue: true, - ttl: 30, - meta: { - advertiserDomains: ['advertiserexample.com'] - }, - native: { - clickUrl: 'https://dsp.logly.co.jp/click?ad=EXAMPECLICKURL', - image: { - url: 'https://cdn.logly.co.jp/images/000/194/300/normal.jpg', - width: '360', - height: '360' - }, - impressionTrackers: [ - 'https://b.logly.co.jp/sorry.html' - ], - sponsoredBy: 'logly', - title: 'Native Title', - privacyLink: 'https://www.logly.co.jp/optout.html', - cta: '詳細はこちら', - } - }], - } - }; - - describe('isBidRequestValid', function () { - [nativeBidRequests, bannerBidRequests].forEach(bidRequests => { - it('should return true if the adspotId parameter is present', function () { - expect(spec.isBidRequestValid(bidRequests[0])).to.be.true; - }); - - it('should return false if the adspotId parameter is not present', function () { - let bidRequest = utils.deepClone(bidRequests[0]); - delete bidRequest.params.adspotId; - expect(spec.isBidRequestValid(bidRequest)).to.be.false; - }); - }); - }); - - describe('buildRequests', function () { - [nativeBidRequests, bannerBidRequests].forEach(bidRequests => { - it('should generate a valid single POST request for multiple bid requests', function () { - const request = spec.buildRequests(bidRequests, bidderRequest)[0]; - expect(request.method).to.equal('POST'); - expect(request.url).to.equal('https://bid.logly.co.jp/prebid/client/v1?adspot_id=16'); - expect(request.data).to.exist; - - const data = JSON.parse(request.data); - expect(data.auctionId).to.equal(bidRequests[0].auctionId); - expect(data.bidderRequestId).to.equal(bidRequests[0].bidderRequestId); - expect(data.transactionId).to.equal(bidRequests[0].transactionId); - expect(data.adUnitCode).to.equal(bidRequests[0].adUnitCode); - expect(data.bidId).to.equal(bidRequests[0].bidId); - expect(data.mediaTypes).to.deep.equal(bidRequests[0].mediaTypes); - expect(data.params).to.deep.equal(bidRequests[0].params); - expect(data.prebidJsVersion).to.equal('$prebid.version$'); - expect(data.url).to.exist; - expect(data.domain).to.exist; - expect(data.referer).to.equal(bidderRequest.refererInfo.page); - expect(data.auctionStartTime).to.equal(bidderRequest.auctionStart); - expect(data.currency).to.exist; - expect(data.timeout).to.equal(bidderRequest.timeout); - }); - }); - }); - - describe('interpretResponse', function () { - it('should return an empty array if an invalid response is passed', function () { - const interpretedResponse = spec.interpretResponse({}, {}); - expect(interpretedResponse).to.be.an('array').that.is.empty; - }); - - describe('nativeServerResponse', function () { - it('should return valid response when passed valid server response', function () { - const request = spec.buildRequests(nativeBidRequests, bidderRequest)[0]; - const interpretedResponse = spec.interpretResponse(nativeServerResponse, request); - - expect(interpretedResponse).to.have.lengthOf(1); - expect(interpretedResponse[0].cpm).to.equal(nativeServerResponse.body.bids[0].cpm); - expect(interpretedResponse[0].width).to.equal(nativeServerResponse.body.bids[0].width); - expect(interpretedResponse[0].height).to.equal(nativeServerResponse.body.bids[0].height); - expect(interpretedResponse[0].creativeId).to.equal(nativeServerResponse.body.bids[0].creativeId); - expect(interpretedResponse[0].currency).to.equal(nativeServerResponse.body.bids[0].currency); - expect(interpretedResponse[0].netRevenue).to.equal(nativeServerResponse.body.bids[0].netRevenue); - expect(interpretedResponse[0].ttl).to.equal(nativeServerResponse.body.bids[0].ttl); - expect(interpretedResponse[0].native).to.deep.equal(nativeServerResponse.body.bids[0].native); - expect(interpretedResponse[0].meta.advertiserDomains[0]).to.equal(nativeServerResponse.body.bids[0].meta.advertiserDomains[0]); - }); - }); - - describe('bannerServerResponse', function () { - it('should return valid response when passed valid server response', function () { - const request = spec.buildRequests(bannerBidRequests, bidderRequest)[0]; - const interpretedResponse = spec.interpretResponse(bannerServerResponse, request); - - expect(interpretedResponse).to.have.lengthOf(1); - expect(interpretedResponse[0].cpm).to.equal(bannerServerResponse.body.bids[0].cpm); - expect(interpretedResponse[0].width).to.equal(bannerServerResponse.body.bids[0].width); - expect(interpretedResponse[0].height).to.equal(bannerServerResponse.body.bids[0].height); - expect(interpretedResponse[0].creativeId).to.equal(bannerServerResponse.body.bids[0].creativeId); - expect(interpretedResponse[0].currency).to.equal(bannerServerResponse.body.bids[0].currency); - expect(interpretedResponse[0].netRevenue).to.equal(bannerServerResponse.body.bids[0].netRevenue); - expect(interpretedResponse[0].ttl).to.equal(bannerServerResponse.body.bids[0].ttl); - expect(interpretedResponse[0].ad).to.equal(bannerServerResponse.body.bids[0].ad); - expect(interpretedResponse[0].meta.advertiserDomains[0]).to.equal(bannerServerResponse.body.bids[0].meta.advertiserDomains[0]); - }); - }); - }); - - describe('getUserSync tests', function () { - it('UserSync test : check type = iframe, check usermatch URL', function () { - const syncOptions = { - 'iframeEnabled': true - } - let userSync = spec.getUserSyncs(syncOptions, [nativeServerResponse]); - expect(userSync[0].type).to.equal('iframe'); - const USER_SYNC_URL = 'https://sync.logly.co.jp/sync/sync.html'; - expect(userSync[0].url).to.equal(USER_SYNC_URL); - }); - - it('When iframeEnabled is false, no userSync should be returned', function () { - const syncOptions = { - 'iframeEnabled': false - } - let userSync = spec.getUserSyncs(syncOptions, [nativeServerResponse]); - expect(userSync).to.be.an('array').that.is.empty; - }); - - it('When serverResponses empty, no userSync should be returned', function () { - const syncOptions = { - 'iframeEnabled': true - } - let userSync = spec.getUserSyncs(syncOptions, []); - expect(userSync).to.be.an('array').that.is.empty; - }); - - it('When mediaType is banner, no userSync should be returned', function () { - const syncOptions = { - 'iframeEnabled': true - } - let userSync = spec.getUserSyncs(syncOptions, [bannerServerResponse]); - expect(userSync).to.be.an('array').that.is.empty; - }); - }); -}); From a367d8c1acd2d873604c595cf255b3119fd76d9e Mon Sep 17 00:00:00 2001 From: Patrick McCann Date: Tue, 1 Apr 2025 12:59:46 -0400 Subject: [PATCH 28/92] Delete modules/yieldmoSyntheticInventoryModule.js Yieldmo told me it is superceded and no longer in use --- modules/yieldmoSyntheticInventoryModule.js | 46 ---------------------- 1 file changed, 46 deletions(-) delete mode 100644 modules/yieldmoSyntheticInventoryModule.js diff --git a/modules/yieldmoSyntheticInventoryModule.js b/modules/yieldmoSyntheticInventoryModule.js deleted file mode 100644 index bca778a7b43..00000000000 --- a/modules/yieldmoSyntheticInventoryModule.js +++ /dev/null @@ -1,46 +0,0 @@ -import { config } from '../src/config.js'; -import { isGptPubadsDefined } from '../src/utils.js'; - -export const MODULE_NAME = 'Yieldmo Synthetic Inventory Module'; - -export function init(config) { - validateConfig(config); - - if (!isGptPubadsDefined()) { - window.googletag = window.googletag || {}; - window.googletag.cmd = window.googletag.cmd || []; - } - - const googletag = window.googletag; - const containerName = 'ym_sim_container_' + config.placementId; - - googletag.cmd.push(() => { - if (window.document.body) { - googletagCmd(config, containerName, googletag); - } else { - window.document.addEventListener('DOMContentLoaded', () => googletagCmd(config, containerName, googletag)); - } - }); -} - -export function validateConfig(config) { - if (!('placementId' in config)) { - throw new Error(`${MODULE_NAME}: placementId required`); - } - if (!('adUnitPath' in config)) { - throw new Error(`${MODULE_NAME}: adUnitPath required`); - } -} - -function googletagCmd(config, containerName, googletag) { - const gamContainer = window.document.createElement('div'); - gamContainer.id = containerName; - window.document.body.appendChild(gamContainer); - googletag.defineSlot(config.adUnitPath, [1, 1], containerName) - .addService(googletag.pubads()) - .setTargeting('ym_sim_p_id', config.placementId); - googletag.enableServices(); - googletag.display(containerName); -} - -config.getConfig('yieldmo_synthetic_inventory', config => init(config.yieldmo_synthetic_inventory)); From c081442596b7f8c5190a5ede7820f5866c908888 Mon Sep 17 00:00:00 2001 From: Patrick McCann Date: Tue, 1 Apr 2025 13:00:07 -0400 Subject: [PATCH 29/92] Delete modules/yieldmoSyntheticInventoryModule.md --- modules/yieldmoSyntheticInventoryModule.md | 68 ---------------------- 1 file changed, 68 deletions(-) delete mode 100644 modules/yieldmoSyntheticInventoryModule.md diff --git a/modules/yieldmoSyntheticInventoryModule.md b/modules/yieldmoSyntheticInventoryModule.md deleted file mode 100644 index dd6f0acf884..00000000000 --- a/modules/yieldmoSyntheticInventoryModule.md +++ /dev/null @@ -1,68 +0,0 @@ -# Yieldmo Synthetic Inventory Module - -## Overview - -This module enables publishers to set up Yieldmo Synthetic Outstream ads on their pages. - -If publishers will enable this module and provide placementId and Google Ad Manager ad unit path, this module will create a placement on the page and inject Yieldmo SDK into this placement. Publisher will then need to get a placement id from their Yieldmo account manager (accounts email) and setup corresponding ad units on the GAM ad server. - -## Integration - -Build the Yieldmo Synthetic Inventory Module into the Prebid.js package with: - -``` -gulp build --modules=yieldmoSyntheticInventoryModule,... -``` - -## Module Configuration - -```js -pbjs.que.push(function() { - pbjs.setConfig({ - yieldmo_synthetic_inventory: { - placementId: '1234567890', - adUnitPath: '/1234567/ad_unit_name_used_in_gam' - } - }); -}); -``` - -### Configuration Parameters - -|Name |Scope |Description | Example| Type -| :------------ | :------------ | :------------ | :------------ | :------------ | -|placementId | required | Yieldmo placement ID | '1234567890' | string -|adUnitPath | required | Google Ad Manager ad unit path | '/6355419/ad_unit_name_used_in_gam' | string - -### How to get ad unit path - -Ad unit path follows the format /network-code/[parent-ad-unit-code/.../]ad-unit-code, where: - -- network-code is a unique identifier for the Ad Manager network the ad unit belongs to -- parent-ad-unit-code are the codes of all parent ad units (only applies to non-top level ad units) -- ad-unit-code is the code for the ad unit to be displayed - -Note that all ad unit codes included in the ad unit path must adhere to the [formatting rules](https://support.google.com/admanager/answer/1628457#ad-unit-codes) specified by Ad Manager. - -Another and probably the easiest way to get an ad unit path is to get it from the google ad manager ad unit document header generated tag: - -```js -googletag.defineSlot('/1234567/ad_unit_name_used_in_gam', [1, 1], 'ad-container-id').addService(googletag.pubads()); -``` - -### How to get Yieldmo placement id - -Please reach out to your Yieldmo account's person or email to support@yieldmo.com - -### Google Ad Manager setup - -Yieldmo Synthetic Inventory Module is designed to be used along with Google Ad Manager. GAM should be set as usual, but there are a few requirements: - -- Ad unit size should be 1x1 -- Creative should NOT be served into a SafeFrame and also should have 1x1 size -- Synthetic Inventory Universal Tag should be used as 3rd party creative code -### Synthetic Inventory Universal Tag - -```js -
-``` \ No newline at end of file From 306eee13e01548ac077cb8c691aed95adc34a8ba Mon Sep 17 00:00:00 2001 From: Patrick McCann Date: Tue, 1 Apr 2025 13:00:21 -0400 Subject: [PATCH 30/92] Delete test/spec/modules/yieldmoSyntheticInventoryModule_spec.js --- .../yieldmoSyntheticInventoryModule_spec.js | 89 ------------------- 1 file changed, 89 deletions(-) delete mode 100644 test/spec/modules/yieldmoSyntheticInventoryModule_spec.js diff --git a/test/spec/modules/yieldmoSyntheticInventoryModule_spec.js b/test/spec/modules/yieldmoSyntheticInventoryModule_spec.js deleted file mode 100644 index 55b4e7255f7..00000000000 --- a/test/spec/modules/yieldmoSyntheticInventoryModule_spec.js +++ /dev/null @@ -1,89 +0,0 @@ -import { expect } from 'chai'; -import { - init, - MODULE_NAME, - validateConfig -} from 'modules/yieldmoSyntheticInventoryModule'; - -const mockedYmConfig = { - placementId: '123456', - adUnitPath: '/6355419/ad_unit_name_used_in_gam' -}; - -const setGoogletag = () => { - window.googletag = { - cmd: [], - defineSlot: sinon.stub(), - addService: sinon.stub(), - pubads: sinon.stub(), - setTargeting: sinon.stub(), - enableServices: sinon.stub(), - display: sinon.stub(), - }; - window.googletag.defineSlot.returns(window.googletag); - window.googletag.addService.returns(window.googletag); - window.googletag.pubads.returns({getSlots: sinon.stub()}); - return window.googletag; -} - -describe('Yieldmo Synthetic Inventory Module', function() { - let config = Object.assign({}, mockedYmConfig); - let googletagBkp; - - beforeEach(function () { - googletagBkp = window.googletag; - delete window.googletag; - }); - - afterEach(function () { - window.googletag = googletagBkp; - }); - - it('should be enabled with valid required params', function() { - expect(function () { - init(mockedYmConfig); - }).not.to.throw() - }); - - it('should throw an error if placementId is missed', function() { - const {placementId, ...config} = mockedYmConfig; - - expect(function () { - validateConfig(config); - }).throw(`${MODULE_NAME}: placementId required`) - }); - - it('should throw an error if adUnitPath is missed', function() { - const {adUnitPath, ...config} = mockedYmConfig; - - expect(function () { - validateConfig(config); - }).throw(`${MODULE_NAME}: adUnitPath required`) - }); - - it('should add correct googletag.cmd', function() { - const containerName = 'ym_sim_container_' + mockedYmConfig.placementId; - const gtag = setGoogletag(); - - init(mockedYmConfig); - - expect(gtag.cmd.length).to.equal(1); - - gtag.cmd[0](); - - expect(gtag.addService.getCall(0)).to.not.be.null; - expect(gtag.setTargeting.getCall(0)).to.not.be.null; - expect(gtag.setTargeting.getCall(0).args[0]).to.exist.and.to.equal('ym_sim_p_id'); - expect(gtag.setTargeting.getCall(0).args[1]).to.exist.and.to.equal(mockedYmConfig.placementId); - expect(gtag.defineSlot.getCall(0)).to.not.be.null; - expect(gtag.enableServices.getCall(0)).to.not.be.null; - expect(gtag.display.getCall(0)).to.not.be.null; - expect(gtag.display.getCall(0).args[0]).to.exist.and.to.equal(containerName); - expect(gtag.pubads.getCall(0)).to.not.be.null; - - const gamContainerEl = window.document.getElementById(containerName); - expect(gamContainerEl).to.not.be.null; - - gamContainerEl.parentNode.removeChild(gamContainerEl); - }); -}); From bf2dff54be7da269007cb2477d87b63dc84b4fcc Mon Sep 17 00:00:00 2001 From: Komal Kumari <169047654+pm-komal-kumari@users.noreply.github.com> Date: Fri, 11 Apr 2025 00:04:50 +0530 Subject: [PATCH 31/92] Core: Remove createBid API and update getHighestUnusedBidResponseForAdUnitCode function (#12986) * getHighestUnusedBidResponseForAdUnitCode function to return null if no bid found * Removed public API pbjs.createBid() --------- Co-authored-by: Komal Kumari --- src/prebid.js | 16 ++-------------- test/spec/unit/pbjs_api_spec.js | 17 ++--------------- 2 files changed, 4 insertions(+), 29 deletions(-) diff --git a/src/prebid.js b/src/prebid.js index e798269fe2c..91a2a71df85 100644 --- a/src/prebid.js +++ b/src/prebid.js @@ -31,7 +31,6 @@ import {isBidUsable, targeting} from './targeting.js'; import {hook, wrapHook} from './hook.js'; import {loadSession} from './debugging.js'; import {includes} from './polyfill.js'; -import {createBid} from './bidfactory.js'; import {storageCallbacks} from './storageManager.js'; import {default as adapterManager, getS2SBidderSet} from './adapterManager.js'; import { BID_STATUS, EVENTS, NATIVE_KEYS } from './constants.js'; @@ -383,14 +382,14 @@ pbjsInstance.getAdserverTargetingForAdUnitCodeStr = function (adunitCode) { * This function returns the query string targeting parameters available at this moment for a given ad unit. Note that some bidder's response may not have been received if you call this function too quickly after the requests are sent. * @param adunitCode {string} adUnitCode to get the bid responses for * @alias module:pbjs.getHighestUnusedBidResponseForAdUnitCode - * @returns {Object} returnObj return bid + * @returns {Object|null} returnObj return bid or null if no suitable bid exists */ pbjsInstance.getHighestUnusedBidResponseForAdUnitCode = function (adunitCode) { if (adunitCode) { const bid = auctionManager.getAllBidsForAdUnitCode(adunitCode) .filter(isBidUsable) - return bid.length ? bid.reduce(getHighestCpm) : {} + return bid.length ? bid.reduce(getHighestCpm) : null } else { logMessage('Need to call getHighestUnusedBidResponseForAdUnitCode with adunitCode'); } @@ -828,17 +827,6 @@ pbjsInstance.registerAnalyticsAdapter = function (options) { } }; -/** - * Wrapper to bidfactory.createBid() - * @param {string} statusCode [description] - * @alias module:pbjs.createBid - * @return {Object} bidResponse [description] - */ -pbjsInstance.createBid = function (statusCode) { - logInfo('Invoking $$PREBID_GLOBAL$$.createBid', arguments); - return createBid(statusCode); -}; - /** * Enable sending analytics data to the analytics provider of your * choice. diff --git a/test/spec/unit/pbjs_api_spec.js b/test/spec/unit/pbjs_api_spec.js index 0536a3e90d0..d939f9de02e 100644 --- a/test/spec/unit/pbjs_api_spec.js +++ b/test/spec/unit/pbjs_api_spec.js @@ -3371,19 +3371,6 @@ describe('Unit: Prebid Module', function () { }); }); - describe('createBid', function () { - it('should return a bid object', function () { - const statusCode = 1; - const bid = $$PREBID_GLOBAL$$.createBid(statusCode); - assert.isObject(bid, 'bid is an object'); - assert.equal(bid.getStatusCode(), statusCode, 'bid has correct status'); - - const defaultStatusBid = $$PREBID_GLOBAL$$.createBid(); - assert.isObject(defaultStatusBid, 'bid is an object'); - assert.equal(defaultStatusBid.getStatusCode(), 0, 'bid has correct status'); - }); - }); - describe('aliasBidder', function () { it('should call adapterManager.aliasBidder', function () { const aliasBidAdapterSpy = sinon.spy(adapterManager, 'aliasBidAdapter'); @@ -3625,9 +3612,9 @@ describe('Unit: Prebid Module', function () { resetAuction(); }) - it('returns an empty object if there is no bid for the given adUnitCode', () => { + it('returns null if there is no bid for the given adUnitCode', () => { const highestBid = $$PREBID_GLOBAL$$.getHighestUnusedBidResponseForAdUnitCode('stallone'); - expect(highestBid).to.deep.equal({}); + expect(highestBid).to.equal(null); }) it('returns undefined if adUnitCode is provided', () => { From beaeb52d0a87dbd6a5a7e57b07f736537534a03d Mon Sep 17 00:00:00 2001 From: Patrick McCann Date: Fri, 11 Apr 2025 09:36:28 -0400 Subject: [PATCH 32/92] Delete modules/adoceanBidAdapter.js --- modules/adoceanBidAdapter.js | 169 ----------------------------------- 1 file changed, 169 deletions(-) delete mode 100644 modules/adoceanBidAdapter.js diff --git a/modules/adoceanBidAdapter.js b/modules/adoceanBidAdapter.js deleted file mode 100644 index d74a78270b2..00000000000 --- a/modules/adoceanBidAdapter.js +++ /dev/null @@ -1,169 +0,0 @@ -import { _each, parseSizesInput, isStr, isArray } from '../src/utils.js'; -import { registerBidder } from '../src/adapters/bidderFactory.js'; - -const BIDDER_CODE = 'adocean'; -const URL_SAFE_FIELDS = { - schain: true, - slaves: true -}; - -function buildEndpointUrl(emiter, payloadMap) { - const payload = []; - _each(payloadMap, function(v, k) { - payload.push(k + '=' + (URL_SAFE_FIELDS[k] ? v : encodeURIComponent(v))); - }); - - const randomizedPart = Math.random().toString().slice(2); - return 'https://' + emiter + '/_' + randomizedPart + '/ad.json?' + payload.join('&'); -} - -function buildRequest(masterBidRequests, masterId, gdprConsent) { - let emiter; - const payload = { - id: masterId, - aosspsizes: [], - slaves: [] - }; - if (gdprConsent) { - payload.gdpr_consent = gdprConsent.consentString || undefined; - payload.gdpr = gdprConsent.gdprApplies ? 1 : 0; - } - const anyKey = Object.keys(masterBidRequests)[0]; - if (masterBidRequests[anyKey].schain) { - payload.schain = serializeSupplyChain(masterBidRequests[anyKey].schain); - } - - const bidIdMap = {}; - const uniquePartLength = 10; - _each(masterBidRequests, function(bid, slaveId) { - if (!emiter) { - emiter = bid.params.emiter; - } - - const slaveSizes = parseSizesInput(bid.mediaTypes.banner.sizes).join('_'); - const rawSlaveId = bid.params.slaveId.replace('adocean', ''); - payload.aosspsizes.push(rawSlaveId + '~' + slaveSizes); - payload.slaves.push(rawSlaveId.slice(-uniquePartLength)); - - bidIdMap[slaveId] = bid.bidId; - }); - - payload.aosspsizes = payload.aosspsizes.join('-'); - payload.slaves = payload.slaves.join(','); - - return { - method: 'GET', - url: buildEndpointUrl(emiter, payload), - data: '', - bidIdMap: bidIdMap - }; -} - -const SCHAIN_FIELDS = ['asi', 'sid', 'hp', 'rid', 'name', 'domain', 'ext']; -function serializeSupplyChain(schain) { - const header = `${schain.ver},${schain.complete}!`; - - const serializedNodes = []; - _each(schain.nodes, function(node) { - const serializedNode = SCHAIN_FIELDS - .map(fieldName => { - if (fieldName === 'ext') { - // do not serialize ext data, just mark if it was available - return ('ext' in node ? '1' : '0'); - } - if (fieldName in node) { - return encodeURIComponent(node[fieldName]).replace(/!/g, '%21'); - } - return ''; - }) - .join(','); - serializedNodes.push(serializedNode); - }); - - return header + serializedNodes.join('!'); -} - -function assignToMaster(bidRequest, bidRequestsByMaster) { - const masterId = bidRequest.params.masterId; - const slaveId = bidRequest.params.slaveId; - const masterBidRequests = bidRequestsByMaster[masterId] = bidRequestsByMaster[masterId] || [{}]; - let i = 0; - while (masterBidRequests[i] && masterBidRequests[i][slaveId]) { - i++; - } - if (!masterBidRequests[i]) { - masterBidRequests[i] = {}; - } - masterBidRequests[i][slaveId] = bidRequest; -} - -function interpretResponse(placementResponse, bidRequest, bids) { - const requestId = bidRequest.bidIdMap[placementResponse.id]; - if (!placementResponse.error && requestId) { - let adCode = ' - + ' + adData.adm; - bidResponse.ad = adm; - bidResponse.mediaType = BANNER; - - bidResponses.push(bidResponse); - } - - return bidResponses; - }, - getBidderHost: function (bid) { - if (bid.bidder === 'adspirit') { - return utils.getBidIdParameter('host', bid.params); - } - if (bid.bidder === 'twiago') { - return 'a.twiago.com'; - } - return null; - }, - - genAdConId: function (bid) { - return bid.bidder + Math.round(Math.random() * 100000); - } -}; - -registerBidder(spec); +import * as utils from '../src/utils.js'; +import { registerBidder } from '../src/adapters/bidderFactory.js'; +import { BANNER, NATIVE } from '../src/mediaTypes.js'; +import { getGlobal } from '../src/prebidGlobal.js'; +const { getWinDimensions } = utils; +const RTB_URL = '/rtb/getbid.php?rtbprovider=prebid'; +const SCRIPT_URL = '/adasync.min.js'; + +export const spec = { + + code: 'adspirit', + aliases: ['twiago'], + supportedMediaTypes: [BANNER, NATIVE], + + isBidRequestValid: function (bid) { + let host = spec.getBidderHost(bid); + if (!host || !bid.params.placementId) { + return false; + } + return true; + }, + getScriptUrl: function () { + return SCRIPT_URL; + }, + buildRequests: function (validBidRequests, bidderRequest) { + let requests = []; + let prebidVersion = getGlobal().version; + const win = getWinDimensions(); + + for (let i = 0; i < validBidRequests.length; i++) { + let bidRequest = validBidRequests[i]; + bidRequest.adspiritConId = spec.genAdConId(bidRequest); + let reqUrl = spec.getBidderHost(bidRequest); + let placementId = utils.getBidIdParameter('placementId', bidRequest.params); + const eids = spec.getEids(bidRequest); + + reqUrl = '//' + reqUrl + RTB_URL + + '&pid=' + placementId + + '&ref=' + encodeURIComponent(bidderRequest.refererInfo.topmostLocation) + + '&scx=' + (win.screen?.width || 0) + + '&scy=' + (win.screen?.height || 0) + + '&wcx=' + win.innerWidth + + '&wcy=' + win.innerHeight + + '&async=' + bidRequest.adspiritConId + + '&t=' + Math.round(Math.random() * 100000); + + let gdprApplies = bidderRequest.gdprConsent ? (bidderRequest.gdprConsent.gdprApplies ? 1 : 0) : 0; + let gdprConsentString = bidderRequest.gdprConsent ? encodeURIComponent(bidderRequest.gdprConsent.consentString) : ''; + + if (bidderRequest.gdprConsent) { + reqUrl += '&gdpr=' + gdprApplies + '&gdpr_consent=' + gdprConsentString; + } + + let openRTBRequest = { + id: bidderRequest.auctionId, + at: 1, + cur: ['EUR'], + imp: [{ + id: bidRequest.bidId, + bidfloor: bidRequest.params.bidfloor !== undefined ? parseFloat(bidRequest.params.bidfloor) : 0, + bidfloorcur: 'EUR', + secure: 1, + banner: (bidRequest.mediaTypes.banner && bidRequest.mediaTypes.banner.sizes?.length > 0) ? { + format: bidRequest.mediaTypes.banner.sizes.map(size => ({ + w: size[0], + h: size[1] + })) + } : undefined, + native: (bidRequest.mediaTypes.native) ? { + request: JSON.stringify({ + ver: '1.2', + assets: bidRequest.mediaTypes.native.ortb?.assets?.length + ? bidRequest.mediaTypes.native.ortb.assets + : [ + { id: 1, required: 1, title: { len: 100 } }, + { id: 2, required: 1, img: { type: 3, wmin: 1200, hmin: 627, mimes: ['image/png', 'image/gif', 'image/jpeg'] } }, + { id: 4, required: 1, data: {type: 2, len: 150} }, + { id: 3, required: 0, data: {type: 12, len: 50} }, + { id: 6, required: 0, data: {type: 1, len: 50} }, + { id: 5, required: 0, img: { type: 1, wmin: 50, hmin: 50, mimes: ['image/png', 'image/gif', 'image/jpeg'] } } + + ] + }) + } : undefined, + ext: { + placementId: bidRequest.params.placementId + } + }], + + site: { + id: bidRequest.params.siteId || '', + domain: new URL(bidderRequest.refererInfo.topmostLocation).hostname, + page: bidderRequest.refererInfo.topmostLocation, + publisher: { + id: bidRequest.params.publisherId || '', + name: bidRequest.params.publisherName || '' + } + }, + user: { + data: bidRequest.userData || [], + ext: { + eids: eids, + consent: gdprConsentString || '' + } + }, + device: { + ua: navigator.userAgent, + language: (navigator.language || '').split('-')[0], + w: win.innerWidth, + h: win.innerHeight, + geo: { + lat: bidderRequest?.geo?.lat || 0, + lon: bidderRequest?.geo?.lon || 0, + country: bidderRequest?.geo?.country || '' + } + }, + regs: { + ext: { + gdpr: gdprApplies ? 1 : 0, + gdpr_consent: gdprConsentString || '' + } + }, + ext: { + oat: 1, + prebidVersion: prebidVersion, + adUnitCode: { + prebidVersion: prebidVersion, + code: bidRequest.adUnitCode, + mediaTypes: bidRequest.mediaTypes + } + } + }; + + + const schain = bidRequest?.ortb2?.source?.ext?.schain; + if (schain) { + openRTBRequest.source = { + ext: { + schain: schain + } + }; + } + requests.push({ + method: 'POST', + url: reqUrl, + data: JSON.stringify(openRTBRequest), + headers: { 'Content-Type': 'application/json' }, + bidRequest: bidRequest + }); + } + + return requests; + }, + getEids: function (bidRequest) { + return utils.deepAccess(bidRequest, 'userIdAsEids') || []; + }, + interpretResponse: function (serverResponse, bidRequest) { + const bidResponses = []; + const bidObj = bidRequest.bidRequest; + let host = spec.getBidderHost(bidObj); + + if (!serverResponse || !serverResponse.body) { + utils.logWarn(`adspirit: Empty response from bidder`); + return []; + } + + if (serverResponse.body.seatbid) { + serverResponse.body.seatbid.forEach(seat => { + seat.bid.forEach(bid => { + const bidResponse = { + requestId: bidObj.bidId, + cpm: bid.price, + width: bid.w || 1, + height: bid.h || 1, + creativeId: bid.crid || bid.impid, + currency: serverResponse.body.cur || 'EUR', + netRevenue: true, + ttl: bid.exp || 300, + meta: { + advertiserDomains: bid.adomain || [] + } + }; + + let adm = bid.adm; + if (typeof adm === 'string' && adm.trim().startsWith('{')) { + adm = JSON.parse(adm || '{}'); + if (typeof adm !== 'object') adm = null; + } + + if (adm?.native?.assets) { + const getAssetValue = (id, type) => { + const assetList = adm.native.assets.filter(a => a.id === id); + if (assetList.length === 0) return ''; + return assetList[0][type]?.text || assetList[0][type]?.value || assetList[0][type]?.url || ''; + }; + + const duplicateTracker = {}; + + bidResponse.native = { + title: getAssetValue(1, 'title'), + body: getAssetValue(4, 'data'), + cta: getAssetValue(3, 'data'), + image: { url: getAssetValue(2, 'img') || '' }, + icon: { url: getAssetValue(5, 'img') || '' }, + sponsoredBy: getAssetValue(6, 'data'), + clickUrl: adm.native.link?.url || '', + impressionTrackers: Array.isArray(adm.native.imptrackers) ? adm.native.imptrackers : [] + }; + + const predefinedAssetIds = Object.entries(bidResponse.native) + .filter(([key, value]) => key !== 'clickUrl' && key !== 'impressionTrackers') + .map(([key, value]) => adm.native.assets.find(asset => + typeof value === 'object' ? value.url === asset?.img?.url : value === asset?.data?.value + )?.id) + .filter(id => id !== undefined); + + adm.native.assets.forEach(asset => { + const type = Object.keys(asset).find(k => k !== 'id'); + + if (!duplicateTracker[asset.id]) { + duplicateTracker[asset.id] = 1; + } else { + duplicateTracker[asset.id]++; + } + + if (predefinedAssetIds.includes(asset.id) && duplicateTracker[asset.id] === 1) return; + + if (type && asset[type]) { + const value = asset[type].text || asset[type].value || asset[type].url || ''; + + if (type === 'img') { + bidResponse.native[`image_${asset.id}_extra${duplicateTracker[asset.id] - 1}`] = { + url: value, width: asset.img.w || null, height: asset.img.h || null + }; + } else { + bidResponse.native[`data_${asset.id}_extra${duplicateTracker[asset.id] - 1}`] = value; + } + } + }); + + bidResponse.mediaType = NATIVE; + } + + bidResponses.push(bidResponse); + }); + }); + } else { + let adData = serverResponse.body; + let cpm = adData.cpm; + + if (!cpm) return []; + const bidResponse = { + requestId: bidObj.bidId, + cpm: cpm, + width: adData.w, + height: adData.h, + creativeId: bidObj.params.placementId, + currency: 'EUR', + netRevenue: true, + ttl: 300, + meta: { + advertiserDomains: adData.adomain || [] + } + }; + let adm = '' + adData.adm; + bidResponse.ad = adm; + bidResponse.mediaType = BANNER; + + bidResponses.push(bidResponse); + } + + return bidResponses; + }, + getBidderHost: function (bid) { + if (bid.bidder === 'adspirit') { + return utils.getBidIdParameter('host', bid.params); + } + if (bid.bidder === 'twiago') { + return 'a.twiago.com'; + } + return null; + }, + + genAdConId: function (bid) { + return bid.bidder + Math.round(Math.random() * 100000); + } +}; + +registerBidder(spec); diff --git a/modules/adstirBidAdapter.js b/modules/adstirBidAdapter.js index a0c67ddac7e..6fadf632c0e 100644 --- a/modules/adstirBidAdapter.js +++ b/modules/adstirBidAdapter.js @@ -40,7 +40,7 @@ export const spec = { gdpr: utils.deepAccess(bidderRequest, 'gdprConsent.gdprApplies', false), usp: (bidderRequest.uspConsent || '1---') !== '1---', eids: utils.deepAccess(r, 'userIdAsEids', []), - schain: serializeSchain(utils.deepAccess(r, 'schain', null)), + schain: serializeSchain(utils.deepAccess(r, 'ortb2.source.ext.schain', null)), pbVersion: '$prebid.version$', }), } diff --git a/modules/adtrgtmeBidAdapter.js b/modules/adtrgtmeBidAdapter.js index 9432031e77e..f3f648c6f2d 100644 --- a/modules/adtrgtmeBidAdapter.js +++ b/modules/adtrgtmeBidAdapter.js @@ -86,7 +86,7 @@ function createORTB(bR, bid) { hb: 1, bidderver: BIDDER_VERSION, prebidjsver: PREBIDJS_VERSION, - ...(bid?.schain && { schain: bid.schain }), + ...(bid?.ortb2?.source?.ext?.schain && { schain: bid?.ortb2?.source?.ext?.schain }), }, fd: 1, }, @@ -99,7 +99,7 @@ function createORTB(bR, bid) { }, }; - if (bid?.schain) { + if (bid?.ortb2?.source?.ext?.schain) { oR.source.ext.schain.nodes[0].rid = oR.id; } diff --git a/modules/adtrueBidAdapter.js b/modules/adtrueBidAdapter.js index a6186d6129f..c9037aeb743 100644 --- a/modules/adtrueBidAdapter.js +++ b/modules/adtrueBidAdapter.js @@ -514,8 +514,9 @@ export const spec = { payload.test = 1; } // adding schain object - if (validBidRequests[0].schain) { - deepSetValue(payload, 'source.ext.schain', validBidRequests[0].schain); + const schain = validBidRequests[0]?.ortb2?.source?.ext?.schain; + if (schain) { + deepSetValue(payload, 'source.ext.schain', schain); } // Attaching GDPR Consent Params if (bidderRequest && bidderRequest.gdprConsent) { diff --git a/modules/advertisingBidAdapter.js b/modules/advertisingBidAdapter.js index 930a4dcd16d..665096fb19d 100644 --- a/modules/advertisingBidAdapter.js +++ b/modules/advertisingBidAdapter.js @@ -60,7 +60,7 @@ export const spec = { openRtbBidRequest.tmax = tmax; } - const schain = validBidReqs[0].schain; + const schain = validBidReqs[0]?.ortb2?.source?.ext?.schain; if (schain) { openRtbBidRequest.source = { ext: { schain } }; } diff --git a/modules/adyoulikeBidAdapter.js b/modules/adyoulikeBidAdapter.js index e2187782be2..3fb8da35e71 100644 --- a/modules/adyoulikeBidAdapter.js +++ b/modules/adyoulikeBidAdapter.js @@ -87,8 +87,9 @@ export const spec = { if (typeof bidReq.getFloor === 'function') { accumulator[bidReq.bidId].Pricing = getFloor(bidReq, size, mediatype); } - if (bidReq.schain) { - accumulator[bidReq.bidId].SChain = bidReq.schain; + const schain = bidReq?.ortb2?.source?.ext?.schain; + if (schain) { + accumulator[bidReq.bidId].SChain = schain; } if (!eids && bidReq.userIdAsEids && bidReq.userIdAsEids.length) { eids = bidReq.userIdAsEids; diff --git a/modules/ajaBidAdapter.js b/modules/ajaBidAdapter.js index 699dfd6fa04..a06c1a873fc 100644 --- a/modules/ajaBidAdapter.js +++ b/modules/ajaBidAdapter.js @@ -67,7 +67,8 @@ export const spec = { queryString = tryAppendQueryString(queryString, 'prebid_id', bidRequest.bidId); queryString = tryAppendQueryString(queryString, 'prebid_ver', '$prebid.version$'); queryString = tryAppendQueryString(queryString, 'page_url', pageUrl); - queryString = tryAppendQueryString(queryString, 'schain', spec.serializeSupplyChain(bidRequest.schain || [])) + const schain = bidRequest?.ortb2?.source?.ext?.schain; + queryString = tryAppendQueryString(queryString, 'schain', spec.serializeSupplyChain(schain || [])) const adFormatIDs = pickAdFormats(bidRequest) if (adFormatIDs && adFormatIDs.length > 0) { diff --git a/modules/alkimiBidAdapter.js b/modules/alkimiBidAdapter.js index f52c3ec7703..36a2600294c 100644 --- a/modules/alkimiBidAdapter.js +++ b/modules/alkimiBidAdapter.js @@ -64,7 +64,7 @@ export const spec = { bidIds, referer: bidderRequest.refererInfo.page, signature: alkimiConfig && alkimiConfig.signature, - schain: validBidRequests[0].schain, + schain: validBidRequests[0]?.ortb2?.source?.ext?.schain, cpp: config.getConfig('coppa') ? 1 : 0, device: { dnt: getDNT() ? 1 : 0, diff --git a/modules/amxBidAdapter.js b/modules/amxBidAdapter.js index 9a3c61135a0..8afab28378f 100644 --- a/modules/amxBidAdapter.js +++ b/modules/amxBidAdapter.js @@ -184,7 +184,7 @@ function convertRequest(bid) { aw: size[0], ah: size[1], tf: 0, - sc: bid.schain || {}, + sc: bid?.ortb2?.source?.ext?.schain || {}, f: ensureFloor(getFloor(bid)), rtb: bid.ortb2Imp, }; diff --git a/modules/apacdexBidAdapter.js b/modules/apacdexBidAdapter.js index 83119052f3a..912f37e35c8 100644 --- a/modules/apacdexBidAdapter.js +++ b/modules/apacdexBidAdapter.js @@ -48,8 +48,9 @@ export const spec = { test = config.getConfig('debug'); validBidRequests.forEach(bidReq => { - if (bidReq.schain) { - schain = schain || bidReq.schain + const bidSchain = bidReq?.ortb2?.source?.ext?.schain; + if (bidSchain) { + schain = schain || bidSchain } if (bidReq.userIdAsEids) { diff --git a/modules/appnexusBidAdapter.js b/modules/appnexusBidAdapter.js index db37362d3a4..ceeae762bc6 100644 --- a/modules/appnexusBidAdapter.js +++ b/modules/appnexusBidAdapter.js @@ -238,7 +238,7 @@ export const spec = { const memberIdBid = ((bidRequests) || []).find(hasMemberId); const member = memberIdBid ? parseInt(memberIdBid.params.member, 10) : 0; - const schain = bidRequests[0].schain; + const schain = bidRequests[0]?.ortb2?.source?.ext?.schain; const omidSupport = ((bidRequests) || []).find(hasOmidSupport); const payload = { diff --git a/modules/appushBidAdapter.js b/modules/appushBidAdapter.js index ec742120582..f06dc0f2c13 100644 --- a/modules/appushBidAdapter.js +++ b/modules/appushBidAdapter.js @@ -27,7 +27,7 @@ function isBidResponseValid(bid) { function getPlacementReqData(bid) { const { params, bidId, mediaTypes } = bid; - const schain = bid.schain || {}; + const schain = bid?.ortb2?.source?.ext?.schain || {}; const { placementId, endpointId } = params; const bidfloor = getBidFloor(bid); diff --git a/modules/audiencerunBidAdapter.js b/modules/audiencerunBidAdapter.js index df3bbda6a53..4d60ee244a3 100644 --- a/modules/audiencerunBidAdapter.js +++ b/modules/audiencerunBidAdapter.js @@ -143,7 +143,7 @@ export const spec = { }; payload.uspConsent = deepAccess(bidderRequest, 'uspConsent'); - payload.schain = deepAccess(bidRequests, '0.schain'); + payload.schain = deepAccess(bidRequests, '0.ortb2.source.ext.schain'); payload.userId = deepAccess(bidRequests, '0.userIdAsEids') || [] if (bidderRequest && bidderRequest.gdprConsent) { diff --git a/modules/beachfrontBidAdapter.js b/modules/beachfrontBidAdapter.js index 59d4d5976be..403a8d1129c 100644 --- a/modules/beachfrontBidAdapter.js +++ b/modules/beachfrontBidAdapter.js @@ -328,8 +328,9 @@ function createVideoRequestData(bid, bidderRequest) { deepSetValue(payload, 'regs.gpp_sid', applicableSections); } - if (bid.schain) { - deepSetValue(payload, 'source.ext.schain', bid.schain); + const schain = bid?.ortb2?.source?.ext?.schain; + if (schain) { + deepSetValue(payload, 'source.ext.schain', schain); } if (eids.length > 0) { @@ -389,8 +390,9 @@ function createBannerRequestData(bids, bidderRequest) { payload.gppSid = applicableSections; } - if (bids[0] && bids[0].schain) { - payload.schain = bids[0].schain; + const schain = bids[0]?.ortb2?.source?.ext?.schain; + if (schain) { + payload.schain = schain; } SUPPORTED_USER_IDS.forEach(({ key, queryParam }) => { diff --git a/modules/betweenBidAdapter.js b/modules/betweenBidAdapter.js index c81c49bc0d9..782ecf42984 100644 --- a/modules/betweenBidAdapter.js +++ b/modules/betweenBidAdapter.js @@ -85,8 +85,9 @@ export const spec = { } } - if (i.schain) { - params.schain = encodeToBase64WebSafe(JSON.stringify(i.schain)); + const schain = i?.ortb2?.source?.ext?.schain; + if (schain) { + params.schain = encodeToBase64WebSafe(JSON.stringify(schain)); } // TODO: is 'page' the right value here? diff --git a/modules/bliinkBidAdapter.js b/modules/bliinkBidAdapter.js index 2020fda84a5..62c2cc47872 100644 --- a/modules/bliinkBidAdapter.js +++ b/modules/bliinkBidAdapter.js @@ -218,7 +218,7 @@ export const buildRequests = (validBidRequests, bidderRequest) => { ect: getEffectiveConnectionType(), }; - const schain = deepAccess(validBidRequests[0], 'schain') + const schain = deepAccess(validBidRequests[0], 'ortb2.source.ext.schain') const eids = getUserIds(validBidRequests) const device = bidderRequest.ortb2?.device if (schain) { diff --git a/modules/bridBidAdapter.js b/modules/bridBidAdapter.js index c9840ad57f8..afe9442e3ac 100644 --- a/modules/bridBidAdapter.js +++ b/modules/bridBidAdapter.js @@ -99,9 +99,10 @@ export const spec = { }; }; - if (bidRequests[0].schain) { + const schain = bidRequests[0]?.ortb2?.source?.ext?.schain; + if (schain) { postBody.source = { - ext: { schain: bidRequests[0].schain } + ext: { schain: schain } }; } diff --git a/modules/brightMountainMediaBidAdapter.js b/modules/brightMountainMediaBidAdapter.js index 5e5b062889d..1047ec931cc 100644 --- a/modules/brightMountainMediaBidAdapter.js +++ b/modules/brightMountainMediaBidAdapter.js @@ -81,7 +81,8 @@ export const spec = { oRTBRequest.imp[0].bidfloor = getFloor(bid, size); oRTBRequest.user = getUserIdAsEids(bid.userIdAsEids) - oRTBRequest.source = getSchain(bid.schain) + const schain = bid?.ortb2?.source?.ext?.schain; + oRTBRequest.source = getSchain(schain) requestData.push({ method: 'POST', diff --git a/modules/browsiBidAdapter.js b/modules/browsiBidAdapter.js index fa1cacaa568..cb256254e12 100644 --- a/modules/browsiBidAdapter.js +++ b/modules/browsiBidAdapter.js @@ -43,7 +43,8 @@ export const spec = { const requests = []; const {refererInfo, bidderRequestId, gdprConsent, uspConsent} = bidderRequest; validBidRequests.forEach(bidRequest => { - const {bidId, adUnitCode, auctionId, ortb2Imp, schain, params} = bidRequest; + const {bidId, adUnitCode, auctionId, ortb2Imp, params} = bidRequest; + const schain = bidRequest?.ortb2?.source?.ext?.schain; const video = getVideoMediaType(bidRequest); const request = { diff --git a/modules/cadentApertureMXBidAdapter.js b/modules/cadentApertureMXBidAdapter.js index 7776583bcda..32103ffd540 100644 --- a/modules/cadentApertureMXBidAdapter.js +++ b/modules/cadentApertureMXBidAdapter.js @@ -185,10 +185,11 @@ export const cadentAdapter = { return cadentData; }, getSupplyChain: (bidderRequest, cadentData) => { - if (bidderRequest.bids[0] && bidderRequest.bids[0].schain) { + const schain = bidderRequest.bids[0]?.ortb2?.source?.ext?.schain; + if (bidderRequest.bids[0] && schain) { cadentData.source = { ext: { - schain: bidderRequest.bids[0].schain + schain: schain } }; } diff --git a/modules/carodaBidAdapter.js b/modules/carodaBidAdapter.js index 3060501ba8d..75af70da4ff 100644 --- a/modules/carodaBidAdapter.js +++ b/modules/carodaBidAdapter.js @@ -49,7 +49,7 @@ export const spec = { const test = getFirstWithKey(validBidRequests, 'params.test'); const currency = getCurrencyFromBidderRequest(bidderRequest); const eids = getFirstWithKey(validBidRequests, 'userIdAsEids'); - const schain = getFirstWithKey(validBidRequests, 'schain'); + const schain = getFirstWithKey(validBidRequests, 'ortb2.source.ext.schain'); const request = { // TODO: fix auctionId leak: https://github.com/prebid/Prebid.js/issues/9781 auctionId: bidderRequest.auctionId, diff --git a/modules/colossussspBidAdapter.js b/modules/colossussspBidAdapter.js index e93d6a2de27..66093ddd480 100644 --- a/modules/colossussspBidAdapter.js +++ b/modules/colossussspBidAdapter.js @@ -144,8 +144,9 @@ export const spec = { floor: {} }; - if (bid.schain) { - placement.schain = bid.schain; + const schain = bid?.ortb2?.source?.ext?.schain; + if (schain) { + placement.schain = schain; } let gpid = deepAccess(bid, 'ortb2Imp.ext.gpid'); if (gpid) { diff --git a/modules/connectadBidAdapter.js b/modules/connectadBidAdapter.js index f214bf5ca2c..5bad4879beb 100644 --- a/modules/connectadBidAdapter.js +++ b/modules/connectadBidAdapter.js @@ -67,8 +67,9 @@ export const spec = { } // adding schain object - if (validBidRequests[0].schain) { - deepSetValue(data, 'source.ext.schain', validBidRequests[0].schain); + const schain = validBidRequests[0]?.ortb2?.source?.ext?.schain; + if (schain) { + deepSetValue(data, 'source.ext.schain', schain); } // Attaching GDPR Consent Params diff --git a/modules/consumableBidAdapter.js b/modules/consumableBidAdapter.js index e01078890f9..b85322e4eb7 100644 --- a/modules/consumableBidAdapter.js +++ b/modules/consumableBidAdapter.js @@ -82,8 +82,9 @@ export const spec = { data.ccpa = bidderRequest.uspConsent; } - if (bidderRequest && bidderRequest.schain) { - data.schain = bidderRequest.schain; + const schain = bidderRequest?.ortb2?.source?.ext?.schain; + if (schain) { + data.schain = schain; } if (config.getConfig('coppa')) { diff --git a/modules/cpmstarBidAdapter.js b/modules/cpmstarBidAdapter.js index 772cb8c537c..b9d61eaf543 100755 --- a/modules/cpmstarBidAdapter.js +++ b/modules/cpmstarBidAdapter.js @@ -71,8 +71,8 @@ export const spec = { url.searchParams.set('requestid', bidRequest.bidId); url.searchParams.set('referer', referer); - if (bidRequest.schain && bidRequest.schain.nodes) { - var schain = bidRequest.schain; + const schain = bidRequest?.ortb2?.source?.ext?.schain; + if (schain && schain.nodes) { var schainString = ''; schainString += schain.ver + ',' + schain.complete; for (var i2 = 0; i2 < schain.nodes.length; i2++) { diff --git a/modules/craftBidAdapter.js b/modules/craftBidAdapter.js index 6ba5ffa038d..d4b45153fca 100644 --- a/modules/craftBidAdapter.js +++ b/modules/craftBidAdapter.js @@ -27,7 +27,7 @@ export const spec = { bidRequests = convertOrtbRequestToProprietaryNative(bidRequests); const bidRequest = bidRequests[0]; const tags = bidRequests.map(bidToTag); - const schain = bidRequest.schain; + const schain = bidRequest?.ortb2?.source?.ext?.schain; const payload = { tags: [...tags], ua: navigator.userAgent, diff --git a/modules/datablocksBidAdapter.js b/modules/datablocksBidAdapter.js index 3cd974b2b13..3a80779fbf5 100644 --- a/modules/datablocksBidAdapter.js +++ b/modules/datablocksBidAdapter.js @@ -357,7 +357,7 @@ export const spec = { domain: window.location.host, // TODO: is 'page' the right value here? page: bidderRequest.refererInfo.page, - schain: validRequests[0].schain || {}, + schain: validRequests[0]?.ortb2?.source?.ext?.schain || {}, ext: { p_domain: bidderRequest.refererInfo.domain, rt: bidderRequest.refererInfo.reachedTop, diff --git a/modules/dianomiBidAdapter.js b/modules/dianomiBidAdapter.js index 5e43bf955ef..a91c5d777cd 100644 --- a/modules/dianomiBidAdapter.js +++ b/modules/dianomiBidAdapter.js @@ -121,7 +121,7 @@ export const spec = { const currency = getCurrencyFromBidderRequest(bidderRequest); const cur = currency && [currency]; const eids = setOnAny(validBidRequests, 'userIdAsEids'); - const schain = setOnAny(validBidRequests, 'schain'); + const schain = setOnAny(validBidRequests, 'ortb2.source.ext.schain'); const imp = validBidRequests.map((bid, id) => { bid.netRevenue = pt; diff --git a/modules/digitalMatterBidAdapter.js b/modules/digitalMatterBidAdapter.js index 34cf84eb9d3..e5acd9e98de 100644 --- a/modules/digitalMatterBidAdapter.js +++ b/modules/digitalMatterBidAdapter.js @@ -36,7 +36,7 @@ export const spec = { } const device = getDevice(common.device); - const schain = getByKey(validBidRequests, 'schain'); + const schain = getByKey(validBidRequests, 'ortb2.source.ext.schain'); const eids = getByKey(validBidRequests, 'userIdAsEids'); const currency = config.getConfig('currency') const cur = currency && [currency]; diff --git a/modules/distroscaleBidAdapter.js b/modules/distroscaleBidAdapter.js index be52023a0e0..911ac0dba35 100644 --- a/modules/distroscaleBidAdapter.js +++ b/modules/distroscaleBidAdapter.js @@ -197,8 +197,9 @@ export const spec = { } // adding schain object - if (validBidRequests[0].schain) { - deepSetValue(payload, 'source.schain', validBidRequests[0].schain); + const schain = validBidRequests[0]?.ortb2?.source?.ext?.schain; + if (schain) { + deepSetValue(payload, 'source.ext.schain', schain); } // Attaching GDPR Consent Params diff --git a/modules/dspxBidAdapter.js b/modules/dspxBidAdapter.js index b72991617c6..54a1d240bc7 100644 --- a/modules/dspxBidAdapter.js +++ b/modules/dspxBidAdapter.js @@ -149,8 +149,9 @@ export const spec = { } // schain - if (bidRequest.schain) { - payload.schain = bidRequest.schain; + const schain = bidRequest?.ortb2?.source?.ext?.schain; + if (schain) { + payload.schain = schain; } // fill userId params diff --git a/modules/eplanningBidAdapter.js b/modules/eplanningBidAdapter.js index 5b3f55b9da6..a739ddc0e3c 100644 --- a/modules/eplanningBidAdapter.js +++ b/modules/eplanningBidAdapter.js @@ -41,7 +41,7 @@ export const spec = { const method = 'GET'; const dfpClientId = '1'; const sec = 'ROS'; - const schain = bidRequests[0].schain; + const schain = bidRequests[0]?.ortb2?.source?.ext?.schain; let url; let params; const urlConfig = getUrlConfig(bidRequests); diff --git a/modules/fluctBidAdapter.js b/modules/fluctBidAdapter.js index 8ed1b52bdb6..78d574ed43b 100644 --- a/modules/fluctBidAdapter.js +++ b/modules/fluctBidAdapter.js @@ -93,8 +93,9 @@ export const spec = { data.params = request.params; - if (request.schain) { - data.schain = request.schain; + const schain = request?.ortb2?.source?.ext?.schain; + if (schain) { + data.schain = schain; } const searchParams = new URLSearchParams({ diff --git a/modules/freewheel-sspBidAdapter.js b/modules/freewheel-sspBidAdapter.js index fc85edc483b..d30be635a50 100644 --- a/modules/freewheel-sspBidAdapter.js +++ b/modules/freewheel-sspBidAdapter.js @@ -399,7 +399,7 @@ export const spec = { } // Add schain object - var schain = currentBidRequest.schain; + var schain = currentBidRequest?.ortb2?.source?.ext?.schain; if (schain) { try { requestParams.schain = JSON.stringify(schain); diff --git a/modules/gamoshiBidAdapter.js b/modules/gamoshiBidAdapter.js index 798cb5acca9..fe0eb5238a5 100644 --- a/modules/gamoshiBidAdapter.js +++ b/modules/gamoshiBidAdapter.js @@ -112,8 +112,9 @@ export const spec = { deepSetValue(rtbBidRequest, 'regs.ext.gdpr', gdprConsent.consent_required === true ? 1 : 0); deepSetValue(rtbBidRequest, 'user.ext.consent', gdprConsent.consent_string); - if (validBidRequests[0].schain) { - deepSetValue(rtbBidRequest, 'source.ext.schain', validBidRequests[0].schain); + const schain = validBidRequests[0]?.ortb2?.source?.ext?.schain; + if (schain) { + deepSetValue(rtbBidRequest, 'source.ext.schain', schain); } if (bidderRequest && bidderRequest.uspConsent) { diff --git a/modules/greenbidsBidAdapter.js b/modules/greenbidsBidAdapter.js index 0ece04fe11f..6dc0364df0d 100644 --- a/modules/greenbidsBidAdapter.js +++ b/modules/greenbidsBidAdapter.js @@ -76,8 +76,9 @@ export const spec = { const firstBidRequest = validBidRequests[0]; - if (firstBidRequest.schain) { - payload.schain = firstBidRequest.schain; + const schain = firstBidRequest?.ortb2?.source?.ext?.schain; + if (schain) { + payload.schain = schain; } hydratePayloadWithGppConsentData(payload, bidderRequest.gppConsent); diff --git a/modules/gridBidAdapter.js b/modules/gridBidAdapter.js index 244981ba94f..e860c292f93 100644 --- a/modules/gridBidAdapter.js +++ b/modules/gridBidAdapter.js @@ -115,7 +115,7 @@ export const spec = { bidderRequestId = bid.bidderRequestId; } if (!schain) { - schain = bid.schain; + schain = bid?.ortb2?.source?.ext?.schain; } if (!userIdAsEids) { userIdAsEids = bid.userIdAsEids; @@ -184,8 +184,10 @@ export const spec = { wrapper_version: '$prebid.version$' } }; - if (bid.schain) { - reqSource.ext.schain = bid.schain; + // Check for schain in the new location + const schain = bid?.ortb2?.source?.ext?.schain; + if (schain) { + reqSource.ext.schain = schain; } const request = { id: bid.bidderRequestId && bid.bidderRequestId.toString(), diff --git a/modules/gumgumBidAdapter.js b/modules/gumgumBidAdapter.js index ddf16e28c8f..df78ba808b8 100644 --- a/modules/gumgumBidAdapter.js +++ b/modules/gumgumBidAdapter.js @@ -366,7 +366,6 @@ function buildRequests(validBidRequests, bidderRequest) { bidId, mediaTypes = {}, params = {}, - schain, userId = {}, ortb2Imp, adUnitCode = '' @@ -489,6 +488,7 @@ function buildRequests(validBidRequests, bidderRequest) { if (coppa) { data.coppa = coppa; } + const schain = bidRequest?.ortb2?.source?.ext?.schain; if (schain && schain.nodes) { data.schain = _serializeSupplyChainObj(schain); } diff --git a/modules/holidBidAdapter.js b/modules/holidBidAdapter.js index abc8e0b403c..58172e204e1 100644 --- a/modules/holidBidAdapter.js +++ b/modules/holidBidAdapter.js @@ -32,7 +32,11 @@ export const spec = { return validBidRequests.map((bid) => { const requestData = { ...bid.ortb2, - source: { schain: bid.schain }, + source: { + ext: { + schain: bid?.ortb2?.source?.ext?.schain + } + }, id: bidderRequest.bidderRequestId, imp: [getImp(bid)], tmax: TMAX, diff --git a/modules/impactifyBidAdapter.js b/modules/impactifyBidAdapter.js index 38a57c4724a..7be716f4089 100644 --- a/modules/impactifyBidAdapter.js +++ b/modules/impactifyBidAdapter.js @@ -137,7 +137,7 @@ function createOpenRtbRequest(validBidRequests, bidderRequest) { } // Set SChain in request - let schain = deepAccess(validBidRequests, '0.schain'); + let schain = deepAccess(validBidRequests, '0.ortb2.source.ext.schain'); if (schain) request.source.ext = { schain: schain }; // Set Eids diff --git a/modules/insticatorBidAdapter.js b/modules/insticatorBidAdapter.js index f26cb8d311b..2aab5a9df03 100644 --- a/modules/insticatorBidAdapter.js +++ b/modules/insticatorBidAdapter.js @@ -355,9 +355,10 @@ function buildUser(bid) { } function extractSchain(bids, requestId) { - if (!bids || bids.length === 0 || !bids[0].schain) return; + if (!bids || bids.length === 0) return; - const schain = bids[0].schain; + const schain = bids[0]?.ortb2?.source?.ext?.schain; + if (!schain) return; if (schain && schain.nodes && schain.nodes.length && schain.nodes[0]) { schain.nodes[0].rid = requestId; } diff --git a/modules/ixBidAdapter.js b/modules/ixBidAdapter.js index 065f891e234..c0185411f78 100644 --- a/modules/ixBidAdapter.js +++ b/modules/ixBidAdapter.js @@ -174,7 +174,7 @@ function setDisplayManager(imp, bid) { renderer = deepAccess(bid, 'renderer'); } - if (deepAccess(bid, 'schain', false)) { + if (deepAccess(bid, 'ortb2.source.ext.schain', false)) { imp.displaymanager = 'pbjs_wrapper'; } else if (renderer && typeof (renderer) === 'object') { if (renderer.url !== undefined) { @@ -860,9 +860,11 @@ function enrichRequest(r, bidderRequest, impressions, validBidRequests, userEids } // if an schain is provided, send it along - if (validBidRequests[0].schain) { - r.source.ext = {}; - r.source.ext.schain = validBidRequests[0].schain; + const schain = validBidRequests[0]?.ortb2?.source?.ext?.schain; + if (schain) { + r.source = r.source || {}; + r.source.ext = r.source.ext || {}; + r.source.ext.schain = schain; } if (userEids.length > 0) { diff --git a/modules/jixieBidAdapter.js b/modules/jixieBidAdapter.js index d4901b097c5..b0c47d2f841 100644 --- a/modules/jixieBidAdapter.js +++ b/modules/jixieBidAdapter.js @@ -209,7 +209,7 @@ export const spec = { let ids = fetchIds_(jxCfg); let eids = []; let miscDims = internal.getMiscDims(); - let schain = deepAccess(validBidRequests[0], 'schain'); + let schain = deepAccess(validBidRequests[0], 'ortb2.source.ext.schain'); let eids1 = validBidRequests[0].userIdAsEids; // all available user ids are sent to our backend in the standard array layout: diff --git a/modules/justpremiumBidAdapter.js b/modules/justpremiumBidAdapter.js index 2ed2d544a34..081a3f44a27 100644 --- a/modules/justpremiumBidAdapter.js +++ b/modules/justpremiumBidAdapter.js @@ -69,8 +69,9 @@ export const spec = { jp_adapter: JP_ADAPTER_VERSION } - if (validBidRequests[0].schain) { - payload.schain = validBidRequests[0].schain; + const schain = validBidRequests[0]?.ortb2?.source?.ext?.schain; + if (schain) { + payload.schain = schain; } const payloadString = JSON.stringify(payload) diff --git a/modules/jwplayerBidAdapter.js b/modules/jwplayerBidAdapter.js index c58eed8ffb8..20fd585a08c 100644 --- a/modules/jwplayerBidAdapter.js +++ b/modules/jwplayerBidAdapter.js @@ -185,8 +185,9 @@ function getBidAdapter() { deepSetValue(openrtbRequest, 'regs.ext.us_privacy', bidderRequest.uspConsent); } - if (bidRequest.schain) { - deepSetValue(openrtbRequest, 'source.schain', bidRequest.schain); + const schain = bidRequest?.ortb2?.source?.ext?.schain; + if (schain) { + deepSetValue(openrtbRequest, 'source.schain', schain); } openrtbRequest.tmax = bidderRequest.timeout || 200; diff --git a/modules/kargoBidAdapter.js b/modules/kargoBidAdapter.js index 229949ddaf3..cd6573869fd 100644 --- a/modules/kargoBidAdapter.js +++ b/modules/kargoBidAdapter.js @@ -104,9 +104,10 @@ function buildRequests(validBidRequests, bidderRequest) { krakenParams.site = { cat: firstBidRequest.ortb2.site.cat }; } - // Add schain - if (firstBidRequest.schain && firstBidRequest.schain.nodes) { - krakenParams.schain = firstBidRequest.schain + // Add schain - check for schain in the new location + const schain = firstBidRequest?.ortb2?.source?.ext?.schain; + if (schain && schain.nodes) { + krakenParams.schain = schain } // Add user data object if available diff --git a/modules/kubientBidAdapter.js b/modules/kubientBidAdapter.js index 8ccfa4ab059..b66b4e851d5 100644 --- a/modules/kubientBidAdapter.js +++ b/modules/kubientBidAdapter.js @@ -49,8 +49,9 @@ export const spec = { adSlot.video = bid.mediaTypes.video; } - if (bid.schain) { - adSlot.schain = bid.schain; + const schain = bid?.ortb2?.source?.ext?.schain; + if (schain) { + adSlot.schain = schain; } let data = { diff --git a/modules/lemmaDigitalBidAdapter.js b/modules/lemmaDigitalBidAdapter.js index d8a9614d5c0..c2b9af2bb5f 100644 --- a/modules/lemmaDigitalBidAdapter.js +++ b/modules/lemmaDigitalBidAdapter.js @@ -483,7 +483,7 @@ export var spec = { return { pchain: params.pchain, ext: { - schain: request.schain + schain: request?.ortb2?.source?.ext?.schain }, }; } diff --git a/modules/limelightDigitalBidAdapter.js b/modules/limelightDigitalBidAdapter.js index bf170738d65..d5318440cc2 100644 --- a/modules/limelightDigitalBidAdapter.js +++ b/modules/limelightDigitalBidAdapter.js @@ -179,7 +179,7 @@ function buildPlacement(bidRequest) { ortb2Imp: bidRequest.ortb2Imp, publisherId: bidRequest.params.publisherId, userIdAsEids: bidRequest.userIdAsEids, - supplyChain: bidRequest.schain, + supplyChain: bidRequest?.ortb2?.source?.ext?.schain, custom1: bidRequest.params.custom1, custom2: bidRequest.params.custom2, custom3: bidRequest.params.custom3, diff --git a/modules/livewrappedBidAdapter.js b/modules/livewrappedBidAdapter.js index 9276671bf87..cdddfd688e9 100644 --- a/modules/livewrappedBidAdapter.js +++ b/modules/livewrappedBidAdapter.js @@ -63,7 +63,7 @@ export const spec = { const ifa = ((bidRequests) || []).find(hasIfaParam); const bundle = ((bidRequests) || []).find(hasBundleParam); const tid = ((bidRequests) || []).find(hasTidParam); - const schain = bidRequests[0].schain; + const schain = bidRequests[0]?.ortb2?.source?.ext?.schain; let ortb2 = bidderRequest.ortb2; const eids = handleEids(bidRequests); bidUrl = bidUrl ? bidUrl.params.bidUrl : URL; diff --git a/modules/lkqdBidAdapter.js b/modules/lkqdBidAdapter.js index a92e431f80a..535dbbf759b 100644 --- a/modules/lkqdBidAdapter.js +++ b/modules/lkqdBidAdapter.js @@ -110,10 +110,11 @@ export const spec = { requestData.device.ifa = bid.params.idfa || bid.params.aid; } - if (bid.schain) { + const schain = bid?.ortb2?.source?.ext?.schain; + if (schain) { requestData.source = { ext: { - schain: bid.schain + schain: schain } }; } else if (bid.params.schain) { diff --git a/modules/lockerdomeBidAdapter.js b/modules/lockerdomeBidAdapter.js index 5038eadce30..e0be50e6d1c 100644 --- a/modules/lockerdomeBidAdapter.js +++ b/modules/lockerdomeBidAdapter.js @@ -12,7 +12,8 @@ export const spec = { let schain; const adUnitBidRequests = bidRequests.map(function (bid) { - if (bid.schain) schain = schain || bid.schain; + const bidSchain = bid?.ortb2?.source?.ext?.schain; + if (bidSchain) schain = schain || bidSchain; return { requestId: bid.bidId, adUnitCode: bid.adUnitCode, diff --git a/modules/loganBidAdapter.js b/modules/loganBidAdapter.js index 4e2652e452f..dd092813eb1 100644 --- a/modules/loganBidAdapter.js +++ b/modules/loganBidAdapter.js @@ -39,7 +39,7 @@ export const spec = { const placement = { placementId: bid.params.placementId, bidId: bid.bidId, - schain: bid.schain || {}, + schain: bid?.ortb2?.source?.ext?.schain || {}, bidfloor: getBidFloor(bid) }; const mediaType = bid.mediaTypes; diff --git a/modules/logicadBidAdapter.js b/modules/logicadBidAdapter.js index 8cf4a8352de..2b78082c184 100644 --- a/modules/logicadBidAdapter.js +++ b/modules/logicadBidAdapter.js @@ -104,8 +104,9 @@ function newBidRequest(bidRequest, bidderRequest) { data.userData = userData; } - if (bidRequest.schain) { - data.schain = bidRequest.schain; + const schain = bidRequest?.ortb2?.source?.ext?.schain; + if (schain) { + data.schain = schain; } return data; diff --git a/modules/luceadBidAdapter.js b/modules/luceadBidAdapter.js index ffc2307bcb8..703384b9e6d 100755 --- a/modules/luceadBidAdapter.js +++ b/modules/luceadBidAdapter.js @@ -75,7 +75,7 @@ function buildRequests(bidRequests, bidderRequest) { sizes: bidRequest.sizes, media_types: bidRequest.mediaTypes, placement_id: bidRequest.params.placementId, - schain: bidRequest.schain, + schain: bidRequest?.ortb2?.source?.ext?.schain, }; }), }), diff --git a/modules/marsmediaBidAdapter.js b/modules/marsmediaBidAdapter.js index 81e78ba87b1..850ed6eaa3a 100644 --- a/modules/marsmediaBidAdapter.js +++ b/modules/marsmediaBidAdapter.js @@ -207,8 +207,9 @@ function MarsmediaAdapter() { } } }; - if (BRs[0].schain) { - deepSetValue(bid, 'source.ext.schain', BRs[0].schain); + const schain = BRs[0]?.ortb2?.source?.ext?.schain; + if (schain) { + deepSetValue(bid, 'source.ext.schain', schain); } if (bidderRequest.uspConsent) { deepSetValue(bid, 'regs.ext.us_privacy', bidderRequest.uspConsent) diff --git a/modules/mediafuseBidAdapter.js b/modules/mediafuseBidAdapter.js index 022e569bb86..a951a53db19 100644 --- a/modules/mediafuseBidAdapter.js +++ b/modules/mediafuseBidAdapter.js @@ -186,7 +186,7 @@ export const spec = { const memberIdBid = ((bidRequests) || []).find(hasMemberId); const member = memberIdBid ? parseInt(memberIdBid.params.member, 10) : 0; - const schain = bidRequests[0].schain; + const schain = bidRequests[0]?.ortb2?.source?.ext?.schain; const omidSupport = ((bidRequests) || []).find(hasOmidSupport); const payload = { diff --git a/modules/mediakeysBidAdapter.js b/modules/mediakeysBidAdapter.js index bdf7b5f8537..f4979bea396 100644 --- a/modules/mediakeysBidAdapter.js +++ b/modules/mediakeysBidAdapter.js @@ -618,8 +618,9 @@ export const spec = { payload.imp.push(imp); }); - if (validBidRequests[0].schain) { - deepSetValue(payload, 'source.ext.schain', validBidRequests[0].schain); + const schain = validBidRequests[0]?.ortb2?.source?.ext?.schain; + if (schain) { + deepSetValue(payload, 'source.ext.schain', schain); } if (bidderRequest && bidderRequest.gdprConsent) { diff --git a/modules/medianetBidAdapter.js b/modules/medianetBidAdapter.js index 757ab9f81aa..9f2cf150d51 100644 --- a/modules/medianetBidAdapter.js +++ b/modules/medianetBidAdapter.js @@ -162,7 +162,7 @@ function extParams(bidRequest, bidderRequests) { const gdpr = deepAccess(bidderRequests, 'gdprConsent'); const uspConsent = deepAccess(bidderRequests, 'uspConsent'); const userId = deepAccess(bidRequest, 'userId'); - const sChain = deepAccess(bidRequest, 'schain') || {}; + const sChain = deepAccess(bidRequest, 'ortb2.source.ext.schain') || {}; const windowSize = spec.getWindowSize(); const gdprApplies = !!(gdpr && gdpr.gdprApplies); const uspApplies = !!(uspConsent); diff --git a/modules/mediasquareBidAdapter.js b/modules/mediasquareBidAdapter.js index 59cff8ace55..d75cf18e729 100644 --- a/modules/mediasquareBidAdapter.js +++ b/modules/mediasquareBidAdapter.js @@ -89,7 +89,7 @@ export const spec = { }; } if (bidderRequest.uspConsent) { payload.uspConsent = bidderRequest.uspConsent; } - if (bidderRequest.schain) { payload.schain = bidderRequest.schain; } + if (bidderRequest?.ortb2?.source?.ext?.schain) { payload.schain = bidderRequest.ortb2.source.ext.schain; } if (bidderRequest.userIdAsEids) { payload.eids = bidderRequest.userIdAsEids }; if (bidderRequest.ortb2?.regs?.ext?.dsa) { payload.dsa = bidderRequest.ortb2.regs.ext.dsa } if (bidderRequest.ortb2) { payload.ortb2 = bidderRequest.ortb2 } diff --git a/modules/mgidBidAdapter.js b/modules/mgidBidAdapter.js index 86cd3fb8250..7a0856a2859 100644 --- a/modules/mgidBidAdapter.js +++ b/modules/mgidBidAdapter.js @@ -304,7 +304,7 @@ export const spec = { deepSetValue(request, 'regs.coppa', 1); } } - const schain = setOnAny(validBidRequests, 'schain'); + const schain = setOnAny(validBidRequests, 'ortb2.source.ext.schain'); if (schain) { deepSetValue(request, 'source.ext.schain', schain); } diff --git a/modules/michaoBidAdapter.js b/modules/michaoBidAdapter.js index 56c073cddde..ff95b509b3b 100644 --- a/modules/michaoBidAdapter.js +++ b/modules/michaoBidAdapter.js @@ -217,8 +217,9 @@ const converter = ortbConverter({ 'site.ext.michao.site', bidRequest.params.site.toString() ); - if (bidRequest?.schain) { - deepSetValue(openRTBBidRequest, 'source.schain', bidRequest.schain); + const schain = bidRequest?.ortb2?.source?.ext?.schain; + if (schain) { + deepSetValue(openRTBBidRequest, 'source.schain', schain); } if (bidRequest.params?.partner) { diff --git a/modules/missenaBidAdapter.js b/modules/missenaBidAdapter.js index 0d2054ca09f..fc7bdaaf5c7 100644 --- a/modules/missenaBidAdapter.js +++ b/modules/missenaBidAdapter.js @@ -68,7 +68,7 @@ function toPayload(bidRequest, bidderRequest) { payload.floor = bidFloor?.floor; payload.floor_currency = bidFloor?.currency; payload.currency = getCurrencyFromBidderRequest(bidderRequest); - payload.schain = bidRequest.schain; + payload.schain = bidRequest?.ortb2?.source?.ext?.schain; payload.autoplay = isAutoplayEnabled() === true ? 1 : 0; payload.screen = { height: getWinDimensions().screen.height, width: getWinDimensions().screen.width }; payload.viewport = getViewportSize(); diff --git a/modules/nextMillenniumBidAdapter.js b/modules/nextMillenniumBidAdapter.js index d4886e9d2ac..a6f90c272f4 100644 --- a/modules/nextMillenniumBidAdapter.js +++ b/modules/nextMillenniumBidAdapter.js @@ -508,8 +508,7 @@ function getDeviceObj() { } export function getSourceObj(validBidRequests, bidderRequest) { - const schain = validBidRequests?.[0]?.schain || - (bidderRequest?.ortb2?.source && (bidderRequest?.ortb2?.source?.schain || bidderRequest?.ortb2?.source?.ext?.schain)); + const schain = validBidRequests?.[0]?.ortb2?.source?.ext?.schain || bidderRequest?.ortb2?.source?.schain || bidderRequest?.ortb2?.source?.ext?.schain; if (!schain) return; diff --git a/modules/nobidBidAdapter.js b/modules/nobidBidAdapter.js index 921fbb25d8a..8b7f9cae319 100644 --- a/modules/nobidBidAdapter.js +++ b/modules/nobidBidAdapter.js @@ -86,10 +86,11 @@ function nobidBuildRequests(bids, bidderRequest) { return gppConsent; } var schain = function(bids) { - if (bids && bids.length > 0) { - return bids[0].schain + try { + return bids[0]?.ortb2?.source?.ext?.schain; + } catch (e) { + return null; } - return null; } var coppa = function() { if (config.getConfig('coppa') === true) { diff --git a/modules/omsBidAdapter.js b/modules/omsBidAdapter.js index 0b9ac002bb0..20a9336b7e3 100644 --- a/modules/omsBidAdapter.js +++ b/modules/omsBidAdapter.js @@ -114,8 +114,9 @@ function buildRequests(bidReqs, bidderRequest) { deepSetValue(payload, 'regs.coppa', 1); } - if (bidReqs?.[0]?.schain) { - deepSetValue(payload, 'source.ext.schain', bidReqs[0].schain) + const schain = bidReqs?.[0]?.ortb2?.source?.ext?.schain; + if (schain) { + deepSetValue(payload, 'source.ext.schain', schain) } if (bidderRequest?.ortb2?.user) { diff --git a/modules/onetagBidAdapter.js b/modules/onetagBidAdapter.js index 656bb50fa2a..0fa73a8631d 100644 --- a/modules/onetagBidAdapter.js +++ b/modules/onetagBidAdapter.js @@ -132,8 +132,9 @@ function buildRequests(validBidRequests, bidderRequest) { if (validBidRequests && validBidRequests.length !== 0 && validBidRequests[0].userIdAsEids) { payload.userId = validBidRequests[0].userIdAsEids; } - if (validBidRequests && validBidRequests.length !== 0 && validBidRequests[0].schain && isSchainValid(validBidRequests[0].schain)) { - payload.schain = validBidRequests[0].schain; + const schain = validBidRequests?.[0]?.ortb2?.source?.ext?.schain; + if (validBidRequests && validBidRequests.length !== 0 && schain && isSchainValid(schain)) { + payload.schain = schain; } try { if (storage.hasLocalStorage()) { diff --git a/modules/opscoBidAdapter.js b/modules/opscoBidAdapter.js index 2ad14227804..4ddb548c0f1 100644 --- a/modules/opscoBidAdapter.js +++ b/modules/opscoBidAdapter.js @@ -59,9 +59,10 @@ export const spec = { deepSetValue(payload, 'user.ext.eids', eids); } - const schainData = deepAccess(validBidRequests[0], 'schain.nodes'); + const schain = validBidRequests[0]?.ortb2?.source?.ext?.schain; + const schainData = schain?.nodes; if (isArray(schainData) && schainData.length > 0) { - deepSetValue(payload, 'source.ext.schain', validBidRequests[0].schain); + deepSetValue(payload, 'source.ext.schain', schain); } if (bidderRequest.uspConsent) { diff --git a/modules/optidigitalBidAdapter.js b/modules/optidigitalBidAdapter.js index b51c8ef4403..7f86dd93d6c 100755 --- a/modules/optidigitalBidAdapter.js +++ b/modules/optidigitalBidAdapter.js @@ -74,8 +74,9 @@ export const spec = { payload.pageTemplate = validBidRequests[0].params.pageTemplate; } - if (validBidRequests[0].schain) { - payload.schain = validBidRequests[0].schain; + const schain = validBidRequests[0]?.ortb2?.source?.ext?.schain; + if (schain) { + payload.schain = schain; } const gdpr = deepAccess(bidderRequest, 'gdprConsent'); diff --git a/modules/ozoneBidAdapter.js b/modules/ozoneBidAdapter.js index ead659a7f3b..1992cfcefe5 100644 --- a/modules/ozoneBidAdapter.js +++ b/modules/ozoneBidAdapter.js @@ -330,8 +330,8 @@ export const spec = { deepSetValue(obj, `ext.${whitelabelBidder}.customData.0.targeting`, fpd.site); } } - if (!schain && deepAccess(ozoneBidRequest, 'schain')) { - schain = ozoneBidRequest.schain; + if (!schain && deepAccess(ozoneBidRequest, 'ortb2.source.ext.schain')) { + schain = ozoneBidRequest.ortb2.source.ext.schain; } let gpid = deepAccess(ozoneBidRequest, 'ortb2Imp.ext.gpid'); if (gpid) { diff --git a/modules/pixfutureBidAdapter.js b/modules/pixfutureBidAdapter.js index 72b97b69f7c..ff2d50398e0 100644 --- a/modules/pixfutureBidAdapter.js +++ b/modules/pixfutureBidAdapter.js @@ -74,7 +74,7 @@ export const spec = { }); } - const schain = validBidRequests[0].schain; + const schain = validBidRequests[0]?.ortb2?.source?.ext?.schain; const payload = { tags: [...tags], diff --git a/modules/prebidServerBidAdapter/ortbConverter.js b/modules/prebidServerBidAdapter/ortbConverter.js index b78c59b2756..003bf2e9235 100644 --- a/modules/prebidServerBidAdapter/ortbConverter.js +++ b/modules/prebidServerBidAdapter/ortbConverter.js @@ -205,13 +205,9 @@ const PBS_CONVERTER = ortbConverter({ if (fpdConfigs.length) { deepSetValue(ortbRequest, 'ext.prebid.bidderconfig', fpdConfigs); } - }, - extPrebidAliases(orig, ortbRequest, proxyBidderRequest, context) { - // override alias processing to do it for each bidder in the request - context.actualBidderRequests.forEach(req => orig(ortbRequest, req, context)); - }, - sourceExtSchain(orig, ortbRequest, proxyBidderRequest, context) { - // pass schains in ext.prebid.schains + + // Handle schain information after FPD processing + // Collect schains from bidder requests and organize into ext.prebid.schains let chains = ortbRequest?.ext?.prebid?.schains || []; const chainBidders = new Set(chains.flatMap((item) => item.bidders)); @@ -221,7 +217,7 @@ const PBS_CONVERTER = ortbConverter({ .filter((req) => !chainBidders.has(req.bidderCode)) // schain defined in s2sConfig.extPrebid takes precedence .map((req) => ({ bidders: [req.bidderCode], - schain: req?.bids?.[0]?.schain + schain: req?.bids?.[0]?.ortb2?.source?.schain }))) .filter(({bidders, schain}) => bidders?.length > 0 && schain) .reduce((chains, {bidders, schain}) => { @@ -237,6 +233,10 @@ const PBS_CONVERTER = ortbConverter({ if (chains.length) { deepSetValue(ortbRequest, 'ext.prebid.schains', chains); } + }, + extPrebidAliases(orig, ortbRequest, proxyBidderRequest, context) { + // override alias processing to do it for each bidder in the request + context.actualBidderRequests.forEach(req => orig(ortbRequest, req, context)); } }, [RESPONSE]: { diff --git a/modules/prismaBidAdapter.js b/modules/prismaBidAdapter.js index efa390bc9bd..8506d29f82b 100644 --- a/modules/prismaBidAdapter.js +++ b/modules/prismaBidAdapter.js @@ -79,7 +79,8 @@ export const spec = { payload.gdprConsent = ''; } if (bidderRequest.uspConsent) { payload.uspConsent = bidderRequest.uspConsent; } - if (bidderRequest.schain) { payload.schain = bidderRequest.schain; } + const schain = bidderRequest?.ortb2?.source?.ext?.schain; + if (schain) { payload.schain = schain; } if (userEids !== null) payload.userEids = userEids; }; payload.connectionType = getConnectionType(); diff --git a/modules/pubgeniusBidAdapter.js b/modules/pubgeniusBidAdapter.js index 19260e65e60..7cb4d2df127 100644 --- a/modules/pubgeniusBidAdapter.js +++ b/modules/pubgeniusBidAdapter.js @@ -71,7 +71,7 @@ export const spec = { deepSetValue(data, 'regs.ext.us_privacy', usp); } - const schain = bidRequests[0].schain; + const schain = bidRequests[0]?.ortb2?.source?.ext?.schain; if (schain) { deepSetValue(data, 'source.ext.schain', schain); } diff --git a/modules/pubwiseBidAdapter.js b/modules/pubwiseBidAdapter.js index 291b637749a..01734e514b5 100644 --- a/modules/pubwiseBidAdapter.js +++ b/modules/pubwiseBidAdapter.js @@ -254,9 +254,10 @@ export const spec = { // passing transactionId in source.tid deepSetValue(payload, 'source.tid', bidderRequest?.ortb2?.source?.tid); - // schain - if (validBidRequests[0].schain) { - deepSetValue(payload, 'source.ext.schain', validBidRequests[0].schain); + // schain - check for schain in the new location + const schain = validBidRequests[0]?.ortb2?.source?.ext?.schain; + if (schain) { + deepSetValue(payload, 'source.ext.schain', schain); } // gdpr consent diff --git a/modules/qwarryBidAdapter.js b/modules/qwarryBidAdapter.js index 4b3e8fa8a19..73191f099a0 100644 --- a/modules/qwarryBidAdapter.js +++ b/modules/qwarryBidAdapter.js @@ -29,7 +29,7 @@ export const spec = { requestId: bidderRequest.bidderRequestId, bids, referer: bidderRequest.refererInfo.page, - schain: validBidRequests[0].schain + schain: validBidRequests[0]?.ortb2?.source?.ext?.schain } if (bidderRequest && bidderRequest.gdprConsent) { diff --git a/modules/rhythmoneBidAdapter.js b/modules/rhythmoneBidAdapter.js index 3ab8b79df81..f710c1a079e 100644 --- a/modules/rhythmoneBidAdapter.js +++ b/modules/rhythmoneBidAdapter.js @@ -168,10 +168,11 @@ function RhythmOneBidAdapter() { } } }; - if (BRs[0].schain) { + const schain = BRs[0]?.ortb2?.source?.ext?.schain; + if (schain) { bid.source = { 'ext': { - 'schain': BRs[0].schain + 'schain': schain } } } diff --git a/modules/richaudienceBidAdapter.js b/modules/richaudienceBidAdapter.js index 9bd395fc166..a241b2fa19e 100644 --- a/modules/richaudienceBidAdapter.js +++ b/modules/richaudienceBidAdapter.js @@ -54,7 +54,7 @@ export const spec = { scr_rsl: raiGetResolution(), cpuc: (typeof window.navigator != 'undefined' ? window.navigator.hardwareConcurrency : null), kws: bid.params.keywords, - schain: bid.schain, + schain: bid?.ortb2?.source?.ext?.schain, gpid: raiSetPbAdSlot(bid), dsa: setDSA(bid), userData: deepAccess(bid, 'ortb2.user.data') diff --git a/modules/rtbhouseBidAdapter.js b/modules/rtbhouseBidAdapter.js index 5a46ed61594..4b261055bb8 100644 --- a/modules/rtbhouseBidAdapter.js +++ b/modules/rtbhouseBidAdapter.js @@ -49,8 +49,9 @@ export const spec = { request.regs = {ext: {gdpr: gdpr}}; request.user = {ext: {consent: consentStr}}; } - if (validBidRequests[0].schain) { - const schain = mapSchain(validBidRequests[0].schain); + const bidSchain = validBidRequests[0]?.ortb2?.source?.ext?.schain; + if (bidSchain) { + const schain = mapSchain(bidSchain); if (schain) { request.ext = { schain: schain, diff --git a/modules/rubiconBidAdapter.js b/modules/rubiconBidAdapter.js index 516a1112025..d320e2710ba 100644 --- a/modules/rubiconBidAdapter.js +++ b/modules/rubiconBidAdapter.js @@ -617,8 +617,9 @@ export const spec = { } // if SupplyChain is supplied and contains all required fields - if (bidRequest.schain && hasValidSupplyChainParams(bidRequest.schain)) { - data.rp_schain = spec.serializeSupplyChain(bidRequest.schain); + const schain = bidRequest?.ortb2?.source?.ext?.schain; + if (schain && hasValidSupplyChainParams(schain)) { + data.rp_schain = spec.serializeSupplyChain(schain); } return data; diff --git a/modules/schain.js b/modules/schain.js deleted file mode 100644 index 726679b133f..00000000000 --- a/modules/schain.js +++ /dev/null @@ -1,207 +0,0 @@ -import {config} from '../src/config.js'; -import adapterManager from '../src/adapterManager.js'; -import { - _each, - deepAccess, - deepClone, - deepSetValue, - isArray, - isInteger, - isNumber, - isPlainObject, - isStr, - logError, - logWarn -} from '../src/utils.js'; -import {registerOrtbProcessor, REQUEST} from '../src/pbjsORTB.js'; - -// https://github.com/InteractiveAdvertisingBureau/openrtb/blob/master/supplychainobject.md - -const schainErrorPrefix = 'Invalid schain object found: '; -const shouldBeAString = ' should be a string'; -const shouldBeAnInteger = ' should be an Integer'; -const shouldBeAnObject = ' should be an object'; -const shouldBeAnArray = ' should be an Array'; -const MODE = { - STRICT: 'strict', - RELAXED: 'relaxed', - OFF: 'off' -}; -const MODES = []; // an array of modes -_each(MODE, mode => MODES.push(mode)); - -// validate the supply chain object -export function isSchainObjectValid(schainObject, returnOnError) { - let failPrefix = 'Detected something wrong within an schain config:'; - let failMsg = ''; - - function appendFailMsg(msg) { - failMsg += '\n' + msg; - } - - function printFailMsg() { - if (returnOnError === true) { - logError(failPrefix, schainObject, failMsg); - } else { - logWarn(failPrefix, schainObject, failMsg); - } - } - - if (!isPlainObject(schainObject)) { - appendFailMsg(`schain.config` + shouldBeAnObject); - printFailMsg(); - if (returnOnError) return false; - } - - // complete: Integer - if (!isNumber(schainObject.complete) || !isInteger(schainObject.complete)) { - appendFailMsg(`schain.config.complete` + shouldBeAnInteger); - } - - // ver: String - if (!isStr(schainObject.ver)) { - appendFailMsg(`schain.config.ver` + shouldBeAString); - } - - // ext: Object [optional] - if (schainObject.hasOwnProperty('ext')) { - if (!isPlainObject(schainObject.ext)) { - appendFailMsg(`schain.config.ext` + shouldBeAnObject); - } - } - - // nodes: Array of objects - if (!isArray(schainObject.nodes)) { - appendFailMsg(`schain.config.nodes` + shouldBeAnArray); - printFailMsg(); - if (returnOnError) return false; - } else { - schainObject.nodes.forEach((node, index) => { - // asi: String - if (!isStr(node.asi)) { - appendFailMsg(`schain.config.nodes[${index}].asi` + shouldBeAString); - } - - // sid: String - if (!isStr(node.sid)) { - appendFailMsg(`schain.config.nodes[${index}].sid` + shouldBeAString); - } - - // hp: Integer - if (!isNumber(node.hp) || !isInteger(node.hp)) { - appendFailMsg(`schain.config.nodes[${index}].hp` + shouldBeAnInteger); - } - - // rid: String [Optional] - if (node.hasOwnProperty('rid')) { - if (!isStr(node.rid)) { - appendFailMsg(`schain.config.nodes[${index}].rid` + shouldBeAString); - } - } - - // name: String [Optional] - if (node.hasOwnProperty('name')) { - if (!isStr(node.name)) { - appendFailMsg(`schain.config.nodes[${index}].name` + shouldBeAString); - } - } - - // domain: String [Optional] - if (node.hasOwnProperty('domain')) { - if (!isStr(node.domain)) { - appendFailMsg(`schain.config.nodes[${index}].domain` + shouldBeAString); - } - } - - // ext: Object [Optional] - if (node.hasOwnProperty('ext')) { - if (!isPlainObject(node.ext)) { - appendFailMsg(`schain.config.nodes[${index}].ext` + shouldBeAnObject); - } - } - }); - } - - if (failMsg.length > 0) { - printFailMsg(); - if (returnOnError) { - return false; - } - } - - return true; -} - -export function isValidSchainConfig(schainObject) { - if (schainObject === undefined) { - return false; - } - if (!isPlainObject(schainObject)) { - logError(schainErrorPrefix + 'the following schain config will not be used as schain is not an object.', schainObject); - return false; - } - return true; -} - -function resolveSchainConfig(schainObject, bidder) { - let mode = MODE.STRICT; - - if (isValidSchainConfig(schainObject)) { - if (isStr(schainObject.validation) && MODES.indexOf(schainObject.validation) != -1) { - mode = schainObject.validation; - } - if (mode === MODE.OFF) { - // no need to validate - return schainObject.config; - } else { - // if strict mode and config is invalid, reject config + throw error; otherwise allow config to go through - if (isSchainObjectValid(schainObject.config, !!(mode === MODE.STRICT))) { - return schainObject.config; - } else { - logError(schainErrorPrefix + `due to the 'strict' validation setting, this schain config will not be passed to bidder '${bidder}'. See above error for details.`); - } - } - } - return null; -} - -export function makeBidRequestsHook(fn, bidderRequests) { - function getSchainForBidder(bidder) { - let bidderSchain = bidderConfigs[bidder] && bidderConfigs[bidder].schain; - return bidderSchain || globalSchainConfig; - } - - const globalSchainConfig = config.getConfig('schain'); - const bidderConfigs = config.getBidderConfig(); - - bidderRequests.forEach(bidderRequest => { - let bidder = bidderRequest.bidderCode; - let schainConfig = getSchainForBidder(bidder); - - bidderRequest.bids.forEach(bid => { - let result = resolveSchainConfig(schainConfig, bidder); - if (result) { - bid.schain = deepClone(result); - } - }); - }); - - fn(bidderRequests); -} - -export function init() { - adapterManager.makeBidRequests.after(makeBidRequestsHook); -} - -init() - -export function setOrtbSourceExtSchain(ortbRequest, bidderRequest, context) { - if (!deepAccess(ortbRequest, 'source.ext.schain')) { - const schain = deepAccess(context, 'bidRequests.0.schain'); - if (schain) { - deepSetValue(ortbRequest, 'source.ext.schain', schain); - } - } -} - -registerOrtbProcessor({type: REQUEST, name: 'sourceExtSchain', fn: setOrtbSourceExtSchain}); diff --git a/modules/schain.md b/modules/schain.md deleted file mode 100644 index f43cf0f0d07..00000000000 --- a/modules/schain.md +++ /dev/null @@ -1,51 +0,0 @@ -# schain module - -Aggregators who manage Prebid wrappers on behalf of multiple publishers and handle payment on behalf of the publishers -need to declare their intermediary status in the Supply Chain Object. As the Supply Chain Object spec prohibits SSPs from adding -upstream intermediaries, Prebid requests in this case need to come with the schain information. In this use case, it's cumbersome -to have every bidder in the wrapper separately configured the same schain information. - -Refer: -- https://iabtechlab.com/sellers-json/ -- https://github.com/InteractiveAdvertisingBureau/openrtb/blob/master/supplychainobject.md - -## Sample code for passing the schain object -``` -pbjs.setConfig( { - "schain": { - "validation": "strict", - "config": { - "ver":"1.0", - "complete": 1, - "nodes": [ - { - "asi":"indirectseller.com", - "sid":"00001", - "hp":1 - }, - - { - "asi":"indirectseller-2.com", - "sid":"00002", - "hp":1 - } - ] - } - } -}); -``` - -## Workflow -The schain module is not enabled by default as it may not be necessary for all publishers. -If required, schain module can be included as following -``` - $ gulp build --modules=schain,pubmaticBidAdapter,openxBidAdapter,rubiconBidAdapter,sovrnBidAdapter -``` -The schain module will validate the schain object passed using pbjs.setConfig API. -If the schain object is valid then it will be passed on to bidders/adapters in ```validBidRequests[].schain``` -You may refer pubmaticBidAdapter implementaion for the same. - -## Validation modes -- ```strict```: It is the default validation mode. In this mode, schain object will not be passed to adapters if it is invalid. Errors are thrown for invalid schain object. -- ```relaxed```: In this mode, errors are thrown for an invalid schain object but the invalid schain object is still passed to adapters. -- ```off```: In this mode, no validations are performed and schain object is passed as is to adapters. diff --git a/modules/seedtagBidAdapter.js b/modules/seedtagBidAdapter.js index 745fd4d7088..3c6b69d3cdc 100644 --- a/modules/seedtagBidAdapter.js +++ b/modules/seedtagBidAdapter.js @@ -320,8 +320,9 @@ export const spec = { payload['uspConsent'] = bidderRequest.uspConsent; } - if (validBidRequests[0].schain) { - payload.schain = validBidRequests[0].schain; + const schain = validBidRequests[0]?.ortb2?.source?.ext?.schain; + if (schain) { + payload.schain = schain; } let coppa = config.getConfig('coppa'); diff --git a/modules/sharethroughBidAdapter.js b/modules/sharethroughBidAdapter.js index 5c5c8fe6eec..ab63b2f36e8 100644 --- a/modules/sharethroughBidAdapter.js +++ b/modules/sharethroughBidAdapter.js @@ -57,7 +57,7 @@ export const sharethroughAdapterSpec = { ext: { version: '$prebid.version$', str: VERSION, - schain: bidRequests[0].schain, + schain: bidRequests[0]?.ortb2?.source?.ext?.schain, }, }, bcat: deepAccess(bidderRequest.ortb2, 'bcat') || bidRequests[0].params.bcat || [], diff --git a/modules/smaatoBidAdapter.js b/modules/smaatoBidAdapter.js index e75c91b4015..cecbd6fa162 100644 --- a/modules/smaatoBidAdapter.js +++ b/modules/smaatoBidAdapter.js @@ -338,7 +338,7 @@ const converter = ortbConverter({ request.source = { ext: { - schain: bidRequest.schain + schain: bidRequest?.ortb2?.source?.ext?.schain } }; request.ext = { diff --git a/modules/smartadserverBidAdapter.js b/modules/smartadserverBidAdapter.js index 790b94165cb..73073e3da31 100644 --- a/modules/smartadserverBidAdapter.js +++ b/modules/smartadserverBidAdapter.js @@ -204,7 +204,7 @@ export const spec = { timeout: config.getConfig('bidderTimeout'), bidId: bid.bidId, prebidVersion: '$prebid.version$', - schain: spec.serializeSupplyChain(bid.schain), + schain: spec.serializeSupplyChain(bid?.ortb2?.source?.ext?.schain), sda: sellerDefinedAudience, sdc: sellerDefinedContext }; diff --git a/modules/smartxBidAdapter.js b/modules/smartxBidAdapter.js index 1442199cd6d..55254e9bd82 100644 --- a/modules/smartxBidAdapter.js +++ b/modules/smartxBidAdapter.js @@ -216,11 +216,12 @@ export const spec = { userExt.fpc = pubcid; } - // Add schain object if available - if (bid && bid.schain) { + // Add schain object if available from the new location + const schain = bid?.ortb2?.source?.ext?.schain; + if (bid && schain) { requestPayload['source'] = { ext: { - schain: bid.schain + schain: schain } }; } diff --git a/modules/smartyadsBidAdapter.js b/modules/smartyadsBidAdapter.js index de7c61b7163..67904d10fe6 100644 --- a/modules/smartyadsBidAdapter.js +++ b/modules/smartyadsBidAdapter.js @@ -63,8 +63,9 @@ export const spec = { traffic: traff, publisherId: bid.params.accountid }); - if (bid.schain) { - placements.schain = bid.schain; + const schain = bid?.ortb2?.source?.ext?.schain; + if (schain) { + placements.schain = schain; } } diff --git a/modules/smilewantedBidAdapter.js b/modules/smilewantedBidAdapter.js index a78c60f8308..cbde1229ac8 100644 --- a/modules/smilewantedBidAdapter.js +++ b/modules/smilewantedBidAdapter.js @@ -84,7 +84,7 @@ export const spec = { */ positionType: bid.params.positionType || '', prebidVersion: '$prebid.version$', - schain: serializeSupplyChain(bid.schain, ['asi', 'sid', 'hp', 'rid', 'name', 'domain', 'ext']), + schain: serializeSupplyChain(bid?.ortb2?.source?.ext?.schain, ['asi', 'sid', 'hp', 'rid', 'name', 'domain', 'ext']), }; const floor = getBidFloor(bid); diff --git a/modules/snigelBidAdapter.js b/modules/snigelBidAdapter.js index 60d5becac36..55a8d4d5246 100644 --- a/modules/snigelBidAdapter.js +++ b/modules/snigelBidAdapter.js @@ -58,7 +58,7 @@ export const spec = { uspConsent: deepAccess(bidderRequest, 'uspConsent'), coppa: getConfig('coppa'), eids: deepAccess(bidRequests, '0.userIdAsEids'), - schain: deepAccess(bidRequests, '0.schain'), + schain: deepAccess(bidRequests, '0.ortb2.source.ext.schain'), page: getPage(bidderRequest), topframe: inIframe() === true ? 0 : 1, device: { diff --git a/modules/sonobiBidAdapter.js b/modules/sonobiBidAdapter.js index b67b0029179..efde29b8cc9 100644 --- a/modules/sonobiBidAdapter.js +++ b/modules/sonobiBidAdapter.js @@ -133,8 +133,9 @@ export const spec = { } } - if (validBidRequests[0].schain) { - payload.schain = JSON.stringify(validBidRequests[0].schain); + const schain = validBidRequests[0]?.ortb2?.source?.ext?.schain; + if (schain) { + payload.schain = JSON.stringify(schain); } const eids = deepAccess(validBidRequests[0], 'userIdAsEids'); diff --git a/modules/sovrnBidAdapter.js b/modules/sovrnBidAdapter.js index 49d7924537f..b117f53d083 100644 --- a/modules/sovrnBidAdapter.js +++ b/modules/sovrnBidAdapter.js @@ -86,8 +86,9 @@ export const spec = { }) } - if (bid.schain) { - schain = schain || bid.schain + const bidSchain = bid?.ortb2?.source?.ext?.schain; + if (bidSchain) { + schain = schain || bidSchain } iv = iv || getBidIdParameter('iv', bid.params) diff --git a/modules/sspBCBidAdapter.js b/modules/sspBCBidAdapter.js index 4da5ba87633..4e759ddf4b9 100644 --- a/modules/sspBCBidAdapter.js +++ b/modules/sspBCBidAdapter.js @@ -628,7 +628,7 @@ const spec = { const ref = bidderRequest.refererInfo.ref; const { source = {}, regs = {} } = ortb2 || {}; - source.schain = setOnAny(validBidRequests, 'schain'); + source.schain = setOnAny(validBidRequests, 'ortb2.source.ext.schain'); const payload = { id: bidderRequest.bidderRequestId, diff --git a/modules/stroeerCoreBidAdapter.js b/modules/stroeerCoreBidAdapter.js index 20e7ea55ca0..1fd79dd47c4 100644 --- a/modules/stroeerCoreBidAdapter.js +++ b/modules/stroeerCoreBidAdapter.js @@ -55,7 +55,7 @@ export const spec = { mpa: isMainPageAccessible(), timeout: bidderRequest.timeout - (Date.now() - bidderRequest.auctionStart), url: refererInfo.page, - schain: anyBid.schain + schain: anyBid?.ortb2?.source?.ext?.schain }; const eids = anyBid.userIdAsEids; diff --git a/modules/stvBidAdapter.js b/modules/stvBidAdapter.js index 2687cbf5b37..646ef591246 100644 --- a/modules/stvBidAdapter.js +++ b/modules/stvBidAdapter.js @@ -69,8 +69,9 @@ export const spec = { if (!isVideoRequest(bidRequest)) { payload._f = 'html'; } - if (bidRequest.schain) { - payload.schain = serializeSChain(bidRequest.schain); + const schain = bidRequest?.ortb2?.source?.ext?.schain; + if (schain) { + payload.schain = serializeSChain(schain); } else { delete payload.schain; } diff --git a/modules/targetVideoBidAdapter.js b/modules/targetVideoBidAdapter.js index c149e80749d..c911e0dc7cb 100644 --- a/modules/targetVideoBidAdapter.js +++ b/modules/targetVideoBidAdapter.js @@ -98,9 +98,10 @@ export const spec = { }; }; - if (bidRequests[0].schain) { + const schain = bidRequests[0]?.ortb2?.source?.ext?.schain; + if (schain) { payload.source = { - ext: { schain: bidRequests[0].schain } + ext: { schain: schain } }; } @@ -110,7 +111,7 @@ export const spec = { case BANNER: { const tags = bidRequests.map(createVideoTag); - const schain = bidRequests[0].schain; + const schain = bidRequests[0]?.ortb2?.source?.ext?.schain; const payload = { tags, diff --git a/modules/teadsBidAdapter.js b/modules/teadsBidAdapter.js index 2db1da57500..73b6b421dc1 100644 --- a/modules/teadsBidAdapter.js +++ b/modules/teadsBidAdapter.js @@ -87,8 +87,9 @@ export const spec = { const firstBidRequest = validBidRequests[0]; - if (firstBidRequest.schain) { - payload.schain = firstBidRequest.schain; + const schain = firstBidRequest?.ortb2?.source?.ext?.schain; + if (schain) { + payload.schain = schain; } let gpp = bidderRequest.gppConsent; diff --git a/modules/themoneytizerBidAdapter.js b/modules/themoneytizerBidAdapter.js index 9f187478fa7..11a609b7874 100644 --- a/modules/themoneytizerBidAdapter.js +++ b/modules/themoneytizerBidAdapter.js @@ -33,7 +33,7 @@ export const spec = { ortb2: bidderRequest.ortb2, eids: bidRequest.userIdAsEids, id: bidRequest.auctionId, - schain: bidRequest.schain, + schain: bidRequest?.ortb2?.source?.ext?.schain, version: '$prebid.version$', excl_sync: window.tmzrBidderExclSync }; diff --git a/modules/tripleliftBidAdapter.js b/modules/tripleliftBidAdapter.js index 0c8bd330e11..aa2865c5251 100644 --- a/modules/tripleliftBidAdapter.js +++ b/modules/tripleliftBidAdapter.js @@ -154,7 +154,7 @@ function _filterSid(sid) { function _buildPostBody(bidRequests, bidderRequest) { let data = {}; - let { schain } = bidRequests[0]; + const schain = bidRequests[0]?.ortb2?.source?.ext?.schain; const globalFpd = _getGlobalFpd(bidderRequest); data.imp = bidRequests.map(function(bidRequest, index) { diff --git a/modules/ttdBidAdapter.js b/modules/ttdBidAdapter.js index e780c467441..d3b05232c3d 100644 --- a/modules/ttdBidAdapter.js +++ b/modules/ttdBidAdapter.js @@ -81,8 +81,9 @@ function getSource(validBidRequests, bidderRequest) { let source = { tid: bidderRequest?.ortb2?.source?.tid, }; - if (validBidRequests[0].schain) { - utils.deepSetValue(source, 'ext.schain', validBidRequests[0].schain); + const schain = validBidRequests[0]?.ortb2?.source?.ext?.schain; + if (schain) { + utils.deepSetValue(source, 'ext.schain', schain); } return source; } diff --git a/modules/ucfunnelBidAdapter.js b/modules/ucfunnelBidAdapter.js index f62e4bc53a9..f2b35e992d7 100644 --- a/modules/ucfunnelBidAdapter.js +++ b/modules/ucfunnelBidAdapter.js @@ -262,7 +262,8 @@ function getRequestData(bid, bidderRequest) { const language = navigator.language; const dnt = (navigator.doNotTrack == 'yes' || navigator.doNotTrack == '1' || navigator.msDoNotTrack == '1') ? 1 : 0; const userIdTdid = (bid.userId && bid.userId.tdid) ? bid.userId.tdid : ''; - const supplyChain = getSupplyChain(bid.schain); + const schain = bid?.ortb2?.source?.ext?.schain; + const supplyChain = getSupplyChain(schain); const bidFloor = getFloor(bid, size, bid.mediaTypes); const gpid = deepAccess(bid, 'ortb2Imp.ext.gpid'); // general bid data diff --git a/modules/undertoneBidAdapter.js b/modules/undertoneBidAdapter.js index ceedc8aa321..e2888af7c1e 100644 --- a/modules/undertoneBidAdapter.js +++ b/modules/undertoneBidAdapter.js @@ -68,8 +68,9 @@ export const spec = { 'uids': validBidRequests[0].userId, 'pageSize': pageSizeArray }; - if (validBidRequests[0].schain) { - commons.schain = validBidRequests[0].schain; + const schain = validBidRequests[0]?.ortb2?.source?.ext?.schain; + if (schain) { + commons.schain = schain; } const payload = { 'x-ut-hb-params': [], diff --git a/modules/validationFpdModule/config.js b/modules/validationFpdModule/config.js index c87265fa1df..89201f55ed4 100644 --- a/modules/validationFpdModule/config.js +++ b/modules/validationFpdModule/config.js @@ -145,5 +145,38 @@ export const ORTB_MAP = { type: TYPES.object, isArray: true, childType: TYPES.string + }, + source: { + type: TYPES.object, + children: { + ext: { + type: TYPES.object, + isArray: false + }, + schain: { + type: TYPES.object, + children: { + complete: { type: TYPES.number }, + ver: { type: TYPES.string }, + nodes: { + type: TYPES.object, + isArray: true, + childType: TYPES.object, + required: ['asi', 'sid', 'hp'], + children: { + asi: { type: TYPES.string }, + sid: { type: TYPES.string }, + hp: { type: TYPES.number }, + rid: { type: TYPES.string }, + name: { type: TYPES.string }, + domain: { type: TYPES.string }, + ext: { type: TYPES.object } + } + }, + ext: { type: TYPES.object } + }, + required: ['complete', 'nodes', 'ver'] + } + } } } diff --git a/modules/vdoaiBidAdapter.js b/modules/vdoaiBidAdapter.js index 6ef5b6ebb3e..a570b6a43f3 100644 --- a/modules/vdoaiBidAdapter.js +++ b/modules/vdoaiBidAdapter.js @@ -169,7 +169,7 @@ function vdoBuildPlacement(vdoBidRequest) { ortb2Imp: vdoBidRequest.ortb2Imp, publisherId: vdoBidRequest.params.publisherId, userIdAsEids: vdoBidRequest.userIdAsEids, - supplyChain: vdoBidRequest.schain, + supplyChain: vdoBidRequest?.ortb2?.source?.ext?.schain, custom1: vdoBidRequest.params.custom1, custom2: vdoBidRequest.params.custom2, custom3: vdoBidRequest.params.custom3, diff --git a/modules/videobyteBidAdapter.js b/modules/videobyteBidAdapter.js index 09c2f747dc6..dca559806fd 100644 --- a/modules/videobyteBidAdapter.js +++ b/modules/videobyteBidAdapter.js @@ -251,8 +251,9 @@ function buildRequestData(bidRequest, bidderRequest) { } // adding schain object - if (bidRequest.schain) { - deepSetValue(openrtbRequest, 'source.ext.schain', bidRequest.schain); + const schain = bidRequest?.ortb2?.source?.ext?.schain; + if (schain) { + deepSetValue(openrtbRequest, 'source.ext.schain', schain); openrtbRequest.source.ext.schain.nodes[0].rid = openrtbRequest.id; } diff --git a/modules/vidoomyBidAdapter.js b/modules/vidoomyBidAdapter.js index 894d3191311..ef1b092c658 100644 --- a/modules/vidoomyBidAdapter.js +++ b/modules/vidoomyBidAdapter.js @@ -158,7 +158,7 @@ const buildRequests = (validBidRequests, bidderRequest) => { dt: /Mobi/.test(navigator.userAgent) ? 2 : 1, pid: bid.params.pid, requestId: bid.bidId, - schain: serializeSupplyChainObj(bid.schain) || '', + schain: serializeSupplyChainObj(bid?.ortb2?.source?.ext?.schain) || '', eids: eids || '', bidfloor: floor, d: getDomainWithoutSubdomain(hostname), // 'vidoomy.com', diff --git a/modules/viouslyBidAdapter.js b/modules/viouslyBidAdapter.js index f0a10f936b9..07d7874973e 100644 --- a/modules/viouslyBidAdapter.js +++ b/modules/viouslyBidAdapter.js @@ -132,8 +132,9 @@ export const spec = { } // Schain - if (validBidRequests[0].schain) { - payload.schain = validBidRequests[0].schain; + const schain = validBidRequests[0]?.ortb2?.source?.ext?.schain; + if (schain) { + payload.schain = schain; } // Currency payload.currency_code = CURRENCY; diff --git a/modules/visxBidAdapter.js b/modules/visxBidAdapter.js index 510106845db..f305a3d3889 100644 --- a/modules/visxBidAdapter.js +++ b/modules/visxBidAdapter.js @@ -87,7 +87,8 @@ export const spec = { imp.push(impObj); bidsMap[bid.bidId] = bid; } - const { params: { uid }, schain, userId, userIdAsEids } = bid; + const { params: { uid }, userId, userIdAsEids } = bid; + const schain = bid?.ortb2?.source?.ext?.schain; if (!payloadSchain && schain) { payloadSchain = schain; } diff --git a/modules/voxBidAdapter.js b/modules/voxBidAdapter.js index 3497f3cee07..32e7f4b85c0 100644 --- a/modules/voxBidAdapter.js +++ b/modules/voxBidAdapter.js @@ -27,7 +27,7 @@ function buildBidRequests(validBidRequests, bidderRequest) { const params = bid.params; const bidRequest = { floorInfo, - schain: bid.schain, + schain: bid?.ortb2?.source?.ext?.schain, userId: bid.userId, bidId: bid.bidId, // TODO: fix transactionId leak: https://github.com/prebid/Prebid.js/issues/9781 diff --git a/modules/vuukleBidAdapter.js b/modules/vuukleBidAdapter.js index f3ab2562ef4..6dd5709fe78 100644 --- a/modules/vuukleBidAdapter.js +++ b/modules/vuukleBidAdapter.js @@ -31,7 +31,7 @@ export const spec = { rnd: Math.random(), bidId: bid.bidId, source: 'pbjs', - schain: JSON.stringify(bid.schain), + schain: JSON.stringify(bid?.ortb2?.source?.ext?.schain), requestId: bid.bidderRequestId, tmax: bidderRequest.timeout, gdpr: (bidderRequest.gdprConsent && bidderRequest.gdprConsent.gdprApplies) ? 1 : 0, diff --git a/modules/winrBidAdapter.js b/modules/winrBidAdapter.js index 06c6f808244..61e12cdc1b4 100644 --- a/modules/winrBidAdapter.js +++ b/modules/winrBidAdapter.js @@ -193,7 +193,7 @@ export const spec = { const memberIdBid = ((bidRequests) || []).find(hasMemberId); const member = memberIdBid ? parseInt(memberIdBid.params.member, 10) : 0; - const schain = bidRequests[0].schain; + const schain = bidRequests[0]?.ortb2?.source?.ext?.schain; const payload = { tags: [...tags], diff --git a/modules/yahooAdsBidAdapter.js b/modules/yahooAdsBidAdapter.js index 6e67df84b2f..a72deab51ae 100644 --- a/modules/yahooAdsBidAdapter.js +++ b/modules/yahooAdsBidAdapter.js @@ -328,9 +328,9 @@ function generateOpenRtbObject(bidderRequest, bid) { outBoundBidRequest = appendFirstPartyData(outBoundBidRequest, bid); }; - const schainData = deepAccess(bid, 'schain.nodes'); - if (isArray(schainData) && schainData.length > 0) { - outBoundBidRequest.source.ext.schain = bid.schain; + const schain = bid?.ortb2?.source?.ext?.schain; + if (schain && isArray(schain.nodes) && schain.nodes.length > 0) { + outBoundBidRequest.source.ext.schain = schain; outBoundBidRequest.source.ext.schain.nodes[0].rid = outBoundBidRequest.id; }; diff --git a/modules/yieldlabBidAdapter.js b/modules/yieldlabBidAdapter.js index 37d1de25240..42633be8730 100644 --- a/modules/yieldlabBidAdapter.js +++ b/modules/yieldlabBidAdapter.js @@ -75,8 +75,9 @@ export const spec = { query[prop] = bid.params.customParams[prop]; } } - if (bid.schain && isPlainObject(bid.schain) && Array.isArray(bid.schain.nodes)) { - query.schain = createSchainString(bid.schain); + const schain = bid?.ortb2?.source?.ext?.schain; + if (schain && isPlainObject(schain) && Array.isArray(schain.nodes)) { + query.schain = createSchainString(schain); } const iabContent = getContentObject(bid); diff --git a/modules/yieldliftBidAdapter.js b/modules/yieldliftBidAdapter.js index d4cafd77c2d..ba53f2a6340 100644 --- a/modules/yieldliftBidAdapter.js +++ b/modules/yieldliftBidAdapter.js @@ -57,8 +57,9 @@ export const spec = { }; // adding schain object - if (validBidRequests[0].schain) { - deepSetValue(openrtbRequest, 'source.ext.schain', validBidRequests[0].schain); + const schain = validBidRequests[0]?.ortb2?.source?.ext?.schain; + if (schain) { + deepSetValue(openrtbRequest, 'source.ext.schain', schain); } // Attaching GDPR Consent Params diff --git a/modules/yieldmoBidAdapter.js b/modules/yieldmoBidAdapter.js index b4aec7982b6..973cdcd4e93 100644 --- a/modules/yieldmoBidAdapter.js +++ b/modules/yieldmoBidAdapter.js @@ -138,8 +138,9 @@ export const spec = { if (criteoId) { serverRequest.cri_prebid = criteoId; } - if (request.schain) { - serverRequest.schain = JSON.stringify(request.schain); + const schain = deepAccess(request, 'ortb2.source.ext.schain'); + if (schain) { + serverRequest.schain = JSON.stringify(schain); } if (deepAccess(request, 'params.lr_env')) { serverRequest.ats_envelope = request.params.lr_env; @@ -413,7 +414,7 @@ function getId(request, idType) { * @return Object OpenRTB request object */ function openRtbRequest(bidRequests, bidderRequest) { - const schain = bidRequests[0].schain; + const schain = bidRequests[0]?.ortb2?.source?.ext?.schain; let openRtbRequest = { id: bidRequests[0].bidderRequestId, tmax: bidderRequest.timeout || 400, diff --git a/modules/zeta_global_sspBidAdapter.js b/modules/zeta_global_sspBidAdapter.js index 62e60db020f..04655b39499 100644 --- a/modules/zeta_global_sspBidAdapter.js +++ b/modules/zeta_global_sspBidAdapter.js @@ -178,11 +178,12 @@ export const spec = { deepSetValue(payload, 'regs.ext.us_privacy', bidderRequest.uspConsent); } - // schain - if (validBidRequests[0].schain) { + // schain - check for schain in the new location + const schain = validBidRequests[0]?.ortb2?.source?.ext?.schain; + if (schain) { payload.source = { ext: { - schain: validBidRequests[0].schain + schain: schain } } } diff --git a/src/adapterManager.js b/src/adapterManager.js index f6b3d85b351..0db1e28d33a 100644 --- a/src/adapterManager.js +++ b/src/adapterManager.js @@ -40,6 +40,7 @@ import { import {getRefererInfo} from './refererDetection.js'; import {GDPR_GVLIDS, gdprDataHandler, gppDataHandler, uspDataHandler, } from './consentHandler.js'; import * as events from './events.js'; +import {moveSchainToExt} from './fpd/schain.js'; import {EVENTS, S2S} from './constants.js'; import {useMetrics} from './utils/perfMetrics.js'; import {auctionManager} from './auctionManager.js'; @@ -323,8 +324,10 @@ adapterManager.makeBidRequests = hook('sync', function (adUnits, auctionStart, a ? s2sActivityParams : activityParams(MODULE_TYPE_BIDDER, bidderRequest.bidderCode) ); + const merged = mergeDeep({source: {tid: auctionId}}, ortb2, bidderOrtb2[bidderRequest.bidderCode]); moveUserEidsToExt(merged); + moveSchainToExt(merged, bidderOrtb2[bidderRequest.bidderCode]); const fpd = Object.freeze(redact.ortb2(merged)); bidderRequest.ortb2 = fpd; bidderRequest.bids = bidderRequest.bids.map((bid) => { diff --git a/src/fpd/schain.js b/src/fpd/schain.js new file mode 100644 index 00000000000..f2e6d3761a6 --- /dev/null +++ b/src/fpd/schain.js @@ -0,0 +1,85 @@ +/** + * This module handles supply chain (schain) processing and relocation + * between different locations in the ortb2 structure + */ +import {config} from '../config.js'; +import {deepClone, logInfo} from '../utils.js'; + +/** + * Applies schain from config to ortb2 fragments with precedence rules + * @param {Object} ortb2Fragments - The ortb2 fragments object + * @returns {Object} - The updated ortb2Fragments object + */ +export function schainPrecedence(ortb2Fragments) { + if (!ortb2Fragments) return ortb2Fragments; + + // Apply global schain config if available + // config's schain will have more precedence over ortb2.source.schain + const globalSchainConfig = config.getConfig('schain'); + if (globalSchainConfig && globalSchainConfig.config) { + if (!ortb2Fragments?.global?.source?.schain) { + logInfo('Applying global schain config with precedence'); + applySchainToPath(ortb2Fragments, 'global.source', globalSchainConfig.config); + } else { + logInfo('Preserving existing global.source.schain from ortb2'); + } + } + + // Apply bidder-specific schain configs + const bidderConfigs = config.getBidderConfig(); + if (!bidderConfigs) return ortb2Fragments; + + Object.entries(bidderConfigs) + .filter(([_, cfg]) => cfg.schain) + .forEach(([bidderCode, cfg]) => { + const bidderPath = `bidder.${bidderCode}.source`; + const hasSchain = ortb2Fragments?.bidder?.[bidderCode]?.source?.schain; + + if (!hasSchain) { + logInfo(`Applying bidder schain config for ${bidderCode}`); + applySchainToPath(ortb2Fragments, bidderPath, cfg.schain?.config); + } else { + logInfo(`Preserving existing schain for bidder ${bidderCode} from ortb2`); + } + }); + + return ortb2Fragments; +} + +/** + * Helper function to apply schain to a specific path in ortb2Fragments + * @param {Object} fragments - The ortb2 fragments object + * @param {String} path - Dot-notation path where to apply the schain + * @param {Object} schainConfig - The schain configuration to apply + */ +function applySchainToPath(fragments, path, schainConfig) { + const parts = path.split('.'); + let current = fragments; + + // Create path if it doesn't exist + parts.forEach(part => { + current[part] = current[part] || {}; + current = current[part]; + }); + + // Apply the schain config + current.schain = deepClone(schainConfig); +} + +/** + * Relocates schain from source.schain to source.ext.schain + * @param {Object} fpd - The first-party data object + * @returns {Object} - The updated FPD object + */ +export function moveSchainToExt(fpd, bidderOrtb2) { + if (!fpd?.source?.schain) return fpd; + + // Ensure source.ext exists + fpd.source.ext = fpd.source.ext || {}; + + // Move schain to the new location and remove from original + fpd.source.ext.schain = bidderOrtb2?.source?.schain || fpd.source.schain; + delete fpd.source.schain; + + return fpd; +} diff --git a/src/prebid.js b/src/prebid.js index de19204e71f..d5ac6e16c8d 100644 --- a/src/prebid.js +++ b/src/prebid.js @@ -37,6 +37,7 @@ import * as events from './events.js'; import {newMetrics, useMetrics} from './utils/perfMetrics.js'; import {defer, PbPromise} from './utils/promise.js'; import {enrichFPD} from './fpd/enrichment.js'; +import {schainPrecedence} from './fpd/schain.js'; import {allConsent} from './consentHandler.js'; import { insertLocatorFrame, @@ -586,10 +587,13 @@ pbjsInstance.requestBids = (function() { adUnitCodes = adUnits && adUnits.map(unit => unit.code); } adUnitCodes = adUnitCodes.filter(uniques); - const ortb2Fragments = { + let ortb2Fragments = { global: mergeDeep({}, config.getAnyConfig('ortb2') || {}, ortb2 || {}), bidder: Object.fromEntries(Object.entries(config.getBidderConfig()).map(([bidder, cfg]) => [bidder, deepClone(cfg.ortb2)]).filter(([_, ortb2]) => ortb2 != null)) } + // Apply schain precedence rules before enrichment + ortb2Fragments = schainPrecedence(ortb2Fragments); + return enrichFPD(PbPromise.resolve(ortb2Fragments.global)).then(global => { ortb2Fragments.global = global; return startAuction({bidsBackHandler, timeout: cbTimeout, adUnits, adUnitCodes, labels, auctionId, ttlBuffer, ortb2Fragments, metrics, defer}); diff --git a/test/spec/fpd/schain_spec.js b/test/spec/fpd/schain_spec.js new file mode 100644 index 00000000000..8532c6e0892 --- /dev/null +++ b/test/spec/fpd/schain_spec.js @@ -0,0 +1,385 @@ +import {expect} from 'chai/index.js'; +import * as utils from 'src/utils.js'; +import {config} from 'src/config.js'; +import {schainPrecedence, moveSchainToExt} from 'src/fpd/schain.js'; + +describe('Supply Chain fpd', function() { + const SAMPLE_SCHAIN = { + ver: '1.0', + complete: 1, + nodes: [{ asi: 'example.com', sid: '00001', hp: 1 }] + }; + + const SAMPLE_SCHAIN_2 = { + ver: '2.0', + complete: 1, + nodes: [{ asi: 'bidder.com', sid: '00002', hp: 1 }] + }; + + let sandbox; + let logInfoStub; + let configGetConfigStub; + let configGetBidderConfigStub; + + beforeEach(function() { + sandbox = sinon.createSandbox(); + logInfoStub = sandbox.stub(utils, 'logInfo'); + configGetConfigStub = sandbox.stub(config, 'getConfig'); + configGetBidderConfigStub = sandbox.stub(config, 'getBidderConfig'); + }); + + afterEach(function() { + sandbox.restore(); + }); + + + describe('schainPrecedence', function() { + describe('preserves existing schain values', function() { + it('should preserve existing global.source.schain', function() { + const existingSchain = { + ver: '1.0', + complete: 1, + nodes: [{ asi: 'existing.com', sid: '99999', hp: 1 }] + }; + + const input = { + global: { + source: { + schain: existingSchain + } + } + }; + + const schainConfig = { + config: SAMPLE_SCHAIN + }; + + configGetConfigStub.returns(schainConfig); + configGetBidderConfigStub.returns(null); + + const result = schainPrecedence(input); + + expect(result.global.source.schain).to.deep.equal(existingSchain); + expect(result.global.source.schain).to.not.deep.equal(SAMPLE_SCHAIN); + expect(logInfoStub.calledWith('Preserving existing global.source.schain from ortb2')).to.be.true; + }); + + it('should preserve existing bidder-specific schain ', function() { + const existingBidderSchain = { + ver: '3.0', + complete: 1, + nodes: [{ asi: 'existingbidder.com', sid: '88888', hp: 1 }] + }; + + const input = { + bidder: { + 'bidderA': { + source: { + schain: existingBidderSchain + } + } + } + }; + + const bidderConfigs = { + 'bidderA': { + schain: { + config: SAMPLE_SCHAIN + } + } + }; + + configGetConfigStub.returns(null); + configGetBidderConfigStub.returns(bidderConfigs); + + const result = schainPrecedence(input); + + expect(result.bidder.bidderA.source.schain).to.deep.equal(existingBidderSchain); + expect(result.bidder.bidderA.source.schain).to.not.deep.equal(SAMPLE_SCHAIN); + expect(logInfoStub.calledWith('Preserving existing schain for bidder bidderA from ortb2')).to.be.true; + }); + }); + + describe('handles edge cases', function() { + it('should handle edge cases and no-op scenarios', function() { + expect(schainPrecedence(null)).to.be.null; + expect(schainPrecedence(undefined)).to.be.undefined; + expect(schainPrecedence({})).to.deep.equal({}); + + const input = { + global: { + source: { + tid: '123' + } + } + }; + configGetConfigStub.returns(null); + configGetBidderConfigStub.returns(null); + + const result = schainPrecedence(input); + expect(result).to.deep.equal(input); + expect(logInfoStub.called).to.be.false; + }); + }); + + describe('global schain config handling', function() { + let input; + + beforeEach(function() { + input = { + global: { + source: {} + } + }; + configGetBidderConfigStub.returns(null); + }); + + it('should correctly handle different global schain config scenarios', function() { + const validSchainConfig = { + config: SAMPLE_SCHAIN + }; + configGetConfigStub.returns(validSchainConfig); + + let result = schainPrecedence(input); + expect(result.global.source.schain).to.deep.equal(SAMPLE_SCHAIN); + expect(logInfoStub.calledWith('Applying global schain config with precedence')).to.be.true; + + logInfoStub.reset(); + input = { global: { source: {} } }; + + const invalidSchainConfig = { + validation: 'strict' + }; + configGetConfigStub.returns(invalidSchainConfig); + + result = schainPrecedence(input); + expect(result.global.source.schain).to.be.undefined; + }); + }); + + describe('bidder-specific schain config handling', function() { + let input; + + beforeEach(function() { + input = { + global: {}, + bidder: {} + }; + configGetConfigStub.returns(null); + logInfoStub.reset(); + }); + + it('should handle various bidder-specific schain scenarios', function() { + const singleBidderConfig = { + 'bidderA': { + schain: { + config: SAMPLE_SCHAIN + } + } + }; + configGetBidderConfigStub.returns(singleBidderConfig); + + let result = schainPrecedence(input); + expect(result.bidder.bidderA.source.schain).to.deep.equal(SAMPLE_SCHAIN); + expect(logInfoStub.calledWith('Applying bidder schain config for bidderA')).to.be.true; + + logInfoStub.reset(); + input = { global: {}, bidder: {} }; + + const multiBidderConfig = { + 'bidderA': { + schain: { + config: SAMPLE_SCHAIN + } + }, + 'bidderB': { + schain: { + config: SAMPLE_SCHAIN_2 + } + }, + 'bidderC': { + } + }; + configGetBidderConfigStub.returns(multiBidderConfig); + + result = schainPrecedence(input); + expect(result.bidder.bidderA.source.schain).to.deep.equal(SAMPLE_SCHAIN); + expect(result.bidder.bidderB.source.schain).to.deep.equal(SAMPLE_SCHAIN_2); + expect(result.bidder.bidderC).to.be.undefined; + expect(logInfoStub.calledWith('Applying bidder schain config for bidderA')).to.be.true; + expect(logInfoStub.calledWith('Applying bidder schain config for bidderB')).to.be.true; + + logInfoStub.reset(); + input = { global: {}, bidder: {} }; + + const invalidBidderConfig = { + 'bidderA': { + schain: { + validation: 'strict' + } + } + }; + configGetBidderConfigStub.returns(invalidBidderConfig); + + result = schainPrecedence(input); + expect(result.bidder.bidderA.source.schain).to.deep.equal({}); + }); + }); + + // Test case: both global and bidder-specific schain configs + it('should apply both global and bidder-specific schain configs', function() { + const input = { + global: {}, + bidder: {} + }; + const globalSchainConfig = { + config: { + ver: '1.0', + complete: 1, + nodes: [{ asi: 'global.com', sid: '00001', hp: 1 }] + } + }; + const bidderConfigs = { + 'bidderA': { + schain: { + config: { + ver: '1.0', + complete: 1, + nodes: [{ asi: 'bidderA.com', sid: '00001', hp: 1 }] + } + } + } + }; + configGetConfigStub.returns(globalSchainConfig); + configGetBidderConfigStub.returns(bidderConfigs); + + const result = schainPrecedence(input); + expect(result.global.source.schain).to.deep.equal(globalSchainConfig.config); + expect(result.bidder.bidderA.source.schain).to.deep.equal(bidderConfigs.bidderA.schain.config); + }); + }); + + describe('moveSchainToExt', function() { + it('should handle various input scenarios correctly', function() { + expect(moveSchainToExt(null)).to.be.null; + expect(moveSchainToExt(undefined)).to.be.undefined; + + const inputNoSource = { user: { id: '123' } }; + expect(moveSchainToExt(inputNoSource)).to.deep.equal(inputNoSource); + + const inputNoSchain = { source: { tid: '123' } }; + expect(moveSchainToExt(inputNoSchain)).to.deep.equal(inputNoSchain); + + const basicInput = { + source: { + tid: '123', + schain: SAMPLE_SCHAIN + } + }; + let result = moveSchainToExt(basicInput); + expect(result.source.schain).to.be.undefined; + expect(result.source.ext.schain).to.deep.equal(SAMPLE_SCHAIN); + expect(result.source.tid).to.equal('123'); + + const inputWithExt = { + source: { + tid: '123', + schain: SAMPLE_SCHAIN, + ext: { + dchain: { ver: '1.0' } + } + } + }; + result = moveSchainToExt(inputWithExt); + expect(result.source.schain).to.be.undefined; + expect(result.source.ext.schain).to.deep.equal(SAMPLE_SCHAIN); + expect(result.source.ext.dchain).to.deep.equal({ ver: '1.0' }); + }); + + describe('bidderOrtb2 parameter handling', function() { + const createFreshFpd = () => ({ + source: { + tid: '123', + schain: SAMPLE_SCHAIN + } + }); + + it('should handle bidderOrtb2 parameter variations', function() { + const bidderOrtb2WithSchain = { + source: { + schain: SAMPLE_SCHAIN_2 + } + }; + + let fpd = createFreshFpd(); + let result = moveSchainToExt(fpd, bidderOrtb2WithSchain); + expect(result.source.schain).to.be.undefined; + expect(result.source.ext.schain).to.deep.equal(SAMPLE_SCHAIN_2); + + const bidderOrtb2WithoutSchain = { + source: {} + }; + + fpd = createFreshFpd(); + result = moveSchainToExt(fpd, bidderOrtb2WithoutSchain); + expect(result.source.schain).to.be.undefined; + expect(result.source.ext.schain).to.deep.equal(SAMPLE_SCHAIN); + + fpd = createFreshFpd(); + result = moveSchainToExt(fpd, null); + expect(result.source.schain).to.be.undefined; + expect(result.source.ext.schain).to.deep.equal(SAMPLE_SCHAIN); + }); + }); + }); + + describe('Integration', function() { + it('should handle the full schain workflow with both global and bidder configs', function() { + const ortb2Fragments = { + global: { + source: { + tid: '123' + } + }, + bidder: { + 'bidderA': { + source: {} + } + } + }; + + configGetConfigStub.returns({ config: SAMPLE_SCHAIN }); + configGetBidderConfigStub.returns({ + 'bidderA': { + schain: { + config: SAMPLE_SCHAIN_2 + } + } + }); + + const updatedFragments = schainPrecedence(ortb2Fragments); + + expect(updatedFragments.global.source.schain).to.deep.equal(SAMPLE_SCHAIN); + expect(updatedFragments.bidder.bidderA.source.schain).to.deep.equal(SAMPLE_SCHAIN_2); + + const merged = { + source: { + tid: '123', + schain: SAMPLE_SCHAIN + } + }; + + const bidderOrtb2 = { + source: { + schain: SAMPLE_SCHAIN_2 + } + }; + + const finalFpd = moveSchainToExt(merged, bidderOrtb2); + + expect(finalFpd.source.schain).to.be.undefined; + expect(finalFpd.source.ext.schain).to.deep.equal(SAMPLE_SCHAIN_2); + expect(finalFpd.source.tid).to.equal('123'); + }); + }); +}); diff --git a/test/spec/modules/33acrossBidAdapter_spec.js b/test/spec/modules/33acrossBidAdapter_spec.js index d4ad0184a17..828178c4ac3 100644 --- a/test/spec/modules/33acrossBidAdapter_spec.js +++ b/test/spec/modules/33acrossBidAdapter_spec.js @@ -1420,7 +1420,7 @@ describe('33acrossBidAdapter:', function () { ]; schainValues.forEach((schain) => { - bidRequests[0].schain = schain; + bidRequests[0].ortb2.source = {ext: {schain: schain}}; const ttxRequest = new TtxRequestBuilder() .withBanner() diff --git a/test/spec/modules/BTBidAdapter_spec.js b/test/spec/modules/BTBidAdapter_spec.js index 2ec0acc424e..18f99929037 100644 --- a/test/spec/modules/BTBidAdapter_spec.js +++ b/test/spec/modules/BTBidAdapter_spec.js @@ -12,7 +12,6 @@ import 'modules/consentManagementUsp.js'; import 'modules/consentManagementGpp.js'; import 'modules/tcfControl.js'; import 'modules/gppControl_usnat.js'; -import 'modules/schain.js'; describe('BT Bid Adapter', () => { const ENDPOINT_URL = 'https://pbs.btloader.com/openrtb2/auction'; diff --git a/test/spec/modules/ViouslyBidAdapter_spec.js b/test/spec/modules/ViouslyBidAdapter_spec.js index f8cd686581c..1a1767e7491 100644 --- a/test/spec/modules/ViouslyBidAdapter_spec.js +++ b/test/spec/modules/ViouslyBidAdapter_spec.js @@ -238,7 +238,13 @@ describe('ViouslyAdapter', function () { }; let bid = mergeDeep(deepClone(VALID_BID_VIDEO), { - schain: schain + ortb2: { + source: { + ext: { + schain: schain + } + } + } }); let requests = mergeDeep(deepClone(VALID_REQUEST_VIDEO), { diff --git a/test/spec/modules/adagioBidAdapter_spec.js b/test/spec/modules/adagioBidAdapter_spec.js index 102812b88cf..ba47463bbff 100644 --- a/test/spec/modules/adagioBidAdapter_spec.js +++ b/test/spec/modules/adagioBidAdapter_spec.js @@ -618,12 +618,13 @@ describe('Adagio bid adapter', () => { }; it('should add the schain if available at bidder level', function() { - const bid01 = new BidRequestBuilder({ schain }).withParams().build(); + const bid01 = new BidRequestBuilder({ ortb2: { source: { ext: { schain } } } }).withParams().build(); const bidderRequest = new BidderRequestBuilder().build(); const requests = spec.buildRequests([bid01], bidderRequest); expect(requests[0].data).to.have.all.keys(expectedDataKeys); + expect(requests[0].data.schain).to.exist; expect(requests[0].data.schain).to.deep.equal(schain); }); diff --git a/test/spec/modules/adfBidAdapter_spec.js b/test/spec/modules/adfBidAdapter_spec.js index 7d9660c089c..2c789767c14 100644 --- a/test/spec/modules/adfBidAdapter_spec.js +++ b/test/spec/modules/adfBidAdapter_spec.js @@ -330,10 +330,16 @@ describe('Adf adapter', function () { let validBidRequests = [{ bidId: 'bidId', params: {}, - schain: { - validation: 'strict', - config: { - ver: '1.0' + ortb2: { + source: { + ext: { + schain: { + validation: 'strict', + config: { + ver: '1.0' + } + } + } } } }]; diff --git a/test/spec/modules/adipoloBidAdapter_spec.js b/test/spec/modules/adipoloBidAdapter_spec.js index 6764d7d20d8..a166d87d0f0 100644 --- a/test/spec/modules/adipoloBidAdapter_spec.js +++ b/test/spec/modules/adipoloBidAdapter_spec.js @@ -120,18 +120,20 @@ describe('adipoloBidAdapter', () => { it('should build request with schain', function () { const schainRequest = deepClone(defaultRequest); - schainRequest.schain = { - validation: 'strict', - config: { - ver: '1.0' + const bidderRequest = { + ortb2: { + source: { + ext: { + schain: { + ver: '1.0' + } + } + } } }; - const request = JSON.parse(spec.buildRequests([schainRequest], {}).data)[0]; + const request = JSON.parse(spec.buildRequests([schainRequest], bidderRequest).data)[0]; expect(request).to.have.property('schain').and.to.deep.equal({ - validation: 'strict', - config: { - ver: '1.0' - } + ver: '1.0' }); }); diff --git a/test/spec/modules/adotBidAdapter_spec.js b/test/spec/modules/adotBidAdapter_spec.js index df628088bb0..3cb2975d0e8 100644 --- a/test/spec/modules/adotBidAdapter_spec.js +++ b/test/spec/modules/adotBidAdapter_spec.js @@ -28,7 +28,7 @@ describe('Adot Adapter', function () { it('should build request (banner)', function () { const bidderRequestId = 'bidderRequestId'; const validBidRequests = [{ bidderRequestId, mediaTypes: {} }, { bidderRequestId, bidId: 'bidId', mediaTypes: { banner: { sizes: [[300, 250]] } }, params: { placementId: 'placementId', adUnitCode: 200 } }]; - const bidderRequest = { position: 2, refererInfo: { page: 'http://localhost.com', domain: 'localhost.com' }, gdprConsent: { consentString: 'consentString', gdprApplies: true }, userId: { pubProvidedId: 'userId' }, schain: { ver: '1.0' } }; + const bidderRequest = { position: 2, refererInfo: { page: 'http://localhost.com', domain: 'localhost.com' }, gdprConsent: { consentString: 'consentString', gdprApplies: true }, userId: { pubProvidedId: 'userId' }, ortb2: { source: { ext: { schain: { ver: '1.0' } } } } }; const request = spec.buildRequests(validBidRequests, bidderRequest); const buildBidRequestResponse = { @@ -77,7 +77,7 @@ describe('Adot Adapter', function () { it('should build request (native)', function () { const bidderRequestId = 'bidderRequestId'; const validBidRequests = [{ bidderRequestId, mediaTypes: {} }, { bidderRequestId, bidId: 'bidId', mediaTypes: { native: { title: { required: true, len: 50, sizes: [[300, 250]] }, wrong: {}, image: {} } }, params: { placementId: 'placementId', adUnitCode: 200 } }]; - const bidderRequest = { position: 2, refererInfo: { page: 'http://localhost.com', domain: 'localhost.com' }, gdprConsent: { consentString: 'consentString', gdprApplies: true }, userId: { pubProvidedId: 'userId' }, schain: { ver: '1.0' } }; + const bidderRequest = { position: 2, refererInfo: { page: 'http://localhost.com', domain: 'localhost.com' }, gdprConsent: { consentString: 'consentString', gdprApplies: true }, userId: { pubProvidedId: 'userId' }, ortb2: { source: { ext: { schain: { ver: '1.0' } } } } }; const request = spec.buildRequests(validBidRequests, bidderRequest); const buildBidRequestResponse = { @@ -125,7 +125,7 @@ describe('Adot Adapter', function () { it('should build request (video)', function () { const bidderRequestId = 'bidderRequestId'; const validBidRequests = [{ bidderRequestId, mediaTypes: {} }, { bidderRequestId, bidId: 'bidId', mediaTypes: { video: { playerSize: [[300, 250]], minduration: 1, maxduration: 2, api: 'api', linearity: 'linearity', mimes: [], plcmt: '1', playbackmethod: 'playbackmethod', protocols: 'protocol', startdelay: 'startdelay' } }, params: { placementId: 'placementId', adUnitCode: 200 } }]; - const bidderRequest = { position: 2, refererInfo: { page: 'http://localhost.com', domain: 'localhost.com' }, gdprConsent: { consentString: 'consentString', gdprApplies: true }, userId: { pubProvidedId: 'userId' }, schain: { ver: '1.0' } }; + const bidderRequest = { position: 2, refererInfo: { page: 'http://localhost.com', domain: 'localhost.com' }, gdprConsent: { consentString: 'consentString', gdprApplies: true }, userId: { pubProvidedId: 'userId' }, ortb2: { source: { ext: { schain: { ver: '1.0' } } } } }; const request = spec.buildRequests(validBidRequests, bidderRequest); const buildBidRequestResponse = { diff --git a/test/spec/modules/adspiritBidAdapter_spec.js b/test/spec/modules/adspiritBidAdapter_spec.js index b0c98fd922f..1d39e4897c1 100644 --- a/test/spec/modules/adspiritBidAdapter_spec.js +++ b/test/spec/modules/adspiritBidAdapter_spec.js @@ -154,16 +154,22 @@ describe('Adspirit Bidder Spec', function () { mediaTypes: { banner: { sizes: [[300, 250]] } }, - schain: { - ver: '1.0', - complete: 1, - nodes: [ - { - asi: 'publisher.com', - sid: '1234', - hp: 1 + ortb2: { + source: { + ext: { + schain: { + ver: '1.0', + complete: 1, + nodes: [ + { + asi: 'publisher.com', + sid: '1234', + hp: 1 + } + ] + } } - ] + } } } ]; @@ -174,7 +180,7 @@ describe('Adspirit Bidder Spec', function () { const requestData = JSON.parse(request.data); expect(requestData.source).to.exist; expect(requestData.source.ext).to.exist; - expect(requestData.source.ext.schain).to.deep.equal(bidRequest[0].schain); + expect(requestData.source.ext.schain).to.deep.equal(bidRequest[0].ortb2.source.ext.schain); }); it('should correctly handle bidfloor values (valid, missing, and non-numeric)', function () { const bidRequest = [ diff --git a/test/spec/modules/adstirBidAdapter_spec.js b/test/spec/modules/adstirBidAdapter_spec.js index a62dce8af97..cbcb4ea0511 100644 --- a/test/spec/modules/adstirBidAdapter_spec.js +++ b/test/spec/modules/adstirBidAdapter_spec.js @@ -279,7 +279,7 @@ describe('AdstirAdapter', function () { }; const serializedSchain = '1.0,1!exchange1.example,1234%21abcd,1,bid-request-1,publisher%2C%20Inc.,publisher.example!exchange2.example,abcd,1,bid-request-2,intermediary,intermediary.example'; - const [ request ] = spec.buildRequests([utils.mergeDeep(utils.deepClone(validBidRequests[0]), { schain })], bidderRequest); + const [ request ] = spec.buildRequests([utils.mergeDeep(utils.deepClone(validBidRequests[0]), { ortb2: { source: { ext: { schain } } } })], bidderRequest); const d = JSON.parse(request.data); expect(d.schain).to.deep.equal(serializedSchain); }); @@ -305,7 +305,7 @@ describe('AdstirAdapter', function () { }; const serializedSchain = '1.0,1!exchange1.example,1234%21abcd,1,,,!,,,,,!exchange2.example,abcd,1,,,'; - const [ request ] = spec.buildRequests([utils.mergeDeep(utils.deepClone(validBidRequests[0]), { schain })], bidderRequest); + const [ request ] = spec.buildRequests([utils.mergeDeep(utils.deepClone(validBidRequests[0]), { ortb2: { source: { ext: { schain } } } })], bidderRequest); const d = JSON.parse(request.data); expect(d.schain).to.deep.equal(serializedSchain); }); diff --git a/test/spec/modules/adtargetBidAdapter_spec.js b/test/spec/modules/adtargetBidAdapter_spec.js index d1221d24022..3548833d757 100644 --- a/test/spec/modules/adtargetBidAdapter_spec.js +++ b/test/spec/modules/adtargetBidAdapter_spec.js @@ -8,7 +8,6 @@ const DISPLAY_REQUEST = { 'params': { 'aid': 12345 }, - 'schain': { ver: 1 }, 'userId': { criteo: 2 }, 'mediaTypes': { 'banner': { 'sizes': [300, 250] } }, 'bidderRequestId': '7101db09af0db2', @@ -95,7 +94,16 @@ const displayBidderRequestWithConsents = { gdprApplies: true, consentString: 'test' }, - uspConsent: 'iHaveIt' + uspConsent: 'iHaveIt', + ortb2: { + source: { + ext: { + schain: { + ver: '1.0' + } + } + } + } }; const videoEqResponse = [{ @@ -284,7 +292,7 @@ describe('adtargetBidAdapter', () => { expect(bidRequestWithPubSettingsData.Coppa).to.be.equal(1); }) it('sets Schain', () => { - expect(bidRequestWithPubSettingsData.Schain).to.be.deep.equal(DISPLAY_REQUEST.schain); + expect(bidRequestWithPubSettingsData.Schain).to.be.deep.equal(displayBidderRequestWithConsents.ortb2.source.ext.schain); }) it('sets UserId\'s', () => { expect(bidRequestWithPubSettingsData.UserIds).to.be.deep.equal(DISPLAY_REQUEST.userId); diff --git a/test/spec/modules/adtelligentBidAdapter_spec.js b/test/spec/modules/adtelligentBidAdapter_spec.js index b12744d3a28..9773f685bf1 100644 --- a/test/spec/modules/adtelligentBidAdapter_spec.js +++ b/test/spec/modules/adtelligentBidAdapter_spec.js @@ -20,13 +20,12 @@ const aliasEP = { 'stellormedia': 'https://ghb.ads.stellormedia.com/v2/auction/', }; -const DEFAULT_ADATPER_REQ = { bidderCode: 'adtelligent' }; +const DEFAULT_ADATPER_REQ = { bidderCode: 'adtelligent', ortb2: { source: { ext: { schain: { ver: 1 } } } } }; const DISPLAY_REQUEST = { 'bidder': 'adtelligent', 'params': { 'aid': 12345 }, - 'schain': { ver: 1 }, 'userId': { criteo: 2 }, 'mediaTypes': { 'banner': { 'sizes': [300, 250] } }, 'bidderRequestId': '7101db09af0db2', diff --git a/test/spec/modules/adtrgtmeBidAdapter_spec.js b/test/spec/modules/adtrgtmeBidAdapter_spec.js index 925f5763c5d..b4abd2709a4 100644 --- a/test/spec/modules/adtrgtmeBidAdapter_spec.js +++ b/test/spec/modules/adtrgtmeBidAdapter_spec.js @@ -266,7 +266,7 @@ describe('Adtrgtme Bid Adapter:', () => { hp: 1 }] }; - bidRequest.schain = globalSchain; + bidRequest.ortb2 = { source: { ext: { schain: globalSchain } } }; const data = spec.buildRequests(validBR, bidderRequest)[0].data; const schain = data.source.ext.schain; expect(schain.nodes.length).to.equal(1); diff --git a/test/spec/modules/adtrueBidAdapter_spec.js b/test/spec/modules/adtrueBidAdapter_spec.js index df8f9013534..4efc8d69707 100644 --- a/test/spec/modules/adtrueBidAdapter_spec.js +++ b/test/spec/modules/adtrueBidAdapter_spec.js @@ -33,22 +33,28 @@ describe('AdTrueBidAdapter', function () { tid: '92489f71-1bf2-49a0-adf9-000cea934729', } }, - schain: { - 'ver': '1.0', - 'complete': 1, - 'nodes': [ - { - 'asi': 'indirectseller.com', - 'sid': '00001', - 'hp': 1 - }, + ortb2: { + source: { + ext: { + schain: { + 'ver': '1.0', + 'complete': 1, + 'nodes': [ + { + 'asi': 'indirectseller.com', + 'sid': '00001', + 'hp': 1 + }, - { - 'asi': 'indirectseller-2.com', - 'sid': '00002', - 'hp': 2 + { + 'asi': 'indirectseller-2.com', + 'sid': '00002', + 'hp': 1 + } + ] + } } - ] + } } } ]; @@ -291,7 +297,7 @@ describe('AdTrueBidAdapter', function () { expect(data.imp[0].tagid).to.equal(bidRequests[0].params.zoneId); // zoneId expect(data.imp[0].banner.w).to.equal(300); // width expect(data.imp[0].banner.h).to.equal(250); // height - expect(data.source.ext.schain).to.deep.equal(bidRequests[0].schain); + expect(data.source.ext.schain).to.deep.equal(bidRequests[0].ortb2.source.ext.schain); }); it('Request params check with GDPR Consent', function () { let bidRequest = { @@ -314,7 +320,7 @@ describe('AdTrueBidAdapter', function () { expect(data.imp[0].tagid).to.equal(bidRequests[0].params.zoneId); // zoneId expect(data.imp[0].banner.w).to.equal(300); // width expect(data.imp[0].banner.h).to.equal(250); // height - expect(data.source.ext.schain).to.deep.equal(bidRequests[0].schain); + expect(data.source.ext.schain).to.deep.equal(bidRequests[0].ortb2.source.ext.schain); }); it('Request params check with USP/CCPA Consent', function () { let bidRequest = { @@ -334,7 +340,7 @@ describe('AdTrueBidAdapter', function () { expect(data.imp[0].tagid).to.equal(bidRequests[0].params.zoneId); // zoneId expect(data.imp[0].banner.w).to.equal(300); // width expect(data.imp[0].banner.h).to.equal(250); // height - expect(data.source.ext.schain).to.deep.equal(bidRequests[0].schain); + expect(data.source.ext.schain).to.deep.equal(bidRequests[0].ortb2.source.ext.schain); }); it('should NOT include coppa flag in bid request if coppa config is not present', () => { diff --git a/test/spec/modules/advertisingBidAdapter_spec.js b/test/spec/modules/advertisingBidAdapter_spec.js index bb4edc9ab05..d73df844d1f 100644 --- a/test/spec/modules/advertisingBidAdapter_spec.js +++ b/test/spec/modules/advertisingBidAdapter_spec.js @@ -878,16 +878,22 @@ describe('advertisingBidAdapter ', function () { bidRequestsCount: 1, bidderRequestsCount: 1, bidderWinsCount: 0, - schain: { - ver: '1.0', - complete: 1, - nodes: [ - { - asi: 'indirectseller.com', - sid: '00001', - hp: 1 + ortb2: { + source: { + ext: { + schain: { + ver: '1.0', + complete: 1, + nodes: [ + { + asi: 'indirectseller.com', + sid: '00001', + hp: 1 + } + ] + } } - ] + } } }; let bidderRequest = { diff --git a/test/spec/modules/adyoulikeBidAdapter_spec.js b/test/spec/modules/adyoulikeBidAdapter_spec.js index 367fc62c719..d67ba92ff8a 100644 --- a/test/spec/modules/adyoulikeBidAdapter_spec.js +++ b/test/spec/modules/adyoulikeBidAdapter_spec.js @@ -99,18 +99,24 @@ describe('Adyoulike Adapter', function () { } }, }, - 'schain': { - validation: 'strict', - config: { - ver: '1.0', - complete: 1, - nodes: [ - { - asi: 'indirectseller.com', - sid: '00001', - hp: 1 + 'ortb2': { + 'source': { + 'ext': { + 'schain': { + validation: 'strict', + config: { + ver: '1.0', + complete: 1, + nodes: [ + { + asi: 'indirectseller.com', + sid: '00001', + hp: 1 + } + ] + } } - ] + } } }, ortb2Imp: { @@ -682,7 +688,7 @@ describe('Adyoulike Adapter', function () { expect(payload.gdprConsent.consentString).to.exist.and.to.equal(consentString); expect(payload.gdprConsent.consentRequired).to.exist.and.to.be.true; expect(payload.uspConsent).to.exist.and.to.equal(uspConsentData); - expect(payload.Bids.bid_id_0.SChain).to.exist.and.to.deep.equal(bidRequestWithSinglePlacement[0].schain); + expect(payload.Bids.bid_id_0.SChain).to.exist.and.to.deep.equal(bidRequestWithSinglePlacement[0].ortb2.source.ext.schain); }); it('should not set a default value for gdpr consentRequired', function () { diff --git a/test/spec/modules/ajaBidAdapter_spec.js b/test/spec/modules/ajaBidAdapter_spec.js index bd2bdd3e407..82c6d1bab65 100644 --- a/test/spec/modules/ajaBidAdapter_spec.js +++ b/test/spec/modules/ajaBidAdapter_spec.js @@ -66,6 +66,32 @@ describe('AjaAdapter', function () { ext: { cdep: 'example_label_1' } + }, + source: { + ext: { + schain: { + ver: '1.0', + complete: 1, + nodes: [ + { + asi: 'exchange1.com', + sid: '1234', + hp: 1, + rid: 'bid-request-1', + name: 'publisher', + domain: 'publisher.com' + }, + { + asi: 'exchange2.com', + sid: 'abcd', + hp: 1, + rid: 'bid-request-2', + name: 'intermediary', + domain: 'intermediary.com' + } + ] + } + } } }, ortb2Imp: { @@ -74,28 +100,7 @@ describe('AjaAdapter', function () { gpid: '/1111/homepage#300x250' } }, - schain: { - ver: '1.0', - complete: 1, - nodes: [ - { - asi: 'exchange1.com', - sid: '1234', - hp: 1, - rid: 'bid-request-1', - name: 'publisher', - domain: 'publisher.com' - }, - { - asi: 'exchange2.com', - sid: 'abcd', - hp: 1, - rid: 'bid-request-2', - name: 'intermediary', - domain: 'intermediary.com' - } - ] - }, + } ]; const serializedSchain = encodeURIComponent('1.0,1!exchange1.com,1234,1,bid-request-1,publisher,publisher.com!exchange2.com,abcd,1,bid-request-2,intermediary,intermediary.com') diff --git a/test/spec/modules/alkimiBidAdapter_spec.js b/test/spec/modules/alkimiBidAdapter_spec.js index d642afd2b26..d2a6a4dcada 100644 --- a/test/spec/modules/alkimiBidAdapter_spec.js +++ b/test/spec/modules/alkimiBidAdapter_spec.js @@ -35,14 +35,20 @@ const REQUEST = { 'atype': 1 }] }], - 'schain': { - ver: '1.0', - complete: 1, - nodes: [{ - asi: 'alkimi-onboarding.com', - sid: '00001', - hp: 1 - }] + 'ortb2': { + 'source': { + 'ext': { + 'schain': { + ver: '1.0', + complete: 1, + nodes: [{ + asi: 'alkimi-onboarding.com', + sid: '00001', + hp: 1 + }] + } + } + } } } @@ -153,7 +159,7 @@ describe('alkimiBidAdapter', function () { expect(bidderRequest.method).to.equal('POST') expect(bidderRequest.data.requestId).to.not.equal(undefined) expect(bidderRequest.data.referer).to.equal('http://test.com/path.html') - expect(bidderRequest.data.schain).to.deep.contains({ ver: '1.0', complete: 1, nodes: [{ asi: 'alkimi-onboarding.com', sid: '00001', hp: 1 }] }) + expect(bidderRequest.data.schain).to.deep.equal({ ver: '1.0', complete: 1, nodes: [{ asi: 'alkimi-onboarding.com', sid: '00001', hp: 1 }] }) expect(bidderRequest.data.signRequest.bids).to.deep.contains({ token: 'e64782a4-8e68-4c38-965b-80ccf115d46f', bidFloor: 0.1, sizes: [{ width: 300, height: 250 }], playerSizes: [], impMediaTypes: ['Banner'], adUnitCode: 'bannerAdUnitCode', instl: undefined, exp: undefined, banner: { sizes: [[300, 250]] }, video: undefined, ext: { gpid: '/111/banner#300x250', tid: 'e64782a4-8e68-4c38-965b-80ccf115d46a' } }) expect(bidderRequest.data.signRequest.randomUUID).to.equal(undefined) expect(bidderRequest.data.bidIds).to.deep.contains('456') diff --git a/test/spec/modules/amxBidAdapter_spec.js b/test/spec/modules/amxBidAdapter_spec.js index 680579aa299..5aa0c0f09d3 100644 --- a/test/spec/modules/amxBidAdapter_spec.js +++ b/test/spec/modules/amxBidAdapter_spec.js @@ -115,7 +115,7 @@ const sampleBidRequestVideo = { ...sampleBidRequestBase, bidId: sampleRequestId + '_video', sizes: [[300, 150]], - schain: schainConfig, + ortb2: { source: { ext: { schain: schainConfig } } }, mediaTypes: { [VIDEO]: { sizes: [[360, 250]], diff --git a/test/spec/modules/anyclipBidAdapter_spec.js b/test/spec/modules/anyclipBidAdapter_spec.js index 3ec81d60546..a25c285e6c4 100644 --- a/test/spec/modules/anyclipBidAdapter_spec.js +++ b/test/spec/modules/anyclipBidAdapter_spec.js @@ -128,18 +128,20 @@ describe('anyclipBidAdapter', () => { it('should build request with schain', function () { const schainRequest = deepClone(defaultRequest); - schainRequest.schain = { - validation: 'strict', - config: { - ver: '1.0' + const bidderRequest = { + ortb2: { + source: { + ext: { + schain: { + ver: '1.0' + } + } + } } }; - const request = JSON.parse(spec.buildRequests([schainRequest], {}).data)[0]; + const request = JSON.parse(spec.buildRequests([schainRequest], bidderRequest).data)[0]; expect(request).to.have.property('schain').and.to.deep.equal({ - validation: 'strict', - config: { - ver: '1.0' - } + ver: '1.0' }); }); diff --git a/test/spec/modules/apacdexBidAdapter_spec.js b/test/spec/modules/apacdexBidAdapter_spec.js index d005934d062..e10b7468695 100644 --- a/test/spec/modules/apacdexBidAdapter_spec.js +++ b/test/spec/modules/apacdexBidAdapter_spec.js @@ -183,21 +183,27 @@ describe('ApacdexBidAdapter', function () { userSync.canBidderRegisterSync.restore(); }); let bidRequest = [{ - 'schain': { - 'ver': '1.0', - 'complete': 1, - 'nodes': [ - { - 'asi': 'indirectseller.com', - 'sid': '00001', - 'hp': 1 - }, - { - 'asi': 'indirectseller-2.com', - 'sid': '00002', - 'hp': 0 - }, - ] + 'ortb2': { + 'source': { + 'ext': { + 'schain': { + 'ver': '1.0', + 'complete': 1, + 'nodes': [ + { + 'asi': 'indirectseller.com', + 'sid': '00001', + 'hp': 1 + }, + { + 'asi': 'indirectseller-2.com', + 'sid': '00002', + 'hp': 0 + }, + ] + } + } + } }, 'bidder': 'apacdex', 'params': { @@ -314,7 +320,7 @@ describe('ApacdexBidAdapter', function () { }) it('should return a properly formatted request with schain defined', function () { const bidRequests = spec.buildRequests(bidRequest, bidderRequests); - expect(bidRequests.data.schain).to.deep.equal(bidRequest[0].schain) + expect(bidRequests.data.schain).to.deep.equal(bidRequest[0].ortb2.source.ext.schain) }); it('should return a properly formatted request with eids defined', function () { const bidRequests = spec.buildRequests(bidRequest, bidderRequests); diff --git a/test/spec/modules/appnexusBidAdapter_spec.js b/test/spec/modules/appnexusBidAdapter_spec.js index 8298f1b56bc..36707601cab 100644 --- a/test/spec/modules/appnexusBidAdapter_spec.js +++ b/test/spec/modules/appnexusBidAdapter_spec.js @@ -1462,16 +1462,22 @@ describe('AppNexusAdapter', function () { it('should populate schain if available', function () { const bidRequest = Object.assign({}, bidRequests[0], { - schain: { - ver: '1.0', - complete: 1, - nodes: [ - { - 'asi': 'blob.com', - 'sid': '001', - 'hp': 1 + ortb2: { + source: { + ext: { + schain: { + ver: '1.0', + complete: 1, + nodes: [ + { + 'asi': 'blob.com', + 'sid': '001', + 'hp': 1 + } + ] + } } - ] + } } }); diff --git a/test/spec/modules/audiencerunBidAdapter_spec.js b/test/spec/modules/audiencerunBidAdapter_spec.js index 65349409e5e..04a0194af54 100644 --- a/test/spec/modules/audiencerunBidAdapter_spec.js +++ b/test/spec/modules/audiencerunBidAdapter_spec.js @@ -239,17 +239,23 @@ describe('AudienceRun bid adapter tests', function () { it('should add schain object if available', function() { const bid = Object.assign({}, bidRequest) - bid.schain = { - ver: '1.0', - complete: 1, - nodes: [ - { - asi: 'directseller.com', - sid: '00001', - rid: 'BidRequest1', - hp: 1, - }, - ], + bid.ortb2 = { + source: { + ext: { + schain: { + ver: '1.0', + complete: 1, + nodes: [ + { + asi: 'directseller.com', + sid: '00001', + rid: 'BidRequest1', + hp: 1, + }, + ], + } + } + } }; const request = spec.buildRequests([bid]); diff --git a/test/spec/modules/beachfrontBidAdapter_spec.js b/test/spec/modules/beachfrontBidAdapter_spec.js index 2e766487951..a881928a017 100644 --- a/test/spec/modules/beachfrontBidAdapter_spec.js +++ b/test/spec/modules/beachfrontBidAdapter_spec.js @@ -327,7 +327,7 @@ describe('BeachfrontAdapter', function () { }; const bidRequest = bidRequests[0]; bidRequest.mediaTypes = { video: {} }; - bidRequest.schain = schain; + bidRequest.ortb2 = { source: { ext: { schain: schain } } }; const requests = spec.buildRequests([ bidRequest ], {}); const data = requests[0].data; expect(data.source.ext.schain).to.deep.equal(schain); @@ -565,7 +565,7 @@ describe('BeachfrontAdapter', function () { }; const bidRequest = bidRequests[0]; bidRequest.mediaTypes = { banner: {} }; - bidRequest.schain = schain; + bidRequest.ortb2 = { source: { ext: { schain: schain } } }; const requests = spec.buildRequests([ bidRequest ], {}); const data = requests[0].data; expect(data.schain).to.deep.equal(schain); diff --git a/test/spec/modules/blastoBidAdapter_spec.js b/test/spec/modules/blastoBidAdapter_spec.js index 2efdd5ad286..7a2c94f3b14 100644 --- a/test/spec/modules/blastoBidAdapter_spec.js +++ b/test/spec/modules/blastoBidAdapter_spec.js @@ -13,7 +13,6 @@ import 'modules/multibid/index.js'; import 'modules/priceFloors.js'; import 'modules/consentManagementTcf.js'; import 'modules/consentManagementUsp.js'; -import 'modules/schain.js'; const SIMPLE_BID_REQUEST = { bidder: 'blasto', diff --git a/test/spec/modules/bliinkBidAdapter_spec.js b/test/spec/modules/bliinkBidAdapter_spec.js index 9568c893b13..7c9694affd5 100644 --- a/test/spec/modules/bliinkBidAdapter_spec.js +++ b/test/spec/modules/bliinkBidAdapter_spec.js @@ -809,16 +809,22 @@ const testsBuildRequests = [ fn: spec.buildRequests( [ { - schain: { - ver: '1.0', - complete: 1, - nodes: [ - { - asi: 'ssp.test', - sid: '00001', - hp: 1, - }, - ], + ortb2: { + source: { + ext: { + schain: { + ver: '1.0', + complete: 1, + nodes: [ + { + asi: 'ssp.test', + sid: '00001', + hp: 1, + }, + ], + } + } + } }, }, ], diff --git a/test/spec/modules/bridBidAdapter_spec.js b/test/spec/modules/bridBidAdapter_spec.js index 20a6542707b..a97e56c5ba0 100644 --- a/test/spec/modules/bridBidAdapter_spec.js +++ b/test/spec/modules/bridBidAdapter_spec.js @@ -50,7 +50,7 @@ describe('Brid Bid Adapter', function() { }; let videoRequestCloned = deepClone(videoRequest); - videoRequestCloned[0].schain = globalSchain; + videoRequestCloned[0].ortb2 = { source: { ext: { schain: globalSchain } } }; const request = spec.buildRequests(videoRequestCloned, videoRequestCloned[0]); expect(request).to.not.be.empty; diff --git a/test/spec/modules/bridgeuppBidAdapter_spec.js b/test/spec/modules/bridgeuppBidAdapter_spec.js index 89164b1948f..a9e7b329cc9 100644 --- a/test/spec/modules/bridgeuppBidAdapter_spec.js +++ b/test/spec/modules/bridgeuppBidAdapter_spec.js @@ -242,7 +242,9 @@ describe('bridgeuppBidAdapter_spec', function () { const ortb2 = { source: { pchain: 'sonarads', - schain: expectedSchain + ext: { + schain: expectedSchain + } } }; const bidRequests = [ @@ -261,7 +263,7 @@ describe('bridgeuppBidAdapter_spec', function () { }, ]; const ortbRequest = spec.buildRequests(bidRequests, await addFPDToBidderRequest({...bidderRequest, ortb2})).data; - expect(ortbRequest.source.schain).to.deep.equal(expectedSchain); + expect(ortbRequest.source.ext.schain).to.deep.equal(expectedSchain); expect(ortbRequest.source.pchain).to.equal('sonarads'); }); diff --git a/test/spec/modules/brightMountainMediaBidAdapter_spec.js b/test/spec/modules/brightMountainMediaBidAdapter_spec.js index 5e433abebd8..58c0d5f5e3c 100644 --- a/test/spec/modules/brightMountainMediaBidAdapter_spec.js +++ b/test/spec/modules/brightMountainMediaBidAdapter_spec.js @@ -26,17 +26,23 @@ describe('brightMountainMediaBidAdapter_spec', function () { floor: 0.5, } }, - schain: { - ver: '1.0', - complete: 1, - nodes: [ - { - asi: 'directseller.com', - sid: '00001', - rid: 'BidRequest1', - hp: 1 + ortb2: { + source: { + ext: { + schain: { + ver: '1.0', + complete: 1, + nodes: [ + { + asi: 'directseller.com', + sid: '00001', + rid: 'BidRequest1', + hp: 1 + } + ] + } } - ] + } }, userIdAsEids: [ { diff --git a/test/spec/modules/browsiBidAdapter_spec.js b/test/spec/modules/browsiBidAdapter_spec.js index 9693972fd7f..4c585ca71b3 100644 --- a/test/spec/modules/browsiBidAdapter_spec.js +++ b/test/spec/modules/browsiBidAdapter_spec.js @@ -69,7 +69,13 @@ describe('browsi Bid Adapter Test', function () { tid: '1234567-3456-4562-7689-98765434B', } }, - 'schain': {}, + 'ortb2': { + 'source': { + 'ext': { + 'schain': {} + } + } + }, 'mediaTypes': {video: {playerSize: [640, 480]}} } ]; @@ -117,7 +123,7 @@ describe('browsi Bid Adapter Test', function () { aUCode: inputRequest.adUnitCode, aID: inputRequest.auctionId, tID: inputRequest.ortb2Imp.ext.tid, - schain: inputRequest.schain, + schain: inputRequest.ortb2?.source?.ext?.schain, params: inputRequest.params } } diff --git a/test/spec/modules/cadentApertureMXBidAdapter_spec.js b/test/spec/modules/cadentApertureMXBidAdapter_spec.js index c5d68f6ab81..16e2bc66b40 100644 --- a/test/spec/modules/cadentApertureMXBidAdapter_spec.js +++ b/test/spec/modules/cadentApertureMXBidAdapter_spec.js @@ -377,23 +377,29 @@ describe('cadent_aperture_mx Adapter', function () { it('should add schain object to request', function() { const schainBidderRequest = utils.deepClone(bidderRequest); - schainBidderRequest.bids[0].schain = { - 'complete': 1, - 'ver': '1.0', - 'nodes': [ - { - 'asi': 'testing.com', - 'sid': 'abc', - 'hp': 1 + schainBidderRequest.bids[0].ortb2 = { + source: { + ext: { + schain: { + 'complete': 1, + 'ver': '1.0', + 'nodes': [ + { + 'asi': 'testing.com', + 'sid': 'abc', + 'hp': 1 + } + ] + } } - ] + } }; let request = spec.buildRequests(schainBidderRequest.bids, schainBidderRequest); request = JSON.parse(request.data); expect(request.source.ext.schain).to.exist; expect(request.source.ext.schain).to.have.property('complete', 1); expect(request.source.ext.schain).to.have.property('ver', '1.0'); - expect(request.source.ext.schain.nodes[0].asi).to.equal(schainBidderRequest.bids[0].schain.nodes[0].asi); + expect(request.source.ext.schain.nodes[0].asi).to.equal(schainBidderRequest.bids[0].ortb2.source.ext.schain.nodes[0].asi); }); it('should add liveramp identitylink id to request', () => { diff --git a/test/spec/modules/carodaBidAdapter_spec.js b/test/spec/modules/carodaBidAdapter_spec.js index 780c81ebe9f..7fa0827b3bf 100644 --- a/test/spec/modules/carodaBidAdapter_spec.js +++ b/test/spec/modules/carodaBidAdapter_spec.js @@ -109,10 +109,16 @@ describe('Caroda adapter', function () { const validBidRequests = [{ bid_id: 'bidId', params: {}, - schain: { - validation: 'strict', - config: { - ver: '1.0' + ortb2: { + source: { + ext: { + schain: { + validation: 'strict', + config: { + ver: '1.0' + } + } + } } } }]; diff --git a/test/spec/modules/colossussspBidAdapter_spec.js b/test/spec/modules/colossussspBidAdapter_spec.js index 9f675592951..69b3dc556f0 100644 --- a/test/spec/modules/colossussspBidAdapter_spec.js +++ b/test/spec/modules/colossussspBidAdapter_spec.js @@ -24,19 +24,25 @@ describe('ColossussspAdapter', function () { data: {} } }, - schain: { - ver: '1.0', - complete: 1, - nodes: [ - { - asi: 'example.com', - sid: '0', - hp: 1, - rid: 'bidrequestid', - // name: 'alladsallthetime', - domain: 'example.com' + ortb2: { + source: { + ext: { + schain: { + ver: '1.0', + complete: 1, + nodes: [ + { + asi: 'example.com', + sid: '0', + hp: 1, + rid: 'bidrequestid', + // name: 'alladsallthetime', + domain: 'example.com' + } + ] + } } - ] + } } }; let bidderRequest = { diff --git a/test/spec/modules/connectadBidAdapter_spec.js b/test/spec/modules/connectadBidAdapter_spec.js index f067d801c5e..030c21c13d7 100644 --- a/test/spec/modules/connectadBidAdapter_spec.js +++ b/test/spec/modules/connectadBidAdapter_spec.js @@ -372,16 +372,22 @@ describe('ConnectAd Adapter', function () { it('should populate schain', function () { const bidRequest = Object.assign({}, bidRequests[0], { - schain: { - ver: '1.0', - complete: 1, - nodes: [ - { - 'asi': 'reseller1.com', - 'sid': 'absc1', - 'hp': 1 + ortb2: { + source: { + ext: { + schain: { + ver: '1.0', + complete: 1, + nodes: [ + { + 'asi': 'reseller1.com', + 'sid': 'absc1', + 'hp': 1 + } + ] + } } - ] + } } }); diff --git a/test/spec/modules/consumableBidAdapter_spec.js b/test/spec/modules/consumableBidAdapter_spec.js index aa3eee990c2..eff4e9e0526 100644 --- a/test/spec/modules/consumableBidAdapter_spec.js +++ b/test/spec/modules/consumableBidAdapter_spec.js @@ -33,22 +33,6 @@ const BIDDER_REQUEST_1 = { transactionId: '92489f71-1bf2-49a0-adf9-000cea934729' } ], - schain: { - 'ver': '1.0', - 'complete': 1, - 'nodes': [ - { - 'asi': 'indirectseller.com', - 'sid': '00001', - 'hp': 1 - }, - { - 'asi': 'indirectseller-2.com', - 'sid': '00002', - 'hp': 2 - }, - ] - }, gdprConsent: { consentString: 'consent-test', gdprApplies: false @@ -70,6 +54,26 @@ const BIDDER_REQUEST_1 = { ortb2: { device: { language: 'en' + }, + source: { + ext: { + schain: { + 'ver': '1.0', + 'complete': 1, + 'nodes': [ + { + 'asi': 'indirectseller.com', + 'sid': '00001', + 'hp': 1 + }, + { + 'asi': 'indirectseller-2.com', + 'sid': '00002', + 'hp': 2 + }, + ] + } + } } } }; @@ -488,7 +492,7 @@ describe('Consumable BidAdapter', function () { let request = spec.buildRequests(BIDDER_REQUEST_1.bidRequest, BIDDER_REQUEST_1); let data = JSON.parse(request.data); - expect(data.schain).to.deep.equal(BIDDER_REQUEST_1.schain) + expect(data.schain).to.deep.equal(BIDDER_REQUEST_1.ortb2.source.ext.schain) }); it('should not contain schain if it does not exist in the bidRequest', function () { diff --git a/test/spec/modules/conversantBidAdapter_spec.js b/test/spec/modules/conversantBidAdapter_spec.js index 8978ac00342..cb07ff422f9 100644 --- a/test/spec/modules/conversantBidAdapter_spec.js +++ b/test/spec/modules/conversantBidAdapter_spec.js @@ -9,7 +9,6 @@ import 'modules/userId/index.js'; // handles eids import 'modules/priceFloors.js'; import 'modules/consentManagementTcf.js'; import 'modules/consentManagementUsp.js'; -import 'modules/schain.js'; // handles schain import {hook} from '../../../src/hook.js' import {BANNER} from '../../../src/mediaTypes'; @@ -450,10 +449,22 @@ describe('Conversant adapter tests', function() { it('Verify supply chain data', () => { const bidderRequest = {refererInfo: {page: 'http://test.com?a=b&c=123'}}; const schain = {complete: 1, ver: '1.0', nodes: [{asi: 'bidderA.com', sid: '00001', hp: 1}]}; + + // Add schain to bidderRequest + bidderRequest.ortb2 = { + source: { + ext: {schain: schain} + } + }; + const bidsWithSchain = bidRequests.map((bid) => { return Object.assign({ - schain: schain - }, bid); + ortb2: { + source: { + ext: {schain: schain} + } + } + }, bid); }); const request = spec.buildRequests(bidsWithSchain, bidderRequest); const payload = request.data; diff --git a/test/spec/modules/cpmstarBidAdapter_spec.js b/test/spec/modules/cpmstarBidAdapter_spec.js index f9b1ba59cde..c1c773d5047 100755 --- a/test/spec/modules/cpmstarBidAdapter_spec.js +++ b/test/spec/modules/cpmstarBidAdapter_spec.js @@ -131,21 +131,27 @@ describe('Cpmstar Bid Adapter', function () { it('should produce a request with support for OpenRTB SupplyChain', function () { var reqs = deepClone(valid_bid_requests); - reqs[0].schain = { - 'ver': '1.0', - 'complete': 1, - 'nodes': [ - { - 'asi': 'exchange1.com', - 'sid': '1234', - 'hp': 1 - }, - { - 'asi': 'exchange2.com', - 'sid': 'abcd', - 'hp': 1 + reqs[0].ortb2 = { + source: { + ext: { + schain: { + 'ver': '1.0', + 'complete': 1, + 'nodes': [ + { + 'asi': 'exchange1.com', + 'sid': '1234', + 'hp': 1 + }, + { + 'asi': 'exchange2.com', + 'sid': 'abcd', + 'hp': 1 + } + ] + } } - ] + } }; var requests = spec.buildRequests(reqs, bidderRequest); expect(requests[0]).to.have.property('url'); diff --git a/test/spec/modules/criteoBidAdapter_spec.js b/test/spec/modules/criteoBidAdapter_spec.js index 960755ee71f..322e0716040 100755 --- a/test/spec/modules/criteoBidAdapter_spec.js +++ b/test/spec/modules/criteoBidAdapter_spec.js @@ -10,7 +10,7 @@ import 'modules/userId/index.js'; import 'modules/consentManagementTcf.js'; import 'modules/consentManagementUsp.js'; import 'modules/consentManagementGpp.js'; -import 'modules/schain.js'; + import {hook} from '../../../src/hook'; describe('The Criteo bidding adapter', function () { @@ -1219,14 +1219,18 @@ describe('The Criteo bidding adapter', function () { expect(ortbRequest.regs.ext.dsa).to.deep.equal(dsa); }); - it('should properly build a request with schain object', async function () { + it('should properly build a request with schain object', function () { const expectedSchain = { someProperty: 'someValue' }; const bidRequests = [ { bidder: 'criteo', - schain: expectedSchain, + ortb2: { + source: { + ext: {schain: expectedSchain} + } + }, adUnitCode: 'bid-123', mediaTypes: { banner: { @@ -1238,9 +1242,19 @@ describe('The Criteo bidding adapter', function () { }, }, ]; + + // Create a modified bidderRequest with schain + const modifiedBidderRequest = { + ...bidderRequest, + ortb2: { + source: { + ext: {schain: expectedSchain} + } + } + }; - const ortbRequest = spec.buildRequests(bidRequests, await addFPDToBidderRequest(bidderRequest)).data; - expect(ortbRequest.source.ext.schain).to.equal(expectedSchain); + const ortbRequest = spec.buildRequests(bidRequests, modifiedBidderRequest).data; + expect(ortbRequest.source.ext.schain).to.deep.equal(expectedSchain); }); it('should properly build a request with bcat field', async function () { diff --git a/test/spec/modules/dianomiBidAdapter_spec.js b/test/spec/modules/dianomiBidAdapter_spec.js index ef9283d3dad..a0cb1be68de 100644 --- a/test/spec/modules/dianomiBidAdapter_spec.js +++ b/test/spec/modules/dianomiBidAdapter_spec.js @@ -284,11 +284,17 @@ describe('Dianomi adapter', () => { { bidId: 'bidId', params: { smartadId: 1234 }, - schain: { - validation: 'strict', - config: { - ver: '1.0', - }, + ortb2: { + source: { + ext: { + schain: { + validation: 'strict', + config: { + ver: '1.0', + }, + } + } + } }, }, ]; diff --git a/test/spec/modules/digitalMatterBidAdapter_spec.js b/test/spec/modules/digitalMatterBidAdapter_spec.js index 87056588cd9..12ff5d89c0b 100644 --- a/test/spec/modules/digitalMatterBidAdapter_spec.js +++ b/test/spec/modules/digitalMatterBidAdapter_spec.js @@ -117,10 +117,16 @@ describe('Digital Matter BidAdapter', function () { it('should pass supply chain object', function () { let validBidRequests = { ...bid, - schain: { - validation: 'strict', - config: { - ver: '1.0' + ortb2: { + source: { + ext: { + schain: { + validation: 'strict', + config: { + ver: '1.0' + } + } + } } } }; diff --git a/test/spec/modules/driftpixelBidAdapter_spec.js b/test/spec/modules/driftpixelBidAdapter_spec.js index 057ca1c7e9f..a7b5a164996 100644 --- a/test/spec/modules/driftpixelBidAdapter_spec.js +++ b/test/spec/modules/driftpixelBidAdapter_spec.js @@ -128,18 +128,20 @@ describe('driftpixelBidAdapter', () => { it('should build request with schain', function () { const schainRequest = deepClone(defaultRequest); - schainRequest.schain = { - validation: 'strict', - config: { - ver: '1.0' + const bidderRequest = { + ortb2: { + source: { + ext: { + schain: { + ver: '1.0' + } + } + } } }; - const request = JSON.parse(spec.buildRequests([schainRequest], {}).data)[0]; + const request = JSON.parse(spec.buildRequests([schainRequest], bidderRequest).data)[0]; expect(request).to.have.property('schain').and.to.deep.equal({ - validation: 'strict', - config: { - ver: '1.0' - } + ver: '1.0' }); }); diff --git a/test/spec/modules/dspxBidAdapter_spec.js b/test/spec/modules/dspxBidAdapter_spec.js index ad7bc827837..fb135142744 100644 --- a/test/spec/modules/dspxBidAdapter_spec.js +++ b/test/spec/modules/dspxBidAdapter_spec.js @@ -110,18 +110,24 @@ describe('dspxAdapter', function () { 'crumbs': { 'pubcid': 'e09ab6a3-ae74-4f01-b2e8-81b141d6dc61' }, - 'schain': { - 'ver': '1.0', - 'complete': 1, - 'nodes': [ - { - 'asi': 'example.com', - 'sid': '0', - 'hp': 1, - 'rid': 'bidrequestid', - 'domain': 'example.com' + 'ortb2': { + 'source': { + 'ext': { + 'schain': { + 'ver': '1.0', + 'complete': 1, + 'nodes': [ + { + 'asi': 'example.com', + 'sid': '0', + 'hp': 1, + 'rid': 'bidrequestid', + 'domain': 'example.com' + } + ] + } } - ] + } } }, { diff --git a/test/spec/modules/eplanningBidAdapter_spec.js b/test/spec/modules/eplanningBidAdapter_spec.js index efb2f4a4cbb..ee9a1ea5f82 100644 --- a/test/spec/modules/eplanningBidAdapter_spec.js +++ b/test/spec/modules/eplanningBidAdapter_spec.js @@ -62,19 +62,25 @@ describe('E-Planning Adapter', function () { }, 'adUnitCode': ADUNIT_CODE2, 'sizes': [[300, 250], [300, 600]], - 'schain': { - ver: '1.0', - complete: 1, - nodes: [ - { - asi: 'directseller.com', - sid: '00001', - rid: 'BidRequest1', - hp: 1, - name: 'publisher', - domain: 'publisher.com' + 'ortb2': { + 'source': { + 'ext': { + 'schain': { + ver: '1.0', + complete: 1, + nodes: [ + { + asi: 'directseller.com', + sid: '00001', + rid: 'BidRequest1', + hp: 1, + name: 'publisher', + domain: 'publisher.com' + } + ] + } } - ] + } } }; const validBidWithSchainNodes = { @@ -85,35 +91,41 @@ describe('E-Planning Adapter', function () { }, 'adUnitCode': ADUNIT_CODE2, 'sizes': [[300, 250], [300, 600]], - 'schain': { - ver: '1.0', - complete: 1, - nodes: [ - { - asi: 'directseller.com', - sid: '00001', - rid: 'BidRequest1', - hp: 1, - name: 'publisher', - domain: 'publisher.com' - }, - { - asi: 'reseller.com', - sid: 'aaaaa', - rid: 'BidRequest2', - hp: 1, - name: 'publisher2', - domain: 'publisher2.com' - }, - { - asi: 'reseller3.com', - sid: 'aaaaab', - rid: 'BidRequest3', - hp: 1, - name: 'publisher3', - domain: 'publisher3.com' + 'ortb2': { + 'source': { + 'ext': { + 'schain': { + ver: '1.0', + complete: 1, + nodes: [ + { + asi: 'directseller.com', + sid: '00001', + rid: 'BidRequest1', + hp: 1, + name: 'publisher', + domain: 'publisher.com' + }, + { + asi: 'reseller.com', + sid: 'aaaaa', + rid: 'BidRequest2', + hp: 1, + name: 'publisher2', + domain: 'publisher2.com' + }, + { + asi: 'reseller3.com', + sid: 'aaaaab', + rid: 'BidRequest3', + hp: 1, + name: 'publisher3', + domain: 'publisher3.com' + } + ] + } } - ] + } } }; const ML = '1'; @@ -796,7 +808,7 @@ describe('E-Planning Adapter', function () { }); it('should return sch parameter', function () { let bidRequests = [validBidWithSchain], schainExpected, schain; - schain = validBidWithSchain.schain; + schain = validBidWithSchain.ortb2.source.ext.schain; schainExpected = schain.ver + ',' + schain.complete + '!' + schain.nodes.map(node => node.asi + ',' + node.sid + ',' + node.hp + ',' + node.rid + ',' + node.name + ',' + node.domain).join('!'); const data = spec.buildRequests(bidRequests, bidderRequest).data; expect(data.sch).to.deep.equal(schainExpected); diff --git a/test/spec/modules/escalaxBidAdapter_spec.js b/test/spec/modules/escalaxBidAdapter_spec.js index 6238e6cb208..3e539cc1bdc 100644 --- a/test/spec/modules/escalaxBidAdapter_spec.js +++ b/test/spec/modules/escalaxBidAdapter_spec.js @@ -12,7 +12,6 @@ import 'modules/multibid/index.js'; import 'modules/priceFloors.js'; import 'modules/consentManagementTcf.js'; import 'modules/consentManagementUsp.js'; -import 'modules/schain.js'; const SIMPLE_BID_REQUEST = { bidder: 'escalax', diff --git a/test/spec/modules/fluctBidAdapter_spec.js b/test/spec/modules/fluctBidAdapter_spec.js index 82edcdac79e..e55e3f4bedc 100644 --- a/test/spec/modules/fluctBidAdapter_spec.js +++ b/test/spec/modules/fluctBidAdapter_spec.js @@ -338,16 +338,22 @@ describe('fluctAdapter', function () { // this should be done by schain.js const bidRequests2 = bidRequests.map( (bidReq) => Object.assign({}, bidReq, { - schain: { - ver: '1.0', - complete: 1, - nodes: [ - { - asi: 'example.com', - sid: 'publisher-id', - hp: 1 + ortb2: { + source: { + ext: { + schain: { + ver: '1.0', + complete: 1, + nodes: [ + { + asi: 'example.com', + sid: 'publisher-id', + hp: 1 + } + ] + } } - ] + } } }) ); diff --git a/test/spec/modules/freewheel-sspBidAdapter_spec.js b/test/spec/modules/freewheel-sspBidAdapter_spec.js index 94b7f04b637..e96208c3b16 100644 --- a/test/spec/modules/freewheel-sspBidAdapter_spec.js +++ b/test/spec/modules/freewheel-sspBidAdapter_spec.js @@ -102,18 +102,24 @@ describe('freewheelSSP BidAdapter Test', () => { 'bidId': '30b31c1838de1e', 'bidderRequestId': '22edbae2733bf6', 'auctionId': '1d1a030790a475', - 'schain': { - 'ver': '1.0', - 'complete': 1, - 'nodes': [ - { - 'asi': 'example.com', - 'sid': '0', - 'hp': 1, - 'rid': 'bidrequestid', - 'domain': 'example.com' + 'ortb2': { + 'source': { + 'ext': { + 'schain': { + 'ver': '1.0', + 'complete': 1, + 'nodes': [ + { + 'asi': 'example.com', + 'sid': '0', + 'hp': 1, + 'rid': 'bidrequestid', + 'domain': 'example.com' + } + ] + } } - ] + } } } ]; diff --git a/test/spec/modules/gamoshiBidAdapter_spec.js b/test/spec/modules/gamoshiBidAdapter_spec.js index 8f9818ed901..270c0f7c2aa 100644 --- a/test/spec/modules/gamoshiBidAdapter_spec.js +++ b/test/spec/modules/gamoshiBidAdapter_spec.js @@ -56,7 +56,13 @@ describe('GamoshiAdapter', () => { consentString: 'some string', gdprApplies: true }, - schain: schainConfig, + ortb2: { + source: { + ext: { + schain: schainConfig + } + } + }, uspConsent: 'gamoshiCCPA' }; @@ -339,7 +345,7 @@ describe('GamoshiAdapter', () => { expect(response.data.imp[0].bidfloor).to.equal(0); expect(response.data.imp[0].bidfloorcur).to.equal('USD'); expect(response.data.regs.ext.us_privacy).to.equal('gamoshiCCPA');// USP/CCPAs - expect(response.data.source.ext.schain).to.deep.equal(bidRequest2.schain); + expect(response.data.source.ext.schain).to.deep.equal(bidRequest2.ortb2.source.ext.schain); const bidRequestWithInstlEquals1 = utils.deepClone(bidRequest); bidRequestWithInstlEquals1.params.instl = 1; diff --git a/test/spec/modules/gridBidAdapter_spec.js b/test/spec/modules/gridBidAdapter_spec.js index e9ad45de45c..d35e8d8dbe8 100644 --- a/test/spec/modules/gridBidAdapter_spec.js +++ b/test/spec/modules/gridBidAdapter_spec.js @@ -540,7 +540,13 @@ describe('TheMediaGrid Adapter', function () { }; const bidRequestsWithSChain = bidRequests.map((bid) => { return Object.assign({ - schain: schain + ortb2: { + source: { + ext: { + schain: schain + } + } + } }, bid); }); const [request] = spec.buildRequests(bidRequestsWithSChain, bidderRequest); diff --git a/test/spec/modules/gumgumBidAdapter_spec.js b/test/spec/modules/gumgumBidAdapter_spec.js index 8618b647d25..f497e49350a 100644 --- a/test/spec/modules/gumgumBidAdapter_spec.js +++ b/test/spec/modules/gumgumBidAdapter_spec.js @@ -171,27 +171,33 @@ describe('gumgumAdapter', function () { adUnitCode: 'adunit-code', sizes: sizesArray, bidId: '30b31c1838de1e', - schain: { - ver: '1.0', - complete: 1, - nodes: [ - { - asi: 'exchange1.com', - sid: '1234', - hp: 1, - rid: 'bid-request-1', - name: 'publisher', - domain: 'publisher.com' - }, - { - asi: 'exchange2.com', - sid: 'abcd', - hp: 1, - rid: 'bid-request-2', - name: 'intermediary', - domain: 'intermediary.com' + ortb2: { + source: { + ext: { + schain: { + ver: '1.0', + complete: 1, + nodes: [ + { + asi: 'exchange1.com', + sid: '1234', + hp: 1, + rid: 'bid-request-1', + name: 'publisher', + domain: 'publisher.com' + }, + { + asi: 'exchange2.com', + sid: 'abcd', + hp: 1, + rid: 'bid-request-2', + name: 'intermediary', + domain: 'intermediary.com' + } + ] + } } - ] + } } } ]; diff --git a/test/spec/modules/improvedigitalBidAdapter_spec.js b/test/spec/modules/improvedigitalBidAdapter_spec.js index adbf30bb5f1..c7c074795f0 100644 --- a/test/spec/modules/improvedigitalBidAdapter_spec.js +++ b/test/spec/modules/improvedigitalBidAdapter_spec.js @@ -12,7 +12,6 @@ import 'modules/multibid/index.js'; import 'modules/priceFloors.js'; import 'modules/consentManagementTcf.js'; import 'modules/consentManagementUsp.js'; -import 'modules/schain.js'; import {decorateAdUnitsWithNativeParams} from '../../../src/native.js'; import {hook} from '../../../src/hook.js'; import {addFPDToBidderRequest} from '../../helpers/fpd.js'; @@ -553,8 +552,25 @@ describe('Improve Digital Adapter Tests', function () { it('should add schain', function () { const schain = '{"ver":"1.0","complete":1,"nodes":[{"asi":"headerlift.com","sid":"xyz","hp":1}]}'; const bidRequest = Object.assign({}, simpleBidRequest); - bidRequest.schain = schain; - const request = spec.buildRequests([bidRequest], bidderRequestReferrer)[0]; + + // Add schain to both locations in the bid + bidRequest.ortb2 = { + source: { + ext: {schain: schain} + } + }; + + // Add schain to bidderRequest as well + const modifiedBidderRequest = { + ...bidderRequestReferrer, + ortb2: { + source: { + ext: {schain: schain} + } + } + }; + + const request = spec.buildRequests([bidRequest], modifiedBidderRequest)[0]; const payload = JSON.parse(request.data); expect(payload.source.ext.schain).to.equal(schain); }); diff --git a/test/spec/modules/insticatorBidAdapter_spec.js b/test/spec/modules/insticatorBidAdapter_spec.js index d6daceddd6e..31df4714d58 100644 --- a/test/spec/modules/insticatorBidAdapter_spec.js +++ b/test/spec/modules/insticatorBidAdapter_spec.js @@ -46,17 +46,23 @@ describe('InsticatorBidAdapter', function () { gpid: '1111/homepage' } }, - schain: { - ver: '1.0', - complete: 1, - nodes: [ - { - asi: 'insticator.com', - sid: '00001', - hp: 1, - rid: bidderRequestId + ortb2: { + source: { + ext: { + schain: { + ver: '1.0', + complete: 1, + nodes: [ + { + asi: 'insticator.com', + sid: '00001', + hp: 1, + rid: bidderRequestId + } + ] + } } - ] + } }, userIdAsEids: [ { diff --git a/test/spec/modules/iqxBidAdapter_spec.js b/test/spec/modules/iqxBidAdapter_spec.js index 553bfa4a87d..8ca6fce841c 100644 --- a/test/spec/modules/iqxBidAdapter_spec.js +++ b/test/spec/modules/iqxBidAdapter_spec.js @@ -117,18 +117,20 @@ describe('iqxBidAdapter', () => { it('should build request with schain', function () { const schainRequest = deepClone(defaultRequest); - schainRequest.schain = { - validation: 'strict', - config: { - ver: '1.0' + const bidderRequest = { + ortb2: { + source: { + ext: { + schain: { + ver: '1.0' + } + } + } } }; - const request = JSON.parse(spec.buildRequests([schainRequest], {}).data)[0]; + const request = JSON.parse(spec.buildRequests([schainRequest], bidderRequest).data)[0]; expect(request).to.have.property('schain').and.to.deep.equal({ - validation: 'strict', - config: { - ver: '1.0' - } + ver: '1.0' }); }); diff --git a/test/spec/modules/ixBidAdapter_spec.js b/test/spec/modules/ixBidAdapter_spec.js index 69a052f0e4e..a792a806e08 100644 --- a/test/spec/modules/ixBidAdapter_spec.js +++ b/test/spec/modules/ixBidAdapter_spec.js @@ -130,7 +130,13 @@ describe('IndexexchangeAdapter', function () { bidId: '1a2b3c4e', bidderRequestId: '11a22b33c44e', auctionId: '1aa2bb3cc4de', - schain: SAMPLE_SCHAIN + ortb2: { + source: { + ext: { + schain: SAMPLE_SCHAIN + } + } + } } ]; @@ -157,7 +163,13 @@ describe('IndexexchangeAdapter', function () { bidId: '1a2b3c4d', bidderRequestId: '11a22b33c44d', auctionId: '1aa2bb3cc4dd', - schain: SAMPLE_SCHAIN + ortb2: { + source: { + ext: { + schain: SAMPLE_SCHAIN + } + } + } } ]; @@ -185,7 +197,13 @@ describe('IndexexchangeAdapter', function () { bidId: '1a2b3c4d', bidderRequestId: '11a22b33c44d', auctionId: '1aa2bb3cc4dd', - schain: SAMPLE_SCHAIN + ortb2: { + source: { + ext: { + schain: SAMPLE_SCHAIN + } + } + } } ]; @@ -214,7 +232,13 @@ describe('IndexexchangeAdapter', function () { bidId: '1a2b3c4d', bidderRequestId: '11a22b33c44d', auctionId: '1aa2bb3cc4dd', - schain: SAMPLE_SCHAIN + ortb2: { + source: { + ext: { + schain: SAMPLE_SCHAIN + } + } + } } ]; @@ -239,7 +263,13 @@ describe('IndexexchangeAdapter', function () { bidId: '1a2b3c4d', bidderRequestId: '11a22b33c44d', auctionId: '1aa2bb3cc4dd', - schain: SAMPLE_SCHAIN + ortb2: { + source: { + ext: { + schain: SAMPLE_SCHAIN + } + } + } } ]; @@ -273,7 +303,13 @@ describe('IndexexchangeAdapter', function () { bidId: '1a2b3c4e', bidderRequestId: '11a22b33c44e', auctionId: '1aa2bb3cc4de', - schain: SAMPLE_SCHAIN + ortb2: { + source: { + ext: { + schain: SAMPLE_SCHAIN + } + } + } } ]; @@ -311,7 +347,13 @@ describe('IndexexchangeAdapter', function () { bidId: '1a2b3c4e', bidderRequestId: '11a22b33c44e', auctionId: '1aa2bb3cc4de', - schain: SAMPLE_SCHAIN + ortb2: { + source: { + ext: { + schain: SAMPLE_SCHAIN + } + } + } } ]; @@ -349,7 +391,13 @@ describe('IndexexchangeAdapter', function () { bidId: '1a2b3c4e', bidderRequestId: '11a22b33c44e', auctionId: '1aa2bb3cc4de', - schain: SAMPLE_SCHAIN + ortb2: { + source: { + ext: { + schain: SAMPLE_SCHAIN + } + } + } } ]; @@ -383,7 +431,13 @@ describe('IndexexchangeAdapter', function () { bidId: '1a2b3c4e', bidderRequestId: '11a22b33c44e', auctionId: '1aa2bb3cc4de', - schain: SAMPLE_SCHAIN + ortb2: { + source: { + ext: { + schain: SAMPLE_SCHAIN + } + } + } } ]; @@ -427,7 +481,13 @@ describe('IndexexchangeAdapter', function () { bidId: '1a2b3c4e', bidderRequestId: '11a22b33c44e', auctionId: '1aa2bb3cc4de', - schain: SAMPLE_SCHAIN + ortb2: { + source: { + ext: { + schain: SAMPLE_SCHAIN + } + } + } } ]; @@ -499,7 +559,13 @@ describe('IndexexchangeAdapter', function () { bidId: '1a2b3c4e', bidderRequestId: '11a22b33c44e', auctionId: '1aa2bb3cc4de', - schain: SAMPLE_SCHAIN + ortb2: { + source: { + ext: { + schain: SAMPLE_SCHAIN + } + } + } } ]; @@ -546,7 +612,13 @@ describe('IndexexchangeAdapter', function () { bidId: '1a2b3c4f', bidderRequestId: '11a22b33c44f', auctionId: '1aa2bb3cc4df', - schain: SAMPLE_SCHAIN + ortb2: { + source: { + ext: { + schain: SAMPLE_SCHAIN + } + } + } } ]; @@ -587,7 +659,13 @@ describe('IndexexchangeAdapter', function () { bidId: '1a2b3c4e', bidderRequestId: '11a22b33c44e', auctionId: '1aa2bb3cc4de', - schain: SAMPLE_SCHAIN + ortb2: { + source: { + ext: { + schain: SAMPLE_SCHAIN + } + } + } } ]; @@ -2074,7 +2152,9 @@ describe('IndexexchangeAdapter', function () { describe('buildRequests', function () { const bidWithoutSchain = utils.deepClone(DEFAULT_BANNER_VALID_BID); - delete bidWithoutSchain[0].schain; + if (bidWithoutSchain[0].ortb2 && bidWithoutSchain[0].ortb2.source && bidWithoutSchain[0].ortb2.source.ext) { + delete bidWithoutSchain[0].ortb2.source.ext.schain; + } const GPID = '/19968336/some-adunit-path'; let request, requestUrl, requestMethod, payloadData, requestWithoutSchain, payloadWithoutSchain; @@ -2682,7 +2762,9 @@ describe('IndexexchangeAdapter', function () { const bid = utils.deepClone(DEFAULT_VIDEO_VALID_BID); bid[0].mediaTypes.video.context = 'outstream'; bid[0].mediaTypes.video.w = [[300, 143]]; - bid[0].schain = undefined; + if (bid[0].ortb2 && bid[0].ortb2.source && bid[0].ortb2.source.ext) { + delete bid[0].ortb2.source.ext.schain; + } const request = spec.buildRequests(bid); const videoImpression = extractPayload(request[0]).imp[0]; expect(videoImpression.displaymanager).to.equal('ix'); @@ -2695,7 +2777,9 @@ describe('IndexexchangeAdapter', function () { url: 'http://publisherplayer.js', render: () => { } }; - bid[0].schain = undefined; + if (bid[0].ortb2 && bid[0].ortb2.source && bid[0].ortb2.source.ext) { + delete bid[0].ortb2.source.ext.schain; + } const request = spec.buildRequests(bid); const videoImpression = extractPayload(request[0]).imp[0]; expect(videoImpression.displaymanager).to.equal('http://publisherplayer.js'); @@ -2708,7 +2792,9 @@ describe('IndexexchangeAdapter', function () { url: 'publisherplayer.js', render: () => { } }; - bid[0].schain = undefined; + if (bid[0].ortb2 && bid[0].ortb2.source && bid[0].ortb2.source.ext) { + delete bid[0].ortb2.source.ext.schain; + } const request = spec.buildRequests(bid); const videoImpression = extractPayload(request[0]).imp[0]; expect(videoImpression.displaymanager).to.be.undefined; @@ -2721,7 +2807,9 @@ describe('IndexexchangeAdapter', function () { url: 'http://js-sec.indexww.rendererplayer.com', render: () => { } }; - bid[0].schain = undefined; + if (bid[0].ortb2 && bid[0].ortb2.source && bid[0].ortb2.source.ext) { + delete bid[0].ortb2.source.ext.schain; + } const request = spec.buildRequests(bid); const videoImpression = extractPayload(request[0]).imp[0]; expect(videoImpression.displaymanager).to.equal('ix'); @@ -2733,7 +2821,9 @@ describe('IndexexchangeAdapter', function () { bid[0].mediaTypes.video.renderer = { render: () => { } }; - bid[0].schain = undefined; + if (bid[0].ortb2 && bid[0].ortb2.source && bid[0].ortb2.source.ext) { + delete bid[0].ortb2.source.ext.schain; + } const request = spec.buildRequests(bid); const videoImpression = extractPayload(request[0]).imp[0]; expect(videoImpression.displaymanager).to.be.undefined; @@ -2742,7 +2832,13 @@ describe('IndexexchangeAdapter', function () { const bid = utils.deepClone(DEFAULT_VIDEO_VALID_BID); bid[0].mediaTypes.video.context = 'outstream'; bid[0].mediaTypes.video.w = [[300, 143]]; - bid[0].schain = SAMPLE_SCHAIN; + bid[0].ortb2 = { + source: { + ext: { + schain: SAMPLE_SCHAIN + } + } + }; const request = spec.buildRequests(bid); const videoImpression = extractPayload(request[0]).imp[0]; expect(videoImpression.displaymanager).to.equal('pbjs_wrapper'); diff --git a/test/spec/modules/jixieBidAdapter_spec.js b/test/spec/modules/jixieBidAdapter_spec.js index 5428fd0db0f..422c1c470ed 100644 --- a/test/spec/modules/jixieBidAdapter_spec.js +++ b/test/spec/modules/jixieBidAdapter_spec.js @@ -369,7 +369,15 @@ describe('jixie Adapter', function () { hp: 1 }] }; - const oneSpecialBidReq = Object.assign({}, bidRequests_[0], { schain: schain }); + const oneSpecialBidReq = Object.assign({}, bidRequests_[0], { + ortb2: { + source: { + ext: { + schain: schain + } + } + } + }); const request = spec.buildRequests([oneSpecialBidReq], bidderRequest_); const payload = JSON.parse(request.data); expect(payload.schain).to.deep.equal(schain); diff --git a/test/spec/modules/justpremiumBidAdapter_spec.js b/test/spec/modules/justpremiumBidAdapter_spec.js index b08be01461b..8133d903df7 100644 --- a/test/spec/modules/justpremiumBidAdapter_spec.js +++ b/test/spec/modules/justpremiumBidAdapter_spec.js @@ -46,7 +46,13 @@ describe('justpremium adapter', function () { zone: 28313, allow: ['lb', 'wp'] }, - schain: schainConfig + ortb2: { + source: { + ext: { + schain: schainConfig + } + } + } }, { adUnitCode: 'div-gpt-ad-1471513102552-2', diff --git a/test/spec/modules/jwplayerBidAdapter_spec.js b/test/spec/modules/jwplayerBidAdapter_spec.js index e19790a9670..ae456919238 100644 --- a/test/spec/modules/jwplayerBidAdapter_spec.js +++ b/test/spec/modules/jwplayerBidAdapter_spec.js @@ -148,16 +148,22 @@ describe('jwplayerBidAdapter', function() { playbackend: 2 } }, - schain: { - ver: '1.0', - complete: 1, - nodes: [ - { - asi: 'publisher.com', - sid: '00001', - hp: 1 + ortb2: { + source: { + ext: { + schain: { + ver: '1.0', + complete: 1, + nodes: [ + { + asi: 'publisher.com', + sid: '00001', + hp: 1 + } + ] + } } - ] + } }, bidRequestsCount: 1, adUnitCode: 'testAdUnitCode', diff --git a/test/spec/modules/kargoBidAdapter_spec.js b/test/spec/modules/kargoBidAdapter_spec.js index 2a92b347bcb..2b9c0e2ec64 100644 --- a/test/spec/modules/kargoBidAdapter_spec.js +++ b/test/spec/modules/kargoBidAdapter_spec.js @@ -512,39 +512,57 @@ describe('kargo adapter tests', function() { schain: {} }, { ...minimumBidParams, - schain: { - complete: 1, - nodes: [{ - asi: 'test-page.com', - hp: 1, - rid: '57bdd953-6e57-4d5b-9351-ed67ca238890', - sid: '8190248274' - }] + ortb2: { + source: { + ext: { + schain: { + complete: 1, + nodes: [{ + asi: 'test-page.com', + hp: 1, + rid: '57bdd953-6e57-4d5b-9351-ed67ca238890', + sid: '8190248274' + }] + } + } + } } }]); expect(payload.schain).to.be.undefined; payload = getPayloadFromTestBids([{ ...minimumBidParams, - schain: { - complete: 1, - nodes: [{ - asi: 'test-page.com', - hp: 1, - rid: '57bdd953-6e57-4d5b-9351-ed67ca238890', - sid: '8190248274' - }] + ortb2: { + source: { + ext: { + schain: { + complete: 1, + nodes: [{ + asi: 'test-page.com', + hp: 1, + rid: '57bdd953-6e57-4d5b-9351-ed67ca238890', + sid: '8190248274' + }] + } + } + } } }, { ...minimumBidParams, - schain: { - complete: 1, - nodes: [{ - asi: 'test-page-2.com', - hp: 1, - rid: 'other-rid', - sid: 'other-sid' - }] + ortb2: { + source: { + ext: { + schain: { + complete: 1, + nodes: [{ + asi: 'test-page-2.com', + hp: 1, + rid: 'other-rid', + sid: 'other-sid' + }] + } + } + } } }]); expect(payload.schain).to.deep.equal({ diff --git a/test/spec/modules/kubientBidAdapter_spec.js b/test/spec/modules/kubientBidAdapter_spec.js index a6241aa8d41..71136c2c8cd 100644 --- a/test/spec/modules/kubientBidAdapter_spec.js +++ b/test/spec/modules/kubientBidAdapter_spec.js @@ -30,18 +30,24 @@ describe('KubientAdapter', function () { } }, transactionId: '3bb2f6da-87a6-4029-aeb0-bfe951372e62', - schain: { - ver: '1.1', - complete: 1, - nodes: [ - { - asi: 'example.com', - sid: '0', - hp: 1, - rid: 'bidrequestid', - domain: 'example.com' + ortb2: { + source: { + ext: { + schain: { + ver: '1.1', + complete: 1, + nodes: [ + { + asi: 'example.com', + sid: '0', + hp: 1, + rid: 'bidrequestid', + domain: 'example.com' + } + ] + } } - ] + } } }; let bidVideo = { @@ -67,18 +73,24 @@ describe('KubientAdapter', function () { } }, transactionId: '3bb2f6da-87a6-4029-aeb0-bfe951372e61', - schain: { - ver: '1.1', - complete: 1, - nodes: [ - { - asi: 'example.com', - sid: '0', - hp: 1, - rid: 'bidrequestid', - domain: 'example.com' + ortb2: { + source: { + ext: { + schain: { + ver: '1.1', + complete: 1, + nodes: [ + { + asi: 'example.com', + sid: '0', + hp: 1, + rid: 'bidrequestid', + domain: 'example.com' + } + ] + } } - ] + } } }; let consentString = 'BOJ8RZsOJ8RZsABAB8AAAAAZ+A=='; diff --git a/test/spec/modules/lemmaDigitalBidAdapter_spec.js b/test/spec/modules/lemmaDigitalBidAdapter_spec.js index ab4c3259671..67e8ea0fb9d 100644 --- a/test/spec/modules/lemmaDigitalBidAdapter_spec.js +++ b/test/spec/modules/lemmaDigitalBidAdapter_spec.js @@ -59,7 +59,7 @@ describe('lemmaDigitalBidAdapter', function () { [300, 250], [300, 600] ], - schain: schainConfig + ortb2: { source: { ext: { schain: schainConfig } } } }]; videoBidRequests = [{ code: 'video1', @@ -84,7 +84,7 @@ describe('lemmaDigitalBidAdapter', function () { maxduration: 30 } }, - schain: schainConfig + ortb2: { source: { ext: { schain: schainConfig } } } }]; bidResponses = { 'body': { @@ -227,7 +227,7 @@ describe('lemmaDigitalBidAdapter', function () { expect(data.imp[0].tagid).to.equal('1'); // tagid expect(data.imp[0].bidfloorcur).to.equal(bidRequests[0].params.currency); expect(data.imp[0].bidfloor).to.equal(bidRequests[0].params.bidFloor); - expect(data.source.ext.schain).to.deep.equal(bidRequests[0].schain); + expect(data.source.ext.schain).to.deep.equal(bidRequests[0].ortb2.source.ext.schain); }); it('Set sizes from mediaTypes object', function () { @@ -245,7 +245,7 @@ describe('lemmaDigitalBidAdapter', function () { }); it('Check device, source object not present', function () { let newBannerRequest = utils.deepClone(bidRequests); - delete newBannerRequest[0].schain; + delete newBannerRequest[0].ortb2; let request = spec.buildRequests(newBannerRequest); let data = JSON.parse(request.data); delete data.device; @@ -461,7 +461,7 @@ describe('lemmaDigitalBidAdapter', function () { expect(data.imp[0]['video']['maxduration']).to.equal(videoBidRequests[0].params.video['maxduration']); expect(data.imp[0]['video']['w']).to.equal(videoBidRequests[0].mediaTypes.video.playerSize[0]); expect(data.imp[0]['video']['h']).to.equal(videoBidRequests[0].mediaTypes.video.playerSize[1]); - expect(data.source.ext.schain).to.deep.equal(videoBidRequests[0].schain); + expect(data.source.ext.schain).to.deep.equal(videoBidRequests[0].ortb2.source.ext.schain); }); describe('setting imp.floor using floorModule', function () { /* diff --git a/test/spec/modules/limelightDigitalBidAdapter_spec.js b/test/spec/modules/limelightDigitalBidAdapter_spec.js index c84586e9064..ef94070bb9b 100644 --- a/test/spec/modules/limelightDigitalBidAdapter_spec.js +++ b/test/spec/modules/limelightDigitalBidAdapter_spec.js @@ -43,16 +43,22 @@ describe('limelightDigitalAdapter', function () { ] } ], - schain: { - ver: '1.0', - complete: 1, - nodes: [ - { - asi: 'example.com', - sid: '1', - hp: 1 + ortb2: { + source: { + ext: { + schain: { + ver: '1.0', + complete: 1, + nodes: [ + { + asi: 'example.com', + sid: '1', + hp: 1 + } + ] + } } - ] + } } } const bid2 = { @@ -91,21 +97,27 @@ describe('limelightDigitalAdapter', function () { ] } ], - schain: { - ver: '1.0', - complete: 1, - nodes: [ - { - asi: 'example.com', - sid: '1', - hp: 1 - }, - { - asi: 'example1.com', - sid: '2', - hp: 1 + ortb2: { + source: { + ext: { + schain: { + ver: '1.0', + complete: 1, + nodes: [ + { + asi: 'example.com', + sid: '1', + hp: 1 + }, + { + asi: 'example1.com', + sid: '2', + hp: 1 + } + ] + } } - ] + } } } const bid3 = { @@ -148,16 +160,22 @@ describe('limelightDigitalAdapter', function () { ] } ], - schain: { - ver: '1.0', - complete: 1, - nodes: [ - { - asi: 'example.com', - sid: '1', - hp: 1 + ortb2: { + source: { + ext: { + schain: { + ver: '1.0', + complete: 1, + nodes: [ + { + asi: 'example.com', + sid: '1', + hp: 1 + } + ] + } } - ] + } } } const bid4 = { @@ -198,16 +216,22 @@ describe('limelightDigitalAdapter', function () { ] } ], - schain: { - ver: '1.0', - complete: 1, - nodes: [ - { - asi: 'example.com', - sid: '1', - hp: 1 + ortb2: { + source: { + ext: { + schain: { + ver: '1.0', + complete: 1, + nodes: [ + { + asi: 'example.com', + sid: '1', + hp: 1 + } + ] + } } - ] + } } } @@ -739,6 +763,6 @@ function validateAdUnit(adUnit, bid) { })); expect(adUnit.publisherId).to.equal(bid.params.publisherId); expect(adUnit.userIdAsEids).to.deep.equal(bid.userIdAsEids); - expect(adUnit.supplyChain).to.deep.equal(bid.schain); + expect(adUnit.supplyChain).to.deep.equal(bid.ortb2.source.ext.schain); expect(adUnit.ortb2Imp).to.deep.equal(bid.ortb2Imp); } diff --git a/test/spec/modules/livewrappedBidAdapter_spec.js b/test/spec/modules/livewrappedBidAdapter_spec.js index e6e23cfef35..3b4fddc7b9b 100644 --- a/test/spec/modules/livewrappedBidAdapter_spec.js +++ b/test/spec/modules/livewrappedBidAdapter_spec.js @@ -1315,7 +1315,10 @@ describe('Livewrapped adapter tests', function () { ] }; - testbidRequest.bids[0].schain = schain; + testbidRequest.bids[0].ortb2 = testbidRequest.bids[0].ortb2 || {}; + testbidRequest.bids[0].ortb2.source = testbidRequest.bids[0].ortb2.source || {}; + testbidRequest.bids[0].ortb2.source.ext = testbidRequest.bids[0].ortb2.source.ext || {}; + testbidRequest.bids[0].ortb2.source.ext.schain = schain; let result = spec.buildRequests(testbidRequest.bids, testbidRequest); let data = JSON.parse(result.data); diff --git a/test/spec/modules/lm_kiviadsBidAdapter_spec.js b/test/spec/modules/lm_kiviadsBidAdapter_spec.js index b6c4ae9bc4b..32b8d56309b 100644 --- a/test/spec/modules/lm_kiviadsBidAdapter_spec.js +++ b/test/spec/modules/lm_kiviadsBidAdapter_spec.js @@ -117,18 +117,20 @@ describe('lm_kiviadsBidAdapter', () => { it('should build request with schain', function () { const schainRequest = deepClone(defaultRequest); - schainRequest.schain = { - validation: 'strict', - config: { - ver: '1.0' + const bidderRequest = { + ortb2: { + source: { + ext: { + schain: { + ver: '1.0' + } + } + } } }; - const request = JSON.parse(spec.buildRequests([schainRequest], {}).data)[0]; + const request = JSON.parse(spec.buildRequests([schainRequest], bidderRequest).data)[0]; expect(request).to.have.property('schain').and.to.deep.equal({ - validation: 'strict', - config: { - ver: '1.0' - } + ver: '1.0' }); }); diff --git a/test/spec/modules/lockerdomeBidAdapter_spec.js b/test/spec/modules/lockerdomeBidAdapter_spec.js index d65837c39ab..ffbeb1c9340 100644 --- a/test/spec/modules/lockerdomeBidAdapter_spec.js +++ b/test/spec/modules/lockerdomeBidAdapter_spec.js @@ -18,16 +18,22 @@ describe('LockerDomeAdapter', function () { bidId: '2652ca954bce9', bidderRequestId: '14a54fade69854', auctionId: 'd4c83108-615d-4c2c-9384-dac9ffd4fd72', - schain: { - ver: '1.0', - complete: 1, - nodes: [ - { - asi: 'indirectseller.com', - sid: '00001', - hp: 1 + ortb2: { + source: { + ext: { + schain: { + ver: '1.0', + complete: 1, + nodes: [ + { + asi: 'indirectseller.com', + sid: '00001', + hp: 1 + } + ] + } } - ] + } } }, { bidder: 'lockerdome', @@ -44,16 +50,22 @@ describe('LockerDomeAdapter', function () { bidId: '4510f2834773ce', bidderRequestId: '14a54fade69854', auctionId: 'd4c83108-615d-4c2c-9384-dac9ffd4fd72', - schain: { - ver: '1.0', - complete: 1, - nodes: [ - { - asi: 'indirectseller.com', - sid: '00001', - hp: 1 + ortb2: { + source: { + ext: { + schain: { + ver: '1.0', + complete: 1, + nodes: [ + { + asi: 'indirectseller.com', + sid: '00001', + hp: 1 + } + ] + } } - ] + } } }]; diff --git a/test/spec/modules/logicadBidAdapter_spec.js b/test/spec/modules/logicadBidAdapter_spec.js index eb7800077b4..6aeb97af9e3 100644 --- a/test/spec/modules/logicadBidAdapter_spec.js +++ b/test/spec/modules/logicadBidAdapter_spec.js @@ -84,19 +84,23 @@ describe('LogicadAdapter', function () { name: 'cd.ladsp.com' } ] + }, + source: { + ext: { + schain: { + ver: '1.0', + complete: 1, + nodes: [ + { + asi: 'exchange1.com', + sid: '1234', + hp: 1 + } + ] + } + } } }, - schain: { - ver: '1.0', - complete: 1, - nodes: [ - { - asi: 'exchange1.com', - sid: '1234', - hp: 1 - } - ] - } }]; const nativeBidRequests = [{ bidder: 'logicad', diff --git a/test/spec/modules/marsmediaBidAdapter_spec.js b/test/spec/modules/marsmediaBidAdapter_spec.js index ca3876703c8..ea0d0592f59 100644 --- a/test/spec/modules/marsmediaBidAdapter_spec.js +++ b/test/spec/modules/marsmediaBidAdapter_spec.js @@ -612,7 +612,13 @@ describe('marsmedia adapter tests', function () { 'auctionId': '18fd8b8b0bd757', 'bidRequestsCount': 1, 'bidId': '51ef8751f9aead', - 'schain': schain + 'ortb2': { + 'source': { + 'ext': { + 'schain': schain + } + } + } } ]; diff --git a/test/spec/modules/mediafuseBidAdapter_spec.js b/test/spec/modules/mediafuseBidAdapter_spec.js index bc943a9c129..4d627da216f 100644 --- a/test/spec/modules/mediafuseBidAdapter_spec.js +++ b/test/spec/modules/mediafuseBidAdapter_spec.js @@ -846,16 +846,22 @@ describe('MediaFuseAdapter', function () { it('should populate schain if available', function () { const bidRequest = Object.assign({}, bidRequests[0], { - schain: { - ver: '1.0', - complete: 1, - nodes: [ - { - 'asi': 'blob.com', - 'sid': '001', - 'hp': 1 + ortb2: { + source: { + ext: { + schain: { + ver: '1.0', + complete: 1, + nodes: [ + { + 'asi': 'blob.com', + 'sid': '001', + 'hp': 1 + } + ] + } } - ] + } } }); diff --git a/test/spec/modules/mediakeysBidAdapter_spec.js b/test/spec/modules/mediakeysBidAdapter_spec.js index f6bd5c917ad..55fa9bfb858 100644 --- a/test/spec/modules/mediakeysBidAdapter_spec.js +++ b/test/spec/modules/mediakeysBidAdapter_spec.js @@ -451,7 +451,10 @@ describe('mediakeysBidAdapter', function () { ], }; const bidRequests = [utils.deepClone(bid)]; - bidRequests[0].schain = schain; + bidRequests[0].ortb2 = bidRequests[0].ortb2 || {}; + bidRequests[0].ortb2.source = bidRequests[0].ortb2.source || {}; + bidRequests[0].ortb2.source.ext = bidRequests[0].ortb2.source.ext || {}; + bidRequests[0].ortb2.source.ext.schain = schain; const request = spec.buildRequests(bidRequests, bidderRequest); const data = request.data; expect(data.source.ext.schain).to.equal(schain); diff --git a/test/spec/modules/mgidBidAdapter_spec.js b/test/spec/modules/mgidBidAdapter_spec.js index 180b0dc723e..38d2fd135bb 100644 --- a/test/spec/modules/mgidBidAdapter_spec.js +++ b/test/spec/modules/mgidBidAdapter_spec.js @@ -398,11 +398,14 @@ describe('Mgid bid adapter', function () { sizes: [[300, 250]] } }; - bid.schain = ['schain1', 'schain2']; + bid.ortb2 = bid.ortb2 || {}; + bid.ortb2.source = bid.ortb2.source || {}; + bid.ortb2.source.ext = bid.ortb2.source.ext || {}; + bid.ortb2.source.ext.schain = ['schain1', 'schain2']; let bidRequests = [bid]; const request = spec.buildRequests(bidRequests); const data = JSON.parse(request.data); - expect(data.source).to.deep.equal({ext: {schain: bid.schain}}); + expect(data.source).to.deep.equal({ext: {schain: bid.ortb2.source.ext.schain}}); }); it('should handle userId', function () { let bid = Object.assign({}, abid); diff --git a/test/spec/modules/minutemediaBidAdapter_spec.js b/test/spec/modules/minutemediaBidAdapter_spec.js index 4e5cd4883d3..ce3ab6dd9d7 100644 --- a/test/spec/modules/minutemediaBidAdapter_spec.js +++ b/test/spec/modules/minutemediaBidAdapter_spec.js @@ -369,12 +369,17 @@ describe('minutemediaAdapter', function () { }); it('should have schain param if it is available in the bidRequest', () => { - const schain = { - ver: '1.0', - complete: 1, - nodes: [{ asi: 'indirectseller.com', sid: '00001', hp: 1 }], + bidderRequest.ortb2 = { + source: { + ext: { + schain: { + ver: '1.0', + complete: 1, + nodes: [{ asi: 'indirectseller.com', sid: '00001', hp: 1 }], + } + } + } }; - bidRequests[0].schain = schain; const request = spec.buildRequests(bidRequests, bidderRequest); expect(request.data.params).to.be.an('object'); expect(request.data.params).to.have.property('schain', '1.0,1!indirectseller.com,00001,1,,,'); diff --git a/test/spec/modules/missenaBidAdapter_spec.js b/test/spec/modules/missenaBidAdapter_spec.js index ee635e2b686..2ad8223d9b5 100644 --- a/test/spec/modules/missenaBidAdapter_spec.js +++ b/test/spec/modules/missenaBidAdapter_spec.js @@ -31,18 +31,22 @@ describe('Missena Adapter', function () { device: { ext: { cdep: COOKIE_DEPRECATION_LABEL }, }, + source: { + ext: { + schain: { + validation: 'strict', + config: { + ver: '1.0', + }, + }, + }, + }, }, params: { apiKey: API_KEY, placement: 'sticky', formats: ['sticky-banner'], }, - schain: { - validation: 'strict', - config: { - ver: '1.0', - }, - }, getFloor: (inputParams) => { if (inputParams.mediaType === BANNER) { return { diff --git a/test/spec/modules/nextMillenniumBidAdapter_spec.js b/test/spec/modules/nextMillenniumBidAdapter_spec.js index 85143f45df8..53e541efded 100644 --- a/test/spec/modules/nextMillenniumBidAdapter_spec.js +++ b/test/spec/modules/nextMillenniumBidAdapter_spec.js @@ -225,12 +225,18 @@ describe('nextMillenniumBidAdapterTests', () => { title: 'schain is validBidReequest', bidderRequest: {}, validBidRequests: [{ - schain: { - validation: 'strict', - config: { - ver: '1.0', - complete: 1, - nodes: [{asi: 'test.test', sid: '00001', hp: 1}], + ortb2: { + source: { + ext: { + schain: { + validation: 'strict', + config: { + ver: '1.0', + complete: 1, + nodes: [{asi: 'test.test', sid: '00001', hp: 1}], + }, + }, + }, }, }, }], diff --git a/test/spec/modules/nobidBidAdapter_spec.js b/test/spec/modules/nobidBidAdapter_spec.js index 2f2c97eae51..6fe29fa3c3b 100644 --- a/test/spec/modules/nobidBidAdapter_spec.js +++ b/test/spec/modules/nobidBidAdapter_spec.js @@ -937,20 +937,26 @@ describe('Nobid Adapter', function () { bidderRequestId: '22edbae2733bf6', auctionId: '1d1a030790a475', coppa: true, - schain: { - validation: 'strict', - config: { - ver: '1.0', - complete: 1, - nodes: [ - { - asi: 'indirectseller.com', - sid: '00001', - name: 'name.com', - hp: 1 - } - ] - } + ortb2: { + source: { + ext: { + schain: { + validation: 'strict', + config: { + ver: '1.0', + complete: 1, + nodes: [ + { + asi: 'indirectseller.com', + sid: '00001', + name: 'name.com', + hp: 1 + } + ] + } + } + } + } } } ]; diff --git a/test/spec/modules/omsBidAdapter_spec.js b/test/spec/modules/omsBidAdapter_spec.js index b2cb9e1c391..4fe5fa7534a 100644 --- a/test/spec/modules/omsBidAdapter_spec.js +++ b/test/spec/modules/omsBidAdapter_spec.js @@ -57,19 +57,25 @@ describe('omsBidAdapter', function () { 'bidId': '5fb26ac22bde4', 'bidderRequestId': '4bf93aeb730cb9', 'auctionId': 'ffe9a1f7-7b67-4bda-a8e0-9ee5dc9f442e', - 'schain': { - 'ver': '1.0', - 'complete': 1, - 'nodes': [ - { - 'asi': 'exchange1.com', - 'sid': '1234', - 'hp': 1, - 'rid': 'bid-request-1', - 'name': 'publisher', - 'domain': 'publisher.com' + 'ortb2': { + 'source': { + 'ext': { + 'schain': { + 'ver': '1.0', + 'complete': 1, + 'nodes': [ + { + 'asi': 'exchange1.com', + 'sid': '1234', + 'hp': 1, + 'rid': 'bid-request-1', + 'name': 'publisher', + 'domain': 'publisher.com' + } + ] + } } - ] + } }, }]; @@ -150,19 +156,25 @@ describe('omsBidAdapter', function () { 'bidId': '5fb26ac22bde4', 'bidderRequestId': '4bf93aeb730cb9', 'auctionId': 'ffe9a1f7-7b67-4bda-a8e0-9ee5dc9f442e', - 'schain': { - 'ver': '1.0', - 'complete': 1, - 'nodes': [ - { - 'asi': 'exchange1.com', - 'sid': '1234', - 'hp': 1, - 'rid': 'bid-request-1', - 'name': 'publisher', - 'domain': 'publisher.com' + 'ortb2': { + 'source': { + 'ext': { + 'schain': { + 'ver': '1.0', + 'complete': 1, + 'nodes': [ + { + 'asi': 'exchange1.com', + 'sid': '1234', + 'hp': 1, + 'rid': 'bid-request-1', + 'name': 'publisher', + 'domain': 'publisher.com' + } + ] + } } - ] + } }, } ] diff --git a/test/spec/modules/openwebBidAdapter_spec.js b/test/spec/modules/openwebBidAdapter_spec.js index 9ab8f608598..02945125ca2 100644 --- a/test/spec/modules/openwebBidAdapter_spec.js +++ b/test/spec/modules/openwebBidAdapter_spec.js @@ -378,12 +378,17 @@ describe('openwebAdapter', function () { }); it('should have schain param if it is available in the bidRequest', () => { - const schain = { - ver: '1.0', - complete: 1, - nodes: [{ asi: 'indirectseller.com', sid: '00001', hp: 1 }], + bidderRequest.ortb2 = { + source: { + ext: { + schain: { + ver: '1.0', + complete: 1, + nodes: [{ asi: 'indirectseller.com', sid: '00001', hp: 1 }], + } + } + } }; - bidRequests[0].schain = schain; const request = spec.buildRequests(bidRequests, bidderRequest); expect(request.data.params).to.be.an('object'); expect(request.data.params).to.have.property('schain', '1.0,1!indirectseller.com,00001,1,,,'); diff --git a/test/spec/modules/openxBidAdapter_spec.js b/test/spec/modules/openxBidAdapter_spec.js index 18e26b7612e..ead908b531b 100644 --- a/test/spec/modules/openxBidAdapter_spec.js +++ b/test/spec/modules/openxBidAdapter_spec.js @@ -12,7 +12,6 @@ import 'modules/multibid/index.js'; import 'modules/priceFloors.js'; import 'modules/consentManagementTcf.js'; import 'modules/consentManagementUsp.js'; -import 'modules/schain.js'; import 'modules/paapi.js'; import {deepClone} from 'src/utils.js'; @@ -1106,13 +1105,24 @@ describe('OpenxRtbAdapter', function () { bidId: 'test-bid-id-1', bidderRequestId: 'test-bid-request-1', auctionId: 'test-auction-1', - schain: schainConfig + ortb2: {source: { + schain: schainConfig, + ext: {schain: schainConfig} + }} }]; + + // Add schain to mockBidderRequest as well + mockBidderRequest.ortb2 = { + source: { + schain: schainConfig, + ext: {schain: schainConfig} + } + }; }); it('should send a supply chain object', function () { const request = spec.buildRequests(bidRequests, mockBidderRequest); - expect(request[0].data.source.ext.schain).to.equal(schainConfig); + expect(request[0].data.source.ext.schain).to.deep.equal(schainConfig); }); it('should send the supply chain object with the right version', function () { diff --git a/test/spec/modules/opscoBidAdapter_spec.js b/test/spec/modules/opscoBidAdapter_spec.js index 38cacff8f82..77051f56de1 100644 --- a/test/spec/modules/opscoBidAdapter_spec.js +++ b/test/spec/modules/opscoBidAdapter_spec.js @@ -134,7 +134,10 @@ describe('opscoBidAdapter', function () { it('should send schain in the payload if present', function () { const schain = {'ver': '1.0', 'complete': 1, 'nodes': [{'asi': 'exchange1.com', 'sid': '1234', 'hp': 1}]}; - validBid.schain = schain; + validBid.ortb2 = validBid.ortb2 || {}; + validBid.ortb2.source = validBid.ortb2.source || {}; + validBid.ortb2.source.ext = validBid.ortb2.source.ext || {}; + validBid.ortb2.source.ext.schain = schain; const request = spec.buildRequests([validBid], bidderRequest); expect(JSON.parse(request.data).source.ext.schain).to.deep.equal(schain); }); diff --git a/test/spec/modules/optidigitalBidAdapter_spec.js b/test/spec/modules/optidigitalBidAdapter_spec.js index 273b29001d1..dfaab4d5372 100755 --- a/test/spec/modules/optidigitalBidAdapter_spec.js +++ b/test/spec/modules/optidigitalBidAdapter_spec.js @@ -222,14 +222,20 @@ describe('optidigitalAdapterTests', function () { it('should add schain object to payload if exists', function () { const bidRequest = Object.assign({}, validBidRequests[0], { - schain: { - ver: '1.0', - complete: 1, - nodes: [{ - asi: 'examplewebsite.com', - sid: '00001', - hp: 1 - }] + ortb2: { + source: { + ext: { + schain: { + ver: '1.0', + complete: 1, + nodes: [{ + asi: 'examplewebsite.com', + sid: '00001', + hp: 1 + }] + } + } + } } }); const request = spec.buildRequests([bidRequest], bidderRequest); diff --git a/test/spec/modules/ozoneBidAdapter_spec.js b/test/spec/modules/ozoneBidAdapter_spec.js index b2b494b04c6..d23cb39c5cb 100644 --- a/test/spec/modules/ozoneBidAdapter_spec.js +++ b/test/spec/modules/ozoneBidAdapter_spec.js @@ -3151,7 +3151,10 @@ describe('ozone Adapter', function () { } ] }; - br[0]['schain'] = schainConfigObject; + br[0].ortb2 = br[0].ortb2 || {}; + br[0].ortb2.source = br[0].ortb2.source || {}; + br[0].ortb2.source.ext = br[0].ortb2.source.ext || {}; + br[0].ortb2.source.ext.schain = schainConfigObject; const request = spec.buildRequests(br, validBidderRequest); const data = JSON.parse(request.data); expect(data.source.ext).to.haveOwnProperty('schain'); diff --git a/test/spec/modules/prebidServerBidAdapter_spec.js b/test/spec/modules/prebidServerBidAdapter_spec.js index 4051521b0d7..3c0fb6e000d 100644 --- a/test/spec/modules/prebidServerBidAdapter_spec.js +++ b/test/spec/modules/prebidServerBidAdapter_spec.js @@ -24,7 +24,6 @@ import 'modules/priceFloors.js'; import 'modules/consentManagementTcf.js'; import 'modules/consentManagementUsp.js'; import 'modules/consentManagementGpp.js'; -import 'modules/schain.js'; import 'modules/paapi.js'; import * as redactor from 'src/activities/redactor.js'; import * as activityRules from 'src/activities/rules.js'; @@ -2421,14 +2420,18 @@ describe('S2S Adapter', function () { it('should have extPrebid.schains present on req object if bidder specific schains were configured with pbjs', function () { let bidRequest = utils.deepClone(BID_REQUESTS); - bidRequest[0].bids[0].schain = { - complete: 1, - nodes: [{ - asi: 'test.com', - hp: 1, - sid: '11111' - }], - ver: '1.0' + bidRequest[0].bids[0].ortb2 = { + source: { + schain: { + complete: 1, + nodes: [{ + asi: 'test.com', + hp: 1, + sid: '11111' + }], + ver: '1.0' + } + } }; adapter.callBids(REQUEST, bidRequest, addBidResponse, done, ajax); @@ -2511,14 +2514,18 @@ describe('S2S Adapter', function () { it('should add a bidder name to pbs schain if the schain is equal to a pbjs one but the pbjs bidder name is not in the bidder array on the pbs side', function () { let bidRequest = utils.deepClone(BID_REQUESTS); - bidRequest[0].bids[0].schain = { - complete: 1, - nodes: [{ - asi: 'test.com', - hp: 1, - sid: '11111' - }], - ver: '1.0' + bidRequest[0].bids[0].ortb2 = { + source: { + schain: { + complete: 1, + nodes: [{ + asi: 'test.com', + hp: 1, + sid: '11111' + }], + ver: '1.0' + } + } }; bidRequest[0].bids[1] = { diff --git a/test/spec/modules/pubgeniusBidAdapter_spec.js b/test/spec/modules/pubgeniusBidAdapter_spec.js index e1d579aaa4a..5475a58d317 100644 --- a/test/spec/modules/pubgeniusBidAdapter_spec.js +++ b/test/spec/modules/pubgeniusBidAdapter_spec.js @@ -295,7 +295,10 @@ describe('pubGENIUS adapter', () => { } ] }; - bidRequest.schain = deepClone(schain); + bidRequest.ortb2 = bidRequest.ortb2 || {}; + bidRequest.ortb2.source = bidRequest.ortb2.source || {}; + bidRequest.ortb2.source.ext = bidRequest.ortb2.source.ext || {}; + bidRequest.ortb2.source.ext.schain = deepClone(schain); expectedRequest.data.source = { ext: { schain: deepClone(schain) }, }; diff --git a/test/spec/modules/publirBidAdapter_spec.js b/test/spec/modules/publirBidAdapter_spec.js index 60840b82efb..0265fbb4020 100644 --- a/test/spec/modules/publirBidAdapter_spec.js +++ b/test/spec/modules/publirBidAdapter_spec.js @@ -215,12 +215,17 @@ describe('publirAdapter', function () { }); it('should have schain param if it is available in the bidRequest', () => { - const schain = { - ver: '1.0', - complete: 1, - nodes: [{ asi: 'indirectseller.com', sid: '00001', hp: 1 }], + bidderRequest.ortb2 = { + source: { + ext: { + schain: { + ver: '1.0', + complete: 1, + nodes: [{ asi: 'indirectseller.com', sid: '00001', hp: 1 }], + } + } + } }; - bidRequests[0].schain = schain; const request = spec.buildRequests(bidRequests, bidderRequest); expect(request.data.params).to.be.an('object'); expect(request.data.params).to.have.property('schain', '1.0,1!indirectseller.com,00001,1,,,'); diff --git a/test/spec/modules/pulsepointBidAdapter_spec.js b/test/spec/modules/pulsepointBidAdapter_spec.js index 263bf08851f..818acea7791 100644 --- a/test/spec/modules/pulsepointBidAdapter_spec.js +++ b/test/spec/modules/pulsepointBidAdapter_spec.js @@ -6,7 +6,6 @@ import {deepClone} from '../../../src/utils'; import 'modules/consentManagementTcf'; import 'modules/consentManagementUsp'; import 'modules/userId/index'; -import 'modules/schain'; describe('PulsePoint Adapter Tests', function () { const slotConfigs = [{ @@ -135,7 +134,7 @@ describe('PulsePoint Adapter Tests', function () { bidfloor: 1.5, badv: ['cocacola.com', 'lays.com'] }, - schain: { + ortb2: {source: {ext: {schain: { 'ver': '1.0', 'complete': 1, 'nodes': [ @@ -148,7 +147,7 @@ describe('PulsePoint Adapter Tests', function () { 'domain': 'publisher.com' } ] - }, + }}}} }]; const bidderRequest = { @@ -466,8 +465,31 @@ describe('PulsePoint Adapter Tests', function () { expect(ortbRequest.imp[0].ext).to.be.undefined; }); - it('Verify schain parameters', async function () { - const request = spec.buildRequests(schainParamsSlotConfig, await addFPDToBidderRequest(bidderRequest)); + it('Verify schain parameters', function () { + const modifiedBidderRequest = { + ...bidderRequest, + ortb2: { + source: { + ext: { + schain: { + 'ver': '1.0', + 'complete': 1, + 'nodes': [ + { + 'asi': 'exchange1.com', + 'sid': '1234', + 'hp': 1, + 'rid': 'bid-request-1', + 'name': 'publisher', + 'domain': 'publisher.com' + } + ] + } + } + } + } + }; + const request = spec.buildRequests(schainParamsSlotConfig, modifiedBidderRequest); const ortbRequest = request.data; expect(ortbRequest).to.not.equal(null); expect(ortbRequest.source).to.not.equal(null); diff --git a/test/spec/modules/qwarryBidAdapter_spec.js b/test/spec/modules/qwarryBidAdapter_spec.js index 5d48d92066a..fef013f8ce6 100644 --- a/test/spec/modules/qwarryBidAdapter_spec.js +++ b/test/spec/modules/qwarryBidAdapter_spec.js @@ -10,14 +10,20 @@ const REQUEST = { zoneToken: 'e64782a4-8e68-4c38-965b-80ccf115d46f', pos: 7 }, - 'schain': { - ver: '1.0', - complete: 1, - nodes: [{ - asi: 'qwarry.com', - sid: '00001', - hp: 1 - }] + 'ortb2': { + 'source': { + 'ext': { + 'schain': { + 'ver': '1.0', + 'complete': 1, + 'nodes': [{ + 'asi': 'qwarry.com', + 'sid': '00001', + 'hp': 1 + }] + } + } + } } } diff --git a/test/spec/modules/r2b2BidAdapter_spec.js b/test/spec/modules/r2b2BidAdapter_spec.js index 63850b78c40..f9040798abc 100644 --- a/test/spec/modules/r2b2BidAdapter_spec.js +++ b/test/spec/modules/r2b2BidAdapter_spec.js @@ -1,7 +1,6 @@ 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) { @@ -91,9 +90,9 @@ describe('R2B2 adapter', function () { } }, site: {}, - device: {} + device: {}, + source: {ext: {schain: schain}} }, - schain }, { bidder: 'r2b2', params: { @@ -128,9 +127,9 @@ describe('R2B2 adapter', function () { } }, site: {}, - device: {} + device: {}, + source: {ext: {schain: schain}} }, - schain }]; bidderRequest = { bidderCode: 'r2b2', @@ -150,7 +149,8 @@ describe('R2B2 adapter', function () { } }, site: {}, - device: {} + device: {}, + source: {ext: {schain: schain}} }, gdprConsent: { consentString: 'consent-string', diff --git a/test/spec/modules/rhythmoneBidAdapter_spec.js b/test/spec/modules/rhythmoneBidAdapter_spec.js index 359b02db37e..77ae6266eda 100644 --- a/test/spec/modules/rhythmoneBidAdapter_spec.js +++ b/test/spec/modules/rhythmoneBidAdapter_spec.js @@ -704,7 +704,13 @@ describe('rhythmone adapter tests', function () { 'auctionId': '18fd8b8b0bd757', 'bidRequestsCount': 1, 'bidId': '51ef8751f9aead', - 'schain': schain + 'ortb2': { + 'source': { + 'ext': { + 'schain': schain + } + } + } } ]; diff --git a/test/spec/modules/richaudienceBidAdapter_spec.js b/test/spec/modules/richaudienceBidAdapter_spec.js index e4fd11d8604..b5209266762 100644 --- a/test/spec/modules/richaudienceBidAdapter_spec.js +++ b/test/spec/modules/richaudienceBidAdapter_spec.js @@ -817,18 +817,24 @@ describe('Richaudience adapter tests', function () { }] } - DEFAULT_PARAMS_NEW_SIZES[0].schain = { - 'ver': '1.0', - 'complete': 1, - 'nodes': [{ - 'asi': 'richaudience.com', - 'sid': '00001', - 'hp': 1 - }, { - 'asi': 'richaudience-2.com', - 'sid': '00002', - 'hp': 1 - }] + DEFAULT_PARAMS_NEW_SIZES[0].ortb2 = { + source: { + ext: { + schain: { + 'ver': '1.0', + 'complete': 1, + 'nodes': [{ + 'asi': 'richaudience.com', + 'sid': '00001', + 'hp': 1 + }, { + 'asi': 'richaudience-2.com', + 'sid': '00002', + 'hp': 1 + }] + } + } + } } const request = spec.buildRequests(DEFAULT_PARAMS_NEW_SIZES, { diff --git a/test/spec/modules/riseBidAdapter_spec.js b/test/spec/modules/riseBidAdapter_spec.js index a3fef50f825..bb7e07bd69e 100644 --- a/test/spec/modules/riseBidAdapter_spec.js +++ b/test/spec/modules/riseBidAdapter_spec.js @@ -395,12 +395,17 @@ describe('riseAdapter', function () { }); it('should have schain param if it is available in the bidRequest', () => { - const schain = { - ver: '1.0', - complete: 1, - nodes: [{ asi: 'indirectseller.com', sid: '00001', hp: 1 }], + bidderRequest.ortb2 = { + source: { + ext: { + schain: { + ver: '1.0', + complete: 1, + nodes: [{ asi: 'indirectseller.com', sid: '00001', hp: 1 }], + } + } + } }; - bidRequests[0].schain = schain; const request = spec.buildRequests(bidRequests, bidderRequest); expect(request.data.params).to.be.an('object'); expect(request.data.params).to.have.property('schain', '1.0,1!indirectseller.com,00001,1,,,'); diff --git a/test/spec/modules/rtbhouseBidAdapter_spec.js b/test/spec/modules/rtbhouseBidAdapter_spec.js index ee18257d171..8dd5dd8eca6 100644 --- a/test/spec/modules/rtbhouseBidAdapter_spec.js +++ b/test/spec/modules/rtbhouseBidAdapter_spec.js @@ -91,17 +91,23 @@ describe('RTBHouseAdapter', () => { 'tid': 'ortb2Imp-transaction-id-1' } }, - 'schain': { - 'ver': '1.0', - 'complete': 1, - 'nodes': [ - { - 'asi': 'directseller.com', - 'sid': '00001', - 'rid': 'BidRequest1', - 'hp': 1 + 'ortb2': { + 'source': { + 'ext': { + 'schain': { + 'ver': '1.0', + 'complete': 1, + 'nodes': [ + { + 'asi': 'directseller.com', + 'sid': '00001', + 'rid': 'BidRequest1', + 'hp': 1 + } + ] + } } - ] + } } } ]; @@ -274,7 +280,10 @@ describe('RTBHouseAdapter', () => { it('should not include invalid schain', () => { const bidRequest = Object.assign([], bidRequests); - bidRequest[0].schain = { + bidRequest[0].ortb2 = bidRequest[0].ortb2 || {}; + bidRequest[0].ortb2.source = bidRequest[0].ortb2.source || {}; + bidRequest[0].ortb2.source.ext = bidRequest[0].ortb2.source.ext || {}; + bidRequest[0].ortb2.source.ext.schain = { 'nodes': [{ 'unknown_key': 1 }] diff --git a/test/spec/modules/rubiconBidAdapter_spec.js b/test/spec/modules/rubiconBidAdapter_spec.js index 391f046d43a..6aa45b1af73 100644 --- a/test/spec/modules/rubiconBidAdapter_spec.js +++ b/test/spec/modules/rubiconBidAdapter_spec.js @@ -11,7 +11,6 @@ import { } from 'modules/rubiconBidAdapter.js'; import {config} from 'src/config.js'; import * as utils from 'src/utils.js'; -import 'modules/schain.js'; import 'modules/consentManagementTcf.js'; import 'modules/consentManagementUsp.js'; import 'modules/userId/index.js'; @@ -130,6 +129,9 @@ describe('the rubicon adapter', function () { tid: 'd45dd707-a418-42ec-b8a7-b70a6c6fab0b', } }, + ortb2: { + source: {} + } } ], start: 1472239426002, @@ -4655,7 +4657,8 @@ describe('the rubicon adapter', function () { beforeEach(() => { bidRequests = getBidderRequest(); schainConfig = getSupplyChainConfig(); - bidRequests.bids[0].schain = schainConfig; + bidRequests.bids[0].ortb2.source.ext = bidRequests.bids[0].ortb2.source.ext || {}; + bidRequests.bids[0].ortb2.source.ext.schain = schainConfig; }); it('should properly serialize schain object with correct delimiters', () => { @@ -4674,14 +4677,14 @@ describe('the rubicon adapter', function () { const results = spec.buildRequests(bidRequests.bids, bidRequests); const schain = new URLSearchParams(results[0].data).get('rp_schain').split('!'); const version = schain.shift().split(',')[0]; - expect(version).to.equal(bidRequests.bids[0].schain.ver); + expect(version).to.equal(bidRequests.bids[0].ortb2.source.ext.schain.ver); }); it('should send the correct value for complete in schain', () => { const results = spec.buildRequests(bidRequests.bids, bidRequests); const schain = new URLSearchParams(results[0].data).get('rp_schain').split('!'); const complete = schain.shift().split(',')[1]; - expect(complete).to.equal(String(bidRequests.bids[0].schain.complete)); + expect(complete).to.equal(String(bidRequests.bids[0].ortb2.source.ext.schain.complete)); }); it('should send available params in the right order', () => { @@ -4702,7 +4705,7 @@ describe('the rubicon adapter', function () { it('should copy the schain JSON to to bid.source.ext.schain', () => { const bidderRequest = createVideoBidderRequest(); const schain = getSupplyChainConfig(); - bidderRequest.bids[0].schain = schain; + bidderRequest.bids[0].ortb2.source.ext = { schain: schain }; const request = spec.buildRequests(bidderRequest.bids, bidderRequest); expect(request[0].data.source.ext.schain).to.deep.equal(schain); }); diff --git a/test/spec/modules/schain_spec.js b/test/spec/modules/schain_spec.js deleted file mode 100644 index eb8e35749db..00000000000 --- a/test/spec/modules/schain_spec.js +++ /dev/null @@ -1,496 +0,0 @@ -import { isValidSchainConfig, isSchainObjectValid, makeBidRequestsHook } from '../../../modules/schain.js'; -import { deepClone } from '../../../src/utils.js'; -import {config} from '../../../src/config.js'; -import { expect } from 'chai'; - -describe('#isValidSchainConfig: module config validation', function() { - it('if config is undefined or not an objct then return false', function() { - expect(isValidSchainConfig()).to.false; - expect(isValidSchainConfig('')).to.false; - expect(isValidSchainConfig([])).to.false; - expect(isValidSchainConfig(12)).to.false; - expect(isValidSchainConfig(3.14)).to.false; - }) - - it('if config is an object then return true', function() { - expect(isValidSchainConfig({})).to.true; - }) -}); - -describe('#isSchainObjectValid: schain object validation', function() { - let schainConfig; - - beforeEach(function() { - schainConfig = { - 'ver': '1.0', - 'complete': 1, - 'nodes': [ - { - 'asi': 'indirectseller.com', - 'sid': '00001', - 'hp': 1 - }, - - { - 'asi': 'indirectseller-2.com', - 'sid': '00002', - 'hp': 2 - } - ] - }; - }); - - it('Return true for correct config', function() { - expect(isSchainObjectValid(schainConfig, true)).to.true; - }); - - it('Return false for string config', function() { - schainConfig = JSON.stringify(schainConfig); - expect(isSchainObjectValid(schainConfig, true)).to.false; - }); - - it('Returns false if complete param is not an Integer', function() { - schainConfig.complete = 1; // integer - expect(isSchainObjectValid(schainConfig, true)).to.true; - schainConfig.complete = '1'; // string - expect(isSchainObjectValid(schainConfig, true)).to.false; - schainConfig.complete = 1.1; // float - expect(isSchainObjectValid(schainConfig, true)).to.false; - schainConfig.complete = {}; // object - expect(isSchainObjectValid(schainConfig, true)).to.false; - delete schainConfig.complete; // undefined - expect(isSchainObjectValid(schainConfig, true)).to.false; - schainConfig.complete = true; // boolean - expect(isSchainObjectValid(schainConfig, true)).to.false; - schainConfig.complete = []; // array - expect(isSchainObjectValid(schainConfig, true)).to.false; - }); - - it('Returns false if version param is not a String', function() { - schainConfig.ver = 1; // Integer - expect(isSchainObjectValid(schainConfig, true)).to.false; - schainConfig.ver = 1.1; // float - expect(isSchainObjectValid(schainConfig, true)).to.false; - schainConfig.ver = {}; // object - expect(isSchainObjectValid(schainConfig, true)).to.false; - delete schainConfig.ver; // undefined - expect(isSchainObjectValid(schainConfig, true)).to.false; - schainConfig.ver = true; // boolean - expect(isSchainObjectValid(schainConfig, true)).to.false; - schainConfig.ver = []; // array - expect(isSchainObjectValid(schainConfig, true)).to.false; - }); - - it('Returns false if ext param is not an Object', function() { - schainConfig.ext = 1; // Integer - expect(isSchainObjectValid(schainConfig, true)).to.false; - schainConfig.ext = 1.1; // float - expect(isSchainObjectValid(schainConfig, true)).to.false; - schainConfig.ext = {}; // object - expect(isSchainObjectValid(schainConfig, true)).to.true; - delete schainConfig.ext; // undefined // param is optional thus this will result true - expect(isSchainObjectValid(schainConfig, true)).to.true; - schainConfig.ext = true; // boolean - expect(isSchainObjectValid(schainConfig, true)).to.false; - schainConfig.ext = []; // array - expect(isSchainObjectValid(schainConfig, true)).to.false; - }); - - it('Returns false if nodes param is not an Array', function() { - // by default schainConfig.nodes is array - expect(isSchainObjectValid(schainConfig, true)).to.true; - schainConfig.nodes = 1; // Integer - expect(isSchainObjectValid(schainConfig, true)).to.false; - schainConfig.nodes = 1.1; // float - expect(isSchainObjectValid(schainConfig, true)).to.false; - schainConfig.nodes = {}; // object - expect(isSchainObjectValid(schainConfig, true)).to.false; - delete schainConfig.nodes; // undefined - expect(isSchainObjectValid(schainConfig, true)).to.false; - schainConfig.nodes = true; // boolean - expect(isSchainObjectValid(schainConfig, true)).to.false; - }); - - it('Returns false if nodes[].asi is not a String', function() { - schainConfig.nodes[0].asi = 1; // Integer - expect(isSchainObjectValid(schainConfig, true)).to.false; - schainConfig.nodes[0].asi = 1.1; // float - expect(isSchainObjectValid(schainConfig, true)).to.false; - schainConfig.nodes[0].asi = {}; // object - expect(isSchainObjectValid(schainConfig, true)).to.false; - delete schainConfig.nodes[0].asi; // undefined - expect(isSchainObjectValid(schainConfig, true)).to.false; - schainConfig.nodes[0].asi = true; // boolean - expect(isSchainObjectValid(schainConfig, true)).to.false; - schainConfig.nodes[0].asi = []; // array - expect(isSchainObjectValid(schainConfig, true)).to.false; - }); - - it('Returns false if nodes[].sid is not a String', function() { - schainConfig.nodes[1].sid = 1; // Integer - expect(isSchainObjectValid(schainConfig, true)).to.false; - schainConfig.nodes[1].sid = 1.1; // float - expect(isSchainObjectValid(schainConfig, true)).to.false; - schainConfig.nodes[1].sid = {}; // object - expect(isSchainObjectValid(schainConfig, true)).to.false; - delete schainConfig.nodes[0].sid; // undefined - expect(isSchainObjectValid(schainConfig, true)).to.false; - schainConfig.nodes[1].sid = true; // boolean - expect(isSchainObjectValid(schainConfig, true)).to.false; - schainConfig.nodes[1].sid = []; // array - expect(isSchainObjectValid(schainConfig, true)).to.false; - }); - - it('Returns false if nodes[].hp is not an Integer', function() { - schainConfig.nodes[0].hp = '1'; // string - expect(isSchainObjectValid(schainConfig, true)).to.false; - schainConfig.nodes[0].hp = 1.1; // float - expect(isSchainObjectValid(schainConfig, true)).to.false; - schainConfig.nodes[0].hp = {}; // object - expect(isSchainObjectValid(schainConfig, true)).to.false; - delete schainConfig.nodes[0].hp; // undefined - expect(isSchainObjectValid(schainConfig, true)).to.false; - schainConfig.nodes[0].hp = true; // boolean - expect(isSchainObjectValid(schainConfig, true)).to.false; - schainConfig.nodes[0].hp = []; // array - expect(isSchainObjectValid(schainConfig, true)).to.false; - }); - - it('Returns false if nodes[].rid is not a String', function() { - schainConfig.nodes[1].rid = 'rid value'; // string - expect(isSchainObjectValid(schainConfig, true)).to.true; - schainConfig.nodes[1].rid = 1; // Integer - expect(isSchainObjectValid(schainConfig, true)).to.false; - schainConfig.nodes[1].rid = 1.1; // float - expect(isSchainObjectValid(schainConfig, true)).to.false; - schainConfig.nodes[1].rid = {}; // object - expect(isSchainObjectValid(schainConfig, true)).to.false; - delete schainConfig.nodes[1].rid; // undefined // param is optional thus this will result true - expect(isSchainObjectValid(schainConfig, true)).to.true; - schainConfig.nodes[1].rid = true; // boolean - expect(isSchainObjectValid(schainConfig, true)).to.false; - schainConfig.nodes[1].rid = []; // array - expect(isSchainObjectValid(schainConfig, true)).to.false; - }); - - it('Returns false if nodes[].name is not a String', function() { - schainConfig.nodes[0].name = 'name value'; // string - expect(isSchainObjectValid(schainConfig, true)).to.true; - schainConfig.nodes[0].name = 1; // Integer - expect(isSchainObjectValid(schainConfig, true)).to.false; - schainConfig.nodes[0].name = 1.1; // float - expect(isSchainObjectValid(schainConfig, true)).to.false; - schainConfig.nodes[0].name = {}; // object - expect(isSchainObjectValid(schainConfig, true)).to.false; - delete schainConfig.nodes[0].name; // undefined // param is optional thus this will result true - expect(isSchainObjectValid(schainConfig, true)).to.true; - schainConfig.nodes[0].name = true; // boolean - expect(isSchainObjectValid(schainConfig, true)).to.false; - schainConfig.nodes[0].name = []; // array - expect(isSchainObjectValid(schainConfig, true)).to.false; - }); - - it('Returns false if nodes[].domain is not a String', function() { - schainConfig.nodes[1].domain = 'domain value'; // string - expect(isSchainObjectValid(schainConfig, true)).to.true; - schainConfig.nodes[1].domain = 1; // Integer - expect(isSchainObjectValid(schainConfig, true)).to.false; - schainConfig.nodes[1].domain = 1.1; // float - expect(isSchainObjectValid(schainConfig, true)).to.false; - schainConfig.nodes[1].domain = {}; // object - expect(isSchainObjectValid(schainConfig, true)).to.false; - delete schainConfig.nodes[1].domain; // undefined // param is optional thus this will result true - expect(isSchainObjectValid(schainConfig, true)).to.true; - schainConfig.nodes[1].domain = true; // boolean - expect(isSchainObjectValid(schainConfig, true)).to.false; - schainConfig.nodes[1].domain = []; // array - expect(isSchainObjectValid(schainConfig, true)).to.false; - }); - - it('Returns false if nodes[].ext param is not an Object', function() { - schainConfig.nodes[0].ext = 1; // Integer - expect(isSchainObjectValid(schainConfig, true)).to.false; - schainConfig.nodes[0].ext = 1.1; // float - expect(isSchainObjectValid(schainConfig, true)).to.false; - schainConfig.nodes[0].ext = {}; // object - expect(isSchainObjectValid(schainConfig, true)).to.true; - delete schainConfig.nodes[0].ext; // undefined // param is optional thus this will result true - expect(isSchainObjectValid(schainConfig, true)).to.true; - schainConfig.nodes[0].ext = true; // boolean - expect(isSchainObjectValid(schainConfig, true)).to.false; - schainConfig.nodes[0].ext = []; // array - expect(isSchainObjectValid(schainConfig, true)).to.false; - }); - - it('Relaxed mode: Returns true even for invalid config if second argument is set to false', function() { - schainConfig = { - 'ver': 1.0, // invalid - 'complete': '1', // invalid - 'nodes': [ - { - 'asi': 'indirectseller.com', - 'sid': 1, // invalid - 'hp': '1' // invalid - }, - - { - 'asi': 'indirectseller-2.com', - 'sid': '00002', - 'hp': 2 - } - ] - }; - expect(isSchainObjectValid(schainConfig, false)).to.true; - - schainConfig = {}; - expect(isSchainObjectValid(schainConfig, false)).to.true; - }) -}); - -describe('#makeBidRequestsHook', function() { - const bidderRequests = [ - { - 'bidderCode': 'rubicon', - 'bids': [ - { - 'bidder': 'rubicon', - 'params': { - 'accountId': 14062, - 'siteId': 70608, - 'zoneId': 498816 - }, - 'mediaTypes': { - 'banner': { - 'sizes': [[300, 250], [300, 600]] - } - }, - 'adUnitCode': 'div-gpt-ad-1460505748561-0', - 'sizes': [[300, 250], [300, 600]], - 'bidId': '2e6d166eb869c3' - - } - ], - }, - { - 'bidderCode': 'districtm', - 'bids': [ - { - 'bidder': 'districtm', - 'params': { - 'placementId': 13144370 - }, - 'mediaTypes': { - 'banner': { - 'sizes': [[300, 250], [300, 600]] - } - }, - 'adUnitCode': 'div-gpt-ad-1460505748561-0', - 'sizes': [[300, 250], [300, 600]], - 'bidId': '41cdeddf7b6905' - } - ], - }, - { - 'bidderCode': 'appnexus', - 'bids': [ - { - 'bidder': 'appnexus', - 'params': { - 'placementId': 13144370 - }, - 'mediaTypes': { - 'banner': { - 'sizes': [[300, 250], [300, 600]] - } - }, - 'adUnitCode': 'div-gpt-ad-1460505748561-0', - 'sizes': [[300, 250], [300, 600]], - 'bidId': '626cc7f1c4ccfc' - } - ], - - } - ]; - - const globalSchainConfig = { - 'schain': { - 'validation': 'off', - 'config': { - 'ver': '1.0', - 'complete': 1, - 'nodes': [ - { - 'asi': 'indirectseller.com', - 'sid': '00001', - 'hp': 1 - }, - - { - 'asi': 'indirectseller-2.com', - 'sid': '00002', - 'hp': 1 - } - ] - } - } - }; - - const goodStrictBidderConfig = { - bidders: ['appnexus'], - config: { - 'schain': { - 'validation': 'strict', - 'config': { - 'ver': '1.0', - 'complete': 1, - 'nodes': [ - { - 'asi': 'myoverride1.com', - 'sid': '00001', - 'hp': 1, - 'name': 'node1' - }, - { - 'asi': 'myoverride2.com', - 'sid': '00001', - 'hp': 1, - 'name': 'node2' - } - ] - } - } - } - } - - const badStrictBidderConfig = { - bidders: ['appnexus'], - config: { - 'schain': { - 'validation': 'strict', - 'config': { - 'ver': '1.0', - 'complete': 1, - 'nodes': [ - { - 'asi': 'myoverride1.com', - 'sid': 1, - 'hp': 1, - 'name': 342 - }, - { - 'asi': 'myoverride2.com', - 'sid': 2, - 'hp': 1, - 'name': '342' - } - ] - } - } - } - }; - - const goodRelaxedBidderConfig = { - bidders: ['districtm'], - config: { - 'schain': { - 'validation': 'relaxed', - 'config': { - 'ver': '1.0', - 'complete': 1, - 'nodes': [ - { - 'asi': 'myoverride.com', - 'sid': '00001', - 'hp': 1, - 'name': 'goodConfig' - } - ] - } - } - } - }; - - const badRelaxedBidderConfig = { - bidders: ['districtm'], - config: { - 'schain': { - 'validation': 'relaxed', - 'config': { - 'ver': 1, - 'complete': 1, - 'nodes': [ - { - 'asi': 'myoverride.com', - 'sid': 1, - 'hp': 1 - } - ] - } - } - } - }; - - beforeEach(function () { - config.setConfig(globalSchainConfig); - }); - - afterEach(function () { - config.resetConfig(); - - config.setBidderConfig({ - bidders: ['districtm'], - config: { - schain: null - } - }); - - config.setBidderConfig({ - bidders: ['appnexus'], - config: { - schain: null - } - }); - }); - - it('should properly read from bidder schain + global schain configs', function() { - function testCallback(bidderRequests) { - expect(bidderRequests[0].bids[0].schain).to.exist; - expect(bidderRequests[0].bids[0].schain).to.deep.equal(globalSchainConfig.schain.config); - expect(bidderRequests[1].bids[0].schain).to.exist; - expect(bidderRequests[1].bids[0].schain).to.deep.equal(goodRelaxedBidderConfig.config.schain.config); - expect(bidderRequests[2].bids[0].schain).to.exist; - expect(bidderRequests[2].bids[0].schain).to.deep.equal(goodStrictBidderConfig.config.schain.config); - } - - const testBidderRequests = deepClone(bidderRequests); - config.setBidderConfig(goodStrictBidderConfig); - config.setBidderConfig(goodRelaxedBidderConfig); - - makeBidRequestsHook(testCallback, testBidderRequests); - }); - - it('should not share the same schain object between different bid requests', (done) => { - config.setBidderConfig(goodStrictBidderConfig); - makeBidRequestsHook((requests) => { - requests[0].bids[0].schain.field = 'value'; - expect(requests[1].bids[0].schain.field).to.not.exist; - done(); - }, deepClone(bidderRequests)) - }); - - it('should reject bad strict config but allow a bad relaxed config for bidders trying to override it', function () { - function testCallback(bidderRequests) { - expect(bidderRequests[0].bids[0].schain).to.exist; - expect(bidderRequests[0].bids[0].schain).to.deep.equal(globalSchainConfig.schain.config); - expect(bidderRequests[1].bids[0].schain).to.exist; - expect(bidderRequests[1].bids[0].schain).to.deep.equal(badRelaxedBidderConfig.config.schain.config); - expect(bidderRequests[2].bids[0].schain).to.be.undefined; - } - - const testBidderRequests = deepClone(bidderRequests); - config.setBidderConfig(badStrictBidderConfig); - config.setBidderConfig(badRelaxedBidderConfig); - - makeBidRequestsHook(testCallback, testBidderRequests); - }); -}); diff --git a/test/spec/modules/seedtagBidAdapter_spec.js b/test/spec/modules/seedtagBidAdapter_spec.js index b64c8675647..96491b3a8ee 100644 --- a/test/spec/modules/seedtagBidAdapter_spec.js +++ b/test/spec/modules/seedtagBidAdapter_spec.js @@ -448,7 +448,10 @@ describe('Seedtag Adapter', function () { // duplicate const bidRequests = JSON.parse(JSON.stringify(validBidRequests)); - bidRequests[0].schain = schain; + bidRequests[0].ortb2 = bidRequests[0].ortb2 || {}; + bidRequests[0].ortb2.source = bidRequests[0].ortb2.source || {}; + bidRequests[0].ortb2.source.ext = bidRequests[0].ortb2.source.ext || {}; + bidRequests[0].ortb2.source.ext.schain = schain; const request = spec.buildRequests(bidRequests, bidderRequest); diff --git a/test/spec/modules/sharethroughBidAdapter_spec.js b/test/spec/modules/sharethroughBidAdapter_spec.js index 2c05799f602..a54fe3f710c 100644 --- a/test/spec/modules/sharethroughBidAdapter_spec.js +++ b/test/spec/modules/sharethroughBidAdapter_spec.js @@ -191,17 +191,23 @@ describe('sharethrough adapter spec', function () { crumbs: { pubcid: 'fake-pubcid-in-crumbs-obj', }, - schain: { - ver: '1.0', - complete: 1, - nodes: [ - { - asi: 'directseller.com', - sid: '00001', - rid: 'BidRequest1', - hp: 1, - }, - ], + ortb2: { + source: { + ext: { + schain: { + ver: '1.0', + complete: 1, + nodes: [ + { + asi: 'directseller.com', + sid: '00001', + rid: 'BidRequest1', + hp: 1, + }, + ], + } + } + } }, getFloor: () => ({ currency: 'USD', floor: 42 }), }, @@ -321,7 +327,7 @@ describe('sharethrough adapter spec', function () { expect(openRtbReq.source.tid).to.equal(bidderRequest.ortb2.source.tid); expect(openRtbReq.source.ext.version).not.to.be.undefined; expect(openRtbReq.source.ext.str).not.to.be.undefined; - expect(openRtbReq.source.ext.schain).to.deep.equal(bidRequests[0].schain); + expect(openRtbReq.source.ext.schain).to.deep.equal(bidRequests[0].ortb2.source.ext.schain); expect(openRtbReq.bcat).to.deep.equal(bidRequests[0].params.bcat); expect(openRtbReq.badv).to.deep.equal(bidRequests[0].params.badv); diff --git a/test/spec/modules/shinezBidAdapter_spec.js b/test/spec/modules/shinezBidAdapter_spec.js index d4ad99359bb..1927ebe8597 100644 --- a/test/spec/modules/shinezBidAdapter_spec.js +++ b/test/spec/modules/shinezBidAdapter_spec.js @@ -313,12 +313,17 @@ describe('shinezAdapter', function () { }); it('should have schain param if it is available in the bidRequest', () => { - const schain = { - ver: '1.0', - complete: 1, - nodes: [{ asi: 'indirectseller.com', sid: '00001', hp: 1 }], + bidderRequest.ortb2 = { + source: { + ext: { + schain: { + ver: '1.0', + complete: 1, + nodes: [{ asi: 'indirectseller.com', sid: '00001', hp: 1 }], + } + } + } }; - bidRequests[0].schain = schain; const request = spec.buildRequests(bidRequests, bidderRequest); expect(request.data.params).to.be.an('object'); expect(request.data.params).to.have.property('schain', '1.0,1!indirectseller.com,00001,1,,,'); diff --git a/test/spec/modules/showheroes-bsBidAdapter_spec.js b/test/spec/modules/showheroes-bsBidAdapter_spec.js index 07211ca37cc..0a1462d9b66 100644 --- a/test/spec/modules/showheroes-bsBidAdapter_spec.js +++ b/test/spec/modules/showheroes-bsBidAdapter_spec.js @@ -4,7 +4,6 @@ import { addFPDToBidderRequest } from '../../helpers/fpd.js'; import 'modules/priceFloors.js'; import 'modules/consentManagementTcf.js'; import 'modules/consentManagementUsp.js'; -import 'modules/schain.js'; import { VIDEO } from 'src/mediaTypes.js' const bidderRequest = { @@ -99,10 +98,18 @@ describe('shBidAdapter', () => { bids: [bidRequestVideoV2], ...bidderRequest, ...gdpr, - ...schain, ...{uspConsent: uspConsent}, + ortb2: { + source: { + ext: {schain: schain.schain.config} + } + } + }; + bidRequest.ortb2 = { + source: { + ext: {schain: schain.schain.config} + } }; - bidRequest.schain = schain.schain.config; const getFloorResponse = {currency: 'EUR', floor: 3}; bidRequest.getFloor = () => getFloorResponse; const request = spec.buildRequests([bidRequest], await addFPDToBidderRequest(fullRequest)); @@ -110,7 +117,7 @@ describe('shBidAdapter', () => { expect(payload.regs.ext.gdpr).to.eql(Number(gdpr.gdprConsent.gdprApplies)); expect(payload.regs.ext.us_privacy).to.eql(uspConsent); expect(payload.user.ext.consent).to.eql(gdpr.gdprConsent.consentString); - expect(payload.source.ext.schain).to.eql(bidRequest.schain); + expect(payload.source.ext.schain).to.deep.equal(bidRequest.ortb2.source.ext.schain); expect(payload.test).to.eql(0); expect(payload.imp[0].bidfloor).eql(3); expect(payload.imp[0].bidfloorcur).eql('EUR'); diff --git a/test/spec/modules/silvermobBidAdapter_spec.js b/test/spec/modules/silvermobBidAdapter_spec.js index b9bf32462d8..b967efdd9b5 100644 --- a/test/spec/modules/silvermobBidAdapter_spec.js +++ b/test/spec/modules/silvermobBidAdapter_spec.js @@ -13,7 +13,6 @@ import 'modules/multibid/index.js'; import 'modules/priceFloors.js'; import 'modules/consentManagementTcf.js'; import 'modules/consentManagementUsp.js'; -import 'modules/schain.js'; const SIMPLE_BID_REQUEST = { bidder: 'silvermob', diff --git a/test/spec/modules/smaatoBidAdapter_spec.js b/test/spec/modules/smaatoBidAdapter_spec.js index 6d117f829f9..f5b1c2827b2 100644 --- a/test/spec/modules/smaatoBidAdapter_spec.js +++ b/test/spec/modules/smaatoBidAdapter_spec.js @@ -10,7 +10,6 @@ import 'modules/multibid/index.js'; import 'modules/priceFloors.js'; import 'modules/consentManagementTcf.js'; import 'modules/consentManagementUsp.js'; -import 'modules/schain.js'; const IMAGE_SYNC_URL = 'https://s.ad.smaato.net/c/?adExInit=p' const IFRAME_SYNC_URL = 'https://s.ad.smaato.net/i/?adExInit=p' @@ -1272,7 +1271,15 @@ describe('smaatoBidAdapterTest', () => { } ] }; - const bidRequestWithSchain = Object.assign({}, singleBannerBidRequest, {schain: schain}); + const bidRequestWithSchain = Object.assign({}, singleBannerBidRequest, { + ortb2: { + source: { + ext: { + schain: schain + } + } + } + }); const reqs = spec.buildRequests([bidRequestWithSchain], defaultBidderRequest); diff --git a/test/spec/modules/smartxBidAdapter_spec.js b/test/spec/modules/smartxBidAdapter_spec.js index d8ddf7a398b..f548151e31b 100644 --- a/test/spec/modules/smartxBidAdapter_spec.js +++ b/test/spec/modules/smartxBidAdapter_spec.js @@ -337,15 +337,21 @@ describe('The smartx adapter', function () { it('should pass schain param', function () { var request; - bid.schain = { - complete: 1, - nodes: [ - { - asi: 'indirectseller.com', - sid: '00001', - hp: 1 + bid.ortb2 = { + source: { + ext: { + schain: { + complete: 1, + nodes: [ + { + asi: 'indirectseller.com', + sid: '00001', + hp: 1 + } + ] + } } - ] + } } request = spec.buildRequests([bid], bidRequestObj)[0]; diff --git a/test/spec/modules/smilewantedBidAdapter_spec.js b/test/spec/modules/smilewantedBidAdapter_spec.js index 183ec4a5001..7c1e007c6a7 100644 --- a/test/spec/modules/smilewantedBidAdapter_spec.js +++ b/test/spec/modules/smilewantedBidAdapter_spec.js @@ -116,7 +116,13 @@ const DISPLAY_REQUEST_WITH_SCHAIN = [{ tid: 'trans_abcd1234', } }, - schain: SCHAIN, + ortb2: { + source: { + ext: { + schain: SCHAIN + } + } + }, }]; const BID_RESPONSE_DISPLAY = { diff --git a/test/spec/modules/sonobiBidAdapter_spec.js b/test/spec/modules/sonobiBidAdapter_spec.js index 27707d5b7d4..78d538c77d2 100644 --- a/test/spec/modules/sonobiBidAdapter_spec.js +++ b/test/spec/modules/sonobiBidAdapter_spec.js @@ -267,21 +267,27 @@ describe('SonobiBidAdapter', function () { sandbox.restore(); }); let bidRequest = [{ - 'schain': { - 'ver': '1.0', - 'complete': 1, - 'nodes': [ - { - 'asi': 'indirectseller.com', - 'sid': '00001', - 'hp': 1 - }, - { - 'asi': 'indirectseller-2.com', - 'sid': '00002', - 'hp': 0 - }, - ] + 'ortb2': { + 'source': { + 'ext': { + 'schain': { + 'ver': '1.0', + 'complete': 1, + 'nodes': [ + { + 'asi': 'indirectseller.com', + 'sid': '00001', + 'hp': 1 + }, + { + 'asi': 'indirectseller-2.com', + 'sid': '00002', + 'hp': 0 + }, + ] + } + } + } }, 'bidder': 'sonobi', 'params': { @@ -564,7 +570,7 @@ describe('SonobiBidAdapter', function () { it('should return a properly formatted request with schain defined', function () { const bidRequests = spec.buildRequests(bidRequest, bidderRequests); - expect(JSON.parse(decodeURIComponent(bidRequests.data.schain))).to.deep.equal(bidRequest[0].schain) + expect(JSON.parse(decodeURIComponent(bidRequests.data.schain))).to.deep.equal(bidRequest[0].ortb2.source.ext.schain) }); it('should return a properly formatted request with eids as a JSON-encoded set of eids', function () { diff --git a/test/spec/modules/sovrnBidAdapter_spec.js b/test/spec/modules/sovrnBidAdapter_spec.js index 05d18a0bb98..2ed79808356 100644 --- a/test/spec/modules/sovrnBidAdapter_spec.js +++ b/test/spec/modules/sovrnBidAdapter_spec.js @@ -529,17 +529,23 @@ describe('sovrnBidAdapter', function() { it('should add schain if present', function() { const schainRequest = { ...baseBidRequest, - schain: { - ver: '1.0', - complete: 1, - nodes: [ - { - asi: 'directseller.com', - sid: '00001', - rid: 'BidRequest1', - hp: 1 + ortb2: { + source: { + ext: { + schain: { + ver: '1.0', + complete: 1, + nodes: [ + { + asi: 'directseller.com', + sid: '00001', + rid: 'BidRequest1', + hp: 1 + } + ] + } } - ] + } } } const schainRequests = [schainRequest, baseBidRequest] diff --git a/test/spec/modules/sspBCBidAdapter_spec.js b/test/spec/modules/sspBCBidAdapter_spec.js index ceaad85faac..32f99096156 100644 --- a/test/spec/modules/sspBCBidAdapter_spec.js +++ b/test/spec/modules/sspBCBidAdapter_spec.js @@ -663,7 +663,7 @@ describe('SSPBC adapter', function () { }, ] } - const bidWithSupplyChain = Object.assign(bids[0], { schain: supplyChain }); + const bidWithSupplyChain = Object.assign(bids[0], { ortb2: { source: { ext: { schain: supplyChain } } } }); const requestWithSupplyChain = spec.buildRequests([bidWithSupplyChain], bidRequest); const payloadWithSupplyChain = requestWithSupplyChain ? JSON.parse(requestWithSupplyChain.data) : { site: false, imp: false }; diff --git a/test/spec/modules/stackadaptBidAdapter_spec.js b/test/spec/modules/stackadaptBidAdapter_spec.js index ea86adf28ca..b55bc8d75c9 100644 --- a/test/spec/modules/stackadaptBidAdapter_spec.js +++ b/test/spec/modules/stackadaptBidAdapter_spec.js @@ -914,8 +914,19 @@ describe('stackadaptBidAdapter', function () { 'ver': '1.0' }; - clonedBidRequests[0].schain = schain; + clonedBidRequests[0].ortb2 = { + source: { + ext: {schain: schain} + } + }; clonedBidderRequest.bids = clonedBidRequests; + + // Add schain to bidderRequest as well + clonedBidderRequest.ortb2 = { + source: { + ext: {schain: schain} + } + }; const ortbRequest = spec.buildRequests(clonedBidRequests, clonedBidderRequest).data; expect(ortbRequest.source.ext.schain).to.deep.equal(schain); diff --git a/test/spec/modules/stnBidAdapter_spec.js b/test/spec/modules/stnBidAdapter_spec.js index 98859385828..18089f64f44 100644 --- a/test/spec/modules/stnBidAdapter_spec.js +++ b/test/spec/modules/stnBidAdapter_spec.js @@ -370,12 +370,17 @@ describe('stnAdapter', function () { }); it('should have schain param if it is available in the bidRequest', () => { - const schain = { - ver: '1.0', - complete: 1, - nodes: [{ asi: 'indirectseller.com', sid: '00001', hp: 1 }], + bidderRequest.ortb2 = { + source: { + ext: { + schain: { + ver: '1.0', + complete: 1, + nodes: [{ asi: 'indirectseller.com', sid: '00001', hp: 1 }], + } + } + } }; - bidRequests[0].schain = schain; const request = spec.buildRequests(bidRequests, bidderRequest); expect(request.data.params).to.be.an('object'); expect(request.data.params).to.have.property('schain', '1.0,1!indirectseller.com,00001,1,,,'); diff --git a/test/spec/modules/stroeerCoreBidAdapter_spec.js b/test/spec/modules/stroeerCoreBidAdapter_spec.js index 6a039fa4ed5..c39fc9e71b5 100644 --- a/test/spec/modules/stroeerCoreBidAdapter_spec.js +++ b/test/spec/modules/stroeerCoreBidAdapter_spec.js @@ -713,7 +713,12 @@ describe('stroeerCore bid adapter', function () { }); const bidReq = buildBidderRequest(); - bidReq.bids.forEach(bid => bid.schain = schain); + bidReq.bids.forEach(bid => { + bid.ortb2 = bid.ortb2 || {}; + bid.ortb2.source = bid.ortb2.source || {}; + bid.ortb2.source.ext = bid.ortb2.source.ext || {}; + bid.ortb2.source.ext.schain = schain; + }); const serverRequestInfo = spec.buildRequests(bidReq.bids, bidReq); assert.deepEqual(serverRequestInfo.data.schain, schain); diff --git a/test/spec/modules/stvBidAdapter_spec.js b/test/spec/modules/stvBidAdapter_spec.js index 7a5e287057b..44effebb0e5 100644 --- a/test/spec/modules/stvBidAdapter_spec.js +++ b/test/spec/modules/stvBidAdapter_spec.js @@ -60,17 +60,23 @@ describe('stvAdapter', function() { 'bidderRequestId': '22edbae2733bf61', 'auctionId': '1d1a030790a475', 'adUnitCode': 'testDiv1', - 'schain': { - 'ver': '1.0', - 'complete': 0, - 'nodes': [ - { - 'asi': 'reseller.com', - 'sid': 'aaaaa', - 'rid': 'BidRequest4', - 'hp': 1 + 'ortb2': { + 'source': { + 'ext': { + 'schain': { + 'ver': '1.0', + 'complete': 0, + 'nodes': [ + { + 'asi': 'reseller.com', + 'sid': 'aaaaa', + 'rid': 'BidRequest4', + 'hp': 1 + } + ] + } } - ] + } }, 'userIdAsEids': [ { diff --git a/test/spec/modules/targetVideoBidAdapter_spec.js b/test/spec/modules/targetVideoBidAdapter_spec.js index f2c59d29031..8d9cb5bf18b 100644 --- a/test/spec/modules/targetVideoBidAdapter_spec.js +++ b/test/spec/modules/targetVideoBidAdapter_spec.js @@ -82,7 +82,13 @@ describe('TargetVideo Bid Adapter', function() { }; let videoRequestCloned = deepClone(videoRequest); - videoRequestCloned[0].schain = globalSchain; + videoRequestCloned[0].ortb2 = { + source: { + ext: { + schain: globalSchain + } + } + }; const request = spec.buildRequests(videoRequestCloned, defaultBidderRequest); expect(request).to.not.be.empty; diff --git a/test/spec/modules/teadsBidAdapter_spec.js b/test/spec/modules/teadsBidAdapter_spec.js index 5ce96e21a20..511be25b49f 100644 --- a/test/spec/modules/teadsBidAdapter_spec.js +++ b/test/spec/modules/teadsBidAdapter_spec.js @@ -743,14 +743,20 @@ describe('teadsBidAdapter', () => { it('should add schain info to payload if available', function () { const bidRequest = Object.assign({}, bidRequests[0], { - schain: { - ver: '1.0', - complete: 1, - nodes: [{ - asi: 'example.com', - sid: '00001', - hp: 1 - }] + ortb2: { + source: { + ext: { + schain: { + ver: '1.0', + complete: 1, + nodes: [{ + asi: 'example.com', + sid: '00001', + hp: 1 + }] + } + } + } } }); diff --git a/test/spec/modules/trafficgateBidAdapter_spec.js b/test/spec/modules/trafficgateBidAdapter_spec.js index fec467309ab..902604f0927 100644 --- a/test/spec/modules/trafficgateBidAdapter_spec.js +++ b/test/spec/modules/trafficgateBidAdapter_spec.js @@ -11,7 +11,6 @@ import 'modules/multibid/index.js'; import 'modules/priceFloors.js'; import 'modules/consentManagementTcf.js'; import 'modules/consentManagementUsp.js'; -import 'modules/schain.js'; import 'modules/paapi.js'; import {deepClone} from 'src/utils.js'; @@ -932,13 +931,22 @@ describe('TrafficgateOpenxRtbAdapter', function () { bidId: 'test-bid-id-1', bidderRequestId: 'test-bid-request-1', auctionId: 'test-auction-1', - schain: schainConfig + ortb2: {source: { + ext: {schain: schainConfig} + }} }]; + + // Add schain to mockBidderRequest as well + mockBidderRequest.ortb2 = { + source: { + ext: {schain: schainConfig} + } + }; }); it('should send a supply chain object', function () { const request = spec.buildRequests(bidRequests, mockBidderRequest); - expect(request[0].data.source.ext.schain).to.equal(schainConfig); + expect(request[0].data.source.ext.schain).to.deep.equal(schainConfig); }); it('should send the supply chain object with the right version', function () { diff --git a/test/spec/modules/tripleliftBidAdapter_spec.js b/test/spec/modules/tripleliftBidAdapter_spec.js index 216142ab02e..14c6b9e1142 100644 --- a/test/spec/modules/tripleliftBidAdapter_spec.js +++ b/test/spec/modules/tripleliftBidAdapter_spec.js @@ -143,7 +143,13 @@ describe('triplelift adapter', function () { transactionId: '173f49a8-7549-4218-a23c-e7ba59b47229', auctionId: '1d1a030790a475', userId: {}, - schain, + ortb2: { + source: { + ext: { + schain + } + } + }, ortb2Imp: { ext: { tid: '173f49a8-7549-4218-a23c-e7ba59b47229' @@ -177,7 +183,13 @@ describe('triplelift adapter', function () { bidderRequestId: '22edbae2733bf6', auctionId: '1d1a030790a475', userId: {}, - schain, + ortb2: { + source: { + ext: { + schain + } + } + }, ortb2Imp: { ext: { data: { @@ -253,7 +265,13 @@ describe('triplelift adapter', function () { bidderRequestId: '22edbae2733bf6', auctionId: '1d1a030790a475', userId: {}, - schain, + ortb2: { + source: { + ext: { + schain + } + } + }, ortb2Imp: { misc: { test: 1 @@ -899,7 +917,7 @@ describe('triplelift adapter', function () { expect(payload.ext.schain).to.deep.equal(schain); }); it('should not create root level ext when schain is not present', function() { - bidRequests[0].schain = undefined; + delete bidRequests[0].ortb2.source.ext.schain; const request = tripleliftAdapterSpec.buildRequests(bidRequests, bidderRequest); const { data: payload } = request; expect(payload.ext).to.deep.equal(undefined); diff --git a/test/spec/modules/ttdBidAdapter_spec.js b/test/spec/modules/ttdBidAdapter_spec.js index 0b040c0b6e7..33d028ea5d1 100644 --- a/test/spec/modules/ttdBidAdapter_spec.js +++ b/test/spec/modules/ttdBidAdapter_spec.js @@ -547,7 +547,7 @@ describe('ttdBidAdapter', function () { }] }; let clonedBannerBidRequests = deepClone(baseBannerBidRequests); - clonedBannerBidRequests[0].schain = schain; + clonedBannerBidRequests[0].ortb2 = { source: { ext: { schain: schain } } }; const requestBody = testBuildRequests(clonedBannerBidRequests, baseBidderRequest).data; expect(requestBody.source.ext.schain).to.deep.equal(schain); diff --git a/test/spec/modules/ucfunnelBidAdapter_spec.js b/test/spec/modules/ucfunnelBidAdapter_spec.js index 998e0db6fe8..10241687e14 100644 --- a/test/spec/modules/ucfunnelBidAdapter_spec.js +++ b/test/spec/modules/ucfunnelBidAdapter_spec.js @@ -39,19 +39,25 @@ const validBannerBidReq = { } }, userId: userId, - 'schain': { - 'ver': '1.0', - 'complete': 1, - 'nodes': [ - { - 'asi': 'exchange1.com', - 'sid': '1234', - 'hp': 1, - 'rid': 'bid-request-1', - 'name': 'publisher', - 'domain': 'publisher.com' + ortb2: { + source: { + ext: { + schain: { + 'ver': '1.0', + 'complete': 1, + 'nodes': [ + { + 'asi': 'exchange1.com', + 'sid': '1234', + 'hp': 1, + 'rid': 'bid-request-1', + 'name': 'publisher', + 'domain': 'publisher.com' + } + ] + } } - ] + } } }; diff --git a/test/spec/modules/undertoneBidAdapter_spec.js b/test/spec/modules/undertoneBidAdapter_spec.js index ed3108c2749..808071be8db 100644 --- a/test/spec/modules/undertoneBidAdapter_spec.js +++ b/test/spec/modules/undertoneBidAdapter_spec.js @@ -116,7 +116,7 @@ const bidReq = [{ sizes: [[1, 1]], bidId: '453cf42d72bb3c', auctionId: '6c22f5a5-59df-4dc6-b92c-f433bcf0a874', - schain: schainObj + ortb2: { source: { ext: { schain: schainObj } } } }]; const supplyChainedBidReqs = [{ @@ -129,7 +129,7 @@ const supplyChainedBidReqs = [{ sizes: [[300, 250], [300, 600]], bidId: '263be71e91dd9d', auctionId: '9ad1fa8d-2297-4660-a018-b39945054746', - schain: schainObj + ortb2: { source: { ext: { schain: schainObj } } } }, { adUnitCode: 'div-gpt-ad-1460505748561-0', bidder: BIDDER_CODE, diff --git a/test/spec/modules/vdoaiBidAdapter_spec.js b/test/spec/modules/vdoaiBidAdapter_spec.js index 1cd361730a9..be2b8fe7386 100644 --- a/test/spec/modules/vdoaiBidAdapter_spec.js +++ b/test/spec/modules/vdoaiBidAdapter_spec.js @@ -43,16 +43,22 @@ describe('vdoaiBidAdapter', function () { ] } ], - schain: { - ver: '1.0', - complete: 1, - nodes: [ - { - asi: 'example.com', - sid: '1', - hp: 1 + ortb2: { + source: { + ext: { + schain: { + ver: '1.0', + complete: 1, + nodes: [ + { + asi: 'example.com', + sid: '1', + hp: 1 + } + ] + } } - ] + } } } const bid2 = { @@ -91,21 +97,27 @@ describe('vdoaiBidAdapter', function () { ] } ], - schain: { - ver: '1.0', - complete: 1, - nodes: [ - { - asi: 'example.com', - sid: '1', - hp: 1 - }, - { - asi: 'example1.com', - sid: '2', - hp: 1 + ortb2: { + source: { + ext: { + schain: { + ver: '1.0', + complete: 1, + nodes: [ + { + asi: 'example.com', + sid: '1', + hp: 1 + }, + { + asi: 'example1.com', + sid: '2', + hp: 1 + } + ] + } } - ] + } } } const bid3 = { @@ -148,16 +160,22 @@ describe('vdoaiBidAdapter', function () { ] } ], - schain: { - ver: '1.0', - complete: 1, - nodes: [ - { - asi: 'example.com', - sid: '1', - hp: 1 + ortb2: { + source: { + ext: { + schain: { + ver: '1.0', + complete: 1, + nodes: [ + { + asi: 'example.com', + sid: '1', + hp: 1 + } + ] + } } - ] + } } } const bid4 = { @@ -198,16 +216,22 @@ describe('vdoaiBidAdapter', function () { ] } ], - schain: { - ver: '1.0', - complete: 1, - nodes: [ - { - asi: 'example.com', - sid: '1', - hp: 1 + ortb2: { + source: { + ext: { + schain: { + ver: '1.0', + complete: 1, + nodes: [ + { + asi: 'example.com', + sid: '1', + hp: 1 + } + ] + } } - ] + } } } @@ -739,6 +763,6 @@ function validateAdUnit(adUnit, bid) { })); expect(adUnit.publisherId).to.equal(bid.params.publisherId); expect(adUnit.userIdAsEids).to.deep.equal(bid.userIdAsEids); - expect(adUnit.supplyChain).to.deep.equal(bid.schain); + expect(adUnit.supplyChain).to.deep.equal(bid.ortb2?.source?.ext?.schain); expect(adUnit.ortb2Imp).to.deep.equal(bid.ortb2Imp); } diff --git a/test/spec/modules/videobyteBidAdapter_spec.js b/test/spec/modules/videobyteBidAdapter_spec.js index 7844e2bd1be..838e56ab801 100644 --- a/test/spec/modules/videobyteBidAdapter_spec.js +++ b/test/spec/modules/videobyteBidAdapter_spec.js @@ -196,7 +196,10 @@ describe('VideoByteBidAdapter', function () { hp: 1 }] }; - bidRequest.schain = globalSchain; + bidRequest.ortb2 = bidRequest.ortb2 || {}; + bidRequest.ortb2.source = bidRequest.ortb2.source || {}; + bidRequest.ortb2.source.ext = bidRequest.ortb2.source.ext || {}; + bidRequest.ortb2.source.ext.schain = globalSchain; const requests = spec.buildRequests([bidRequest], bidderRequest); const data = JSON.parse(requests[0].data); const schain = data.source.ext.schain; diff --git a/test/spec/modules/vidoomyBidAdapter_spec.js b/test/spec/modules/vidoomyBidAdapter_spec.js index 6f7c0beb29f..b05fbe43e8a 100644 --- a/test/spec/modules/vidoomyBidAdapter_spec.js +++ b/test/spec/modules/vidoomyBidAdapter_spec.js @@ -74,32 +74,38 @@ describe('vidoomyBidAdapter', function() { 'sizes': [[300, 250], [200, 100]] } }, - 'schain': { - ver: '1.0', - complete: 1, - nodes: [ - { - 'asi': 'exchange1.com', - 'sid': '1234!abcd', - 'hp': 1, - 'rid': 'bid-request-1', - 'name': 'publisher, Inc.', - 'domain': 'publisher.com' - }, - { - 'asi': 'exchange2.com', - 'sid': 'abcd', - 'hp': 1 - }, - { - 'asi': 'exchange2.com', - 'sid': 'abcd', - 'hp': 1, - 'rid': 'bid-request-2', - 'name': 'intermediary', - 'domain': 'intermediary.com' + 'ortb2': { + 'source': { + 'ext': { + 'schain': { + ver: '1.0', + complete: 1, + nodes: [ + { + 'asi': 'exchange1.com', + 'sid': '1234!abcd', + 'hp': 1, + 'rid': 'bid-request-1', + 'name': 'publisher, Inc.', + 'domain': 'publisher.com' + }, + { + 'asi': 'exchange2.com', + 'sid': 'abcd', + 'hp': 1 + }, + { + 'asi': 'exchange2.com', + 'sid': 'abcd', + 'hp': 1, + 'rid': 'bid-request-2', + 'name': 'intermediary', + 'domain': 'intermediary.com' + } + ] + } } - ] + } } }, { diff --git a/test/spec/modules/visxBidAdapter_spec.js b/test/spec/modules/visxBidAdapter_spec.js index 923ff7e86b2..c8b15527ab6 100755 --- a/test/spec/modules/visxBidAdapter_spec.js +++ b/test/spec/modules/visxBidAdapter_spec.js @@ -597,7 +597,7 @@ describe('VisxAdapter', function () { it('if schain is present payload must have schain param', function () { const schainBidRequests = [ - Object.assign({schain: schainObject}, bidRequests[0]), + Object.assign({ortb2: {source: {ext: {schain: schainObject}}}}, bidRequests[0]), bidRequests[1], bidRequests[2] ]; diff --git a/test/spec/modules/voxBidAdapter_spec.js b/test/spec/modules/voxBidAdapter_spec.js index 65752837b23..c2fe6abc4db 100644 --- a/test/spec/modules/voxBidAdapter_spec.js +++ b/test/spec/modules/voxBidAdapter_spec.js @@ -212,10 +212,16 @@ describe('VOX Adapter', function() { it('should set schain if not specified', function () { const requests = validBidRequests.map(bid => ({ ...bid, - schain: { - validation: 'strict', - config: { - ver: '1.0' + ortb2: { + source: { + ext: { + schain: { + validation: 'strict', + config: { + ver: '1.0' + } + } + } } } })) diff --git a/test/spec/modules/winrBidAdapter_spec.js b/test/spec/modules/winrBidAdapter_spec.js index 6246912d318..59e06b61405 100644 --- a/test/spec/modules/winrBidAdapter_spec.js +++ b/test/spec/modules/winrBidAdapter_spec.js @@ -470,16 +470,22 @@ describe('WinrAdapter', function () { it('should populate schain if available', function () { const bidRequest = Object.assign({}, bidRequests[0], { - schain: { - ver: '1.0', - complete: 1, - nodes: [ - { - 'asi': 'blob.com', - 'sid': '001', - 'hp': 1 + ortb2: { + source: { + ext: { + schain: { + ver: '1.0', + complete: 1, + nodes: [ + { + 'asi': 'blob.com', + 'sid': '001', + 'hp': 1 + } + ] + } } - ] + } } }); diff --git a/test/spec/modules/xeBidAdapter_spec.js b/test/spec/modules/xeBidAdapter_spec.js index c4e91d9943c..651d4cb0a6b 100644 --- a/test/spec/modules/xeBidAdapter_spec.js +++ b/test/spec/modules/xeBidAdapter_spec.js @@ -128,18 +128,20 @@ describe('xeBidAdapter', () => { it('should build request with schain', function () { const schainRequest = deepClone(defaultRequest); - schainRequest.schain = { - validation: 'strict', - config: { - ver: '1.0' + const bidderRequest = { + ortb2: { + source: { + ext: { + schain: { + ver: '1.0' + } + } + } } }; - const request = JSON.parse(spec.buildRequests([schainRequest], {}).data)[0]; + const request = JSON.parse(spec.buildRequests([schainRequest], bidderRequest).data)[0]; expect(request).to.have.property('schain').and.to.deep.equal({ - validation: 'strict', - config: { - ver: '1.0' - } + ver: '1.0' }); }); diff --git a/test/spec/modules/yahooAdsBidAdapter_spec.js b/test/spec/modules/yahooAdsBidAdapter_spec.js index f8ed7693f3f..7909fe141b7 100644 --- a/test/spec/modules/yahooAdsBidAdapter_spec.js +++ b/test/spec/modules/yahooAdsBidAdapter_spec.js @@ -424,7 +424,10 @@ describe('Yahoo Advertising Bid Adapter:', () => { complete: 1, nodes: [] }; - bidRequest.schain = globalSchain; + bidRequest.ortb2 = bidRequest.ortb2 || {}; + bidRequest.ortb2.source = bidRequest.ortb2.source || {}; + bidRequest.ortb2.source.ext = bidRequest.ortb2.source.ext || {}; + bidRequest.ortb2.source.ext.schain = globalSchain; const data = spec.buildRequests(validBidRequests, bidderRequest)[0].data; const schain = data.source.ext.schain; expect(schain).to.be.undefined; @@ -442,7 +445,10 @@ describe('Yahoo Advertising Bid Adapter:', () => { hp: 1 }] }; - bidRequest.schain = globalSchain; + bidRequest.ortb2 = bidRequest.ortb2 || {}; + bidRequest.ortb2.source = bidRequest.ortb2.source || {}; + bidRequest.ortb2.source.ext = bidRequest.ortb2.source.ext || {}; + bidRequest.ortb2.source.ext.schain = globalSchain; const data = spec.buildRequests(validBidRequests, bidderRequest)[0].data; const schain = data.source.ext.schain; expect(schain.nodes.length).to.equal(1); diff --git a/test/spec/modules/yieldlabBidAdapter_spec.js b/test/spec/modules/yieldlabBidAdapter_spec.js index 92fd20fb37d..7ce61de834c 100644 --- a/test/spec/modules/yieldlabBidAdapter_spec.js +++ b/test/spec/modules/yieldlabBidAdapter_spec.js @@ -52,22 +52,28 @@ const DEFAULT_REQUEST = () => ({ atype: 2, }], }], - schain: { - ver: '1.0', - complete: 1, - nodes: [ - { - asi: 'indirectseller.com', - sid: '1', - hp: 1, - }, - { - asi: 'indirectseller2.com', - name: 'indirectseller2 name with comma , and bang !', - sid: '2', - hp: 1, - }, - ], + ortb2: { + source: { + ext: { + schain: { + ver: '1.0', + complete: 1, + nodes: [ + { + asi: 'indirectseller.com', + sid: '1', + hp: 1, + }, + { + asi: 'indirectseller2.com', + name: 'indirectseller2 name with comma , and bang !', + sid: '2', + hp: 1, + }, + ], + } + } + } }, }); @@ -430,7 +436,7 @@ describe('yieldlabBidAdapter', () => { it('passes unencoded schain string to bid request when complete == 0', () => { const schainRequest = DEFAULT_REQUEST(); - schainRequest.schain.complete = 0; // + schainRequest.ortb2.source.ext.schain.complete = 0; const request = spec.buildRequests([schainRequest]); expect(request.url).to.include('schain=1.0,0!indirectseller.com,1,1,,,,!indirectseller2.com,2,1,,indirectseller2%20name%20with%20comma%20%2C%20and%20bang%20%21,,'); }); diff --git a/test/spec/modules/yieldmoBidAdapter_spec.js b/test/spec/modules/yieldmoBidAdapter_spec.js index 1f32742876b..9a312285a6e 100644 --- a/test/spec/modules/yieldmoBidAdapter_spec.js +++ b/test/spec/modules/yieldmoBidAdapter_spec.js @@ -304,7 +304,7 @@ describe('YieldmoAdapter', function () { complete: 1, nodes: [{asi: 'indirectseller.com', sid: '00001', hp: 1}], }; - const data = buildAndGetData([mockBannerBid({schain})]); + const data = buildAndGetData([mockBannerBid({ortb2: {source: {ext: {schain}}}})]); expect(data.schain).equal(JSON.stringify(schain)); }); @@ -631,7 +631,7 @@ describe('YieldmoAdapter', function () { hp: 1 }], }; - expect(buildAndGetData([mockVideoBid({schain})]).schain).to.deep.equal(schain); + expect(buildAndGetData([mockVideoBid({ortb2: {source: {ext: {schain}}}})]).schain).to.deep.equal(schain); }); it('should add gpid to the video request', function () { diff --git a/test/spec/modules/zeta_global_sspBidAdapter_spec.js b/test/spec/modules/zeta_global_sspBidAdapter_spec.js index 0a4e86e27ab..e031f99dc4e 100644 --- a/test/spec/modules/zeta_global_sspBidAdapter_spec.js +++ b/test/spec/modules/zeta_global_sspBidAdapter_spec.js @@ -126,12 +126,16 @@ describe('Zeta Ssp Bid Adapter', function () { gdprApplies: 1, consentString: 'consentString' }, - schain: schain, uspConsent: 'someCCPAString', params: params, userIdAsEids: eids, timeout: 500, ortb2: { + source: { + ext: { + schain: schain + } + }, bcat: ['CAT1'], badv: ['test1.com'], site: { @@ -191,7 +195,13 @@ describe('Zeta Ssp Bid Adapter', function () { gdprApplies: 1, consentString: 'consentString' }, - schain: schain, + ortb2: { + source: { + ext: { + schain: schain + } + } + }, uspConsent: 'someCCPAString', params: params, userIdAsEids: eids, diff --git a/test/spec/ortbConverter/schain_spec.js b/test/spec/ortbConverter/schain_spec.js deleted file mode 100644 index 8eeef445948..00000000000 --- a/test/spec/ortbConverter/schain_spec.js +++ /dev/null @@ -1,33 +0,0 @@ -import {setOrtbSourceExtSchain} from '../../../modules/schain.js'; - -describe('pbjs - ortb source.ext.schain', () => { - it('sets schain from request', () => { - const req = {}; - setOrtbSourceExtSchain(req, {}, { - bidRequests: [{schain: {s: 'chain'}}] - }); - expect(req.source.ext.schain).to.eql({s: 'chain'}); - }); - - it('does not set it if missing', () => { - const req = {}; - setOrtbSourceExtSchain(req, {}, {bidRequests: [{}]}); - expect(req).to.eql({}); - }) - - it('does not set it if already in request', () => { - const req = { - source: { - ext: { - schain: {s: 'chain'} - } - } - } - setOrtbSourceExtSchain(req, {}, { - bidRequests: [{ - schain: {other: 'chain'} - }] - }); - expect(req.source.ext.schain).to.eql({s: 'chain'}); - }) -}); From 8d483f88c98a562f6d4b066847994c68cc6e59f0 Mon Sep 17 00:00:00 2001 From: Chris Corbo Date: Fri, 6 Jun 2025 11:37:26 -0400 Subject: [PATCH 63/92] chore pbjs 10 support [PB-4010] (#13302) Co-authored-by: Love Sharma --- modules/ixBidAdapter.js | 30 +------------------------- test/spec/modules/ixBidAdapter_spec.js | 26 ---------------------- 2 files changed, 1 insertion(+), 55 deletions(-) diff --git a/modules/ixBidAdapter.js b/modules/ixBidAdapter.js index c0185411f78..a34277c8e17 100644 --- a/modules/ixBidAdapter.js +++ b/modules/ixBidAdapter.js @@ -59,17 +59,6 @@ const SOURCE_RTI_MAPPING = { 'uidapi.com': 'UID2', 'adserver.org': 'TDID' }; -const PROVIDERS = [ - 'lipbid', - 'criteoId', - 'merkleId', - 'parrableId', - 'connectid', - 'tapadId', - 'quantcastId', - 'pubProvidedId', - 'pairId' -]; const REQUIRED_VIDEO_PARAMS = ['mimes', 'minduration', 'maxduration']; // note: protocol/protocols is also reqd const VIDEO_PARAMS_ALLOW_LIST = [ 'mimes', 'minduration', 'maxduration', 'protocols', 'protocol', @@ -1255,12 +1244,10 @@ function addAdUnitFPD(imp, bid) { * @return {object} Reqyest object with added indentigfier info to ixDiag. */ function addIdentifiersInfo(impressions, r, impKeys, adUnitIndex, payload, baseUrl) { - const pbaAdSlot = impressions[impKeys[adUnitIndex]].pbadslot; const tagId = impressions[impKeys[adUnitIndex]].tagId; const adUnitCode = impressions[impKeys[adUnitIndex]].adUnitCode; const divId = impressions[impKeys[adUnitIndex]].divId; - if (pbaAdSlot || tagId || adUnitCode || divId) { - r.ext.ixdiag.pbadslot = pbaAdSlot; + if (tagId || adUnitCode || divId) { r.ext.ixdiag.tagid = tagId; r.ext.ixdiag.adunitcode = adUnitCode; r.ext.ixdiag.divId = divId; @@ -1269,17 +1256,6 @@ function addIdentifiersInfo(impressions, r, impKeys, adUnitIndex, payload, baseU return r; } -/** - * Return an object of user IDs stored by Prebid User ID module - * - * @returns {Array} ID providers that are present in userIds - */ -function _getUserIds(bidRequest) { - const userIds = bidRequest.userId || {}; - - return PROVIDERS.filter(provider => userIds[provider]); -} - /** * Calculates IX diagnostics values and packages them into an object * @@ -1302,7 +1278,6 @@ function buildIXDiag(validBidRequests, fledgeEnabled) { allu: 0, ren: false, version: '$prebid.version$', - userIds: _getUserIds(validBidRequests[0]), url: window.location.href.split('?')[0], vpd: defaultVideoPlacement, ae: fledgeEnabled, @@ -1378,7 +1353,6 @@ function createNativeImps(validBidRequest, nativeImps) { nativeImps[validBidRequest.adUnitCode].ixImps.push(imp); nativeImps[validBidRequest.adUnitCode].gpid = deepAccess(validBidRequest, 'ortb2Imp.ext.gpid'); nativeImps[validBidRequest.adUnitCode].dfp_ad_unit_code = deepAccess(validBidRequest, 'ortb2Imp.ext.data.adserver.adslot'); - nativeImps[validBidRequest.adUnitCode].pbadslot = deepAccess(validBidRequest, 'ortb2Imp.ext.data.pbadslot'); nativeImps[validBidRequest.adUnitCode].tagId = deepAccess(validBidRequest, 'params.tagId'); const adUnitCode = validBidRequest.adUnitCode; @@ -1401,7 +1375,6 @@ function createVideoImps(validBidRequest, videoImps) { videoImps[validBidRequest.adUnitCode].ixImps.push(imp); videoImps[validBidRequest.adUnitCode].gpid = deepAccess(validBidRequest, 'ortb2Imp.ext.gpid'); videoImps[validBidRequest.adUnitCode].dfp_ad_unit_code = deepAccess(validBidRequest, 'ortb2Imp.ext.data.adserver.adslot'); - videoImps[validBidRequest.adUnitCode].pbadslot = deepAccess(validBidRequest, 'ortb2Imp.ext.data.pbadslot'); videoImps[validBidRequest.adUnitCode].tagId = deepAccess(validBidRequest, 'params.tagId'); const adUnitCode = validBidRequest.adUnitCode; @@ -1429,7 +1402,6 @@ function createBannerImps(validBidRequest, missingBannerSizes, bannerImps, bidde bannerImps[validBidRequest.adUnitCode].gpid = deepAccess(validBidRequest, 'ortb2Imp.ext.gpid'); bannerImps[validBidRequest.adUnitCode].dfp_ad_unit_code = deepAccess(validBidRequest, 'ortb2Imp.ext.data.adserver.adslot'); bannerImps[validBidRequest.adUnitCode].tid = deepAccess(validBidRequest, 'ortb2Imp.ext.tid'); - bannerImps[validBidRequest.adUnitCode].pbadslot = deepAccess(validBidRequest, 'ortb2Imp.ext.data.pbadslot'); bannerImps[validBidRequest.adUnitCode].tagId = deepAccess(validBidRequest, 'params.tagId'); bannerImps[validBidRequest.adUnitCode].pos = deepAccess(validBidRequest, 'mediaTypes.banner.pos'); diff --git a/test/spec/modules/ixBidAdapter_spec.js b/test/spec/modules/ixBidAdapter_spec.js index a792a806e08..aec66180d57 100644 --- a/test/spec/modules/ixBidAdapter_spec.js +++ b/test/spec/modules/ixBidAdapter_spec.js @@ -1889,32 +1889,6 @@ describe('IndexexchangeAdapter', function () { }); }); - describe('getUserIds', function () { - it('request should contain userId information if configured and within bid request', function () { - config.setConfig({ - userSync: { - syncDelay: 0, - userIds: [ - { name: 'lotamePanoramaId' }, - { name: 'merkleId' }, - { name: 'parrableId' }, - ] - } - }); - - const bid = utils.deepClone(DEFAULT_BANNER_VALID_BID[0]); - bid.userId = DEFAULT_USERID_BID_DATA; - - const request = spec.buildRequests([bid], DEFAULT_OPTION)[0]; - const r = extractPayload(request); - - expect(r.ext.ixdiag.userIds).to.be.an('array'); - expect(r.ext.ixdiag.userIds.should.not.include('lotamePanoramaId')); - expect(r.ext.ixdiag.userIds.should.not.include('merkleId')); - expect(r.ext.ixdiag.userIds.should.not.include('parrableId')); - }); - }); - describe('First party data', function () { it('should not set ixdiag.fpd value if not defined', function () { const request = spec.buildRequests(DEFAULT_BANNER_VALID_BID, { ortb2: {} })[0]; From e3c3d419d166c371aff49837152f505189e89c33 Mon Sep 17 00:00:00 2001 From: pm-azhar-mulla <75726247+pm-azhar-mulla@users.noreply.github.com> Date: Mon, 9 Jun 2025 17:02:41 +0530 Subject: [PATCH 64/92] Updated pr_review for schain related changes (#13321) Co-authored-by: pm-azhar-mulla --- PR_REVIEW.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/PR_REVIEW.md b/PR_REVIEW.md index 14696c6b1d5..08505197b07 100644 --- a/PR_REVIEW.md +++ b/PR_REVIEW.md @@ -55,7 +55,7 @@ Follow steps above for general review process. In addition, please verify the fo - Video params must be read from AdUnit.mediaTypes.video when available; however bidder config can override the ad unit. - First party data must be read from the bid request object: bidrequest.ortb2 - Adapters that accept a floor parameter must also support the [floors module](https://docs.prebid.org/dev-docs/modules/floors.html) -- look for a call to the `getFloor()` function. - - Adapters cannot accept an schain parameter. Rather, they must look for the schain parameter at bidRequest.ortb2.source.ext.schain. + - Adapters cannot accept an schain parameter. Rather, they must look for the schain parameter at bidderRequest.ortb2.source.ext.schain or bidRequest.ortb2.source.ext.schain. - The bidderRequest.refererInfo.referer must be checked in addition to any bidder-specific parameter. - Page position must come from bidrequest.mediaTypes.banner.pos or bidrequest.mediaTypes.video.pos - Eids object is to be preferred to Userids object in the bid request, as the userid object may be removed in a future version From 6a511e82c9611a7e8d667cb760f1275b874134e8 Mon Sep 17 00:00:00 2001 From: Patrick McCann Date: Mon, 9 Jun 2025 09:26:26 -0400 Subject: [PATCH 65/92] maintenance: fix trailing spaces in module docs (#13323) --- modules/afpBidAdapter.md | 2 +- modules/contxtfulBidAdapter.md | 4 ++-- modules/dspxBidAdapter.md | 2 +- modules/growthCodeIdSystem.md | 4 ++-- modules/optoutBidAdapter.md | 4 ++-- modules/paapiForGpt.md | 2 +- modules/precisoBidAdapter.md | 2 +- modules/pubmaticBidAdapter.md | 10 +++++----- modules/quantcastIdSystem.md | 4 ++-- modules/robustaBidAdapter.md | 6 +++--- modules/teadsBidAdapter.md | 10 +++++----- modules/videonowBidAdapter.md | 2 +- modules/wurflRtdProvider.md | 10 +++++----- 13 files changed, 31 insertions(+), 31 deletions(-) diff --git a/modules/afpBidAdapter.md b/modules/afpBidAdapter.md index 76707b10194..d8e427cfd6e 100644 --- a/modules/afpBidAdapter.md +++ b/modules/afpBidAdapter.md @@ -238,7 +238,7 @@ var adUnits = [{ params = pbjs.getAdserverTargetingForAdUnitCode("jb-target"); iframe = document.getElementById("jb-target"); - + if (params && params['hb_adid']) { pbjs.renderAd(iframe.contentDocument, params['hb_adid']); } diff --git a/modules/contxtfulBidAdapter.md b/modules/contxtfulBidAdapter.md index 87a78c38a85..857c8f05d83 100644 --- a/modules/contxtfulBidAdapter.md +++ b/modules/contxtfulBidAdapter.md @@ -9,11 +9,11 @@ Maintainer: contact@contxtful.com # Description The Contxtful Bidder Adapter supports all mediatypes and connects to demand sources for bids. - + # Configuration ## Global Configuration Contxtful uses the global configuration to store params once instead of duplicating for each ad unit. -Also, enabling user syncing greatly increases match rates and monetization. +Also, enabling user syncing greatly increases match rates and monetization. Be sure to call `pbjs.setConfig()` only once. ```javascript diff --git a/modules/dspxBidAdapter.md b/modules/dspxBidAdapter.md index 50e3cd98278..81a6adaa07f 100644 --- a/modules/dspxBidAdapter.md +++ b/modules/dspxBidAdapter.md @@ -29,7 +29,7 @@ DSPx adapter for Prebid. params: { placement: '101', // [required] info available from your contact with DSPx team /* - bcat: "IAB2,IAB4", // [optional] list of blocked advertiser categories (IAB), comma separated + bcat: "IAB2,IAB4", // [optional] list of blocked advertiser categories (IAB), comma separated */ /* pfilter: { // [optional] diff --git a/modules/growthCodeIdSystem.md b/modules/growthCodeIdSystem.md index de5344e966b..d30d3e4984c 100644 --- a/modules/growthCodeIdSystem.md +++ b/modules/growthCodeIdSystem.md @@ -1,6 +1,6 @@ ## GrowthCode User ID Submodule -GrowthCode provides Id Enrichment for requests. +GrowthCode provides Id Enrichment for requests. ## Building Prebid with GrowthCode Support @@ -18,7 +18,7 @@ pbjs.setConfig({ userIds: [{ name: 'growthCodeId', params: { - customerEids: 'customerEids', + customerEids: 'customerEids', } }] } diff --git a/modules/optoutBidAdapter.md b/modules/optoutBidAdapter.md index de70f3e3569..098d7175aff 100644 --- a/modules/optoutBidAdapter.md +++ b/modules/optoutBidAdapter.md @@ -1,6 +1,6 @@ # Overview -Module Name: Opt Out Advertising Bidder Adapter Module -Type: Bidder Adapter +Module Name: Opt Out Advertising Bidder Adapter Module +Type: Bidder Adapter Maintainer: rob@optoutadvertising.com # Description diff --git a/modules/paapiForGpt.md b/modules/paapiForGpt.md index 31cde2e268d..8565987eb5b 100644 --- a/modules/paapiForGpt.md +++ b/modules/paapiForGpt.md @@ -15,7 +15,7 @@ This is accomplished by adding the `paapiForGpt` module to the list of modules t gulp build --modules=paapiForGpt,... ``` -Second, they must enable PAAPI in their Prebid.js configuration. +Second, they must enable PAAPI in their Prebid.js configuration. This is done through module level configuration, but to provide a high degree of flexiblity for testing, PAAPI settings also exist the slot level. ### Module Configuration diff --git a/modules/precisoBidAdapter.md b/modules/precisoBidAdapter.md index 97521f195d8..52946f9731b 100644 --- a/modules/precisoBidAdapter.md +++ b/modules/precisoBidAdapter.md @@ -87,5 +87,5 @@ Module that connects to preciso' demand sources } ] } - ]; + ]; ``` \ No newline at end of file diff --git a/modules/pubmaticBidAdapter.md b/modules/pubmaticBidAdapter.md index 6fe84d81350..4192c62221a 100644 --- a/modules/pubmaticBidAdapter.md +++ b/modules/pubmaticBidAdapter.md @@ -16,11 +16,11 @@ PubMatic bid adapter supports Video, Banner and Native currently. ``` var adUnits = [ { - code: 'test-div', + code: 'test-div', sizes: [ [300, 250], [728, 90] - ], + ], bids: [{ bidder: 'pubmatic', params: { @@ -84,7 +84,7 @@ var adVideoAdUnits = [ ``` var adUnits = [ { - code: 'test-div', + code: 'test-div', mediaTypes: { native: { image: { @@ -208,9 +208,9 @@ pbjs.setConfig({ }); ``` -Note: Combine the above the configuration with any other UserSync configuration. Multiple setConfig() calls overwrite each other and only last call for a given attribute will take effect. +Note: Combine the above the configuration with any other UserSync configuration. Multiple setConfig() calls overwrite each other and only last call for a given attribute will take effect. -# Notes: +# Notes: - PubMatic will return a test-bid if "pubmaticTest=true" is present in page URL - PubMatic will set bid.adserverTargeting.hb_buyid_pubmatic targeting key while submitting a bid into Prebid diff --git a/modules/quantcastIdSystem.md b/modules/quantcastIdSystem.md index cf76099e4a5..7e90764432b 100644 --- a/modules/quantcastIdSystem.md +++ b/modules/quantcastIdSystem.md @@ -17,10 +17,10 @@ Maintainer: asig@quantcast.com Quantcast’s privacy policies for the services rendered can be found at https://www.quantcast.com/privacy/ - Publishers deploying the module are responsible for ensuring legally required notices and choices for users. + Publishers deploying the module are responsible for ensuring legally required notices and choices for users. The Quantcast ID module will only perform any action and return an ID in situations where: - 1. the publisher has not set a ‘coppa' flag on the prebid configuration on their site (see [pbjs.setConfig.coppa](https://docs.prebid.org/dev-docs/publisher-api-reference/setConfig.html#setConfig-coppa)) + 1. the publisher has not set a ‘coppa' flag on the prebid configuration on their site (see [pbjs.setConfig.coppa](https://docs.prebid.org/dev-docs/publisher-api-reference/setConfig.html#setConfig-coppa)) 2. there is not a IAB us-privacy string indicating the digital property has provided user notice and the user has made a choice to opt out of sale 3. if GDPR applies, an IAB TCF v2 string exists indicating that Quantcast does not have consent for purpose 1 (cookies, device identifiers, or other information can be stored or accessed on your device for the purposes presented to you), or an established legal basis (by default legitimate interest) for purpose 10 (your data can be used to improve existing systems and software, and to develop new products). diff --git a/modules/robustaBidAdapter.md b/modules/robustaBidAdapter.md index 14be3b2c40e..dc49888c2f8 100644 --- a/modules/robustaBidAdapter.md +++ b/modules/robustaBidAdapter.md @@ -1,8 +1,8 @@ # Overview -Module Name: Robusta Bid Adapter -Module Type: Bidder Adapter -Maintainer: dev@robustadigital.com +Module Name: Robusta Bid Adapter +Module Type: Bidder Adapter +Maintainer: dev@robustadigital.com # Description diff --git a/modules/teadsBidAdapter.md b/modules/teadsBidAdapter.md index ded9323540b..3e0c294cd57 100644 --- a/modules/teadsBidAdapter.md +++ b/modules/teadsBidAdapter.md @@ -1,8 +1,8 @@ # Overview -**Module Name**: Teads Bidder Adapter -**Module Type**: Bidder Adapter -**Maintainer**: innov-ssp@teads.tv +**Module Name**: Teads Bidder Adapter +**Module Type**: Bidder Adapter +**Maintainer**: innov-ssp@teads.tv # Description @@ -17,7 +17,7 @@ Use `teads` as bidder. sizes: [[300, 250]], bids: [{ bidder: 'teads', - params: { + params: { placementId: 12345, pageId: 1234 } @@ -27,7 +27,7 @@ Use `teads` as bidder. sizes: [[600, 800]], bids: [{ bidder: 'teads', - params: { + params: { placementId: 12345, pageId: 1234 } diff --git a/modules/videonowBidAdapter.md b/modules/videonowBidAdapter.md index 0762880f666..d9077818cc8 100644 --- a/modules/videonowBidAdapter.md +++ b/modules/videonowBidAdapter.md @@ -28,7 +28,7 @@ Use `videonow` as bidder: }, bids: [{ bidder: 'videonow', - params: { + params: { pId: '1234', currency: 'RUB', } diff --git a/modules/wurflRtdProvider.md b/modules/wurflRtdProvider.md index c7993a67364..abee06c02e7 100644 --- a/modules/wurflRtdProvider.md +++ b/modules/wurflRtdProvider.md @@ -15,18 +15,18 @@ For a more detailed analysis bidders can subscribe to detect iPhone and iPad mod ## User-Agent Client Hints -WURFL.js is fully compatible with Chromium's User-Agent Client Hints (UA-CH) initiative. If User-Agent Client Hints are absent in the HTTP headers that WURFL.js receives, the service will automatically fall back to using the User-Agent Client Hints' JS API to fetch [high entropy client hint values](https://wicg.github.io/ua-client-hints/#getHighEntropyValues) from the client device. However, we recommend that you explicitly opt-in/advertise support for User-Agent Client Hints on your website and delegate them to the WURFL.js service for the fastest detection experience. Our documentation regarding implementing User-Agent Client Hint support [is available here](https://docs.scientiamobile.com/guides/implementing-useragent-clienthints). +WURFL.js is fully compatible with Chromium's User-Agent Client Hints (UA-CH) initiative. If User-Agent Client Hints are absent in the HTTP headers that WURFL.js receives, the service will automatically fall back to using the User-Agent Client Hints' JS API to fetch [high entropy client hint values](https://wicg.github.io/ua-client-hints/#getHighEntropyValues) from the client device. However, we recommend that you explicitly opt-in/advertise support for User-Agent Client Hints on your website and delegate them to the WURFL.js service for the fastest detection experience. Our documentation regarding implementing User-Agent Client Hint support [is available here](https://docs.scientiamobile.com/guides/implementing-useragent-clienthints). ## Usage ### Build ``` -gulp build --modules="wurflRtdProvider,appnexusBidAdapter,..." +gulp build --modules="wurflRtdProvider,appnexusBidAdapter,..." ``` ### Configuration -Use `setConfig` to instruct Prebid.js to initilize the WURFL RTD module, as specified below. +Use `setConfig` to instruct Prebid.js to initilize the WURFL RTD module, as specified below. This module is configured as part of the `realTimeData.dataProviders` @@ -46,7 +46,7 @@ pbjs.setConfig({ }); ``` -### Parameters +### Parameters | Name | Type | Description | Default | | :------------------------ | :------------ | :--------------------------------------------------------------- |:----------------- | @@ -56,7 +56,7 @@ pbjs.setConfig({ | params.altHost | String | Alternate host to connect to WURFL.js | | | params.debug | Boolean | Enable debug | `false` | -## Testing +## Testing To view an example of how the WURFL RTD module works : From cf57165d8a91623684c7622f655d5979dba0210a Mon Sep 17 00:00:00 2001 From: Patrick McCann Date: Mon, 9 Jun 2025 11:00:50 -0400 Subject: [PATCH 66/92] lintfix (#13328) --- test/spec/fpd/schain_spec.js | 78 +++++++++---------- .../spec/modules/conversantBidAdapter_spec.js | 6 +- test/spec/modules/criteoBidAdapter_spec.js | 2 +- .../modules/improvedigitalBidAdapter_spec.js | 6 +- test/spec/modules/jixieBidAdapter_spec.js | 2 +- test/spec/modules/openxBidAdapter_spec.js | 2 +- .../modules/prebidServerBidAdapter_spec.js | 2 +- .../spec/modules/stackadaptBidAdapter_spec.js | 2 +- .../modules/trafficgateBidAdapter_spec.js | 2 +- 9 files changed, 51 insertions(+), 51 deletions(-) diff --git a/test/spec/fpd/schain_spec.js b/test/spec/fpd/schain_spec.js index 8532c6e0892..062a4f5d62e 100644 --- a/test/spec/fpd/schain_spec.js +++ b/test/spec/fpd/schain_spec.js @@ -41,7 +41,7 @@ describe('Supply Chain fpd', function() { complete: 1, nodes: [{ asi: 'existing.com', sid: '99999', hp: 1 }] }; - + const input = { global: { source: { @@ -49,28 +49,28 @@ describe('Supply Chain fpd', function() { } } }; - + const schainConfig = { config: SAMPLE_SCHAIN }; - + configGetConfigStub.returns(schainConfig); configGetBidderConfigStub.returns(null); - + const result = schainPrecedence(input); - + expect(result.global.source.schain).to.deep.equal(existingSchain); expect(result.global.source.schain).to.not.deep.equal(SAMPLE_SCHAIN); expect(logInfoStub.calledWith('Preserving existing global.source.schain from ortb2')).to.be.true; }); - + it('should preserve existing bidder-specific schain ', function() { const existingBidderSchain = { ver: '3.0', complete: 1, nodes: [{ asi: 'existingbidder.com', sid: '88888', hp: 1 }] }; - + const input = { bidder: { 'bidderA': { @@ -80,7 +80,7 @@ describe('Supply Chain fpd', function() { } } }; - + const bidderConfigs = { 'bidderA': { schain: { @@ -88,24 +88,24 @@ describe('Supply Chain fpd', function() { } } }; - + configGetConfigStub.returns(null); configGetBidderConfigStub.returns(bidderConfigs); - + const result = schainPrecedence(input); - + expect(result.bidder.bidderA.source.schain).to.deep.equal(existingBidderSchain); expect(result.bidder.bidderA.source.schain).to.not.deep.equal(SAMPLE_SCHAIN); expect(logInfoStub.calledWith('Preserving existing schain for bidder bidderA from ortb2')).to.be.true; }); }); - + describe('handles edge cases', function() { it('should handle edge cases and no-op scenarios', function() { expect(schainPrecedence(null)).to.be.null; expect(schainPrecedence(undefined)).to.be.undefined; expect(schainPrecedence({})).to.deep.equal({}); - + const input = { global: { source: { @@ -124,7 +124,7 @@ describe('Supply Chain fpd', function() { describe('global schain config handling', function() { let input; - + beforeEach(function() { input = { global: { @@ -133,7 +133,7 @@ describe('Supply Chain fpd', function() { }; configGetBidderConfigStub.returns(null); }); - + it('should correctly handle different global schain config scenarios', function() { const validSchainConfig = { config: SAMPLE_SCHAIN @@ -143,10 +143,10 @@ describe('Supply Chain fpd', function() { let result = schainPrecedence(input); expect(result.global.source.schain).to.deep.equal(SAMPLE_SCHAIN); expect(logInfoStub.calledWith('Applying global schain config with precedence')).to.be.true; - + logInfoStub.reset(); input = { global: { source: {} } }; - + const invalidSchainConfig = { validation: 'strict' }; @@ -159,7 +159,7 @@ describe('Supply Chain fpd', function() { describe('bidder-specific schain config handling', function() { let input; - + beforeEach(function() { input = { global: {}, @@ -168,7 +168,7 @@ describe('Supply Chain fpd', function() { configGetConfigStub.returns(null); logInfoStub.reset(); }); - + it('should handle various bidder-specific schain scenarios', function() { const singleBidderConfig = { 'bidderA': { @@ -182,10 +182,10 @@ describe('Supply Chain fpd', function() { let result = schainPrecedence(input); expect(result.bidder.bidderA.source.schain).to.deep.equal(SAMPLE_SCHAIN); expect(logInfoStub.calledWith('Applying bidder schain config for bidderA')).to.be.true; - + logInfoStub.reset(); input = { global: {}, bidder: {} }; - + const multiBidderConfig = { 'bidderA': { schain: { @@ -208,10 +208,10 @@ describe('Supply Chain fpd', function() { expect(result.bidder.bidderC).to.be.undefined; expect(logInfoStub.calledWith('Applying bidder schain config for bidderA')).to.be.true; expect(logInfoStub.calledWith('Applying bidder schain config for bidderB')).to.be.true; - + logInfoStub.reset(); input = { global: {}, bidder: {} }; - + const invalidBidderConfig = { 'bidderA': { schain: { @@ -263,13 +263,13 @@ describe('Supply Chain fpd', function() { it('should handle various input scenarios correctly', function() { expect(moveSchainToExt(null)).to.be.null; expect(moveSchainToExt(undefined)).to.be.undefined; - + const inputNoSource = { user: { id: '123' } }; expect(moveSchainToExt(inputNoSource)).to.deep.equal(inputNoSource); - + const inputNoSchain = { source: { tid: '123' } }; expect(moveSchainToExt(inputNoSchain)).to.deep.equal(inputNoSchain); - + const basicInput = { source: { tid: '123', @@ -280,7 +280,7 @@ describe('Supply Chain fpd', function() { expect(result.source.schain).to.be.undefined; expect(result.source.ext.schain).to.deep.equal(SAMPLE_SCHAIN); expect(result.source.tid).to.equal('123'); - + const inputWithExt = { source: { tid: '123', @@ -303,28 +303,28 @@ describe('Supply Chain fpd', function() { schain: SAMPLE_SCHAIN } }); - + it('should handle bidderOrtb2 parameter variations', function() { const bidderOrtb2WithSchain = { source: { schain: SAMPLE_SCHAIN_2 } }; - + let fpd = createFreshFpd(); let result = moveSchainToExt(fpd, bidderOrtb2WithSchain); expect(result.source.schain).to.be.undefined; expect(result.source.ext.schain).to.deep.equal(SAMPLE_SCHAIN_2); - + const bidderOrtb2WithoutSchain = { source: {} }; - + fpd = createFreshFpd(); result = moveSchainToExt(fpd, bidderOrtb2WithoutSchain); expect(result.source.schain).to.be.undefined; expect(result.source.ext.schain).to.deep.equal(SAMPLE_SCHAIN); - + fpd = createFreshFpd(); result = moveSchainToExt(fpd, null); expect(result.source.schain).to.be.undefined; @@ -347,7 +347,7 @@ describe('Supply Chain fpd', function() { } } }; - + configGetConfigStub.returns({ config: SAMPLE_SCHAIN }); configGetBidderConfigStub.returns({ 'bidderA': { @@ -356,27 +356,27 @@ describe('Supply Chain fpd', function() { } } }); - + const updatedFragments = schainPrecedence(ortb2Fragments); - + expect(updatedFragments.global.source.schain).to.deep.equal(SAMPLE_SCHAIN); expect(updatedFragments.bidder.bidderA.source.schain).to.deep.equal(SAMPLE_SCHAIN_2); - + const merged = { source: { tid: '123', schain: SAMPLE_SCHAIN } }; - + const bidderOrtb2 = { source: { schain: SAMPLE_SCHAIN_2 } }; - + const finalFpd = moveSchainToExt(merged, bidderOrtb2); - + expect(finalFpd.source.schain).to.be.undefined; expect(finalFpd.source.ext.schain).to.deep.equal(SAMPLE_SCHAIN_2); expect(finalFpd.source.tid).to.equal('123'); diff --git a/test/spec/modules/conversantBidAdapter_spec.js b/test/spec/modules/conversantBidAdapter_spec.js index cb07ff422f9..357fea272ab 100644 --- a/test/spec/modules/conversantBidAdapter_spec.js +++ b/test/spec/modules/conversantBidAdapter_spec.js @@ -449,14 +449,14 @@ describe('Conversant adapter tests', function() { it('Verify supply chain data', () => { const bidderRequest = {refererInfo: {page: 'http://test.com?a=b&c=123'}}; const schain = {complete: 1, ver: '1.0', nodes: [{asi: 'bidderA.com', sid: '00001', hp: 1}]}; - + // Add schain to bidderRequest bidderRequest.ortb2 = { source: { ext: {schain: schain} } }; - + const bidsWithSchain = bidRequests.map((bid) => { return Object.assign({ ortb2: { @@ -464,7 +464,7 @@ describe('Conversant adapter tests', function() { ext: {schain: schain} } } - }, bid); + }, bid); }); const request = spec.buildRequests(bidsWithSchain, bidderRequest); const payload = request.data; diff --git a/test/spec/modules/criteoBidAdapter_spec.js b/test/spec/modules/criteoBidAdapter_spec.js index 322e0716040..b450850b33e 100755 --- a/test/spec/modules/criteoBidAdapter_spec.js +++ b/test/spec/modules/criteoBidAdapter_spec.js @@ -1242,7 +1242,7 @@ describe('The Criteo bidding adapter', function () { }, }, ]; - + // Create a modified bidderRequest with schain const modifiedBidderRequest = { ...bidderRequest, diff --git a/test/spec/modules/improvedigitalBidAdapter_spec.js b/test/spec/modules/improvedigitalBidAdapter_spec.js index c7c074795f0..3ab641f1ce4 100644 --- a/test/spec/modules/improvedigitalBidAdapter_spec.js +++ b/test/spec/modules/improvedigitalBidAdapter_spec.js @@ -552,14 +552,14 @@ describe('Improve Digital Adapter Tests', function () { it('should add schain', function () { const schain = '{"ver":"1.0","complete":1,"nodes":[{"asi":"headerlift.com","sid":"xyz","hp":1}]}'; const bidRequest = Object.assign({}, simpleBidRequest); - + // Add schain to both locations in the bid bidRequest.ortb2 = { source: { ext: {schain: schain} } }; - + // Add schain to bidderRequest as well const modifiedBidderRequest = { ...bidderRequestReferrer, @@ -569,7 +569,7 @@ describe('Improve Digital Adapter Tests', function () { } } }; - + const request = spec.buildRequests([bidRequest], modifiedBidderRequest)[0]; const payload = JSON.parse(request.data); expect(payload.source.ext.schain).to.equal(schain); diff --git a/test/spec/modules/jixieBidAdapter_spec.js b/test/spec/modules/jixieBidAdapter_spec.js index 422c1c470ed..d40d32f7446 100644 --- a/test/spec/modules/jixieBidAdapter_spec.js +++ b/test/spec/modules/jixieBidAdapter_spec.js @@ -369,7 +369,7 @@ describe('jixie Adapter', function () { hp: 1 }] }; - const oneSpecialBidReq = Object.assign({}, bidRequests_[0], { + const oneSpecialBidReq = Object.assign({}, bidRequests_[0], { ortb2: { source: { ext: { diff --git a/test/spec/modules/openxBidAdapter_spec.js b/test/spec/modules/openxBidAdapter_spec.js index ead908b531b..3812083bf59 100644 --- a/test/spec/modules/openxBidAdapter_spec.js +++ b/test/spec/modules/openxBidAdapter_spec.js @@ -1110,7 +1110,7 @@ describe('OpenxRtbAdapter', function () { ext: {schain: schainConfig} }} }]; - + // Add schain to mockBidderRequest as well mockBidderRequest.ortb2 = { source: { diff --git a/test/spec/modules/prebidServerBidAdapter_spec.js b/test/spec/modules/prebidServerBidAdapter_spec.js index 3c0fb6e000d..e15d9f1ecce 100644 --- a/test/spec/modules/prebidServerBidAdapter_spec.js +++ b/test/spec/modules/prebidServerBidAdapter_spec.js @@ -3889,7 +3889,7 @@ describe('S2S Adapter', function () { config.setConfig({ s2sConfig: options }); sinon.assert.calledOnce(logErrorSpy); }); - + it('should set adapterOptions', function () { config.setConfig({ s2sConfig: { diff --git a/test/spec/modules/stackadaptBidAdapter_spec.js b/test/spec/modules/stackadaptBidAdapter_spec.js index b55bc8d75c9..4a1943babdf 100644 --- a/test/spec/modules/stackadaptBidAdapter_spec.js +++ b/test/spec/modules/stackadaptBidAdapter_spec.js @@ -920,7 +920,7 @@ describe('stackadaptBidAdapter', function () { } }; clonedBidderRequest.bids = clonedBidRequests; - + // Add schain to bidderRequest as well clonedBidderRequest.ortb2 = { source: { diff --git a/test/spec/modules/trafficgateBidAdapter_spec.js b/test/spec/modules/trafficgateBidAdapter_spec.js index 902604f0927..392ebc63318 100644 --- a/test/spec/modules/trafficgateBidAdapter_spec.js +++ b/test/spec/modules/trafficgateBidAdapter_spec.js @@ -935,7 +935,7 @@ describe('TrafficgateOpenxRtbAdapter', function () { ext: {schain: schainConfig} }} }]; - + // Add schain to mockBidderRequest as well mockBidderRequest.ortb2 = { source: { From fd2fdd98c0a0057e28b2bda163f9c03ba3afedbb Mon Sep 17 00:00:00 2001 From: Patrick McCann Date: Mon, 9 Jun 2025 17:03:57 -0400 Subject: [PATCH 67/92] adapter: move schain to source (#13336) --- modules/adtrgtmeBidAdapter.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/modules/adtrgtmeBidAdapter.js b/modules/adtrgtmeBidAdapter.js index 31131ad685b..537ec2817d6 100644 --- a/modules/adtrgtmeBidAdapter.js +++ b/modules/adtrgtmeBidAdapter.js @@ -84,9 +84,9 @@ function createORTB(bR, bid) { hb: 1, bidderver: BIDDER_VERSION, prebidjsver: PREBIDJS_VERSION, - ...(bid?.ortb2?.source?.ext?.schain && { schain: bid?.ortb2?.source?.ext?.schain }), }, fd: 1, + ...(bid?.ortb2?.source?.ext?.schain && { schain: bid?.ortb2?.source?.ext?.schain }), }, user: { ...user, @@ -98,7 +98,7 @@ function createORTB(bR, bid) { }; if (bid?.ortb2?.source?.ext?.schain) { - oR.source.ext.schain.nodes[0].rid = oR.id; + oR.source.schain.nodes[0].rid = oR.id; } return oR; From ce487908b544adc0cceebb6d98dd1d45e92a1096 Mon Sep 17 00:00:00 2001 From: Demetrio Girardi Date: Tue, 10 Jun 2025 11:40:29 -0700 Subject: [PATCH 68/92] Prebid 10: normalize EID and schain in FPD; move legacy schain configuration logic to schain module (#13343) * normalize EIDs * normalize schain * reintroduce schain --- modules/schain.js | 66 ++++++++ modules/schain.md | 35 +++++ src/adapterManager.js | 12 -- src/fpd/normalize.js | 59 +++++++ src/fpd/schain.js | 85 ---------- src/prebid.js | 5 +- test/spec/fpd/normalize_spec.js | 91 +++++++++++ test/spec/{fpd => modules}/schain_spec.js | 173 +++------------------ test/spec/unit/core/adapterManager_spec.js | 14 -- test/spec/unit/pbjs_api_spec.js | 40 +++++ 10 files changed, 314 insertions(+), 266 deletions(-) create mode 100644 modules/schain.js create mode 100644 modules/schain.md create mode 100644 src/fpd/normalize.js delete mode 100644 src/fpd/schain.js create mode 100644 test/spec/fpd/normalize_spec.js rename test/spec/{fpd => modules}/schain_spec.js (54%) diff --git a/modules/schain.js b/modules/schain.js new file mode 100644 index 00000000000..0fe980982e3 --- /dev/null +++ b/modules/schain.js @@ -0,0 +1,66 @@ +import {config} from '../src/config.js'; +import {deepClone, logWarn} from '../src/utils.js'; +import {normalizeFPD} from '../src/fpd/normalize.js'; + + +export function applySchainConfig(ortb2Fragments) { + if (!ortb2Fragments) return ortb2Fragments; + + let warned = false; + function warnDeprecated() { + if (!warned) { + logWarn('The schain module is deprecated and no longer needed; you may provide schain directly as FPD (e.g., "setConfig({ortb2: {source: {schain: {...}})")'); + warned = true; + } + } + + // Apply global schain config if available + // config's schain will have more precedence over ortb2.source.schain + const globalSchainConfig = config.getConfig('schain'); + if (globalSchainConfig && globalSchainConfig.config) { + warnDeprecated(); + if (!ortb2Fragments?.global?.source?.schain) { + applySchainToPath(ortb2Fragments, 'global.source', globalSchainConfig.config); + } else { + logWarn('Disregarding global schain config as schain is already provided in FPD') + } + } + + // Apply bidder-specific schain configs + const bidderConfigs = config.getBidderConfig(); + if (!bidderConfigs) return ortb2Fragments; + + Object.entries(bidderConfigs) + .filter(([_, cfg]) => cfg.schain) + .forEach(([bidderCode, cfg]) => { + warnDeprecated(); + const bidderPath = `bidder.${bidderCode}.source`; + const hasSchain = ortb2Fragments?.bidder?.[bidderCode]?.source?.schain; + if (!hasSchain) { + applySchainToPath(ortb2Fragments, bidderPath, cfg.schain?.config); + } else { + logWarn(`Disregarding schain config for bidder "${bidderCode}" as schain is already provided in FPD`); + } + }); + + return ortb2Fragments; +} + +function applySchainToPath(fragments, path, schainConfig) { + const parts = path.split('.'); + let current = fragments; + + // Create path if it doesn't exist + parts.forEach(part => { + current[part] = current[part] || {}; + current = current[part]; + }); + + // Apply the schain config + current.schain = deepClone(schainConfig); +} + +normalizeFPD.before((next, ortb2Fragments) => { + applySchainConfig(ortb2Fragments); + next(ortb2Fragments); +}) diff --git a/modules/schain.md b/modules/schain.md new file mode 100644 index 00000000000..44deef99886 --- /dev/null +++ b/modules/schain.md @@ -0,0 +1,35 @@ +# schain module + +** DEPRECATED **. + +This module is deprecated since prebid 10; schain may be provided directly as fpd, for example: + +```typescript +pbjs.setConfig({ + ortb2: { + source: { + schain: { + "ver":"1.0", + "complete": 1, + "nodes": [ + { + "asi":"indirectseller.com", + "sid":"00001", + "hp":1 + }, + + { + "asi":"indirectseller-2.com", + "sid":"00002", + "hp":1 + } + ] + } + } + } +}) +``` + +You may also use the [FPD validation module](https://docs.prebid.org/dev-docs/modules/validationFpdModule.html) to validate your schain configuration. + + diff --git a/src/adapterManager.js b/src/adapterManager.js index 0db1e28d33a..98da1e3e3c1 100644 --- a/src/adapterManager.js +++ b/src/adapterManager.js @@ -40,7 +40,6 @@ import { import {getRefererInfo} from './refererDetection.js'; import {GDPR_GVLIDS, gdprDataHandler, gppDataHandler, uspDataHandler, } from './consentHandler.js'; import * as events from './events.js'; -import {moveSchainToExt} from './fpd/schain.js'; import {EVENTS, S2S} from './constants.js'; import {useMetrics} from './utils/perfMetrics.js'; import {auctionManager} from './auctionManager.js'; @@ -309,15 +308,6 @@ adapterManager.makeBidRequests = hook('sync', function (adUnits, auctionStart, a const ortb2 = ortb2Fragments.global || {}; const bidderOrtb2 = ortb2Fragments.bidder || {}; - function moveUserEidsToExt(o) { - const eids = o.user?.eids; - if (Array.isArray(eids) && eids.length) { - o.user.ext = o.user.ext || {}; - o.user.ext.eids = [...(o.user.ext.eids || []), ...eids]; - delete o.user.eids; - } - } - function addOrtb2(bidderRequest, s2sActivityParams) { const redact = dep.redact( s2sActivityParams != null @@ -326,8 +316,6 @@ adapterManager.makeBidRequests = hook('sync', function (adUnits, auctionStart, a ); const merged = mergeDeep({source: {tid: auctionId}}, ortb2, bidderOrtb2[bidderRequest.bidderCode]); - moveUserEidsToExt(merged); - moveSchainToExt(merged, bidderOrtb2[bidderRequest.bidderCode]); const fpd = Object.freeze(redact.ortb2(merged)); bidderRequest.ortb2 = fpd; bidderRequest.bids = bidderRequest.bids.map((bid) => { diff --git a/src/fpd/normalize.js b/src/fpd/normalize.js new file mode 100644 index 00000000000..0e8b20de00b --- /dev/null +++ b/src/fpd/normalize.js @@ -0,0 +1,59 @@ +import {deepEqual, deepSetValue, logWarn} from '../utils.js'; +import {hook} from '../hook.js'; + +export const normalizeFPD = hook('sync', function(ortb2Fragments) { + [ + normalizeEIDs, + normalizeSchain + ].forEach(normalizer => applyNormalizer(normalizer, ortb2Fragments)); + return ortb2Fragments; +}) + +function applyNormalizer(normalizer, ortb2Fragments) { + ortb2Fragments.global = normalizer(ortb2Fragments.global, 'global FPD'); + Object.entries(ortb2Fragments.bidder).forEach(([bidder, ortb2]) => { + ortb2Fragments.bidder[bidder] = normalizer(ortb2, `bidder '${bidder}' FPD`); + }) +} + +export function normalizeEIDs(target, context) { + if (!target) return target; + const seen = []; + const eids = [ + ...(target?.user?.eids ?? []).map(eid => [0, eid]), + ...(target?.user?.ext?.eids ?? []).map(eid => [1, eid]) + ].filter(([source, eid]) => { + if (seen.findIndex(([candidateSource, candidateEid]) => + source !== candidateSource && deepEqual(candidateEid, eid) + ) > -1) { + logWarn(`Found duplicate EID in user.eids and user.ext.eids (${context})`, eid) + return false; + } else { + seen.push([source, eid]); + return true; + } + }); + if (eids.length > 0) { + deepSetValue(target, 'user.ext.eids', eids.map(([_, eid]) => eid)); + } + delete target?.user?.eids; + return target; +} + + +export function normalizeSchain(target, context) { + if (!target) return target; + const schain = target.source?.schain; + const extSchain = target.source?.ext?.schain; + if (schain != null && extSchain != null && !deepEqual(schain, extSchain)) { + logWarn(`Conflicting source.schain and source.ext.schain (${context}), preferring source.schain`, { + 'source.schain': schain, + 'source.ext.schain': extSchain + }) + } + if ((schain ?? extSchain) != null) { + deepSetValue(target, 'source.ext.schain', schain ?? extSchain); + } + delete target.source?.schain; + return target; +} diff --git a/src/fpd/schain.js b/src/fpd/schain.js deleted file mode 100644 index f2e6d3761a6..00000000000 --- a/src/fpd/schain.js +++ /dev/null @@ -1,85 +0,0 @@ -/** - * This module handles supply chain (schain) processing and relocation - * between different locations in the ortb2 structure - */ -import {config} from '../config.js'; -import {deepClone, logInfo} from '../utils.js'; - -/** - * Applies schain from config to ortb2 fragments with precedence rules - * @param {Object} ortb2Fragments - The ortb2 fragments object - * @returns {Object} - The updated ortb2Fragments object - */ -export function schainPrecedence(ortb2Fragments) { - if (!ortb2Fragments) return ortb2Fragments; - - // Apply global schain config if available - // config's schain will have more precedence over ortb2.source.schain - const globalSchainConfig = config.getConfig('schain'); - if (globalSchainConfig && globalSchainConfig.config) { - if (!ortb2Fragments?.global?.source?.schain) { - logInfo('Applying global schain config with precedence'); - applySchainToPath(ortb2Fragments, 'global.source', globalSchainConfig.config); - } else { - logInfo('Preserving existing global.source.schain from ortb2'); - } - } - - // Apply bidder-specific schain configs - const bidderConfigs = config.getBidderConfig(); - if (!bidderConfigs) return ortb2Fragments; - - Object.entries(bidderConfigs) - .filter(([_, cfg]) => cfg.schain) - .forEach(([bidderCode, cfg]) => { - const bidderPath = `bidder.${bidderCode}.source`; - const hasSchain = ortb2Fragments?.bidder?.[bidderCode]?.source?.schain; - - if (!hasSchain) { - logInfo(`Applying bidder schain config for ${bidderCode}`); - applySchainToPath(ortb2Fragments, bidderPath, cfg.schain?.config); - } else { - logInfo(`Preserving existing schain for bidder ${bidderCode} from ortb2`); - } - }); - - return ortb2Fragments; -} - -/** - * Helper function to apply schain to a specific path in ortb2Fragments - * @param {Object} fragments - The ortb2 fragments object - * @param {String} path - Dot-notation path where to apply the schain - * @param {Object} schainConfig - The schain configuration to apply - */ -function applySchainToPath(fragments, path, schainConfig) { - const parts = path.split('.'); - let current = fragments; - - // Create path if it doesn't exist - parts.forEach(part => { - current[part] = current[part] || {}; - current = current[part]; - }); - - // Apply the schain config - current.schain = deepClone(schainConfig); -} - -/** - * Relocates schain from source.schain to source.ext.schain - * @param {Object} fpd - The first-party data object - * @returns {Object} - The updated FPD object - */ -export function moveSchainToExt(fpd, bidderOrtb2) { - if (!fpd?.source?.schain) return fpd; - - // Ensure source.ext exists - fpd.source.ext = fpd.source.ext || {}; - - // Move schain to the new location and remove from original - fpd.source.ext.schain = bidderOrtb2?.source?.schain || fpd.source.schain; - delete fpd.source.schain; - - return fpd; -} diff --git a/src/prebid.js b/src/prebid.js index d5ac6e16c8d..73ba49301f7 100644 --- a/src/prebid.js +++ b/src/prebid.js @@ -37,7 +37,6 @@ import * as events from './events.js'; import {newMetrics, useMetrics} from './utils/perfMetrics.js'; import {defer, PbPromise} from './utils/promise.js'; import {enrichFPD} from './fpd/enrichment.js'; -import {schainPrecedence} from './fpd/schain.js'; import {allConsent} from './consentHandler.js'; import { insertLocatorFrame, @@ -52,6 +51,7 @@ import { ORTB_BANNER_PARAMS } from './banner.js'; import { BANNER, VIDEO } from './mediaTypes.js'; import {delayIfPrerendering} from './utils/prerendering.js'; import { newBidder } from './adapters/bidderFactory.js'; +import {normalizeFPD} from './fpd/normalize.js'; const pbjsInstance = getGlobal(); const { triggerUserSyncs } = userSync; @@ -591,8 +591,7 @@ pbjsInstance.requestBids = (function() { global: mergeDeep({}, config.getAnyConfig('ortb2') || {}, ortb2 || {}), bidder: Object.fromEntries(Object.entries(config.getBidderConfig()).map(([bidder, cfg]) => [bidder, deepClone(cfg.ortb2)]).filter(([_, ortb2]) => ortb2 != null)) } - // Apply schain precedence rules before enrichment - ortb2Fragments = schainPrecedence(ortb2Fragments); + ortb2Fragments = normalizeFPD(ortb2Fragments); return enrichFPD(PbPromise.resolve(ortb2Fragments.global)).then(global => { ortb2Fragments.global = global; diff --git a/test/spec/fpd/normalize_spec.js b/test/spec/fpd/normalize_spec.js new file mode 100644 index 00000000000..e3a818549d6 --- /dev/null +++ b/test/spec/fpd/normalize_spec.js @@ -0,0 +1,91 @@ +import {normalizeEIDs, normalizeFPD, normalizeSchain} from '../../../src/fpd/normalize.js'; +import * as utils from '../../../src/utils'; + +describe('FPD normalization', () => { + let sandbox; + beforeEach(() => { + sandbox = sinon.createSandbox(); + sandbox.stub(utils, 'logWarn'); + }); + afterEach(() => { + sandbox.restore(); + }) + + describe('EIDs', () => { + it('should merge user.eids into user.ext.eids', () => { + const fpd = { + user: { + eids: [{source: 'idA'}], + ext: {eids: [{source: 'idB'}]} + } + }; + const result = normalizeEIDs(fpd); + expect(result.user.eids).to.not.exist; + expect(result.user.ext.eids).to.deep.have.members([ + {source: 'idA'}, + {source: 'idB'} + ]) + }); + it('should remove duplicates', () => { + const fpd = { + user: { + eids: [{source: 'id'}], + ext: {eids: [{source: 'id'}]} + } + } + expect(normalizeEIDs(fpd).user.ext.eids).to.eql([ + {source: 'id'} + ]) + sinon.assert.called(utils.logWarn); + }); + it('should NOT remove duplicates if they come from the same place', () => { + const fpd = { + user: { + eids: [{source: 'id'}, {source: 'id'}] + } + } + expect(normalizeEIDs(fpd).user.ext.eids.length).to.eql(2); + }); + it('should do nothing if there are no eids', () => { + expect(normalizeEIDs({})).to.eql({}); + }) + }) + describe('schain', () => { + it('should move schain to ext.schain', () => { + const fpd = { + source: { + schain: 'foo' + } + } + expect(normalizeSchain(fpd)).to.deep.equal({ + source: { + ext: { + schain: 'foo' + } + } + }) + }); + it('should warn on conflict', () => { + const fpd = { + source: { + schain: 'foo', + ext: { + schain: 'bar' + } + }, + } + expect(normalizeSchain(fpd)).to.eql({ + source: { + ext: { + schain: 'foo' + } + } + }); + sinon.assert.called(utils.logWarn); + }); + + it('should do nothing if there is no schain', () => { + expect(normalizeSchain({})).to.eql({}); + }) + }) +}) diff --git a/test/spec/fpd/schain_spec.js b/test/spec/modules/schain_spec.js similarity index 54% rename from test/spec/fpd/schain_spec.js rename to test/spec/modules/schain_spec.js index 062a4f5d62e..0d959b95906 100644 --- a/test/spec/fpd/schain_spec.js +++ b/test/spec/modules/schain_spec.js @@ -1,7 +1,7 @@ import {expect} from 'chai/index.js'; import * as utils from 'src/utils.js'; import {config} from 'src/config.js'; -import {schainPrecedence, moveSchainToExt} from 'src/fpd/schain.js'; +import {applySchainConfig} from 'modules/schain.js'; describe('Supply Chain fpd', function() { const SAMPLE_SCHAIN = { @@ -17,13 +17,13 @@ describe('Supply Chain fpd', function() { }; let sandbox; - let logInfoStub; + let logWarnStub; let configGetConfigStub; let configGetBidderConfigStub; beforeEach(function() { sandbox = sinon.createSandbox(); - logInfoStub = sandbox.stub(utils, 'logInfo'); + logWarnStub = sandbox.stub(utils, 'logWarn'); configGetConfigStub = sandbox.stub(config, 'getConfig'); configGetBidderConfigStub = sandbox.stub(config, 'getBidderConfig'); }); @@ -33,7 +33,7 @@ describe('Supply Chain fpd', function() { }); - describe('schainPrecedence', function() { + describe('applySchainConfig', function() { describe('preserves existing schain values', function() { it('should preserve existing global.source.schain', function() { const existingSchain = { @@ -57,11 +57,11 @@ describe('Supply Chain fpd', function() { configGetConfigStub.returns(schainConfig); configGetBidderConfigStub.returns(null); - const result = schainPrecedence(input); + const result = applySchainConfig(input); expect(result.global.source.schain).to.deep.equal(existingSchain); expect(result.global.source.schain).to.not.deep.equal(SAMPLE_SCHAIN); - expect(logInfoStub.calledWith('Preserving existing global.source.schain from ortb2')).to.be.true; + sinon.assert.called(logWarnStub); }); it('should preserve existing bidder-specific schain ', function() { @@ -92,19 +92,19 @@ describe('Supply Chain fpd', function() { configGetConfigStub.returns(null); configGetBidderConfigStub.returns(bidderConfigs); - const result = schainPrecedence(input); + const result = applySchainConfig(input); expect(result.bidder.bidderA.source.schain).to.deep.equal(existingBidderSchain); expect(result.bidder.bidderA.source.schain).to.not.deep.equal(SAMPLE_SCHAIN); - expect(logInfoStub.calledWith('Preserving existing schain for bidder bidderA from ortb2')).to.be.true; + sinon.assert.called(logWarnStub); }); }); describe('handles edge cases', function() { it('should handle edge cases and no-op scenarios', function() { - expect(schainPrecedence(null)).to.be.null; - expect(schainPrecedence(undefined)).to.be.undefined; - expect(schainPrecedence({})).to.deep.equal({}); + expect(applySchainConfig(null)).to.be.null; + expect(applySchainConfig(undefined)).to.be.undefined; + expect(applySchainConfig({})).to.deep.equal({}); const input = { global: { @@ -116,9 +116,8 @@ describe('Supply Chain fpd', function() { configGetConfigStub.returns(null); configGetBidderConfigStub.returns(null); - const result = schainPrecedence(input); + const result = applySchainConfig(input); expect(result).to.deep.equal(input); - expect(logInfoStub.called).to.be.false; }); }); @@ -140,11 +139,10 @@ describe('Supply Chain fpd', function() { }; configGetConfigStub.returns(validSchainConfig); - let result = schainPrecedence(input); + let result = applySchainConfig(input); expect(result.global.source.schain).to.deep.equal(SAMPLE_SCHAIN); - expect(logInfoStub.calledWith('Applying global schain config with precedence')).to.be.true; - logInfoStub.reset(); + logWarnStub.reset(); input = { global: { source: {} } }; const invalidSchainConfig = { @@ -152,7 +150,7 @@ describe('Supply Chain fpd', function() { }; configGetConfigStub.returns(invalidSchainConfig); - result = schainPrecedence(input); + result = applySchainConfig(input); expect(result.global.source.schain).to.be.undefined; }); }); @@ -166,7 +164,7 @@ describe('Supply Chain fpd', function() { bidder: {} }; configGetConfigStub.returns(null); - logInfoStub.reset(); + logWarnStub.reset(); }); it('should handle various bidder-specific schain scenarios', function() { @@ -179,11 +177,10 @@ describe('Supply Chain fpd', function() { }; configGetBidderConfigStub.returns(singleBidderConfig); - let result = schainPrecedence(input); + let result = applySchainConfig(input); expect(result.bidder.bidderA.source.schain).to.deep.equal(SAMPLE_SCHAIN); - expect(logInfoStub.calledWith('Applying bidder schain config for bidderA')).to.be.true; - logInfoStub.reset(); + logWarnStub.reset(); input = { global: {}, bidder: {} }; const multiBidderConfig = { @@ -202,14 +199,10 @@ describe('Supply Chain fpd', function() { }; configGetBidderConfigStub.returns(multiBidderConfig); - result = schainPrecedence(input); + result = applySchainConfig(input); expect(result.bidder.bidderA.source.schain).to.deep.equal(SAMPLE_SCHAIN); expect(result.bidder.bidderB.source.schain).to.deep.equal(SAMPLE_SCHAIN_2); expect(result.bidder.bidderC).to.be.undefined; - expect(logInfoStub.calledWith('Applying bidder schain config for bidderA')).to.be.true; - expect(logInfoStub.calledWith('Applying bidder schain config for bidderB')).to.be.true; - - logInfoStub.reset(); input = { global: {}, bidder: {} }; const invalidBidderConfig = { @@ -221,7 +214,7 @@ describe('Supply Chain fpd', function() { }; configGetBidderConfigStub.returns(invalidBidderConfig); - result = schainPrecedence(input); + result = applySchainConfig(input); expect(result.bidder.bidderA.source.schain).to.deep.equal({}); }); }); @@ -253,133 +246,9 @@ describe('Supply Chain fpd', function() { configGetConfigStub.returns(globalSchainConfig); configGetBidderConfigStub.returns(bidderConfigs); - const result = schainPrecedence(input); + const result = applySchainConfig(input); expect(result.global.source.schain).to.deep.equal(globalSchainConfig.config); expect(result.bidder.bidderA.source.schain).to.deep.equal(bidderConfigs.bidderA.schain.config); }); }); - - describe('moveSchainToExt', function() { - it('should handle various input scenarios correctly', function() { - expect(moveSchainToExt(null)).to.be.null; - expect(moveSchainToExt(undefined)).to.be.undefined; - - const inputNoSource = { user: { id: '123' } }; - expect(moveSchainToExt(inputNoSource)).to.deep.equal(inputNoSource); - - const inputNoSchain = { source: { tid: '123' } }; - expect(moveSchainToExt(inputNoSchain)).to.deep.equal(inputNoSchain); - - const basicInput = { - source: { - tid: '123', - schain: SAMPLE_SCHAIN - } - }; - let result = moveSchainToExt(basicInput); - expect(result.source.schain).to.be.undefined; - expect(result.source.ext.schain).to.deep.equal(SAMPLE_SCHAIN); - expect(result.source.tid).to.equal('123'); - - const inputWithExt = { - source: { - tid: '123', - schain: SAMPLE_SCHAIN, - ext: { - dchain: { ver: '1.0' } - } - } - }; - result = moveSchainToExt(inputWithExt); - expect(result.source.schain).to.be.undefined; - expect(result.source.ext.schain).to.deep.equal(SAMPLE_SCHAIN); - expect(result.source.ext.dchain).to.deep.equal({ ver: '1.0' }); - }); - - describe('bidderOrtb2 parameter handling', function() { - const createFreshFpd = () => ({ - source: { - tid: '123', - schain: SAMPLE_SCHAIN - } - }); - - it('should handle bidderOrtb2 parameter variations', function() { - const bidderOrtb2WithSchain = { - source: { - schain: SAMPLE_SCHAIN_2 - } - }; - - let fpd = createFreshFpd(); - let result = moveSchainToExt(fpd, bidderOrtb2WithSchain); - expect(result.source.schain).to.be.undefined; - expect(result.source.ext.schain).to.deep.equal(SAMPLE_SCHAIN_2); - - const bidderOrtb2WithoutSchain = { - source: {} - }; - - fpd = createFreshFpd(); - result = moveSchainToExt(fpd, bidderOrtb2WithoutSchain); - expect(result.source.schain).to.be.undefined; - expect(result.source.ext.schain).to.deep.equal(SAMPLE_SCHAIN); - - fpd = createFreshFpd(); - result = moveSchainToExt(fpd, null); - expect(result.source.schain).to.be.undefined; - expect(result.source.ext.schain).to.deep.equal(SAMPLE_SCHAIN); - }); - }); - }); - - describe('Integration', function() { - it('should handle the full schain workflow with both global and bidder configs', function() { - const ortb2Fragments = { - global: { - source: { - tid: '123' - } - }, - bidder: { - 'bidderA': { - source: {} - } - } - }; - - configGetConfigStub.returns({ config: SAMPLE_SCHAIN }); - configGetBidderConfigStub.returns({ - 'bidderA': { - schain: { - config: SAMPLE_SCHAIN_2 - } - } - }); - - const updatedFragments = schainPrecedence(ortb2Fragments); - - expect(updatedFragments.global.source.schain).to.deep.equal(SAMPLE_SCHAIN); - expect(updatedFragments.bidder.bidderA.source.schain).to.deep.equal(SAMPLE_SCHAIN_2); - - const merged = { - source: { - tid: '123', - schain: SAMPLE_SCHAIN - } - }; - - const bidderOrtb2 = { - source: { - schain: SAMPLE_SCHAIN_2 - } - }; - - const finalFpd = moveSchainToExt(merged, bidderOrtb2); - - expect(finalFpd.source.schain).to.be.undefined; - expect(finalFpd.source.ext.schain).to.deep.equal(SAMPLE_SCHAIN_2); - expect(finalFpd.source.tid).to.equal('123'); - }); - }); }); diff --git a/test/spec/unit/core/adapterManager_spec.js b/test/spec/unit/core/adapterManager_spec.js index a8ac1351f80..b890115df36 100644 --- a/test/spec/unit/core/adapterManager_spec.js +++ b/test/spec/unit/core/adapterManager_spec.js @@ -2084,20 +2084,6 @@ describe('adapterManager tests', function () { requests.appnexus.bids.forEach((bid) => expect(bid.ortb2).to.eql(requests.appnexus.ortb2)); }); - it('should move user.eids into user.ext.eids', () => { - const global = { - user: { - eids: [{source: 'idA'}], - ext: {eids: [{source: 'idB'}]} - } - }; - const reqs = adapterManager.makeBidRequests(adUnits, 123, 'auction-id', 123, [], {global}); - reqs.forEach(req => { - expect(req.ortb2.user.ext.eids).to.deep.equal([{source: 'idB'}, {source: 'idA'}]); - expect(req.ortb2.user.eids).to.not.exist; - }); - }); - describe('source.tid', () => { beforeEach(() => { sinon.stub(dep, 'redact').returns({ diff --git a/test/spec/unit/pbjs_api_spec.js b/test/spec/unit/pbjs_api_spec.js index ef163b3b547..b7f6a702091 100644 --- a/test/spec/unit/pbjs_api_spec.js +++ b/test/spec/unit/pbjs_api_spec.js @@ -1818,6 +1818,46 @@ describe('Unit: Prebid Module', function () { await auctionStarted; } + it('with normalized FPD', async () => { + configObj.setBidderConfig({ + bidders: ['test'], + config: { + ortb2: { + source: { + schain: 'foo' + } + } + } + }); + configObj.setConfig({ + ortb2: { + source: { + schain: 'bar' + } + } + }); + await runAuction(); + sinon.assert.calledWith(startAuctionStub, sinon.match({ + ortb2Fragments: { + global: { + source: { + ext: { + schain: 'bar' + } + } + }, + bidder: { + test: { + source: { + ext: { + schain: 'foo' + } + } + } + } + } + })); + }) describe('with FPD', () => { let globalFPD, auctionFPD, mergedFPD; beforeEach(() => { From 67c4dd4e4aeb7b5bae18cff557dbd1662c6665c6 Mon Sep 17 00:00:00 2001 From: Patrick McCann Date: Tue, 10 Jun 2025 22:08:03 -0400 Subject: [PATCH 69/92] Prebid 10: Update gulpfile.js (#13137) * Update gulpfile.js * Update gulpfile.js * Update gulpfile.js * Update gulpfile.js * Update gulpfile.js * Update gulpfile.js * Update gulpfile.js --------- Co-authored-by: Chris Huie --- gulpfile.js | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/gulpfile.js b/gulpfile.js index 4317d32c9c3..657a8493a36 100644 --- a/gulpfile.js +++ b/gulpfile.js @@ -527,9 +527,10 @@ gulp.task('build-bundle-prod', gulp.series('build-creative-prod', makeWebpackPkg gulp.task('build-bundle-verbose', gulp.series('build-creative-dev', makeWebpackPkg(makeVerbose(standaloneDebuggingConfig)), makeWebpackPkg(makeVerbose()), gulpBundle.bind(null, false))); // public tasks (dependencies are needed for each task since they can be ran on their own) +gulp.task('update-browserslist', execaTask('npx update-browserslist-db@latest')); 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, 'test-all-features-disabled', 'test-only')); +gulp.task('test', gulp.series('update-browserslist', clean, lint, 'test-all-features-disabled', 'test-only')); gulp.task('test-coverage', gulp.series(clean, testCoverage)); gulp.task(viewCoverage); @@ -538,7 +539,7 @@ gulp.task('coveralls', gulp.series('test-coverage', coveralls)); // npm will by default use .gitignore, so create an .npmignore that is a copy of it except it includes "dist" gulp.task('setup-npmignore', execaTask("sed 's/^\\/\\?dist\\/\\?$//g;w .npmignore' .gitignore")); -gulp.task('build', gulp.series(clean, 'build-bundle-prod', updateCreativeExample, setupDist)); +gulp.task('build', gulp.series('update-browserslist', clean, 'build-bundle-prod', updateCreativeExample, setupDist)); gulp.task('build-release', gulp.series('build', 'setup-npmignore')); gulp.task('build-postbid', gulp.series(escapePostbidConfig, buildPostbid)); From 54466d46dcda742a8678fe975a844a7e9daa26af Mon Sep 17 00:00:00 2001 From: Patrick McCann Date: Wed, 11 Jun 2025 08:45:31 -0400 Subject: [PATCH 70/92] bump coveralls --- gulpfile.js | 2 +- package-lock.json | 768 +++++----------------------------------------- package.json | 2 +- 3 files changed, 76 insertions(+), 696 deletions(-) diff --git a/gulpfile.js b/gulpfile.js index 4317d32c9c3..602c4a177d5 100644 --- a/gulpfile.js +++ b/gulpfile.js @@ -447,7 +447,7 @@ function testCoverage(done) { function coveralls() { // 2nd arg is a dependency: 'test' must be finished // first send results of istanbul's test coverage to coveralls.io. - return execaTask('cat build/coverage/lcov.info | node_modules/coveralls/bin/coveralls.js')(); + return execaTask('cat build/coverage/lcov.info | node_modules/coveralls-next/bin/coveralls.js')(); } // This task creates postbid.js. Postbid setup is different from prebid.js diff --git a/package-lock.json b/package-lock.json index 5cd1a06cae0..f3ad2ffb8e6 100644 --- a/package-lock.json +++ b/package-lock.json @@ -13,6 +13,7 @@ "@babel/preset-env": "^7.27.2", "@babel/runtime": "^7.27.6", "core-js": "^3.42.0", + "coveralls-next": "^4.2.1", "crypto-js": "^4.2.0", "dlv": "^1.1.3", "dset": "^3.1.4", @@ -38,7 +39,6 @@ "babel-plugin-istanbul": "^6.1.1", "body-parser": "^1.19.0", "chai": "^4.2.0", - "coveralls": "^3.1.0", "deep-equal": "^2.0.3", "eslint": "^9.22.0", "eslint-plugin-import": "^2.31.0", @@ -5948,6 +5948,7 @@ "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.3.tgz", "integrity": "sha512-4K0cK3L1hsqk9xIb2z9vs/XU+PGJZ9PNpJRDS9YLzmNdX6jmVPfamLvTJr0aDAusnHyCHO6MjzlkAsgtqp9teA==", "dev": true, + "peer": true, "dependencies": { "fast-deep-equal": "^3.1.1", "fast-json-stable-stringify": "^2.0.0", @@ -6487,15 +6488,6 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/asn1": { - "version": "0.2.6", - "resolved": "https://registry.npmjs.org/asn1/-/asn1-0.2.6.tgz", - "integrity": "sha512-ix/FxPn0MDjeyJ7i/yoHGFt/EX6LyNbxSEhPPXODPL+KB0VPk86UYfL0lMdy+KCnv+fmvIzySwaK5COwqVbWTQ==", - "dev": true, - "dependencies": { - "safer-buffer": "~2.1.0" - } - }, "node_modules/assert": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/assert/-/assert-2.1.0.tgz", @@ -6509,15 +6501,6 @@ "util": "^0.12.5" } }, - "node_modules/assert-plus": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-1.0.0.tgz", - "integrity": "sha512-NfJ4UzBCcQGLDlQq7nHxH+tv3kyZ0hHQqF5BO6J7tNJeP5do1llPr8dZ8zHonfhAu0PHAdMkSo+8o0wxg9lZWw==", - "dev": true, - "engines": { - "node": ">=0.8" - } - }, "node_modules/assertion-error": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/assertion-error/-/assertion-error-1.1.0.tgz", @@ -6610,8 +6593,7 @@ "node_modules/asynckit": { "version": "0.4.0", "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", - "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==", - "dev": true + "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==" }, "node_modules/available-typed-arrays": { "version": "1.0.7", @@ -6628,21 +6610,6 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/aws-sign2": { - "version": "0.7.0", - "resolved": "https://registry.npmjs.org/aws-sign2/-/aws-sign2-0.7.0.tgz", - "integrity": "sha512-08kcGqnYf/YmjoRhfxyu+CLxBjUtHLXLXX/vUfx9l2LYzG3c1m61nrpyFUZI6zeS+Li/wWMMidD9KgrqtGq3mA==", - "dev": true, - "engines": { - "node": "*" - } - }, - "node_modules/aws4": { - "version": "1.13.0", - "resolved": "https://registry.npmjs.org/aws4/-/aws4-1.13.0.tgz", - "integrity": "sha512-3AungXC4I8kKsS9PuS4JH2nc+0bVY/mjgrephHTIi8fpEeGsTHBUJeosp0Wc1myYMElmD0B3Oc4XL/HVJ4PV2g==", - "dev": true - }, "node_modules/axios": { "version": "1.8.3", "resolved": "https://registry.npmjs.org/axios/-/axios-1.8.3.tgz", @@ -6655,20 +6622,6 @@ "proxy-from-env": "^1.1.0" } }, - "node_modules/axios/node_modules/form-data": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.0.tgz", - "integrity": "sha512-ETEklSGi5t0QMZuiXoA/Q6vcnxcLQP5vdugSpuAyi6SVGi2clPPp+xgEhuMaHC+zGgn31Kd235W35f7Hykkaww==", - "dev": true, - "dependencies": { - "asynckit": "^0.4.0", - "combined-stream": "^1.0.8", - "mime-types": "^2.1.12" - }, - "engines": { - "node": ">= 6" - } - }, "node_modules/b4a": { "version": "1.6.6", "resolved": "https://registry.npmjs.org/b4a/-/b4a-1.6.6.tgz", @@ -6902,15 +6855,6 @@ "node": ">=10.0.0" } }, - "node_modules/bcrypt-pbkdf": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/bcrypt-pbkdf/-/bcrypt-pbkdf-1.0.2.tgz", - "integrity": "sha512-qeFIXtP4MSoi6NLqO12WfqARWWuCKi2Rn/9hJLEmtB5yTNr9DqFWkJRCf2qShWzPeAMRnOgCrq0sg/KLv5ES9w==", - "dev": true, - "dependencies": { - "tweetnacl": "^0.14.3" - } - }, "node_modules/big-integer": { "version": "1.6.52", "resolved": "https://registry.npmjs.org/big-integer/-/big-integer-1.6.52.tgz", @@ -7414,12 +7358,6 @@ ], "license": "CC-BY-4.0" }, - "node_modules/caseless": { - "version": "0.12.0", - "resolved": "https://registry.npmjs.org/caseless/-/caseless-0.12.0.tgz", - "integrity": "sha512-4tYFyifaFfGacoiObjJegolkwSU4xQNGbVgUiNYVUxbQ2x2lUsFvY4hVgVzGiIe6WLOPqycWXA40l+PWsxthUw==", - "dev": true - }, "node_modules/chai": { "version": "4.4.1", "resolved": "https://registry.npmjs.org/chai/-/chai-4.4.1.tgz", @@ -7795,7 +7733,6 @@ "version": "1.0.8", "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", - "dev": true, "dependencies": { "delayed-stream": "~1.0.0" }, @@ -8099,23 +8036,41 @@ "node": ">= 0.10" } }, - "node_modules/coveralls": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/coveralls/-/coveralls-3.1.1.tgz", - "integrity": "sha512-+dxnG2NHncSD1NrqbSM3dn/lE57O6Qf/koe9+I7c+wzkqRmEvcp0kgJdxKInzYzkICKkFMZsX3Vct3++tsF9ww==", - "dev": true, + "node_modules/coveralls-next": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/coveralls-next/-/coveralls-next-4.2.1.tgz", + "integrity": "sha512-O/SBGZsCryt+6Q3NuJHENyQYaucTEV9qp0KGaed+y42PUh+GuF949LRLHKZbxWwOIc1tV8bJRIVWlfbZ8etEwQ==", + "license": "BSD-2-Clause", "dependencies": { - "js-yaml": "^3.13.1", - "lcov-parse": "^1.0.0", - "log-driver": "^1.2.7", - "minimist": "^1.2.5", - "request": "^2.88.2" + "form-data": "4.0.0", + "js-yaml": "4.1.0", + "lcov-parse": "1.0.0", + "log-driver": "1.2.7", + "minimist": "1.2.8" }, "bin": { "coveralls": "bin/coveralls.js" }, "engines": { - "node": ">=6" + "node": ">=16" + } + }, + "node_modules/coveralls-next/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==", + "license": "Python-2.0" + }, + "node_modules/coveralls-next/node_modules/js-yaml": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", + "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==", + "license": "MIT", + "dependencies": { + "argparse": "^2.0.1" + }, + "bin": { + "js-yaml": "bin/js-yaml.js" } }, "node_modules/crc-32": { @@ -8285,18 +8240,6 @@ "integrity": "sha512-GAj5FOq0Hd+RsCGVJxZuKaIDXDf3h6GQoNEjFgbLLI/trgtavwUbSnZ5pVfg27DVCaWjIohryS0JFwIJyT2cMg==", "dev": true }, - "node_modules/dashdash": { - "version": "1.14.1", - "resolved": "https://registry.npmjs.org/dashdash/-/dashdash-1.14.1.tgz", - "integrity": "sha512-jRFi8UDGo6j+odZiEpjazZaWqEal3w/basFjQHQEwVtZJGDpxbH1MeYluwCS8Xq5wmLJooDlMgvVarmWfGM44g==", - "dev": true, - "dependencies": { - "assert-plus": "^1.0.0" - }, - "engines": { - "node": ">=0.10" - } - }, "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", @@ -8593,7 +8536,6 @@ "version": "1.0.0", "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", "integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==", - "dev": true, "engines": { "node": ">=0.4.0" } @@ -8855,22 +8797,6 @@ "wcwidth": "^1.0.1" } }, - "node_modules/ecc-jsbn": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/ecc-jsbn/-/ecc-jsbn-0.1.2.tgz", - "integrity": "sha512-eh9O+hwRHNbG4BLTjEl3nw044CkGm5X6LoaCf7LPp7UU8Qrt47JYNi6nPX8xjW97TKGKm1ouctg0QSpZe9qrnw==", - "dev": true, - "dependencies": { - "jsbn": "~0.1.0", - "safer-buffer": "^2.1.0" - } - }, - "node_modules/ecc-jsbn/node_modules/jsbn": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/jsbn/-/jsbn-0.1.1.tgz", - "integrity": "sha512-UVU9dibq2JcFWxQPA6KCqj5O42VOmAY3zQUfEKxU0KpTGXwNoCjkX1e13eHNvw/xPynt6pU0rZ1htjWTNTSXsg==", - "dev": true - }, "node_modules/edge-paths": { "version": "3.0.5", "resolved": "https://registry.npmjs.org/edge-paths/-/edge-paths-3.0.5.tgz", @@ -10775,15 +10701,6 @@ "fd-slicer": "~1.1.0" } }, - "node_modules/extsprintf": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/extsprintf/-/extsprintf-1.3.0.tgz", - "integrity": "sha512-11Ndz7Nv+mvAC1j0ktTa7fAb0vLyGGX+rMHNBYQviQDGU0Hw7lhctJANqbPhu9nV9/izT/IntTgZ7Im/9LJs9g==", - "dev": true, - "engines": [ - "node >=0.6.0" - ] - }, "node_modules/faker": { "version": "5.5.3", "resolved": "https://registry.npmjs.org/faker/-/faker-5.5.3.tgz", @@ -11222,15 +11139,6 @@ "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", - "integrity": "sha512-j0KLYPhm6zeac4lz3oJ3o65qvgQCcPubiyotZrXqEaG4hNagNYO8qdlUrX5vwqv9ohqeT/Z3j6+yW067yWWdUw==", - "dev": true, - "engines": { - "node": "*" - } - }, "node_modules/fork-stream": { "version": "0.0.4", "resolved": "https://registry.npmjs.org/fork-stream/-/fork-stream-0.0.4.tgz", @@ -11238,17 +11146,17 @@ "dev": true }, "node_modules/form-data": { - "version": "2.3.3", - "resolved": "https://registry.npmjs.org/form-data/-/form-data-2.3.3.tgz", - "integrity": "sha512-1lLKB2Mu3aGP1Q/2eCOx0fNbRMe7XdwktwOruhfqqd0rIJWwN4Dh+E3hrPSlDCXnSR7UtZ1N38rVXm+6+MEhJQ==", - "dev": true, + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.0.tgz", + "integrity": "sha512-ETEklSGi5t0QMZuiXoA/Q6vcnxcLQP5vdugSpuAyi6SVGi2clPPp+xgEhuMaHC+zGgn31Kd235W35f7Hykkaww==", + "license": "MIT", "dependencies": { "asynckit": "^0.4.0", - "combined-stream": "^1.0.6", + "combined-stream": "^1.0.8", "mime-types": "^2.1.12" }, "engines": { - "node": ">= 0.12" + "node": ">= 6" } }, "node_modules/formdata-node": { @@ -11658,15 +11566,6 @@ "node": ">= 14" } }, - "node_modules/getpass": { - "version": "0.1.7", - "resolved": "https://registry.npmjs.org/getpass/-/getpass-0.1.7.tgz", - "integrity": "sha512-0fzj9JxOLfJ+XGLhR8ze3unN0KZCgZwiSSDz168VERjK8Wl8kVSdcu2kspd4s4wtAa1y/qrVRiAA0WclVsu0ng==", - "dev": true, - "dependencies": { - "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", @@ -12535,29 +12434,6 @@ "node": ">=0.10.0" } }, - "node_modules/har-schema": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/har-schema/-/har-schema-2.0.0.tgz", - "integrity": "sha512-Oqluz6zhGX8cyRaTQlFMPw80bSJVG2x/cFb8ZPhUILGgHka9SsokCCOQgpveePerqidZOrT14ipqfJb7ILcW5Q==", - "dev": true, - "engines": { - "node": ">=4" - } - }, - "node_modules/har-validator": { - "version": "5.1.5", - "resolved": "https://registry.npmjs.org/har-validator/-/har-validator-5.1.5.tgz", - "integrity": "sha512-nmT2T0lljbxdQZfspsno9hgrG3Uir6Ks5afism62poxqBM6sDnMEuPmzTq8XN0OEwqKLLdh1jQI3qyE66Nzb3w==", - "deprecated": "this library is no longer supported", - "dev": true, - "dependencies": { - "ajv": "^6.12.3", - "har-schema": "^2.0.0" - }, - "engines": { - "node": ">=6" - } - }, "node_modules/has": { "version": "1.0.4", "resolved": "https://registry.npmjs.org/has/-/has-1.0.4.tgz", @@ -12782,21 +12658,6 @@ "node": ">= 14" } }, - "node_modules/http-signature": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/http-signature/-/http-signature-1.2.0.tgz", - "integrity": "sha512-CAbnr6Rz4CYQkLYUtSNXxQPUH2gK8f3iWexVlsnMeD+GjlsQ0Xsy1cOX+mN3dtxYomRy21CiOzU8Uhw6OwncEQ==", - "dev": true, - "dependencies": { - "assert-plus": "^1.0.0", - "jsprim": "^1.2.2", - "sshpk": "^1.7.0" - }, - "engines": { - "node": ">=0.8", - "npm": ">=1.3.7" - } - }, "node_modules/https-proxy-agent": { "version": "5.0.1", "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-5.0.1.tgz", @@ -13503,12 +13364,6 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/is-typedarray": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-typedarray/-/is-typedarray-1.0.0.tgz", - "integrity": "sha512-cyA56iCMHAh5CdzjJIa4aohJyeO1YbwLi3Jc35MmRU6poroFjIGZzUzupGiRPOjgHg9TLu43xbpwXk523fMxKA==", - "dev": true - }, "node_modules/is-unc-path": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/is-unc-path/-/is-unc-path-1.0.0.tgz", @@ -13642,12 +13497,6 @@ "node": ">=0.10.0" } }, - "node_modules/isstream": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/isstream/-/isstream-0.1.2.tgz", - "integrity": "sha512-Yljz7ffyPbrLpLngrMtZ7NduUgVvi6wG9RJ9IUcyCd59YQ911PBJphODUcbOVbqYfxe1wuYf/LJ8PauMRwsM/g==", - "dev": true - }, "node_modules/istanbul": { "version": "0.4.5", "resolved": "https://registry.npmjs.org/istanbul/-/istanbul-0.4.5.tgz", @@ -14476,12 +14325,6 @@ "node": "^14.17.0 || ^16.13.0 || >=18.0.0" } }, - "node_modules/json-schema": { - "version": "0.4.0", - "resolved": "https://registry.npmjs.org/json-schema/-/json-schema-0.4.0.tgz", - "integrity": "sha512-es94M3nTIfsEPisRafak+HDLfHXnKBhV3vU5eqPcS3flIWqcxJWgXHXiey3YrpaNsanY5ei1VoYEbOzijuq9BA==", - "dev": true - }, "node_modules/json-schema-traverse": { "version": "0.4.1", "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", @@ -14494,12 +14337,6 @@ "integrity": "sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==", "dev": true }, - "node_modules/json-stringify-safe": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz", - "integrity": "sha512-ZClg6AaYvamvYEE82d3Iyd3vSSIjQ+odgjaTzRuO3s7toCdFKczob2i0zCh7JE8kWn17yvAWhUVxvqGwUalsRA==", - "dev": true - }, "node_modules/json5": { "version": "2.2.3", "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz", @@ -14524,21 +14361,6 @@ "graceful-fs": "^4.1.6" } }, - "node_modules/jsprim": { - "version": "1.4.2", - "resolved": "https://registry.npmjs.org/jsprim/-/jsprim-1.4.2.tgz", - "integrity": "sha512-P2bSOMAc/ciLz6DzgjVlGJP9+BrJWu5UDGK70C2iweC5QBIeFf0ZXRvGjEj2uYgrY2MkAAhsSWHDWlFtEroZWw==", - "dev": true, - "dependencies": { - "assert-plus": "1.0.0", - "extsprintf": "1.3.0", - "json-schema": "0.4.0", - "verror": "1.10.0" - }, - "engines": { - "node": ">=0.6.0" - } - }, "node_modules/jsx-ast-utils": { "version": "3.3.5", "resolved": "https://registry.npmjs.org/jsx-ast-utils/-/jsx-ast-utils-3.3.5.tgz", @@ -15205,7 +15027,7 @@ "version": "1.0.0", "resolved": "https://registry.npmjs.org/lcov-parse/-/lcov-parse-1.0.0.tgz", "integrity": "sha512-aprLII/vPzuQvYZnDRU78Fns9I2Ag3gi4Ipga/hxnVMCZC8DnR2nI7XBqrPoywGfxqIx/DgarGvDJZAD3YBTgQ==", - "dev": true, + "license": "BSD-3-Clause", "bin": { "lcov-parse": "bin/cli.js" } @@ -15441,7 +15263,7 @@ "version": "1.2.7", "resolved": "https://registry.npmjs.org/log-driver/-/log-driver-1.2.7.tgz", "integrity": "sha512-U7KCmLdqsGHBLeWqYlFA0V0Sl6P08EE1ZrmA9cxjUE0WVqT9qnyVDPz1kzpFEP0jdJuFnasWIfSd7fsaNXkpbg==", - "dev": true, + "license": "ISC", "engines": { "node": ">=0.8.6" } @@ -15721,7 +15543,6 @@ "version": "1.2.8", "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.8.tgz", "integrity": "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==", - "dev": true, "funding": { "url": "https://github.com/sponsors/ljharb" } @@ -16593,15 +16414,6 @@ "url": "https://github.com/fb55/nth-check?sponsor=1" } }, - "node_modules/oauth-sign": { - "version": "0.9.0", - "resolved": "https://registry.npmjs.org/oauth-sign/-/oauth-sign-0.9.0.tgz", - "integrity": "sha512-fexhUFFPTGV8ybAtSIGbV6gOkSv8UtRbDBnAyLQw4QPKkgNlsH2ByPGtMUqdWkos6YCRmAqViwgZrJc/mRDzZQ==", - "dev": true, - "engines": { - "node": "*" - } - }, "node_modules/object-assign": { "version": "4.1.1", "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", @@ -17252,12 +17064,6 @@ "node": ">=18.6.0" } }, - "node_modules/performance-now": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/performance-now/-/performance-now-2.1.0.tgz", - "integrity": "sha512-7EAHlyLHI56VEIdK57uwHdHKIaAGbnXPiw0yWbarQZOKaKpvUIgW0jWRVLiatnM+XXlSwsanIBH/hzGMJulMow==", - "dev": true - }, "node_modules/picocolors": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz", @@ -17542,12 +17348,6 @@ "node": ">= 0.10" } }, - "node_modules/psl": { - "version": "1.9.0", - "resolved": "https://registry.npmjs.org/psl/-/psl-1.9.0.tgz", - "integrity": "sha512-E/ZsdU4HLs/68gYzgGTkMicWTLPdAftJLfJFlLUAAKZGkStNU72sZjT66SnMDVOfOWY/YAoiD7Jxa9iHvngcag==", - "dev": true - }, "node_modules/pump": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/pump/-/pump-3.0.0.tgz", @@ -18034,57 +17834,6 @@ "readable-stream": "^2.0.2" } }, - "node_modules/request": { - "version": "2.88.2", - "resolved": "https://registry.npmjs.org/request/-/request-2.88.2.tgz", - "integrity": "sha512-MsvtOrfG9ZcrOwAW+Qi+F6HbD0CWXEh9ou77uOb7FM2WPhwT7smM833PzanhJLsgXjN89Ir6V2PczXNnMpwKhw==", - "deprecated": "request has been deprecated, see https://github.com/request/request/issues/3142", - "dev": true, - "dependencies": { - "aws-sign2": "~0.7.0", - "aws4": "^1.8.0", - "caseless": "~0.12.0", - "combined-stream": "~1.0.6", - "extend": "~3.0.2", - "forever-agent": "~0.6.1", - "form-data": "~2.3.2", - "har-validator": "~5.1.3", - "http-signature": "~1.2.0", - "is-typedarray": "~1.0.0", - "isstream": "~0.1.2", - "json-stringify-safe": "~5.0.1", - "mime-types": "~2.1.19", - "oauth-sign": "~0.9.0", - "performance-now": "^2.1.0", - "qs": "~6.5.2", - "safe-buffer": "^5.1.2", - "tough-cookie": "~2.5.0", - "tunnel-agent": "^0.6.0", - "uuid": "^3.3.2" - }, - "engines": { - "node": ">= 6" - } - }, - "node_modules/request/node_modules/qs": { - "version": "6.5.3", - "resolved": "https://registry.npmjs.org/qs/-/qs-6.5.3.tgz", - "integrity": "sha512-qxXIEh4pCGfHICj1mAJQ2/2XVZkjCDTcEgfoSQxc/fYivUZxTkk7L3bDBJSoNrEzXI17oUO5Dp07ktqE5KzczA==", - "dev": true, - "engines": { - "node": ">=0.6" - } - }, - "node_modules/request/node_modules/uuid": { - "version": "3.4.0", - "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.4.0.tgz", - "integrity": "sha512-HjSDRw6gZE5JMggctHBcjVak08+KEVhSIiDzFnT9S9aegmp85S/bReBVTb4QTFaRNptJ9kuYaNhnbNEOkbKb/A==", - "deprecated": "Please upgrade to version 7 or higher. Older versions may use Math.random() in certain circumstances, which is known to be problematic. See https://v8.dev/blog/math-random for details.", - "dev": true, - "bin": { - "uuid": "bin/uuid" - } - }, "node_modules/require-directory": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", @@ -18973,37 +18722,6 @@ "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz", "integrity": "sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g==" }, - "node_modules/sshpk": { - "version": "1.18.0", - "resolved": "https://registry.npmjs.org/sshpk/-/sshpk-1.18.0.tgz", - "integrity": "sha512-2p2KJZTSqQ/I3+HX42EpYOa2l3f8Erv8MWKsy2I9uf4wA7yFIkXRffYdsx86y6z4vHtV8u7g+pPlr8/4ouAxsQ==", - "dev": true, - "dependencies": { - "asn1": "~0.2.3", - "assert-plus": "^1.0.0", - "bcrypt-pbkdf": "^1.0.0", - "dashdash": "^1.12.0", - "ecc-jsbn": "~0.1.1", - "getpass": "^0.1.1", - "jsbn": "~0.1.0", - "safer-buffer": "^2.0.2", - "tweetnacl": "~0.14.0" - }, - "bin": { - "sshpk-conv": "bin/sshpk-conv", - "sshpk-sign": "bin/sshpk-sign", - "sshpk-verify": "bin/sshpk-verify" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/sshpk/node_modules/jsbn": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/jsbn/-/jsbn-0.1.1.tgz", - "integrity": "sha512-UVU9dibq2JcFWxQPA6KCqj5O42VOmAY3zQUfEKxU0KpTGXwNoCjkX1e13eHNvw/xPynt6pU0rZ1htjWTNTSXsg==", - "dev": true - }, "node_modules/stable-hash": { "version": "0.0.4", "resolved": "https://registry.npmjs.org/stable-hash/-/stable-hash-0.0.4.tgz", @@ -19887,19 +19605,6 @@ "node": ">=6" } }, - "node_modules/tough-cookie": { - "version": "2.5.0", - "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-2.5.0.tgz", - "integrity": "sha512-nlLsUzgm1kfLXSXfRZMc1KLAugd4hqJHDTvc2hDIwS3mZAfMEuMbc03SujMF+GEcpaX/qboeycw6iO8JwVv2+g==", - "dev": true, - "dependencies": { - "psl": "^1.1.28", - "punycode": "^2.1.1" - }, - "engines": { - "node": ">=0.8" - } - }, "node_modules/traverse": { "version": "0.3.9", "resolved": "https://registry.npmjs.org/traverse/-/traverse-0.3.9.tgz", @@ -19979,24 +19684,6 @@ "fsevents": "~2.3.3" } }, - "node_modules/tunnel-agent": { - "version": "0.6.0", - "resolved": "https://registry.npmjs.org/tunnel-agent/-/tunnel-agent-0.6.0.tgz", - "integrity": "sha512-McnNiV1l8RYeY8tBgEpuodCC1mLUdbSN+CYBL7kJsJNInOP8UjDDEwdk6Mw60vdLLrr5NHKZhMAOSrR2NZuQ+w==", - "dev": true, - "dependencies": { - "safe-buffer": "^5.0.1" - }, - "engines": { - "node": "*" - } - }, - "node_modules/tweetnacl": { - "version": "0.14.5", - "resolved": "https://registry.npmjs.org/tweetnacl/-/tweetnacl-0.14.5.tgz", - "integrity": "sha512-KXXFFdAbFXY4geFIwoyNK+f5Z1b7swfXABfL7HXCmoIWMKU3dmS26672A4EeQtDzLKy7SXmfBu51JolvEKwtGA==", - "dev": true - }, "node_modules/type-check": { "version": "0.4.0", "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz", @@ -20563,26 +20250,6 @@ "node": ">= 0.8" } }, - "node_modules/verror": { - "version": "1.10.0", - "resolved": "https://registry.npmjs.org/verror/-/verror-1.10.0.tgz", - "integrity": "sha512-ZZKSmDAEFOijERBLkmYfJ+vmk3w+7hOLYDNkRCuRuMJGEmqYNCNLyBBFwWKVMhfwaEF3WOd0Zlw86U/WC/+nYw==", - "dev": true, - "engines": [ - "node >=0.6.0" - ], - "dependencies": { - "assert-plus": "^1.0.0", - "core-util-is": "1.0.2", - "extsprintf": "^1.2.0" - } - }, - "node_modules/verror/node_modules/core-util-is": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz", - "integrity": "sha512-3lqz5YjWTYnW6dlDa5TLaTCcShfar1e40rmcJVwCBJC6mWlFuj0eCHIElmG1g5kyuJ/GD+8Wn4FFCcz4gJPfaQ==", - "dev": true - }, "node_modules/video.js": { "version": "7.21.6", "resolved": "https://registry.npmjs.org/video.js/-/video.js-7.21.6.tgz", @@ -26174,6 +25841,7 @@ "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.3.tgz", "integrity": "sha512-4K0cK3L1hsqk9xIb2z9vs/XU+PGJZ9PNpJRDS9YLzmNdX6jmVPfamLvTJr0aDAusnHyCHO6MjzlkAsgtqp9teA==", "dev": true, + "peer": true, "requires": { "fast-deep-equal": "^3.1.1", "fast-json-stable-stringify": "^2.0.0", @@ -26557,15 +26225,6 @@ "is-array-buffer": "^3.0.4" } }, - "asn1": { - "version": "0.2.6", - "resolved": "https://registry.npmjs.org/asn1/-/asn1-0.2.6.tgz", - "integrity": "sha512-ix/FxPn0MDjeyJ7i/yoHGFt/EX6LyNbxSEhPPXODPL+KB0VPk86UYfL0lMdy+KCnv+fmvIzySwaK5COwqVbWTQ==", - "dev": true, - "requires": { - "safer-buffer": "~2.1.0" - } - }, "assert": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/assert/-/assert-2.1.0.tgz", @@ -26579,12 +26238,6 @@ "util": "^0.12.5" } }, - "assert-plus": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-1.0.0.tgz", - "integrity": "sha512-NfJ4UzBCcQGLDlQq7nHxH+tv3kyZ0hHQqF5BO6J7tNJeP5do1llPr8dZ8zHonfhAu0PHAdMkSo+8o0wxg9lZWw==", - "dev": true - }, "assertion-error": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/assertion-error/-/assertion-error-1.1.0.tgz", @@ -26654,8 +26307,7 @@ "asynckit": { "version": "0.4.0", "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", - "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==", - "dev": true + "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==" }, "available-typed-arrays": { "version": "1.0.7", @@ -26666,18 +26318,6 @@ "possible-typed-array-names": "^1.0.0" } }, - "aws-sign2": { - "version": "0.7.0", - "resolved": "https://registry.npmjs.org/aws-sign2/-/aws-sign2-0.7.0.tgz", - "integrity": "sha512-08kcGqnYf/YmjoRhfxyu+CLxBjUtHLXLXX/vUfx9l2LYzG3c1m61nrpyFUZI6zeS+Li/wWMMidD9KgrqtGq3mA==", - "dev": true - }, - "aws4": { - "version": "1.13.0", - "resolved": "https://registry.npmjs.org/aws4/-/aws4-1.13.0.tgz", - "integrity": "sha512-3AungXC4I8kKsS9PuS4JH2nc+0bVY/mjgrephHTIi8fpEeGsTHBUJeosp0Wc1myYMElmD0B3Oc4XL/HVJ4PV2g==", - "dev": true - }, "axios": { "version": "1.8.3", "resolved": "https://registry.npmjs.org/axios/-/axios-1.8.3.tgz", @@ -26687,19 +26327,6 @@ "follow-redirects": "^1.15.6", "form-data": "^4.0.0", "proxy-from-env": "^1.1.0" - }, - "dependencies": { - "form-data": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.0.tgz", - "integrity": "sha512-ETEklSGi5t0QMZuiXoA/Q6vcnxcLQP5vdugSpuAyi6SVGi2clPPp+xgEhuMaHC+zGgn31Kd235W35f7Hykkaww==", - "dev": true, - "requires": { - "asynckit": "^0.4.0", - "combined-stream": "^1.0.8", - "mime-types": "^2.1.12" - } - } } }, "b4a": { @@ -26858,15 +26485,6 @@ "integrity": "sha512-4Bcg1P8xhUuqcii/S0Z9wiHIrQVPMermM1any+MX5GeGD7faD3/msQUDGLol9wOcz4/jbg/WJnGqoJF6LiBdtg==", "dev": true }, - "bcrypt-pbkdf": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/bcrypt-pbkdf/-/bcrypt-pbkdf-1.0.2.tgz", - "integrity": "sha512-qeFIXtP4MSoi6NLqO12WfqARWWuCKi2Rn/9hJLEmtB5yTNr9DqFWkJRCf2qShWzPeAMRnOgCrq0sg/KLv5ES9w==", - "dev": true, - "requires": { - "tweetnacl": "^0.14.3" - } - }, "big-integer": { "version": "1.6.52", "resolved": "https://registry.npmjs.org/big-integer/-/big-integer-1.6.52.tgz", @@ -27231,12 +26849,6 @@ "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001720.tgz", "integrity": "sha512-Ec/2yV2nNPwb4DnTANEV99ZWwm3ZWfdlfkQbWSDDt+PsXEVYwlhPH8tdMaPunYTKKmz7AnHi2oNEi1GcmKCD8g==" }, - "caseless": { - "version": "0.12.0", - "resolved": "https://registry.npmjs.org/caseless/-/caseless-0.12.0.tgz", - "integrity": "sha512-4tYFyifaFfGacoiObjJegolkwSU4xQNGbVgUiNYVUxbQ2x2lUsFvY4hVgVzGiIe6WLOPqycWXA40l+PWsxthUw==", - "dev": true - }, "chai": { "version": "4.4.1", "resolved": "https://registry.npmjs.org/chai/-/chai-4.4.1.tgz", @@ -27523,7 +27135,6 @@ "version": "1.0.8", "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", - "dev": true, "requires": { "delayed-stream": "~1.0.0" } @@ -27755,17 +27366,31 @@ "vary": "^1" } }, - "coveralls": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/coveralls/-/coveralls-3.1.1.tgz", - "integrity": "sha512-+dxnG2NHncSD1NrqbSM3dn/lE57O6Qf/koe9+I7c+wzkqRmEvcp0kgJdxKInzYzkICKkFMZsX3Vct3++tsF9ww==", - "dev": true, + "coveralls-next": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/coveralls-next/-/coveralls-next-4.2.1.tgz", + "integrity": "sha512-O/SBGZsCryt+6Q3NuJHENyQYaucTEV9qp0KGaed+y42PUh+GuF949LRLHKZbxWwOIc1tV8bJRIVWlfbZ8etEwQ==", "requires": { - "js-yaml": "^3.13.1", - "lcov-parse": "^1.0.0", - "log-driver": "^1.2.7", - "minimist": "^1.2.5", - "request": "^2.88.2" + "form-data": "4.0.0", + "js-yaml": "4.1.0", + "lcov-parse": "1.0.0", + "log-driver": "1.2.7", + "minimist": "1.2.8" + }, + "dependencies": { + "argparse": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", + "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==" + }, + "js-yaml": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", + "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==", + "requires": { + "argparse": "^2.0.1" + } + } } }, "crc-32": { @@ -27894,15 +27519,6 @@ "integrity": "sha512-GAj5FOq0Hd+RsCGVJxZuKaIDXDf3h6GQoNEjFgbLLI/trgtavwUbSnZ5pVfg27DVCaWjIohryS0JFwIJyT2cMg==", "dev": true }, - "dashdash": { - "version": "1.14.1", - "resolved": "https://registry.npmjs.org/dashdash/-/dashdash-1.14.1.tgz", - "integrity": "sha512-jRFi8UDGo6j+odZiEpjazZaWqEal3w/basFjQHQEwVtZJGDpxbH1MeYluwCS8Xq5wmLJooDlMgvVarmWfGM44g==", - "dev": true, - "requires": { - "assert-plus": "^1.0.0" - } - }, "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", @@ -28103,8 +27719,7 @@ "delayed-stream": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", - "integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==", - "dev": true + "integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==" }, "depd": { "version": "2.0.0", @@ -28301,24 +27916,6 @@ "wcwidth": "^1.0.1" } }, - "ecc-jsbn": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/ecc-jsbn/-/ecc-jsbn-0.1.2.tgz", - "integrity": "sha512-eh9O+hwRHNbG4BLTjEl3nw044CkGm5X6LoaCf7LPp7UU8Qrt47JYNi6nPX8xjW97TKGKm1ouctg0QSpZe9qrnw==", - "dev": true, - "requires": { - "jsbn": "~0.1.0", - "safer-buffer": "^2.1.0" - }, - "dependencies": { - "jsbn": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/jsbn/-/jsbn-0.1.1.tgz", - "integrity": "sha512-UVU9dibq2JcFWxQPA6KCqj5O42VOmAY3zQUfEKxU0KpTGXwNoCjkX1e13eHNvw/xPynt6pU0rZ1htjWTNTSXsg==", - "dev": true - } - } - }, "edge-paths": { "version": "3.0.5", "resolved": "https://registry.npmjs.org/edge-paths/-/edge-paths-3.0.5.tgz", @@ -29748,12 +29345,6 @@ } } }, - "extsprintf": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/extsprintf/-/extsprintf-1.3.0.tgz", - "integrity": "sha512-11Ndz7Nv+mvAC1j0ktTa7fAb0vLyGGX+rMHNBYQviQDGU0Hw7lhctJANqbPhu9nV9/izT/IntTgZ7Im/9LJs9g==", - "dev": true - }, "faker": { "version": "5.5.3", "resolved": "https://registry.npmjs.org/faker/-/faker-5.5.3.tgz", @@ -30075,12 +29666,6 @@ } } }, - "forever-agent": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/forever-agent/-/forever-agent-0.6.1.tgz", - "integrity": "sha512-j0KLYPhm6zeac4lz3oJ3o65qvgQCcPubiyotZrXqEaG4hNagNYO8qdlUrX5vwqv9ohqeT/Z3j6+yW067yWWdUw==", - "dev": true - }, "fork-stream": { "version": "0.0.4", "resolved": "https://registry.npmjs.org/fork-stream/-/fork-stream-0.0.4.tgz", @@ -30088,13 +29673,12 @@ "dev": true }, "form-data": { - "version": "2.3.3", - "resolved": "https://registry.npmjs.org/form-data/-/form-data-2.3.3.tgz", - "integrity": "sha512-1lLKB2Mu3aGP1Q/2eCOx0fNbRMe7XdwktwOruhfqqd0rIJWwN4Dh+E3hrPSlDCXnSR7UtZ1N38rVXm+6+MEhJQ==", - "dev": true, + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.0.tgz", + "integrity": "sha512-ETEklSGi5t0QMZuiXoA/Q6vcnxcLQP5vdugSpuAyi6SVGi2clPPp+xgEhuMaHC+zGgn31Kd235W35f7Hykkaww==", "requires": { "asynckit": "^0.4.0", - "combined-stream": "^1.0.6", + "combined-stream": "^1.0.8", "mime-types": "^2.1.12" } }, @@ -30393,15 +29977,6 @@ } } }, - "getpass": { - "version": "0.1.7", - "resolved": "https://registry.npmjs.org/getpass/-/getpass-0.1.7.tgz", - "integrity": "sha512-0fzj9JxOLfJ+XGLhR8ze3unN0KZCgZwiSSDz168VERjK8Wl8kVSdcu2kspd4s4wtAa1y/qrVRiAA0WclVsu0ng==", - "dev": true, - "requires": { - "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", @@ -31083,22 +30658,6 @@ } } }, - "har-schema": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/har-schema/-/har-schema-2.0.0.tgz", - "integrity": "sha512-Oqluz6zhGX8cyRaTQlFMPw80bSJVG2x/cFb8ZPhUILGgHka9SsokCCOQgpveePerqidZOrT14ipqfJb7ILcW5Q==", - "dev": true - }, - "har-validator": { - "version": "5.1.5", - "resolved": "https://registry.npmjs.org/har-validator/-/har-validator-5.1.5.tgz", - "integrity": "sha512-nmT2T0lljbxdQZfspsno9hgrG3Uir6Ks5afism62poxqBM6sDnMEuPmzTq8XN0OEwqKLLdh1jQI3qyE66Nzb3w==", - "dev": true, - "requires": { - "ajv": "^6.12.3", - "har-schema": "^2.0.0" - } - }, "has": { "version": "1.0.4", "resolved": "https://registry.npmjs.org/has/-/has-1.0.4.tgz", @@ -31262,17 +30821,6 @@ } } }, - "http-signature": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/http-signature/-/http-signature-1.2.0.tgz", - "integrity": "sha512-CAbnr6Rz4CYQkLYUtSNXxQPUH2gK8f3iWexVlsnMeD+GjlsQ0Xsy1cOX+mN3dtxYomRy21CiOzU8Uhw6OwncEQ==", - "dev": true, - "requires": { - "assert-plus": "^1.0.0", - "jsprim": "^1.2.2", - "sshpk": "^1.7.0" - } - }, "https-proxy-agent": { "version": "5.0.1", "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-5.0.1.tgz", @@ -31747,12 +31295,6 @@ "which-typed-array": "^1.1.16" } }, - "is-typedarray": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-typedarray/-/is-typedarray-1.0.0.tgz", - "integrity": "sha512-cyA56iCMHAh5CdzjJIa4aohJyeO1YbwLi3Jc35MmRU6poroFjIGZzUzupGiRPOjgHg9TLu43xbpwXk523fMxKA==", - "dev": true - }, "is-unc-path": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/is-unc-path/-/is-unc-path-1.0.0.tgz", @@ -31837,12 +31379,6 @@ "resolved": "https://registry.npmjs.org/isobject/-/isobject-3.0.1.tgz", "integrity": "sha512-WhB9zCku7EGTj/HQQRz5aUQEUeoQZH2bWcltRErOpymJ4boYE6wL9Tbr23krRPSZ+C5zqNSrSw+Cc7sZZ4b7vg==" }, - "isstream": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/isstream/-/isstream-0.1.2.tgz", - "integrity": "sha512-Yljz7ffyPbrLpLngrMtZ7NduUgVvi6wG9RJ9IUcyCd59YQ911PBJphODUcbOVbqYfxe1wuYf/LJ8PauMRwsM/g==", - "dev": true - }, "istanbul": { "version": "0.4.5", "resolved": "https://registry.npmjs.org/istanbul/-/istanbul-0.4.5.tgz", @@ -32459,12 +31995,6 @@ "integrity": "sha512-fi0NG4bPjCHunUJffmLd0gxssIgkNmArMvis4iNah6Owg1MCJjWhEcDLmsK6iGkJq3tHwbDkTlce70/tmXN4cQ==", "dev": true }, - "json-schema": { - "version": "0.4.0", - "resolved": "https://registry.npmjs.org/json-schema/-/json-schema-0.4.0.tgz", - "integrity": "sha512-es94M3nTIfsEPisRafak+HDLfHXnKBhV3vU5eqPcS3flIWqcxJWgXHXiey3YrpaNsanY5ei1VoYEbOzijuq9BA==", - "dev": true - }, "json-schema-traverse": { "version": "0.4.1", "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", @@ -32477,12 +32007,6 @@ "integrity": "sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==", "dev": true }, - "json-stringify-safe": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz", - "integrity": "sha512-ZClg6AaYvamvYEE82d3Iyd3vSSIjQ+odgjaTzRuO3s7toCdFKczob2i0zCh7JE8kWn17yvAWhUVxvqGwUalsRA==", - "dev": true - }, "json5": { "version": "2.2.3", "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz", @@ -32498,18 +32022,6 @@ "universalify": "^2.0.0" } }, - "jsprim": { - "version": "1.4.2", - "resolved": "https://registry.npmjs.org/jsprim/-/jsprim-1.4.2.tgz", - "integrity": "sha512-P2bSOMAc/ciLz6DzgjVlGJP9+BrJWu5UDGK70C2iweC5QBIeFf0ZXRvGjEj2uYgrY2MkAAhsSWHDWlFtEroZWw==", - "dev": true, - "requires": { - "assert-plus": "1.0.0", - "extsprintf": "1.3.0", - "json-schema": "0.4.0", - "verror": "1.10.0" - } - }, "jsx-ast-utils": { "version": "3.3.5", "resolved": "https://registry.npmjs.org/jsx-ast-utils/-/jsx-ast-utils-3.3.5.tgz", @@ -33034,8 +32546,7 @@ "lcov-parse": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/lcov-parse/-/lcov-parse-1.0.0.tgz", - "integrity": "sha512-aprLII/vPzuQvYZnDRU78Fns9I2Ag3gi4Ipga/hxnVMCZC8DnR2nI7XBqrPoywGfxqIx/DgarGvDJZAD3YBTgQ==", - "dev": true + "integrity": "sha512-aprLII/vPzuQvYZnDRU78Fns9I2Ag3gi4Ipga/hxnVMCZC8DnR2nI7XBqrPoywGfxqIx/DgarGvDJZAD3YBTgQ==" }, "lead": { "version": "4.0.0", @@ -33221,8 +32732,7 @@ "log-driver": { "version": "1.2.7", "resolved": "https://registry.npmjs.org/log-driver/-/log-driver-1.2.7.tgz", - "integrity": "sha512-U7KCmLdqsGHBLeWqYlFA0V0Sl6P08EE1ZrmA9cxjUE0WVqT9qnyVDPz1kzpFEP0jdJuFnasWIfSd7fsaNXkpbg==", - "dev": true + "integrity": "sha512-U7KCmLdqsGHBLeWqYlFA0V0Sl6P08EE1ZrmA9cxjUE0WVqT9qnyVDPz1kzpFEP0jdJuFnasWIfSd7fsaNXkpbg==" }, "log-symbols": { "version": "2.2.0", @@ -33433,8 +32943,7 @@ "minimist": { "version": "1.2.8", "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.8.tgz", - "integrity": "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==", - "dev": true + "integrity": "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==" }, "minipass": { "version": "7.1.2", @@ -34057,12 +33566,6 @@ "boolbase": "^1.0.0" } }, - "oauth-sign": { - "version": "0.9.0", - "resolved": "https://registry.npmjs.org/oauth-sign/-/oauth-sign-0.9.0.tgz", - "integrity": "sha512-fexhUFFPTGV8ybAtSIGbV6gOkSv8UtRbDBnAyLQw4QPKkgNlsH2ByPGtMUqdWkos6YCRmAqViwgZrJc/mRDzZQ==", - "dev": true - }, "object-assign": { "version": "4.1.1", "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", @@ -34543,12 +34046,6 @@ "integrity": "sha512-BYIrwr8JCXY49jUZscgw311w9oGEKo7ux/s+BxrhKTQbiQ0iYNdZNJ5LgagaeercQdFHwnR7Z5IxxFWVQ+BasQ==", "dev": true }, - "performance-now": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/performance-now/-/performance-now-2.1.0.tgz", - "integrity": "sha512-7EAHlyLHI56VEIdK57uwHdHKIaAGbnXPiw0yWbarQZOKaKpvUIgW0jWRVLiatnM+XXlSwsanIBH/hzGMJulMow==", - "dev": true - }, "picocolors": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz", @@ -34756,12 +34253,6 @@ "event-stream": "=3.3.4" } }, - "psl": { - "version": "1.9.0", - "resolved": "https://registry.npmjs.org/psl/-/psl-1.9.0.tgz", - "integrity": "sha512-E/ZsdU4HLs/68gYzgGTkMicWTLPdAftJLfJFlLUAAKZGkStNU72sZjT66SnMDVOfOWY/YAoiD7Jxa9iHvngcag==", - "dev": true - }, "pump": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/pump/-/pump-3.0.0.tgz", @@ -35116,48 +34607,6 @@ "readable-stream": "^2.0.2" } }, - "request": { - "version": "2.88.2", - "resolved": "https://registry.npmjs.org/request/-/request-2.88.2.tgz", - "integrity": "sha512-MsvtOrfG9ZcrOwAW+Qi+F6HbD0CWXEh9ou77uOb7FM2WPhwT7smM833PzanhJLsgXjN89Ir6V2PczXNnMpwKhw==", - "dev": true, - "requires": { - "aws-sign2": "~0.7.0", - "aws4": "^1.8.0", - "caseless": "~0.12.0", - "combined-stream": "~1.0.6", - "extend": "~3.0.2", - "forever-agent": "~0.6.1", - "form-data": "~2.3.2", - "har-validator": "~5.1.3", - "http-signature": "~1.2.0", - "is-typedarray": "~1.0.0", - "isstream": "~0.1.2", - "json-stringify-safe": "~5.0.1", - "mime-types": "~2.1.19", - "oauth-sign": "~0.9.0", - "performance-now": "^2.1.0", - "qs": "~6.5.2", - "safe-buffer": "^5.1.2", - "tough-cookie": "~2.5.0", - "tunnel-agent": "^0.6.0", - "uuid": "^3.3.2" - }, - "dependencies": { - "qs": { - "version": "6.5.3", - "resolved": "https://registry.npmjs.org/qs/-/qs-6.5.3.tgz", - "integrity": "sha512-qxXIEh4pCGfHICj1mAJQ2/2XVZkjCDTcEgfoSQxc/fYivUZxTkk7L3bDBJSoNrEzXI17oUO5Dp07ktqE5KzczA==", - "dev": true - }, - "uuid": { - "version": "3.4.0", - "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.4.0.tgz", - "integrity": "sha512-HjSDRw6gZE5JMggctHBcjVak08+KEVhSIiDzFnT9S9aegmp85S/bReBVTb4QTFaRNptJ9kuYaNhnbNEOkbKb/A==", - "dev": true - } - } - }, "require-directory": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", @@ -35818,31 +35267,6 @@ "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz", "integrity": "sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g==" }, - "sshpk": { - "version": "1.18.0", - "resolved": "https://registry.npmjs.org/sshpk/-/sshpk-1.18.0.tgz", - "integrity": "sha512-2p2KJZTSqQ/I3+HX42EpYOa2l3f8Erv8MWKsy2I9uf4wA7yFIkXRffYdsx86y6z4vHtV8u7g+pPlr8/4ouAxsQ==", - "dev": true, - "requires": { - "asn1": "~0.2.3", - "assert-plus": "^1.0.0", - "bcrypt-pbkdf": "^1.0.0", - "dashdash": "^1.12.0", - "ecc-jsbn": "~0.1.1", - "getpass": "^0.1.1", - "jsbn": "~0.1.0", - "safer-buffer": "^2.0.2", - "tweetnacl": "~0.14.0" - }, - "dependencies": { - "jsbn": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/jsbn/-/jsbn-0.1.1.tgz", - "integrity": "sha512-UVU9dibq2JcFWxQPA6KCqj5O42VOmAY3zQUfEKxU0KpTGXwNoCjkX1e13eHNvw/xPynt6pU0rZ1htjWTNTSXsg==", - "dev": true - } - } - }, "stable-hash": { "version": "0.0.4", "resolved": "https://registry.npmjs.org/stable-hash/-/stable-hash-0.0.4.tgz", @@ -36508,16 +35932,6 @@ "integrity": "sha512-sf4i37nQ2LBx4m3wB74y+ubopq6W/dIzXg0FDGjsYnZHVa1Da8FH853wlL2gtUhg+xJXjfk3kUZS3BRoQeoQBQ==", "dev": true }, - "tough-cookie": { - "version": "2.5.0", - "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-2.5.0.tgz", - "integrity": "sha512-nlLsUzgm1kfLXSXfRZMc1KLAugd4hqJHDTvc2hDIwS3mZAfMEuMbc03SujMF+GEcpaX/qboeycw6iO8JwVv2+g==", - "dev": true, - "requires": { - "psl": "^1.1.28", - "punycode": "^2.1.1" - } - }, "traverse": { "version": "0.3.9", "resolved": "https://registry.npmjs.org/traverse/-/traverse-0.3.9.tgz", @@ -36576,21 +35990,6 @@ "get-tsconfig": "^4.7.5" } }, - "tunnel-agent": { - "version": "0.6.0", - "resolved": "https://registry.npmjs.org/tunnel-agent/-/tunnel-agent-0.6.0.tgz", - "integrity": "sha512-McnNiV1l8RYeY8tBgEpuodCC1mLUdbSN+CYBL7kJsJNInOP8UjDDEwdk6Mw60vdLLrr5NHKZhMAOSrR2NZuQ+w==", - "dev": true, - "requires": { - "safe-buffer": "^5.0.1" - } - }, - "tweetnacl": { - "version": "0.14.5", - "resolved": "https://registry.npmjs.org/tweetnacl/-/tweetnacl-0.14.5.tgz", - "integrity": "sha512-KXXFFdAbFXY4geFIwoyNK+f5Z1b7swfXABfL7HXCmoIWMKU3dmS26672A4EeQtDzLKy7SXmfBu51JolvEKwtGA==", - "dev": true - }, "type-check": { "version": "0.4.0", "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz", @@ -36990,25 +36389,6 @@ "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz", "integrity": "sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg==" }, - "verror": { - "version": "1.10.0", - "resolved": "https://registry.npmjs.org/verror/-/verror-1.10.0.tgz", - "integrity": "sha512-ZZKSmDAEFOijERBLkmYfJ+vmk3w+7hOLYDNkRCuRuMJGEmqYNCNLyBBFwWKVMhfwaEF3WOd0Zlw86U/WC/+nYw==", - "dev": true, - "requires": { - "assert-plus": "^1.0.0", - "core-util-is": "1.0.2", - "extsprintf": "^1.2.0" - }, - "dependencies": { - "core-util-is": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz", - "integrity": "sha512-3lqz5YjWTYnW6dlDa5TLaTCcShfar1e40rmcJVwCBJC6mWlFuj0eCHIElmG1g5kyuJ/GD+8Wn4FFCcz4gJPfaQ==", - "dev": true - } - } - }, "video.js": { "version": "7.21.6", "resolved": "https://registry.npmjs.org/video.js/-/video.js-7.21.6.tgz", diff --git a/package.json b/package.json index 40d9ba886fa..a3c69a04b0d 100644 --- a/package.json +++ b/package.json @@ -60,7 +60,6 @@ "babel-plugin-istanbul": "^6.1.1", "body-parser": "^1.19.0", "chai": "^4.2.0", - "coveralls": "^3.1.0", "deep-equal": "^2.0.3", "eslint": "^9.22.0", "eslint-plugin-import": "^2.31.0", @@ -125,6 +124,7 @@ "@babel/preset-env": "^7.27.2", "@babel/runtime": "^7.27.6", "core-js": "^3.42.0", + "coveralls-next": "^4.2.1", "crypto-js": "^4.2.0", "dlv": "^1.1.3", "dset": "^3.1.4", From dd2dc4966aafd34ab727c0b0b38a9d7484e14030 Mon Sep 17 00:00:00 2001 From: Patrick McCann Date: Wed, 11 Jun 2025 10:38:21 -0400 Subject: [PATCH 71/92] fix lint --- modules/adspiritBidAdapter.js | 1 - modules/schain.js | 1 - modules/userId/index.js | 1 - src/fpd/normalize.js | 1 - test/spec/modules/schain_spec.js | 1 - 5 files changed, 5 deletions(-) diff --git a/modules/adspiritBidAdapter.js b/modules/adspiritBidAdapter.js index 2d1ceb5902d..39fd0917bad 100644 --- a/modules/adspiritBidAdapter.js +++ b/modules/adspiritBidAdapter.js @@ -131,7 +131,6 @@ export const spec = { } }; - const schain = bidRequest?.ortb2?.source?.ext?.schain; if (schain) { openRTBRequest.source = { diff --git a/modules/schain.js b/modules/schain.js index 0fe980982e3..768e2247b18 100644 --- a/modules/schain.js +++ b/modules/schain.js @@ -2,7 +2,6 @@ import {config} from '../src/config.js'; import {deepClone, logWarn} from '../src/utils.js'; import {normalizeFPD} from '../src/fpd/normalize.js'; - export function applySchainConfig(ortb2Fragments) { if (!ortb2Fragments) return ortb2Fragments; diff --git a/modules/userId/index.js b/modules/userId/index.js index 76e2cf74b2b..e3182ae7557 100644 --- a/modules/userId/index.js +++ b/modules/userId/index.js @@ -1175,7 +1175,6 @@ function updateSubmodules(options = {}) { submodules.splice(0, submodules.length); submodules.push(...updatedContainers); - if (submodules.length) { if (!addedStartAuctionHook()) { startAuction.getHooks({hook: addUserIdsHook}).remove(); diff --git a/src/fpd/normalize.js b/src/fpd/normalize.js index 0e8b20de00b..cf4a5c5d2b9 100644 --- a/src/fpd/normalize.js +++ b/src/fpd/normalize.js @@ -40,7 +40,6 @@ export function normalizeEIDs(target, context) { return target; } - export function normalizeSchain(target, context) { if (!target) return target; const schain = target.source?.schain; diff --git a/test/spec/modules/schain_spec.js b/test/spec/modules/schain_spec.js index 0d959b95906..4517eb976fb 100644 --- a/test/spec/modules/schain_spec.js +++ b/test/spec/modules/schain_spec.js @@ -32,7 +32,6 @@ describe('Supply Chain fpd', function() { sandbox.restore(); }); - describe('applySchainConfig', function() { describe('preserves existing schain values', function() { it('should preserve existing global.source.schain', function() { From c14b4dc6bef22f8868e47e71cb6a15be3b337469 Mon Sep 17 00:00:00 2001 From: Demetrio Girardi Date: Wed, 11 Jun 2025 12:10:06 -0700 Subject: [PATCH 72/92] Revert "removing sendTargetingKeys (#13255)" (#13364) This reverts commit a52744a3e2d1737f9864acce583e36e897a92f59. --- modules/criteoBidAdapter.js | 12 +- modules/ixBidAdapter.md | 1 + modules/ringieraxelspringerBidAdapter.js | 1 + modules/smaatoBidAdapter.md | 1 + src/constants.js | 1 + src/native.js | 13 ++ src/prebid.js | 13 ++ test/spec/modules/adagioBidAdapter_spec.js | 2 + test/spec/modules/admaticBidAdapter_spec.js | 1 + test/spec/modules/adxcgBidAdapter_spec.js | 1 + test/spec/modules/criteoBidAdapter_spec.js | 110 +++++++++++++ test/spec/modules/inmobiBidAdapter_spec.js | 2 + .../spec/modules/pulsepointBidAdapter_spec.js | 1 + .../ringieraxelspringerBidAdapter_spec.js | 2 + .../modules/smilewantedBidAdapter_spec.js | 1 + test/spec/native_spec.js | 154 ++++++++++++++++++ test/spec/unit/pbjs_api_spec.js | 20 +++ 17 files changed, 330 insertions(+), 6 deletions(-) diff --git a/modules/criteoBidAdapter.js b/modules/criteoBidAdapter.js index be3d90ff0e4..a38660c4f25 100644 --- a/modules/criteoBidAdapter.js +++ b/modules/criteoBidAdapter.js @@ -522,12 +522,12 @@ function buildCdbUrl(context) { function checkNativeSendId(bidRequest) { return !(bidRequest.nativeParams && ( - (bidRequest.nativeParams.image && bidRequest.nativeParams.image.sendId !== true) || - (bidRequest.nativeParams.icon && bidRequest.nativeParams.icon.sendId !== true) || - (bidRequest.nativeParams.clickUrl && bidRequest.nativeParams.clickUrl.sendId !== true) || - (bidRequest.nativeParams.displayUrl && bidRequest.nativeParams.displayUrl.sendId !== true) || - (bidRequest.nativeParams.privacyLink && bidRequest.nativeParams.privacyLink.sendId !== true) || - (bidRequest.nativeParams.privacyIcon && bidRequest.nativeParams.privacyIcon.sendId !== true) + (bidRequest.nativeParams.image && ((bidRequest.nativeParams.image.sendId !== true || bidRequest.nativeParams.image.sendTargetingKeys === true))) || + (bidRequest.nativeParams.icon && ((bidRequest.nativeParams.icon.sendId !== true || bidRequest.nativeParams.icon.sendTargetingKeys === true))) || + (bidRequest.nativeParams.clickUrl && ((bidRequest.nativeParams.clickUrl.sendId !== true || bidRequest.nativeParams.clickUrl.sendTargetingKeys === true))) || + (bidRequest.nativeParams.displayUrl && ((bidRequest.nativeParams.displayUrl.sendId !== true || bidRequest.nativeParams.displayUrl.sendTargetingKeys === true))) || + (bidRequest.nativeParams.privacyLink && ((bidRequest.nativeParams.privacyLink.sendId !== true || bidRequest.nativeParams.privacyLink.sendTargetingKeys === true))) || + (bidRequest.nativeParams.privacyIcon && ((bidRequest.nativeParams.privacyIcon.sendId !== true || bidRequest.nativeParams.privacyIcon.sendTargetingKeys === true))) )); } diff --git a/modules/ixBidAdapter.md b/modules/ixBidAdapter.md index 36ecf9dbe8f..1e17f450c0e 100644 --- a/modules/ixBidAdapter.md +++ b/modules/ixBidAdapter.md @@ -96,6 +96,7 @@ object are detailed here. | Key | Scope | Type | Description | --- | --- | --- | --- +| sendTargetingKeys | Optional | Boolean | Defines whether or not to send the hb_native_ASSET targeting keys to the ad server. Defaults to true. | adTemplate | Optional | String | Used in the ‘AdUnit-Defined Creative Scenario’, this value controls the Native template right in the page. | rendererUrl | Optional | String | Used in the ‘Custom Renderer Scenario’, this points to javascript code that will produce the Native template. | title | Optional | Title asset | The title of the ad, usually a call to action or a brand name. diff --git a/modules/ringieraxelspringerBidAdapter.js b/modules/ringieraxelspringerBidAdapter.js index 326fecc9d32..14033c1247f 100644 --- a/modules/ringieraxelspringerBidAdapter.js +++ b/modules/ringieraxelspringerBidAdapter.js @@ -187,6 +187,7 @@ function parseNativeResponse(ad) { const { dsaurl, height, width, adclick } = ad.data.meta; const link = adclick + (url || Thirdpartyclicktracker); const nativeResponse = { + sendTargetingKeys: false, title: title || Headline || '', image: { url: image || Image || '', diff --git a/modules/smaatoBidAdapter.md b/modules/smaatoBidAdapter.md index 35e8b17da4a..170880c3fc0 100644 --- a/modules/smaatoBidAdapter.md +++ b/modules/smaatoBidAdapter.md @@ -128,6 +128,7 @@ var adUnits = [{ } ] }, + sendTargetingKeys: false, } }, "bids": [{ diff --git a/src/constants.js b/src/constants.js index 07c56e01931..07a806e125f 100644 --- a/src/constants.js +++ b/src/constants.js @@ -186,6 +186,7 @@ export const NATIVE_IMAGE_TYPES = { export const NATIVE_KEYS_THAT_ARE_NOT_ASSETS = [ 'privacyIcon', 'clickUrl', + 'sendTargetingKeys', 'adTemplate', 'rendererUrl', 'type' diff --git a/src/native.js b/src/native.js index 48ff409e2d3..00d79050eea 100644 --- a/src/native.js +++ b/src/native.js @@ -344,6 +344,8 @@ export function getNativeTargeting(bid, {index = auctionManager.index} = {}) { let keyValues = {}; const adUnit = index.getAdUnit(bid); + const globalSendTargetingKeys = adUnit?.nativeParams?.ortb == null && adUnit?.nativeParams?.sendTargetingKeys !== false; + const nativeKeys = getNativeKeys(adUnit); const flatBidNativeKeys = { ...bid.native, ...bid.native.ext }; @@ -366,6 +368,17 @@ export function getNativeTargeting(bid, {index = auctionManager.index} = {}) { const placeholder = `${key}:${bid.adId}`; value = placeholder; } + + let assetSendTargetingKeys = adUnit?.nativeParams?.[asset]?.sendTargetingKeys; + if (typeof assetSendTargetingKeys !== 'boolean') { + assetSendTargetingKeys = adUnit?.nativeParams?.ext?.[asset]?.sendTargetingKeys; + } + + const sendTargeting = typeof assetSendTargetingKeys === 'boolean' ? assetSendTargetingKeys : globalSendTargetingKeys; + + if (sendTargeting) { + keyValues[key] = value; + } }); return keyValues; diff --git a/src/prebid.js b/src/prebid.js index 73ba49301f7..a3a72ce75fc 100644 --- a/src/prebid.js +++ b/src/prebid.js @@ -220,6 +220,14 @@ function validateNativeMediaType(adUnit) { delete validatedAdUnit.mediaTypes.native; return validatedAdUnit; } + function checkDeprecated(onDeprecated) { + for (const key of ['sendTargetingKeys', 'types']) { + if (native.hasOwnProperty(key)) { + const res = onDeprecated(key); + if (res) return res; + } + } + } const validatedAdUnit = deepClone(adUnit); const native = validatedAdUnit.mediaTypes.native; // if native assets are specified in OpenRTB format, remove legacy assets and print a warn. @@ -227,6 +235,9 @@ function validateNativeMediaType(adUnit) { if (native.ortb.assets?.some(asset => !isNumber(asset.id) || asset.id < 0 || asset.id % 1 !== 0)) { return err('native asset ID must be a nonnegative integer'); } + if (checkDeprecated(key => err(`ORTB native requests cannot specify "${key}"`))) { + return validatedAdUnit; + } const legacyNativeKeys = Object.keys(NATIVE_KEYS).filter(key => NATIVE_KEYS[key].includes('hb_native_')); const nativeKeys = Object.keys(native); const intersection = nativeKeys.filter(nativeKey => legacyNativeKeys.includes(nativeKey)); @@ -234,6 +245,8 @@ function validateNativeMediaType(adUnit) { logError(`when using native OpenRTB format, you cannot use legacy native properties. Deleting ${intersection} keys from request.`); intersection.forEach(legacyKey => delete validatedAdUnit.mediaTypes.native[legacyKey]); } + } else { + checkDeprecated(key => `mediaTypes.native.${key} is deprecated, consider using native ORTB instead`, adUnit); } if (native.image && native.image.sizes && !Array.isArray(native.image.sizes)) { logError('Please use an array of sizes for native.image.sizes field. Removing invalid mediaTypes.native.image.sizes property from request.'); diff --git a/test/spec/modules/adagioBidAdapter_spec.js b/test/spec/modules/adagioBidAdapter_spec.js index ba47463bbff..106dbd0e082 100644 --- a/test/spec/modules/adagioBidAdapter_spec.js +++ b/test/spec/modules/adagioBidAdapter_spec.js @@ -1444,6 +1444,8 @@ describe('Adagio bid adapter', () => { const bidRequestNative = utils.deepClone(bidRequest) bidRequestNative.nativeParams = { + sendTargetingKeys: false, + clickUrl: { required: true, }, diff --git a/test/spec/modules/admaticBidAdapter_spec.js b/test/spec/modules/admaticBidAdapter_spec.js index ad8586d4e9d..da4329b6405 100644 --- a/test/spec/modules/admaticBidAdapter_spec.js +++ b/test/spec/modules/admaticBidAdapter_spec.js @@ -1046,6 +1046,7 @@ describe('admaticBidAdapter', () => { ], 'type': 'native', 'mediatype': { + 'sendTargetingKeys': false, 'ortb': { 'ver': '1.1', 'context': 2, diff --git a/test/spec/modules/adxcgBidAdapter_spec.js b/test/spec/modules/adxcgBidAdapter_spec.js index 221f5c036fa..14b399eaad4 100644 --- a/test/spec/modules/adxcgBidAdapter_spec.js +++ b/test/spec/modules/adxcgBidAdapter_spec.js @@ -145,6 +145,7 @@ describe('adxcg v8 oRtbConverter Adapter Tests', function () { bidId: 'bid12345', mediaTypes: { native: { + sendTargetingKeys: false, ortb: nativeOrtbRequest } }, diff --git a/test/spec/modules/criteoBidAdapter_spec.js b/test/spec/modules/criteoBidAdapter_spec.js index 2f1100b9152..08ef7e7e877 100644 --- a/test/spec/modules/criteoBidAdapter_spec.js +++ b/test/spec/modules/criteoBidAdapter_spec.js @@ -2431,6 +2431,116 @@ describe('The Criteo bidding adapter', function () { }); } + if (FEATURES.NATIVE) { + it('should warn only once if sendTargetingKeys set to true on required fields for native bidRequest', async () => { + const bidRequests = [ + { + bidder: 'criteo', + adUnitCode: 'bid-123', + mediaTypes: { + native: {} + }, + nativeOrtbRequest: { + assets: [{ + required: 1, + id: 1, + img: { + type: 3, + wmin: 100, + hmin: 100, + } + }] + }, + transactionId: 'transaction-123', + sizes: [[728, 90]], + params: { + zoneId: 123, + publisherSubId: '123' + }, + }, + { + bidder: 'criteo', + adUnitCode: 'bid-456', + mediaTypes: { + native: {} + }, + nativeOrtbRequest: { + assets: [{ + required: 1, + id: 1, + img: { + type: 3, + wmin: 100, + hmin: 100, + } + }] + }, + transactionId: 'transaction-456', + sizes: [[728, 90]], + params: { + zoneId: 456, + publisherSubId: '456' + }, + }, + ]; + + const nativeParamsWithSendTargetingKeys = [ + { + nativeParams: { + image: { + sendTargetingKeys: true + }, + } + }, + { + nativeParams: { + icon: { + sendTargetingKeys: true + }, + } + }, + { + nativeParams: { + clickUrl: { + sendTargetingKeys: true + }, + } + }, + { + nativeParams: { + displayUrl: { + sendTargetingKeys: true + }, + } + }, + { + nativeParams: { + privacyLink: { + sendTargetingKeys: true + }, + } + }, + { + nativeParams: { + privacyIcon: { + sendTargetingKeys: true + }, + } + } + ]; + + utilsMock.expects('logWarn') + .withArgs('Criteo: all native assets containing URL should be sent as placeholders with sendId(icon, image, clickUrl, displayUrl, privacyLink, privacyIcon)') + .exactly(nativeParamsWithSendTargetingKeys.length * bidRequests.length); + for (const nativeParams of nativeParamsWithSendTargetingKeys) { + let transformedBidRequests = {...bidRequests}; + transformedBidRequests = [Object.assign(transformedBidRequests[0], nativeParams), Object.assign(transformedBidRequests[1], nativeParams)]; + spec.buildRequests(transformedBidRequests, await addFPDToBidderRequest(bidderRequest)); + } + utilsMock.verify(); + }); + } + it('should properly parse a bid response with FLEDGE auction configs', async function () { let auctionConfig1 = { auctionSignals: {}, diff --git a/test/spec/modules/inmobiBidAdapter_spec.js b/test/spec/modules/inmobiBidAdapter_spec.js index 3a762446614..7f5c363b0dc 100644 --- a/test/spec/modules/inmobiBidAdapter_spec.js +++ b/test/spec/modules/inmobiBidAdapter_spec.js @@ -1877,6 +1877,7 @@ describe('The inmobi bidding adapter', function () { required: true, sizes: [120, 60], sendId: true, + sendTargetingKeys: false } } } @@ -1920,6 +1921,7 @@ describe('The inmobi bidding adapter', function () { required: true, sizes: [120, 60], sendId: true, + sendTargetingKeys: false } } } diff --git a/test/spec/modules/pulsepointBidAdapter_spec.js b/test/spec/modules/pulsepointBidAdapter_spec.js index 818acea7791..81b6b1b20ca 100644 --- a/test/spec/modules/pulsepointBidAdapter_spec.js +++ b/test/spec/modules/pulsepointBidAdapter_spec.js @@ -65,6 +65,7 @@ describe('PulsePoint Adapter Tests', function () { bidId: 'bid12345', mediaTypes: { native: { + sendTargetingKeys: false, ortb: nativeOrtbRequest } }, diff --git a/test/spec/modules/ringieraxelspringerBidAdapter_spec.js b/test/spec/modules/ringieraxelspringerBidAdapter_spec.js index a68d51a9456..3539dad9362 100644 --- a/test/spec/modules/ringieraxelspringerBidAdapter_spec.js +++ b/test/spec/modules/ringieraxelspringerBidAdapter_spec.js @@ -545,6 +545,7 @@ describe('ringieraxelspringerBidAdapter', function () { privacy: '//dsa.url' }; const expectedTeaserStandardResponse = { + sendTargetingKeys: false, title: 'Headline', image: { url: '//img.url', @@ -632,6 +633,7 @@ describe('ringieraxelspringerBidAdapter', function () { privacy: '//dsa.url', }; const expectedNativeInFeedResponse = { + sendTargetingKeys: false, title: 'Headline', image: { url: '//img.url', diff --git a/test/spec/modules/smilewantedBidAdapter_spec.js b/test/spec/modules/smilewantedBidAdapter_spec.js index 7c1e007c6a7..8063c1d7abe 100644 --- a/test/spec/modules/smilewantedBidAdapter_spec.js +++ b/test/spec/modules/smilewantedBidAdapter_spec.js @@ -238,6 +238,7 @@ const NATIVE_REQUEST = [{ ], mediaTypes: { native: { + sendTargetingKeys: false, title: { required: true, len: 140 diff --git a/test/spec/native_spec.js b/test/spec/native_spec.js index f531a6ac773..9b905d66ef9 100644 --- a/test/spec/native_spec.js +++ b/test/spec/native_spec.js @@ -201,6 +201,16 @@ describe('native.js', function () { sandbox.restore(); }); + it('gets native targeting keys', function () { + const targeting = getNativeTargeting(bid); + expect(targeting[NATIVE_KEYS.title]).to.equal(bid.native.title); + expect(targeting[NATIVE_KEYS.body]).to.equal(bid.native.body); + expect(targeting[NATIVE_KEYS.clickUrl]).to.equal( + bid.native.clickUrl + ); + expect(targeting.hb_native_foo).to.equal(bid.native.foo); + }); + it('does not include targeting keys if request is ortb', () => { const targeting = getNativeTargeting(bid, deps({ adUnitId: bid.adUnitId, @@ -218,6 +228,150 @@ describe('native.js', function () { expect(targeting.hb_native_displayurl).to.not.be.ok; }) + it('sends placeholders for configured assets', function () { + const adUnit = { + adUnitId: 'au', + nativeParams: { + body: { sendId: true }, + clickUrl: { sendId: true }, + ext: { + foo: { + sendId: false, + }, + baz: { + sendId: true, + }, + }, + }, + }; + const targeting = getNativeTargeting(bid, deps(adUnit)); + + expect(targeting[NATIVE_KEYS.title]).to.equal(bid.native.title); + expect(targeting[NATIVE_KEYS.body]).to.equal( + 'hb_native_body:123' + ); + expect(targeting[NATIVE_KEYS.clickUrl]).to.equal( + 'hb_native_linkurl:123' + ); + expect(targeting.hb_native_foo).to.equal(bid.native.ext.foo); + expect(targeting.hb_native_baz).to.equal('hb_native_baz:123'); + }); + + it('sends placeholdes targetings with ortb native response', function () { + const targeting = getNativeTargeting(completeNativeBid); + + expect(targeting[NATIVE_KEYS.title]).to.equal('Native Creative'); + expect(targeting[NATIVE_KEYS.body]).to.equal('Cool description great stuff'); + expect(targeting[NATIVE_KEYS.clickUrl]).to.equal('https://www.link.example'); + }); + + it('should only include native targeting keys with values', function () { + const adUnit = { + adUnitId: 'au', + nativeParams: { + body: { sendId: true }, + clickUrl: { sendId: true }, + ext: { + foo: { + required: false, + }, + baz: { + required: false, + }, + }, + }, + }; + + const targeting = getNativeTargeting(bidWithUndefinedFields, deps(adUnit)); + + expect(Object.keys(targeting)).to.deep.equal([ + NATIVE_KEYS.title, + NATIVE_KEYS.sponsoredBy, + NATIVE_KEYS.clickUrl, + 'hb_native_foo', + ]); + }); + + it('should only include targeting that has sendTargetingKeys set to true', function () { + const adUnit = { + adUnitId: 'au', + nativeParams: { + image: { + required: true, + sizes: [150, 50], + }, + title: { + required: true, + len: 80, + sendTargetingKeys: true, + }, + sendTargetingKeys: false, + }, + }; + const targeting = getNativeTargeting(bid, deps(adUnit)); + + expect(Object.keys(targeting)).to.deep.equal([NATIVE_KEYS.title]); + }); + + it('should only include targeting if sendTargetingKeys not set to false', function () { + const adUnit = { + adUnitId: 'au', + nativeParams: { + image: { + required: true, + sizes: [150, 50], + }, + title: { + required: true, + len: 80, + }, + body: { + required: true, + }, + clickUrl: { + required: true, + }, + icon: { + required: false, + sendTargetingKeys: false, + }, + cta: { + required: false, + sendTargetingKeys: false, + }, + sponsoredBy: { + required: false, + sendTargetingKeys: false, + }, + privacyLink: { + required: false, + sendTargetingKeys: false, + }, + ext: { + foo: { + required: false, + sendTargetingKeys: true, + }, + }, + }, + }; + const targeting = getNativeTargeting(bid, deps(adUnit)); + + expect(Object.keys(targeting)).to.deep.equal([ + NATIVE_KEYS.title, + NATIVE_KEYS.body, + NATIVE_KEYS.image, + NATIVE_KEYS.clickUrl, + 'hb_native_foo', + ]); + }); + + it('should include rendererUrl in targeting', function () { + const rendererUrl = 'https://www.renderer.com/'; + const targeting = getNativeTargeting({...bid, native: {...bid.native, rendererUrl: {url: rendererUrl}}}, deps({})); + expect(targeting[NATIVE_KEYS.rendererUrl]).to.eql(rendererUrl); + }); + it('fires impression trackers', function () { fireNativeTrackers({}, bid); sinon.assert.calledOnce(triggerPixelStub); diff --git a/test/spec/unit/pbjs_api_spec.js b/test/spec/unit/pbjs_api_spec.js index bfd61dc6ecc..67494fedc86 100644 --- a/test/spec/unit/pbjs_api_spec.js +++ b/test/spec/unit/pbjs_api_spec.js @@ -2742,7 +2742,27 @@ describe('Unit: Prebid Module', function () { expect(auctionArgs.adUnits[0].bids.length).to.equal(0); }); }); + + ['sendTargetingKeys', 'types'].forEach(key => { + it(`should reject native that includes both ortb and ${key}`, async () => { + const adUnit = { + code: 'au', + mediaTypes: { + native: { + ortb: {}, + [key]: {} + } + }, + bids: [{bidder: 'appnexus'}] + }; + await runAuction({ + adUnits: [adUnit] + }); + expect(auctionArgs.adUnits[0].bids.length).to.equal(0); + }) + }); } + it('should throw error message and remove adUnit if adUnit.bids is not defined correctly', async function () { const adUnits = [{ code: 'ad-unit-1', From 3101d0f062eba6dd6fd1a0108aa2f9d09d44c1c5 Mon Sep 17 00:00:00 2001 From: Demetrio Girardi Date: Thu, 12 Jun 2025 04:13:35 -0700 Subject: [PATCH 73/92] Typescript support (#12879) * Add more eslint exceptions * Add some more eslint exceptions * Add some lint exceptions and fix some really weird ones * Update package-lock.json * Update .nvmrc and package-lock.json * ts compilation * tsc type check only * do not lint ts files (for now) * run type checker on build * WIP: rework build system with precompilation step * auto-precompile on watch * fix module detection * fix build picking up unprecompiled files * Fix library discovery * Rename prebid.js -> prebid.ts * transform ts imports into js imports * Undo linter & gitignore changes * enable linting of ts * fix linter: 33across * fix ts linter * extract common build params * WIP: public API * Export typed files * build flag types * export PrebidJS type * update CommandQueue * WIP: public API * hooks * improve addApiMethod * Type async/sync hooks * bidfactory * bidderfactory * WIP: bid response flow * Metrics * WIP: bid response flow * update public API * Copy d.ts files during precompilation * fix broken .js imports * Update validateImports to allow .js instead of .ts * prefer .js imports * video.ts * video bid validation * reorganize NamedHooks * rearrange BannerBidProperties * update bid fields * native.ts * Native response properties * separate properties for BidResponse and Bid * remove some noise * Fix ortb prop * auction.ts * WIP: bid response flow * Core handling of Bid objects * Fix lint * Minor improvements * fix source maps * export more types * Add some docs * Small improvements * videoCache.ts * currency.ts * bid response flow: currency * ORTB dchain * Fix build of ts modules * dchain typing * currency doc comment * bid response flow: multibid * bid response flow: bidfloor * type some Bid-related public methods * comment cleanup * fix lint * Move webpack chunks into dist/chunks * Put precompiled sources in dist/src * include package.json in dist/src * events.ts * event typing * use typings from @hogekai * fix package exports * add typing for instl * Allow extensionless import of modules * remove incomplete ortb type * adUnits.ts * refactor count logic * adunits: banner * video media type * include ORTB params for banner * mediaTypes: native * use types in native logic * Ad unit type * Always log public API invocation * Use types in adunit validation * type requestBids * fix references to global.requestBids * fix type of getHighestUnusedBidResponseForAdUnitCode * Fix type of timedAuctionHook * fix type of bidsBackHandler * fix hook types * fix type of bidsBackHandler (again) * Use DeepPartial for ortb2Imp * promise.ts * remove package.json TODO * type deepClone * improve promise types * wait for DOM to load before starting tests * videoModule/index.ts * type videoModule * type adUnit.video * type ortb2Fragments * Fix lint * type renderAd * type clearAllAuctions * remove log assertions from tests * Fix ignored type declarations * adapterManager.ts * Add some comments on build logic * distinct AdUnitBid and AdUnitBidDefinition * improve native types * initial BidRequest type * fix raynRtd tests * Less verbose type casts * Less verbose type casts * use Size type from priceFloors * BidderRequest * refererDetection.ts * fix lint * consent data * Remove odd isArray tests * type getNoBids * make banner sizes optional * targeting.ts * use ByAdUnit in prebid.ts * update auction types * Local type definitions * Include GPT types * fix lint * Default targeting * public API targeting methods * Fix lint * more public API methods * Update bid types * Update consent meta types * aliasBidder * processQueue * triggerBilling * Update RequestBidsResult * discover of video cache props * WIP AnalyticsAdapter.ts * WIP: events * PBS events * Video events * Fix psb new invocation * events public APIs * isArrayOfNums * Fix plcmt / playbackmethod * Normalize w/h and playerSize * update isArrayOfNums * Update PBS events * Allow more than one argument in event typedefs * ajax.ts * BIDDER_DONE, BIDDER_ERROR and BEFORE_BIDDER_HTTP * start of bidder spec api * more bidder spec * BEFORE_REQUEST_BIDS, BID_REQUESTED, SET_TARGETING * event descriptions * config.ts * all of core, except debugging * public config APIs * storageManager.ts * userId * sharedId * allow import type * Fix circular import * Adjust multibid config * consentManagement: TCF and USP * consentManagement: gpp * tcfControl * mspa * minor adjustment to currency * priceFloors * add bid request userId fields * Fix default field type * gptPreAuction * Fix floors auctionDelay * fix lint * schain * enableAnalytics / registerAnalyticsAdapter * fix lint * fix installed modules detection for ts modules * michaoBidAdapter * registerBidAdapter * fix michao tests * fix type of ortbConverter overrides * Fix s2sConfigName type * more pleading to the autocompleter * Reduce use of any as type param specifier * ortbConverter context type improvement * Remove unnecessary linter exceptions * split precompilation logic * public versions of modules * Include all types in core when importing through npm * make complete types available under prebid.d.ts * export more types * do not include summaries in summaries * prebidServerBidAdapter * export event type * rename pure type files * Fix watch task * global def & README updates * Update global definition * try to reduce linter noise * firstPartyData config * update README * extract userId types * Improve userId types * rename SerializedId to SerializableId * RTD & geolocation * remove more redundant comments * bidderSettings * update package-lock * Fix jsdoc warnings * export types from modules * Update src/adUnits.ts Co-authored-by: Muki Seiler * Update modules/consentManagementTcf.ts Co-authored-by: Muki Seiler * Update modules/schain.ts Co-authored-by: Muki Seiler * Update modules/consentManagementTcf.ts Co-authored-by: Muki Seiler * Fix lint --------- Co-authored-by: Nepomuk Seiler Co-authored-by: Muki Seiler --- .devcontainer/devcontainer.json | 4 +- README.md | 83 +- babelConfig.js | 7 +- eslint.config.js | 88 +- gulp.precompilation.js | 164 + gulpHelpers.js | 58 +- gulpfile.js | 74 +- .../gpt/x-domain/creative.html | 2 +- karma.conf.maker.js | 28 +- ...nalyticsAdapter.js => AnalyticsAdapter.ts} | 67 +- .../{cmUtils.js => cmUtils.ts} | 56 +- .../creative-renderer-display/renderer.js | 2 +- .../creative-renderer-native/renderer.js | 2 +- libraries/domainOverrideToRootDomain/index.js | 2 +- libraries/ortbConverter/converter.js | 136 - libraries/ortbConverter/converter.ts | 246 ++ .../constants/{constants.js => constants.ts} | 2 +- libraries/video/constants/events.js | 98 - libraries/video/constants/events.ts | 110 + .../{vendorCodes.js => vendorCodes.ts} | 2 + libraries/video/shared/parentModule.js | 6 +- modules/adpod.js | 2 - ...nagementGpp.js => consentManagementGpp.ts} | 40 +- ...nagementTcf.js => consentManagementTcf.ts} | 79 +- ...nagementUsp.js => consentManagementUsp.ts} | 35 +- modules/{currency.js => currency.ts} | 128 +- modules/{dchain.js => dchain.ts} | 17 +- modules/debugging/bidInterceptor.js | 24 +- ...sAdapter.js => genericAnalyticsAdapter.ts} | 78 +- modules/geolocationRtdProvider.js | 65 - modules/geolocationRtdProvider.ts | 79 + modules/goldfishAdsRtdProvider.js | 28 +- ...rol_usstates.js => gppControl_usstates.ts} | 62 +- .../{gptPreAuction.js => gptPreAuction.ts} | 40 +- ...ichaoBidAdapter.js => michaoBidAdapter.ts} | 25 +- modules/multibid/{index.js => index.ts} | 48 +- modules/paapi.js | 7 +- .../{index.js => index.ts} | 240 +- modules/{priceFloors.js => priceFloors.ts} | 214 +- modules/pubxBidAdapter.js | 12 +- modules/rtdModule/{index.js => index.ts} | 194 +- modules/rtdModule/spec.ts | 64 + modules/{schain.js => schain.ts} | 11 + .../{sharedIdSystem.js => sharedIdSystem.ts} | 80 +- modules/sizeMappingV2.js | 14 +- modules/{tcfControl.js => tcfControl.ts} | 101 +- modules/userId/eids.js | 7 - modules/userId/{index.js => index.ts} | 375 +- modules/userId/spec.ts | 185 + .../{index.js => index.ts} | 6 + modules/videoModule/{index.js => index.ts} | 114 +- package-lock.json | 3530 +++++++++-------- package.json | 30 +- plugins/buildOptions.js | 40 + plugins/eslint/index.js | 4 +- plugins/eslint/validateImports.js | 49 +- plugins/pbjsGlobals.js | 36 +- public/README.md | 4 + src/Renderer.js | 4 + src/activities/{modules.js => modules.ts} | 5 + src/activities/{redactor.js => redactor.ts} | 10 + src/activities/rules.js | 4 + src/{adRendering.js => adRendering.ts} | 103 +- src/adUnits.js | 94 - src/adUnits.ts | 209 + src/adapterManager.js | 771 ---- src/adapterManager.ts | 940 +++++ .../{bidderFactory.js => bidderFactory.ts} | 293 +- src/{ajax.js => ajax.ts} | 74 +- src/{auction.js => auction.ts} | 420 +- src/auctionIndex.js | 10 +- src/auctionManager.js | 11 +- src/banner.js | 21 - src/banner.ts | 48 + src/{bidTTL.js => bidTTL.ts} | 19 + src/bidderSettings.js | 68 - src/bidderSettings.ts | 121 + src/bidfactory.js | 53 - src/bidfactory.ts | 214 + src/{config.js => config.ts} | 126 +- src/{consentHandler.js => consentHandler.ts} | 116 +- src/{constants.js => constants.ts} | 48 +- ...pmBucketManager.js => cpmBucketManager.ts} | 12 +- src/events.js | 173 - src/events.ts | 198 + src/fpd/{enrichment.js => enrichment.ts} | 18 +- src/hook.js | 80 - src/hook.ts | 120 + src/mediaTypes.js | 22 - src/mediaTypes.ts | 41 + src/{native.js => native.ts} | 178 +- src/{pbjsORTB.js => pbjsORTB.ts} | 2 +- src/prebid.js | 1145 ------ src/prebid.public.js | 1 - src/prebid.public.ts | 7 + src/prebid.ts | 1190 ++++++ src/prebidGlobal.js | 21 - src/prebidGlobal.ts | 44 + ...efererDetection.js => refererDetection.ts} | 128 +- src/{storageManager.js => storageManager.ts} | 52 +- src/{targeting.js => targeting.ts} | 536 +-- src/types/common.d.ts | 45 + src/types/functions.d.ts | 2 + src/types/local/buildOptions.d.ts | 6 + src/types/local/gpt.d.ts | 7 + src/types/local/shim.d.ts | 10 + src/types/objects.d.ts | 27 + src/types/ortb/common.d.ts | 5 + src/types/ortb/ext/dchain.d.ts | 78 + src/types/ortb/ext/dsa.d.ts | 60 + src/types/ortb/native.d.ts | 6 + src/types/ortb/request.d.ts | 33 + src/types/ortb/response.d.ts | 18 + src/types/ortb2.d.ts | 59 - src/types/summary/core.d.ts | 1 + src/types/summary/exports.d.ts | 10 + src/types/summary/global.d.ts | 1 + src/types/summary/modules.d.ts | 1 + src/types/summary/types.d.ts | 6 + src/types/tuples.d.ts | 17 + src/{userSync.js => userSync.ts} | 45 +- src/utils.js | 92 +- src/utils/cpm.js | 1 - src/utils/focusTimeout.js | 2 +- src/utils/objects.ts | 70 + src/utils/perfMetrics.js | 387 -- src/utils/perfMetrics.ts | 396 ++ src/utils/prerendering.js | 22 - src/utils/prerendering.ts | 22 + src/utils/promise.js | 31 - src/utils/promise.ts | 55 + .../{ttlCollection.js => ttlCollection.ts} | 60 +- src/video.js | 142 - src/video.ts | 187 + src/{videoCache.js => videoCache.ts} | 131 +- .../modules/33acrossAnalyticsAdapter_spec.js | 1 - .../spec/modules/consentManagementUsp_spec.js | 16 +- test/spec/modules/criteoBidAdapter_spec.js | 10 +- test/spec/modules/euidIdSystem_spec.js | 4 +- test/spec/modules/instreamTracking_spec.js | 2 +- test/spec/modules/mediakeysBidAdapter_spec.js | 9 - .../modules/mediasniperBidAdapter_spec.js | 8 - test/spec/modules/michaoBidAdapter_spec.js | 6 +- .../modules/prebidServerBidAdapter_spec.js | 8 +- test/spec/modules/raynRtdProvider_spec.js | 41 +- test/spec/modules/tcfControl_spec.js | 4 +- test/spec/modules/uid2IdSystem_spec.js | 4 +- test/spec/modules/userId_spec.js | 12 +- test/spec/modules/videoModule/pbVideo_spec.js | 27 +- test/spec/unit/core/adapterManager_spec.js | 7 +- test/spec/unit/pbjs_api_spec.js | 26 +- test/spec/video_spec.js | 65 +- tsconfig.json | 29 + webpack.conf.js | 57 +- webpack.creative.js | 4 +- webpack.debugging.js | 18 +- 156 files changed, 10280 insertions(+), 7167 deletions(-) create mode 100644 gulp.precompilation.js rename libraries/analyticsAdapter/{AnalyticsAdapter.js => AnalyticsAdapter.ts} (70%) rename libraries/consentManagement/{cmUtils.js => cmUtils.ts} (81%) delete mode 100644 libraries/ortbConverter/converter.js create mode 100644 libraries/ortbConverter/converter.ts rename libraries/video/constants/{constants.js => constants.ts} (60%) delete mode 100644 libraries/video/constants/events.js create mode 100644 libraries/video/constants/events.ts rename libraries/video/constants/{vendorCodes.js => vendorCodes.ts} (78%) rename modules/{consentManagementGpp.js => consentManagementGpp.ts} (87%) rename modules/{consentManagementTcf.js => consentManagementTcf.ts} (70%) rename modules/{consentManagementUsp.js => consentManagementUsp.ts} (91%) rename modules/{currency.js => currency.ts} (80%) rename modules/{dchain.js => dchain.ts} (92%) rename modules/{genericAnalyticsAdapter.js => genericAnalyticsAdapter.ts} (64%) delete mode 100644 modules/geolocationRtdProvider.js create mode 100644 modules/geolocationRtdProvider.ts rename modules/{gppControl_usstates.js => gppControl_usstates.ts} (68%) rename modules/{gptPreAuction.js => gptPreAuction.ts} (81%) rename modules/{michaoBidAdapter.js => michaoBidAdapter.ts} (92%) rename modules/multibid/{index.js => index.ts} (87%) rename modules/prebidServerBidAdapter/{index.js => index.ts} (74%) rename modules/{priceFloors.js => priceFloors.ts} (83%) rename modules/rtdModule/{index.js => index.ts} (71%) create mode 100644 modules/rtdModule/spec.ts rename modules/{schain.js => schain.ts} (90%) rename modules/{sharedIdSystem.js => sharedIdSystem.ts} (76%) rename modules/{tcfControl.js => tcfControl.ts} (82%) rename modules/userId/{index.js => index.ts} (80%) create mode 100644 modules/userId/spec.ts rename modules/validationFpdModule/{index.js => index.ts} (98%) rename modules/videoModule/{index.js => index.ts} (71%) create mode 100644 plugins/buildOptions.js create mode 100644 public/README.md rename src/activities/{modules.js => modules.ts} (55%) rename src/activities/{redactor.js => redactor.ts} (93%) rename src/{adRendering.js => adRendering.ts} (82%) delete mode 100644 src/adUnits.js create mode 100644 src/adUnits.ts delete mode 100644 src/adapterManager.js create mode 100644 src/adapterManager.ts rename src/adapters/{bidderFactory.js => bidderFactory.ts} (71%) rename src/{ajax.js => ajax.ts} (72%) rename src/{auction.js => auction.ts} (74%) delete mode 100644 src/banner.js create mode 100644 src/banner.ts rename src/{bidTTL.js => bidTTL.ts} (52%) delete mode 100644 src/bidderSettings.js create mode 100644 src/bidderSettings.ts delete mode 100644 src/bidfactory.js create mode 100644 src/bidfactory.ts rename src/{config.js => config.ts} (77%) rename src/{consentHandler.js => consentHandler.ts} (59%) rename src/{constants.js => constants.ts} (90%) rename src/{cpmBucketManager.js => cpmBucketManager.ts} (94%) delete mode 100644 src/events.js create mode 100644 src/events.ts rename src/fpd/{enrichment.js => enrichment.ts} (91%) delete mode 100644 src/hook.js create mode 100644 src/hook.ts delete mode 100644 src/mediaTypes.js create mode 100644 src/mediaTypes.ts rename src/{native.js => native.ts} (85%) rename src/{pbjsORTB.js => pbjsORTB.ts} (97%) delete mode 100644 src/prebid.js delete mode 100644 src/prebid.public.js create mode 100644 src/prebid.public.ts create mode 100644 src/prebid.ts delete mode 100644 src/prebidGlobal.js create mode 100644 src/prebidGlobal.ts rename src/{refererDetection.js => refererDetection.ts} (66%) rename src/{storageManager.js => storageManager.ts} (81%) rename src/{targeting.js => targeting.ts} (59%) create mode 100644 src/types/common.d.ts create mode 100644 src/types/functions.d.ts create mode 100644 src/types/local/buildOptions.d.ts create mode 100644 src/types/local/gpt.d.ts create mode 100644 src/types/local/shim.d.ts create mode 100644 src/types/objects.d.ts create mode 100644 src/types/ortb/common.d.ts create mode 100644 src/types/ortb/ext/dchain.d.ts create mode 100644 src/types/ortb/ext/dsa.d.ts create mode 100644 src/types/ortb/native.d.ts create mode 100644 src/types/ortb/request.d.ts create mode 100644 src/types/ortb/response.d.ts delete mode 100644 src/types/ortb2.d.ts create mode 100644 src/types/summary/core.d.ts create mode 100644 src/types/summary/exports.d.ts create mode 100644 src/types/summary/global.d.ts create mode 100644 src/types/summary/modules.d.ts create mode 100644 src/types/summary/types.d.ts create mode 100644 src/types/tuples.d.ts rename src/{userSync.js => userSync.ts} (88%) create mode 100644 src/utils/objects.ts delete mode 100644 src/utils/perfMetrics.js create mode 100644 src/utils/perfMetrics.ts delete mode 100644 src/utils/prerendering.js create mode 100644 src/utils/prerendering.ts delete mode 100644 src/utils/promise.js create mode 100644 src/utils/promise.ts rename src/utils/{ttlCollection.js => ttlCollection.ts} (62%) delete mode 100644 src/video.js create mode 100644 src/video.ts rename src/{videoCache.js => videoCache.ts} (65%) create mode 100644 tsconfig.json diff --git a/.devcontainer/devcontainer.json b/.devcontainer/devcontainer.json index d4c34929569..b74be8ac841 100644 --- a/.devcontainer/devcontainer.json +++ b/.devcontainer/devcontainer.json @@ -5,7 +5,9 @@ "build": { "dockerfile": "Dockerfile", - "args": { "VARIANT": "12" } + "args": { + "VARIANT": "18" + } }, "postCreateCommand": "bash .devcontainer/postCreate.sh", diff --git a/README.md b/README.md index 887c7cde432..539fbf885e9 100644 --- a/README.md +++ b/README.md @@ -24,71 +24,8 @@ Prebid.js is open source software that is offered for free as a convenience. Whi ## Usage (as a npm dependency) -*Note:* Requires Prebid.js v1.38.0+ - -Prebid.js depends on Babel and some Babel Plugins in order to run correctly in the browser. Here are some examples for -configuring webpack to work with Prebid.js. - -With Babel 7: -```javascript -// webpack.conf.js -let path = require('path'); -module.exports = { - mode: 'production', - module: { - rules: [ - - // this rule can be excluded if you don't require babel-loader for your other application files - { - test: /\.m?js$/, - exclude: /node_modules/, - use: { - loader: 'babel-loader', - } - }, - - // this separate rule is required to make sure that the Prebid.js files are babel-ified. this rule will - // override the regular exclusion from above (for being inside node_modules). - { - test: /.js$/, - include: new RegExp(`\\${path.sep}prebid\\.js`), - use: { - loader: 'babel-loader', - // presets and plugins for Prebid.js must be manually specified separate from your other babel rule. - // this can be accomplished by requiring prebid's .babelrc.js file (requires Babel 7 and Node v8.9.0+) - // as of Prebid 6, babelrc.js only targets modern browsers. One can change the targets and build for - // older browsers if they prefer, but integration tests on ie11 were removed in Prebid.js 6.0 - options: require('prebid.js/.babelrc.js') - } - } - ] - } -} -``` - -Or for Babel 6: -```javascript - // you must manually install and specify the presets and plugins yourself - options: { - plugins: [ - "transform-object-assign", // required (for IE support) and "babel-plugin-transform-object-assign" - // must be installed as part of your package. - require('prebid.js/plugins/pbjsGlobals.js') // required! - ], - presets: [ - ["env", { // you can use other presets if you wish. - "targets": { // this example is using "babel-presets-env", which must be installed if you - "browsers": [ // follow this example. - ... // your browser targets. they should probably match the targets you're using for the rest - // of your application - ] - } - }] - ] - } -``` - -Then you can use Prebid.js as any other npm dependency +**Note**: versions prior to v10 required some Babel plugins to be configured when used as an NPM dependency - +refer to [v9 README](https://github.com/prebid/Prebid.js/blob/9.43.0/README.md) ```javascript import pbjs from 'prebid.js'; @@ -99,10 +36,26 @@ pbjs.processQueue(); // required to process existing pbjs.queue blocks and setu pbjs.requestBids({ ... }) +``` + +You can import just type definitions for every module from `types.d.ts`, and for the `pbjs` global from `global.d.ts`: +```typescript +import 'prebid.js/types.d.ts'; +import 'prebid.js/global.d.ts'; +pbjs.que.push(/* ... */) ``` +Or, if your Prebid bundle uses a different global variable name: +```typescript +import type {PrebidJS} from 'prebid.js/types.d.ts'; +declare global { + interface Window { + myCustomPrebidGlobal: PrebidJS; + } +} +``` diff --git a/babelConfig.js b/babelConfig.js index 615405e1c19..2403abff65d 100644 --- a/babelConfig.js +++ b/babelConfig.js @@ -12,13 +12,13 @@ function useLocal(module) { module.exports = function (options = {}) { return { 'presets': [ + useLocal('@babel/preset-typescript'), [ useLocal('@babel/preset-env'), { 'useBuiltIns': 'entry', 'corejs': '3.42.0', - // a lot of tests use sinon.stub & others that stopped working on ES6 modules with webpack 5 - 'modules': options.test ? 'commonjs' : 'auto', + 'modules': false, } ] ], @@ -27,9 +27,6 @@ module.exports = function (options = {}) { [path.resolve(__dirname, './plugins/pbjsGlobals.js'), options], [useLocal('@babel/plugin-transform-runtime')], ]; - if (options.codeCoverage) { - plugins.push([useLocal('babel-plugin-istanbul')]) - } return plugins; })(), } diff --git a/eslint.config.js b/eslint.config.js index 5dd9621d32b..acdc98b4a28 100644 --- a/eslint.config.js +++ b/eslint.config.js @@ -1,19 +1,25 @@ const jsdoc = require('eslint-plugin-jsdoc') const lintImports = require('eslint-plugin-import') const neostandard = require('neostandard') -const babelParser = require('@babel/eslint-parser'); const globals = require('globals'); const prebid = require('./plugins/eslint/index.js'); const {includeIgnoreFile} = require('@eslint/compat'); const path = require('path'); const _ = require('lodash'); +const tseslint = require('typescript-eslint'); +const {getSourceFolders, getIgnoreSources} = require('./gulpHelpers.js'); -function sourcePattern(name) { +function jsPattern(name) { return [`${name}/**/*.js`, `${name}/**/*.mjs`] } -const sources = ['src', 'modules', 'libraries', 'creative'].flatMap(sourcePattern) -const autogen = 'libraries/creative-renderer-*/**/*' +function tsPattern(name) { + return [`${name}/**/*.ts`] +} + +function sourcePattern(name) { + return jsPattern(name).concat(tsPattern(name)); +} const allowedImports = { modules: [ @@ -44,8 +50,28 @@ function noGlobals(names) { } } -function commonConfig(overrides) { - return _.merge({ + +module.exports = [ + includeIgnoreFile(path.resolve(__dirname, '.gitignore')), + { + ignores: [ + ...getIgnoreSources(), + 'integrationExamples/**/*', + // do not lint build-related stuff + '*.js', + ...jsPattern('plugins'), + ...jsPattern('.github'), + ], + }, + jsdoc.configs['flat/recommended'], + ...tseslint.configs.recommended, + ...neostandard({ + files: getSourceFolders().flatMap(jsPattern), + ts: true, + filesTs: getSourceFolders().flatMap(tsPattern) + }), + { + files: getSourceFolders().flatMap(sourcePattern), plugins: { jsdoc, import: lintImports, @@ -59,7 +85,6 @@ function commonConfig(overrides) { } }, languageOptions: { - parser: babelParser, sourceType: 'module', ecmaVersion: 2018, globals: { @@ -132,35 +157,14 @@ function commonConfig(overrides) { '@stylistic/object-property-newline': 'off', } - }, overrides); -} - -module.exports = [ - includeIgnoreFile(path.resolve(__dirname, '.gitignore')), - { - ignores: [ - autogen, - 'integrationExamples/**/*', - // do not lint build-related stuff - '*.js', - ...sourcePattern('plugins'), - ...sourcePattern('.github'), - ], }, - jsdoc.configs['flat/recommended'], - ...neostandard({ - files: sources, - }), - commonConfig({ - files: sources, - }), ...Object.entries(allowedImports).map(([path, allowed]) => { const {globals, props} = noGlobals({ require: 'use import instead', ...Object.fromEntries(['localStorage', 'sessionStorage'].map(k => [k, 'use storageManager instead'])), XMLHttpRequest: 'use ajax.js instead' }) - return commonConfig({ + return { files: sourcePattern(path), plugins: { prebid, @@ -199,7 +203,7 @@ module.exports = [ })) ] } - }) + } }), { files: ['**/*BidAdapter.js'], @@ -214,7 +218,7 @@ module.exports = [ ] } }, - commonConfig({ + { files: sourcePattern('test'), languageOptions: { globals: { @@ -239,7 +243,25 @@ module.exports = [ 'default-case-last': 'off', '@stylistic/no-mixed-spaces-and-tabs': 'off', '@stylistic/no-tabs': 'off', - '@stylistic/no-trailing-spaces': 'error' + '@stylistic/no-trailing-spaces': 'error', + } + }, + { + files: getSourceFolders().flatMap(tsPattern), + rules: { + // turn off no-undef for TS files - type checker does better + 'no-undef': 'off', + '@typescript-eslint/no-explicit-any': 'off' } - }) + }, + { + files: getSourceFolders().flatMap(jsPattern), + rules: { + // turn off typescript rules on js files - just too many violations + '@typescript-eslint/no-unused-vars': 'off', + '@typescript-eslint/no-unused-expressions': 'off', + '@typescript-eslint/no-this-alias': 'off', + '@typescript-eslint/no-require-imports': 'off' + } + }, ] diff --git a/gulp.precompilation.js b/gulp.precompilation.js new file mode 100644 index 00000000000..da4d4d95e35 --- /dev/null +++ b/gulp.precompilation.js @@ -0,0 +1,164 @@ +const gulp = require('gulp'); +const helpers = require('./gulpHelpers.js'); +const {argv} = require('yargs'); +const sourcemaps = require('gulp-sourcemaps'); +const babel = require('gulp-babel'); +const {glob} = require('glob'); +const path = require('path'); +const tap = require('gulp-tap'); +const _ = require('lodash'); +const fs = require('fs'); +const filter = import('gulp-filter'); +const {buildOptions} = require('./plugins/buildOptions.js'); + + +// do not generate more than one task for a given build config - so that `gulp.lastRun` can work properly +const PRECOMP_TASKS = new Map(); + +function babelPrecomp({distUrlBase = null, disableFeatures = null, dev = false} = {}) { + if (dev && distUrlBase == null) { + distUrlBase = argv.distUrlBase || '/build/dev/' + } + const key = `${distUrlBase}::${disableFeatures}`; + if (!PRECOMP_TASKS.has(key)) { + const babelConfig = require('./babelConfig.js')({ + disableFeatures: disableFeatures ?? helpers.getDisabledFeatures(), + prebidDistUrlBase: distUrlBase ?? argv.distUrlBase + }); + const precompile = function () { + // `since: gulp.lastRun(task)` selects files that have been modified since the last time this gulp process ran `task` + return gulp.src(helpers.getSourcePatterns(), {base: '.', since: gulp.lastRun(precompile)}) + .pipe(sourcemaps.init()) + .pipe(babel(babelConfig)) + .pipe(sourcemaps.write('.')) + .pipe(gulp.dest(helpers.getPrecompiledPath())); + } + PRECOMP_TASKS.set(key, precompile) + } + return PRECOMP_TASKS.get(key); +} + + +/** + * .json and .d.ts files are used at runtime, so make them part of the precompilation output + */ +function copyVerbatim() { + return gulp.src(helpers.getSourceFolders().flatMap(name => [ + `${name}/**/*.json`, + `${name}/**/*.d.ts`, + ]).concat([ + './package.json', + '!./src/types/local/**/*' // exclude "local", type definitions that should not be visible to consumers + ]), {base: '.'}) + .pipe(gulp.dest(helpers.getPrecompiledPath())) +} + +/** + * Generate "public" versions of module files (used in package.json "exports") that + * just import the "real" module + * + * This achieves two things: + * + * - removes the need for awkward "index" imports, e.g. userId/index + * - hides their exports from NPM consumers + */ +function generatePublicModules(ext, template) { + const publicDir = helpers.getPrecompiledPath('public'); + + function getNames(file) { + const filePath = path.parse(file.path); + const fileName = filePath.name.replace(/\.d$/gi, ''); + const moduleName = fileName === 'index' ? path.basename(filePath.dir) : fileName; + const publicName = `${moduleName}.${ext}`; + const modulePath = path.relative(publicDir, file.path); + const publicPath = path.join(publicDir, publicName); + return {modulePath, publicPath} + } + + function publicVersionDoesNotExist(file) { + // allow manual definition of a module's public version by leaving it + // alone if it exists under `public` + return !fs.existsSync(getNames(file).publicPath) + } + + return function (done) { + filter.then(({default: filter}) => { + gulp.src([ + helpers.getPrecompiledPath(`modules/*.${ext}`), + helpers.getPrecompiledPath(`modules/**/index.${ext}`), + `!${publicDir}/**/*` + ]) + .pipe(filter(publicVersionDoesNotExist)) + .pipe(tap((file) => { + const {modulePath, publicPath} = getNames(file); + file.contents = Buffer.from(template({modulePath})); + file.path = publicPath; + })) + .pipe(gulp.dest(publicDir)) + .on('end', done); + }) + } +} + +function generateTypeSummary(folder, dest, ignore = dest) { + const template = _.template(`<% _.forEach(files, (file) => { %>import '<%= file %>'; +<% }) %>`); + const destDir = path.parse(dest).dir; + return function (done) { + glob([`${folder}/**/*.d.ts`], {ignore}).then(files => { + files = files.map(file => path.relative(destDir, file)) + if (!fs.existsSync(destDir)) { + fs.mkdirSync(destDir, {recursive: true}); + } + fs.writeFile(dest, template({files}), done); + }) + } +} + +const generateCoreSummary = generateTypeSummary( + helpers.getPrecompiledPath('src'), + helpers.getPrecompiledPath('src/types/summary/core.d.ts'), + helpers.getPrecompiledPath('src/types/summary/**/*') +); +const generateModuleSummary = generateTypeSummary(helpers.getPrecompiledPath('modules'), helpers.getPrecompiledPath('src/types/summary/modules.d.ts')) +const publicModules = gulp.parallel(Object.entries({ + 'js': _.template(`import '<%= modulePath %>';`), + 'd.ts': _.template(`export type * from '<%= modulePath %>'`) +}).map(args => generatePublicModules.apply(null, args))); + + +const globalTemplate = _.template(`<% if (defineGlobal) {%> +import type {PrebidJS} from "../../prebidGlobal.ts"; +declare global { + let <%= pbGlobal %>: PrebidJS; + interface Window { + <%= pbGlobal %>: PrebidJS; + } +}<% } %>`); + +function generateGlobalDef(options) { + return function (done) { + fs.writeFile(helpers.getPrecompiledPath('src/types/summary/global.d.ts'), globalTemplate(buildOptions(options)), done); + } +} + +function precompile(options = {}) { + return gulp.series([ + 'ts', + gulp.parallel([copyVerbatim, babelPrecomp(options)]), + gulp.parallel([publicModules, generateCoreSummary, generateModuleSummary, generateGlobalDef(options)]) + ]); +} + + +gulp.task('ts', helpers.execaTask('tsc')); +gulp.task('transpile', babelPrecomp()); +gulp.task('precompile-dev', precompile({dev: true})); +gulp.task('precompile', precompile()); +gulp.task('verbatim', copyVerbatim) + + +module.exports = { + precompile, + babelPrecomp +} diff --git a/gulpHelpers.js b/gulpHelpers.js index adc43d1edaa..6539c749150 100644 --- a/gulpHelpers.js +++ b/gulpHelpers.js @@ -6,12 +6,25 @@ const MANIFEST = 'package.json'; const through = require('through2'); const _ = require('lodash'); const PluginError = require('plugin-error'); +const execaCmd = require('execa'); const submodules = require('./modules/.submodules.json').parentModules; +const PRECOMPILED_PATH = './dist/src' const MODULE_PATH = './modules'; const BUILD_PATH = './build/dist'; const DEV_PATH = './build/dev'; const ANALYTICS_PATH = '../analytics'; +const SOURCE_FOLDERS = [ + 'src', + 'creative', + 'libraries', + 'modules', + 'test', + 'public' +] +const IGNORE_SOURCES = [ + 'libraries/creative-renderer-*/**/*' +] // get only subdirectories that contain package.json with 'main' property function isModuleDirectory(filePath) { @@ -25,19 +38,19 @@ function isModuleDirectory(filePath) { } module.exports = { + getSourceFolders() { + return SOURCE_FOLDERS + }, + getSourcePatterns() { + return SOURCE_FOLDERS.flatMap(dir => [`./${dir}/**/*.js`, `./${dir}/**/*.ts`]) + }, + getIgnoreSources() { + return IGNORE_SOURCES + }, parseBrowserArgs: function (argv) { return (argv.browsers) ? argv.browsers.split(',') : []; }, - toCapitalCase: function (str) { - return str.charAt(0).toUpperCase() + str.slice(1); - }, - - jsonifyHTML: function (str) { - return str.replace(/\n/g, '') - .replace(/<\//g, '<\\/') - .replace(/\/>/g, '\\/>'); - }, getArgModules() { var modules = (argv.modules || '') .split(',') @@ -73,14 +86,26 @@ module.exports = { try { var absoluteModulePath = path.join(__dirname, MODULE_PATH); internalModules = fs.readdirSync(absoluteModulePath) - .filter(file => (/^[^\.]+(\.js)?$/).test(file)) + .filter(file => (/^[^\.]+(\.js|\.tsx?)?$/).test(file)) .reduce((memo, file) => { - var moduleName = file.split(new RegExp('[.\\' + path.sep + ']'))[0]; + let moduleName = file.split(new RegExp('[.\\' + path.sep + ']'))[0]; var modulePath = path.join(absoluteModulePath, file); + let candidates; if (fs.lstatSync(modulePath).isDirectory()) { - modulePath = path.join(modulePath, 'index.js') + candidates = [ + path.join(modulePath, 'index.js'), + path.join(modulePath, 'index.ts') + ] + } else { + candidates = [modulePath] } - if (fs.existsSync(modulePath)) { + const target = candidates.find(name => fs.existsSync(name)); + if (target) { + modulePath = this.getPrecompiledPath(path.relative(__dirname, path.format({ + ...path.parse(target), + base: null, + ext: '.js' + }))); memo[modulePath] = moduleName; } return memo; @@ -106,6 +131,10 @@ module.exports = { return path.join(__dirname, dev ? DEV_PATH : BUILD_PATH, assetPath) }, + getPrecompiledPath(filePath) { + return path.resolve(filePath ? path.join(PRECOMPILED_PATH, filePath) : PRECOMPILED_PATH) + }, + getBuiltModules: function(dev, externalModules) { var modules = this.getModuleNames(externalModules); if (Array.isArray(externalModules)) { @@ -177,4 +206,7 @@ module.exports = { .map((s) => s.trim()) .filter((s) => s); }, + execaTask(cmd) { + return () => execaCmd.shell(cmd, {stdio: 'inherit'}); + } }; diff --git a/gulpfile.js b/gulpfile.js index 5f4e7f96084..50828d9c9e3 100644 --- a/gulpfile.js +++ b/gulpfile.js @@ -15,6 +15,7 @@ var opens = require('opn'); var webpackConfig = require('./webpack.conf.js'); const standaloneDebuggingConfig = require('./webpack.debugging.js'); var helpers = require('./gulpHelpers.js'); +const execaTask = helpers.execaTask; var concat = require('gulp-concat'); var replace = require('gulp-replace'); const execaCmd = require('execa'); @@ -29,11 +30,6 @@ const Vinyl = require('vinyl'); const wrap = require('gulp-wrap'); const rename = require('gulp-rename'); -function execaTask(cmd) { - return () => execaCmd.shell(cmd, {stdio: 'inherit'}); -} - - var prebid = require('./package.json'); var port = 9999; const INTEG_SERVER_HOST = argv.host ? argv.host : 'localhost'; @@ -41,6 +37,8 @@ const INTEG_SERVER_PORT = 4444; const { spawn, fork } = require('child_process'); const TerserPlugin = require('terser-webpack-plugin'); +const {precompile, babelPrecomp} = require('./gulp.precompilation.js'); + // these modules must be explicitly listed in --modules to be included in the build, won't be part of "all" modules var explicitModules = [ 'pre1api' @@ -150,7 +148,7 @@ function prebidSource(webpackCfg) { const analyticsSources = helpers.getAnalyticsSources(); const moduleSources = helpers.getModulePaths(externalModules); - return gulp.src([].concat(moduleSources, analyticsSources, 'src/prebid.js')) + return gulp.src([].concat(moduleSources, analyticsSources, helpers.getPrecompiledPath('src/prebid.js'))) .pipe(helpers.nameModules(externalModules)) .pipe(webpackStream(webpackCfg, webpack)); } @@ -163,14 +161,6 @@ function makeDevpackPkg(config = webpackConfig) { mode: 'development' }) - const babelConfig = require('./babelConfig.js')({disableFeatures: helpers.getDisabledFeatures(), prebidDistUrlBase: argv.distUrlBase || '/build/dev/'}); - - // update babel config to set local dist url - cloned.module.rules - .flatMap((rule) => rule.use) - .filter((use) => use.loader === 'babel-loader') - .forEach((use) => use.options = Object.assign({}, use.options, babelConfig)); - return prebidSource(cloned) .pipe(gulp.dest('build/dev')); } @@ -331,10 +321,10 @@ function setupDist() { return gulp.src(['build/dist/**/*']) .pipe(rename(function (path) { if (path.dirname === '.' && path.basename === 'prebid') { - path.dirname = 'not-for-prod'; + path.dirname = '../not-for-prod'; } })) - .pipe(gulp.dest('dist')) + .pipe(gulp.dest('dist/chunks')) } // Run the unit tests. @@ -352,8 +342,6 @@ function testTaskMaker(options = {}) { options[opt] = options.hasOwnProperty(opt) ? options[opt] : argv[opt]; }) - options.disableFeatures = options.disableFeatures || helpers.getDisabledFeatures(); - return function test(done) { if (options.notest) { done(); @@ -493,23 +481,23 @@ function watchTaskMaker(options = {}) { options.alsoWatch = options.alsoWatch || []; return function watch(done) { - var mainWatcher = gulp.watch([ - 'src/**/*.js', - 'libraries/**/*.js', - '!libraries/creative-renderer-*/**/*.js', - 'creative/**/*.js', - 'modules/**/*.js', - ].concat(options.alsoWatch)); + gulp.watch(helpers.getSourcePatterns().concat( + helpers.getIgnoreSources().map(src => `!${src}`) + ), babelPrecomp(options)); + gulp.watch([ + helpers.getPrecompiledPath('**/*.js'), + ...helpers.getIgnoreSources().map(src => `!${helpers.getPrecompiledPath(src)}`), + `!${helpers.getPrecompiledPath('test/**/*')}`, + ], options.task()); startLocalServer(); - mainWatcher.on('all', options.task()); done(); } } -const watch = watchTaskMaker({alsoWatch: ['test/**/*.js'], task: () => gulp.series(clean, gulp.parallel(lint, 'build-bundle-dev', test))}); -const watchFast = watchTaskMaker({task: () => gulp.series('build-bundle-dev')}); +const watch = watchTaskMaker({task: () => gulp.series(clean, gulp.parallel(lint, 'build-bundle-dev', test))}); +const watchFast = watchTaskMaker({dev: true, livereload: false, task: () => gulp.series('build-bundle-dev')}); // support tasks gulp.task(lint); @@ -526,36 +514,36 @@ gulp.task('build-bundle-dev', gulp.series('build-creative-dev', makeDevpackPkg(s gulp.task('build-bundle-prod', gulp.series('build-creative-prod', makeWebpackPkg(standaloneDebuggingConfig), makeWebpackPkg(), gulpBundle.bind(null, false))); // build-bundle-verbose - prod bundle except names and comments are preserved. Use this to see the effects // of dead code elimination. -gulp.task('build-bundle-verbose', gulp.series('build-creative-dev', makeWebpackPkg(makeVerbose(standaloneDebuggingConfig)), makeWebpackPkg(makeVerbose()), gulpBundle.bind(null, false))); +gulp.task('build-bundle-verbose', gulp.series(precompile(), 'build-creative-dev', makeWebpackPkg(makeVerbose(standaloneDebuggingConfig)), makeWebpackPkg(makeVerbose()), gulpBundle.bind(null, false))); // public tasks (dependencies are needed for each task since they can be ran on their own) gulp.task('update-browserslist', execaTask('npx update-browserslist-db@latest')); -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('update-browserslist', clean, lint, 'test-all-features-disabled', 'test-only')); +gulp.task('test-only', gulp.series(precompile(), test)); +gulp.task('test-all-features-disabled', gulp.series(precompile({disableFeatures: require('./features.json')}), testTaskMaker({disableFeatures: require('./features.json'), oneBrowser: 'chrome', watch: false}))); +gulp.task('test', gulp.series(clean, lint, 'test-all-features-disabled', 'test-only')); -gulp.task('test-coverage', gulp.series(clean, testCoverage)); +gulp.task('test-coverage', gulp.series(clean, precompile(), testCoverage)); gulp.task(viewCoverage); gulp.task('coveralls', gulp.series('test-coverage', coveralls)); // npm will by default use .gitignore, so create an .npmignore that is a copy of it except it includes "dist" -gulp.task('setup-npmignore', execaTask("sed 's/^\\/\\?dist\\/\\?$//g;w .npmignore' .gitignore")); -gulp.task('build', gulp.series('update-browserslist', clean, 'build-bundle-prod', updateCreativeExample, setupDist)); +gulp.task('setup-npmignore', execaTask("sed 's/^\\/\\?dist\\/\\?$//g;w .npmignore' .gitignore", {quiet: true})); +gulp.task('build', gulp.series(clean, 'update-browserslist', precompile(), 'build-bundle-prod', updateCreativeExample, setupDist)); gulp.task('build-release', gulp.series('build', 'setup-npmignore')); 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-prod', gulp.series(clean, gulp.parallel('build-bundle-prod', startLocalServer))); -gulp.task('serve-and-test', gulp.series(clean, gulp.parallel('build-bundle-dev', watchFast, testTaskMaker({watch: true})))); -gulp.task('serve-e2e', gulp.series(clean, 'build-bundle-prod', gulp.parallel(() => startIntegServer(), startLocalServer))); -gulp.task('serve-e2e-dev', gulp.series(clean, 'build-bundle-dev', gulp.parallel(() => startIntegServer(true), startLocalServer))); +gulp.task('serve', gulp.series(clean, lint, precompile(), gulp.parallel('build-bundle-dev', watch, test))); +gulp.task('serve-fast', gulp.series(clean, precompile({dev: true}), gulp.parallel('build-bundle-dev', watchFast))); +gulp.task('serve-prod', gulp.series(clean, precompile(), gulp.parallel('build-bundle-prod', startLocalServer))); +gulp.task('serve-and-test', gulp.series(clean, precompile({dev: true}), gulp.parallel('build-bundle-dev', watchFast, testTaskMaker({watch: true})))); +gulp.task('serve-e2e', gulp.series(clean, precompile(), 'build-bundle-prod', gulp.parallel(() => startIntegServer(), startLocalServer))); +gulp.task('serve-e2e-dev', gulp.series(clean, precompile(), 'build-bundle-dev', gulp.parallel(() => startIntegServer(true), startLocalServer))); -gulp.task('default', gulp.series(clean, 'build-bundle-prod')); +gulp.task('default', gulp.series('build')); 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())); +gulp.task('e2e-test', gulp.series(requireNodeVersion(16), clean, precompile(), 'build-bundle-prod', e2eTestTaskMaker())); // other tasks gulp.task(bundleToStdout); diff --git a/integrationExamples/gpt/x-domain/creative.html b/integrationExamples/gpt/x-domain/creative.html index d7b97b93baa..d98376d92e3 100644 --- a/integrationExamples/gpt/x-domain/creative.html +++ b/integrationExamples/gpt/x-domain/creative.html @@ -2,7 +2,7 @@ // creative will be rendered, e.g. GAM delivering a SafeFrame // this code is autogenerated, also available in 'build/creative/creative.js' - + - * - * - * If the page's script runs before prebid loads, then their function gets added to the queue, and executed - * by prebid once it's done loading. If it runs after prebid loads, then this monkey-patch causes their - * function to execute immediately. - * - * @param {function} command A function which takes no arguments. This is guaranteed to run exactly once, and only after - * the Prebid script has been fully loaded. - * @alias module:pbjs.cmd.push - * @alias module:pbjs.que.push - */ -function quePush(command) { - if (typeof command === 'function') { - try { - command.call(); - } catch (e) { - logError('Error processing command :', e.message, e.stack); - } - } else { - logError('Commands written into $$PREBID_GLOBAL$$.cmd.push must be wrapped in a function'); - } -} - -function processQueue(queue) { - queue.forEach(function (cmd) { - if (typeof cmd.called === 'undefined') { - try { - cmd.call(); - cmd.called = true; - } catch (e) { - logError('Error processing command :', 'prebid.js', e); - } - } - }); -} - -/** - * @alias module:pbjs.processQueue - */ -pbjsInstance.processQueue = delayIfPrerendering(() => getGlobal().delayPrerendering, function () { - logInfo('Invoking $$PREBID_GLOBAL$$.processQueue', arguments); - pbjsInstance.que.push = pbjsInstance.cmd.push = quePush; - insertLocatorFrame(); - hook.ready(); - processQueue(pbjsInstance.que); - processQueue(pbjsInstance.cmd); -}); - -/** - * @alias module:pbjs.triggerBilling - */ -pbjsInstance.triggerBilling = ({adId, adUnitCode}) => { - logInfo('Invoking $$PREBID_GLOBAL$$.triggerBilling', {adId, adUnitCode}); - auctionManager.getAllWinningBids() - .filter((bid) => bid.adId === adId || (adId == null && bid.adUnitCode === adUnitCode)) - .forEach((bid) => { - adapterManager.triggerBilling(bid); - renderIfDeferred(bid); - }); -}; - -export default pbjsInstance; diff --git a/src/prebid.public.js b/src/prebid.public.js deleted file mode 100644 index f05e671ac24..00000000000 --- a/src/prebid.public.js +++ /dev/null @@ -1 +0,0 @@ -export {default} from './prebid.js'; diff --git a/src/prebid.public.ts b/src/prebid.public.ts new file mode 100644 index 00000000000..3404b32ef4d --- /dev/null +++ b/src/prebid.public.ts @@ -0,0 +1,7 @@ +// a ts file importing types from other ts files picks up type declarations along the way, +// but importing just real code does not. Import an autogenerated list of all type definitions in core +// to get around this problem. +import './types/summary/core.d.ts'; +import './types/summary/global.d.ts'; +export {default} from './prebid.ts'; +export type * from './types/summary/exports.d.ts'; diff --git a/src/prebid.ts b/src/prebid.ts new file mode 100644 index 00000000000..2932589d072 --- /dev/null +++ b/src/prebid.ts @@ -0,0 +1,1190 @@ +/** @module pbjs */ + +import {getGlobal, type PrebidJS} from './prebidGlobal.js'; +import { + deepAccess, + deepClone, + deepEqual, + deepSetValue, + flatten, + generateUUID, + isArray, + isArrayOfNums, + isEmpty, + isFn, + isGptPubadsDefined, + isNumber, + logError, + logInfo, + logMessage, + logWarn, + mergeDeep, + transformAdServerTargetingObj, + uniques, + unsupportedBidderMessage +} from './utils.js'; +import {listenMessagesFromCreative} from './secureCreatives.js'; +import {userSync} from './userSync.js'; +import {config} from './config.js'; +import {auctionManager} from './auctionManager.js'; +import {isBidUsable, type SlotMatchingFn, targeting} from './targeting.js'; +import {hook, wrapHook} from './hook.js'; +import {loadSession} from './debugging.js'; +import {storageCallbacks} from './storageManager.js'; +import { + type AliasBidderOptions, + type BidRequest, + default as adapterManager, + getS2SBidderSet +} from './adapterManager.js'; +import {BID_STATUS, EVENTS, NATIVE_KEYS} from './constants.js'; +import type {EventHandler, EventIDs, Event} from "./events.js"; +import * as events from './events.js'; +import {type Metrics, newMetrics, useMetrics} from './utils/perfMetrics.js'; +import {type Defer, defer, PbPromise} from './utils/promise.js'; +import {enrichFPD} from './fpd/enrichment.js'; +import {allConsent} from './consentHandler.js'; +import { + insertLocatorFrame, + markBidAsRendered, + markWinningBid, + renderAdDirect, + renderIfDeferred +} from './adRendering.js'; +import {getHighestCpm} from './utils/reducers.js'; +import {fillVideoDefaults, ORTB_VIDEO_PARAMS, validateOrtbVideoFields} from './video.js'; +import {ORTB_BANNER_PARAMS} from './banner.js'; +import {BANNER, VIDEO} from './mediaTypes.js'; +import {delayIfPrerendering} from './utils/prerendering.js'; +import {type BidAdapter, type BidderSpec, newBidder} from './adapters/bidderFactory.js'; +import {normalizeFPD} from './fpd/normalize.js'; +import type {Bid} from "./bidfactory.ts"; +import type {AdUnit, AdUnitDefinition, BidderParams} from "./adUnits.ts"; +import type {AdUnitCode, BidderCode, ByAdUnit, Identifier, ORTBFragments} from "./types/common.d.ts"; +import type {ORTBRequest} from "./types/ortb/request.d.ts"; +import type {DeepPartial} from "./types/objects.d.ts"; +import type {AnyFunction, Wraps} from "./types/functions.d.ts"; +import type {BidderScopedSettings, BidderSettings} from "./bidderSettings.ts"; + +const pbjsInstance = getGlobal(); +const { triggerUserSyncs } = userSync; + +/* private variables */ +const { ADD_AD_UNITS, REQUEST_BIDS, SET_TARGETING } = EVENTS; + +// initialize existing debugging sessions if present +loadSession(); + +declare module './prebidGlobal' { + interface PrebidJS { + bidderSettings: { + standard?: BidderSettings + } & { + [B in BidderCode]?: BidderScopedSettings + } & { + [B in keyof BidderParams]?: BidderScopedSettings + }; + /** + * True once Prebid is loaded. + */ + libLoaded?: true; + /** + * Prebid version. + */ + version: string; + /** + * Set this to true to delay processing of `que` / `cmd` until prerendering is complete + * (applies only when the page is prerendering). + */ + delayPrerendering?: boolean + adUnits: AdUnitDefinition[]; + } +} + +pbjsInstance.bidderSettings = pbjsInstance.bidderSettings || {}; +pbjsInstance.libLoaded = true; +// version auto generated from build +pbjsInstance.version = 'v$prebid.version$'; +logInfo('Prebid.js v$prebid.version$ loaded'); + +// create adUnit array +pbjsInstance.adUnits = pbjsInstance.adUnits || []; + +function validateSizes(sizes, targLength?: number) { + let cleanSizes = []; + if (isArray(sizes) && ((targLength) ? sizes.length === targLength : sizes.length > 0)) { + // check if an array of arrays or array of numbers + if (sizes.every(sz => isArrayOfNums(sz, 2))) { + cleanSizes = sizes; + } else if (isArrayOfNums(sizes, 2)) { + cleanSizes.push(sizes); + } + } + return cleanSizes; +} + +// synchronize fields between mediaTypes[mediaType] and ortb2Imp[mediaType] +export function syncOrtb2(adUnit, mediaType) { + const ortb2Imp = deepAccess(adUnit, `ortb2Imp.${mediaType}`); + const mediaTypes = deepAccess(adUnit, `mediaTypes.${mediaType}`); + + if (!ortb2Imp && !mediaTypes) { + // omitting sync due to not present mediaType + return; + } + + const fields = { + [VIDEO]: FEATURES.VIDEO && ORTB_VIDEO_PARAMS, + [BANNER]: ORTB_BANNER_PARAMS + }[mediaType]; + + if (!fields) { + return; + } + + [...fields].forEach(([key, validator]) => { + const mediaTypesFieldValue = deepAccess(adUnit, `mediaTypes.${mediaType}.${key}`); + const ortbFieldValue = deepAccess(adUnit, `ortb2Imp.${mediaType}.${key}`); + + if (mediaTypesFieldValue == undefined && ortbFieldValue == undefined) { + // omitting the params if it's not defined on either of sides + } else if (mediaTypesFieldValue == undefined) { + deepSetValue(adUnit, `mediaTypes.${mediaType}.${key}`, ortbFieldValue); + } else if (ortbFieldValue == undefined) { + deepSetValue(adUnit, `ortb2Imp.${mediaType}.${key}`, mediaTypesFieldValue); + } else { + logWarn(`adUnit ${adUnit.code}: specifies conflicting ortb2Imp.${mediaType}.${key} and mediaTypes.${mediaType}.${key}, the latter will be ignored`, adUnit); + deepSetValue(adUnit, `mediaTypes.${mediaType}.${key}`, ortbFieldValue); + } + }); +} + +function validateBannerMediaType(adUnit: AdUnit) { + const validatedAdUnit = deepClone(adUnit); + const banner = validatedAdUnit.mediaTypes.banner; + const bannerSizes = banner.sizes == null ? null : validateSizes(banner.sizes); + const format = adUnit.ortb2Imp?.banner?.format ?? banner?.format; + let formatSizes; + if (format != null) { + deepSetValue(validatedAdUnit, 'ortb2Imp.banner.format', format); + banner.format = format; + try { + formatSizes = format + .filter(({w, h, wratio, hratio}) => { + if ((w ?? h) != null && (wratio ?? hratio) != null) { + logWarn(`Ad unit banner.format specifies both w/h and wratio/hratio`, adUnit); + return false; + } + return (w != null && h != null) || (wratio != null && hratio != null); + }) + .map(({w, h, wratio, hratio}) => [w ?? wratio, h ?? hratio]); + } catch (e) { + logError(`Invalid format definition on ad unit ${adUnit.code}`, format); + } + if (formatSizes != null && bannerSizes != null && !deepEqual(bannerSizes, formatSizes)) { + logWarn(`Ad unit ${adUnit.code} has conflicting sizes and format definitions`, adUnit); + } + } + const sizes = formatSizes ?? bannerSizes ?? []; + const expdir = adUnit.ortb2Imp?.banner?.expdir ?? banner.expdir; + if (expdir != null) { + banner.expdir = expdir; + deepSetValue(validatedAdUnit, 'ortb2Imp.banner.expdir', expdir); + } + if (sizes.length > 0) { + banner.sizes = sizes; + // Deprecation Warning: This property will be deprecated in next release in favor of adUnit.mediaTypes.banner.sizes + validatedAdUnit.sizes = sizes; + } else { + logError('Detected a mediaTypes.banner object without a proper sizes field. Please ensure the sizes are listed like: [[300, 250], ...]. Removing invalid mediaTypes.banner object from request.'); + delete validatedAdUnit.mediaTypes.banner + } + syncOrtb2(validatedAdUnit, 'banner') + return validatedAdUnit; +} + +function validateVideoMediaType(adUnit: AdUnit) { + const validatedAdUnit = deepClone(adUnit); + const video = validatedAdUnit.mediaTypes.video; + if (video.playerSize) { + const tarPlayerSizeLen = (typeof video.playerSize[0] === 'number') ? 2 : 1; + + const videoSizes = validateSizes(video.playerSize, tarPlayerSizeLen); + if (videoSizes.length > 0) { + if (tarPlayerSizeLen === 2) { + logInfo('Transforming video.playerSize from [640,480] to [[640,480]] so it\'s in the proper format.'); + } + video.playerSize = videoSizes; + // Deprecation Warning: This property will be deprecated in next release in favor of adUnit.mediaTypes.video.playerSize + validatedAdUnit.sizes = videoSizes; + } else { + logError('Detected incorrect configuration of mediaTypes.video.playerSize. Please specify only one set of dimensions in a format like: [[640, 480]]. Removing invalid mediaTypes.video.playerSize property from request.'); + delete validatedAdUnit.mediaTypes.video.playerSize; + } + } + validateOrtbVideoFields(validatedAdUnit); + syncOrtb2(validatedAdUnit, 'video'); + return validatedAdUnit; +} + +function validateNativeMediaType(adUnit: AdUnit) { + function err(msg) { + logError(`Error in adUnit "${adUnit.code}": ${msg}. Removing native request from ad unit`, adUnit); + delete validatedAdUnit.mediaTypes.native; + return validatedAdUnit; + } + function checkDeprecated(onDeprecated) { + for (const key of ['sendTargetingKeys', 'types']) { + if (native.hasOwnProperty(key)) { + const res = onDeprecated(key); + if (res) return res; + } + } + } + const validatedAdUnit = deepClone(adUnit); + const native = validatedAdUnit.mediaTypes.native; + // if native assets are specified in OpenRTB format, remove legacy assets and print a warn. + if (native.ortb) { + if (native.ortb.assets?.some(asset => !isNumber(asset.id) || asset.id < 0 || asset.id % 1 !== 0)) { + return err('native asset ID must be a nonnegative integer'); + } + if (checkDeprecated(key => err(`ORTB native requests cannot specify "${key}"`))) { + return validatedAdUnit; + } + const legacyNativeKeys = Object.keys(NATIVE_KEYS).filter(key => NATIVE_KEYS[key].includes('hb_native_')); + const nativeKeys = Object.keys(native); + const intersection = nativeKeys.filter(nativeKey => legacyNativeKeys.includes(nativeKey)); + if (intersection.length > 0) { + logError(`when using native OpenRTB format, you cannot use legacy native properties. Deleting ${intersection} keys from request.`); + intersection.forEach(legacyKey => delete validatedAdUnit.mediaTypes.native[legacyKey]); + } + } else { + checkDeprecated(key => logWarn(`mediaTypes.native.${key} is deprecated, consider using native ORTB instead`, adUnit)); + } + if (native.image && native.image.sizes && !Array.isArray(native.image.sizes)) { + logError('Please use an array of sizes for native.image.sizes field. Removing invalid mediaTypes.native.image.sizes property from request.'); + delete validatedAdUnit.mediaTypes.native.image.sizes; + } + if (native.image && native.image.aspect_ratios && !Array.isArray(native.image.aspect_ratios)) { + logError('Please use an array of sizes for native.image.aspect_ratios field. Removing invalid mediaTypes.native.image.aspect_ratios property from request.'); + delete validatedAdUnit.mediaTypes.native.image.aspect_ratios; + } + if (native.icon && native.icon.sizes && !Array.isArray(native.icon.sizes)) { + logError('Please use an array of sizes for native.icon.sizes field. Removing invalid mediaTypes.native.icon.sizes property from request.'); + delete validatedAdUnit.mediaTypes.native.icon.sizes; + } + return validatedAdUnit; +} + +function validateAdUnitPos(adUnit, mediaType) { + const pos = adUnit?.mediaTypes?.[mediaType]?.pos; + + if (!isNumber(pos) || isNaN(pos) || !isFinite(pos)) { + const warning = `Value of property 'pos' on ad unit ${adUnit.code} should be of type: Number`; + + logWarn(warning); + delete adUnit.mediaTypes[mediaType].pos; + } + + return adUnit +} + +function validateAdUnit(adUnitDef: AdUnitDefinition): AdUnit { + const msg = (msg) => `adUnit.code '${adUnit.code}' ${msg}`; + const adUnit = adUnitDef as AdUnit; + const mediaTypes = adUnit.mediaTypes; + const bids = adUnit.bids; + + if (bids != null && !isArray(bids)) { + logError(msg(`defines 'adUnit.bids' that is not an array. Removing adUnit from auction`)); + return null; + } + if (bids == null && adUnit.ortb2Imp == null) { + logError(msg(`has no 'adUnit.bids' and no 'adUnit.ortb2Imp'. Removing adUnit from auction`)); + return null; + } + if (!mediaTypes || Object.keys(mediaTypes).length === 0) { + logError(msg(`does not define a 'mediaTypes' object. This is a required field for the auction, so this adUnit has been removed.`)); + return null; + } + if (adUnit.ortb2Imp != null && (bids == null || bids.length === 0)) { + adUnit.bids = [{bidder: null}]; // the 'null' bidder is treated as an s2s-only placeholder by adapterManager + logMessage(msg(`defines 'adUnit.ortb2Imp' with no 'adUnit.bids'; it will be seen only by S2S adapters`)); + } + + return adUnit; +} + +export const adUnitSetupChecks = { + validateAdUnit, + validateBannerMediaType, + validateSizes +}; + +if (FEATURES.NATIVE) { + Object.assign(adUnitSetupChecks, { validateNativeMediaType }); +} + +if (FEATURES.VIDEO) { + Object.assign(adUnitSetupChecks, { validateVideoMediaType }); +} + +export const checkAdUnitSetup = hook('sync', function (adUnits: AdUnitDefinition[]) { + const validatedAdUnits = []; + + adUnits.forEach(adUnitDef => { + const adUnit = validateAdUnit(adUnitDef); + if (adUnit == null) return; + + const mediaTypes = adUnit.mediaTypes; + let validatedBanner, validatedVideo, validatedNative; + + if (mediaTypes.banner) { + validatedBanner = validateBannerMediaType(adUnit); + if (mediaTypes.banner.hasOwnProperty('pos')) validatedBanner = validateAdUnitPos(validatedBanner, 'banner'); + } + + if (FEATURES.VIDEO && mediaTypes.video) { + validatedVideo = validatedBanner ? validateVideoMediaType(validatedBanner) : validateVideoMediaType(adUnit); + if (mediaTypes.video.hasOwnProperty('pos')) validatedVideo = validateAdUnitPos(validatedVideo, 'video'); + } + + if (FEATURES.NATIVE && mediaTypes.native) { + validatedNative = validatedVideo ? validateNativeMediaType(validatedVideo) : validatedBanner ? validateNativeMediaType(validatedBanner) : validateNativeMediaType(adUnit); + } + + const validatedAdUnit = Object.assign({}, validatedBanner, validatedVideo, validatedNative); + + validatedAdUnits.push(validatedAdUnit); + }); + + return validatedAdUnits; +}, 'checkAdUnitSetup'); + +function fillAdUnitDefaults(adUnits: AdUnitDefinition[]) { + if (FEATURES.VIDEO) { + adUnits.forEach(au => fillVideoDefaults(au)) + } +} + +function logInvocation(name: string, fn: T): Wraps { + return function (...args) { + logInfo(`Invoking $$PREBID_GLOBAL$$.${name}`, args); + return fn.apply(this, args); + } +} + +export function addApiMethod(name: N, method: PrebidJS[N], log = true) { + getGlobal()[name] = log ? logInvocation(name, method) as PrebidJS[N] : method; +} + +/// /////////////////////////////// +// // +// Start Public APIs // +// // +/// /////////////////////////////// + +declare module './prebidGlobal' { + interface PrebidJS { + /** + * Re-trigger user syncs. Requires the `userSync.enableOverride` config to be set. + */ + triggerUserSyncs: typeof triggerUserSyncs; + getAdserverTargetingForAdUnitCodeStr: typeof getAdserverTargetingForAdUnitCodeStr; + getHighestUnusedBidResponseForAdUnitCode: typeof getHighestUnusedBidResponseForAdUnitCode; + getAdserverTargetingForAdUnitCode: typeof getAdserverTargetingForAdUnitCode; + getAdserverTargeting: typeof getAdserverTargeting; + getConsentMetadata: typeof getConsentMetadata; + getNoBids: typeof getNoBids; + getNoBidsForAdUnitCode: typeof getNoBidsForAdUnitCode; + getBidResponses: typeof getBidResponses; + getBidResponsesForAdUnitCode: typeof getBidResponsesForAdUnitCode; + setTargetingForGPTAsync: typeof setTargetingForGPTAsync; + setTargetingForAst: typeof setTargetingForAst; + renderAd: typeof renderAd; + removeAdUnit: typeof removeAdUnit; + requestBids: RequestBids; + addAdUnits: typeof addAdUnits; + onEvent: typeof onEvent; + offEvent: typeof offEvent; + getEvents: typeof getEvents; + registerBidAdapter: typeof registerBidAdapter; + registerAnalyticsAdapter: typeof adapterManager.registerAnalyticsAdapter; + enableAnalytics: typeof adapterManager.enableAnalytics; + aliasBidder: typeof aliasBidder; + aliasRegistry: typeof adapterManager.aliasRegistry; + getAllWinningBids: typeof getAllWinningBids; + getAllPrebidWinningBids: typeof getAllPrebidWinningBids; + getHighestCpmBids: typeof getHighestCpmBids; + clearAllAuctions: typeof clearAllAuctions; + markWinningBidAsUsed: typeof markWinningBidAsUsed; + getConfig: typeof config.getConfig; + readConfig: typeof config.readConfig; + mergeConfig: typeof config.mergeConfig; + mergeBidderConfig: typeof config.mergeBidderConfig; + setConfig: typeof config.setConfig; + setBidderConfig: typeof config.setBidderConfig; + processQueue: typeof processQueue; + triggerBilling: typeof triggerBilling; + } +} + +// Allow publishers who enable user sync override to trigger their sync +addApiMethod('triggerUserSyncs', triggerUserSyncs); + +/** + * Return a query string with all available targeting parameters for the given ad unit. + * + * @param adUnitCode ad unit code to target + */ +function getAdserverTargetingForAdUnitCodeStr(adUnitCode: AdUnitCode): string { + if (adUnitCode) { + const res = getAdserverTargetingForAdUnitCode(adUnitCode); + return transformAdServerTargetingObj(res); + } else { + logMessage('Need to call getAdserverTargetingForAdUnitCodeStr with adunitCode'); + } +} +addApiMethod('getAdserverTargetingForAdUnitCodeStr', getAdserverTargetingForAdUnitCodeStr); + +/** + * Return the highest cpm, unused bid for the given ad unit. + * @param adUnitCode + */ +function getHighestUnusedBidResponseForAdUnitCode(adUnitCode: AdUnitCode): Bid { + if (adUnitCode) { + const bid = auctionManager.getAllBidsForAdUnitCode(adUnitCode) + .filter(isBidUsable) + + return bid.length ? bid.reduce(getHighestCpm) : null + } else { + logMessage('Need to call getHighestUnusedBidResponseForAdUnitCode with adunitCode'); + } +} +addApiMethod('getHighestUnusedBidResponseForAdUnitCode', getHighestUnusedBidResponseForAdUnitCode); + +/** + * Returns targeting key-value pairs available at this moment for a given ad unit. + * @param adUnitCode adUnitCode to get the bid responses for + */ +function getAdserverTargetingForAdUnitCode(adUnitCode) { + return getAdserverTargeting(adUnitCode)[adUnitCode]; +} +addApiMethod('getAdserverTargetingForAdUnitCode', getAdserverTargetingForAdUnitCode); + +/** + * returns all ad server targeting, optionally scoped to the given ad unit(s). + * @return Map of adUnitCodes to targeting key-value pairs + */ +function getAdserverTargeting(adUnitCode?: AdUnitCode | AdUnitCode[]) { + return targeting.getAllTargeting(adUnitCode); +} +addApiMethod('getAdserverTargeting', getAdserverTargeting); + +function getConsentMetadata() { + return allConsent.getConsentMeta() +} +addApiMethod('getConsentMetadata', getConsentMetadata); + +type WrapsInBids = T[] & { + bids: T[] +} + +function wrapInBids(arr) { + arr = arr.slice(); + arr.bids = arr; + return arr; +} + +function getBids(type): ByAdUnit> { + const responses = auctionManager[type]() + .filter(bid => auctionManager.getAdUnitCodes().includes(bid.adUnitCode)) + + // find the last auction id to get responses for most recent auction only + const currentAuctionId = auctionManager.getLastAuctionId(); + + return responses + .map(bid => bid.adUnitCode) + .filter(uniques).map(adUnitCode => responses + .filter(bid => bid.auctionId === currentAuctionId && bid.adUnitCode === adUnitCode)) + .filter(bids => bids && bids[0] && bids[0].adUnitCode) + .map(bids => { + return { + [bids[0].adUnitCode]: wrapInBids(bids) + }; + }) + .reduce((a, b) => Object.assign(a, b), {}); +} + +/** + * @returns the bids requests involved in an auction but not bid on + */ +function getNoBids() { + return getBids>('getNoBids'); +} +addApiMethod('getNoBids', getNoBids); + +/** + * @returns the bids requests involved in an auction but not bid on or the specified adUnitCode + */ +function getNoBidsForAdUnitCode(adUnitCode: AdUnitCode): WrapsInBids> { + const bids = auctionManager.getNoBids().filter(bid => bid.adUnitCode === adUnitCode); + return wrapInBids(bids); +} +addApiMethod('getNoBidsForAdUnitCode', getNoBidsForAdUnitCode); + +/** + * @return a map from ad unit code to all bids received for that ad unit code. + */ +function getBidResponses() { + return getBids('getBidsReceived'); +} +addApiMethod('getBidResponses', getBidResponses); + +/** + * Returns bids received for the specified ad unit. + * @param adUnitCode ad unit code + */ +function getBidResponsesForAdUnitCode(adUnitCode: AdUnitCode): WrapsInBids { + const bids = auctionManager.getBidsReceived().filter(bid => bid.adUnitCode === adUnitCode); + return wrapInBids(bids); +} +addApiMethod('getBidResponsesForAdUnitCode', getBidResponsesForAdUnitCode); + +/** + * Set query string targeting on one or more GPT ad units. + * @param adUnit a single `adUnit.code` or multiple. + * @param customSlotMatching gets a GoogleTag slot and returns a filter function for adUnitCode, so you can decide to match on either eg. return slot => { return adUnitCode => { return slot.getSlotElementId() === 'myFavoriteDivId'; } }; + */ +function setTargetingForGPTAsync(adUnit?: AdUnitCode | AdUnitCode[], customSlotMatching?: SlotMatchingFn) { + if (!isGptPubadsDefined()) { + logError('window.googletag is not defined on the page'); + return; + } + targeting.setTargetingForGPT(adUnit, customSlotMatching); +} +addApiMethod('setTargetingForGPTAsync', setTargetingForGPTAsync); + +/** + * Set query string targeting on all AST (AppNexus Seller Tag) ad units. Note that this function has to be called after all ad units on page are defined. For working example code, see [Using Prebid.js with AppNexus Publisher Ad Server](http://prebid.org/dev-docs/examples/use-prebid-with-appnexus-ad-server.html). + * @param adUnitCodes adUnitCode or array of adUnitCodes + */ +function setTargetingForAst(adUnitCodes?: AdUnitCode | AdUnitCode[]) { + if (!targeting.isApntagDefined()) { + logError('window.apntag is not defined on the page'); + return; + } + + targeting.setTargetingForAst(adUnitCodes); + events.emit(SET_TARGETING, targeting.getAllTargeting()); +} + +addApiMethod('setTargetingForAst', setTargetingForAst); + +type RenderAdOptions = { + /** + * Click through URL. Used to replace ${CLICKTHROUGH} macro in ad markup. + */ + clickThrough?: string; +} +/** + * 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 + * @param doc document + * @param id adId of the bid to render + * @param options + */ +function renderAd(doc: Document, id: Bid['adId'], options?: RenderAdOptions) { + renderAdDirect(doc, id, options); +} +addApiMethod('renderAd', renderAd); + +/** + * Remove adUnit from the $$PREBID_GLOBAL$$ configuration, if there are no addUnitCode(s) it will remove all + * @param adUnitCode the adUnitCode(s) to remove + * @alias module:pbjs.removeAdUnit + */ +function removeAdUnit(adUnitCode?: AdUnitCode) { + if (!adUnitCode) { + pbjsInstance.adUnits = []; + return; + } + + let adUnitCodes; + + if (isArray(adUnitCode)) { + adUnitCodes = adUnitCode; + } else { + adUnitCodes = [adUnitCode]; + } + + adUnitCodes.forEach((adUnitCode) => { + for (let i = pbjsInstance.adUnits.length - 1; i >= 0; i--) { + if (pbjsInstance.adUnits[i].code === adUnitCode) { + pbjsInstance.adUnits.splice(i, 1); + } + } + }); +} +addApiMethod('removeAdUnit', removeAdUnit); + +export type RequestBidsOptions = { + /** + * Callback to execute when all the bid responses are back or the timeout hits. Parameters may be undefined + * in situations where the auction is canceled prematurely (e.g. CMP errors) + */ + bidsBackHandler?: (bids?: RequestBidsResult['bids'], timedOut?: RequestBidsResult['timedOut'], auctionId?: RequestBidsResult['auctionId']) => void; + /** + * TTL buffer override for this auction. + */ + ttlBuffer?: number; + /** + * Timeout for requesting the bids specified in milliseconds + */ + timeout?: number; + /** + * AdUnit definitions to request. Use this or adUnitCodes. Default to all adUnits if empty. + */ + adUnits?: AdUnitDefinition[]; + /** + * adUnit codes to request. Use this or adUnits. Default to all adUnits if empty. + */ + adUnitCodes?: AdUnitCode[]; + /** + * Defines labels that may be matched on ad unit targeting conditions. + */ + labels?: string[]; + /** + * Defines an auction ID to be used rather than having Prebid generate one. + * This can be useful if there are multiple wrappers on a page and a single auction ID + * is desired to tie them together in analytics. + */ + auctionId?: string; + /** + * Additional first-party data to use for this auction only + */ + ortb2?: DeepPartial; +} + +type RequestBidsResult = { + /** + * Bids received, grouped by ad unit. + */ + bids?: ByAdUnit>; + /** + * True if any bidder timed out. + */ + timedOut?: boolean; + /** + * The auction's ID + */ + auctionId?: Identifier; +} + +export type PrivRequestBidsOptions = RequestBidsOptions & { + defer: Defer; + metrics: Metrics; + /** + * Ad units are always defined and fixed here (as opposed to the public API where we may fall back to + * the global array). + */ + adUnits: AdUnitDefinition[]; +} + +export type StartAuctionOptions = Omit & { + ortb2Fragments: ORTBFragments +} + +declare module './hook' { + interface NamedHooks { + requestBids: typeof requestBids; + startAuction: typeof startAuction; + } +} + +interface RequestBids { + (options?: RequestBidsOptions): Promise; +} + +declare module './events' { + interface Events { + /** + * Fired when `requestBids` is called. + */ + [REQUEST_BIDS]: []; + } +} + +export const requestBids = (function() { + const delegate = hook('async', function (reqBidOptions: PrivRequestBidsOptions): void { + let { bidsBackHandler, timeout, adUnits, adUnitCodes, labels, auctionId, ttlBuffer, ortb2, metrics, defer } = reqBidOptions ?? {}; + events.emit(REQUEST_BIDS); + const cbTimeout = timeout || config.getConfig('bidderTimeout'); + if (adUnitCodes != null && !Array.isArray(adUnitCodes)) { + adUnitCodes = [adUnitCodes]; + } + if (adUnitCodes && adUnitCodes.length) { + // if specific adUnitCodes supplied filter adUnits for those codes + adUnits = adUnits.filter(unit => adUnitCodes.includes(unit.code)); + } else { + // otherwise derive adUnitCodes from adUnits + adUnitCodes = adUnits && adUnits.map(unit => unit.code); + } + adUnitCodes = adUnitCodes.filter(uniques); + let ortb2Fragments = { + global: mergeDeep({}, config.getAnyConfig('ortb2') || {}, ortb2 || {}), + bidder: Object.fromEntries(Object.entries(config.getBidderConfig()).map(([bidder, cfg]) => [bidder, deepClone(cfg.ortb2)]).filter(([_, ortb2]) => ortb2 != null)) + } + ortb2Fragments = normalizeFPD(ortb2Fragments); + + enrichFPD(PbPromise.resolve(ortb2Fragments.global)).then(global => { + ortb2Fragments.global = global; + return startAuction({bidsBackHandler, timeout: cbTimeout, adUnits, adUnitCodes, labels, auctionId, ttlBuffer, ortb2Fragments, metrics, defer}); + }) + }, 'requestBids'); + + return wrapHook(delegate, delayIfPrerendering(() => !config.getConfig('allowPrerendering'), function requestBids(options: RequestBidsOptions = {}) { + // unlike the main body of `delegate`, this runs before any other hook has a chance to; + // it's also not restricted in its return value in the way `async` hooks are. + + // if the request does not specify adUnits, clone the global adUnit array; + // otherwise, if the caller goes on to use addAdUnits/removeAdUnits, any asynchronous logic + // in any hook might see their effects. + const req = options as PrivRequestBidsOptions; + let adUnits = req.adUnits || pbjsInstance.adUnits; + req.adUnits = (Array.isArray(adUnits) ? adUnits.slice() : [adUnits]); + + req.metrics = newMetrics(); + req.metrics.checkpoint('requestBids'); + req.defer = defer({ promiseFactory: (r) => new Promise(r)}) + delegate.call(this, req); + return req.defer.promise; + })); +})(); + +addApiMethod('requestBids', requestBids as unknown as RequestBids); + +export const startAuction = hook('async', function ({ bidsBackHandler, timeout: cbTimeout, adUnits: adUnitDefs, ttlBuffer, adUnitCodes, labels, auctionId, ortb2Fragments, metrics, defer }: StartAuctionOptions = {} as any) { + const s2sBidders = getS2SBidderSet(config.getConfig('s2sConfig') || []); + fillAdUnitDefaults(adUnitDefs); + const adUnits: AdUnit[] = useMetrics(metrics).measureTime('requestBids.validate', () => checkAdUnitSetup(adUnitDefs)); + + function auctionDone(bids?, timedOut?: boolean, auctionId?: string) { + if (typeof bidsBackHandler === 'function') { + try { + bidsBackHandler(bids, timedOut, auctionId); + } catch (e) { + logError('Error executing bidsBackHandler', null, e); + } + } + 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 + * a bidder is eligible to participate on the adunit + * if it supports at least one of the mediaTypes on the adunit + */ + adUnits.forEach(adUnit => { + // get the adunit's mediaTypes, defaulting to banner if mediaTypes isn't present + const adUnitMediaTypes = Object.keys(adUnit.mediaTypes || { 'banner': 'banner' }); + + // get the bidder's mediaTypes + const allBidders = adUnit.bids.map(bid => bid.bidder); + const bidderRegistry = adapterManager.bidderRegistry; + + const bidders = allBidders.filter(bidder => !s2sBidders.has(bidder)); + 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; + } + bidders.forEach(bidder => { + const adapter = bidderRegistry[bidder]; + const spec = adapter && adapter.getSpec && adapter.getSpec(); + // banner is default if not specified in spec + const bidderMediaTypes = (spec && spec.supportedMediaTypes) || ['banner']; + + // check if the bidder's mediaTypes are not in the adUnit's mediaTypes + const bidderEligible = adUnitMediaTypes.some(type => bidderMediaTypes.includes(type)); + if (!bidderEligible) { + // drop the bidder from the ad unit if it's not compatible + logWarn(unsupportedBidderMessage(adUnit, bidder)); + adUnit.bids = adUnit.bids.filter(bid => bid.bidder !== bidder); + } + }); + }); + 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, + callback: auctionDone, + cbTimeout, + labels, + auctionId, + ortb2Fragments, + metrics, + }); + + const adUnitsLen = adUnits.length; + if (adUnitsLen > 15) { + logInfo(`Current auction ${auction.getAuctionId()} contains ${adUnitsLen} adUnits.`, adUnits); + } + + adUnitCodes.forEach(code => targeting.setLatestAuctionForAdUnit(code, auction.getAuctionId())); + auction.callBids(); + } +}, 'startAuction'); + +export function executeCallbacks(fn, reqBidsConfigObj) { + runAll(storageCallbacks); + runAll(enableAnalyticsCallbacks); + fn.call(this, reqBidsConfigObj); + + function runAll(queue) { + let queued; + while ((queued = queue.shift())) { + queued(); + } + } +} + +// This hook will execute all storage callbacks which were registered before gdpr enforcement hook was added. Some bidders, user id modules use storage functions when module is parsed but gdpr enforcement hook is not added at that stage as setConfig callbacks are yet to be called. Hence for such calls we execute all the stored callbacks just before requestBids. At this hook point we will know for sure that tcfControl module is added or not +requestBids.before(executeCallbacks, 49); + +declare module './events' { + interface Events { + /** + * Fired when `.addAdUniuts` is called. + */ + [ADD_AD_UNITS]: []; + } +} +/** + * Add ad unit(s) + * @param adUnits + */ +function addAdUnits(adUnits: AdUnitDefinition | AdUnitDefinition[]) { + pbjsInstance.adUnits.push(...(Array.isArray(adUnits) ? adUnits : [adUnits])) + events.emit(ADD_AD_UNITS); +} + +addApiMethod('addAdUnits', addAdUnits); + +const eventIdValidators = { + bidWon(id) { + const adUnitCodes = auctionManager.getBidsRequested().map(bidSet => bidSet.bids.map(bid => bid.adUnitCode)) + .reduce(flatten) + .filter(uniques); + + if (!adUnitCodes.includes(id)) { + logError('The "' + id + '" placement is not defined.'); + return; + } + + return true; + } +}; + +function validateEventId(event, id) { + return eventIdValidators.hasOwnProperty(event) && eventIdValidators[event](id); +} + +/** + * @param event the name of the event + * @param handler a callback to set on event + * @param id an identifier in the context of the event + * + * This API call allows you to register a callback to handle a Prebid.js event. + * An optional `id` parameter provides more finely-grained event callback registration. + * This makes it possible to register callback events for a specific item in the + * event context. For example, `bidWon` events will accept an `id` for ad unit code. + * `bidWon` callbacks registered with an ad unit code id will be called when a bid + * for that ad unit code wins the auction. Without an `id` this method registers the + * callback for every `bidWon` event. + * + * Currently `bidWon` is the only event that accepts an `id` parameter. + */ +function onEvent(event: E, handler: EventHandler, id?: EventIDs[E]) { + if (!isFn(handler)) { + logError('The event handler provided is not a function and was not set on event "' + event + '".'); + return; + } + + if (id && !validateEventId(event, id)) { + logError('The id provided is not valid for event "' + event + '" and no handler was set.'); + return; + } + + events.on(event, handler, id); +} +addApiMethod('onEvent', onEvent); + +/** + * @param event the name of the event + * @param handler a callback to remove from the event + * @param id an identifier in the context of the event (see `$$PREBID_GLOBAL$$.onEvent`) + */ +function offEvent(event: E, handler: EventHandler, id?: EventIDs[E]) { + if (id && !validateEventId(event, id)) { + return; + } + events.off(event, handler, id); +} +addApiMethod('offEvent', offEvent); + +/** + * Return a copy of all events emitted + */ +function getEvents() { + return events.getEvents(); +} +addApiMethod('getEvents', getEvents); + +function registerBidAdapter(adapter: BidAdapter, bidderCode: BidderCode): void; +function registerBidAdapter(adapter: void, bidderCode: B, spec: BidderSpec): void; +function registerBidAdapter(bidderAdaptor, bidderCode, spec?) { + try { + const bidder = spec ? newBidder(spec) : bidderAdaptor(); + adapterManager.registerBidAdapter(bidder, bidderCode); + } catch (e) { + logError('Error registering bidder adapter : ' + e.message); + } +} +addApiMethod('registerBidAdapter', registerBidAdapter); + +function registerAnalyticsAdapter(options) { + try { + adapterManager.registerAnalyticsAdapter(options); + } catch (e) { + logError('Error registering analytics adapter : ' + e.message); + } +} +addApiMethod('registerAnalyticsAdapter', registerAnalyticsAdapter); + +const enableAnalyticsCallbacks = []; + +const enableAnalyticsCb = hook('async', function (config) { + if (config && !isEmpty(config)) { + adapterManager.enableAnalytics(config); + } else { + logError('$$PREBID_GLOBAL$$.enableAnalytics should be called with option {}'); + } +}, 'enableAnalyticsCb'); + +function enableAnalytics(config) { + enableAnalyticsCallbacks.push(enableAnalyticsCb.bind(this, config)); +} +addApiMethod('enableAnalytics', enableAnalytics); + +/** + * Define an alias for a bid adapter. + */ +function aliasBidder(bidderCode: BidderCode, alias: BidderCode, options?: AliasBidderOptions) { + if (bidderCode && alias) { + adapterManager.aliasBidAdapter(bidderCode, alias, options); + } else { + logError('bidderCode and alias must be passed as arguments', '$$PREBID_GLOBAL$$.aliasBidder'); + } +} +addApiMethod('aliasBidder', aliasBidder); + +pbjsInstance.aliasRegistry = adapterManager.aliasRegistry; +config.getConfig('aliasRegistry', config => { + if (config.aliasRegistry === 'private') delete pbjsInstance.aliasRegistry; +}); + +/** + * @return All bids that have been rendered. Useful for [troubleshooting your integration](http://prebid.org/dev-docs/prebid-troubleshooting-guide.html). + */ +function getAllWinningBids(): Bid[] { + return auctionManager.getAllWinningBids(); +} + +addApiMethod('getAllWinningBids', getAllWinningBids) + +/** + * @return Bids that have won their respective auctions but have not been rendered yet. + */ +function getAllPrebidWinningBids(): Bid[] { + logWarn('getAllPrebidWinningBids may be removed or renamed in a future version. This function returns bids that have won in prebid and have had targeting set but have not (yet?) won in the ad server. It excludes bids that have been rendered.'); + return auctionManager.getBidsReceived() + .filter(bid => bid.status === BID_STATUS.BID_TARGETING_SET); +} + +addApiMethod('getAllPrebidWinningBids', getAllPrebidWinningBids); + +/** + * Get highest cpm bids for all adUnits, or highest cpm bid object for the given adUnit + * @param adUnitCode - ad unit code + */ +function getHighestCpmBids(adUnitCode?: string): Bid[] { + return targeting.getWinningBids(adUnitCode); +} + +addApiMethod('getHighestCpmBids', getHighestCpmBids); + +/** + * Clear all auctions (and their bids) from the bid cache. + */ +function clearAllAuctions() { + auctionManager.clearAllAuctions(); +} +addApiMethod('clearAllAuctions', clearAllAuctions); + +type MarkWinningBidAsUsedOptions = ({ + /** + * The id representing the ad we want to mark + */ + adId: string; + adUnitCode?: undefined | null +} | { + /** + * The ad unit code + */ + adUnitCode: AdUnitCode; + adId?: undefined | null; + +}) & { + /** + * If true, fires tracking pixels and BID_WON handlers + */ + events?: boolean; + /** + * @deprecated - alias of `events` + */ + analytics?: boolean +} + +/** + * Mark the winning bid as used, should only be used in conjunction with video + */ +function markWinningBidAsUsed({adId, adUnitCode, analytics = false, events = false}: MarkWinningBidAsUsedOptions) { + let bids; + if (adUnitCode && adId == null) { + bids = targeting.getWinningBids(adUnitCode); + } else if (adId) { + bids = auctionManager.getBidsReceived().filter(bid => bid.adId === adId) + } else { + logWarn('Improper use of markWinningBidAsUsed. It needs an adUnitCode or an adId to function.'); + } + if (bids.length > 0) { + if (analytics || events) { + markWinningBid(bids[0]); + } else { + auctionManager.addWinningBid(bids[0]); + } + markBidAsRendered(bids[0]) + } +} + +if (FEATURES.VIDEO) { + addApiMethod('markWinningBidAsUsed', markWinningBidAsUsed); +} + +addApiMethod('getConfig', config.getAnyConfig); +addApiMethod('readConfig', config.readAnyConfig); +addApiMethod('mergeConfig', config.mergeConfig); +addApiMethod('mergeBidderConfig', config.mergeBidderConfig); +addApiMethod('setConfig', config.setConfig); +addApiMethod('setBidderConfig', config.setBidderConfig); + +pbjsInstance.que.push(() => listenMessagesFromCreative()); + +/** + * This queue lets users load Prebid asynchronously, but run functions the same way regardless of whether it gets loaded + * before or after their script executes. For example, given the code: + * + * + * + * + * If the page's script runs before prebid loads, then their function gets added to the queue, and executed + * by prebid once it's done loading. If it runs after prebid loads, then this monkey-patch causes their + * function to execute immediately. + * + * @param {function} command A function which takes no arguments. This is guaranteed to run exactly once, and only after + * the Prebid script has been fully loaded. + * @alias module:pbjs.cmd.push + * @alias module:pbjs.que.push + */ +function quePush(command) { + if (typeof command === 'function') { + try { + command.call(); + } catch (e) { + logError('Error processing command :', e.message, e.stack); + } + } else { + logError('Commands written into $$PREBID_GLOBAL$$.cmd.push must be wrapped in a function'); + } +} + +function _processQueue(queue) { + queue.forEach(function (cmd) { + if (typeof cmd.called === 'undefined') { + try { + cmd.call(); + cmd.called = true; + } catch (e) { + logError('Error processing command :', 'prebid.js', e); + } + } + }); +} + +/** + * Process the command queue, effectively booting up Prebid. + * Bundles generated by the build automatically include a call to this; NPM consumers + * should call this after loading all modules and before using other APIs. + */ +const processQueue = delayIfPrerendering(() => pbjsInstance.delayPrerendering, function () { + pbjsInstance.que.push = pbjsInstance.cmd.push = quePush; + insertLocatorFrame(); + hook.ready(); + _processQueue(pbjsInstance.que); + _processQueue(pbjsInstance.cmd); +}) +addApiMethod('processQueue', processQueue, false); + +/** + * Manually trigger billing for a winning bid, idendified either by ad ID or ad unit code. + * Used in conjunction with `adUnit.deferBilling`. + */ +function triggerBilling({adId, adUnitCode}: { + adId?: string; + adUnitCode?: AdUnitCode +}) { + auctionManager.getAllWinningBids() + .filter((bid) => bid.adId === adId || (adId == null && bid.adUnitCode === adUnitCode)) + .forEach((bid) => { + adapterManager.triggerBilling(bid); + renderIfDeferred(bid); + }); +} +addApiMethod('triggerBilling', triggerBilling); + +export default pbjsInstance; diff --git a/src/prebidGlobal.js b/src/prebidGlobal.js deleted file mode 100644 index 4cbc3e10ad1..00000000000 --- a/src/prebidGlobal.js +++ /dev/null @@ -1,21 +0,0 @@ -// if $$PREBID_GLOBAL$$ already exists in global document scope, use it, if not, create the object -// global defination should happen BEFORE imports to avoid global undefined errors. -/* global $$DEFINE_PREBID_GLOBAL$$ */ -const scope = !$$DEFINE_PREBID_GLOBAL$$ ? {} : window; -const global = scope.$$PREBID_GLOBAL$$ = scope.$$PREBID_GLOBAL$$ || {}; -global.cmd = global.cmd || []; -global.que = global.que || []; - -// create a pbjs global pointer -if (scope === window) { - scope._pbjsGlobals = scope._pbjsGlobals || []; - scope._pbjsGlobals.push('$$PREBID_GLOBAL$$'); -} - -export function getGlobal() { - return global; -} - -export function registerModule(name) { - global.installedModules.push(name); -} diff --git a/src/prebidGlobal.ts b/src/prebidGlobal.ts new file mode 100644 index 00000000000..9cc94dc2ecc --- /dev/null +++ b/src/prebidGlobal.ts @@ -0,0 +1,44 @@ +interface Command { + (): any; +} + +interface CommandQueue extends Omit { + push(cmd: Command): void; +} + +export interface PrebidJS { + /** + * Command queue. Use cmd.push(function F() { ... }) to queue F until Prebid has loaded. + */ + cmd: CommandQueue, + /** + * Alias of `cmd` + */ + que: CommandQueue + /** + * Names of all installed modules. + */ + installedModules: string[] +} + +// if $$PREBID_GLOBAL$$ already exists in global document scope, use it, if not, create the object +declare const $$DEFINE_PREBID_GLOBAL$$: boolean; +const scope: any = !$$DEFINE_PREBID_GLOBAL$$ ? {} : window; +const global: PrebidJS = scope.$$PREBID_GLOBAL$$ = scope.$$PREBID_GLOBAL$$ || {}; +global.cmd = global.cmd || []; +global.que = global.que || []; +global.installedModules = global.installedModules || [] + +// create a pbjs global pointer +if (scope === window) { + scope._pbjsGlobals = scope._pbjsGlobals || []; + scope._pbjsGlobals.push('$$PREBID_GLOBAL$$'); +} + +export function getGlobal() { + return global; +} + +export function registerModule(name: string) { + global.installedModules.push(name); +} diff --git a/src/refererDetection.js b/src/refererDetection.ts similarity index 66% rename from src/refererDetection.js rename to src/refererDetection.ts index bfe7fb02671..d2e4e2d330c 100644 --- a/src/refererDetection.js +++ b/src/refererDetection.ts @@ -34,23 +34,24 @@ export function ensureProtocol(url, win = window) { /** * Extract the domain portion from a URL. - * @param {string} url - The URL to extract the domain from. - * @param {Object} options - Options for parsing the domain. - * @param {boolean} options.noLeadingWww - If true, remove 'www.' appearing at the beginning of the domain. - * @param {boolean} options.noPort - If true, do not include the ':[port]' portion. - * @return {string|undefined} - The extracted domain or undefined if the URL is invalid. + * @param url - The URL to extract the domain from. + * @param options - Options for parsing the domain. + * @param options.noLeadingWww - If true, remove 'www.' appearing at the beginning of the domain. + * @param options.noPort - If true, do not include the ':[port]' portion. + * @return The extracted domain or undefined if the URL is invalid. */ -export function parseDomain(url, {noLeadingWww = false, noPort = false} = {}) { +export function parseDomain(url: string, {noLeadingWww = false, noPort = false} = {}): string | null { + let target; try { - url = new URL(ensureProtocol(url)); + target = new URL(ensureProtocol(url)); } catch (e) { return; } - url = noPort ? url.hostname : url.host; - if (noLeadingWww && url.startsWith('www.')) { - url = url.substring(4); + target = noPort ? target.hostname : target.host; + if (noLeadingWww && target.startsWith('www.')) { + target = target.substring(4); } - return url; + return target; } /** @@ -73,6 +74,20 @@ function getCanonicalUrl(doc) { return null; } +declare module './config' { + interface Config { + /** + * Prebid.js will loop upward through nested iframes to find the top-most referrer. T + * his setting limits how many iterations it will attempt before giving up and not setting referrer. + */ + maxNestedIframes?: number; + /** + * Override the Prebid.js page referrer. + */ + pageUrl?: string; + } +} + /** * @param {Window} win Window * @returns {Function} @@ -101,30 +116,18 @@ export function detectReferer(win) { // the typedef for now. (for example, unit tests enforce that "reachedTop" should be false in some situations where we // happily provide a location for the top). - /** - * @typedef {Object} refererInfo - * @property {string|null} location the browser's location, or null if not available (due to cross-origin restrictions) - * @property {string|null} canonicalUrl the site's canonical URL as set by the publisher, through setConfig({pageUrl}) or - * @property {string|null} page the best candidate for the current page URL: `canonicalUrl`, falling back to `location` - * @property {string|null} domain the domain portion of `page` - * @property {string|null} ref the referrer (document.referrer) to the current page, or null if not available (due to cross-origin restrictions) - * @property {string} topmostLocation of the top-most frame for which we could guess the location. Outside of cross-origin scenarios, this is equivalent to `location`. - * @property {number} numIframes number of steps between window.self and window.top - * @property {Array} stack our best guess at the location for each frame, in the direction top -> self. - */ - /** * Walk up the windows to get the origin stack and best available referrer, canonical URL, etc. * - * @returns {refererInfo} An object containing referer information. + * @returns An object containing referer information. */ function refererInfo() { - const stack = []; + const stack: string[] = []; const ancestors = getAncestorOrigins(win); const maxNestedIframes = config.getConfig('maxNestedIframes'); let currentWindow; - let bestLocation; + let bestLocation: string; let bestCanonicalUrl; let reachedTop = false; let level = 0; @@ -222,40 +225,67 @@ export function detectReferer(win) { stack.reverse(); - let ref; + let ref: string; try { ref = win.top.document.referrer; } catch (e) {} - const location = reachedTop || hasTopLocation ? bestLocation : null; - const canonicalUrl = config.getConfig('pageUrl') || bestCanonicalUrl || null; - let page = config.getConfig('pageUrl') || location || ensureProtocol(canonicalUrl, win); + const location: string = reachedTop || hasTopLocation ? bestLocation : null; + const canonicalUrl: string | null = config.getConfig('pageUrl') || bestCanonicalUrl || null; + let page: string = config.getConfig('pageUrl') || location || ensureProtocol(canonicalUrl, win); if (location && location.indexOf('?') > -1 && page.indexOf('?') === -1) { page = `${page}${location.substring(location.indexOf('?'))}`; } return { - reachedTop, - isAmp: valuesFromAmp, - numIframes: level - 1, - stack, - topmostLocation: bestLocation || null, - location, - canonicalUrl, - page, - domain: parseDomain(page) || null, - ref: ref || null, - // TODO: the "legacy" refererInfo object is provided here, for now, to accomodate - // adapters that decided to just send it verbatim to their backend. - legacy: { + /** + * True if the top window is accessible. + */ reachedTop, isAmp: valuesFromAmp, + /** + * number of steps between window.self and window.top + */ numIframes: level - 1, + /** + * our best guess at the location for each frame, in the direction top -> self. + */ stack, - referer: bestLocation || null, - canonicalUrl - } + /** + * of the top-most frame for which we could guess the location. Outside of cross-origin scenarios, this is equivalent to `location`. + */ + topmostLocation: bestLocation || null, + /** + * the browser's location, or null if not available (due to cross-origin restrictions) + */ + location, + /** + * the site's canonical URL as set by the publisher, through setConfig({pageUrl}) or + */ + canonicalUrl, + /** + * the best candidate for the current page URL: `canonicalUrl`, falling back to `location` + */ + page, + /** + * the domain portion of `page` + */ + domain: parseDomain(page) || null, + /** + * the referrer (document.referrer) to the current page, or null if not available (due to cross-origin restrictions) + */ + ref: ref || null, + // TODO: the "legacy" refererInfo object is provided here, for now, to accomodate + // adapters that decided to just send it verbatim to their backend. + legacy: { + reachedTop, + isAmp: valuesFromAmp, + numIframes: level - 1, + stack, + referer: bestLocation || null, + canonicalUrl + } }; } @@ -280,7 +310,5 @@ export function cacheWithLocation(fn, win = window) { } } -/** - * @type {function(): refererInfo} - */ -export const getRefererInfo = cacheWithLocation(detectReferer(window)); +export type RefererInfo = ReturnType>; +export const getRefererInfo: () => RefererInfo = cacheWithLocation(detectReferer(window)); diff --git a/src/storageManager.js b/src/storageManager.ts similarity index 81% rename from src/storageManager.js rename to src/storageManager.ts index eea653bcb34..3237c00bd72 100644 --- a/src/storageManager.js +++ b/src/storageManager.ts @@ -1,6 +1,6 @@ import {checkCookieSupport, hasDeviceAccess, logError} from './utils.js'; import {bidderSettings} from './bidderSettings.js'; -import {MODULE_TYPE_BIDDER, MODULE_TYPE_PREBID} from './activities/modules.js'; +import {MODULE_TYPE_BIDDER, MODULE_TYPE_PREBID, type ModuleType} from './activities/modules.js'; import {isActivityAllowed, registerActivityControl} from './activities/rules.js'; import { ACTIVITY_PARAM_ADAPTER_CODE, @@ -12,18 +12,52 @@ import {ACTIVITY_ACCESS_DEVICE} from './activities/activities.js'; import {config} from './config.js'; import adapterManager from './adapterManager.js'; import {activityParams} from './activities/activityParams.js'; +import type {AnyFunction} from "./types/functions.d.ts"; +import type {BidderCode} from "./types/common.d.ts"; export const STORAGE_TYPE_LOCALSTORAGE = 'html5'; export const STORAGE_TYPE_COOKIES = 'cookie'; +export type StorageType = typeof STORAGE_TYPE_LOCALSTORAGE | typeof STORAGE_TYPE_COOKIES; + export let storageCallbacks = []; /* eslint-disable no-restricted-properties */ +interface AcceptsCallback { + (...args: Parameters): ReturnType; + (...args: [...Parameters, (result: ReturnType) => void]): void; +} + +type BrowserStorage = 'localStorage' | 'sessionStorage'; + +export type StorageManager = { + [M in BrowserStorage as `has${Capitalize}`]: AcceptsCallback<() => boolean>; +} & { + [M in BrowserStorage as `${M}IsEnabled`]: AcceptsCallback<() => boolean>; +} & { + // eslint-disable-next-line no-restricted-globals + [M in BrowserStorage as `setDataIn${Capitalize}`]: AcceptsCallback; +} & { + // eslint-disable-next-line no-restricted-globals + [M in BrowserStorage as `getDataFrom${Capitalize}`]: AcceptsCallback; +} & { + // eslint-disable-next-line no-restricted-globals + [M in BrowserStorage as `removeDataFrom${Capitalize}`]: AcceptsCallback +} & { + setCookie: AcceptsCallback<(name: string, value: string, expires?: string, sameSite?: string, domain?: string) => void>; + getCookie: AcceptsCallback<(name: string) => string>; + cookiesAreEnabled: AcceptsCallback<() => boolean>; + findSimilarCookies: AcceptsCallback<(contains: string) => string[]> +} + /* * Storage manager constructor. Consumers should prefer one of `getStorageManager` or `getCoreStorageManager`. */ -export function newStorageManager({moduleName, moduleType} = {}, {isAllowed = isActivityAllowed} = {}) { +export function newStorageManager({moduleName, moduleType}: { + moduleName: string; + moduleType: ModuleType; +} = {} as any, {isAllowed = isActivityAllowed} = {}) { function isValid(cb, storageType) { let mod = moduleName; const curBidder = config.getCurrentBidder(); @@ -105,9 +139,9 @@ export function newStorageManager({moduleName, moduleType} = {}, {isAllowed = is function storageMethods(name) { const capName = name.charAt(0).toUpperCase() + name.substring(1); - const backend = () => window[name]; + const backend = () => window[name] as any; - const hasStorage = function (done) { + const hasStorage: AcceptsCallback<() => boolean> = function (done) { let cb = function (result) { if (result && result.valid) { try { @@ -119,7 +153,7 @@ export function newStorageManager({moduleName, moduleType} = {}, {isAllowed = is return false; } return schedule(cb, STORAGE_TYPE_LOCALSTORAGE, done); - } + } as any; return { [`has${capName}`]: hasStorage, @@ -205,7 +239,7 @@ export function newStorageManager({moduleName, moduleType} = {}, {isAllowed = is ...storageMethods('localStorage'), ...storageMethods('sessionStorage'), findSimilarCookies - } + } as StorageManager; } /** @@ -215,7 +249,11 @@ export function newStorageManager({moduleName, moduleType} = {}, {isAllowed = is * for `{moduleType: 'bidder', moduleName: bidderCode}`. * */ -export function getStorageManager({moduleType, moduleName, bidderCode} = {}) { +export function getStorageManager({moduleType, moduleName, bidderCode}: { + moduleType?: ModuleType; + moduleName?: string; + bidderCode?: BidderCode; +} = {}) { function err() { throw new Error(`Invalid invocation for getStorageManager: must set either bidderCode, or moduleType + moduleName`) } diff --git a/src/targeting.js b/src/targeting.ts similarity index 59% rename from src/targeting.js rename to src/targeting.ts index b4c4ebf7cf5..cb8e9eb731c 100644 --- a/src/targeting.js +++ b/src/targeting.ts @@ -1,37 +1,39 @@ -import { auctionManager } from './auctionManager.js'; -import { getBufferedTTL } from './bidTTL.js'; -import { bidderSettings } from './bidderSettings.js'; -import { config } from './config.js'; +import {auctionManager} from './auctionManager.js'; +import {getBufferedTTL} from './bidTTL.js'; +import {bidderSettings} from './bidderSettings.js'; +import {config} from './config.js'; import { - BID_STATUS, - DEFAULT_TARGETING_KEYS, - EVENTS, - JSON_MAPPING, - NATIVE_KEYS, - TARGETING_KEYS + BID_STATUS, + DEFAULT_TARGETING_KEYS, + EVENTS, + JSON_MAPPING, + NATIVE_KEYS, + TARGETING_KEYS } from './constants.js'; import * as events from './events.js'; -import { hook } from './hook.js'; -import { ADPOD } from './mediaTypes.js'; -import { NATIVE_TARGETING_KEYS } from './native.js'; +import {hook} from './hook.js'; +import {ADPOD} from './mediaTypes.js'; +import {NATIVE_TARGETING_KEYS} from './native.js'; import { - deepAccess, - deepClone, - groupBy, - isAdUnitCodeMatchingSlot, - isArray, - isFn, - isGptPubadsDefined, - isStr, - logError, - logInfo, - logMessage, - logWarn, - sortByHighestCpm, - timestamp, - uniques, + deepAccess, + deepClone, + groupBy, + isAdUnitCodeMatchingSlot, + isArray, + isFn, + isStr, + logError, + logInfo, + logMessage, + logWarn, + sortByHighestCpm, + timestamp, + uniques, } from './utils.js'; -import { getHighestCpm, getOldestHighestCpmBid } from './utils/reducers.js'; +import {getHighestCpm, getOldestHighestCpmBid} from './utils/reducers.js'; +import type {Bid} from "./bidfactory.ts"; +import type {AdUnitCode, ByAdUnit, Identifier} from "./types/common.d.ts"; +import type {DefaultTargeting} from "./auction.ts"; var pbTargetingKeys = []; @@ -133,12 +135,11 @@ export function sortByDealAndPriceBucketOrCpm(useCpm = false) { /** * Return a map where each code in `adUnitCodes` maps to a list of GPT slots that match it. * - * @param {Array} adUnitCodes + * @param adUnitCodes * @param customSlotMatching * @param getSlots - * @return {Object.} */ -export function getGPTSlotsForAdUnits(adUnitCodes, customSlotMatching, getSlots = () => window.googletag.pubads().getSlots()) { +export function getGPTSlotsForAdUnits(adUnitCodes: AdUnitCode[], customSlotMatching, getSlots = () => window.googletag.pubads().getSlots()): ByAdUnit { return getSlots().reduce((auToSlots, slot) => { const customMatch = isFn(customSlotMatching) && customSlotMatching(slot); Object.keys(auToSlots).filter(isFn(customMatch) ? customMatch : isAdUnitCodeMatchingSlot(slot)).forEach(au => auToSlots[au].push(slot)); @@ -146,63 +147,237 @@ export function getGPTSlotsForAdUnits(adUnitCodes, customSlotMatching, getSlots }, Object.fromEntries(adUnitCodes.map(au => [au, []]))); } -/** - * Clears targeting for bids - */ -function clearTargeting(slot) { - pbTargetingKeys.forEach(key => { - if (slot.getTargeting(key)) { - slot.clearTargeting(key) +export type TargetingMap = Partial & { + [targetingKey: string]: V +} + +export type TargetingValues = TargetingMap; +type GPTTargetingValues = TargetingMap; + +type TargetingValueLists = TargetingMap; +type TargetingArray = ByAdUnit[]; + +type AdUnitPredicate = (adUnitCode: AdUnitCode) => boolean; +export type SlotMatchingFn = (slot: googletag.Slot) => AdUnitPredicate; + +declare module './events' { + interface Events { + [EVENTS.SET_TARGETING]: [ByAdUnit]; } - }) } -/** - * @typedef {Object.} targeting - * @property {string} targeting_key - */ +export interface TargetingControlsConfig { + /** + * Specifies the maximum number of characters the system can add to ad server targeting. + */ + auctionKeyMaxChars?: number; + /** + * If enableSendAllBids is false, set this value to true to ensure that deals are sent along with the winning bid + */ + alwaysIncludeDeals?: boolean; + /** + * Selects supported default targeting keys. + */ + allowTargetingKeys?: (keyof DefaultTargeting)[]; + /** + * Selects targeting keys to be supported in addition to the default ones + */ + addTargetingKeys?: (keyof DefaultTargeting)[]; + /** + * Selects supported default targeting keys. + */ + allowSendAllBidsTargetingKeys?: (keyof DefaultTargeting)[]; + /** + * Set to false to prevent custom targeting values from being set for non-winning bids + */ + allBidsCustomTargeting?: boolean +} -/** - * @typedef {Object.[]>[]} targetingArray - */ +declare module './config' { + interface Config { + targetingControls?: TargetingControlsConfig; + } +} export function newTargeting(auctionManager) { - let targeting = {}; let latestAuctionForAdUnit = {}; - targeting.setLatestAuctionForAdUnit = function(adUnitCode, auctionId) { - latestAuctionForAdUnit[adUnitCode] = auctionId; - }; - - targeting.resetPresetTargeting = function(adUnitCode, customSlotMatching) { - if (isGptPubadsDefined()) { - const adUnitCodes = getAdUnitCodes(adUnitCode); - Object.values(getGPTSlotsForAdUnits(adUnitCodes, customSlotMatching)).forEach((slots) => { - slots.forEach(slot => { - clearTargeting(slot) - }) - }) - } - }; - - targeting.resetPresetTargetingAST = function(adUnitCode) { - const adUnitCodes = getAdUnitCodes(adUnitCode); - adUnitCodes.forEach(function(unit) { - const astTag = window.apntag.getTag(unit); - if (astTag && astTag.keywords) { - const currentKeywords = Object.keys(astTag.keywords); - const newKeywords = {}; - currentKeywords.forEach((key) => { - if (!pbTargetingKeys.includes(key.toLowerCase())) { - newKeywords[key] = astTag.keywords[key]; + const targeting = { + setLatestAuctionForAdUnit(adUnitCode: AdUnitCode, auctionId: Identifier) { + latestAuctionForAdUnit[adUnitCode] = auctionId; + }, + + resetPresetTargetingAST(adUnitCode?: AdUnitCode | AdUnitCode[]) { + const adUnitCodes = getAdUnitCodes(adUnitCode); + adUnitCodes.forEach(function(unit) { + const astTag = window.apntag.getTag(unit); + if (astTag && astTag.keywords) { + const currentKeywords = Object.keys(astTag.keywords); + const newKeywords = {}; + currentKeywords.forEach((key) => { + if (!pbTargetingKeys.includes(key.toLowerCase())) { + newKeywords[key] = astTag.keywords[key]; + } + }) + window.apntag.modifyTag(unit, { keywords: newKeywords }) + } + }); + }, + + /** + * Returns all ad server targeting for all ad units. + * @param adUnitCode + * @param bidLimit + * @param bidsReceived - The received bids, defaulting to the result of getBidsReceived(). + * @param [winReducer = getHighestCpm] - reducer method + * @param [winSorter = sortByHighestCpm] - sorter method + * @return targeting + */ + getAllTargeting(adUnitCode?: AdUnitCode | AdUnitCode[], bidLimit?: number, bidsReceived?: Bid[], winReducer = getHighestCpm, winSorter = sortByHighestCpm): ByAdUnit { + bidsReceived ||= getBidsReceived(winReducer, winSorter); + const adUnitCodes = getAdUnitCodes(adUnitCode); + const sendAllBids = config.getConfig('enableSendAllBids'); + const bidLimitConfigValue = config.getConfig('sendBidsControl.bidLimit'); + const adUnitBidLimit = (sendAllBids && (bidLimit || bidLimitConfigValue)) || 0; + const { customKeysByUnit, filteredBids } = getfilteredBidsAndCustomKeys(adUnitCodes, bidsReceived); + const bidsSorted = getHighestCpmBidsFromBidPool(filteredBids, winReducer, adUnitBidLimit, undefined, winSorter); + let targeting = getTargetingLevels(bidsSorted, customKeysByUnit, adUnitCodes); + + const defaultKeys = Object.keys(Object.assign({}, DEFAULT_TARGETING_KEYS, NATIVE_KEYS)); + let allowedKeys = config.getConfig(CFG_ALLOW_TARGETING_KEYS); + const addedKeys = config.getConfig(CFG_ADD_TARGETING_KEYS); + + if (addedKeys != null && allowedKeys != null) { + throw new Error(TARGETING_KEY_CONFIGURATION_ERROR_MSG); + } else if (addedKeys != null) { + allowedKeys = defaultKeys.concat(addedKeys) as any; + } else { + allowedKeys = allowedKeys || defaultKeys as any; } - }) - window.apntag.modifyTag(unit, { keywords: newKeywords }) - } - }); - }; - function addBidToTargeting(bids, enableSendAllBids = false, deals = false) { + if (Array.isArray(allowedKeys) && allowedKeys.length > 0) { + targeting = getAllowedTargetingKeyValues(targeting, allowedKeys); + } + + let flatTargeting = flattenTargeting(targeting); + + const auctionKeysThreshold = config.getConfig('targetingControls.auctionKeyMaxChars'); + if (auctionKeysThreshold) { + logInfo(`Detected 'targetingControls.auctionKeyMaxChars' was active for this auction; set with a limit of ${auctionKeysThreshold} characters. Running checks on auction keys...`); + flatTargeting = filterTargetingKeys(flatTargeting, auctionKeysThreshold); + } + + // make sure at least there is a entry per adUnit code in the targetingSet so receivers of SET_TARGETING call's can know what ad units are being invoked + adUnitCodes.forEach(code => { + if (!flatTargeting[code]) { + flatTargeting[code] = {}; + } + }); + + return flatTargeting; + }, + + setTargetingForGPT: hook('sync', function (adUnit?: AdUnitCode | AdUnitCode[], customSlotMatching?: SlotMatchingFn) { + // get our ad unit codes + let targetingSet: ByAdUnit = targeting.getAllTargeting(adUnit); + + let resetMap = Object.fromEntries(pbTargetingKeys.map(key => [key, null])); + + Object.entries(getGPTSlotsForAdUnits(Object.keys(targetingSet), customSlotMatching)).forEach(([targetId, slots]) => { + slots.forEach(slot => { + // now set new targeting keys + Object.keys(targetingSet[targetId]).forEach(key => { + let value: string | string[] = targetingSet[targetId][key]; + if (typeof value === 'string' && value.indexOf(',') !== -1) { + // due to the check the array will be formed only if string has ',' else plain string will be assigned as value + value = value.split(','); + } + targetingSet[targetId][key] = value; + }); + logMessage(`Attempting to set targeting-map for slot: ${slot.getSlotElementId()} with targeting-map:`, targetingSet[targetId]); + slot.updateTargetingFromMap(Object.assign({}, resetMap, targetingSet[targetId])) + }) + }) + + Object.keys(targetingSet).forEach((adUnitCode) => { + Object.keys(targetingSet[adUnitCode]).forEach((targetingKey) => { + if (targetingKey === 'hb_adid') { + auctionManager.setStatusForBids(targetingSet[adUnitCode][targetingKey], BID_STATUS.BID_TARGETING_SET); + } + }); + }); + + targeting.targetingDone(targetingSet); + + // emit event + events.emit(EVENTS.SET_TARGETING, targetingSet); + }, 'setTargetingForGPT'), + + targetingDone: hook('sync', function (targetingSet: ByAdUnit) { + return targetingSet; + }, 'targetingDone'), + + /** + * Returns top bids for a given adUnit or set of adUnits. + * @param adUnitCode adUnitCode or array of adUnitCodes + * @param bids - The received bids, defaulting to the result of getBidsReceived(). + * @param [winReducer = getHighestCpm] - reducer method + * @param [winSorter = sortByHighestCpm] - sorter method + * @return An array of winning bids. + */ + getWinningBids(adUnitCode: AdUnitCode | AdUnitCode[], bids?: Bid[], winReducer = getHighestCpm, winSorter = sortByHighestCpm): Bid[] { + const bidsReceived = bids || getBidsReceived(winReducer, winSorter); + const adUnitCodes = getAdUnitCodes(adUnitCode); + + return bidsReceived + .filter(bid => adUnitCodes.includes(bid.adUnitCode)) + .filter(bid => (bidderSettings.get(bid.bidderCode, 'allowZeroCpmBids') === true) ? bid.cpm >= 0 : bid.cpm > 0) + .map(bid => bid.adUnitCode) + .filter(uniques) + .map(adUnitCode => bidsReceived + .filter(bid => bid.adUnitCode === adUnitCode ? bid : null) + .reduce(getHighestCpm)); + }, + + /** + * @param adUnitCodes adUnitCode or array of adUnitCodes + * Sets targeting for AST + */ + setTargetingForAst(adUnitCodes?: AdUnitCode | AdUnitCode[]) { + let astTargeting = targeting.getAllTargeting(adUnitCodes); + + try { + targeting.resetPresetTargetingAST(adUnitCodes); + } catch (e) { + logError('unable to reset targeting for AST' + e) + } + + Object.keys(astTargeting).forEach(targetId => + Object.keys(astTargeting[targetId]).forEach(key => { + logMessage(`Attempting to set targeting for targetId: ${targetId} key: ${key} value: ${astTargeting[targetId][key]}`); + // setKeywords supports string and array as value + if (isStr(astTargeting[targetId][key]) || isArray(astTargeting[targetId][key])) { + let keywordsObj = {}; + let regex = /pt[0-9]/; + if (key.search(regex) < 0) { + keywordsObj[key.toUpperCase()] = astTargeting[targetId][key]; + } else { + // pt${n} keys should not be uppercased + keywordsObj[key] = astTargeting[targetId][key]; + } + window.apntag.setKeywords(targetId, keywordsObj, { overrideKeyValue: true }); + } + }) + ); + }, + isApntagDefined() { + if (window.apntag && isFn(window.apntag.setKeywords)) { + return true; + } + }, + } + + function addBidToTargeting(bids, enableSendAllBids = false, deals = false): TargetingArray { const standardKeys = FEATURES.NATIVE ? TARGETING_KEYS_ARR.concat(NATIVE_TARGETING_KEYS) : TARGETING_KEYS_ARR.slice(); const allowSendAllBidsTargetingKeys = config.getConfig('targetingControls.allowSendAllBidsTargetingKeys'); @@ -232,11 +407,11 @@ export function newTargeting(auctionManager) { /** * Returns filtered ad server targeting for custom and allowed keys. - * @param {targetingArray} targeting - * @param {string[]} allowedKeys - * @return {targetingArray} filtered targeting + * @param targeting + * @param allowedKeys + * @return filtered targeting */ - function getAllowedTargetingKeyValues(targeting, allowedKeys) { + function getAllowedTargetingKeyValues(targeting: TargetingArray, allowedKeys: string[]): TargetingArray { const defaultKeyring = Object.assign({}, TARGETING_KEYS, NATIVE_KEYS); const defaultKeys = Object.keys(defaultKeyring); const keyDispositions = {}; @@ -272,55 +447,6 @@ export function newTargeting(auctionManager) { return filteredTargeting } - /** - * Returns all ad server targeting for all ad units. - * @param {string=} adUnitCode - * @return {Object.} targeting - */ - targeting.getAllTargeting = function(adUnitCode, bidLimit, bidsReceived, winReducer = getHighestCpm, winSorter = sortByHighestCpm) { - bidsReceived ||= getBidsReceived(winReducer, winSorter); - const adUnitCodes = getAdUnitCodes(adUnitCode); - const sendAllBids = config.getConfig('enableSendAllBids'); - const bidLimitConfigValue = config.getConfig('sendBidsControl.bidLimit'); - const adUnitBidLimit = (sendAllBids && (bidLimit || bidLimitConfigValue)) || 0; - const { customKeysByUnit, filteredBids } = getfilteredBidsAndCustomKeys(adUnitCodes, bidsReceived); - const bidsSorted = getHighestCpmBidsFromBidPool(filteredBids, winReducer, adUnitBidLimit, undefined, winSorter); - let targeting = getTargetingLevels(bidsSorted, customKeysByUnit, adUnitCodes); - - const defaultKeys = Object.keys(Object.assign({}, DEFAULT_TARGETING_KEYS, NATIVE_KEYS)); - let allowedKeys = config.getConfig(CFG_ALLOW_TARGETING_KEYS); - const addedKeys = config.getConfig(CFG_ADD_TARGETING_KEYS); - - if (addedKeys != null && allowedKeys != null) { - throw new Error(TARGETING_KEY_CONFIGURATION_ERROR_MSG); - } else if (addedKeys != null) { - allowedKeys = defaultKeys.concat(addedKeys); - } else { - allowedKeys = allowedKeys || defaultKeys; - } - - if (Array.isArray(allowedKeys) && allowedKeys.length > 0) { - targeting = getAllowedTargetingKeyValues(targeting, allowedKeys); - } - - targeting = flattenTargeting(targeting); - - const auctionKeysThreshold = config.getConfig('targetingControls.auctionKeyMaxChars'); - if (auctionKeysThreshold) { - logInfo(`Detected 'targetingControls.auctionKeyMaxChars' was active for this auction; set with a limit of ${auctionKeysThreshold} characters. Running checks on auction keys...`); - targeting = filterTargetingKeys(targeting, auctionKeysThreshold); - } - - // make sure at least there is a entry per adUnit code in the targetingSet so receivers of SET_TARGETING call's can know what ad units are being invoked - adUnitCodes.forEach(code => { - if (!targeting[code]) { - targeting[code] = {}; - } - }); - - return targeting; - }; - function updatePBTargetingKeys(adUnitCode) { (Object.keys(adUnitCode)).forEach(key => { adUnitCode[key].forEach(targetKey => { @@ -399,7 +525,7 @@ export function newTargeting(auctionManager) { }, ''); } - function filterTargetingKeys(targeting, auctionKeysThreshold) { + function filterTargetingKeys(targeting: ByAdUnit, auctionKeysThreshold: number) { // read each targeting.adUnit object and sort the adUnits into a list of adUnitCodes based on priorization setting (eg CPM) let targetingCopy = deepClone(targeting); @@ -461,11 +587,11 @@ export function newTargeting(auctionManager) { * } * ``` * - * @param {targetingArray} targeting - * @return {Object.} targeting + * @param targeting + * @return targeting */ - function flattenTargeting(targeting) { - let targetingObj = targeting.map(targeting => { + function flattenTargeting(targeting: TargetingArray): ByAdUnit { + return targeting.map(targeting => { return { [Object.keys(targeting)[0]]: targeting[Object.keys(targeting)[0]] .map(target => { @@ -474,67 +600,19 @@ export function newTargeting(auctionManager) { }; }).reduce((p, c) => Object.assign(c, p), {}) }; - }) - - targetingObj = targetingObj.reduce(function (accumulator, targeting) { + }).reduce(function (accumulator, targeting) { var key = Object.keys(targeting)[0]; accumulator[key] = Object.assign({}, accumulator[key], targeting[key]); return accumulator; }, {}); - - return targetingObj; } - targeting.setTargetingForGPT = hook('sync', function (adUnit, customSlotMatching) { - // get our ad unit codes - let targetingSet = targeting.getAllTargeting(adUnit); - - let resetMap = Object.fromEntries(pbTargetingKeys.map(key => [key, null])); - - Object.entries(getGPTSlotsForAdUnits(Object.keys(targetingSet), customSlotMatching)).forEach(([targetId, slots]) => { - if (slots.length > 1) { - // This can lead to duplicate impressions. This is existing behavior and changing to only target one slot could be a breaking change for existing integrations. - logWarn(`Multiple slots found matching: ${targetId}. Targeting will be set on all matching slots, which can lead to duplicate impressions if more than one are requested from GAM. To resolve this, ensure the arguments to setTargetingForGPTAsync resolve to a single slot by explicitly matching the desired slotElementID.`); - } - slots.forEach(slot => { - // now set new targeting keys - Object.keys(targetingSet[targetId]).forEach(key => { - let value = targetingSet[targetId][key]; - if (typeof value === 'string' && value.indexOf(',') !== -1) { - // due to the check the array will be formed only if string has ',' else plain string will be assigned as value - value = value.split(','); - } - targetingSet[targetId][key] = value; - }); - logMessage(`Attempting to set targeting-map for slot: ${slot.getSlotElementId()} with targeting-map:`, targetingSet[targetId]); - slot.updateTargetingFromMap(Object.assign({}, resetMap, targetingSet[targetId])) - }) - }) - - Object.keys(targetingSet).forEach((adUnitCode) => { - Object.keys(targetingSet[adUnitCode]).forEach((targetingKey) => { - if (targetingKey === 'hb_adid') { - auctionManager.setStatusForBids(targetingSet[adUnitCode][targetingKey], BID_STATUS.BID_TARGETING_SET); - } - }); - }); - - targeting.targetingDone(targetingSet); - - // emit event - events.emit(EVENTS.SET_TARGETING, targetingSet); - }, 'setTargetingForGPT'); - - targeting.targetingDone = hook('sync', function (targetingSet) { - return targetingSet; - }, 'targetingDone'); - /** * normlizes input to a `adUnit.code` array - * @param {(string|string[])} adUnitCode [description] - * @return {string[]} AdUnit code array + * @param adUnitCode + * @return AdUnit code array */ - function getAdUnitCodes(adUnitCode) { + function getAdUnitCodes(adUnitCode?: AdUnitCode | AdUnitCode[]): AdUnitCode[] { if (typeof adUnitCode === 'string') { return [adUnitCode]; } else if (isArray(adUnitCode)) { @@ -560,74 +638,20 @@ export function newTargeting(auctionManager) { return bids; }, []); - return getHighestCpmBidsFromBidPool(bidsReceived, winReducer, undefined, undefined, undefined, winSorter); + return getHighestCpmBidsFromBidPool(bidsReceived, winReducer, undefined, undefined, winSorter); } - /** - * Returns top bids for a given adUnit or set of adUnits. - * @param {(string|string[])} adUnitCode adUnitCode or array of adUnitCodes - * @param {(Array|undefined)} bids - The received bids, defaulting to the result of getBidsReceived(). - * @param {function(Array): Array} [winReducer = getHighestCpm] - reducer method - * @param {function(Array): Array} [winSorter = sortByHighestCpm] - sorter method - * @return {Array} - An array of winning bids. - */ - targeting.getWinningBids = function(adUnitCode, bids, winReducer = getHighestCpm, winSorter = sortByHighestCpm) { - const bidsReceived = bids || getBidsReceived(winReducer, winSorter); - const adUnitCodes = getAdUnitCodes(adUnitCode); - - return bidsReceived - .filter(bid => adUnitCodes.includes(bid.adUnitCode)) - .filter(bid => (bidderSettings.get(bid.bidderCode, 'allowZeroCpmBids') === true) ? bid.cpm >= 0 : bid.cpm > 0) - .map(bid => bid.adUnitCode) - .filter(uniques) - .map(adUnitCode => bidsReceived - .filter(bid => bid.adUnitCode === adUnitCode ? bid : null) - .reduce(getHighestCpm)); - }; - - /** - * @param {(string|string[])} adUnitCodes adUnitCode or array of adUnitCodes - * Sets targeting for AST - */ - targeting.setTargetingForAst = function(adUnitCodes) { - let astTargeting = targeting.getAllTargeting(adUnitCodes); - - try { - targeting.resetPresetTargetingAST(adUnitCodes); - } catch (e) { - logError('unable to reset targeting for AST' + e) - } - - Object.keys(astTargeting).forEach(targetId => - Object.keys(astTargeting[targetId]).forEach(key => { - logMessage(`Attempting to set targeting for targetId: ${targetId} key: ${key} value: ${astTargeting[targetId][key]}`); - // setKeywords supports string and array as value - if (isStr(astTargeting[targetId][key]) || isArray(astTargeting[targetId][key])) { - let keywordsObj = {}; - let regex = /pt[0-9]/; - if (key.search(regex) < 0) { - keywordsObj[key.toUpperCase()] = astTargeting[targetId][key]; - } else { - // pt${n} keys should not be uppercased - keywordsObj[key] = astTargeting[targetId][key]; - } - window.apntag.setKeywords(targetId, keywordsObj, { overrideKeyValue: true }); - } - }) - ); - }; - /** * Get targeting key value pairs for winning bid. - * @param {Array} bidsReceived code array - * @param {string[]} adUnitCodes code array - * @return {targetingArray} winning bids targeting + * @param bidsReceived code array + * @param adUnitCodes code array + * @return winning bids targeting */ - function getWinningBidTargeting(bidsReceived, adUnitCodes) { + function getWinningBidTargeting(bidsReceived, adUnitCodes): TargetingArray { let winners = targeting.getWinningBids(adUnitCodes, bidsReceived); let standardKeys = getStandardKeys(); - winners = winners.map(winner => { + return winners.map(winner => { return { [winner.adUnitCode]: Object.keys(winner.adserverTargeting) .filter(key => @@ -646,8 +670,6 @@ export function newTargeting(auctionManager) { }, []) }; }); - - return winners; } function getStandardKeys() { @@ -670,9 +692,9 @@ export function newTargeting(auctionManager) { * Get custom targeting key value pairs for bids. * @param {Array} bidsSorted code array * @param {Object} customKeysByUnit code array - * @return {targetingArray} bids with custom targeting defined in bidderSettings + * @return bids with custom targeting defined in bidderSettings */ - function getCustomBidTargeting(bidsSorted, customKeysByUnit) { + function getCustomBidTargeting(bidsSorted: Bid[], customKeysByUnit: ByAdUnit): TargetingArray { return bidsSorted .reduce((acc, bid) => { const newBid = Object.assign({}, bid); @@ -691,7 +713,7 @@ export function newTargeting(auctionManager) { }, []); } - function getTargetingMap(bid, keys) { + function getTargetingMap(bid: Bid, keys: string[]): TargetingValueLists[] { return keys.reduce((targeting, key) => { const value = bid.adserverTargeting[key]; if (value) { @@ -727,12 +749,6 @@ export function newTargeting(auctionManager) { }, []); } - targeting.isApntagDefined = function() { - if (window.apntag && isFn(window.apntag.setKeywords)) { - return true; - } - }; - return targeting; } diff --git a/src/types/common.d.ts b/src/types/common.d.ts new file mode 100644 index 00000000000..58817052ca6 --- /dev/null +++ b/src/types/common.d.ts @@ -0,0 +1,45 @@ +import type {DeepPartial} from "./objects.d.ts"; +import type {ORTBRequest} from "./ortb/request.d.ts"; + +/** + * Prebid-generated identifier. + */ +export type Identifier = string; +/** + * A bidder code. + */ +export type BidderCode = string; +export type BidSource = 's2s' | 'client'; +export type Currency = string; +export type AdUnitCode = string; +export type Size = [number, number]; +export type ContextIdentifiers = { + /** + * Auction ID. Unique for any given auction, but shared across all requests and responses within that auction. + */ + auctionId: Identifier; + /** + * Transaction ID. Unique for any given impression opportunity (every auction presents an opportunity for each slot), + * but shared across all bid requests and responses for that impression opportunity. + */ + transactionId: Identifier; + /** + * Ad unit ID. Similar to transaction IDs in that any slot and auction pair will have different IDs, but unlike transactions, + * twin ad units will have different ad unit IDs. + */ + adUnitId: Identifier; +} +export type ORTBFragments = { + /** + * Global first party data for this auction. + */ + global?: DeepPartial; + /** + * Bidder-specific first party data for this auction (mapped by bidder). + */ + bidder?: { + [bidderCode: BidderCode]: DeepPartial + } +} + +export type ByAdUnit = { [adUnit: AdUnitCode]: T }; diff --git a/src/types/functions.d.ts b/src/types/functions.d.ts new file mode 100644 index 00000000000..e01405e37f9 --- /dev/null +++ b/src/types/functions.d.ts @@ -0,0 +1,2 @@ +export type AnyFunction = (...args: any[]) => any; +export type Wraps = (...args: Parameters) => ReturnType; diff --git a/src/types/local/buildOptions.d.ts b/src/types/local/buildOptions.d.ts new file mode 100644 index 00000000000..d57cf8468be --- /dev/null +++ b/src/types/local/buildOptions.d.ts @@ -0,0 +1,6 @@ +export {}; +declare global { + const FEATURES : { + [tag: string]: boolean + } +} diff --git a/src/types/local/gpt.d.ts b/src/types/local/gpt.d.ts new file mode 100644 index 00000000000..8e693c5e65b --- /dev/null +++ b/src/types/local/gpt.d.ts @@ -0,0 +1,7 @@ +// include GPT type definitions, but only locally, so that we don't pollute +// the global namespace for pubs that are not using it. +// Using these types from our public API works as intended if the consumer +// also includes the GPT typedefs, otherwise the ts compiler appears to swap in "any". + +// eslint-disable-next-line prebid/validate-imports +import 'google-publisher-tag'; diff --git a/src/types/local/shim.d.ts b/src/types/local/shim.d.ts new file mode 100644 index 00000000000..d40bd2e3410 --- /dev/null +++ b/src/types/local/shim.d.ts @@ -0,0 +1,10 @@ +export {}; +declare global { + // the es5 lib declarations only accept strings as input + function parseInt(n: number, radix?: number): number; + function parseFloat(n: number): number; + + interface Window { + apntag: any; + } +} diff --git a/src/types/objects.d.ts b/src/types/objects.d.ts new file mode 100644 index 00000000000..83d84c296d9 --- /dev/null +++ b/src/types/objects.d.ts @@ -0,0 +1,27 @@ +export type DeepPartial = { + [K in keyof T]?: T[K] extends object ? DeepPartial : T[K]; +}; + +type DotNotation = `${PREFIX}.${REST}`; + +export type DeepProperty = keyof T | { + [K in keyof T]: T[K] extends object + ? DotNotation> + : never +}[keyof T]; + +export type TypeOfDeepProperty> = + K extends keyof T + ? T[K] + : K extends DotNotation + ? PREFIX extends keyof T + ? T[PREFIX] extends object + ? REST extends DeepProperty ? + TypeOfDeepProperty : never + : never + : never + : never; + +export type DeepPropertyName = K extends DotNotation + ? DeepPropertyName + : K; diff --git a/src/types/ortb/common.d.ts b/src/types/ortb/common.d.ts new file mode 100644 index 00000000000..7d4c6737bb9 --- /dev/null +++ b/src/types/ortb/common.d.ts @@ -0,0 +1,5 @@ +export type Ext = { [key: string]: unknown }; +export type Extensible = { + ext?: Ext; +} +export type BooleanInt = 0 | 1; diff --git a/src/types/ortb/ext/dchain.d.ts b/src/types/ortb/ext/dchain.d.ts new file mode 100644 index 00000000000..6b2bc57f9f0 --- /dev/null +++ b/src/types/ortb/ext/dchain.d.ts @@ -0,0 +1,78 @@ +// https://iabtechlab.com/wp-content/uploads/2021/03/DemandChainObject-1-0.pdf + +import type {BooleanInt, Extensible} from "../common.d.ts"; + +export type DemandChainNode = Extensible & { + /** + * The canonical domain name of the DSP or other buyer system + * that is generating the bid response. This should be the same + * location that hosts a buyers.json file. + * This field is required for any ASI that is involved in the + * programmatic demand chain, but may be omitted for buy-side + * entities involved in the payment flow prior to reaching the + * first DSP. + * If present, must be a hostname or domain, not full URL. + * Correct: domain.com. + * Incorrect: https://domain.com. + */ + asi?: string; + /** + * The identifier associated with the buyer seat within the + * advertising system. This must contain the same value, if any, + * used in transactions (i.e. BidResponse.SeatBid.Seat in + * OpenRTB bid responses), and must be a value that appears as + * a buyer_id in the buyers.json file. Should be limited to 64 + * characters in length. + * This field is required for any ASI that is involved in the + * programmatic demand chain, but may be omitted when the + * asi itself is omitted, that is for buy-side entities involved in the + * payment flow prior to reaching the first DSP + */ + bsid?: string; + /** + * The OpenRTB bid request or auction ID (i.e. BidRequest.id) of + * the request as issued by this seller. + */ + rid?: string; + /** + * The name of the company (the legal entity) that is paying + * under the given bsid. This value is recommended but should + * NOT be included if it exists in the advertising system’s + * buyers.json file (and is not marked confidential there). It + * MUST be included if the asi is absent or null. + */ + name?: string; + /** + * The business domain name of the entity represented by this + * node. This value is recommended but should NOT be included + * if it exists in the advertising system’s buyers.json file (and is + * not marked confidential there). It MUST be included if the asi + * is absent or null, unless the buyer has literally no web presence. + */ + domain?: string; +} + +export type DemandChain = Extensible & { + /** + * Flag indicating whether the chain contains all nodes involved + * in the transaction leading back to the originator and ultimate + * source of payment for the creative, where 0 = no, 1 = yes. + */ + complete: BooleanInt; + /** + * Array of DemandChainNode objects in the order of the chain. + * In a complete demand chain, the first node represents the + * initial advertising system and buyer ID involved in the + * transaction, i.e. the originator and ultimate source of + * payment for the creative. In an incomplete demand chain, it + * represents the first known node. The last node represents + * the entity sending this bid response. + */ + nodes: DemandChainNode[]; + /** + * Version of the DemandChain specification in use, in the + * format of “major.minor”. For example, for version 1.0 of the + * spec, use the string “1.0” (numeric values are invalid) + */ + ver: string; +} diff --git a/src/types/ortb/ext/dsa.d.ts b/src/types/ortb/ext/dsa.d.ts new file mode 100644 index 00000000000..c2df405bf02 --- /dev/null +++ b/src/types/ortb/ext/dsa.d.ts @@ -0,0 +1,60 @@ +// https://github.com/InteractiveAdvertisingBureau/openrtb/blob/main/extensions/community_extensions/dsa_transparency.md + +export type DSATransparency = Partial<{ + /** + * Domain of the entity that applied user parameters + */ + domain: string; + /** + * Array for platform or sell-side use of any user parameters (using the list provided by DSA Transparency Taskforce). + */ + dsaparams: number[]; +}>; + +export type DSARequest = Partial<{ + /** + * 0 = Not required + * 1 = Supported, bid responses with or without DSA object will be accepted + * 2 = Required, bid responses without DSA object will not be accepted + * 3 = Required, bid responses without DSA object will not be accepted, Publisher is an Online Platform + */ + dsarequired: 0 | 1 | 2 | 3; + /** + * 0 = Publisher can't render + * 1 = Publisher could render depending on adrender + * 2 = Publisher will render + */ + pubrender: 0 | 1 | 2; + /** + * 0 = do not send transparency data + * 1 = optional to send transparency data + * 2 = send transparency data + */ + datatopub: 0 | 1 | 2; + /** + * Array of objects of the entities that applied user parameters and the parameters they applied. + */ + transparency: DSATransparency[]; +}>; + +export type DSAResponse = Partial<{ + /** + * Advertiser Transparency: Free UNICODE text string with a name of whose behalf the ad is displayed. Maximum 100 characters. + */ + behalf: string; + /** + * Advertiser Transparency: Free UNICODE text string of who paid for the ad. + * Must always be included even if it's the same as what is listed in the behalf attribute. + * Maximum 100 characters + */ + paid: string; + /** + * Array of objects of the entities that applied user parameters and the parameters they applied. + */ + transparency: DSATransparency[]; + /** + * 0 = buyer/advertiser will not render + * 1 = buyer/advertiser will render + */ + adrender: 0 | 1; +}> diff --git a/src/types/ortb/native.d.ts b/src/types/ortb/native.d.ts new file mode 100644 index 00000000000..ed099d10b11 --- /dev/null +++ b/src/types/ortb/native.d.ts @@ -0,0 +1,6 @@ +import type {EventTrackerResponse as oEventTrackerResponse, NativeResponse as oNativeResponse, LinkResponse, NativeRequest as oNativeRequest} from 'iab-native'; + +export type NativeRequest = oNativeRequest; +export type EventTrackerResponse = oEventTrackerResponse; +export type NativeResponse = oNativeResponse; +export type Link = LinkResponse; diff --git a/src/types/ortb/request.d.ts b/src/types/ortb/request.d.ts new file mode 100644 index 00000000000..291b5a57a83 --- /dev/null +++ b/src/types/ortb/request.d.ts @@ -0,0 +1,33 @@ +/* eslint prebid/validate-imports: 0 */ + +import type {Ext} from './common.d.ts'; +import type {DSARequest} from "./ext/dsa.d.ts"; + +import type {BidRequest, Imp} from 'iab-openrtb/v26'; + +export interface ORTBRequest extends BidRequest { + ext: Ext & { + dsa?: DSARequest + } +} + +export type ORTBImp = Imp & { + video?: Imp['video'] & { + // placement & sequence are deprecated in 2.6 and not in the iab-openrtb types, so we replicate them here + /** + * @deprecated - use plcmt instead. + */ + placement?: number; + /** + * @deprecated - use slotinpod instead. + */ + sequence?: number; + }; + ext?: Ext & { + /** + * Transaction ID for this imp. Unique for each impression opportunity (slot & auction) + * but common across all requests for that opportunity. + */ + tid?: string; + } +}; diff --git a/src/types/ortb/response.d.ts b/src/types/ortb/response.d.ts new file mode 100644 index 00000000000..22a3fea8ec6 --- /dev/null +++ b/src/types/ortb/response.d.ts @@ -0,0 +1,18 @@ +import type {BidResponse, SeatBid, Bid} from "iab-openrtb/v26"; +import type {Ext} from './common.d.ts'; +import type {DSAResponse} from "./ext/dsa.d.ts"; +import type {DemandChain} from "./ext/dchain.d.ts"; + +export interface ORTBBid extends Bid { + ext: Ext & { + dsa?: DSAResponse + dchain?: DemandChain + } +} +export interface ORTBSeatBid extends SeatBid { + bid: ORTBBid[]; +} + +export interface ORTBResponse extends BidResponse { + seatbid?: ORTBSeatBid[]; +} diff --git a/src/types/ortb2.d.ts b/src/types/ortb2.d.ts deleted file mode 100644 index f38545c0c31..00000000000 --- a/src/types/ortb2.d.ts +++ /dev/null @@ -1,59 +0,0 @@ -/** - * @see https://iabtechlab.com/standards/openrtb/ - */ -export namespace Ortb2 { - type Site = { - page?: string; - ref?: string; - domain?: string; - publisher?: { - domain?: string; - }; - keywords?: string; - ext?: Record; - }; - - type Device = { - w?: number; - h?: number; - dnt?: 0 | 1; - ua?: string; - language?: string; - sua?: { - source?: number; - platform?: unknown; - browsers?: unknown[]; - mobile?: number; - }; - ext?: { - webdriver?: true; - [key: string]: unknown; - }; - }; - - type Regs = { - coppa?: unknown; - ext?: { - gdpr?: unknown; - us_privacy?: unknown; - [key: string]: unknown; - }; - }; - - type User = { - ext?: Record; - }; - - /** - * Ortb2 info provided in bidder request. Some of the sections are mutually exclusive. - * @see clientSectionChecker - */ - type BidRequest = { - device?: Device; - regs?: Regs; - user?: User; - site?: Site; - app?: unknown; - dooh?: unknown; - }; -} diff --git a/src/types/summary/core.d.ts b/src/types/summary/core.d.ts new file mode 100644 index 00000000000..ba4619f781f --- /dev/null +++ b/src/types/summary/core.d.ts @@ -0,0 +1 @@ +// this file is replaced with an autogenerated one during precompilation diff --git a/src/types/summary/exports.d.ts b/src/types/summary/exports.d.ts new file mode 100644 index 00000000000..f44277fe613 --- /dev/null +++ b/src/types/summary/exports.d.ts @@ -0,0 +1,10 @@ +export type {PrebidJS} from '../../prebidGlobal.ts'; +// Type definitions (besides the prebid global) that may be useful to consumers, +export type {Bid, VideoBid, BannerBid, NativeBid} from '../../bidfactory.ts'; +export type {BidRequest, BidderRequest} from '../../adapterManager.ts'; +export type {Config} from '../../config.ts'; +export type {AdUnit, AdUnitDefinition, AdUnitBid} from '../../adUnits.ts' +export type {ORTBRequest, ORTBImp} from '../ortb/request.d.ts'; +export type {ORTBResponse} from '../ortb/response.d.ts'; +export type {NativeRequest as ORTBNativeRequest} from '../ortb/native.d.ts'; +export type {Event, EventRecord, EventPayload, EventHandler} from '../../events.ts'; diff --git a/src/types/summary/global.d.ts b/src/types/summary/global.d.ts new file mode 100644 index 00000000000..ba4619f781f --- /dev/null +++ b/src/types/summary/global.d.ts @@ -0,0 +1 @@ +// this file is replaced with an autogenerated one during precompilation diff --git a/src/types/summary/modules.d.ts b/src/types/summary/modules.d.ts new file mode 100644 index 00000000000..ba4619f781f --- /dev/null +++ b/src/types/summary/modules.d.ts @@ -0,0 +1 @@ +// this file is replaced with an autogenerated one during precompilation diff --git a/src/types/summary/types.d.ts b/src/types/summary/types.d.ts new file mode 100644 index 00000000000..c214dabbcfb --- /dev/null +++ b/src/types/summary/types.d.ts @@ -0,0 +1,6 @@ +import './core.d.ts'; +import './modules.d.ts'; + +// export the complete public API type definition - for those that are interested in just the +// types and do not automatically import the right modules +export type * from './exports.d.ts'; diff --git a/src/types/tuples.d.ts b/src/types/tuples.d.ts new file mode 100644 index 00000000000..53ed56fd86c --- /dev/null +++ b/src/types/tuples.d.ts @@ -0,0 +1,17 @@ +type IfUnbounded = number extends T['length'] ? THEN : ELSE; +type AddUntil = T['length'] extends L ? T : AddUntil; +/** + * Last => type of last element in tuple T + */ +export type Last = + [...T] extends [...rest: any[], last: infer R] ? R : IfUnbounded +/** + * AllExceptLast => Type of tuple that matches T except it drops the last element + */ +export type AllExceptLast = + [...T] extends [...rest: infer R, last: any] ? R : IfUnbounded; + +/** + * Repeat => tuple of length L where each member is of type T + */ +export type Repeat = number extends L ? T[] : AddUntil; diff --git a/src/userSync.js b/src/userSync.ts similarity index 88% rename from src/userSync.js rename to src/userSync.ts index b6cd2097b14..3e1f7738ee2 100644 --- a/src/userSync.js +++ b/src/userSync.ts @@ -14,8 +14,49 @@ import { } from './activities/params.js'; import {MODULE_TYPE_BIDDER} from './activities/modules.js'; import {activityParams} from './activities/activityParams.js'; +import type {BidderCode} from "./types/common.d.ts"; -export const USERSYNC_DEFAULT_CONFIG = { +export type SyncType = 'image' | 'iframe'; +type SyncConfig = { + bidders: '*' | BidderCode[]; + filter: 'include' | 'exclude' +} +type FilterSettings = {[K in SyncType | 'all']?: SyncConfig}; + +export interface UserSyncConfig { + /** + * Enable/disable the user syncing feature. Default: true. + */ + syncEnabled?: boolean; + /** + * Configure lists of adapters to include or exclude their user syncing based on the pixel type (image/iframe). + */ + filterSettings?: FilterSettings; + /** + * Number of registered syncs allowed per adapter. Default: 5. To allow all, set to 0. + */ + syncsPerBidder?: number; + /** + * Delay in milliseconds for user syncing (both bid adapter user sync pixels and userId module ID providers) + * after the auction ends. Default: 3000. Ignored by the userId module if auctionDelay > 0. + */ + syncDelay?: number; + /** + * Delay in milliseconds of the auction to retrieve user ids via the userId module before the auction starts. + * Continues auction once all IDs are retrieved or delay times out. Does not apply to bid adapter user sync pixels. Default: 0. + */ + auctionDelay?: number; + /** + * Enable/disable publisher to trigger user syncs by calling pbjs.triggerUserSyncs(). Default: false. + */ + enableOverride?: boolean; + /** + * Enable/disable registered syncs for aliased adapters. Default: false. + */ + aliasSyncEnabled?: boolean; +} + +export const USERSYNC_DEFAULT_CONFIG: UserSyncConfig = { syncEnabled: true, filterSettings: { image: { @@ -42,7 +83,7 @@ const storage = getCoreStorageManager('usersync'); * UserSync object needs in order to behave properly. */ export function newUserSync(deps) { - let publicApi = {}; + let publicApi: any = {}; // A queue of user syncs for each adapter // Let getDefaultQueue() set the defaults let queue = getDefaultQueue(); diff --git a/src/utils.js b/src/utils.js index 6d6573dd061..ea5a2ef2d1a 100644 --- a/src/utils.js +++ b/src/utils.js @@ -1,20 +1,14 @@ import {config} from './config.js'; -import {klona} from 'klona/json'; import {EVENTS} from './constants.js'; import {PbPromise} from './utils/promise.js'; -import {getGlobal} from './prebidGlobal.js'; import { default as deepAccess } from 'dlv/index.js'; +import {isArray, isFn, isStr, isPlainObject} from './utils/objects.js'; export { deepAccess }; export { dset as deepSetValue } from 'dset'; +export * from './utils/objects.js' -var tStr = 'String'; -var tFn = 'Function'; -var tNumb = 'Number'; -var tObject = 'Object'; -var tBoolean = 'Boolean'; -var toString = Object.prototype.toString; let consoleExists = Boolean(window.console); let consoleLogExists = Boolean(consoleExists && window.console.log); let consoleInfoExists = Boolean(consoleExists && window.console.info); @@ -24,8 +18,6 @@ let consoleErrorExists = Boolean(consoleExists && window.console.error); let eventEmitter; let windowDimensions; -const pbjsInstance = getGlobal(); - export function _setEventEmitter(emitFn) { // called from events.js - this hoop is to avoid circular imports eventEmitter = emitFn; @@ -377,39 +369,6 @@ export function getParameterByName(name) { return parseQS(getWindowLocation().search)[name] || ''; } -/** - * Return if the object is of the - * given type. - * @param {*} object to test - * @param {String} _t type string (e.g., Array) - * @return {Boolean} if object is of type _t - */ -export function isA(object, _t) { - return toString.call(object) === '[object ' + _t + ']'; -} - -export function isFn(object) { - return isA(object, tFn); -} - -export function isStr(object) { - return isA(object, tStr); -} - -export const isArray = Array.isArray.bind(Array); - -export function isNumber(object) { - return isA(object, tNumb); -} - -export function isPlainObject(object) { - return isA(object, tObject); -} - -export function isBoolean(object) { - return isA(object, tBoolean); -} - /** * Return if the object is "empty"; * this includes falsey, no keys, or no items at indices @@ -642,7 +601,7 @@ export function getValue(obj, key) { return obj[key]; } -export function getBidderCodes(adUnits = pbjsInstance.adUnits) { +export function getBidderCodes(adUnits) { // this could memoize adUnits return adUnits.map(unit => unit.bids.map(bid => bid.bidder) .reduce(flatten, [])).reduce(flatten, []).filter((bidder) => typeof bidder !== 'undefined').filter(uniques); @@ -690,10 +649,6 @@ export function shuffle(array) { return array; } -export function deepClone(obj) { - return klona(obj) || {}; -} - export function inIframe() { try { return internal.getWindowSelf() !== internal.getWindowTop(); @@ -844,30 +799,10 @@ export function groupBy(xs, key) { }, {}); } -/** - * Build an object consisting of only defined parameters to avoid creating an - * object with defined keys and undefined values. - * @param {Object} object The object to pick defined params out of - * @param {string[]} params An array of strings representing properties to look for in the object - * @returns {Object} An object containing all the specified values that are defined - */ -export function getDefinedParams(object, params) { - return params - .filter(param => object[param]) - .reduce((bid, param) => Object.assign(bid, { [param]: object[param] }), {}); -} - -/** - * @typedef {Object} MediaTypes - * @property {Object} banner banner configuration - * @property {Object} native native configuration - * @property {Object} video video configuration - */ - /** * Validates an adunit's `mediaTypes` parameter - * @param {MediaTypes} mediaTypes mediaTypes parameter to validate - * @return {boolean} If object is valid + * @param mediaTypes mediaTypes parameter to validate + * @return If object is valid */ export function isValidMediaTypes(mediaTypes) { const SUPPORTED_MEDIA_TYPES = ['banner', 'native', 'video']; @@ -912,8 +847,8 @@ export const compareCodeAndSlot = (slot, adUnitCode) => slot.getAdUnitPath() === /** * Returns filter function to match adUnitCode in slot - * @param {Object} slot GoogleTag slot - * @return {function} filter function + * @param slot GoogleTag slot + * @return filter function */ export function isAdUnitCodeMatchingSlot(slot) { return (adUnitCode) => compareCodeAndSlot(slot, adUnitCode); @@ -935,13 +870,6 @@ export function unsupportedBidderMessage(adUnit, bidder) { `; } -/** - * Checks input is integer or not - * https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Number/isInteger - * @param {*} value - */ -export const isInteger = Number.isInteger.bind(Number); - /** * Returns a new object with undefined properties removed from given object * @param obj the object to clean @@ -984,10 +912,6 @@ export function pick(obj, properties) { }, {}); } -export function isArrayOfNums(val, size) { - return (isArray(val)) && ((size) ? val.length === size : true) && (val.every(v => isInteger(v))); -} - export function parseQS(query) { return !query ? {} : query .replace(/^\?/, '') @@ -1216,7 +1140,7 @@ export function safeJSONEncode(data) { * @param fn * @param key cache key generator, invoked with the same arguments passed to `fn`. * By default, the first argument is used as key. - * @return {function(): any} + * @return {*} */ export function memoize(fn, key = function (arg) { return arg; }) { const cache = new Map(); diff --git a/src/utils/cpm.js b/src/utils/cpm.js index 7601d0643fd..7dfabbe53be 100644 --- a/src/utils/cpm.js +++ b/src/utils/cpm.js @@ -8,7 +8,6 @@ export function adjustCpm(cpm, bidResponse, bidRequest, {index = auctionManager. const bidderCode = bidResponse?.bidderCode || bidRequest?.bidder; const adjustAlternateBids = bs.get(bidResponse?.adapterCode, 'adjustAlternateBids'); const bidCpmAdjustment = bs.getOwn(bidderCode, 'bidCpmAdjustment') || bs.get(adjustAlternateBids ? adapterCode : bidderCode, 'bidCpmAdjustment'); - if (bidCpmAdjustment && typeof bidCpmAdjustment === 'function') { try { return bidCpmAdjustment(cpm, Object.assign({}, bidResponse), bidRequest); diff --git a/src/utils/focusTimeout.js b/src/utils/focusTimeout.js index 196f324d6b9..894c646625e 100644 --- a/src/utils/focusTimeout.js +++ b/src/utils/focusTimeout.js @@ -26,7 +26,7 @@ export function reset() { /** * Wraps native setTimeout function in order to count time only when page is focused * - * @param {function(): void} [callback] - A function that will be invoked after the passed time + * @param {function(void): void} [callback] - A function that will be invoked after passed time * @param {number} [milliseconds] - Minimum duration (in milliseconds) that the callback will be executed after * @returns {function(): number} - Getter function for current timer id */ diff --git a/src/utils/objects.ts b/src/utils/objects.ts new file mode 100644 index 00000000000..393e405e919 --- /dev/null +++ b/src/utils/objects.ts @@ -0,0 +1,70 @@ +import {klona} from "klona/json"; +import type {AnyFunction} from "../types/functions.d.ts"; +import type {Repeat} from "../types/tuples.d.ts"; + +export function deepClone(obj: T): T { + return (klona(obj) || {}) as T; +} + +/** + * Build an object consisting of only defined parameters to avoid creating an + * object with defined keys and undefined values. + * @param object The object to pick defined params out of + * @param params An array of strings representing properties to look for in the object + * @returns An object containing all the specified values that are defined + */ +export function getDefinedParams(object: T, params: K): Partial> { + return params + .filter(param => object[param]) + .reduce((bid, param) => Object.assign(bid, { [param]: object[param] }), {}); +} + +const tStr = 'String'; +const tFn = 'Function'; +const tNumb = 'Number'; +const tObject = 'Object'; +const tBoolean = 'Boolean'; +const toString = Object.prototype.toString; + +/** + * Return if the object is of the + * given type. + * @param {*} object to test + * @param {String} _t type string (e.g., Array) + * @return {Boolean} if object is of type _t + */ +export function isA(object, _t) { + return toString.call(object) === '[object ' + _t + ']'; +} + +export function isFn(object): object is AnyFunction { + return isA(object, tFn); +} + +export function isStr(object): object is string { + return isA(object, tStr); +} + +export const isArray: (object) => object is any[] = Array.isArray.bind(Array); + +export function isNumber(object): object is number { + return isA(object, tNumb); +} + +export function isPlainObject(object): object is Record { + return isA(object, tObject); +} + +export function isBoolean(object): object is boolean { + return isA(object, tBoolean); +} + +/** + * Checks input is integer or not + * https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Number/isInteger + */ +export const isInteger: (value) => value is number = Number.isInteger.bind(Number); + +export function isArrayOfNums(val, size?: L): val is Repeat { + return (isArray(val)) && ((size) ? val.length === size : true) && (val.every(v => isInteger(v))); +} diff --git a/src/utils/perfMetrics.js b/src/utils/perfMetrics.js deleted file mode 100644 index d717306be49..00000000000 --- a/src/utils/perfMetrics.js +++ /dev/null @@ -1,387 +0,0 @@ -import {config} from '../config.js'; -export const CONFIG_TOGGLE = 'performanceMetrics'; -const getTime = window.performance && window.performance.now ? () => window.performance.now() : () => Date.now(); -const NODES = new WeakMap(); - -export function metricsFactory({now = getTime, mkNode = makeNode, mkTimer = makeTimer, mkRenamer = (rename) => rename, nodes = NODES} = {}) { - return function newMetrics() { - function makeMetrics(self, rename = (n) => ({forEach(fn) { fn(n); }})) { - rename = mkRenamer(rename); - - function accessor(slot) { - return function (name) { - return self.dfWalk({ - visit(edge, node) { - const obj = node[slot]; - if (obj.hasOwnProperty(name)) { - return obj[name]; - } - } - }); - }; - } - - const getTimestamp = accessor('timestamps'); - - /** - * Register a metric. - * - * @param name metric name - * @param value metric valiue - */ - function setMetric(name, value) { - const names = rename(name); - self.dfWalk({ - follow(inEdge, outEdge) { - return outEdge.propagate && (!inEdge || !inEdge.stopPropagation) - }, - visit(edge, node) { - names.forEach(name => { - if (edge == null) { - node.metrics[name] = value; - } else { - if (!node.groups.hasOwnProperty(name)) { - node.groups[name] = []; - } - node.groups[name].push(value); - } - }) - } - }); - } - - /** - * Mark the current time as a checkpoint with the given name, to be referenced later - * by `timeSince` or `timeBetween`. - * - * @param name checkpoint name - */ - function checkpoint(name) { - self.timestamps[name] = now(); - } - - /** - * Get the tame passed since `checkpoint`, and optionally save it as a metric. - * - * @param {string} checkpoint checkpoint name - * @param {string} [metric] - The name of the metric to save. Optional. - * @returns {number|null} - The time in milliseconds between now and the checkpoint, or `null` if the checkpoint is not found. - */ - function timeSince(checkpoint, metric) { - const ts = getTimestamp(checkpoint); - const elapsed = ts != null ? now() - ts : null; - if (metric != null) { - setMetric(metric, elapsed); - } - return elapsed; - } - - /** - * Get the time passed between `startCheckpoint` and `endCheckpoint`, optionally saving it as a metric. - * - * @param {string} startCheckpoint - The name of the starting checkpoint. - * @param {string} endCheckpoint - The name of the ending checkpoint. - * @param {string} [metric] - The name of the metric to save. Optional. - * @returns {number|null} - The time in milliseconds between `startCheckpoint` and `endCheckpoint`, or `null` if either checkpoint is not found. - */ - function timeBetween(startCheckpoint, endCheckpoint, metric) { - const start = getTimestamp(startCheckpoint); - const end = getTimestamp(endCheckpoint); - const elapsed = start != null && end != null ? end - start : null; - if (metric != null) { - setMetric(metric, elapsed); - } - return elapsed; - } - - /** - * A function that, when called, stops a time measure and saves it as a metric. - * - * @typedef {function(): void} MetricsTimer - * @template {function} F - * @property {function(F): F} stopBefore returns a wrapper around the given function that begins by - * stopping this time measure. - * @property {function(F): F} stopAfter returns a wrapper around the given function that ends by - * stopping this time measure. - */ - - /** - * Start measuring a time metric with the given name. - * - * @param name metric name - * @return {MetricsTimer} - */ - function startTiming(name) { - return mkTimer(now, (val) => setMetric(name, val)) - } - - /** - * Run fn and measure the time spent in it. - * - * @template T - * @param name the name to use for the measured time metric - * @param {function(): T} fn - * @return {T} the return value of `fn` - */ - function measureTime(name, fn) { - return startTiming(name).stopAfter(fn)(); - } - - /** - * @typedef {Function} HookFn - * @property {(function(T): void)} bail - * - * @template T - * @typedef {HookFn} TimedHookFn - * @property {(function(): void)} stopTiming - * @property {T} untimed - */ - - /** - * Convenience method for measuring time spent in a `.before` or `.after` hook. - * - * @template T - * @param {string} name - The metric name. - * @param {HookFn} next - The hook's `next` (first) argument. - * @param {function(TimedHookFn): T} fn - A function that will be run immediately; it takes `next`, - * where both `next` and `next.bail` automatically - * call `stopTiming` before continuing with the original hook. - * @return {T} - The return value of `fn`. - */ - function measureHookTime(name, next, fn) { - const stopTiming = startTiming(name); - return fn((function (orig) { - const next = stopTiming.stopBefore(orig); - next.bail = orig.bail && stopTiming.stopBefore(orig.bail); - next.stopTiming = stopTiming; - next.untimed = orig; - return next; - })(next)); - } - - /** - * Get all registered metrics. - * @return {{}} - */ - function getMetrics() { - let result = {} - self.dfWalk({ - visit(edge, node) { - result = Object.assign({}, !edge || edge.includeGroups ? node.groups : null, node.metrics, result); - } - }); - return result; - } - - /** - * Create and return a new metrics object that starts as a view on all metrics registered here, - * and - by default - also propagates all new metrics here. - * - * Propagated metrics are grouped together, and intended for repeated operations. For example, with the following: - * - * ``` - * const metrics = newMetrics(); - * const requests = metrics.measureTime('buildRequests', buildRequests) - * requests.forEach((req) => { - * const requestMetrics = metrics.fork(); - * requestMetrics.measureTime('processRequest', () => processRequest(req); - * }) - * ``` - * - * if `buildRequests` takes 10ms and returns 3 objects, which respectively take 100, 200, and 300ms in `processRequest`, then - * the final `metrics.getMetrics()` would be: - * - * ``` - * { - * buildRequests: 10, - * processRequest: [100, 200, 300] - * } - * ``` - * - * while the inner `requestMetrics.getMetrics()` would be: - * - * ``` - * { - * buildRequests: 10, - * processRequest: 100 // or 200 for the 2nd loop, etc - * } - * ``` - * - * - * @param {Object} [options={}] - Options for forking the metrics. - * @param {boolean} [options.propagate=true] - If false, the forked metrics will not be propagated here. - * @param {boolean} [options.stopPropagation=false] - If true, propagation from the new metrics is stopped here, instead of - * continuing up the chain (if for example these metrics were themselves created through `.fork()`). - * @param {boolean} [options.includeGroups=false] - If true, the forked metrics will also replicate metrics that were propagated - * here from elsewhere. For example: - * ``` - * const metrics = newMetrics(); - * const op1 = metrics.fork(); - * const withoutGroups = metrics.fork(); - * const withGroups = metrics.fork({includeGroups: true}); - * op1.setMetric('foo', 'bar'); - * withoutGroups.getMetrics() // {} - * withGroups.getMetrics() // {foo: ['bar']} - * ``` - * @returns {Object} - The new metrics object. - */ - function fork({propagate = true, stopPropagation = false, includeGroups = false} = {}) { - return makeMetrics(mkNode([[self, {propagate, stopPropagation, includeGroups}]]), rename); - } - - /** - * Join `otherMetrics` with these; all metrics from `otherMetrics` will (by default) be propagated here, - * and all metrics from here will be included in `otherMetrics`. - * - * `propagate`, `stopPropagation` and `includeGroups` have the same semantics as in `.fork()`. - */ - function join(otherMetrics, {propagate = true, stopPropagation = false, includeGroups = false} = {}) { - const other = nodes.get(otherMetrics); - if (other != null) { - other.addParent(self, {propagate, stopPropagation, includeGroups}); - } - } - - /** - * return a version of these metrics where all new metrics are renamed according to `renameFn`. - * - * @param {function(String): Array[String]} renameFn - */ - function renameWith(renameFn) { - return makeMetrics(self, renameFn); - } - - /** - * Create a new metrics object that uses the same propagation and renaming rules as this one. - */ - function newMetrics() { - return makeMetrics(self.newSibling(), rename); - } - - const metrics = { - startTiming, - measureTime, - measureHookTime, - checkpoint, - timeSince, - timeBetween, - setMetric, - getMetrics, - fork, - join, - newMetrics, - renameWith, - toJSON() { - return getMetrics(); - } - }; - nodes.set(metrics, self); - return metrics; - } - - return makeMetrics(mkNode([])); - } -} - -function wrapFn(fn, before, after) { - return function () { - before && before(); - try { - return fn.apply(this, arguments); - } finally { - after && after(); - } - }; -} - -function makeTimer(now, cb) { - const start = now(); - let done = false; - function stopTiming() { - if (!done) { - cb(now() - start); - done = true; - } - } - stopTiming.stopBefore = (fn) => wrapFn(fn, stopTiming); - stopTiming.stopAfter = (fn) => wrapFn(fn, null, stopTiming); - return stopTiming; -} - -function makeNode(parents) { - return { - metrics: {}, - timestamps: {}, - groups: {}, - addParent(node, edge) { - parents.push([node, edge]); - }, - newSibling() { - return makeNode(parents.slice()); - }, - dfWalk({visit, follow = () => true, visited = new Set(), inEdge} = {}) { - let res; - if (!visited.has(this)) { - visited.add(this); - res = visit(inEdge, this); - if (res != null) return res; - for (const [parent, outEdge] of parents) { - if (follow(inEdge, outEdge)) { - res = parent.dfWalk({visit, follow, visited, inEdge: outEdge}); - if (res != null) return res; - } - } - } - } - }; -} - -const nullMetrics = (() => { - const nop = function () {}; - const empty = () => ({}); - const none = {forEach: nop}; - const nullTimer = () => null; - nullTimer.stopBefore = (fn) => fn; - nullTimer.stopAfter = (fn) => fn; - const nullNode = Object.defineProperties( - {dfWalk: nop, newSibling: () => nullNode, addParent: nop}, - Object.fromEntries(['metrics', 'timestamps', 'groups'].map(prop => [prop, {get: empty}]))); - return metricsFactory({ - now: () => 0, - mkNode: () => nullNode, - mkRenamer: () => () => none, - mkTimer: () => nullTimer, - nodes: {get: nop, set: nop} - })(); -})(); - -let enabled = true; -config.getConfig(CONFIG_TOGGLE, (cfg) => { enabled = !!cfg[CONFIG_TOGGLE] }); - -/** - * convenience fallback function for metrics that may be undefined, especially during tests. - */ -export function useMetrics(metrics) { - return (enabled && metrics) || nullMetrics; -} - -export const newMetrics = (() => { - const makeMetrics = metricsFactory(); - return function () { - return enabled ? makeMetrics() : nullMetrics; - } -})(); - -export function hookTimer(prefix, getMetrics) { - return function(name, hookFn) { - return function (next, ...args) { - const that = this; - return useMetrics(getMetrics.apply(that, args)).measureHookTime(prefix + name, next, function (next) { - return hookFn.call(that, next, ...args); - }); - } - } -} - -export const timedAuctionHook = hookTimer('requestBids.', (req) => req.metrics); -export const timedBidResponseHook = hookTimer('addBidResponse.', (_, bid) => bid.metrics) diff --git a/src/utils/perfMetrics.ts b/src/utils/perfMetrics.ts new file mode 100644 index 00000000000..7bee4f8b66c --- /dev/null +++ b/src/utils/perfMetrics.ts @@ -0,0 +1,396 @@ +import {config} from '../config.js'; +import type {AnyFunction, Wraps} from "../types/functions.d.ts"; +import {type BeforeHook, type BeforeHookParams, type HookType, Next} from "../hook.ts"; +import type {addBidResponse} from "../auction.ts"; +import type {PrivRequestBidsOptions, StartAuctionOptions} from "../prebid.ts"; + +export const CONFIG_TOGGLE = 'performanceMetrics'; +const getTime = window.performance && window.performance.now ? () => window.performance.now() : () => Date.now(); +const NODES = new WeakMap(); + +export type Metrics = ReturnType>; + +/** + * A function that, when called, stops a time measure and saves it as a metric. + */ +export type MetricsTimer = { + (): void; + /** + * @return a wrapper around the given function that begins by stopping this time measure. + */ + stopBefore(fn: F): Wraps; + /** + * @return a wrapper around the given function that ends by stopping this time measure. + */ + stopAfter(fn: F): Wraps; +}; + +export type InstrumentedNext = Next & { + /** + * The original `next` argument; using it will not affect the timer. + */ + untimed: Next; + stopTiming: MetricsTimer; +} + +function wrapFn(fn: F, before?: () => void, after?: () => void): Wraps { + return function (...args) { + before && before(); + try { + return fn.apply(this, args); + } finally { + after && after(); + } + }; +} + +export function metricsFactory({now = getTime, mkNode = makeNode, mkTimer = makeTimer, mkRenamer = (rename) => rename, nodes = NODES} = {}) { + return function newMetrics() { + function makeMetrics(self, rename = (n) => ({forEach(fn) { fn(n); }})) { + rename = mkRenamer(rename); + + function accessor(slot) { + return function (name) { + return self.dfWalk({ + visit(edge, node) { + const obj = node[slot]; + if (obj.hasOwnProperty(name)) { + return obj[name]; + } + } + }); + }; + } + + const getTimestamp = accessor('timestamps'); + + /** + * Register a metric. + * + * @param name metric name + * @param value metric valiue + */ + function setMetric(name: string, value: unknown): void { + const names = rename(name); + self.dfWalk({ + follow(inEdge, outEdge) { + return outEdge.propagate && (!inEdge || !inEdge.stopPropagation) + }, + visit(edge, node) { + names.forEach(name => { + if (edge == null) { + node.metrics[name] = value; + } else { + if (!node.groups.hasOwnProperty(name)) { + node.groups[name] = []; + } + node.groups[name].push(value); + } + }) + } + }); + } + + /** + * Mark the current time as a checkpoint with the given name, to be referenced later + * by `timeSince` or `timeBetween`. + * + * @param name checkpoint name + */ + function checkpoint(name: string): void { + self.timestamps[name] = now(); + } + + /** + * Get the tame passed since `checkpoint`, and optionally save it as a metric. + * + * @param checkpoint checkpoint name + * @param metric The name of the metric to save. Optional. + * @return The time in milliseconds between now and the checkpoint, or `null` if the checkpoint is not found. + */ + function timeSince(checkpoint: string, metric?: string): number | null { + const ts = getTimestamp(checkpoint); + const elapsed = ts != null ? now() - ts : null; + if (metric != null) { + setMetric(metric, elapsed); + } + return elapsed; + } + + /** + * Get the time passed between `startCheckpoint` and `endCheckpoint`, optionally saving it as a metric. + * + * @param startCheckpoint - The name of the starting checkpoint. + * @param endCheckpoint - The name of the ending checkpoint. + * @param metric - The name of the metric to save. + * @return The time in milliseconds between `startCheckpoint` and `endCheckpoint`, or `null` if either checkpoint is not found. + */ + function timeBetween(startCheckpoint: string, endCheckpoint: string, metric?: string): number | null { + const start = getTimestamp(startCheckpoint); + const end = getTimestamp(endCheckpoint); + const elapsed = start != null && end != null ? end - start : null; + if (metric != null) { + setMetric(metric, elapsed); + } + return elapsed; + } + + /** + * Start measuring a time metric with the given name. + * + * @param name metric name + */ + function startTiming(name: string): MetricsTimer { + return mkTimer(now, (val) => setMetric(name, val)) + } + + /** + * Run fn and measure the time spent in it. + * + * @param name the name to use for the measured time metric + * @param fn the function to run + * @return the return value of `fn` + */ + function measureTime(name: string, fn: () => R): R { + return startTiming(name).stopAfter(fn)(); + } + + /** + * Convenience method for measuring time spent in a `.before` or `.after` hook. + * + * @param name - The metric name. + * @param next - The hook's `next` (first) argument. + * @param fn - A function that will be run immediately; it takes `next`, where both `next` and + * `next.bail` automatically call `stopTiming` before continuing with the original hook. + * @return The return value of `fn`. + */ + function measureHookTime(name: string, next: Next, fn: (next: InstrumentedNext) => R): R { + const stopTiming = startTiming(name); + return fn((function (orig) { + const next = stopTiming.stopBefore(orig) as InstrumentedNext; + next.bail = orig.bail && stopTiming.stopBefore(orig.bail); + next.stopTiming = stopTiming; + next.untimed = orig; + return next; + })(next)); + } + + /** + * Get all registered metrics. + */ + function getMetrics(): { [name: string]: unknown } { + let result = {} + self.dfWalk({ + visit(edge, node) { + result = Object.assign({}, !edge || edge.includeGroups ? node.groups : null, node.metrics, result); + } + }); + return result; + } + + type PropagationOptions = { + /** + * If false, the forked or joined metrics will not be propagated here. + */ + propagate?: boolean; + /** + * If true, propagation from the new metrics is stopped here, instead of + * continuing up the chain (if for example these metrics were themselves created through `.fork()`). + */ + stopPropagation?: boolean; + /** + * If true, the forked metrics will also replicate metrics that were propagated + * here from elsewhere. For example: + * ``` + * const metrics = newMetrics(); + * const op1 = metrics.fork(); + * const withoutGroups = metrics.fork(); + * const withGroups = metrics.fork({includeGroups: true}); + * op1.setMetric('foo', 'bar'); + * withoutGroups.getMetrics() // {} + * withGroups.getMetrics() // {foo: ['bar']} + * ``` + */ + includeGroups?: boolean; + }; + + /** + * Create and return a new metrics object that starts as a view on all metrics registered here, + * and - by default - also propagates all new metrics here. + * + * Propagated metrics are grouped together, and intended for repeated operations. For example, with the following: + * + * ``` + * const metrics = newMetrics(); + * const requests = metrics.measureTime('buildRequests', buildRequests) + * requests.forEach((req) => { + * const requestMetrics = metrics.fork(); + * requestMetrics.measureTime('processRequest', () => processRequest(req); + * }) + * ``` + * + * if `buildRequests` takes 10ms and returns 3 objects, which respectively take 100, 200, and 300ms in `processRequest`, then + * the final `metrics.getMetrics()` would be: + * + * ``` + * { + * buildRequests: 10, + * processRequest: [100, 200, 300] + * } + * ``` + * + * while the inner `requestMetrics.getMetrics()` would be: + * + * ``` + * { + * buildRequests: 10, + * processRequest: 100 // or 200 for the 2nd loop, etc + * } + * ``` + */ + function fork({propagate = true, stopPropagation = false, includeGroups = false}: PropagationOptions = {}): Metrics { + return makeMetrics(mkNode([[self, {propagate, stopPropagation, includeGroups}]]), rename); + } + + /** + * Join `otherMetrics` with these; all metrics from `otherMetrics` will (by default) be propagated here, + * and all metrics from here will be included in `otherMetrics`. + */ + function join(otherMetrics: Metrics, {propagate = true, stopPropagation = false, includeGroups = false}: PropagationOptions = {}): void { + const other = nodes.get(otherMetrics); + if (other != null) { + other.addParent(self, {propagate, stopPropagation, includeGroups}); + } + } + + /** + * @return a version of these metrics with the same propagation rules, but: + * - all metrics are renamed according to `renameFn`, or + * - without these metrics' rename rule (if `renameFn` is omitted). + */ + function renameWith(renameFn?: (name: string) => string[]): Metrics { + return makeMetrics(self, renameFn); + } + + /** + * @return a new metrics object that uses the same propagation and renaming rules as this one. + */ + function newMetrics(): Metrics { + return makeMetrics(self.newSibling(), rename); + } + + const metrics = { + startTiming, + measureTime, + measureHookTime, + checkpoint, + timeSince, + timeBetween, + setMetric, + getMetrics, + fork, + join, + newMetrics, + renameWith, + toJSON() { + return getMetrics(); + } + }; + nodes.set(metrics, self); + return metrics; + } + + return makeMetrics(mkNode([])); + } +} + +function makeTimer(now: () => number, cb: (elapsed: number) => void): MetricsTimer { + const start = now(); + let done = false; + function stopTiming() { + if (!done) { + cb(now() - start); + done = true; + } + } + stopTiming.stopBefore = (fn) => wrapFn(fn, stopTiming); + stopTiming.stopAfter = (fn) => wrapFn(fn, null, stopTiming); + return stopTiming; +} + +function makeNode(parents) { + return { + metrics: {}, + timestamps: {}, + groups: {}, + addParent(node, edge) { + parents.push([node, edge]); + }, + newSibling() { + return makeNode(parents.slice()); + }, + dfWalk({visit, follow = () => true, visited = new Set(), inEdge} = {} as any) { + let res; + if (!visited.has(this)) { + visited.add(this); + res = visit(inEdge, this); + if (res != null) return res; + for (const [parent, outEdge] of parents) { + if (follow(inEdge, outEdge)) { + res = parent.dfWalk({visit, follow, visited, inEdge: outEdge}); + if (res != null) return res; + } + } + } + } + }; +} + +const nullMetrics: Metrics = (() => { + const nop = function () {}; + const empty = () => ({}); + const none = {forEach: nop}; + const nullTimer = () => null; + nullTimer.stopBefore = (fn) => fn; + nullTimer.stopAfter = (fn) => fn; + const nullNode = Object.defineProperties( + {dfWalk: nop, newSibling: () => nullNode, addParent: nop}, + Object.fromEntries(['metrics', 'timestamps', 'groups'].map(prop => [prop, {get: empty}]))); + return metricsFactory({ + now: () => 0, + mkNode: () => nullNode as any, + mkRenamer: () => () => none, + mkTimer: () => nullTimer, + nodes: {get: nop, set: nop} as unknown as Map + })(); +})(); + +let enabled = true; +config.getConfig(CONFIG_TOGGLE, (cfg) => { enabled = !!cfg[CONFIG_TOGGLE] }); + +/** + * convenience fallback function for metrics that may be undefined, especially during tests. + */ +export function useMetrics(metrics: Metrics): Metrics { + return (enabled && metrics) || nullMetrics; +} + +export const newMetrics = (() => { + const makeMetrics = metricsFactory(); + return function (): Metrics { + return enabled ? makeMetrics() : nullMetrics; + } +})(); + +export function hookTimer(prefix: string, getMetrics: (...args: Parameters) => Metrics) { + return function(name: string, hookFn: (next: InstrumentedNext<(...args: BeforeHookParams) => ReturnType>, ...args: BeforeHookParams) => unknown): BeforeHook { + return (next, ...args) => { + return useMetrics(getMetrics.apply(this, args)).measureHookTime(prefix + name, next, (next) => { + return hookFn.call(this, next, ...args); + }); + } + } +} + +export const timedAuctionHook = hookTimer<'async', (options: PrivRequestBidsOptions | StartAuctionOptions) => void>('requestBids.', (req) => req.metrics); +export const timedBidResponseHook = hookTimer<'async', typeof addBidResponse>('addBidResponse.', (_, bid) => bid.metrics) diff --git a/src/utils/prerendering.js b/src/utils/prerendering.js deleted file mode 100644 index b89b8d895eb..00000000000 --- a/src/utils/prerendering.js +++ /dev/null @@ -1,22 +0,0 @@ -import {logInfo} from '../utils.js'; - -/** - * Returns a wrapper around fn that delays execution until the page if activated, if it was prerendered and isDelayEnabled returns true. - * https://developer.chrome.com/docs/web-platform/prerender-pages - */ -export function delayIfPrerendering(isDelayEnabled, fn) { - return function () { - if (document.prerendering && isDelayEnabled()) { - const that = this; - const args = Array.from(arguments); - return new Promise((resolve) => { - document.addEventListener('prerenderingchange', () => { - logInfo(`Auctions were suspended while page was prerendering`) - resolve(fn.apply(that, args)) - }, {once: true}) - }) - } else { - return Promise.resolve(fn.apply(this, arguments)); - } - } -} diff --git a/src/utils/prerendering.ts b/src/utils/prerendering.ts new file mode 100644 index 00000000000..088f98f7a41 --- /dev/null +++ b/src/utils/prerendering.ts @@ -0,0 +1,22 @@ +import {logInfo} from '../utils.js'; +import type {AnyFunction} from "../types/functions.d.ts"; +import type {UnwrapPromise, ToPromise} from "./promise.ts"; + +/** + * Returns a wrapper around fn that delays execution until the page if activated, if it was prerendered and isDelayEnabled returns true. + * https://developer.chrome.com/docs/web-platform/prerender-pages + */ +export function delayIfPrerendering(isDelayEnabled: () => boolean, fn: F): (...args: Parameters) => ToPromise> { + return (...args) => { + if ((document as any).prerendering && isDelayEnabled()) { + return new Promise>>((resolve) => { + document.addEventListener('prerenderingchange', () => { + logInfo(`Auctions were suspended while page was prerendering`) + resolve(fn.apply(this, args)) + }, {once: true}) + }) + } else { + return Promise.resolve>>(fn.apply(this, args)); + } + } +} diff --git a/src/utils/promise.js b/src/utils/promise.js deleted file mode 100644 index 5c13c09c78d..00000000000 --- a/src/utils/promise.js +++ /dev/null @@ -1,31 +0,0 @@ -import {GreedyPromise, greedySetTimeout} from '../../libraries/greedy/greedyPromise.js'; -import {getGlobal} from '../prebidGlobal.js'; - -export const pbSetTimeout = getGlobal().setTimeout ?? (FEATURES.GREEDY ? greedySetTimeout : setTimeout) -export const PbPromise = getGlobal().Promise ?? (FEATURES.GREEDY ? GreedyPromise : Promise); - -export function delay(delayMs = 0) { - return new PbPromise((resolve) => { - pbSetTimeout(resolve, delayMs); - }); -} - -/** - * @returns a {promise, resolve, reject} trio where `promise` is resolved by calling `resolve` or `reject`. - */ -export function defer({promiseFactory = (resolver) => new PbPromise(resolver)} = {}) { - function invoker(delegate) { - return (val) => delegate(val); - } - - let resolveFn, rejectFn; - - return { - promise: promiseFactory((resolve, reject) => { - resolveFn = resolve; - rejectFn = reject; - }), - resolve: invoker(resolveFn), - reject: invoker(rejectFn) - } -} diff --git a/src/utils/promise.ts b/src/utils/promise.ts new file mode 100644 index 00000000000..7de1d1e4df4 --- /dev/null +++ b/src/utils/promise.ts @@ -0,0 +1,55 @@ +import {GreedyPromise, greedySetTimeout} from '../../libraries/greedy/greedyPromise.js'; +import {getGlobal} from '../prebidGlobal.js'; + +declare module '../prebidGlobal' { + interface PrebidJS { + /** + * The setTimeout implementation Prebid should use. + */ + setTimeout?: typeof setTimeout; + /** + * The Promise constructor Prebid should use. + */ + Promise?: typeof Promise + } +} + +export const pbSetTimeout: typeof setTimeout = getGlobal().setTimeout ?? (FEATURES.GREEDY ? greedySetTimeout : setTimeout) +export const PbPromise: typeof Promise = getGlobal().Promise ?? (FEATURES.GREEDY ? GreedyPromise : Promise) as any; + +export function delay(delayMs = 0): Promise { + return new PbPromise((resolve) => { + pbSetTimeout(resolve, delayMs); + }); +} + +export interface Defer { + promise: Promise; + resolve: Parameters>[0]>[0], + reject: Parameters>[0]>[1], +} + +export type UnwrapPromise = T extends PromiseLike ? R : T; +export type ToPromise = Promise>; + +/** + * @returns a {promise, resolve, reject} trio where `promise` is resolved by calling `resolve` or `reject`. + */ +export function defer({promiseFactory = (resolver) => new PbPromise(resolver) as Promise}: { + promiseFactory?: (...args: ConstructorParameters>) => Promise +} = {}): Defer { + function invoker(delegate) { + return (val) => delegate(val); + } + + let resolveFn, rejectFn; + + return { + promise: promiseFactory((resolve, reject) => { + resolveFn = resolve; + rejectFn = reject; + }), + resolve: invoker(resolveFn), + reject: invoker(rejectFn) + } +} diff --git a/src/utils/ttlCollection.js b/src/utils/ttlCollection.ts similarity index 62% rename from src/utils/ttlCollection.js rename to src/utils/ttlCollection.ts index fb9ac3eaaa0..693d18af862 100644 --- a/src/utils/ttlCollection.js +++ b/src/utils/ttlCollection.ts @@ -2,40 +2,46 @@ import {PbPromise} from './promise.js'; import {binarySearch, logError, timestamp} from '../utils.js'; import {setFocusTimeout} from './focusTimeout.js'; +export type TTLCollection = ReturnType>; + /** * Create a set-like collection that automatically forgets items after a certain time. - * - * @param {Object} [options={}] - Optional settings - * @param {function(*): (number|Promise)} [options.startTime=timestamp] - A function taking an item added to this collection, - * and returning (a promise to) a timestamp to be used as the starting time for the item - * (the item will be dropped after `ttl(item)` milliseconds have elapsed since this timestamp). - * Defaults to the time the item was added to the collection. - * @param {function(*): (number|void|Promise)} [options.ttl=() => null] - A function taking an item added to this collection, - * and returning (a promise to) the duration (in milliseconds) the item should be kept in it. - * May return null to indicate that the item should be persisted indefinitely. - * @param {boolean} [options.monotonic=false] - Set to true for better performance, but only if, given any two items A and B in this collection: - * if A was added before B, then: - * - startTime(A) + ttl(A) <= startTime(B) + ttl(B) - * - Promise.all([startTime(A), ttl(A)]) never resolves later than Promise.all([startTime(B), ttl(B)]) - * @param {number} [options.slack=5000] - Maximum duration (in milliseconds) that an item is allowed to persist - * once past its TTL. This is also roughly the interval between "garbage collection" sweeps. - * @returns {Object} A set-like collection with automatic TTL expiration. - * @returns {function(*): void} return.add - Add an item to the collection. - * @returns {function(): void} return.clear - Clear the collection. - * @returns {function(): Array<*>} return.toArray - Get all the items in the collection, in insertion order. - * @returns {function(): void} return.refresh - Refresh the TTL for each item in the collection. - * @returns {(function(function(*): void): function(): void)} return.onExpiry - Register a callback to be run when an item has expired and is about to be - * removed from the collection. Returns an un-registration function */ -export function ttlCollection( +export function ttlCollection( { startTime = timestamp, ttl = () => null, monotonic = false, slack = 5000 + }: { + /** + * A function taking an item added to this collection, + * and returning (a promise to) a timestamp to be used as the starting time for the item + * (the item will be dropped after `ttl(item)` milliseconds have elapsed since this timestamp). + * Defaults to the time the item was added to the collection. + */ + startTime?: (item: T) => number | Promise; + /** + * A function taking an item added to this collection, + * and returning (a promise to) the duration (in milliseconds) the item should be kept in it. + * May return null to indicate that the item should be persisted indefinitely. + */ + ttl?: (item: T) => number | null | Promise; + /** + * Set to true for better performance, but only if, given any two items A and B in this collection: + * if A was added before B, then: + * - startTime(A) + ttl(A) <= startTime(B) + ttl(B) + * - Promise.all([startTime(A), ttl(A)]) never resolves later than Promise.all([startTime(B), ttl(B)]) + */ + monotonic?: boolean + /** + * Maximum duration (in milliseconds) that an item is allowed to persist + * once past its TTL. This is also roughly the interval between "garbage collection" sweeps. + */ + slack?: number; } = {} ) { - const items = new Map(); + const items = new Map(); const callbacks = []; const pendingPurge = []; const markForPurge = monotonic @@ -73,7 +79,7 @@ export function ttlCollection( } function mkEntry(item) { - const values = {}; + const values: any = {}; const thisCohort = currentCohort; let expiry; @@ -124,7 +130,7 @@ export function ttlCollection( * Add an item to this collection. * @param item */ - add(item) { + add(item: T) { !items.has(item) && items.set(item, mkEntry(item)); }, /** @@ -158,7 +164,7 @@ export function ttlCollection( * @param cb a callback that takes the expired item as argument * @return an unregistration function. */ - onExpiry(cb) { + onExpiry(cb: (item: T) => void) { callbacks.push(cb); return () => { const idx = callbacks.indexOf(cb); diff --git a/src/video.js b/src/video.js deleted file mode 100644 index 3df69d3aabd..00000000000 --- a/src/video.js +++ /dev/null @@ -1,142 +0,0 @@ -import {isArrayOfNums, isInteger, isNumber, isPlainObject, isStr, logError, logWarn} from './utils.js'; -import {config} from '../src/config.js'; -import {hook} from './hook.js'; -import {auctionManager} from './auctionManager.js'; - -export const OUTSTREAM = 'outstream'; -export const INSTREAM = 'instream'; - -/** - * List of OpenRTB 2.x video object properties with simple validators. - * Not included: `companionad`, `durfloors`, `ext` - * reference: https://github.com/InteractiveAdvertisingBureau/openrtb2.x/blob/main/2.6.md - */ -export const ORTB_VIDEO_PARAMS = new Map([ - [ 'mimes', value => Array.isArray(value) && value.length > 0 && value.every(v => typeof v === 'string') ], - [ 'minduration', isInteger ], - [ 'maxduration', isInteger ], - [ 'startdelay', isInteger ], - [ 'maxseq', isInteger ], - [ 'poddur', isInteger ], - [ 'protocols', isArrayOfNums ], - [ 'w', isInteger ], - [ 'h', isInteger ], - [ 'podid', isStr ], - [ 'podseq', isInteger ], - [ 'rqddurs', isArrayOfNums ], - [ 'placement', isInteger ], // deprecated, see plcmt - [ 'plcmt', isInteger ], - [ 'linearity', isInteger ], - [ 'skip', value => [1, 0].includes(value) ], - [ 'skipmin', isInteger ], - [ 'skipafter', isInteger ], - [ 'sequence', isInteger ], // deprecated - [ 'slotinpod', isInteger ], - [ 'mincpmpersec', isNumber ], - [ 'battr', isArrayOfNums ], - [ 'maxextended', isInteger ], - [ 'minbitrate', isInteger ], - [ 'maxbitrate', isInteger ], - [ 'boxingallowed', isInteger ], - [ 'playbackmethod', isArrayOfNums ], - [ 'playbackend', isInteger ], - [ 'delivery', isArrayOfNums ], - [ 'pos', isInteger ], - [ 'api', isArrayOfNums ], - [ 'companiontype', isArrayOfNums ], - [ 'poddedupe', isArrayOfNums ] -]); - -export function fillVideoDefaults(adUnit) { - const video = adUnit?.mediaTypes?.video; - if (video != null && video.plcmt == null) { - if (video.context === OUTSTREAM || [2, 3, 4].includes(video.placement)) { - video.plcmt = 4; - } else if (video.context !== OUTSTREAM && [2, 6].includes(video.playbackmethod)) { - video.plcmt = 2; - } - } -} - -/** - * validateOrtbVideoFields mutates the `adUnit.mediaTypes.video` object by removing invalid ortb properties (default). - * The onInvalidParam callback can be used to handle invalid properties differently. - * Other properties are ignored and kept as is. - * - * @param {Object} adUnit - The adUnit object. - * @param {Function} onInvalidParam - The callback function to be called with key, value, and adUnit. - * @returns {void} - */ -export function validateOrtbVideoFields(adUnit, onInvalidParam) { - const videoParams = adUnit?.mediaTypes?.video; - - if (!isPlainObject(videoParams)) { - logWarn(`validateOrtbVideoFields: videoParams must be an object.`); - return; - } - - if (videoParams != null) { - Object.entries(videoParams) - .forEach(([key, value]) => { - if (!ORTB_VIDEO_PARAMS.has(key)) { - return - } - const isValid = ORTB_VIDEO_PARAMS.get(key)(value); - if (!isValid) { - if (typeof onInvalidParam === 'function') { - onInvalidParam(key, value, adUnit); - } else { - delete videoParams[key]; - logWarn(`Invalid prop in adUnit "${adUnit.code}": Invalid value for mediaTypes.video.${key} ORTB property. The property has been removed.`); - } - } - }); - } -} - -/** - * @typedef {object} VideoBid - * @property {string} adId id of the bid - */ - -/** - * Validate that the assets required for video context are present on the bid - * @param {VideoBid} bid Video bid to validate - * @param {Object} [options] - Options object - * @param {Object} [options.index=auctionManager.index] - Index object, defaulting to `auctionManager.index` - * @return {Boolean} If object is valid - */ -export function isValidVideoBid(bid, {index = auctionManager.index} = {}) { - const videoMediaType = index.getMediaTypes(bid)?.video; - const context = videoMediaType && videoMediaType?.context; - const useCacheKey = videoMediaType && videoMediaType?.useCacheKey; - const adUnit = index.getAdUnit(bid); - - // if context not defined assume default 'instream' for video bids - // instream bids require a vast url or vast xml content - return checkVideoBidSetup(bid, adUnit, videoMediaType, context, useCacheKey); -} - -export const checkVideoBidSetup = hook('sync', function(bid, adUnit, videoMediaType, context, useCacheKey) { - if (videoMediaType && (useCacheKey || context !== OUTSTREAM)) { - // xml-only video bids require a prebid cache url - const { url, useLocal } = config.getConfig('cache') || {}; - if ((!url && !useLocal) && bid.vastXml && !bid.vastUrl) { - logError(` - This bid contains only vastXml and will not work when a prebid cache url is not specified. - Try enabling either prebid cache with $$PREBID_GLOBAL$$.setConfig({ cache: {url: "..."} }); - or local cache with $$PREBID_GLOBAL$$.setConfig({ cache: { useLocal: true }}); - `); - return false; - } - - return !!(bid.vastUrl || bid.vastXml); - } - - // outstream bids require a renderer on the bid or pub-defined on adunit - if (context === OUTSTREAM && !useCacheKey) { - return !!(bid.renderer || (adUnit && adUnit.renderer) || videoMediaType.renderer); - } - - return true; -}, 'checkVideoBidSetup'); diff --git a/src/video.ts b/src/video.ts new file mode 100644 index 00000000000..e90a7f1b639 --- /dev/null +++ b/src/video.ts @@ -0,0 +1,187 @@ +import {isArrayOfNums, isInteger, isNumber, isPlainObject, isStr, logError, logWarn} from './utils.js'; +import {config} from './config.js'; +import {hook} from './hook.js'; +import {auctionManager} from './auctionManager.js'; +import type {VideoBid} from "./bidfactory.ts"; +import {ADPOD, type BaseMediaType} from "./mediaTypes.ts"; +import type {ORTBImp} from "./types/ortb/request.d.ts"; +import type {Size} from "./types/common.d.ts"; +import type {AdUnitDefinition} from "./adUnits.ts"; + +export const OUTSTREAM = 'outstream'; +export const INSTREAM = 'instream'; + +const ORTB_PARAMS = [ + [ 'mimes', value => Array.isArray(value) && value.length > 0 && value.every(v => typeof v === 'string') ], + [ 'minduration', isInteger ], + [ 'maxduration', isInteger ], + [ 'startdelay', isInteger ], + [ 'maxseq', isInteger ], + [ 'poddur', isInteger ], + [ 'protocols', isArrayOfNums ], + [ 'w', isInteger ], + [ 'h', isInteger ], + [ 'podid', isStr ], + [ 'podseq', isInteger ], + [ 'rqddurs', isArrayOfNums ], + [ 'placement', isInteger ], // deprecated, see plcmt + [ 'plcmt', isInteger ], + [ 'linearity', isInteger ], + [ 'skip', value => [1, 0].includes(value) ], + [ 'skipmin', isInteger ], + [ 'skipafter', isInteger ], + [ 'sequence', isInteger ], // deprecated + [ 'slotinpod', isInteger ], + [ 'mincpmpersec', isNumber ], + [ 'battr', isArrayOfNums ], + [ 'maxextended', isInteger ], + [ 'minbitrate', isInteger ], + [ 'maxbitrate', isInteger ], + [ 'boxingallowed', isInteger ], + [ 'playbackmethod', isArrayOfNums ], + [ 'playbackend', isInteger ], + [ 'delivery', isArrayOfNums ], + [ 'pos', isInteger ], + [ 'api', isArrayOfNums ], + [ 'companiontype', isArrayOfNums ], + [ 'poddedupe', isArrayOfNums ] +] as const; + +/** + * List of OpenRTB 2.x video object properties with simple validators. + * Not included: `companionad`, `durfloors`, `ext` + * reference: https://github.com/InteractiveAdvertisingBureau/openrtb2.x/blob/main/2.6.md + */ +export const ORTB_VIDEO_PARAMS = new Map(ORTB_PARAMS); + +export type VideoContext = typeof INSTREAM | typeof OUTSTREAM | typeof ADPOD; + +export interface VideoMediaType extends BaseMediaType, Pick { + context: VideoContext; + playerSize?: Size | Size[]; +} + +export function fillVideoDefaults(adUnit: AdUnitDefinition) { + const video = adUnit?.mediaTypes?.video; + if (video != null) { + if (video.plcmt == null) { + if (video.context === OUTSTREAM || [2, 3, 4].includes(video.placement)) { + video.plcmt = 4; + } else if (video.playbackmethod?.some?.(method => [2, 6].includes(method))) { + video.plcmt = 2; + } + } + const playerSize = isArrayOfNums(video.playerSize, 2) + ? video.playerSize + : Array.isArray(video.playerSize) && isArrayOfNums(video.playerSize[0]) ? video.playerSize[0] : null; + const size: [number, number] = isNumber(video.w) && isNumber(video.h) ? [video.w, video.h] : null; + let conflict = false; + if (playerSize == null) { + if (size != null) { + if (video.playerSize != null) { + conflict = true; + } else { + video.playerSize = [size]; + } + } + } else { + ['w', 'h'].forEach((prop, i) => { + if (video[prop] != null && video[prop] !== playerSize[i]) { + conflict = true; + } else { + video[prop] = playerSize[i]; + } + }) + } + if (conflict) { + logWarn(`Ad unit "${adUnit.code} has conflicting playerSize and w/h`, adUnit) + } + } +} + +/** + * validateOrtbVideoFields mutates the `adUnit.mediaTypes.video` object by removing invalid ortb properties (default). + * The onInvalidParam callback can be used to handle invalid properties differently. + * Other properties are ignored and kept as is. + * + * @param {Object} adUnit - The adUnit object. + * @param {Function=} onInvalidParam - The callback function to be called with key, value, and adUnit. + * @returns {void} + */ +export function validateOrtbVideoFields(adUnit, onInvalidParam?) { + const videoParams = adUnit?.mediaTypes?.video; + + if (!isPlainObject(videoParams)) { + logWarn(`validateOrtbVideoFields: videoParams must be an object.`); + return; + } + + if (videoParams != null) { + Object.entries(videoParams) + .forEach(([key, value]: any) => { + if (!ORTB_VIDEO_PARAMS.has(key)) { + return + } + const isValid = ORTB_VIDEO_PARAMS.get(key)(value); + if (!isValid) { + if (typeof onInvalidParam === 'function') { + onInvalidParam(key, value, adUnit); + } else { + delete videoParams[key]; + logWarn(`Invalid prop in adUnit "${adUnit.code}": Invalid value for mediaTypes.video.${key} ORTB property. The property has been removed.`); + } + } + }); + } +} + +/** + * Validate that the assets required for video context are present on the bid + */ +export function isValidVideoBid(bid: VideoBid, {index = auctionManager.index} = {}): boolean { + const videoMediaType = index.getMediaTypes(bid)?.video; + const context = videoMediaType && videoMediaType?.context; + const useCacheKey = videoMediaType && videoMediaType?.useCacheKey; + const adUnit = index.getAdUnit(bid); + + // if context not defined assume default 'instream' for video bids + // instream bids require a vast url or vast xml content + return checkVideoBidSetup(bid, adUnit, videoMediaType, context, useCacheKey); +} + +declare module './bidfactory' { + interface VideoBidResponseProperties { + vastXml?: string; + vastUrl?: string; + } +} + +declare module './hook' { + interface NamedHooks { + checkVideoBidSetup: typeof checkVideoBidSetup + } +} + +export const checkVideoBidSetup = hook('sync', function(bid: VideoBid, adUnit, videoMediaType, context, useCacheKey) { + if (videoMediaType && (useCacheKey || context !== OUTSTREAM)) { + // xml-only video bids require a prebid cache url + const { url, useLocal } = config.getConfig('cache') || {}; + if ((!url && !useLocal) && bid.vastXml && !bid.vastUrl) { + logError(` + This bid contains only vastXml and will not work when a prebid cache url is not specified. + Try enabling either prebid cache with $$PREBID_GLOBAL$$.setConfig({ cache: {url: "..."} }); + or local cache with $$PREBID_GLOBAL$$.setConfig({ cache: { useLocal: true }}); + `); + return false; + } + + return !!(bid.vastUrl || bid.vastXml); + } + + // outstream bids require a renderer on the bid or pub-defined on adunit + if (context === OUTSTREAM && !useCacheKey) { + return !!(bid.renderer || (adUnit && adUnit.renderer) || videoMediaType.renderer); + } + + return true; +}, 'checkVideoBidSetup'); diff --git a/src/videoCache.js b/src/videoCache.ts similarity index 65% rename from src/videoCache.js rename to src/videoCache.ts index 79751b2d35e..dfedb0ceb93 100644 --- a/src/videoCache.js +++ b/src/videoCache.ts @@ -14,39 +14,25 @@ import {config} from './config.js'; import {auctionManager} from './auctionManager.js'; import {generateUUID, logError, logWarn} from './utils.js'; import {addBidToAuction} from './auction.js'; +import type {VideoBid} from "./bidfactory.ts"; /** * Might be useful to be configurable in the future * Depending on publisher needs */ +// TODO: we have a `ttlBuffer` setting const ttlBufferInSeconds = 15; export const vastLocalCache = new Map(); -/** - * @typedef {object} CacheableUrlBid - * @property {string} vastUrl A URL which loads some valid VAST XML. - */ - -/** - * @typedef {object} CacheablePayloadBid - * @property {string} vastXml Some VAST XML which loads an ad in a video player. - */ - -/** - * A CacheableBid describes the types which the videoCache can store. - * - * @typedef {CacheableUrlBid|CacheablePayloadBid} CacheableBid - */ - /** * Function which wraps a URI that serves VAST XML, so that it can be loaded. * - * @param {string} uri The URI where the VAST content can be found. - * @param {(string|string[])} impTrackerURLs An impression tracker URL for the delivery of the video ad + * @param uri The URI where the VAST content can be found. + * @param impTrackerURLs 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, impTrackerURLs) { +function wrapURI(uri: string, impTrackerURLs: string | string[]) { 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 @@ -64,20 +50,77 @@ function wrapURI(uri, impTrackerURLs) { `; } +declare module './bidfactory' { + interface VideoBidResponseProperties { + /** + * VAST impression trackers to attach to this bid. + */ + vastImpUrl?: string | string [] + /** + * Cache key to use for caching this bid's VAST. + */ + customCacheKey?: string + } + interface VideoBidProperties { + /** + * The cache key that was used for this bid. + */ + videoCacheKey?: string; + } +} + +export interface CacheConfig { + /** + * The URL of the Prebid Cache server endpoint where VAST creatives will be sent. + */ + url: string; + /** + * Flag determining whether to locally save VAST XML as a blob + */ + useLocal?: boolean; + /** + * Timeout (in milliseconds) for network requests to the cache + */ + timeout?: number; + /** + * Passes additional data to the url, used for additional event tracking data. Defaults to false. + */ + vasttrack?: boolean; + /** + * If the bidder supplied their own cache key, setting this value to true adds a VAST wrapper around that URL, + * stores it in the cache defined by the url parameter, and replaces the original video cache key with the new one. + * This can dramatically simplify ad server setup because it means all VAST creatives reside behind a single URL. + * The tradeoff: this approach requires the video player to unwrap one extra level of VAST. Defaults to false. + */ + ignoreBidderCacheKey?: boolean; + /** + * Enables video cache requests to be batched by a specified amount (defaults to 1) instead of making a single request per each video. + */ + batchSize?: number; + /** + * Used in conjunction with batchSize, batchTimeout specifies how long to wait in milliseconds before sending + * a batch video cache request based on the value for batchSize (if present). A batch request will be made whether + * the batchSize amount was reached or the batchTimeout timer runs out. batchTimeout defaults to 0. + */ + batchTimeout?: number; +} + +declare module './config' { + interface Config { + cache?: CacheConfig; + } +} /** * Wraps a bid in the format expected by the prebid-server endpoints, or returns null if * the bid can't be converted cleanly. * - * @param {CacheableBid} bid - * @param {Object} [options] - Options object. - * @param {Object} [options.index=auctionManager.index] - Index object, defaulting to `auctionManager.index`. * @return {Object|null} - The payload to be sent to the prebid-server endpoints, or null if the bid can't be converted cleanly. */ function toStorageRequest(bid, {index = auctionManager.index} = {}) { - const vastValue = getVastXml(bid); +const vastValue = getVastXml(bid); const auction = index.getAuction(bid); const ttlWithBuffer = Number(bid.ttl) + ttlBufferInSeconds; - let payload = { + let payload: any = { type: 'xml', value: vastValue, ttlseconds: ttlWithBuffer @@ -100,26 +143,25 @@ function toStorageRequest(bid, {index = auctionManager.index} = {}) { return payload; } -/** - * A function which should be called with the results of the storage operation. - * - * @callback videoCacheStoreCallback - * - * @param {Error} [error] The error, if one occurred. - * @param {?string[]} uuids An array of unique IDs. The array will have one element for each bid we were asked - * to store. It may include null elements if some of the bids were malformed, or an error occurred. - * Each non-null element in this array is a valid input into the retrieve function, which will fetch - * some VAST XML which can be used to render this bid's ad. - */ +interface VideoCacheStoreCallback { + /** + * A function which should be called with the results of the storage operation. + * + * @param error The error, if one occurred. + * @param uuids An array of unique IDs. The array will have one element for each bid we were asked + * to store. It may include null elements if some of the bids were malformed, or an error occurred. + * Each non-null element in this array is a valid input into the retrieve function, which will fetch + * some VAST XML which can be used to render this bid's ad. + */ + (error: Error | null, uuids: { uuid: string }[]) +} /** * A function which bridges the APIs between the videoCacheStoreCallback and our ajax function's API. * - * @param {videoCacheStoreCallback} done A callback to the "store" function. - * @return {Function} A callback which interprets the cache server's responses, and makes up the right - * arguments for our callback. + * @param done A callback to the "store" function. */ -function shimStorageCallback(done) { +function shimStorageCallback(done: VideoCacheStoreCallback) { return { success: function (responseBody) { let ids; @@ -149,13 +191,14 @@ function getVastXml(bid) { /** * If the given bid is for a Video ad, generate a unique ID and cache it somewhere server-side. * - * @param {CacheableBid[]} bids A list of bid objects which should be cached. - * @param {videoCacheStoreCallback} [done] An optional callback which should be executed after + * @param bids A list of bid objects which should be cached. + * @param done An optional callback which should be executed after + * @param getAjax * the data has been stored in the cache. */ -export function store(bids, done, getAjax = ajaxBuilder) { +export function store(bids: VideoBid[], done?: VideoCacheStoreCallback, getAjax = ajaxBuilder) { const requestData = { - puts: bids.map(toStorageRequest) + puts: bids.map(bid => toStorageRequest(bid)) }; const ajax = getAjax(config.getConfig('cache.timeout')); ajax(config.getConfig('cache.url'), shimStorageCallback(done), JSON.stringify(requestData), { @@ -177,7 +220,7 @@ export const storeLocally = (bid) => { vastLocalCache.set(bid.videoCacheKey, bidVastUrl); }; -const assignVastUrlAndCacheId = (bid, vastUrl, videoCacheKey) => { +const assignVastUrlAndCacheId = (bid, vastUrl, videoCacheKey?) => { bid.videoCacheKey = videoCacheKey || generateUUID(); if (!bid.vastUrl) { bid.vastUrl = vastUrl; diff --git a/test/spec/modules/33acrossAnalyticsAdapter_spec.js b/test/spec/modules/33acrossAnalyticsAdapter_spec.js index 5089f12a461..5b2591eeec8 100644 --- a/test/spec/modules/33acrossAnalyticsAdapter_spec.js +++ b/test/spec/modules/33acrossAnalyticsAdapter_spec.js @@ -1,4 +1,3 @@ -// @ts-nocheck import analyticsAdapter from 'modules/33acrossAnalyticsAdapter.js'; import { log } from 'modules/33acrossAnalyticsAdapter.js'; import * as mockGpt from 'test/spec/integration/faker/googletag.js'; diff --git a/test/spec/modules/consentManagementUsp_spec.js b/test/spec/modules/consentManagementUsp_spec.js index 6df125eddd8..fb0167a4d11 100644 --- a/test/spec/modules/consentManagementUsp_spec.js +++ b/test/spec/modules/consentManagementUsp_spec.js @@ -9,7 +9,7 @@ import { import * as utils from 'src/utils.js'; import { config } from 'src/config.js'; import adapterManager, {gdprDataHandler, uspDataHandler} from 'src/adapterManager.js'; -import 'src/prebid.js'; +import {requestBids} from '../../../src/prebid.js'; import {defer} from '../../../src/utils/promise.js'; let expect = require('chai').expect; @@ -121,7 +121,7 @@ describe('consentManagement', function () { describe('valid setConsentConfig value', function () { afterEach(function () { config.resetConfig(); - $$PREBID_GLOBAL$$.requestBids.removeAll(); + requestBids.removeAll(); }); it('results in all user settings overriding system defaults', function () { @@ -156,7 +156,7 @@ describe('consentManagement', function () { describe('static consent string setConsentConfig value', () => { afterEach(() => { config.resetConfig(); - $$PREBID_GLOBAL$$.requestBids.removeAll(); + requestBids.removeAll(); }); it('results in user settings overriding system defaults', () => { let staticConfig = { @@ -208,7 +208,7 @@ describe('consentManagement', function () { utils.logWarn.restore(); utils.logError.restore(); config.resetConfig(); - $$PREBID_GLOBAL$$.requestBids.removeAll(); + requestBids.removeAll(); resetConsentData(); }); @@ -246,7 +246,7 @@ describe('consentManagement', function () { afterEach(function () { config.resetConfig(); - $$PREBID_GLOBAL$$.requestBids.removeAll(); + requestBids.removeAll(); uspStub.restore(); document.body.removeChild(ifr); delete window.__uspapi; @@ -325,7 +325,7 @@ describe('consentManagement', function () { afterEach(function () { config.resetConfig(); - $$PREBID_GLOBAL$$.requestBids.removeAll(); + requestBids.removeAll(); delete window.__uspapi; utils.logError.restore(); utils.logWarn.restore(); @@ -406,7 +406,7 @@ describe('consentManagement', function () { afterEach(function () { config.resetConfig(); - $$PREBID_GLOBAL$$.requestBids.removeAll(); + requestBids.removeAll(); uspapiStub.restore(); utils.logError.restore(); utils.logWarn.restore(); @@ -449,7 +449,7 @@ describe('consentManagement', function () { afterEach(function () { config.resetConfig(); - $$PREBID_GLOBAL$$.requestBids.removeAll(); + requestBids.removeAll(); sandbox.restore(); document.body.removeChild(ifr); delete window.__uspapi; diff --git a/test/spec/modules/criteoBidAdapter_spec.js b/test/spec/modules/criteoBidAdapter_spec.js index 08ef7e7e877..9006ab9609f 100644 --- a/test/spec/modules/criteoBidAdapter_spec.js +++ b/test/spec/modules/criteoBidAdapter_spec.js @@ -2529,15 +2529,17 @@ describe('The Criteo bidding adapter', function () { } ]; - utilsMock.expects('logWarn') - .withArgs('Criteo: all native assets containing URL should be sent as placeholders with sendId(icon, image, clickUrl, displayUrl, privacyLink, privacyIcon)') - .exactly(nativeParamsWithSendTargetingKeys.length * bidRequests.length); for (const nativeParams of nativeParamsWithSendTargetingKeys) { let transformedBidRequests = {...bidRequests}; transformedBidRequests = [Object.assign(transformedBidRequests[0], nativeParams), Object.assign(transformedBidRequests[1], nativeParams)]; spec.buildRequests(transformedBidRequests, await addFPDToBidderRequest(bidderRequest)); } - utilsMock.verify(); + + expect( + logWarnStub + .withArgs('Criteo: all native assets containing URL should be sent as placeholders with sendId(icon, image, clickUrl, displayUrl, privacyLink, privacyIcon)') + .callCount + ).to.eql(nativeParamsWithSendTargetingKeys.length * bidRequests.length); }); } diff --git a/test/spec/modules/euidIdSystem_spec.js b/test/spec/modules/euidIdSystem_spec.js index c9718e22839..29279158016 100644 --- a/test/spec/modules/euidIdSystem_spec.js +++ b/test/spec/modules/euidIdSystem_spec.js @@ -2,7 +2,7 @@ import {attachIdSystem, coreStorage, init, setSubmoduleRegistry} from 'modules/u import {config} from 'src/config.js'; import {euidIdSubmodule} from 'modules/euidIdSystem.js'; import 'modules/consentManagementTcf.js'; -import 'src/prebid.js'; +import {requestBids} from '../../../src/prebid.js'; import {apiHelpers, cookieHelpers, runAuction, setGdprApplies} from './uid2IdSystem_helpers.js'; import {hook} from 'src/hook.js'; import {uninstall as uninstallTcfControl} from 'modules/tcfControl.js'; @@ -76,7 +76,7 @@ describe('EUID module', function() { setSubmoduleRegistry([euidIdSubmodule]); }); afterEach(function() { - $$PREBID_GLOBAL$$.requestBids.removeAll(); + requestBids.removeAll(); config.resetConfig(); cookieHelpers.clearCookies(moduleCookieName, publisherCookieName); coreStorage.removeDataFromLocalStorage(moduleCookieName); diff --git a/test/spec/modules/instreamTracking_spec.js b/test/spec/modules/instreamTracking_spec.js index 379c288bca1..800efa9a6ec 100644 --- a/test/spec/modules/instreamTracking_spec.js +++ b/test/spec/modules/instreamTracking_spec.js @@ -123,7 +123,6 @@ function getMockInput(mediaType) { let adUnit; switch (mediaType) { - default: case 'banner': adUnit = bannerAdUnit; break; @@ -133,6 +132,7 @@ function getMockInput(mediaType) { case INSTREAM: adUnit = inStreamAdUnit; break; + default: } const bidResponse = mockBidResponse(adUnit, utils.getUniqueIdentifierStr()); diff --git a/test/spec/modules/mediakeysBidAdapter_spec.js b/test/spec/modules/mediakeysBidAdapter_spec.js index 55fa9bfb858..d724b0891b1 100644 --- a/test/spec/modules/mediakeysBidAdapter_spec.js +++ b/test/spec/modules/mediakeysBidAdapter_spec.js @@ -692,15 +692,6 @@ describe('mediakeysBidAdapter', function () { expect(response06.length).to.equal(0); }); - it('Log an error', function () { - const bidRequests = [utils.deepClone(bid)]; - const request = spec.buildRequests(bidRequests, bidderRequest); - sinon.stub(utils, 'isArray').throws(); - utilsMock.expects('logError').once(); - spec.interpretResponse(rawServerResponse, request); - utils.isArray.restore(); - }); - it('Meta Primary category handling', function() { const rawServerResponseCopy = utils.deepClone(rawServerResponse); const rawServerResponseCopy2 = utils.deepClone(rawServerResponse); diff --git a/test/spec/modules/mediasniperBidAdapter_spec.js b/test/spec/modules/mediasniperBidAdapter_spec.js index 30437205067..1e2ec5f0721 100644 --- a/test/spec/modules/mediasniperBidAdapter_spec.js +++ b/test/spec/modules/mediasniperBidAdapter_spec.js @@ -343,14 +343,6 @@ describe('mediasniperBidAdapter', function () { expect(response06.length).to.equal(0); }); - it('Log an error', function () { - const request = ''; - sinon.stub(utils, 'isArray').throws(); - utilsMock.expects('logError').once(); - spec.interpretResponse(rawServerResponse, request); - utils.isArray.restore(); - }); - describe('Build banner response', function () { it('Retrurn successful response', function () { const request = ''; diff --git a/test/spec/modules/michaoBidAdapter_spec.js b/test/spec/modules/michaoBidAdapter_spec.js index d4af3b3aa68..2ff13e37c9a 100644 --- a/test/spec/modules/michaoBidAdapter_spec.js +++ b/test/spec/modules/michaoBidAdapter_spec.js @@ -379,7 +379,7 @@ describe('Michao Bid Adapter', () => { }; const request = spec.buildRequests([videoBidRequest], bidderRequest); - const result = spec.interpretResponse(videoServerResponse, request[0]); + const result = spec.interpretResponse(videoServerResponse, request[0]).bids; expect(result[0].renderer.url).to.equal( 'https://cdn.jsdelivr.net/npm/in-renderer-js@1/dist/in-video-renderer.umd.min.js' @@ -399,7 +399,7 @@ describe('Michao Bid Adapter', () => { }; const request = spec.buildRequests([videoBidRequest], bidderRequest); - const result = spec.interpretResponse(videoServerResponse, request[0]); + const result = spec.interpretResponse(videoServerResponse, request[0]).bids; expect(result[0].renderer).to.be.undefined; }); @@ -413,7 +413,7 @@ describe('Michao Bid Adapter', () => { }; const request = spec.buildRequests([bannerBidRequest], bidderRequest); - const result = spec.interpretResponse(bannerServerResponse, request[0]); + const result = spec.interpretResponse(bannerServerResponse, request[0]).bids; expect(result[0].renderer).to.be.undefined; }); diff --git a/test/spec/modules/prebidServerBidAdapter_spec.js b/test/spec/modules/prebidServerBidAdapter_spec.js index 2fb23a29daf..b802da664fb 100644 --- a/test/spec/modules/prebidServerBidAdapter_spec.js +++ b/test/spec/modules/prebidServerBidAdapter_spec.js @@ -15,7 +15,7 @@ import { EVENTS } from 'src/constants.js'; import {server} from 'test/mocks/xhr.js'; import 'modules/appnexusBidAdapter.js'; // appnexus alias test import 'modules/rubiconBidAdapter.js'; // rubicon alias test -import 'src/prebid.js'; // $$PREBID_GLOBAL$$.aliasBidder test +import {requestBids} from 'src/prebid.js'; import 'modules/currency.js'; // adServerCurrency test import 'modules/userId/index.js'; import 'modules/multibid/index.js'; @@ -976,7 +976,7 @@ describe('S2S Adapter', function () { describe('gdpr tests', function () { afterEach(function () { - $$PREBID_GLOBAL$$.requestBids.removeAll(); + requestBids.removeAll(); }); it('adds gdpr consent information to ortb2 request depending on presence of module', async function () { @@ -1031,7 +1031,7 @@ describe('S2S Adapter', function () { describe('us_privacy (ccpa) consent data', function () { afterEach(function () { - $$PREBID_GLOBAL$$.requestBids.removeAll(); + requestBids.removeAll(); }); it('is added to ortb2 request when in FPD', async function () { @@ -1057,7 +1057,7 @@ describe('S2S Adapter', function () { describe('gdpr and us_privacy (ccpa) consent data', function () { afterEach(function () { - $$PREBID_GLOBAL$$.requestBids.removeAll(); + requestBids.removeAll(); }); it('is added to ortb2 request when in bidRequest', async function () { diff --git a/test/spec/modules/raynRtdProvider_spec.js b/test/spec/modules/raynRtdProvider_spec.js index 1c846f8f45b..65584c84cc1 100644 --- a/test/spec/modules/raynRtdProvider_spec.js +++ b/test/spec/modules/raynRtdProvider_spec.js @@ -97,6 +97,12 @@ describe('rayn RTD Submodule', function () { delete global.window.raynJS; }); + function expectLog(spy, message) { + // TODO: instead of testing the business logic, this suite tests + // what it logs (and the module is then forced to log its request payloads, which is just noise). + expect(spy.args.map(args => args[args.length - 1])).to.contain(message); + } + describe('Initialize module', function () { it('should initialize and return true', function () { expect(raynRTD.raynSubmodule.init(RTD_CONFIG.dataProviders[0])).to.equal( @@ -224,7 +230,7 @@ describe('rayn RTD Submodule', function () { describe('Alter Bid Requests', function () { it('should update reqBidsConfigObj and execute callback', function () { const callbackSpy = sinon.spy(); - const logMessageSpy = sinon.spy(utils, 'logMessage'); + const logMessageSpy = sandbox.spy(utils, 'logMessage'); getDataFromLocalStorageStub .withArgs(raynRTD.RAYN_LOCAL_STORAGE_KEY) @@ -235,14 +241,12 @@ describe('rayn RTD Submodule', function () { 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(); + expectLog(logMessageSpy, `Segtax data from localStorage: ${JSON.stringify(TEST_SEGMENTS)}`); }); it('should update reqBidsConfigObj and execute callback using user segments from localStorage', function () { const callbackSpy = sinon.spy(); - const logMessageSpy = sinon.spy(utils, 'logMessage'); + const logMessageSpy = sandbox.spy(utils, 'logMessage'); const testSegments = { 4: { 3: ['4', '17', '72', '612'] @@ -267,14 +271,12 @@ describe('rayn RTD Submodule', function () { 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(); + expectLog(logMessageSpy, `Segtax data from localStorage: ${JSON.stringify(testSegments)}`) }); it('should update reqBidsConfigObj and execute callback using persona segment from localStorage', function () { const callbackSpy = sinon.spy(); - const logMessageSpy = sinon.spy(utils, 'logMessage'); + const logMessageSpy = sandbox.spy(utils, 'logMessage'); const testSegments = { 103015: ['agdv23', 'avscg3'] }; @@ -288,14 +290,12 @@ describe('rayn RTD Submodule', function () { 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(); + expectLog(logMessageSpy, `Segtax data from localStorage: ${JSON.stringify(testSegments)}`) }); it('should update reqBidsConfigObj and execute callback using segments from raynJS', function () { const callbackSpy = sinon.spy(); - const logMessageSpy = sinon.spy(utils, 'logMessage'); + const logMessageSpy = sandbox.spy(utils, 'logMessage'); getDataFromLocalStorageStub .withArgs(raynRTD.RAYN_LOCAL_STORAGE_KEY) @@ -306,14 +306,12 @@ describe('rayn RTD Submodule', function () { 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(); + expectLog(logMessageSpy, `No segtax data`) }); it('should update reqBidsConfigObj and execute callback using audience from localStorage', function (done) { const callbackSpy = sinon.spy(); - const logMessageSpy = sinon.spy(utils, 'logMessage'); + const logMessageSpy = sandbox.spy(utils, 'logMessage'); const testSegments = { 6: { 4: ['3', '27', '177'] @@ -336,16 +334,14 @@ describe('rayn RTD Submodule', function () { setTimeout(() => { expect(callbackSpy.calledOnce).to.be.true; - const messages = logMessageSpy.getCalls().map(call => call.lastArg); - expect(messages).to.include(`Segtax data from RaynJS: ${JSON.stringify(testSegments)}`); - logMessageSpy.restore(); + expectLog(logMessageSpy, `Segtax data from RaynJS: ${JSON.stringify(testSegments)}`) done(); }, 0) }); it('should execute callback if log error', function (done) { const callbackSpy = sinon.spy(); - const logErrorSpy = sinon.spy(utils, 'logError'); + const logErrorSpy = sandbox.spy(utils, 'logError'); const rejectError = 'Error'; global.window.raynJS = { @@ -364,8 +360,7 @@ describe('rayn RTD Submodule', function () { setTimeout(() => { expect(callbackSpy.calledOnce).to.be.true; - expect(logErrorSpy.lastCall.lastArg).to.equal(rejectError); - logErrorSpy.restore(); + expectLog(logErrorSpy, rejectError); done(); }, 0) }); diff --git a/test/spec/modules/tcfControl_spec.js b/test/spec/modules/tcfControl_spec.js index 83b100b34b9..d4d2194e620 100644 --- a/test/spec/modules/tcfControl_spec.js +++ b/test/spec/modules/tcfControl_spec.js @@ -25,7 +25,7 @@ import { } from '../../../src/activities/modules.js'; import * as events from 'src/events.js'; import 'modules/appnexusBidAdapter.js'; // some tests expect this to be in the adapter registry -import 'src/prebid.js'; +import {requestBids} from 'src/prebid.js'; import {hook} from '../../../src/hook.js'; import {GDPR_GVLIDS, VENDORLESS_GVLID} from '../../../src/consentHandler.js'; import {activityParams} from '../../../src/activities/activityParams.js'; @@ -125,7 +125,7 @@ describe('gdpr enforcement', function () { }); after(function () { - $$PREBID_GLOBAL$$.requestBids.getHooks().remove(); + requestBids.getHooks().remove(); }) function expectAllow(allow, ruleResult) { diff --git a/test/spec/modules/uid2IdSystem_spec.js b/test/spec/modules/uid2IdSystem_spec.js index 23b810832a9..3d659485a2e 100644 --- a/test/spec/modules/uid2IdSystem_spec.js +++ b/test/spec/modules/uid2IdSystem_spec.js @@ -2,7 +2,7 @@ import {attachIdSystem, coreStorage, init, setSubmoduleRegistry} from 'modules/u import {config} from 'src/config.js'; import * as utils from 'src/utils.js'; import { uid2IdSubmodule } from 'modules/uid2IdSystem.js'; -import 'src/prebid.js'; +import {requestBids} from '../../../src/prebid.js'; import 'modules/consentManagementTcf.js'; import { getGlobal } from 'src/prebidGlobal.js'; import { configureTimerInterceptors } from 'test/mocks/timers.js'; @@ -151,7 +151,7 @@ describe(`UID2 module`, function () { }); afterEach(async function() { - $$PREBID_GLOBAL$$.requestBids.removeAll(); + requestBids.removeAll(); config.resetConfig(); testSandbox.restore(); if (timerSpy.timers.length > 0) { diff --git a/test/spec/modules/userId_spec.js b/test/spec/modules/userId_spec.js index f1a977f7aff..0567f30f7b0 100644 --- a/test/spec/modules/userId_spec.js +++ b/test/spec/modules/userId_spec.js @@ -18,7 +18,6 @@ import {UID1_EIDS} from 'libraries/uid1Eids/uid1Eids.js'; import {createEidsArray, EID_CONFIG} from 'modules/userId/eids.js'; import {config} from 'src/config.js'; import * as utils from 'src/utils.js'; -import {deepAccess, getPrebidInternal} from 'src/utils.js'; import * as events from 'src/events.js'; import {EVENTS} from 'src/constants.js'; import {getGlobal} from 'src/prebidGlobal.js'; @@ -27,8 +26,7 @@ import {setEventFiredFlag as liveIntentIdSubmoduleDoNotFireEvent} from '../../.. import {sharedIdSystemSubmodule} from 'modules/sharedIdSystem.js'; import {pubProvidedIdSubmodule} from 'modules/pubProvidedIdSystem.js'; import * as mockGpt from '../integration/faker/googletag.js'; -import 'src/prebid.js'; -import {startAuction} from 'src/prebid'; +import {requestBids, startAuction} from 'src/prebid.js'; import {hook} from '../../../src/hook.js'; import {mockGdprConsent} from '../../helpers/consentData.js'; import {getPPID} from '../../../src/adserver.js'; @@ -271,7 +269,7 @@ describe('User ID', function () { afterEach(function () { mockGpt.enable(); - $$PREBID_GLOBAL$$.requestBids.removeAll(); + requestBids.removeAll(); config.resetConfig(); coreStorage.setCookie.restore(); utils.logWarn.restore(); @@ -1480,7 +1478,7 @@ describe('User ID', function () { afterEach(function () { // removed cookie coreStorage.setCookie(PBJS_USER_ID_OPTOUT_NAME, '', EXPIRED_COOKIE_DATE); - $$PREBID_GLOBAL$$.requestBids.removeAll(); + requestBids.removeAll(); utils.logInfo.restore(); }); @@ -1509,7 +1507,7 @@ describe('User ID', function () { }); afterEach(function () { - $$PREBID_GLOBAL$$.requestBids.removeAll(); + requestBids.removeAll(); utils.logInfo.restore(); }); @@ -1678,7 +1676,7 @@ describe('User ID', function () { }); afterEach(function () { - $$PREBID_GLOBAL$$.requestBids.removeAll(); + requestBids.removeAll(); config.resetConfig(); sandbox.restore(); coreStorage.setCookie('MOCKID', '', EXPIRED_COOKIE_DATE); diff --git a/test/spec/modules/videoModule/pbVideo_spec.js b/test/spec/modules/videoModule/pbVideo_spec.js index 58af1a15e43..569853f6594 100644 --- a/test/spec/modules/videoModule/pbVideo_spec.js +++ b/test/spec/modules/videoModule/pbVideo_spec.js @@ -35,7 +35,6 @@ function resetTestVars() { before: sinon.spy() }; pbGlobalMock = { - requestBids: requestBidsMock, getHighestCpmBids: sinon.spy(), getBidResponsesForAdUnitCode: sinon.spy(), setConfig: sinon.spy(), @@ -68,11 +67,12 @@ function resetTestVars() { adQueueCoordinatorFactoryMock = () => adQueueCoordinatorMock; } -let pbVideoFactory = (videoCore, getConfig, pbGlobal, pbEvents, videoEvents, gamSubmoduleFactory, videoImpressionVerifierFactory, adQueueCoordinator) => { +let pbVideoFactory = (videoCore, getConfig, pbGlobal, requestBids, pbEvents, videoEvents, gamSubmoduleFactory, videoImpressionVerifierFactory, adQueueCoordinator) => { const pbVideo = PbVideo( videoCore || videoCoreMock, getConfig || getConfigMock, pbGlobal || pbGlobalMock, + requestBids || requestBidsMock, pbEvents || pbEventsMock, videoEvents || videoEventsMock, gamSubmoduleFactory || gamSubmoduleFactoryMock, @@ -158,7 +158,7 @@ describe('Prebid Video', function () { before: callback_ => beforeBidRequestCallback = callback_ }; - pbVideoFactory(null, null, Object.assign({}, pbGlobalMock, { requestBids })); + pbVideoFactory(null, null, Object.assign({}, pbGlobalMock), requestBids); expect(beforeBidRequestCallback).to.not.be.undefined; const nextFn = sinon.spy(); const adUnits = [{ @@ -188,7 +188,7 @@ describe('Prebid Video', function () { before: callback_ => beforeBidRequestCallback = callback_ }; - pbVideoFactory(null, null, Object.assign({}, pbGlobalMock, { requestBids })); + pbVideoFactory(null, null, Object.assign({}, pbGlobalMock), requestBids); expect(beforeBidRequestCallback).to.not.be.undefined; const nextFn = sinon.spy(); const adUnits = [{ @@ -263,13 +263,12 @@ describe('Prebid Video', function () { const expectedVastUrl = 'expectedVastUrl'; const expectedVastXml = 'expectedVastXml'; const pbGlobal = Object.assign({}, pbGlobalMock, { - requestBids, getHighestCpmBids: () => [{ vastUrl: expectedVastUrl, vastXml: expectedVastXml }, {}, {}, {}] }); - pbVideoFactory(null, getConfig, pbGlobal, pbEvents); + pbVideoFactory(null, getConfig, pbGlobal, requestBids, pbEvents); beforeBidRequestCallback(() => {}, {}); auctionEndCallback(auctionResults); @@ -286,13 +285,12 @@ describe('Prebid Video', function () { const expectedVastUrl = 'expectedVastUrl'; const expectedVastXml = 'expectedVastXml'; const pbGlobal = Object.assign({}, pbGlobalMock, { - requestBids, getHighestCpmBids: () => [{ vastUrl: expectedVastUrl, vastXml: expectedVastXml }, {}, {}, {}] }); - pbVideoFactory(null, getConfig, pbGlobal, pbEvents, null, gamSubmoduleFactory); + pbVideoFactory(null, getConfig, pbGlobal, requestBids, pbEvents, null, gamSubmoduleFactory); beforeBidRequestCallback(() => {}, {}); auctionEndCallback(auctionResults); expect(adQueueCoordinatorMock.queueAd.calledOnce).to.be.true; @@ -305,7 +303,6 @@ describe('Prebid Video', function () { const expectedVastUrl = 'expectedVastUrl'; const expectedVastXml = 'expectedVastXml'; const pbGlobal = Object.assign({}, pbGlobalMock, { - requestBids, getHighestCpmBids: () => [{ vastUrl: expectedVastUrl, vastXml: expectedVastXml @@ -317,7 +314,7 @@ describe('Prebid Video', function () { }; const auctionResults = { adUnits: [ expectedAdUnit, {} ] }; - pbVideoFactory(null, () => ({ providers: [] }), pbGlobal, pbEvents); + pbVideoFactory(null, () => ({ providers: [] }), pbGlobal, requestBids, pbEvents); beforeBidRequestCallback(() => {}, {}); auctionEndCallback(auctionResults); expect(adQueueCoordinatorMock.queueAd.calledOnce).to.be.true; @@ -349,7 +346,7 @@ describe('Prebid Video', function () { }; it('should ask Impression Verifier to track bid on Bid Adjustment', function () { - pbVideoFactory(null, null, null, pbEvents); + pbVideoFactory(null, null, null, null, pbEvents); bidAdjustmentCb(); expect(videoImpressionVerifierMock.trackBid.calledOnce).to.be.true; }); @@ -358,7 +355,7 @@ describe('Prebid Video', function () { pbEvents.emit.resetHistory(); const pbGlobal = Object.assign({}, pbGlobalMock, { getBidResponsesForAdUnitCode: () => ({ bids: [expectedBid] }) }); const videoImpressionVerifier = Object.assign({}, videoImpressionVerifierMock, { getBidIdentifiers: () => ({}) }); - pbVideoFactory(null, null, pbGlobal, pbEvents, null, null, () => videoImpressionVerifier); + pbVideoFactory(null, null, pbGlobal, null, pbEvents, null, null, () => videoImpressionVerifier); adImpressionCb(expectedAdEventPayload); expect(pbEvents.emit.calledOnce).to.be.true; @@ -373,7 +370,7 @@ describe('Prebid Video', function () { pbEvents.emit.resetHistory(); const pbGlobal = Object.assign({}, pbGlobalMock, { getBidResponsesForAdUnitCode: () => ({ bids: [expectedBid] }) }); const videoImpressionVerifier = Object.assign({}, videoImpressionVerifierMock, { getBidIdentifiers: () => ({}) }); - pbVideoFactory(null, null, pbGlobal, pbEvents, null, null, () => videoImpressionVerifier); + pbVideoFactory(null, null, pbGlobal, null, pbEvents, null, null, () => videoImpressionVerifier); adErrorCb(expectedAdEventPayload); expect(pbEvents.emit.calledOnce).to.be.true; @@ -388,7 +385,7 @@ describe('Prebid Video', function () { pbEvents.emit.resetHistory(); const pbGlobal = Object.assign({}, pbGlobalMock, { getBidResponsesForAdUnitCode: () => ({ bids: [expectedBid] }) }); const videoImpressionVerifier = Object.assign({}, videoImpressionVerifierMock, { getBidIdentifiers: () => ({ auctionId: 'id' }) }); - pbVideoFactory(null, null, pbGlobal, pbEvents, null, null, () => videoImpressionVerifier); + pbVideoFactory(null, null, pbGlobal, null, pbEvents, null, null, () => videoImpressionVerifier); adImpressionCb(expectedAdEventPayload); expect(pbEvents.emit.called).to.be.false; @@ -398,7 +395,7 @@ describe('Prebid Video', function () { pbEvents.emit.resetHistory(); const pbGlobal = Object.assign({}, pbGlobalMock, { getBidResponsesForAdUnitCode: () => ({ bids: [expectedBid] }) }); const videoImpressionVerifier = Object.assign({}, videoImpressionVerifierMock, { getBidIdentifiers: () => ({ auctionId: 'id' }) }); - pbVideoFactory(null, null, pbGlobal, pbEvents, null, null, () => videoImpressionVerifier); + pbVideoFactory(null, null, pbGlobal, null, pbEvents, null, null, () => videoImpressionVerifier); adErrorCb(expectedAdEventPayload); expect(pbEvents.emit.called).to.be.false; diff --git a/test/spec/unit/core/adapterManager_spec.js b/test/spec/unit/core/adapterManager_spec.js index 2b2abd6a3ab..785e65c1ae1 100644 --- a/test/spec/unit/core/adapterManager_spec.js +++ b/test/spec/unit/core/adapterManager_spec.js @@ -4,7 +4,7 @@ import adapterManager, { coppaDataHandler, _partitionBidders, PARTITIONS, - getS2SBidderSet, _filterBidsForAdUnit, dep + getS2SBidderSet, filterBidsForAdUnit, dep } from 'src/adapterManager.js'; import { getAdUnits, @@ -2922,8 +2922,11 @@ describe('adapterManager tests', function () { }); describe('filterBidsForAdUnit', () => { + before(() => { + filterBidsForAdUnit.removeAll(); + }) function filterBids(bids, s2sConfig) { - return _filterBidsForAdUnit(bids, s2sConfig, {getS2SBidders}); + return filterBidsForAdUnit(bids, s2sConfig, {getS2SBidders}); } it('should not filter any bids when s2sConfig == null', () => { const bids = ['untouched', 'data']; diff --git a/test/spec/unit/pbjs_api_spec.js b/test/spec/unit/pbjs_api_spec.js index 67494fedc86..cceb9f34475 100644 --- a/test/spec/unit/pbjs_api_spec.js +++ b/test/spec/unit/pbjs_api_spec.js @@ -208,7 +208,7 @@ describe('Unit: Prebid Module', function () { } before((done) => { hook.ready(); - $$PREBID_GLOBAL$$.requestBids.getHooks().remove(); + pbjsModule.requestBids.getHooks().remove(); resetDebugging(); getBidToRender.before(getBidToRenderHook, 100); // preload creative renderer @@ -284,7 +284,7 @@ describe('Unit: Prebid Module', function () { } beforeEach(() => { - $$PREBID_GLOBAL$$.requestBids.before(deferringHook, 99); + pbjsModule.requestBids.before(deferringHook, 99); hookRan = new Promise((resolve) => { done = resolve; }); @@ -292,7 +292,7 @@ describe('Unit: Prebid Module', function () { }); afterEach(() => { - $$PREBID_GLOBAL$$.requestBids.getHooks({hook: deferringHook}).remove(); + pbjsModule.requestBids.getHooks({hook: deferringHook}).remove(); $$PREBID_GLOBAL$$.adUnits.splice(0, $$PREBID_GLOBAL$$.adUnits.length); }) @@ -1281,13 +1281,6 @@ describe('Unit: Prebid Module', function () { }) }); - it('should log message with bid id', function () { - return renderAd(doc, bidId).then(() => { - var message = 'Calling renderAd with adId :' + bidId; - assert.ok(spyLogMessage.calledWith(message), 'expected message was logged'); - }) - }); - it('should write the ad to the doc', function () { pushBidResponseToAuction({ ad: "" @@ -1397,16 +1390,12 @@ describe('Unit: Prebid Module', function () { ad: "" }); return renderAd(doc, bidId).then(() => { - var message = 'Calling renderAd with adId :' + bidId; - sinon.assert.calledWith(spyLogMessage, message); - sinon.assert.calledOnce(spyAddWinningBid); sinon.assert.calledWith(spyAddWinningBid, adResponse); }); }); it('should warn stale rendering', function () { - var message = 'Calling renderAd with adId :' + bidId; var warning = `Ad id ${bidId} has been rendered before`; var onWonEvent = sinon.stub(); var onStaleEvent = sinon.stub(); @@ -1420,7 +1409,6 @@ describe('Unit: Prebid Module', function () { // First render should pass with no warning and added to winning bids return renderAd(doc, bidId).then(() => { - sinon.assert.calledWith(spyLogMessage, message); sinon.assert.neverCalledWith(spyLogWarn, warning); sinon.assert.calledOnce(spyAddWinningBid); @@ -1440,7 +1428,6 @@ describe('Unit: Prebid Module', function () { return renderAd(doc, bidId); }).then(() => { // Second render should have a warning but still be rendered - sinon.assert.calledWith(spyLogMessage, message); sinon.assert.calledWith(spyLogWarn, warning); sinon.assert.calledWith(onStaleEvent, adResponse); sinon.assert.called(doc.write); @@ -1452,7 +1439,6 @@ describe('Unit: Prebid Module', function () { }); it('should stop stale rendering', function () { - var message = 'Calling renderAd with adId :' + bidId; var warning = `Ad id ${bidId} has been rendered before`; var onWonEvent = sinon.stub(); var onStaleEvent = sinon.stub(); @@ -1469,7 +1455,6 @@ describe('Unit: Prebid Module', function () { // First render should pass with no warning and added to winning bids return renderAd(doc, bidId).then(() => { - sinon.assert.calledWith(spyLogMessage, message); sinon.assert.neverCalledWith(spyLogWarn, warning); sinon.assert.calledOnce(spyAddWinningBid); @@ -1489,7 +1474,6 @@ describe('Unit: Prebid Module', function () { // Second render should have a warning and do not proceed further return renderAd(doc, bidId); }).then(() => { - sinon.assert.calledWith(spyLogMessage, message); sinon.assert.calledWith(spyLogWarn, warning); sinon.assert.notCalled(spyAddWinningBid); @@ -1680,11 +1664,11 @@ describe('Unit: Prebid Module', function () { beforeEach(() => { // make sure the return value works correctly when hooks give up priority - $$PREBID_GLOBAL$$.requestBids.before(delayHook) + pbjsModule.requestBids.before(delayHook) }); afterEach(() => { - $$PREBID_GLOBAL$$.requestBids.getHooks({hook: delayHook}).remove(); + pbjsModule.requestBids.getHooks({hook: delayHook}).remove(); }); Object.entries({ diff --git a/test/spec/video_spec.js b/test/spec/video_spec.js index 0d2a32659e9..8ab50293f75 100644 --- a/test/spec/video_spec.js +++ b/test/spec/video_spec.js @@ -46,7 +46,7 @@ describe('video.js', function () { }); }); describe('should set plcmt = 2 when', () => { - [2, 6].forEach(playbackmethod => { + [[2], [6]].forEach(playbackmethod => { it(`playbackmethod is "${playbackmethod}"`, () => { expect(fillDefaults({playbackmethod})).to.eql({ playbackmethod, @@ -89,7 +89,68 @@ describe('video.js', function () { expect(fillDefaults(video).plcmt).to.eql(expected); }) }) - }) + }); + describe('video.playerSize', () => { + Object.entries({ + 'single size': [1, 2], + 'single size, wrapped in array': [[1, 2]], + 'multiple sizes': [[1, 2], [3, 4]] + }).forEach(([t, playerSize]) => { + it(`should set w/h from playerSize (${t})`, () => { + const adUnit = { + mediaTypes: { + video: { + playerSize + } + } + } + fillVideoDefaults(adUnit); + + sinon.assert.match(adUnit.mediaTypes.video, { + w: 1, + h: 2 + }); + }); + it('should not override w/h when they exist', () => { + const adUnit = { + mediaTypes: { + video: { + playerSize, + w: 123 + } + } + } + fillVideoDefaults(adUnit); + expect(adUnit.mediaTypes.video.w).to.eql(123); + }) + }); + + it('should set playerSize from w/h (if they are not defined)', () => { + const adUnit = { + mediaTypes: { + video: { + w: 1, + h: 2 + } + } + } + fillVideoDefaults(adUnit); + expect(adUnit.mediaTypes.video.playerSize).to.eql([[1, 2]]); + }); + it('should not override playerSize', () => { + const adUnit = { + mediaTypes: { + video: { + playerSize: [1, 2], + w: 3, + h: 4 + } + } + } + fillVideoDefaults(adUnit); + expect(adUnit.mediaTypes.video.playerSize).to.eql([1, 2]); + }) + }); }) describe('validateOrtbVideoFields', () => { diff --git a/tsconfig.json b/tsconfig.json new file mode 100644 index 00000000000..eb6e9650ba8 --- /dev/null +++ b/tsconfig.json @@ -0,0 +1,29 @@ +{ + "compilerOptions": { + // Ensure that .d.ts files are created by tsc, but not .js files + "declaration": true, + "emitDeclarationOnly": true, + // Ensure that Babel can safely transpile files in the TypeScript project + "isolatedModules": true, + "rootDir": "./", + "outDir": "./dist/src/", + "noImplicitAny": false, + "allowJs": true, + "checkJs": false, + "types": [], + "lib": ["es2019", "DOM"], + "target": "es2019", + "allowImportingTsExtensions": true, + "module": "NodeNext", + "moduleResolution": "NodeNext" + }, + "include": [ + "./**/*.ts", + ], + "exclude": [ + "./dist/**/*", + "./build/**/*", + "./node_modules/**/*", + "integrationExamples/**/*" + ] +} diff --git a/webpack.conf.js b/webpack.conf.js index 5f3588dd95b..690d00bccd3 100644 --- a/webpack.conf.js +++ b/webpack.conf.js @@ -7,7 +7,6 @@ var helpers = require('./gulpHelpers.js'); var { BundleAnalyzerPlugin } = require('webpack-bundle-analyzer'); var argv = require('yargs').argv; const fs = require('fs'); -const babelConfig = require('./babelConfig.js')({disableFeatures: helpers.getDisabledFeatures(), prebidDistUrlBase: argv.distUrlBase}); const {WebpackManifestPlugin} = require('webpack-manifest-plugin') var plugins = [ @@ -45,12 +44,23 @@ module.exports = { type: 'filesystem', cacheDirectory: path.resolve(__dirname, '.cache/webpack') }, + context: helpers.getPrecompiledPath(), resolve: { modules: [ - path.resolve('.'), + helpers.getPrecompiledPath(), 'node_modules' ], }, + module: { + rules: [ + { + test: /\.js$/, + exclude: path.resolve('./node_modules'), + enforce: "pre", + use: ["source-map-loader"], + }, + ], + }, entry: (() => { const entry = { 'prebid-core': { @@ -75,34 +85,6 @@ module.exports = { chunkLoadingGlobal: prebid.globalVarName + 'Chunk', chunkLoading: 'jsonp', }, - module: { - rules: [ - { - test: /\.js$/, - exclude: path.resolve('./node_modules'), // required to prevent loader from choking non-Prebid.js node_modules - use: [ - { - loader: 'babel-loader', - options: Object.assign( - {cacheDirectory: cacheDir, cacheCompression: false}, - babelConfig, - helpers.getAnalyticsOptions() - ), - } - ] - }, - { // This makes sure babel-loader is ran on our intended Prebid.js modules that happen to be in node_modules - test: /\.js$/, - include: helpers.getArgModules().map(module => new RegExp('node_modules/' + module + '/')), - use: [ - { - loader: 'babel-loader', - options: Object.assign({cacheDirectory: cacheDir, cacheCompression: false}, babelConfig) - } - ], - } - ] - }, optimization: { usedExports: true, sideEffects: true, @@ -124,7 +106,7 @@ module.exports = { fs.readdirSync(libRoot) .filter((f) => fs.lstatSync(path.resolve(libRoot, f)).isDirectory()) .map(lib => { - const dir = path.resolve(libRoot, lib) + const dir = helpers.getPrecompiledPath(lib) const def = { name: lib, test: (module) => { @@ -134,13 +116,22 @@ module.exports = { return [lib, def]; }) ); - const core = path.resolve('./src'); + const core = helpers.getPrecompiledPath('./src'); + const nodeMods = path.resolve(__dirname, 'node_modules') + const precompiled = helpers.getPrecompiledPath(); return Object.assign(libraries, { core: { name: 'chunk-core', test: (module) => { - return module.resource && module.resource.startsWith(core); + let resource = module.resource; + if (resource) { + if (resource.startsWith(__dirname) && + !(resource.startsWith(precompiled) || resource.startsWith(nodeMods))) { + throw new Error(`Un-precompiled module: ${resource}`) + } + return resource.startsWith(core); + } } }, }, { diff --git a/webpack.creative.js b/webpack.creative.js index 86f5f24d580..cd442b0ad2c 100644 --- a/webpack.creative.js +++ b/webpack.creative.js @@ -1,10 +1,12 @@ const path = require('path'); +const helpers = require('./gulpHelpers.js'); module.exports = { mode: 'production', + context: helpers.getPrecompiledPath(), resolve: { modules: [ - path.resolve('.'), + helpers.getPrecompiledPath(), 'node_modules' ], }, diff --git a/webpack.debugging.js b/webpack.debugging.js index 3952649c557..c085edd1fa9 100644 --- a/webpack.debugging.js +++ b/webpack.debugging.js @@ -1,11 +1,12 @@ -var path = require('path'); +const helpers = require('./gulpHelpers.js'); module.exports = { mode: 'production', devtool: 'source-map', + context: helpers.getPrecompiledPath(), resolve: { modules: [ - path.resolve('.'), + helpers.getPrecompiledPath(), 'node_modules' ], }, @@ -14,17 +15,4 @@ module.exports = { import: './modules/debugging/standalone.js', } }, - module: { - rules: [ - { - test: /\.js$/, - exclude: path.resolve('./node_modules'), // required to prevent loader from choking non-Prebid.js node_modules - use: [ - { - loader: 'babel-loader' - } - ] - }, - ] - } }; From 4f805cf2f18b9a38081bd8ea5ecc385ce2da9843 Mon Sep 17 00:00:00 2001 From: Demetrio Girardi Date: Thu, 12 Jun 2025 08:04:58 -0700 Subject: [PATCH 74/92] Prebid 10: rename fanAdapter to fanBidAdapter (#13371) --- modules/{fanAdapter.js => fanBidAdapter.js} | 0 modules/{fanAdapter.md => fanBidAdapter.md} | 0 test/spec/modules/{fanAdapter_spec.js => fanBidAdapter_spec.js} | 2 +- 3 files changed, 1 insertion(+), 1 deletion(-) rename modules/{fanAdapter.js => fanBidAdapter.js} (100%) rename modules/{fanAdapter.md => fanBidAdapter.md} (100%) rename test/spec/modules/{fanAdapter_spec.js => fanBidAdapter_spec.js} (99%) diff --git a/modules/fanAdapter.js b/modules/fanBidAdapter.js similarity index 100% rename from modules/fanAdapter.js rename to modules/fanBidAdapter.js diff --git a/modules/fanAdapter.md b/modules/fanBidAdapter.md similarity index 100% rename from modules/fanAdapter.md rename to modules/fanBidAdapter.md diff --git a/test/spec/modules/fanAdapter_spec.js b/test/spec/modules/fanBidAdapter_spec.js similarity index 99% rename from test/spec/modules/fanAdapter_spec.js rename to test/spec/modules/fanBidAdapter_spec.js index 50407e40e03..cada99ef244 100644 --- a/test/spec/modules/fanAdapter_spec.js +++ b/test/spec/modules/fanBidAdapter_spec.js @@ -1,6 +1,6 @@ import * as ajax from 'src/ajax.js'; import { expect } from 'chai'; -import { spec } from 'modules/fanAdapter.js'; +import { spec } from 'modules/fanBidAdapter.js'; import { newBidder } from 'src/adapters/bidderFactory.js'; import { BANNER, NATIVE } from 'src/mediaTypes.js'; From 40a805ff31d1021d5b86ccdd1b9c7b0d7e09514d Mon Sep 17 00:00:00 2001 From: Patrick McCann Date: Fri, 13 Jun 2025 09:47:02 -0400 Subject: [PATCH 75/92] resolve conflicts package lock --- package-lock.json | 2418 ++++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 2268 insertions(+), 150 deletions(-) diff --git a/package-lock.json b/package-lock.json index f3ad2ffb8e6..7051b9e5452 100644 --- a/package-lock.json +++ b/package-lock.json @@ -10,7 +10,9 @@ "license": "Apache-2.0", "dependencies": { "@babel/core": "^7.27.4", + "@babel/plugin-transform-runtime": "^7.18.9", "@babel/preset-env": "^7.27.2", + "@babel/preset-typescript": "^7.26.0", "@babel/runtime": "^7.27.6", "core-js": "^3.42.0", "coveralls-next": "^4.2.1", @@ -19,7 +21,11 @@ "dset": "^3.1.4", "express": "^4.15.4", "fun-hooks": "^1.1.0", + "gulp-babel": "^8.0.0", "gulp-wrap": "^0.15.0", + "iab-adcom": "^1.0.6", + "iab-native": "^1.0.0", + "iab-openrtb": "^1.0.1", "klona": "^2.0.6", "live-connect-js": "^7.2.0" }, @@ -28,6 +34,7 @@ "@babel/plugin-transform-runtime": "^7.27.4", "@babel/register": "^7.24.6", "@eslint/compat": "^1.2.7", + "@types/google-publisher-tag": "^1.20250210.0", "@wdio/browserstack-service": "^9.0.5", "@wdio/cli": "^9.0.5", "@wdio/concise-reporter": "^8.29.0", @@ -51,10 +58,14 @@ "gulp": "^5.0.1", "gulp-clean": "^0.4.0", "gulp-concat": "^2.6.0", + "gulp-connect": "^5.7.0", + "gulp-filter": "^9.0.1", "gulp-if": "^3.0.0", "gulp-js-escape": "^1.0.1", "gulp-rename": "^2.0.0", "gulp-replace": "^1.0.0", + "gulp-sourcemaps": "^3.0.0", + "gulp-tap": "^2.0.0", "is-docker": "^2.2.1", "istanbul": "^0.4.5", "karma": "^6.3.2", @@ -84,7 +95,10 @@ "plugin-error": "^2.0.1", "resolve-from": "^5.0.0", "sinon": "^20.0.0", + "source-map-loader": "^5.0.0", "through2": "^4.0.2", + "typescript": "^5.8.2", + "typescript-eslint": "^8.26.1", "url": "^0.11.0", "url-parse": "^1.0.5", "video.js": "^7.17.0", @@ -586,6 +600,36 @@ "@babel/core": "^7.0.0-0" } }, + "node_modules/@babel/plugin-syntax-jsx": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-jsx/-/plugin-syntax-jsx-7.27.1.tgz", + "integrity": "sha512-y8YTNIeKoyhGd9O0Jiyzyyqk8gdjnumGTQPsz0xOZOQ2RmkVJeZ1vmmfIvFEKqucBG6axJGBZDE/7iI5suUI/w==", + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-typescript": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-typescript/-/plugin-syntax-typescript-7.27.1.tgz", + "integrity": "sha512-xfYCBMxveHrRMnAWl1ZlPXOZjzkN82THFvLhQhFXFt81Z5HnN+EtUkZhv/zcKpmT3fzmWZB0ywiBrbC3vogbwQ==", + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, "node_modules/@babel/plugin-syntax-unicode-sets-regex": { "version": "7.18.6", "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-unicode-sets-regex/-/plugin-syntax-unicode-sets-regex-7.18.6.tgz", @@ -1355,6 +1399,25 @@ "@babel/core": "^7.0.0-0" } }, + "node_modules/@babel/plugin-transform-typescript": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-typescript/-/plugin-transform-typescript-7.27.1.tgz", + "integrity": "sha512-Q5sT5+O4QUebHdbwKedFBEwRLb02zJ7r4A5Gg2hUoLuU3FjdMcyqcywqUrLCaDsFCxzokf7u9kuy7qz51YUuAg==", + "license": "MIT", + "dependencies": { + "@babel/helper-annotate-as-pure": "^7.27.1", + "@babel/helper-create-class-features-plugin": "^7.27.1", + "@babel/helper-plugin-utils": "^7.27.1", + "@babel/helper-skip-transparent-expression-wrappers": "^7.27.1", + "@babel/plugin-syntax-typescript": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, "node_modules/@babel/plugin-transform-unicode-escapes": { "version": "7.27.1", "resolved": "https://registry.npmjs.org/@babel/plugin-transform-unicode-escapes/-/plugin-transform-unicode-escapes-7.27.1.tgz", @@ -1514,6 +1577,25 @@ "@babel/core": "^7.0.0-0 || ^8.0.0-0 <8.0.0" } }, + "node_modules/@babel/preset-typescript": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/preset-typescript/-/preset-typescript-7.27.1.tgz", + "integrity": "sha512-l7WfQfX0WK4M0v2RudjuQK4u99BS6yLHYEmdtVPP7lKV013zr9DygFuWNlnbvQ9LR+LS0Egz/XAvGx5U9MX0fQ==", + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.27.1", + "@babel/helper-validator-option": "^7.27.1", + "@babel/plugin-syntax-jsx": "^7.27.1", + "@babel/plugin-transform-modules-commonjs": "^7.27.1", + "@babel/plugin-transform-typescript": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, "node_modules/@babel/register": { "version": "7.24.6", "resolved": "https://registry.npmjs.org/@babel/register/-/register-7.24.6.tgz", @@ -1636,15 +1718,6 @@ "semver": "bin/semver" } }, - "node_modules/@babel/register/node_modules/source-map": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, "node_modules/@babel/register/node_modules/source-map-support": { "version": "0.5.21", "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.21.tgz", @@ -2396,6 +2469,85 @@ "node": "^18.18.0 || ^20.9.0 || >=21.1.0" } }, + "node_modules/@gulp-sourcemaps/identity-map": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/@gulp-sourcemaps/identity-map/-/identity-map-2.0.1.tgz", + "integrity": "sha512-Tb+nSISZku+eQ4X1lAkevcQa+jknn/OVUgZ3XCxEKIsLsqYuPoJwJOPQeaOk75X3WPftb29GWY1eqE7GLsXb1Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "acorn": "^6.4.1", + "normalize-path": "^3.0.0", + "postcss": "^7.0.16", + "source-map": "^0.6.0", + "through2": "^3.0.1" + }, + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/@gulp-sourcemaps/identity-map/node_modules/acorn": { + "version": "6.4.2", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-6.4.2.tgz", + "integrity": "sha512-XtGIhXwF8YM8bJhGxG5kXgjkEuNGLTkoYqVE+KMR+aspr4KGYmKYg7yUe3KghyQ9yheNwLnjmzh/7+gfDBmHCQ==", + "dev": true, + "license": "MIT", + "bin": { + "acorn": "bin/acorn" + }, + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/@gulp-sourcemaps/identity-map/node_modules/through2": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/through2/-/through2-3.0.2.tgz", + "integrity": "sha512-enaDQ4MUyP2W6ZyT6EsMzqBPZaM/avg8iuo+l2d3QCs0J+6RaqkHV/2/lOwDTueBHeJ/2LG9lrLW3d5rWPucuQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "inherits": "^2.0.4", + "readable-stream": "2 || 3" + } + }, + "node_modules/@gulp-sourcemaps/map-sources": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/@gulp-sourcemaps/map-sources/-/map-sources-1.0.0.tgz", + "integrity": "sha512-o/EatdaGt8+x2qpb0vFLC/2Gug/xYPRXb6a+ET1wGYKozKN3krDWC/zZFZAtrzxJHuDL12mwdfEFKcKMNvc55A==", + "dev": true, + "license": "MIT", + "dependencies": { + "normalize-path": "^2.0.1", + "through2": "^2.0.3" + }, + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/@gulp-sourcemaps/map-sources/node_modules/normalize-path": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-2.1.1.tgz", + "integrity": "sha512-3pKJwH184Xo/lnH6oyP1q2pMd7HcypqqmRs91/6/i2CGtWwIKGCkOOMTm/zXbgTEWHw1uNpNi/igc3ePOYHb6w==", + "dev": true, + "license": "MIT", + "dependencies": { + "remove-trailing-separator": "^1.0.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/@gulp-sourcemaps/map-sources/node_modules/through2": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/through2/-/through2-2.0.5.tgz", + "integrity": "sha512-/mrRod8xqpA+IHSLyGCQ2s8SPHiCDEeQJSep1jqLYeEUClOFG2Qsh+4FU6G9VeqpZnGW/Su8LQGc4YKni5rYSQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "readable-stream": "~2.3.6", + "xtend": "~4.0.1" + } + }, "node_modules/@gulpjs/messages": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/@gulpjs/messages/-/messages-1.1.0.tgz", @@ -3439,6 +3591,13 @@ "integrity": "sha512-W6hyZux6TrtKfF2I9XNLVcsFr4xRr0T+S6hrJ9nDkhA2vzsFPIEAbnY4vgb6v2yKXQ9MJVcbLsARNlMfg4EVtQ==", "dev": true }, + "node_modules/@types/google-publisher-tag": { + "version": "1.20250428.0", + "resolved": "https://registry.npmjs.org/@types/google-publisher-tag/-/google-publisher-tag-1.20250428.0.tgz", + "integrity": "sha512-W+aTMsM4e8PE/TkH/RkMbmmwEFg2si9eUugS5/lt88wkEClqcALi+3WLXW39Xgzu89+3igi/RNIpPLKdt6W7Dg==", + "dev": true, + "license": "MIT" + }, "node_modules/@types/istanbul-lib-coverage": { "version": "2.0.6", "resolved": "https://registry.npmjs.org/@types/istanbul-lib-coverage/-/istanbul-lib-coverage-2.0.6.tgz", @@ -6330,6 +6489,19 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/array-differ": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/array-differ/-/array-differ-4.0.0.tgz", + "integrity": "sha512-Q6VPTLMsmXZ47ENG3V+wQyZS1ZxXMxFyYzA+Z/GMrJ6yIutAIEf9wTyroTzmGjNfox9/h3GdGBCVh43GVFx4Uw==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/array-each": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/array-each/-/array-each-1.0.1.tgz", @@ -6375,6 +6547,19 @@ "node": ">=0.10.0" } }, + "node_modules/array-union": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/array-union/-/array-union-3.0.1.tgz", + "integrity": "sha512-1OvF9IbWwaeiM9VhzYXVQacMibxpXOMYVNIvMtKRyX9SImBXpKcFr8XvFDeEslCyuH/t6KRt7HEO94AlP8Iatw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/array.prototype.findlast": { "version": "1.2.5", "resolved": "https://registry.npmjs.org/array.prototype.findlast/-/array.prototype.findlast-1.2.5.tgz", @@ -6595,6 +6780,19 @@ "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==" }, + "node_modules/atob": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/atob/-/atob-2.1.2.tgz", + "integrity": "sha512-Wm6ukoaOGJi/73p/cl2GvLjTI5JM1k/O14isD73YML8StrH/7/lRFgmg8nICZgD3bZZvjwCGxtMOD3wWNAu8cg==", + "dev": true, + "license": "(MIT OR Apache-2.0)", + "bin": { + "atob": "bin/atob.js" + }, + "engines": { + "node": ">= 4.5.0" + } + }, "node_modules/available-typed-arrays": { "version": "1.0.7", "resolved": "https://registry.npmjs.org/available-typed-arrays/-/available-typed-arrays-1.0.7.tgz", @@ -6855,6 +7053,13 @@ "node": ">=10.0.0" } }, + "node_modules/batch": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/batch/-/batch-0.6.1.tgz", + "integrity": "sha512-x+VAiMRL6UPkx+kudNvxTl6hB2XNNCG2r+7wixVfIYwu/2HKRXimwQyaumLjMveWvT2Hkd/cAJw+QBMfJ/EKVw==", + "dev": true, + "license": "MIT" + }, "node_modules/big-integer": { "version": "1.6.52", "resolved": "https://registry.npmjs.org/big-integer/-/big-integer-1.6.52.tgz", @@ -6967,6 +7172,18 @@ "resolved": "https://registry.npmjs.org/bluebird/-/bluebird-3.7.2.tgz", "integrity": "sha512-XpNj6GDQzdfW+r2Wnn7xiSAd7TM3jzkxGXBGTtWKuSXv1xUV+azxAm8jdWZN06QTQk+2N2XB9jRDkvbmQmcRtg==" }, + "node_modules/body": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/body/-/body-5.1.0.tgz", + "integrity": "sha512-chUsBxGRtuElD6fmw1gHLpvnKdVLK302peeFa9ZqAEk8TyzZ3fygLyUEDDPTJvL9+Bor0dIwn6ePOsRM2y0zQQ==", + "dev": true, + "dependencies": { + "continuable-cache": "^0.3.1", + "error": "^7.0.0", + "raw-body": "~1.1.0", + "safe-json-parse": "~1.0.1" + } + }, "node_modules/body-parser": { "version": "1.20.3", "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.20.3.tgz", @@ -7003,6 +7220,33 @@ "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==" }, + "node_modules/body/node_modules/bytes": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/bytes/-/bytes-1.0.0.tgz", + "integrity": "sha512-/x68VkHLeTl3/Ll8IvxdwzhrT+IyKc52e/oyHhA2RwqPqswSnjVbSddfPRwAsJtbilMAPSRWwAlpxdYsSWOTKQ==", + "dev": true + }, + "node_modules/body/node_modules/raw-body": { + "version": "1.1.7", + "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-1.1.7.tgz", + "integrity": "sha512-WmJJU2e9Y6M5UzTOkHaM7xJGAPQD8PNzx3bAd2+uhZAim6wDk6dAZxPVYLF67XhbR4hmKGh33Lpmh4XWrCH5Mg==", + "dev": true, + "license": "MIT", + "dependencies": { + "bytes": "1", + "string_decoder": "0.10" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/body/node_modules/string_decoder": { + "version": "0.10.31", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-0.10.31.tgz", + "integrity": "sha512-ev2QzSzWPYmy9GuqfIVildA4OdcGLeFZQrq5ys6RtiuF+RQQiZWr8TZNyAcuVXyQRYfEO+MsoB/1BuQVhOJuoQ==", + "dev": true, + "license": "MIT" + }, "node_modules/boolbase": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/boolbase/-/boolbase-1.0.0.tgz", @@ -7853,15 +8097,6 @@ "source-map": "^0.6.1" } }, - "node_modules/concat-with-sourcemaps/node_modules/source-map": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, "node_modules/connect": { "version": "3.7.0", "resolved": "https://registry.npmjs.org/connect/-/connect-3.7.0.tgz", @@ -7877,6 +8112,16 @@ "node": ">= 0.10.0" } }, + "node_modules/connect-livereload": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/connect-livereload/-/connect-livereload-0.6.1.tgz", + "integrity": "sha512-3R0kMOdL7CjJpU66fzAkCe6HNtd3AavCS4m+uW4KtJjrdGPT0SQEZieAYd+cm+lJoBznNQ4lqipYWkhBMgk00g==", + "dev": true, + "license": "MIT", + "engines": { + "node": "*" + } + }, "node_modules/connect/node_modules/debug": { "version": "2.6.9", "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", @@ -7962,6 +8207,12 @@ "node": ">= 0.6" } }, + "node_modules/continuable-cache": { + "version": "0.3.1", + "resolved": "https://registry.npmjs.org/continuable-cache/-/continuable-cache-0.3.1.tgz", + "integrity": "sha512-TF30kpKhTH8AGCG3dut0rdd/19B7Z+qCnrMoBLpyQu/2drZdNrrpcjPEoJeSVsQM+8KmWG5O56oPDjSSUsuTyA==", + "dev": true + }, "node_modules/convert-source-map": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-2.0.0.tgz", @@ -8188,6 +8439,18 @@ "integrity": "sha512-KALDyEYgpY+Rlob/iriUtjV6d5Eq+Y191A5g4UqLAi8CyGP9N1+FdVbkc1SxKc2r4YAYqG8JzO2KGL+AizD70Q==", "license": "MIT" }, + "node_modules/css": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/css/-/css-3.0.0.tgz", + "integrity": "sha512-DG9pFfwOrzc+hawpmqX/dHYHJG+Bsdb0klhyi1sDneOgGOXy9wQIC8hzyVp1e4NRYDBdxcylvywPkkXCHAzTyQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "inherits": "^2.0.4", + "source-map": "^0.6.1", + "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", @@ -8240,6 +8503,20 @@ "integrity": "sha512-GAj5FOq0Hd+RsCGVJxZuKaIDXDf3h6GQoNEjFgbLLI/trgtavwUbSnZ5pVfg27DVCaWjIohryS0JFwIJyT2cMg==", "dev": true }, + "node_modules/d": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/d/-/d-1.0.2.tgz", + "integrity": "sha512-MOqHvMWF9/9MX6nza0KgvFH4HpMU0EF5uUDXqX/BtxtU8NfB0QzRtJ8Oe/6SuS4kbhyzVJwjd97EA4PKrzJ8bw==", + "dev": true, + "license": "ISC", + "dependencies": { + "es5-ext": "^0.10.64", + "type": "^2.7.2" + }, + "engines": { + "node": ">=0.12" + } + }, "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", @@ -8331,6 +8608,28 @@ } } }, + "node_modules/debug-fabulous": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/debug-fabulous/-/debug-fabulous-1.1.0.tgz", + "integrity": "sha512-GZqvGIgKNlUnHUPQhepnUZFIMoi3dgZKQBzKDeL2g7oJF9SNAji/AAu36dusFUas0O+pae74lNeoIPHqXWDkLg==", + "dev": true, + "license": "MIT", + "dependencies": { + "debug": "3.X", + "memoizee": "0.4.X", + "object-assign": "4.X" + } + }, + "node_modules/debug-fabulous/node_modules/debug": { + "version": "3.2.7", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz", + "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "ms": "^2.1.1" + } + }, "node_modules/decamelize": { "version": "6.0.0", "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-6.0.0.tgz", @@ -8343,6 +8642,16 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/decode-uri-component": { + "version": "0.2.2", + "resolved": "https://registry.npmjs.org/decode-uri-component/-/decode-uri-component-0.2.2.tgz", + "integrity": "sha512-FqUYQ+8o158GyGTrMFJms9qh3CqTKvAqgqsTnkLI8sKu0028orqBhxNMFkFen0zGyg6epACD32pjVk58ngIErQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10" + } + }, "node_modules/deep-eql": { "version": "4.1.4", "resolved": "https://registry.npmjs.org/deep-eql/-/deep-eql-4.1.4.tgz", @@ -8521,17 +8830,6 @@ "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, - "license": "BSD-3-Clause", - "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", @@ -8576,6 +8874,16 @@ "node": ">=0.10.0" } }, + "node_modules/detect-newline": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/detect-newline/-/detect-newline-2.1.0.tgz", + "integrity": "sha512-CwffZFvlJffUg9zZA0uqrjQayUTC8ob94pnr5sFwaVv3IOmkfUHcWH+jXaQK3askE51Cqe8/9Ql/0uXNwqZ8Zg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/devtools-protocol": { "version": "0.0.1260888", "resolved": "https://registry.npmjs.org/devtools-protocol/-/devtools-protocol-0.0.1260888.tgz", @@ -9019,6 +9327,15 @@ "errno": "cli.js" } }, + "node_modules/error": { + "version": "7.2.1", + "resolved": "https://registry.npmjs.org/error/-/error-7.2.1.tgz", + "integrity": "sha512-fo9HBvWnx3NGUKMvMwB/CBCMMrfEJgbDTVDEkPygA3Bdd3lM1OyCd+rbQ8BwnpF6GdVeOLDNmyL4N5Bg80ZvdA==", + "dev": true, + "dependencies": { + "string-template": "~0.2.1" + } + }, "node_modules/error-ex": { "version": "1.3.2", "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.2.tgz", @@ -9214,6 +9531,35 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/es5-ext": { + "version": "0.10.64", + "resolved": "https://registry.npmjs.org/es5-ext/-/es5-ext-0.10.64.tgz", + "integrity": "sha512-p2snDhiLaXe6dahss1LddxqEm+SkuDvV8dnIQG0MWjyHpcMNfXKPE+/Cc0y+PhxJX3A4xGNeFCj5oc0BUh6deg==", + "dev": true, + "hasInstallScript": true, + "license": "ISC", + "dependencies": { + "es6-iterator": "^2.0.3", + "es6-symbol": "^3.1.3", + "esniff": "^2.0.1", + "next-tick": "^1.1.0" + }, + "engines": { + "node": ">=0.10" + } + }, + "node_modules/es6-iterator": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/es6-iterator/-/es6-iterator-2.0.3.tgz", + "integrity": "sha512-zw4SRzoUkd+cl+ZoE15A9o1oQd920Bb0iOJMQkQhl3jNc03YqVjAhG7scf9C5KWRU/R13Orf588uCC6525o02g==", + "dev": true, + "license": "MIT", + "dependencies": { + "d": "1", + "es5-ext": "^0.10.35", + "es6-symbol": "^3.1.1" + } + }, "node_modules/es6-promise": { "version": "4.2.8", "resolved": "https://registry.npmjs.org/es6-promise/-/es6-promise-4.2.8.tgz", @@ -9228,6 +9574,33 @@ "es6-promise": "^4.0.3" } }, + "node_modules/es6-symbol": { + "version": "3.1.4", + "resolved": "https://registry.npmjs.org/es6-symbol/-/es6-symbol-3.1.4.tgz", + "integrity": "sha512-U9bFFjX8tFiATgtkJ1zg25+KviIXpgRvRHS8sau3GfhVzThRQrOeksPeT0BWW2MNZs1OEWJ1DPXOQMn0KKRkvg==", + "dev": true, + "license": "ISC", + "dependencies": { + "d": "^1.0.2", + "ext": "^1.7.0" + }, + "engines": { + "node": ">=0.12" + } + }, + "node_modules/es6-weak-map": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/es6-weak-map/-/es6-weak-map-2.0.3.tgz", + "integrity": "sha512-p5um32HOTO1kP+w7PRnB+5lQ43Z6muuMuIMffvDN8ZB4GcnjLBV6zGStpbASIMk4DCAvEaamhe2zhyCb/QXXsA==", + "dev": true, + "license": "ISC", + "dependencies": { + "d": "1", + "es5-ext": "^0.10.46", + "es6-iterator": "^2.0.3", + "es6-symbol": "^3.1.1" + } + }, "node_modules/esbuild": { "version": "0.25.2", "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.25.2.tgz", @@ -10201,6 +10574,22 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/esniff": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/esniff/-/esniff-2.0.1.tgz", + "integrity": "sha512-kTUIGKQ/mDPFoJ0oVfcmyJn4iBDRptjNVIzwIFR7tqWXdVI9xfA2RMwY/gbSpJG3lkdWNEjLap/NqVHZiJsdfg==", + "dev": true, + "license": "ISC", + "dependencies": { + "d": "^1.0.1", + "es5-ext": "^0.10.62", + "event-emitter": "^0.3.5", + "type": "^2.7.2" + }, + "engines": { + "node": ">=0.10" + } + }, "node_modules/espree": { "version": "10.3.0", "resolved": "https://registry.npmjs.org/espree/-/espree-10.3.0.tgz", @@ -10322,6 +10711,17 @@ "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, + "license": "MIT", + "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", @@ -10601,6 +11001,16 @@ "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==" }, + "node_modules/ext": { + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/ext/-/ext-1.7.0.tgz", + "integrity": "sha512-6hxeJYaL110a9b5TEJSj0gojyHQAmA2ch5Os+ySCiA1QGdS697XWY1pzsrSjqA9LDEEgdB/KypIlR59RcLuHYw==", + "dev": true, + "license": "ISC", + "dependencies": { + "type": "^2.7.2" + } + }, "node_modules/extend": { "version": "3.0.2", "resolved": "https://registry.npmjs.org/extend/-/extend-3.0.2.tgz", @@ -10798,6 +11208,19 @@ "reusify": "^1.0.4" } }, + "node_modules/faye-websocket": { + "version": "0.10.0", + "resolved": "https://registry.npmjs.org/faye-websocket/-/faye-websocket-0.10.0.tgz", + "integrity": "sha512-Xhj93RXbMSq8urNCUq4p9l0P6hnySJ/7YNRhYNug0bLOuii7pKO7xQFb5mx9xZXWCar88pLPb805PvUkwrLZpQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "websocket-driver": ">=0.5.1" + }, + "engines": { + "node": ">=0.4.0" + } + }, "node_modules/fd-slicer": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/fd-slicer/-/fd-slicer-1.1.0.tgz", @@ -11903,6 +12326,101 @@ "node": ">=10.13.0" } }, + "node_modules/gulp-babel": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/gulp-babel/-/gulp-babel-8.0.0.tgz", + "integrity": "sha512-oomaIqDXxFkg7lbpBou/gnUkX51/Y/M2ZfSjL2hdqXTAlSWZcgZtd2o0cOH0r/eE8LWD0+Q/PsLsr2DKOoqToQ==", + "license": "MIT", + "dependencies": { + "plugin-error": "^1.0.1", + "replace-ext": "^1.0.0", + "through2": "^2.0.0", + "vinyl-sourcemaps-apply": "^0.2.0" + }, + "engines": { + "node": ">=6" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/gulp-babel/node_modules/ansi-colors": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-1.1.0.tgz", + "integrity": "sha512-SFKX67auSNoVR38N3L+nvsPjOE0bybKTYbkf5tRvushrAPQ9V75huw0ZxBkKVeRU9kqH3d6HA4xTckbwZ4ixmA==", + "license": "MIT", + "dependencies": { + "ansi-wrap": "^0.1.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/gulp-babel/node_modules/arr-diff": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/arr-diff/-/arr-diff-4.0.0.tgz", + "integrity": "sha512-YVIQ82gZPGBebQV/a8dar4AitzCQs0jjXwMPZllpXMaGjXPYVUawSxQrRsjhjupyVxEvbHgUmIhKVlND+j02kA==", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/gulp-babel/node_modules/arr-union": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/arr-union/-/arr-union-3.1.0.tgz", + "integrity": "sha512-sKpyeERZ02v1FeCZT8lrfJq5u6goHCtpTAzPwJYe7c8SPFOboNjNg1vz2L4VTn9T4PQxEx13TbXLmYUcS6Ug7Q==", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/gulp-babel/node_modules/extend-shallow": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-3.0.2.tgz", + "integrity": "sha512-BwY5b5Ql4+qZoefgMj2NUmx+tehVTH/Kf4k1ZEtOHNFcm2wSxMRo992l6X3TIgni2eZVTZ85xMOjF31fwZAj6Q==", + "license": "MIT", + "dependencies": { + "assign-symbols": "^1.0.0", + "is-extendable": "^1.0.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/gulp-babel/node_modules/plugin-error": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/plugin-error/-/plugin-error-1.0.1.tgz", + "integrity": "sha512-L1zP0dk7vGweZME2i+EeakvUNqSrdiI3F91TwEoYiGrAfUXmVv6fJIq4g82PAXxNsWOp0J7ZqQy/3Szz0ajTxA==", + "license": "MIT", + "dependencies": { + "ansi-colors": "^1.0.1", + "arr-diff": "^4.0.0", + "arr-union": "^3.1.0", + "extend-shallow": "^3.0.2" + }, + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/gulp-babel/node_modules/replace-ext": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/replace-ext/-/replace-ext-1.0.1.tgz", + "integrity": "sha512-yD5BHCe7quCgBph4rMQ+0KkIRKwWCrHDOX1p1Gp6HwjPM5kVoCdKGNhN7ydqqsX6lJEnQDKZ/tFMiEdQ1dvPEw==", + "license": "MIT", + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/gulp-babel/node_modules/through2": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/through2/-/through2-2.0.5.tgz", + "integrity": "sha512-/mrRod8xqpA+IHSLyGCQ2s8SPHiCDEeQJSep1jqLYeEUClOFG2Qsh+4FU6G9VeqpZnGW/Su8LQGc4YKni5rYSQ==", + "license": "MIT", + "dependencies": { + "readable-stream": "~2.3.6", + "xtend": "~4.0.1" + } + }, "node_modules/gulp-clean": { "version": "0.4.0", "resolved": "https://registry.npmjs.org/gulp-clean/-/gulp-clean-0.4.0.tgz", @@ -12195,6 +12713,81 @@ "xtend": "~4.0.1" } }, + "node_modules/gulp-connect": { + "version": "5.7.0", + "resolved": "https://registry.npmjs.org/gulp-connect/-/gulp-connect-5.7.0.tgz", + "integrity": "sha512-8tRcC6wgXMLakpPw9M7GRJIhxkYdgZsXwn7n56BA2bQYGLR9NOPhMzx7js+qYDy6vhNkbApGKURjAw1FjY4pNA==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-colors": "^2.0.5", + "connect": "^3.6.6", + "connect-livereload": "^0.6.0", + "fancy-log": "^1.3.2", + "map-stream": "^0.0.7", + "send": "^0.16.2", + "serve-index": "^1.9.1", + "serve-static": "^1.13.2", + "tiny-lr": "^1.1.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/gulp-connect/node_modules/ansi-colors": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-2.0.5.tgz", + "integrity": "sha512-yAdfUZ+c2wetVNIFsNRn44THW+Lty6S5TwMpUfLA/UaGhiXbBv/F8E60/1hMLd0cnF/CDoWH8vzVaI5bAcHCjw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/gulp-connect/node_modules/fancy-log": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/fancy-log/-/fancy-log-1.3.3.tgz", + "integrity": "sha512-k9oEhlyc0FrVh25qYuSELjr8oxsCoc4/LEZfg2iJJrfEk/tZL9bCoJE47gqAvI2m/AUjluCS4+3I0eTx8n3AEw==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-gray": "^0.1.1", + "color-support": "^1.1.3", + "parse-node-version": "^1.0.0", + "time-stamp": "^1.0.0" + }, + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/gulp-filter": { + "version": "9.0.1", + "resolved": "https://registry.npmjs.org/gulp-filter/-/gulp-filter-9.0.1.tgz", + "integrity": "sha512-knVYL8h9bfYIeft3VokVTkuaWJkQJMrFCS3yVjZQC6BGg+1dZFoeUY++B9D2X4eFpeNTx9StWK0qnDby3NO3PA==", + "dev": true, + "license": "MIT", + "dependencies": { + "multimatch": "^7.0.0", + "plugin-error": "^2.0.1", + "slash": "^5.1.0", + "streamfilter": "^3.0.0", + "to-absolute-glob": "^3.0.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + }, + "peerDependencies": { + "gulp": ">=4" + }, + "peerDependenciesMeta": { + "gulp": { + "optional": true + } + } + }, "node_modules/gulp-if": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/gulp-if/-/gulp-if-3.0.0.tgz", @@ -12293,6 +12886,81 @@ "node": ">=10" } }, + "node_modules/gulp-sourcemaps": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/gulp-sourcemaps/-/gulp-sourcemaps-3.0.0.tgz", + "integrity": "sha512-RqvUckJkuYqy4VaIH60RMal4ZtG0IbQ6PXMNkNsshEGJ9cldUPRb/YCgboYae+CLAs1HQNb4ADTKCx65HInquQ==", + "dev": true, + "license": "ISC", + "dependencies": { + "@gulp-sourcemaps/identity-map": "^2.0.1", + "@gulp-sourcemaps/map-sources": "^1.0.0", + "acorn": "^6.4.1", + "convert-source-map": "^1.0.0", + "css": "^3.0.0", + "debug-fabulous": "^1.0.0", + "detect-newline": "^2.0.0", + "graceful-fs": "^4.0.0", + "source-map": "^0.6.0", + "strip-bom-string": "^1.0.0", + "through2": "^2.0.0" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/gulp-sourcemaps/node_modules/acorn": { + "version": "6.4.2", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-6.4.2.tgz", + "integrity": "sha512-XtGIhXwF8YM8bJhGxG5kXgjkEuNGLTkoYqVE+KMR+aspr4KGYmKYg7yUe3KghyQ9yheNwLnjmzh/7+gfDBmHCQ==", + "dev": true, + "license": "MIT", + "bin": { + "acorn": "bin/acorn" + }, + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/gulp-sourcemaps/node_modules/convert-source-map": { + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-1.9.0.tgz", + "integrity": "sha512-ASFBup0Mz1uyiIjANan1jzLQami9z1PoYSZCiiYW2FczPbenXc45FZdBZLzOT+r6+iciuEModtmCti+hjaAk0A==", + "dev": true, + "license": "MIT" + }, + "node_modules/gulp-sourcemaps/node_modules/through2": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/through2/-/through2-2.0.5.tgz", + "integrity": "sha512-/mrRod8xqpA+IHSLyGCQ2s8SPHiCDEeQJSep1jqLYeEUClOFG2Qsh+4FU6G9VeqpZnGW/Su8LQGc4YKni5rYSQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "readable-stream": "~2.3.6", + "xtend": "~4.0.1" + } + }, + "node_modules/gulp-tap": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/gulp-tap/-/gulp-tap-2.0.0.tgz", + "integrity": "sha512-U5/v1bTozx672QHzrvzPe6fPl2io7Wqyrx2y30AG53eMU/idH4BrY/b2yikOkdyhjDqGgPoMUMnpBg9e9LK8Nw==", + "dev": true, + "license": "MIT", + "dependencies": { + "through2": "^3.0.1" + } + }, + "node_modules/gulp-tap/node_modules/through2": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/through2/-/through2-3.0.2.tgz", + "integrity": "sha512-enaDQ4MUyP2W6ZyT6EsMzqBPZaM/avg8iuo+l2d3QCs0J+6RaqkHV/2/lOwDTueBHeJ/2LG9lrLW3d5rWPucuQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "inherits": "^2.0.4", + "readable-stream": "2 || 3" + } + }, "node_modules/gulp-wrap": { "version": "0.15.0", "resolved": "https://registry.npmjs.org/gulp-wrap/-/gulp-wrap-0.15.0.tgz", @@ -12425,15 +13093,6 @@ "uglify-js": "^3.1.4" } }, - "node_modules/handlebars/node_modules/source-map": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, "node_modules/has": { "version": "1.0.4", "resolved": "https://registry.npmjs.org/has/-/has-1.0.4.tgz", @@ -12619,6 +13278,13 @@ "node": ">= 0.8" } }, + "node_modules/http-parser-js": { + "version": "0.5.10", + "resolved": "https://registry.npmjs.org/http-parser-js/-/http-parser-js-0.5.10.tgz", + "integrity": "sha512-Pysuw9XpUq5dVc/2SMHpuTY01RFl8fttgcyunjL7eEMhGM3cI4eOmiCycJDVCo/7O7ClfQD3SaI6ftDzqOXYMA==", + "dev": true, + "license": "MIT" + }, "node_modules/http-proxy": { "version": "1.18.1", "resolved": "https://registry.npmjs.org/http-proxy/-/http-proxy-1.18.1.tgz", @@ -12680,6 +13346,36 @@ "node": ">=18.18.0" } }, + "node_modules/iab-adcom": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/iab-adcom/-/iab-adcom-1.0.6.tgz", + "integrity": "sha512-XAJdidfrFgZNKmHqcXD3Zhqik2rdSmOs+PGgeVfPWgthxvzNBQxkZnKkW3QAau6mrLjtJc8yOQC6awcEv7gryA==", + "license": "MIT", + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/iab-native": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/iab-native/-/iab-native-1.0.0.tgz", + "integrity": "sha512-AxGYpKGRcyG5pbEAqj+ssxNwZAfxC0pRwyKc0MYoKjm0UeOoUNCWrZV0HGimcQii6ebe6MRqBQEeENyHM4qTdQ==", + "license": "MIT", + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/iab-openrtb": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/iab-openrtb/-/iab-openrtb-1.0.1.tgz", + "integrity": "sha512-egawJx6+pMh/6uA/hak1y+R2+XCSH2jxteSkWlY98/XdQQftaMUMllUFNMKrHwq9lgCI70Me06g4JCCnV6E62g==", + "license": "MIT", + "dependencies": { + "iab-adcom": "1.0.6" + }, + "engines": { + "node": ">=14.0.0" + } + }, "node_modules/iconv-lite": { "version": "0.4.24", "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz", @@ -13244,6 +13940,13 @@ "node": ">=0.10.0" } }, + "node_modules/is-promise": { + "version": "2.2.2", + "resolved": "https://registry.npmjs.org/is-promise/-/is-promise-2.2.2.tgz", + "integrity": "sha512-+lP4/6lKUBfQjZ2pdxThZvLUAafmZb8OAxFb8XXtiQmS35INgr85hdOGoEs124ez1FCnZJt6jau/T+alh58QFQ==", + "dev": true, + "license": "MIT" + }, "node_modules/is-regex": { "version": "1.2.1", "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.2.1.tgz", @@ -13624,15 +14327,6 @@ "node": ">=10" } }, - "node_modules/istanbul-lib-source-maps/node_modules/source-map": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, "node_modules/istanbul-reports": { "version": "3.1.7", "resolved": "https://registry.npmjs.org/istanbul-reports/-/istanbul-reports-3.1.7.tgz", @@ -14619,15 +15313,6 @@ "semver": "bin/semver" } }, - "node_modules/karma-coverage-istanbul-reporter/node_modules/source-map": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, "node_modules/karma-firefox-launcher": { "version": "2.1.3", "resolved": "https://registry.npmjs.org/karma-firefox-launcher/-/karma-firefox-launcher-2.1.3.tgz", @@ -14903,15 +15588,6 @@ "url": "https://github.com/sponsors/isaacs" } }, - "node_modules/karma/node_modules/source-map": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, "node_modules/karma/node_modules/strip-ansi": { "version": "6.0.1", "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", @@ -15119,6 +15795,13 @@ "node": ">=20" } }, + "node_modules/livereload-js": { + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/livereload-js/-/livereload-js-2.4.0.tgz", + "integrity": "sha512-XPQH8Z2GDP/Hwz2PCDrh2mth4yFejwA1OZ/81Ti3LgKyhDcEjsSsqFWZojHG0va/duGd+WyosY7eXLDoOyqcPw==", + "dev": true, + "license": "MIT" + }, "node_modules/loader-runner": { "version": "4.3.0", "resolved": "https://registry.npmjs.org/loader-runner/-/loader-runner-4.3.0.tgz", @@ -15370,6 +16053,16 @@ "yallist": "^3.0.2" } }, + "node_modules/lru-queue": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/lru-queue/-/lru-queue-0.1.0.tgz", + "integrity": "sha512-BpdYkt9EvGl8OfWHDQPISVpcl5xZthb+XPsbELj5AQXxIC8IriDZIQYjBJPEm5rS420sjZ0TLEzRcq5KdBhYrQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "es5-ext": "~0.10.2" + } + }, "node_modules/m3u8-parser": { "version": "4.8.0", "resolved": "https://registry.npmjs.org/m3u8-parser/-/m3u8-parser-4.8.0.tgz", @@ -15414,6 +16107,13 @@ "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", + "integrity": "sha512-C0X0KQmGm3N2ftbTGBhSyuydQ+vV1LC3f3zPvT3RXHXNZrvfPZcoXp/N5DOa8vedX/rTMm2CjTtivFg2STJMRQ==", + "dev": true, + "license": "MIT" + }, "node_modules/math-intrinsics": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/math-intrinsics/-/math-intrinsics-1.1.0.tgz", @@ -15430,6 +16130,26 @@ "node": ">= 0.6" } }, + "node_modules/memoizee": { + "version": "0.4.17", + "resolved": "https://registry.npmjs.org/memoizee/-/memoizee-0.4.17.tgz", + "integrity": "sha512-DGqD7Hjpi/1or4F/aYAspXKNm5Yili0QDAFAY4QYvpqpgiY6+1jOfqpmByzjxbWd/T9mChbCArXAbDAsTm5oXA==", + "dev": true, + "license": "ISC", + "dependencies": { + "d": "^1.0.2", + "es5-ext": "^0.10.64", + "es6-weak-map": "^2.0.3", + "event-emitter": "^0.3.5", + "is-promise": "^2.2.2", + "lru-queue": "^0.1.0", + "next-tick": "^1.1.0", + "timers-ext": "^0.1.7" + }, + "engines": { + "node": ">=0.12" + } + }, "node_modules/memory-fs": { "version": "0.5.0", "resolved": "https://registry.npmjs.org/memory-fs/-/memory-fs-0.5.0.tgz", @@ -16034,6 +16754,50 @@ "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" }, + "node_modules/multimatch": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/multimatch/-/multimatch-7.0.0.tgz", + "integrity": "sha512-SYU3HBAdF4psHEL/+jXDKHO95/m5P2RvboHT2Y0WtTttvJLP4H/2WS9WlQPFvF6C8d6SpLw8vjCnQOnVIVOSJQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "array-differ": "^4.0.0", + "array-union": "^3.0.1", + "minimatch": "^9.0.3" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/multimatch/node_modules/brace-expansion": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.2.tgz", + "integrity": "sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0" + } + }, + "node_modules/multimatch/node_modules/minimatch": { + "version": "9.0.5", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.5.tgz", + "integrity": "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==", + "dev": true, + "license": "ISC", + "dependencies": { + "brace-expansion": "^2.0.1" + }, + "engines": { + "node": ">=16 || 14 >=14.17" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, "node_modules/mute-stdout": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/mute-stdout/-/mute-stdout-2.0.0.tgz", @@ -16213,6 +16977,13 @@ "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", + "integrity": "sha512-CXdUiJembsNjuToQvxayPZF9Vqht7hewsvy2sOWafLvi2awflj9mOC6bHIg50orX8IJvWKY9wYQ/zB2kogPslQ==", + "dev": true, + "license": "ISC" + }, "node_modules/nice-try": { "version": "1.0.5", "resolved": "https://registry.npmjs.org/nice-try/-/nice-try-1.0.5.tgz", @@ -17150,6 +17921,31 @@ "node": ">= 0.4" } }, + "node_modules/postcss": { + "version": "7.0.39", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.39.tgz", + "integrity": "sha512-yioayjNbHn6z1/Bywyb2Y4s3yvDAeXGOyxqD+LnVOinq6Mdmd++SW2wUNVzavyyHxd6+DxzWGIuosg6P1Rj8uA==", + "dev": true, + "license": "MIT", + "dependencies": { + "picocolors": "^0.2.1", + "source-map": "^0.6.1" + }, + "engines": { + "node": ">=6.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + } + }, + "node_modules/postcss/node_modules/picocolors": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-0.2.1.tgz", + "integrity": "sha512-cMlDqaLEqfSaW8Z7N5Jw+lyIW869EzT73/F5lhtY9cLGoVxSXznfgfXMO0Z5K0o0Q2TkTXq+0KFsdnSe3jDViA==", + "dev": true, + "license": "ISC" + }, "node_modules/prelude-ls": { "version": "1.2.1", "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz", @@ -18084,6 +18880,12 @@ } ] }, + "node_modules/safe-json-parse": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/safe-json-parse/-/safe-json-parse-1.0.1.tgz", + "integrity": "sha512-o0JmTu17WGUaUOHa1l0FPGXKBfijbxK6qoHzlkihsDXxzBHvJcA7zgviKR92Xs841rX9pK16unfphLq0/KqX7A==", + "dev": true + }, "node_modules/safe-push-apply": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/safe-push-apply/-/safe-push-apply-1.0.0.tgz", @@ -18186,6 +18988,128 @@ "node": ">= 10.13.0" } }, + "node_modules/send": { + "version": "0.16.2", + "resolved": "https://registry.npmjs.org/send/-/send-0.16.2.tgz", + "integrity": "sha512-E64YFPUssFHEFBvpbbjr44NCLtI1AohxQ8ZSiJjQLskAdKuriYEP6VyGEsRDH8ScozGpkaX1BGvhanqCwkcEZw==", + "dev": true, + "license": "MIT", + "dependencies": { + "debug": "2.6.9", + "depd": "~1.1.2", + "destroy": "~1.0.4", + "encodeurl": "~1.0.2", + "escape-html": "~1.0.3", + "etag": "~1.8.1", + "fresh": "0.5.2", + "http-errors": "~1.6.2", + "mime": "1.4.1", + "ms": "2.0.0", + "on-finished": "~2.3.0", + "range-parser": "~1.2.0", + "statuses": "~1.4.0" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/send/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, + "license": "MIT", + "dependencies": { + "ms": "2.0.0" + } + }, + "node_modules/send/node_modules/depd": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/depd/-/depd-1.1.2.tgz", + "integrity": "sha512-7emPTl6Dpo6JRXOXjLRxck+FlLRX5847cLKEn00PLAgc3g2hTZZgr+e4c2v6QpSmLeFP3n5yUo7ft6avBK/5jQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/send/node_modules/destroy": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/destroy/-/destroy-1.0.4.tgz", + "integrity": "sha512-3NdhDuEXnfun/z7x9GOElY49LoqVHoGScmOKwmxhsS8N5Y+Z8KyPPDnaSzqWgYt/ji4mqwfTS34Htrk0zPIXVg==", + "dev": true, + "license": "MIT" + }, + "node_modules/send/node_modules/http-errors": { + "version": "1.6.3", + "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-1.6.3.tgz", + "integrity": "sha512-lks+lVC8dgGyh97jxvxeYTWQFvh4uw4yC12gVl63Cg30sjPX4wuGcdkICVXDAESr6OJGjqGA8Iz5mkeN6zlD7A==", + "dev": true, + "license": "MIT", + "dependencies": { + "depd": "~1.1.2", + "inherits": "2.0.3", + "setprototypeof": "1.1.0", + "statuses": ">= 1.4.0 < 2" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/send/node_modules/inherits": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz", + "integrity": "sha512-x00IRNXNy63jwGkJmzPigoySHbaqpNuzKbBOmzK+g2OdZpQ9w+sxCN+VSB3ja7IAge2OP2qpfxTjeNcyjmW1uw==", + "dev": true, + "license": "ISC" + }, + "node_modules/send/node_modules/mime": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/mime/-/mime-1.4.1.tgz", + "integrity": "sha512-KI1+qOZu5DcW6wayYHSzR/tXKCDC5Om4s1z2QJjDULzLcmf3DvzS7oluY4HCTrc+9FiKmWUgeNLg7W3uIQvxtQ==", + "dev": true, + "license": "MIT", + "bin": { + "mime": "cli.js" + } + }, + "node_modules/send/node_modules/ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", + "dev": true, + "license": "MIT" + }, + "node_modules/send/node_modules/on-finished": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.3.0.tgz", + "integrity": "sha512-ikqdkGAAyf/X/gPhXGvfgAytDZtDbr+bkNUJ0N9h5MI/dmdgCs3l6hoHrcUv41sRKew3jIwrp4qQDXiK99Utww==", + "dev": true, + "license": "MIT", + "dependencies": { + "ee-first": "1.1.1" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/send/node_modules/setprototypeof": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.1.0.tgz", + "integrity": "sha512-BvE/TwpZX4FXExxOxZyRGQQv651MSwmWKZGqvmPcRIjDqWub67kTKuIMx43cZZrS/cBBzwBcNDWoFxt2XEFIpQ==", + "dev": true, + "license": "ISC" + }, + "node_modules/send/node_modules/statuses": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/statuses/-/statuses-1.4.0.tgz", + "integrity": "sha512-zhSCtt8v2NDrRlPQpCNtw/heZLtfUDqxBM1udqikb/Hbk52LK4nQSwr10u77iopCW5LsyHpuXS0GnEc48mLeew==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, "node_modules/serialize-error": { "version": "11.0.3", "resolved": "https://registry.npmjs.org/serialize-error/-/serialize-error-11.0.3.tgz", @@ -18222,6 +19146,92 @@ "randombytes": "^2.1.0" } }, + "node_modules/serve-index": { + "version": "1.9.1", + "resolved": "https://registry.npmjs.org/serve-index/-/serve-index-1.9.1.tgz", + "integrity": "sha512-pXHfKNP4qujrtteMrSBb0rc8HJ9Ms/GrXwcUtUtD5s4ewDJI8bT3Cz2zTVRMKtri49pLx2e0Ya8ziP5Ya2pZZw==", + "dev": true, + "license": "MIT", + "dependencies": { + "accepts": "~1.3.4", + "batch": "0.6.1", + "debug": "2.6.9", + "escape-html": "~1.0.3", + "http-errors": "~1.6.2", + "mime-types": "~2.1.17", + "parseurl": "~1.3.2" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/serve-index/node_modules/debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "dev": true, + "license": "MIT", + "dependencies": { + "ms": "2.0.0" + } + }, + "node_modules/serve-index/node_modules/depd": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/depd/-/depd-1.1.2.tgz", + "integrity": "sha512-7emPTl6Dpo6JRXOXjLRxck+FlLRX5847cLKEn00PLAgc3g2hTZZgr+e4c2v6QpSmLeFP3n5yUo7ft6avBK/5jQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/serve-index/node_modules/http-errors": { + "version": "1.6.3", + "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-1.6.3.tgz", + "integrity": "sha512-lks+lVC8dgGyh97jxvxeYTWQFvh4uw4yC12gVl63Cg30sjPX4wuGcdkICVXDAESr6OJGjqGA8Iz5mkeN6zlD7A==", + "dev": true, + "license": "MIT", + "dependencies": { + "depd": "~1.1.2", + "inherits": "2.0.3", + "setprototypeof": "1.1.0", + "statuses": ">= 1.4.0 < 2" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/serve-index/node_modules/inherits": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz", + "integrity": "sha512-x00IRNXNy63jwGkJmzPigoySHbaqpNuzKbBOmzK+g2OdZpQ9w+sxCN+VSB3ja7IAge2OP2qpfxTjeNcyjmW1uw==", + "dev": true, + "license": "ISC" + }, + "node_modules/serve-index/node_modules/ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", + "dev": true, + "license": "MIT" + }, + "node_modules/serve-index/node_modules/setprototypeof": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.1.0.tgz", + "integrity": "sha512-BvE/TwpZX4FXExxOxZyRGQQv651MSwmWKZGqvmPcRIjDqWub67kTKuIMx43cZZrS/cBBzwBcNDWoFxt2XEFIpQ==", + "dev": true, + "license": "ISC" + }, + "node_modules/serve-index/node_modules/statuses": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/statuses/-/statuses-1.5.0.tgz", + "integrity": "sha512-OpZ3zP+jT1PI7I8nemJX4AKmAX070ZkYPVWV/AaKTJl+tXCTGyVdC1a4SL8RUQYEwk/f34ZX8UTykN68FwrqAA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, "node_modules/serve-static": { "version": "1.16.2", "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.16.2.tgz", @@ -18533,6 +19543,19 @@ "node": ">= 10" } }, + "node_modules/slash": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/slash/-/slash-5.1.0.tgz", + "integrity": "sha512-ZA6oR3T/pEyuqwMgAKT0/hAv8oAXckzbkmR0UkUosQ+Mc4RxGoJkRmwHgHufaenlyAgE1Mxgpdcrf75y6XcnDg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=14.16" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/slashes": { "version": "3.0.12", "resolved": "https://registry.npmjs.org/slashes/-/slashes-3.0.12.tgz", @@ -18638,6 +19661,72 @@ "integrity": "sha512-qnQ7gVMxGNxsiL4lEuJwe/To8UnK7fAnmbGEEH8RpLouuKbeEm0lhbQVFIrNSuB+G7tVrAlVsZgETT5nljf+Iw==", "dev": true }, + "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, + "license": "BSD-3-Clause", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/source-map-js": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.1.tgz", + "integrity": "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==", + "dev": true, + "license": "BSD-3-Clause", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/source-map-loader": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/source-map-loader/-/source-map-loader-5.0.0.tgz", + "integrity": "sha512-k2Dur7CbSLcAH73sBcIkV5xjPV4SzqO1NJ7+XaQl8if3VODDUj3FNchNGpqgJSKbvUfJuhVdv8K2Eu8/TNl2eA==", + "dev": true, + "license": "MIT", + "dependencies": { + "iconv-lite": "^0.6.3", + "source-map-js": "^1.0.2" + }, + "engines": { + "node": ">= 18.12.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + }, + "peerDependencies": { + "webpack": "^5.72.1" + } + }, + "node_modules/source-map-loader/node_modules/iconv-lite": { + "version": "0.6.3", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz", + "integrity": "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==", + "dev": true, + "license": "MIT", + "dependencies": { + "safer-buffer": ">= 2.1.2 < 3.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/source-map-resolve": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/source-map-resolve/-/source-map-resolve-0.6.0.tgz", + "integrity": "sha512-KXBr9d/fO/bWo97NXsPIAW1bFSBOuCnjbNTBMO7N59hsv5i9yzRDfcYwwt0l04+VqnKC+EwzvJZIP/qkuMgR/w==", + "deprecated": "See https://github.com/lydell/source-map-resolve#deprecated", + "dev": true, + "license": "MIT", + "dependencies": { + "atob": "^2.1.2", + "decode-uri-component": "^0.2.0" + } + }, "node_modules/spacetrim": { "version": "0.11.25", "resolved": "https://registry.npmjs.org/spacetrim/-/spacetrim-0.11.25.tgz", @@ -18810,6 +19899,34 @@ "integrity": "sha512-76ORR0DO1o1hlKwTbi/DM3EXWGf3ZJYO8cXX5RJwnul2DEg2oyoZyjLNoQM8WsvZiFKCRfC1O0J7iCvie3RZmQ==", "dev": true }, + "node_modules/streamfilter": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/streamfilter/-/streamfilter-3.0.0.tgz", + "integrity": "sha512-kvKNfXCmUyC8lAXSSHCIXBUlo/lhsLcCU/OmzACZYpRUdtKIH68xYhm/+HI15jFJYtNJGYtCgn2wmIiExY1VwA==", + "dev": true, + "license": "MIT", + "dependencies": { + "readable-stream": "^3.0.6" + }, + "engines": { + "node": ">=8.12.0" + } + }, + "node_modules/streamfilter/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, + "license": "MIT", + "dependencies": { + "inherits": "^2.0.3", + "string_decoder": "^1.1.1", + "util-deprecate": "^1.0.1" + }, + "engines": { + "node": ">= 6" + } + }, "node_modules/streamroller": { "version": "3.1.5", "resolved": "https://registry.npmjs.org/streamroller/-/streamroller-3.1.5.tgz", @@ -18889,6 +20006,12 @@ "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==" }, + "node_modules/string-template": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/string-template/-/string-template-0.2.1.tgz", + "integrity": "sha512-Yptehjogou2xm4UJbxJ4CxgZx12HBfeystp0y3x7s4Dj32ltVVG1Gg8YhKjHZkHicuKpZX/ffilA8505VbUbpw==", + "dev": true + }, "node_modules/string-width": { "version": "4.2.3", "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", @@ -19084,6 +20207,16 @@ "node": ">=4" } }, + "node_modules/strip-bom-string": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/strip-bom-string/-/strip-bom-string-1.0.0.tgz", + "integrity": "sha512-uCC2VHvQRYu+lMh4My/sFNmF2klFymLX1wHJeXnbEJERpV/ZsVuonzerjfrGpIGF7LBVa1O7i9kjiWvJiFck8g==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/strip-eof": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/strip-eof/-/strip-eof-1.0.0.tgz", @@ -19384,15 +20517,6 @@ "node": ">=0.4.0" } }, - "node_modules/terser/node_modules/source-map": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, "node_modules/terser/node_modules/source-map-support": { "version": "0.5.21", "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.21.tgz", @@ -19498,11 +20622,50 @@ "node": ">=0.10.0" } }, + "node_modules/timers-ext": { + "version": "0.1.8", + "resolved": "https://registry.npmjs.org/timers-ext/-/timers-ext-0.1.8.tgz", + "integrity": "sha512-wFH7+SEAcKfJpfLPkrgMPvvwnEtj8W4IurvEyrKsDleXnKLCDw71w8jltvfLa8Rm4qQxxT4jmDBYbJG/z7qoww==", + "dev": true, + "license": "ISC", + "dependencies": { + "es5-ext": "^0.10.64", + "next-tick": "^1.1.0" + }, + "engines": { + "node": ">=0.12" + } + }, "node_modules/tiny-hashes": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/tiny-hashes/-/tiny-hashes-1.0.1.tgz", "integrity": "sha512-knIN5zj4fl7kW4EBU5sLP20DWUvi/rVouvJezV0UAym2DkQaqm365Nyc8F3QEiOvunNDMxR8UhcXd1d5g+Wg1g==" }, + "node_modules/tiny-lr": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/tiny-lr/-/tiny-lr-1.1.1.tgz", + "integrity": "sha512-44yhA3tsaRoMOjQQ+5v5mVdqef+kH6Qze9jTpqtVufgYjYt08zyZAwNwwVBj3i1rJMnR52IxOW0LK0vBzgAkuA==", + "dev": true, + "license": "MIT", + "dependencies": { + "body": "^5.1.0", + "debug": "^3.1.0", + "faye-websocket": "~0.10.0", + "livereload-js": "^2.3.0", + "object-assign": "^4.1.0", + "qs": "^6.4.0" + } + }, + "node_modules/tiny-lr/node_modules/debug": { + "version": "3.2.7", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz", + "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "ms": "^2.1.1" + } + }, "node_modules/tinyglobby": { "version": "0.2.12", "resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.12.tgz", @@ -19563,6 +20726,20 @@ "node": ">=14.14" } }, + "node_modules/to-absolute-glob": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/to-absolute-glob/-/to-absolute-glob-3.0.0.tgz", + "integrity": "sha512-loO/XEWTRqpfcpI7+Jr2RR2Umaaozx1t6OSVWtMi0oy5F/Fxg3IC+D/TToDnxyAGs7uZBGT/6XmyDUxgsObJXA==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-absolute": "^1.0.0", + "is-negated-glob": "^1.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/to-regex-range": { "version": "5.0.1", "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", @@ -19684,6 +20861,13 @@ "fsevents": "~2.3.3" } }, + "node_modules/type": { + "version": "2.7.3", + "resolved": "https://registry.npmjs.org/type/-/type-2.7.3.tgz", + "integrity": "sha512-8j+1QmAbPvLZow5Qpi6NCaN8FB60p/6x8/vfNqOk/hC+HuvFZhL4+WfekuhQLiqFZXOgQdrs3B+XxEmCc6b3FQ==", + "dev": true, + "license": "ISC" + }, "node_modules/type-check": { "version": "0.4.0", "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz", @@ -19808,7 +20992,6 @@ "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.8.2.tgz", "integrity": "sha512-aJn6wq13/afZp/jT9QZmwEjDqqvSGp1VT5GVg+f/t6/oVyrgXM6BY1h9BRh/O5p3PlUPAe+WuiEZOmb/49RqoQ==", "dev": true, - "peer": true, "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" @@ -20484,6 +21667,24 @@ "node": ">=10.13.0" } }, + "node_modules/vinyl-sourcemaps-apply": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/vinyl-sourcemaps-apply/-/vinyl-sourcemaps-apply-0.2.1.tgz", + "integrity": "sha512-+oDh3KYZBoZC8hfocrbrxbLUeaYtQK7J5WU5Br9VqWqmCll3tFJqKp97GC9GmMsVIL0qnx2DgEDVxdo5EZ5sSw==", + "license": "ISC", + "dependencies": { + "source-map": "^0.5.1" + } + }, + "node_modules/vinyl-sourcemaps-apply/node_modules/source-map": { + "version": "0.5.7", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz", + "integrity": "sha512-LbrmJOMUSdEVxIKvdcJzQC+nQhe8FUZQTXQy6+I75skNgn3OoQ0DZA8YnFa7gp8tqtL3KPf1kmo0R5DoApeSGQ==", + "license": "BSD-3-Clause", + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/vinyl/node_modules/replace-ext": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/replace-ext/-/replace-ext-1.0.1.tgz", @@ -21045,15 +22246,6 @@ "webpack": "^5.47.0" } }, - "node_modules/webpack-manifest-plugin/node_modules/source-map": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, "node_modules/webpack-manifest-plugin/node_modules/webpack-sources": { "version": "2.3.1", "resolved": "https://registry.npmjs.org/webpack-sources/-/webpack-sources-2.3.1.tgz", @@ -21266,6 +22458,31 @@ "url": "https://opencollective.com/webpack" } }, + "node_modules/websocket-driver": { + "version": "0.7.4", + "resolved": "https://registry.npmjs.org/websocket-driver/-/websocket-driver-0.7.4.tgz", + "integrity": "sha512-b17KeDIQVjvb0ssuSDF2cYXSg2iztliJ4B9WdsuB6J952qCPKmnVq4DyW5motImXHDC1cBT/1UezrJVsKw5zjg==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "http-parser-js": ">=0.5.1", + "safe-buffer": ">=5.1.0", + "websocket-extensions": ">=0.1.1" + }, + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/websocket-extensions": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/websocket-extensions/-/websocket-extensions-0.1.4.tgz", + "integrity": "sha512-OqedPIGOfsDlo31UNwYbCFMSaO9m9G/0faIHj5/dZFDMFqPTcx6UwqyOy3COEaEOg/9VsGIpdqn62W5KhoKSpg==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=0.8.0" + } + }, "node_modules/whatwg-encoding": { "version": "3.1.1", "resolved": "https://registry.npmjs.org/whatwg-encoding/-/whatwg-encoding-3.1.1.tgz", @@ -21602,7 +22819,6 @@ "version": "4.0.2", "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.2.tgz", "integrity": "sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==", - "dev": true, "engines": { "node": ">=0.4" } @@ -22118,6 +23334,22 @@ "@babel/helper-plugin-utils": "^7.27.1" } }, + "@babel/plugin-syntax-jsx": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-jsx/-/plugin-syntax-jsx-7.27.1.tgz", + "integrity": "sha512-y8YTNIeKoyhGd9O0Jiyzyyqk8gdjnumGTQPsz0xOZOQ2RmkVJeZ1vmmfIvFEKqucBG6axJGBZDE/7iI5suUI/w==", + "requires": { + "@babel/helper-plugin-utils": "^7.27.1" + } + }, + "@babel/plugin-syntax-typescript": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-typescript/-/plugin-syntax-typescript-7.27.1.tgz", + "integrity": "sha512-xfYCBMxveHrRMnAWl1ZlPXOZjzkN82THFvLhQhFXFt81Z5HnN+EtUkZhv/zcKpmT3fzmWZB0ywiBrbC3vogbwQ==", + "requires": { + "@babel/helper-plugin-utils": "^7.27.1" + } + }, "@babel/plugin-syntax-unicode-sets-regex": { "version": "7.18.6", "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-unicode-sets-regex/-/plugin-syntax-unicode-sets-regex-7.18.6.tgz", @@ -22550,6 +23782,18 @@ "@babel/helper-plugin-utils": "^7.27.1" } }, + "@babel/plugin-transform-typescript": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-typescript/-/plugin-transform-typescript-7.27.1.tgz", + "integrity": "sha512-Q5sT5+O4QUebHdbwKedFBEwRLb02zJ7r4A5Gg2hUoLuU3FjdMcyqcywqUrLCaDsFCxzokf7u9kuy7qz51YUuAg==", + "requires": { + "@babel/helper-annotate-as-pure": "^7.27.1", + "@babel/helper-create-class-features-plugin": "^7.27.1", + "@babel/helper-plugin-utils": "^7.27.1", + "@babel/helper-skip-transparent-expression-wrappers": "^7.27.1", + "@babel/plugin-syntax-typescript": "^7.27.1" + } + }, "@babel/plugin-transform-unicode-escapes": { "version": "7.27.1", "resolved": "https://registry.npmjs.org/@babel/plugin-transform-unicode-escapes/-/plugin-transform-unicode-escapes-7.27.1.tgz", @@ -22671,6 +23915,18 @@ "esutils": "^2.0.2" } }, + "@babel/preset-typescript": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/preset-typescript/-/preset-typescript-7.27.1.tgz", + "integrity": "sha512-l7WfQfX0WK4M0v2RudjuQK4u99BS6yLHYEmdtVPP7lKV013zr9DygFuWNlnbvQ9LR+LS0Egz/XAvGx5U9MX0fQ==", + "requires": { + "@babel/helper-plugin-utils": "^7.27.1", + "@babel/helper-validator-option": "^7.27.1", + "@babel/plugin-syntax-jsx": "^7.27.1", + "@babel/plugin-transform-modules-commonjs": "^7.27.1", + "@babel/plugin-transform-typescript": "^7.27.1" + } + }, "@babel/register": { "version": "7.24.6", "resolved": "https://registry.npmjs.org/@babel/register/-/register-7.24.6.tgz", @@ -22760,12 +24016,6 @@ "integrity": "sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g==", "dev": true }, - "source-map": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", - "dev": true - }, "source-map-support": { "version": "0.5.21", "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.21.tgz", @@ -23173,6 +24423,68 @@ "levn": "^0.4.1" } }, + "@gulp-sourcemaps/identity-map": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/@gulp-sourcemaps/identity-map/-/identity-map-2.0.1.tgz", + "integrity": "sha512-Tb+nSISZku+eQ4X1lAkevcQa+jknn/OVUgZ3XCxEKIsLsqYuPoJwJOPQeaOk75X3WPftb29GWY1eqE7GLsXb1Q==", + "dev": true, + "requires": { + "acorn": "^6.4.1", + "normalize-path": "^3.0.0", + "postcss": "^7.0.16", + "source-map": "^0.6.0", + "through2": "^3.0.1" + }, + "dependencies": { + "acorn": { + "version": "6.4.2", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-6.4.2.tgz", + "integrity": "sha512-XtGIhXwF8YM8bJhGxG5kXgjkEuNGLTkoYqVE+KMR+aspr4KGYmKYg7yUe3KghyQ9yheNwLnjmzh/7+gfDBmHCQ==", + "dev": true + }, + "through2": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/through2/-/through2-3.0.2.tgz", + "integrity": "sha512-enaDQ4MUyP2W6ZyT6EsMzqBPZaM/avg8iuo+l2d3QCs0J+6RaqkHV/2/lOwDTueBHeJ/2LG9lrLW3d5rWPucuQ==", + "dev": true, + "requires": { + "inherits": "^2.0.4", + "readable-stream": "2 || 3" + } + } + } + }, + "@gulp-sourcemaps/map-sources": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/@gulp-sourcemaps/map-sources/-/map-sources-1.0.0.tgz", + "integrity": "sha512-o/EatdaGt8+x2qpb0vFLC/2Gug/xYPRXb6a+ET1wGYKozKN3krDWC/zZFZAtrzxJHuDL12mwdfEFKcKMNvc55A==", + "dev": true, + "requires": { + "normalize-path": "^2.0.1", + "through2": "^2.0.3" + }, + "dependencies": { + "normalize-path": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-2.1.1.tgz", + "integrity": "sha512-3pKJwH184Xo/lnH6oyP1q2pMd7HcypqqmRs91/6/i2CGtWwIKGCkOOMTm/zXbgTEWHw1uNpNi/igc3ePOYHb6w==", + "dev": true, + "requires": { + "remove-trailing-separator": "^1.0.1" + } + }, + "through2": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/through2/-/through2-2.0.5.tgz", + "integrity": "sha512-/mrRod8xqpA+IHSLyGCQ2s8SPHiCDEeQJSep1jqLYeEUClOFG2Qsh+4FU6G9VeqpZnGW/Su8LQGc4YKni5rYSQ==", + "dev": true, + "requires": { + "readable-stream": "~2.3.6", + "xtend": "~4.0.1" + } + } + } + }, "@gulpjs/messages": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/@gulpjs/messages/-/messages-1.1.0.tgz", @@ -23961,6 +25273,12 @@ "integrity": "sha512-W6hyZux6TrtKfF2I9XNLVcsFr4xRr0T+S6hrJ9nDkhA2vzsFPIEAbnY4vgb6v2yKXQ9MJVcbLsARNlMfg4EVtQ==", "dev": true }, + "@types/google-publisher-tag": { + "version": "1.20250428.0", + "resolved": "https://registry.npmjs.org/@types/google-publisher-tag/-/google-publisher-tag-1.20250428.0.tgz", + "integrity": "sha512-W+aTMsM4e8PE/TkH/RkMbmmwEFg2si9eUugS5/lt88wkEClqcALi+3WLXW39Xgzu89+3igi/RNIpPLKdt6W7Dg==", + "dev": true + }, "@types/istanbul-lib-coverage": { "version": "2.0.6", "resolved": "https://registry.npmjs.org/@types/istanbul-lib-coverage/-/istanbul-lib-coverage-2.0.6.tgz", @@ -26114,6 +27432,12 @@ "is-array-buffer": "^3.0.5" } }, + "array-differ": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/array-differ/-/array-differ-4.0.0.tgz", + "integrity": "sha512-Q6VPTLMsmXZ47ENG3V+wQyZS1ZxXMxFyYzA+Z/GMrJ6yIutAIEf9wTyroTzmGjNfox9/h3GdGBCVh43GVFx4Uw==", + "dev": true + }, "array-each": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/array-each/-/array-each-1.0.1.tgz", @@ -26145,6 +27469,12 @@ "integrity": "sha512-B1qMD3RBP7O8o0H2KbrXDyB0IccejMF15+87Lvlor12ONPRHP6gTjXMNkt/d3ZuOGbAe66hFmaCfECI24Ufp6w==", "dev": true }, + "array-union": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/array-union/-/array-union-3.0.1.tgz", + "integrity": "sha512-1OvF9IbWwaeiM9VhzYXVQacMibxpXOMYVNIvMtKRyX9SImBXpKcFr8XvFDeEslCyuH/t6KRt7HEO94AlP8Iatw==", + "dev": true + }, "array.prototype.findlast": { "version": "1.2.5", "resolved": "https://registry.npmjs.org/array.prototype.findlast/-/array.prototype.findlast-1.2.5.tgz", @@ -26309,6 +27639,12 @@ "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==" }, + "atob": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/atob/-/atob-2.1.2.tgz", + "integrity": "sha512-Wm6ukoaOGJi/73p/cl2GvLjTI5JM1k/O14isD73YML8StrH/7/lRFgmg8nICZgD3bZZvjwCGxtMOD3wWNAu8cg==", + "dev": true + }, "available-typed-arrays": { "version": "1.0.7", "resolved": "https://registry.npmjs.org/available-typed-arrays/-/available-typed-arrays-1.0.7.tgz", @@ -26485,6 +27821,12 @@ "integrity": "sha512-4Bcg1P8xhUuqcii/S0Z9wiHIrQVPMermM1any+MX5GeGD7faD3/msQUDGLol9wOcz4/jbg/WJnGqoJF6LiBdtg==", "dev": true }, + "batch": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/batch/-/batch-0.6.1.tgz", + "integrity": "sha512-x+VAiMRL6UPkx+kudNvxTl6hB2XNNCG2r+7wixVfIYwu/2HKRXimwQyaumLjMveWvT2Hkd/cAJw+QBMfJ/EKVw==", + "dev": true + }, "big-integer": { "version": "1.6.52", "resolved": "https://registry.npmjs.org/big-integer/-/big-integer-1.6.52.tgz", @@ -26558,6 +27900,42 @@ "resolved": "https://registry.npmjs.org/bluebird/-/bluebird-3.7.2.tgz", "integrity": "sha512-XpNj6GDQzdfW+r2Wnn7xiSAd7TM3jzkxGXBGTtWKuSXv1xUV+azxAm8jdWZN06QTQk+2N2XB9jRDkvbmQmcRtg==" }, + "body": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/body/-/body-5.1.0.tgz", + "integrity": "sha512-chUsBxGRtuElD6fmw1gHLpvnKdVLK302peeFa9ZqAEk8TyzZ3fygLyUEDDPTJvL9+Bor0dIwn6ePOsRM2y0zQQ==", + "dev": true, + "requires": { + "continuable-cache": "^0.3.1", + "error": "^7.0.0", + "raw-body": "~1.1.0", + "safe-json-parse": "~1.0.1" + }, + "dependencies": { + "bytes": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/bytes/-/bytes-1.0.0.tgz", + "integrity": "sha512-/x68VkHLeTl3/Ll8IvxdwzhrT+IyKc52e/oyHhA2RwqPqswSnjVbSddfPRwAsJtbilMAPSRWwAlpxdYsSWOTKQ==", + "dev": true + }, + "raw-body": { + "version": "1.1.7", + "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-1.1.7.tgz", + "integrity": "sha512-WmJJU2e9Y6M5UzTOkHaM7xJGAPQD8PNzx3bAd2+uhZAim6wDk6dAZxPVYLF67XhbR4hmKGh33Lpmh4XWrCH5Mg==", + "dev": true, + "requires": { + "bytes": "1", + "string_decoder": "0.10" + } + }, + "string_decoder": { + "version": "0.10.31", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-0.10.31.tgz", + "integrity": "sha512-ev2QzSzWPYmy9GuqfIVildA4OdcGLeFZQrq5ys6RtiuF+RQQiZWr8TZNyAcuVXyQRYfEO+MsoB/1BuQVhOJuoQ==", + "dev": true + } + } + }, "body-parser": { "version": "1.20.3", "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.20.3.tgz", @@ -27223,14 +28601,6 @@ "dev": true, "requires": { "source-map": "^0.6.1" - }, - "dependencies": { - "source-map": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", - "dev": true - } } }, "connect": { @@ -27292,6 +28662,12 @@ } } }, + "connect-livereload": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/connect-livereload/-/connect-livereload-0.6.1.tgz", + "integrity": "sha512-3R0kMOdL7CjJpU66fzAkCe6HNtd3AavCS4m+uW4KtJjrdGPT0SQEZieAYd+cm+lJoBznNQ4lqipYWkhBMgk00g==", + "dev": true + }, "consolidate": { "version": "0.15.1", "resolved": "https://registry.npmjs.org/consolidate/-/consolidate-0.15.1.tgz", @@ -27313,6 +28689,12 @@ "resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.5.tgz", "integrity": "sha512-nTjqfcBFEipKdXCv4YDQWCfmcLZKm81ldF0pAopTvyrFGVbcR6P/VAAd5G7N+0tTr8QqiU0tFadD6FK4NtJwOA==" }, + "continuable-cache": { + "version": "0.3.1", + "resolved": "https://registry.npmjs.org/continuable-cache/-/continuable-cache-0.3.1.tgz", + "integrity": "sha512-TF30kpKhTH8AGCG3dut0rdd/19B7Z+qCnrMoBLpyQu/2drZdNrrpcjPEoJeSVsQM+8KmWG5O56oPDjSSUsuTyA==", + "dev": true + }, "convert-source-map": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-2.0.0.tgz", @@ -27476,6 +28858,17 @@ "resolved": "https://registry.npmjs.org/crypto-js/-/crypto-js-4.2.0.tgz", "integrity": "sha512-KALDyEYgpY+Rlob/iriUtjV6d5Eq+Y191A5g4UqLAi8CyGP9N1+FdVbkc1SxKc2r4YAYqG8JzO2KGL+AizD70Q==" }, + "css": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/css/-/css-3.0.0.tgz", + "integrity": "sha512-DG9pFfwOrzc+hawpmqX/dHYHJG+Bsdb0klhyi1sDneOgGOXy9wQIC8hzyVp1e4NRYDBdxcylvywPkkXCHAzTyQ==", + "dev": true, + "requires": { + "inherits": "^2.0.4", + "source-map": "^0.6.1", + "source-map-resolve": "^0.6.0" + } + }, "css-select": { "version": "5.1.0", "resolved": "https://registry.npmjs.org/css-select/-/css-select-5.1.0.tgz", @@ -27519,6 +28912,16 @@ "integrity": "sha512-GAj5FOq0Hd+RsCGVJxZuKaIDXDf3h6GQoNEjFgbLLI/trgtavwUbSnZ5pVfg27DVCaWjIohryS0JFwIJyT2cMg==", "dev": true }, + "d": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/d/-/d-1.0.2.tgz", + "integrity": "sha512-MOqHvMWF9/9MX6nza0KgvFH4HpMU0EF5uUDXqX/BtxtU8NfB0QzRtJ8Oe/6SuS4kbhyzVJwjd97EA4PKrzJ8bw==", + "dev": true, + "requires": { + "es5-ext": "^0.10.64", + "type": "^2.7.2" + } + }, "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", @@ -27578,12 +28981,40 @@ "ms": "2.1.2" } }, + "debug-fabulous": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/debug-fabulous/-/debug-fabulous-1.1.0.tgz", + "integrity": "sha512-GZqvGIgKNlUnHUPQhepnUZFIMoi3dgZKQBzKDeL2g7oJF9SNAji/AAu36dusFUas0O+pae74lNeoIPHqXWDkLg==", + "dev": true, + "requires": { + "debug": "3.X", + "memoizee": "0.4.X", + "object-assign": "4.X" + }, + "dependencies": { + "debug": { + "version": "3.2.7", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz", + "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==", + "dev": true, + "requires": { + "ms": "^2.1.1" + } + } + } + }, "decamelize": { "version": "6.0.0", "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-6.0.0.tgz", "integrity": "sha512-Fv96DCsdOgB6mdGl67MT5JaTNKRzrzill5OH5s8bjYJXVlcXyPYGyPsUkWyGV5p1TXI5esYIYMMeDJL0hEIwaA==", "dev": true }, + "decode-uri-component": { + "version": "0.2.2", + "resolved": "https://registry.npmjs.org/decode-uri-component/-/decode-uri-component-0.2.2.tgz", + "integrity": "sha512-FqUYQ+8o158GyGTrMFJms9qh3CqTKvAqgqsTnkLI8sKu0028orqBhxNMFkFen0zGyg6epACD32pjVk58ngIErQ==", + "dev": true + }, "deep-eql": { "version": "4.1.4", "resolved": "https://registry.npmjs.org/deep-eql/-/deep-eql-4.1.4.tgz", @@ -27706,13 +29137,6 @@ "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 } } }, @@ -27743,6 +29167,12 @@ "integrity": "sha512-DtCOLG98P007x7wiiOmfI0fi3eIKyWiLTGJ2MDnVi/E04lWGbf+JzrRHMm0rgIIZJGtHpKpbVgLWHrv8xXpc3Q==", "dev": true }, + "detect-newline": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/detect-newline/-/detect-newline-2.1.0.tgz", + "integrity": "sha512-CwffZFvlJffUg9zZA0uqrjQayUTC8ob94pnr5sFwaVv3IOmkfUHcWH+jXaQK3askE51Cqe8/9Ql/0uXNwqZ8Zg==", + "dev": true + }, "devtools-protocol": { "version": "0.0.1260888", "resolved": "https://registry.npmjs.org/devtools-protocol/-/devtools-protocol-0.0.1260888.tgz", @@ -28088,6 +29518,15 @@ "prr": "~1.0.1" } }, + "error": { + "version": "7.2.1", + "resolved": "https://registry.npmjs.org/error/-/error-7.2.1.tgz", + "integrity": "sha512-fo9HBvWnx3NGUKMvMwB/CBCMMrfEJgbDTVDEkPygA3Bdd3lM1OyCd+rbQ8BwnpF6GdVeOLDNmyL4N5Bg80ZvdA==", + "dev": true, + "requires": { + "string-template": "~0.2.1" + } + }, "error-ex": { "version": "1.3.2", "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.2.tgz", @@ -28253,6 +29692,29 @@ "is-symbol": "^1.0.4" } }, + "es5-ext": { + "version": "0.10.64", + "resolved": "https://registry.npmjs.org/es5-ext/-/es5-ext-0.10.64.tgz", + "integrity": "sha512-p2snDhiLaXe6dahss1LddxqEm+SkuDvV8dnIQG0MWjyHpcMNfXKPE+/Cc0y+PhxJX3A4xGNeFCj5oc0BUh6deg==", + "dev": true, + "requires": { + "es6-iterator": "^2.0.3", + "es6-symbol": "^3.1.3", + "esniff": "^2.0.1", + "next-tick": "^1.1.0" + } + }, + "es6-iterator": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/es6-iterator/-/es6-iterator-2.0.3.tgz", + "integrity": "sha512-zw4SRzoUkd+cl+ZoE15A9o1oQd920Bb0iOJMQkQhl3jNc03YqVjAhG7scf9C5KWRU/R13Orf588uCC6525o02g==", + "dev": true, + "requires": { + "d": "1", + "es5-ext": "^0.10.35", + "es6-symbol": "^3.1.1" + } + }, "es6-promise": { "version": "4.2.8", "resolved": "https://registry.npmjs.org/es6-promise/-/es6-promise-4.2.8.tgz", @@ -28267,6 +29729,28 @@ "es6-promise": "^4.0.3" } }, + "es6-symbol": { + "version": "3.1.4", + "resolved": "https://registry.npmjs.org/es6-symbol/-/es6-symbol-3.1.4.tgz", + "integrity": "sha512-U9bFFjX8tFiATgtkJ1zg25+KviIXpgRvRHS8sau3GfhVzThRQrOeksPeT0BWW2MNZs1OEWJ1DPXOQMn0KKRkvg==", + "dev": true, + "requires": { + "d": "^1.0.2", + "ext": "^1.7.0" + } + }, + "es6-weak-map": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/es6-weak-map/-/es6-weak-map-2.0.3.tgz", + "integrity": "sha512-p5um32HOTO1kP+w7PRnB+5lQ43Z6muuMuIMffvDN8ZB4GcnjLBV6zGStpbASIMk4DCAvEaamhe2zhyCb/QXXsA==", + "dev": true, + "requires": { + "d": "1", + "es5-ext": "^0.10.46", + "es6-iterator": "^2.0.3", + "es6-symbol": "^3.1.1" + } + }, "esbuild": { "version": "0.25.2", "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.25.2.tgz", @@ -28956,6 +30440,18 @@ "integrity": "sha512-0rSmRBzXgDzIsD6mGdJgevzgezI534Cer5L/vyMX0kHzT/jiB43jRhd9YUlMGYLQy2zprNmoT8qasCGtY+QaKw==", "dev": true }, + "esniff": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/esniff/-/esniff-2.0.1.tgz", + "integrity": "sha512-kTUIGKQ/mDPFoJ0oVfcmyJn4iBDRptjNVIzwIFR7tqWXdVI9xfA2RMwY/gbSpJG3lkdWNEjLap/NqVHZiJsdfg==", + "dev": true, + "requires": { + "d": "^1.0.1", + "es5-ext": "^0.10.62", + "event-emitter": "^0.3.5", + "type": "^2.7.2" + } + }, "espree": { "version": "10.3.0", "resolved": "https://registry.npmjs.org/espree/-/espree-10.3.0.tgz", @@ -29037,6 +30533,16 @@ "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz", "integrity": "sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg==" }, + "event-emitter": { + "version": "0.3.5", + "resolved": "https://registry.npmjs.org/event-emitter/-/event-emitter-0.3.5.tgz", + "integrity": "sha512-D9rRn9y7kLPnJ+hMq7S/nhvoKwwvVJahBi2BPmx3bvbsEdK3W9ii8cBSGjP+72/LnM4n6fo3+dkCX5FeTQruXA==", + "dev": true, + "requires": { + "d": "1", + "es5-ext": "~0.10.14" + } + }, "event-stream": { "version": "3.3.4", "resolved": "https://registry.npmjs.org/event-stream/-/event-stream-3.3.4.tgz", @@ -29267,6 +30773,15 @@ } } }, + "ext": { + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/ext/-/ext-1.7.0.tgz", + "integrity": "sha512-6hxeJYaL110a9b5TEJSj0gojyHQAmA2ch5Os+ySCiA1QGdS697XWY1pzsrSjqA9LDEEgdB/KypIlR59RcLuHYw==", + "dev": true, + "requires": { + "type": "^2.7.2" + } + }, "extend": { "version": "3.0.2", "resolved": "https://registry.npmjs.org/extend/-/extend-3.0.2.tgz", @@ -29421,6 +30936,15 @@ "reusify": "^1.0.4" } }, + "faye-websocket": { + "version": "0.10.0", + "resolved": "https://registry.npmjs.org/faye-websocket/-/faye-websocket-0.10.0.tgz", + "integrity": "sha512-Xhj93RXbMSq8urNCUq4p9l0P6hnySJ/7YNRhYNug0bLOuii7pKO7xQFb5mx9xZXWCar88pLPb805PvUkwrLZpQ==", + "dev": true, + "requires": { + "websocket-driver": ">=0.5.1" + } + }, "fd-slicer": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/fd-slicer/-/fd-slicer-1.1.0.tgz", @@ -30238,6 +31762,71 @@ "vinyl-fs": "^4.0.2" } }, + "gulp-babel": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/gulp-babel/-/gulp-babel-8.0.0.tgz", + "integrity": "sha512-oomaIqDXxFkg7lbpBou/gnUkX51/Y/M2ZfSjL2hdqXTAlSWZcgZtd2o0cOH0r/eE8LWD0+Q/PsLsr2DKOoqToQ==", + "requires": { + "plugin-error": "^1.0.1", + "replace-ext": "^1.0.0", + "through2": "^2.0.0", + "vinyl-sourcemaps-apply": "^0.2.0" + }, + "dependencies": { + "ansi-colors": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-1.1.0.tgz", + "integrity": "sha512-SFKX67auSNoVR38N3L+nvsPjOE0bybKTYbkf5tRvushrAPQ9V75huw0ZxBkKVeRU9kqH3d6HA4xTckbwZ4ixmA==", + "requires": { + "ansi-wrap": "^0.1.0" + } + }, + "arr-diff": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/arr-diff/-/arr-diff-4.0.0.tgz", + "integrity": "sha512-YVIQ82gZPGBebQV/a8dar4AitzCQs0jjXwMPZllpXMaGjXPYVUawSxQrRsjhjupyVxEvbHgUmIhKVlND+j02kA==" + }, + "arr-union": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/arr-union/-/arr-union-3.1.0.tgz", + "integrity": "sha512-sKpyeERZ02v1FeCZT8lrfJq5u6goHCtpTAzPwJYe7c8SPFOboNjNg1vz2L4VTn9T4PQxEx13TbXLmYUcS6Ug7Q==" + }, + "extend-shallow": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-3.0.2.tgz", + "integrity": "sha512-BwY5b5Ql4+qZoefgMj2NUmx+tehVTH/Kf4k1ZEtOHNFcm2wSxMRo992l6X3TIgni2eZVTZ85xMOjF31fwZAj6Q==", + "requires": { + "assign-symbols": "^1.0.0", + "is-extendable": "^1.0.1" + } + }, + "plugin-error": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/plugin-error/-/plugin-error-1.0.1.tgz", + "integrity": "sha512-L1zP0dk7vGweZME2i+EeakvUNqSrdiI3F91TwEoYiGrAfUXmVv6fJIq4g82PAXxNsWOp0J7ZqQy/3Szz0ajTxA==", + "requires": { + "ansi-colors": "^1.0.1", + "arr-diff": "^4.0.0", + "arr-union": "^3.1.0", + "extend-shallow": "^3.0.2" + } + }, + "replace-ext": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/replace-ext/-/replace-ext-1.0.1.tgz", + "integrity": "sha512-yD5BHCe7quCgBph4rMQ+0KkIRKwWCrHDOX1p1Gp6HwjPM5kVoCdKGNhN7ydqqsX6lJEnQDKZ/tFMiEdQ1dvPEw==" + }, + "through2": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/through2/-/through2-2.0.5.tgz", + "integrity": "sha512-/mrRod8xqpA+IHSLyGCQ2s8SPHiCDEeQJSep1jqLYeEUClOFG2Qsh+4FU6G9VeqpZnGW/Su8LQGc4YKni5rYSQ==", + "requires": { + "readable-stream": "~2.3.6", + "xtend": "~4.0.1" + } + } + } + }, "gulp-clean": { "version": "0.4.0", "resolved": "https://registry.npmjs.org/gulp-clean/-/gulp-clean-0.4.0.tgz", @@ -30457,6 +32046,56 @@ } } }, + "gulp-connect": { + "version": "5.7.0", + "resolved": "https://registry.npmjs.org/gulp-connect/-/gulp-connect-5.7.0.tgz", + "integrity": "sha512-8tRcC6wgXMLakpPw9M7GRJIhxkYdgZsXwn7n56BA2bQYGLR9NOPhMzx7js+qYDy6vhNkbApGKURjAw1FjY4pNA==", + "dev": true, + "requires": { + "ansi-colors": "^2.0.5", + "connect": "^3.6.6", + "connect-livereload": "^0.6.0", + "fancy-log": "^1.3.2", + "map-stream": "^0.0.7", + "send": "^0.16.2", + "serve-index": "^1.9.1", + "serve-static": "^1.13.2", + "tiny-lr": "^1.1.1" + }, + "dependencies": { + "ansi-colors": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-2.0.5.tgz", + "integrity": "sha512-yAdfUZ+c2wetVNIFsNRn44THW+Lty6S5TwMpUfLA/UaGhiXbBv/F8E60/1hMLd0cnF/CDoWH8vzVaI5bAcHCjw==", + "dev": true + }, + "fancy-log": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/fancy-log/-/fancy-log-1.3.3.tgz", + "integrity": "sha512-k9oEhlyc0FrVh25qYuSELjr8oxsCoc4/LEZfg2iJJrfEk/tZL9bCoJE47gqAvI2m/AUjluCS4+3I0eTx8n3AEw==", + "dev": true, + "requires": { + "ansi-gray": "^0.1.1", + "color-support": "^1.1.3", + "parse-node-version": "^1.0.0", + "time-stamp": "^1.0.0" + } + } + } + }, + "gulp-filter": { + "version": "9.0.1", + "resolved": "https://registry.npmjs.org/gulp-filter/-/gulp-filter-9.0.1.tgz", + "integrity": "sha512-knVYL8h9bfYIeft3VokVTkuaWJkQJMrFCS3yVjZQC6BGg+1dZFoeUY++B9D2X4eFpeNTx9StWK0qnDby3NO3PA==", + "dev": true, + "requires": { + "multimatch": "^7.0.0", + "plugin-error": "^2.0.1", + "slash": "^5.1.0", + "streamfilter": "^3.0.0", + "to-absolute-glob": "^3.0.0" + } + }, "gulp-if": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/gulp-if/-/gulp-if-3.0.0.tgz", @@ -30553,6 +32192,70 @@ "yargs-parser": ">=5.0.0-security.0" } }, + "gulp-sourcemaps": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/gulp-sourcemaps/-/gulp-sourcemaps-3.0.0.tgz", + "integrity": "sha512-RqvUckJkuYqy4VaIH60RMal4ZtG0IbQ6PXMNkNsshEGJ9cldUPRb/YCgboYae+CLAs1HQNb4ADTKCx65HInquQ==", + "dev": true, + "requires": { + "@gulp-sourcemaps/identity-map": "^2.0.1", + "@gulp-sourcemaps/map-sources": "^1.0.0", + "acorn": "^6.4.1", + "convert-source-map": "^1.0.0", + "css": "^3.0.0", + "debug-fabulous": "^1.0.0", + "detect-newline": "^2.0.0", + "graceful-fs": "^4.0.0", + "source-map": "^0.6.0", + "strip-bom-string": "^1.0.0", + "through2": "^2.0.0" + }, + "dependencies": { + "acorn": { + "version": "6.4.2", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-6.4.2.tgz", + "integrity": "sha512-XtGIhXwF8YM8bJhGxG5kXgjkEuNGLTkoYqVE+KMR+aspr4KGYmKYg7yUe3KghyQ9yheNwLnjmzh/7+gfDBmHCQ==", + "dev": true + }, + "convert-source-map": { + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-1.9.0.tgz", + "integrity": "sha512-ASFBup0Mz1uyiIjANan1jzLQami9z1PoYSZCiiYW2FczPbenXc45FZdBZLzOT+r6+iciuEModtmCti+hjaAk0A==", + "dev": true + }, + "through2": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/through2/-/through2-2.0.5.tgz", + "integrity": "sha512-/mrRod8xqpA+IHSLyGCQ2s8SPHiCDEeQJSep1jqLYeEUClOFG2Qsh+4FU6G9VeqpZnGW/Su8LQGc4YKni5rYSQ==", + "dev": true, + "requires": { + "readable-stream": "~2.3.6", + "xtend": "~4.0.1" + } + } + } + }, + "gulp-tap": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/gulp-tap/-/gulp-tap-2.0.0.tgz", + "integrity": "sha512-U5/v1bTozx672QHzrvzPe6fPl2io7Wqyrx2y30AG53eMU/idH4BrY/b2yikOkdyhjDqGgPoMUMnpBg9e9LK8Nw==", + "dev": true, + "requires": { + "through2": "^3.0.1" + }, + "dependencies": { + "through2": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/through2/-/through2-3.0.2.tgz", + "integrity": "sha512-enaDQ4MUyP2W6ZyT6EsMzqBPZaM/avg8iuo+l2d3QCs0J+6RaqkHV/2/lOwDTueBHeJ/2LG9lrLW3d5rWPucuQ==", + "dev": true, + "requires": { + "inherits": "^2.0.4", + "readable-stream": "2 || 3" + } + } + } + }, "gulp-wrap": { "version": "0.15.0", "resolved": "https://registry.npmjs.org/gulp-wrap/-/gulp-wrap-0.15.0.tgz", @@ -30648,14 +32351,6 @@ "source-map": "^0.6.1", "uglify-js": "^3.1.4", "wordwrap": "^1.0.0" - }, - "dependencies": { - "source-map": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", - "dev": true - } } }, "has": { @@ -30789,6 +32484,12 @@ "toidentifier": "1.0.1" } }, + "http-parser-js": { + "version": "0.5.10", + "resolved": "https://registry.npmjs.org/http-parser-js/-/http-parser-js-0.5.10.tgz", + "integrity": "sha512-Pysuw9XpUq5dVc/2SMHpuTY01RFl8fttgcyunjL7eEMhGM3cI4eOmiCycJDVCo/7O7ClfQD3SaI6ftDzqOXYMA==", + "dev": true + }, "http-proxy": { "version": "1.18.1", "resolved": "https://registry.npmjs.org/http-proxy/-/http-proxy-1.18.1.tgz", @@ -30837,6 +32538,24 @@ "integrity": "sha512-/1/GPCpDUCCYwlERiYjxoczfP0zfvZMU/OWgQPMya9AbAE24vseigFdhAMObpc8Q4lc/kjutPfUddDYyAmejnA==", "dev": true }, + "iab-adcom": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/iab-adcom/-/iab-adcom-1.0.6.tgz", + "integrity": "sha512-XAJdidfrFgZNKmHqcXD3Zhqik2rdSmOs+PGgeVfPWgthxvzNBQxkZnKkW3QAau6mrLjtJc8yOQC6awcEv7gryA==" + }, + "iab-native": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/iab-native/-/iab-native-1.0.0.tgz", + "integrity": "sha512-AxGYpKGRcyG5pbEAqj+ssxNwZAfxC0pRwyKc0MYoKjm0UeOoUNCWrZV0HGimcQii6ebe6MRqBQEeENyHM4qTdQ==" + }, + "iab-openrtb": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/iab-openrtb/-/iab-openrtb-1.0.1.tgz", + "integrity": "sha512-egawJx6+pMh/6uA/hak1y+R2+XCSH2jxteSkWlY98/XdQQftaMUMllUFNMKrHwq9lgCI70Me06g4JCCnV6E62g==", + "requires": { + "iab-adcom": "1.0.6" + } + }, "iconv-lite": { "version": "0.4.24", "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz", @@ -31217,6 +32936,12 @@ "integrity": "sha512-VRSzKkbMm5jMDoKLbltAkFQ5Qr7VDiTFGXxYFXXowVj387GeGNOCsOH6Msy00SGZ3Fp84b1Naa1psqgcCIEP5Q==", "dev": true }, + "is-promise": { + "version": "2.2.2", + "resolved": "https://registry.npmjs.org/is-promise/-/is-promise-2.2.2.tgz", + "integrity": "sha512-+lP4/6lKUBfQjZ2pdxThZvLUAafmZb8OAxFb8XXtiQmS35INgr85hdOGoEs124ez1FCnZJt6jau/T+alh58QFQ==", + "dev": true + }, "is-regex": { "version": "1.2.1", "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.2.1.tgz", @@ -31523,14 +33248,6 @@ "debug": "^4.1.1", "istanbul-lib-coverage": "^3.0.0", "source-map": "^0.6.1" - }, - "dependencies": { - "source-map": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", - "dev": true - } } }, "istanbul-reports": { @@ -32133,12 +33850,6 @@ "path-is-absolute": "^1.0.0" } }, - "source-map": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", - "dev": true - }, "strip-ansi": { "version": "6.0.1", "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", @@ -32325,12 +34036,6 @@ "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.2.tgz", "integrity": "sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g==", "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 } } }, @@ -32614,6 +34319,12 @@ "tiny-hashes": "1.0.1" } }, + "livereload-js": { + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/livereload-js/-/livereload-js-2.4.0.tgz", + "integrity": "sha512-XPQH8Z2GDP/Hwz2PCDrh2mth4yFejwA1OZ/81Ti3LgKyhDcEjsSsqFWZojHG0va/duGd+WyosY7eXLDoOyqcPw==", + "dev": true + }, "loader-runner": { "version": "4.3.0", "resolved": "https://registry.npmjs.org/loader-runner/-/loader-runner-4.3.0.tgz", @@ -32816,6 +34527,15 @@ "yallist": "^3.0.2" } }, + "lru-queue": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/lru-queue/-/lru-queue-0.1.0.tgz", + "integrity": "sha512-BpdYkt9EvGl8OfWHDQPISVpcl5xZthb+XPsbELj5AQXxIC8IriDZIQYjBJPEm5rS420sjZ0TLEzRcq5KdBhYrQ==", + "dev": true, + "requires": { + "es5-ext": "~0.10.2" + } + }, "m3u8-parser": { "version": "4.8.0", "resolved": "https://registry.npmjs.org/m3u8-parser/-/m3u8-parser-4.8.0.tgz", @@ -32851,6 +34571,12 @@ "integrity": "sha512-8y/eV9QQZCiyn1SprXSrCmqJN0yNRATe+PO8ztwqrvrbdRLA3eYJF0yaR0YayLWkMbsQSKWS9N2gPcGEc4UsZg==", "dev": true }, + "map-stream": { + "version": "0.0.7", + "resolved": "https://registry.npmjs.org/map-stream/-/map-stream-0.0.7.tgz", + "integrity": "sha512-C0X0KQmGm3N2ftbTGBhSyuydQ+vV1LC3f3zPvT3RXHXNZrvfPZcoXp/N5DOa8vedX/rTMm2CjTtivFg2STJMRQ==", + "dev": true + }, "math-intrinsics": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/math-intrinsics/-/math-intrinsics-1.1.0.tgz", @@ -32861,6 +34587,22 @@ "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz", "integrity": "sha512-dq+qelQ9akHpcOl/gUVRTxVIOkAJ1wR3QAvb4RsVjS8oVoFjDGTc679wJYmUmknUF5HwMLOgb5O+a3KxfWapPQ==" }, + "memoizee": { + "version": "0.4.17", + "resolved": "https://registry.npmjs.org/memoizee/-/memoizee-0.4.17.tgz", + "integrity": "sha512-DGqD7Hjpi/1or4F/aYAspXKNm5Yili0QDAFAY4QYvpqpgiY6+1jOfqpmByzjxbWd/T9mChbCArXAbDAsTm5oXA==", + "dev": true, + "requires": { + "d": "^1.0.2", + "es5-ext": "^0.10.64", + "es6-weak-map": "^2.0.3", + "event-emitter": "^0.3.5", + "is-promise": "^2.2.2", + "lru-queue": "^0.1.0", + "next-tick": "^1.1.0", + "timers-ext": "^0.1.7" + } + }, "memory-fs": { "version": "0.5.0", "resolved": "https://registry.npmjs.org/memory-fs/-/memory-fs-0.5.0.tgz", @@ -33301,6 +35043,37 @@ "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" }, + "multimatch": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/multimatch/-/multimatch-7.0.0.tgz", + "integrity": "sha512-SYU3HBAdF4psHEL/+jXDKHO95/m5P2RvboHT2Y0WtTttvJLP4H/2WS9WlQPFvF6C8d6SpLw8vjCnQOnVIVOSJQ==", + "dev": true, + "requires": { + "array-differ": "^4.0.0", + "array-union": "^3.0.1", + "minimatch": "^9.0.3" + }, + "dependencies": { + "brace-expansion": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.2.tgz", + "integrity": "sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ==", + "dev": true, + "requires": { + "balanced-match": "^1.0.0" + } + }, + "minimatch": { + "version": "9.0.5", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.5.tgz", + "integrity": "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==", + "dev": true, + "requires": { + "brace-expansion": "^2.0.1" + } + } + } + }, "mute-stdout": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/mute-stdout/-/mute-stdout-2.0.0.tgz", @@ -33416,6 +35189,12 @@ "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", + "integrity": "sha512-CXdUiJembsNjuToQvxayPZF9Vqht7hewsvy2sOWafLvi2awflj9mOC6bHIg50orX8IJvWKY9wYQ/zB2kogPslQ==", + "dev": true + }, "nice-try": { "version": "1.0.5", "resolved": "https://registry.npmjs.org/nice-try/-/nice-try-1.0.5.tgz", @@ -34107,6 +35886,24 @@ "integrity": "sha512-d7Uw+eZoloe0EHDIYoe+bQ5WXnGMOpmiZFTuMWCwpjzzkL2nTjcKiAk4hh8TjnGye2TwWOk3UXucZ+3rbmBa8Q==", "dev": true }, + "postcss": { + "version": "7.0.39", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.39.tgz", + "integrity": "sha512-yioayjNbHn6z1/Bywyb2Y4s3yvDAeXGOyxqD+LnVOinq6Mdmd++SW2wUNVzavyyHxd6+DxzWGIuosg6P1Rj8uA==", + "dev": true, + "requires": { + "picocolors": "^0.2.1", + "source-map": "^0.6.1" + }, + "dependencies": { + "picocolors": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-0.2.1.tgz", + "integrity": "sha512-cMlDqaLEqfSaW8Z7N5Jw+lyIW869EzT73/F5lhtY9cLGoVxSXznfgfXMO0Z5K0o0Q2TkTXq+0KFsdnSe3jDViA==", + "dev": true + } + } + }, "prelude-ls": { "version": "1.2.1", "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz", @@ -34785,6 +36582,12 @@ "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==" }, + "safe-json-parse": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/safe-json-parse/-/safe-json-parse-1.0.1.tgz", + "integrity": "sha512-o0JmTu17WGUaUOHa1l0FPGXKBfijbxK6qoHzlkihsDXxzBHvJcA7zgviKR92Xs841rX9pK16unfphLq0/KqX7A==", + "dev": true + }, "safe-push-apply": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/safe-push-apply/-/safe-push-apply-1.0.0.tgz", @@ -34856,6 +36659,101 @@ "sver": "^1.8.3" } }, + "send": { + "version": "0.16.2", + "resolved": "https://registry.npmjs.org/send/-/send-0.16.2.tgz", + "integrity": "sha512-E64YFPUssFHEFBvpbbjr44NCLtI1AohxQ8ZSiJjQLskAdKuriYEP6VyGEsRDH8ScozGpkaX1BGvhanqCwkcEZw==", + "dev": true, + "requires": { + "debug": "2.6.9", + "depd": "~1.1.2", + "destroy": "~1.0.4", + "encodeurl": "~1.0.2", + "escape-html": "~1.0.3", + "etag": "~1.8.1", + "fresh": "0.5.2", + "http-errors": "~1.6.2", + "mime": "1.4.1", + "ms": "2.0.0", + "on-finished": "~2.3.0", + "range-parser": "~1.2.0", + "statuses": "~1.4.0" + }, + "dependencies": { + "debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "dev": true, + "requires": { + "ms": "2.0.0" + } + }, + "depd": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/depd/-/depd-1.1.2.tgz", + "integrity": "sha512-7emPTl6Dpo6JRXOXjLRxck+FlLRX5847cLKEn00PLAgc3g2hTZZgr+e4c2v6QpSmLeFP3n5yUo7ft6avBK/5jQ==", + "dev": true + }, + "destroy": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/destroy/-/destroy-1.0.4.tgz", + "integrity": "sha512-3NdhDuEXnfun/z7x9GOElY49LoqVHoGScmOKwmxhsS8N5Y+Z8KyPPDnaSzqWgYt/ji4mqwfTS34Htrk0zPIXVg==", + "dev": true + }, + "http-errors": { + "version": "1.6.3", + "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-1.6.3.tgz", + "integrity": "sha512-lks+lVC8dgGyh97jxvxeYTWQFvh4uw4yC12gVl63Cg30sjPX4wuGcdkICVXDAESr6OJGjqGA8Iz5mkeN6zlD7A==", + "dev": true, + "requires": { + "depd": "~1.1.2", + "inherits": "2.0.3", + "setprototypeof": "1.1.0", + "statuses": ">= 1.4.0 < 2" + } + }, + "inherits": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz", + "integrity": "sha512-x00IRNXNy63jwGkJmzPigoySHbaqpNuzKbBOmzK+g2OdZpQ9w+sxCN+VSB3ja7IAge2OP2qpfxTjeNcyjmW1uw==", + "dev": true + }, + "mime": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/mime/-/mime-1.4.1.tgz", + "integrity": "sha512-KI1+qOZu5DcW6wayYHSzR/tXKCDC5Om4s1z2QJjDULzLcmf3DvzS7oluY4HCTrc+9FiKmWUgeNLg7W3uIQvxtQ==", + "dev": true + }, + "ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", + "dev": true + }, + "on-finished": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.3.0.tgz", + "integrity": "sha512-ikqdkGAAyf/X/gPhXGvfgAytDZtDbr+bkNUJ0N9h5MI/dmdgCs3l6hoHrcUv41sRKew3jIwrp4qQDXiK99Utww==", + "dev": true, + "requires": { + "ee-first": "1.1.1" + } + }, + "setprototypeof": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.1.0.tgz", + "integrity": "sha512-BvE/TwpZX4FXExxOxZyRGQQv651MSwmWKZGqvmPcRIjDqWub67kTKuIMx43cZZrS/cBBzwBcNDWoFxt2XEFIpQ==", + "dev": true + }, + "statuses": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/statuses/-/statuses-1.4.0.tgz", + "integrity": "sha512-zhSCtt8v2NDrRlPQpCNtw/heZLtfUDqxBM1udqikb/Hbk52LK4nQSwr10u77iopCW5LsyHpuXS0GnEc48mLeew==", + "dev": true + } + } + }, "serialize-error": { "version": "11.0.3", "resolved": "https://registry.npmjs.org/serialize-error/-/serialize-error-11.0.3.tgz", @@ -34882,6 +36780,74 @@ "randombytes": "^2.1.0" } }, + "serve-index": { + "version": "1.9.1", + "resolved": "https://registry.npmjs.org/serve-index/-/serve-index-1.9.1.tgz", + "integrity": "sha512-pXHfKNP4qujrtteMrSBb0rc8HJ9Ms/GrXwcUtUtD5s4ewDJI8bT3Cz2zTVRMKtri49pLx2e0Ya8ziP5Ya2pZZw==", + "dev": true, + "requires": { + "accepts": "~1.3.4", + "batch": "0.6.1", + "debug": "2.6.9", + "escape-html": "~1.0.3", + "http-errors": "~1.6.2", + "mime-types": "~2.1.17", + "parseurl": "~1.3.2" + }, + "dependencies": { + "debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "dev": true, + "requires": { + "ms": "2.0.0" + } + }, + "depd": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/depd/-/depd-1.1.2.tgz", + "integrity": "sha512-7emPTl6Dpo6JRXOXjLRxck+FlLRX5847cLKEn00PLAgc3g2hTZZgr+e4c2v6QpSmLeFP3n5yUo7ft6avBK/5jQ==", + "dev": true + }, + "http-errors": { + "version": "1.6.3", + "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-1.6.3.tgz", + "integrity": "sha512-lks+lVC8dgGyh97jxvxeYTWQFvh4uw4yC12gVl63Cg30sjPX4wuGcdkICVXDAESr6OJGjqGA8Iz5mkeN6zlD7A==", + "dev": true, + "requires": { + "depd": "~1.1.2", + "inherits": "2.0.3", + "setprototypeof": "1.1.0", + "statuses": ">= 1.4.0 < 2" + } + }, + "inherits": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz", + "integrity": "sha512-x00IRNXNy63jwGkJmzPigoySHbaqpNuzKbBOmzK+g2OdZpQ9w+sxCN+VSB3ja7IAge2OP2qpfxTjeNcyjmW1uw==", + "dev": true + }, + "ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", + "dev": true + }, + "setprototypeof": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.1.0.tgz", + "integrity": "sha512-BvE/TwpZX4FXExxOxZyRGQQv651MSwmWKZGqvmPcRIjDqWub67kTKuIMx43cZZrS/cBBzwBcNDWoFxt2XEFIpQ==", + "dev": true + }, + "statuses": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/statuses/-/statuses-1.5.0.tgz", + "integrity": "sha512-OpZ3zP+jT1PI7I8nemJX4AKmAX070ZkYPVWV/AaKTJl+tXCTGyVdC1a4SL8RUQYEwk/f34ZX8UTykN68FwrqAA==", + "dev": true + } + } + }, "serve-static": { "version": "1.16.2", "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.16.2.tgz", @@ -35121,6 +37087,12 @@ "totalist": "^3.0.0" } }, + "slash": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/slash/-/slash-5.1.0.tgz", + "integrity": "sha512-ZA6oR3T/pEyuqwMgAKT0/hAv8oAXckzbkmR0UkUosQ+Mc4RxGoJkRmwHgHufaenlyAgE1Mxgpdcrf75y6XcnDg==", + "dev": true + }, "slashes": { "version": "3.0.12", "resolved": "https://registry.npmjs.org/slashes/-/slashes-3.0.12.tgz", @@ -35203,6 +37175,49 @@ "integrity": "sha512-qnQ7gVMxGNxsiL4lEuJwe/To8UnK7fAnmbGEEH8RpLouuKbeEm0lhbQVFIrNSuB+G7tVrAlVsZgETT5nljf+Iw==", "dev": true }, + "source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true + }, + "source-map-js": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.1.tgz", + "integrity": "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==", + "dev": true + }, + "source-map-loader": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/source-map-loader/-/source-map-loader-5.0.0.tgz", + "integrity": "sha512-k2Dur7CbSLcAH73sBcIkV5xjPV4SzqO1NJ7+XaQl8if3VODDUj3FNchNGpqgJSKbvUfJuhVdv8K2Eu8/TNl2eA==", + "dev": true, + "requires": { + "iconv-lite": "^0.6.3", + "source-map-js": "^1.0.2" + }, + "dependencies": { + "iconv-lite": { + "version": "0.6.3", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz", + "integrity": "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==", + "dev": true, + "requires": { + "safer-buffer": ">= 2.1.2 < 3.0.0" + } + } + } + }, + "source-map-resolve": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/source-map-resolve/-/source-map-resolve-0.6.0.tgz", + "integrity": "sha512-KXBr9d/fO/bWo97NXsPIAW1bFSBOuCnjbNTBMO7N59hsv5i9yzRDfcYwwt0l04+VqnKC+EwzvJZIP/qkuMgR/w==", + "dev": true, + "requires": { + "atob": "^2.1.2", + "decode-uri-component": "^0.2.0" + } + }, "spacetrim": { "version": "0.11.25", "resolved": "https://registry.npmjs.org/spacetrim/-/spacetrim-0.11.25.tgz", @@ -35340,6 +37355,28 @@ "integrity": "sha512-76ORR0DO1o1hlKwTbi/DM3EXWGf3ZJYO8cXX5RJwnul2DEg2oyoZyjLNoQM8WsvZiFKCRfC1O0J7iCvie3RZmQ==", "dev": true }, + "streamfilter": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/streamfilter/-/streamfilter-3.0.0.tgz", + "integrity": "sha512-kvKNfXCmUyC8lAXSSHCIXBUlo/lhsLcCU/OmzACZYpRUdtKIH68xYhm/+HI15jFJYtNJGYtCgn2wmIiExY1VwA==", + "dev": true, + "requires": { + "readable-stream": "^3.0.6" + }, + "dependencies": { + "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" + } + } + } + }, "streamroller": { "version": "3.1.5", "resolved": "https://registry.npmjs.org/streamroller/-/streamroller-3.1.5.tgz", @@ -35411,6 +37448,12 @@ } } }, + "string-template": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/string-template/-/string-template-0.2.1.tgz", + "integrity": "sha512-Yptehjogou2xm4UJbxJ4CxgZx12HBfeystp0y3x7s4Dj32ltVVG1Gg8YhKjHZkHicuKpZX/ffilA8505VbUbpw==", + "dev": true + }, "string-width": { "version": "4.2.3", "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", @@ -35556,6 +37599,12 @@ "integrity": "sha512-vavAMRXOgBVNF6nyEEmL3DBK19iRpDcoIwW+swQ+CbGiu7lju6t+JklA1MHweoWtadgt4ISVUsXLyDq34ddcwA==", "dev": true }, + "strip-bom-string": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/strip-bom-string/-/strip-bom-string-1.0.0.tgz", + "integrity": "sha512-uCC2VHvQRYu+lMh4My/sFNmF2klFymLX1wHJeXnbEJERpV/ZsVuonzerjfrGpIGF7LBVa1O7i9kjiWvJiFck8g==", + "dev": true + }, "strip-eof": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/strip-eof/-/strip-eof-1.0.0.tgz", @@ -35729,12 +37778,6 @@ "integrity": "sha512-Y9rRfJG5jcKOE0CLisYbojUjIrIEE7AGMzA/Sm4BslANhbS+cDMpgBdcPT91oJ7OuJ9hYJBx59RjbhxVnrF8Xg==", "dev": true }, - "source-map": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", - "dev": true - }, "source-map-support": { "version": "0.5.21", "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.21.tgz", @@ -35861,11 +37904,46 @@ "integrity": "sha512-gLCeArryy2yNTRzTGKbZbloctj64jkZ57hj5zdraXue6aFgd6PmvVtEyiUU+hvU0v7q08oVv8r8ev0tRo6bvgw==", "dev": true }, + "timers-ext": { + "version": "0.1.8", + "resolved": "https://registry.npmjs.org/timers-ext/-/timers-ext-0.1.8.tgz", + "integrity": "sha512-wFH7+SEAcKfJpfLPkrgMPvvwnEtj8W4IurvEyrKsDleXnKLCDw71w8jltvfLa8Rm4qQxxT4jmDBYbJG/z7qoww==", + "dev": true, + "requires": { + "es5-ext": "^0.10.64", + "next-tick": "^1.1.0" + } + }, "tiny-hashes": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/tiny-hashes/-/tiny-hashes-1.0.1.tgz", "integrity": "sha512-knIN5zj4fl7kW4EBU5sLP20DWUvi/rVouvJezV0UAym2DkQaqm365Nyc8F3QEiOvunNDMxR8UhcXd1d5g+Wg1g==" }, + "tiny-lr": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/tiny-lr/-/tiny-lr-1.1.1.tgz", + "integrity": "sha512-44yhA3tsaRoMOjQQ+5v5mVdqef+kH6Qze9jTpqtVufgYjYt08zyZAwNwwVBj3i1rJMnR52IxOW0LK0vBzgAkuA==", + "dev": true, + "requires": { + "body": "^5.1.0", + "debug": "^3.1.0", + "faye-websocket": "~0.10.0", + "livereload-js": "^2.3.0", + "object-assign": "^4.1.0", + "qs": "^6.4.0" + }, + "dependencies": { + "debug": { + "version": "3.2.7", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz", + "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==", + "dev": true, + "requires": { + "ms": "^2.1.1" + } + } + } + }, "tinyglobby": { "version": "0.2.12", "resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.12.tgz", @@ -35903,6 +37981,16 @@ "integrity": "sha512-nZD7m9iCPC5g0pYmcaxogYKggSfLsdxl8of3Q/oIbqCqLLIO9IAF0GWjX1z9NZRHPiXv8Wex4yDCaZsgEw0Y8w==", "dev": true }, + "to-absolute-glob": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/to-absolute-glob/-/to-absolute-glob-3.0.0.tgz", + "integrity": "sha512-loO/XEWTRqpfcpI7+Jr2RR2Umaaozx1t6OSVWtMi0oy5F/Fxg3IC+D/TToDnxyAGs7uZBGT/6XmyDUxgsObJXA==", + "dev": true, + "requires": { + "is-absolute": "^1.0.0", + "is-negated-glob": "^1.0.0" + } + }, "to-regex-range": { "version": "5.0.1", "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", @@ -35990,6 +38078,12 @@ "get-tsconfig": "^4.7.5" } }, + "type": { + "version": "2.7.3", + "resolved": "https://registry.npmjs.org/type/-/type-2.7.3.tgz", + "integrity": "sha512-8j+1QmAbPvLZow5Qpi6NCaN8FB60p/6x8/vfNqOk/hC+HuvFZhL4+WfekuhQLiqFZXOgQdrs3B+XxEmCc6b3FQ==", + "dev": true + }, "type-check": { "version": "0.4.0", "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz", @@ -36077,8 +38171,7 @@ "version": "5.8.2", "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.8.2.tgz", "integrity": "sha512-aJn6wq13/afZp/jT9QZmwEjDqqvSGp1VT5GVg+f/t6/oVyrgXM6BY1h9BRh/O5p3PlUPAe+WuiEZOmb/49RqoQ==", - "dev": true, - "peer": true + "dev": true }, "typescript-compare": { "version": "0.0.2", @@ -36595,6 +38688,21 @@ } } }, + "vinyl-sourcemaps-apply": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/vinyl-sourcemaps-apply/-/vinyl-sourcemaps-apply-0.2.1.tgz", + "integrity": "sha512-+oDh3KYZBoZC8hfocrbrxbLUeaYtQK7J5WU5Br9VqWqmCll3tFJqKp97GC9GmMsVIL0qnx2DgEDVxdo5EZ5sSw==", + "requires": { + "source-map": "^0.5.1" + }, + "dependencies": { + "source-map": { + "version": "0.5.7", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz", + "integrity": "sha512-LbrmJOMUSdEVxIKvdcJzQC+nQhe8FUZQTXQy6+I75skNgn3OoQ0DZA8YnFa7gp8tqtL3KPf1kmo0R5DoApeSGQ==" + } + } + }, "void-elements": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/void-elements/-/void-elements-2.0.1.tgz", @@ -37044,12 +39152,6 @@ "webpack-sources": "^2.2.0" }, "dependencies": { - "source-map": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", - "dev": true - }, "webpack-sources": { "version": "2.3.1", "resolved": "https://registry.npmjs.org/webpack-sources/-/webpack-sources-2.3.1.tgz", @@ -37165,6 +39267,23 @@ } } }, + "websocket-driver": { + "version": "0.7.4", + "resolved": "https://registry.npmjs.org/websocket-driver/-/websocket-driver-0.7.4.tgz", + "integrity": "sha512-b17KeDIQVjvb0ssuSDF2cYXSg2iztliJ4B9WdsuB6J952qCPKmnVq4DyW5motImXHDC1cBT/1UezrJVsKw5zjg==", + "dev": true, + "requires": { + "http-parser-js": ">=0.5.1", + "safe-buffer": ">=5.1.0", + "websocket-extensions": ">=0.1.1" + } + }, + "websocket-extensions": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/websocket-extensions/-/websocket-extensions-0.1.4.tgz", + "integrity": "sha512-OqedPIGOfsDlo31UNwYbCFMSaO9m9G/0faIHj5/dZFDMFqPTcx6UwqyOy3COEaEOg/9VsGIpdqn62W5KhoKSpg==", + "dev": true + }, "whatwg-encoding": { "version": "3.1.1", "resolved": "https://registry.npmjs.org/whatwg-encoding/-/whatwg-encoding-3.1.1.tgz", @@ -37411,8 +39530,7 @@ "xtend": { "version": "4.0.2", "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.2.tgz", - "integrity": "sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==", - "dev": true + "integrity": "sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==" }, "y18n": { "version": "5.0.8", From 3dd609fe286427b259427bb01ac7004ee826c6ed Mon Sep 17 00:00:00 2001 From: Patrick McCann Date: Sat, 14 Jun 2025 11:55:18 -0400 Subject: [PATCH 76/92] Prebid 10: delete freewheelssp adapter (#13378) * adapter: rename freewheelssp adapter files * Delete modules/freewheelsspBidAdapter.js * Delete modules/freewheelsspBidAdapter.md * Delete test/spec/modules/freewheelsspBidAdapter_spec.js --- modules/freewheel-sspBidAdapter.js | 606 -------------- modules/freewheel-sspBidAdapter.md | 33 - .../modules/freewheel-sspBidAdapter_spec.js | 738 ------------------ 3 files changed, 1377 deletions(-) delete mode 100644 modules/freewheel-sspBidAdapter.js delete mode 100644 modules/freewheel-sspBidAdapter.md delete mode 100644 test/spec/modules/freewheel-sspBidAdapter_spec.js diff --git a/modules/freewheel-sspBidAdapter.js b/modules/freewheel-sspBidAdapter.js deleted file mode 100644 index d30be635a50..00000000000 --- a/modules/freewheel-sspBidAdapter.js +++ /dev/null @@ -1,606 +0,0 @@ -import { logWarn, isArray, isFn, deepAccess, formatQS } from '../src/utils.js'; -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; - -const PROTOCOL = getProtocol(); -const FREEWHEEL_ADSSETUP = PROTOCOL + '://ads.stickyadstv.com/www/delivery/swfIndex.php'; -const MUSTANG_URL = PROTOCOL + '://cdn.stickyadstv.com/mustang/mustang.min.js'; -const PRIMETIME_URL = PROTOCOL + '://cdn.stickyadstv.com/prime-time/'; -const USER_SYNC_URL = PROTOCOL + '://ads.stickyadstv.com/auto-user-sync'; - -function getProtocol() { - return 'https'; -} - -function isValidUrl(str) { - if (!str) { - return false; - } - - // regExp for url validation - var pattern = /^(https?|ftp|file):\/\/([a-zA-Z0-9.-]+(:[a-zA-Z0-9.&%$-]+)*@)*((25[0-5]|2[0-4][0-9]|1[0-9]{2}|[1-9][0-9]?)(\.(25[0-5]|2[0-4][0-9]|1[0-9]{2}|[1-9]?[0-9])){3}|([a-zA-Z0-9-]+\.)*[a-zA-Z0-9-]+\.(com|edu|gov|int|mil|net|org|biz|arpa|info|name|pro|aero|coop|museum|[a-zA-Z]{2}))(:[0-9]+)*(\/($|[a-zA-Z0-9.,?'\\+&%$#=~_-]+))*$/; - return pattern.test(str); -} - -function getBiggerSize(array) { - var result = [0, 0]; - for (var i = 0; i < array.length; i++) { - if (array[i][0] * array[i][1] > result[0] * result[1]) { - result = array[i]; - } - } - return result; -} - -function getBiggerSizeWithLimit(array, minSizeLimit, maxSizeLimit) { - var minSize = minSizeLimit || [0, 0]; - var maxSize = maxSizeLimit || [Number.MAX_VALUE, Number.MAX_VALUE]; - var candidates = []; - - for (var i = 0; i < array.length; i++) { - if (array[i][0] * array[i][1] >= minSize[0] * minSize[1] && array[i][0] * array[i][1] <= maxSize[0] * maxSize[1]) { - candidates.push(array[i]); - } - } - - return getBiggerSize(candidates); -} - -/* -* read the pricing extension with this format: 1.0000 -* @return {object} pricing data in format: {currency: "EUR", price:"1.000"} -*/ -function getPricing(xmlNode) { - var pricingExtNode; - var princingData = {}; - - var extensions = xmlNode.querySelectorAll('Extension'); - // Nodelist.forEach is not supported in IE and Edge - // Workaround given here https://developer.microsoft.com/en-us/microsoft-edge/platform/issues/10638731/ - Array.prototype.forEach.call(extensions, function(node) { - if (node.getAttribute('type') === 'StickyPricing') { - pricingExtNode = node; - } - }); - - if (pricingExtNode) { - var priceNode = pricingExtNode.querySelector('Price'); - princingData = { - currency: priceNode.getAttribute('currency'), - price: priceNode.textContent - }; - } else { - logWarn('PREBID - ' + BIDDER_CODE + ': No bid received or missing pricing extension.'); - } - - return princingData; -} - -/* -* Read the StickyBrand extension with this format: -* -* -* -* -* -* -* @return {object} pricing data in format: {currency: "EUR", price:"1.000"} -*/ -function getAdvertiserDomain(xmlNode) { - var domain = []; - var brandExtNode; - var extensions = xmlNode.querySelectorAll('Extension'); - // Nodelist.forEach is not supported in IE and Edge - // Workaround given here https://developer.microsoft.com/en-us/microsoft-edge/platform/issues/10638731/ - Array.prototype.forEach.call(extensions, function(node) { - if (node.getAttribute('type') === 'StickyBrand') { - brandExtNode = node; - } - }); - - // Currently we only return one Domain - if (brandExtNode) { - var domainNode = brandExtNode.querySelector('Domain'); - domain.push(domainNode.textContent); - } else { - logWarn('PREBID - ' + BIDDER_CODE + ': No bid received or missing StickyBrand extension.'); - } - - return domain; -} - -function hashcode(inputString) { - var hash = 0; - var char; - if (inputString.length == 0) return hash; - for (var i = 0; i < inputString.length; i++) { - char = inputString.charCodeAt(i); - hash = ((hash << 5) - hash) + char; - hash = hash & hash; // Convert to 32bit integer - } - return hash; -} - -function getCreativeId(xmlNode) { - var creaId = ''; - var adNodes = xmlNode.querySelectorAll('Ad'); - // Nodelist.forEach is not supported in IE and Edge - // Workaround given here https://developer.microsoft.com/en-us/microsoft-edge/platform/issues/10638731/ - Array.prototype.forEach.call(adNodes, function(el) { - creaId += '[' + el.getAttribute('id') + ']'; - }); - - return creaId; -} - -function getValueFromKeyInImpressionNode(xmlNode, key) { - var value = ''; - var impNodes = xmlNode.querySelectorAll('Impression'); // Nodelist.forEach is not supported in IE and Edge - var isRootViewKeyPresent = false; - var isAdsDisplayStartedPresent = false; - Array.prototype.forEach.call(impNodes, function (el) { - if (isRootViewKeyPresent && isAdsDisplayStartedPresent) { - return value; - } - isRootViewKeyPresent = false; - isAdsDisplayStartedPresent = false; - var text = el.textContent; - var queries = text.substring(el.textContent.indexOf('?') + 1).split('&'); - var tempValue = ''; - Array.prototype.forEach.call(queries, function (item) { - var split = item.split('='); - if (split[0] == key) { - tempValue = split[1]; - } - if (split[0] == 'reqType' && split[1] == 'AdsDisplayStarted') { - isAdsDisplayStartedPresent = true; - } - if (split[0] == 'rootViewKey') { - isRootViewKeyPresent = true; - } - }); - if (isAdsDisplayStartedPresent) { - value = tempValue; - } - }); - return value; -} - -function getDealId(xmlNode) { - return getValueFromKeyInImpressionNode(xmlNode, 'dealId'); -} - -function getBannerId(xmlNode) { - return getValueFromKeyInImpressionNode(xmlNode, 'adId'); -} - -function getCampaignId(xmlNode) { - return getValueFromKeyInImpressionNode(xmlNode, 'campaignId'); -} - -/** - * returns the top most accessible window - */ -function getTopMostWindow() { - var res = window; - - try { - while (top !== res) { - if (res.parent.location.href.length) { res = res.parent; } - } - } catch (e) {} - - return res; -} - -function getComponentId(inputFormat) { - var component = 'mustang'; // default component id - - if (inputFormat && inputFormat !== 'inbanner') { - // format identifiers are equals to their component ids. - component = inputFormat; - } - - return component; -} - -function getAPIName(componentId) { - componentId = componentId || ''; - - // remove dash in componentId to get API name - return componentId.replace('-', ''); -} - -function getBidFloor(bid, config) { - if (!isFn(bid.getFloor)) { - return deepAccess(bid, 'params.bidfloor', 0); - } - - try { - const bidFloor = bid.getFloor({ - currency: getFloorCurrency(config), - mediaType: typeof bid.mediaTypes['banner'] == 'object' ? 'banner' : 'video', - size: '*', - }); - return bidFloor?.floor; - } catch (e) { - return -1; - } -} - -function getFloorCurrency(config) { - return config.getConfig('floors.data.currency') != null ? config.getConfig('floors.data.currency') : 'USD'; -} - -function formatAdHTML(bid, size) { - var integrationType = bid.params.format; - - var divHtml = '
'; - - var script = ''; - var libUrl = ''; - if (integrationType && integrationType !== 'inbanner') { - libUrl = PRIMETIME_URL + getComponentId(bid.params.format) + '.min.js'; - script = getOutstreamScript(bid); - } else { - libUrl = MUSTANG_URL; - script = getInBannerScript(bid, size); - } - - return divHtml + - ''; -} - -var getInBannerScript = function(bid, size) { - return 'var config = {' + - ' preloadedVast:vast,' + - ' autoPlay:true' + - ' };' + - ' var ad = new window.com.stickyadstv.vpaid.Ad(document.getElementById("freewheelssp_prebid_target"),config);' + - ' (new window.com.stickyadstv.tools.ASLoader(' + bid.params.zoneId + ', \'' + getComponentId(bid.params.format) + '\')).registerEvents(ad);' + - ' ad.initAd(' + size[0] + ',' + size[1] + ',"",0,"","");'; -}; - -var getOutstreamScript = function(bid) { - var config = bid.params; - - // default placement if no placement is set - if (!config.hasOwnProperty('domId') && !config.hasOwnProperty('auto') && !config.hasOwnProperty('p') && !config.hasOwnProperty('article')) { - if (config.format === 'intext-roll') { - config.iframeMode = 'dfp'; - } else { - config.domId = 'freewheelssp_prebid_target'; - } - } - - var script = 'var config = {' + - ' preloadedVast:vast,' + - ' ASLoader:new window.com.stickyadstv.tools.ASLoader(' + bid.params.zoneId + ', \'' + getComponentId(bid.params.format) + '\')'; - - for (var key in config) { - // dont' send format parameter - // neither zone nor vastUrlParams value as Vast is already loaded - if (config.hasOwnProperty(key) && key !== 'format' && key !== 'zone' && key !== 'zoneId' && key !== 'vastUrlParams') { - script += ',' + key + ':"' + config[key] + '"'; - } - } - script += '};' + - - 'window.com.stickyadstv.' + getAPIName(bid.params.format) + '.start(config);'; - - return script; -}; - -export const spec = { - code: BIDDER_CODE, - gvlid: GVL_ID, - 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. - */ - 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. - */ - buildRequests: function(bidRequests, bidderRequest) { - // var currency = config.getConfig(currency); - - let buildRequest = (currentBidRequest, bidderRequest) => { - var zone = currentBidRequest.params.zoneId; - var timeInMillis = new Date().getTime(); - var keyCode = hashcode(zone + '' + timeInMillis); - var bidfloor = getBidFloor(currentBidRequest, config); - var format = currentBidRequest.params.format; - - var requestParams = { - reqType: 'AdsSetup', - protocolVersion: '4.2', - zoneId: zone, - componentId: 'prebid', - componentSubId: getComponentId(currentBidRequest.params.format), - timestamp: timeInMillis, - _fw_bidfloor: (bidfloor > 0) ? bidfloor : 0, - _fw_bidfloorcur: (bidfloor > 0) ? getFloorCurrency(config) : '', - pbjs_version: '$prebid.version$', - pKey: keyCode - }; - - // Add GDPR flag and consent string - if (bidderRequest && bidderRequest.gdprConsent) { - requestParams._fw_gdpr_consent = bidderRequest.gdprConsent.consentString; - - if (typeof bidderRequest.gdprConsent.gdprApplies === 'boolean') { - requestParams._fw_gdpr = bidderRequest.gdprConsent.gdprApplies; - } - } - - if (currentBidRequest.params.gdpr_consented_providers) { - requestParams._fw_gdpr_consented_providers = currentBidRequest.params.gdpr_consented_providers; - } - - // Add CCPA consent string - if (bidderRequest && bidderRequest.uspConsent) { - requestParams._fw_us_privacy = bidderRequest.uspConsent; - } - - // Add GPP consent - if (bidderRequest && bidderRequest.gppConsent) { - requestParams.gpp = bidderRequest.gppConsent.gppString; - requestParams.gpp_sid = bidderRequest.gppConsent.applicableSections; - } else if (bidderRequest && bidderRequest.ortb2 && bidderRequest.ortb2.regs && bidderRequest.ortb2.regs.gpp) { - requestParams.gpp = bidderRequest.ortb2.regs.gpp; - requestParams.gpp_sid = bidderRequest.ortb2.regs.gpp_sid; - } - - // Add content object - if (bidderRequest && bidderRequest.ortb2 && bidderRequest.ortb2.site && bidderRequest.ortb2.site.content && typeof bidderRequest.ortb2.site.content === 'object') { - try { - requestParams._fw_prebid_content = JSON.stringify(bidderRequest.ortb2.site.content); - } catch (error) { - logWarn('PREBID - ' + BIDDER_CODE + ': Unable to stringify the content object: ' + error); - } - } - - // Add schain object - var schain = currentBidRequest?.ortb2?.source?.ext?.schain; - if (schain) { - try { - requestParams.schain = JSON.stringify(schain); - } catch (error) { - logWarn('PREBID - ' + BIDDER_CODE + ': Unable to stringify the schain: ' + error); - } - } - - if (currentBidRequest.userIdAsEids && currentBidRequest.userIdAsEids.length > 0) { - try { - requestParams._fw_prebid_3p_UID = JSON.stringify(currentBidRequest.userIdAsEids); - } catch (error) { - logWarn('PREBID - ' + BIDDER_CODE + ': Unable to stringify the userIdAsEids: ' + error); - } - } - - var vastParams = currentBidRequest.params.vastUrlParams; - if (typeof vastParams === 'object') { - for (var key in vastParams) { - if (vastParams.hasOwnProperty(key)) { - requestParams[key] = vastParams[key]; - } - } - } - - var location = bidderRequest?.refererInfo?.page; - if (isValidUrl(location)) { - requestParams.loc = location; - } - - var playerSize = []; - if (currentBidRequest.mediaTypes.video && currentBidRequest.mediaTypes.video.playerSize) { - // If mediaTypes is video, get size from mediaTypes.video.playerSize per http://prebid.org/blog/pbjs-3 - if (isArray(currentBidRequest.mediaTypes.video.playerSize[0])) { - playerSize = currentBidRequest.mediaTypes.video.playerSize[0]; - } else { - playerSize = currentBidRequest.mediaTypes.video.playerSize; - } - } else if (currentBidRequest.mediaTypes.banner.sizes) { - // If mediaTypes is banner, get size from mediaTypes.banner.sizes per http://prebid.org/blog/pbjs-3 - playerSize = getBiggerSizeWithLimit(currentBidRequest.mediaTypes.banner.sizes, currentBidRequest.mediaTypes.banner.minSizeLimit, currentBidRequest.mediaTypes.banner.maxSizeLimit); - } else { - // Backward compatible code, in case size still pass by sizes in bid request - playerSize = getBiggerSize(currentBidRequest.sizes); - } - - if (playerSize[0] > 0 || playerSize[1] > 0) { - requestParams.playerSize = playerSize[0] + 'x' + playerSize[1]; - } - - // Add video context and placement in requestParams - if (currentBidRequest.mediaTypes.video) { - var videoContext = currentBidRequest.mediaTypes.video.context ? currentBidRequest.mediaTypes.video.context : ''; - var videoPlacement = currentBidRequest.mediaTypes.video.placement ? currentBidRequest.mediaTypes.video.placement : null; - var videoPlcmt = currentBidRequest.mediaTypes.video.plcmt ? currentBidRequest.mediaTypes.video.plcmt : null; - - if (format == 'inbanner') { - videoPlacement = 2; - videoContext = 'In-Banner'; - } - requestParams.video_context = videoContext; - requestParams.video_placement = videoPlacement; - requestParams.video_plcmt = videoPlcmt; - } - - return { - method: 'GET', - url: FREEWHEEL_ADSSETUP, - data: requestParams, - bidRequest: currentBidRequest - }; - }; - - return bidRequests.map(function(currentBidRequest) { - return buildRequest(currentBidRequest, bidderRequest); - }); - }, - - /** - * 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 = []; - if (bidrequest.mediaTypes.video && bidrequest.mediaTypes.video.playerSize) { - // If mediaTypes is video, get size from mediaTypes.video.playerSize per http://prebid.org/blog/pbjs-3 - if (isArray(bidrequest.mediaTypes.video.playerSize[0])) { - playerSize = bidrequest.mediaTypes.video.playerSize[0]; - } else { - playerSize = bidrequest.mediaTypes.video.playerSize; - } - } else if (bidrequest.mediaTypes.banner.sizes) { - // If mediaTypes is banner, get size from mediaTypes.banner.sizes per http://prebid.org/blog/pbjs-3 - playerSize = getBiggerSizeWithLimit(bidrequest.mediaTypes.banner.sizes, bidrequest.mediaTypes.banner.minSizeLimit, bidrequest.mediaTypes.banner.maxSizeLimit); - } else { - // Backward compatible code, in case size still pass by sizes in bid request - playerSize = getBiggerSize(bidrequest.sizes); - } - - if (typeof serverResponse == 'object' && typeof serverResponse.body == 'string') { - serverResponse = serverResponse.body; - } - - var xmlDoc; - try { - var parser = new DOMParser(); - xmlDoc = parser.parseFromString(serverResponse, 'application/xml'); - } catch (err) { - logWarn('Prebid.js - ' + BIDDER_CODE + ' : ' + err); - return; - } - - const princingData = getPricing(xmlDoc); - const creativeId = getCreativeId(xmlDoc); - const dealId = getDealId(xmlDoc); - const campaignId = getCampaignId(xmlDoc); - const bannerId = getBannerId(xmlDoc); - const topWin = getTopMostWindow(); - const advertiserDomains = getAdvertiserDomain(xmlDoc); - - if (!topWin.freewheelssp_cache) { - topWin.freewheelssp_cache = {}; - } - topWin.freewheelssp_cache[bidrequest.adUnitCode] = serverResponse; - - const bidResponses = []; - - if (princingData.price) { - const bidResponse = { - requestId: bidrequest.bidId, - cpm: princingData.price, - width: playerSize[0], - height: playerSize[1], - creativeId: creativeId, - currency: princingData.currency, - netRevenue: true, - ttl: 360, - meta: { advertiserDomains: advertiserDomains }, - dealId: dealId, - campaignId: campaignId, - bannerId: bannerId - }; - - if (bidrequest.mediaTypes.video) { - bidResponse.mediaType = 'video'; - } - - bidResponse.vastXml = serverResponse; - - bidResponse.ad = formatAdHTML(bidrequest, playerSize); - bidResponses.push(bidResponse); - } - - return bidResponses; - }, - - getUserSyncs: function(syncOptions, responses, gdprConsent, usPrivacy, gppConsent) { - const params = {}; - - if (gdprConsent) { - if (typeof gdprConsent.gdprApplies === 'boolean') { - params.gdpr = Number(gdprConsent.gdprApplies); - params.gdpr_consent = gdprConsent.consentString; - } else { - params.gdpr_consent = gdprConsent.consentString; - } - } - - if (gppConsent) { - if (typeof gppConsent.gppString === 'string') { - params.gpp = gppConsent.gppString; - } - if (gppConsent.applicableSections) { - params.gpp_sid = gppConsent.applicableSections; - } - } - - var queryString = ''; - if (params) { - queryString = '?' + `${formatQS(params)}`; - } - - const syncs = []; - if (syncOptions && syncOptions.pixelEnabled) { - syncs.push({ - type: 'image', - url: USER_SYNC_URL + queryString - }); - } else if (syncOptions.iframeEnabled) { - syncs.push({ - type: 'iframe', - url: USER_SYNC_URL + queryString - }); - } - - return syncs; - }, -}; - -registerBidder(spec); diff --git a/modules/freewheel-sspBidAdapter.md b/modules/freewheel-sspBidAdapter.md deleted file mode 100644 index a445280f2b0..00000000000 --- a/modules/freewheel-sspBidAdapter.md +++ /dev/null @@ -1,33 +0,0 @@ -# Overview - -Module Name: Freewheel SSP Bidder Adapter -Module Type: Bidder Adapter -Maintainer: clientsidesdk@freewheel.tv - -# Description - -Module that connects to Freewheel ssp's demand sources - -# Test Parameters -``` - var adUnits = [ - { - code: 'test-div', - - mediaTypes: { - banner: { - sizes: [[300, 250]], // a display size - } - }, - - bids: [ - { - bidder: "freewheelssp", // or use alias "freewheel-ssp" - params: { - zoneId : '277225' - } - } - ] - } - ]; -``` diff --git a/test/spec/modules/freewheel-sspBidAdapter_spec.js b/test/spec/modules/freewheel-sspBidAdapter_spec.js deleted file mode 100644 index e96208c3b16..00000000000 --- a/test/spec/modules/freewheel-sspBidAdapter_spec.js +++ /dev/null @@ -1,738 +0,0 @@ -import { expect } from 'chai'; -import { spec } from 'modules/freewheel-sspBidAdapter.js'; -import { newBidder } from 'src/adapters/bidderFactory.js'; -import { createEidsArray } from 'modules/userId/eids.js'; -import { config } from 'src/config.js'; - -const ENDPOINT = '//ads.stickyadstv.com/www/delivery/swfIndex.php'; -const PREBID_VERSION = '$prebid.version$'; - -describe('freewheelSSP BidAdapter Test', () => { - const adapter = newBidder(spec); - - describe('inherited functions', () => { - it('exists and is a function', () => { - expect(adapter.callBids).to.exist.and.to.be.a('function'); - }); - }); - - describe('isBidRequestValidForBanner', () => { - let bid = { - 'bidder': 'freewheel-ssp', - 'params': { - 'zoneId': '277225' - }, - 'adUnitCode': 'adunit-code', - 'mediaTypes': { - 'banner': { - 'sizes': [ - [300, 250], [300, 600] - ] - } - }, - 'sizes': [[300, 250], [300, 600]], - 'bidId': '30b31c1838de1e', - 'bidderRequestId': '22edbae2733bf6', - 'auctionId': '1d1a030790a475', - }; - - it('should return true when required params found', () => { - expect(spec.isBidRequestValid(bid)).to.equal(true); - }); - - it('should return false when required params are not passed', () => { - let invalidBid = Object.assign({}, bid); - delete invalidBid.params; - invalidBid.params = { - wrong: 'missing zone id' - }; - expect(spec.isBidRequestValid(invalidBid)).to.equal(false); - }); - }); - - describe('isBidRequestValidForVideo', () => { - let bid = { - 'bidder': 'freewheel-ssp', - 'params': { - 'zoneId': '277225' - }, - 'adUnitCode': 'adunit-code', - 'mediaTypes': { - 'video': { - 'playerSize': [300, 250], - } - }, - 'sizes': [[300, 250]], - 'bidId': '30b31c1838de1e', - 'bidderRequestId': '22edbae2733bf6', - 'auctionId': '1d1a030790a475', - }; - - it('should return true when required params found', () => { - expect(spec.isBidRequestValid(bid)).to.equal(true); - }); - - it('should return false when required params are not passed', () => { - let invalidBid = Object.assign({}, bid); - delete invalidBid.params; - invalidBid.params = { - wrong: 'missing zone id' - }; - expect(spec.isBidRequestValid(invalidBid)).to.equal(false); - }); - }); - - describe('buildRequestsForBanner', () => { - let bidRequests = [ - { - 'bidder': 'freewheel-ssp', - 'params': { - 'zoneId': '277225', - 'bidfloor': 2.00, - }, - 'adUnitCode': 'adunit-code', - 'mediaTypes': { - 'banner': { - 'sizes': [ - [300, 250], [300, 600] - ] - } - }, - 'sizes': [[300, 250], [300, 600]], - 'bidId': '30b31c1838de1e', - 'bidderRequestId': '22edbae2733bf6', - 'auctionId': '1d1a030790a475', - 'ortb2': { - 'source': { - 'ext': { - 'schain': { - 'ver': '1.0', - 'complete': 1, - 'nodes': [ - { - 'asi': 'example.com', - 'sid': '0', - 'hp': 1, - 'rid': 'bidrequestid', - 'domain': 'example.com' - } - ] - } - } - } - } - } - ]; - - it('should get bidfloor value from params if no getFloor method', () => { - const request = spec.buildRequests(bidRequests); - const payload = request[0].data; - expect(payload._fw_bidfloor).to.equal(2.00); - expect(payload._fw_bidfloorcur).to.deep.equal('USD'); - }); - - it('should get bidfloor value from getFloor method if available', () => { - const bidRequest = bidRequests[0]; - bidRequest.getFloor = () => ({ currency: 'USD', floor: 1.16 }); - const request = spec.buildRequests(bidRequests); - const payload = request[0].data; - expect(payload._fw_bidfloor).to.equal(1.16); - expect(payload._fw_bidfloorcur).to.deep.equal('USD'); - }); - - it('should pass 3rd party IDs with the request when present', function () { - const bidRequest = bidRequests[0]; - bidRequest.userIdAsEids = [ - {source: 'adserver.org', uids: [{id: 'TTD_ID_FROM_USER_ID_MODULE', atype: 1, ext: {rtiPartner: 'TDID'}}]}, - {source: 'admixer.net', uids: [{id: 'admixerId_FROM_USER_ID_MODULE', atype: 3}]}, - {source: 'adtelligent.com', uids: [{id: 'adtelligentId_FROM_USER_ID_MODULE', atype: 3}]}, - ]; - const request = spec.buildRequests(bidRequests); - const payload = request[0].data; - expect(payload._fw_prebid_3p_UID).to.deep.equal(JSON.stringify([ - {source: 'adserver.org', uids: [{id: 'TTD_ID_FROM_USER_ID_MODULE', atype: 1, ext: {rtiPartner: 'TDID'}}]}, - {source: 'admixer.net', uids: [{id: 'admixerId_FROM_USER_ID_MODULE', atype: 3}]}, - {source: 'adtelligent.com', uids: [{id: 'adtelligentId_FROM_USER_ID_MODULE', atype: 3}]}, - ])); - }); - - it('should return empty bidFloorCurrency when bidfloor <= 0', () => { - const bidRequest = bidRequests[0]; - bidRequest.getFloor = () => ({ currency: 'USD', floor: -1 }); - const request = spec.buildRequests(bidRequests); - const payload = request[0].data; - expect(payload._fw_bidfloor).to.equal(0); - expect(payload._fw_bidfloorcur).to.deep.equal(''); - }); - - it('should add parameters to the tag', () => { - const request = spec.buildRequests(bidRequests); - const payload = request[0].data; - expect(payload.reqType).to.equal('AdsSetup'); - expect(payload.protocolVersion).to.equal('4.2'); - expect(payload.zoneId).to.equal('277225'); - expect(payload.componentId).to.equal('prebid'); - expect(payload.componentSubId).to.equal('mustang'); - expect(payload.playerSize).to.equal('300x600'); - expect(payload.pbjs_version).to.equal(PREBID_VERSION); - }); - - it('should return a properly formatted request with schain defined', function () { - const request = spec.buildRequests(bidRequests); - const payload = request[0].data; - expect(payload.schain).to.deep.equal('{\"ver\":\"1.0\",\"complete\":1,\"nodes\":[{\"asi\":\"example.com\",\"sid\":\"0\",\"hp\":1,\"rid\":\"bidrequestid\",\"domain\":\"example.com\"}]}'); - }); - - it('sends bid request to ENDPOINT via GET', () => { - const request = spec.buildRequests(bidRequests); - expect(request[0].url).to.contain(ENDPOINT); - expect(request[0].method).to.equal('GET'); - }); - - it('should add usp consent to the request', () => { - let uspConsentString = '1FW-SSP-uspConsent-'; - let bidderRequest = {}; - bidderRequest.uspConsent = uspConsentString; - const request = spec.buildRequests(bidRequests, bidderRequest); - const payload = request[0].data; - expect(payload.reqType).to.equal('AdsSetup'); - expect(payload.protocolVersion).to.equal('4.2'); - expect(payload.zoneId).to.equal('277225'); - expect(payload.componentId).to.equal('prebid'); - expect(payload.componentSubId).to.equal('mustang'); - expect(payload.playerSize).to.equal('300x600'); - expect(payload._fw_us_privacy).to.exist.and.to.be.a('string'); - expect(payload._fw_us_privacy).to.equal(uspConsentString); - }); - - it('should add gdpr consent to the request', () => { - let gdprConsentString = '1FW-SSP-gdprConsent-'; - let bidderRequest = { - 'gdprConsent': { - 'consentString': gdprConsentString - }, - 'ortb2': { - 'site': { - 'content': { - 'test': 'news', - 'test2': 'param' - } - } - } - }; - - const request = spec.buildRequests(bidRequests, bidderRequest); - const payload = request[0].data; - expect(payload.reqType).to.equal('AdsSetup'); - expect(payload.protocolVersion).to.equal('4.2'); - expect(payload.zoneId).to.equal('277225'); - expect(payload.componentId).to.equal('prebid'); - expect(payload.componentSubId).to.equal('mustang'); - expect(payload.playerSize).to.equal('300x600'); - expect(payload._fw_gdpr_consent).to.exist.and.to.be.a('string'); - expect(payload._fw_gdpr_consent).to.equal(gdprConsentString); - expect(payload._fw_prebid_content).to.deep.equal('{\"test\":\"news\",\"test2\":\"param\"}'); - - let gdprConsent = { - 'gdprApplies': true, - 'consentString': gdprConsentString - } - let syncOptions = { - 'pixelEnabled': true - } - const userSyncs = spec.getUserSyncs(syncOptions, null, gdprConsent, null, null); - expect(userSyncs).to.deep.equal([{ - type: 'image', - url: 'https://ads.stickyadstv.com/auto-user-sync?gdpr=1&gdpr_consent=1FW-SSP-gdprConsent-' - }]); - }); - - it('should add gpp information to the request via bidderRequest.gppConsent', function () { - let consentString = 'abc1234'; - let bidderRequest = { - 'gppConsent': { - 'gppString': consentString, - 'applicableSections': [8] - } - }; - - const request = spec.buildRequests(bidRequests, bidderRequest); - const payload = request[0].data; - - expect(payload.gpp).to.equal(consentString); - expect(payload.gpp_sid).to.deep.equal([8]); - - let gppConsent = { - 'applicableSections': [8], - 'gppString': consentString - } - let syncOptions = { - 'pixelEnabled': true - } - const userSyncs = spec.getUserSyncs(syncOptions, null, null, null, gppConsent); - expect(userSyncs).to.deep.equal([{ - type: 'image', - url: 'https://ads.stickyadstv.com/auto-user-sync?gpp=abc1234&gpp_sid[]=8' - }]); - }); - }) - - describe('buildRequestsForVideo', () => { - let bidRequests = [ - { - 'bidder': 'freewheel-ssp', - 'params': { - 'zoneId': '277225' - }, - 'adUnitCode': 'adunit-code', - 'mediaTypes': { - 'video': { - 'playerSize': [300, 600], - } - }, - 'sizes': [[300, 250], [300, 600]], - 'bidId': '30b31c1838de1e', - 'bidderRequestId': '22edbae2733bf6', - 'auctionId': '1d1a030790a475', - } - ]; - - it('should return context and placement with default values', () => { - const request = spec.buildRequests(bidRequests); - const payload = request[0].data; - expect(payload.video_context).to.equal(''); ; - expect(payload.video_placement).to.equal(null); - expect(payload.video_plcmt).to.equal(null); - }); - - it('should add parameters to the tag', () => { - const request = spec.buildRequests(bidRequests); - const payload = request[0].data; - expect(payload.reqType).to.equal('AdsSetup'); - expect(payload.protocolVersion).to.equal('4.2'); - expect(payload.zoneId).to.equal('277225'); - expect(payload.componentId).to.equal('prebid'); - expect(payload.componentSubId).to.equal('mustang'); - expect(payload.playerSize).to.equal('300x600'); - }); - - it('sends bid request to ENDPOINT via GET', () => { - const request = spec.buildRequests(bidRequests); - expect(request[0].url).to.contain(ENDPOINT); - expect(request[0].method).to.equal('GET'); - }); - - it('should add usp consent to the request', () => { - let uspConsentString = '1FW-SSP-uspConsent-'; - let bidderRequest = {}; - bidderRequest.uspConsent = uspConsentString; - const request = spec.buildRequests(bidRequests, bidderRequest); - const payload = request[0].data; - expect(payload.reqType).to.equal('AdsSetup'); - expect(payload.protocolVersion).to.equal('4.2'); - expect(payload.zoneId).to.equal('277225'); - expect(payload.componentId).to.equal('prebid'); - expect(payload.componentSubId).to.equal('mustang'); - expect(payload.playerSize).to.equal('300x600'); - expect(payload._fw_us_privacy).to.exist.and.to.be.a('string'); - expect(payload._fw_us_privacy).to.equal(uspConsentString); - }); - - it('should add gdpr consent to the request', () => { - let gdprConsentString = '1FW-SSP-gdprConsent-'; - let bidderRequest = { - 'gdprConsent': { - 'consentString': gdprConsentString - } - }; - - const request = spec.buildRequests(bidRequests, bidderRequest); - const payload = request[0].data; - expect(payload.reqType).to.equal('AdsSetup'); - expect(payload.protocolVersion).to.equal('4.2'); - expect(payload.zoneId).to.equal('277225'); - expect(payload.componentId).to.equal('prebid'); - expect(payload.componentSubId).to.equal('mustang'); - expect(payload.playerSize).to.equal('300x600'); - expect(payload._fw_gdpr_consent).to.exist.and.to.be.a('string'); - expect(payload._fw_gdpr_consent).to.equal(gdprConsentString); - - let gdprConsent = { - 'gdprApplies': true, - 'consentString': gdprConsentString - } - let syncOptions = { - 'pixelEnabled': true - } - const userSyncs = spec.getUserSyncs(syncOptions, null, gdprConsent, null, null); - expect(userSyncs).to.deep.equal([{ - type: 'image', - url: 'https://ads.stickyadstv.com/auto-user-sync?gdpr=1&gdpr_consent=1FW-SSP-gdprConsent-' - }]); - }); - }) - - describe('buildRequestsForVideoWithContextAndPlacement', () => { - let bidRequests = [ - { - 'bidder': 'freewheel-ssp', - 'params': { - 'zoneId': '277225' - }, - 'adUnitCode': 'adunit-code', - 'mediaTypes': { - 'video': { - 'context': 'outstream', - 'placement': 2, - 'plcmt': 3, - 'playerSize': [300, 600], - } - }, - 'sizes': [[300, 250], [300, 600]], - 'bidId': '30b31c1838de1e', - 'bidderRequestId': '22edbae2733bf6', - 'auctionId': '1d1a030790a475', - } - ]; - - it('should return input context and placement', () => { - const request = spec.buildRequests(bidRequests); - const payload = request[0].data; - expect(payload.video_context).to.equal('outstream'); ; - expect(payload.video_placement).to.equal(2); - expect(payload.video_plcmt).to.equal(3); - }); - }) - - describe('interpretResponseForBanner', () => { - let bidRequests = [ - { - 'bidder': 'freewheel-ssp', - 'params': { - 'zoneId': '277225' - }, - 'adUnitCode': 'adunit-code', - 'mediaTypes': { - 'banner': { - 'sizes': [ - [300, 250], [300, 600] - ] - } - }, - 'sizes': [[300, 250], [300, 600]], - 'bidId': '30b31c1838de1e', - 'bidderRequestId': '22edbae2733bf6', - 'auctionId': '1d1a030790a475', - } - ]; - - let formattedBidRequests = [ - { - 'bidder': 'freewheel-ssp', - 'params': { - 'zoneId': '277225', - 'format': 'floorad' - }, - 'adUnitCode': 'adunit-code', - 'mediaTypes': { - 'banner': { - 'sizes': [ - [300, 250], [300, 600] - ] - } - }, - 'sizes': [[600, 250], [300, 600]], - 'bidId': '30b3other1c1838de1e', - 'bidderRequestId': '22edbae273other3bf6', - 'auctionId': '1d1a03079test0a475', - }, - { - 'bidder': 'stickyadstv', - 'params': { - 'zoneId': '277225', - 'format': 'test' - }, - 'adUnitCode': 'adunit-code', - 'mediaTypes': { - 'banner': { - 'sizes': [ - [300, 600] - ] - } - }, - 'sizes': [[300, 600]], - 'bidId': '2', - 'bidderRequestId': '3', - 'auctionId': '4', - } - ]; - - let response = '' + - '' + - ' ' + - ' Adswizz' + - ' ' + - ' https://ads.stickyadstv.com/auto-user-sync?dealId=NRJ-PRO-12008' + - ' ' + - ' ' + - ' ' + - ' ' + - ' ' + - ' ' + - ' 00:00:09' + - ' ' + - ' ' + - ' ' + - ' ' + - ' ' + - ' ' + - ' ' + - ' 0.2000' + - ' ' + - ' ' + - ' ' + - ''; - - let ad = '
'; - let formattedAd = '
'; - - it('should get correct bid response', () => { - var request = spec.buildRequests(bidRequests); - - let expectedResponse = [ - { - requestId: '30b31c1838de1e', - cpm: '0.2000', - width: 300, - height: 600, - creativeId: '28517153', - currency: 'EUR', - netRevenue: true, - ttl: 360, - dealId: 'NRJ-PRO-00008', - campaignId: 'SMF-WOW-55555', - bannerId: '12345', - ad: ad - } - ]; - - let result = spec.interpretResponse(response, request[0]); - expect(result[0].meta.advertiserDomains).to.deep.equal([]); - expect(result[0].dealId).to.equal('NRJ-PRO-00008'); - expect(result[0].campaignId).to.equal('SMF-WOW-55555'); - expect(result[0].bannerId).to.equal('12345'); - }); - - it('should get correct bid response with formated ad', () => { - var request = spec.buildRequests(formattedBidRequests); - - let expectedResponse = [ - { - requestId: '30b31c1838de1e', - cpm: '0.2000', - width: 300, - height: 600, - creativeId: '28517153', - currency: 'EUR', - netRevenue: true, - ttl: 360, - dealId: 'NRJ-PRO-00008', - campaignId: 'SMF-WOW-55555', - bannerId: '12345', - ad: formattedAd - } - ]; - - let result = spec.interpretResponse(response, request[0]); - expect(result[0].meta.advertiserDomains).to.deep.equal([]); - expect(result[0].dealId).to.equal('NRJ-PRO-00008'); - expect(result[0].campaignId).to.equal('SMF-WOW-55555'); - expect(result[0].bannerId).to.equal('12345'); - }); - - it('handles nobid responses', () => { - var request = spec.buildRequests(formattedBidRequests); - let response = ''; - - let result = spec.interpretResponse(response, request[0]); - expect(result.length).to.equal(0); - }); - }); - - describe('interpretResponseForVideo', () => { - let bidRequests = [ - { - 'bidder': 'freewheel-ssp', - 'params': { - 'zoneId': '277225' - }, - 'adUnitCode': 'adunit-code', - 'mediaTypes': { - 'video': { - 'playerSize': [300, 600], - } - }, - 'sizes': [[300, 400]], - 'bidId': '30b31c1838de1e', - 'bidderRequestId': '22edbae2733bf6', - 'auctionId': '1d1a030790a475', - } - ]; - - let formattedBidRequests = [ - { - 'bidder': 'freewheel-ssp', - 'params': { - 'zoneId': '277225', - 'format': 'floorad' - }, - 'adUnitCode': 'adunit-code', - 'mediaTypes': { - 'video': { - 'playerSize': [300, 600], - } - }, - 'sizes': [[300, 400]], - 'bidId': '30b3other1c1838de1e', - 'bidderRequestId': '22edbae273other3bf6', - 'auctionId': '1d1a03079test0a475', - }, - { - 'bidder': 'stickyadstv', - 'params': { - 'zoneId': '277225', - 'format': 'test' - }, - 'adUnitCode': 'adunit-code', - 'mediaTypes': { - 'video': { - 'playerSize': [300, 600], - } - }, - 'sizes': [[300, 400]], - 'bidId': '2', - 'bidderRequestId': '3', - 'auctionId': '4', - }, - { - 'bidder': 'freewheelssp', - 'params': { - 'zoneId': '277225', - 'format': 'test' - }, - 'adUnitCode': 'adunit-code', - 'mediaTypes': { - 'video': { - 'playerSize': [300, 600], - } - }, - 'sizes': [[300, 400]], - 'bidId': '2', - 'bidderRequestId': '3', - 'auctionId': '4', - } - ]; - - let response = '' + - '' + - ' ' + - ' Adswizz' + - ' ' + - ' https://ads.stickyadstv.com/auto-user-sync?dealId=NRJ-PRO-00008' + - ' ' + - ' ' + - ' ' + - ' ' + - ' ' + - ' ' + - ' ' + - ' ' + - ' ' + - ' 00:00:09' + - ' ' + - ' ' + - ' ' + - ' ' + - ' ' + - ' ' + - ' ' + - ' 0.2000' + - ' ' + - ' ' + - ' ' + - ' ' + - ''; - - let ad = '
'; - let formattedAd = '
'; - - it('should get correct bid response', () => { - var request = spec.buildRequests(bidRequests); - - let expectedResponse = [ - { - requestId: '30b31c1838de1e', - cpm: '0.2000', - width: 300, - height: 600, - creativeId: '28517153', - currency: 'EUR', - netRevenue: true, - ttl: 360, - dealId: 'NRJ-PRO-00008', - campaignId: 'SMF-WOW-55555', - bannerId: '12345', - vastXml: response, - mediaType: 'video', - ad: ad, - meta: { - advertiserDomains: 'minotaur.com' - } - } - ]; - - let result = spec.interpretResponse(response, request[0]); - expect(result[0].meta.advertiserDomains).to.deep.equal(['minotaur.com']); - expect(result[0].dealId).to.equal('NRJ-PRO-00008'); - expect(result[0].campaignId).to.equal('SMF-WOW-55555'); - expect(result[0].bannerId).to.equal('12345'); - }); - - it('should get correct bid response with formated ad', () => { - var request = spec.buildRequests(formattedBidRequests); - - let expectedResponse = [ - { - requestId: '30b31c1838de1e', - cpm: '0.2000', - width: 300, - height: 600, - creativeId: '28517153', - currency: 'EUR', - netRevenue: true, - ttl: 360, - dealId: 'NRJ-PRO-00008', - campaignId: 'SMF-WOW-55555', - bannerId: '12345', - vastXml: response, - mediaType: 'video', - ad: formattedAd - } - ]; - - let result = spec.interpretResponse(response, request[0]); - expect(result[0].meta.advertiserDomains).to.deep.equal(['minotaur.com']); - expect(result[0].dealId).to.equal('NRJ-PRO-00008'); - expect(result[0].campaignId).to.equal('SMF-WOW-55555'); - expect(result[0].bannerId).to.equal('12345'); - }); - - it('handles nobid responses', () => { - var request = spec.buildRequests(formattedBidRequests); - let response = ''; - - let result = spec.interpretResponse(response, request[0]); - expect(result.length).to.equal(0); - }); - }); -}); From 3bdbcbbd7c4d11912a4bdd556e5bbc393a600ec8 Mon Sep 17 00:00:00 2001 From: Demetrio Girardi Date: Mon, 16 Jun 2025 17:03:00 -0700 Subject: [PATCH 77/92] Prebid 10: fix build-bundle tasks (#13393) --- gulpfile.js | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/gulpfile.js b/gulpfile.js index 50828d9c9e3..85edf49897c 100644 --- a/gulpfile.js +++ b/gulpfile.js @@ -510,8 +510,8 @@ gulp.task(escapePostbidConfig); gulp.task('build-creative-dev', gulp.series(buildCreative(argv.creativeDev ? 'development' : 'production'), updateCreativeRenderers)); gulp.task('build-creative-prod', gulp.series(buildCreative(), updateCreativeRenderers)); -gulp.task('build-bundle-dev', gulp.series('build-creative-dev', makeDevpackPkg(standaloneDebuggingConfig), makeDevpackPkg(), gulpBundle.bind(null, true))); -gulp.task('build-bundle-prod', gulp.series('build-creative-prod', makeWebpackPkg(standaloneDebuggingConfig), makeWebpackPkg(), gulpBundle.bind(null, false))); +gulp.task('build-bundle-dev', gulp.series(precompile({dev: true}), 'build-creative-dev', makeDevpackPkg(standaloneDebuggingConfig), makeDevpackPkg(), gulpBundle.bind(null, true))); +gulp.task('build-bundle-prod', gulp.series(precompile(), 'build-creative-prod', makeWebpackPkg(standaloneDebuggingConfig), makeWebpackPkg(), gulpBundle.bind(null, false))); // build-bundle-verbose - prod bundle except names and comments are preserved. Use this to see the effects // of dead code elimination. gulp.task('build-bundle-verbose', gulp.series(precompile(), 'build-creative-dev', makeWebpackPkg(makeVerbose(standaloneDebuggingConfig)), makeWebpackPkg(makeVerbose()), gulpBundle.bind(null, false))); @@ -529,21 +529,21 @@ gulp.task('coveralls', gulp.series('test-coverage', coveralls)); // npm will by default use .gitignore, so create an .npmignore that is a copy of it except it includes "dist" gulp.task('setup-npmignore', execaTask("sed 's/^\\/\\?dist\\/\\?$//g;w .npmignore' .gitignore", {quiet: true})); -gulp.task('build', gulp.series(clean, 'update-browserslist', precompile(), 'build-bundle-prod', updateCreativeExample, setupDist)); +gulp.task('build', gulp.series(clean, 'update-browserslist', 'build-bundle-prod', updateCreativeExample, setupDist)); gulp.task('build-release', gulp.series('build', 'setup-npmignore')); gulp.task('build-postbid', gulp.series(escapePostbidConfig, buildPostbid)); -gulp.task('serve', gulp.series(clean, lint, precompile(), gulp.parallel('build-bundle-dev', watch, test))); -gulp.task('serve-fast', gulp.series(clean, precompile({dev: true}), gulp.parallel('build-bundle-dev', watchFast))); -gulp.task('serve-prod', gulp.series(clean, precompile(), gulp.parallel('build-bundle-prod', startLocalServer))); -gulp.task('serve-and-test', gulp.series(clean, precompile({dev: true}), gulp.parallel('build-bundle-dev', watchFast, testTaskMaker({watch: true})))); -gulp.task('serve-e2e', gulp.series(clean, precompile(), 'build-bundle-prod', gulp.parallel(() => startIntegServer(), startLocalServer))); -gulp.task('serve-e2e-dev', gulp.series(clean, precompile(), 'build-bundle-dev', gulp.parallel(() => startIntegServer(true), startLocalServer))); +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-prod', gulp.series(clean, gulp.parallel('build-bundle-prod', startLocalServer))); +gulp.task('serve-and-test', gulp.series(clean, gulp.parallel('build-bundle-dev', watchFast, testTaskMaker({watch: true})))); +gulp.task('serve-e2e', gulp.series(clean, 'build-bundle-prod', gulp.parallel(() => startIntegServer(), startLocalServer))); +gulp.task('serve-e2e-dev', gulp.series(clean, 'build-bundle-dev', gulp.parallel(() => startIntegServer(true), startLocalServer))); gulp.task('default', gulp.series('build')); gulp.task('e2e-test-only', gulp.series(requireNodeVersion(16), () => runWebdriver({file: argv.file}))); -gulp.task('e2e-test', gulp.series(requireNodeVersion(16), clean, precompile(), 'build-bundle-prod', e2eTestTaskMaker())); +gulp.task('e2e-test', gulp.series(requireNodeVersion(16), clean, 'build-bundle-prod', e2eTestTaskMaker())); // other tasks gulp.task(bundleToStdout); From 2ef15608c2e1679483bd03e7f196940d108b3e19 Mon Sep 17 00:00:00 2001 From: Demetrio Girardi Date: Mon, 23 Jun 2025 12:53:15 -0700 Subject: [PATCH 78/92] Prebid 10: fix library bundling (#13418) * Prebid 10: fix library bundling * Fix --- webpack.conf.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/webpack.conf.js b/webpack.conf.js index 690d00bccd3..dbceb808f77 100644 --- a/webpack.conf.js +++ b/webpack.conf.js @@ -106,7 +106,7 @@ module.exports = { fs.readdirSync(libRoot) .filter((f) => fs.lstatSync(path.resolve(libRoot, f)).isDirectory()) .map(lib => { - const dir = helpers.getPrecompiledPath(lib) + const dir = helpers.getPrecompiledPath(path.join('libraries', lib)) const def = { name: lib, test: (module) => { From 3496fbf214bde84e35df42b778c02aaab3d77690 Mon Sep 17 00:00:00 2001 From: Demetrio Girardi Date: Mon, 23 Jun 2025 12:53:49 -0700 Subject: [PATCH 79/92] Prebid 10: fix serve-and-test build task (#13417) --- gulpfile.js | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/gulpfile.js b/gulpfile.js index 85edf49897c..18c9ea9a97f 100644 --- a/gulpfile.js +++ b/gulpfile.js @@ -510,7 +510,8 @@ gulp.task(escapePostbidConfig); gulp.task('build-creative-dev', gulp.series(buildCreative(argv.creativeDev ? 'development' : 'production'), updateCreativeRenderers)); gulp.task('build-creative-prod', gulp.series(buildCreative(), updateCreativeRenderers)); -gulp.task('build-bundle-dev', gulp.series(precompile({dev: true}), 'build-creative-dev', makeDevpackPkg(standaloneDebuggingConfig), makeDevpackPkg(), gulpBundle.bind(null, true))); +gulp.task('build-bundle-dev-no-precomp', gulp.series('build-creative-dev', makeDevpackPkg(standaloneDebuggingConfig), makeDevpackPkg(), gulpBundle.bind(null, true))); +gulp.task('build-bundle-dev', gulp.series(precompile({dev: true}), 'build-bundle-dev-no-precomp')); gulp.task('build-bundle-prod', gulp.series(precompile(), 'build-creative-prod', makeWebpackPkg(standaloneDebuggingConfig), makeWebpackPkg(), gulpBundle.bind(null, false))); // build-bundle-verbose - prod bundle except names and comments are preserved. Use this to see the effects // of dead code elimination. @@ -536,7 +537,7 @@ 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-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-and-test', gulp.series(clean, precompile({dev: true}), gulp.parallel('build-bundle-dev-no-precomp', watchFast, testTaskMaker({watch: true})))); gulp.task('serve-e2e', gulp.series(clean, 'build-bundle-prod', gulp.parallel(() => startIntegServer(), startLocalServer))); gulp.task('serve-e2e-dev', gulp.series(clean, 'build-bundle-dev', gulp.parallel(() => startIntegServer(true), startLocalServer))); From fc7921fad01b30c6f20d55ee7b9c91a88239b63d Mon Sep 17 00:00:00 2001 From: mkomorski Date: Tue, 24 Jun 2025 05:54:39 +0200 Subject: [PATCH 80/92] Prebid 10: access request credentials check (#13094) * access request credentials check * moving accessRequestCredentials rule to storage manager * hook test * reorganizing * Update src/storageManager.js Co-authored-by: Demetrio Girardi * Update modules/tcfControl.js Co-authored-by: Demetrio Girardi * enforce vendor * adding import * rule enforcePurpose * lint fix * adding types --------- Co-authored-by: Demetrio Girardi --- modules/tcfControl.ts | 21 +++++++++++++++++++++ src/activities/activities.js | 5 +++++ src/ajax.ts | 23 ++++++++++++++++++----- src/storageManager.ts | 7 ++++++- test/spec/modules/tcfControl_spec.js | 24 ++++++++++++++++++++++++ 5 files changed, 74 insertions(+), 6 deletions(-) diff --git a/modules/tcfControl.ts b/modules/tcfControl.ts index 55bfcd0fd13..9796d68c095 100644 --- a/modules/tcfControl.ts +++ b/modules/tcfControl.ts @@ -23,6 +23,7 @@ import { import {registerActivityControl} from '../src/activities/rules.js'; import { ACTIVITY_ACCESS_DEVICE, + ACTIVITY_ACCESS_REQUEST_CREDENTIALS, ACTIVITY_ENRICH_EIDS, ACTIVITY_ENRICH_UFPD, ACTIVITY_FETCH_BIDS, @@ -32,6 +33,7 @@ import { ACTIVITY_TRANSMIT_PRECISE_GEO, ACTIVITY_TRANSMIT_UFPD } from '../src/activities/activities.js'; +import {processRequestOptions} from '../src/ajax.js'; export const STRICT_STORAGE_ENFORCEMENT = 'strictStorageEnforcement'; @@ -400,6 +402,7 @@ export function setEnforcementConfig(config) { RULE_HANDLES.push(registerActivityControl(ACTIVITY_ACCESS_DEVICE, RULE_NAME, accessDeviceRule)); RULE_HANDLES.push(registerActivityControl(ACTIVITY_SYNC_USER, RULE_NAME, syncUserRule)); RULE_HANDLES.push(registerActivityControl(ACTIVITY_ENRICH_EIDS, RULE_NAME, enrichEidsRule)); + processRequestOptions.after(checkIfCredentialsAllowed); } if (ACTIVE_RULES.purpose[2] != null) { RULE_HANDLES.push(registerActivityControl(ACTIVITY_FETCH_BIDS, RULE_NAME, fetchBidsRule)); @@ -420,8 +423,26 @@ export function setEnforcementConfig(config) { } } +export function checkIfCredentialsAllowed(next, options: { withCredentials?: boolean } = {}, moduleType?: string, moduleName?: string) { + if (!options.withCredentials || (moduleType && moduleName)) { + next(options); + return; + } + const consentData = gdprDataHandler.getConsentData(); + const rule = ACTIVE_RULES.purpose[1]; + const ruleOptions = CONFIGURABLE_RULES[rule.purpose]; + const {purpose} = getConsent(consentData, ruleOptions.type, ruleOptions.id, null); + + if (!purpose && rule.enforcePurpose) { + options.withCredentials = false; + logWarn(`${RULE_NAME} denied ${ACTIVITY_ACCESS_REQUEST_CREDENTIALS}`); + } + next(options); +} + export function uninstall() { while (RULE_HANDLES.length) RULE_HANDLES.pop()(); + processRequestOptions.getHooks({hook: checkIfCredentialsAllowed}).remove(); hooksAdded = false; } diff --git a/src/activities/activities.js b/src/activities/activities.js index 40f43fb9114..53d73e26c3b 100644 --- a/src/activities/activities.js +++ b/src/activities/activities.js @@ -55,3 +55,8 @@ export const ACTIVITY_TRANSMIT_TID = 'transmitTid'; * loadExternalScript: adLoader.js is allowed to load external script */ export const LOAD_EXTERNAL_SCRIPT = 'loadExternalScript'; + +/** + * accessRequestCredentials: setting withCredentials flag in ajax request config + */ +export const ACTIVITY_ACCESS_REQUEST_CREDENTIALS = 'accessRequestCredentials'; diff --git a/src/ajax.ts b/src/ajax.ts index 822cc98521b..ad627c821f0 100644 --- a/src/ajax.ts +++ b/src/ajax.ts @@ -1,5 +1,9 @@ +import { ACTIVITY_ACCESS_REQUEST_CREDENTIALS } from './activities/activities.js'; +import { activityParams } from './activities/activityParams.js'; +import { isActivityAllowed } from './activities/rules.js'; import {config} from './config.js'; -import {buildUrl, logError, parseUrl} from './utils.js'; +import { hook } from './hook.js'; +import {buildUrl, hasDeviceAccess, logError, parseUrl} from './utils.js'; export const dep = { fetch: window.fetch.bind(window), @@ -23,7 +27,6 @@ export const dep = { const GET = 'GET'; const POST = 'POST'; const CTYPE = 'Content-Type'; - export interface AjaxOptions { /** * HTTP method. @@ -56,6 +59,13 @@ export interface AjaxOptions { adAuctionHeaders?: boolean; } +export const processRequestOptions = hook('async', function(options = {}, moduleType, moduleName) { + if (options.withCredentials) { + options.withCredentials = (moduleType && moduleName) ? isActivityAllowed(ACTIVITY_ACCESS_REQUEST_CREDENTIALS, activityParams(moduleType, moduleName)) : hasDeviceAccess(); + } + return options; +}, 'processRequestOptions'); + /** * transform legacy `ajax` parameters into a fetch request. * @returns {Request} @@ -101,13 +111,16 @@ export function toFetchRequest(url, data, options: AjaxOptions = {}) { * `request` is invoked at the beginning of each request, and `done` at the end; both are passed its origin. * */ -export function fetcherFactory(timeout = 3000, {request, done}: any = {}): typeof window['fetch'] { +export function fetcherFactory(timeout = 3000, {request, done}: any = {}, moduleType?: string, moduleName?: string): typeof window['fetch'] { let fetcher = (resource, options) => { let to; if (timeout != null && options?.signal == null && !config.getConfig('disableAjaxTimeout')) { to = dep.timeout(timeout, resource); options = Object.assign({signal: to.signal}, options); } + + processRequestOptions(options, moduleType, moduleName); + let pm = dep.fetch(resource, options); if (to?.done != null) pm = pm.finally(to.done); return pm; @@ -189,8 +202,8 @@ export type AjaxSuccessCallback = (responseText: string, xhr: XHR) => void; export type AjaxErrorCallback = (statusText: string, xhr: XHR) => void; export type AjaxCallback = AjaxSuccessCallback | { success?: AjaxErrorCallback; error?: AjaxSuccessCallback }; -export function ajaxBuilder(timeout = 3000, {request, done} = {} as any) { - const fetcher = fetcherFactory(timeout, {request, done}); +export function ajaxBuilder(timeout = 3000, {request, done} = {} as any, moduleType?: string, moduleName?: string) { + const fetcher = fetcherFactory(timeout, {request, done}, moduleType, moduleName); return function (url: string, callback?: AjaxCallback, data?: unknown, options: AjaxOptions = {}) { attachCallbacks(fetcher(toFetchRequest(url, data, options)), callback); }; diff --git a/src/storageManager.ts b/src/storageManager.ts index 3237c00bd72..f04857f3236 100644 --- a/src/storageManager.ts +++ b/src/storageManager.ts @@ -8,7 +8,7 @@ import { ACTIVITY_PARAM_STORAGE_TYPE } from './activities/params.js'; -import {ACTIVITY_ACCESS_DEVICE} from './activities/activities.js'; +import {ACTIVITY_ACCESS_DEVICE, ACTIVITY_ACCESS_REQUEST_CREDENTIALS} from './activities/activities.js'; import {config} from './config.js'; import adapterManager from './adapterManager.js'; import {activityParams} from './activities/activityParams.js'; @@ -286,6 +286,11 @@ export function deviceAccessRule() { } registerActivityControl(ACTIVITY_ACCESS_DEVICE, 'deviceAccess config', deviceAccessRule); +/** + * Block all access to request credentials when deviceAccess = false + */ +registerActivityControl(ACTIVITY_ACCESS_REQUEST_CREDENTIALS, 'deviceAccess config', deviceAccessRule); + /** * By default, deny bidders accessDevice unless they enable it through bidderSettings * diff --git a/test/spec/modules/tcfControl_spec.js b/test/spec/modules/tcfControl_spec.js index d4d2194e620..8e8c96347f9 100644 --- a/test/spec/modules/tcfControl_spec.js +++ b/test/spec/modules/tcfControl_spec.js @@ -29,6 +29,7 @@ import {requestBids} from 'src/prebid.js'; import {hook} from '../../../src/hook.js'; import {GDPR_GVLIDS, VENDORLESS_GVLID} from '../../../src/consentHandler.js'; import {activityParams} from '../../../src/activities/activityParams.js'; +import { checkIfCredentialsAllowed } from '../../../modules/tcfControl.js'; describe('gdpr enforcement', function () { let nextFnSpy; @@ -1089,4 +1090,27 @@ describe('gdpr enforcement', function () { }); }); }) + describe('checkIfCredentialsAllowed', () => { + it('should not allow access credentials for lack of purpose consent 1', () => { + const logWarn = sinon.spy(utils, 'logWarn'); + const rules = [{ + purpose: 'storage', + enforcePurpose: true, + enforceVendor: false + }] + setEnforcementConfig({gdpr: {rules}}); + const consent = setupConsentData({gdprApplies: false}); + consent.vendorData.purpose.consents['1'] = false; + const nextSpy = sinon.spy(); + const options = { + withCredentials: true + } + + checkIfCredentialsAllowed(nextSpy, options); + + sinon.assert.calledWith(nextSpy, {withCredentials: false}); + expect(logWarn.calledOnce).to.equal(true); + logWarn.restore(); + }) + }) }); From 252271d1a287c3755d993df74d80553bbe6fb18f Mon Sep 17 00:00:00 2001 From: Patrick McCann Date: Tue, 24 Jun 2025 10:27:13 -0500 Subject: [PATCH 81/92] adapter: remove bidadapter from alias names (#13379) --- modules/adbutlerBidAdapter.js | 2 +- modules/{BTBidAdapter.js => blockthroughBidAdapter.js} | 1 + modules/{BTBidAdapter.md => blockthroughBidAdapter.md} | 0 .../{brightMountainMediaBidAdapter.js => bmtmBidAdapter.js} | 0 .../{brightMountainMediaBidAdapter.md => bmtmBidAdapter.md} | 0 ...rtureMXBidAdapter.js => cadent_aperture_mxBidAdapter.js} | 1 + ...rtureMXBidAdapter.md => cadent_aperture_mxBidAdapter.md} | 0 modules/{epomDspBidAdapter.js => epom_dspBidAdapter.js} | 1 + modules/{epomDspBidAdapter.md => epom_dspBidAdapter.md} | 0 .../{growadvertisingBidAdapter.js => growadsBidAdapter.js} | 1 + .../{growadvertisingBidAdapter.md => growadsBidAdapter.md} | 0 modules/{incrxBidAdapter.js => incrementxBidAdapter.js} | 1 + modules/{incrxBidAdapter.md => incrementxBidAdapter.md} | 0 modules/{pubwiseBidAdapter.js => pwbidBidAdapter.js} | 1 + modules/{pubwiseBidAdapter.md => pwbidBidAdapter.md} | 0 modules/{bridgeuppBidAdapter.js => sonaradsBidAdapter.js} | 0 modules/{bridgeuppBidAdapter.md => sonaradsBidAdapter.md} | 0 modules/{viantOrtbBidAdapter.js => viantBidAdapter.js} | 1 + modules/{viantOrtbBidAdapter.md => viantBidAdapter.md} | 0 modules/{zetaBidAdapter.js => zeta_globalBidAdapter.js} | 1 + modules/{zetaBidAdapter.md => zeta_globalBidAdapter.md} | 0 ...{BTBidAdapter_spec.js => blockthroughBidAdapter_spec.js} | 2 +- ...untainMediaBidAdapter_spec.js => bmtmBidAdapter_spec.js} | 2 +- ...Adapter_spec.js => cadent_aperture_mxBidAdapter_spec.js} | 2 +- ...epomDspBidAdapter_spec.js => epom_dspBidAdapter_spec.js} | 2 +- ...ertisingBidAdapter_spec.js => growadsBidAdapter_spec.js} | 2 +- ...incrxBidAdapter_spec.js => incrementxBidAdapter_spec.js} | 2 +- .../{pubwiseBidAdapter_spec.js => pwbidBidAdapter_spec.js} | 6 +++--- ...idgeuppBidAdapter_spec.js => sonaradsBidAdapter_spec.js} | 2 +- ...{viantOrtbBidAdapter_spec.js => viantBidAdapter_spec.js} | 2 +- ...zetaBidAdapter_spec.js => zeta_globalBidAdapter_spec.js} | 2 +- 31 files changed, 21 insertions(+), 13 deletions(-) rename modules/{BTBidAdapter.js => blockthroughBidAdapter.js} (99%) rename modules/{BTBidAdapter.md => blockthroughBidAdapter.md} (100%) rename modules/{brightMountainMediaBidAdapter.js => bmtmBidAdapter.js} (100%) rename modules/{brightMountainMediaBidAdapter.md => bmtmBidAdapter.md} (100%) rename modules/{cadentApertureMXBidAdapter.js => cadent_aperture_mxBidAdapter.js} (99%) rename modules/{cadentApertureMXBidAdapter.md => cadent_aperture_mxBidAdapter.md} (100%) rename modules/{epomDspBidAdapter.js => epom_dspBidAdapter.js} (99%) rename modules/{epomDspBidAdapter.md => epom_dspBidAdapter.md} (100%) rename modules/{growadvertisingBidAdapter.js => growadsBidAdapter.js} (99%) rename modules/{growadvertisingBidAdapter.md => growadsBidAdapter.md} (100%) rename modules/{incrxBidAdapter.js => incrementxBidAdapter.js} (99%) rename modules/{incrxBidAdapter.md => incrementxBidAdapter.md} (100%) rename modules/{pubwiseBidAdapter.js => pwbidBidAdapter.js} (99%) rename modules/{pubwiseBidAdapter.md => pwbidBidAdapter.md} (100%) rename modules/{bridgeuppBidAdapter.js => sonaradsBidAdapter.js} (100%) rename modules/{bridgeuppBidAdapter.md => sonaradsBidAdapter.md} (100%) rename modules/{viantOrtbBidAdapter.js => viantBidAdapter.js} (99%) rename modules/{viantOrtbBidAdapter.md => viantBidAdapter.md} (100%) rename modules/{zetaBidAdapter.js => zeta_globalBidAdapter.js} (99%) rename modules/{zetaBidAdapter.md => zeta_globalBidAdapter.md} (100%) rename test/spec/modules/{BTBidAdapter_spec.js => blockthroughBidAdapter_spec.js} (99%) rename test/spec/modules/{brightMountainMediaBidAdapter_spec.js => bmtmBidAdapter_spec.js} (99%) rename test/spec/modules/{cadentApertureMXBidAdapter_spec.js => cadent_aperture_mxBidAdapter_spec.js} (99%) rename test/spec/modules/{epomDspBidAdapter_spec.js => epom_dspBidAdapter_spec.js} (98%) rename test/spec/modules/{growadvertisingBidAdapter_spec.js => growadsBidAdapter_spec.js} (99%) rename test/spec/modules/{incrxBidAdapter_spec.js => incrementxBidAdapter_spec.js} (99%) rename test/spec/modules/{pubwiseBidAdapter_spec.js => pwbidBidAdapter_spec.js} (98%) rename test/spec/modules/{bridgeuppBidAdapter_spec.js => sonaradsBidAdapter_spec.js} (99%) rename test/spec/modules/{viantOrtbBidAdapter_spec.js => viantBidAdapter_spec.js} (99%) rename test/spec/modules/{zetaBidAdapter_spec.js => zeta_globalBidAdapter_spec.js} (96%) diff --git a/modules/adbutlerBidAdapter.js b/modules/adbutlerBidAdapter.js index de430a5c916..754a51e4c54 100644 --- a/modules/adbutlerBidAdapter.js +++ b/modules/adbutlerBidAdapter.js @@ -13,7 +13,7 @@ function getTrackingPixelsMarkup(pixelURLs) { export const spec = { code: BIDDER_CODE, pageID: Math.floor(Math.random() * 10e6), - aliases: ['divreach', 'doceree'], + aliases: ['divreach'], supportedMediaTypes: [BANNER], isBidRequestValid(bid) { diff --git a/modules/BTBidAdapter.js b/modules/blockthroughBidAdapter.js similarity index 99% rename from modules/BTBidAdapter.js rename to modules/blockthroughBidAdapter.js index 7b50b90124b..50ada068986 100644 --- a/modules/BTBidAdapter.js +++ b/modules/blockthroughBidAdapter.js @@ -193,6 +193,7 @@ function getUserSyncs( export const spec = { code: BIDDER_CODE, + aliases: ['bt'], gvlid: GVLID, supportedMediaTypes: [BANNER], isBidRequestValid, diff --git a/modules/BTBidAdapter.md b/modules/blockthroughBidAdapter.md similarity index 100% rename from modules/BTBidAdapter.md rename to modules/blockthroughBidAdapter.md diff --git a/modules/brightMountainMediaBidAdapter.js b/modules/bmtmBidAdapter.js similarity index 100% rename from modules/brightMountainMediaBidAdapter.js rename to modules/bmtmBidAdapter.js diff --git a/modules/brightMountainMediaBidAdapter.md b/modules/bmtmBidAdapter.md similarity index 100% rename from modules/brightMountainMediaBidAdapter.md rename to modules/bmtmBidAdapter.md diff --git a/modules/cadentApertureMXBidAdapter.js b/modules/cadent_aperture_mxBidAdapter.js similarity index 99% rename from modules/cadentApertureMXBidAdapter.js rename to modules/cadent_aperture_mxBidAdapter.js index 32103ffd540..df1e27f7d07 100644 --- a/modules/cadentApertureMXBidAdapter.js +++ b/modules/cadent_aperture_mxBidAdapter.js @@ -22,6 +22,7 @@ const ALIASES = [ { code: 'emx_digital', gvlid: 183 }, { code: 'cadent', gvlid: 183 }, { code: 'emxdigital', gvlid: 183 }, + { code: 'cadentaperturemx', gvlid: 183 }, ]; const EIDS_SUPPORTED = [ diff --git a/modules/cadentApertureMXBidAdapter.md b/modules/cadent_aperture_mxBidAdapter.md similarity index 100% rename from modules/cadentApertureMXBidAdapter.md rename to modules/cadent_aperture_mxBidAdapter.md diff --git a/modules/epomDspBidAdapter.js b/modules/epom_dspBidAdapter.js similarity index 99% rename from modules/epomDspBidAdapter.js rename to modules/epom_dspBidAdapter.js index 8996fc96d14..7393e5086f4 100644 --- a/modules/epomDspBidAdapter.js +++ b/modules/epom_dspBidAdapter.js @@ -13,6 +13,7 @@ const BIDDER_CODE = 'epom_dsp'; export const spec = { code: BIDDER_CODE, + aliases: ['epomdsp'], isBidRequestValid(bid) { const globalSettings = config.getBidderConfig()[BIDDER_CODE]?.epomSettings || {}; diff --git a/modules/epomDspBidAdapter.md b/modules/epom_dspBidAdapter.md similarity index 100% rename from modules/epomDspBidAdapter.md rename to modules/epom_dspBidAdapter.md diff --git a/modules/growadvertisingBidAdapter.js b/modules/growadsBidAdapter.js similarity index 99% rename from modules/growadvertisingBidAdapter.js rename to modules/growadsBidAdapter.js index f6f7867f0fe..58213bfeba4 100644 --- a/modules/growadvertisingBidAdapter.js +++ b/modules/growadsBidAdapter.js @@ -9,6 +9,7 @@ const BIDDER_CODE = 'growads'; export const spec = { code: BIDDER_CODE, + aliases: ['growadvertising'], supportedMediaTypes: [BANNER, NATIVE], isBidRequestValid: function (bid) { diff --git a/modules/growadvertisingBidAdapter.md b/modules/growadsBidAdapter.md similarity index 100% rename from modules/growadvertisingBidAdapter.md rename to modules/growadsBidAdapter.md diff --git a/modules/incrxBidAdapter.js b/modules/incrementxBidAdapter.js similarity index 99% rename from modules/incrxBidAdapter.js rename to modules/incrementxBidAdapter.js index 57faff63ccf..e7f8ff51fa9 100644 --- a/modules/incrxBidAdapter.js +++ b/modules/incrementxBidAdapter.js @@ -16,6 +16,7 @@ const CREATIVE_TTL = 300; export const spec = { code: BIDDER_CODE, + aliases: ['incrx'], supportedMediaTypes: [BANNER, VIDEO], /** diff --git a/modules/incrxBidAdapter.md b/modules/incrementxBidAdapter.md similarity index 100% rename from modules/incrxBidAdapter.md rename to modules/incrementxBidAdapter.md diff --git a/modules/pubwiseBidAdapter.js b/modules/pwbidBidAdapter.js similarity index 99% rename from modules/pubwiseBidAdapter.js rename to modules/pwbidBidAdapter.js index 41c7685f3dd..9aea154d25b 100644 --- a/modules/pubwiseBidAdapter.js +++ b/modules/pwbidBidAdapter.js @@ -133,6 +133,7 @@ _each(NATIVE_ASSETS, anAsset => { NATIVE_ASSET_KEY_TO_ASSET_MAP[anAsset.KEY] = a export const spec = { code: BIDDER_CODE, + aliases: ['pubwise'], gvlid: GVLID, supportedMediaTypes: [BANNER, VIDEO, NATIVE], /** diff --git a/modules/pubwiseBidAdapter.md b/modules/pwbidBidAdapter.md similarity index 100% rename from modules/pubwiseBidAdapter.md rename to modules/pwbidBidAdapter.md diff --git a/modules/bridgeuppBidAdapter.js b/modules/sonaradsBidAdapter.js similarity index 100% rename from modules/bridgeuppBidAdapter.js rename to modules/sonaradsBidAdapter.js diff --git a/modules/bridgeuppBidAdapter.md b/modules/sonaradsBidAdapter.md similarity index 100% rename from modules/bridgeuppBidAdapter.md rename to modules/sonaradsBidAdapter.md diff --git a/modules/viantOrtbBidAdapter.js b/modules/viantBidAdapter.js similarity index 99% rename from modules/viantOrtbBidAdapter.js rename to modules/viantBidAdapter.js index e6f30592bf6..12c4c3f1a36 100644 --- a/modules/viantOrtbBidAdapter.js +++ b/modules/viantBidAdapter.js @@ -18,6 +18,7 @@ const DEFAULT_NET_REVENUE = true; export const spec = { code: BIDDER_CODE, + aliases: ['viantortb'], supportedMediaTypes: [BANNER, VIDEO, NATIVE], isBidRequestValid: function (bid) { diff --git a/modules/viantOrtbBidAdapter.md b/modules/viantBidAdapter.md similarity index 100% rename from modules/viantOrtbBidAdapter.md rename to modules/viantBidAdapter.md diff --git a/modules/zetaBidAdapter.js b/modules/zeta_globalBidAdapter.js similarity index 99% rename from modules/zetaBidAdapter.js rename to modules/zeta_globalBidAdapter.js index 658d3198df0..65b9a71f6c2 100644 --- a/modules/zetaBidAdapter.js +++ b/modules/zeta_globalBidAdapter.js @@ -22,6 +22,7 @@ const NET_REV = true; export const spec = { code: BIDDER_CODE, + aliases: ['zeta'], supportedMediaTypes: [BANNER], /** diff --git a/modules/zetaBidAdapter.md b/modules/zeta_globalBidAdapter.md similarity index 100% rename from modules/zetaBidAdapter.md rename to modules/zeta_globalBidAdapter.md diff --git a/test/spec/modules/BTBidAdapter_spec.js b/test/spec/modules/blockthroughBidAdapter_spec.js similarity index 99% rename from test/spec/modules/BTBidAdapter_spec.js rename to test/spec/modules/blockthroughBidAdapter_spec.js index 18f99929037..f0778eba780 100644 --- a/test/spec/modules/BTBidAdapter_spec.js +++ b/test/spec/modules/blockthroughBidAdapter_spec.js @@ -1,5 +1,5 @@ import { expect } from 'chai'; -import { spec } from 'modules/BTBidAdapter.js'; +import { spec } from 'modules/blockthroughBidAdapter.js'; import { BANNER } from '../../../src/mediaTypes.js'; // load modules that register ORTB processors import 'src/prebid.js'; diff --git a/test/spec/modules/brightMountainMediaBidAdapter_spec.js b/test/spec/modules/bmtmBidAdapter_spec.js similarity index 99% rename from test/spec/modules/brightMountainMediaBidAdapter_spec.js rename to test/spec/modules/bmtmBidAdapter_spec.js index 58c0d5f5e3c..4de78c2fca1 100644 --- a/test/spec/modules/brightMountainMediaBidAdapter_spec.js +++ b/test/spec/modules/bmtmBidAdapter_spec.js @@ -1,5 +1,5 @@ import { expect } from 'chai'; -import { spec } from '../../../modules/brightMountainMediaBidAdapter.js'; +import { spec } from '../../../modules/bmtmBidAdapter.js'; const BIDDER_CODE = 'bmtm'; const PLACEMENT_ID = 329; diff --git a/test/spec/modules/cadentApertureMXBidAdapter_spec.js b/test/spec/modules/cadent_aperture_mxBidAdapter_spec.js similarity index 99% rename from test/spec/modules/cadentApertureMXBidAdapter_spec.js rename to test/spec/modules/cadent_aperture_mxBidAdapter_spec.js index 16e2bc66b40..602fc6dc6b2 100644 --- a/test/spec/modules/cadentApertureMXBidAdapter_spec.js +++ b/test/spec/modules/cadent_aperture_mxBidAdapter_spec.js @@ -2,7 +2,7 @@ import * as utils from 'src/utils.js'; import { expect } from 'chai'; import { newBidder } from 'src/adapters/bidderFactory.js'; -import { spec } from 'modules/cadentApertureMXBidAdapter.js'; +import { spec } from 'modules/cadent_aperture_mxBidAdapter.js'; describe('cadent_aperture_mx Adapter', function () { describe('callBids', function () { diff --git a/test/spec/modules/epomDspBidAdapter_spec.js b/test/spec/modules/epom_dspBidAdapter_spec.js similarity index 98% rename from test/spec/modules/epomDspBidAdapter_spec.js rename to test/spec/modules/epom_dspBidAdapter_spec.js index fc60a2fea2d..b483b16d03c 100644 --- a/test/spec/modules/epomDspBidAdapter_spec.js +++ b/test/spec/modules/epom_dspBidAdapter_spec.js @@ -1,5 +1,5 @@ import { expect } from 'chai'; -import { spec } from '../../../modules/epomDspBidAdapter.js'; +import { spec } from '../../../modules/epom_dspBidAdapter.js'; const VALID_BID_REQUEST = { bidder: 'epom_dsp', diff --git a/test/spec/modules/growadvertisingBidAdapter_spec.js b/test/spec/modules/growadsBidAdapter_spec.js similarity index 99% rename from test/spec/modules/growadvertisingBidAdapter_spec.js rename to test/spec/modules/growadsBidAdapter_spec.js index 55eea06cca8..ef2e9e4e9fa 100644 --- a/test/spec/modules/growadvertisingBidAdapter_spec.js +++ b/test/spec/modules/growadsBidAdapter_spec.js @@ -1,5 +1,5 @@ import { expect } from 'chai'; -import { spec } from 'modules/growadvertisingBidAdapter.js'; +import { spec } from 'modules/growadsBidAdapter.js'; import * as utils from '../../../src/utils.js'; import {BANNER, NATIVE} from '../../../src/mediaTypes.js'; diff --git a/test/spec/modules/incrxBidAdapter_spec.js b/test/spec/modules/incrementxBidAdapter_spec.js similarity index 99% rename from test/spec/modules/incrxBidAdapter_spec.js rename to test/spec/modules/incrementxBidAdapter_spec.js index 24be0dbde57..3fcf3bcc978 100644 --- a/test/spec/modules/incrxBidAdapter_spec.js +++ b/test/spec/modules/incrementxBidAdapter_spec.js @@ -1,5 +1,5 @@ import { expect } from 'chai'; -import { spec } from 'modules/incrxBidAdapter.js'; +import { spec } from 'modules/incrementxBidAdapter.js'; import { BANNER, VIDEO } from 'src/mediaTypes.js'; import { INSTREAM, OUTSTREAM } from 'src/video.js'; diff --git a/test/spec/modules/pubwiseBidAdapter_spec.js b/test/spec/modules/pwbidBidAdapter_spec.js similarity index 98% rename from test/spec/modules/pubwiseBidAdapter_spec.js rename to test/spec/modules/pwbidBidAdapter_spec.js index 7f4695b6f72..9df8693f830 100644 --- a/test/spec/modules/pubwiseBidAdapter_spec.js +++ b/test/spec/modules/pwbidBidAdapter_spec.js @@ -1,9 +1,9 @@ // import or require modules necessary for the test, e.g.: import {expect} from 'chai'; -import {spec} from 'modules/pubwiseBidAdapter.js'; -import {_checkVideoPlacement, _checkMediaType} from 'modules/pubwiseBidAdapter.js'; // this is exported only for testing so maintaining the JS convention of _ to indicate the intent -import {_parseAdSlot} from 'modules/pubwiseBidAdapter.js'; // this is exported only for testing so maintaining the JS convention of _ to indicate the intent +import {spec} from 'modules/pwbidBidAdapter.js'; +import {_checkVideoPlacement, _checkMediaType} from 'modules/pwbidBidAdapter.js'; // this is exported only for testing so maintaining the JS convention of _ to indicate the intent +import {_parseAdSlot} from 'modules/pwbidBidAdapter.js'; // this is exported only for testing so maintaining the JS convention of _ to indicate the intent import * as utils from 'src/utils.js'; const sampleRequestBanner = { diff --git a/test/spec/modules/bridgeuppBidAdapter_spec.js b/test/spec/modules/sonaradsBidAdapter_spec.js similarity index 99% rename from test/spec/modules/bridgeuppBidAdapter_spec.js rename to test/spec/modules/sonaradsBidAdapter_spec.js index 6a69b077881..bc0de144363 100644 --- a/test/spec/modules/bridgeuppBidAdapter_spec.js +++ b/test/spec/modules/sonaradsBidAdapter_spec.js @@ -1,7 +1,7 @@ import { expect } from 'chai'; import { spec, BIDDER_CODE, SERVER_PATH_US1_SYNC, SERVER_PATH_US1_EVENTS -} from '../../../modules/bridgeuppBidAdapter.js'; +} from '../../../modules/sonaradsBidAdapter.js'; import * as utils from 'src/utils.js'; import * as ajax from 'src/ajax.js'; import { hook } from '../../../src/hook'; diff --git a/test/spec/modules/viantOrtbBidAdapter_spec.js b/test/spec/modules/viantBidAdapter_spec.js similarity index 99% rename from test/spec/modules/viantOrtbBidAdapter_spec.js rename to test/spec/modules/viantBidAdapter_spec.js index 67ae8b07821..315ca35f964 100644 --- a/test/spec/modules/viantOrtbBidAdapter_spec.js +++ b/test/spec/modules/viantBidAdapter_spec.js @@ -1,4 +1,4 @@ -import {spec, converter} from 'modules/viantOrtbBidAdapter.js'; +import {spec, converter} from 'modules/viantBidAdapter.js'; import {assert, expect} from 'chai'; import {deepClone} from '../../../src/utils'; import {buildWindowTree} from '../../helpers/refererDetectionHelper'; diff --git a/test/spec/modules/zetaBidAdapter_spec.js b/test/spec/modules/zeta_globalBidAdapter_spec.js similarity index 96% rename from test/spec/modules/zetaBidAdapter_spec.js rename to test/spec/modules/zeta_globalBidAdapter_spec.js index 529fb8e8d31..031b52d194f 100644 --- a/test/spec/modules/zetaBidAdapter_spec.js +++ b/test/spec/modules/zeta_globalBidAdapter_spec.js @@ -1,4 +1,4 @@ -import { spec } from '../../../modules/zetaBidAdapter.js' +import { spec } from '../../../modules/zeta_globalBidAdapter.js' describe('Zeta Bid Adapter', function() { const bannerRequest = [{ From 35a3acdb0162d306c9aec336fb572ab88fd1cedd Mon Sep 17 00:00:00 2001 From: mkomorski Date: Tue, 24 Jun 2025 17:27:25 +0200 Subject: [PATCH 82/92] Prebid 10: Native send targeting keys removal (#13425) * Prebid 10: native send targeting keys removal * test fix --- modules/criteoBidAdapter.js | 12 +- modules/ixBidAdapter.md | 1 - modules/ringieraxelspringerBidAdapter.js | 1 - modules/smaatoBidAdapter.md | 1 - src/auction.ts | 7 +- src/constants.ts | 1 - src/native.ts | 69 ------- src/prebid.ts | 2 +- src/targeting.ts | 13 +- .../e2e/modules/e2e_bidderSettings.spec.js | 6 - .../multi-bidder/e2e_multiple_bidders.spec.js | 6 - test/spec/e2e/native/basic_native_ad.spec.js | 6 - test/spec/modules/adagioBidAdapter_spec.js | 2 - test/spec/modules/admaticBidAdapter_spec.js | 1 - test/spec/modules/adxcgBidAdapter_spec.js | 1 - test/spec/modules/criteoBidAdapter_spec.js | 112 ------------ test/spec/modules/inmobiBidAdapter_spec.js | 2 - .../spec/modules/pulsepointBidAdapter_spec.js | 1 - .../ringieraxelspringerBidAdapter_spec.js | 2 - .../modules/smilewantedBidAdapter_spec.js | 1 - test/spec/native_spec.js | 172 ------------------ test/spec/unit/core/targeting_spec.js | 8 - test/spec/unit/pbjs_api_spec.js | 2 +- 23 files changed, 13 insertions(+), 416 deletions(-) diff --git a/modules/criteoBidAdapter.js b/modules/criteoBidAdapter.js index a38660c4f25..ca0e8f949fd 100644 --- a/modules/criteoBidAdapter.js +++ b/modules/criteoBidAdapter.js @@ -522,12 +522,12 @@ function buildCdbUrl(context) { function checkNativeSendId(bidRequest) { return !(bidRequest.nativeParams && ( - (bidRequest.nativeParams.image && ((bidRequest.nativeParams.image.sendId !== true || bidRequest.nativeParams.image.sendTargetingKeys === true))) || - (bidRequest.nativeParams.icon && ((bidRequest.nativeParams.icon.sendId !== true || bidRequest.nativeParams.icon.sendTargetingKeys === true))) || - (bidRequest.nativeParams.clickUrl && ((bidRequest.nativeParams.clickUrl.sendId !== true || bidRequest.nativeParams.clickUrl.sendTargetingKeys === true))) || - (bidRequest.nativeParams.displayUrl && ((bidRequest.nativeParams.displayUrl.sendId !== true || bidRequest.nativeParams.displayUrl.sendTargetingKeys === true))) || - (bidRequest.nativeParams.privacyLink && ((bidRequest.nativeParams.privacyLink.sendId !== true || bidRequest.nativeParams.privacyLink.sendTargetingKeys === true))) || - (bidRequest.nativeParams.privacyIcon && ((bidRequest.nativeParams.privacyIcon.sendId !== true || bidRequest.nativeParams.privacyIcon.sendTargetingKeys === true))) + (bidRequest.nativeParams.image && ((bidRequest.nativeParams.image.sendId !== true))) || + (bidRequest.nativeParams.icon && ((bidRequest.nativeParams.icon.sendId !== true))) || + (bidRequest.nativeParams.clickUrl && ((bidRequest.nativeParams.clickUrl.sendId !== true))) || + (bidRequest.nativeParams.displayUrl && ((bidRequest.nativeParams.displayUrl.sendId !== true))) || + (bidRequest.nativeParams.privacyLink && ((bidRequest.nativeParams.privacyLink.sendId !== true))) || + (bidRequest.nativeParams.privacyIcon && ((bidRequest.nativeParams.privacyIcon.sendId !== true))) )); } diff --git a/modules/ixBidAdapter.md b/modules/ixBidAdapter.md index 1e17f450c0e..36ecf9dbe8f 100644 --- a/modules/ixBidAdapter.md +++ b/modules/ixBidAdapter.md @@ -96,7 +96,6 @@ object are detailed here. | Key | Scope | Type | Description | --- | --- | --- | --- -| sendTargetingKeys | Optional | Boolean | Defines whether or not to send the hb_native_ASSET targeting keys to the ad server. Defaults to true. | adTemplate | Optional | String | Used in the ‘AdUnit-Defined Creative Scenario’, this value controls the Native template right in the page. | rendererUrl | Optional | String | Used in the ‘Custom Renderer Scenario’, this points to javascript code that will produce the Native template. | title | Optional | Title asset | The title of the ad, usually a call to action or a brand name. diff --git a/modules/ringieraxelspringerBidAdapter.js b/modules/ringieraxelspringerBidAdapter.js index 14033c1247f..326fecc9d32 100644 --- a/modules/ringieraxelspringerBidAdapter.js +++ b/modules/ringieraxelspringerBidAdapter.js @@ -187,7 +187,6 @@ function parseNativeResponse(ad) { const { dsaurl, height, width, adclick } = ad.data.meta; const link = adclick + (url || Thirdpartyclicktracker); const nativeResponse = { - sendTargetingKeys: false, title: title || Headline || '', image: { url: image || Image || '', diff --git a/modules/smaatoBidAdapter.md b/modules/smaatoBidAdapter.md index 170880c3fc0..35e8b17da4a 100644 --- a/modules/smaatoBidAdapter.md +++ b/modules/smaatoBidAdapter.md @@ -128,7 +128,6 @@ var adUnits = [{ } ] }, - sendTargetingKeys: false, } }, "bids": [{ diff --git a/src/auction.ts b/src/auction.ts index c479897b168..6d83a582399 100644 --- a/src/auction.ts +++ b/src/auction.ts @@ -11,7 +11,7 @@ import { timestamp } from './utils.js'; import {getPriceBucketString} from './cpmBucketManager.js'; -import {getNativeTargeting, isNativeResponse, setNativeResponseProperties} from './native.js'; +import {isNativeResponse, setNativeResponseProperties} from './native.js'; import {batchAndStore, storeLocally} from './videoCache.js'; import {Renderer} from './Renderer.js'; import {config} from './config.js'; @@ -1032,11 +1032,6 @@ export function getKeyValueTargetingPairs(bidderCode, custBidObj: Bid, {index = custBidObj.sendStandardTargeting = bidderSettings.get(bidderCode, 'sendStandardTargeting'); } - // set native key value targeting - if (FEATURES.NATIVE && custBidObj['native']) { - keyValues = Object.assign({}, keyValues, getNativeTargeting(custBidObj)); - } - return keyValues; } diff --git a/src/constants.ts b/src/constants.ts index 3a4e4c588a1..7c51502e223 100644 --- a/src/constants.ts +++ b/src/constants.ts @@ -186,7 +186,6 @@ export const NATIVE_IMAGE_TYPES = { export const NATIVE_KEYS_THAT_ARE_NOT_ASSETS = [ 'privacyIcon', 'clickUrl', - 'sendTargetingKeys', 'adTemplate', 'rendererUrl', 'type' diff --git a/src/native.ts b/src/native.ts index 3bd7bffac68..72d968b081c 100644 --- a/src/native.ts +++ b/src/native.ts @@ -103,10 +103,6 @@ export interface NativeMediaType extends LegacyNativeRequest { export const nativeAdapters = []; -export const NATIVE_TARGETING_KEYS = Object.keys(NATIVE_KEYS).map( - key => NATIVE_KEYS[key] -); - export const IMAGE: NativeMediaType = { ortb: { ver: '1.2', @@ -419,55 +415,6 @@ export function setNativeResponseProperties(bid, adUnit) { }); } -/** - * Gets native targeting key-value pairs - * @param {Object} bid - * @return {Object} targeting - */ -export function getNativeTargeting(bid, {index = auctionManager.index} = {}) { - let keyValues = {}; - const adUnit = index.getAdUnit(bid); - - const globalSendTargetingKeys = adUnit?.nativeParams?.ortb == null && adUnit?.nativeParams?.sendTargetingKeys !== false; - - const nativeKeys = getNativeKeys(adUnit); - - const flatBidNativeKeys = { ...bid.native, ...bid.native.ext }; - delete flatBidNativeKeys.ext; - - Object.keys(flatBidNativeKeys).forEach(asset => { - const key = nativeKeys[asset]; - let value = getAssetValue(bid.native[asset]) || getAssetValue(bid?.native?.ext?.[asset]); - - if (asset === 'adTemplate' || !key || !value) { - return; - } - - let sendPlaceholder = adUnit?.nativeParams?.[asset]?.sendId; - if (typeof sendPlaceholder !== 'boolean') { - sendPlaceholder = adUnit?.nativeParams?.ext?.[asset]?.sendId; - } - - if (sendPlaceholder) { - const placeholder = `${key}:${bid.adId}`; - value = placeholder; - } - - let assetSendTargetingKeys = adUnit?.nativeParams?.[asset]?.sendTargetingKeys; - if (typeof assetSendTargetingKeys !== 'boolean') { - assetSendTargetingKeys = adUnit?.nativeParams?.ext?.[asset]?.sendTargetingKeys; - } - - const sendTargeting = typeof assetSendTargetingKeys === 'boolean' ? assetSendTargetingKeys : globalSendTargetingKeys; - - if (sendTargeting) { - keyValues[key] = value; - } - }); - - return keyValues; -} - function getNativeAssets(nativeProps, keys, ext = false) { let assets = []; Object.entries(nativeProps) @@ -540,22 +487,6 @@ export function getAllAssetsMessage(data, adObject) { function getAssetValue(value) { return value?.url || value; } - -function getNativeKeys(adUnit) { - const extraNativeKeys = {} - - if (adUnit?.nativeParams?.ext) { - Object.keys(adUnit.nativeParams.ext).forEach(extKey => { - extraNativeKeys[extKey] = `hb_native_${extKey}`; - }) - } - - return { - ...NATIVE_KEYS, - ...extraNativeKeys - } -} - /** * converts Prebid legacy native assets request to OpenRTB format * @param {object} legacyNativeAssets an object that describes a native bid request in Prebid proprietary format diff --git a/src/prebid.ts b/src/prebid.ts index 2932589d072..301d976c74b 100644 --- a/src/prebid.ts +++ b/src/prebid.ts @@ -234,7 +234,7 @@ function validateNativeMediaType(adUnit: AdUnit) { return validatedAdUnit; } function checkDeprecated(onDeprecated) { - for (const key of ['sendTargetingKeys', 'types']) { + for (const key of ['types']) { if (native.hasOwnProperty(key)) { const res = onDeprecated(key); if (res) return res; diff --git a/src/targeting.ts b/src/targeting.ts index cb8e9eb731c..91a5e6509b5 100644 --- a/src/targeting.ts +++ b/src/targeting.ts @@ -7,13 +7,11 @@ import { DEFAULT_TARGETING_KEYS, EVENTS, JSON_MAPPING, - NATIVE_KEYS, TARGETING_KEYS } from './constants.js'; import * as events from './events.js'; import {hook} from './hook.js'; import {ADPOD} from './mediaTypes.js'; -import {NATIVE_TARGETING_KEYS} from './native.js'; import { deepAccess, deepClone, @@ -243,7 +241,7 @@ export function newTargeting(auctionManager) { const bidsSorted = getHighestCpmBidsFromBidPool(filteredBids, winReducer, adUnitBidLimit, undefined, winSorter); let targeting = getTargetingLevels(bidsSorted, customKeysByUnit, adUnitCodes); - const defaultKeys = Object.keys(Object.assign({}, DEFAULT_TARGETING_KEYS, NATIVE_KEYS)); + const defaultKeys = Object.keys(Object.assign({}, DEFAULT_TARGETING_KEYS)); let allowedKeys = config.getConfig(CFG_ALLOW_TARGETING_KEYS); const addedKeys = config.getConfig(CFG_ADD_TARGETING_KEYS); @@ -378,7 +376,7 @@ export function newTargeting(auctionManager) { } function addBidToTargeting(bids, enableSendAllBids = false, deals = false): TargetingArray { - const standardKeys = FEATURES.NATIVE ? TARGETING_KEYS_ARR.concat(NATIVE_TARGETING_KEYS) : TARGETING_KEYS_ARR.slice(); + const standardKeys = TARGETING_KEYS_ARR.slice(); const allowSendAllBidsTargetingKeys = config.getConfig('targetingControls.allowSendAllBidsTargetingKeys'); const allowedSendAllBidTargeting = allowSendAllBidsTargetingKeys @@ -412,8 +410,8 @@ export function newTargeting(auctionManager) { * @return filtered targeting */ function getAllowedTargetingKeyValues(targeting: TargetingArray, allowedKeys: string[]): TargetingArray { - const defaultKeyring = Object.assign({}, TARGETING_KEYS, NATIVE_KEYS); - const defaultKeys = Object.keys(defaultKeyring); + const defaultKeyring = Object.assign({}, TARGETING_KEYS); + const defaultKeys = Object.keys(TARGETING_KEYS); const keyDispositions = {}; logInfo(`allowTargetingKeys - allowed keys [ ${allowedKeys.map(k => defaultKeyring[k]).join(', ')} ]`); targeting.map(adUnit => { @@ -680,9 +678,6 @@ export function newTargeting(auctionManager) { function getCustomKeys() { let standardKeys = getStandardKeys(); - if (FEATURES.NATIVE) { - standardKeys = standardKeys.concat(NATIVE_TARGETING_KEYS); - } return function(key) { return standardKeys.indexOf(key) === -1; } diff --git a/test/spec/e2e/modules/e2e_bidderSettings.spec.js b/test/spec/e2e/modules/e2e_bidderSettings.spec.js index 46251d39be3..dbf0f99d01e 100644 --- a/test/spec/e2e/modules/e2e_bidderSettings.spec.js +++ b/test/spec/e2e/modules/e2e_bidderSettings.spec.js @@ -6,16 +6,10 @@ const CREATIVE_IFRAME_CSS_SELECTOR = 'iframe[id="google_ads_iframe_/19968336/hea const EXPECTED_TARGETING_KEYS = { hb_pb_appnexus: '10.00', - hb_native_title_appn: 'This is a Prebid Native Creative', - hb_native_linkurl: 'http://prebid.org/dev-docs/show-native-ads.html', hb_format: 'native', - hb_native_brand: 'Prebid.org', hb_size: '0x0', hb_bidder_appnexus: 'appnexus', - hb_native_linkurl_ap: 'http://prebid.org/dev-docs/show-native-ads.html', - hb_native_title: 'This is a Prebid Native Creative', hb_pb: '10.00', - hb_native_brand_appn: 'Prebid.org', hb_bidder: 'appnexus', hb_format_appnexus: 'native', hb_size_appnexus: '0x0', 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 098dee3647d..749f410d3f0 100644 --- a/test/spec/e2e/multi-bidder/e2e_multiple_bidders.spec.js +++ b/test/spec/e2e/multi-bidder/e2e_multiple_bidders.spec.js @@ -6,16 +6,10 @@ const CREATIVE_BANNER_CSS_SELECTOR = 'iframe[id="google_ads_iframe_/19968336/pre const EXPECTED_TARGETING_KEYS = { hb_pb_adasta: '10.00', - hb_native_title_adas: 'This is a Prebid Native Creative', - hb_native_linkurl: 'http://prebid.org/dev-docs/show-multi-format-ads.html', hb_format: 'native', - hb_native_brand: 'Prebid.org', hb_size: '0x0', hb_bidder_adasta: 'adasta', - hb_native_linkurl_ad: 'http://prebid.org/dev-docs/show-multi-format-ads.html', - hb_native_title: 'This is a Prebid Native Creative', hb_pb: '10.00', - hb_native_brand_adas: 'Prebid.org', hb_bidder: 'adasta', hb_format_adasta: 'native', hb_size_adasta: '0x0' diff --git a/test/spec/e2e/native/basic_native_ad.spec.js b/test/spec/e2e/native/basic_native_ad.spec.js index ded7ba610f2..9404cf3304e 100644 --- a/test/spec/e2e/native/basic_native_ad.spec.js +++ b/test/spec/e2e/native/basic_native_ad.spec.js @@ -6,16 +6,10 @@ const CREATIVE_IFRAME_CSS_SELECTOR = 'iframe[id="google_ads_iframe_/19968336/pre const EXPECTED_TARGETING_KEYS = { hb_pb_appnexus: '10.00', - hb_native_title_appn: 'This is a Prebid Native Creative', - hb_native_linkurl: 'http://prebid.org/dev-docs/show-native-ads.html', hb_format: 'native', - hb_native_brand: 'Prebid.org', hb_size: '0x0', hb_bidder_appnexus: 'appnexus', - hb_native_linkurl_ap: 'http://prebid.org/dev-docs/show-native-ads.html', - hb_native_title: 'This is a Prebid Native Creative', hb_pb: '10.00', - hb_native_brand_appn: 'Prebid.org', hb_bidder: 'appnexus', hb_format_appnexus: 'native', hb_size_appnexus: '0x0' diff --git a/test/spec/modules/adagioBidAdapter_spec.js b/test/spec/modules/adagioBidAdapter_spec.js index 106dbd0e082..ba47463bbff 100644 --- a/test/spec/modules/adagioBidAdapter_spec.js +++ b/test/spec/modules/adagioBidAdapter_spec.js @@ -1444,8 +1444,6 @@ describe('Adagio bid adapter', () => { const bidRequestNative = utils.deepClone(bidRequest) bidRequestNative.nativeParams = { - sendTargetingKeys: false, - clickUrl: { required: true, }, diff --git a/test/spec/modules/admaticBidAdapter_spec.js b/test/spec/modules/admaticBidAdapter_spec.js index da4329b6405..ad8586d4e9d 100644 --- a/test/spec/modules/admaticBidAdapter_spec.js +++ b/test/spec/modules/admaticBidAdapter_spec.js @@ -1046,7 +1046,6 @@ describe('admaticBidAdapter', () => { ], 'type': 'native', 'mediatype': { - 'sendTargetingKeys': false, 'ortb': { 'ver': '1.1', 'context': 2, diff --git a/test/spec/modules/adxcgBidAdapter_spec.js b/test/spec/modules/adxcgBidAdapter_spec.js index 14b399eaad4..221f5c036fa 100644 --- a/test/spec/modules/adxcgBidAdapter_spec.js +++ b/test/spec/modules/adxcgBidAdapter_spec.js @@ -145,7 +145,6 @@ describe('adxcg v8 oRtbConverter Adapter Tests', function () { bidId: 'bid12345', mediaTypes: { native: { - sendTargetingKeys: false, ortb: nativeOrtbRequest } }, diff --git a/test/spec/modules/criteoBidAdapter_spec.js b/test/spec/modules/criteoBidAdapter_spec.js index 9006ab9609f..2f1100b9152 100644 --- a/test/spec/modules/criteoBidAdapter_spec.js +++ b/test/spec/modules/criteoBidAdapter_spec.js @@ -2431,118 +2431,6 @@ describe('The Criteo bidding adapter', function () { }); } - if (FEATURES.NATIVE) { - it('should warn only once if sendTargetingKeys set to true on required fields for native bidRequest', async () => { - const bidRequests = [ - { - bidder: 'criteo', - adUnitCode: 'bid-123', - mediaTypes: { - native: {} - }, - nativeOrtbRequest: { - assets: [{ - required: 1, - id: 1, - img: { - type: 3, - wmin: 100, - hmin: 100, - } - }] - }, - transactionId: 'transaction-123', - sizes: [[728, 90]], - params: { - zoneId: 123, - publisherSubId: '123' - }, - }, - { - bidder: 'criteo', - adUnitCode: 'bid-456', - mediaTypes: { - native: {} - }, - nativeOrtbRequest: { - assets: [{ - required: 1, - id: 1, - img: { - type: 3, - wmin: 100, - hmin: 100, - } - }] - }, - transactionId: 'transaction-456', - sizes: [[728, 90]], - params: { - zoneId: 456, - publisherSubId: '456' - }, - }, - ]; - - const nativeParamsWithSendTargetingKeys = [ - { - nativeParams: { - image: { - sendTargetingKeys: true - }, - } - }, - { - nativeParams: { - icon: { - sendTargetingKeys: true - }, - } - }, - { - nativeParams: { - clickUrl: { - sendTargetingKeys: true - }, - } - }, - { - nativeParams: { - displayUrl: { - sendTargetingKeys: true - }, - } - }, - { - nativeParams: { - privacyLink: { - sendTargetingKeys: true - }, - } - }, - { - nativeParams: { - privacyIcon: { - sendTargetingKeys: true - }, - } - } - ]; - - for (const nativeParams of nativeParamsWithSendTargetingKeys) { - let transformedBidRequests = {...bidRequests}; - transformedBidRequests = [Object.assign(transformedBidRequests[0], nativeParams), Object.assign(transformedBidRequests[1], nativeParams)]; - spec.buildRequests(transformedBidRequests, await addFPDToBidderRequest(bidderRequest)); - } - - expect( - logWarnStub - .withArgs('Criteo: all native assets containing URL should be sent as placeholders with sendId(icon, image, clickUrl, displayUrl, privacyLink, privacyIcon)') - .callCount - ).to.eql(nativeParamsWithSendTargetingKeys.length * bidRequests.length); - }); - } - it('should properly parse a bid response with FLEDGE auction configs', async function () { let auctionConfig1 = { auctionSignals: {}, diff --git a/test/spec/modules/inmobiBidAdapter_spec.js b/test/spec/modules/inmobiBidAdapter_spec.js index 7f5c363b0dc..3a762446614 100644 --- a/test/spec/modules/inmobiBidAdapter_spec.js +++ b/test/spec/modules/inmobiBidAdapter_spec.js @@ -1877,7 +1877,6 @@ describe('The inmobi bidding adapter', function () { required: true, sizes: [120, 60], sendId: true, - sendTargetingKeys: false } } } @@ -1921,7 +1920,6 @@ describe('The inmobi bidding adapter', function () { required: true, sizes: [120, 60], sendId: true, - sendTargetingKeys: false } } } diff --git a/test/spec/modules/pulsepointBidAdapter_spec.js b/test/spec/modules/pulsepointBidAdapter_spec.js index 81b6b1b20ca..818acea7791 100644 --- a/test/spec/modules/pulsepointBidAdapter_spec.js +++ b/test/spec/modules/pulsepointBidAdapter_spec.js @@ -65,7 +65,6 @@ describe('PulsePoint Adapter Tests', function () { bidId: 'bid12345', mediaTypes: { native: { - sendTargetingKeys: false, ortb: nativeOrtbRequest } }, diff --git a/test/spec/modules/ringieraxelspringerBidAdapter_spec.js b/test/spec/modules/ringieraxelspringerBidAdapter_spec.js index 3539dad9362..a68d51a9456 100644 --- a/test/spec/modules/ringieraxelspringerBidAdapter_spec.js +++ b/test/spec/modules/ringieraxelspringerBidAdapter_spec.js @@ -545,7 +545,6 @@ describe('ringieraxelspringerBidAdapter', function () { privacy: '//dsa.url' }; const expectedTeaserStandardResponse = { - sendTargetingKeys: false, title: 'Headline', image: { url: '//img.url', @@ -633,7 +632,6 @@ describe('ringieraxelspringerBidAdapter', function () { privacy: '//dsa.url', }; const expectedNativeInFeedResponse = { - sendTargetingKeys: false, title: 'Headline', image: { url: '//img.url', diff --git a/test/spec/modules/smilewantedBidAdapter_spec.js b/test/spec/modules/smilewantedBidAdapter_spec.js index 8063c1d7abe..7c1e007c6a7 100644 --- a/test/spec/modules/smilewantedBidAdapter_spec.js +++ b/test/spec/modules/smilewantedBidAdapter_spec.js @@ -238,7 +238,6 @@ const NATIVE_REQUEST = [{ ], mediaTypes: { native: { - sendTargetingKeys: false, title: { required: true, len: 140 diff --git a/test/spec/native_spec.js b/test/spec/native_spec.js index 9b905d66ef9..7e5c14e99b2 100644 --- a/test/spec/native_spec.js +++ b/test/spec/native_spec.js @@ -1,7 +1,6 @@ import { expect } from 'chai'; import { fireNativeTrackers, - getNativeTargeting, nativeBidIsValid, getAssetMessage, getAllAssetsMessage, @@ -201,177 +200,6 @@ describe('native.js', function () { sandbox.restore(); }); - it('gets native targeting keys', function () { - const targeting = getNativeTargeting(bid); - expect(targeting[NATIVE_KEYS.title]).to.equal(bid.native.title); - expect(targeting[NATIVE_KEYS.body]).to.equal(bid.native.body); - expect(targeting[NATIVE_KEYS.clickUrl]).to.equal( - bid.native.clickUrl - ); - expect(targeting.hb_native_foo).to.equal(bid.native.foo); - }); - - it('does not include targeting keys if request is ortb', () => { - const targeting = getNativeTargeting(bid, deps({ - adUnitId: bid.adUnitId, - nativeParams: { - ortb: { - assets: [{id: 1, type: '2'}] - } - } - })); - expect(Object.keys(targeting)).to.eql([]); - }); - - it('can get targeting from null native keys', () => { - const targeting = getNativeTargeting({...bid, native: {...bid.native, displayUrl: null}}); - expect(targeting.hb_native_displayurl).to.not.be.ok; - }) - - it('sends placeholders for configured assets', function () { - const adUnit = { - adUnitId: 'au', - nativeParams: { - body: { sendId: true }, - clickUrl: { sendId: true }, - ext: { - foo: { - sendId: false, - }, - baz: { - sendId: true, - }, - }, - }, - }; - const targeting = getNativeTargeting(bid, deps(adUnit)); - - expect(targeting[NATIVE_KEYS.title]).to.equal(bid.native.title); - expect(targeting[NATIVE_KEYS.body]).to.equal( - 'hb_native_body:123' - ); - expect(targeting[NATIVE_KEYS.clickUrl]).to.equal( - 'hb_native_linkurl:123' - ); - expect(targeting.hb_native_foo).to.equal(bid.native.ext.foo); - expect(targeting.hb_native_baz).to.equal('hb_native_baz:123'); - }); - - it('sends placeholdes targetings with ortb native response', function () { - const targeting = getNativeTargeting(completeNativeBid); - - expect(targeting[NATIVE_KEYS.title]).to.equal('Native Creative'); - expect(targeting[NATIVE_KEYS.body]).to.equal('Cool description great stuff'); - expect(targeting[NATIVE_KEYS.clickUrl]).to.equal('https://www.link.example'); - }); - - it('should only include native targeting keys with values', function () { - const adUnit = { - adUnitId: 'au', - nativeParams: { - body: { sendId: true }, - clickUrl: { sendId: true }, - ext: { - foo: { - required: false, - }, - baz: { - required: false, - }, - }, - }, - }; - - const targeting = getNativeTargeting(bidWithUndefinedFields, deps(adUnit)); - - expect(Object.keys(targeting)).to.deep.equal([ - NATIVE_KEYS.title, - NATIVE_KEYS.sponsoredBy, - NATIVE_KEYS.clickUrl, - 'hb_native_foo', - ]); - }); - - it('should only include targeting that has sendTargetingKeys set to true', function () { - const adUnit = { - adUnitId: 'au', - nativeParams: { - image: { - required: true, - sizes: [150, 50], - }, - title: { - required: true, - len: 80, - sendTargetingKeys: true, - }, - sendTargetingKeys: false, - }, - }; - const targeting = getNativeTargeting(bid, deps(adUnit)); - - expect(Object.keys(targeting)).to.deep.equal([NATIVE_KEYS.title]); - }); - - it('should only include targeting if sendTargetingKeys not set to false', function () { - const adUnit = { - adUnitId: 'au', - nativeParams: { - image: { - required: true, - sizes: [150, 50], - }, - title: { - required: true, - len: 80, - }, - body: { - required: true, - }, - clickUrl: { - required: true, - }, - icon: { - required: false, - sendTargetingKeys: false, - }, - cta: { - required: false, - sendTargetingKeys: false, - }, - sponsoredBy: { - required: false, - sendTargetingKeys: false, - }, - privacyLink: { - required: false, - sendTargetingKeys: false, - }, - ext: { - foo: { - required: false, - sendTargetingKeys: true, - }, - }, - }, - }; - const targeting = getNativeTargeting(bid, deps(adUnit)); - - expect(Object.keys(targeting)).to.deep.equal([ - NATIVE_KEYS.title, - NATIVE_KEYS.body, - NATIVE_KEYS.image, - NATIVE_KEYS.clickUrl, - 'hb_native_foo', - ]); - }); - - it('should include rendererUrl in targeting', function () { - const rendererUrl = 'https://www.renderer.com/'; - const targeting = getNativeTargeting({...bid, native: {...bid.native, rendererUrl: {url: rendererUrl}}}, deps({})); - expect(targeting[NATIVE_KEYS.rendererUrl]).to.eql(rendererUrl); - }); - it('fires impression trackers', function () { fireNativeTrackers({}, bid); sinon.assert.calledOnce(triggerPixelStub); diff --git a/test/spec/unit/core/targeting_spec.js b/test/spec/unit/core/targeting_spec.js index b80992b5e57..31a75a4f8e5 100644 --- a/test/spec/unit/core/targeting_spec.js +++ b/test/spec/unit/core/targeting_spec.js @@ -684,9 +684,6 @@ describe('targeting tests', function () { } }); const defaultKeys = new Set(Object.values(DEFAULT_TARGETING_KEYS)); - if (FEATURES.NATIVE) { - Object.values(NATIVE_KEYS).forEach((k) => defaultKeys.add(k)); - } const expectedKeys = new Set(); bidsReceived @@ -1028,12 +1025,7 @@ describe('targeting tests', function () { }); let targeting = targetingInstance.getAllTargeting([nativeAdUnitCode]); - expect(targeting[nativeAdUnitCode].hb_native_image).to.equal(nativeBid1.native.image.url); - expect(targeting[nativeAdUnitCode].hb_native_linkurl).to.equal(nativeBid1.native.clickUrl); - expect(targeting[nativeAdUnitCode].hb_native_title).to.equal(nativeBid1.native.title); - expect(targeting[nativeAdUnitCode].hb_native_image_dgad).to.exist.and.to.equal(nativeBid2.native.image.url); expect(targeting[nativeAdUnitCode].hb_pb_dgads).to.exist.and.to.equal(nativeBid2.pbMg); - expect(targeting[nativeAdUnitCode].hb_native_body_appne).to.exist.and.to.equal(nativeBid1.native.body); }); } diff --git a/test/spec/unit/pbjs_api_spec.js b/test/spec/unit/pbjs_api_spec.js index cceb9f34475..289b05956e6 100644 --- a/test/spec/unit/pbjs_api_spec.js +++ b/test/spec/unit/pbjs_api_spec.js @@ -2727,7 +2727,7 @@ describe('Unit: Prebid Module', function () { }); }); - ['sendTargetingKeys', 'types'].forEach(key => { + ['types'].forEach(key => { it(`should reject native that includes both ortb and ${key}`, async () => { const adUnit = { code: 'au', From f5393b9cabf545a3afd1bf55ec5eeddced8badbf Mon Sep 17 00:00:00 2001 From: agentmoose Date: Tue, 24 Jun 2025 10:00:06 -0600 Subject: [PATCH 83/92] remove adman from 10 --- modules/admanBidAdapter.js | 51 --- modules/admanBidAdapter.md | 69 ---- test/spec/modules/admanBidAdapter_spec.js | 471 ---------------------- 3 files changed, 591 deletions(-) delete mode 100644 modules/admanBidAdapter.js delete mode 100644 modules/admanBidAdapter.md delete mode 100644 test/spec/modules/admanBidAdapter_spec.js diff --git a/modules/admanBidAdapter.js b/modules/admanBidAdapter.js deleted file mode 100644 index 6778e536a1b..00000000000 --- a/modules/admanBidAdapter.js +++ /dev/null @@ -1,51 +0,0 @@ -import { registerBidder } from '../src/adapters/bidderFactory.js'; -import { BANNER, NATIVE, VIDEO } from '../src/mediaTypes.js'; -import { deepAccess } from '../src/utils.js'; -import { config } from '../src/config.js'; -import { - isBidRequestValid, - buildRequestsBase, - interpretResponse, - getUserSyncs, - buildPlacementProcessingFunction -} from '../libraries/teqblazeUtils/bidderUtils.js'; - -const GVLID = 149; -const BIDDER_CODE = 'adman'; -const AD_URL = 'https://pub.admanmedia.com/?c=o&m=multi'; -const SYNC_URL = 'https://sync.admanmedia.com'; - -const addCustomFieldsToPlacement = (bid, bidderRequest, placement) => { - placement.traffic = placement.adFormat; - - if (placement.adFormat === VIDEO) { - placement.wPlayer = placement.playerSize?.[0]?.[0]; - placement.hPlayer = placement.playerSize?.[0]?.[1]; - } -}; - -const placementProcessingFunction = buildPlacementProcessingFunction({ addCustomFieldsToPlacement }); - -const buildRequests = (validBidRequests = [], bidderRequest = {}) => { - const request = buildRequestsBase({ adUrl: AD_URL, validBidRequests, bidderRequest, placementProcessingFunction }); - const content = deepAccess(bidderRequest, 'ortb2.site.content', config.getAnyConfig('ortb2.site.content')); - - if (content) { - request.data.content = content; - } - - return request; -}; - -export const spec = { - code: BIDDER_CODE, - gvlid: GVLID, - supportedMediaTypes: [BANNER, VIDEO, NATIVE], - - isBidRequestValid: isBidRequestValid(['placementId']), - buildRequests, - interpretResponse, - getUserSyncs: getUserSyncs(SYNC_URL) -}; - -registerBidder(spec); diff --git a/modules/admanBidAdapter.md b/modules/admanBidAdapter.md deleted file mode 100644 index 07a268af489..00000000000 --- a/modules/admanBidAdapter.md +++ /dev/null @@ -1,69 +0,0 @@ -# Overview - -``` -Module Name: adman Bidder Adapter -Module Type: Bidder Adapter -``` - -# Description - -Module that connects to AdmanMedia' demand sources - -# 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: 'adman', - params: { - placementId: 0, - traffic: 'native' - } - } - ] - }, - // Will return static test banner - { - code: 'placementId_0', - mediaTypes: { - banner: { - sizes: [[300, 250]], - } - }, - bids: [ - { - bidder: 'adman', - params: { - placementId: 0, - traffic: 'banner' - } - } - ] - }, - // Will return test vast xml. All video params are stored under placement in publishers UI - { - code: 'placementId_0', - mediaTypes: { - video: { - playerSize: [640, 480], - context: 'instream' - } - }, - bids: [ - { - bidder: 'adman', - params: { - placementId: 0, - traffic: 'video' - } - } - ] - } - ]; -``` diff --git a/test/spec/modules/admanBidAdapter_spec.js b/test/spec/modules/admanBidAdapter_spec.js deleted file mode 100644 index abae74b9749..00000000000 --- a/test/spec/modules/admanBidAdapter_spec.js +++ /dev/null @@ -1,471 +0,0 @@ -import { expect } from 'chai'; -import { spec } from '../../../modules/admanBidAdapter.js'; -import { BANNER, VIDEO, NATIVE } from '../../../src/mediaTypes.js'; -import { getUniqueIdentifierStr } from '../../../src/utils.js'; - -const bidder = 'adman'; - -describe('AdmanBidAdapter', function () { - const userIdAsEids = [{ - source: 'test.org', - uids: [{ - id: '01**********', - atype: 1, - ext: { - third: '01***********' - } - }] - }]; - const bids = [ - { - bidId: getUniqueIdentifierStr(), - bidder: bidder, - mediaTypes: { - [BANNER]: { - sizes: [[300, 250]] - } - }, - params: { - placementId: 'testBanner' - }, - userIdAsEids - }, - { - bidId: getUniqueIdentifierStr(), - bidder: bidder, - mediaTypes: { - [VIDEO]: { - playerSize: [[300, 300]], - minduration: 5, - maxduration: 60 - } - }, - params: { - placementId: 'testVideo' - }, - userIdAsEids - }, - { - bidId: getUniqueIdentifierStr(), - bidder: bidder, - mediaTypes: { - [NATIVE]: { - native: { - title: { - required: true - }, - body: { - required: true - }, - icon: { - required: true, - size: [64, 64] - } - } - } - }, - params: { - placementId: 'testNative' - }, - userIdAsEids - } - ]; - - const invalidBid = { - bidId: getUniqueIdentifierStr(), - bidder: bidder, - mediaTypes: { - [BANNER]: { - sizes: [[300, 250]] - } - }, - params: { - - } - } - - const bidderRequest = { - uspConsent: '1---', - gdprConsent: { - consentString: 'COvFyGBOvFyGBAbAAAENAPCAAOAAAAAAAAAAAEEUACCKAAA.IFoEUQQgAIQwgIwQABAEAAAAOIAACAIAAAAQAIAgEAACEAAAAAgAQBAAAAAAAGBAAgAAAAAAAFAAECAAAgAAQARAEQAAAAAJAAIAAgAAAYQEAAAQmAgBC3ZAYzUw', - vendorData: {} - }, - refererInfo: { - referer: 'https://test.com', - page: 'https://test.com' - }, - ortb2: { - device: { - w: 1512, - h: 982, - language: 'en-UK' - } - }, - timeout: 500 - }; - - describe('isBidRequestValid', function () { - it('Should return true if there are bidId, params and key parameters present', function () { - expect(spec.isBidRequestValid(bids[0])).to.be.true; - }); - it('Should return false if at least one of parameters is not present', function () { - expect(spec.isBidRequestValid(invalidBid)).to.be.false; - }); - }); - - describe('buildRequests', function () { - let serverRequest = spec.buildRequests(bids, bidderRequest); - - it('Creates a ServerRequest object with method, URL and data', function () { - expect(serverRequest).to.exist; - expect(serverRequest.method).to.exist; - expect(serverRequest.url).to.exist; - expect(serverRequest.data).to.exist; - }); - - it('Returns POST method', function () { - expect(serverRequest.method).to.equal('POST'); - }); - - it('Returns valid URL', function () { - expect(serverRequest.url).to.equal('https://pub.admanmedia.com/?c=o&m=multi'); - }); - - it('Returns general data valid', function () { - let data = serverRequest.data; - expect(data).to.be.an('object'); - expect(data).to.have.all.keys('deviceWidth', - 'deviceHeight', - 'device', - 'language', - 'secure', - 'host', - 'page', - 'placements', - 'coppa', - 'ccpa', - 'gdpr', - 'tmax', - 'bcat', - 'badv', - 'bapp', - 'battr' - ); - expect(data.deviceWidth).to.be.a('number'); - expect(data.deviceHeight).to.be.a('number'); - expect(data.language).to.be.a('string'); - expect(data.secure).to.be.within(0, 1); - expect(data.host).to.be.a('string'); - expect(data.page).to.be.a('string'); - expect(data.coppa).to.be.a('number'); - expect(data.gdpr).to.be.a('object'); - expect(data.ccpa).to.be.a('string'); - expect(data.tmax).to.be.a('number'); - expect(data.placements).to.have.lengthOf(3); - }); - - it('Returns valid placements', function () { - const { placements } = serverRequest.data; - - for (let i = 0, len = placements.length; i < len; i++) { - const placement = placements[i]; - expect(placement.placementId).to.be.oneOf(['testBanner', 'testVideo', 'testNative']); - expect(placement.traffic).to.be.oneOf([BANNER, VIDEO, NATIVE]); - expect(placement.bidId).to.be.a('string'); - expect(placement.schain).to.be.an('object'); - expect(placement.bidfloor).to.exist.and.to.equal(0); - expect(placement.type).to.exist.and.to.equal('publisher'); - expect(placement.eids).to.exist.and.to.be.deep.equal(userIdAsEids); - - if (placement.traffic === BANNER) { - expect(placement.sizes).to.be.an('array'); - } - switch (placement.traffic) { - case BANNER: - expect(placement.sizes).to.be.an('array'); - break; - case VIDEO: - expect(placement.wPlayer).to.be.an('number'); - expect(placement.hPlayer).to.be.an('number'); - expect(placement.minduration).to.be.an('number'); - expect(placement.maxduration).to.be.an('number'); - break; - case NATIVE: - expect(placement.native).to.be.an('object'); - break; - } - } - }); - - it('Returns data with gdprConsent and without uspConsent', function () { - delete bidderRequest.uspConsent; - serverRequest = spec.buildRequests(bids, bidderRequest); - let data = serverRequest.data; - expect(data.gdpr).to.exist; - expect(data.gdpr).to.be.a('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; - }); - - it('Returns data with uspConsent and without gdprConsent', function () { - bidderRequest.uspConsent = '1---'; - delete bidderRequest.gdprConsent; - serverRequest = spec.buildRequests(bids, bidderRequest); - let data = serverRequest.data; - expect(data.ccpa).to.exist; - expect(data.ccpa).to.be.a('string'); - expect(data.ccpa).to.equal(bidderRequest.uspConsent); - expect(data.gdpr).to.not.exist; - }); - }); - - describe('gpp consent', function () { - it('bidderRequest.gppConsent', () => { - bidderRequest.gppConsent = { - gppString: 'abc123', - applicableSections: [8] - }; - - let serverRequest = spec.buildRequests(bids, bidderRequest); - let data = serverRequest.data; - expect(data).to.be.an('object'); - expect(data).to.have.property('gpp'); - expect(data).to.have.property('gpp_sid'); - - delete bidderRequest.gppConsent; - }) - - it('bidderRequest.ortb2.regs.gpp', () => { - bidderRequest.ortb2 = bidderRequest.ortb2 || {}; - bidderRequest.ortb2.regs = bidderRequest.ortb2.regs || {}; - bidderRequest.ortb2.regs.gpp = 'abc123'; - bidderRequest.ortb2.regs.gpp_sid = [8]; - - let serverRequest = spec.buildRequests(bids, bidderRequest); - let data = serverRequest.data; - expect(data).to.be.an('object'); - expect(data).to.have.property('gpp'); - expect(data).to.have.property('gpp_sid'); - - bidderRequest.ortb2; - }) - }); - - describe('interpretResponse', function () { - it('Should interpret banner response', function () { - const banner = { - body: [{ - mediaType: 'banner', - width: 300, - height: 250, - cpm: 0.4, - ad: 'Test', - requestId: '23fhj33i987f', - ttl: 120, - creativeId: '2', - netRevenue: true, - currency: 'USD', - dealId: '1', - meta: { - advertiserDomains: ['google.com'], - advertiserId: 1234 - } - }] - }; - let bannerResponses = spec.interpretResponse(banner); - expect(bannerResponses).to.be.an('array').that.is.not.empty; - let dataItem = bannerResponses[0]; - expect(dataItem).to.have.all.keys('requestId', 'cpm', 'width', 'height', 'ad', 'ttl', 'creativeId', - 'netRevenue', 'currency', 'dealId', 'mediaType', 'meta'); - expect(dataItem.requestId).to.equal(banner.body[0].requestId); - expect(dataItem.cpm).to.equal(banner.body[0].cpm); - expect(dataItem.width).to.equal(banner.body[0].width); - expect(dataItem.height).to.equal(banner.body[0].height); - expect(dataItem.ad).to.equal(banner.body[0].ad); - expect(dataItem.ttl).to.equal(banner.body[0].ttl); - expect(dataItem.creativeId).to.equal(banner.body[0].creativeId); - expect(dataItem.netRevenue).to.be.true; - expect(dataItem.currency).to.equal(banner.body[0].currency); - expect(dataItem.meta).to.be.an('object').that.has.any.key('advertiserDomains'); - }); - it('Should interpret video response', function () { - const video = { - body: [{ - vastUrl: 'test.com', - mediaType: 'video', - cpm: 0.5, - requestId: '23fhj33i987f', - ttl: 120, - creativeId: '2', - netRevenue: true, - currency: 'USD', - dealId: '1', - meta: { - advertiserDomains: ['google.com'], - advertiserId: 1234 - } - }] - }; - let videoResponses = spec.interpretResponse(video); - expect(videoResponses).to.be.an('array').that.is.not.empty; - - let dataItem = videoResponses[0]; - expect(dataItem).to.have.all.keys('requestId', 'cpm', 'vastUrl', 'ttl', 'creativeId', - 'netRevenue', 'currency', 'dealId', 'mediaType', 'meta'); - expect(dataItem.requestId).to.equal('23fhj33i987f'); - expect(dataItem.cpm).to.equal(0.5); - expect(dataItem.vastUrl).to.equal('test.com'); - expect(dataItem.ttl).to.equal(120); - expect(dataItem.creativeId).to.equal('2'); - expect(dataItem.netRevenue).to.be.true; - expect(dataItem.currency).to.equal('USD'); - expect(dataItem.meta).to.be.an('object').that.has.any.key('advertiserDomains'); - }); - it('Should interpret native response', function () { - const native = { - body: [{ - mediaType: 'native', - native: { - clickUrl: 'test.com', - title: 'Test', - image: 'test.com', - impressionTrackers: ['test.com'], - }, - ttl: 120, - cpm: 0.4, - requestId: '23fhj33i987f', - creativeId: '2', - netRevenue: true, - currency: 'USD', - meta: { - advertiserDomains: ['google.com'], - advertiserId: 1234 - } - }] - }; - let nativeResponses = spec.interpretResponse(native); - expect(nativeResponses).to.be.an('array').that.is.not.empty; - - let dataItem = nativeResponses[0]; - expect(dataItem).to.have.keys('requestId', 'cpm', 'ttl', 'creativeId', 'netRevenue', 'currency', 'mediaType', 'native', 'meta'); - expect(dataItem.native).to.have.keys('clickUrl', 'impressionTrackers', 'title', 'image') - expect(dataItem.requestId).to.equal('23fhj33i987f'); - expect(dataItem.cpm).to.equal(0.4); - expect(dataItem.native.clickUrl).to.equal('test.com'); - expect(dataItem.native.title).to.equal('Test'); - expect(dataItem.native.image).to.equal('test.com'); - expect(dataItem.native.impressionTrackers).to.be.an('array').that.is.not.empty; - expect(dataItem.native.impressionTrackers[0]).to.equal('test.com'); - expect(dataItem.ttl).to.equal(120); - expect(dataItem.creativeId).to.equal('2'); - expect(dataItem.netRevenue).to.be.true; - expect(dataItem.currency).to.equal('USD'); - expect(dataItem.meta).to.be.an('object').that.has.any.key('advertiserDomains'); - }); - it('Should return an empty array if invalid banner response is passed', function () { - const invBanner = { - body: [{ - width: 300, - cpm: 0.4, - ad: 'Test', - requestId: '23fhj33i987f', - ttl: 120, - creativeId: '2', - netRevenue: true, - currency: 'USD', - dealId: '1' - }] - }; - - let serverResponses = spec.interpretResponse(invBanner); - expect(serverResponses).to.be.an('array').that.is.empty; - }); - it('Should return an empty array if invalid video response is passed', function () { - const invVideo = { - body: [{ - mediaType: 'video', - cpm: 0.5, - requestId: '23fhj33i987f', - ttl: 120, - creativeId: '2', - netRevenue: true, - currency: 'USD', - dealId: '1' - }] - }; - let serverResponses = spec.interpretResponse(invVideo); - expect(serverResponses).to.be.an('array').that.is.empty; - }); - it('Should return an empty array if invalid native response is passed', function () { - const invNative = { - body: [{ - mediaType: 'native', - clickUrl: 'test.com', - title: 'Test', - impressionTrackers: ['test.com'], - ttl: 120, - requestId: '23fhj33i987f', - creativeId: '2', - netRevenue: true, - currency: 'USD', - }] - }; - let serverResponses = spec.interpretResponse(invNative); - expect(serverResponses).to.be.an('array').that.is.empty; - }); - it('Should return an empty array if invalid response is passed', function () { - const invalid = { - body: [{ - ttl: 120, - creativeId: '2', - netRevenue: true, - currency: 'USD', - dealId: '1' - }] - }; - let serverResponses = spec.interpretResponse(invalid); - expect(serverResponses).to.be.an('array').that.is.empty; - }); - }); - - describe('getUserSyncs', function() { - it('Should return array of objects with proper sync config , include GDPR', function() { - const syncData = spec.getUserSyncs({}, {}, { - consentString: 'ALL', - gdprApplies: true, - }, {}); - expect(syncData).to.be.an('array').which.is.not.empty; - expect(syncData[0]).to.be.an('object') - expect(syncData[0].type).to.be.a('string') - expect(syncData[0].type).to.equal('image') - expect(syncData[0].url).to.be.a('string') - expect(syncData[0].url).to.equal('https://sync.admanmedia.com/image?pbjs=1&gdpr=1&gdpr_consent=ALL&coppa=0') - }); - it('Should return array of objects with proper sync config , include CCPA', function() { - const syncData = spec.getUserSyncs({}, {}, {}, { - consentString: '1---' - }); - expect(syncData).to.be.an('array').which.is.not.empty; - expect(syncData[0]).to.be.an('object') - expect(syncData[0].type).to.be.a('string') - expect(syncData[0].type).to.equal('image') - expect(syncData[0].url).to.be.a('string') - expect(syncData[0].url).to.equal('https://sync.admanmedia.com/image?pbjs=1&ccpa_consent=1---&coppa=0') - }); - it('Should return array of objects with proper sync config , include GPP', function() { - const syncData = spec.getUserSyncs({}, {}, {}, {}, { - gppString: 'abc123', - applicableSections: [8] - }); - expect(syncData).to.be.an('array').which.is.not.empty; - expect(syncData[0]).to.be.an('object') - expect(syncData[0].type).to.be.a('string') - expect(syncData[0].type).to.equal('image') - expect(syncData[0].url).to.be.a('string') - expect(syncData[0].url).to.equal('https://sync.admanmedia.com/image?pbjs=1&gpp=abc123&gpp_sid=8&coppa=0') - }); - }); -}); From 0bf5c66c06186866c4cd49ea8235e0487e1395d4 Mon Sep 17 00:00:00 2001 From: pm-kapil-tuptewar <91458408+pm-kapil-tuptewar@users.noreply.github.com> Date: Thu, 26 Jun 2025 02:10:14 +0530 Subject: [PATCH 84/92] Core : ORTB banner params validation (#13432) * Validate ortb params from adunits media-type object * Update src/banner.ts Co-authored-by: Demetrio Girardi --------- Co-authored-by: Patrick McCann Co-authored-by: Demetrio Girardi --- src/banner.ts | 38 +++++++++++++++++++++++- src/prebid.ts | 3 +- test/spec/banner_spec.js | 64 ++++++++++++++++++++++++++++++++++++++++ 3 files changed, 103 insertions(+), 2 deletions(-) diff --git a/src/banner.ts b/src/banner.ts index 1cc16d9bdb5..276c442983c 100644 --- a/src/banner.ts +++ b/src/banner.ts @@ -1,4 +1,4 @@ -import { isArrayOfNums, isInteger, isStr } from './utils.js'; +import { isArrayOfNums, isInteger, isStr, isPlainObject, logWarn } from './utils.js'; import type {Size} from "./types/common.d.ts"; import type {ORTBImp} from "./types/ortb/request.d.ts"; import type {BaseMediaType} from "./mediaTypes.ts"; @@ -25,6 +25,42 @@ const ORTB_PARAMS = [ */ export const ORTB_BANNER_PARAMS = new Map(ORTB_PARAMS); +/** + * validateOrtbBannerFields mutates the `adUnit.mediaTypes.banner` object by removing invalid ortb properties (default). + * The onInvalidParam callback can be used to handle invalid properties differently. + * Other properties are ignored and kept as is. + * + * @param {Object} adUnit - The adUnit object. + * @param {Function=} onInvalidParam - The callback function to be called with key, value, and adUnit. + * @returns {void} + */ +export function validateOrtbBannerFields(adUnit, onInvalidParam?) { + const bannerParams = adUnit?.mediaTypes?.banner; + + if (!isPlainObject(bannerParams)) { + logWarn(`validateOrtbBannerFields: bannerParams must be an object.`); + return; + } + + if (bannerParams != null) { + Object.entries(bannerParams) + .forEach(([key, value]: any) => { + if (!ORTB_BANNER_PARAMS.has(key)) { + return + } + const isValid = ORTB_BANNER_PARAMS.get(key)(value); + if (!isValid) { + if (typeof onInvalidParam === 'function') { + onInvalidParam(key, value, adUnit); + } else { + logWarn(`Invalid prop in adUnit "${adUnit.code}": Invalid value for mediaTypes.banner.${key} ORTB property. The property has been removed.`, adUnit); + delete bannerParams[key]; + } + } + }); + } +} + export interface BannerMediaType extends BaseMediaType, Partial> { /** * All sizes this ad unit can accept. diff --git a/src/prebid.ts b/src/prebid.ts index 301d976c74b..de79590821c 100644 --- a/src/prebid.ts +++ b/src/prebid.ts @@ -53,7 +53,7 @@ import { } from './adRendering.js'; import {getHighestCpm} from './utils/reducers.js'; import {fillVideoDefaults, ORTB_VIDEO_PARAMS, validateOrtbVideoFields} from './video.js'; -import {ORTB_BANNER_PARAMS} from './banner.js'; +import {ORTB_BANNER_PARAMS, validateOrtbBannerFields} from './banner.js'; import {BANNER, VIDEO} from './mediaTypes.js'; import {delayIfPrerendering} from './utils/prerendering.js'; import {type BidAdapter, type BidderSpec, newBidder} from './adapters/bidderFactory.js'; @@ -199,6 +199,7 @@ function validateBannerMediaType(adUnit: AdUnit) { logError('Detected a mediaTypes.banner object without a proper sizes field. Please ensure the sizes are listed like: [[300, 250], ...]. Removing invalid mediaTypes.banner object from request.'); delete validatedAdUnit.mediaTypes.banner } + validateOrtbBannerFields(validatedAdUnit); syncOrtb2(validatedAdUnit, 'banner') return validatedAdUnit; } diff --git a/test/spec/banner_spec.js b/test/spec/banner_spec.js index 80273686ff3..93e6fe5a878 100644 --- a/test/spec/banner_spec.js +++ b/test/spec/banner_spec.js @@ -1,7 +1,71 @@ import * as utils from '../../src/utils.js'; import { syncOrtb2 } from '../../src/prebid.js'; +import { validateOrtbBannerFields } from '../../src/banner.ts'; describe('banner', () => { + describe('validateOrtbBannerFields', () => { + let sandbox; + + beforeEach(() => { + sandbox = sinon.createSandbox(); + }) + + afterEach(() => { + sandbox.restore(); + }); + + it('removes incorrect or invalid ortb properties, and keep non ortb ones', () => { + const mt = { + mimes: ['video/mp4'], + w: 600, + h: 480, + battr: [6, 7], + api: 6, // -- INVALID + otherOne: 'test' + }; + + const expected = {...mt}; + delete expected.api; + + const adUnit = { + code: 'adUnitCode', + mediaTypes: { banner: mt } + }; + validateOrtbBannerFields(adUnit); + + expect(adUnit.mediaTypes.banner).to.eql(expected); + }); + + it('Early return when 1st param is not a plain object', () => { + sandbox.spy(utils, 'logWarn'); + + validateOrtbBannerFields(); + validateOrtbBannerFields([]); + validateOrtbBannerFields(null); + validateOrtbBannerFields('hello'); + validateOrtbBannerFields(() => {}); + + sinon.assert.callCount(utils.logWarn, 5); + }); + + it('Calls onInvalidParam when a property is invalid', () => { + const onInvalidParam = sandbox.spy(); + const adUnit = { + code: 'adUnitCode', + mediaTypes: { + banner: { + mimes: ['video/mp4'], + api: 6 + } + } + }; + validateOrtbBannerFields(adUnit, onInvalidParam); + + sinon.assert.calledOnce(onInvalidParam); + sinon.assert.calledWith(onInvalidParam, 'api', 6, adUnit); + }); + }); + describe('syncOrtb2', () => { let logWarnSpy; From dacfe07e2d2b2022f490f72f7a06ac4c7420f107 Mon Sep 17 00:00:00 2001 From: Patrick McCann Date: Thu, 26 Jun 2025 10:51:50 -0400 Subject: [PATCH 85/92] Prebid 10: sayonara bid.userId (#13386) * Update index.ts * Update userId_spec.js * submodules: update tests for removal of userId (#13388) * Deluserid: lmpid and zeotap (#13389) * submodules: update tests for removal of userId * core: update submodule id tests * Deluserid (#13390) * submodules: update tests for removal of userId * core: update submodule id tests * core: update userid tests for bid userId removal --- modules/userId/index.ts | 6 -- test/spec/modules/euidIdSystem_spec.js | 12 ++- test/spec/modules/id5IdSystem_spec.js | 20 ----- test/spec/modules/idxIdSystem_spec.js | 2 - test/spec/modules/lmpIdSystem_spec.js | 2 - test/spec/modules/uid2IdSystem_spec.js | 27 +++++-- test/spec/modules/userId_spec.js | 73 +++---------------- .../spec/modules/zeotapIdPlusIdSystem_spec.js | 2 - 8 files changed, 39 insertions(+), 105 deletions(-) diff --git a/modules/userId/index.ts b/modules/userId/index.ts index 455bb3a378f..d7ed6259986 100644 --- a/modules/userId/index.ts +++ b/modules/userId/index.ts @@ -448,7 +448,6 @@ export function enrichEids(ortb2Fragments) { declare module '../../src/adapterManager' { interface BaseBidRequest { - userId: UserId; userIdAsEids: ORTBRequest['user']['eids']; } } @@ -459,16 +458,11 @@ export function addIdData({adUnits, ortb2Fragments}) { if ([adUnits].some(i => !Array.isArray(i) || !i.length)) { return; } - const globalIds = getIds(initializedSubmodules.global); const globalEids = ortb2Fragments.global.user?.ext?.eids || []; adUnits.forEach(adUnit => { if (adUnit.bids && isArray(adUnit.bids)) { adUnit.bids.forEach(bid => { - const bidderIds = Object.assign({}, globalIds, getIds(initializedSubmodules.bidder[bid.bidder] ?? {})); const bidderEids = globalEids.concat(ortb2Fragments.bidder?.[bid.bidder]?.user?.ext?.eids || []); - if (Object.keys(bidderIds).length > 0) { - bid.userId = bidderIds; - } if (bidderEids.length > 0) { bid.userIdAsEids = bidderEids; } diff --git a/test/spec/modules/euidIdSystem_spec.js b/test/spec/modules/euidIdSystem_spec.js index 29279158016..9074638a495 100644 --- a/test/spec/modules/euidIdSystem_spec.js +++ b/test/spec/modules/euidIdSystem_spec.js @@ -38,9 +38,15 @@ const cstgApiUrl = 'https://prod.euid.eu/v2/token/client-generate'; const headers = { 'Content-Type': 'application/json' }; const makeSuccessResponseBody = (token) => btoa(JSON.stringify({ status: 'success', body: { ...apiHelpers.makeTokenResponse(initialToken), advertising_token: token } })); const makeOptoutResponseBody = (token) => btoa(JSON.stringify({ status: 'optout', body: { ...apiHelpers.makeTokenResponse(initialToken), advertising_token: token } })); -const expectToken = (bid, token) => expect(bid?.userId ?? {}).to.deep.include(makeEuidIdentityContainer(token)); -const expectOptout = (bid, token) => expect(bid?.userId ?? {}).to.deep.include(makeEuidOptoutContainer(token)); -const expectNoIdentity = (bid) => expect(bid).to.not.haveOwnProperty('userId'); +function findEuid(bid) { + return (bid?.userIdAsEids ?? []).find(e => e.source === 'euid.eu'); +} +const expectToken = (bid, token) => { + const eid = findEuid(bid); + expect(eid && eid.uids[0].id).to.equal(token); +}; +const expectOptout = (bid) => expect(findEuid(bid)).to.be.undefined; +const expectNoIdentity = (bid) => expect(findEuid(bid)).to.be.undefined; describe('EUID module', function() { let suiteSandbox, restoreSubtleToUndefined = false; diff --git a/test/spec/modules/id5IdSystem_spec.js b/test/spec/modules/id5IdSystem_spec.js index 538fccc58b5..4d093e9b831 100644 --- a/test/spec/modules/id5IdSystem_spec.js +++ b/test/spec/modules/id5IdSystem_spec.js @@ -960,8 +960,6 @@ describe('ID5 ID System', function () { startAuctionHook(wrapAsyncExpects(done, () => { adUnits.forEach(unit => { unit.bids.forEach(bid => { - expect(bid).to.have.deep.nested.property(`userId.${ID5_EIDS_NAME}`); - expect(bid.userId.id5id.uid).is.equal(ID5_STORED_ID); expect(bid.userIdAsEids[0]).is.eql({ source: ID5_SOURCE, uids: [{ @@ -988,8 +986,6 @@ describe('ID5 ID System', function () { startAuctionHook(function () { adUnits.forEach(unit => { unit.bids.forEach(bid => { - expect(bid).to.have.deep.nested.property(`userId.euid`); - expect(bid.userId.euid.uid).is.equal(EUID_STORED_ID); expect(bid.userIdAsEids[0].uids[0].id).is.equal(ID5_STORED_ID); expect(bid.userIdAsEids[1]).is.eql({ source: EUID_SOURCE, @@ -1017,8 +1013,6 @@ describe('ID5 ID System', function () { startAuctionHook(wrapAsyncExpects(done, function () { adUnits.forEach(unit => { unit.bids.forEach(bid => { - expect(bid).to.have.deep.nested.property(`userId.trueLinkId`); - expect(bid.userId.trueLinkId.uid).is.equal(TRUE_LINK_STORED_ID); expect(bid.userIdAsEids[1]).is.eql({ source: TRUE_LINK_SOURCE, uids: [{ @@ -1074,11 +1068,6 @@ describe('ID5 ID System', function () { startAuctionHook(wrapAsyncExpects(done, () => { adUnits.forEach(unit => { unit.bids.forEach(bid => { - expect(bid).to.have.deep.nested.property(`userId.${ID5_EIDS_NAME}`); - expect(bid.userId.id5id).is.eql({ - uid: id5IdEidUid.id, - ext: id5IdEidUid.ext - }); expect(bid.userIdAsEids[0]).is.eql({ source: IDS_ID5ID.eid.source, uids: [{ @@ -1108,11 +1097,6 @@ describe('ID5 ID System', function () { startAuctionHook(wrapAsyncExpects(done, () => { adUnits.forEach(unit => { unit.bids.forEach(bid => { - expect(bid).to.have.deep.nested.property(`userId.euid`); - expect(bid.userId.euid).is.eql({ - uid: IDS_EUID.eid.uids[0].id, - ext: IDS_EUID.eid.uids[0].ext - }); expect(bid.userIdAsEids[0]).is.eql(IDS_ID5ID.eid); expect(bid.userIdAsEids[1]).is.eql(IDS_EUID.eid); }); @@ -1137,8 +1121,6 @@ describe('ID5 ID System', function () { startAuctionHook(wrapAsyncExpects(done, function () { adUnits.forEach(unit => { unit.bids.forEach(bid => { - expect(bid).to.have.deep.nested.property(`userId.trueLinkId`); - expect(bid.userId.trueLinkId.uid).is.eql(IDS_TRUE_LINK_ID.eid.uids[0].id); expect(bid.userIdAsEids[1]).is.eql(IDS_TRUE_LINK_ID.eid); }); }); @@ -1178,8 +1160,6 @@ describe('ID5 ID System', function () { startAuctionHook(wrapAsyncExpects(done, function () { adUnits.forEach(unit => { unit.bids.forEach(bid => { - expect(bid).to.have.deep.nested.property(`userId.otherId`); - expect(bid.userId.otherId.uid).is.eql('other-id-value'); expect(bid.userIdAsEids[1]).is.eql({ source: 'other-id.com', inserter: 'id5-sync.com', diff --git a/test/spec/modules/idxIdSystem_spec.js b/test/spec/modules/idxIdSystem_spec.js index 2ab2e303b28..e47c606776d 100644 --- a/test/spec/modules/idxIdSystem_spec.js +++ b/test/spec/modules/idxIdSystem_spec.js @@ -111,8 +111,6 @@ describe('IDx ID System', () => { startAuctionHook(() => { adUnits.forEach(unit => { unit.bids.forEach(bid => { - expect(bid).to.have.deep.nested.property('userId.idx'); - expect(bid.userId.idx).to.equal(IDX_DUMMY_VALUE); const idxIdAsEid = bid.userIdAsEids.find(e => e.source == 'idx.lat'); expect(idxIdAsEid).to.deep.equal({ source: 'idx.lat', diff --git a/test/spec/modules/lmpIdSystem_spec.js b/test/spec/modules/lmpIdSystem_spec.js index e7bec2cd32d..4758d943f6c 100644 --- a/test/spec/modules/lmpIdSystem_spec.js +++ b/test/spec/modules/lmpIdSystem_spec.js @@ -114,8 +114,6 @@ describe('LMPID System', () => { startAuctionHook(() => { adUnits.forEach(unit => { unit.bids.forEach(bid => { - expect(bid).to.have.deep.nested.property('userId.lmpid'); - expect(bid.userId.lmpid).to.equal('stored-lmpid'); const lmpidAsEid = bid.userIdAsEids.find(e => e.source == 'loblawmedia.ca'); expect(lmpidAsEid).to.deep.equal({ source: 'loblawmedia.ca', diff --git a/test/spec/modules/uid2IdSystem_spec.js b/test/spec/modules/uid2IdSystem_spec.js index 3d659485a2e..5c42218fb66 100644 --- a/test/spec/modules/uid2IdSystem_spec.js +++ b/test/spec/modules/uid2IdSystem_spec.js @@ -47,13 +47,26 @@ const getFromAppropriateStorage = () => { else return coreStorage.getCookie(moduleCookieName); } -const expectToken = (bid, token) => expect(bid?.userId ?? {}).to.deep.include(makeUid2IdentityContainer(token)); -const expectLegacyToken = (bid) => expect(bid.userId).to.deep.include(makeUid2IdentityContainer(legacyToken)); -const expectNoIdentity = (bid) => expect(bid).to.not.haveOwnProperty('userId'); -const expectOptout = (bid, token) => expect(bid?.userId ?? {}).to.deep.include(makeUid2OptoutContainer(token)); +const UID2_SOURCE = 'uidapi.com'; +function findUid2(bid) { + return (bid?.userIdAsEids ?? []).find(e => e.source === UID2_SOURCE); +} +const expectToken = (bid, token) => { + const eid = findUid2(bid); + expect(eid && eid.uids[0].id).to.equal(token); +}; +const expectLegacyToken = (bid) => { + const eid = findUid2(bid); + expect(eid && eid.uids[0].id).to.equal(legacyToken); +}; +const expectNoIdentity = (bid) => expect(findUid2(bid)).to.be.undefined; +const expectOptout = (bid) => expect(findUid2(bid)).to.be.undefined; const expectGlobalToHaveToken = (token) => expect(getGlobal().getUserIds()).to.deep.include(makeUid2IdentityContainer(token)); const expectGlobalToHaveNoUid2 = () => expect(getGlobal().getUserIds()).to.not.haveOwnProperty('uid2'); -const expectNoLegacyToken = (bid) => expect(bid.userId).to.not.deep.include(makeUid2IdentityContainer(legacyToken)); +const expectNoLegacyToken = (bid) => { + const eid = findUid2(bid); + if (eid) expect(eid.uids[0].id).to.not.equal(legacyToken); +}; const expectModuleStorageEmptyOrMissing = () => expect(getFromAppropriateStorage()).to.be.null; const expectModuleStorageToContain = (originalAdvertisingToken, latestAdvertisingToken, originalIdentity) => { const cookie = JSON.parse(getFromAppropriateStorage()); @@ -240,7 +253,9 @@ describe(`UID2 module`, function () { config.setConfig(makePrebidConfig(legacyConfigParams)); const bid2 = await runAuction(); - expect(bid.userId.uid2.id).to.equal(bid2.userId.uid2.id); + const first = findUid2(bid); + const second = findUid2(bid2); + expect(first && second && first.uids[0].id).to.equal(second.uids[0].id); }); }); diff --git a/test/spec/modules/userId_spec.js b/test/spec/modules/userId_spec.js index 0567f30f7b0..aea371d069c 100644 --- a/test/spec/modules/userId_spec.js +++ b/test/spec/modules/userId_spec.js @@ -301,8 +301,6 @@ describe('User ID', function () { innerAdUnits1.forEach(unit => { unit.bids.forEach(bid => { - expect(bid).to.have.deep.nested.property('userId.pubcid'); - expect(bid.userId.pubcid).to.equal(pubcid); expect(bid.userIdAsEids[0]).to.deep.equal({ source: 'pubcid.org', uids: [{id: pubcid, atype: 1}] @@ -338,8 +336,6 @@ describe('User ID', function () { innerAdUnits1.forEach((unit) => { unit.bids.forEach((bid) => { - expect(bid).to.have.deep.nested.property('userId.pubcid'); - expect(bid.userId.pubcid).to.equal(pubcid1); expect(bid.userIdAsEids[0]).to.deep.equal({ source: 'pubcid.org', uids: [{id: pubcid1, atype: 1}] @@ -358,8 +354,6 @@ describe('User ID', function () { innerAdUnits2.forEach((unit) => { unit.bids.forEach((bid) => { - expect(bid).to.have.deep.nested.property('userId.pubcid'); - expect(bid.userId.pubcid).to.equal(pubcid2); expect(bid.userIdAsEids[0]).to.deep.equal({ source: 'pubcid.org', uids: [{id: pubcid2, atype: 1}] @@ -385,8 +379,6 @@ describe('User ID', function () { }, {adUnits}).then(() => { innerAdUnits.forEach((unit) => { unit.bids.forEach((bid) => { - expect(bid).to.have.deep.nested.property('userId.pubcid'); - expect(bid.userId.pubcid).to.equal('altpubcid200000'); expect(bid.userIdAsEids[0]).to.deep.equal({ source: 'pubcid.org', uids: [{id: 'altpubcid200000', atype: 1}] @@ -411,8 +403,6 @@ describe('User ID', function () { }, {adUnits}).then(() => { innerAdUnits.forEach((unit) => { unit.bids.forEach((bid) => { - expect(bid).to.have.deep.nested.property('userId.pubcid'); - expect(bid.userId.pubcid).to.equal('altpubcid200000'); expect(bid.userIdAsEids[0]).to.deep.equal({ source: 'pubcid.org', uids: [{id: 'altpubcid200000', atype: 1}] @@ -437,7 +427,6 @@ describe('User ID', function () { }, {adUnits}).then(() => { innerAdUnits.forEach((unit) => { unit.bids.forEach((bid) => { - expect(bid).to.not.have.deep.nested.property('userId.pubcid'); expect(bid).to.not.have.deep.nested.property('userIdAsEids'); }); }); @@ -1743,8 +1732,6 @@ describe('User ID', function () { // check ids were copied to bids adUnits.forEach(unit => { unit.bids.forEach(bid => { - expect(bid).to.have.deep.nested.property('userId.mid'); - expect(bid.userId.mid).to.equal('1234'); expect(bid.userIdAsEids).to.not.exist;// "mid" is an un-known submodule for USER_IDS_CONFIG in eids.js }); }); @@ -1889,8 +1876,6 @@ describe('User ID', function () { startAuctionHook(function () { adUnits.forEach(unit => { unit.bids.forEach(bid => { - expect(bid).to.have.deep.nested.property('userId.pubcid'); - expect(bid.userId.pubcid).to.equal('testpubcid'); expect(bid.userIdAsEids[0]).to.deep.equal({ source: 'pubcid.org', uids: [{id: 'testpubcid', atype: 1}] @@ -1914,8 +1899,6 @@ describe('User ID', function () { startAuctionHook(function () { adUnits.forEach(unit => { unit.bids.forEach(bid => { - expect(bid).to.have.deep.nested.property('userId.pubcid'); - expect(bid.userId.pubcid).to.equal('testpubcid'); expect(bid.userIdAsEids[0]).to.deep.equal({ source: 'pubcid.org', uids: [{id: 'testpubcid', atype: 1}] @@ -1941,8 +1924,6 @@ describe('User ID', function () { startAuctionHook(function () { adUnits.forEach(unit => { unit.bids.forEach(bid => { - expect(bid).to.have.deep.nested.property('userId.pubcid'); - expect(bid.userId.pubcid).to.equal('testpubcid'); expect(bid.userIdAsEids[0]).to.deep.equal({ source: 'pubcid.org', uids: [{id: 'testpubcid', atype: 1}] @@ -1969,8 +1950,6 @@ describe('User ID', function () { startAuctionHook(function () { adUnits.forEach(unit => { unit.bids.forEach(bid => { - expect(bid).to.have.deep.nested.property('userId.pubcid'); - expect(bid.userId.pubcid).to.equal('testpubcid'); expect(bid.userIdAsEids[0]).to.deep.equal({ source: 'pubcid.org', uids: [{id: 'testpubcid', atype: 1}] @@ -1995,8 +1974,6 @@ describe('User ID', function () { startAuctionHook(function () { adUnits.forEach(unit => { unit.bids.forEach(bid => { - expect(bid).to.have.deep.nested.property('userId.pubcid'); - expect(bid.userId.pubcid).to.equal('testpubcid'); expect(bid.userIdAsEids[0]).to.deep.equal({ source: 'pubcid.org', uids: [{id: 'testpubcid', atype: 1}] @@ -2018,8 +1995,6 @@ describe('User ID', function () { startAuctionHook(function () { adUnits.forEach(unit => { unit.bids.forEach(bid => { - expect(bid).to.have.deep.nested.property('userId.pubcidvalue'); - expect(bid.userId.pubcidvalue).to.equal('testpubcidvalue'); expect(bid.userIdAsEids).to.not.exist; // "pubcidvalue" is an un-known submodule for USER_IDS_CONFIG in eids.js }); }); @@ -2073,33 +2048,6 @@ describe('User ID', function () { startAuctionHook(function () { adUnits.forEach(unit => { unit.bids.forEach(bid => { - expect(bid).to.have.deep.nested.property('userId.pubProvidedId'); - expect(bid.userId.pubProvidedId).to.deep.equal([{ - source: 'example.com', - uids: [{ - id: 'value read from cookie or local storage', - ext: { - stype: 'ppuid' - } - }] - }, { - source: 'id-partner.com', - uids: [{ - id: 'value read from cookie or local storage', - ext: { - stype: 'dmp' - } - }] - }, { - source: 'provider.com', - uids: [{ - id: 'value read from cookie or local storage', - ext: { - stype: 'sha256email' - } - }] - }]); - expect(bid.userIdAsEids[0]).to.deep.equal({ source: 'example.com', uids: [{ @@ -2158,11 +2106,8 @@ describe('User ID', function () { startAuctionHook(function () { adUnits.forEach(unit => { unit.bids.forEach(bid => { - // check PubCommonId id data was copied to bid - expect(bid).to.have.deep.nested.property('userId.pubcid'); - // check MockId data was copied to bid - expect(bid).to.have.deep.nested.property('userId.mid'); - expect(bid.userId.mid).to.equal('1234'); + // check id data was copied to bid + expect(bid).to.have.property('userIdAsEids'); }); }); coreStorage.setCookie('pubcid', '', EXPIRED_COOKIE_DATE); @@ -2195,7 +2140,7 @@ describe('User ID', function () { isAllowed.restore(); }); - it('should check for enrichEids activity permissions', (done) => { + it('should check for enrichEids activity permissions', () => { isAllowed.callsFake((activity, params) => { return !(activity === ACTIVITY_ENRICH_EIDS && params[ACTIVITY_PARAM_COMPONENT_TYPE] === MODULE_TYPE_UID && @@ -2210,10 +2155,10 @@ describe('User ID', function () { })) } }); - startAuctionHook((req) => { - const activeIds = req.adUnits.flatMap(au => au.bids).flatMap(bid => Object.keys(bid.userId)); - expect(Array.from(new Set(activeIds))).to.have.members([MOCK_IDS[1]]); - done(); + return expectImmediateBidHook((req) => { + const activeSources = req.adUnits.flatMap(au => au.bids) + .flatMap(bid => bid.userIdAsEids ? bid.userIdAsEids.map(eid => eid.source) : []); + expect(Array.from(new Set(activeSources))).to.have.members([MOCK_IDS[1]]); }, {adUnits}) }); }) @@ -3204,8 +3149,8 @@ describe('User ID', function () { }; addIdData({ adUnits, ortb2Fragments }); - adUnits[0].bids.forEach(({userId}) => { - const userIdModules = Object.keys(userId); + adUnits[0].bids.forEach(({userIdAsEids}) => { + const userIdModules = (userIdAsEids || []).map(eid => eid.source.replace('.com', '')); expect(userIdModules).to.include(ALLOWED_MODULE); expect(userIdModules).to.not.include(UNALLOWED_MODULE); }); diff --git a/test/spec/modules/zeotapIdPlusIdSystem_spec.js b/test/spec/modules/zeotapIdPlusIdSystem_spec.js index a9d56f4045a..448abc29fc4 100644 --- a/test/spec/modules/zeotapIdPlusIdSystem_spec.js +++ b/test/spec/modules/zeotapIdPlusIdSystem_spec.js @@ -178,8 +178,6 @@ describe('Zeotap ID System', function() { startAuctionHook(function() { adUnits.forEach(unit => { unit.bids.forEach(bid => { - expect(bid).to.have.deep.nested.property('userId.IDP'); - expect(bid.userId.IDP).to.equal(ZEOTAP_COOKIE); const zeotapIdAsEid = bid.userIdAsEids.find(e => e.source == 'zeotap.com'); expect(zeotapIdAsEid).to.deep.equal({ source: 'zeotap.com', From 0c9f89f9065cac161ac8b3f7774a5edef5574c7f Mon Sep 17 00:00:00 2001 From: Patrick McCann Date: Thu, 26 Jun 2025 11:04:25 -0400 Subject: [PATCH 86/92] Prebid 10: fill out gvls (#13428) * adapter: address feedback * Update braveBidAdapter.js Undo brave * Update underdogmediaBidAdapter.js --- modules/addefendBidAdapter.js | 2 ++ modules/admixerBidAdapter.js | 2 ++ modules/adnowBidAdapter.js | 2 ++ modules/adnuntiusAnalyticsAdapter.js | 1 + modules/adnuntiusRtdProvider.js | 1 + modules/adponeBidAdapter.js | 2 ++ modules/appierBidAdapter.js | 2 ++ modules/appushBidAdapter.js | 2 ++ modules/axisBidAdapter.js | 2 ++ modules/beachfrontBidAdapter.js | 2 ++ modules/betweenBidAdapter.js | 2 ++ modules/boldwinBidAdapter.js | 2 ++ modules/deltaprojectsBidAdapter.js | 2 ++ modules/docereeBidAdapter.js | 2 ++ modules/edge226BidAdapter.js | 2 ++ modules/gamoshiBidAdapter.js | 2 ++ modules/hybridBidAdapter.js | 2 ++ modules/innityBidAdapter.js | 2 ++ modules/luponmediaBidAdapter.js | 2 ++ modules/madvertiseBidAdapter.js | 3 +++ modules/marsmediaBidAdapter.js | 1 + modules/mediaforceBidAdapter.js | 2 ++ modules/nextrollBidAdapter.js | 2 ++ modules/optoutBidAdapter.js | 2 ++ modules/quantcastBidAdapter.js | 2 +- modules/readpeakBidAdapter.js | 2 ++ modules/relayBidAdapter.js | 2 ++ modules/resetdigitalBidAdapter.js | 2 ++ modules/revcontentBidAdapter.js | 2 ++ modules/underdogmediaBidAdapter.js | 1 + modules/vlybyBidAdapter.js | 2 ++ modules/yieldliftBidAdapter.js | 2 ++ modules/zeta_globalBidAdapter.js | 2 ++ 33 files changed, 62 insertions(+), 1 deletion(-) diff --git a/modules/addefendBidAdapter.js b/modules/addefendBidAdapter.js index a646400b083..dba5b9c152b 100644 --- a/modules/addefendBidAdapter.js +++ b/modules/addefendBidAdapter.js @@ -1,9 +1,11 @@ import {registerBidder} from '../src/adapters/bidderFactory.js'; const BIDDER_CODE = 'addefend'; +const GVLID = 539; export const spec = { code: BIDDER_CODE, + gvlid: GVLID, hostname: 'https://addefend-platform.com', getHostname() { diff --git a/modules/admixerBidAdapter.js b/modules/admixerBidAdapter.js index 2b21f259a7b..92631a0fca6 100644 --- a/modules/admixerBidAdapter.js +++ b/modules/admixerBidAdapter.js @@ -5,6 +5,7 @@ import {BANNER, VIDEO, NATIVE} from '../src/mediaTypes.js'; import {convertOrtbRequestToProprietaryNative} from '../src/native.js'; const BIDDER_CODE = 'admixer'; +const GVLID = 511; const ENDPOINT_URL = 'https://inv-nets.admixer.net/prebid.1.2.aspx'; const ALIASES = [ {code: 'go2net', endpoint: 'https://ads.go2net.com.ua/prebid.1.2.aspx'}, @@ -16,6 +17,7 @@ const ALIASES = [ ]; export const spec = { code: BIDDER_CODE, + gvlid: GVLID, aliases: ALIASES.map(val => isStr(val) ? val : val.code), supportedMediaTypes: [BANNER, VIDEO, NATIVE], /** diff --git a/modules/adnowBidAdapter.js b/modules/adnowBidAdapter.js index 9acbd3153c8..579afa89be8 100644 --- a/modules/adnowBidAdapter.js +++ b/modules/adnowBidAdapter.js @@ -5,6 +5,7 @@ import {deepAccess, parseQueryStringParameters, parseSizesInput} from '../src/ut import { convertOrtbRequestToProprietaryNative } from '../src/native.js'; const BIDDER_CODE = 'adnow'; +const GVLID = 1210; const ENDPOINT = 'https://n.nnowa.com/a'; /** @@ -28,6 +29,7 @@ const ENDPOINT = 'https://n.nnowa.com/a'; /** @type {BidderSpec} */ export const spec = { code: BIDDER_CODE, + gvlid: GVLID, supportedMediaTypes: [ NATIVE, BANNER ], /** diff --git a/modules/adnuntiusAnalyticsAdapter.js b/modules/adnuntiusAnalyticsAdapter.js index eb38782986e..c5913e94ad0 100644 --- a/modules/adnuntiusAnalyticsAdapter.js +++ b/modules/adnuntiusAnalyticsAdapter.js @@ -401,6 +401,7 @@ function getBidAdUnits() { adapterManager.registerAnalyticsAdapter({ adapter: adnAnalyticsAdapter, + gvlid: 855, code: 'adnuntius' }); diff --git a/modules/adnuntiusRtdProvider.js b/modules/adnuntiusRtdProvider.js index d82bb72dac7..e9538414e51 100644 --- a/modules/adnuntiusRtdProvider.js +++ b/modules/adnuntiusRtdProvider.js @@ -87,6 +87,7 @@ function alterBidRequests(reqBidsConfigObj, callback, config, userConsent) { /** @type {RtdSubmodule} */ export const adnuntiusSubmodule = { name: 'adnuntius', + gvlid: GVLID, init: init, getBidRequestData: alterBidRequests, setGlobalConfig: setGlobalConfig, diff --git a/modules/adponeBidAdapter.js b/modules/adponeBidAdapter.js index 49c0365fc87..4e457b86f84 100644 --- a/modules/adponeBidAdapter.js +++ b/modules/adponeBidAdapter.js @@ -7,8 +7,10 @@ const ADPONE_ENDPOINT = 'https://rtb.adpone.com/bid-request'; const ADPONE_REQUEST_METHOD = 'POST'; const ADPONE_CURRENCY = 'EUR'; +const GVLID = 799; export const spec = { code: ADPONE_CODE, + gvlid: GVLID, supportedMediaTypes: [BANNER], isBidRequestValid: bid => { diff --git a/modules/appierBidAdapter.js b/modules/appierBidAdapter.js index cf89aeefffa..d26ae4d7162 100644 --- a/modules/appierBidAdapter.js +++ b/modules/appierBidAdapter.js @@ -8,6 +8,7 @@ import { config } from '../src/config.js'; */ export const ADAPTER_VERSION = '1.0.0'; +const GVLID = 728; const SUPPORTED_AD_TYPES = [BANNER]; // we have different servers for different regions / farms @@ -21,6 +22,7 @@ const BIDDER_API_ENDPOINT = '/v1/prebid/bid'; export const spec = { code: 'appier', + gvlid: GVLID, aliases: ['appierBR', 'appierExt', 'appierGM'], supportedMediaTypes: SUPPORTED_AD_TYPES, diff --git a/modules/appushBidAdapter.js b/modules/appushBidAdapter.js index f06dc0f2c13..c83682647ec 100644 --- a/modules/appushBidAdapter.js +++ b/modules/appushBidAdapter.js @@ -6,6 +6,7 @@ import { BANNER, NATIVE, VIDEO } from '../src/mediaTypes.js'; import { config } from '../src/config.js'; const BIDDER_CODE = 'appush'; +const GVLID = 879; const AD_URL = 'https://hb.appush.com/pbjs'; function isBidResponseValid(bid) { @@ -94,6 +95,7 @@ function getBidFloor(bid) { export const spec = { code: BIDDER_CODE, + gvlid: GVLID, supportedMediaTypes: [BANNER, VIDEO, NATIVE], isBidRequestValid: (bid = {}) => { diff --git a/modules/axisBidAdapter.js b/modules/axisBidAdapter.js index c2ad40b2b94..162910076a4 100644 --- a/modules/axisBidAdapter.js +++ b/modules/axisBidAdapter.js @@ -10,6 +10,7 @@ import { } from '../libraries/teqblazeUtils/bidderUtils.js'; const BIDDER_CODE = 'axis'; +const GVLID = 1197; const AD_URL = 'https://prebid.axis-marketplace.com/pbjs'; const SYNC_URL = 'https://cs.axis-marketplace.com'; @@ -41,6 +42,7 @@ const buildRequests = (validBidRequests = [], bidderRequest = {}) => { export const spec = { code: BIDDER_CODE, + gvlid: GVLID, supportedMediaTypes: [BANNER, VIDEO, NATIVE], isBidRequestValid: isBidRequestValid(['integration', 'token'], 'every'), diff --git a/modules/beachfrontBidAdapter.js b/modules/beachfrontBidAdapter.js index 403a8d1129c..7cbd5f8a65a 100644 --- a/modules/beachfrontBidAdapter.js +++ b/modules/beachfrontBidAdapter.js @@ -13,6 +13,7 @@ import {BANNER, VIDEO} from '../src/mediaTypes.js'; import { getFirstSize, getOsVersion, getVideoSizes, getBannerSizes, isConnectedTV, getDoNotTrack, isMobile, isBannerBid, isVideoBid, getBannerBidFloor, getVideoBidFloor, getVideoTargetingParams, getTopWindowLocation } from '../libraries/advangUtils/index.js'; const ADAPTER_VERSION = '1.21'; +const GVLID = 335; const ADAPTER_NAME = 'BFIO_PREBID'; const OUTSTREAM = 'outstream'; const CURRENCY = 'USD'; @@ -37,6 +38,7 @@ let appId = ''; export const spec = { code: 'beachfront', + gvlid: GVLID, supportedMediaTypes: [ VIDEO, BANNER ], isBidRequestValid(bid) { diff --git a/modules/betweenBidAdapter.js b/modules/betweenBidAdapter.js index 782ecf42984..24d3ad22480 100644 --- a/modules/betweenBidAdapter.js +++ b/modules/betweenBidAdapter.js @@ -13,11 +13,13 @@ import {getAdUnitSizes} from '../libraries/sizeUtils/sizeUtils.js'; */ const BIDDER_CODE = 'between'; +const GVLID = 724; let ENDPOINT = 'https://ads.betweendigital.com/adjson?t=prebid'; const CODE_TYPES = ['inpage', 'preroll', 'midroll', 'postroll']; export const spec = { code: BIDDER_CODE, + gvlid: GVLID, aliases: ['btw'], supportedMediaTypes: ['banner', 'video'], /** diff --git a/modules/boldwinBidAdapter.js b/modules/boldwinBidAdapter.js index 1cf3bf889b7..bbece2cacd5 100644 --- a/modules/boldwinBidAdapter.js +++ b/modules/boldwinBidAdapter.js @@ -9,6 +9,7 @@ import { } from '../libraries/teqblazeUtils/bidderUtils.js'; const BIDDER_CODE = 'boldwin'; +const GVLID = 1151; const AD_URL = 'https://ssp.videowalldirect.com/pbjs'; const SYNC_URL = 'https://sync.videowalldirect.com'; @@ -27,6 +28,7 @@ const buildRequests = (validBidRequests = [], bidderRequest = {}) => { export const spec = { code: BIDDER_CODE, + gvlid: GVLID, supportedMediaTypes: [BANNER, VIDEO, NATIVE], isBidRequestValid: isBidRequestValid(), diff --git a/modules/deltaprojectsBidAdapter.js b/modules/deltaprojectsBidAdapter.js index 151544e32e5..02d0e21cf9d 100644 --- a/modules/deltaprojectsBidAdapter.js +++ b/modules/deltaprojectsBidAdapter.js @@ -14,6 +14,7 @@ import { } from '../src/utils.js'; export const BIDDER_CODE = 'deltaprojects'; +const GVLID = 209; export const BIDDER_ENDPOINT_URL = 'https://d5p.de17a.com/dogfight/prebid'; export const USERSYNC_URL = 'https://userservice.de17a.com/getuid/prebid'; @@ -236,6 +237,7 @@ export function getBidFloor(bid, mediaType, size, currency) { /** -- Register -- */ export const spec = { code: BIDDER_CODE, + gvlid: GVLID, supportedMediaTypes: [BANNER], isBidRequestValid, buildRequests, diff --git a/modules/docereeBidAdapter.js b/modules/docereeBidAdapter.js index 2731e1ff397..897129ff3a5 100644 --- a/modules/docereeBidAdapter.js +++ b/modules/docereeBidAdapter.js @@ -4,11 +4,13 @@ import { config } from '../src/config.js'; import { BANNER } from '../src/mediaTypes.js'; import {tryAppendQueryString} from '../libraries/urlUtils/urlUtils.js'; const BIDDER_CODE = 'doceree'; +const GVLID = 1063; const END_POINT = 'https://bidder.doceree.com' const TRACKING_END_POINT = 'https://tracking.doceree.com' export const spec = { code: BIDDER_CODE, + gvlid: GVLID, url: '', supportedMediaTypes: [ BANNER ], diff --git a/modules/edge226BidAdapter.js b/modules/edge226BidAdapter.js index ae235f02f64..645178012cb 100644 --- a/modules/edge226BidAdapter.js +++ b/modules/edge226BidAdapter.js @@ -3,10 +3,12 @@ import { BANNER, NATIVE, VIDEO } from '../src/mediaTypes.js'; import { isBidRequestValid, buildRequests, interpretResponse } from '../libraries/teqblazeUtils/bidderUtils.js'; const BIDDER_CODE = 'edge226'; +const GVLID = 1202; const AD_URL = 'https://ssp.dauup.com/pbjs'; export const spec = { code: BIDDER_CODE, + gvlid: GVLID, supportedMediaTypes: [BANNER, VIDEO, NATIVE], isBidRequestValid: isBidRequestValid(), diff --git a/modules/gamoshiBidAdapter.js b/modules/gamoshiBidAdapter.js index 9f761fbd2a5..8a6d6cdca6e 100644 --- a/modules/gamoshiBidAdapter.js +++ b/modules/gamoshiBidAdapter.js @@ -19,6 +19,7 @@ const ENDPOINTS = { 'gamoshi': 'https://rtb.gamoshi.io', 'cleanmedianet': 'https://bidder.cleanmediaads.com' }; +const GVLID = 644; const DEFAULT_TTL = 360; @@ -66,6 +67,7 @@ export const helper = { export const spec = { code: 'gamoshi', + gvlid: GVLID, aliases: ['gambid', 'cleanmedianet'], supportedMediaTypes: ['banner', 'video'], diff --git a/modules/hybridBidAdapter.js b/modules/hybridBidAdapter.js index bfee25859f3..9cc66566a9f 100644 --- a/modules/hybridBidAdapter.js +++ b/modules/hybridBidAdapter.js @@ -11,6 +11,7 @@ import {createRenderer, getMediaTypeFromBid, hasVideoMandatoryParams} from '../l */ const BIDDER_CODE = 'hybrid'; +const GVLID = 206; const DSP_ENDPOINT = 'https://hbe198.hybrid.ai/prebidhb'; const TRAFFIC_TYPE_WEB = 1; const PLACEMENT_TYPE_BANNER = 1; @@ -130,6 +131,7 @@ function wrapAd(bid, bidData) { export const spec = { code: BIDDER_CODE, + gvlid: GVLID, supportedMediaTypes: [BANNER, VIDEO], placementTypes: placementTypes, diff --git a/modules/innityBidAdapter.js b/modules/innityBidAdapter.js index 9bd0538ff0a..da5003cd46e 100644 --- a/modules/innityBidAdapter.js +++ b/modules/innityBidAdapter.js @@ -2,10 +2,12 @@ import { parseSizesInput, timestamp } from '../src/utils.js'; import { registerBidder } from '../src/adapters/bidderFactory.js'; const BIDDER_CODE = 'innity'; +const GVLID = 535; const ENDPOINT = 'https://as.innity.com/synd/'; export const spec = { code: BIDDER_CODE, + gvlid: GVLID, isBidRequestValid: function(bid) { return !!(bid.params && bid.params.pub && bid.params.zone); }, diff --git a/modules/luponmediaBidAdapter.js b/modules/luponmediaBidAdapter.js index 9d06d0b90c1..8d3ec123b42 100755 --- a/modules/luponmediaBidAdapter.js +++ b/modules/luponmediaBidAdapter.js @@ -5,6 +5,7 @@ import {ortbConverter} from '../libraries/ortbConverter/converter.js'; import {config} from '../src/config.js'; const BIDDER_CODE = 'luponmedia'; +const GVLID = 1132; const keyIdRegex = /^uid(?:@[\w-]+)?_.*$/; const buildServerUrl = (keyId) => { @@ -69,6 +70,7 @@ export const converter = ortbConverter({ }); export const spec = { code: BIDDER_CODE, + gvlid: GVLID, supportedMediaTypes: [BANNER], isBidRequestValid: function (bid) { return keyIdRegex.test(bid?.params?.keyId); diff --git a/modules/madvertiseBidAdapter.js b/modules/madvertiseBidAdapter.js index 9fc7ceb68aa..f2830a8ab9f 100644 --- a/modules/madvertiseBidAdapter.js +++ b/modules/madvertiseBidAdapter.js @@ -9,8 +9,11 @@ import {registerBidder} from '../src/adapters/bidderFactory.js'; // use protocol relative urls for http or https const MADVERTISE_ENDPOINT = 'https://mobile.mng-ads.com/'; +const GVLID = 153; + export const spec = { code: 'madvertise', + gvlid: GVLID, /** * @param {object} bid * @return boolean diff --git a/modules/marsmediaBidAdapter.js b/modules/marsmediaBidAdapter.js index c3b5102318a..8aab9c5c4a4 100644 --- a/modules/marsmediaBidAdapter.js +++ b/modules/marsmediaBidAdapter.js @@ -10,6 +10,7 @@ function MarsmediaAdapter() { this.aliases = ['mars']; this.supportedMediaTypes = [VIDEO, BANNER]; + this.gvlid = 776; let SUPPORTED_VIDEO_PROTOCOLS = [2, 3, 5, 6]; let SUPPORTED_VIDEO_MIMES = ['video/mp4']; let SUPPORTED_VIDEO_PLAYBACK_METHODS = [1, 2, 3, 4]; diff --git a/modules/mediaforceBidAdapter.js b/modules/mediaforceBidAdapter.js index 4bcc2de3bc1..ba7b8a8275e 100644 --- a/modules/mediaforceBidAdapter.js +++ b/modules/mediaforceBidAdapter.js @@ -23,6 +23,7 @@ import { convertOrtbRequestToProprietaryNative } from '../src/native.js'; */ const BIDDER_CODE = 'mediaforce'; +const GVLID = 671; const ENDPOINT_URL = 'https://rtb.mfadsrvr.com/header_bid'; const TEST_ENDPOINT_URL = 'https://rtb.mfadsrvr.com/header_bid?debug_key=abcdefghijklmnop'; const NATIVE_ID_MAP = {}; @@ -112,6 +113,7 @@ const DEFAULT_CURRENCY = 'USD' export const spec = { code: BIDDER_CODE, + gvlid: GVLID, supportedMediaTypes: SUPPORTED_MEDIA_TYPES, /** diff --git a/modules/nextrollBidAdapter.js b/modules/nextrollBidAdapter.js index 447a2253733..fea3fe48cf5 100644 --- a/modules/nextrollBidAdapter.js +++ b/modules/nextrollBidAdapter.js @@ -19,11 +19,13 @@ import { getOsVersion } from '../libraries/advangUtils/index.js'; * @typedef {import('../src/adapters/bidderFactory.js').validBidRequests} validBidRequests */ const BIDDER_CODE = 'nextroll'; +const GVLID = 130; const BIDDER_ENDPOINT = 'https://d.adroll.com/bid/prebid/'; const ADAPTER_VERSION = 5; export const spec = { code: BIDDER_CODE, + gvlid: GVLID, supportedMediaTypes: [BANNER, NATIVE], /** diff --git a/modules/optoutBidAdapter.js b/modules/optoutBidAdapter.js index f0010d54833..0fbfb0fd0e6 100644 --- a/modules/optoutBidAdapter.js +++ b/modules/optoutBidAdapter.js @@ -4,6 +4,7 @@ import {registerBidder} from '../src/adapters/bidderFactory.js'; import {hasPurpose1Consent} from '../src/utils/gdpr.js'; const BIDDER_CODE = 'optout'; +const GVLID = 227; function getDomain(bidderRequest) { return deepAccess(bidderRequest, 'refererInfo.canonicalUrl') || deepAccess(window, 'location.href'); @@ -22,6 +23,7 @@ function getCurrency() { export const spec = { code: BIDDER_CODE, + gvlid: GVLID, isBidRequestValid: function(bid) { return !!bid.params.publisher && !!bid.params.adslot; diff --git a/modules/quantcastBidAdapter.js b/modules/quantcastBidAdapter.js index 904f44f43f5..5f82e56ce02 100644 --- a/modules/quantcastBidAdapter.js +++ b/modules/quantcastBidAdapter.js @@ -108,7 +108,7 @@ let hasUserSynced = false; */ export const spec = { code: BIDDER_CODE, - GVLID: QUANTCAST_VENDOR_ID, + gvlid: QUANTCAST_VENDOR_ID, supportedMediaTypes: ['banner', 'video'], /** diff --git a/modules/readpeakBidAdapter.js b/modules/readpeakBidAdapter.js index da3153c0b68..ebc1426ad8d 100644 --- a/modules/readpeakBidAdapter.js +++ b/modules/readpeakBidAdapter.js @@ -16,9 +16,11 @@ const NATIVE_DEFAULTS = { }; const BIDDER_CODE = 'readpeak'; +const GVLID = 290; export const spec = { code: BIDDER_CODE, + gvlid: GVLID, supportedMediaTypes: [NATIVE, BANNER], diff --git a/modules/relayBidAdapter.js b/modules/relayBidAdapter.js index af145a5e163..eed075aff9f 100644 --- a/modules/relayBidAdapter.js +++ b/modules/relayBidAdapter.js @@ -5,6 +5,7 @@ import { BANNER, VIDEO, NATIVE } from '../src/mediaTypes.js'; import { ortbConverter } from '../libraries/ortbConverter/converter.js' const BIDDER_CODE = 'relay'; +const GVLID = 631; const METHOD = 'POST'; const ENDPOINT_URL = 'https://e.relay.bid/p/openrtb2'; @@ -81,6 +82,7 @@ function getUserSyncs(syncOptions, serverResponses, gdprConsent, uspConsent) { export const spec = { code: BIDDER_CODE, + gvlid: GVLID, isBidRequestValid, buildRequests, interpretResponse, diff --git a/modules/resetdigitalBidAdapter.js b/modules/resetdigitalBidAdapter.js index f9a3eb64347..77f8f44c53b 100644 --- a/modules/resetdigitalBidAdapter.js +++ b/modules/resetdigitalBidAdapter.js @@ -5,10 +5,12 @@ import { registerBidder } from '../src/adapters/bidderFactory.js'; import { BANNER } from '../src/mediaTypes.js'; const BIDDER_CODE = 'resetdigital'; +const GVLID = 1162; const CURRENCY = 'USD'; export const spec = { code: BIDDER_CODE, + gvlid: GVLID, supportedMediaTypes: ['banner', 'video'], isBidRequestValid: function (bid) { return !!(bid.params.pubId || bid.params.zoneId); diff --git a/modules/revcontentBidAdapter.js b/modules/revcontentBidAdapter.js index ce04e3aa822..33583c8e501 100644 --- a/modules/revcontentBidAdapter.js +++ b/modules/revcontentBidAdapter.js @@ -9,6 +9,7 @@ import {convertOrtbRequestToProprietaryNative} from '../src/native.js'; import {getAdUnitSizes} from '../libraries/sizeUtils/sizeUtils.js'; const BIDDER_CODE = 'revcontent'; +const GVLID = 203; const NATIVE_PARAMS = { title: { id: 0, @@ -29,6 +30,7 @@ const STYLE_EXTRA = '