From 38e1ade765cf96edcce063664b52b37fcc98c531 Mon Sep 17 00:00:00 2001 From: Josh Date: Mon, 22 Feb 2021 10:30:26 -0800 Subject: [PATCH 01/53] Initial nativoBidAdapter document creation (js, md and spec) --- modules/nativoBidAdapter.js | 194 +++++++++++++++++++++ modules/nativoBidAdapter.md | 54 ++++++ package-lock.json | 13 +- test/spec/modules/nativoBidAdapter_spec.js | 158 +++++++++++++++++ 4 files changed, 408 insertions(+), 11 deletions(-) create mode 100644 modules/nativoBidAdapter.js create mode 100644 modules/nativoBidAdapter.md create mode 100644 test/spec/modules/nativoBidAdapter_spec.js diff --git a/modules/nativoBidAdapter.js b/modules/nativoBidAdapter.js new file mode 100644 index 00000000000..bb78ff06d79 --- /dev/null +++ b/modules/nativoBidAdapter.js @@ -0,0 +1,194 @@ +import * as utils from '../src/utils.js' +import { registerBidder } from '../src/adapters/bidderFactory.js' +import { BANNER } from '../src/mediaTypes.js' +// import { config } from 'src/config' + +const BIDDER_CODE = 'nativo' +const BIDDER_ENDPOINT = 'http://www.testlocalbidrequest.com:3000/requestBid/' // test local endpoint +const USER_SYNC_URL_IFRAME = 'http://www.testlocalbidrequest.com:3000/' +const USER_SYNC_URL_IMAGE = 'http://www.testlocalbidrequest.com:3000/' + +const TIME_TO_LIVE = 360 + +const SUPPORTED_AD_TYPES = [BANNER] + +// Prebid adapter referrence doc: https://docs.prebid.org/dev-docs/bidder-adaptor.html + +export const spec = { + code: BIDDER_CODE, + aliases: ['ntv'], // short code + supportedMediaTypes: SUPPORTED_AD_TYPES, + + /** + * Determines whether or not the given bid request is valid. + * + * @param {BidRequest} bid The bid params to validate. + * @return boolean True if this is a valid bid, and false otherwise. + */ + isBidRequestValid: function (bid) { + return !!(bid.adUnitCode) + }, + + /** + * Called when the page asks Prebid.js for bids + * Make a server request from the list of BidRequests + * + * @param {Array} validBidRequests - An array of bidRequest objects, one for each AdUnit that your module is involved in. This array has been processed for special features like sizeConfig, so it’s the list that you should be looping through + * @param {Object} bidderRequest - The master bidRequest object. This object is useful because it carries a couple of bid parameters that are global to all the bids. + * @return ServerRequest Info describing the request to the server. + */ + buildRequests: function (validBidRequests, bidderRequest) { + const payload = { + selector: validBidRequests[0].adUnitCode, + id: validBidRequests[0].bidId + } + const payloadString = JSON.stringify(payload); + + let serverRequest = { + method: 'POST', + url: BIDDER_ENDPOINT, + data: payloadString + } + + return serverRequest + }, + + /** + * Will be called when the browser has received the response from your server. + * The function will parse the response and create a bidResponse object containing one or more bids. + * The adapter should indicate no valid bids by returning an empty array. + * + * @param {Object} response - Data returned from the bidding server request endpoint + * @param {Object} request - The request object used to call the server request endpoint + * @return {Array} An array of bids which were nested inside the server. + */ + interpretResponse: function (response, request) { + // If the bid response was empty, return [] + if (!response || !response.body || utils.isEmpty(response.body)) return [] + + try { + // Parse the response and return a bidResponses array + const body = response.body + // const headerValue = response.headers.get('some-response-header') + const bidResponses = [] + + let seatbids = body.seatbid + // Step through and grab pertinent data + let bidResponse + seatbids.forEach((seatbid) => { + seatbid.bid.forEach((bid) => { + bidResponse = { + requestId: body.id, + cpm: bid.price, + currency: body.cur, + width: bid.w, + height: bid.h, + creativeId: bid.crid, + dealId: bid.id, + netRevenue: true, + ttl: bid.ttl || TIME_TO_LIVE, + ad: bid.adm, + meta: { + advertiserDomains: bid.adomain, + // cat: bid.cat, + // impid: bid.impid, + // networkId: NETWORK_ID, + // networkName: NETWORK_NAME, + // agencyId: AGENCY_ID, + // agencyName: AGENCY_NAME, + // advertiserId: ADVERTISER_ID, + // advertiserName: ADVERTISER_NAME, + // advertiserDomains: [ARRAY_OF_ADVERTISER_DOMAINS], + // brandId: BRAND_ID, + // brandName: BRAND_NAME, + // primaryCatId: IAB_CATEGORY, + // secondaryCatIds: [ARRAY_OF_IAB_CATEGORIES], + // mediaType: MEDIA_TYPE + }, + } + + bidResponses.push(bidResponse) + }) + }) + + return bidResponses + } catch (error) { + // If there is an error, return [] + return [] + } + }, + + /** + * All user ID sync activity should be done using the getUserSyncs callback of the BaseAdapter model. + * Given an array of all the responses from the server, getUserSyncs is used to determine which user syncs should occur. + * The order of syncs in the serverResponses array matters. The most important ones should come first, since publishers may limit how many are dropped on their page. + * @param {Object} syncOptions - Which user syncs are allowed? + * @param {Array} serverResponses - Array of server's responses + * @param {Object} gdprConsent - GDPR consent data + * @param {Object} uspConsent - USP consent data + * @return {Array} The user syncs which should be dropped. + */ + getUserSyncs: function ( + syncOptions, + serverResponses, + gdprConsent, + uspConsent + ) { + // console.log('syncOptions', syncOptions) // eslint-disable-line no-console + // console.log('serverResponses', serverResponses) // eslint-disable-line no-console + // console.log('gdprConsent', gdprConsent) // eslint-disable-line no-console + // console.log('uspConsent', uspConsent) // eslint-disable-line no-console + + const syncs = [] + + let params = '' + + // GDPR + if (gdprConsent) { + params += '&gdpr=' + (gdprConsent.gdprApplies ? 1 : 0) + params += + '&gdpr_consent=' + encodeURIComponent(gdprConsent.consentString || '') + } + + // CCPA + if (uspConsent) { + params += '&us_privacy=' + encodeURIComponent(uspConsent) + } + + // TODO: We need to determine how we're including sync urls + if (syncOptions.iframeEnabled) { + syncs.push({ + type: 'iframe', + url: USER_SYNC_URL_IFRAME + params, + }) + } + if (syncOptions.pixelEnabled) { + syncs.push({ + type: 'image', + url: USER_SYNC_URL_IMAGE + params, + }) + } + + return syncs + }, + + /** + * Will be called when an adpater timed out for an auction. + * Adapter can fire a ajax or pixel call to register a timeout at thier end. + * @param {Object} timeoutData - Timeout specific data + */ + onTimeout: function (timeoutData) {}, + + /** + * Will be called when a bid from the adapter won the auction. + * @param {Object} bid - The bid that won the auction + */ + onBidWon: function (bid) {}, + + /** + * Will be called when the adserver targeting has been set for a bid from the adapter. + * @param {Object} bidder - The bid of which the targeting has been set + */ + onSetTargeting: function (bid) {}, +} +registerBidder(spec) diff --git a/modules/nativoBidAdapter.md b/modules/nativoBidAdapter.md new file mode 100644 index 00000000000..1ab7e1c7e3c --- /dev/null +++ b/modules/nativoBidAdapter.md @@ -0,0 +1,54 @@ +# Overview + +``` +Module Name: Nativo Bid Adapter +Module Type: Bidder Adapter +Maintainer: nativo@nativo.com +``` + +# Description + +Module that connects to Nativo's demand sources + +# Dev + +gulp serve --modules=nativoBidAdapter + +# Test Parameters + +``` +var adUnits = [ + { + code: 'test-div', + mediaTypes: { + banner: { + sizes: [[300, 250]], // a display size + } + }, + bids: [ + { + bidder: "nativo", + params: { + placement: '12345' + } + } + ] + },{ + code: 'test-div', + mediaTypes: { + banner: { + sizes: [[320, 50]], // a mobile size + } + }, + bids: [ + { + bidder: "nativo", + params: { + placement: 67890 + } + } + ] + } + ]; + +``` diff --git a/package-lock.json b/package-lock.json index 330298cbaac..3fe857a89e8 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,6 +1,6 @@ { "name": "prebid.js", - "version": "4.15.0-pre", + "version": "4.27.0-pre", "lockfileVersion": 1, "requires": true, "dependencies": { @@ -22437,7 +22437,6 @@ "resolved": "https://registry.npmjs.org/braces/-/braces-2.3.2.tgz", "integrity": "sha512-aNdbnj9P8PjdXU4ybaWLK2IF3jc/EoDYbC7AazW6to3TRsfXxscC9UXOB5iDiEQrkyIbWp2SLQda4+QAa7nc3w==", "dev": true, - "optional": true, "requires": { "arr-flatten": "^1.1.0", "array-unique": "^0.3.2", @@ -22456,7 +22455,6 @@ "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", "dev": true, - "optional": true, "requires": { "is-extendable": "^0.1.0" } @@ -22489,7 +22487,6 @@ "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-4.0.0.tgz", "integrity": "sha1-1USBHUKPmOsGpj3EAtJAPDKMOPc=", "dev": true, - "optional": true, "requires": { "extend-shallow": "^2.0.1", "is-number": "^3.0.0", @@ -22502,7 +22499,6 @@ "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", "dev": true, - "optional": true, "requires": { "is-extendable": "^0.1.0" } @@ -22557,15 +22553,13 @@ "version": "1.1.6", "resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-1.1.6.tgz", "integrity": "sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w==", - "dev": true, - "optional": true + "dev": true }, "is-number": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/is-number/-/is-number-3.0.0.tgz", "integrity": "sha1-JP1iAaR4LPUFYcgQJ2r8fRLXEZU=", "dev": true, - "optional": true, "requires": { "kind-of": "^3.0.2" }, @@ -22575,7 +22569,6 @@ "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", "dev": true, - "optional": true, "requires": { "is-buffer": "^1.1.5" } @@ -22587,7 +22580,6 @@ "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-3.1.10.tgz", "integrity": "sha512-MWikgl9n9M3w+bpsY3He8L+w9eF9338xRl8IAO5viDizwSzziFEyUzo2xrrloB64ADbTf8uA8vRqqttDTOmccg==", "dev": true, - "optional": true, "requires": { "arr-diff": "^4.0.0", "array-unique": "^0.3.2", @@ -22637,7 +22629,6 @@ "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-2.1.1.tgz", "integrity": "sha1-fIDBe53+vlmeJzZ+DU3VWQFB2zg=", "dev": true, - "optional": true, "requires": { "is-number": "^3.0.0", "repeat-string": "^1.6.1" diff --git a/test/spec/modules/nativoBidAdapter_spec.js b/test/spec/modules/nativoBidAdapter_spec.js new file mode 100644 index 00000000000..01919554151 --- /dev/null +++ b/test/spec/modules/nativoBidAdapter_spec.js @@ -0,0 +1,158 @@ +import { expect } from 'chai' +import { spec } from 'modules/nativoBidAdapter.js' +// import { newBidder } from 'src/adapters/bidderFactory.js' +// import * as bidderFactory from 'src/adapters/bidderFactory.js' +// import { deepClone } from 'src/utils.js' +// import { config } from 'src/config.js' + +describe('nativoBidAdapterTests', function () { + describe('isBidRequestValid', function () { + let bid = { + bidder: 'nativo', + params: { + placementId: '10433394', + }, + adUnitCode: 'adunit-code', + sizes: [ + [300, 250], + [300, 600], + ], + bidId: '27b02036ccfa6e', + bidderRequestId: '1372cd8bd8d6a8', + auctionId: 'cfc467e4-2707-48da-becb-bcaab0b2c114', + } + + it('should return true when required params found', function () { + expect(spec.isBidRequestValid(bid)).to.equal(true) + }) + + it('should return false when required params are not passed', function () { + let bid = Object.assign({}, bid) + delete bid.adUnitCode + bid.adUnitCode = 0 + expect(spec.isBidRequestValid(bid)).to.equal(false) + }) + }) + + describe('buildRequests', function () { + let bidRequests = [ + { + bidder: 'nativo', + params: { + placementId: '10433394', + }, + adUnitCode: 'adunit-code', + sizes: [ + [300, 250], + [300, 600], + ], + bidId: '27b02036ccfa6e', + bidderRequestId: '1372cd8bd8d6a8', + auctionId: 'cfc467e4-2707-48da-becb-bcaab0b2c114', + transactionId: '3b36e7e0-0c3e-4006-a279-a741239154ff', + }, + ] + + it('should contain a valid selector', function () { + const request = spec.buildRequests(bidRequests) + const payload = JSON.parse(request.data) + + expect(payload.selector).to.exist + expect(payload.selector).to.be.a('string') + expect(payload.selector).to.not.have.lengthOf(0) + }) + }) +}) + +describe('interpretResponse', function () { + let response = { + id: '1F254428-AB11-4D5E-9887-567B3F952CA5', + seatbid: [ + { + seat: 'seat_0', + bid: [ + { + id: 'f70362ac-f3cf-4225-82a5-948b690927a6', + impid: '1', + price: 3.569, + adm: '', + h: 300, + w: 250, + cat: [], + adomain: ['test.com'], + crid: '1060_72_6760217', + }, + ], + }, + ], + cur: 'USD', + } + + it('should get correct bid response', function () { + let expectedResponse = [ + { + requestId: '1F254428-AB11-4D5E-9887-567B3F952CA5', + cpm: 3.569, + currency: 'USD', + width: 300, + height: 250, + creativeId: '1060_72_6760217', + dealId: 'f70362ac-f3cf-4225-82a5-948b690927a6', + netRevenue: true, + ttl: 360, + ad: '', + meta: { + advertiserDomains: ['test.com'], + }, + }, + ] + + let bidderRequest = { + bids: [ + { + adUnitCode: 'code', + }, + ], + } + let result = spec.interpretResponse({ body: response }, { bidderRequest }) + expect(Object.keys(result[0])).to.have.deep.members( + Object.keys(expectedResponse[0]) + ) + }) + + it('handles nobid responses', function () { + let response = {} + let bidderRequest + + let result = spec.interpretResponse({ body: response }, { bidderRequest }) + expect(result.length).to.equal(0) + }) +}) + +describe('getUserSyncs', function () { + const USER_SYNC_URL_IFRAME = 'http://www.testlocalbidrequest.com:3000/' + const USER_SYNC_URL_IMAGE = 'http://www.testlocalbidrequest.com:3000/' + + it('Returns empty array if no supported user syncs', function () { + let userSync = spec.getUserSyncs({ iframeEnabled: false, pixelEnabled: false }); + expect(userSync).to.be.an('array').with.lengthOf(0); + }); + + it('Returns valid iframe user sync', function () { + let userSync = spec.getUserSyncs({ iframeEnabled: true, pixelEnabled: false }); + expect(userSync).to.be.an('array').with.lengthOf(1); + expect(userSync[0].type).to.exist; + expect(userSync[0].url).to.exist; + expect(userSync[0].type).to.be.equal('iframe'); + expect(userSync[0].url).to.be.equal(USER_SYNC_URL_IFRAME); + }); + + it('Returns valid URL and type', function () { + let userSync = spec.getUserSyncs({ iframeEnabled: false, pixelEnabled: true }); + expect(userSync).to.be.an('array').with.lengthOf(1); + expect(userSync[0].type).to.exist; + expect(userSync[0].url).to.exist; + expect(userSync[0].type).to.be.equal('image'); + expect(userSync[0].url).to.be.equal(USER_SYNC_URL_IMAGE); + }); +}); From 0792373b4c878c63316f22ad72586fa7eed4649a Mon Sep 17 00:00:00 2001 From: Josh Date: Fri, 12 Mar 2021 07:32:41 -0800 Subject: [PATCH 02/53] Fulling working prebid using nativoBidAdapter. Support for GDPR and CCPA in user syncs. --- modules/nativoBidAdapter.js | 161 ++++++++++++++------- modules/nativoBidAdapter.md | 35 ++--- test/spec/modules/nativoBidAdapter_spec.js | 133 +++++++++++++---- 3 files changed, 219 insertions(+), 110 deletions(-) diff --git a/modules/nativoBidAdapter.js b/modules/nativoBidAdapter.js index bb78ff06d79..a196d2efd77 100644 --- a/modules/nativoBidAdapter.js +++ b/modules/nativoBidAdapter.js @@ -4,14 +4,14 @@ import { BANNER } from '../src/mediaTypes.js' // import { config } from 'src/config' const BIDDER_CODE = 'nativo' -const BIDDER_ENDPOINT = 'http://www.testlocalbidrequest.com:3000/requestBid/' // test local endpoint -const USER_SYNC_URL_IFRAME = 'http://www.testlocalbidrequest.com:3000/' -const USER_SYNC_URL_IMAGE = 'http://www.testlocalbidrequest.com:3000/' +const BIDDER_ENDPOINT = 'https://jadserve.postrelease.com/prebid' const TIME_TO_LIVE = 360 const SUPPORTED_AD_TYPES = [BANNER] +const bidRequestMap = {} + // Prebid adapter referrence doc: https://docs.prebid.org/dev-docs/bidder-adaptor.html export const spec = { @@ -26,7 +26,7 @@ export const spec = { * @return boolean True if this is a valid bid, and false otherwise. */ isBidRequestValid: function (bid) { - return !!(bid.adUnitCode) + return bid.params && !!bid.params.placementId }, /** @@ -38,16 +38,25 @@ export const spec = { * @return ServerRequest Info describing the request to the server. */ buildRequests: function (validBidRequests, bidderRequest) { - const payload = { - selector: validBidRequests[0].adUnitCode, - id: validBidRequests[0].bidId + const placementIds = [] + const placmentBidIdMap = {} + let placementId + validBidRequests.forEach((request) => { + placementId = request.params.placementId + placementIds.push(placementId) + placmentBidIdMap[placementId] = request.bidId + }) + bidRequestMap[bidderRequest.bidderRequestId] = placmentBidIdMap + + let params = { + ntv_ptd: placementIds.toString(), + ntv_pb_rid: bidderRequest.bidderRequestId, + ntv_url: encodeURIComponent(bidderRequest.refererInfo.referer), } - const payloadString = JSON.stringify(payload); let serverRequest = { - method: 'POST', - url: BIDDER_ENDPOINT, - data: payloadString + method: 'GET', + url: BIDDER_ENDPOINT + objectToQS(params), } return serverRequest @@ -67,18 +76,16 @@ export const spec = { if (!response || !response.body || utils.isEmpty(response.body)) return [] try { - // Parse the response and return a bidResponses array const body = response.body - // const headerValue = response.headers.get('some-response-header') const bidResponses = [] + const seatbids = body.seatbid - let seatbids = body.seatbid // Step through and grab pertinent data let bidResponse seatbids.forEach((seatbid) => { seatbid.bid.forEach((bid) => { bidResponse = { - requestId: body.id, + requestId: this.getRequestId(body.id, bid.impid), cpm: bid.price, currency: body.cur, width: bid.w, @@ -90,20 +97,6 @@ export const spec = { ad: bid.adm, meta: { advertiserDomains: bid.adomain, - // cat: bid.cat, - // impid: bid.impid, - // networkId: NETWORK_ID, - // networkName: NETWORK_NAME, - // agencyId: AGENCY_ID, - // agencyName: AGENCY_NAME, - // advertiserId: ADVERTISER_ID, - // advertiserName: ADVERTISER_NAME, - // advertiserDomains: [ARRAY_OF_ADVERTISER_DOMAINS], - // brandId: BRAND_ID, - // brandName: BRAND_NAME, - // primaryCatId: IAB_CATEGORY, - // secondaryCatIds: [ARRAY_OF_IAB_CATEGORIES], - // mediaType: MEDIA_TYPE }, } @@ -111,6 +104,9 @@ export const spec = { }) }) + // Don't need the map anymore as it was unique for one request/response + delete bidRequestMap[body.id] + return bidResponses } catch (error) { // If there is an error, return [] @@ -134,40 +130,60 @@ export const spec = { gdprConsent, uspConsent ) { - // console.log('syncOptions', syncOptions) // eslint-disable-line no-console - // console.log('serverResponses', serverResponses) // eslint-disable-line no-console - // console.log('gdprConsent', gdprConsent) // eslint-disable-line no-console - // console.log('uspConsent', uspConsent) // eslint-disable-line no-console - - const syncs = [] - + // Generate consent qs string let params = '' - // GDPR if (gdprConsent) { - params += '&gdpr=' + (gdprConsent.gdprApplies ? 1 : 0) - params += - '&gdpr_consent=' + encodeURIComponent(gdprConsent.consentString || '') + params = appendQSParamString( + params, + 'gdpr', + gdprConsent.gdprApplies ? 1 : 0 + ) + params = appendQSParamString( + params, + 'gdpr_consent', + encodeURIComponent(gdprConsent.consentString || '') + ) } - // CCPA if (uspConsent) { - params += '&us_privacy=' + encodeURIComponent(uspConsent) + params = appendQSParamString( + params, + 'us_privacy', + encodeURIComponent(uspConsent.uspConsent) + ) } - // TODO: We need to determine how we're including sync urls - if (syncOptions.iframeEnabled) { - syncs.push({ - type: 'iframe', - url: USER_SYNC_URL_IFRAME + params, - }) + // Get sync urls from the respnse and inject cinbsent params + const types = { + iframe: syncOptions.iframeEnabled, + image: syncOptions.pixelEnabled, } - if (syncOptions.pixelEnabled) { - syncs.push({ - type: 'image', - url: USER_SYNC_URL_IMAGE + params, + const syncs = [] + + let body + serverResponses.forEach((response) => { + if (!response) return + + body = response.body + + // Make sure we have valid content + if (!body && !body.seatbid && body.seatbid.length === 0) return + + body.seatbid.forEach((seatbid) => { + // Grab the syncs for each seatbid + seatbid.syncUrls.forEach((sync) => { + if (types[sync.type]) { + if (sync.url.trim() !== '') { + syncs.push({ + type: sync.type, + url: sync.url.replace('{GDPR_params}', params), + }) + } + } + }) }) - } + }) return syncs }, @@ -190,5 +206,44 @@ export const spec = { * @param {Object} bidder - The bid of which the targeting has been set */ onSetTargeting: function (bid) {}, + + /** + * Maps Prebid's bidId to Nativo's placementId values per unique bidderRequestId + * @param {String} bidderRequestId - The unique ID value associated with the bidderRequest + * @param {String} placementId - The placement ID value from Nativo + * @returns {String} - The bidId value associated with the corresponding placementId + */ + getRequestId: function (bidderRequestId, placementId) { + return ( + bidRequestMap[bidderRequestId] && + bidRequestMap[bidderRequestId][placementId] + ) + }, } registerBidder(spec) + +// Utils +/** + * Append QS param to existing string + * @param {String} str - String to append to + * @param {String} key - Key to append + * @param {String} value - Value to append + * @returns + */ +function appendQSParamString(str, key, value) { + return str + `${str.length ? '&' : ''}${key}=${value}` +} + +/** + * Convert an object to query string parameters + * @param {Object} obj - Object to convert + * @returns + */ +function objectToQS(obj) { + return ( + '?' + + Object.keys(obj).reduce((value, key) => { + return appendQSParamString(value, key, obj[key]) + }, '') + ) +} diff --git a/modules/nativoBidAdapter.md b/modules/nativoBidAdapter.md index 1ab7e1c7e3c..2c4f595d23f 100644 --- a/modules/nativoBidAdapter.md +++ b/modules/nativoBidAdapter.md @@ -3,7 +3,7 @@ ``` Module Name: Nativo Bid Adapter Module Type: Bidder Adapter -Maintainer: nativo@nativo.com +Maintainer: prebiddev@nativo.com ``` # Description @@ -19,35 +19,20 @@ gulp serve --modules=nativoBidAdapter ``` var adUnits = [ { - code: 'test-div', + code: 'div-gpt-ad-1460505748561-0', mediaTypes: { banner: { - sizes: [[300, 250]], // a display size + sizes: [[300, 250], [300,600]], } }, - bids: [ - { - bidder: "nativo", - params: { - placement: '12345' - } + // Replace this object to test a new Adapter! + bids: [{ + bidder: 'nativo', + params: { + placementId: 13144370 } - ] - },{ - code: 'test-div', - mediaTypes: { - banner: { - sizes: [[320, 50]], // a mobile size - } - }, - bids: [ - { - bidder: "nativo", - params: { - placement: 67890 - } - } - ] + }] + } ]; diff --git a/test/spec/modules/nativoBidAdapter_spec.js b/test/spec/modules/nativoBidAdapter_spec.js index 01919554151..e1132bf1b74 100644 --- a/test/spec/modules/nativoBidAdapter_spec.js +++ b/test/spec/modules/nativoBidAdapter_spec.js @@ -27,10 +27,10 @@ describe('nativoBidAdapterTests', function () { }) it('should return false when required params are not passed', function () { - let bid = Object.assign({}, bid) - delete bid.adUnitCode - bid.adUnitCode = 0 - expect(spec.isBidRequestValid(bid)).to.equal(false) + let bid2 = Object.assign({}, bid) + delete bid2.params + bid2.params = {} + expect(spec.isBidRequestValid(bid2)).to.equal(false) }) }) @@ -53,20 +53,27 @@ describe('nativoBidAdapterTests', function () { }, ] - it('should contain a valid selector', function () { - const request = spec.buildRequests(bidRequests) - const payload = JSON.parse(request.data) + it('url should contain query string parameters', function () { + const request = spec.buildRequests(bidRequests, { + bidderRequestId: 123456, + refererInfo: { + referer: 'https://www.test.com', + }, + }) + + expect(request.url).to.exist + expect(request.url).to.be.a('string') - expect(payload.selector).to.exist - expect(payload.selector).to.be.a('string') - expect(payload.selector).to.not.have.lengthOf(0) + expect(request.url).to.include('?') + expect(request.url).to.include('ntv_url') + expect(request.url).to.include('ntv_ptd') }) }) }) describe('interpretResponse', function () { let response = { - id: '1F254428-AB11-4D5E-9887-567B3F952CA5', + id: '126456', seatbid: [ { seat: 'seat_0', @@ -108,12 +115,19 @@ describe('interpretResponse', function () { ] let bidderRequest = { + id: 123456, bids: [ { - adUnitCode: 'code', + params: { + placementId: 1 + } }, ], } + + // mock + spec.getRequestId = () => 123456 + let result = spec.interpretResponse({ body: response }, { bidderRequest }) expect(Object.keys(result[0])).to.have.deep.members( Object.keys(expectedResponse[0]) @@ -130,29 +144,84 @@ describe('interpretResponse', function () { }) describe('getUserSyncs', function () { - const USER_SYNC_URL_IFRAME = 'http://www.testlocalbidrequest.com:3000/' - const USER_SYNC_URL_IMAGE = 'http://www.testlocalbidrequest.com:3000/' + const response = [ + { + body: { + cur: 'USD', + id: 'a136dbd8-4387-48bf-b8e4-ff9c1d6056ee', + seatbid: [ + { + bid: [{}], + seat: 'seat_0', + syncUrls: [ + { + type: 'image', + url: 'pixel-tracker-test-url/?{GDPR_params}', + }, + { + type: 'iframe', + url: 'iframe-tracker-test-url/?{GDPR_params}', + }, + ], + }, + ], + }, + }, + ] + + const gdprConsent = { + gdprApplies: true, + consentString: '111111' + } + + const uspConsent = { + uspConsent: '1YYY' + } it('Returns empty array if no supported user syncs', function () { - let userSync = spec.getUserSyncs({ iframeEnabled: false, pixelEnabled: false }); - expect(userSync).to.be.an('array').with.lengthOf(0); - }); + let userSync = spec.getUserSyncs( + { + iframeEnabled: false, + pixelEnabled: false, + }, + response, + gdprConsent, + uspConsent + ) + expect(userSync).to.be.an('array').with.lengthOf(0) + }) it('Returns valid iframe user sync', function () { - let userSync = spec.getUserSyncs({ iframeEnabled: true, pixelEnabled: false }); - expect(userSync).to.be.an('array').with.lengthOf(1); - expect(userSync[0].type).to.exist; - expect(userSync[0].url).to.exist; - expect(userSync[0].type).to.be.equal('iframe'); - expect(userSync[0].url).to.be.equal(USER_SYNC_URL_IFRAME); - }); + let userSync = spec.getUserSyncs( + { + iframeEnabled: true, + pixelEnabled: false, + }, + response, + gdprConsent, + uspConsent + ) + expect(userSync).to.be.an('array').with.lengthOf(1) + expect(userSync[0].type).to.exist + expect(userSync[0].url).to.exist + expect(userSync[0].type).to.be.equal('iframe') + expect(userSync[0].url).to.contain('gdpr=1&gdpr_consent=111111&us_privacy=1YYY') + }) it('Returns valid URL and type', function () { - let userSync = spec.getUserSyncs({ iframeEnabled: false, pixelEnabled: true }); - expect(userSync).to.be.an('array').with.lengthOf(1); - expect(userSync[0].type).to.exist; - expect(userSync[0].url).to.exist; - expect(userSync[0].type).to.be.equal('image'); - expect(userSync[0].url).to.be.equal(USER_SYNC_URL_IMAGE); - }); -}); + let userSync = spec.getUserSyncs( + { + iframeEnabled: false, + pixelEnabled: true, + }, + response, + gdprConsent, + uspConsent + ) + expect(userSync).to.be.an('array').with.lengthOf(1) + expect(userSync[0].type).to.exist + expect(userSync[0].url).to.exist + expect(userSync[0].type).to.be.equal('image') + expect(userSync[0].url).to.contain('gdpr=1&gdpr_consent=111111&us_privacy=1YYY') + }) +}) From 008e9068282b600d6aebd86baa3fca6374a75e49 Mon Sep 17 00:00:00 2001 From: Josh Date: Mon, 5 Apr 2021 10:27:38 -0700 Subject: [PATCH 03/53] Added defult size settings based on the largest ad unit. Added response body validation. Added consent to request url qs params. --- modules/nativoBidAdapter.js | 123 ++++++++++++++++++++++++++---------- 1 file changed, 89 insertions(+), 34 deletions(-) diff --git a/modules/nativoBidAdapter.js b/modules/nativoBidAdapter.js index a196d2efd77..99a570dee6b 100644 --- a/modules/nativoBidAdapter.js +++ b/modules/nativoBidAdapter.js @@ -1,10 +1,10 @@ -import * as utils from '../src/utils.js' -import { registerBidder } from '../src/adapters/bidderFactory.js' -import { BANNER } from '../src/mediaTypes.js' +import * as utils from "../src/utils.js" +import { registerBidder } from "../src/adapters/bidderFactory.js" +import { BANNER } from "../src/mediaTypes.js" // import { config } from 'src/config' -const BIDDER_CODE = 'nativo' -const BIDDER_ENDPOINT = 'https://jadserve.postrelease.com/prebid' +const BIDDER_CODE = "nativo" +const BIDDER_ENDPOINT = "https://jadserve.postrelease.com/prebid" const TIME_TO_LIVE = 360 @@ -16,7 +16,7 @@ const bidRequestMap = {} export const spec = { code: BIDDER_CODE, - aliases: ['ntv'], // short code + aliases: ["ntv"], // short code supportedMediaTypes: SUPPORTED_AD_TYPES, /** @@ -44,19 +44,38 @@ export const spec = { validBidRequests.forEach((request) => { placementId = request.params.placementId placementIds.push(placementId) - placmentBidIdMap[placementId] = request.bidId + placmentBidIdMap[placementId] = { + bidId: request.bidId, + size: getLargestSize(request.sizes), + } }) bidRequestMap[bidderRequest.bidderRequestId] = placmentBidIdMap - let params = { - ntv_ptd: placementIds.toString(), - ntv_pb_rid: bidderRequest.bidderRequestId, - ntv_url: encodeURIComponent(bidderRequest.refererInfo.referer), + let params = [ + { key: "ntv_ptd", value: placementIds.toString() }, + { key: "ntv_pb_rid", value: bidderRequest.bidderRequestId }, + { + key: "ntv_url", + value: encodeURIComponent(bidderRequest.refererInfo.referer), + }, + ] + + if (bidderRequest.gdprConsent) { + // Put on the beginning of the qs param array + params.unshift({ + key: "ntv_gdpr_consent", + value: bidderRequest.gdprConsent.consentString, + }) + } + + if (bidderRequest.uspConsent) { + // Put on the beginning of the qs param array + params.unshift({ key: "us_privacy", value: bidderRequest.uspConsent }) } let serverRequest = { - method: 'GET', - url: BIDDER_ENDPOINT + objectToQS(params), + method: "GET", + url: BIDDER_ENDPOINT + arrayToQS(params), } return serverRequest @@ -76,20 +95,25 @@ export const spec = { if (!response || !response.body || utils.isEmpty(response.body)) return [] try { - const body = response.body + const body = + typeof response.body === "string" + ? JSON.parse(response.body) + : response.body + const bidResponses = [] const seatbids = body.seatbid // Step through and grab pertinent data - let bidResponse + let bidResponse, adUnit seatbids.forEach((seatbid) => { seatbid.bid.forEach((bid) => { + adUnit = this.getRequestId(body.id, bid.impid) bidResponse = { - requestId: this.getRequestId(body.id, bid.impid), + requestId: adUnit.bidId, cpm: bid.price, currency: body.cur, - width: bid.w, - height: bid.h, + width: bid.w || adUnit.size[0], + height: bid.h || adUnit.size[1], creativeId: bid.crid, dealId: bid.id, netRevenue: true, @@ -131,25 +155,25 @@ export const spec = { uspConsent ) { // Generate consent qs string - let params = '' + let params = "" // GDPR if (gdprConsent) { params = appendQSParamString( params, - 'gdpr', + "gdpr", gdprConsent.gdprApplies ? 1 : 0 ) params = appendQSParamString( params, - 'gdpr_consent', - encodeURIComponent(gdprConsent.consentString || '') + "gdpr_consent", + encodeURIComponent(gdprConsent.consentString || "") ) } // CCPA if (uspConsent) { params = appendQSParamString( params, - 'us_privacy', + "us_privacy", encodeURIComponent(uspConsent.uspConsent) ) } @@ -163,21 +187,27 @@ export const spec = { let body serverResponses.forEach((response) => { - if (!response) return + // If the bid response was empty, return [] + if (!response || !response.body || utils.isEmpty(response.body)) { + return syncs + } - body = response.body + body = + typeof response.body === "string" + ? JSON.parse(response.body) + : response.body // Make sure we have valid content - if (!body && !body.seatbid && body.seatbid.length === 0) return + if (!body || !body.seatbid || body.seatbid.length === 0) return body.seatbid.forEach((seatbid) => { // Grab the syncs for each seatbid seatbid.syncUrls.forEach((sync) => { if (types[sync.type]) { - if (sync.url.trim() !== '') { + if (sync.url.trim() !== "") { syncs.push({ type: sync.type, - url: sync.url.replace('{GDPR_params}', params), + url: sync.url.replace("{GDPR_params}", params), }) } } @@ -231,7 +261,7 @@ registerBidder(spec) * @returns */ function appendQSParamString(str, key, value) { - return str + `${str.length ? '&' : ''}${key}=${value}` + return str + `${str.length ? "&" : ""}${key}=${value}` } /** @@ -239,11 +269,36 @@ function appendQSParamString(str, key, value) { * @param {Object} obj - Object to convert * @returns */ -function objectToQS(obj) { +function arrayToQS(arr) { return ( - '?' + - Object.keys(obj).reduce((value, key) => { - return appendQSParamString(value, key, obj[key]) - }, '') + "?" + + arr.reduce((value, obj) => { + return appendQSParamString(value, obj.key, obj.value) + }, "") ) } + +/** + * Get the largest size array + * @param {Array} sizes - Array of size arrays + * @returns Size array with the largest area + */ +function getLargestSize(sizes, method = area) { + if (!sizes || sizes.length === 0) return [] + if (sizes.length === 1) return sizes[0] + + return sizes.reduce((prev, current) => { + if (method(current) > method(prev)) { + return current + } else { + return prev + } + }) +} + +/** + * Calculate the area + * @param {Array} size - [width, height] + * @returns The calculated area + */ +const area = (size) => size[0] * size[1] From 21f3e0c7c241e2751a61a92d130f3d8d1caa448c Mon Sep 17 00:00:00 2001 From: Josh Date: Wed, 7 Apr 2021 10:13:25 -0700 Subject: [PATCH 04/53] Changed bidder endpoint url --- modules/nativoBidAdapter.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/nativoBidAdapter.js b/modules/nativoBidAdapter.js index 99a570dee6b..4feee0d1fdb 100644 --- a/modules/nativoBidAdapter.js +++ b/modules/nativoBidAdapter.js @@ -4,7 +4,7 @@ import { BANNER } from "../src/mediaTypes.js" // import { config } from 'src/config' const BIDDER_CODE = "nativo" -const BIDDER_ENDPOINT = "https://jadserve.postrelease.com/prebid" +const BIDDER_ENDPOINT = "https://exchange.postrelease.com/prebid" const TIME_TO_LIVE = 360 From 1a33f2cac9e7c44a6b4df02b22c4729231e01827 Mon Sep 17 00:00:00 2001 From: Josh Date: Wed, 7 Apr 2021 10:34:10 -0700 Subject: [PATCH 05/53] Changed double quotes to single quotes. --- modules/nativoBidAdapter.js | 48 ++++++++++++++++++------------------- 1 file changed, 24 insertions(+), 24 deletions(-) diff --git a/modules/nativoBidAdapter.js b/modules/nativoBidAdapter.js index 4feee0d1fdb..94a93667ca9 100644 --- a/modules/nativoBidAdapter.js +++ b/modules/nativoBidAdapter.js @@ -1,10 +1,10 @@ -import * as utils from "../src/utils.js" -import { registerBidder } from "../src/adapters/bidderFactory.js" -import { BANNER } from "../src/mediaTypes.js" +import * as utils from '../src/utils.js' +import { registerBidder } from '../src/adapters/bidderFactory.js' +import { BANNER } from '../src/mediaTypes.js' // import { config } from 'src/config' -const BIDDER_CODE = "nativo" -const BIDDER_ENDPOINT = "https://exchange.postrelease.com/prebid" +const BIDDER_CODE = 'nativo' +const BIDDER_ENDPOINT = 'https://exchange.postrelease.com/prebid' const TIME_TO_LIVE = 360 @@ -16,7 +16,7 @@ const bidRequestMap = {} export const spec = { code: BIDDER_CODE, - aliases: ["ntv"], // short code + aliases: ['ntv'], // short code supportedMediaTypes: SUPPORTED_AD_TYPES, /** @@ -52,10 +52,10 @@ export const spec = { bidRequestMap[bidderRequest.bidderRequestId] = placmentBidIdMap let params = [ - { key: "ntv_ptd", value: placementIds.toString() }, - { key: "ntv_pb_rid", value: bidderRequest.bidderRequestId }, + { key: 'ntv_ptd', value: placementIds.toString() }, + { key: 'ntv_pb_rid', value: bidderRequest.bidderRequestId }, { - key: "ntv_url", + key: 'ntv_url', value: encodeURIComponent(bidderRequest.refererInfo.referer), }, ] @@ -63,18 +63,18 @@ export const spec = { if (bidderRequest.gdprConsent) { // Put on the beginning of the qs param array params.unshift({ - key: "ntv_gdpr_consent", + key: 'ntv_gdpr_consent', value: bidderRequest.gdprConsent.consentString, }) } if (bidderRequest.uspConsent) { // Put on the beginning of the qs param array - params.unshift({ key: "us_privacy", value: bidderRequest.uspConsent }) + params.unshift({ key: 'us_privacy', value: bidderRequest.uspConsent }) } let serverRequest = { - method: "GET", + method: 'GET', url: BIDDER_ENDPOINT + arrayToQS(params), } @@ -96,7 +96,7 @@ export const spec = { try { const body = - typeof response.body === "string" + typeof response.body === 'string' ? JSON.parse(response.body) : response.body @@ -155,25 +155,25 @@ export const spec = { uspConsent ) { // Generate consent qs string - let params = "" + let params = '' // GDPR if (gdprConsent) { params = appendQSParamString( params, - "gdpr", + 'gdpr', gdprConsent.gdprApplies ? 1 : 0 ) params = appendQSParamString( params, - "gdpr_consent", - encodeURIComponent(gdprConsent.consentString || "") + 'gdpr_consent', + encodeURIComponent(gdprConsent.consentString || '') ) } // CCPA if (uspConsent) { params = appendQSParamString( params, - "us_privacy", + 'us_privacy', encodeURIComponent(uspConsent.uspConsent) ) } @@ -193,7 +193,7 @@ export const spec = { } body = - typeof response.body === "string" + typeof response.body === 'string' ? JSON.parse(response.body) : response.body @@ -204,10 +204,10 @@ export const spec = { // Grab the syncs for each seatbid seatbid.syncUrls.forEach((sync) => { if (types[sync.type]) { - if (sync.url.trim() !== "") { + if (sync.url.trim() !== '') { syncs.push({ type: sync.type, - url: sync.url.replace("{GDPR_params}", params), + url: sync.url.replace('{GDPR_params}', params), }) } } @@ -261,7 +261,7 @@ registerBidder(spec) * @returns */ function appendQSParamString(str, key, value) { - return str + `${str.length ? "&" : ""}${key}=${value}` + return str + `${str.length ? '&' : ''}${key}=${value}` } /** @@ -271,10 +271,10 @@ function appendQSParamString(str, key, value) { */ function arrayToQS(arr) { return ( - "?" + + '?' + arr.reduce((value, obj) => { return appendQSParamString(value, obj.key, obj.value) - }, "") + }, '') ) } From 517cd5b2f20bc31ec75b68df76a2047923bcf3e1 Mon Sep 17 00:00:00 2001 From: Josh Date: Thu, 8 Apr 2021 07:34:10 -0700 Subject: [PATCH 06/53] Reverted package-json.lock to remove modifications from PR --- package-lock.json | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/package-lock.json b/package-lock.json index 3fe857a89e8..330298cbaac 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,6 +1,6 @@ { "name": "prebid.js", - "version": "4.27.0-pre", + "version": "4.15.0-pre", "lockfileVersion": 1, "requires": true, "dependencies": { @@ -22437,6 +22437,7 @@ "resolved": "https://registry.npmjs.org/braces/-/braces-2.3.2.tgz", "integrity": "sha512-aNdbnj9P8PjdXU4ybaWLK2IF3jc/EoDYbC7AazW6to3TRsfXxscC9UXOB5iDiEQrkyIbWp2SLQda4+QAa7nc3w==", "dev": true, + "optional": true, "requires": { "arr-flatten": "^1.1.0", "array-unique": "^0.3.2", @@ -22455,6 +22456,7 @@ "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", "dev": true, + "optional": true, "requires": { "is-extendable": "^0.1.0" } @@ -22487,6 +22489,7 @@ "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-4.0.0.tgz", "integrity": "sha1-1USBHUKPmOsGpj3EAtJAPDKMOPc=", "dev": true, + "optional": true, "requires": { "extend-shallow": "^2.0.1", "is-number": "^3.0.0", @@ -22499,6 +22502,7 @@ "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", "dev": true, + "optional": true, "requires": { "is-extendable": "^0.1.0" } @@ -22553,13 +22557,15 @@ "version": "1.1.6", "resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-1.1.6.tgz", "integrity": "sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w==", - "dev": true + "dev": true, + "optional": true }, "is-number": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/is-number/-/is-number-3.0.0.tgz", "integrity": "sha1-JP1iAaR4LPUFYcgQJ2r8fRLXEZU=", "dev": true, + "optional": true, "requires": { "kind-of": "^3.0.2" }, @@ -22569,6 +22575,7 @@ "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", "dev": true, + "optional": true, "requires": { "is-buffer": "^1.1.5" } @@ -22580,6 +22587,7 @@ "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-3.1.10.tgz", "integrity": "sha512-MWikgl9n9M3w+bpsY3He8L+w9eF9338xRl8IAO5viDizwSzziFEyUzo2xrrloB64ADbTf8uA8vRqqttDTOmccg==", "dev": true, + "optional": true, "requires": { "arr-diff": "^4.0.0", "array-unique": "^0.3.2", @@ -22629,6 +22637,7 @@ "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-2.1.1.tgz", "integrity": "sha1-fIDBe53+vlmeJzZ+DU3VWQFB2zg=", "dev": true, + "optional": true, "requires": { "is-number": "^3.0.0", "repeat-string": "^1.6.1" From 44bf4286f482af11c94e03f8d238409ee9b14310 Mon Sep 17 00:00:00 2001 From: Josh Date: Thu, 15 Apr 2021 14:14:05 -0700 Subject: [PATCH 07/53] Added optional bidder param 'url' so the ad server can force- match an existing placement --- modules/nativoBidAdapter.js | 7 +++++-- modules/nativoBidAdapter.md | 3 ++- 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/modules/nativoBidAdapter.js b/modules/nativoBidAdapter.js index 94a93667ca9..aea875b443d 100644 --- a/modules/nativoBidAdapter.js +++ b/modules/nativoBidAdapter.js @@ -40,8 +40,9 @@ export const spec = { buildRequests: function (validBidRequests, bidderRequest) { const placementIds = [] const placmentBidIdMap = {} - let placementId + let placementId, pageUrl validBidRequests.forEach((request) => { + pageUrl = pageUrl || request.params.url // Use the first url value found placementId = request.params.placementId placementIds.push(placementId) placmentBidIdMap[placementId] = { @@ -51,12 +52,14 @@ export const spec = { }) bidRequestMap[bidderRequest.bidderRequestId] = placmentBidIdMap + if(!pageUrl) pageUrl = bidderRequest.refererInfo.referer + let params = [ { key: 'ntv_ptd', value: placementIds.toString() }, { key: 'ntv_pb_rid', value: bidderRequest.bidderRequestId }, { key: 'ntv_url', - value: encodeURIComponent(bidderRequest.refererInfo.referer), + value: encodeURIComponent(pageUrl), }, ] diff --git a/modules/nativoBidAdapter.md b/modules/nativoBidAdapter.md index 2c4f595d23f..ec0980aae50 100644 --- a/modules/nativoBidAdapter.md +++ b/modules/nativoBidAdapter.md @@ -29,7 +29,8 @@ var adUnits = [ bids: [{ bidder: 'nativo', params: { - placementId: 13144370 + placementId: 1125609, + url: 'https://test-sites.internal.nativo.net/testing/prebid_adpater.html' } }] From bfe0e1e60167927003cb693814547dd01de5efcc Mon Sep 17 00:00:00 2001 From: Josh Date: Thu, 15 Apr 2021 14:19:27 -0700 Subject: [PATCH 08/53] Lint fix. Added space after if. --- modules/nativoBidAdapter.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/nativoBidAdapter.js b/modules/nativoBidAdapter.js index aea875b443d..d396bd4d495 100644 --- a/modules/nativoBidAdapter.js +++ b/modules/nativoBidAdapter.js @@ -52,7 +52,7 @@ export const spec = { }) bidRequestMap[bidderRequest.bidderRequestId] = placmentBidIdMap - if(!pageUrl) pageUrl = bidderRequest.refererInfo.referer + if (!pageUrl) pageUrl = bidderRequest.refererInfo.referer let params = [ { key: 'ntv_ptd', value: placementIds.toString() }, From e927584bffff4ddb1e55a5e662f352748a8a10fb Mon Sep 17 00:00:00 2001 From: Josh Date: Wed, 26 May 2021 09:37:22 -0700 Subject: [PATCH 09/53] Added new QS param to send various adUnit data to adapter endpopint --- modules/nativoBidAdapter.js | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/modules/nativoBidAdapter.js b/modules/nativoBidAdapter.js index d396bd4d495..fc0925bf2ca 100644 --- a/modules/nativoBidAdapter.js +++ b/modules/nativoBidAdapter.js @@ -54,9 +54,24 @@ export const spec = { if (!pageUrl) pageUrl = bidderRequest.refererInfo.referer + // Build adUnit data + const adUnitData = { + adUnits: validBidRequests.map((adUnit) => { + return { + adUnitCode: adUnit.adUnitCode, + mediaTypes: adUnit.mediaTypes, + } + }), + } + + // Build QS Params let params = [ { key: 'ntv_ptd', value: placementIds.toString() }, { key: 'ntv_pb_rid', value: bidderRequest.bidderRequestId }, + { + key: 'ntv_ppc', + value: btoa(JSON.stringify(adUnitData)), // Convert to Base 64 + }, { key: 'ntv_url', value: encodeURIComponent(pageUrl), From 26d8b862ed245faeb565e25f77c75b6557776fe1 Mon Sep 17 00:00:00 2001 From: Josh Date: Wed, 26 May 2021 09:57:31 -0700 Subject: [PATCH 10/53] Updated unit test for new QS param --- test/spec/modules/nativoBidAdapter_spec.js | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/test/spec/modules/nativoBidAdapter_spec.js b/test/spec/modules/nativoBidAdapter_spec.js index e1132bf1b74..6f489a65d3c 100644 --- a/test/spec/modules/nativoBidAdapter_spec.js +++ b/test/spec/modules/nativoBidAdapter_spec.js @@ -65,8 +65,10 @@ describe('nativoBidAdapterTests', function () { expect(request.url).to.be.a('string') expect(request.url).to.include('?') - expect(request.url).to.include('ntv_url') expect(request.url).to.include('ntv_ptd') + expect(request.url).to.include('ntv_pb_rid') + expect(request.url).to.include('ntv_ppc') + expect(request.url).to.include('ntv_url') }) }) }) From ead77de717635e78bc28aac71635ac7690bd992f Mon Sep 17 00:00:00 2001 From: Josh Date: Tue, 20 Jul 2021 09:32:33 -0700 Subject: [PATCH 11/53] Added qs param to keep track of ad unit refreshes --- modules/nativoBidAdapter.js | 9 ++++++++- test/spec/modules/nativoBidAdapter_spec.js | 17 +++++++++++------ 2 files changed, 19 insertions(+), 7 deletions(-) diff --git a/modules/nativoBidAdapter.js b/modules/nativoBidAdapter.js index fc0925bf2ca..880ff4b6a8e 100644 --- a/modules/nativoBidAdapter.js +++ b/modules/nativoBidAdapter.js @@ -11,6 +11,7 @@ const TIME_TO_LIVE = 360 const SUPPORTED_AD_TYPES = [BANNER] const bidRequestMap = {} +const adUnitsRequested = {} // Prebid adapter referrence doc: https://docs.prebid.org/dev-docs/bidder-adaptor.html @@ -57,6 +58,8 @@ export const spec = { // Build adUnit data const adUnitData = { adUnits: validBidRequests.map((adUnit) => { + // Track if we've already requested for this ad unit code + adUnitsRequested[adUnit.adUnitCode] = adUnitsRequested[adUnit.adUnitCode] !== undefined ? adUnitsRequested[adUnit.adUnitCode]++ : 0 return { adUnitCode: adUnit.adUnitCode, mediaTypes: adUnit.mediaTypes, @@ -72,10 +75,14 @@ export const spec = { key: 'ntv_ppc', value: btoa(JSON.stringify(adUnitData)), // Convert to Base 64 }, + { + key: 'ntv_dbr', + value: btoa(JSON.stringify(adUnitsRequested)) + }, { key: 'ntv_url', value: encodeURIComponent(pageUrl), - }, + } ] if (bidderRequest.gdprConsent) { diff --git a/test/spec/modules/nativoBidAdapter_spec.js b/test/spec/modules/nativoBidAdapter_spec.js index 6f489a65d3c..4202b7c6f91 100644 --- a/test/spec/modules/nativoBidAdapter_spec.js +++ b/test/spec/modules/nativoBidAdapter_spec.js @@ -69,6 +69,7 @@ describe('nativoBidAdapterTests', function () { expect(request.url).to.include('ntv_pb_rid') expect(request.url).to.include('ntv_ppc') expect(request.url).to.include('ntv_url') + expect(request.url).to.include('ntv_dbr') }) }) }) @@ -121,8 +122,8 @@ describe('interpretResponse', function () { bids: [ { params: { - placementId: 1 - } + placementId: 1, + }, }, ], } @@ -173,11 +174,11 @@ describe('getUserSyncs', function () { const gdprConsent = { gdprApplies: true, - consentString: '111111' + consentString: '111111', } const uspConsent = { - uspConsent: '1YYY' + uspConsent: '1YYY', } it('Returns empty array if no supported user syncs', function () { @@ -207,7 +208,9 @@ describe('getUserSyncs', function () { expect(userSync[0].type).to.exist expect(userSync[0].url).to.exist expect(userSync[0].type).to.be.equal('iframe') - expect(userSync[0].url).to.contain('gdpr=1&gdpr_consent=111111&us_privacy=1YYY') + expect(userSync[0].url).to.contain( + 'gdpr=1&gdpr_consent=111111&us_privacy=1YYY' + ) }) it('Returns valid URL and type', function () { @@ -224,6 +227,8 @@ describe('getUserSyncs', function () { expect(userSync[0].type).to.exist expect(userSync[0].url).to.exist expect(userSync[0].type).to.be.equal('image') - expect(userSync[0].url).to.contain('gdpr=1&gdpr_consent=111111&us_privacy=1YYY') + expect(userSync[0].url).to.contain( + 'gdpr=1&gdpr_consent=111111&us_privacy=1YYY' + ) }) }) From c338c4d3a474f51ab77f35ad4482431c1760a5fa Mon Sep 17 00:00:00 2001 From: Josh Date: Wed, 29 Sep 2021 20:39:15 -0700 Subject: [PATCH 12/53] Updated bidMap key default value --- modules/nativoBidAdapter.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/nativoBidAdapter.js b/modules/nativoBidAdapter.js index a8737c73eff..8259c179675 100644 --- a/modules/nativoBidAdapter.js +++ b/modules/nativoBidAdapter.js @@ -57,7 +57,7 @@ export const spec = { placementIds.add(placementId) } - var key = placementId || validBidRequests.adUnitCode + var key = placementId || request.adUnitCode placmentBidIdMap[key] = { bidId: request.bidId, size: getLargestSize(request.sizes), From bfe7b02f58f01bfdcfc3c777ce5747d829622311 Mon Sep 17 00:00:00 2001 From: Joshua Fledderjohn Date: Tue, 12 Oct 2021 14:02:40 -0700 Subject: [PATCH 13/53] Updated refresh increment logic --- modules/nativoBidAdapter.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/nativoBidAdapter.js b/modules/nativoBidAdapter.js index 8259c179675..9a0cd62258b 100644 --- a/modules/nativoBidAdapter.js +++ b/modules/nativoBidAdapter.js @@ -71,7 +71,7 @@ export const spec = { // Track if we've already requested for this ad unit code adUnitsRequested[adUnit.adUnitCode] = adUnitsRequested[adUnit.adUnitCode] !== undefined - ? adUnitsRequested[adUnit.adUnitCode]++ + ? adUnitsRequested[adUnit.adUnitCode] + 1 : 0 return { adUnitCode: adUnit.adUnitCode, From 978ffd72d2c6311fb47aa749ce3d8f71d609d505 Mon Sep 17 00:00:00 2001 From: Joshua Fledderjohn Date: Mon, 25 Oct 2021 11:27:28 -0700 Subject: [PATCH 14/53] Refactored spread operator for IE11 support --- modules/nativoBidAdapter.js | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/modules/nativoBidAdapter.js b/modules/nativoBidAdapter.js index a8737c73eff..06586b80102 100644 --- a/modules/nativoBidAdapter.js +++ b/modules/nativoBidAdapter.js @@ -57,7 +57,7 @@ export const spec = { placementIds.add(placementId) } - var key = placementId || validBidRequests.adUnitCode + var key = placementId || request.adUnitCode placmentBidIdMap[key] = { bidId: request.bidId, size: getLargestSize(request.sizes), @@ -71,7 +71,7 @@ export const spec = { // Track if we've already requested for this ad unit code adUnitsRequested[adUnit.adUnitCode] = adUnitsRequested[adUnit.adUnitCode] !== undefined - ? adUnitsRequested[adUnit.adUnitCode]++ + ? adUnitsRequested[adUnit.adUnitCode] + 1 : 0 return { adUnitCode: adUnit.adUnitCode, @@ -98,7 +98,11 @@ export const spec = { ] if (placementIds.size > 0) { - params.unshift({ key: 'ntv_ptd', value: [...placementIds].join(',') }) + // Convert Set to Array (IE 11 Safe) + const placements = [] + placementIds.forEach((value) => placements.push(value)) + // Append to query string paramters + params.unshift({ key: 'ntv_ptd', value: placements.join(',') }) } if (bidderRequest.gdprConsent) { From f74f370328a8715622b996ee8b76756a7bdcf8fd Mon Sep 17 00:00:00 2001 From: Joshua Fledderjohn Date: Tue, 26 Oct 2021 11:06:15 -0700 Subject: [PATCH 15/53] Updated isBidRequestValid check --- modules/nativoBidAdapter.js | 27 ++++++++++- test/spec/modules/nativoBidAdapter_spec.js | 53 ++++++++++++++-------- 2 files changed, 61 insertions(+), 19 deletions(-) diff --git a/modules/nativoBidAdapter.js b/modules/nativoBidAdapter.js index 06586b80102..71447b863b4 100644 --- a/modules/nativoBidAdapter.js +++ b/modules/nativoBidAdapter.js @@ -17,6 +17,16 @@ const adUnitsRequested = {} // Prebid adapter referrence doc: https://docs.prebid.org/dev-docs/bidder-adaptor.html +// Validity checks for optionsl paramters +const validParameter = { + url: (value) => typeof value === 'string', + placementId: (value) => { + const isString = typeof value === 'string' + const isNumber = typeof value === 'number' + return isString || isNumber + }, +} + export const spec = { code: BIDDER_CODE, gvlid: GVLID, @@ -30,7 +40,22 @@ export const spec = { * @return boolean True if this is a valid bid, and false otherwise. */ isBidRequestValid: function (bid) { - return true + // We don't need any specific parameters to make a bid request + // If not parameters are supplied just verify it's the correct bidder code + if (!bid.params) return bid.bidder === BIDDER_CODE + + // Check if any supplied parameters are invalid + const hasInvalidParameters = Object.entries(bid.params).some(([key, value]) => { + const validityCheck = validParameter[key] + + // We don't have a test for this so it's not a paramter we care about + if (!validityCheck) return false + + // Return if the check is not passed + return !validityCheck(value) + }) + + return !hasInvalidParameters }, /** diff --git a/test/spec/modules/nativoBidAdapter_spec.js b/test/spec/modules/nativoBidAdapter_spec.js index dfc9f5b99b3..23f48f3661a 100644 --- a/test/spec/modules/nativoBidAdapter_spec.js +++ b/test/spec/modules/nativoBidAdapter_spec.js @@ -8,29 +8,46 @@ import { spec } from 'modules/nativoBidAdapter.js' describe('nativoBidAdapterTests', function () { describe('isBidRequestValid', function () { let bid = { - bidder: 'nativo', - params: { - placementId: '10433394', - }, - adUnitCode: 'adunit-code', - sizes: [ - [300, 250], - [300, 600], - ], - bidId: '27b02036ccfa6e', - bidderRequestId: '1372cd8bd8d6a8', - auctionId: 'cfc467e4-2707-48da-becb-bcaab0b2c114', + bidder: 'nativo' } - it('should return true when required params found', function () { + it('should return true if no params found', function () { + expect(spec.isBidRequestValid(bid)).to.equal(true) + }) + + it('should return true for valid placementId value', function () { + bid.params = { + placementId: '10433394', + } + expect(spec.isBidRequestValid(bid)).to.equal(true) + }) + + it('should return true for valid placementId value', function () { + bid.params = { + placementId: 10433394, + } + expect(spec.isBidRequestValid(bid)).to.equal(true) + }) + + it('should return false for invalid placementId value', function () { + bid.params = { + placementId: true, + } + expect(spec.isBidRequestValid(bid)).to.equal(false) + }) + + it('should return true for valid placementId value', function () { + bid.params = { + url: 'www.test.com', + } expect(spec.isBidRequestValid(bid)).to.equal(true) }) - it('should return true when params are not passed', function () { - let bid2 = Object.assign({}, bid) - delete bid2.params - bid2.params = {} - expect(spec.isBidRequestValid(bid2)).to.equal(true) + it('should return false for invalid placementId value', function () { + bid.params = { + url: 4567890, + } + expect(spec.isBidRequestValid(bid)).to.equal(false) }) }) From 7b9c9bbb5210d720c2d3742d178c0a278afddb60 Mon Sep 17 00:00:00 2001 From: Joshua Fledderjohn Date: Tue, 26 Oct 2021 12:00:50 -0700 Subject: [PATCH 16/53] Refactored Object.enties to use Object.keys to fix CircleCI testing errors --- modules/nativoBidAdapter.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/modules/nativoBidAdapter.js b/modules/nativoBidAdapter.js index 71447b863b4..c9e6a1f659f 100644 --- a/modules/nativoBidAdapter.js +++ b/modules/nativoBidAdapter.js @@ -45,7 +45,8 @@ export const spec = { if (!bid.params) return bid.bidder === BIDDER_CODE // Check if any supplied parameters are invalid - const hasInvalidParameters = Object.entries(bid.params).some(([key, value]) => { + const hasInvalidParameters = Object.keys(bid.params).some(key => { + const value = bid.params[key] const validityCheck = validParameter[key] // We don't have a test for this so it's not a paramter we care about From 7274900f76f2ad2bf4bd1c85683b28ba80b2d17b Mon Sep 17 00:00:00 2001 From: Joshua Fledderjohn Date: Fri, 14 Jan 2022 11:57:16 -0800 Subject: [PATCH 17/53] Updated bid mapping key creation to prioritize ad unit code over placementId --- modules/nativoBidAdapter.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/nativoBidAdapter.js b/modules/nativoBidAdapter.js index c9e6a1f659f..96d959c1a75 100644 --- a/modules/nativoBidAdapter.js +++ b/modules/nativoBidAdapter.js @@ -83,7 +83,7 @@ export const spec = { placementIds.add(placementId) } - var key = placementId || request.adUnitCode + var key = request.adUnitCode || placementId placmentBidIdMap[key] = { bidId: request.bidId, size: getLargestSize(request.sizes), From 95cc80eb70124af3eedc87513633590a2158eb76 Mon Sep 17 00:00:00 2001 From: Joshua Fledderjohn Date: Mon, 7 Feb 2022 14:17:28 -0800 Subject: [PATCH 18/53] Added filtering by ad, advertiser and campaign. --- modules/nativoBidAdapter.js | 47 +++++- test/spec/modules/nativoBidAdapter_spec.js | 167 +++++++++++++++++++-- 2 files changed, 200 insertions(+), 14 deletions(-) diff --git a/modules/nativoBidAdapter.js b/modules/nativoBidAdapter.js index 96d959c1a75..88727d2457e 100644 --- a/modules/nativoBidAdapter.js +++ b/modules/nativoBidAdapter.js @@ -14,6 +14,12 @@ const SUPPORTED_AD_TYPES = [BANNER] const bidRequestMap = {} const adUnitsRequested = {} +const extData = {} + +// Filtering +const adsToFilter = [] +const advertisersToFilter = [] +const campaignsToFilter = [] // Prebid adapter referrence doc: https://docs.prebid.org/dev-docs/bidder-adaptor.html @@ -45,7 +51,7 @@ export const spec = { if (!bid.params) return bid.bidder === BIDDER_CODE // Check if any supplied parameters are invalid - const hasInvalidParameters = Object.keys(bid.params).some(key => { + const hasInvalidParameters = Object.keys(bid.params).some((key) => { const value = bid.params[key] const validityCheck = validParameter[key] @@ -123,6 +129,20 @@ export const spec = { }, ] + // Add filtering + if (adsToFilter.length > 0) { + params.unshift({ key: 'ntv_atf', value: adsToFilter.join(',') }) + } + + if (advertisersToFilter.length > 0) { + params.unshift({ key: 'ntv_avtf', value: advertisersToFilter.join(',') }) + } + + if (campaignsToFilter.length > 0) { + params.unshift({ key: 'ntv_ctf', value: campaignsToFilter.join(',') }) + } + + // Add placement IDs if (placementIds.size > 0) { // Convert Set to Array (IE 11 Safe) const placements = [] @@ -131,6 +151,7 @@ export const spec = { params.unshift({ key: 'ntv_ptd', value: placements.join(',') }) } + // Add GDPR params if (bidderRequest.gdprConsent) { // Put on the beginning of the qs param array params.unshift({ @@ -139,6 +160,7 @@ export const spec = { }) } + // Add USP params if (bidderRequest.uspConsent) { // Put on the beginning of the qs param array params.unshift({ key: 'us_privacy', value: bidderRequest.uspConsent }) @@ -195,6 +217,8 @@ export const spec = { }, } + if (bid.ext) extData[bid.id] = bid.ext + bidResponses.push(bidResponse) }) }) @@ -300,7 +324,15 @@ export const spec = { * Will be called when a bid from the adapter won the auction. * @param {Object} bid - The bid that won the auction */ - onBidWon: function (bid) {}, + onBidWon: function (bid) { + const ext = extData[bid.dealId] + + if (!ext) return + + appendFilterData(adsToFilter, ext.adsToFilter) + appendFilterData(advertisersToFilter, ext.advertisersToFilter) + appendFilterData(campaignsToFilter, ext.campaignsToFilter) + }, /** * Will be called when the adserver targeting has been set for a bid from the adapter. @@ -375,3 +407,14 @@ function getLargestSize(sizes, method = area) { * @returns The calculated area */ const area = (size) => size[0] * size[1] + +/** + * Save any filter data from winning bid requests for subsequent requests + * @param {Array} filter - The filter data bucket currently stored + * @param {Array} filterData - The filter data to add + */ +function appendFilterData(filter, filterData) { + if (filterData && Array.isArray(filterData) && filterData.length > 0) { + filterData.forEach((ad) => filter.push(ad)) + } +} diff --git a/test/spec/modules/nativoBidAdapter_spec.js b/test/spec/modules/nativoBidAdapter_spec.js index 23f48f3661a..c552090cf6e 100644 --- a/test/spec/modules/nativoBidAdapter_spec.js +++ b/test/spec/modules/nativoBidAdapter_spec.js @@ -1,14 +1,10 @@ import { expect } from 'chai' import { spec } from 'modules/nativoBidAdapter.js' -// import { newBidder } from 'src/adapters/bidderFactory.js' -// import * as bidderFactory from 'src/adapters/bidderFactory.js' -// import { deepClone } from 'src/utils.js' -// import { config } from 'src/config.js' describe('nativoBidAdapterTests', function () { describe('isBidRequestValid', function () { let bid = { - bidder: 'nativo' + bidder: 'nativo', } it('should return true if no params found', function () { @@ -273,9 +269,7 @@ describe('getAdUnitData', () => { } const data = spec.getAdUnitData(9876543, { impid: 12345 }) - expect(Object.keys(data)).to.have.deep.members( - Object.keys(adUnitData) - ) + expect(Object.keys(data)).to.have.deep.members(Object.keys(adUnitData)) }) it('Falls back to ad unit code value', () => { @@ -290,9 +284,158 @@ describe('getAdUnitData', () => { }, } - const data = spec.getAdUnitData(9876543, { impid: 12345, ext: { ad_unit_code: '#test-code' } }) - expect(Object.keys(data)).to.have.deep.members( - Object.keys(adUnitData) - ) + const data = spec.getAdUnitData(9876543, { + impid: 12345, + ext: { ad_unit_code: '#test-code' }, + }) + expect(Object.keys(data)).to.have.deep.members(Object.keys(adUnitData)) + }) +}) + +describe('Response to Request Filter Flow', () => { + let bidRequests = [ + { + bidder: 'nativo', + params: { + placementId: '10433394', + }, + adUnitCode: 'adunit-code', + sizes: [ + [300, 250], + [300, 600], + ], + bidId: '27b02036ccfa6e', + bidderRequestId: '1372cd8bd8d6a8', + auctionId: 'cfc467e4-2707-48da-becb-bcaab0b2c114', + transactionId: '3b36e7e0-0c3e-4006-a279-a741239154ff', + }, + ] + + let response + + beforeEach(() => { + response = { + id: '126456', + seatbid: [ + { + seat: 'seat_0', + bid: [ + { + id: 'f70362ac-f3cf-4225-82a5-948b690927a6', + impid: '1', + price: 3.569, + adm: '', + h: 300, + w: 250, + cat: [], + adomain: ['test.com'], + crid: '1060_72_6760217', + }, + ], + }, + ], + cur: 'USD', + } + }) + + let bidderRequest = { + id: 123456, + bids: [ + { + params: { + placementId: 1, + }, + }, + ], + } + + // mock + spec.getAdUnitData = () => { + return { + bidId: 123456, + size: [300, 250], + } + } + + it('Appends NO filter based on previous response', () => { + // Getting the mock response + let result = spec.interpretResponse({ body: response }, { bidderRequest }) + + // Winning the bid + spec.onBidWon(result[0]) + + // Making another request + const request = spec.buildRequests(bidRequests, { + bidderRequestId: 123456, + refererInfo: { + referer: 'https://www.test.com', + }, + }) + expect(request.url).to.not.include('ntv_aft') + expect(request.url).to.not.include('ntv_avtf') + expect(request.url).to.not.include('ntv_ctf') + }) + + it('Appends Ads filter based on previous response', () => { + response.seatbid[0].bid[0].ext = { adsToFilter: ['12345'] } + + // Getting the mock response + let result = spec.interpretResponse({ body: response }, { bidderRequest }) + + // Winning the bid + spec.onBidWon(result[0]) + + // Making another request + const request = spec.buildRequests(bidRequests, { + bidderRequestId: 123456, + refererInfo: { + referer: 'https://www.test.com', + }, + }) + expect(request.url).to.include(`ntv_atf=12345`) + expect(request.url).to.not.include('ntv_avtf') + expect(request.url).to.not.include('ntv_ctf') + }) + + it('Appends Advertiser filter based on previous response', () => { + response.seatbid[0].bid[0].ext = { advertisersToFilter: ['1'] } + + // Getting the mock response + let result = spec.interpretResponse({ body: response }, { bidderRequest }) + + // Winning the bid + spec.onBidWon(result[0]) + + // Making another request + const request = spec.buildRequests(bidRequests, { + bidderRequestId: 123456, + refererInfo: { + referer: 'https://www.test.com', + }, + }) + expect(request.url).to.include(`ntv_atf=12345`) + expect(request.url).to.include('ntv_avtf=1') + expect(request.url).to.not.include('ntv_ctf') + }) + + it('Appends Campaign filter based on previous response', () => { + response.seatbid[0].bid[0].ext = { campaignsToFilter: ['234'] } + + // Getting the mock response + let result = spec.interpretResponse({ body: response }, { bidderRequest }) + + // Winning the bid + spec.onBidWon(result[0]) + + // Making another request + const request = spec.buildRequests(bidRequests, { + bidderRequestId: 123456, + refererInfo: { + referer: 'https://www.test.com', + }, + }) + expect(request.url).to.include(`ntv_atf=12345`) + expect(request.url).to.include('ntv_avtf=1') + expect(request.url).to.include('ntv_ctf=234') }) }) From a6fc7896011a26ba7ae7c5ce1efaa3bfdf528eb0 Mon Sep 17 00:00:00 2001 From: Joshua Fledderjohn Date: Thu, 17 Feb 2022 11:13:00 -0800 Subject: [PATCH 19/53] Merged master --- package-lock.json | 99 ++++++++++++++++++++++------------------------- 1 file changed, 46 insertions(+), 53 deletions(-) diff --git a/package-lock.json b/package-lock.json index 09f7dabd16f..a68b149f993 100644 --- a/package-lock.json +++ b/package-lock.json @@ -35519,6 +35519,16 @@ } } }, + "JSONStream": { + "version": "1.3.5", + "resolved": "https://registry.npmjs.org/JSONStream/-/JSONStream-1.3.5.tgz", + "integrity": "sha512-E+iruNOY8VV9s4JEbe1aNEm6MiszPRr/UfcHMz0TQh1BXSxHK+ASV1R6W4HpjBhSeS+54PIsAMCBmwD06LLsqQ==", + "dev": true, + "requires": { + "jsonparse": "^1.2.0", + "through": ">=2.2.7 <3" + } + }, "abbrev": { "version": "1.0.9", "resolved": "https://registry.npmjs.org/abbrev/-/abbrev-1.0.9.tgz", @@ -35561,8 +35571,7 @@ "version": "5.3.1", "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.1.tgz", "integrity": "sha512-K0Ptm/47OKfQRpNQ2J/oIN/3QYiK6FwW+eJbILhsdxh2WTLdl+30o8aGdTbm5JbffpFFAg/g+zi1E+jvJha5ng==", - "dev": true, - "requires": {} + "dev": true }, "acorn-node": { "version": "1.8.2", @@ -35629,8 +35638,7 @@ "version": "3.5.2", "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-3.5.2.tgz", "integrity": "sha512-5p6WTN0DdTGVQk6VjcEju19IgaHudalcfabD7yhDGeA6bcQnmL+CpveLJq/3hvfwd1aof6L386Ougkx6RfyMIQ==", - "dev": true, - "requires": {} + "dev": true }, "align-text": { "version": "0.1.4", @@ -36330,8 +36338,7 @@ "version": "10.0.0", "resolved": "https://registry.npmjs.org/babelify/-/babelify-10.0.0.tgz", "integrity": "sha512-X40FaxyH7t3X+JFAKvb1H9wooWKLRCi8pg3m8poqtdZaIng+bjzp9RvKQCvRjF9isHiPkXspbbXT/zwXLtwgwg==", - "dev": true, - "requires": {} + "dev": true }, "bach": { "version": "1.2.0", @@ -38594,8 +38601,8 @@ "integrity": "sha512-OG9kQtmMZBJD/32NEw5IhN5+HnBqVjy03eC+I71I0oQRFA5rOgA4OtPOYG7mz1GkCfCNxn3gKIX8EiHJYuf1cA==", "dev": true, "requires": { - "is-text-path": "^1.0.1", "JSONStream": "^1.0.4", + "is-text-path": "^1.0.1", "lodash": "^4.17.15", "meow": "^8.0.0", "split2": "^3.0.0", @@ -40340,8 +40347,7 @@ "version": "7.4.6", "resolved": "https://registry.npmjs.org/ws/-/ws-7.4.6.tgz", "integrity": "sha512-YmhHDO4MzaDLB+M9ym/mDA5z0naX8j7SIlT8f8z+I0VtzsRbekxEutHSme7NPS2qE8StCYQNUnfWdXta/Yu85A==", - "dev": true, - "requires": {} + "dev": true } } }, @@ -40895,8 +40901,7 @@ "version": "10.2.1", "resolved": "https://registry.npmjs.org/eslint-config-standard/-/eslint-config-standard-10.2.1.tgz", "integrity": "sha1-wGHk0GbzedwXzVYsZOgZtN1FRZE=", - "dev": true, - "requires": {} + "dev": true }, "eslint-import-resolver-node": { "version": "0.3.4", @@ -42086,15 +42091,13 @@ "version": "5.1.0", "resolved": "https://registry.npmjs.org/eslint-plugin-promise/-/eslint-plugin-promise-5.1.0.tgz", "integrity": "sha512-NGmI6BH5L12pl7ScQHbg7tvtk4wPxxj8yPHH47NvSmMtFneC077PSeY3huFj06ZWZvtbfxSPt3RuOQD5XcR4ng==", - "dev": true, - "requires": {} + "dev": true }, "eslint-plugin-standard": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/eslint-plugin-standard/-/eslint-plugin-standard-3.1.0.tgz", "integrity": "sha512-fVcdyuKRr0EZ4fjWl3c+gp1BANFJD1+RaWa2UPYfMZ6jCtp5RG00kSaXnK/dE5sYzt4kaWJ9qdxqUfc0d9kX0w==", - "dev": true, - "requires": {} + "dev": true }, "eslint-scope": { "version": "5.1.1", @@ -44817,8 +44820,7 @@ "version": "2.1.1", "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-2.1.1.tgz", "integrity": "sha1-YXmX/F9gV2iUxDX5QNgZ4TW4B2I=", - "dev": true, - "requires": {} + "dev": true }, "ansi-escapes": { "version": "3.2.0", @@ -46474,8 +46476,7 @@ "resolved": "https://registry.npmjs.org/icss-utils/-/icss-utils-5.1.0.tgz", "integrity": "sha512-soFhflCVWLfRNOPU3iv5Z9VUdT44xFRbzjLsEzSr5AQmgqPMTHdU3PMT1Cf1ssx8fLNJDA1juftYl+PUcv3MqA==", "dev": true, - "optional": true, - "requires": {} + "optional": true }, "ieee754": { "version": "1.2.1", @@ -48044,8 +48045,7 @@ "version": "8.0.1", "resolved": "https://registry.npmjs.org/karma-babel-preprocessor/-/karma-babel-preprocessor-8.0.1.tgz", "integrity": "sha512-5upyawNi3c7Gg6tPH1FWRVTmUijGf3v1GV4ScLM/2jKdDP18SlaKlUpu8eJrRI3STO8qK1bkqFcdgAA364nLYQ==", - "dev": true, - "requires": {} + "dev": true }, "karma-browserstack-launcher": { "version": "1.4.0", @@ -48062,8 +48062,7 @@ "version": "0.1.0", "resolved": "https://registry.npmjs.org/karma-chai/-/karma-chai-0.1.0.tgz", "integrity": "sha1-vuWtQEAFF4Ea40u5RfdikJEIt5o=", - "dev": true, - "requires": {} + "dev": true }, "karma-chrome-launcher": { "version": "3.1.0", @@ -48265,29 +48264,25 @@ "version": "1.0.0", "resolved": "https://registry.npmjs.org/karma-opera-launcher/-/karma-opera-launcher-1.0.0.tgz", "integrity": "sha1-+lFihTGh0L6EstjcDX7iCfyP+Ro=", - "dev": true, - "requires": {} + "dev": true }, "karma-safari-launcher": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/karma-safari-launcher/-/karma-safari-launcher-1.0.0.tgz", "integrity": "sha1-lpgqLMR9BmquccVTursoMZEVos4=", - "dev": true, - "requires": {} + "dev": true }, "karma-script-launcher": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/karma-script-launcher/-/karma-script-launcher-1.0.0.tgz", "integrity": "sha1-zQF8TeXvCeWp2nkydhdhCN1LVC0=", - "dev": true, - "requires": {} + "dev": true }, "karma-sinon": { "version": "1.0.5", "resolved": "https://registry.npmjs.org/karma-sinon/-/karma-sinon-1.0.5.tgz", "integrity": "sha1-TjRD8oMP3s/2JNN0cWPxIX2qKpo=", - "dev": true, - "requires": {} + "dev": true }, "karma-sourcemap-loader": { "version": "0.3.8", @@ -50639,6 +50634,7 @@ "integrity": "sha512-eiyIZj/A0dj1o4ywXWqicazUL3l0HP3TydUR6xF0X3xh3LGBMLqW8a9aFe6MuNH4mxNMk53QKBHM6LOPR8kSgw==", "dev": true, "requires": { + "JSONStream": "^1.0.3", "browser-resolve": "^1.7.0", "cached-path-relative": "^1.0.0", "concat-stream": "~1.5.0", @@ -50646,7 +50642,6 @@ "detective": "^5.2.0", "duplexer2": "^0.1.2", "inherits": "^2.0.1", - "JSONStream": "^1.0.3", "konan": "^2.1.1", "readable-stream": "^2.0.2", "resolve": "^1.1.3", @@ -52362,8 +52357,7 @@ "resolved": "https://registry.npmjs.org/postcss-modules-extract-imports/-/postcss-modules-extract-imports-3.0.0.tgz", "integrity": "sha512-bdHleFnP3kZ4NYDhuGlVK+CMrQ/pqUm8bx/oGL93K6gVwiclvX5x0n76fYMKuIGKzlABOy13zsvqjb0f92TEXw==", "dev": true, - "optional": true, - "requires": {} + "optional": true }, "postcss-modules-local-by-default": { "version": "4.0.0", @@ -54984,23 +54978,6 @@ "integrity": "sha1-ucczDHBChi9rFC3CdLvMWGbONUY=", "dev": true }, - "string_decoder": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz", - "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==", - "dev": true, - "requires": { - "safe-buffer": "~5.2.0" - }, - "dependencies": { - "safe-buffer": { - "version": "5.2.1", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", - "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", - "dev": true - } - } - }, "string-hash": { "version": "1.1.3", "resolved": "https://registry.npmjs.org/string-hash/-/string-hash-1.1.3.tgz", @@ -55045,6 +55022,23 @@ "define-properties": "^1.1.3" } }, + "string_decoder": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz", + "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==", + "dev": true, + "requires": { + "safe-buffer": "~5.2.0" + }, + "dependencies": { + "safe-buffer": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", + "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", + "dev": true + } + } + }, "stringify-entities": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/stringify-entities/-/stringify-entities-3.1.0.tgz", @@ -58766,8 +58760,7 @@ "version": "7.5.0", "resolved": "https://registry.npmjs.org/ws/-/ws-7.5.0.tgz", "integrity": "sha512-6ezXvzOZupqKj4jUqbQ9tXuJNo+BR2gU8fFRk3XCP3e0G6WT414u5ELe6Y0vtp7kmSJ3F7YWObSNr1ESsgi4vw==", - "dev": true, - "requires": {} + "dev": true }, "xml-escape": { "version": "1.0.0", From 2c0c8f5a1ff7204347e7e49fd9c7dd42441d5042 Mon Sep 17 00:00:00 2001 From: Joshua Fledderjohn Date: Mon, 14 Mar 2022 13:59:38 -0700 Subject: [PATCH 20/53] Added more robust bidDataMap with multiple key access --- modules/nativoBidAdapter.js | 73 +++++++++++++++++++++++++++++++++---- 1 file changed, 65 insertions(+), 8 deletions(-) diff --git a/modules/nativoBidAdapter.js b/modules/nativoBidAdapter.js index 88727d2457e..da4f1ffddd6 100644 --- a/modules/nativoBidAdapter.js +++ b/modules/nativoBidAdapter.js @@ -12,6 +12,63 @@ const TIME_TO_LIVE = 360 const SUPPORTED_AD_TYPES = [BANNER] +/** + * Keep track of bid data by keys + * @returns {Object} - Map of bid data that can be referenced by multiple keys + */ +const BidDataMap = () => { + const referenceMap = {} + const bids = [] + + /** + * Add a refence to the index by key value + * @param {String} key - The key to store the index reference + * @param {Integer} index - The index value of the bidData + */ + function adKeyReference(key, index) { + if (!referenceMap.hasOwnProperty(key)) { + referenceMap[key] = index + } + } + + /** + * Adds a bid to the map + * @param {Object} bid - Bid data + * @param {Array/String} keys - Keys to reference the index value + */ + function addBidData(bid, keys) { + const index = bids.length + bids.push(bid) + + if (Array.isArray(keys)) { + keys.forEach((key) => { + adKeyReference(String(key), index) + }) + return + } + + adKeyReference(String(keys), index) + } + + /** + * Get's the bid data refrerenced by the key + * @param {String} key - The key value to find the bid data by + * @returns {Object} - The bid data + */ + function getBidData(key) { + const stringKey = String(key) + if (referenceMap.hasOwnProperty(stringKey)) { + return bids[referenceMap[stringKey]] + } + } + + // Return API + return { + addBidData, + getBidData, + } +} + const bidRequestMap = {} const adUnitsRequested = {} const extData = {} @@ -75,8 +132,8 @@ export const spec = { */ buildRequests: function (validBidRequests, bidderRequest) { const placementIds = new Set() - const placmentBidIdMap = {} let placementId, pageUrl + const bidDataMap = BidDataMap() validBidRequests.forEach((request) => { pageUrl = deepAccess( request, @@ -89,13 +146,13 @@ export const spec = { placementIds.add(placementId) } - var key = request.adUnitCode || placementId - placmentBidIdMap[key] = { + const bidData = { bidId: request.bidId, size: getLargestSize(request.sizes), } + bidDataMap.addBidData(bidData, [placementId, request.adUnitCode]) }) - bidRequestMap[bidderRequest.bidderRequestId] = placmentBidIdMap + bidRequestMap[bidderRequest.bidderRequestId] = bidDataMap // Build adUnit data const adUnitData = { @@ -347,12 +404,12 @@ export const spec = { * @returns {String} - The bidId value associated with the corresponding placementId */ getAdUnitData: function (bidderRequestId, bid) { - var data = deepAccess(bidRequestMap, `${bidderRequestId}.${bid.impid}`) + const bidDataMap = bidRequestMap[bidderRequestId] - if (data) return data + const placementId = bid.impid + const adUnitCode = deepAccess(bid, 'ext.ad_unit_id') - var unitCode = deepAccess(bid, 'ext.ad_unit_id') - return deepAccess(bidRequestMap, `${bidderRequestId}.${unitCode}`) + return bidDataMap.getBidData(adUnitCode) || bidDataMap.getBidData(placementId) }, } registerBidder(spec) From 97f020d65f61b303e8dd42a358ec827141f945cc Mon Sep 17 00:00:00 2001 From: Joshua Fledderjohn Date: Wed, 16 Mar 2022 14:40:26 -0700 Subject: [PATCH 21/53] Deduped filer values --- modules/nativoBidAdapter.js | 24 +++++++++++++----------- 1 file changed, 13 insertions(+), 11 deletions(-) diff --git a/modules/nativoBidAdapter.js b/modules/nativoBidAdapter.js index da4f1ffddd6..f5b2595604d 100644 --- a/modules/nativoBidAdapter.js +++ b/modules/nativoBidAdapter.js @@ -74,9 +74,9 @@ const adUnitsRequested = {} const extData = {} // Filtering -const adsToFilter = [] -const advertisersToFilter = [] -const campaignsToFilter = [] +const adsToFilter = new Set() +const advertisersToFilter = new Set() +const campaignsToFilter = new Set() // Prebid adapter referrence doc: https://docs.prebid.org/dev-docs/bidder-adaptor.html @@ -187,16 +187,16 @@ export const spec = { ] // Add filtering - if (adsToFilter.length > 0) { - params.unshift({ key: 'ntv_atf', value: adsToFilter.join(',') }) + if (adsToFilter.size > 0) { + params.unshift({ key: 'ntv_atf', value: Array.from(adsToFilter).join(',') }) } - if (advertisersToFilter.length > 0) { - params.unshift({ key: 'ntv_avtf', value: advertisersToFilter.join(',') }) + if (advertisersToFilter.size > 0) { + params.unshift({ key: 'ntv_avtf', value: Array.from(advertisersToFilter).join(',') }) } - if (campaignsToFilter.length > 0) { - params.unshift({ key: 'ntv_ctf', value: campaignsToFilter.join(',') }) + if (campaignsToFilter.size > 0) { + params.unshift({ key: 'ntv_ctf', value: Array.from(campaignsToFilter).join(',') }) } // Add placement IDs @@ -409,7 +409,9 @@ export const spec = { const placementId = bid.impid const adUnitCode = deepAccess(bid, 'ext.ad_unit_id') - return bidDataMap.getBidData(adUnitCode) || bidDataMap.getBidData(placementId) + return ( + bidDataMap.getBidData(adUnitCode) || bidDataMap.getBidData(placementId) + ) }, } registerBidder(spec) @@ -472,6 +474,6 @@ const area = (size) => size[0] * size[1] */ function appendFilterData(filter, filterData) { if (filterData && Array.isArray(filterData) && filterData.length > 0) { - filterData.forEach((ad) => filter.push(ad)) + filterData.forEach((ad) => filter.add(ad)) } } From b23518fb8431ccff0be51230cccc4b71955e61e4 Mon Sep 17 00:00:00 2001 From: Joshua Fledderjohn Date: Wed, 30 Mar 2022 07:24:22 -0700 Subject: [PATCH 22/53] Rolled back package.json --- package-lock.json | 2797 +++------------------------------------------ 1 file changed, 155 insertions(+), 2642 deletions(-) diff --git a/package-lock.json b/package-lock.json index db95af32cbd..15649c35740 100644 --- a/package-lock.json +++ b/package-lock.json @@ -29803,2590 +29803,7 @@ "@types/node": "*" } }, -<<<<<<< HEAD - "@babel/plugin-syntax-object-rest-spread": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-object-rest-spread/-/plugin-syntax-object-rest-spread-7.8.3.tgz", - "integrity": "sha512-XoqMijGZb9y3y2XskN+P1wUGiVwWZ5JmoDRwx5+3GmEplNyVM2s2Dg8ILFQm8rWM48orGy5YpI5Bl8U1y7ydlA==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.8.0" - } - }, - "@babel/plugin-syntax-optional-catch-binding": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-optional-catch-binding/-/plugin-syntax-optional-catch-binding-7.8.3.tgz", - "integrity": "sha512-6VPD0Pc1lpTqw0aKoeRTMiB+kWhAoT24PA+ksWSBrFtl5SIRVpZlwN3NNPQjehA2E/91FV3RjLWoVTglWcSV3Q==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.8.0" - } - }, - "@babel/plugin-syntax-optional-chaining": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-optional-chaining/-/plugin-syntax-optional-chaining-7.8.3.tgz", - "integrity": "sha512-KoK9ErH1MBlCPxV0VANkXW2/dw4vlbGDrFgz8bmUsBGYkFRcbRwMh6cIJubdPrkxRwuGdtCk0v/wPTKbQgBjkg==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.8.0" - } - }, - "@babel/plugin-syntax-private-property-in-object": { - "version": "7.14.5", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-private-property-in-object/-/plugin-syntax-private-property-in-object-7.14.5.tgz", - "integrity": "sha512-0wVnp9dxJ72ZUJDV27ZfbSj6iHLoytYZmh3rFcxNnvsJF3ktkzLDZPy/mA17HGsaQT3/DQsWYX1f1QGWkCoVUg==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.14.5" - } - }, - "@babel/plugin-syntax-top-level-await": { - "version": "7.14.5", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-top-level-await/-/plugin-syntax-top-level-await-7.14.5.tgz", - "integrity": "sha512-hx++upLv5U1rgYfwe1xBQUhRmU41NEvpUvrp8jkrSCdvGSnM5/qdRMtylJ6PG5OFkBaHkbTAKTnd3/YyESRHFw==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.14.5" - } - }, - "@babel/plugin-transform-arrow-functions": { - "version": "7.14.5", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-arrow-functions/-/plugin-transform-arrow-functions-7.14.5.tgz", - "integrity": "sha512-KOnO0l4+tD5IfOdi4x8C1XmEIRWUjNRV8wc6K2vz/3e8yAOoZZvsRXRRIF/yo/MAOFb4QjtAw9xSxMXbSMRy8A==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.14.5" - } - }, - "@babel/plugin-transform-async-to-generator": { - "version": "7.14.5", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-async-to-generator/-/plugin-transform-async-to-generator-7.14.5.tgz", - "integrity": "sha512-szkbzQ0mNk0rpu76fzDdqSyPu0MuvpXgC+6rz5rpMb5OIRxdmHfQxrktL8CYolL2d8luMCZTR0DpIMIdL27IjA==", - "dev": true, - "requires": { - "@babel/helper-module-imports": "^7.14.5", - "@babel/helper-plugin-utils": "^7.14.5", - "@babel/helper-remap-async-to-generator": "^7.14.5" - } - }, - "@babel/plugin-transform-block-scoped-functions": { - "version": "7.14.5", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-block-scoped-functions/-/plugin-transform-block-scoped-functions-7.14.5.tgz", - "integrity": "sha512-dtqWqdWZ5NqBX3KzsVCWfQI3A53Ft5pWFCT2eCVUftWZgjc5DpDponbIF1+c+7cSGk2wN0YK7HGL/ezfRbpKBQ==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.14.5" - } - }, - "@babel/plugin-transform-block-scoping": { - "version": "7.14.5", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-block-scoping/-/plugin-transform-block-scoping-7.14.5.tgz", - "integrity": "sha512-LBYm4ZocNgoCqyxMLoOnwpsmQ18HWTQvql64t3GvMUzLQrNoV1BDG0lNftC8QKYERkZgCCT/7J5xWGObGAyHDw==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.14.5" - } - }, - "@babel/plugin-transform-classes": { - "version": "7.14.5", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-classes/-/plugin-transform-classes-7.14.5.tgz", - "integrity": "sha512-J4VxKAMykM06K/64z9rwiL6xnBHgB1+FVspqvlgCdwD1KUbQNfszeKVVOMh59w3sztHYIZDgnhOC4WbdEfHFDA==", - "dev": true, - "requires": { - "@babel/helper-annotate-as-pure": "^7.14.5", - "@babel/helper-function-name": "^7.14.5", - "@babel/helper-optimise-call-expression": "^7.14.5", - "@babel/helper-plugin-utils": "^7.14.5", - "@babel/helper-replace-supers": "^7.14.5", - "@babel/helper-split-export-declaration": "^7.14.5", - "globals": "^11.1.0" - } - }, - "@babel/plugin-transform-computed-properties": { - "version": "7.14.5", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-computed-properties/-/plugin-transform-computed-properties-7.14.5.tgz", - "integrity": "sha512-pWM+E4283UxaVzLb8UBXv4EIxMovU4zxT1OPnpHJcmnvyY9QbPPTKZfEj31EUvG3/EQRbYAGaYEUZ4yWOBC2xg==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.14.5" - } - }, - "@babel/plugin-transform-destructuring": { - "version": "7.14.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-destructuring/-/plugin-transform-destructuring-7.14.7.tgz", - "integrity": "sha512-0mDE99nK+kVh3xlc5vKwB6wnP9ecuSj+zQCa/n0voENtP/zymdT4HH6QEb65wjjcbqr1Jb/7z9Qp7TF5FtwYGw==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.14.5" - } - }, - "@babel/plugin-transform-dotall-regex": { - "version": "7.14.5", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-dotall-regex/-/plugin-transform-dotall-regex-7.14.5.tgz", - "integrity": "sha512-loGlnBdj02MDsFaHhAIJzh7euK89lBrGIdM9EAtHFo6xKygCUGuuWe07o1oZVk287amtW1n0808sQM99aZt3gw==", - "dev": true, - "requires": { - "@babel/helper-create-regexp-features-plugin": "^7.14.5", - "@babel/helper-plugin-utils": "^7.14.5" - } - }, - "@babel/plugin-transform-duplicate-keys": { - "version": "7.14.5", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-duplicate-keys/-/plugin-transform-duplicate-keys-7.14.5.tgz", - "integrity": "sha512-iJjbI53huKbPDAsJ8EmVmvCKeeq21bAze4fu9GBQtSLqfvzj2oRuHVx4ZkDwEhg1htQ+5OBZh/Ab0XDf5iBZ7A==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.14.5" - } - }, - "@babel/plugin-transform-exponentiation-operator": { - "version": "7.14.5", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-exponentiation-operator/-/plugin-transform-exponentiation-operator-7.14.5.tgz", - "integrity": "sha512-jFazJhMBc9D27o9jDnIE5ZErI0R0m7PbKXVq77FFvqFbzvTMuv8jaAwLZ5PviOLSFttqKIW0/wxNSDbjLk0tYA==", - "dev": true, - "requires": { - "@babel/helper-builder-binary-assignment-operator-visitor": "^7.14.5", - "@babel/helper-plugin-utils": "^7.14.5" - } - }, - "@babel/plugin-transform-for-of": { - "version": "7.14.5", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-for-of/-/plugin-transform-for-of-7.14.5.tgz", - "integrity": "sha512-CfmqxSUZzBl0rSjpoQSFoR9UEj3HzbGuGNL21/iFTmjb5gFggJp3ph0xR1YBhexmLoKRHzgxuFvty2xdSt6gTA==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.14.5" - } - }, - "@babel/plugin-transform-function-name": { - "version": "7.14.5", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-function-name/-/plugin-transform-function-name-7.14.5.tgz", - "integrity": "sha512-vbO6kv0fIzZ1GpmGQuvbwwm+O4Cbm2NrPzwlup9+/3fdkuzo1YqOZcXw26+YUJB84Ja7j9yURWposEHLYwxUfQ==", - "dev": true, - "requires": { - "@babel/helper-function-name": "^7.14.5", - "@babel/helper-plugin-utils": "^7.14.5" - } - }, - "@babel/plugin-transform-literals": { - "version": "7.14.5", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-literals/-/plugin-transform-literals-7.14.5.tgz", - "integrity": "sha512-ql33+epql2F49bi8aHXxvLURHkxJbSmMKl9J5yHqg4PLtdE6Uc48CH1GS6TQvZ86eoB/ApZXwm7jlA+B3kra7A==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.14.5" - } - }, - "@babel/plugin-transform-member-expression-literals": { - "version": "7.14.5", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-member-expression-literals/-/plugin-transform-member-expression-literals-7.14.5.tgz", - "integrity": "sha512-WkNXxH1VXVTKarWFqmso83xl+2V3Eo28YY5utIkbsmXoItO8Q3aZxN4BTS2k0hz9dGUloHK26mJMyQEYfkn/+Q==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.14.5" - } - }, - "@babel/plugin-transform-modules-amd": { - "version": "7.14.5", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-amd/-/plugin-transform-modules-amd-7.14.5.tgz", - "integrity": "sha512-3lpOU8Vxmp3roC4vzFpSdEpGUWSMsHFreTWOMMLzel2gNGfHE5UWIh/LN6ghHs2xurUp4jRFYMUIZhuFbody1g==", - "dev": true, - "requires": { - "@babel/helper-module-transforms": "^7.14.5", - "@babel/helper-plugin-utils": "^7.14.5", - "babel-plugin-dynamic-import-node": "^2.3.3" - } - }, - "@babel/plugin-transform-modules-commonjs": { - "version": "7.14.5", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-commonjs/-/plugin-transform-modules-commonjs-7.14.5.tgz", - "integrity": "sha512-en8GfBtgnydoao2PS+87mKyw62k02k7kJ9ltbKe0fXTHrQmG6QZZflYuGI1VVG7sVpx4E1n7KBpNlPb8m78J+A==", - "dev": true, - "requires": { - "@babel/helper-module-transforms": "^7.14.5", - "@babel/helper-plugin-utils": "^7.14.5", - "@babel/helper-simple-access": "^7.14.5", - "babel-plugin-dynamic-import-node": "^2.3.3" - } - }, - "@babel/plugin-transform-modules-systemjs": { - "version": "7.14.5", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-systemjs/-/plugin-transform-modules-systemjs-7.14.5.tgz", - "integrity": "sha512-mNMQdvBEE5DcMQaL5LbzXFMANrQjd2W7FPzg34Y4yEz7dBgdaC+9B84dSO+/1Wba98zoDbInctCDo4JGxz1VYA==", - "dev": true, - "requires": { - "@babel/helper-hoist-variables": "^7.14.5", - "@babel/helper-module-transforms": "^7.14.5", - "@babel/helper-plugin-utils": "^7.14.5", - "@babel/helper-validator-identifier": "^7.14.5", - "babel-plugin-dynamic-import-node": "^2.3.3" - } - }, - "@babel/plugin-transform-modules-umd": { - "version": "7.14.5", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-umd/-/plugin-transform-modules-umd-7.14.5.tgz", - "integrity": "sha512-RfPGoagSngC06LsGUYyM9QWSXZ8MysEjDJTAea1lqRjNECE3y0qIJF/qbvJxc4oA4s99HumIMdXOrd+TdKaAAA==", - "dev": true, - "requires": { - "@babel/helper-module-transforms": "^7.14.5", - "@babel/helper-plugin-utils": "^7.14.5" - } - }, - "@babel/plugin-transform-named-capturing-groups-regex": { - "version": "7.14.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-named-capturing-groups-regex/-/plugin-transform-named-capturing-groups-regex-7.14.7.tgz", - "integrity": "sha512-DTNOTaS7TkW97xsDMrp7nycUVh6sn/eq22VaxWfEdzuEbRsiaOU0pqU7DlyUGHVsbQbSghvjKRpEl+nUCKGQSg==", - "dev": true, - "requires": { - "@babel/helper-create-regexp-features-plugin": "^7.14.5" - } - }, - "@babel/plugin-transform-new-target": { - "version": "7.14.5", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-new-target/-/plugin-transform-new-target-7.14.5.tgz", - "integrity": "sha512-Nx054zovz6IIRWEB49RDRuXGI4Gy0GMgqG0cII9L3MxqgXz/+rgII+RU58qpo4g7tNEx1jG7rRVH4ihZoP4esQ==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.14.5" - } - }, - "@babel/plugin-transform-object-super": { - "version": "7.14.5", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-object-super/-/plugin-transform-object-super-7.14.5.tgz", - "integrity": "sha512-MKfOBWzK0pZIrav9z/hkRqIk/2bTv9qvxHzPQc12RcVkMOzpIKnFCNYJip00ssKWYkd8Sf5g0Wr7pqJ+cmtuFg==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.14.5", - "@babel/helper-replace-supers": "^7.14.5" - } - }, - "@babel/plugin-transform-parameters": { - "version": "7.14.5", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-parameters/-/plugin-transform-parameters-7.14.5.tgz", - "integrity": "sha512-Tl7LWdr6HUxTmzQtzuU14SqbgrSKmaR77M0OKyq4njZLQTPfOvzblNKyNkGwOfEFCEx7KeYHQHDI0P3F02IVkA==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.14.5" - } - }, - "@babel/plugin-transform-property-literals": { - "version": "7.14.5", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-property-literals/-/plugin-transform-property-literals-7.14.5.tgz", - "integrity": "sha512-r1uilDthkgXW8Z1vJz2dKYLV1tuw2xsbrp3MrZmD99Wh9vsfKoob+JTgri5VUb/JqyKRXotlOtwgu4stIYCmnw==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.14.5" - } - }, - "@babel/plugin-transform-regenerator": { - "version": "7.14.5", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-regenerator/-/plugin-transform-regenerator-7.14.5.tgz", - "integrity": "sha512-NVIY1W3ITDP5xQl50NgTKlZ0GrotKtLna08/uGY6ErQt6VEQZXla86x/CTddm5gZdcr+5GSsvMeTmWA5Ii6pkg==", - "dev": true, - "requires": { - "regenerator-transform": "^0.14.2" - } - }, - "@babel/plugin-transform-reserved-words": { - "version": "7.14.5", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-reserved-words/-/plugin-transform-reserved-words-7.14.5.tgz", - "integrity": "sha512-cv4F2rv1nD4qdexOGsRQXJrOcyb5CrgjUH9PKrrtyhSDBNWGxd0UIitjyJiWagS+EbUGjG++22mGH1Pub8D6Vg==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.14.5" - } - }, - "@babel/plugin-transform-shorthand-properties": { - "version": "7.14.5", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-shorthand-properties/-/plugin-transform-shorthand-properties-7.14.5.tgz", - "integrity": "sha512-xLucks6T1VmGsTB+GWK5Pl9Jl5+nRXD1uoFdA5TSO6xtiNjtXTjKkmPdFXVLGlK5A2/or/wQMKfmQ2Y0XJfn5g==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.14.5" - } - }, - "@babel/plugin-transform-spread": { - "version": "7.14.6", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-spread/-/plugin-transform-spread-7.14.6.tgz", - "integrity": "sha512-Zr0x0YroFJku7n7+/HH3A2eIrGMjbmAIbJSVv0IZ+t3U2WUQUA64S/oeied2e+MaGSjmt4alzBCsK9E8gh+fag==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.14.5", - "@babel/helper-skip-transparent-expression-wrappers": "^7.14.5" - } - }, - "@babel/plugin-transform-sticky-regex": { - "version": "7.14.5", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-sticky-regex/-/plugin-transform-sticky-regex-7.14.5.tgz", - "integrity": "sha512-Z7F7GyvEMzIIbwnziAZmnSNpdijdr4dWt+FJNBnBLz5mwDFkqIXU9wmBcWWad3QeJF5hMTkRe4dAq2sUZiG+8A==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.14.5" - } - }, - "@babel/plugin-transform-template-literals": { - "version": "7.14.5", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-template-literals/-/plugin-transform-template-literals-7.14.5.tgz", - "integrity": "sha512-22btZeURqiepOfuy/VkFr+zStqlujWaarpMErvay7goJS6BWwdd6BY9zQyDLDa4x2S3VugxFb162IZ4m/S/+Gg==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.14.5" - } - }, - "@babel/plugin-transform-typeof-symbol": { - "version": "7.14.5", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-typeof-symbol/-/plugin-transform-typeof-symbol-7.14.5.tgz", - "integrity": "sha512-lXzLD30ffCWseTbMQzrvDWqljvZlHkXU+CnseMhkMNqU1sASnCsz3tSzAaH3vCUXb9PHeUb90ZT1BdFTm1xxJw==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.14.5" - } - }, - "@babel/plugin-transform-unicode-escapes": { - "version": "7.14.5", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-unicode-escapes/-/plugin-transform-unicode-escapes-7.14.5.tgz", - "integrity": "sha512-crTo4jATEOjxj7bt9lbYXcBAM3LZaUrbP2uUdxb6WIorLmjNKSpHfIybgY4B8SRpbf8tEVIWH3Vtm7ayCrKocA==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.14.5" - } - }, - "@babel/plugin-transform-unicode-regex": { - "version": "7.14.5", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-unicode-regex/-/plugin-transform-unicode-regex-7.14.5.tgz", - "integrity": "sha512-UygduJpC5kHeCiRw/xDVzC+wj8VaYSoKl5JNVmbP7MadpNinAm3SvZCxZ42H37KZBKztz46YC73i9yV34d0Tzw==", - "dev": true, - "requires": { - "@babel/helper-create-regexp-features-plugin": "^7.14.5", - "@babel/helper-plugin-utils": "^7.14.5" - } - }, - "@babel/preset-env": { - "version": "7.14.7", - "resolved": "https://registry.npmjs.org/@babel/preset-env/-/preset-env-7.14.7.tgz", - "integrity": "sha512-itOGqCKLsSUl0Y+1nSfhbuuOlTs0MJk2Iv7iSH+XT/mR8U1zRLO7NjWlYXB47yhK4J/7j+HYty/EhFZDYKa/VA==", - "dev": true, - "requires": { - "@babel/compat-data": "^7.14.7", - "@babel/helper-compilation-targets": "^7.14.5", - "@babel/helper-plugin-utils": "^7.14.5", - "@babel/helper-validator-option": "^7.14.5", - "@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining": "^7.14.5", - "@babel/plugin-proposal-async-generator-functions": "^7.14.7", - "@babel/plugin-proposal-class-properties": "^7.14.5", - "@babel/plugin-proposal-class-static-block": "^7.14.5", - "@babel/plugin-proposal-dynamic-import": "^7.14.5", - "@babel/plugin-proposal-export-namespace-from": "^7.14.5", - "@babel/plugin-proposal-json-strings": "^7.14.5", - "@babel/plugin-proposal-logical-assignment-operators": "^7.14.5", - "@babel/plugin-proposal-nullish-coalescing-operator": "^7.14.5", - "@babel/plugin-proposal-numeric-separator": "^7.14.5", - "@babel/plugin-proposal-object-rest-spread": "^7.14.7", - "@babel/plugin-proposal-optional-catch-binding": "^7.14.5", - "@babel/plugin-proposal-optional-chaining": "^7.14.5", - "@babel/plugin-proposal-private-methods": "^7.14.5", - "@babel/plugin-proposal-private-property-in-object": "^7.14.5", - "@babel/plugin-proposal-unicode-property-regex": "^7.14.5", - "@babel/plugin-syntax-async-generators": "^7.8.4", - "@babel/plugin-syntax-class-properties": "^7.12.13", - "@babel/plugin-syntax-class-static-block": "^7.14.5", - "@babel/plugin-syntax-dynamic-import": "^7.8.3", - "@babel/plugin-syntax-export-namespace-from": "^7.8.3", - "@babel/plugin-syntax-json-strings": "^7.8.3", - "@babel/plugin-syntax-logical-assignment-operators": "^7.10.4", - "@babel/plugin-syntax-nullish-coalescing-operator": "^7.8.3", - "@babel/plugin-syntax-numeric-separator": "^7.10.4", - "@babel/plugin-syntax-object-rest-spread": "^7.8.3", - "@babel/plugin-syntax-optional-catch-binding": "^7.8.3", - "@babel/plugin-syntax-optional-chaining": "^7.8.3", - "@babel/plugin-syntax-private-property-in-object": "^7.14.5", - "@babel/plugin-syntax-top-level-await": "^7.14.5", - "@babel/plugin-transform-arrow-functions": "^7.14.5", - "@babel/plugin-transform-async-to-generator": "^7.14.5", - "@babel/plugin-transform-block-scoped-functions": "^7.14.5", - "@babel/plugin-transform-block-scoping": "^7.14.5", - "@babel/plugin-transform-classes": "^7.14.5", - "@babel/plugin-transform-computed-properties": "^7.14.5", - "@babel/plugin-transform-destructuring": "^7.14.7", - "@babel/plugin-transform-dotall-regex": "^7.14.5", - "@babel/plugin-transform-duplicate-keys": "^7.14.5", - "@babel/plugin-transform-exponentiation-operator": "^7.14.5", - "@babel/plugin-transform-for-of": "^7.14.5", - "@babel/plugin-transform-function-name": "^7.14.5", - "@babel/plugin-transform-literals": "^7.14.5", - "@babel/plugin-transform-member-expression-literals": "^7.14.5", - "@babel/plugin-transform-modules-amd": "^7.14.5", - "@babel/plugin-transform-modules-commonjs": "^7.14.5", - "@babel/plugin-transform-modules-systemjs": "^7.14.5", - "@babel/plugin-transform-modules-umd": "^7.14.5", - "@babel/plugin-transform-named-capturing-groups-regex": "^7.14.7", - "@babel/plugin-transform-new-target": "^7.14.5", - "@babel/plugin-transform-object-super": "^7.14.5", - "@babel/plugin-transform-parameters": "^7.14.5", - "@babel/plugin-transform-property-literals": "^7.14.5", - "@babel/plugin-transform-regenerator": "^7.14.5", - "@babel/plugin-transform-reserved-words": "^7.14.5", - "@babel/plugin-transform-shorthand-properties": "^7.14.5", - "@babel/plugin-transform-spread": "^7.14.6", - "@babel/plugin-transform-sticky-regex": "^7.14.5", - "@babel/plugin-transform-template-literals": "^7.14.5", - "@babel/plugin-transform-typeof-symbol": "^7.14.5", - "@babel/plugin-transform-unicode-escapes": "^7.14.5", - "@babel/plugin-transform-unicode-regex": "^7.14.5", - "@babel/preset-modules": "^0.1.4", - "@babel/types": "^7.14.5", - "babel-plugin-polyfill-corejs2": "^0.2.2", - "babel-plugin-polyfill-corejs3": "^0.2.2", - "babel-plugin-polyfill-regenerator": "^0.2.2", - "core-js-compat": "^3.15.0", - "semver": "^6.3.0" - } - }, - "@babel/preset-modules": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/@babel/preset-modules/-/preset-modules-0.1.4.tgz", - "integrity": "sha512-J36NhwnfdzpmH41M1DrnkkgAqhZaqr/NBdPfQ677mLzlaXo+oDiv1deyCDtgAhz8p328otdob0Du7+xgHGZbKg==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.0.0", - "@babel/plugin-proposal-unicode-property-regex": "^7.4.4", - "@babel/plugin-transform-dotall-regex": "^7.4.4", - "@babel/types": "^7.4.4", - "esutils": "^2.0.2" - } - }, - "@babel/runtime": { - "version": "7.14.6", - "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.14.6.tgz", - "integrity": "sha512-/PCB2uJ7oM44tz8YhC4Z/6PeOKXp4K588f+5M3clr1M4zbqztlo0XEfJ2LEzj/FgwfgGcIdl8n7YYjTCI0BYwg==", - "dev": true, - "requires": { - "regenerator-runtime": "^0.13.4" - }, - "dependencies": { - "regenerator-runtime": { - "version": "0.13.7", - "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.7.tgz", - "integrity": "sha512-a54FxoJDIr27pgf7IgeQGxmqUNYrcV338lf/6gH456HZ/PhX+5BcwHXG9ajESmwe6WRO0tAzRUrRmNONWgkrew==", - "dev": true - } - } - }, - "@babel/runtime-corejs3": { - "version": "7.14.7", - "resolved": "https://registry.npmjs.org/@babel/runtime-corejs3/-/runtime-corejs3-7.14.7.tgz", - "integrity": "sha512-Wvzcw4mBYbTagyBVZpAJWI06auSIj033T/yNE0Zn1xcup83MieCddZA7ls3kme17L4NOGBrQ09Q+nKB41RLWBA==", - "dev": true, - "requires": { - "core-js-pure": "^3.15.0", - "regenerator-runtime": "^0.13.4" - }, - "dependencies": { - "regenerator-runtime": { - "version": "0.13.7", - "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.7.tgz", - "integrity": "sha512-a54FxoJDIr27pgf7IgeQGxmqUNYrcV338lf/6gH456HZ/PhX+5BcwHXG9ajESmwe6WRO0tAzRUrRmNONWgkrew==", - "dev": true - } - } - }, - "@babel/template": { - "version": "7.14.5", - "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.14.5.tgz", - "integrity": "sha512-6Z3Po85sfxRGachLULUhOmvAaOo7xCvqGQtxINai2mEGPFm6pQ4z5QInFnUrRpfoSV60BnjyF5F3c+15fxFV1g==", - "dev": true, - "requires": { - "@babel/code-frame": "^7.14.5", - "@babel/parser": "^7.14.5", - "@babel/types": "^7.14.5" - } - }, - "@babel/traverse": { - "version": "7.14.7", - "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.14.7.tgz", - "integrity": "sha512-9vDr5NzHu27wgwejuKL7kIOm4bwEtaPQ4Z6cpCmjSuaRqpH/7xc4qcGEscwMqlkwgcXl6MvqoAjZkQ24uSdIZQ==", - "dev": true, - "requires": { - "@babel/code-frame": "^7.14.5", - "@babel/generator": "^7.14.5", - "@babel/helper-function-name": "^7.14.5", - "@babel/helper-hoist-variables": "^7.14.5", - "@babel/helper-split-export-declaration": "^7.14.5", - "@babel/parser": "^7.14.7", - "@babel/types": "^7.14.5", - "debug": "^4.1.0", - "globals": "^11.1.0" - }, - "dependencies": { - "debug": { - "version": "4.3.1", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.1.tgz", - "integrity": "sha512-doEwdvm4PCeK4K3RQN2ZC2BYUBaxwLARCqZmMjtF8a51J2Rb0xpVloFRnCODwqjpwnAoao4pelN8l3RJdv3gRQ==", - "dev": true, - "requires": { - "ms": "2.1.2" - } - }, - "ms": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", - "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", - "dev": true - } - } - }, - "@babel/types": { - "version": "7.14.5", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.14.5.tgz", - "integrity": "sha512-M/NzBpEL95I5Hh4dwhin5JlE7EzO5PHMAuzjxss3tiOBD46KfQvVedN/3jEPZvdRvtsK2222XfdHogNIttFgcg==", - "dev": true, - "requires": { - "@babel/helper-validator-identifier": "^7.14.5", - "to-fast-properties": "^2.0.0" - } - }, - "@eslint/eslintrc": { - "version": "0.4.2", - "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-0.4.2.tgz", - "integrity": "sha512-8nmGq/4ycLpIwzvhI4tNDmQztZ8sp+hI7cyG8i1nQDhkAbRzHpXPidRAHlNvCZQpJTKw5ItIpMw9RSToGF00mg==", - "dev": true, - "requires": { - "ajv": "^6.12.4", - "debug": "^4.1.1", - "espree": "^7.3.0", - "globals": "^13.9.0", - "ignore": "^4.0.6", - "import-fresh": "^3.2.1", - "js-yaml": "^3.13.1", - "minimatch": "^3.0.4", - "strip-json-comments": "^3.1.1" - }, - "dependencies": { - "ajv": { - "version": "6.12.6", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", - "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", - "dev": true, - "requires": { - "fast-deep-equal": "^3.1.1", - "fast-json-stable-stringify": "^2.0.0", - "json-schema-traverse": "^0.4.1", - "uri-js": "^4.2.2" - } - }, - "argparse": { - "version": "1.0.10", - "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", - "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==", - "dev": true, - "requires": { - "sprintf-js": "~1.0.2" - } - }, - "debug": { - "version": "4.3.1", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.1.tgz", - "integrity": "sha512-doEwdvm4PCeK4K3RQN2ZC2BYUBaxwLARCqZmMjtF8a51J2Rb0xpVloFRnCODwqjpwnAoao4pelN8l3RJdv3gRQ==", - "dev": true, - "requires": { - "ms": "2.1.2" - } - }, - "globals": { - "version": "13.9.0", - "resolved": "https://registry.npmjs.org/globals/-/globals-13.9.0.tgz", - "integrity": "sha512-74/FduwI/JaIrr1H8e71UbDE+5x7pIPs1C2rrwC52SszOo043CsWOZEMW7o2Y58xwm9b+0RBKDxY5n2sUpEFxA==", - "dev": true, - "requires": { - "type-fest": "^0.20.2" - } - }, - "js-yaml": { - "version": "3.14.1", - "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.14.1.tgz", - "integrity": "sha512-okMH7OXXJ7YrN9Ok3/SXrnu4iX9yOk+25nqX4imS2npuvTYDmo/QEZoqwZkYaIDk3jVvBOTOIEgEhaLOynBS9g==", - "dev": true, - "requires": { - "argparse": "^1.0.7", - "esprima": "^4.0.0" - } - }, - "ms": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", - "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", - "dev": true - }, - "type-fest": { - "version": "0.20.2", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.20.2.tgz", - "integrity": "sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==", - "dev": true - } - } - }, - "@gulp-sourcemaps/identity-map": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/@gulp-sourcemaps/identity-map/-/identity-map-2.0.1.tgz", - "integrity": "sha512-Tb+nSISZku+eQ4X1lAkevcQa+jknn/OVUgZ3XCxEKIsLsqYuPoJwJOPQeaOk75X3WPftb29GWY1eqE7GLsXb1Q==", - "dev": true, - "requires": { - "acorn": "^6.4.1", - "normalize-path": "^3.0.0", - "postcss": "^7.0.16", - "source-map": "^0.6.0", - "through2": "^3.0.1" - }, - "dependencies": { - "acorn": { - "version": "6.4.2", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-6.4.2.tgz", - "integrity": "sha512-XtGIhXwF8YM8bJhGxG5kXgjkEuNGLTkoYqVE+KMR+aspr4KGYmKYg7yUe3KghyQ9yheNwLnjmzh/7+gfDBmHCQ==", - "dev": true - }, - "inherits": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", - "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", - "dev": true - }, - "postcss": { - "version": "7.0.36", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.36.tgz", - "integrity": "sha512-BebJSIUMwJHRH0HAQoxN4u1CN86glsrwsW0q7T+/m44eXOUAxSNdHRkNZPYz5vVUbg17hFgOQDE7fZk7li3pZw==", - "dev": true, - "requires": { - "chalk": "^2.4.2", - "source-map": "^0.6.1", - "supports-color": "^6.1.0" - } - }, - "source-map": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", - "dev": true - }, - "supports-color": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-6.1.0.tgz", - "integrity": "sha512-qe1jfm1Mg7Nq/NSh6XE24gPXROEVsWHxC1LIx//XNlD9iw7YZQGjZNjYN7xGaEG6iKdA8EtNFW6R0gjnVXp+wQ==", - "dev": true, - "requires": { - "has-flag": "^3.0.0" - } - }, - "through2": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/through2/-/through2-3.0.2.tgz", - "integrity": "sha512-enaDQ4MUyP2W6ZyT6EsMzqBPZaM/avg8iuo+l2d3QCs0J+6RaqkHV/2/lOwDTueBHeJ/2LG9lrLW3d5rWPucuQ==", - "dev": true, - "requires": { - "inherits": "^2.0.4", - "readable-stream": "2 || 3" - } - } - } - }, - "@gulp-sourcemaps/map-sources": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/@gulp-sourcemaps/map-sources/-/map-sources-1.0.0.tgz", - "integrity": "sha1-iQrnxdjId/bThIYCFazp1+yUW9o=", - "dev": true, - "requires": { - "normalize-path": "^2.0.1", - "through2": "^2.0.3" - }, - "dependencies": { - "normalize-path": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-2.1.1.tgz", - "integrity": "sha1-GrKLVW4Zg2Oowab35vogE3/mrtk=", - "dev": true, - "requires": { - "remove-trailing-separator": "^1.0.1" - } - }, - "readable-stream": { - "version": "2.3.7", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz", - "integrity": "sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==", - "dev": true, - "requires": { - "core-util-is": "~1.0.0", - "inherits": "~2.0.3", - "isarray": "~1.0.0", - "process-nextick-args": "~2.0.0", - "safe-buffer": "~5.1.1", - "string_decoder": "~1.1.1", - "util-deprecate": "~1.0.1" - } - }, - "string_decoder": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", - "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", - "dev": true, - "requires": { - "safe-buffer": "~5.1.0" - } - }, - "through2": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/through2/-/through2-2.0.5.tgz", - "integrity": "sha512-/mrRod8xqpA+IHSLyGCQ2s8SPHiCDEeQJSep1jqLYeEUClOFG2Qsh+4FU6G9VeqpZnGW/Su8LQGc4YKni5rYSQ==", - "dev": true, - "requires": { - "readable-stream": "~2.3.6", - "xtend": "~4.0.1" - } - } - } - }, - "@istanbuljs/schema": { - "version": "0.1.3", - "resolved": "https://registry.npmjs.org/@istanbuljs/schema/-/schema-0.1.3.tgz", - "integrity": "sha512-ZXRY4jNvVgSVQ8DL3LTcakaAtXwTVUxE81hslsyD2AtoXW/wVob10HkOJ1X/pAlcI7D+2YoZKg5do8G/w6RYgA==", - "dev": true - }, - "@jest/types": { - "version": "27.0.2", - "resolved": "https://registry.npmjs.org/@jest/types/-/types-27.0.2.tgz", - "integrity": "sha512-XpjCtJ/99HB4PmyJ2vgmN7vT+JLP7RW1FBT9RgnMFS4Dt7cvIyBee8O3/j98aUZ34ZpenPZFqmaaObWSeL65dg==", - "dev": true, - "requires": { - "@types/istanbul-lib-coverage": "^2.0.0", - "@types/istanbul-reports": "^3.0.0", - "@types/node": "*", - "@types/yargs": "^16.0.0", - "chalk": "^4.0.0" - }, - "dependencies": { - "ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "dev": true, - "requires": { - "color-convert": "^2.0.1" - } - }, - "chalk": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.1.tgz", - "integrity": "sha512-diHzdDKxcU+bAsUboHLPEDQiw0qEe0qd7SYUn3HgcFlWgbDcfLGswOHYeGrHKzG9z6UYf01d9VFMfZxPM1xZSg==", - "dev": true, - "requires": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - } - }, - "color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "dev": true, - "requires": { - "color-name": "~1.1.4" - } - }, - "color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", - "dev": true - }, - "has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", - "dev": true - }, - "supports-color": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", - "dev": true, - "requires": { - "has-flag": "^4.0.0" - } - } - } - }, - "@jsdevtools/coverage-istanbul-loader": { - "version": "3.0.5", - "resolved": "https://registry.npmjs.org/@jsdevtools/coverage-istanbul-loader/-/coverage-istanbul-loader-3.0.5.tgz", - "integrity": "sha512-EUCPEkaRPvmHjWAAZkWMT7JDzpw7FKB00WTISaiXsbNOd5hCHg77XLA8sLYLFDo1zepYLo2w7GstN8YBqRXZfA==", - "dev": true, - "requires": { - "convert-source-map": "^1.7.0", - "istanbul-lib-instrument": "^4.0.3", - "loader-utils": "^2.0.0", - "merge-source-map": "^1.1.0", - "schema-utils": "^2.7.0" - } - }, - "@sindresorhus/is": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/@sindresorhus/is/-/is-4.0.1.tgz", - "integrity": "sha512-Qm9hBEBu18wt1PO2flE7LPb30BHMQt1eQgbV76YntdNk73XZGpn3izvGTYxbGgzXKgbCjiia0uxTd3aTNQrY/g==", - "dev": true - }, - "@sinonjs/commons": { - "version": "1.8.3", - "resolved": "https://registry.npmjs.org/@sinonjs/commons/-/commons-1.8.3.tgz", - "integrity": "sha512-xkNcLAn/wZaX14RPlwizcKicDk9G3F8m2nU3L7Ukm5zBgTwiT0wsoFAHx9Jq56fJA1z/7uKGtCRu16sOUCLIHQ==", - "dev": true, - "requires": { - "type-detect": "4.0.8" - } - }, - "@sinonjs/formatio": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/@sinonjs/formatio/-/formatio-2.0.0.tgz", - "integrity": "sha512-ls6CAMA6/5gG+O/IdsBcblvnd8qcO/l1TYoNeAzp3wcISOxlPXQEus0mLcdwazEkWjaBdaJ3TaxmNgCLWwvWzg==", - "dev": true, - "requires": { - "samsam": "1.3.0" - } - }, - "@sinonjs/samsam": { - "version": "3.3.3", - "resolved": "https://registry.npmjs.org/@sinonjs/samsam/-/samsam-3.3.3.tgz", - "integrity": "sha512-bKCMKZvWIjYD0BLGnNrxVuw4dkWCYsLqFOUWw8VgKF/+5Y+mE7LfHWPIYoDXowH+3a9LsWDMo0uAP8YDosPvHQ==", - "dev": true, - "requires": { - "@sinonjs/commons": "^1.3.0", - "array-from": "^2.1.1", - "lodash": "^4.17.15" - } - }, - "@sinonjs/text-encoding": { - "version": "0.7.1", - "resolved": "https://registry.npmjs.org/@sinonjs/text-encoding/-/text-encoding-0.7.1.tgz", - "integrity": "sha512-+iTbntw2IZPb/anVDbypzfQa+ay64MW0Zo8aJ8gZPWMMK6/OubMVb6lUPMagqjOPnmtauXnFCACVl3O7ogjeqQ==", - "dev": true - }, - "@szmarczak/http-timer": { - "version": "4.0.5", - "resolved": "https://registry.npmjs.org/@szmarczak/http-timer/-/http-timer-4.0.5.tgz", - "integrity": "sha512-PyRA9sm1Yayuj5OIoJ1hGt2YISX45w9WcFbh6ddT0Z/0yaFxOtGLInr4jUfU1EAFVs0Yfyfev4RNwBlUaHdlDQ==", - "dev": true, - "requires": { - "defer-to-connect": "^2.0.0" - } - }, - "@types/aria-query": { - "version": "4.2.1", - "resolved": "https://registry.npmjs.org/@types/aria-query/-/aria-query-4.2.1.tgz", - "integrity": "sha512-S6oPal772qJZHoRZLFc/XoZW2gFvwXusYUmXPXkgxJLuEk2vOt7jc4Yo6z/vtI0EBkbPBVrJJ0B+prLIKiWqHg==", - "dev": true - }, - "@types/cacheable-request": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/@types/cacheable-request/-/cacheable-request-6.0.1.tgz", - "integrity": "sha512-ykFq2zmBGOCbpIXtoVbz4SKY5QriWPh3AjyU4G74RYbtt5yOc5OfaY75ftjg7mikMOla1CTGpX3lLbuJh8DTrQ==", - "dev": true, - "requires": { - "@types/http-cache-semantics": "*", - "@types/keyv": "*", - "@types/node": "*", - "@types/responselike": "*" - } - }, - "@types/component-emitter": { - "version": "1.2.10", - "resolved": "https://registry.npmjs.org/@types/component-emitter/-/component-emitter-1.2.10.tgz", - "integrity": "sha512-bsjleuRKWmGqajMerkzox19aGbscQX5rmmvvXl3wlIp5gMG1HgkiwPxsN5p070fBDKTNSPgojVbuY1+HWMbFhg==", - "dev": true - }, - "@types/cookie": { - "version": "0.4.0", - "resolved": "https://registry.npmjs.org/@types/cookie/-/cookie-0.4.0.tgz", - "integrity": "sha512-y7mImlc/rNkvCRmg8gC3/lj87S7pTUIJ6QGjwHR9WQJcFs+ZMTOaoPrkdFA/YdbuqVEmEbb5RdhVxMkAcgOnpg==", - "dev": true - }, - "@types/cors": { - "version": "2.8.10", - "resolved": "https://registry.npmjs.org/@types/cors/-/cors-2.8.10.tgz", - "integrity": "sha512-C7srjHiVG3Ey1nR6d511dtDkCEjxuN9W1HWAEjGq8kpcwmNM6JJkpC0xvabM7BXTG2wDq8Eu33iH9aQKa7IvLQ==", - "dev": true - }, - "@types/easy-table": { - "version": "0.0.32", - "resolved": "https://registry.npmjs.org/@types/easy-table/-/easy-table-0.0.32.tgz", - "integrity": "sha512-zKh0f/ixYFnr3Ldf5ZJTi1ZpnRqAynTTtVyGvWDf/TT12asE8ac98t3/WGWfFdRPp/qsccxg82C/Kl3NPNhqEw==", - "dev": true - }, - "@types/ejs": { - "version": "3.0.6", - "resolved": "https://registry.npmjs.org/@types/ejs/-/ejs-3.0.6.tgz", - "integrity": "sha512-fj1hi+ZSW0xPLrJJD+YNwIh9GZbyaIepG26E/gXvp8nCa2pYokxUYO1sK9qjGxp2g8ryZYuon7wmjpwE2cyASQ==", - "dev": true - }, - "@types/estree": { - "version": "0.0.48", - "resolved": "https://registry.npmjs.org/@types/estree/-/estree-0.0.48.tgz", - "integrity": "sha512-LfZwXoGUDo0C3me81HXgkBg5CTQYb6xzEl+fNmbO4JdRiSKQ8A0GD1OBBvKAIsbCUgoyAty7m99GqqMQe784ew==", - "dev": true, - "optional": true - }, - "@types/expect": { - "version": "1.20.4", - "resolved": "https://registry.npmjs.org/@types/expect/-/expect-1.20.4.tgz", - "integrity": "sha512-Q5Vn3yjTDyCMV50TB6VRIbQNxSE4OmZR86VSbGaNpfUolm0iePBB4KdEEHmxoY5sT2+2DIvXW0rvMDP2nHZ4Mg==", - "dev": true - }, - "@types/fibers": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/@types/fibers/-/fibers-3.1.0.tgz", - "integrity": "sha512-1o3I9xtk2PZFxwaLCC6gTaBfBZ5rvw/DSZZPK89fwuwO6LNrzSbC6rEs1xI0bQ3fCRWmO+uNJQQeD2J56oTMDg==", - "dev": true - }, - "@types/fs-extra": { - "version": "9.0.11", - "resolved": "https://registry.npmjs.org/@types/fs-extra/-/fs-extra-9.0.11.tgz", - "integrity": "sha512-mZsifGG4QeQ7hlkhO56u7zt/ycBgGxSVsFI/6lGTU34VtwkiqrrSDgw0+ygs8kFGWcXnFQWMrzF2h7TtDFNixA==", - "dev": true, - "requires": { - "@types/node": "*" - } - }, - "@types/http-cache-semantics": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/@types/http-cache-semantics/-/http-cache-semantics-4.0.0.tgz", - "integrity": "sha512-c3Xy026kOF7QOTn00hbIllV1dLR9hG9NkSrLQgCVs8NF6sBU+VGWjD3wLPhmh1TYAc7ugCFsvHYMN4VcBN1U1A==", - "dev": true - }, - "@types/inquirer": { - "version": "7.3.2", - "resolved": "https://registry.npmjs.org/@types/inquirer/-/inquirer-7.3.2.tgz", - "integrity": "sha512-EkeX/hU0SWinA2c7Qu/+6+7KbepFPYJcjankUgtA/VSY6BlVHybL0Cgyey9PDbXwhNXnNGBLU3t+MORp23RgAw==", - "dev": true, - "requires": { - "@types/through": "*", - "rxjs": "^6.4.0" - } - }, - "@types/istanbul-lib-coverage": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/@types/istanbul-lib-coverage/-/istanbul-lib-coverage-2.0.3.tgz", - "integrity": "sha512-sz7iLqvVUg1gIedBOvlkxPlc8/uVzyS5OwGz1cKjXzkl3FpL3al0crU8YGU1WoHkxn0Wxbw5tyi6hvzJKNzFsw==", - "dev": true - }, - "@types/istanbul-lib-report": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/@types/istanbul-lib-report/-/istanbul-lib-report-3.0.0.tgz", - "integrity": "sha512-plGgXAPfVKFoYfa9NpYDAkseG+g6Jr294RqeqcqDixSbU34MZVJRi/P+7Y8GDpzkEwLaGZZOpKIEmeVZNtKsrg==", - "dev": true, - "requires": { - "@types/istanbul-lib-coverage": "*" - } - }, - "@types/istanbul-reports": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/@types/istanbul-reports/-/istanbul-reports-3.0.1.tgz", - "integrity": "sha512-c3mAZEuK0lvBp8tmuL74XRKn1+y2dcwOUpH7x4WrF6gk1GIgiluDRgMYQtw2OFcBvAJWlt6ASU3tSqxp0Uu0Aw==", - "dev": true, - "requires": { - "@types/istanbul-lib-report": "*" - } - }, - "@types/json-schema": { - "version": "7.0.7", - "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.7.tgz", - "integrity": "sha512-cxWFQVseBm6O9Gbw1IWb8r6OS4OhSt3hPZLkFApLjM8TEXROBuQGLAH2i2gZpcXdLBIrpXuTDhH7Vbm1iXmNGA==", - "dev": true - }, - "@types/json5": { - "version": "0.0.29", - "resolved": "https://registry.npmjs.org/@types/json5/-/json5-0.0.29.tgz", - "integrity": "sha1-7ihweulOEdK4J7y+UnC86n8+ce4=", - "dev": true - }, - "@types/keyv": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/@types/keyv/-/keyv-3.1.1.tgz", - "integrity": "sha512-MPtoySlAZQ37VoLaPcTHCu1RWJ4llDkULYZIzOYxlhxBqYPB0RsRlmMU0R6tahtFe27mIdkHV+551ZWV4PLmVw==", - "dev": true, - "requires": { - "@types/node": "*" - } - }, - "@types/lodash": { - "version": "4.14.170", - "resolved": "https://registry.npmjs.org/@types/lodash/-/lodash-4.14.170.tgz", - "integrity": "sha512-bpcvu/MKHHeYX+qeEN8GE7DIravODWdACVA1ctevD8CN24RhPZIKMn9ntfAsrvLfSX3cR5RrBKAbYm9bGs0A+Q==", - "dev": true - }, - "@types/lodash.flattendeep": { - "version": "4.4.6", - "resolved": "https://registry.npmjs.org/@types/lodash.flattendeep/-/lodash.flattendeep-4.4.6.tgz", - "integrity": "sha512-uLm2MaRVlqJSGsMK0RZpP5T3KqReq+9WbYDHCUhBhp98v56hMG/Yht52bsoTSui9xz2mUvQ9NfG3LrNGDL92Ng==", - "dev": true, - "requires": { - "@types/lodash": "*" - } - }, - "@types/lodash.pickby": { - "version": "4.6.6", - "resolved": "https://registry.npmjs.org/@types/lodash.pickby/-/lodash.pickby-4.6.6.tgz", - "integrity": "sha512-NFa13XxlMd9eFi0UFZFWIztpMpXhozbijrx3Yb1viYZphT7jyopIFVoIRF4eYMjruWNEG1rnyrRmg/8ej9T8Iw==", - "dev": true, - "requires": { - "@types/lodash": "*" - } - }, - "@types/lodash.union": { - "version": "4.6.6", - "resolved": "https://registry.npmjs.org/@types/lodash.union/-/lodash.union-4.6.6.tgz", - "integrity": "sha512-Wu0ZEVNcyCz8eAn6TlUbYWZoGbH9E+iOHxAZbwUoCEXdUiy6qpcz5o44mMXViM4vlPLLCPlkAubEP1gokoSZaw==", - "dev": true, - "requires": { - "@types/lodash": "*" - } - }, - "@types/mdast": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/@types/mdast/-/mdast-3.0.3.tgz", - "integrity": "sha512-SXPBMnFVQg1s00dlMCc/jCdvPqdE4mXaMMCeRlxLDmTAEoegHT53xKtkDnzDTOcmMHUfcjyf36/YYZ6SxRdnsw==", - "dev": true, - "requires": { - "@types/unist": "*" - } - }, - "@types/minimist": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/@types/minimist/-/minimist-1.2.1.tgz", - "integrity": "sha512-fZQQafSREFyuZcdWFAExYjBiCL7AUCdgsk80iO0q4yihYYdcIiH28CcuPTGFgLOCC8RlW49GSQxdHwZP+I7CNg==", - "dev": true - }, - "@types/mocha": { - "version": "8.2.2", - "resolved": "https://registry.npmjs.org/@types/mocha/-/mocha-8.2.2.tgz", - "integrity": "sha512-Lwh0lzzqT5Pqh6z61P3c3P5nm6fzQK/MMHl9UKeneAeInVflBSz1O2EkX6gM6xfJd7FBXBY5purtLx7fUiZ7Hw==", - "dev": true - }, - "@types/node": { - "version": "15.12.4", - "resolved": "https://registry.npmjs.org/@types/node/-/node-15.12.4.tgz", - "integrity": "sha512-zrNj1+yqYF4WskCMOHwN+w9iuD12+dGm0rQ35HLl9/Ouuq52cEtd0CH9qMgrdNmi5ejC1/V7vKEXYubB+65DkA==", - "dev": true - }, - "@types/normalize-package-data": { - "version": "2.4.0", - "resolved": "https://registry.npmjs.org/@types/normalize-package-data/-/normalize-package-data-2.4.0.tgz", - "integrity": "sha512-f5j5b/Gf71L+dbqxIpQ4Z2WlmI/mPJ0fOkGGmFgtb6sAu97EPczzbS3/tJKxmcYDj55OX6ssqwDAWOHIYDRDGA==", - "dev": true - }, - "@types/puppeteer": { - "version": "5.4.3", - "resolved": "https://registry.npmjs.org/@types/puppeteer/-/puppeteer-5.4.3.tgz", - "integrity": "sha512-3nE8YgR9DIsgttLW+eJf6mnXxq8Ge+27m5SU3knWmrlfl6+KOG0Bf9f7Ua7K+C4BnaTMAh3/UpySqdAYvrsvjg==", - "dev": true, - "requires": { - "@types/node": "*" - } - }, - "@types/recursive-readdir": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/@types/recursive-readdir/-/recursive-readdir-2.2.0.tgz", - "integrity": "sha512-HGk753KRu2N4mWduovY4BLjYq4jTOL29gV2OfGdGxHcPSWGFkC5RRIdk+VTs5XmYd7MVAD+JwKrcb5+5Y7FOCg==", - "dev": true, - "requires": { - "@types/node": "*" - } - }, - "@types/responselike": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/@types/responselike/-/responselike-1.0.0.tgz", - "integrity": "sha512-85Y2BjiufFzaMIlvJDvTTB8Fxl2xfLo4HgmHzVBz08w4wDePCTjYw66PdrolO0kzli3yam/YCgRufyo1DdQVTA==", - "dev": true, - "requires": { - "@types/node": "*" - } - }, - "@types/stack-utils": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/@types/stack-utils/-/stack-utils-2.0.0.tgz", - "integrity": "sha512-RJJrrySY7A8havqpGObOB4W92QXKJo63/jFLLgpvOtsGUqbQZ9Sbgl35KMm1DjC6j7AvmmU2bIno+3IyEaemaw==", - "dev": true - }, - "@types/stream-buffers": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/@types/stream-buffers/-/stream-buffers-3.0.3.tgz", - "integrity": "sha512-NeFeX7YfFZDYsCfbuaOmFQ0OjSmHreKBpp7MQ4alWQBHeh2USLsj7qyMyn9t82kjqIX516CR/5SRHnARduRtbQ==", - "dev": true, - "requires": { - "@types/node": "*" - } - }, - "@types/through": { - "version": "0.0.30", - "resolved": "https://registry.npmjs.org/@types/through/-/through-0.0.30.tgz", - "integrity": "sha512-FvnCJljyxhPM3gkRgWmxmDZyAQSiBQQWLI0A0VFL0K7W1oRUrPJSqNO0NvTnLkBcotdlp3lKvaT0JrnyRDkzOg==", - "dev": true, - "requires": { - "@types/node": "*" - } - }, - "@types/unist": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/@types/unist/-/unist-2.0.3.tgz", - "integrity": "sha512-FvUupuM3rlRsRtCN+fDudtmytGO6iHJuuRKS1Ss0pG5z8oX0diNEw94UEL7hgDbpN94rgaK5R7sWm6RrSkZuAQ==", - "dev": true - }, - "@types/vinyl": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/@types/vinyl/-/vinyl-2.0.4.tgz", - "integrity": "sha512-2o6a2ixaVI2EbwBPg1QYLGQoHK56p/8X/sGfKbFC8N6sY9lfjsMf/GprtkQkSya0D4uRiutRZ2BWj7k3JvLsAQ==", - "dev": true, - "requires": { - "@types/expect": "^1.20.4", - "@types/node": "*" - } - }, - "@types/which": { - "version": "1.3.2", - "resolved": "https://registry.npmjs.org/@types/which/-/which-1.3.2.tgz", - "integrity": "sha512-8oDqyLC7eD4HM307boe2QWKyuzdzWBj56xI/imSl2cpL+U3tCMaTAkMJ4ee5JBZ/FsOJlvRGeIShiZDAl1qERA==", - "dev": true - }, - "@types/yargs": { - "version": "16.0.3", - "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-16.0.3.tgz", - "integrity": "sha512-YlFfTGS+zqCgXuXNV26rOIeETOkXnGQXP/pjjL9P0gO/EP9jTmc7pUBhx+jVEIxpq41RX33GQ7N3DzOSfZoglQ==", - "dev": true, - "requires": { - "@types/yargs-parser": "*" - } - }, - "@types/yargs-parser": { - "version": "20.2.0", - "resolved": "https://registry.npmjs.org/@types/yargs-parser/-/yargs-parser-20.2.0.tgz", - "integrity": "sha512-37RSHht+gzzgYeobbG+KWryeAW8J33Nhr69cjTqSYymXVZEN9NbRYWoYlRtDhHKPVT1FyNKwaTPC1NynKZpzRA==", - "dev": true - }, - "@types/yauzl": { - "version": "2.9.1", - "resolved": "https://registry.npmjs.org/@types/yauzl/-/yauzl-2.9.1.tgz", - "integrity": "sha512-A1b8SU4D10uoPjwb0lnHmmu8wZhR9d+9o2PKBQT2jU5YPTKsxac6M2qGAdY7VcL+dHHhARVUDmeg0rOrcd9EjA==", - "dev": true, - "optional": true, - "requires": { - "@types/node": "*" - } - }, - "@ungap/promise-all-settled": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/@ungap/promise-all-settled/-/promise-all-settled-1.1.2.tgz", - "integrity": "sha512-sL/cEvJWAnClXw0wHk85/2L0G6Sj8UB0Ctc1TEMbKSsmpRosqhwj9gWgFRZSrBr2f9tiXISwNhCPmlfqUqyb9Q==", - "dev": true - }, - "@vue/compiler-core": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/@vue/compiler-core/-/compiler-core-3.1.2.tgz", - "integrity": "sha512-nHmq7vLjq/XM2IMbZUcKWoH5sPXa2uR/nIKZtjbK5F3TcbnYE/zKsrSUR9WZJ03unlwotNBX1OyxVt9HbWD7/Q==", - "dev": true, - "optional": true, - "requires": { - "@babel/parser": "^7.12.0", - "@babel/types": "^7.12.0", - "@vue/shared": "3.1.2", - "estree-walker": "^2.0.1", - "source-map": "^0.6.1" - }, - "dependencies": { - "source-map": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", - "dev": true, - "optional": true - } - } - }, - "@vue/compiler-dom": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/@vue/compiler-dom/-/compiler-dom-3.1.2.tgz", - "integrity": "sha512-k2+SWcWH0jL6WQAX7Or2ONqu5MbtTgTO0dJrvebQYzgqaKMXNI90RNeWeCxS4BnNFMDONpHBeFgbwbnDWIkmRg==", - "dev": true, - "optional": true, - "requires": { - "@vue/compiler-core": "3.1.2", - "@vue/shared": "3.1.2" - } - }, - "@vue/compiler-sfc": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/@vue/compiler-sfc/-/compiler-sfc-3.1.2.tgz", - "integrity": "sha512-SeG/2+DvwejQ7oAiSx8BrDh5qOdqCYHGClPiTvVIHTfSIHiS2JjMbCANdDCjHkTOh/O7WZzo2JhdKm98bRBxTw==", - "dev": true, - "optional": true, - "requires": { - "@babel/parser": "^7.13.9", - "@babel/types": "^7.13.0", - "@types/estree": "^0.0.48", - "@vue/compiler-core": "3.1.2", - "@vue/compiler-dom": "3.1.2", - "@vue/compiler-ssr": "3.1.2", - "@vue/shared": "3.1.2", - "consolidate": "^0.16.0", - "estree-walker": "^2.0.1", - "hash-sum": "^2.0.0", - "lru-cache": "^5.1.1", - "magic-string": "^0.25.7", - "merge-source-map": "^1.1.0", - "postcss": "^8.1.10", - "postcss-modules": "^4.0.0", - "postcss-selector-parser": "^6.0.4", - "source-map": "^0.6.1" - }, - "dependencies": { - "lru-cache": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz", - "integrity": "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==", - "dev": true, - "optional": true, - "requires": { - "yallist": "^3.0.2" - } - }, - "source-map": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", - "dev": true, - "optional": true - }, - "yallist": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz", - "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==", - "dev": true, - "optional": true - } - } - }, - "@vue/compiler-ssr": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/@vue/compiler-ssr/-/compiler-ssr-3.1.2.tgz", - "integrity": "sha512-BwXo9LFk5OSWdMyZQ4bX1ELHX0Z/9F+ld/OaVnpUPzAZCHslBYLvyKUVDwv2C/lpLjRffpC2DOUEdl1+RP1aGg==", - "dev": true, - "optional": true, - "requires": { - "@vue/compiler-dom": "3.1.2", - "@vue/shared": "3.1.2" - } - }, - "@vue/shared": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/@vue/shared/-/shared-3.1.2.tgz", - "integrity": "sha512-EmH/poaDWBPJaPILXNI/1fvUbArJQmmTyVCwvvyDYDFnkPoTclAbHRAtyIvqfez7jybTDn077HTNILpxlsoWhg==", - "dev": true, - "optional": true - }, - "@wdio/browserstack-service": { - "version": "6.12.1", - "resolved": "https://registry.npmjs.org/@wdio/browserstack-service/-/browserstack-service-6.12.1.tgz", - "integrity": "sha512-B4zYlaE8q1Jxb6ctcuUPlKL3inwloETwks+cB9fFtVMDf/HH2Cau3Pi0CoIs8435EI+J4/1LxLHQV2uhzbBSlQ==", - "dev": true, - "requires": { - "@wdio/logger": "6.10.10", - "browserstack-local": "^1.4.5", - "got": "^11.0.2" - } - }, - "@wdio/cli": { - "version": "7.7.4", - "resolved": "https://registry.npmjs.org/@wdio/cli/-/cli-7.7.4.tgz", - "integrity": "sha512-npgpaIpPoSpyef1Pv0ZKyB5FJAZnDJiVphgda0RysXd6J0S3mlwzXrcIoapl28mmeWI6NIvvv65u0sominhsyQ==", - "dev": true, - "requires": { - "@types/ejs": "^3.0.5", - "@types/fs-extra": "^9.0.4", - "@types/inquirer": "^7.3.1", - "@types/lodash.flattendeep": "^4.4.6", - "@types/lodash.pickby": "^4.6.6", - "@types/lodash.union": "^4.6.6", - "@types/recursive-readdir": "^2.2.0", - "@wdio/config": "7.7.3", - "@wdio/logger": "7.7.0", - "@wdio/types": "7.7.3", - "@wdio/utils": "7.7.3", - "async-exit-hook": "^2.0.1", - "chalk": "^4.0.0", - "chokidar": "^3.0.0", - "cli-spinners": "^2.1.0", - "ejs": "^3.0.1", - "fs-extra": "^10.0.0", - "inquirer": "^8.0.0", - "lodash.flattendeep": "^4.4.0", - "lodash.pickby": "^4.6.0", - "lodash.union": "^4.6.0", - "mkdirp": "^1.0.4", - "recursive-readdir": "^2.2.2", - "webdriverio": "7.7.4", - "yargs": "^17.0.0", - "yarn-install": "^1.0.0" - }, - "dependencies": { - "@wdio/logger": { - "version": "7.7.0", - "resolved": "https://registry.npmjs.org/@wdio/logger/-/logger-7.7.0.tgz", - "integrity": "sha512-XX/OkC8NlvsBdhKsb9j7ZbuQtF/Vuo0xf38PXdqYtVezOrYbDuba0hPG++g/IGNuAF34ZbSi+49cvz4u5w92kQ==", - "dev": true, - "requires": { - "chalk": "^4.0.0", - "loglevel": "^1.6.0", - "loglevel-plugin-prefix": "^0.8.4", - "strip-ansi": "^6.0.0" - } - }, - "ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "dev": true, - "requires": { - "color-convert": "^2.0.1" - } - }, - "chalk": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.1.tgz", - "integrity": "sha512-diHzdDKxcU+bAsUboHLPEDQiw0qEe0qd7SYUn3HgcFlWgbDcfLGswOHYeGrHKzG9z6UYf01d9VFMfZxPM1xZSg==", - "dev": true, - "requires": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - } - }, - "color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "dev": true, - "requires": { - "color-name": "~1.1.4" - } - }, - "color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", - "dev": true - }, - "has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", - "dev": true - }, - "supports-color": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", - "dev": true, - "requires": { - "has-flag": "^4.0.0" - } - }, - "yargs": { - "version": "17.0.1", - "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.0.1.tgz", - "integrity": "sha512-xBBulfCc8Y6gLFcrPvtqKz9hz8SO0l1Ni8GgDekvBX2ro0HRQImDGnikfc33cgzcYUSncapnNcZDjVFIH3f6KQ==", - "dev": true, - "requires": { - "cliui": "^7.0.2", - "escalade": "^3.1.1", - "get-caller-file": "^2.0.5", - "require-directory": "^2.1.1", - "string-width": "^4.2.0", - "y18n": "^5.0.5", - "yargs-parser": "^20.2.2" - } - } - } - }, - "@wdio/concise-reporter": { - "version": "7.7.3", - "resolved": "https://registry.npmjs.org/@wdio/concise-reporter/-/concise-reporter-7.7.3.tgz", - "integrity": "sha512-2Ix20n48N+lvvU4NzqMP7z+daG748RRsmDqdstCoBrJgXV6frvu38HVHV90U5uKt5Vmp6/QQl05A4OliaNoO9w==", - "dev": true, - "requires": { - "@wdio/reporter": "7.7.3", - "@wdio/types": "7.7.3", - "chalk": "^4.0.0", - "pretty-ms": "^7.0.0" - }, - "dependencies": { - "ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "dev": true, - "requires": { - "color-convert": "^2.0.1" - } - }, - "chalk": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.1.tgz", - "integrity": "sha512-diHzdDKxcU+bAsUboHLPEDQiw0qEe0qd7SYUn3HgcFlWgbDcfLGswOHYeGrHKzG9z6UYf01d9VFMfZxPM1xZSg==", - "dev": true, - "requires": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - } - }, - "color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "dev": true, - "requires": { - "color-name": "~1.1.4" - } - }, - "color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", - "dev": true - }, - "has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", - "dev": true - }, - "supports-color": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", - "dev": true, - "requires": { - "has-flag": "^4.0.0" - } - } - } - }, - "@wdio/config": { - "version": "7.7.3", - "resolved": "https://registry.npmjs.org/@wdio/config/-/config-7.7.3.tgz", - "integrity": "sha512-I8gkb5BjXLe6/9NK7OCA9Mc+A6xeGUqbYTRd4PNKdObE6HomKOxw4plVZCYF0DlD2FCo4OGrvYGmalojFsCMdA==", - "dev": true, - "requires": { - "@wdio/logger": "7.7.0", - "@wdio/types": "7.7.3", - "deepmerge": "^4.0.0", - "glob": "^7.1.2" - }, - "dependencies": { - "@wdio/logger": { - "version": "7.7.0", - "resolved": "https://registry.npmjs.org/@wdio/logger/-/logger-7.7.0.tgz", - "integrity": "sha512-XX/OkC8NlvsBdhKsb9j7ZbuQtF/Vuo0xf38PXdqYtVezOrYbDuba0hPG++g/IGNuAF34ZbSi+49cvz4u5w92kQ==", - "dev": true, - "requires": { - "chalk": "^4.0.0", - "loglevel": "^1.6.0", - "loglevel-plugin-prefix": "^0.8.4", - "strip-ansi": "^6.0.0" - } - }, - "ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "dev": true, - "requires": { - "color-convert": "^2.0.1" - } - }, - "chalk": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.1.tgz", - "integrity": "sha512-diHzdDKxcU+bAsUboHLPEDQiw0qEe0qd7SYUn3HgcFlWgbDcfLGswOHYeGrHKzG9z6UYf01d9VFMfZxPM1xZSg==", - "dev": true, - "requires": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - } - }, - "color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "dev": true, - "requires": { - "color-name": "~1.1.4" - } - }, - "color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", - "dev": true - }, - "has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", - "dev": true - }, - "supports-color": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", - "dev": true, - "requires": { - "has-flag": "^4.0.0" - } - } - } - }, - "@wdio/local-runner": { - "version": "7.7.4", - "resolved": "https://registry.npmjs.org/@wdio/local-runner/-/local-runner-7.7.4.tgz", - "integrity": "sha512-ubBr9+pDZuOg6i/EJdW8E71dXrE1A63+wsZH6lpdm1fFWqfRvjl+DTCdE2rtLhr44vNSmiHxIIQnCjvZXwjiFg==", - "dev": true, - "requires": { - "@types/stream-buffers": "^3.0.3", - "@wdio/logger": "7.7.0", - "@wdio/repl": "7.7.3", - "@wdio/runner": "7.7.4", - "@wdio/types": "7.7.3", - "async-exit-hook": "^2.0.1", - "split2": "^3.2.2", - "stream-buffers": "^3.0.2" - }, - "dependencies": { - "@wdio/logger": { - "version": "7.7.0", - "resolved": "https://registry.npmjs.org/@wdio/logger/-/logger-7.7.0.tgz", - "integrity": "sha512-XX/OkC8NlvsBdhKsb9j7ZbuQtF/Vuo0xf38PXdqYtVezOrYbDuba0hPG++g/IGNuAF34ZbSi+49cvz4u5w92kQ==", - "dev": true, - "requires": { - "chalk": "^4.0.0", - "loglevel": "^1.6.0", - "loglevel-plugin-prefix": "^0.8.4", - "strip-ansi": "^6.0.0" - } - }, - "ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "dev": true, - "requires": { - "color-convert": "^2.0.1" - } - }, - "chalk": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.1.tgz", - "integrity": "sha512-diHzdDKxcU+bAsUboHLPEDQiw0qEe0qd7SYUn3HgcFlWgbDcfLGswOHYeGrHKzG9z6UYf01d9VFMfZxPM1xZSg==", - "dev": true, - "requires": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - } - }, - "color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "dev": true, - "requires": { - "color-name": "~1.1.4" - } - }, - "color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", - "dev": true - }, - "has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", - "dev": true - }, - "supports-color": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", - "dev": true, - "requires": { - "has-flag": "^4.0.0" - } - } - } - }, - "@wdio/logger": { - "version": "6.10.10", - "resolved": "https://registry.npmjs.org/@wdio/logger/-/logger-6.10.10.tgz", - "integrity": "sha512-2nh0hJz9HeZE0VIEMI+oPgjr/Q37ohrR9iqsl7f7GW5ik+PnKYCT9Eab5mR1GNMG60askwbskgGC1S9ygtvrSw==", - "dev": true, - "requires": { - "chalk": "^4.0.0", - "loglevel": "^1.6.0", - "loglevel-plugin-prefix": "^0.8.4", - "strip-ansi": "^6.0.0" - }, - "dependencies": { - "ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "dev": true, - "requires": { - "color-convert": "^2.0.1" - } - }, - "chalk": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.1.tgz", - "integrity": "sha512-diHzdDKxcU+bAsUboHLPEDQiw0qEe0qd7SYUn3HgcFlWgbDcfLGswOHYeGrHKzG9z6UYf01d9VFMfZxPM1xZSg==", - "dev": true, - "requires": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - } - }, - "color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "dev": true, - "requires": { - "color-name": "~1.1.4" - } - }, - "color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", - "dev": true - }, - "has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", - "dev": true - }, - "supports-color": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", - "dev": true, - "requires": { - "has-flag": "^4.0.0" - } - } - } - }, - "@wdio/mocha-framework": { - "version": "7.7.4", - "resolved": "https://registry.npmjs.org/@wdio/mocha-framework/-/mocha-framework-7.7.4.tgz", - "integrity": "sha512-zLhMJBAp4HOP0qGffCNSA1UBdRystn9o5y7EEQXU6Gu+ktrSOV/RU+pvd+kqHo6RfOIcwShljZVStf3zh8cY6Q==", - "dev": true, - "requires": { - "@types/mocha": "^8.0.0", - "@wdio/logger": "7.7.0", - "@wdio/types": "7.7.3", - "@wdio/utils": "7.7.3", - "expect-webdriverio": "^3.0.0", - "mocha": "^9.0.0" - }, - "dependencies": { - "@wdio/logger": { - "version": "7.7.0", - "resolved": "https://registry.npmjs.org/@wdio/logger/-/logger-7.7.0.tgz", - "integrity": "sha512-XX/OkC8NlvsBdhKsb9j7ZbuQtF/Vuo0xf38PXdqYtVezOrYbDuba0hPG++g/IGNuAF34ZbSi+49cvz4u5w92kQ==", - "dev": true, - "requires": { - "chalk": "^4.0.0", - "loglevel": "^1.6.0", - "loglevel-plugin-prefix": "^0.8.4", - "strip-ansi": "^6.0.0" - } - }, - "ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "dev": true, - "requires": { - "color-convert": "^2.0.1" - } - }, - "chalk": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.1.tgz", - "integrity": "sha512-diHzdDKxcU+bAsUboHLPEDQiw0qEe0qd7SYUn3HgcFlWgbDcfLGswOHYeGrHKzG9z6UYf01d9VFMfZxPM1xZSg==", - "dev": true, - "requires": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - } - }, - "chokidar": { - "version": "3.5.1", - "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.5.1.tgz", - "integrity": "sha512-9+s+Od+W0VJJzawDma/gvBNQqkTiqYTWLuZoyAsivsI4AaWTCzHG06/TMjsf1cYe9Cb97UCEhjz7HvnPk2p/tw==", - "dev": true, - "requires": { - "anymatch": "~3.1.1", - "braces": "~3.0.2", - "fsevents": "~2.3.1", - "glob-parent": "~5.1.0", - "is-binary-path": "~2.1.0", - "is-glob": "~4.0.1", - "normalize-path": "~3.0.0", - "readdirp": "~3.5.0" - } - }, - "color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "dev": true, - "requires": { - "color-name": "~1.1.4" - } - }, - "color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", - "dev": true - }, - "debug": { - "version": "4.3.1", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.1.tgz", - "integrity": "sha512-doEwdvm4PCeK4K3RQN2ZC2BYUBaxwLARCqZmMjtF8a51J2Rb0xpVloFRnCODwqjpwnAoao4pelN8l3RJdv3gRQ==", - "dev": true, - "requires": { - "ms": "2.1.2" - }, - "dependencies": { - "ms": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", - "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", - "dev": true - } - } - }, - "escape-string-regexp": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", - "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", - "dev": true - }, - "find-up": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz", - "integrity": "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==", - "dev": true, - "requires": { - "locate-path": "^6.0.0", - "path-exists": "^4.0.0" - } - }, - "has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", - "dev": true - }, - "locate-path": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz", - "integrity": "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==", - "dev": true, - "requires": { - "p-locate": "^5.0.0" - } - }, - "mocha": { - "version": "9.0.1", - "resolved": "https://registry.npmjs.org/mocha/-/mocha-9.0.1.tgz", - "integrity": "sha512-9zwsavlRO+5csZu6iRtl3GHImAbhERoDsZwdRkdJ/bE+eVplmoxNKE901ZJ9LdSchYBjSCPbjKc5XvcAri2ylw==", - "dev": true, - "requires": { - "@ungap/promise-all-settled": "1.1.2", - "ansi-colors": "4.1.1", - "browser-stdout": "1.3.1", - "chokidar": "3.5.1", - "debug": "4.3.1", - "diff": "5.0.0", - "escape-string-regexp": "4.0.0", - "find-up": "5.0.0", - "glob": "7.1.7", - "growl": "1.10.5", - "he": "1.2.0", - "js-yaml": "4.1.0", - "log-symbols": "4.1.0", - "minimatch": "3.0.4", - "ms": "2.1.3", - "nanoid": "3.1.23", - "serialize-javascript": "5.0.1", - "strip-json-comments": "3.1.1", - "supports-color": "8.1.1", - "which": "2.0.2", - "wide-align": "1.1.3", - "workerpool": "6.1.4", - "yargs": "16.2.0", - "yargs-parser": "20.2.4", - "yargs-unparser": "2.0.0" - }, - "dependencies": { - "supports-color": { - "version": "8.1.1", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz", - "integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==", - "dev": true, - "requires": { - "has-flag": "^4.0.0" - } - } - } - }, - "ms": { - "version": "2.1.3", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", - "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", - "dev": true - }, - "p-limit": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", - "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==", - "dev": true, - "requires": { - "yocto-queue": "^0.1.0" - } - }, - "p-locate": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-5.0.0.tgz", - "integrity": "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==", - "dev": true, - "requires": { - "p-limit": "^3.0.2" - } - }, - "readdirp": { - "version": "3.5.0", - "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.5.0.tgz", - "integrity": "sha512-cMhu7c/8rdhkHXWsY+osBhfSy0JikwpHK/5+imo+LpeasTF8ouErHrlYkwT0++njiyuDvc7OFY5T3ukvZ8qmFQ==", - "dev": true, - "requires": { - "picomatch": "^2.2.1" - } - }, - "supports-color": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", - "dev": true, - "requires": { - "has-flag": "^4.0.0" - } - }, - "yargs": { - "version": "16.2.0", - "resolved": "https://registry.npmjs.org/yargs/-/yargs-16.2.0.tgz", - "integrity": "sha512-D1mvvtDG0L5ft/jGWkLpG1+m0eQxOfaBvTNELraWj22wSVUMWxZUvYgJYcKh6jGGIkJFhH4IZPQhR4TKpc8mBw==", - "dev": true, - "requires": { - "cliui": "^7.0.2", - "escalade": "^3.1.1", - "get-caller-file": "^2.0.5", - "require-directory": "^2.1.1", - "string-width": "^4.2.0", - "y18n": "^5.0.5", - "yargs-parser": "^20.2.2" - } - }, - "yargs-parser": { - "version": "20.2.4", - "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-20.2.4.tgz", - "integrity": "sha512-WOkpgNhPTlE73h4VFAFsOnomJVaovO8VqLDzy5saChRBFQFBoMYirowyW+Q9HB4HFF4Z7VZTiG3iSzJJA29yRA==", - "dev": true - } - } - }, - "@wdio/protocols": { - "version": "7.7.4", - "resolved": "https://registry.npmjs.org/@wdio/protocols/-/protocols-7.7.4.tgz", - "integrity": "sha512-gfGPOjvqUws3/dTnrXbCYP2keYE6O5BK5qHWnOEu6c7ubE4hebxV8W5c822L7ntabc1e38+diEbM+qFuIT890Q==", - "dev": true - }, - "@wdio/repl": { - "version": "7.7.3", - "resolved": "https://registry.npmjs.org/@wdio/repl/-/repl-7.7.3.tgz", - "integrity": "sha512-7nhvUa3Zd5Ny9topJGRZwkomlveuO3RIv+jBUHgQ2jiDIGvG9MroHxKEniIbscVSsD32XFOOZY59kSpX1b50VQ==", - "dev": true, - "requires": { - "@wdio/utils": "7.7.3" - } - }, - "@wdio/reporter": { - "version": "7.7.3", - "resolved": "https://registry.npmjs.org/@wdio/reporter/-/reporter-7.7.3.tgz", - "integrity": "sha512-zAUGgP/FZ3XF5s4RUcDGIAeum3WzkA9ll5lymytxhh/9Jj9/5c77o498ic3RGQlB8FTz+5SVmw08r7g3uekI8g==", - "dev": true, - "requires": { - "@types/node": "^14.14.31", - "@wdio/types": "7.7.3", - "fs-extra": "^10.0.0" - }, - "dependencies": { - "@types/node": { - "version": "14.17.4", - "resolved": "https://registry.npmjs.org/@types/node/-/node-14.17.4.tgz", - "integrity": "sha512-8kQ3+wKGRNN0ghtEn7EGps/B8CzuBz1nXZEIGGLP2GnwbqYn4dbTs7k+VKLTq1HvZLRCIDtN3Snx1Ege8B7L5A==", - "dev": true - } - } - }, - "@wdio/runner": { - "version": "7.7.4", - "resolved": "https://registry.npmjs.org/@wdio/runner/-/runner-7.7.4.tgz", - "integrity": "sha512-Ahfrv3TM9y2KMjWI1xKc+tnLVO+X1/Gf5QPjprmLlRxf/rSQDfX+wMmQP/g0wsLtm4pXy0kR1K/76WWvZgzSkw==", - "dev": true, - "requires": { - "@wdio/config": "7.7.3", - "@wdio/logger": "7.7.0", - "@wdio/types": "7.7.3", - "@wdio/utils": "7.7.3", - "deepmerge": "^4.0.0", - "gaze": "^1.1.2", - "webdriver": "7.7.4", - "webdriverio": "7.7.4" - }, - "dependencies": { - "@wdio/logger": { - "version": "7.7.0", - "resolved": "https://registry.npmjs.org/@wdio/logger/-/logger-7.7.0.tgz", - "integrity": "sha512-XX/OkC8NlvsBdhKsb9j7ZbuQtF/Vuo0xf38PXdqYtVezOrYbDuba0hPG++g/IGNuAF34ZbSi+49cvz4u5w92kQ==", - "dev": true, - "requires": { - "chalk": "^4.0.0", - "loglevel": "^1.6.0", - "loglevel-plugin-prefix": "^0.8.4", - "strip-ansi": "^6.0.0" - } - }, - "ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "dev": true, - "requires": { - "color-convert": "^2.0.1" - } - }, - "chalk": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.1.tgz", - "integrity": "sha512-diHzdDKxcU+bAsUboHLPEDQiw0qEe0qd7SYUn3HgcFlWgbDcfLGswOHYeGrHKzG9z6UYf01d9VFMfZxPM1xZSg==", - "dev": true, - "requires": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - } - }, - "color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "dev": true, - "requires": { - "color-name": "~1.1.4" - } - }, - "color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", - "dev": true - }, - "has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", - "dev": true - }, - "supports-color": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", - "dev": true, - "requires": { - "has-flag": "^4.0.0" - } - } - } - }, - "@wdio/spec-reporter": { - "version": "7.7.3", - "resolved": "https://registry.npmjs.org/@wdio/spec-reporter/-/spec-reporter-7.7.3.tgz", - "integrity": "sha512-5elsNfZd3kbBaKY5IK5ZmdZsWZNSOCqXnM2fYryAh2RBoXbcXkak4D5PbLehusZhp6CQ7UpXEKf4BDDYfd0ebw==", - "dev": true, - "requires": { - "@types/easy-table": "^0.0.32", - "@wdio/reporter": "7.7.3", - "@wdio/types": "7.7.3", - "chalk": "^4.0.0", - "easy-table": "^1.1.1", - "pretty-ms": "^7.0.0" - }, - "dependencies": { - "ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "dev": true, - "requires": { - "color-convert": "^2.0.1" - } - }, - "chalk": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.1.tgz", - "integrity": "sha512-diHzdDKxcU+bAsUboHLPEDQiw0qEe0qd7SYUn3HgcFlWgbDcfLGswOHYeGrHKzG9z6UYf01d9VFMfZxPM1xZSg==", - "dev": true, - "requires": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - } - }, - "color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "dev": true, - "requires": { - "color-name": "~1.1.4" - } - }, - "color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", - "dev": true - }, - "has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", - "dev": true - }, - "supports-color": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", - "dev": true, - "requires": { - "has-flag": "^4.0.0" - } - } - } - }, - "@wdio/sync": { - "version": "7.7.4", - "resolved": "https://registry.npmjs.org/@wdio/sync/-/sync-7.7.4.tgz", - "integrity": "sha512-x0ZU78Je0yl05TfwiNtkKkJZ+90y6MndR4z5n/m6ADRzSGdFOazGJSFO0h2bN8MkPRusfqYsJwB6MKftCP0URA==", - "dev": true, - "requires": { - "@types/fibers": "^3.1.0", - "@types/puppeteer": "^5.4.0", - "@wdio/logger": "7.7.0", - "@wdio/types": "7.7.3", - "fibers": "^5.0.0", - "webdriverio": "7.7.4" - }, - "dependencies": { - "@wdio/logger": { - "version": "7.7.0", - "resolved": "https://registry.npmjs.org/@wdio/logger/-/logger-7.7.0.tgz", - "integrity": "sha512-XX/OkC8NlvsBdhKsb9j7ZbuQtF/Vuo0xf38PXdqYtVezOrYbDuba0hPG++g/IGNuAF34ZbSi+49cvz4u5w92kQ==", - "dev": true, - "requires": { - "chalk": "^4.0.0", - "loglevel": "^1.6.0", - "loglevel-plugin-prefix": "^0.8.4", - "strip-ansi": "^6.0.0" - } - }, - "ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "dev": true, - "requires": { - "color-convert": "^2.0.1" - } - }, - "chalk": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.1.tgz", - "integrity": "sha512-diHzdDKxcU+bAsUboHLPEDQiw0qEe0qd7SYUn3HgcFlWgbDcfLGswOHYeGrHKzG9z6UYf01d9VFMfZxPM1xZSg==", - "dev": true, - "requires": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - } - }, - "color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "dev": true, - "requires": { - "color-name": "~1.1.4" - } - }, - "color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", - "dev": true - }, - "has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", - "dev": true - }, - "supports-color": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", - "dev": true, - "requires": { - "has-flag": "^4.0.0" - } - } - } - }, - "@wdio/types": { - "version": "7.7.3", - "resolved": "https://registry.npmjs.org/@wdio/types/-/types-7.7.3.tgz", - "integrity": "sha512-ZZBQHCXKjZSQj9pf4df/QhfgQQj0vzm9hkK7YyNM+S+qnW0LExL8qQKLxTlGHDaYxk/+Jrd9pcZrJXRCoSnUaA==", - "dev": true, - "requires": { - "@types/node": "^14.14.31", - "got": "^11.8.1" - }, - "dependencies": { - "@types/node": { - "version": "14.17.4", - "resolved": "https://registry.npmjs.org/@types/node/-/node-14.17.4.tgz", - "integrity": "sha512-8kQ3+wKGRNN0ghtEn7EGps/B8CzuBz1nXZEIGGLP2GnwbqYn4dbTs7k+VKLTq1HvZLRCIDtN3Snx1Ege8B7L5A==", - "dev": true - } - } - }, - "@wdio/utils": { - "version": "7.7.3", - "resolved": "https://registry.npmjs.org/@wdio/utils/-/utils-7.7.3.tgz", - "integrity": "sha512-bvOoE2gve8Z8HFguVw0RMp5BbSmJR4zSr8DwbwnA8RSL3NshKlRk33HWYLmKsxjkH+ZWI2ihFbpvLD4W4imXag==", - "dev": true, - "requires": { - "@wdio/logger": "7.7.0", - "@wdio/types": "7.7.3" - }, - "dependencies": { - "@wdio/logger": { - "version": "7.7.0", - "resolved": "https://registry.npmjs.org/@wdio/logger/-/logger-7.7.0.tgz", - "integrity": "sha512-XX/OkC8NlvsBdhKsb9j7ZbuQtF/Vuo0xf38PXdqYtVezOrYbDuba0hPG++g/IGNuAF34ZbSi+49cvz4u5w92kQ==", - "dev": true, - "requires": { - "chalk": "^4.0.0", - "loglevel": "^1.6.0", - "loglevel-plugin-prefix": "^0.8.4", - "strip-ansi": "^6.0.0" - } - }, - "ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "dev": true, - "requires": { - "color-convert": "^2.0.1" - } - }, - "chalk": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.1.tgz", - "integrity": "sha512-diHzdDKxcU+bAsUboHLPEDQiw0qEe0qd7SYUn3HgcFlWgbDcfLGswOHYeGrHKzG9z6UYf01d9VFMfZxPM1xZSg==", - "dev": true, - "requires": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - } - }, - "color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "dev": true, - "requires": { - "color-name": "~1.1.4" - } - }, - "color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", - "dev": true - }, - "has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", - "dev": true - }, - "supports-color": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", - "dev": true, - "requires": { - "has-flag": "^4.0.0" - } - } - } - }, - "JSONStream": { - "version": "1.3.5", - "resolved": "https://registry.npmjs.org/JSONStream/-/JSONStream-1.3.5.tgz", - "integrity": "sha512-E+iruNOY8VV9s4JEbe1aNEm6MiszPRr/UfcHMz0TQh1BXSxHK+ASV1R6W4HpjBhSeS+54PIsAMCBmwD06LLsqQ==", - "dev": true, - "requires": { - "jsonparse": "^1.2.0", - "through": ">=2.2.7 <3" - } - }, - "abbrev": { - "version": "1.0.9", - "resolved": "https://registry.npmjs.org/abbrev/-/abbrev-1.0.9.tgz", - "integrity": "sha1-kbR5JYinc4wl813W9jdSovh3YTU=", - "dev": true - }, - "accepts": { - "version": "1.3.7", - "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.7.tgz", - "integrity": "sha512-Il80Qs2WjYlJIBNzNkK6KYqlVMTbZLXgHx2oT0pU/fjRHyEp+PEfEPY0R3WCwAGVOtauxh1hOxNgIf5bv7dQpA==", - "requires": { - "mime-types": "~2.1.24", - "negotiator": "0.6.2" - } - }, - "acorn": { - "version": "7.4.1", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-7.4.1.tgz", - "integrity": "sha512-nQyp0o1/mNdbTO1PO6kHkwSrmgZ0MT/jCCpNiwbUjGoRN4dlBhqJtoQuCnEOKzgTVwg0ZWiCoQy6SxMebQVh8A==", - "dev": true - }, - "acorn-dynamic-import": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/acorn-dynamic-import/-/acorn-dynamic-import-2.0.2.tgz", - "integrity": "sha1-x1K9IQvvZ5UBtsbLf8hPj0cVjMQ=", - "dev": true, - "requires": { - "acorn": "^4.0.3" - }, - "dependencies": { - "acorn": { - "version": "4.0.13", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-4.0.13.tgz", - "integrity": "sha1-EFSVrlNh1pe9GVyCUZLhrX8lN4c=", - "dev": true - } - } - }, - "acorn-jsx": { - "version": "5.3.1", - "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.1.tgz", - "integrity": "sha512-K0Ptm/47OKfQRpNQ2J/oIN/3QYiK6FwW+eJbILhsdxh2WTLdl+30o8aGdTbm5JbffpFFAg/g+zi1E+jvJha5ng==", - "dev": true - }, - "acorn-node": { - "version": "1.8.2", - "resolved": "https://registry.npmjs.org/acorn-node/-/acorn-node-1.8.2.tgz", - "integrity": "sha512-8mt+fslDufLYntIoPAaIMUe/lrbrehIiwmR3t2k9LljIzoigEPF27eLk2hy8zSGzmR/ogr7zbRKINMo1u0yh5A==", - "dev": true, - "requires": { - "acorn": "^7.0.0", - "acorn-walk": "^7.0.0", - "xtend": "^4.0.2" - } - }, - "acorn-walk": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-7.2.0.tgz", - "integrity": "sha512-OPdCF6GsMIP+Az+aWfAAOEt2/+iVDKE7oy6lJ098aoe59oAmK76qV6Gw60SbZ8jHuG2wH058GF4pLFbYamYrVA==", - "dev": true - }, - "add-stream": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/add-stream/-/add-stream-1.0.0.tgz", - "integrity": "sha1-anmQQ3ynNtXhKI25K9MmbV9csqo=", - "dev": true - }, - "adm-zip": { - "version": "0.2.1", - "resolved": "https://registry.npmjs.org/adm-zip/-/adm-zip-0.2.1.tgz", - "integrity": "sha1-6AHO3rW9mk6Y1pnFwPQjnicx3L8=", - "dev": true - }, - "agent-base": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-5.1.1.tgz", - "integrity": "sha512-TMeqbNl2fMW0nMjTEPOwe3J/PRFP4vqeoNuQMG0HlMrtm5QxKqdvAkZ1pRBQ/ulIyDD5Yq0nJ7YbdD8ey0TO3g==", - "dev": true - }, - "ajv": { - "version": "5.5.2", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-5.5.2.tgz", - "integrity": "sha1-c7Xuyj+rZT49P5Qis0GtQiBdyWU=", - "dev": true, - "requires": { - "co": "^4.6.0", - "fast-deep-equal": "^1.0.0", - "fast-json-stable-stringify": "^2.0.0", - "json-schema-traverse": "^0.3.0" - }, - "dependencies": { - "fast-deep-equal": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-1.1.0.tgz", - "integrity": "sha1-wFNHeBfIa1HaqFPIHgWbcz0CNhQ=", - "dev": true - }, - "json-schema-traverse": { - "version": "0.3.1", - "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.3.1.tgz", - "integrity": "sha1-NJptRMU6Ud6JtAgFxdXlm0F9M0A=", - "dev": true - } - } - }, - "ajv-keywords": { - "version": "3.5.2", - "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-3.5.2.tgz", - "integrity": "sha512-5p6WTN0DdTGVQk6VjcEju19IgaHudalcfabD7yhDGeA6bcQnmL+CpveLJq/3hvfwd1aof6L386Ougkx6RfyMIQ==", - "dev": true - }, - "align-text": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/align-text/-/align-text-0.1.4.tgz", - "integrity": "sha1-DNkKVhCT810KmSVsIrcGlDP60Rc=", - "dev": true, - "requires": { - "kind-of": "^3.0.2", - "longest": "^1.0.1", - "repeat-string": "^1.5.2" - }, - "dependencies": { - "kind-of": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", - "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", - "dev": true, - "requires": { - "is-buffer": "^1.1.5" - } - } - } - }, - "amdefine": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/amdefine/-/amdefine-1.0.1.tgz", - "integrity": "sha1-SlKCrBZHKek2Gbz9OtFR+BfOkfU=", - "dev": true - }, - "ansi-colors": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-4.1.1.tgz", - "integrity": "sha512-JoX0apGbHaUJBNl6yF+p6JAFYZ666/hhCGKN5t9QFjbJQKUU/g8MNbFDbvfrgKXvI1QpZplPOnwIo99lX/AAmA==", - "dev": true - }, - "ansi-escapes": { - "version": "4.3.2", - "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-4.3.2.tgz", - "integrity": "sha512-gKXj5ALrKWQLsYG9jlTRmR/xKluxHV+Z9QEwNIgCfM1/uwPMCuzVVnh5mwTd+OuBZcwSIMbqssNWRm1lE51QaQ==", - "dev": true, - "requires": { - "type-fest": "^0.21.3" - } - }, - "ansi-gray": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/ansi-gray/-/ansi-gray-0.1.1.tgz", - "integrity": "sha1-KWLPVOyXksSFEKPetSRDaGHvclE=", - "dev": true, - "requires": { - "ansi-wrap": "0.1.0" - } - }, - "ansi-html": { - "version": "0.0.7", - "resolved": "https://registry.npmjs.org/ansi-html/-/ansi-html-0.0.7.tgz", - "integrity": "sha1-gTWEAhliqenm/QOflA0S9WynhZ4=", - "dev": true - }, - "ansi-regex": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.0.tgz", - "integrity": "sha512-bY6fj56OUQ0hU1KjFNDQuJFezqKdrAyFdIevADiqrWHwSlbmBNMHp5ak2f40Pm8JTFyM2mqxkG6ngkHO11f/lg==", - "dev": true - }, - "ansi-styles": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", - "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", - "dev": true, - "requires": { - "color-convert": "^1.9.0" - } - }, - "ansi-wrap": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/ansi-wrap/-/ansi-wrap-0.1.0.tgz", - "integrity": "sha1-qCJQ3bABXponyoLoLqYDu/pF768=", - "dev": true - }, - "anymatch": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.2.tgz", - "integrity": "sha512-P43ePfOAIupkguHUycrc4qJ9kz8ZiuOUijaETwX7THt0Y/GNK7v0aa8rY816xWjZ7rJdA5XdMcpVFTKMq+RvWg==", - "dev": true, - "requires": { - "normalize-path": "^3.0.0", - "picomatch": "^2.0.4" - } - }, - "append-buffer": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/append-buffer/-/append-buffer-1.0.2.tgz", - "integrity": "sha1-2CIM9GYIFSXv6lBhTz3mUU36WPE=", - "dev": true, - "requires": { - "buffer-equal": "^1.0.0" - } - }, - "archiver": { - "version": "5.3.0", - "resolved": "https://registry.npmjs.org/archiver/-/archiver-5.3.0.tgz", - "integrity": "sha512-iUw+oDwK0fgNpvveEsdQ0Ase6IIKztBJU2U0E9MzszMfmVVUyv1QJhS2ITW9ZCqx8dktAxVAjWWkKehuZE8OPg==", - "dev": true, - "requires": { - "archiver-utils": "^2.1.0", - "async": "^3.2.0", - "buffer-crc32": "^0.2.1", - "readable-stream": "^3.6.0", - "readdir-glob": "^1.0.0", - "tar-stream": "^2.2.0", - "zip-stream": "^4.1.0" - }, - "dependencies": { - "async": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/async/-/async-3.2.0.tgz", - "integrity": "sha512-TR2mEZFVOj2pLStYxLht7TyfuRzaydfpxr3k9RpHIzMgw7A64dzsdqCxH1WJyQdoe8T10nDXd9wnEigmiuHIZw==", - "dev": true - } - } - }, - "archiver-utils": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/archiver-utils/-/archiver-utils-2.1.0.tgz", - "integrity": "sha512-bEL/yUb/fNNiNTuUz979Z0Yg5L+LzLxGJz8x79lYmR54fmTIb6ob/hNQgkQnIUDWIFjZVQwl9Xs356I6BAMHfw==", - "dev": true, - "requires": { - "glob": "^7.1.4", - "graceful-fs": "^4.2.0", - "lazystream": "^1.0.0", - "lodash.defaults": "^4.2.0", - "lodash.difference": "^4.5.0", - "lodash.flatten": "^4.4.0", - "lodash.isplainobject": "^4.0.6", - "lodash.union": "^4.6.0", - "normalize-path": "^3.0.0", - "readable-stream": "^2.0.0" - }, - "dependencies": { - "readable-stream": { - "version": "2.3.7", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz", - "integrity": "sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==", - "dev": true, - "requires": { - "core-util-is": "~1.0.0", - "inherits": "~2.0.3", - "isarray": "~1.0.0", - "process-nextick-args": "~2.0.0", - "safe-buffer": "~5.1.1", - "string_decoder": "~1.1.1", - "util-deprecate": "~1.0.1" - } - }, - "string_decoder": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", - "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", - "dev": true, - "requires": { - "safe-buffer": "~5.1.0" - } - } - } - }, - "archy": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/archy/-/archy-1.0.0.tgz", - "integrity": "sha1-+cjBN1fMHde8N5rHeyxipcKGjEA=", - "dev": true - }, - "argparse": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", - "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", - "dev": true - }, - "aria-query": { - "version": "4.2.2", - "resolved": "https://registry.npmjs.org/aria-query/-/aria-query-4.2.2.tgz", - "integrity": "sha512-o/HelwhuKpTj/frsOsbNLNgnNGVIFsVP/SW2BSF14gVl7kAfMOJ6/8wUAUvG1R1NHKrfG+2sHZTu0yauT1qBrA==", - "dev": true, - "requires": { - "@babel/runtime": "^7.10.2", - "@babel/runtime-corejs3": "^7.10.2" - } - }, - "arr-diff": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/arr-diff/-/arr-diff-4.0.0.tgz", - "integrity": "sha1-1kYQdP6/7HHn4VI1dhoyml3HxSA=", - "dev": true - }, - "arr-filter": { -======= "@ungap/promise-all-settled": { ->>>>>>> master "version": "1.1.2", "resolved": "https://registry.npmjs.org/@ungap/promise-all-settled/-/promise-all-settled-1.1.2.tgz", "integrity": "sha512-sL/cEvJWAnClXw0wHk85/2L0G6Sj8UB0Ctc1TEMbKSsmpRosqhwj9gWgFRZSrBr2f9tiXISwNhCPmlfqUqyb9Q==", @@ -32489,20 +29906,12 @@ "@vue/shared": "3.1.2" } }, -<<<<<<< HEAD - "babelify": { - "version": "10.0.0", - "resolved": "https://registry.npmjs.org/babelify/-/babelify-10.0.0.tgz", - "integrity": "sha512-X40FaxyH7t3X+JFAKvb1H9wooWKLRCi8pg3m8poqtdZaIng+bjzp9RvKQCvRjF9isHiPkXspbbXT/zwXLtwgwg==", - "dev": true -======= "@vue/shared": { "version": "3.1.2", "resolved": "https://registry.npmjs.org/@vue/shared/-/shared-3.1.2.tgz", "integrity": "sha512-EmH/poaDWBPJaPILXNI/1fvUbArJQmmTyVCwvvyDYDFnkPoTclAbHRAtyIvqfez7jybTDn077HTNILpxlsoWhg==", "dev": true, "optional": true ->>>>>>> master }, "@wdio/browserstack-service": { "version": "6.12.1", @@ -32907,10 +30316,109 @@ } } }, - "@wdio/mocha-framework": { - "version": "7.7.4", - "resolved": "https://registry.npmjs.org/@wdio/mocha-framework/-/mocha-framework-7.7.4.tgz", - "integrity": "sha512-zLhMJBAp4HOP0qGffCNSA1UBdRystn9o5y7EEQXU6Gu+ktrSOV/RU+pvd+kqHo6RfOIcwShljZVStf3zh8cY6Q==", + "babel-plugin-dynamic-import-node": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/babel-plugin-dynamic-import-node/-/babel-plugin-dynamic-import-node-2.3.3.tgz", + "integrity": "sha512-jZVI+s9Zg3IqA/kdi0i6UDCybUI3aSBLnglhYbSSjKlV7yF1F/5LWv8MakQmvYpnbJDS6fcBL2KzHSxNCMtWSQ==", + "dev": true, + "requires": { + "object.assign": "^4.1.0" + } + }, + "babel-plugin-polyfill-corejs2": { + "version": "0.2.2", + "resolved": "https://registry.npmjs.org/babel-plugin-polyfill-corejs2/-/babel-plugin-polyfill-corejs2-0.2.2.tgz", + "integrity": "sha512-kISrENsJ0z5dNPq5eRvcctITNHYXWOA4DUZRFYCz3jYCcvTb/A546LIddmoGNMVYg2U38OyFeNosQwI9ENTqIQ==", + "dev": true, + "requires": { + "@babel/compat-data": "^7.13.11", + "@babel/helper-define-polyfill-provider": "^0.2.2", + "semver": "^6.1.1" + } + }, + "babel-plugin-polyfill-corejs3": { + "version": "0.2.3", + "resolved": "https://registry.npmjs.org/babel-plugin-polyfill-corejs3/-/babel-plugin-polyfill-corejs3-0.2.3.tgz", + "integrity": "sha512-rCOFzEIJpJEAU14XCcV/erIf/wZQMmMT5l5vXOpL5uoznyOGfDIjPj6FVytMvtzaKSTSVKouOCTPJ5OMUZH30g==", + "dev": true, + "requires": { + "@babel/helper-define-polyfill-provider": "^0.2.2", + "core-js-compat": "^3.14.0" + } + }, + "babel-plugin-polyfill-regenerator": { + "version": "0.2.2", + "resolved": "https://registry.npmjs.org/babel-plugin-polyfill-regenerator/-/babel-plugin-polyfill-regenerator-0.2.2.tgz", + "integrity": "sha512-Goy5ghsc21HgPDFtzRkSirpZVW35meGoTmTOb2bxqdl60ghub4xOidgNTHaZfQ2FaxQsKmwvXtOAkcIS4SMBWg==", + "dev": true, + "requires": { + "@babel/helper-define-polyfill-provider": "^0.2.2" + } + }, + "babel-plugin-transform-object-assign": { + "version": "6.22.0", + "resolved": "https://registry.npmjs.org/babel-plugin-transform-object-assign/-/babel-plugin-transform-object-assign-6.22.0.tgz", + "integrity": "sha1-+Z0vZvGgsNSY40bFNZaEdAyqILo=", + "requires": { + "babel-runtime": "^6.22.0" + } + }, + "babel-runtime": { + "version": "6.26.0", + "resolved": "https://registry.npmjs.org/babel-runtime/-/babel-runtime-6.26.0.tgz", + "integrity": "sha1-llxwWGaOgrVde/4E/yM3vItWR/4=", + "requires": { + "core-js": "^2.4.0", + "regenerator-runtime": "^0.11.0" + }, + "dependencies": { + "core-js": { + "version": "2.6.12", + "resolved": "https://registry.npmjs.org/core-js/-/core-js-2.6.12.tgz", + "integrity": "sha512-Kb2wC0fvsWfQrgk8HU5lW6U/Lcs8+9aaYcy4ZFc6DDlo4nZ7n70dEgE5rtR0oG6ufKDUnrwfWL1mXR5ljDatrQ==" + } + } + }, + "babelify": { + "version": "10.0.0", + "resolved": "https://registry.npmjs.org/babelify/-/babelify-10.0.0.tgz", + "integrity": "sha512-X40FaxyH7t3X+JFAKvb1H9wooWKLRCi8pg3m8poqtdZaIng+bjzp9RvKQCvRjF9isHiPkXspbbXT/zwXLtwgwg==", + "dev": true, + "requires": {} + }, + "bach": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/bach/-/bach-1.2.0.tgz", + "integrity": "sha1-Szzpa/JxNPeaG0FKUcFONMO9mIA=", + "dev": true, + "requires": { + "arr-filter": "^1.1.1", + "arr-flatten": "^1.0.1", + "arr-map": "^2.0.0", + "array-each": "^1.0.0", + "array-initial": "^1.0.0", + "array-last": "^1.1.1", + "async-done": "^1.2.2", + "async-settle": "^1.0.0", + "now-and-later": "^2.0.0" + } + }, + "bail": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/bail/-/bail-1.0.5.tgz", + "integrity": "sha512-xFbRxM1tahm08yHBP16MMjVUAvDaBMD38zsM9EMAUN61omwLmKlOpB/Zku5QkjZ8TZ4vn53pj+t518cH0S03RQ==", + "dev": true + }, + "balanced-match": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", + "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", + "dev": true + }, + "base": { + "version": "0.11.2", + "resolved": "https://registry.npmjs.org/base/-/base-0.11.2.tgz", + "integrity": "sha512-5T6P4xPgpp0YDFvSWwEZ4NoE3aM4QBQXDzmVbraCkFj8zHM+mba8SyqB5DbZWyR7mYHo6Y7BdQo3MoA4m0TeQg==", "dev": true, "requires": { "@types/mocha": "^8.0.0", @@ -34484,28 +31992,11 @@ "integrity": "sha512-XpNj6GDQzdfW+r2Wnn7xiSAd7TM3jzkxGXBGTtWKuSXv1xUV+azxAm8jdWZN06QTQk+2N2XB9jRDkvbmQmcRtg==", "dev": true }, -<<<<<<< HEAD - "conventional-commits-parser": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/conventional-commits-parser/-/conventional-commits-parser-3.2.1.tgz", - "integrity": "sha512-OG9kQtmMZBJD/32NEw5IhN5+HnBqVjy03eC+I71I0oQRFA5rOgA4OtPOYG7mz1GkCfCNxn3gKIX8EiHJYuf1cA==", - "dev": true, - "requires": { - "JSONStream": "^1.0.4", - "is-text-path": "^1.0.1", - "lodash": "^4.17.15", - "meow": "^8.0.0", - "split2": "^3.0.0", - "through2": "^4.0.0", - "trim-off-newlines": "^1.0.0" - } -======= "bn.js": { "version": "5.2.0", "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-5.2.0.tgz", "integrity": "sha512-D7iWRBvnZE8ecXiLj/9wbxH7Tk79fAh8IHaTNq1RWRixsS02W+5qS+iE9yq6RYl0asXx5tw0bLhmT5pIfbSquw==", "dev": true ->>>>>>> master }, "body": { "version": "5.1.0", @@ -36519,6 +34010,7 @@ "version": "7.4.6", "resolved": "https://registry.npmjs.org/ws/-/ws-7.4.6.tgz", "integrity": "sha512-YmhHDO4MzaDLB+M9ym/mDA5z0naX8j7SIlT8f8z+I0VtzsRbekxEutHSme7NPS2qE8StCYQNUnfWdXta/Yu85A==", +<<<<<<< HEAD "dev": true ======= "is-descriptor": { @@ -36532,6 +34024,10 @@ "kind-of": "^6.0.2" } >>>>>>> master +======= + "dev": true, + "requires": {} +>>>>>>> parent of a6fc789... Merged master } } }, @@ -37519,6 +35015,7 @@ "version": "10.2.1", "resolved": "https://registry.npmjs.org/eslint-config-standard/-/eslint-config-standard-10.2.1.tgz", "integrity": "sha1-wGHk0GbzedwXzVYsZOgZtN1FRZE=", +<<<<<<< HEAD ======= "emoji-regex": { "version": "8.0.0", @@ -37526,6 +35023,10 @@ "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", >>>>>>> master "dev": true +======= + "dev": true, + "requires": {} +>>>>>>> parent of a6fc789... Merged master }, "emojis-list": { "version": "3.0.0", @@ -38420,13 +35921,15 @@ "version": "5.1.0", "resolved": "https://registry.npmjs.org/eslint-plugin-promise/-/eslint-plugin-promise-5.1.0.tgz", "integrity": "sha512-NGmI6BH5L12pl7ScQHbg7tvtk4wPxxj8yPHH47NvSmMtFneC077PSeY3huFj06ZWZvtbfxSPt3RuOQD5XcR4ng==", - "dev": true + "dev": true, + "requires": {} }, "eslint-plugin-standard": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/eslint-plugin-standard/-/eslint-plugin-standard-3.1.0.tgz", "integrity": "sha512-fVcdyuKRr0EZ4fjWl3c+gp1BANFJD1+RaWa2UPYfMZ6jCtp5RG00kSaXnK/dE5sYzt4kaWJ9qdxqUfc0d9kX0w==", - "dev": true + "dev": true, + "requires": {} }, "eslint-scope": { "version": "5.1.1", @@ -40978,7 +38481,8 @@ "version": "2.1.1", "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-2.1.1.tgz", "integrity": "sha1-YXmX/F9gV2iUxDX5QNgZ4TW4B2I=", - "dev": true + "dev": true, + "requires": {} }, "ansi-escapes": { "version": "3.2.0", @@ -42462,7 +39966,8 @@ "resolved": "https://registry.npmjs.org/icss-utils/-/icss-utils-5.1.0.tgz", "integrity": "sha512-soFhflCVWLfRNOPU3iv5Z9VUdT44xFRbzjLsEzSr5AQmgqPMTHdU3PMT1Cf1ssx8fLNJDA1juftYl+PUcv3MqA==", "dev": true, - "optional": true + "optional": true, + "requires": {} }, "ieee754": { "version": "1.2.1", @@ -43748,7 +41253,8 @@ "version": "8.0.1", "resolved": "https://registry.npmjs.org/karma-babel-preprocessor/-/karma-babel-preprocessor-8.0.1.tgz", "integrity": "sha512-5upyawNi3c7Gg6tPH1FWRVTmUijGf3v1GV4ScLM/2jKdDP18SlaKlUpu8eJrRI3STO8qK1bkqFcdgAA364nLYQ==", - "dev": true + "dev": true, + "requires": {} }, "karma-browserstack-launcher": { "version": "1.4.0", @@ -43765,7 +41271,8 @@ "version": "0.1.0", "resolved": "https://registry.npmjs.org/karma-chai/-/karma-chai-0.1.0.tgz", "integrity": "sha1-vuWtQEAFF4Ea40u5RfdikJEIt5o=", - "dev": true + "dev": true, + "requires": {} }, "karma-chrome-launcher": { "version": "3.1.0", @@ -43967,25 +41474,29 @@ "version": "1.0.0", "resolved": "https://registry.npmjs.org/karma-opera-launcher/-/karma-opera-launcher-1.0.0.tgz", "integrity": "sha1-+lFihTGh0L6EstjcDX7iCfyP+Ro=", - "dev": true + "dev": true, + "requires": {} }, "karma-safari-launcher": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/karma-safari-launcher/-/karma-safari-launcher-1.0.0.tgz", "integrity": "sha1-lpgqLMR9BmquccVTursoMZEVos4=", - "dev": true + "dev": true, + "requires": {} }, "karma-script-launcher": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/karma-script-launcher/-/karma-script-launcher-1.0.0.tgz", "integrity": "sha1-zQF8TeXvCeWp2nkydhdhCN1LVC0=", - "dev": true + "dev": true, + "requires": {} }, "karma-sinon": { "version": "1.0.5", "resolved": "https://registry.npmjs.org/karma-sinon/-/karma-sinon-1.0.5.tgz", "integrity": "sha1-TjRD8oMP3s/2JNN0cWPxIX2qKpo=", - "dev": true + "dev": true, + "requires": {} }, "karma-sourcemap-loader": { "version": "0.3.8", @@ -45785,7 +43296,6 @@ "integrity": "sha512-eiyIZj/A0dj1o4ywXWqicazUL3l0HP3TydUR6xF0X3xh3LGBMLqW8a9aFe6MuNH4mxNMk53QKBHM6LOPR8kSgw==", "dev": true, "requires": { - "JSONStream": "^1.0.3", "browser-resolve": "^1.7.0", "cached-path-relative": "^1.0.0", "concat-stream": "~1.5.0", @@ -45793,6 +43303,7 @@ "detective": "^5.2.0", "duplexer2": "^0.1.2", "inherits": "^2.0.1", + "JSONStream": "^1.0.3", "konan": "^2.1.1", "readable-stream": "^2.0.2", "resolve": "^1.1.3", @@ -47116,7 +44627,8 @@ "resolved": "https://registry.npmjs.org/postcss-modules-extract-imports/-/postcss-modules-extract-imports-3.0.0.tgz", "integrity": "sha512-bdHleFnP3kZ4NYDhuGlVK+CMrQ/pqUm8bx/oGL93K6gVwiclvX5x0n76fYMKuIGKzlABOy13zsvqjb0f92TEXw==", "dev": true, - "optional": true + "optional": true, + "requires": {} }, "postcss-modules-local-by-default": { "version": "4.0.0", @@ -49264,6 +46776,23 @@ "integrity": "sha1-ucczDHBChi9rFC3CdLvMWGbONUY=", "dev": true }, + "string_decoder": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz", + "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==", + "dev": true, + "requires": { + "safe-buffer": "~5.2.0" + }, + "dependencies": { + "safe-buffer": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", + "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", + "dev": true + } + } + }, "string-hash": { "version": "1.1.3", "resolved": "https://registry.npmjs.org/string-hash/-/string-hash-1.1.3.tgz", @@ -49308,23 +46837,6 @@ "define-properties": "^1.1.3" } }, - "string_decoder": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz", - "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==", - "dev": true, - "requires": { - "safe-buffer": "~5.2.0" - }, - "dependencies": { - "safe-buffer": { - "version": "5.2.1", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", - "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", - "dev": true - } - } - }, "stringify-entities": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/stringify-entities/-/stringify-entities-3.1.0.tgz", @@ -52840,7 +50352,8 @@ "version": "7.5.0", "resolved": "https://registry.npmjs.org/ws/-/ws-7.5.0.tgz", "integrity": "sha512-6ezXvzOZupqKj4jUqbQ9tXuJNo+BR2gU8fFRk3XCP3e0G6WT414u5ELe6Y0vtp7kmSJ3F7YWObSNr1ESsgi4vw==", - "dev": true + "dev": true, + "requires": {} }, "xtend": { "version": "4.0.2", From 729e6082c983f46c88c0dfc9d6862b10d01adc16 Mon Sep 17 00:00:00 2001 From: Joshua Fledderjohn Date: Wed, 30 Mar 2022 07:31:11 -0700 Subject: [PATCH 23/53] Duped upstream/master's package.lock file ... not sure how it got changed in the first place --- package-lock.json | 177 ---------------------------------------------- 1 file changed, 177 deletions(-) diff --git a/package-lock.json b/package-lock.json index 5f2510442ba..9fbd4aea6a8 100644 --- a/package-lock.json +++ b/package-lock.json @@ -27460,111 +27460,6 @@ } } }, -<<<<<<< HEAD - "babel-plugin-dynamic-import-node": { - "version": "2.3.3", - "resolved": "https://registry.npmjs.org/babel-plugin-dynamic-import-node/-/babel-plugin-dynamic-import-node-2.3.3.tgz", - "integrity": "sha512-jZVI+s9Zg3IqA/kdi0i6UDCybUI3aSBLnglhYbSSjKlV7yF1F/5LWv8MakQmvYpnbJDS6fcBL2KzHSxNCMtWSQ==", - "dev": true, - "requires": { - "object.assign": "^4.1.0" - } - }, - "babel-plugin-polyfill-corejs2": { - "version": "0.2.2", - "resolved": "https://registry.npmjs.org/babel-plugin-polyfill-corejs2/-/babel-plugin-polyfill-corejs2-0.2.2.tgz", - "integrity": "sha512-kISrENsJ0z5dNPq5eRvcctITNHYXWOA4DUZRFYCz3jYCcvTb/A546LIddmoGNMVYg2U38OyFeNosQwI9ENTqIQ==", - "dev": true, - "requires": { - "@babel/compat-data": "^7.13.11", - "@babel/helper-define-polyfill-provider": "^0.2.2", - "semver": "^6.1.1" - } - }, - "babel-plugin-polyfill-corejs3": { - "version": "0.2.3", - "resolved": "https://registry.npmjs.org/babel-plugin-polyfill-corejs3/-/babel-plugin-polyfill-corejs3-0.2.3.tgz", - "integrity": "sha512-rCOFzEIJpJEAU14XCcV/erIf/wZQMmMT5l5vXOpL5uoznyOGfDIjPj6FVytMvtzaKSTSVKouOCTPJ5OMUZH30g==", - "dev": true, - "requires": { - "@babel/helper-define-polyfill-provider": "^0.2.2", - "core-js-compat": "^3.14.0" - } - }, - "babel-plugin-polyfill-regenerator": { - "version": "0.2.2", - "resolved": "https://registry.npmjs.org/babel-plugin-polyfill-regenerator/-/babel-plugin-polyfill-regenerator-0.2.2.tgz", - "integrity": "sha512-Goy5ghsc21HgPDFtzRkSirpZVW35meGoTmTOb2bxqdl60ghub4xOidgNTHaZfQ2FaxQsKmwvXtOAkcIS4SMBWg==", - "dev": true, - "requires": { - "@babel/helper-define-polyfill-provider": "^0.2.2" - } - }, - "babel-plugin-transform-object-assign": { - "version": "6.22.0", - "resolved": "https://registry.npmjs.org/babel-plugin-transform-object-assign/-/babel-plugin-transform-object-assign-6.22.0.tgz", - "integrity": "sha1-+Z0vZvGgsNSY40bFNZaEdAyqILo=", - "requires": { - "babel-runtime": "^6.22.0" - } - }, - "babel-runtime": { - "version": "6.26.0", - "resolved": "https://registry.npmjs.org/babel-runtime/-/babel-runtime-6.26.0.tgz", - "integrity": "sha1-llxwWGaOgrVde/4E/yM3vItWR/4=", - "requires": { - "core-js": "^2.4.0", - "regenerator-runtime": "^0.11.0" - }, - "dependencies": { - "core-js": { - "version": "2.6.12", - "resolved": "https://registry.npmjs.org/core-js/-/core-js-2.6.12.tgz", - "integrity": "sha512-Kb2wC0fvsWfQrgk8HU5lW6U/Lcs8+9aaYcy4ZFc6DDlo4nZ7n70dEgE5rtR0oG6ufKDUnrwfWL1mXR5ljDatrQ==" - } - } - }, - "babelify": { - "version": "10.0.0", - "resolved": "https://registry.npmjs.org/babelify/-/babelify-10.0.0.tgz", - "integrity": "sha512-X40FaxyH7t3X+JFAKvb1H9wooWKLRCi8pg3m8poqtdZaIng+bjzp9RvKQCvRjF9isHiPkXspbbXT/zwXLtwgwg==", - "dev": true, - "requires": {} - }, - "bach": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/bach/-/bach-1.2.0.tgz", - "integrity": "sha1-Szzpa/JxNPeaG0FKUcFONMO9mIA=", - "dev": true, - "requires": { - "arr-filter": "^1.1.1", - "arr-flatten": "^1.0.1", - "arr-map": "^2.0.0", - "array-each": "^1.0.0", - "array-initial": "^1.0.0", - "array-last": "^1.1.1", - "async-done": "^1.2.2", - "async-settle": "^1.0.0", - "now-and-later": "^2.0.0" - } - }, - "bail": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/bail/-/bail-1.0.5.tgz", - "integrity": "sha512-xFbRxM1tahm08yHBP16MMjVUAvDaBMD38zsM9EMAUN61omwLmKlOpB/Zku5QkjZ8TZ4vn53pj+t518cH0S03RQ==", - "dev": true - }, - "balanced-match": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", - "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", - "dev": true - }, - "base": { - "version": "0.11.2", - "resolved": "https://registry.npmjs.org/base/-/base-0.11.2.tgz", - "integrity": "sha512-5T6P4xPgpp0YDFvSWwEZ4NoE3aM4QBQXDzmVbraCkFj8zHM+mba8SyqB5DbZWyR7mYHo6Y7BdQo3MoA4m0TeQg==", -======= "character-entities": { "version": "1.2.4", "resolved": "https://registry.npmjs.org/character-entities/-/character-entities-1.2.4.tgz", @@ -27605,7 +27500,6 @@ "version": "3.5.3", "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.5.3.tgz", "integrity": "sha512-Dr3sfKRP6oTcjf2JmUmFJfeVMvXBdegxB0iVQ5eb2V10uFJUCAS8OByZdVAyVb8xXNz3GjjTgj9kLWsZTqE6kw==", ->>>>>>> master "dev": true, "requires": { "anymatch": "~3.1.2", @@ -31053,25 +30947,10 @@ "xtend": "~4.0.1" } }, -<<<<<<< HEAD -<<<<<<< HEAD - "ws": { - "version": "7.4.6", - "resolved": "https://registry.npmjs.org/ws/-/ws-7.4.6.tgz", - "integrity": "sha512-YmhHDO4MzaDLB+M9ym/mDA5z0naX8j7SIlT8f8z+I0VtzsRbekxEutHSme7NPS2qE8StCYQNUnfWdXta/Yu85A==", -<<<<<<< HEAD - "dev": true -======= - "is-descriptor": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-1.0.2.tgz", - "integrity": "sha512-2eis5WqQGV7peooDyLmNEPUrps9+SXX5c9pL3xEB+4e9HnGuDa7mB7kHxHw4CbqS9k1T2hOH3miL8n8WtiYVtg==", -======= "yargs": { "version": "16.2.0", "resolved": "https://registry.npmjs.org/yargs/-/yargs-16.2.0.tgz", "integrity": "sha512-D1mvvtDG0L5ft/jGWkLpG1+m0eQxOfaBvTNELraWj22wSVUMWxZUvYgJYcKh6jGGIkJFhH4IZPQhR4TKpc8mBw==", ->>>>>>> master "dev": true, "requires": { "cliui": "^7.0.2", @@ -31082,20 +30961,12 @@ "y18n": "^5.0.5", "yargs-parser": "^20.2.2" } -<<<<<<< HEAD ->>>>>>> master -======= - "dev": true, - "requires": {} ->>>>>>> parent of a6fc789... Merged master -======= }, "yargs-parser": { "version": "20.2.9", "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-20.2.9.tgz", "integrity": "sha512-y11nGElTIV+CT3Zv9t7VKl+Q3hTQoT9a1Qzezhhl6Rp21gJ/IVTW7Z3y9EWXhuUBC2Shnf+DX0antecpAwSP8w==", "dev": true ->>>>>>> master } } }, @@ -31601,59 +31472,11 @@ "integrity": "sha512-NtNxqUcXgpW2iMrfqSfR73Glt39K+BLwWsPs94yR63v45T0Wbej7eRmL5cWfwEgqXnmjQp3zaJTshdRW/qC2ZQ==", "dev": true }, -<<<<<<< HEAD - "elliptic": { - "version": "6.5.4", - "resolved": "https://registry.npmjs.org/elliptic/-/elliptic-6.5.4.tgz", - "integrity": "sha512-iLhC6ULemrljPZb+QutR5TQGB+pdW6KGD5RSegS+8sorOZT+rdQFbsQFJgvN3eRqNALqJer4oQ16YvJHlU8hzQ==", - "dev": true, - "requires": { - "bn.js": "^4.11.9", - "brorand": "^1.1.0", - "hash.js": "^1.0.0", - "hmac-drbg": "^1.0.1", - "inherits": "^2.0.4", - "minimalistic-assert": "^1.0.1", - "minimalistic-crypto-utils": "^1.0.1" - }, - "dependencies": { - "bn.js": { - "version": "4.12.0", - "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.12.0.tgz", - "integrity": "sha512-c98Bf3tPniI+scsdk237ku1Dc3ujXQTSgyiPUDEOe7tRkhrqridvh8klBv0HCEso1OLOYcHuCv/cS6DNxKH+ZA==", - "dev": true - }, - "inherits": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", - "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", - "dev": true - } - } - }, -<<<<<<< HEAD - "eslint-config-standard": { - "version": "10.2.1", - "resolved": "https://registry.npmjs.org/eslint-config-standard/-/eslint-config-standard-10.2.1.tgz", - "integrity": "sha1-wGHk0GbzedwXzVYsZOgZtN1FRZE=", -<<<<<<< HEAD -======= - "emoji-regex": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", - "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", ->>>>>>> master -======= "grapheme-splitter": { "version": "1.0.4", "resolved": "https://registry.npmjs.org/grapheme-splitter/-/grapheme-splitter-1.0.4.tgz", "integrity": "sha512-bzh50DW9kTPM00T8y4o8vQg89Di9oLJVLW/KaOGIXJWP/iqCN6WKYkbNOF04vFLJhwcpYUh9ydh/+5vpOqV4YQ==", ->>>>>>> master "dev": true -======= - "dev": true, - "requires": {} ->>>>>>> parent of a6fc789... Merged master }, "growl": { "version": "1.10.5", From f26e0646240464e2b72c77152d1aecea17a82944 Mon Sep 17 00:00:00 2001 From: Joshua Fledderjohn Date: Wed, 30 Mar 2022 08:51:45 -0700 Subject: [PATCH 24/53] Small refactor of filterData length check. Removed comparison with 0 since a length value of 0 is already falsy. --- modules/nativoBidAdapter.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/nativoBidAdapter.js b/modules/nativoBidAdapter.js index f5b2595604d..e07a124665f 100644 --- a/modules/nativoBidAdapter.js +++ b/modules/nativoBidAdapter.js @@ -473,7 +473,7 @@ const area = (size) => size[0] * size[1] * @param {Array} filterData - The filter data to add */ function appendFilterData(filter, filterData) { - if (filterData && Array.isArray(filterData) && filterData.length > 0) { + if (filterData && Array.isArray(filterData) && filterData.length) { filterData.forEach((ad) => filter.add(ad)) } } From 060ff4cd1cb8ee43ba596faacd82e38786a52ed1 Mon Sep 17 00:00:00 2001 From: Joshua Fledderjohn Date: Mon, 6 Jun 2022 09:55:36 -0700 Subject: [PATCH 25/53] Added bid sizes to request --- modules/nativoBidAdapter.js | 18 +++++++++++++++--- 1 file changed, 15 insertions(+), 3 deletions(-) diff --git a/modules/nativoBidAdapter.js b/modules/nativoBidAdapter.js index e07a124665f..e15177ed0fc 100644 --- a/modules/nativoBidAdapter.js +++ b/modules/nativoBidAdapter.js @@ -132,8 +132,9 @@ export const spec = { */ buildRequests: function (validBidRequests, bidderRequest) { const placementIds = new Set() - let placementId, pageUrl const bidDataMap = BidDataMap() + const placementSizes = { length: 0 } + let placementId, pageUrl validBidRequests.forEach((request) => { pageUrl = deepAccess( request, @@ -142,15 +143,21 @@ export const spec = { ) placementId = deepAccess(request, 'params.placementId') - if (placementId) { + const bidDataKeys = [request.adUnitCode] + + if (placementId && !placementIds.has(placementId)) { placementIds.add(placementId) + bidDataKeys.push(placementId) + + placementSizes[placementId] = request.sizes + placementSizes.length++ } const bidData = { bidId: request.bidId, size: getLargestSize(request.sizes), } - bidDataMap.addBidData(bidData, [placementId, request.adUnitCode]) + bidDataMap.addBidData(bidData, bidDataKeys) }) bidRequestMap[bidderRequest.bidderRequestId] = bidDataMap @@ -199,6 +206,11 @@ export const spec = { params.unshift({ key: 'ntv_ctf', value: Array.from(campaignsToFilter).join(',') }) } + // Placement Sizes + if (placementSizes.length) { + params.unshift({ key: 'ntv_pas', value: btoa(JSON.stringify(placementSizes)) }) + } + // Add placement IDs if (placementIds.size > 0) { // Convert Set to Array (IE 11 Safe) From 15d34f1840c252ced25282fbf9a2e702a78e7bf1 Mon Sep 17 00:00:00 2001 From: Joshua Fledderjohn Date: Mon, 6 Jun 2022 13:49:25 -0700 Subject: [PATCH 26/53] Fixed function name in spec. Added unit tests. --- modules/nativoBidAdapter.js | 8 +-- test/spec/modules/nativoBidAdapter_spec.js | 84 +++++++++++++++++----- 2 files changed, 71 insertions(+), 21 deletions(-) diff --git a/modules/nativoBidAdapter.js b/modules/nativoBidAdapter.js index e15177ed0fc..837fc81c319 100644 --- a/modules/nativoBidAdapter.js +++ b/modules/nativoBidAdapter.js @@ -16,7 +16,7 @@ const SUPPORTED_AD_TYPES = [BANNER] * Keep track of bid data by keys * @returns {Object} - Map of bid data that can be referenced by multiple keys */ -const BidDataMap = () => { +export const BidDataMap = () => { const referenceMap = {} const bids = [] @@ -25,7 +25,7 @@ const BidDataMap = () => { * @param {String} key - The key to store the index reference * @param {Integer} index - The index value of the bidData */ - function adKeyReference(key, index) { + function addKeyReference(key, index) { if (!referenceMap.hasOwnProperty(key)) { referenceMap[key] = index } @@ -42,12 +42,12 @@ const BidDataMap = () => { if (Array.isArray(keys)) { keys.forEach((key) => { - adKeyReference(String(key), index) + addKeyReference(String(key), index) }) return } - adKeyReference(String(keys), index) + addKeyReference(String(keys), index) } /** diff --git a/test/spec/modules/nativoBidAdapter_spec.js b/test/spec/modules/nativoBidAdapter_spec.js index c552090cf6e..0690c7f90e1 100644 --- a/test/spec/modules/nativoBidAdapter_spec.js +++ b/test/spec/modules/nativoBidAdapter_spec.js @@ -1,5 +1,33 @@ import { expect } from 'chai' -import { spec } from 'modules/nativoBidAdapter.js' +import { spec, BidDataMap } from 'modules/nativoBidAdapter.js' + +describe('bidDataMap', function () { + it('Should fail gracefully if no key value pairs have been added and no key is sent', function () { + const bdm = new BidDataMap() + const bidData = bdm.getBidData() + expect(bidData).to.be.undefined + }) + + it('Should fail gracefully if no key value pairs have been added', function () { + const bdm = new BidDataMap() + const bidData = bdm.getBidData('testKey') + expect(bidData).to.be.undefined + }) + + it('Should add bid data to corresponding keys', function () { + const keys = ['key1', 'anotherKey', 6] + const bidData = { prop: 'value' } + + const bdm = new BidDataMap() + bdm.addBidData(bidData, keys) + const bidDataKey0 = bdm.getBidData(keys[0]) + const bidDataKey1 = bdm.getBidData(keys[1]) + const bidDataKey2 = bdm.getBidData(keys[2]) + expect(bidDataKey0).to.be.equal(bidData) + expect(bidDataKey1).to.be.equal(bidData) + expect(bidDataKey2).to.be.equal(bidData) + }) +}) describe('nativoBidAdapterTests', function () { describe('isBidRequestValid', function () { @@ -48,23 +76,28 @@ describe('nativoBidAdapterTests', function () { }) describe('buildRequests', function () { - let bidRequests = [ - { - bidder: 'nativo', - params: { - placementId: '10433394', - }, - adUnitCode: 'adunit-code', - sizes: [ - [300, 250], - [300, 600], - ], - bidId: '27b02036ccfa6e', - bidderRequestId: '1372cd8bd8d6a8', - auctionId: 'cfc467e4-2707-48da-becb-bcaab0b2c114', - transactionId: '3b36e7e0-0c3e-4006-a279-a741239154ff', + const bidRequest = { + bidder: 'nativo', + params: { + placementId: '10433394', }, - ] + adUnitCode: 'adunit-code', + sizes: [ + [300, 250], + [300, 600], + ], + bidId: '27b02036ccfa6e', + bidderRequestId: '1372cd8bd8d6a8', + auctionId: 'cfc467e4-2707-48da-becb-bcaab0b2c114', + transactionId: '3b36e7e0-0c3e-4006-a279-a741239154ff', + } + const bidRequestString = JSON.stringify(bidRequest) + let bidRequests + + beforeEach(function() { + // Clone bidRequest each time + bidRequests = [JSON.parse(bidRequestString)] + }) it('url should contain query string parameters', function () { const request = spec.buildRequests(bidRequests, { @@ -83,6 +116,23 @@ describe('nativoBidAdapterTests', function () { expect(request.url).to.include('ntv_ppc') expect(request.url).to.include('ntv_url') expect(request.url).to.include('ntv_dbr') + expect(request.url).to.include('ntv_pas') + }) + + it('url should NOT contain placement specific query string parameters if placementId option is not provided', function () { + bidRequests[0].params = {} + const request = spec.buildRequests(bidRequests, { + bidderRequestId: 123456, + refererInfo: { + referer: 'https://www.test.com', + }, + }) + + expect(request.url).to.exist + expect(request.url).to.be.a('string') + + expect(request.url).to.not.include('ntv_pas') + expect(request.url).to.not.include('ntv_ptd') }) }) }) From 7874280d468d33878f55e3c893da1e3133c52d95 Mon Sep 17 00:00:00 2001 From: Joshua Fledderjohn Date: Fri, 8 Jul 2022 12:38:43 -0700 Subject: [PATCH 27/53] Added priceFloor module support --- modules/nativoBidAdapter.js | 193 ++++++++++++++++++--- test/spec/modules/nativoBidAdapter_spec.js | 135 +++++++++++++- 2 files changed, 301 insertions(+), 27 deletions(-) diff --git a/modules/nativoBidAdapter.js b/modules/nativoBidAdapter.js index 271ecac7aa2..82c7a02f74c 100644 --- a/modules/nativoBidAdapter.js +++ b/modules/nativoBidAdapter.js @@ -11,6 +11,8 @@ const GVLID = 263 const TIME_TO_LIVE = 360 const SUPPORTED_AD_TYPES = [BANNER] +const FLOOR_PRICE_CURRENCY = 'USD' +const PRICE_FLOOR_WILDCARD = '*' /** * Keep track of bid data by keys @@ -131,84 +133,103 @@ export const spec = { * @return ServerRequest Info describing the request to the server. */ buildRequests: function (validBidRequests, bidderRequest) { + // Parse values from bid requests const placementIds = new Set() const bidDataMap = BidDataMap() const placementSizes = { length: 0 } + const floorPriceData = {} let placementId, pageUrl - validBidRequests.forEach((request) => { + validBidRequests.forEach((bidRequest) => { pageUrl = deepAccess( - request, + bidRequest, 'params.url', bidderRequest.refererInfo.page ) - placementId = deepAccess(request, 'params.placementId') + placementId = deepAccess(bidRequest, 'params.placementId') - const bidDataKeys = [request.adUnitCode] + const bidDataKeys = [bidRequest.adUnitCode] if (placementId && !placementIds.has(placementId)) { placementIds.add(placementId) bidDataKeys.push(placementId) - placementSizes[placementId] = request.sizes + placementSizes[placementId] = bidRequest.sizes placementSizes.length++ } const bidData = { - bidId: request.bidId, - size: getLargestSize(request.sizes), + bidId: bidRequest.bidId, + size: getLargestSize(bidRequest.sizes), } bidDataMap.addBidData(bidData, bidDataKeys) + + const bidRequestFloorPriceData = parseFloorPriceData(bidRequest) + if (bidRequestFloorPriceData) { + floorPriceData[bidRequest.adUnitCode] = bidRequestFloorPriceData + } }) bidRequestMap[bidderRequest.bidderRequestId] = bidDataMap // Build adUnit data - const adUnitData = { - adUnits: validBidRequests.map((adUnit) => { - // Track if we've already requested for this ad unit code - adUnitsRequested[adUnit.adUnitCode] = - adUnitsRequested[adUnit.adUnitCode] !== undefined - ? adUnitsRequested[adUnit.adUnitCode] + 1 - : 0 - return { - adUnitCode: adUnit.adUnitCode, - mediaTypes: adUnit.mediaTypes, - } - }), - } + const adUnitData = buildAdUnitData(validBidRequests) - // Build QS Params + // Build basic required QS Params let params = [ + // Prebid request id { key: 'ntv_pb_rid', value: bidderRequest.bidderRequestId }, + // Ad unit data { key: 'ntv_ppc', value: btoa(JSON.stringify(adUnitData)), // Convert to Base 64 }, + // Number count of requests per ad unit { key: 'ntv_dbr', - value: btoa(JSON.stringify(adUnitsRequested)), + value: btoa(JSON.stringify(adUnitsRequested)), // Convert to Base 64 }, + // Page url { key: 'ntv_url', value: encodeURIComponent(pageUrl), }, ] + // Floor pricing + if (Object.keys(floorPriceData).length) { + params.unshift({ + key: 'ntv_ppf', + value: btoa(JSON.stringify(floorPriceData)), + }) + } + // Add filtering if (adsToFilter.size > 0) { - params.unshift({ key: 'ntv_atf', value: Array.from(adsToFilter).join(',') }) + params.unshift({ + key: 'ntv_atf', + value: Array.from(adsToFilter).join(','), + }) } if (advertisersToFilter.size > 0) { - params.unshift({ key: 'ntv_avtf', value: Array.from(advertisersToFilter).join(',') }) + params.unshift({ + key: 'ntv_avtf', + value: Array.from(advertisersToFilter).join(','), + }) } if (campaignsToFilter.size > 0) { - params.unshift({ key: 'ntv_ctf', value: Array.from(campaignsToFilter).join(',') }) + params.unshift({ + key: 'ntv_ctf', + value: Array.from(campaignsToFilter).join(','), + }) } // Placement Sizes if (placementSizes.length) { - params.unshift({ key: 'ntv_pas', value: btoa(JSON.stringify(placementSizes)) }) + params.unshift({ + key: 'ntv_pas', + value: btoa(JSON.stringify(placementSizes)), + }) } // Add placement IDs @@ -429,6 +450,126 @@ export const spec = { registerBidder(spec) // Utils +export function parseFloorPriceData(bidRequest) { + if (typeof bidRequest.getFloor !== 'function') return + + // Setup price floor data per bid request + let bidRequestFloorPriceData = {} + let bidMediaTypes = bidRequest.mediaTypes + let sizeOptions = new Set() + // Step through meach media type so we can get floor data for each media type per bid request + Object.keys(bidMediaTypes).forEach((mediaType) => { + // Setup price floor data per media type + let mediaTypeData = bidMediaTypes[mediaType] + let mediaTypeFloorPriceData = {} + // Step through each size of the media type so we can get floor data for each size per media type + mediaTypeData.sizes.forEach((size) => { + // Get floor price data per the getFloor method and respective media type / size combination + const priceFloorData = bidRequest.getFloor({ + currency: FLOOR_PRICE_CURRENCY, + mediaType, + size, + }) + // Save the data and track the sizes + mediaTypeFloorPriceData[sizeToString(size)] = priceFloorData.floor + sizeOptions.add(size) + }) + bidRequestFloorPriceData[mediaType] = mediaTypeFloorPriceData + + // Get floor price of current media type with a wildcard size + const sizeWildcardFloor = getSizeWildcardPrice(bidRequest, mediaType) + // Save the wildcard floor price if it was retrieved successfully + if (sizeWildcardFloor.floor > 0) { + mediaTypeFloorPriceData['*'] = sizeWildcardFloor.floor + } + }) + + // Get floor price for wildcard media type using all of the sizes present in the previous media types + const mediaWildCardPrices = getMediaWildcardPrices(bidRequest, [ + PRICE_FLOOR_WILDCARD, + ...Array.from(sizeOptions), + ]) + bidRequestFloorPriceData['*'] = mediaWildCardPrices + + return bidRequestFloorPriceData +} + +/** + * Get price floor data by always setting the size value to the wildcard for a specific size + * @param {Object} bidRequest - The bid request + * @param {String} mediaType - The media type + * @returns {Object} - Bid floor data + */ +export function getSizeWildcardPrice(bidRequest, mediaType) { + return bidRequest.getFloor({ + currency: FLOOR_PRICE_CURRENCY, + mediaType, + size: PRICE_FLOOR_WILDCARD, + }) +} + +/** + * Get price data for a range of sizes and always setting the media type to the wildcard value + * @param {*} bidRequest - The bid request + * @param {*} sizes - The sizes to get the floor price data for + * @returns {Object} - Bid floor data + */ +export function getMediaWildcardPrices( + bidRequest, + sizes = [PRICE_FLOOR_WILDCARD] +) { + const sizePrices = {} + sizes.forEach((size) => { + // MODIFY the bid request's mediaTypes property (so we can get the wildcard media type value) + const temp = bidRequest.mediaTypes + bidRequest.mediaTypes = { PRICE_FLOOR_WILDCARD: temp.sizes } + // Get price floor data + const priceFloorData = bidRequest.getFloor({ + currency: FLOOR_PRICE_CURRENCY, + mediaType: PRICE_FLOOR_WILDCARD, + size, + }) + // RESTORE initial property value + bidRequest.mediaTypes = temp + + // Only save valid floor price data + const key = + size !== PRICE_FLOOR_WILDCARD ? sizeToString(size) : PRICE_FLOOR_WILDCARD + sizePrices[key] = priceFloorData.floor + }) + return sizePrices +} + +/** + * Format size array to a string + * @param {Array} size - Size data [width, height] + * @returns {String} - Formated size string + */ +export function sizeToString(size) { + if (!Array.isArray(size) || size.length < 2) return '' + return `${size[0]}x${size[1]}` +} + +/** + * Build the ad unit data to send back to the request endpoint + * @param {Array} requests - Bid requests + * @returns {Array} - Array of ad unit data + */ +function buildAdUnitData(requests) { + return requests.map((request) => { + // Track if we've already requested for this ad unit code + adUnitsRequested[request.adUnitCode] = + adUnitsRequested[request.adUnitCode] !== undefined + ? adUnitsRequested[request.adUnitCode] + 1 + : 0 + // Return a new object with only the data we need + return { + adUnitCode: request.adUnitCode, + mediaTypes: request.mediaTypes, + } + }) +} + /** * Append QS param to existing string * @param {String} str - String to append to diff --git a/test/spec/modules/nativoBidAdapter_spec.js b/test/spec/modules/nativoBidAdapter_spec.js index 0690c7f90e1..f3a04b05892 100644 --- a/test/spec/modules/nativoBidAdapter_spec.js +++ b/test/spec/modules/nativoBidAdapter_spec.js @@ -1,5 +1,11 @@ import { expect } from 'chai' import { spec, BidDataMap } from 'modules/nativoBidAdapter.js' +import { + getSizeWildcardPrice, + getMediaWildcardPrices, + sizeToString, + parseFloorPriceData, +} from '../../../modules/nativoBidAdapter' describe('bidDataMap', function () { it('Should fail gracefully if no key value pairs have been added and no key is sent', function () { @@ -94,7 +100,7 @@ describe('nativoBidAdapterTests', function () { const bidRequestString = JSON.stringify(bidRequest) let bidRequests - beforeEach(function() { + beforeEach(function () { // Clone bidRequest each time bidRequests = [JSON.parse(bidRequestString)] }) @@ -489,3 +495,130 @@ describe('Response to Request Filter Flow', () => { expect(request.url).to.include('ntv_ctf=234') }) }) + +describe('sizeToString', () => { + it('Formats size array correctly', () => { + const sizeString = sizeToString([300, 250]) + expect(sizeString).to.be.equal('300x250') + }) + + it('Returns an empty array for invalid data', () => { + // Not an array + let sizeString = sizeToString(300, 350) + expect(sizeString).to.be.equal('') + // Single entry + sizeString = sizeToString([300]) + expect(sizeString).to.be.equal('') + // Undefined + sizeString = sizeToString(undefined) + expect(sizeString).to.be.equal('') + }) +}) + +describe('getSizeWildcardPrice', () => { + it('Generates the correct floor price data', () => { + let floorPrice = { + currency: 'USD', + floor: 1.0, + } + let getFloorMock = () => { + return floorPrice + } + let floorMockSpy = sinon.spy(getFloorMock) + let bidRequest = { + getFloor: floorMockSpy, + mediaTypes: { + banner: { + sizes: [300, 250], + }, + }, + } + + let result = getSizeWildcardPrice(bidRequest, 'banner') + expect( + floorMockSpy.calledWith({ + currency: 'USD', + mediaType: 'banner', + size: '*', + }) + ).to.be.true + expect(result).to.equal(floorPrice) + }) +}) + +describe('getMediaWildcardPrices', () => { + it('Generates the correct floor price data', () => { + let defaultFloorPrice = { + currency: 'USD', + floor: 1.1, + } + let sizefloorPrice = { + currency: 'USD', + floor: 2.2, + } + let getFloorMock = ({ currency, mediaType, size }) => { + if (Array.isArray(size)) return sizefloorPrice + + return defaultFloorPrice + } + let floorMockSpy = sinon.spy(getFloorMock) + let bidRequest = { + getFloor: floorMockSpy, + mediaTypes: { + banner: { + sizes: [300, 250], + }, + }, + } + + let result = getMediaWildcardPrices(bidRequest, ['*', [300, 250]]) + expect( + floorMockSpy.calledWith({ + currency: 'USD', + mediaType: '*', + size: '*', + }) + ).to.be.true + expect( + floorMockSpy.calledWith({ + currency: 'USD', + mediaType: '*', + size: [300, 250], + }) + ).to.be.true + expect(result).to.deep.equal({ '*': 1.1, '300x250': 2.2 }) + }) +}) + +describe('parseFloorPriceData', () => { + it('Generates the correct floor price data', () => { + let defaultFloorPrice = { + currency: 'USD', + floor: 1.1, + } + let sizefloorPrice = { + currency: 'USD', + floor: 2.2, + } + let getFloorMock = ({ currency, mediaType, size }) => { + if (Array.isArray(size)) return sizefloorPrice + + return defaultFloorPrice + } + let floorMockSpy = sinon.spy(getFloorMock) + let bidRequest = { + getFloor: floorMockSpy, + mediaTypes: { + banner: { + sizes: [[300, 250]], + }, + }, + } + + let result = parseFloorPriceData(bidRequest) + expect(result).to.deep.equal({ + '*': { '*': 1.1, '300x250': 2.2 }, + banner: { '*': 1.1, '300x250': 2.2 }, + }) + }) +}) From 761959062ddb6dd96eb46abea585173a46441fa2 Mon Sep 17 00:00:00 2001 From: Joshua Fledderjohn Date: Wed, 13 Jul 2022 09:21:55 -0700 Subject: [PATCH 28/53] Added protection agains empty url parameter --- modules/nativoBidAdapter.js | 5 ++++- test/spec/modules/nativoBidAdapter_spec.js | 14 ++++++++++++++ 2 files changed, 18 insertions(+), 1 deletion(-) diff --git a/modules/nativoBidAdapter.js b/modules/nativoBidAdapter.js index 82c7a02f74c..54c99b17834 100644 --- a/modules/nativoBidAdapter.js +++ b/modules/nativoBidAdapter.js @@ -143,8 +143,11 @@ export const spec = { pageUrl = deepAccess( bidRequest, 'params.url', - bidderRequest.refererInfo.page ) + if (pageUrl == undefined || pageUrl === '') { + pageUrl = bidderRequest.refererInfo.page + } + placementId = deepAccess(bidRequest, 'params.placementId') const bidDataKeys = [bidRequest.adUnitCode] diff --git a/test/spec/modules/nativoBidAdapter_spec.js b/test/spec/modules/nativoBidAdapter_spec.js index f3a04b05892..be6b07f9acc 100644 --- a/test/spec/modules/nativoBidAdapter_spec.js +++ b/test/spec/modules/nativoBidAdapter_spec.js @@ -125,6 +125,20 @@ describe('nativoBidAdapterTests', function () { expect(request.url).to.include('ntv_pas') }) + it('ntv_url parameter should NOT be empty even if the utl parameter was set as an empty value', function () { + bidRequests[0].params.url = '' + const request = spec.buildRequests(bidRequests, { + bidderRequestId: 123456, + refererInfo: { + referer: 'https://www.test.com', + }, + }) + + expect(request.url).to.exist + expect(request.url).to.be.a('string') + expect(request.url).to.not.be.empty + }) + it('url should NOT contain placement specific query string parameters if placementId option is not provided', function () { bidRequests[0].params = {} const request = spec.buildRequests(bidRequests, { From c5b8fabf3c1325c78087abbf58803a92d81f751d Mon Sep 17 00:00:00 2001 From: Joshua Fledderjohn Date: Wed, 5 Oct 2022 13:15:03 -0700 Subject: [PATCH 29/53] Changed ntv_url QS param to use referrer.location instead of referrer.page --- modules/nativoBidAdapter.js | 2 +- test/spec/modules/nativoBidAdapter_spec.js | 17 ++++++++++++++--- 2 files changed, 15 insertions(+), 4 deletions(-) diff --git a/modules/nativoBidAdapter.js b/modules/nativoBidAdapter.js index 7177abecae0..7eb37ea8b82 100644 --- a/modules/nativoBidAdapter.js +++ b/modules/nativoBidAdapter.js @@ -145,7 +145,7 @@ export const spec = { 'params.url', ) if (pageUrl == undefined || pageUrl === '') { - pageUrl = bidderRequest.refererInfo.page + pageUrl = bidderRequest.refererInfo.location } placementId = deepAccess(bidRequest, 'params.placementId') diff --git a/test/spec/modules/nativoBidAdapter_spec.js b/test/spec/modules/nativoBidAdapter_spec.js index be6b07f9acc..cc400784b67 100644 --- a/test/spec/modules/nativoBidAdapter_spec.js +++ b/test/spec/modules/nativoBidAdapter_spec.js @@ -81,7 +81,7 @@ describe('nativoBidAdapterTests', function () { }) }) - describe('buildRequests', function () { + describe.only('buildRequests', function () { const bidRequest = { bidder: 'nativo', params: { @@ -125,12 +125,23 @@ describe('nativoBidAdapterTests', function () { expect(request.url).to.include('ntv_pas') }) + it.only('ntv_url should contain query params', function () { + const request = spec.buildRequests(bidRequests, { + bidderRequestId: 123456, + refererInfo: { + location: 'https://www.test.com?queryTest=true', + }, + }) + console.log(request.url) // eslint-disable-line no-console + expect(request.url).to.include(encodeURIComponent('?queryTest=true')) + }) + it('ntv_url parameter should NOT be empty even if the utl parameter was set as an empty value', function () { bidRequests[0].params.url = '' const request = spec.buildRequests(bidRequests, { bidderRequestId: 123456, refererInfo: { - referer: 'https://www.test.com', + location: 'https://www.test.com', }, }) @@ -144,7 +155,7 @@ describe('nativoBidAdapterTests', function () { const request = spec.buildRequests(bidRequests, { bidderRequestId: 123456, refererInfo: { - referer: 'https://www.test.com', + location: 'https://www.test.com', }, }) From 51ba4852f3e422bc8f71a8742af37b4ee5e5574c Mon Sep 17 00:00:00 2001 From: Joshua Fledderjohn Date: Wed, 5 Oct 2022 13:16:02 -0700 Subject: [PATCH 30/53] Removed testing 'only' flag --- test/spec/modules/nativoBidAdapter_spec.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/test/spec/modules/nativoBidAdapter_spec.js b/test/spec/modules/nativoBidAdapter_spec.js index cc400784b67..4cd4c92281b 100644 --- a/test/spec/modules/nativoBidAdapter_spec.js +++ b/test/spec/modules/nativoBidAdapter_spec.js @@ -81,7 +81,7 @@ describe('nativoBidAdapterTests', function () { }) }) - describe.only('buildRequests', function () { + describe('buildRequests', function () { const bidRequest = { bidder: 'nativo', params: { @@ -125,7 +125,7 @@ describe('nativoBidAdapterTests', function () { expect(request.url).to.include('ntv_pas') }) - it.only('ntv_url should contain query params', function () { + it('ntv_url should contain query params', function () { const request = spec.buildRequests(bidRequests, { bidderRequestId: 123456, refererInfo: { From b6ba5b5f8634a85f7cd28cc460d0a6029c8add03 Mon Sep 17 00:00:00 2001 From: Joshua Fledderjohn Date: Tue, 13 Dec 2022 08:02:00 -0800 Subject: [PATCH 31/53] Added ntv_url QS param value validation --- modules/nativoBidAdapter.js | 56 +++++++++++---- test/spec/modules/nativoBidAdapter_spec.js | 84 ++++++++++++++++++++++ 2 files changed, 128 insertions(+), 12 deletions(-) diff --git a/modules/nativoBidAdapter.js b/modules/nativoBidAdapter.js index 7eb37ea8b82..a92168492d0 100644 --- a/modules/nativoBidAdapter.js +++ b/modules/nativoBidAdapter.js @@ -140,13 +140,9 @@ export const spec = { const floorPriceData = {} let placementId, pageUrl validBidRequests.forEach((bidRequest) => { - pageUrl = deepAccess( - bidRequest, - 'params.url', - ) - if (pageUrl == undefined || pageUrl === '') { - pageUrl = bidderRequest.refererInfo.location - } + pageUrl = + getPageUrlFromBidRequest(bidRequest) || + bidderRequest.refererInfo.location placementId = deepAccess(bidRequest, 'params.placementId') @@ -380,10 +376,12 @@ export const spec = { return syncs } - body = - typeof response.body === 'string' - ? JSON.parse(response.body) - : response.body + try { + body = + typeof response.body === 'string' + ? JSON.parse(response.body) + : response.body + } catch (err) { return } // Make sure we have valid content if (!body || !body.seatbid || body.seatbid.length === 0) return @@ -465,7 +463,7 @@ export function parseFloorPriceData(bidRequest) { // Setup price floor data per media type let mediaTypeData = bidMediaTypes[mediaType] let mediaTypeFloorPriceData = {} - let mediaTypeSizes = mediaTypeData.sizes || mediaTypeData.playerSize || []; + let mediaTypeSizes = mediaTypeData.sizes || mediaTypeData.playerSize || [] // Step through each size of the media type so we can get floor data for each size per media type mediaTypeSizes.forEach((size) => { // Get floor price data per the getFloor method and respective media type / size combination @@ -634,3 +632,37 @@ function appendFilterData(filter, filterData) { filterData.forEach((ad) => filter.add(ad)) } } + +export function getPageUrlFromBidRequest(bidRequest) { + let paramPageUrl = deepAccess(bidRequest, 'params.url') + + if (paramPageUrl == undefined) return + + if (hasProtocol(paramPageUrl)) return paramPageUrl + + paramPageUrl = addProtocol(paramPageUrl) + + try { + const url = new URL(paramPageUrl) + return url.href + } catch (err) {} +} + +export function hasProtocol(url) { + const protocolRegexp = /^http[s]?\:/ + return protocolRegexp.test(url) +} + +export function addProtocol(url) { + if (hasProtocol(url)) { + return url + } + + let protocolPrefix = 'https:' + + if (url.indexOf('//') !== 0) { + protocolPrefix += '//' + } + + return `${protocolPrefix}${url}` +} diff --git a/test/spec/modules/nativoBidAdapter_spec.js b/test/spec/modules/nativoBidAdapter_spec.js index 4cd4c92281b..4d70e6f7071 100644 --- a/test/spec/modules/nativoBidAdapter_spec.js +++ b/test/spec/modules/nativoBidAdapter_spec.js @@ -5,6 +5,9 @@ import { getMediaWildcardPrices, sizeToString, parseFloorPriceData, + getPageUrlFromBidRequest, + hasProtocol, + addProtocol, } from '../../../modules/nativoBidAdapter' describe('bidDataMap', function () { @@ -647,3 +650,84 @@ describe('parseFloorPriceData', () => { }) }) }) + +describe('hasProtocol', () => { + it('https://www.testpage.com', () => { + expect(hasProtocol('https://www.testpage.com')).to.be.true + }) + it('http://www.testpage.com', () => { + expect(hasProtocol('http://www.testpage.com')).to.be.true + }) + it('//www.testpage.com', () => { + expect(hasProtocol('//www.testpage.com')).to.be.false + }) + it('www.testpage.com', () => { + expect(hasProtocol('www.testpage.com')).to.be.false + }) + it('httpsgsjhgflih', () => { + expect(hasProtocol('httpsgsjhgflih')).to.be.false + }) +}) + +describe('addProtocol', () => { + it('www.testpage.com', () => { + expect(addProtocol('www.testpage.com')).to.be.equal('https://www.testpage.com') + }) + it('//www.testpage.com', () => { + expect(addProtocol('//www.testpage.com')).to.be.equal('https://www.testpage.com') + }) + it('http://www.testpage.com', () => { + expect(addProtocol('http://www.testpage.com')).to.be.equal('http://www.testpage.com') + }) + it('https://www.testpage.com', () => { + expect(addProtocol('https://www.testpage.com')).to.be.equal('https://www.testpage.com') + }) +}) + +describe('getPageUrlFromBidRequest', () => { + const bidRequest = {} + + beforeEach(() => { + bidRequest.params = {} + }) + + it('Returns undefined for no url param', () => { + const url = getPageUrlFromBidRequest(bidRequest) + expect(url).to.be.undefined + }) + + it('@testUrl', () => { + const url = getPageUrlFromBidRequest(bidRequest) + expect(url).to.be.undefined + }) + + it('https://www.testpage.com', () => { + bidRequest.params.url = 'https://www.testpage.com' + const url = getPageUrlFromBidRequest(bidRequest) + expect(url).not.to.be.undefined + }) + + it('https://www.testpage.com/test/path', () => { + bidRequest.params.url = 'https://www.testpage.com/test/path' + const url = getPageUrlFromBidRequest(bidRequest) + expect(url).not.to.be.undefined + }) + + it('www.testpage.com', () => { + bidRequest.params.url = 'www.testpage.com' + const url = getPageUrlFromBidRequest(bidRequest) + expect(url).not.to.be.undefined + }) + + it('http://www.testpage.com', () => { + bidRequest.params.url = 'http://www.testpage.com' + const url = getPageUrlFromBidRequest(bidRequest) + expect(url).not.to.be.undefined + }) + + it('//www.testpage.com', () => { + bidRequest.params.url = '//www.testpage.com' + const url = getPageUrlFromBidRequest(bidRequest) + expect(url).not.to.be.undefined + }) +}) From e04cf3dffd25f70f700ff6194c7d02638bd53795 Mon Sep 17 00:00:00 2001 From: Joshua Fledderjohn Date: Thu, 23 Feb 2023 08:26:29 -0800 Subject: [PATCH 32/53] Added userId support --- modules/nativoBidAdapter.js | 86 ++++++++++++++++++++++++++++++++++++- 1 file changed, 84 insertions(+), 2 deletions(-) diff --git a/modules/nativoBidAdapter.js b/modules/nativoBidAdapter.js index a92168492d0..2fe7f42dea0 100644 --- a/modules/nativoBidAdapter.js +++ b/modules/nativoBidAdapter.js @@ -133,6 +133,9 @@ export const spec = { * @return ServerRequest Info describing the request to the server. */ buildRequests: function (validBidRequests, bidderRequest) { + const requestData = new RequestData() + requestData.addBidRequestDataSource(new UserEIDs()) + // Parse values from bid requests const placementIds = new Set() const bidDataMap = BidDataMap() @@ -166,6 +169,8 @@ export const spec = { if (bidRequestFloorPriceData) { floorPriceData[bidRequest.adUnitCode] = bidRequestFloorPriceData } + + requestData.processBidRequestData(bidRequest, bidderRequest) }) bidRequestMap[bidderRequest.bidderRequestId] = bidDataMap @@ -257,7 +262,7 @@ export const spec = { let serverRequest = { method: 'GET', - url: BIDDER_ENDPOINT + arrayToQS(params), + url: BIDDER_ENDPOINT + '?' + requestData.getRequestDataQueryString() + arrayToQS(params), } return serverRequest @@ -451,6 +456,83 @@ export const spec = { registerBidder(spec) // Utils +export class RequestData { + constructor() { + this.bidRequestDataSources = [] + } + + addBidRequestDataSource(bidRequestDataSource) { + this.bidRequestDataSources.push(bidRequestDataSource) + } + + processBidRequestData(bidRequest, bidderRequest) { + for (bidRequestDataSource of this.bidRequestDataSources) { + bidRequestDataSource.processBidRequestData(bidRequest, bidderRequest) + } + } + + getRequestDataQueryString() { + if(this.bidRequestDataSources.length == 0) return + + const queryString = this.bidRequestDataSources[0].getRequestQueryString() + for (let i=1; i { return appendQSParamString(value, obj.key, obj.value) }, '') From e9bb4ab2a2a7e28add11b1f0b932ee7982c94ae9 Mon Sep 17 00:00:00 2001 From: Joshua Fledderjohn Date: Thu, 23 Feb 2023 11:41:37 -0800 Subject: [PATCH 33/53] Added unit tests, refactored for bugs --- modules/nativoBidAdapter.js | 113 ++++++++++++++++----- test/spec/modules/nativoBidAdapter_spec.js | 76 ++++++++++++++ 2 files changed, 162 insertions(+), 27 deletions(-) diff --git a/modules/nativoBidAdapter.js b/modules/nativoBidAdapter.js index 2fe7f42dea0..227ce22350f 100644 --- a/modules/nativoBidAdapter.js +++ b/modules/nativoBidAdapter.js @@ -260,9 +260,12 @@ export const spec = { params.unshift({ key: 'us_privacy', value: bidderRequest.uspConsent }) } + const qsParamStrings = [requestData.getRequestDataQueryString(), arrayToQS(params)] + const requestUrl = buildRequestUrl(BIDDER_ENDPOINT, qsParamStrings) + let serverRequest = { method: 'GET', - url: BIDDER_ENDPOINT + '?' + requestData.getRequestDataQueryString() + arrayToQS(params), + url: requestUrl } return serverRequest @@ -414,7 +417,7 @@ export const spec = { * Adapter can fire a ajax or pixel call to register a timeout at thier end. * @param {Object} timeoutData - Timeout specific data */ - onTimeout: function (timeoutData) {}, + onTimeout: function (timeoutData) { }, /** * Will be called when a bid from the adapter won the auction. @@ -434,7 +437,7 @@ export const spec = { * Will be called when the adserver targeting has been set for a bid from the adapter. * @param {Object} bidder - The bid of which the targeting has been set */ - onSetTargeting: function (bid) {}, + onSetTargeting: function (bid) { }, /** * Maps Prebid's bidId to Nativo's placementId values per unique bidderRequestId @@ -462,54 +465,60 @@ export class RequestData { } addBidRequestDataSource(bidRequestDataSource) { + if (!(bidRequestDataSource instanceof BidRequestDataSource)) return + this.bidRequestDataSources.push(bidRequestDataSource) } processBidRequestData(bidRequest, bidderRequest) { - for (bidRequestDataSource of this.bidRequestDataSources) { + for (let bidRequestDataSource of this.bidRequestDataSources) { bidRequestDataSource.processBidRequestData(bidRequest, bidderRequest) } } getRequestDataQueryString() { - if(this.bidRequestDataSources.length == 0) return + if (this.bidRequestDataSources.length == 0) return - const queryString = this.bidRequestDataSources[0].getRequestQueryString() - for (let i=1; i dataSource.getRequestQueryString()).filter(queryString => queryString !== '') + return queryParams.join('&') } } export class BidRequestDataSource { constructor() { - + this.type = 'BidRequestDataSource' } - - processBidRequestData(bidRequest, bidderRequest) {} - getRequestQueryString() {} + processBidRequestData(bidRequest, bidderRequest) { } + getRequestQueryString() { return '' } } export class UserEIDs extends BidRequestDataSource { constructor() { super() + this.type = 'UserEIDs' this.qsParam = new QueryStringParam('ntv_pb_eid') this.values = new Set() } processBidRequestData(bidRequest, bidderRequest) { - const eids = bidRequest.userIdAsEids - for(let eid of eids) { - if(this.values.has(eid)) - console.log("DUPLICATE") - + const eids = bidRequest.userIdAsEids + + if (eids == undefined) return + + for (let eid of eids) { + if (this.values.has(eid)) { + fireNativoError(`Duplicate eid value: ${JSON.stringify(eid)} in bidRequest for adUnitCode: ${bidRequest.adUnitCode} on url: ${bidderRequest.refererInfo.location}`) + } + this.values.add(eid) } } getRequestQueryString() { const valueArray = Array.from(this.values) + + if (valueArray.length === 0) return '' + const encodedValueArray = encodeToBase64(valueArray) this.qsParam.value = encodedValueArray return this.qsParam.toString() @@ -530,7 +539,7 @@ QueryStringParam.prototype.toString = function () { export function encodeToBase64(value) { try { return btoa(JSON.stringify(value)) - } catch (err) {} + } catch (err) { } } export function parseFloorPriceData(bidRequest) { @@ -671,12 +680,9 @@ function appendQSParamString(str, key, value) { * @returns */ function arrayToQS(arr) { - return ( - '&' + - arr.reduce((value, obj) => { - return appendQSParamString(value, obj.key, obj.value) - }, '') - ) + return arr.reduce((value, obj) => { + return appendQSParamString(value, obj.key, obj.value) + }, '') } /** @@ -697,6 +703,59 @@ function getLargestSize(sizes, method = area) { }) } +/** + * Fire a Nativo Error AJAX request to log errors + * @param {String} errorMessage - The error message to send + */ +function fireNativoError(errorMessage) { + if (!(typeof errorMessage === 'string') || errorMessage.trim() === '') return + + // Sampling @ 1% + if (!shouldBeSampled()) return + + const httpRequest = new XMLHttpRequest() + + if (!httpRequest) return + + httpRequest.open('POST', 'https://jadserve.postrelease.com/clientErrorLogging') + httpRequest.setRequestHeader('Content-type', 'application/json') + httpRequest.send(JSON.stringify({ + errorType: 'Prebid Adapter Error', + errorMessage, + severityLevel: 'Error', + errorStacktrace: [] + })) +} + +/** + * Randomly decided if sampling should occur + * @param {Integer} percentage - The percantage it should be sampled + * @returns {Boolean} - Should be sampled or not + */ +export function shouldBeSampled(percentage = 1) { + const rand = Math.floor(Math.random() * 100) + if (rand < percentage) return true + return false +} + +/** + * Build the final request url + */ +function buildRequestUrl(baseUrl, qsParamStringArray) { + if (qsParamStringArray.length === 0 || !Array.isArray(qsParamStringArray)) return baseUrl + + const nonEmptyQSParamStrings = qsParamStringArray.filter(qsParamString => qsParamString.trim() !== '') + + if (nonEmptyQSParamStrings.length === 0) return baseUrl + + let requestUrl = `${baseUrl}?${nonEmptyQSParamStrings[0]}` + for (let i = 1; i < nonEmptyQSParamStrings.length; i++) { + requestUrl += `&${nonEmptyQSParamStrings[i]}` + } + + return requestUrl +} + /** * Calculate the area * @param {Array} size - [width, height] @@ -727,7 +786,7 @@ export function getPageUrlFromBidRequest(bidRequest) { try { const url = new URL(paramPageUrl) return url.href - } catch (err) {} + } catch (err) { } } export function hasProtocol(url) { diff --git a/test/spec/modules/nativoBidAdapter_spec.js b/test/spec/modules/nativoBidAdapter_spec.js index 4d70e6f7071..e3f4c01599b 100644 --- a/test/spec/modules/nativoBidAdapter_spec.js +++ b/test/spec/modules/nativoBidAdapter_spec.js @@ -8,6 +8,9 @@ import { getPageUrlFromBidRequest, hasProtocol, addProtocol, + BidRequestDataSource, + RequestData, + UserEIDs, } from '../../../modules/nativoBidAdapter' describe('bidDataMap', function () { @@ -731,3 +734,76 @@ describe('getPageUrlFromBidRequest', () => { expect(url).not.to.be.undefined }) }) + +describe('RequestData', () => { + describe('addBidRequestDataSource', () => { + it('Adds a BidRequestDataSource', () => { + const requestData = new RequestData() + const testBidRequestDataSource = new BidRequestDataSource() + + requestData.addBidRequestDataSource(testBidRequestDataSource) + + expect(requestData.bidRequestDataSources.length == 1) + }) + + it("Doeasn't add a non BidRequestDataSource", () => { + const requestData = new RequestData() + + requestData.addBidRequestDataSource({}) + requestData.addBidRequestDataSource('test') + requestData.addBidRequestDataSource(1) + requestData.addBidRequestDataSource(true) + + expect(requestData.bidRequestDataSources.length == 0) + }) + }) + + describe('getRequestDataString', () => { + it("Doesn't append empty query strings", () => { + const requestData = new RequestData() + const testBidRequestDataSource = new BidRequestDataSource() + + requestData.addBidRequestDataSource(testBidRequestDataSource) + + let qs = requestData.getRequestDataQueryString() + expect(qs).to.be.empty + + testBidRequestDataSource.getRequestQueryString = () => { + return 'ntv_test=true' + } + qs = requestData.getRequestDataQueryString() + expect(qs).to.be.equal('ntv_test=true') + }) + }) +}) + +describe('UserEIDs', () => { + const userEids = new UserEIDs() + const eids = [{ 'testId': 1111 }] + + describe('processBidRequestData', () => { + it("Processes bid request without eids", () => { + userEids.processBidRequestData({}) + + expect(userEids.values).to.be.empty + }) + + it("Processed bid request with eids", () => { + userEids.processBidRequestData({ userIdAsEids: eids }) + + expect(userEids.values).to.not.be.empty + }) + }) + + describe('getRequestQueryString', () => { + it("Correctly prints out QS param string", () => { + const qs = userEids.getRequestQueryString() + const value = qs.slice(11) + + expect(qs).to.include('ntv_pb_eid=') + try { + expect(JSON.parse(value)).to.be.equal(eids) + } catch (err) { } + }) + }) +}) From ce1b931e3fde3cfcd5e462dccc702ad8e394299d Mon Sep 17 00:00:00 2001 From: Joshua Fledderjohn Date: Thu, 23 Feb 2023 11:43:15 -0800 Subject: [PATCH 34/53] Wrapped ajax in try/catch --- modules/nativoBidAdapter.js | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/modules/nativoBidAdapter.js b/modules/nativoBidAdapter.js index 227ce22350f..36ac62dbba0 100644 --- a/modules/nativoBidAdapter.js +++ b/modules/nativoBidAdapter.js @@ -717,14 +717,16 @@ function fireNativoError(errorMessage) { if (!httpRequest) return - httpRequest.open('POST', 'https://jadserve.postrelease.com/clientErrorLogging') - httpRequest.setRequestHeader('Content-type', 'application/json') - httpRequest.send(JSON.stringify({ - errorType: 'Prebid Adapter Error', - errorMessage, - severityLevel: 'Error', - errorStacktrace: [] - })) + try { + httpRequest.open('POST', 'https://jadserve.postrelease.com/clientErrorLogging') + httpRequest.setRequestHeader('Content-type', 'application/json') + httpRequest.send(JSON.stringify({ + errorType: 'Prebid Adapter Error', + errorMessage, + severityLevel: 'Error', + errorStacktrace: [] + })) + } catch(err) {} } /** From a3af4e2663a09deed3af8f030ada6eeae5cdfdfc Mon Sep 17 00:00:00 2001 From: Joshua Fledderjohn Date: Thu, 23 Feb 2023 11:50:51 -0800 Subject: [PATCH 35/53] Added more unit testing --- modules/nativoBidAdapter.js | 4 +-- test/spec/modules/nativoBidAdapter_spec.js | 30 +++++++++++++++++++--- 2 files changed, 29 insertions(+), 5 deletions(-) diff --git a/modules/nativoBidAdapter.js b/modules/nativoBidAdapter.js index 36ac62dbba0..7518c6175c6 100644 --- a/modules/nativoBidAdapter.js +++ b/modules/nativoBidAdapter.js @@ -726,7 +726,7 @@ function fireNativoError(errorMessage) { severityLevel: 'Error', errorStacktrace: [] })) - } catch(err) {} + } catch (err) {} } /** @@ -743,7 +743,7 @@ export function shouldBeSampled(percentage = 1) { /** * Build the final request url */ -function buildRequestUrl(baseUrl, qsParamStringArray) { +export function buildRequestUrl(baseUrl, qsParamStringArray = []) { if (qsParamStringArray.length === 0 || !Array.isArray(qsParamStringArray)) return baseUrl const nonEmptyQSParamStrings = qsParamStringArray.filter(qsParamString => qsParamString.trim() !== '') diff --git a/test/spec/modules/nativoBidAdapter_spec.js b/test/spec/modules/nativoBidAdapter_spec.js index e3f4c01599b..6b7621f64d3 100644 --- a/test/spec/modules/nativoBidAdapter_spec.js +++ b/test/spec/modules/nativoBidAdapter_spec.js @@ -11,6 +11,7 @@ import { BidRequestDataSource, RequestData, UserEIDs, + buildRequestUrl, } from '../../../modules/nativoBidAdapter' describe('bidDataMap', function () { @@ -782,13 +783,13 @@ describe('UserEIDs', () => { const eids = [{ 'testId': 1111 }] describe('processBidRequestData', () => { - it("Processes bid request without eids", () => { + it('Processes bid request without eids', () => { userEids.processBidRequestData({}) expect(userEids.values).to.be.empty }) - it("Processed bid request with eids", () => { + it('Processed bid request with eids', () => { userEids.processBidRequestData({ userIdAsEids: eids }) expect(userEids.values).to.not.be.empty @@ -796,7 +797,7 @@ describe('UserEIDs', () => { }) describe('getRequestQueryString', () => { - it("Correctly prints out QS param string", () => { + it('Correctly prints out QS param string', () => { const qs = userEids.getRequestQueryString() const value = qs.slice(11) @@ -807,3 +808,26 @@ describe('UserEIDs', () => { }) }) }) + +describe.only('buildRequestUrl', () => { + const baseUrl = 'https://www.testExchange.com' + it('Returns baseUrl if no QS strings passed', () => { + const url = buildRequestUrl(baseUrl) + expect(url).to.be.equal(baseUrl) + }) + + it('Returns baseUrl if empty QS strings passed', () => { + const url = buildRequestUrl(baseUrl, ['', '', '']) + expect(url).to.be.equal(baseUrl) + }) + + it('Returns baseUrl + QS params if QS strings passed', () => { + const url = buildRequestUrl(baseUrl, ['ntv_ptd=123456&ntv_test=true', 'ntv_foo=bar']) + expect(url).to.be.equal(`${baseUrl}?ntv_ptd=123456&ntv_test=true&ntv_foo=bar`) + }) + + it('Returns baseUrl + QS params if mixed QS strings passed', () => { + const url = buildRequestUrl(baseUrl, ['ntv_ptd=123456&ntv_test=true', '', '', 'ntv_foo=bar']) + expect(url).to.be.equal(`${baseUrl}?ntv_ptd=123456&ntv_test=true&ntv_foo=bar`) + }) +}) From 883ebd3e169391d25c26f0d33283e985462621fa Mon Sep 17 00:00:00 2001 From: Joshua Fledderjohn Date: Fri, 3 Mar 2023 08:47:38 -0800 Subject: [PATCH 36/53] Updated eid check for duplicate values. Removed error logging as we no longer need it. --- modules/nativoBidAdapter.js | 58 ++++--------------------------------- 1 file changed, 5 insertions(+), 53 deletions(-) diff --git a/modules/nativoBidAdapter.js b/modules/nativoBidAdapter.js index 7518c6175c6..ebf41338cbb 100644 --- a/modules/nativoBidAdapter.js +++ b/modules/nativoBidAdapter.js @@ -497,29 +497,18 @@ export class UserEIDs extends BidRequestDataSource { super() this.type = 'UserEIDs' this.qsParam = new QueryStringParam('ntv_pb_eid') - this.values = new Set() + this.eids = [] } processBidRequestData(bidRequest, bidderRequest) { - const eids = bidRequest.userIdAsEids - - if (eids == undefined) return - - for (let eid of eids) { - if (this.values.has(eid)) { - fireNativoError(`Duplicate eid value: ${JSON.stringify(eid)} in bidRequest for adUnitCode: ${bidRequest.adUnitCode} on url: ${bidderRequest.refererInfo.location}`) - } - - this.values.add(eid) - } + if (bidRequest.userIdAsEids === undefined || this.eids.length > 0) return + this.eids = bidRequest.userIdAsEids } getRequestQueryString() { - const valueArray = Array.from(this.values) + if (this.eids.length === 0) return '' - if (valueArray.length === 0) return '' - - const encodedValueArray = encodeToBase64(valueArray) + const encodedValueArray = encodeToBase64(this.eids) this.qsParam.value = encodedValueArray return this.qsParam.toString() } @@ -703,43 +692,6 @@ function getLargestSize(sizes, method = area) { }) } -/** - * Fire a Nativo Error AJAX request to log errors - * @param {String} errorMessage - The error message to send - */ -function fireNativoError(errorMessage) { - if (!(typeof errorMessage === 'string') || errorMessage.trim() === '') return - - // Sampling @ 1% - if (!shouldBeSampled()) return - - const httpRequest = new XMLHttpRequest() - - if (!httpRequest) return - - try { - httpRequest.open('POST', 'https://jadserve.postrelease.com/clientErrorLogging') - httpRequest.setRequestHeader('Content-type', 'application/json') - httpRequest.send(JSON.stringify({ - errorType: 'Prebid Adapter Error', - errorMessage, - severityLevel: 'Error', - errorStacktrace: [] - })) - } catch (err) {} -} - -/** - * Randomly decided if sampling should occur - * @param {Integer} percentage - The percantage it should be sampled - * @returns {Boolean} - Should be sampled or not - */ -export function shouldBeSampled(percentage = 1) { - const rand = Math.floor(Math.random() * 100) - if (rand < percentage) return true - return false -} - /** * Build the final request url */ From be48d28f5752cda02d8ceb320f13e5de0d25def7 Mon Sep 17 00:00:00 2001 From: Joshua Fledderjohn Date: Mon, 20 Mar 2023 09:38:38 -0700 Subject: [PATCH 37/53] Removed spec test .only. Fixed unit tests that were breaking. --- test/spec/modules/nativoBidAdapter_spec.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/test/spec/modules/nativoBidAdapter_spec.js b/test/spec/modules/nativoBidAdapter_spec.js index 6b7621f64d3..f93da0c2a6b 100644 --- a/test/spec/modules/nativoBidAdapter_spec.js +++ b/test/spec/modules/nativoBidAdapter_spec.js @@ -786,13 +786,13 @@ describe('UserEIDs', () => { it('Processes bid request without eids', () => { userEids.processBidRequestData({}) - expect(userEids.values).to.be.empty + expect(userEids.eids).to.be.empty }) it('Processed bid request with eids', () => { userEids.processBidRequestData({ userIdAsEids: eids }) - expect(userEids.values).to.not.be.empty + expect(userEids.eids).to.not.be.empty }) }) @@ -809,7 +809,7 @@ describe('UserEIDs', () => { }) }) -describe.only('buildRequestUrl', () => { +describe('buildRequestUrl', () => { const baseUrl = 'https://www.testExchange.com' it('Returns baseUrl if no QS strings passed', () => { const url = buildRequestUrl(baseUrl) From 79880a80af8335cbed640b9c12d39e11807ea880 Mon Sep 17 00:00:00 2001 From: Joshua Fledderjohn Date: Fri, 14 Apr 2023 10:16:53 -0700 Subject: [PATCH 38/53] Added Prebid version to nativo exchange request --- modules/nativoBidAdapter.js | 7 +++++++ test/spec/modules/nativoBidAdapter_spec.js | 1 + 2 files changed, 8 insertions(+) diff --git a/modules/nativoBidAdapter.js b/modules/nativoBidAdapter.js index ebf41338cbb..a12007b3cba 100644 --- a/modules/nativoBidAdapter.js +++ b/modules/nativoBidAdapter.js @@ -1,6 +1,7 @@ import { deepAccess, isEmpty } from '../src/utils.js' import { registerBidder } from '../src/adapters/bidderFactory.js' import { BANNER } from '../src/mediaTypes.js' +import { getGlobal } from '../src/prebidGlobal.js' // import { config } from 'src/config' const BIDDER_CODE = 'nativo' @@ -14,6 +15,8 @@ const SUPPORTED_AD_TYPES = [BANNER] const FLOOR_PRICE_CURRENCY = 'USD' const PRICE_FLOOR_WILDCARD = '*' +const localPbjsRef = getGlobal() + /** * Keep track of bid data by keys * @returns {Object} - Map of bid data that can be referenced by multiple keys @@ -179,6 +182,10 @@ export const spec = { // Build basic required QS Params let params = [ + // Prebid version + { + key: 'ntv_pbv', value: localPbjsRef.version + }, // Prebid request id { key: 'ntv_pb_rid', value: bidderRequest.bidderRequestId }, // Ad unit data diff --git a/test/spec/modules/nativoBidAdapter_spec.js b/test/spec/modules/nativoBidAdapter_spec.js index f93da0c2a6b..51e78d1f6d6 100644 --- a/test/spec/modules/nativoBidAdapter_spec.js +++ b/test/spec/modules/nativoBidAdapter_spec.js @@ -124,6 +124,7 @@ describe('nativoBidAdapterTests', function () { expect(request.url).to.be.a('string') expect(request.url).to.include('?') + expect(request.url).to.include('ntv_pbv') expect(request.url).to.include('ntv_ptd') expect(request.url).to.include('ntv_pb_rid') expect(request.url).to.include('ntv_ppc') From dbc86184cfff7eca804b298f593423133687c0e8 Mon Sep 17 00:00:00 2001 From: Joshua Fledderjohn Date: Fri, 14 Apr 2023 10:25:14 -0700 Subject: [PATCH 39/53] Removed unused bidder methods --- modules/nativoBidAdapter.js | 13 ------------- 1 file changed, 13 deletions(-) diff --git a/modules/nativoBidAdapter.js b/modules/nativoBidAdapter.js index a12007b3cba..c62a74e6d6c 100644 --- a/modules/nativoBidAdapter.js +++ b/modules/nativoBidAdapter.js @@ -419,13 +419,6 @@ export const spec = { return syncs }, - /** - * Will be called when an adpater timed out for an auction. - * Adapter can fire a ajax or pixel call to register a timeout at thier end. - * @param {Object} timeoutData - Timeout specific data - */ - onTimeout: function (timeoutData) { }, - /** * Will be called when a bid from the adapter won the auction. * @param {Object} bid - The bid that won the auction @@ -440,12 +433,6 @@ export const spec = { appendFilterData(campaignsToFilter, ext.campaignsToFilter) }, - /** - * Will be called when the adserver targeting has been set for a bid from the adapter. - * @param {Object} bidder - The bid of which the targeting has been set - */ - onSetTargeting: function (bid) { }, - /** * Maps Prebid's bidId to Nativo's placementId values per unique bidderRequestId * @param {String} bidderRequestId - The unique ID value associated with the bidderRequest From 0961ed411c2deab0a28c6a6ec41ec78c96f8c115 Mon Sep 17 00:00:00 2001 From: Joshua Fledderjohn Date: Mon, 7 Aug 2023 08:36:34 -0700 Subject: [PATCH 40/53] Added OpenRTB payload response. Changes requerst type to POST. --- modules/nativoBidAdapter.js | 25 ++++++++++++++++++++++--- 1 file changed, 22 insertions(+), 3 deletions(-) diff --git a/modules/nativoBidAdapter.js b/modules/nativoBidAdapter.js index c62a74e6d6c..f088433a0dd 100644 --- a/modules/nativoBidAdapter.js +++ b/modules/nativoBidAdapter.js @@ -2,7 +2,20 @@ import { deepAccess, isEmpty } from '../src/utils.js' import { registerBidder } from '../src/adapters/bidderFactory.js' import { BANNER } from '../src/mediaTypes.js' import { getGlobal } from '../src/prebidGlobal.js' -// import { config } from 'src/config' +import { ortbConverter } from '../libraries/ortbConverter/converter.js' + +const converter = ortbConverter({ + context: { + // `netRevenue` and `ttl` are required properties of bid responses - provide a default for them + netRevenue: true, // or false if your adapter should set bidResponse.netRevenue = false + ttl: 30 // default bidResponse.ttl (when not specified in ORTB response.seatbid[].bid[].exp) + }, + imp(buildImp, bidRequest, context) { + const imp = buildImp(bidRequest, context); + imp.tagid = bidRequest.adUnitCode + return imp; +} +}); const BIDDER_CODE = 'nativo' const BIDDER_ENDPOINT = 'https://exchange.postrelease.com/prebid' @@ -136,6 +149,10 @@ export const spec = { * @return ServerRequest Info describing the request to the server. */ buildRequests: function (validBidRequests, bidderRequest) { + // Get OpenRTB Data + const openRTBData = converter.toORTB({validBidRequests, bidderRequest}) + const openRTBDataString = JSON.stringify(openRTBData) + const requestData = new RequestData() requestData.addBidRequestDataSource(new UserEIDs()) @@ -271,8 +288,9 @@ export const spec = { const requestUrl = buildRequestUrl(BIDDER_ENDPOINT, qsParamStrings) let serverRequest = { - method: 'GET', - url: requestUrl + method: 'POST', + url: requestUrl, + data: openRTBDataString, } return serverRequest @@ -495,6 +513,7 @@ export class UserEIDs extends BidRequestDataSource { } processBidRequestData(bidRequest, bidderRequest) { + console.log(bidRequest) if (bidRequest.userIdAsEids === undefined || this.eids.length > 0) return this.eids = bidRequest.userIdAsEids } From d090447864bcdaaccfdcac41c8a655ba1972f8d5 Mon Sep 17 00:00:00 2001 From: Joshua Fledderjohn Date: Mon, 7 Aug 2023 08:38:00 -0700 Subject: [PATCH 41/53] Removed debug log --- modules/nativoBidAdapter.js | 1 - 1 file changed, 1 deletion(-) diff --git a/modules/nativoBidAdapter.js b/modules/nativoBidAdapter.js index f088433a0dd..fd388515683 100644 --- a/modules/nativoBidAdapter.js +++ b/modules/nativoBidAdapter.js @@ -513,7 +513,6 @@ export class UserEIDs extends BidRequestDataSource { } processBidRequestData(bidRequest, bidderRequest) { - console.log(bidRequest) if (bidRequest.userIdAsEids === undefined || this.eids.length > 0) return this.eids = bidRequest.userIdAsEids } From 722da51f5409625e23300474b9096110eecd80a7 Mon Sep 17 00:00:00 2001 From: Joshua Fledderjohn Date: Mon, 7 Aug 2023 09:02:49 -0700 Subject: [PATCH 42/53] Added/fixed tests --- modules/nativoBidAdapter.js | 12 ++++++------ test/spec/modules/nativoBidAdapter_spec.js | 7 ++++++- 2 files changed, 12 insertions(+), 7 deletions(-) diff --git a/modules/nativoBidAdapter.js b/modules/nativoBidAdapter.js index fd388515683..69a270247cd 100644 --- a/modules/nativoBidAdapter.js +++ b/modules/nativoBidAdapter.js @@ -4,17 +4,17 @@ import { BANNER } from '../src/mediaTypes.js' import { getGlobal } from '../src/prebidGlobal.js' import { ortbConverter } from '../libraries/ortbConverter/converter.js' -const converter = ortbConverter({ +const converter = ortbConverter({ context: { - // `netRevenue` and `ttl` are required properties of bid responses - provide a default for them - netRevenue: true, // or false if your adapter should set bidResponse.netRevenue = false - ttl: 30 // default bidResponse.ttl (when not specified in ORTB response.seatbid[].bid[].exp) + // `netRevenue` and `ttl` are required properties of bid responses - provide a default for them + netRevenue: true, // or false if your adapter should set bidResponse.netRevenue = false + ttl: 30 // default bidResponse.ttl (when not specified in ORTB response.seatbid[].bid[].exp) }, imp(buildImp, bidRequest, context) { const imp = buildImp(bidRequest, context); imp.tagid = bidRequest.adUnitCode return imp; -} + } }); const BIDDER_CODE = 'nativo' @@ -150,7 +150,7 @@ export const spec = { */ buildRequests: function (validBidRequests, bidderRequest) { // Get OpenRTB Data - const openRTBData = converter.toORTB({validBidRequests, bidderRequest}) + const openRTBData = converter.toORTB({bidRequests: validBidRequests, bidderRequest}) const openRTBDataString = JSON.stringify(openRTBData) const requestData = new RequestData() diff --git a/test/spec/modules/nativoBidAdapter_spec.js b/test/spec/modules/nativoBidAdapter_spec.js index 51e78d1f6d6..75fb357b196 100644 --- a/test/spec/modules/nativoBidAdapter_spec.js +++ b/test/spec/modules/nativoBidAdapter_spec.js @@ -112,7 +112,7 @@ describe('nativoBidAdapterTests', function () { bidRequests = [JSON.parse(bidRequestString)] }) - it('url should contain query string parameters', function () { + it('Request should be POST, with JSON string payload and QS params should be added to the url', function () { const request = spec.buildRequests(bidRequests, { bidderRequestId: 123456, refererInfo: { @@ -120,6 +120,11 @@ describe('nativoBidAdapterTests', function () { }, }) + expect(request.method).to.equal('POST') + + expect(request.data).to.exist + expect(request.data).to.be.a('string') + expect(request.url).to.exist expect(request.url).to.be.a('string') From 8d0d5afeca749a3e6536ce12483b3403cf9b914c Mon Sep 17 00:00:00 2001 From: doctafaustus Date: Thu, 1 Aug 2024 12:17:32 -0500 Subject: [PATCH 43/53] Handle video mediaType --- modules/nativoBidAdapter.js | 18 +++++-- test/spec/modules/nativoBidAdapter_spec.js | 59 ++++++++++++++++++++++ 2 files changed, 74 insertions(+), 3 deletions(-) diff --git a/modules/nativoBidAdapter.js b/modules/nativoBidAdapter.js index c9da876b292..32c2bafe48f 100644 --- a/modules/nativoBidAdapter.js +++ b/modules/nativoBidAdapter.js @@ -1,6 +1,6 @@ import { deepAccess, isEmpty } from '../src/utils.js' import { registerBidder } from '../src/adapters/bidderFactory.js' -import { BANNER } from '../src/mediaTypes.js' +import { BANNER, VIDEO, NATIVE } from '../src/mediaTypes.js' import { getGlobal } from '../src/prebidGlobal.js' import { ortbConverter } from '../libraries/ortbConverter/converter.js' @@ -13,6 +13,8 @@ const converter = ortbConverter({ imp(buildImp, bidRequest, context) { const imp = buildImp(bidRequest, context); imp.tagid = bidRequest.adUnitCode + if (imp.ext) imp.ext.placementId = bidRequest.params.placementId + return imp; } }); @@ -24,7 +26,7 @@ const GVLID = 263 const TIME_TO_LIVE = 360 -const SUPPORTED_AD_TYPES = [BANNER] +const SUPPORTED_AD_TYPES = [BANNER, VIDEO, NATIVE] const FLOOR_PRICE_CURRENCY = 'USD' const PRICE_FLOOR_WILDCARD = '*' @@ -291,6 +293,7 @@ export const spec = { method: 'POST', url: requestUrl, data: openRTBDataString, + bidderRequest: bidderRequest } return serverRequest @@ -320,9 +323,10 @@ export const spec = { // Step through and grab pertinent data let bidResponse, adUnit - seatbids.forEach((seatbid) => { + seatbids.forEach((seatbid, i) => { seatbid.bid.forEach((bid) => { adUnit = this.getAdUnitData(body.id, bid) + bidResponse = { requestId: adUnit.bidId, cpm: bid.price, @@ -337,9 +341,13 @@ export const spec = { meta: { advertiserDomains: bid.adomain, }, + mediaType: getMediaType(request.bidderRequest.bids[i]), } if (bid.ext) extData[bid.id] = bid.ext + if (bidResponse.mediaType === VIDEO) { + bidResponse.vastUrl = 'data:text/xml;charset=utf-8,' + encodeURIComponent(bid.adm) + } bidResponses.push(bidResponse) }) @@ -348,6 +356,10 @@ export const spec = { // Don't need the map anymore as it was unique for one request/response delete bidRequestMap[body.id] + function getMediaType(accessObj) { + return deepAccess(accessObj, 'mediaTypes.video') ? VIDEO : BANNER; + } + return bidResponses } catch (error) { // If there is an error, return [] diff --git a/test/spec/modules/nativoBidAdapter_spec.js b/test/spec/modules/nativoBidAdapter_spec.js index 75fb357b196..9893490e833 100644 --- a/test/spec/modules/nativoBidAdapter_spec.js +++ b/test/spec/modules/nativoBidAdapter_spec.js @@ -837,3 +837,62 @@ describe('buildRequestUrl', () => { expect(url).to.be.equal(`${baseUrl}?ntv_ptd=123456&ntv_test=true&ntv_foo=bar`) }) }) + +describe('Prebid Video', function () { + it('should handle video bid requests', function () { + const videoBidRequest = { + bidder: 'nativo', + params: { + video: { + mimes: ['video/mp4'], + protocols: [2, 3, 5, 6], + playbackmethod: [1, 2], + skip: 1, + skipafter: 5 + }, + } + }; + + const isValid = spec.isBidRequestValid(videoBidRequest); + expect(isValid).to.be.true; + }); +}); + +describe('Prebid Native', function () { + it('should handle native bid requests', function () { + const nativeBidRequest = { + bidder: 'nativo', + params: { + native: { + title: { + required: true, + len: 80 + }, + image: { + required: true, + sizes: [150, 50] + }, + sponsoredBy: { + required: true + }, + clickUrl: { + required: true + }, + privacyLink: { + required: false + }, + body: { + required: true + }, + icon: { + required: true, + sizes: [50, 50] + } + }, + } + }; + + const isValid = spec.isBidRequestValid(nativeBidRequest); + expect(isValid).to.be.true; + }); +}); From d05ecb4ac29b559dfadd452f097272629143350b Mon Sep 17 00:00:00 2001 From: doctafaustus Date: Thu, 1 Aug 2024 12:19:52 -0500 Subject: [PATCH 44/53] Add built renderer files --- libraries/creative-renderer-display/renderer.js | 2 +- libraries/creative-renderer-native/renderer.js | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/libraries/creative-renderer-display/renderer.js b/libraries/creative-renderer-display/renderer.js index 72f3658fe79..146afab46ae 100644 --- a/libraries/creative-renderer-display/renderer.js +++ b/libraries/creative-renderer-display/renderer.js @@ -1,2 +1,2 @@ // this file is autogenerated, see creative/README.md -export const RENDERER = "!function(){\"use strict\";window.render=function({ad:d,adUrl:i,width:n,height:e},{mkFrame:o},r){if(!d&&!i)throw{reason:\"noAd\",message:\"Missing ad markup or URL\"};{const t=r.document,s={width:n,height:e};i&&!d?s.src=i:s.srcdoc=d,t.body.appendChild(o(t,s))}}}();" \ No newline at end of file +export const RENDERER = "(()=>{\"use strict\";window.render=function({ad:d,adUrl:e,width:i,height:r},{mkFrame:n},o){if(!d&&!e)throw{reason:\"noAd\",message:\"Missing ad markup or URL\"};{const s=o.document,t={width:i,height:r};e&&!d?t.src=e:t.srcdoc=d,s.body.appendChild(n(s,t))}}})();" \ No newline at end of file diff --git a/libraries/creative-renderer-native/renderer.js b/libraries/creative-renderer-native/renderer.js index 57d86fc8ce3..d7d85cdd7ba 100644 --- a/libraries/creative-renderer-native/renderer.js +++ b/libraries/creative-renderer-native/renderer.js @@ -1,2 +1,2 @@ // this file is autogenerated, see creative/README.md -export const RENDERER = "!function(){\"use strict\";const e=\"Prebid Native\",t={title:\"text\",data:\"value\",img:\"url\",video:\"vasttag\"};function n(e,t){return new Promise(((n,r)=>{const i=t.createElement(\"script\");i.onload=n,i.onerror=r,i.src=e,t.body.appendChild(i)}))}function r(e,t,r,i,o=n){const{rendererUrl:s,assets:a,ortb:d,adTemplate:c}=t,l=i.document;return s?o(s,l).then((()=>{if(\"function\"!=typeof i.renderAd)throw new Error(`Renderer from '${s}' does not define renderAd()`);const e=a||[];return e.ortb=d,i.renderAd(e)})):Promise.resolve(r(c??l.body.innerHTML))}window.render=function({adId:n,native:i},{sendMessage:o},s,a=r){const{head:d,body:c}=s.document,l=()=>o(e,{action:\"resizeNativeHeight\",height:c.offsetHeight,width:c.offsetWidth}),u=function(e,{assets:n=[],ortb:r,nativeKeys:i={}}){const o=Object.fromEntries(n.map((({key:e,value:t})=>[e,t])));let s=Object.fromEntries(Object.entries(i).flatMap((([t,n])=>{const r=o.hasOwnProperty(t)?o[t]:void 0;return[[`##${n}##`,r],[`${n}:${e}`,r]]})));return r&&Object.assign(s,{\"##hb_native_linkurl##\":r.link?.url,\"##hb_native_privacy##\":r.privacy},Object.fromEntries((r.assets||[]).flatMap((e=>{const n=Object.keys(t).find((t=>e[t]));return[n&&[`##hb_native_asset_id_${e.id}##`,e[n][t[n]]],e.link?.url&&[`##hb_native_asset_link_id_${e.id}##`,e.link.url]].filter((e=>e))})))),s=Object.entries(s).concat([[/##hb_native_asset_(link_)?id_\\d+##/g]]),function(e){return s.reduce(((e,[t,n])=>e.replaceAll(t,n||\"\")),e)}}(n,i);return d&&(d.innerHTML=u(d.innerHTML)),a(n,i,u,s).then((t=>{c.innerHTML=t,\"function\"==typeof s.postRenderAd&&s.postRenderAd({adId:n,...i}),s.document.querySelectorAll(\".pb-click\").forEach((t=>{const n=t.getAttribute(\"hb_native_asset_id\");t.addEventListener(\"click\",(()=>o(e,{action:\"click\",assetId:n})))})),o(e,{action:\"fireNativeImpressionTrackers\"}),\"complete\"===s.document.readyState?l():s.onload=l}))}}();" \ No newline at end of file +export const RENDERER = "(()=>{\"use strict\";const e=\"Prebid Native\",t={title:\"text\",data:\"value\",img:\"url\",video:\"vasttag\"};function n(e,t){return new Promise(((n,r)=>{const i=t.createElement(\"script\");i.onload=n,i.onerror=r,i.src=e,t.body.appendChild(i)}))}function r(e,t,r,i,o=n){const{rendererUrl:s,assets:a,ortb:d,adTemplate:c}=t,l=i.document;return s?o(s,l).then((()=>{if(\"function\"!=typeof i.renderAd)throw new Error(`Renderer from '${s}' does not define renderAd()`);const e=a||[];return e.ortb=d,i.renderAd(e)})):Promise.resolve(r(c??l.body.innerHTML))}window.render=function({adId:n,native:i},{sendMessage:o},s,a=r){const{head:d,body:c}=s.document,l=()=>o(e,{action:\"resizeNativeHeight\",height:c.offsetHeight,width:c.offsetWidth}),u=function(e,{assets:n=[],ortb:r,nativeKeys:i={}}){const o=Object.fromEntries(n.map((({key:e,value:t})=>[e,t])));let s=Object.fromEntries(Object.entries(i).flatMap((([t,n])=>{const r=o.hasOwnProperty(t)?o[t]:void 0;return[[`##${n}##`,r],[`${n}:${e}`,r]]})));return r&&Object.assign(s,{\"##hb_native_linkurl##\":r.link?.url,\"##hb_native_privacy##\":r.privacy},Object.fromEntries((r.assets||[]).flatMap((e=>{const n=Object.keys(t).find((t=>e[t]));return[n&&[`##hb_native_asset_id_${e.id}##`,e[n][t[n]]],e.link?.url&&[`##hb_native_asset_link_id_${e.id}##`,e.link.url]].filter((e=>e))})))),s=Object.entries(s).concat([[/##hb_native_asset_(link_)?id_\\d+##/g]]),function(e){return s.reduce(((e,[t,n])=>e.replaceAll(t,n||\"\")),e)}}(n,i);return d&&(d.innerHTML=u(d.innerHTML)),a(n,i,u,s).then((t=>{c.innerHTML=t,\"function\"==typeof s.postRenderAd&&s.postRenderAd({adId:n,...i}),s.document.querySelectorAll(\".pb-click\").forEach((t=>{const n=t.getAttribute(\"hb_native_asset_id\");t.addEventListener(\"click\",(()=>o(e,{action:\"click\",assetId:n})))})),o(e,{action:\"fireNativeImpressionTrackers\"}),\"complete\"===s.document.readyState?l():s.onload=l}))}})();" \ No newline at end of file From 09407fad88aa5ce4d73db2909686ade32b32ae4f Mon Sep 17 00:00:00 2001 From: doctafaustus Date: Thu, 1 Aug 2024 12:32:01 -0500 Subject: [PATCH 45/53] Fix no-inner-declarations linting error --- modules/nativoBidAdapter.js | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/modules/nativoBidAdapter.js b/modules/nativoBidAdapter.js index 32c2bafe48f..b7e36dd3e40 100644 --- a/modules/nativoBidAdapter.js +++ b/modules/nativoBidAdapter.js @@ -32,6 +32,10 @@ const PRICE_FLOOR_WILDCARD = '*' const localPbjsRef = getGlobal() +function getMediaType(accessObj) { + return deepAccess(accessObj, 'mediaTypes.video') ? VIDEO : BANNER; +} + /** * Keep track of bid data by keys * @returns {Object} - Map of bid data that can be referenced by multiple keys @@ -356,10 +360,6 @@ export const spec = { // Don't need the map anymore as it was unique for one request/response delete bidRequestMap[body.id] - function getMediaType(accessObj) { - return deepAccess(accessObj, 'mediaTypes.video') ? VIDEO : BANNER; - } - return bidResponses } catch (error) { // If there is an error, return [] From 1288e4cfa02ec11b2b044dde2e8a6da451551844 Mon Sep 17 00:00:00 2001 From: doctafaustus Date: Mon, 5 Aug 2024 15:47:00 -0500 Subject: [PATCH 46/53] Handle native requests --- modules/nativoBidAdapter.js | 16 +++++++++++++--- 1 file changed, 13 insertions(+), 3 deletions(-) diff --git a/modules/nativoBidAdapter.js b/modules/nativoBidAdapter.js index b7e36dd3e40..73e1968da54 100644 --- a/modules/nativoBidAdapter.js +++ b/modules/nativoBidAdapter.js @@ -33,7 +33,13 @@ const PRICE_FLOOR_WILDCARD = '*' const localPbjsRef = getGlobal() function getMediaType(accessObj) { - return deepAccess(accessObj, 'mediaTypes.video') ? VIDEO : BANNER; + if (deepAccess(accessObj, 'mediaTypes.video')) { + return VIDEO; + } else if (deepAccess(accessObj, 'mediaTypes.native')) { + return NATIVE; + } else { + return BANNER; + } } /** @@ -352,7 +358,11 @@ export const spec = { if (bidResponse.mediaType === VIDEO) { bidResponse.vastUrl = 'data:text/xml;charset=utf-8,' + encodeURIComponent(bid.adm) } - + if (bidResponse.mediaType === NATIVE) { + bidResponse.native = { + ortb: JSON.parse(bidResponse.ad) + }; + } bidResponses.push(bidResponse) }) }) @@ -433,7 +443,7 @@ export const spec = { body.seatbid.forEach((seatbid) => { // Grab the syncs for each seatbid - seatbid.syncUrls.forEach((sync) => { + seatbid.syncUrls?.forEach((sync) => { if (types[sync.type]) { if (sync.url.trim() !== '') { syncs.push({ From 3314390d9c334e60bbf626ff2cb4eda7d7b13dad Mon Sep 17 00:00:00 2001 From: doctafaustus Date: Thu, 15 Aug 2024 11:19:05 -0500 Subject: [PATCH 47/53] Add examples in Nativo readme --- modules/nativoBidAdapter.md | 101 ++++++++++++++++++++++++++++++------ 1 file changed, 84 insertions(+), 17 deletions(-) diff --git a/modules/nativoBidAdapter.md b/modules/nativoBidAdapter.md index f83fb45b52e..515d87af28e 100644 --- a/modules/nativoBidAdapter.md +++ b/modules/nativoBidAdapter.md @@ -16,24 +16,91 @@ gulp serve --modules=nativoBidAdapter # Test Parameters +## Banner + +```js +var adUnits = [ + { + code: 'div-gpt-ad-1460505748561-0', + mediaTypes: { + banner: { + sizes: [ + [300, 250], + [300, 600], + ], + }, + }, + // Replace this object to test a new Adapter! + bids: [ + { + bidder: 'nativo', + params: { + url: 'https://test-sites.internal.nativo.net/testing/prebid_adpater.html', + }, + }, + ], + }, +] ``` + +## Video + +```js var adUnits = [ - { - code: 'div-gpt-ad-1460505748561-0', - mediaTypes: { - banner: { - sizes: [[300, 250], [300,600]], - } - }, - // Replace this object to test a new Adapter! - bids: [{ - bidder: 'nativo', - params: { - url: 'https://test-sites.internal.nativo.net/testing/prebid_adpater.html' - } - }] - - } - ]; + { + code: 'ntvPlaceholder-1', + mediaTypes: { + video: { + mimes: ['video/mp4'], + protocols: [2, 3, 5, 6], + playbackmethod: [1, 2], + skip: 1, + skipafter: 5, + }, + }, + video: { + divId: 'player', + }, + bids: [ + { + bidder: 'nativo', + params: { + url: 'https://test-sites.internal.nativo.net/testing/prebid_adpater.html', + }, + }, + ], + }, +] +``` + +## Native +```js +var adUnits = [ + { + code: '/416881364/prebid-native-test-unit', + sizes: [[300, 250]], + mediaTypes: { + native: { + title: { + required: true, + }, + image: { + required: true, + }, + sponsoredBy: { + required: true, + }, + }, + }, + bids: [ + { + bidder: 'nativo', + params: { + url: 'https://test-sites.internal.nativo.net/testing/prebid_adpater.html', + }, + }, + ], + }, +] ``` From e5382318d85bcd5032dc2c6a7ee8a032535d156b Mon Sep 17 00:00:00 2001 From: doctafaustus Date: Thu, 15 Aug 2024 11:27:09 -0500 Subject: [PATCH 48/53] Add 'mediaType' property to tests for compatibility with adapter code --- test/spec/modules/nativoBidAdapter_spec.js | 83 ++++++++++++++-------- 1 file changed, 52 insertions(+), 31 deletions(-) diff --git a/test/spec/modules/nativoBidAdapter_spec.js b/test/spec/modules/nativoBidAdapter_spec.js index 9893490e833..349051cb48e 100644 --- a/test/spec/modules/nativoBidAdapter_spec.js +++ b/test/spec/modules/nativoBidAdapter_spec.js @@ -221,6 +221,7 @@ describe('interpretResponse', function () { meta: { advertiserDomains: ['test.com'], }, + mediaType: 'banner', }, ] @@ -681,16 +682,24 @@ describe('hasProtocol', () => { describe('addProtocol', () => { it('www.testpage.com', () => { - expect(addProtocol('www.testpage.com')).to.be.equal('https://www.testpage.com') + expect(addProtocol('www.testpage.com')).to.be.equal( + 'https://www.testpage.com' + ) }) it('//www.testpage.com', () => { - expect(addProtocol('//www.testpage.com')).to.be.equal('https://www.testpage.com') + expect(addProtocol('//www.testpage.com')).to.be.equal( + 'https://www.testpage.com' + ) }) it('http://www.testpage.com', () => { - expect(addProtocol('http://www.testpage.com')).to.be.equal('http://www.testpage.com') + expect(addProtocol('http://www.testpage.com')).to.be.equal( + 'http://www.testpage.com' + ) }) it('https://www.testpage.com', () => { - expect(addProtocol('https://www.testpage.com')).to.be.equal('https://www.testpage.com') + expect(addProtocol('https://www.testpage.com')).to.be.equal( + 'https://www.testpage.com' + ) }) }) @@ -786,7 +795,7 @@ describe('RequestData', () => { describe('UserEIDs', () => { const userEids = new UserEIDs() - const eids = [{ 'testId': 1111 }] + const eids = [{ testId: 1111 }] describe('processBidRequestData', () => { it('Processes bid request without eids', () => { @@ -810,7 +819,7 @@ describe('UserEIDs', () => { expect(qs).to.include('ntv_pb_eid=') try { expect(JSON.parse(value)).to.be.equal(eids) - } catch (err) { } + } catch (err) {} }) }) }) @@ -828,13 +837,25 @@ describe('buildRequestUrl', () => { }) it('Returns baseUrl + QS params if QS strings passed', () => { - const url = buildRequestUrl(baseUrl, ['ntv_ptd=123456&ntv_test=true', 'ntv_foo=bar']) - expect(url).to.be.equal(`${baseUrl}?ntv_ptd=123456&ntv_test=true&ntv_foo=bar`) + const url = buildRequestUrl(baseUrl, [ + 'ntv_ptd=123456&ntv_test=true', + 'ntv_foo=bar', + ]) + expect(url).to.be.equal( + `${baseUrl}?ntv_ptd=123456&ntv_test=true&ntv_foo=bar` + ) }) it('Returns baseUrl + QS params if mixed QS strings passed', () => { - const url = buildRequestUrl(baseUrl, ['ntv_ptd=123456&ntv_test=true', '', '', 'ntv_foo=bar']) - expect(url).to.be.equal(`${baseUrl}?ntv_ptd=123456&ntv_test=true&ntv_foo=bar`) + const url = buildRequestUrl(baseUrl, [ + 'ntv_ptd=123456&ntv_test=true', + '', + '', + 'ntv_foo=bar', + ]) + expect(url).to.be.equal( + `${baseUrl}?ntv_ptd=123456&ntv_test=true&ntv_foo=bar` + ) }) }) @@ -848,15 +869,15 @@ describe('Prebid Video', function () { protocols: [2, 3, 5, 6], playbackmethod: [1, 2], skip: 1, - skipafter: 5 + skipafter: 5, }, - } - }; + }, + } - const isValid = spec.isBidRequestValid(videoBidRequest); - expect(isValid).to.be.true; - }); -}); + const isValid = spec.isBidRequestValid(videoBidRequest) + expect(isValid).to.be.true + }) +}) describe('Prebid Native', function () { it('should handle native bid requests', function () { @@ -866,33 +887,33 @@ describe('Prebid Native', function () { native: { title: { required: true, - len: 80 + len: 80, }, image: { required: true, - sizes: [150, 50] + sizes: [150, 50], }, sponsoredBy: { - required: true + required: true, }, clickUrl: { - required: true + required: true, }, privacyLink: { - required: false + required: false, }, body: { - required: true + required: true, }, icon: { required: true, - sizes: [50, 50] - } + sizes: [50, 50], + }, }, - } - }; + }, + } - const isValid = spec.isBidRequestValid(nativeBidRequest); - expect(isValid).to.be.true; - }); -}); + const isValid = spec.isBidRequestValid(nativeBidRequest) + expect(isValid).to.be.true + }) +}) From 8661827ca34f9e6286f864844fd8eb134858f56c Mon Sep 17 00:00:00 2001 From: doctafaustus Date: Thu, 15 Aug 2024 11:30:48 -0500 Subject: [PATCH 49/53] Remove data URI from VAST XML --- modules/nativoBidAdapter.js | 62 +++++++++++++++++++++++-------------- 1 file changed, 39 insertions(+), 23 deletions(-) diff --git a/modules/nativoBidAdapter.js b/modules/nativoBidAdapter.js index 73e1968da54..8f41a807b1f 100644 --- a/modules/nativoBidAdapter.js +++ b/modules/nativoBidAdapter.js @@ -8,16 +8,16 @@ const converter = ortbConverter({ context: { // `netRevenue` and `ttl` are required properties of bid responses - provide a default for them netRevenue: true, // or false if your adapter should set bidResponse.netRevenue = false - ttl: 30 // default bidResponse.ttl (when not specified in ORTB response.seatbid[].bid[].exp) + ttl: 30, // default bidResponse.ttl (when not specified in ORTB response.seatbid[].bid[].exp) }, imp(buildImp, bidRequest, context) { - const imp = buildImp(bidRequest, context); + const imp = buildImp(bidRequest, context) imp.tagid = bidRequest.adUnitCode if (imp.ext) imp.ext.placementId = bidRequest.params.placementId - return imp; - } -}); + return imp + }, +}) const BIDDER_CODE = 'nativo' const BIDDER_ENDPOINT = 'https://exchange.postrelease.com/prebid' @@ -34,11 +34,11 @@ const localPbjsRef = getGlobal() function getMediaType(accessObj) { if (deepAccess(accessObj, 'mediaTypes.video')) { - return VIDEO; + return VIDEO } else if (deepAccess(accessObj, 'mediaTypes.native')) { - return NATIVE; + return NATIVE } else { - return BANNER; + return BANNER } } @@ -162,7 +162,10 @@ export const spec = { */ buildRequests: function (validBidRequests, bidderRequest) { // Get OpenRTB Data - const openRTBData = converter.toORTB({bidRequests: validBidRequests, bidderRequest}) + const openRTBData = converter.toORTB({ + bidRequests: validBidRequests, + bidderRequest, + }) const openRTBDataString = JSON.stringify(openRTBData) const requestData = new RequestData() @@ -213,7 +216,8 @@ export const spec = { let params = [ // Prebid version { - key: 'ntv_pbv', value: localPbjsRef.version + key: 'ntv_pbv', + value: localPbjsRef.version, }, // Prebid request id { key: 'ntv_pb_rid', value: bidderRequest.bidderRequestId }, @@ -296,14 +300,17 @@ export const spec = { params.unshift({ key: 'us_privacy', value: bidderRequest.uspConsent }) } - const qsParamStrings = [requestData.getRequestDataQueryString(), arrayToQS(params)] + const qsParamStrings = [ + requestData.getRequestDataQueryString(), + arrayToQS(params), + ] const requestUrl = buildRequestUrl(BIDDER_ENDPOINT, qsParamStrings) let serverRequest = { method: 'POST', url: requestUrl, data: openRTBDataString, - bidderRequest: bidderRequest + bidderRequest: bidderRequest, } return serverRequest @@ -356,12 +363,12 @@ export const spec = { if (bid.ext) extData[bid.id] = bid.ext if (bidResponse.mediaType === VIDEO) { - bidResponse.vastUrl = 'data:text/xml;charset=utf-8,' + encodeURIComponent(bid.adm) + bidResponse.vastUrl = bid.adm } if (bidResponse.mediaType === NATIVE) { bidResponse.native = { - ortb: JSON.parse(bidResponse.ad) - }; + ortb: JSON.parse(bidResponse.ad), + } } bidResponses.push(bidResponse) }) @@ -436,7 +443,9 @@ export const spec = { typeof response.body === 'string' ? JSON.parse(response.body) : response.body - } catch (err) { return } + } catch (err) { + return + } // Make sure we have valid content if (!body || !body.seatbid || body.seatbid.length === 0) return @@ -513,7 +522,9 @@ export class RequestData { getRequestDataQueryString() { if (this.bidRequestDataSources.length == 0) return - const queryParams = this.bidRequestDataSources.map(dataSource => dataSource.getRequestQueryString()).filter(queryString => queryString !== '') + const queryParams = this.bidRequestDataSources + .map((dataSource) => dataSource.getRequestQueryString()) + .filter((queryString) => queryString !== '') return queryParams.join('&') } } @@ -522,8 +533,10 @@ export class BidRequestDataSource { constructor() { this.type = 'BidRequestDataSource' } - processBidRequestData(bidRequest, bidderRequest) { } - getRequestQueryString() { return '' } + processBidRequestData(bidRequest, bidderRequest) {} + getRequestQueryString() { + return '' + } } export class UserEIDs extends BidRequestDataSource { @@ -562,7 +575,7 @@ QueryStringParam.prototype.toString = function () { export function encodeToBase64(value) { try { return btoa(JSON.stringify(value)) - } catch (err) { } + } catch (err) {} } export function parseFloorPriceData(bidRequest) { @@ -730,9 +743,12 @@ function getLargestSize(sizes, method = area) { * Build the final request url */ export function buildRequestUrl(baseUrl, qsParamStringArray = []) { - if (qsParamStringArray.length === 0 || !Array.isArray(qsParamStringArray)) return baseUrl + if (qsParamStringArray.length === 0 || !Array.isArray(qsParamStringArray)) + return baseUrl - const nonEmptyQSParamStrings = qsParamStringArray.filter(qsParamString => qsParamString.trim() !== '') + const nonEmptyQSParamStrings = qsParamStringArray.filter( + (qsParamString) => qsParamString.trim() !== '' + ) if (nonEmptyQSParamStrings.length === 0) return baseUrl @@ -774,7 +790,7 @@ export function getPageUrlFromBidRequest(bidRequest) { try { const url = new URL(paramPageUrl) return url.href - } catch (err) { } + } catch (err) {} } export function hasProtocol(url) { From 1ebc2ee7d0b5bb2b4fdacfec9004040813bbacf9 Mon Sep 17 00:00:00 2001 From: doctafaustus Date: Thu, 15 Aug 2024 11:37:21 -0500 Subject: [PATCH 50/53] Fix 'no-unused-expressions' lint error --- modules/nativoBidAdapter.js | 20 +++++++++++--------- 1 file changed, 11 insertions(+), 9 deletions(-) diff --git a/modules/nativoBidAdapter.js b/modules/nativoBidAdapter.js index 8f41a807b1f..9e37cfce44d 100644 --- a/modules/nativoBidAdapter.js +++ b/modules/nativoBidAdapter.js @@ -452,16 +452,18 @@ export const spec = { body.seatbid.forEach((seatbid) => { // Grab the syncs for each seatbid - seatbid.syncUrls?.forEach((sync) => { - if (types[sync.type]) { - if (sync.url.trim() !== '') { - syncs.push({ - type: sync.type, - url: sync.url.replace('{GDPR_params}', params), - }) + if (seatbid.syncUrls) { + seatbid.syncUrls.forEach((sync) => { + if (types[sync.type]) { + if (sync.url.trim() !== '') { + syncs.push({ + type: sync.type, + url: sync.url.replace('{GDPR_params}', params), + }) + } } - } - }) + }) + } }) }) From af21a08d57ec61d9e42785f2c98b722e1ea48bca Mon Sep 17 00:00:00 2001 From: doctafaustus Date: Fri, 16 Aug 2024 13:02:51 -0500 Subject: [PATCH 51/53] Fix lint error 'curcly' --- modules/nativoBidAdapter.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/modules/nativoBidAdapter.js b/modules/nativoBidAdapter.js index 9e37cfce44d..ba257a395f6 100644 --- a/modules/nativoBidAdapter.js +++ b/modules/nativoBidAdapter.js @@ -745,8 +745,9 @@ function getLargestSize(sizes, method = area) { * Build the final request url */ export function buildRequestUrl(baseUrl, qsParamStringArray = []) { - if (qsParamStringArray.length === 0 || !Array.isArray(qsParamStringArray)) + if (qsParamStringArray.length === 0 || !Array.isArray(qsParamStringArray)) { return baseUrl + } const nonEmptyQSParamStrings = qsParamStringArray.filter( (qsParamString) => qsParamString.trim() !== '' From d336bae753dd38ce36c6575806bb8d8676d44dc7 Mon Sep 17 00:00:00 2001 From: Bill Coloe Date: Mon, 19 Aug 2024 12:49:29 -0500 Subject: [PATCH 52/53] Remove bidder name validation in 'isBidRequestValid' (#4) --- modules/nativoBidAdapter.js | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/modules/nativoBidAdapter.js b/modules/nativoBidAdapter.js index ba257a395f6..74ae4664086 100644 --- a/modules/nativoBidAdapter.js +++ b/modules/nativoBidAdapter.js @@ -134,8 +134,7 @@ export const spec = { */ isBidRequestValid: function (bid) { // We don't need any specific parameters to make a bid request - // If not parameters are supplied just verify it's the correct bidder code - if (!bid.params) return bid.bidder === BIDDER_CODE + if (!bid.params) return true // Check if any supplied parameters are invalid const hasInvalidParameters = Object.keys(bid.params).some((key) => { From 75eb52e61c40521f4d773bf4d1a11e4b554b36ca Mon Sep 17 00:00:00 2001 From: Bill Coloe Date: Fri, 11 Oct 2024 11:40:53 -0500 Subject: [PATCH 53/53] Add GPP consent string to req (#5) --- modules/nativoBidAdapter.js | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/modules/nativoBidAdapter.js b/modules/nativoBidAdapter.js index 74ae4664086..c945619c667 100644 --- a/modules/nativoBidAdapter.js +++ b/modules/nativoBidAdapter.js @@ -293,6 +293,14 @@ export const spec = { }) } + // Add GPP params + if (bidderRequest.gppConsent) { + params.unshift({ + key: 'ntv_gpp_consent', + value: bidderRequest.gppConsent.gppString, + }) + } + // Add USP params if (bidderRequest.uspConsent) { // Put on the beginning of the qs param array