diff --git a/.babelrc.js b/.babelrc.js index 74d26caf0fd..43303f59a8b 100644 --- a/.babelrc.js +++ b/.babelrc.js @@ -14,16 +14,7 @@ module.exports = { [ useLocal('@babel/preset-env'), { - "targets": { - "browsers": [ - "chrome >= 75", - "safari >=10", - "edge >= 70", - "ff >= 70", - "ie >= 11", - "ios >= 11" - ] - } + "useBuiltIns": "entry" } ] ], diff --git a/.circleci/config.yml b/.circleci/config.yml index ea5fb916a91..404026d9446 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -52,7 +52,7 @@ aliases: - &unit_test_steps - checkout - restore_cache: *restore_dep_cache - - run: npm install + - run: npm ci - save_cache: *save_dep_cache - run: *install - run: *setup_browserstack diff --git a/README.md b/README.md index be16e9e3547..bc0e64afa06 100644 --- a/README.md +++ b/README.md @@ -59,6 +59,8 @@ module.exports = { loader: 'babel-loader', // presets and plugins for Prebid.js must be manually specified separate from your other babel rule. // this can be accomplished by requiring prebid's .babelrc.js file (requires Babel 7 and Node v8.9.0+) + // as of Prebid 6, babelrc.js only targets modern browsers. One can change the targets and build for + // older browsers if they prefer, but integration tests on ie11 were removed in Prebid.js 6.0 options: require('prebid.js/.babelrc.js') } } @@ -272,7 +274,7 @@ As you make code changes, the bundles will be rebuilt and the page reloaded auto ## Contribute -Many SSPs, bidders, and publishers have contributed to this project. [Hundreds of bidders](https://github.com/prebid/Prebid.js/tree/master/src/adapters) are supported by Prebid.js. +Many SSPs, bidders, and publishers have contributed to this project. [Hundreds of bidders](https://github.com/prebid/Prebid.js/tree/master/modules) are supported by Prebid.js. For guidelines, see [Contributing](./CONTRIBUTING.md). @@ -314,7 +316,7 @@ For instructions on writing tests for Prebid.js, see [Testing Prebid.js](http:// ### Supported Browsers -Prebid.js is supported on IE11 and modern browsers. +Prebid.js is supported on IE11 and modern browsers until 5.x. 6.x+ transpiles to target >0.25%; not Opera Mini; not IE11. ### Governance Review our governance model [here](https://github.com/prebid/Prebid.js/tree/master/governance.md). diff --git a/browsers.json b/browsers.json index 4f2ea456f68..bd6bd5772d6 100644 --- a/browsers.json +++ b/browsers.json @@ -1,74 +1,51 @@ { - "bs_edge_17_windows_10": { + "bs_edge_latest_windows_10": { "base": "BrowserStack", "os_version": "10", "browser": "edge", - "browser_version": "17.0", + "browser_version": "latest", "device": null, "os": "Windows" }, - "bs_edge_90_windows_10": { - "base": "BrowserStack", - "os_version": "10", - "browser": "edge", - "browser_version": "90.0", - "device": null, - "os": "Windows" - }, - "bs_ie_11_windows_10": { - "base": "BrowserStack", - "os_version": "10", - "browser": "ie", - "browser_version": "11.0", - "device": null, - "os": "Windows" - }, - "bs_chrome_90_windows_10": { + "bs_chrome_latest_windows_10": { "base": "BrowserStack", "os_version": "10", "browser": "chrome", - "browser_version": "90.0", + "browser_version": "latest", "device": null, "os": "Windows" }, - "bs_chrome_79_windows_10": { + "bs_chrome_87_windows_10": { "base": "BrowserStack", "os_version": "10", "browser": "chrome", - "browser_version": "79.0", - "device": null, - "os": "Windows" - }, - "bs_firefox_88_windows_10": { - "base": "BrowserStack", - "os_version": "10", - "browser": "firefox", - "browser_version": "88.0", + "browser_version": "87.0", "device": null, "os": "Windows" }, - "bs_firefox_72_windows_10": { + "bs_firefox_latest_windows_10": { "base": "BrowserStack", "os_version": "10", "browser": "firefox", - "browser_version": "72.0", + "browser_version": "latest", "device": null, "os": "Windows" }, - "bs_safari_14_mac_bigsur": { + "bs_safari_latest_mac_bigsur": { "base": "BrowserStack", "os_version": "Big Sur", "browser": "safari", - "browser_version": "14.0", + "browser_version": "latest", "device": null, "os": "OS X" }, - "bs_safari_12_mac_mojave": { + "bs_safari_15_catalina": { "base": "BrowserStack", - "os_version": "Mojave", + "os_version": "Catalina", "browser": "safari", - "browser_version": "12.0", + "browser_version": "13.1", "device": null, "os": "OS X" } + } diff --git a/gulpfile.js b/gulpfile.js index 8609177a8b9..6ecfee1b672 100644 --- a/gulpfile.js +++ b/gulpfile.js @@ -76,6 +76,7 @@ function lint(done) { 'modules/**/*.js', 'test/**/*.js', 'plugins/**/*.js', + '!plugins/**/node_modules/**', './*.js' ], { base: './' }) .pipe(gulpif(argv.nolintfix, eslint(), eslint({ fix: true }))) @@ -114,38 +115,6 @@ function viewReview(done) { viewReview.displayName = 'view-review'; -// Watch Task with Live Reload -function watch(done) { - var mainWatcher = gulp.watch([ - 'src/**/*.js', - 'modules/**/*.js', - 'test/spec/**/*.js', - '!test/spec/loaders/**/*.js' - ]); - var loaderWatcher = gulp.watch([ - 'loaders/**/*.js', - 'test/spec/loaders/**/*.js' - ]); - - connect.server({ - https: argv.https, - port: port, - host: FAKE_SERVER_HOST, - root: './', - livereload: true - }); - - mainWatcher.on('all', gulp.series(clean, gulp.parallel(lint, 'build-bundle-dev', test))); - loaderWatcher.on('all', gulp.series(lint)); - done(); -}; - -function makeModuleList(modules) { - return modules.map(module => { - return '"' + module + '"' - }); -} - function makeDevpackPkg() { var cloned = _.cloneDeep(webpackConfig); cloned.devtool = 'source-map'; @@ -157,7 +126,6 @@ function makeDevpackPkg() { return gulp.src([].concat(moduleSources, analyticsSources, 'src/prebid.js')) .pipe(helpers.nameModules(externalModules)) .pipe(webpackStream(cloned, webpack)) - .pipe(replace(/('|")v\$prebid\.modulesList\$('|")/g, makeModuleList(externalModules))) .pipe(gulp.dest('build/dev')) .pipe(connect.reload()); } @@ -175,7 +143,6 @@ function makeWebpackPkg() { .pipe(helpers.nameModules(externalModules)) .pipe(webpackStream(cloned, webpack)) .pipe(terser()) - .pipe(replace(/('|")v\$prebid\.modulesList\$('|")/g, makeModuleList(externalModules))) .pipe(gulpif(file => file.basename === 'prebid-core.js', header(banner, { prebid: prebid }))) .pipe(gulp.dest('build/dist')); } @@ -254,60 +221,68 @@ function bundle(dev, moduleArr) { // If --browsers is given, browsers can be chosen explicitly. e.g. --browsers=chrome,firefox,ie9 // If --notest is given, it will immediately skip the test task (useful for developing changes with `gulp serve --notest`) -function test(done) { - if (argv.notest) { - done(); - } else if (argv.e2e) { - let wdioCmd = path.join(__dirname, 'node_modules/.bin/wdio'); - let wdioConf = path.join(__dirname, 'wdio.conf.js'); - let wdioOpts; - - if (argv.file) { - wdioOpts = [ - wdioConf, - `--spec`, - `${argv.file}` - ] - } else { - wdioOpts = [ - wdioConf - ]; - } +function testTaskMaker(options = {}) { + ['watch', 'e2e', 'file', 'browserstack', 'notest'].forEach(opt => { + options[opt] = options[opt] || argv[opt]; + }) - // run fake-server - const fakeServer = spawn('node', ['./test/fake-server/index.js', `--port=${FAKE_SERVER_PORT}`]); - fakeServer.stdout.on('data', (data) => { - console.log(`stdout: ${data}`); - }); - fakeServer.stderr.on('data', (data) => { - console.log(`stderr: ${data}`); - }); + return function test(done) { + if (options.notest) { + done(); + } else if (options.e2e) { + let wdioCmd = path.join(__dirname, 'node_modules/.bin/wdio'); + let wdioConf = path.join(__dirname, 'wdio.conf.js'); + let wdioOpts; + + if (options.file) { + wdioOpts = [ + wdioConf, + `--spec`, + `${options.file}` + ] + } else { + wdioOpts = [ + wdioConf + ]; + } - execa(wdioCmd, wdioOpts, { stdio: 'inherit' }) - .then(stdout => { - // kill fake server - fakeServer.kill('SIGINT'); - done(); - process.exit(0); - }) - .catch(err => { - // kill fake server - fakeServer.kill('SIGINT'); - done(new Error(`Tests failed with error: ${err}`)); - process.exit(1); + // run fake-server + const fakeServer = spawn('node', ['./test/fake-server/index.js', `--port=${FAKE_SERVER_PORT}`]); + fakeServer.stdout.on('data', (data) => { + console.log(`stdout: ${data}`); + }); + fakeServer.stderr.on('data', (data) => { + console.log(`stderr: ${data}`); }); - } else { - var karmaConf = karmaConfMaker(false, argv.browserstack, argv.watch, argv.file); - var browserOverride = helpers.parseBrowserArgs(argv); - if (browserOverride.length > 0) { - karmaConf.browsers = browserOverride; - } + execa(wdioCmd, wdioOpts, { stdio: 'inherit' }) + .then(stdout => { + // kill fake server + fakeServer.kill('SIGINT'); + done(); + process.exit(0); + }) + .catch(err => { + // kill fake server + fakeServer.kill('SIGINT'); + done(new Error(`Tests failed with error: ${err}`)); + process.exit(1); + }); + } else { + var karmaConf = karmaConfMaker(false, options.browserstack, options.watch, options.file); - new KarmaServer(karmaConf, newKarmaCallback(done)).start(); + var browserOverride = helpers.parseBrowserArgs(argv); + if (browserOverride.length > 0) { + karmaConf.browsers = browserOverride; + } + + new KarmaServer(karmaConf, newKarmaCallback(done)).start(); + } } } +const test = testTaskMaker(); + function newKarmaCallback(done) { return function (exitCode) { if (exitCode) { @@ -384,6 +359,35 @@ function startFakeServer() { }); } +// Watch Task with Live Reload +function watchTaskMaker(options = {}) { + if (options.livereload == null) { + options.livereload = true; + } + options.alsoWatch = options.alsoWatch || []; + + return function watch(done) { + var mainWatcher = gulp.watch([ + 'src/**/*.js', + 'modules/**/*.js', + ].concat(options.alsoWatch)); + + connect.server({ + https: argv.https, + port: port, + host: FAKE_SERVER_HOST, + root: './', + livereload: options.livereload + }); + + mainWatcher.on('all', options.task()); + done(); + } +} + +const watch = watchTaskMaker({alsoWatch: ['test/**/*.js'], task: () => gulp.series(clean, gulp.parallel(lint, 'build-bundle-dev', test))}); +const watchFast = watchTaskMaker({livereload: false, task: () => gulp.series('build-bundle-dev')}); + // support tasks gulp.task(lint); gulp.task(watch); @@ -396,7 +400,8 @@ gulp.task('build-bundle-dev', gulp.series(makeDevpackPkg, gulpBundle.bind(null, gulp.task('build-bundle-prod', gulp.series(makeWebpackPkg, gulpBundle.bind(null, false))); // public tasks (dependencies are needed for each task since they can be ran on their own) -gulp.task('test', gulp.series(clean, lint, test)); +gulp.task('test-only', test); +gulp.task('test', gulp.series(clean, lint, 'test-only')); gulp.task('test-coverage', gulp.series(clean, testCoverage)); gulp.task(viewCoverage); @@ -407,7 +412,8 @@ gulp.task('build', gulp.series(clean, 'build-bundle-prod')); gulp.task('build-postbid', gulp.series(escapePostbidConfig, buildPostbid)); gulp.task('serve', gulp.series(clean, lint, gulp.parallel('build-bundle-dev', watch, test))); -gulp.task('serve-fast', gulp.series(clean, gulp.parallel('build-bundle-dev', watch))); +gulp.task('serve-fast', gulp.series(clean, gulp.parallel('build-bundle-dev', watchFast))); +gulp.task('serve-and-test', gulp.series(clean, gulp.parallel('build-bundle-dev', watchFast, testTaskMaker({watch: true})))); gulp.task('serve-fake', gulp.series(clean, gulp.parallel('build-bundle-dev', watch), injectFakeServerEndpointDev, test, startFakeServer)); gulp.task('default', gulp.series(clean, makeWebpackPkg)); diff --git a/integrationExamples/gpt/akamaidap_segments_example.html b/integrationExamples/gpt/akamaidap_segments_example.html new file mode 100644 index 00000000000..e85ac8e1337 --- /dev/null +++ b/integrationExamples/gpt/akamaidap_segments_example.html @@ -0,0 +1,132 @@ + + + + + + + + + + + + + +

Prebid.js Test

+
Div-1
+
+ +
+
Segments Sent to Bidding Adapter
+
+ + diff --git a/integrationExamples/gpt/amp/creative.html b/integrationExamples/gpt/amp/creative.html index 86f669dd6b5..384b81107cc 100644 --- a/integrationExamples/gpt/amp/creative.html +++ b/integrationExamples/gpt/amp/creative.html @@ -1,38 +1,16 @@ + diff --git a/integrationExamples/gpt/weboramaRtdProvider_example.html b/integrationExamples/gpt/weboramaRtdProvider_example.html index 824d7a2f0c7..66e4a57d2a6 100644 --- a/integrationExamples/gpt/weboramaRtdProvider_example.html +++ b/integrationExamples/gpt/weboramaRtdProvider_example.html @@ -17,22 +17,31 @@ debug: true, realTimeData: { auctionDelay: 1000, - dataProviders: [ - { + dataProviders: [{ name: "weborama", waitForIt: true, params: { - weboCtxConf: { - setTargeting: true, - token: "to-be-defined", - targetURL: "https://prebid.org/", - defaultProfile: { - webo_ctx: ['moon'] - } - } - } - } - ] + weboCtxConf: { + token: "to-be-defined", // mandatory + targetURL: "https://prebid.org", // default is document.URL + setPrebidTargeting: true, // default + sendToBidders: true, // default + defaultProfile: { // optional + webo_ctx: ['moon'], + webo_ds: ['bar'] + } + }, + weboUserDataConf: { + setPrebidTargeting: true, // default + sendToBidders: true, // default + defaultProfile: { // optional + webo_cs: ['Red'], + webo_audiences: ['bam'] + }, + localStorageProfileKey: 'webo_wam2gam_entry' // default + } + } + }] } }); }); @@ -54,7 +63,7 @@ } }, bids: [{ - bidder: 'appnexus', + bidder: 'smartadserver', params: { placementId: 1 } diff --git a/integrationExamples/gpt/x-domain/creative.html b/integrationExamples/gpt/x-domain/creative.html index fce46bb380f..610fb222787 100644 --- a/integrationExamples/gpt/x-domain/creative.html +++ b/integrationExamples/gpt/x-domain/creative.html @@ -2,37 +2,40 @@ // this script can be returned by an ad server delivering a cross domain iframe, into which the // creative will be rendered, e.g. DFP delivering a SafeFrame -let windowLocation = window.location; -var urlParser = document.createElement('a'); +const windowLocation = window.location; +const urlParser = document.createElement('a'); urlParser.href = '%%PATTERN:url%%'; -var publisherDomain = urlParser.protocol + '//' + urlParser.hostname; +const publisherDomain = urlParser.protocol + '//' + urlParser.hostname; +const adId = '%%PATTERN:hb_adid%%'; function renderAd(ev) { - var key = ev.message ? 'message' : 'data'; - var adObject = {}; - try { - adObject = JSON.parse(ev[key]); - } catch (e) { - return; - } + const key = ev.message ? 'message' : 'data'; + let adObject = {}; + try { + adObject = JSON.parse(ev[key]); + } catch (e) { + return; + } - var origin = ev.origin || ev.originalEvent.origin; - if (adObject.message && adObject.message === 'Prebid Response' && - publisherDomain === origin && - adObject.adId === '%%PATTERN:hb_adid%%' && - (adObject.ad || adObject.adUrl)) { - var body = window.document.body; - var ad = adObject.ad; - var url = adObject.adUrl; - var width = adObject.width; - var height = adObject.height; + const origin = ev.origin || ev.originalEvent.origin; + if (adObject.message && adObject.message === 'Prebid Response' && + publisherDomain === origin && + adObject.adId === adId) { + try { + const body = window.document.body; + const ad = adObject.ad; + const url = adObject.adUrl; + const width = adObject.width; + const height = adObject.height; if (adObject.mediaType === 'video') { + signalRenderResult(false, { + reason: 'preventWritingOnMainDocument', + message: `Cannot render video ad ${adId}` + }); console.log('Error trying to write ad.'); - } else - - if (ad) { - var frame = document.createElement('iframe'); + } else if (ad) { + const frame = document.createElement('iframe'); frame.setAttribute('FRAMEBORDER', 0); frame.setAttribute('SCROLLING', 'no'); frame.setAttribute('MARGINHEIGHT', 0); @@ -46,18 +49,42 @@ frame.contentDocument.open(); frame.contentDocument.write(ad); frame.contentDocument.close(); + signalRenderResult(true); } else if (url) { body.insertAdjacentHTML('beforeend', ''); + signalRenderResult(true); } else { - console.log('Error trying to write ad. No ad for bid response id: ' + id); + signalRenderResult(false, { + reason: 'noAd', + message: `No ad for ${adId}` + }); + console.log(`Error trying to write ad. No ad markup or adUrl for ${adId}`); } + } catch (e) { + signalRenderResult(false, {reason: 'exception', message: e.message}); + console.log(`Error in rendering ad`, e); } } + function signalRenderResult(success, {reason, message} = {}) { + const payload = { + message: 'Prebid Event', + adId, + event: success ? 'adRenderSucceeded' : 'adRenderFailed', + } + if (!success) { + payload.info = {reason, message}; + } + ev.source.postMessage(JSON.stringify(payload), publisherDomain); + } + +} + + function requestAdFromPrebid() { var message = JSON.stringify({ message: 'Prebid Request', - adId: '%%PATTERN:hb_adid%%' + adId }); window.parent.postMessage(message, publisherDomain); } diff --git a/karma.conf.maker.js b/karma.conf.maker.js index cf5999ba85e..be51947dae8 100644 --- a/karma.conf.maker.js +++ b/karma.conf.maker.js @@ -111,7 +111,7 @@ module.exports = function(codeCoverage, browserstack, watchMode, file) { var webpackConfig = newWebpackConfig(codeCoverage); var plugins = newPluginsArray(browserstack); - var files = file ? ['test/helpers/prebidGlobal.js', file] : ['test/test_index.js']; + var files = file ? ['test/test_deps.js', file] : ['test/test_index.js']; // This file opens the /debug.html tab automatically. // It has no real value unless you're running --watch, and intend to do some debugging in the browser. if (watchMode) { @@ -166,7 +166,7 @@ module.exports = function(codeCoverage, browserstack, watchMode, file) { browserNoActivityTimeout: 3e5, // default 10000 captureTimeout: 3e5, // default 60000, browserDisconnectTolerance: 3, - concurrency: 5, + concurrency: 6, plugins: plugins } diff --git a/modules/.submodules.json b/modules/.submodules.json index 3ac1e1ef59f..2e77873dc78 100644 --- a/modules/.submodules.json +++ b/modules/.submodules.json @@ -5,6 +5,7 @@ "akamaiDAPIdSystem", "amxIdSystem", "britepoolIdSystem", + "connectIdSystem", "criteoIdSystem", "deepintentDpesIdSystem", "dmdIdSystem", @@ -34,7 +35,8 @@ "uid2IdSystem", "unifiedIdSystem", "verizonMediaIdSystem", - "zeotapIdPlusIdSystem" + "zeotapIdPlusIdSystem", + "adqueryIdSystem" ], "adpod": [ "freeWheelAdserverVideo", diff --git a/modules/33acrossBidAdapter.js b/modules/33acrossBidAdapter.js index 2bdbdd6414b..af67bb2bf48 100644 --- a/modules/33acrossBidAdapter.js +++ b/modules/33acrossBidAdapter.js @@ -1,11 +1,20 @@ import { registerBidder } from '../src/adapters/bidderFactory.js'; import { config } from '../src/config.js'; import { - deepAccess, uniques, isArray, getWindowTop, isGptPubadsDefined, isSlotMatchingAdUnitCode, logInfo, logWarn, - getWindowSelf + deepAccess, + uniques, + isArray, + getWindowTop, + isGptPubadsDefined, + isSlotMatchingAdUnitCode, + logInfo, + logWarn, + getWindowSelf, + mergeDeep, } from '../src/utils.js'; -import {BANNER, VIDEO} from '../src/mediaTypes.js'; +import { BANNER, VIDEO } from '../src/mediaTypes.js'; +// **************************** UTILS *************************** // const BIDDER_CODE = '33across'; const END_POINT = 'https://ssc.33across.com/api/v1/hb'; const SYNC_ENDPOINT = 'https://ssc-cms.33across.com/ps/?m=xch&rt=html&ru=deb'; @@ -42,6 +51,14 @@ const adapterState = { const NON_MEASURABLE = 'nm'; +function getTTXConfig() { + const ttxSettings = Object.assign({}, + config.getConfig('ttxSettings') + ); + + return ttxSettings; +} + // **************************** VALIDATION *************************** // function isBidRequestValid(bid) { return ( @@ -74,6 +91,7 @@ function _validateGUID(bid) { function _validateBanner(bid) { const banner = deepAccess(bid, 'mediaTypes.banner'); + // If there's no banner no need to validate against banner rules if (banner === undefined) { return true; @@ -140,91 +158,125 @@ function _validateVideo(bid) { // NOTE: With regards to gdrp consent data, the server will independently // infer the gdpr applicability therefore, setting the default value to false function buildRequests(bidRequests, bidderRequest) { + const { + ttxSettings, + gdprConsent, + uspConsent, + pageUrl + } = _buildRequestParams(bidRequests, bidderRequest); + + const groupedRequests = _buildRequestGroups(ttxSettings, bidRequests); + + const serverRequests = []; + + for (const key in groupedRequests) { + serverRequests.push( + _createServerRequest({ + bidRequests: groupedRequests[key], + gdprConsent, + uspConsent, + pageUrl, + ttxSettings + }) + ) + } + + return serverRequests; +} + +function _buildRequestParams(bidRequests, bidderRequest) { + const ttxSettings = getTTXConfig(); + const gdprConsent = Object.assign({ consentString: undefined, gdprApplies: false }, bidderRequest && bidderRequest.gdprConsent); const uspConsent = bidderRequest && bidderRequest.uspConsent; + const pageUrl = (bidderRequest && bidderRequest.refererInfo) ? (bidderRequest.refererInfo.referer) : (undefined); adapterState.uniqueSiteIds = bidRequests.map(req => req.params.siteId).filter(uniques); - return bidRequests.map(bidRequest => _createServerRequest( - { - bidRequest, - gdprConsent, - uspConsent, - pageUrl - }) - ); + return { + ttxSettings, + gdprConsent, + uspConsent, + pageUrl + } +} + +function _buildRequestGroups(ttxSettings, bidRequests) { + const bidRequestsComplete = bidRequests.map(_inferProduct); + const enableSRAMode = ttxSettings && ttxSettings.enableSRAMode; + const keyFunc = (enableSRAMode === true) ? _getSRAKey : _getMRAKey; + + return _groupBidRequests(bidRequestsComplete, keyFunc); +} + +function _groupBidRequests(bidRequests, keyFunc) { + const groupedRequests = {}; + + bidRequests.forEach((req) => { + const key = keyFunc(req); + + groupedRequests[key] = groupedRequests[key] || []; + groupedRequests[key].push(req); + }); + + return groupedRequests; +} + +function _getSRAKey(bidRequest) { + return `${bidRequest.params.siteId}:${bidRequest.params.productId}`; +} + +function _getMRAKey(bidRequest) { + return `${bidRequest.bidId}`; } // Infer the necessary data from valid bid for a minimal ttxRequest and create HTTP request -// NOTE: At this point, TTX only accepts request for a single impression -function _createServerRequest({bidRequest, gdprConsent = {}, uspConsent, pageUrl}) { +function _createServerRequest({ bidRequests, gdprConsent = {}, uspConsent, pageUrl, ttxSettings }) { const ttxRequest = {}; - const params = bidRequest.params; + const { siteId, test } = bidRequests[0].params; /* * Infer data for the request payload */ - ttxRequest.imp = [{}]; + ttxRequest.imp = []; - if (deepAccess(bidRequest, 'mediaTypes.banner')) { - ttxRequest.imp[0].banner = { - ..._buildBannerORTB(bidRequest) - } - } - - if (deepAccess(bidRequest, 'mediaTypes.video')) { - ttxRequest.imp[0].video = _buildVideoORTB(bidRequest); - } - - ttxRequest.imp[0].ext = { - ttx: { - prod: _getProduct(bidRequest) - } - }; + bidRequests.forEach((req) => { + ttxRequest.imp.push(_buildImpORTB(req)); + }); - ttxRequest.site = { id: params.siteId }; + ttxRequest.site = { id: siteId }; if (pageUrl) { ttxRequest.site.page = pageUrl; } - // Go ahead send the bidId in request to 33exchange so it's kept track of in the bid response and - // therefore in ad targetting process - ttxRequest.id = bidRequest.bidId; + ttxRequest.id = bidRequests[0].auctionId; if (gdprConsent.consentString) { - ttxRequest.user = setExtension( - ttxRequest.user, - 'consent', - gdprConsent.consentString - ) + ttxRequest.user = setExtensions(ttxRequest.user, { + 'consent': gdprConsent.consentString + }); } - if (Array.isArray(bidRequest.userIdAsEids) && bidRequest.userIdAsEids.length > 0) { - ttxRequest.user = setExtension( - ttxRequest.user, - 'eids', - bidRequest.userIdAsEids - ) + if (Array.isArray(bidRequests[0].userIdAsEids) && bidRequests[0].userIdAsEids.length > 0) { + ttxRequest.user = setExtensions(ttxRequest.user, { + 'eids': bidRequests[0].userIdAsEids + }); } - ttxRequest.regs = setExtension( - ttxRequest.regs, - 'gdpr', - Number(gdprConsent.gdprApplies) - ); + ttxRequest.regs = setExtensions(ttxRequest.regs, { + 'gdpr': Number(gdprConsent.gdprApplies) + }); if (uspConsent) { - ttxRequest.regs = setExtension( - ttxRequest.regs, - 'us_privacy', - uspConsent - ) + ttxRequest.regs = setExtensions(ttxRequest.regs, { + 'us_privacy': uspConsent + }); } ttxRequest.ext = { @@ -237,16 +289,14 @@ function _createServerRequest({bidRequest, gdprConsent = {}, uspConsent, pageUrl } }; - if (bidRequest.schain) { - ttxRequest.source = setExtension( - ttxRequest.source, - 'schain', - bidRequest.schain - ) + if (bidRequests[0].schain) { + ttxRequest.source = setExtensions(ttxRequest.source, { + 'schain': bidRequests[0].schain + }); } // Finally, set the openRTB 'test' param if this is to be a test bid - if (params.test === 1) { + if (test === 1) { ttxRequest.test = 1; } @@ -259,8 +309,7 @@ function _createServerRequest({bidRequest, gdprConsent = {}, uspConsent, pageUrl }; // Allow the ability to configure the HB endpoint for testing purposes. - const ttxSettings = config.getConfig('ttxSettings'); - const url = (ttxSettings && ttxSettings.url) || `${END_POINT}?guid=${params.siteId}`; + const url = (ttxSettings && ttxSettings.url) || `${END_POINT}?guid=${siteId}`; // Return the server request return { @@ -272,14 +321,36 @@ function _createServerRequest({bidRequest, gdprConsent = {}, uspConsent, pageUrl } // BUILD REQUESTS: SET EXTENSIONS -function setExtension(obj = {}, key, value) { - return Object.assign({}, obj, { - ext: Object.assign({}, obj.ext, { - [key]: value - }) +function setExtensions(obj = {}, extFields) { + return mergeDeep({}, obj, { + 'ext': extFields }); } +// BUILD REQUESTS: IMP +function _buildImpORTB(bidRequest) { + const imp = { + id: bidRequest.bidId, + ext: { + ttx: { + prod: deepAccess(bidRequest, 'params.productId') + } + } + }; + + if (deepAccess(bidRequest, 'mediaTypes.banner')) { + imp.banner = { + ..._buildBannerORTB(bidRequest) + } + } + + if (deepAccess(bidRequest, 'mediaTypes.video')) { + imp.video = _buildVideoORTB(bidRequest); + } + + return imp; +} + // BUILD REQUESTS: SIZE INFERENCE function _transformSizes(sizes) { if (isArray(sizes) && sizes.length === 2 && !isArray(sizes[0])) { @@ -297,6 +368,14 @@ function _getSize(size) { } // BUILD REQUESTS: PRODUCT INFERENCE +function _inferProduct(bidRequest) { + return mergeDeep({}, bidRequest, { + params: { + productId: _getProduct(bidRequest) + } + }); +} + function _getProduct(bidRequest) { const { params, mediaTypes } = bidRequest; @@ -367,7 +446,7 @@ function _buildVideoORTB(bidRequest) { const video = {} - const {w, h} = _getSize(videoParams.playerSize[0]); + const { w, h } = _getSize(videoParams.playerSize[0]); video.w = w; video.h = h; @@ -388,11 +467,11 @@ function _buildVideoORTB(bidRequest) { if (product === PRODUCT.INSTREAM) { video.startdelay = video.startdelay || 0; video.placement = 1; - }; + } // bidfloors if (typeof bidRequest.getFloor === 'function') { - const bidfloors = _getBidFloors(bidRequest, {w: video.w, h: video.h}, VIDEO); + const bidfloors = _getBidFloors(bidRequest, { w: video.w, h: video.h }, VIDEO); if (bidfloors) { Object.assign(video, { @@ -404,6 +483,7 @@ function _buildVideoORTB(bidRequest) { }); } } + return video; } @@ -556,54 +636,61 @@ function _isIframe() { } // **************************** INTERPRET RESPONSE ******************************** // -// NOTE: At this point, the response from 33exchange will only ever contain one bid -// i.e. the highest bid function interpretResponse(serverResponse, bidRequest) { - const bidResponses = []; - - // If there are bids, look at the first bid of the first seatbid (see NOTE above for assumption about ttx) - if (serverResponse.body.seatbid.length > 0 && serverResponse.body.seatbid[0].bid.length > 0) { - bidResponses.push(_createBidResponse(serverResponse.body)); - } - - return bidResponses; + const { seatbid, cur = 'USD' } = serverResponse.body; + + if (!isArray(seatbid)) { + return []; + } + + // Pick seats with valid bids and convert them into an Array of responses + // in format expected by Prebid Core + return seatbid + .filter((seat) => ( + isArray(seat.bid) && + seat.bid.length > 0 + )) + .reduce((acc, seat) => { + return acc.concat( + seat.bid.map((bid) => _createBidResponse(bid, cur)) + ); + }, []); } -// All this assumes that only one bid is ever returned by ttx -function _createBidResponse(response) { +function _createBidResponse(bid, cur) { const isADomainPresent = - response.seatbid[0].bid[0].adomain && response.seatbid[0].bid[0].adomain.length; - const bid = { - requestId: response.id, + bid.adomain && bid.adomain.length; + const bidResponse = { + requestId: bid.impid, bidderCode: BIDDER_CODE, - cpm: response.seatbid[0].bid[0].price, - width: response.seatbid[0].bid[0].w, - height: response.seatbid[0].bid[0].h, - ad: response.seatbid[0].bid[0].adm, - ttl: response.seatbid[0].bid[0].ttl || 60, - creativeId: response.seatbid[0].bid[0].crid, - mediaType: deepAccess(response.seatbid[0].bid[0], 'ext.ttx.mediaType', BANNER), - currency: response.cur, + cpm: bid.price, + width: bid.w, + height: bid.h, + ad: bid.adm, + ttl: bid.ttl || 60, + creativeId: bid.crid, + mediaType: deepAccess(bid, 'ext.ttx.mediaType', BANNER), + currency: cur, netRevenue: true } if (isADomainPresent) { - bid.meta = { - advertiserDomains: response.seatbid[0].bid[0].adomain + bidResponse.meta = { + advertiserDomains: bid.adomain }; } - if (bid.mediaType === VIDEO) { - const vastType = deepAccess(response.seatbid[0].bid[0], 'ext.ttx.vastType', 'xml'); + if (bidResponse.mediaType === VIDEO) { + const vastType = deepAccess(bid, 'ext.ttx.vastType', 'xml'); if (vastType === 'xml') { - bid.vastXml = bid.ad; + bidResponse.vastXml = bidResponse.ad; } else { - bid.vastUrl = bid.ad; + bidResponse.vastUrl = bidResponse.ad; } } - return bid; + return bidResponse; } // **************************** USER SYNC *************************** // diff --git a/modules/adagioBidAdapter.js b/modules/adagioBidAdapter.js index 264cf5f9fcb..3381f00ff8f 100644 --- a/modules/adagioBidAdapter.js +++ b/modules/adagioBidAdapter.js @@ -268,8 +268,6 @@ function getSite(bidderRequest) { } else if (refererInfo.stack && refererInfo.stack.length && refererInfo.stack[0]) { // important note check if refererInfo.stack[0] is 'thruly' because a `null` value // will be considered as "localhost" by the parseUrl function. - // As the isBidRequestValid returns false when it does not reach the referer - // this should never called. const url = parseUrl(refererInfo.stack[0]); domain = url.hostname; } @@ -308,7 +306,7 @@ function getElementFromTopWindow(element, currentWindow) { function autoDetectAdUnitElementIdFromGpt(adUnitCode) { const autoDetectedAdUnit = getGptSlotInfoForAdUnitCode(adUnitCode); - if (autoDetectedAdUnit && autoDetectedAdUnit.divId) { + if (autoDetectedAdUnit.divId) { return autoDetectedAdUnit.divId; } }; @@ -873,12 +871,6 @@ export const spec = { autoFillParams(bid); - if (!internal.getRefererInfo().reachedTop) { - logWarn(`${LOG_PREFIX} the main page url is unreachabled.`); - // internal.enqueue(debugData()); - return false; - } - if (!(bid.params.organizationId && bid.params.site && bid.params.placement)) { logWarn(`${LOG_PREFIX} at least one required param is missing.`); // internal.enqueue(debugData()); diff --git a/modules/adagioBidAdapter.md b/modules/adagioBidAdapter.md index 2779ced8cea..889822d9bd4 100644 --- a/modules/adagioBidAdapter.md +++ b/modules/adagioBidAdapter.md @@ -18,7 +18,7 @@ Below, the list of Adagio params and where they can be set. | ---------- | ------------- | ------------- | | siteId | x | | organizationId (obsolete) | | x -| site (obsolete) | | x +| site (obsolete) | | x | pagetype | x | x | environment | x | x | category | x | x diff --git a/modules/adbookpspBidAdapter.js b/modules/adbookpspBidAdapter.js index ca4795c574f..1b93d4fe1c6 100644 --- a/modules/adbookpspBidAdapter.js +++ b/modules/adbookpspBidAdapter.js @@ -363,7 +363,7 @@ function impBidsToPrebidBids( } const impToPrebidBid = - (bidderRequestBody, bidResponseCurrency, referrer, targetingMap) => (bid) => { + (bidderRequestBody, bidResponseCurrency, referrer, targetingMap) => (bid, bidIndex) => { try { const bidRequest = findBidRequest(bidderRequestBody, bid); @@ -377,7 +377,7 @@ const impToPrebidBid = let prebidBid = { ad: bid.adm, adId: bid.adid, - adserverTargeting: targetingMap[bid.impid], + adserverTargeting: targetingMap[bidIndex], adUnitCode: bidRequest.tagid, bidderRequestId: bidderRequestBody.id, bidId: bid.id, @@ -408,6 +408,9 @@ const impToPrebidBid = }; } + if (deepAccess(bid, 'ext.pa_win') === true) { + prebidBid.auctionWinner = true; + } return prebidBid; } catch (error) { logError(`${BIDDER_CODE}: Error while building bid`, error); @@ -429,29 +432,43 @@ function buildTargetingMap(bids) { const values = impIds.reduce((result, id) => { result[id] = { lineItemIds: [], + orderIds: [], dealIds: [], adIds: [], + adAndOrderIndexes: [] }; return result; }, {}); - bids.forEach((bid) => { - values[bid.impid].lineItemIds.push(bid.ext.liid); - values[bid.impid].dealIds.push(bid.dealid); - values[bid.impid].adIds.push(bid.adid); + bids.forEach((bid, bidIndex) => { + let impId = bid.impid; + values[impId].lineItemIds.push(bid.ext.liid); + values[impId].dealIds.push(bid.dealid); + values[impId].adIds.push(bid.adid); + + if (deepAccess(bid, 'ext.ordid')) { + values[impId].orderIds.push(bid.ext.ordid); + bid.ext.ordid.split(TARGETING_VALUE_SEPARATOR).forEach((ordid, ordIndex) => { + let adIdIndex = values[impId].adIds.indexOf(bid.adid); + values[impId].adAndOrderIndexes.push(adIdIndex + '_' + ordIndex) + }) + } }); const targetingMap = {}; - for (const id of impIds) { - targetingMap[id] = { + bids.forEach((bid, bidIndex) => { + let id = bid.impid; + + targetingMap[bidIndex] = { hb_liid_adbookpsp: values[id].lineItemIds.join(TARGETING_VALUE_SEPARATOR), hb_deal_adbookpsp: values[id].dealIds.join(TARGETING_VALUE_SEPARATOR), + hb_ad_ord_adbookpsp: values[id].adAndOrderIndexes.join(TARGETING_VALUE_SEPARATOR), hb_adid_c_adbookpsp: values[id].adIds.join(TARGETING_VALUE_SEPARATOR), + hb_ordid_adbookpsp: values[id].orderIds.join(TARGETING_VALUE_SEPARATOR), }; - } - + }) return targetingMap; } diff --git a/modules/addefendBidAdapter.js b/modules/addefendBidAdapter.js index 18cafe829b5..dcc453ef35a 100644 --- a/modules/addefendBidAdapter.js +++ b/modules/addefendBidAdapter.js @@ -19,6 +19,7 @@ export const spec = { v: $$PREBID_GLOBAL$$.version, auctionId: false, pageId: false, + gdpr_applies: bidderRequest.gdprConsent && bidderRequest.gdprConsent.gdprApplies ? bidderRequest.gdprConsent.gdprApplies : 'true', gdpr_consent: bidderRequest.gdprConsent && bidderRequest.gdprConsent.consentString ? bidderRequest.gdprConsent.consentString : '', referer: bidderRequest.refererInfo.referer, bids: [], diff --git a/modules/adfBidAdapter.js b/modules/adfBidAdapter.js index f7727a168b8..f0425a174ff 100644 --- a/modules/adfBidAdapter.js +++ b/modules/adfBidAdapter.js @@ -58,7 +58,11 @@ export const spec = { aliases: BIDDER_ALIAS, gvlid: GVLID, supportedMediaTypes: [ NATIVE, BANNER, VIDEO ], - isBidRequestValid: bid => !!bid.params.mid, + isBidRequestValid: (bid) => { + const params = bid.params || {}; + const { mid, inv, mname } = params; + return !!(mid || (inv && mname)); + }, buildRequests: (validBidRequests, bidderRequest) => { let app, site; @@ -104,12 +108,19 @@ export const spec = { }) : {}; const bidfloor = floorInfo.floor; const bidfloorcur = floorInfo.currency; + const { mid, inv, mname } = bid.params; const imp = { id: id + 1, - tagid: bid.params.mid, + tagid: mid, bidfloor, - bidfloorcur + bidfloorcur, + ext: { + bidder: { + inv, + mname + } + } }; const assets = _map(bid.nativeParams, (bidParams, key) => { @@ -153,9 +164,6 @@ export const spec = { assets } }; - - bid.mediaType = NATIVE; - return imp; } const bannerParams = deepAccess(bid, 'mediaTypes.banner'); @@ -172,18 +180,14 @@ export const spec = { imp.banner = { format }; - bid.mediaType = BANNER; - - return imp; } const videoParams = deepAccess(bid, 'mediaTypes.video'); if (videoParams) { imp.video = videoParams; - bid.mediaType = VIDEO; - - return imp; } + + return imp; }); const request = { @@ -243,6 +247,7 @@ export const spec = { return bids.map((bid, id) => { const bidResponse = bidResponses[id]; if (bidResponse) { + const mediaType = deepAccess(bidResponse, 'ext.prebid.type'); const result = { requestId: bid.bidId, cpm: bidResponse.price, @@ -250,12 +255,12 @@ export const spec = { ttl: 360, netRevenue: bid.netRevenue === 'net', currency: cur, - mediaType: bid.mediaType, + mediaType, width: bidResponse.w, height: bidResponse.h, dealId: bidResponse.dealid, meta: { - mediaType: bid.mediaType, + mediaType, advertiserDomains: bidResponse.adomain } }; @@ -263,10 +268,10 @@ export const spec = { if (bidResponse.native) { result.native = parseNative(bidResponse); } else { - result[ bid.mediaType === VIDEO ? 'vastXml' : 'ad' ] = bidResponse.adm; + result[ mediaType === VIDEO ? 'vastXml' : 'ad' ] = bidResponse.adm; } - if (!bid.renderer && bid.mediaType === VIDEO && deepAccess(bid, 'mediaTypes.video.context') === 'outstream') { + if (!bid.renderer && mediaType === VIDEO && deepAccess(bid, 'mediaTypes.video.context') === 'outstream') { result.renderer = Renderer.install({id: bid.bidId, url: OUTSTREAM_RENDERER_URL, adUnitCode: bid.adUnitCode}); result.renderer.setRender(renderer); } diff --git a/modules/adgenerationBidAdapter.js b/modules/adgenerationBidAdapter.js index 2696915ea0a..4dd320d3f24 100644 --- a/modules/adgenerationBidAdapter.js +++ b/modules/adgenerationBidAdapter.js @@ -1,4 +1,4 @@ -import { tryAppendQueryString, getBidIdParameter } from '../src/utils.js'; +import {tryAppendQueryString, getBidIdParameter} from '../src/utils.js'; import {registerBidder} from '../src/adapters/bidderFactory.js'; import {BANNER, NATIVE} from '../src/mediaTypes.js'; import {config} from '../src/config.js'; @@ -25,7 +25,7 @@ export const spec = { * @return ServerRequest Info describing the request to the server. */ buildRequests: function (validBidRequests, bidderRequest) { - const ADGENE_PREBID_VERSION = '1.1.0'; + const ADGENE_PREBID_VERSION = '1.2.0'; let serverRequests = []; for (let i = 0, len = validBidRequests.length; i < len; i++) { const validReq = validBidRequests[i]; @@ -118,13 +118,25 @@ export const spec = { function createAd(body, bidRequest) { let ad = body.ad; if (body.vastxml && body.vastxml.length > 0) { - ad = `
${createAPVTag()}${insertVASTMethod(bidRequest.bidId, body.vastxml)}`; + if (isUpperBillboard(body)) { + const marginTop = bidRequest.params.marginTop ? bidRequest.params.marginTop : '0'; + ad = `${createADGBrowserMTag()}${insertVASTMethodForADGBrowserM(body.vastxml, marginTop)}`; + } else { + ad = `
${createAPVTag()}${insertVASTMethodForAPV(bidRequest.bidId, body.vastxml)}`; + } } ad = appendChildToBody(ad, body.beacon); if (removeWrapper(ad)) return removeWrapper(ad); return ad; } +function isUpperBillboard(body) { + if (body.location_params && body.location_params.option && body.location_params.option.ad_type) { + return body.location_params.option.ad_type === 'upper_billboard'; + } + return false; +} + function isNative(body) { if (!body) return false; return body.native_ad && body.native_ad.assets.length > 0; @@ -190,7 +202,12 @@ function createAPVTag() { return apvScript.outerHTML; } -function insertVASTMethod(targetId, vastXml) { +function createADGBrowserMTag() { + const ADGBrowserMURL = 'https://i.socdm.com/sdk/js/adg-browser-m.js'; + return ``; +} + +function insertVASTMethodForAPV(targetId, vastXml) { let apvVideoAdParam = { s: targetId }; @@ -200,6 +217,13 @@ function insertVASTMethod(targetId, vastXml) { return script.outerHTML; } +function insertVASTMethodForADGBrowserM(vastXml, marginTop) { + const script = document.createElement(`script`); + script.type = 'text/javascript'; + script.innerHTML = `window.ADGBrowserM.init({vastXml: '${vastXml.replace(/\r?\n/g, '')}', marginTop: '${marginTop}'});`; + return script.outerHTML; +} + /** * * @param ad diff --git a/modules/adhashBidAdapter.js b/modules/adhashBidAdapter.js index c94a4e35efd..6a8c98650c0 100644 --- a/modules/adhashBidAdapter.js +++ b/modules/adhashBidAdapter.js @@ -6,7 +6,7 @@ const VERSION = '1.0'; export const spec = { code: 'adhash', - url: 'https://bidder.adhash.org/rtb?version=' + VERSION + '&prebid=true', + url: 'https://bidder.adhash.com/rtb?version=' + VERSION + '&prebid=true', supportedMediaTypes: [ BANNER ], isBidRequestValid: (bid) => { @@ -37,7 +37,7 @@ export const spec = { var size = validBidRequests[i].sizes[index].join('x'); bidRequests.push({ method: 'POST', - url: url, + url: url + '&publisher=' + validBidRequests[i].params.publisherId, bidRequest: validBidRequests[i], data: { timezone: new Date().getTimezoneOffset() / 60, @@ -87,7 +87,7 @@ export const spec = { cpm: responseBody.creatives[0].costEUR, ad: `
- + `, width: request.bidRequest.sizes[0][0], height: request.bidRequest.sizes[0][1], diff --git a/modules/adheseBidAdapter.js b/modules/adheseBidAdapter.js index 88f3e0e0e4f..145b5605bc2 100644 --- a/modules/adheseBidAdapter.js +++ b/modules/adheseBidAdapter.js @@ -2,6 +2,7 @@ import { registerBidder } from '../src/adapters/bidderFactory.js'; import { BANNER, VIDEO } from '../src/mediaTypes.js'; +import { config } from '../src/config.js'; const BIDDER_CODE = 'adhese'; const GVLID = 553; @@ -20,11 +21,15 @@ export const spec = { if (validBidRequests.length === 0) { return null; } + const { gdprConsent, refererInfo } = bidderRequest; + const adheseConfig = config.getConfig('adhese'); const gdprParams = (gdprConsent && gdprConsent.consentString) ? { xt: [gdprConsent.consentString] } : {}; const refererParams = (refererInfo && refererInfo.referer) ? { xf: [base64urlEncode(refererInfo.referer)] } : {}; - const commonParams = { ...gdprParams, ...refererParams }; + const globalCustomParams = (adheseConfig && adheseConfig.globalTargets) ? cleanTargets(adheseConfig.globalTargets) : {}; + const commonParams = { ...globalCustomParams, ...gdprParams, ...refererParams }; + const vastContentAsUrl = !(adheseConfig && adheseConfig.vastContentAsUrl == false); const slots = validBidRequests.map(bid => ({ slotname: bidToSlotName(bid), @@ -34,7 +39,7 @@ export const spec = { const payload = { slots: slots, parameters: commonParams, - vastContentAsUrl: true, + vastContentAsUrl: vastContentAsUrl, user: { ext: { eids: getEids(validBidRequests), diff --git a/modules/adkernelBidAdapter.js b/modules/adkernelBidAdapter.js index 7c685a64255..c23eca2f96a 100644 --- a/modules/adkernelBidAdapter.js +++ b/modules/adkernelBidAdapter.js @@ -1,6 +1,6 @@ import { isStr, isArray, isPlainObject, deepSetValue, isNumber, deepAccess, getAdUnitSizes, parseGPTSingleSizeArrayToRtbSize, - cleanObj, contains, getDNT, parseUrl, createTrackPixelHtml, _each, isArrayOfNums + cleanObj, contains, getDNT, parseUrl, createTrackPixelHtml, _each, isArrayOfNums, mergeDeep, isEmpty, inIframe } from '../src/utils.js'; import {BANNER, NATIVE, VIDEO} from '../src/mediaTypes.js'; import {registerBidder} from '../src/adapters/bidderFactory.js'; @@ -75,7 +75,10 @@ export const spec = { {code: 'denakop'}, {code: 'rtbanalytica'}, {code: 'unibots'}, - {code: 'ergadx'} + {code: 'catapultx'}, + {code: 'ergadx'}, + {code: 'turktelekom'}, + {code: 'felixads'} ], supportedMediaTypes: [BANNER, VIDEO, NATIVE], @@ -101,17 +104,16 @@ export const spec = { * @returns {ServerRequest[]} */ buildRequests: function (bidRequests, bidderRequest) { - let impDispatch = dispatchImps(bidRequests, bidderRequest.refererInfo); + let impGroups = groupImpressionsByHostZone(bidRequests, bidderRequest.refererInfo); let requests = []; let schain = bidRequests[0].schain; - Object.keys(impDispatch).forEach(host => { - Object.keys(impDispatch[host]).forEach(zoneId => { - const request = buildRtbRequest(impDispatch[host][zoneId], bidderRequest, schain); - requests.push({ - method: 'POST', - url: `https://${host}/hb?zone=${zoneId}&v=${VERSION}`, - data: JSON.stringify(request) - }); + _each(impGroups, impGroup => { + let {host, zoneId, imps} = impGroup; + const request = buildRtbRequest(imps, bidderRequest, schain); + requests.push({ + method: 'POST', + url: `https://${host}/hb?zone=${zoneId}&v=${VERSION}`, + data: JSON.stringify(request) }); }); return requests; @@ -207,17 +209,19 @@ registerBidder(spec); * @param bidRequests {BidRequest[]} * @param refererInfo {refererInfo} */ -function dispatchImps(bidRequests, refererInfo) { +function groupImpressionsByHostZone(bidRequests, refererInfo) { let secure = (refererInfo && refererInfo.referer.indexOf('https:') === 0); - return bidRequests.map(bidRequest => buildImp(bidRequest, secure)) - .reduce((acc, curr, index) => { - let bidRequest = bidRequests[index]; - let {zoneId, host} = bidRequest.params; - acc[host] = acc[host] || {}; - acc[host][zoneId] = acc[host][zoneId] || []; - acc[host][zoneId].push(curr); - return acc; - }, {}); + return Object.values( + bidRequests.map(bidRequest => buildImp(bidRequest, secure)) + .reduce((acc, curr, index) => { + let bidRequest = bidRequests[index]; + let {zoneId, host} = bidRequest.params; + let key = `${host}_${zoneId}`; + acc[key] = acc[key] || {host: host, zoneId: zoneId, imps: []}; + acc[key].imps.push(curr); + return acc; + }, {}) + ); } function getBidFloor(bid, mediaType, sizes) { @@ -363,57 +367,142 @@ function getAllowedSyncMethod(bidderCode) { } /** - * Builds complete rtb request - * @param imps {Object} Collection of rtb impressions - * @param bidderRequest {BidderRequest} - * @param schain {Object=} Supply chain config - * @return {Object} Complete rtb request + * Create device object from fpd and host-collected data + * @param fpd {Object} + * @returns {{device: Object}} */ -function buildRtbRequest(imps, bidderRequest, schain) { - let {bidderCode, gdprConsent, auctionId, refererInfo, timeout, uspConsent} = bidderRequest; - let coppa = config.getConfig('coppa'); - let req = { - 'id': auctionId, - 'imp': imps, - 'site': createSite(refererInfo), - 'at': 1, - 'device': { - 'ip': 'caller', - 'ipv6': 'caller', - 'ua': 'caller', - 'js': 1, - 'language': getLanguage() - }, - 'tmax': parseInt(timeout) - }; +function makeDevice(fpd) { + let device = mergeDeep({ + 'ip': 'caller', + 'ipv6': 'caller', + 'ua': 'caller', + 'js': 1, + 'language': getLanguage() + }, fpd.device || {}); if (getDNT()) { - req.device.dnt = 1; + device.dnt = 1; + } + return {device: device}; +} + +/** + * Create site or app description object + * @param bidderRequest {BidderRequest} + * @param fpd {Object} + * @returns {{site: Object}|{app: Object}} + */ +function makeSiteOrApp(bidderRequest, fpd) { + let {refererInfo} = bidderRequest; + let appConfig = config.getConfig('app'); + if (isEmpty(appConfig)) { + return {site: createSite(refererInfo, fpd)} + } else { + return {app: appConfig}; } +} + +/** + * Create user description object + * @param bidderRequest {BidderRequest} + * @param fpd {Object} + * @returns {{user: Object} | undefined} + */ +function makeUser(bidderRequest, fpd) { + let {gdprConsent} = bidderRequest; + let user = fpd.user || {}; + if (gdprConsent && gdprConsent.consentString !== undefined) { + deepSetValue(user, 'ext.consent', gdprConsent.consentString); + } + let eids = getExtendedUserIds(bidderRequest); + if (eids) { + deepSetValue(user, 'ext.eids', eids); + } + if (!isEmpty(user)) { return {user: user}; } +} + +/** + * Create privacy regulations object + * @param bidderRequest {BidderRequest} + * @returns {{regs: Object} | undefined} + */ +function makeRegulations(bidderRequest) { + let {gdprConsent, uspConsent} = bidderRequest; + let regs = {}; if (gdprConsent) { if (gdprConsent.gdprApplies !== undefined) { - deepSetValue(req, 'regs.ext.gdpr', ~~gdprConsent.gdprApplies); - } - if (gdprConsent.consentString !== undefined) { - deepSetValue(req, 'user.ext.consent', gdprConsent.consentString); + deepSetValue(regs, 'regs.ext.gdpr', ~~gdprConsent.gdprApplies); } } if (uspConsent) { - deepSetValue(req, 'regs.ext.us_privacy', uspConsent); + deepSetValue(regs, 'regs.ext.us_privacy', uspConsent); + } + if (config.getConfig('coppa')) { + deepSetValue(regs, 'regs.coppa', 1); } - if (coppa) { - deepSetValue(req, 'regs.coppa', 1); + if (!isEmpty(regs)) { + return regs; } +} + +/** + * Create top-level request object + * @param bidderRequest {BidderRequest} + * @param imps {Object} Impressions + * @param fpd {Object} First party data + * @returns + */ +function makeBaseRequest(bidderRequest, imps, fpd) { + let {auctionId, timeout} = bidderRequest; + let request = { + 'id': auctionId, + 'imp': imps, + 'at': 1, + 'tmax': parseInt(timeout) + }; + if (!isEmpty(fpd.bcat)) { + request.bcat = fpd.bcat; + } + if (!isEmpty(fpd.badv)) { + request.badv = fpd.badv; + } + return request; +} + +/** + * Initialize sync capabilities + * @param bidderRequest {BidderRequest} + */ +function makeSyncInfo(bidderRequest) { + let {bidderCode} = bidderRequest; let syncMethod = getAllowedSyncMethod(bidderCode); if (syncMethod) { - deepSetValue(req, 'ext.adk_usersync', syncMethod); + let res = {}; + deepSetValue(res, 'ext.adk_usersync', syncMethod); + return res; } +} + +/** + * Builds complete rtb request + * @param imps {Object} Collection of rtb impressions + * @param bidderRequest {BidderRequest} + * @param schain {Object=} Supply chain config + * @return {Object} Complete rtb request + */ +function buildRtbRequest(imps, bidderRequest, schain) { + let fpd = config.getConfig('ortb2') || {}; + + let req = mergeDeep( + makeBaseRequest(bidderRequest, imps, fpd), + makeDevice(fpd), + makeSiteOrApp(bidderRequest, fpd), + makeUser(bidderRequest, fpd), + makeRegulations(bidderRequest), + makeSyncInfo(bidderRequest) + ); if (schain) { deepSetValue(req, 'source.ext.schain', schain); } - let eids = getExtendedUserIds(bidderRequest); - if (eids) { - deepSetValue(req, 'user.ext.eids', eids); - } return req; } @@ -429,18 +518,17 @@ function getLanguage() { /** * Creates site description object */ -function createSite(refInfo) { +function createSite(refInfo, fpd) { let url = parseUrl(refInfo.referer); let site = { 'domain': url.hostname, 'page': `${url.protocol}://${url.hostname}${url.pathname}` }; - if (self === top && document.referrer) { + mergeDeep(site, fpd.site); + if (!inIframe() && document.referrer) { site.ref = document.referrer; - } - let keywords = document.getElementsByTagName('meta')['keywords']; - if (keywords && keywords.content) { - site.keywords = keywords.content; + } else { + delete site.ref; } return site; } diff --git a/modules/adlivetechBidAdapter.md b/modules/adlivetechBidAdapter.md new file mode 100644 index 00000000000..612e669ea1a --- /dev/null +++ b/modules/adlivetechBidAdapter.md @@ -0,0 +1,61 @@ +# Overview + +Module Name: Adlivetech Bidder Adapter +Module Type: Bidder Adapter +Maintainer: grid-tech@themediagrid.com + +# Description + +Module that connects to Grid demand source to fetch bids. +The adapter is GDPR compliant and supports banner and video (instream and outstream). + +# Test Parameters +``` + var adUnits = [ + { + code: 'test-div', + sizes: [[300, 250]], + bids: [ + { + bidder: "adlivetech", + params: { + uid: '1', + bidFloor: 0.5 + } + } + ] + },{ + code: 'test-div', + sizes: [[728, 90]], + bids: [ + { + bidder: "adlivetech", + params: { + uid: 2, + keywords: { + brandsafety: ['disaster'], + topic: ['stress', 'fear'] + } + } + } + ] + }, + { + code: 'test-div', + sizes: [[728, 90]], + mediaTypes: { video: { + context: 'instream', + playerSize: [728, 90], + mimes: ['video/mp4'] + }, + bids: [ + { + bidder: "adlivetech", + params: { + uid: 11 + } + } + ] + } + ]; +``` diff --git a/modules/adlooxAdServerVideo.js b/modules/adlooxAdServerVideo.js index 7305283039c..bd715cb34f3 100644 --- a/modules/adlooxAdServerVideo.js +++ b/modules/adlooxAdServerVideo.js @@ -9,7 +9,7 @@ import { registerVideoSupport } from '../src/adServerManager.js'; import { command as analyticsCommand, COMMAND } from './adlooxAnalyticsAdapter.js'; import { ajax } from '../src/ajax.js'; -import { EVENTS } from '../src/constants.json'; +import CONSTANTS from '../src/constants.json'; import { targeting } from '../src/targeting.js'; import { logInfo, isFn, logError, isPlainObject, isStr, isBoolean, deepSetValue, deepClone, timestamp, logWarn } from '../src/utils.js'; @@ -74,7 +74,7 @@ function track(options, callback) { bid.ext.adloox.video.adserver = false; analyticsCommand(COMMAND.TRACK, { - eventType: EVENTS.BID_WON, + eventType: CONSTANTS.EVENTS.BID_WON, args: bid }); } diff --git a/modules/adlooxAnalyticsAdapter.js b/modules/adlooxAnalyticsAdapter.js index 6ea1df1b72c..781b8db830a 100644 --- a/modules/adlooxAnalyticsAdapter.js +++ b/modules/adlooxAnalyticsAdapter.js @@ -9,7 +9,7 @@ import adapter from '../src/AnalyticsAdapter.js'; import { loadExternalScript } from '../src/adloader.js'; import { auctionManager } from '../src/auctionManager.js'; import { AUCTION_COMPLETED } from '../src/auction.js'; -import { EVENTS } from '../src/constants.json'; +import CONSTANTS from '../src/constants.json'; import find from 'core-js-pure/features/array/find.js'; import { deepAccess, logInfo, isPlainObject, logError, isStr, isNumber, getGptSlotInfoForAdUnitCode, @@ -199,9 +199,9 @@ analyticsAdapter.url = function(url, args, bid) { return url + a2qs(args); } -analyticsAdapter[`handle_${EVENTS.AUCTION_END}`] = function(auctionDetails) { +analyticsAdapter[`handle_${CONSTANTS.EVENTS.AUCTION_END}`] = function(auctionDetails) { if (!(auctionDetails.auctionStatus == AUCTION_COMPLETED && auctionDetails.bidsReceived.length > 0)) return; - analyticsAdapter[`handle_${EVENTS.AUCTION_END}`] = NOOP; + analyticsAdapter[`handle_${CONSTANTS.EVENTS.AUCTION_END}`] = NOOP; logMessage(MODULE, 'preloading verification JS'); @@ -214,7 +214,7 @@ analyticsAdapter[`handle_${EVENTS.AUCTION_END}`] = function(auctionDetails) { insertElement(link); } -analyticsAdapter[`handle_${EVENTS.BID_WON}`] = function(bid) { +analyticsAdapter[`handle_${CONSTANTS.EVENTS.BID_WON}`] = function(bid) { if (deepAccess(bid, 'ext.adloox.video.adserver')) { logMessage(MODULE, `measuring '${bid.mediaType}' ad unit code '${bid.adUnitCode}' via Ad Server module`); return; diff --git a/modules/admanBidAdapter.js b/modules/admanBidAdapter.js index e02a1a9df04..666e9aea309 100644 --- a/modules/admanBidAdapter.js +++ b/modules/admanBidAdapter.js @@ -1,10 +1,11 @@ import {registerBidder} from '../src/adapters/bidderFactory.js'; import { BANNER, NATIVE, VIDEO } from '../src/mediaTypes.js'; import { isFn, deepAccess, logMessage } from '../src/utils.js'; +import {config} from '../src/config.js'; const BIDDER_CODE = 'adman'; const AD_URL = 'https://pub.admanmedia.com/?c=o&m=multi'; -const URL_SYNC = 'https://pub.admanmedia.com/?c=o&m=sync'; +const URL_SYNC = 'https://pub.admanmedia.com'; function isBidResponseValid(bid) { if (!bid.requestId || !bid.cpm || !bid.creativeId || @@ -108,6 +109,7 @@ export const spec = { } if (bid.userId) { getUserId(placement.eids, bid.userId.uid2 && bid.userId.uid2.id, 'uidapi.com'); + getUserId(placement.eids, bid.userId.lotamePanoramaId, 'lotame.com'); } if (traff === VIDEO) { placement.playerSize = bid.mediaTypes[VIDEO].playerSize; @@ -151,19 +153,24 @@ export const spec = { }, getUserSyncs: (syncOptions, serverResponses, gdprConsent, uspConsent) => { - let syncUrl = URL_SYNC + let syncType = syncOptions.iframeEnabled ? 'iframe' : 'image'; + let syncUrl = URL_SYNC + `/${syncType}?pbjs=1`; if (gdprConsent && gdprConsent.consentString) { if (typeof gdprConsent.gdprApplies === 'boolean') { syncUrl += `&gdpr=${Number(gdprConsent.gdprApplies)}&gdpr_consent=${gdprConsent.consentString}`; } else { - syncUrl += `&gdpr==0&gdpr_consent=${gdprConsent.consentString}`; + syncUrl += `&gdpr=0&gdpr_consent=${gdprConsent.consentString}`; } } if (uspConsent && uspConsent.consentString) { syncUrl += `&ccpa_consent=${uspConsent.consentString}`; } + + const coppa = config.getConfig('coppa') ? 1 : 0; + syncUrl += `&coppa=${coppa}`; + return [{ - type: 'image', + type: syncType, url: syncUrl }]; } diff --git a/modules/admixerBidAdapter.js b/modules/admixerBidAdapter.js index e136dfcfbdb..dfb76a03804 100644 --- a/modules/admixerBidAdapter.js +++ b/modules/admixerBidAdapter.js @@ -1,14 +1,15 @@ import { logError } from '../src/utils.js'; import {registerBidder} from '../src/adapters/bidderFactory.js'; import {config} from '../src/config.js'; +import {BANNER, VIDEO, NATIVE} from '../src/mediaTypes.js'; const BIDDER_CODE = 'admixer'; -const ALIASES = ['go2net', 'adblender', 'adsyield']; +const ALIASES = ['go2net', 'adblender', 'adsyield', 'futureads']; const ENDPOINT_URL = 'https://inv-nets.admixer.net/prebid.1.2.aspx'; export const spec = { code: BIDDER_CODE, aliases: ALIASES, - supportedMediaTypes: ['banner', 'video'], + supportedMediaTypes: [BANNER, VIDEO, NATIVE], /** * Determines whether or not the given bid request is valid. */ @@ -19,9 +20,20 @@ export const spec = { * Make a server request from the list of BidRequests. */ buildRequests: function (validRequest, bidderRequest) { + let w; + let docRef; + do { + w = w ? w.parent : window; + try { + docRef = w.document.referrer; + } catch (e) { + break; + } + } while (w !== window.top); const payload = { imps: [], ortb2: config.getConfig('ortb2'), + docReferrer: docRef, }; let endpointUrl; if (bidderRequest) { diff --git a/modules/adnuntiusBidAdapter.js b/modules/adnuntiusBidAdapter.js index a1dff3d258d..f05cd9f9f32 100644 --- a/modules/adnuntiusBidAdapter.js +++ b/modules/adnuntiusBidAdapter.js @@ -37,7 +37,8 @@ const handleMeta = function () { } const getUsi = function (meta, ortb2, bidderRequest) { - const usi = (meta !== null) ? meta.usi : false; + let usi = (meta !== null && meta.usi) ? meta.usi : false; + if (ortb2 && ortb2.user && ortb2.user.id) { usi = ortb2.user.id } return usi } @@ -55,6 +56,8 @@ export const spec = { const requests = []; const request = []; const ortb2 = config.getConfig('ortb2'); + const bidderConfig = config.getConfig(); + const adnMeta = handleMeta() const usi = getUsi(adnMeta, ortb2, bidderRequest) const segments = getSegmentsFromOrtb(ortb2); @@ -67,7 +70,7 @@ export const spec = { if (gdprApplies !== undefined) request.push('consentString=' + consentString); if (segments.length > 0) request.push('segments=' + segments.join(',')); if (usi) request.push('userId=' + usi); - + if (bidderConfig.useCookie === false) request.push('noCookies=true') for (var i = 0; i < validBidRequests.length; i++) { const bid = validBidRequests[i] const network = bid.params.network || 'network'; diff --git a/modules/adomikAnalyticsAdapter.js b/modules/adomikAnalyticsAdapter.js index 5bbee86df54..e81f6e50054 100644 --- a/modules/adomikAnalyticsAdapter.js +++ b/modules/adomikAnalyticsAdapter.js @@ -12,6 +12,9 @@ const bidRequested = CONSTANTS.EVENTS.BID_REQUESTED; const bidResponse = CONSTANTS.EVENTS.BID_RESPONSE; const bidWon = CONSTANTS.EVENTS.BID_WON; const bidTimeout = CONSTANTS.EVENTS.BID_TIMEOUT; +const ua = navigator.userAgent; + +var _sampled = true; let adomikAdapter = Object.assign(adapter({}), { @@ -47,7 +50,7 @@ let adomikAdapter = Object.assign(adapter({}), type: 'request', event: { bidder: bid.bidder.toUpperCase(), - placementCode: bid.placementCode + placementCode: bid.adUnitCode } }); }); @@ -67,13 +70,20 @@ adomikAdapter.initializeBucketEvents = function() { adomikAdapter.bucketEvents = []; } +adomikAdapter.maxPartLength = function () { + return (ua.includes(' MSIE ')) ? 1600 : 60000; +}; + adomikAdapter.sendTypedEvent = function() { const groupedTypedEvents = adomikAdapter.buildTypedEvents(); const bulkEvents = { + testId: adomikAdapter.currentContext.testId, + testValue: adomikAdapter.currentContext.testValue, uid: adomikAdapter.currentContext.uid, ahbaid: adomikAdapter.currentContext.id, hostname: window.location.hostname, + sampling: adomikAdapter.currentContext.sampling, eventsByPlacementCode: groupedTypedEvents.map(function(typedEventsByType) { let sizes = []; const eventKeys = ['request', 'response', 'winner']; @@ -108,9 +118,10 @@ adomikAdapter.sendTypedEvent = function() { // Encode object in base64 const encodedBuf = window.btoa(stringBulkEvents); - // Create final url and split it in 1600 characters max (+endpoint length) + // Create final url and split it (+endpoint length) const encodedUri = encodeURIComponent(encodedBuf); - const splittedUrl = encodedUri.match(/.{1,1600}/g); + const maxLength = adomikAdapter.maxPartLength(); + const splittedUrl = encodedUri.match(new RegExp(`.{1,${maxLength}}`, 'g')); splittedUrl.forEach((split, i) => { const partUrl = `${split}&id=${adomikAdapter.currentContext.id}&part=${i}&on=${splittedUrl.length - 1}`; @@ -120,8 +131,10 @@ adomikAdapter.sendTypedEvent = function() { }; adomikAdapter.sendWonEvent = function (wonEvent) { + let keyValues = { testId: adomikAdapter.currentContext.testId, testValue: adomikAdapter.currentContext.testValue } + wonEvent = {...wonEvent, ...keyValues} const stringWonEvent = JSON.stringify(wonEvent) - logInfo('Won event sent to adomik prebid analytic ' + wonEvent); + logInfo('Won event sent to adomik prebid analytic ' + stringWonEvent); // Encode object in base64 const encodedBuf = window.btoa(stringWonEvent); @@ -193,17 +206,28 @@ adomikAdapter.adapterEnableAnalytics = adomikAdapter.enableAnalytics; adomikAdapter.enableAnalytics = function (config) { adomikAdapter.currentContext = {}; - const initOptions = config.options; - if (initOptions) { - adomikAdapter.currentContext = { - uid: initOptions.id, - url: initOptions.url, - id: '', - timeouted: false, + + _sampled = typeof config === 'undefined' || + typeof config.sampling === 'undefined' || + Math.random() < parseFloat(config.sampling); + + if (_sampled) { + if (initOptions) { + adomikAdapter.currentContext = { + uid: initOptions.id, + url: initOptions.url, + testId: initOptions.testId, + testValue: initOptions.testValue, + id: '', + timeouted: false, + sampling: config.sampling + } + logInfo('Adomik Analytics enabled with config', initOptions); + adomikAdapter.adapterEnableAnalytics(config); } - logInfo('Adomik Analytics enabled with config', initOptions); - adomikAdapter.adapterEnableAnalytics(config); + } else { + logInfo('Adomik Analytics ignored for sampling', config.sampling); } }; diff --git a/modules/adplusBidAdapter.js b/modules/adplusBidAdapter.js new file mode 100644 index 00000000000..c001781a792 --- /dev/null +++ b/modules/adplusBidAdapter.js @@ -0,0 +1,203 @@ +import { registerBidder } from '../src/adapters/bidderFactory.js'; +import * as utils from '../src/utils.js'; +import { BANNER } from '../src/mediaTypes.js'; +import { getStorageManager } from '../src/storageManager.js'; + +// #region Constants +export const BIDDER_CODE = 'adplus'; +export const ADPLUS_ENDPOINT = 'https://ssp.ad-plus.com.tr/server/headerBidding'; +export const DGID_CODE = 'adplus_dg_id'; +export const SESSION_CODE = 'adplus_s_id'; +export const storage = getStorageManager(undefined, BIDDER_CODE); +const COOKIE_EXP = 1000 * 60 * 60 * 24; // 1 day +// #endregion + +// #region Helpers +export function isValidUuid (uuid) { + return /^[0-9a-f]{8}-[0-9a-f]{4}-[1-5][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i.test( + uuid + ); +} + +function getSessionId() { + let sid = storage.cookiesAreEnabled() && storage.getCookie(SESSION_CODE); + + if ( + !sid || !isValidUuid(sid) + ) { + sid = utils.generateUUID(); + setSessionId(sid); + } + + return sid; +} + +function setSessionId(sid) { + if (storage.cookiesAreEnabled()) { + const expires = new Date(Date.now() + COOKIE_EXP).toISOString(); + + storage.setCookie(SESSION_CODE, sid, expires); + } +} +// #endregion + +// #region Bid request validation +function isBidRequestValid(bid) { + if (!bid) { + utils.logError(BIDDER_CODE, 'bid, can not be empty', bid); + return false; + } + + if (!bid.params) { + utils.logError(BIDDER_CODE, 'bid.params is required.'); + return false; + } + + if (!bid.params.adUnitId || typeof bid.params.adUnitId !== 'string') { + utils.logError( + BIDDER_CODE, + 'bid.params.adUnitId is missing or has wrong type.' + ); + return false; + } + + if (!bid.params.inventoryId || typeof bid.params.inventoryId !== 'string') { + utils.logError( + BIDDER_CODE, + 'bid.params.inventoryId is missing or has wrong type.' + ); + return false; + } + + if ( + !bid.mediaTypes || + !bid.mediaTypes[BANNER] || + !utils.isArray(bid.mediaTypes[BANNER].sizes) || + bid.mediaTypes[BANNER].sizes.length <= 0 || + !utils.isArrayOfNums(bid.mediaTypes[BANNER].sizes[0]) + ) { + utils.logError(BIDDER_CODE, 'Wrong or missing size parameters.'); + return false; + } + + return true; +} +// #endregion + +// #region Building the bid requests +/** + * + * @param {object} bid + * @returns + */ +function createBidRequest(bid) { + // Developer Params + const { + inventoryId, + adUnitId, + extraData, + yearOfBirth, + gender, + categories, + latitude, + longitude, + sdkVersion, + } = bid.params; + + return { + method: 'GET', + url: ADPLUS_ENDPOINT, + data: utils.cleanObj({ + bidId: bid.bidId, + inventoryId, + adUnitId, + adUnitWidth: bid.mediaTypes[BANNER].sizes[0][0], + adUnitHeight: bid.mediaTypes[BANNER].sizes[0][1], + extraData, + yearOfBirth, + gender, + categories, + latitude, + longitude, + sdkVersion: sdkVersion || '1', + session: getSessionId(), + interstitial: 0, + token: typeof window.top === 'object' && window.top[DGID_CODE] ? window.top[DGID_CODE] : undefined, + secure: window.location.protocol === 'https:' ? 1 : 0, + screenWidth: screen.width, + screenHeight: screen.height, + language: window.navigator.language || 'en-US', + pageUrl: window.location.href, + domain: window.location.hostname, + referrer: window.location.referrer, + }), + }; +} + +function buildRequests(validBidRequests, bidderRequest) { + return validBidRequests.map((req) => createBidRequest(req)); +} +// #endregion + +// #region Interpreting Responses +/** + * + * @param {HeaderBiddingResponse} responseData + * @param { object } bidParams + * @returns + */ +function createAdResponse(responseData, bidParams) { + return { + requestId: responseData.requestID, + cpm: responseData.cpm, + currency: responseData.currency, + width: responseData.width, + height: responseData.height, + creativeId: responseData.creativeID, + dealId: responseData.dealID, + netRevenue: responseData.netRevenue, + ttl: responseData.ttl, + ad: responseData.ad, + mediaType: responseData.mediaType, + meta: { + advertiserDomains: responseData.advertiserDomains, + primaryCatId: utils.isArray(responseData.categoryIDs) && responseData.categoryIDs.length > 0 + ? responseData.categoryIDs[0] : undefined, + secondaryCatIds: responseData.categoryIDs, + }, + }; +} + +function interpretResponse(response, request) { + // In case of empty response + if ( + response.body == null || + !utils.isArray(response.body) || + response.body.length === 0 + ) { + return []; + } + const bids = response.body.map((bid) => createAdResponse(bid)); + return bids; +} +// #endregion + +// #region Bidder +export const spec = { + code: BIDDER_CODE, + supportedMediaTypes: [BANNER], + isBidRequestValid, + buildRequests, + interpretResponse, + onTimeout(timeoutData) { + utils.logError('Adplus adapter timed out for the auction.', timeoutData); + }, + onBidWon(bid) { + utils.logInfo( + `Adplus adapter won the auction. Bid id: ${bid.bidId}, Ad Unit Id: ${bid.adUnitId}, Inventory Id: ${bid.inventoryId}` + ); + }, +}; + +registerBidder(spec); +// #endregion diff --git a/modules/adplusBidAdapter.md b/modules/adplusBidAdapter.md new file mode 100644 index 00000000000..dce9e4a312f --- /dev/null +++ b/modules/adplusBidAdapter.md @@ -0,0 +1,39 @@ +# Overview + +Module Name: AdPlus Bidder Adapter + +Module Type: Bidder Adapter + +Maintainer: adplus.destek@yaani.com.tr + +# Description + +AdPlus Prebid.js Bidder Adapter. Only banner formats are supported. + +About us : https://ssp.ad-plus.com.tr/ + +# Test Parameters + +```javascript +var adUnits = [ + { + code: "div-adplus", + mediaTypes: { + banner: { + sizes: [ + [300, 250], + ], + }, + }, + bids: [ + { + bidder: "adplus", + params: { + inventoryId: "-1", + adUnitId: "-3", + }, + }, + ], + }, +]; +``` diff --git a/modules/adqueryIdSystem.js b/modules/adqueryIdSystem.js new file mode 100644 index 00000000000..5357c1a1ffd --- /dev/null +++ b/modules/adqueryIdSystem.js @@ -0,0 +1,103 @@ +/** + * This module adds Adquery QID to the User ID module + * The {@link module:modules/userId} module is required + * @module modules/adqueryIdSystem + * @requires module:modules/userId + */ + +import {ajax} from '../src/ajax.js'; +import {getStorageManager} from '../src/storageManager.js'; +import {submodule} from '../src/hook.js'; +import * as utils from '../src/utils.js'; + +const MODULE_NAME = 'qid'; +const AU_GVLID = 902; + +export const storage = getStorageManager(AU_GVLID, 'qid'); + +/** + * Param or default. + * @param {String} param + * @param {String} defaultVal + */ +function paramOrDefault(param, defaultVal, arg) { + if (utils.isFn(param)) { + return param(arg); + } else if (utils.isStr(param)) { + return param; + } + return defaultVal; +} + +/** @type {Submodule} */ +export const adqueryIdSubmodule = { + /** + * used to link submodule with config + * @type {string} + */ + name: MODULE_NAME, + + /** + * IAB TCF Vendor ID + * @type {string} + */ + gvlid: AU_GVLID, + + /** + * decode the stored id value for passing to bid requests + * @function + * @param {{value:string}} value + * @returns {{qid:Object}} + */ + decode(value) { + let qid = storage.getDataFromLocalStorage('qid'); + if (utils.isStr(qid)) { + return {qid: qid}; + } + return (value && typeof value['qid'] === 'string') ? { 'qid': value['qid'] } : undefined; + }, + /** + * performs action to obtain id and return a value in the callback's response argument + * @function + * @param {SubmoduleConfig} [config] + * @returns {IdResponse|undefined} + */ + getId(config) { + if (!utils.isPlainObject(config.params)) { + config.params = {}; + } + const url = paramOrDefault(config.params.url, + `https://bidder.adquery.io/prebid/qid`, + config.params.urlArg); + + const resp = function (callback) { + let qid = storage.getDataFromLocalStorage('qid'); + if (utils.isStr(qid)) { + const responseObj = {qid: qid}; + callback(responseObj); + } else { + const callbacks = { + success: response => { + let responseObj; + if (response) { + try { + responseObj = JSON.parse(response); + } catch (error) { + utils.logError(error); + } + } + callback(responseObj); + }, + error: error => { + utils.logError(`${MODULE_NAME}: ID fetch encountered an error`, error); + callback(); + } + }; + ajax(url, callbacks, undefined, {method: 'GET'}); + } + }; + return {callback: resp}; + } +}; + +submodule('userId', adqueryIdSubmodule); diff --git a/modules/adqueryIdSystem.md b/modules/adqueryIdSystem.md new file mode 100644 index 00000000000..3a49ffbe4da --- /dev/null +++ b/modules/adqueryIdSystem.md @@ -0,0 +1,35 @@ +# Adquery QID + +Adquery QID Module. For assistance setting up your module please contact us at [prebid@adquery.io](prebid@adquery.io). + +### Prebid Params + +Individual params may be set for the Adquery ID Submodule. At least one identifier must be set in the params. + +``` +pbjs.setConfig({ + usersync: { + userIds: [{ + name: 'qid', + storage: { + name: 'qid', + type: 'html5' + } + }] + } +}); +``` +## Parameter Descriptions for the `usersync` Configuration Section +The below parameters apply only to the Adquery User ID Module integration. + +| Param under usersync.userIds[] | Scope | Type | Description | Example | +| --- | --- | --- | --- | --- | +| name | Required | String | ID value for the Adquery ID module - `"qid"` | `"qid"` | +| storage | Required | Object | The publisher must specify the local storage in which to store the results of the call to get the user ID. | | +| storage.type | Required | String | This is where the results of the user ID will be stored. The recommended method is `localStorage` by specifying `html5`. | `"html5"` | +| storage.name | Required | String | The name of the html5 local storage where the user ID will be stored. | `"qid"` | +| storage.expires | Optional | Integer | How long (in days) the user ID information will be stored. | `365` | +| value | Optional | Object | Used only if the page has a separate mechanism for storing the Adquery ID. The value is an object containing the values to be sent to the adapters. In this scenario, no URL is called and nothing is added to local storage | `{"qid": "2abf9f001fcd81241b67"}` | +| params | Optional | Object | Used to store params for the id system | +| params.url | Optional | String | Set an alternate GET url for qid with this parameter | +| params.urlArg | Optional | Object | Optional url parameter for params.url | diff --git a/modules/adtelligentBidAdapter.js b/modules/adtelligentBidAdapter.js index 8523e01c0ea..44a9c90d438 100644 --- a/modules/adtelligentBidAdapter.js +++ b/modules/adtelligentBidAdapter.js @@ -20,6 +20,7 @@ const HOST_GETTERS = { onefiftytwomedia: () => 'ghb.ads.152media.com', mediafuse: () => 'ghb.hbmp.mediafuse.com', bidsxchange: () => 'ghb.hbd.bidsxchange.com', + streamkey: () => 'ghb.hb.streamkey.net', } const getUri = function (bidderCode) { let bidderWithoutSuffix = bidderCode.split('_')[0]; @@ -35,7 +36,7 @@ const syncsCache = {}; export const spec = { code: BIDDER_CODE, gvlid: 410, - aliases: ['onefiftytwomedia', 'selectmedia', 'appaloosa', 'bidsxchange', + aliases: ['onefiftytwomedia', 'selectmedia', 'appaloosa', 'bidsxchange', 'streamkey', { code: 'navelix', gvlid: 380 }, { code: 'mediafuse', diff --git a/modules/adxcgBidAdapter.js b/modules/adxcgBidAdapter.js index a02812a1608..81872100cd1 100644 --- a/modules/adxcgBidAdapter.js +++ b/modules/adxcgBidAdapter.js @@ -1,410 +1,362 @@ -import { logWarn, isStr, deepAccess, inIframe, checkCookieSupport, timestamp, getBidIdParameter, parseSizesInput, buildUrl, logMessage, isArray, deepSetValue, isPlainObject, triggerPixel, replaceAuctionPrice, isFn } from '../src/utils.js'; -import {config} from '../src/config.js' -import {registerBidder} from '../src/adapters/bidderFactory.js' -import {BANNER, NATIVE, VIDEO} from '../src/mediaTypes.js' -import includes from 'core-js-pure/features/array/includes.js' - -/** - * Adapter for requesting bids from adxcg.net - * updated to latest prebid repo on 2017.10.20 - * updated for gdpr compliance on 2018.05.22 -requires gdpr compliance module - * updated to pass aditional auction and impression level parameters. added pass for video targeting parameters - * updated to fix native support for image width/height and icon 2019.03.17 - * updated support for userid - pubcid,ttid 2019.05.28 - * updated to support prebid 3.0 - remove non https, move to banner.xx.sizes, remove utils.getTopWindowLocation,remove utils.getTopWindowUrl(),remove utils.getTopWindowReferrer() - * updated to support prebid 4.0 - standardized video params, updated video validation, add onBidWon, onTimeOut, use standardized getFloor - */ - -const BIDDER_CODE = 'adxcg' -const SUPPORTED_AD_TYPES = [BANNER, VIDEO, NATIVE] -const SOURCE = 'pbjs10' -const VIDEO_TARGETING = ['id', 'minduration', 'maxduration', 'startdelay', 'skippable', 'playback_method', 'frameworks'] -const USER_PARAMS_AUCTION = ['forcedDspIds', 'forcedCampaignIds', 'forcedCreativeIds', 'gender', 'dnt', 'language'] -const USER_PARAMS_BID = ['lineparam1', 'lineparam2', 'lineparam3'] -const BIDADAPTERVERSION = 'r20210330PB40' -const DEFAULT_MIN_FLOOR = 0; +// jshint esversion: 6, es3: false, node: true +'use strict'; + +import {registerBidder} from '../src/adapters/bidderFactory.js'; +import {NATIVE, BANNER, VIDEO} from '../src/mediaTypes.js'; +import { + mergeDeep, + _map, + deepAccess, + getDNT, + parseSizesInput, + deepSetValue, + isStr, + isArray, + isPlainObject, + parseUrl, + replaceAuctionPrice, triggerPixel +} from '../src/utils.js'; +import {config} from '../src/config.js'; + +const { getConfig } = config; + +const BIDDER_CODE = 'adxcg'; +const SECURE_BID_URL = 'https://pbc.adxcg.net/rtb/ortb/pbc?adExchangeId=1'; + +const NATIVE_ASSET_IDS = { 0: 'title', 2: 'icon', 3: 'image', 5: 'sponsoredBy', 4: 'body', 1: 'cta' }; +const NATIVE_PARAMS = { + title: { + id: 0, + name: 'title' + }, + icon: { + id: 2, + type: 1, + name: 'img' + }, + image: { + id: 3, + type: 3, + name: 'img' + }, + sponsoredBy: { + id: 5, + name: 'data', + type: 1 + }, + body: { + id: 4, + name: 'data', + type: 2 + }, + cta: { + id: 1, + type: 12, + name: 'data' + } +}; export const spec = { code: BIDDER_CODE, - supportedMediaTypes: SUPPORTED_AD_TYPES, - - /** - * Determines whether or not the given bid request is valid. - * - * @param {object} bid The bid params to validate. - * @return boolean True if this is a valid bid, and false otherwise. - */ - isBidRequestValid: function (bid) { - if (!bid || !bid.params) { - logWarn(BIDDER_CODE + ': Missing bid parameters'); - return false - } + supportedMediaTypes: [ NATIVE, BANNER, VIDEO ], + isBidRequestValid: (bid) => { + const params = bid.params || {}; + const { adzoneid } = params; + return !!(adzoneid); + }, + buildRequests: (validBidRequests, bidderRequest) => { + let app, site; - if (!isStr(bid.params.adzoneid)) { - logWarn(BIDDER_CODE + ': adzoneid must be specified as a string'); - return false - } + const commonFpd = getConfig('ortb2') || {}; + let { user } = commonFpd; + + if (typeof getConfig('app') === 'object') { + app = getConfig('app') || {}; + if (commonFpd.app) { + mergeDeep(app, commonFpd.app); + } + } else { + site = getConfig('site') || {}; + if (commonFpd.site) { + mergeDeep(site, commonFpd.site); + } - if (isBannerRequest(bid)) { - const banneroAdUnit = deepAccess(bid, 'mediaTypes.banner'); - if (!banneroAdUnit.sizes) { - logWarn(BIDDER_CODE + ': banner sizes must be specified'); - return false; + if (!site.page) { + site.page = bidderRequest.refererInfo.referer; + site.domain = parseUrl(bidderRequest.refererInfo.referer).hostname; } } - if (isVideoRequest(bid)) { - // prebid 4.0 use standardized Video parameters - const videoAdUnit = deepAccess(bid, 'mediaTypes.video'); + const device = getConfig('device') || {}; + device.w = device.w || window.innerWidth; + device.h = device.h || window.innerHeight; + device.ua = device.ua || navigator.userAgent; + device.dnt = getDNT() ? 1 : 0; + device.language = (navigator && navigator.language) ? navigator.language.split('-')[0] : ''; + + const tid = validBidRequests[0].transactionId; + const test = setOnAny(validBidRequests, 'params.test'); + const currency = getConfig('currency.adServerCurrency'); + const cur = currency && [ currency ]; + const eids = setOnAny(validBidRequests, 'userIdAsEids'); + const schain = setOnAny(validBidRequests, 'schain'); + + const imp = validBidRequests.map((bid, id) => { + const floorInfo = bid.getFloor ? bid.getFloor({ + currency: currency || 'USD' + }) : {}; + const bidfloor = floorInfo.floor; + const bidfloorcur = floorInfo.currency; + const { adzoneid } = bid.params; + + const imp = { + id: id + 1, + tagid: adzoneid, + secure: 1, + bidfloor, + bidfloorcur, + ext: { + } + }; - if (!Array.isArray(videoAdUnit.playerSize)) { - logWarn(BIDDER_CODE + ': video playerSize must be an array of integers'); - return false; - } + const assets = _map(bid.nativeParams, (bidParams, key) => { + const props = NATIVE_PARAMS[key]; + const asset = { + required: bidParams.required & 1, + }; + if (props) { + asset.id = props.id; + let wmin, hmin, w, h; + let aRatios = bidParams.aspect_ratios; + + if (aRatios && aRatios[0]) { + aRatios = aRatios[0]; + wmin = aRatios.min_width || 0; + hmin = aRatios.ratio_height * wmin / aRatios.ratio_width | 0; + } - if (!videoAdUnit.context) { - logWarn(BIDDER_CODE + ': video context must be specified'); - return false; - } + if (bidParams.sizes) { + const sizes = flatten(bidParams.sizes); + w = sizes[0]; + h = sizes[1]; + } - if (!Array.isArray(videoAdUnit.mimes) || videoAdUnit.mimes.length === 0) { - logWarn(BIDDER_CODE + ': video mimes must be an array of strings'); - return false; + asset[props.name] = { + len: bidParams.len, + type: props.type, + wmin, + hmin, + w, + h + }; + + return asset; + } + }).filter(Boolean); + + if (assets.length) { + imp.native = { + request: JSON.stringify({assets: assets}) + }; } - if (!Array.isArray(videoAdUnit.protocols) || videoAdUnit.protocols.length === 0) { - logWarn(BIDDER_CODE + ': video protocols must be an array of integers'); - return false; + const bannerParams = deepAccess(bid, 'mediaTypes.banner'); + + if (bannerParams && bannerParams.sizes) { + const sizes = parseSizesInput(bannerParams.sizes); + const format = sizes.map(size => { + const [ width, height ] = size.split('x'); + const w = parseInt(width, 10); + const h = parseInt(height, 10); + return { w, h }; + }); + + imp.banner = { + format + }; } - } - return true - }, + const videoParams = deepAccess(bid, 'mediaTypes.video'); + if (videoParams) { + imp.video = videoParams; + } - /** - * Make a server request from the list of BidRequests. - * - * an array of validBidRequests - * Info describing the request to the server. - */ - buildRequests: function (validBidRequests, bidderRequest) { - let dt = new Date(); - let ratio = window.devicePixelRatio || 1; - let iobavailable = window && window.IntersectionObserver && window.IntersectionObserverEntry && window.IntersectionObserverEntry.prototype && 'intersectionRatio' in window.IntersectionObserverEntry.prototype - - let bt = config.getConfig('bidderTimeout'); - if (window.PREBID_TIMEOUT) { - bt = Math.min(window.PREBID_TIMEOUT, bt); - } + return imp; + }); - let referrer = deepAccess(bidderRequest, 'refererInfo.referer'); - let page = deepAccess(bidderRequest, 'refererInfo.canonicalUrl') || config.getConfig('pageUrl') || deepAccess(window, 'location.href'); - - // add common parameters - let beaconParams = { - renderformat: 'javascript', - ver: BIDADAPTERVERSION, - secure: '1', - source: SOURCE, - uw: window.screen.width, - uh: window.screen.height, - dpr: ratio, - bt: bt, - isinframe: inIframe(), - cookies: checkCookieSupport() ? '1' : '0', - tz: dt.getTimezoneOffset(), - dt: timestamp(), - iob: iobavailable ? '1' : '0', - pbjs: '$prebid.version$', - rndid: Math.floor(Math.random() * (999999 - 100000 + 1)) + 100000, - ref: encodeURIComponent(referrer), - url: encodeURIComponent(page) + const request = { + id: bidderRequest.auctionId, + site, + app, + user, + geo: { utcoffset: new Date().getTimezoneOffset() }, + device, + source: { tid, fd: 1 }, + ext: { + prebid: { + channel: { + name: 'pbjs', + version: '$prebid.version$' + } + } + }, + cur, + imp }; - if (bidderRequest && bidderRequest.gdprConsent && bidderRequest.gdprConsent.gdprApplies) { - beaconParams.gdpr = bidderRequest.gdprConsent.gdprApplies ? '1' : '0'; - beaconParams.gdpr_consent = bidderRequest.gdprConsent.consentString; - } - - if (isStr(deepAccess(validBidRequests, '0.userId.pubcid'))) { - beaconParams.pubcid = validBidRequests[0].userId.pubcid; + if (test) { + request.is_debug = !!test; + request.test = 1; } - - if (isStr(deepAccess(validBidRequests, '0.userId.tdid'))) { - beaconParams.tdid = validBidRequests[0].userId.tdid; + if (deepAccess(bidderRequest, 'gdprConsent.gdprApplies') !== undefined) { + deepSetValue(request, 'user.ext.consent', bidderRequest.gdprConsent.consentString); + deepSetValue(request, 'regs.ext.gdpr', bidderRequest.gdprConsent.gdprApplies & 1); } - if (isStr(deepAccess(validBidRequests, '0.userId.id5id.uid'))) { - beaconParams.id5id = validBidRequests[0].userId.id5id.uid; + if (bidderRequest.uspConsent) { + deepSetValue(request, 'regs.ext.us_privacy', bidderRequest.uspConsent); } - if (isStr(deepAccess(validBidRequests, '0.userId.idl_env'))) { - beaconParams.idl_env = validBidRequests[0].userId.idl_env; + if (eids) { + deepSetValue(request, 'user.ext.eids', eids); } - let biddercustom = config.getConfig(BIDDER_CODE); - if (biddercustom) { - Object.keys(biddercustom) - .filter(param => includes(USER_PARAMS_AUCTION, param)) - .forEach(param => beaconParams[param] = encodeURIComponent(biddercustom[param])) + if (schain) { + deepSetValue(request, 'source.ext.schain', schain); } - // per impression parameters - let adZoneIds = []; - let prebidBidIds = []; - let sizes = []; - let bidfloors = []; - - validBidRequests.forEach((bid, index) => { - adZoneIds.push(getBidIdParameter('adzoneid', bid.params)); - prebidBidIds.push(bid.bidId); - - let bidfloor = getFloor(bid); - bidfloors.push(bidfloor); - - // copy all custom parameters impression level parameters not supported above - let customBidParams = getBidIdParameter('custom', bid.params) || {} - if (customBidParams) { - Object.keys(customBidParams) - .filter(param => includes(USER_PARAMS_BID, param)) - .forEach(param => beaconParams[param + '.' + index] = encodeURIComponent(customBidParams[param])) - } - - if (isBannerRequest(bid)) { - sizes.push(parseSizesInput(bid.mediaTypes.banner.sizes).join('|')); - } - - if (isNativeRequest(bid)) { - sizes.push('0x0'); - } - - if (isVideoRequest(bid)) { - if (bid.params.video) { - Object.keys(bid.params.video) - .filter(param => includes(VIDEO_TARGETING, param)) - .forEach(param => beaconParams['video.' + param + '.' + index] = encodeURIComponent(bid.params.video[param])) - } - // copy video standarized params - beaconParams['video.context' + '.' + index] = deepAccess(bid, 'mediaTypes.video.context'); - sizes.push(parseSizesInput(bid.mediaTypes.video.playerSize).join('|')); - beaconParams['video.mimes' + '.' + index] = deepAccess(bid, 'mediaTypes.video.mimes').join(','); - beaconParams['video.protocols' + '.' + index] = deepAccess(bid, 'mediaTypes.video.protocols').join(','); - } - }) - - beaconParams.adzoneid = adZoneIds.join(','); - beaconParams.format = sizes.join(','); - beaconParams.prebidBidIds = prebidBidIds.join(','); - beaconParams.bidfloors = bidfloors.join(','); - - let adxcgRequestUrl = buildUrl({ - protocol: 'https', - hostname: 'hbps.adxcg.net', - pathname: '/get/adi', - search: beaconParams - }); - - logMessage(`calling adi adxcg`); return { - contentType: 'text/plain', - method: 'GET', - url: adxcgRequestUrl, - withCredentials: true + method: 'POST', + url: SECURE_BID_URL, + data: JSON.stringify(request), + options: { + contentType: 'application/json' + }, + bids: validBidRequests }; }, - /** - * Unpack the response from the server into a list of bids. - * - * @param {*} serverResponse A successful response from the server. - * @return {bidRequests[]} An array of bids which were nested inside the server. - */ - interpretResponse: - function (serverResponse) { - logMessage(`interpretResponse adxcg`); - let bidsAll = []; - - if (!serverResponse || !serverResponse.body || !isArray(serverResponse.body.seatbid) || !serverResponse.body.seatbid.length) { - logWarn(BIDDER_CODE + ': empty bid response'); - return bidsAll; - } - - serverResponse.body.seatbid.forEach((bids) => { - bids.bid.forEach((serverResponseOneItem) => { - let bid = {} - // parse general fields - bid.requestId = serverResponseOneItem.impid; - bid.cpm = serverResponseOneItem.price; - bid.creativeId = parseInt(serverResponseOneItem.crid); - bid.currency = serverResponseOneItem.currency ? serverResponseOneItem.currency : 'USD'; - bid.netRevenue = serverResponseOneItem.netRevenue ? serverResponseOneItem.netRevenue : true; - bid.ttl = serverResponseOneItem.ttl ? serverResponseOneItem.ttl : 300; - bid.width = serverResponseOneItem.w; - bid.height = serverResponseOneItem.h; - bid.burl = serverResponseOneItem.burl || ''; - - if (serverResponseOneItem.dealid != null && serverResponseOneItem.dealid.trim().length > 0) { - bid.dealId = serverResponseOneItem.dealid; - } + interpretResponse: function(serverResponse, { bids }) { + if (!serverResponse.body) { + return; + } + const { seatbid, cur } = serverResponse.body; + + const bidResponses = flatten(seatbid.map(seat => seat.bid)).reduce((result, bid) => { + result[bid.impid - 1] = bid; + return result; + }, []); + + return bids.map((bid, id) => { + const bidResponse = bidResponses[id]; + if (bidResponse) { + const mediaType = deepAccess(bidResponse, 'ext.crType'); + const result = { + requestId: bid.bidId, + cpm: bidResponse.price, + creativeId: bidResponse.crid, + ttl: bidResponse.ttl ? bidResponse.ttl : 300, + netRevenue: bid.netRevenue === 'net', + currency: cur, + burl: bid.burl || '', + mediaType: mediaType, + width: bidResponse.w, + height: bidResponse.h, + dealId: bidResponse.dealid, + }; + + deepSetValue(result, 'meta.mediaType', mediaType); + if (isArray(bidResponse.adomain)) { + deepSetValue(result, 'meta.advertiserDomains', bidResponse.adomain); + } - if (serverResponseOneItem.ext.crType === 'banner') { - bid.ad = serverResponseOneItem.adm; - } else if (serverResponseOneItem.ext.crType === 'video') { - bid.vastUrl = serverResponseOneItem.nurl; - bid.vastXml = serverResponseOneItem.adm; - bid.mediaType = 'video'; - } else if (serverResponseOneItem.ext.crType === 'native') { - bid.mediaType = 'native'; - bid.native = parseNative(JSON.parse(serverResponseOneItem.adm)); - } else { - logWarn(BIDDER_CODE + ': unknown or undefined crType'); + if (isPlainObject(bidResponse.ext)) { + if (isStr(bidResponse.ext.mediaType)) { + deepSetValue(result, 'meta.mediaType', mediaType); } - - // prebid 4.0 meta taxonomy - if (isArray(serverResponseOneItem.adomain)) { - deepSetValue(bid, 'meta.advertiserDomains', serverResponseOneItem.adomain); + if (isStr(bidResponse.ext.advertiser_id)) { + deepSetValue(result, 'meta.advertiserId', bidResponse.ext.advertiser_id); } - if (isArray(serverResponseOneItem.cat)) { - deepSetValue(bid, 'meta.secondaryCatIds', serverResponseOneItem.cat); + if (isStr(bidResponse.ext.advertiser_name)) { + deepSetValue(result, 'meta.advertiserName', bidResponse.ext.advertiser_name); } - if (isPlainObject(serverResponseOneItem.ext)) { - if (isStr(serverResponseOneItem.ext.advertiser_id)) { - deepSetValue(bid, 'meta.mediaType', serverResponseOneItem.ext.mediaType); - } - if (isStr(serverResponseOneItem.ext.advertiser_id)) { - deepSetValue(bid, 'meta.advertiserId', serverResponseOneItem.ext.advertiser_id); - } - if (isStr(serverResponseOneItem.ext.advertiser_name)) { - deepSetValue(bid, 'meta.advertiserName', serverResponseOneItem.ext.advertiser_name); - } - if (isStr(serverResponseOneItem.ext.agency_name)) { - deepSetValue(bid, 'meta.agencyName', serverResponseOneItem.ext.agency_name); - } + if (isStr(bidResponse.ext.agency_name)) { + deepSetValue(result, 'meta.agencyName', bidResponse.ext.agency_name); } - bidsAll.push(bid) - }) - }) - return bidsAll - }, + } + if (mediaType === BANNER) { + result.ad = bidResponse.adm; + } else if (mediaType === NATIVE) { + result.native = parseNative(bidResponse); + result.width = 0; + result.height = 0; + } else if (mediaType === VIDEO) { + result.vastUrl = bidResponse.nurl; + result.vastXml = bidResponse.adm; + } - onBidWon: (bid) => { - if (bid.burl) { - triggerPixel(replaceAuctionPrice(bid.burl, bid.originalCpm)); - } + return result; + } + }).filter(Boolean); }, + getUserSyncs: (syncOptions, responses, gdprConsent, uspConsent) => { + const syncs = []; + let syncUrl = config.getConfig('adxcg.usersyncUrl'); + + let query = []; + if (syncOptions.pixelEnabled && syncUrl) { + if (gdprConsent) { + query.push('gdpr=' + (gdprConsent.gdprApplies & 1)); + query.push('gdpr_consent=' + encodeURIComponent(gdprConsent.consentString || '')); + } + if (uspConsent) { + query.push('us_privacy=' + encodeURIComponent(uspConsent)); + } - onTimeout(timeoutData) { - if (timeoutData == null) { - return; + syncs.push({ + type: 'image', + url: syncUrl + (query.length ? '?' + query.join('&') : '') + }); } - - let beaconParams = { - A: timeoutData.bidder, - bid: timeoutData.bidId, - a: timeoutData.adUnitCode, - cn: timeoutData.timeout, - aud: timeoutData.auctionId, - }; - let adxcgRequestUrl = buildUrl({ - protocol: 'https', - hostname: 'hbps.adxcg.net', - pathname: '/event/timeout.gif', - search: beaconParams - }); - logWarn(BIDDER_CODE + ': onTimeout called'); - triggerPixel(adxcgRequestUrl); + return syncs; }, - - getUserSyncs: function (syncOptions, serverResponses, gdprConsent) { - let params = ''; - if (gdprConsent && 'gdprApplies' in gdprConsent) { - if (gdprConsent.consentString) { - if (typeof gdprConsent.gdprApplies === 'boolean') { - params += `?gdpr=${Number(gdprConsent.gdprApplies)}&gdpr_consent=${gdprConsent.consentString}`; - } else { - params += `?gdpr=0&gdpr_consent=${gdprConsent.consentString}`; - } - } - } - - if (syncOptions.iframeEnabled) { - return [{ - type: 'iframe', - url: 'https://cdn.adxcg.net/pb-sync.html' + params - }]; + onBidWon: (bid) => { + // for native requests we put the nurl as an imp tracker, otherwise if the auction takes place on prebid server + // the server JS adapter puts the nurl in the adm as a tracking pixel and removes the attribute + if (bid.nurl) { + triggerPixel(replaceAuctionPrice(bid.nurl, bid.originalCpm)) } } -} +}; -function isVideoRequest(bid) { - return bid.mediaType === 'video' || !!deepAccess(bid, 'mediaTypes.video'); -} +registerBidder(spec); -function isBannerRequest(bid) { - return bid.mediaType === 'banner' || !!deepAccess(bid, 'mediaTypes.banner'); -} - -function isNativeRequest(bid) { - return bid.mediaType === 'native' || !!deepAccess(bid, 'mediaTypes.native'); -} - -function getFloor(bid) { - if (!isFn(bid.getFloor)) { - return deepAccess(bid, 'params.floor', DEFAULT_MIN_FLOOR); - } - - try { - const floor = bid.getFloor({ - currency: 'EUR', - mediaType: '*', - size: '*', - bidRequest: bid - }); - return floor.floor; - } catch (e) { - logWarn(BIDDER_CODE + ': call to getFloor failed:' + e.message); - return DEFAULT_MIN_FLOOR; - } -} - -function parseNative(nativeResponse) { - let bidNative = {}; - bidNative = { - clickUrl: nativeResponse.link.url, - impressionTrackers: nativeResponse.imptrackers, - clickTrackers: nativeResponse.clktrackers, - javascriptTrackers: nativeResponse.jstrackers +function parseNative(bid) { + const { assets, link, imptrackers, jstracker } = JSON.parse(bid.adm); + const result = { + clickUrl: link.url, + clickTrackers: link.clicktrackers || undefined, + impressionTrackers: imptrackers || undefined, + javascriptTrackers: jstracker ? [ jstracker ] : undefined }; - - nativeResponse.assets.forEach(asset => { - if (asset.title && asset.title.text) { - bidNative.title = asset.title.text; - } - - if (asset.img && asset.img.url) { - bidNative.image = { - url: asset.img.url, - height: asset.img.h, - width: asset.img.w - }; - } - - if (asset.icon && asset.icon.url) { - bidNative.icon = { - url: asset.icon.url, - height: asset.icon.h, - width: asset.icon.w - }; - } - - if (asset.data && asset.data.label === 'DESC' && asset.data.value) { - bidNative.body = asset.data.value; + assets.forEach(asset => { + const kind = NATIVE_ASSET_IDS[asset.id]; + const content = kind && asset[NATIVE_PARAMS[kind].name]; + if (content) { + result[kind] = content.text || content.value || { url: content.url, width: content.w, height: content.h }; } + }); + return result; +} - if (asset.data && asset.data.label === 'SPONSORED' && asset.data.value) { - bidNative.sponsoredBy = asset.data.value; +function setOnAny(collection, key) { + for (let i = 0, result; i < collection.length; i++) { + result = deepAccess(collection[i], key); + if (result) { + return result; } - }) - return bidNative; + } } -registerBidder(spec) +function flatten(arr) { + return [].concat(...arr); +} diff --git a/modules/adxcgBidAdapter.md b/modules/adxcgBidAdapter.md index 8eccdb11dee..1e4ef9cd6f9 100644 --- a/modules/adxcgBidAdapter.md +++ b/modules/adxcgBidAdapter.md @@ -34,31 +34,34 @@ Module that connects to an Adxcg.com zone to fetch bids. code: 'native-ad-div', mediaTypes: { native: { - image: { + image: { sendId: false, - required: true, - sizes: [80, 80] + required: false, + sizes: [127, 83] }, icon: { - sendId: true, - }, - brand: { + sizes: [80, 80], + required: false, sendId: true, }, title: { sendId: false, - required: true, + required: false, len: 75 }, body: { sendId: false, - required: true, + required: false, len: 200 }, - sponsoredBy: { + cta: { sendId: false, required: false, - len: 20 + len: 75 + }, + sponsoredBy: { + sendId: false, + required: false } } }, @@ -73,21 +76,19 @@ Module that connects to an Adxcg.com zone to fetch bids. code: 'video-div', mediaTypes: { video: { - playerSize: [640, 480], - context: 'instream', - mimes: ['video/mp4'], - protocols: [5, 6, 8], - playback_method: ['auto_play_sound_off'] - } + playerSize: [640, 480], + context: 'instream', + mimes: ['video/mp4'], + protocols: [2, 3, 5, 6, 8], + playback_method: ['auto_play_sound_off'], + maxduration: 100, + skip: 1 + } }, bids: [{ bidder: 'adxcg', params: { - adzoneid: '20', - video: { - maxduration: 100, - skippable: true - } + adzoneid: '20' } }] } diff --git a/modules/adyoulikeBidAdapter.js b/modules/adyoulikeBidAdapter.js index 334309aec5c..155e8ca3c7a 100644 --- a/modules/adyoulikeBidAdapter.js +++ b/modules/adyoulikeBidAdapter.js @@ -1,6 +1,7 @@ import { deepAccess, buildUrl, parseSizesInput } from '../src/utils.js'; import { registerBidder } from '../src/adapters/bidderFactory.js'; import { config } from '../src/config.js'; +import { createEidsArray } from './userId/eids.js'; import find from 'core-js-pure/features/array/find.js'; import {BANNER, NATIVE, VIDEO} from '../src/mediaTypes.js'; @@ -97,17 +98,21 @@ export const spec = { PageRefreshed: getPageRefreshed() }; - if (bidderRequest && bidderRequest.gdprConsent) { + if (bidderRequest.gdprConsent) { payload.gdprConsent = { consentString: bidderRequest.gdprConsent.consentString, consentRequired: (typeof bidderRequest.gdprConsent.gdprApplies === 'boolean') ? bidderRequest.gdprConsent.gdprApplies : null }; } - if (bidderRequest && bidderRequest.uspConsent) { + if (bidderRequest.uspConsent) { payload.uspConsent = bidderRequest.uspConsent; } + if (deepAccess(bidderRequest, 'userId')) { + payload.userId = createEidsArray(bidderRequest.userId); + } + const data = JSON.stringify(payload); const options = { withCredentials: true @@ -175,11 +180,13 @@ function getCanonicalUrl() { /* Get mediatype from bidRequest */ function getMediatype(bidRequest) { + if (deepAccess(bidRequest, 'mediaTypes.banner')) { + return BANNER; + } if (deepAccess(bidRequest, 'mediaTypes.video')) { return VIDEO; - } else if (deepAccess(bidRequest, 'mediaTypes.banner')) { - return BANNER; - } else if (deepAccess(bidRequest, 'mediaTypes.native')) { + } + if (deepAccess(bidRequest, 'mediaTypes.native')) { return NATIVE; } } @@ -340,7 +347,7 @@ function getTrackers(eventsArray, jsTrackers) { function getVideoAd(response) { var adJson = {}; - if (typeof response.Ad === 'string') { + if (typeof response.Ad === 'string' && response.Ad.indexOf('\/\*PREBID\*\/') > 0) { adJson = JSON.parse(response.Ad.match(/\/\*PREBID\*\/(.*)\/\*PREBID\*\//)[1]); return deepAccess(adJson, 'Content.MainVideo.Vast'); } @@ -473,13 +480,15 @@ function createBid(response, bidRequests) { meta: response.Meta || { advertiserDomains: [] } }; - if (request && request.Native) { + // retreive video response if present + const vast64 = response.Vast || getVideoAd(response); + if (vast64) { + bid.vastXml = window.atob(vast64); + bid.mediaType = 'video'; + } else if (request.Native) { + // format Native response if Native was requested bid.native = getNativeAssets(response, request.Native); bid.mediaType = 'native'; - } else if (request && request.Video) { - const vast64 = response.Vast || getVideoAd(response); - bid.vastXml = vast64 ? window.atob(vast64) : ''; - bid.mediaType = 'video'; } else { bid.width = response.Width; bid.height = response.Height; diff --git a/modules/airgridRtdProvider.js b/modules/airgridRtdProvider.js index 8d212204da8..f5403cca3eb 100644 --- a/modules/airgridRtdProvider.js +++ b/modules/airgridRtdProvider.js @@ -33,7 +33,7 @@ export function attachScriptTagToDOM(rtdConfig) { edktInitializor.load = function(e) { var p = e || 'sdk'; var n = document.createElement('script'); - n.type = 'text/javascript'; + n.type = 'module'; n.async = true; n.src = 'https://cdn.edkt.io/' + p + '/edgekit.min.js'; document.getElementsByTagName('head')[0].appendChild(n); diff --git a/modules/akamaiDapRtdProvider.js b/modules/akamaiDapRtdProvider.js new file mode 100644 index 00000000000..d143a53fbf4 --- /dev/null +++ b/modules/akamaiDapRtdProvider.js @@ -0,0 +1,474 @@ +/** + * This module adds the Akamai DAP RTD provider to the real time data module + * The {@link module:modules/realTimeData} module is required + * The module will fetch real-time data from DAP + * @module modules/akamaiDapRtdProvider + * @requires module:modules/realTimeData + */ +import {ajax} from '../src/ajax.js'; +import {config} from '../src/config.js'; +import {getStorageManager} from '../src/storageManager.js'; +import {submodule} from '../src/hook.js'; +import {isPlainObject, mergeDeep, logMessage, logInfo, logError} from '../src/utils.js'; + +const MODULE_NAME = 'realTimeData'; +const SUBMODULE_NAME = 'dap'; + +export const SEGMENTS_STORAGE_KEY = 'akamaiDapSegments'; +export const storage = getStorageManager(null, SUBMODULE_NAME); + +/** + * Lazy merge objects. + * @param {String} target + * @param {String} source + */ +function mergeLazy(target, source) { + if (!isPlainObject(target)) { + target = {}; + } + if (!isPlainObject(source)) { + source = {}; + } + return mergeDeep(target, source); +} + +/** + * Add real-time data & merge segments. + * @param {Object} bidConfig + * @param {Object} rtd + * @param {Object} rtdConfig + */ +export function addRealTimeData(rtd) { + logInfo('DEBUG(addRealTimeData) - ENTER'); + if (isPlainObject(rtd.ortb2)) { + let ortb2 = config.getConfig('ortb2') || {}; + logMessage('DEBUG(addRealTimeData): merging original: ', ortb2); + logMessage('DEBUG(addRealTimeData): merging in: ', rtd.ortb2); + config.setConfig({ortb2: mergeLazy(ortb2, rtd.ortb2)}); + } + logInfo('DEBUG(addRealTimeData) - EXIT'); +} + +/** + * Real-time data retrieval from Audigent + * @param {Object} reqBidsConfigObj + * @param {function} onDone + * @param {Object} rtdConfi + * @param {Object} userConsent + */ +export function getRealTimeData(bidConfig, onDone, rtdConfig, userConsent) { + logInfo('DEBUG(getRealTimeData) - ENTER'); + logMessage(' - apiHostname: ' + rtdConfig.params.apiHostname); + logMessage(' - apiVersion: ' + rtdConfig.params.apiVersion); + let jsonData = storage.getDataFromLocalStorage(SEGMENTS_STORAGE_KEY); + if (jsonData) { + let data = JSON.parse(jsonData); + if (data.rtd) { + addRealTimeData(data.rtd); + onDone(); + logInfo('DEBUG(getRealTimeData) - 1'); + // Don't return - ensure the data is always fresh. + } + } + + if (rtdConfig && isPlainObject(rtdConfig.params)) { + let config = { + api_hostname: rtdConfig.params.apiHostname, + api_version: rtdConfig.params.apiVersion, + domain: rtdConfig.params.domain, + segtax: rtdConfig.params.segtax + }; + let identity = { + type: rtdConfig.params.identityType + }; + let token = dapUtils.dapGetToken(config, identity, rtdConfig.params.tokenTtl); + if (token !== null) { + let membership = dapUtils.dapGetMembership(config, token); + let udSegment = dapUtils.dapMembershipToRtbSegment(membership, config); + logMessage('DEBUG(getRealTimeData) - token: ' + token + ', user.data.segment: ', udSegment); + let data = { + rtd: { + ortb2: { + user: { + data: [ + udSegment + ] + }, + site: { + ext: { + data: { + dapSAID: membership.said + } + } + } + } + } + }; + storage.setDataInLocalStorage(SEGMENTS_STORAGE_KEY, JSON.stringify(data)); + onDone(); + } + } +} + +/** + * Module init + * @param {Object} provider + * @param {Object} userConsent + * @return {boolean} + */ +function init(provider, userConsent) { + return true; +} + +/** @type {RtdSubmodule} */ +export const akamaiDapRtdSubmodule = { + name: SUBMODULE_NAME, + getBidRequestData: getRealTimeData, + init: init +}; + +submodule(MODULE_NAME, akamaiDapRtdSubmodule); + +export const dapUtils = { + + dapGetToken: function(config, identity, ttl) { + let now = Math.round(Date.now() / 1000.0); // in seconds + let storageName = 'async_dap_token'; + let token = null; + + if (ttl == 0) { + localStorage.removeItem(storageName); + } + + let item = JSON.parse(localStorage.getItem(storageName)); + if (item == null) { + item = { + expires_at: now - 1, + token: null + }; + } else { + token = item.token; + } + + if (now > item.expires_at) { + dapUtils.dapLog('Token missing or expired, fetching a new one...'); + // Trigger a refresh + let configAsync = {...config}; + dapUtils.dapTokenize(configAsync, identity, + function(token, status, xhr) { + item.expires_at = now + ttl; + item.token = token; + localStorage.setItem(storageName, JSON.stringify(item)); + dapUtils.dapLog('Successfully updated and stored token; expires in ' + ttl + ' seconds'); + let deviceId100 = xhr.getResponseHeader('Akamai-DAP-100'); + if (deviceId100 != null) { + localStorage.setItem('dap_deviceId100', deviceId100); + dapUtils.dapLog('Successfully stored DAP 100 Device ID: ' + deviceId100); + } + }, + function(xhr, status, error) { + logError('ERROR(' + error + '): failed to retrieve token! ' + status); + } + ); + } + + return token; + }, + + dapGetMembership: function(config, token) { + let now = Math.round(Date.now() / 1000.0); // in seconds + let storageName = 'async_dap_membership'; + let maxTtl = 3600; // if the cached membership is older than this, return null + let membership = null; + let item = JSON.parse(localStorage.getItem(storageName)); + if (item == null || (now - item.expires_at) > maxTtl) { + item = { + expires_at: now - 1, + said: null, + cohorts: null, + attributes: null + }; + } else { + membership = { + said: item.said, + cohorts: item.cohorts, + attributes: null + }; + } + + // Always refresh the cached membership. + let configAsync = {...config}; + dapUtils.dapMembership(configAsync, token, + function(membership, status, xhr) { + item.expires_at = now + maxTtl; + item.said = membership.said; + item.cohorts = membership.cohorts; + localStorage.setItem(storageName, JSON.stringify(item)); + dapUtils.dapLog('Successfully updated and stored membership:'); + dapUtils.dapLog(item); + }, + function(xhr, status, error) { + logError('ERROR(' + error + '): failed to retrieve membership! ' + status); + } + ); + + return membership; + }, + + /** + * DESCRIPTION + * + * Convert a DAP membership response to an OpenRTB2 segment object suitable + * for insertion into user.data.segment or site.data.segment. + */ + dapMembershipToRtbSegment: function(membership, config) { + let segment = { + name: 'dap.akamai.com', + ext: { + 'segtax': config.segtax + }, + segment: [] + }; + if (membership != null) { + for (const i of membership.cohorts) { + segment.segment.push({ id: i }); + } + } + return segment; + }, + + dapLog: function(args) { + let css = ''; + css += 'display: inline-block;'; + css += 'color: #fff;'; + css += 'background: #F28B20;'; + css += 'padding: 1px 4px;'; + css += 'border-radius: 3px'; + + logInfo('%cDAP Client', css, args); + }, + + /******************************************************************************* + * + * V2 (And Beyond) API + * + ******************************************************************************/ + + /** + * SYNOPSIS + * + * dapTokenize( config, identity ); + * + * DESCRIPTION + * + * Tokenize an identity into a secure, privacy safe pseudonymiziation. + * + * PARAMETERS + * + * config: an array of system configuration parameters + * + * identity: an array of identity parameters passed to the tokenizing system + * + * EXAMPLE + * + * config = { + * api_hostname: "prebid.dap.akadns.net", // required + * domain: "prebid.org", // required + * api_version: "x1", // optional, default "x1" + * }; + * + * token = null; + * identity_email = { + * type: "email", + * identity: "obiwan@jedi.com" + * attributes: { cohorts: [ "100:1641013200", "101:1641013200", "102":3:1641013200" ] }, + * }; + * dap_x1_tokenize( config, identity_email, + * function( response, status, xhr ) { token = response; }, + * function( xhr, status, error ) { ; } // handle error + * + * token = null; + * identity_signature = { type: "signature:1.0.0" }; + * dap_x1_tokenize( config, identity_signature, + * function( response, status, xhr } { token = response; }, + * function( xhr, status, error ) { ; } // handle error + */ + dapTokenize: function(config, identity, onSuccess = null, onError = null) { + if (onError == null) { + onError = function(xhr, status, error) {}; + } + + if (config == null || typeof (config) == typeof (undefined)) { + onError(null, 'Invalid config object', 'ClientError'); + return; + } + + if (typeof (config.domain) != 'string') { + onError(null, 'Invalid config.domain: must be a string', 'ClientError'); + return; + } + + if (config.domain.length <= 0) { + onError(null, 'Invalid config.domain: must have non-zero length', 'ClientError'); + return; + } + + if (!('api_version' in config) || (typeof (config.api_version) == 'string' && config.api_version.length == 0)) { + config.api_version = 'x1'; + } + + if (typeof (config.api_version) != 'string') { + onError(null, "Invalid api_version: must be a string like 'x1', etc.", 'ClientError'); + return; + } + + if (!(('api_hostname') in config) || typeof (config.api_hostname) != 'string' || config.api_hostname.length == 0) { + onError(null, 'Invalid api_hostname: must be a non-empty string', 'ClientError'); + return; + } + + if (identity == null || typeof (identity) == typeof (undefined)) { + onError(null, 'Invalid identity object', 'ClientError'); + return; + } + + if (!('type' in identity) || typeof (identity.type) != 'string' || identity.type.length <= 0) { + onError(null, "Identity must contain a valid 'type' field", 'ClientError'); + return; + } + + let apiParams = { + 'type': identity.type, + }; + + if (typeof (identity.identity) != typeof (undefined)) { + apiParams.identity = identity.identity; + } + if (typeof (identity.attributes) != typeof (undefined)) { + apiParams.attributes = identity.attributes; + } + + let method; + let body; + let path; + switch (config.api_version) { + case 'x1': + case 'x1-dev': + method = 'POST'; + path = '/data-activation/' + config.api_version + '/domain/' + config.domain + '/identity/tokenize'; + body = JSON.stringify(apiParams); + break; + default: + onError(null, 'Invalid api_version: ' + config.api_version, 'ClientError'); + return; + } + + let url = 'https://' + config.api_hostname + path; + let cb = { + success: (response, request) => { + let token = null; + switch (config.api_version) { + case 'x1': + case 'x1-dev': + token = request.getResponseHeader('Akamai-DAP-Token'); + break; + } + onSuccess(token, request.status, request); + }, + error: (request, error) => { + onError(request, request.statusText, error); + } + }; + + ajax(url, cb, body, { + method: method, + customHeaders: { + 'Content-Type': 'application/json', + 'Pragma': 'akamai-x-cache-on' + } + }); + }, + + /** + * SYNOPSIS + * + * dapMembership( config, token, onSuccess, onError ); + * + * DESCRIPTION + * + * Return the audience segment membership along with a new Secure Advertising + * ID for this token. + * + * PARAMETERS + * + * config: an array of system configuration parameters + * + * token: the token previously returned from the tokenize API + * + * EXAMPLE + * + * config = { + * api_hostname: 'api.dap.akadns.net', + * }; + * + * // token from dap_x1_tokenize + * + * dapMembership( config, token, + * function( membership, status, xhr ) { + * // Run auction with membership.segments and membership.said + * }, + * function( xhr, status, error ) { + * // error + * } ); + * + */ + dapMembership: function(config, token, onSuccess = null, onError = null) { + if (onError == null) { + onError = function(xhr, status, error) {}; + } + + if (config == null || typeof (config) == typeof (undefined)) { + onError(null, 'Invalid config object', 'ClientError'); + return; + } + + if (!('api_version' in config) || (typeof (config.api_version) == 'string' && config.api_version.length == 0)) { + config.api_version = 'x1'; + } + + if (typeof (config.api_version) != 'string') { + onError(null, "Invalid api_version: must be a string like 'x1', etc.", 'ClientError'); + return; + } + + if (!(('api_hostname') in config) || typeof (config.api_hostname) != 'string' || config.api_hostname.length == 0) { + onError(null, 'Invalid api_hostname: must be a non-empty string', 'ClientError'); + return; + } + + if (token == null || typeof (token) != 'string') { + onError(null, 'Invalid token: must be a non-null string', 'ClientError'); + return; + } + let path = '/data-activation/' + + config.api_version + + '/token/' + token + + '/membership'; + + let url = 'https://' + config.api_hostname + path; + + let cb = { + success: (response, request) => { + onSuccess(JSON.parse(response), request.status, request); + }, + error: (error, request) => { + onError(request, request.status, error); + } + }; + + ajax(url, cb, undefined, { + method: 'GET', + customHeaders: {} + }); + } +} diff --git a/modules/akamaiDapRtdProvider.md b/modules/akamaiDapRtdProvider.md new file mode 100644 index 00000000000..ade11b88602 --- /dev/null +++ b/modules/akamaiDapRtdProvider.md @@ -0,0 +1,47 @@ +### Overview + + Akamai DAP Real time data Provider automatically invokes the DAP APIs and submit audience segments and the SAID to the bid-stream. + +### Integration + + 1) Build the akamaiDapRTD module into the Prebid.js package with: + + ``` + gulp build --modules=akamaiDapRtdProvider,... + ``` + + 2) Use `setConfig` to instruct Prebid.js to initilaize the akamaiDapRtdProvider module, as specified below. + +### Configuration + +``` + pbjs.setConfig({ + realTimeData: { + dataProviders: [ + { + name: "dap", + waitForIt: true, + params: { + apiHostname: '', + apiVersion: "x1", + domain: 'your-domain.com', + identityType: 'email' | 'mobile' | ... | 'dap-signature:1.0.0', + segtax: , + tokenTtl: 5, + } + } + ] + } + }); + ``` + +Please reach out to your Akamai account representative(Prebid@akamai.com) to get provisioned on the DAP platform. + + +### Testing +To view an example of available segments returned by dap: +``` +‘gulp serve --modules=rtdModule,akamaiDapRtdProvider,appnexusBidAdapter,sovrnBidAdapter’ +``` +and then point your browser at: +"http://localhost:9999/integrationExamples/gpt/akamaidap_segments_example.html" diff --git a/modules/aniviewBidAdapter.js b/modules/aniviewBidAdapter.js index 96bdf153e3f..53249e92a77 100644 --- a/modules/aniviewBidAdapter.js +++ b/modules/aniviewBidAdapter.js @@ -309,7 +309,7 @@ function getUserSyncs(syncOptions, serverResponses) { export const spec = { code: BIDDER_CODE, gvlid: GVLID, - aliases: ['avantisvideo', 'selectmediavideo', 'vidcrunch', 'openwebvideo'], + aliases: ['avantisvideo', 'selectmediavideo', 'vidcrunch', 'openwebvideo', 'didnavideo'], supportedMediaTypes: [VIDEO, BANNER], isBidRequestValid, buildRequests, diff --git a/modules/aolBidAdapter.js b/modules/aolBidAdapter.js index 7203439059b..c9f64ab66b0 100644 --- a/modules/aolBidAdapter.js +++ b/modules/aolBidAdapter.js @@ -38,7 +38,8 @@ const SUPPORTED_USER_ID_SOURCES = [ 'liveintent.com', 'quantcast.com', 'verizonmedia.com', - 'liveramp.com' + 'liveramp.com', + 'yahoo.com' ]; const pubapiTemplate = template`${'host'}/pubapi/3.0/${'network'}/${'placement'}/${'pageid'}/${'sizeid'}/ADTECH;v=2;cmd=bid;cors=yes;alias=${'alias'};misc=${'misc'};${'dynamicParams'}`; diff --git a/modules/appnexusBidAdapter.js b/modules/appnexusBidAdapter.js index 7dcbd74d779..008a3f7287d 100644 --- a/modules/appnexusBidAdapter.js +++ b/modules/appnexusBidAdapter.js @@ -1,4 +1,4 @@ -import { convertCamelToUnderscore, isArray, isNumber, isPlainObject, logError, logInfo, deepAccess, logMessage, convertTypes, isStr, getParameterByName, deepClone, chunk, logWarn, getBidRequest, createTrackPixelHtml, isEmpty, transformBidderParamKeywords, getMaxValueFromArray, fill, getMinValueFromArray, isArrayOfNums, isFn } from '../src/utils.js'; +import { convertCamelToUnderscore, isArray, isNumber, isPlainObject, logError, logInfo, deepAccess, logMessage, convertTypes, isStr, getParameterByName, deepClone, chunk, logWarn, getBidRequest, createTrackPixelHtml, isEmpty, transformBidderParamKeywords, getMaxValueFromArray, fill, getMinValueFromArray, isArrayOfNums, isFn, isAllowZeroCpmBidsEnabled } from '../src/utils.js'; import { Renderer } from '../src/Renderer.js'; import { config } from '../src/config.js'; import { registerBidder, getIabSubCategory } from '../src/adapters/bidderFactory.js'; @@ -78,7 +78,6 @@ export const spec = { { code: 'districtm', gvlid: 144 }, { code: 'adasta' }, { code: 'beintoo', gvlid: 618 }, - { code: 'targetVideo' }, ], supportedMediaTypes: [BANNER, VIDEO, NATIVE], @@ -202,6 +201,17 @@ export const spec = { payload.app = appIdObj; } + let auctionKeywords = config.getConfig('appnexusAuctionKeywords'); + if (isPlainObject(auctionKeywords)) { + let aucKeywords = transformBidderParamKeywords(auctionKeywords); + + if (aucKeywords.length > 0) { + aucKeywords.forEach(deleteValues); + } + + payload.keywords = aucKeywords; + } + if (config.getConfig('adpod.brandCategoryExclusion')) { payload.brand_category_uniqueness = true; } @@ -293,7 +303,8 @@ export const spec = { serverResponse.tags.forEach(serverBid => { const rtbBid = getRtbBid(serverBid); if (rtbBid) { - if (rtbBid.cpm !== 0 && includes(this.supportedMediaTypes, rtbBid.ad_type)) { + const cpmCheck = (isAllowZeroCpmBidsEnabled(bidderRequest.bidderCode)) ? rtbBid.cpm >= 0 : rtbBid.cpm > 0; + if (cpmCheck && includes(this.supportedMediaTypes, rtbBid.ad_type)) { const bid = newBid(serverBid, rtbBid, bidderRequest); bid.mediaType = parseMediaType(rtbBid); bids.push(bid); @@ -598,6 +609,26 @@ function newBid(serverBid, rtbBid, bidderRequest) { bid.meta = Object.assign({}, bid.meta, { advertiserId: rtbBid.advertiser_id }); } + // temporary function; may remove at later date if/when adserver fully supports dchain + function setupDChain(rtbBid) { + let dchain = { + ver: '1.0', + complete: 0, + nodes: [{ + bsid: rtbBid.buyer_member_id.toString() + }], + }; + + return dchain; + } + if (rtbBid.buyer_member_id) { + bid.meta = Object.assign({}, bid.meta, {dchain: setupDChain(rtbBid)}); + } + + if (rtbBid.brand_id) { + bid.meta = Object.assign({}, bid.meta, { brandId: rtbBid.brand_id }); + } + if (rtbBid.rtb.video) { // shared video properties used for all 3 contexts Object.assign(bid, { @@ -696,9 +727,11 @@ function newBid(serverBid, rtbBid, bidderRequest) { }); try { if (rtbBid.rtb.trackers) { - const url = rtbBid.rtb.trackers[0].impression_urls[0]; - const tracker = createTrackPixelHtml(url); - bid.ad += tracker; + for (let i = 0; i < rtbBid.rtb.trackers[0].impression_urls.length; i++) { + const url = rtbBid.rtb.trackers[0].impression_urls[i]; + const tracker = createTrackPixelHtml(url); + bid.ad += tracker; + } } } catch (error) { logError('Error appending tracking pixel', error); diff --git a/modules/asealBidAdapter.js b/modules/asealBidAdapter.js new file mode 100644 index 00000000000..559afefa94b --- /dev/null +++ b/modules/asealBidAdapter.js @@ -0,0 +1,58 @@ +import { registerBidder } from '../src/adapters/bidderFactory.js'; +import { BANNER } from '../src/mediaTypes.js'; +import { config } from '../src/config.js'; + +export const BIDDER_CODE = 'aseal'; +const SUPPORTED_AD_TYPES = [BANNER]; +export const API_ENDPOINT = 'https://tkprebid.aotter.net/prebid/adapter'; +export const HEADER_AOTTER_VERSION = 'prebid_0.0.1'; + +export const spec = { + code: BIDDER_CODE, + aliases: ['aotter', 'trek'], + supportedMediaTypes: SUPPORTED_AD_TYPES, + + isBidRequestValid: (bid) => !!bid.params.placeUid && typeof bid.params.placeUid === 'string', + + buildRequests: (validBidRequests, bidderRequest) => { + if (validBidRequests.length === 0) { + return []; + } + + const clientId = + config.getConfig('aseal.clientId') || ''; + + const data = { + bids: validBidRequests, + refererInfo: bidderRequest.refererInfo, + }; + + const options = { + contentType: 'application/json', + withCredentials: true, + customHeaders: { + 'x-aotter-clientid': clientId, + 'x-aotter-version': HEADER_AOTTER_VERSION, + }, + }; + + return [{ + method: 'POST', + url: API_ENDPOINT, + data, + options, + }]; + }, + + interpretResponse: (serverResponse, bidRequest) => { + if (!Array.isArray(serverResponse.body)) { + return []; + } + + const bidResponses = serverResponse.body; + + return bidResponses; + }, +}; + +registerBidder(spec); diff --git a/modules/asealBidAdapter.md b/modules/asealBidAdapter.md new file mode 100644 index 00000000000..d13b802f736 --- /dev/null +++ b/modules/asealBidAdapter.md @@ -0,0 +1,52 @@ +# Overview + +``` +Module Name: Aseal Bid Adapter +Module Type: Bidder Adapter +Maintainer: tech-service@aotter.net +``` + +# Description + +Module that connects to Aseal server for bids. +Supported Ad Formats: + +- Banner + +# Configuration + +Following configuration is required: + +```js +pbjs.setConfig({ + aseal: { + clientId: "YOUR_CLIENT_ID" + } +}); +``` + +# Ad Unit Example + +```js +var adUnits = [ + { + code: "banner-div", + mediaTypes: { + banner: { + sizes: [ + [300, 250], + [300, 600] + ] + } + }, + bids: [ + { + bidder: "aseal", + params: { + placeUid: "f4a74f73-9a74-4a87-91c9-545c6316c23d" + } + } + ] + } +]; +``` diff --git a/modules/atsAnalyticsAdapter.js b/modules/atsAnalyticsAdapter.js index d1e520b4b8f..f45d2e80055 100644 --- a/modules/atsAnalyticsAdapter.js +++ b/modules/atsAnalyticsAdapter.js @@ -20,7 +20,7 @@ export const analyticsUrl = 'https://analytics.rlcdn.com'; let handlerRequest = []; let handlerResponse = []; -let atsAnalyticsAdapterVersion = 2; +let atsAnalyticsAdapterVersion = 3; let browsersList = [ /* Googlebot */ @@ -222,7 +222,8 @@ function bidRequestedHandler(args) { auction_start: new Date(args.auctionStart).toJSON(), domain: window.location.hostname, pid: atsAnalyticsAdapter.context.pid, - adapter_version: atsAnalyticsAdapterVersion + adapter_version: atsAnalyticsAdapterVersion, + bid_won: false }; }); return requests; @@ -251,13 +252,14 @@ export function parseBrowser() { } } -function sendDataToAnalytic () { +function sendDataToAnalytic (events) { // send data to ats analytic endpoint try { - let dataToSend = {'Data': atsAnalyticsAdapter.context.events}; + let dataToSend = {'Data': events}; let strJSON = JSON.stringify(dataToSend); logInfo('ATS Analytics - tried to send analytics data!'); ajax(analyticsUrl, function () { + logInfo('ATS Analytics - events sent successfully!'); }, strJSON, {method: 'POST', contentType: 'application/json'}); } catch (err) { logError('ATS Analytics - request encounter an error: ', err); @@ -265,7 +267,7 @@ function sendDataToAnalytic () { } // preflight request, to check did publisher have permission to send data to analytics endpoint -function preflightRequest (envelopeSourceCookieValue) { +function preflightRequest (envelopeSourceCookieValue, events) { logInfo('ATS Analytics - preflight request!'); ajax(preflightUrl + atsAnalyticsAdapter.context.pid, { @@ -276,7 +278,8 @@ function preflightRequest (envelopeSourceCookieValue) { atsAnalyticsAdapter.setSamplingCookie(samplingRate); let samplingRateNumber = Number(samplingRate); if (data && samplingRate && atsAnalyticsAdapter.shouldFireRequest(samplingRateNumber) && envelopeSourceCookieValue != null) { - sendDataToAnalytic(); + logInfo('ATS Analytics - events to send: ', events); + sendDataToAnalytic(events); } }, error: function () { @@ -286,29 +289,6 @@ function preflightRequest (envelopeSourceCookieValue) { }, undefined, {method: 'GET', crossOrigin: true}); } -function callHandler(evtype, args) { - if (evtype === CONSTANTS.EVENTS.BID_REQUESTED) { - handlerRequest = handlerRequest.concat(bidRequestedHandler(args)); - } else if (evtype === CONSTANTS.EVENTS.BID_RESPONSE) { - handlerResponse.push(bidResponseHandler(args)); - } - if (evtype === CONSTANTS.EVENTS.AUCTION_END) { - if (handlerRequest.length) { - let events = []; - if (handlerResponse.length) { - events = handlerRequest.filter(request => handlerResponse.filter(function(response) { - if (request.bid_id === response.bid_id) { - Object.assign(request, response); - } - })); - } else { - events = handlerRequest; - } - atsAnalyticsAdapter.context.events = events; - } - } -} - let atsAnalyticsAdapter = Object.assign(adapter( { analyticsType @@ -316,22 +296,7 @@ let atsAnalyticsAdapter = Object.assign(adapter( { track({eventType, args}) { if (typeof args !== 'undefined') { - callHandler(eventType, args); - } - if (eventType === CONSTANTS.EVENTS.AUCTION_END) { - let envelopeSourceCookieValue = storage.getCookie('_lr_env_src_ats'); - try { - let samplingRateCookie = storage.getCookie('_lr_sampling_rate'); - if (!samplingRateCookie) { - preflightRequest(envelopeSourceCookieValue); - } else { - if (atsAnalyticsAdapter.shouldFireRequest(parseInt(samplingRateCookie)) && envelopeSourceCookieValue != null) { - sendDataToAnalytic(); - } - } - } catch (err) { - logError('ATS Analytics - preflight request encounter an error: ', err); - } + atsAnalyticsAdapter.callHandler(eventType, args); } } }); @@ -369,13 +334,69 @@ atsAnalyticsAdapter.enableAnalytics = function (config) { } atsAnalyticsAdapter.context = { events: [], - pid: config.options.pid + pid: config.options.pid, + bidWonTimeout: config.options.bidWonTimeout }; let initOptions = config.options; logInfo('ATS Analytics - adapter enabled! '); atsAnalyticsAdapter.originEnableAnalytics(initOptions); // call the base class function }; +atsAnalyticsAdapter.callHandler = function (evtype, args) { + if (evtype === CONSTANTS.EVENTS.BID_REQUESTED) { + handlerRequest = handlerRequest.concat(bidRequestedHandler(args)); + } else if (evtype === CONSTANTS.EVENTS.BID_RESPONSE) { + handlerResponse.push(bidResponseHandler(args)); + } + if (evtype === CONSTANTS.EVENTS.AUCTION_END) { + let bidWonTimeout = atsAnalyticsAdapter.context.bidWonTimeout ? atsAnalyticsAdapter.context.bidWonTimeout : 2000; + let events = []; + setTimeout(() => { + let winningBids = $$PREBID_GLOBAL$$.getAllWinningBids(); + logInfo('ATS Analytics - winning bids: ', winningBids) + // prepare format data for sending to analytics endpoint + if (handlerRequest.length) { + let wonEvent = {}; + if (handlerResponse.length) { + events = handlerRequest.filter(request => handlerResponse.filter(function (response) { + if (request.bid_id === response.bid_id) { + Object.assign(request, response); + } + })); + if (winningBids.length) { + events = events.filter(event => winningBids.filter(function (won) { + wonEvent.bid_id = won.requestId; + wonEvent.bid_won = true; + if (event.bid_id === wonEvent.bid_id) { + Object.assign(event, wonEvent); + } + })) + } + } else { + events = handlerRequest; + } + // check should we send data to analytics or not, check first cookie value _lr_sampling_rate + try { + let envelopeSourceCookieValue = storage.getCookie('_lr_env_src_ats'); + let samplingRateCookie = storage.getCookie('_lr_sampling_rate'); + if (!samplingRateCookie) { + preflightRequest(envelopeSourceCookieValue, events); + } else { + if (atsAnalyticsAdapter.shouldFireRequest(parseInt(samplingRateCookie)) && envelopeSourceCookieValue != null) { + logInfo('ATS Analytics - events to send: ', events); + sendDataToAnalytic(events); + } + } + // empty events array to not send duplicate events + events = []; + } catch (err) { + logError('ATS Analytics - preflight request encounter an error: ', err); + } + } + }, bidWonTimeout); + } +} + adaptermanager.registerAnalyticsAdapter({ adapter: atsAnalyticsAdapter, code: 'atsAnalytics', diff --git a/modules/atsAnalyticsAdapter.md b/modules/atsAnalyticsAdapter.md index 7c634f39ae2..17819ac61b3 100644 --- a/modules/atsAnalyticsAdapter.md +++ b/modules/atsAnalyticsAdapter.md @@ -17,6 +17,7 @@ Analytics adapter for Authenticated Traffic Solution(ATS), provided by LiveRamp. provider: 'atsAnalytics', options: { pid: '999', // publisher ID + bidWonTimeout: 2000 // on auction end for how long to wait for bid_won events, by default it's 2000 miliseconds, if it's not set it will be 2000 miliseconds. } } ``` diff --git a/modules/beachfrontBidAdapter.js b/modules/beachfrontBidAdapter.js index a882a796851..e705156d4a2 100644 --- a/modules/beachfrontBidAdapter.js +++ b/modules/beachfrontBidAdapter.js @@ -1,4 +1,4 @@ -import { logWarn, deepAccess, isArray, parseSizesInput, isFn, parseUrl, getUniqueIdentifierStr } from '../src/utils.js'; +import { logWarn, deepAccess, deepSetValue, deepClone, isArray, parseSizesInput, isFn, parseUrl, getUniqueIdentifierStr } from '../src/utils.js'; import { config } from '../src/config.js'; import { registerBidder } from '../src/adapters/bidderFactory.js'; import { Renderer } from '../src/Renderer.js'; @@ -6,7 +6,7 @@ import { VIDEO, BANNER } from '../src/mediaTypes.js'; import find from 'core-js-pure/features/array/find.js'; import includes from 'core-js-pure/features/array/includes.js'; -const ADAPTER_VERSION = '1.18'; +const ADAPTER_VERSION = '1.19'; const ADAPTER_NAME = 'BFIO_PREBID'; const OUTSTREAM = 'outstream'; const CURRENCY = 'USD'; @@ -360,6 +360,7 @@ function createVideoRequestData(bid, bidderRequest) { let tagid = getVideoBidParam(bid, 'tagid'); let topLocation = getTopWindowLocation(bidderRequest); let eids = getEids(bid); + let ortb2 = deepClone(config.getConfig('ortb2')); let payload = { isPrebid: true, appId: appId, @@ -378,6 +379,7 @@ function createVideoRequestData(bid, bidderRequest) { displaymanagerver: ADAPTER_VERSION }], site: { + ...deepAccess(ortb2, 'site', {}), page: topLocation.href, domain: topLocation.hostname }, @@ -389,39 +391,32 @@ function createVideoRequestData(bid, bidderRequest) { js: 1, geo: {} }, - regs: { - ext: {} - }, - source: { - ext: {} - }, - user: { - ext: {} - }, + app: deepAccess(ortb2, 'app'), + user: deepAccess(ortb2, 'user'), cur: [CURRENCY] }; if (bidderRequest && bidderRequest.uspConsent) { - payload.regs.ext.us_privacy = bidderRequest.uspConsent; + deepSetValue(payload, 'regs.ext.us_privacy', bidderRequest.uspConsent); } if (bidderRequest && bidderRequest.gdprConsent) { let { gdprApplies, consentString } = bidderRequest.gdprConsent; - payload.regs.ext.gdpr = gdprApplies ? 1 : 0; - payload.user.ext.consent = consentString; + deepSetValue(payload, 'regs.ext.gdpr', gdprApplies ? 1 : 0); + deepSetValue(payload, 'user.ext.consent', consentString); } if (bid.schain) { - payload.source.ext.schain = bid.schain; + deepSetValue(payload, 'source.ext.schain', bid.schain); } if (eids.length > 0) { - payload.user.ext.eids = eids; + deepSetValue(payload, 'user.ext.eids', eids); } let connection = navigator.connection || navigator.webkitConnection; if (connection && connection.effectiveType) { - payload.device.connectiontype = connection.effectiveType; + deepSetValue(payload, 'device.connectiontype', connection.effectiveType); } return payload; @@ -439,8 +434,10 @@ function createBannerRequestData(bids, bidderRequest) { sizes: getBannerSizes(bid) }; }); + let ortb2 = deepClone(config.getConfig('ortb2')); let payload = { slots: slots, + ortb2: ortb2, page: topLocation.href, domain: topLocation.hostname, search: topLocation.search, diff --git a/modules/beopBidAdapter.js b/modules/beopBidAdapter.js index a6bc8a5687d..2e74170fcaf 100644 --- a/modules/beopBidAdapter.js +++ b/modules/beopBidAdapter.js @@ -36,7 +36,7 @@ export const spec = { */ buildRequests: function(validBidRequests, bidderRequest) { const slots = validBidRequests.map(beOpRequestSlotsMaker); - let pageUrl = deepAccess(bidderRequest, 'refererInfo.canonicalUrl') || config.getConfig('pageUrl') || deepAccess(window, 'location.href'); + let pageUrl = deepAccess(window, 'location.href') || deepAccess(bidderRequest, 'refererInfo.canonicalUrl') || config.getConfig('pageUrl'); let fpd = config.getLegacyFpd(config.getConfig('ortb2')); let gdpr = bidderRequest.gdprConsent; let firstSlot = slots[0]; @@ -99,16 +99,18 @@ export const spec = { } function buildTrackingParams(data, info, value) { + const accountId = data.params.accountId; return { - pid: data.params.accountId, + pid: accountId === undefined ? data.ad.match(/account: \“([a-f\d]{24})\“/)[1] : accountId, nid: data.params.networkId, nptnid: data.params.networkPartnerId, - bid: data.bidId, + bid: data.bidId || data.requestId, sl_n: data.adUnitCode, aid: data.auctionId, se_ca: 'bid', se_ac: info, - se_va: value + se_va: value, + url: window.location.href }; } diff --git a/modules/betweenBidAdapter.js b/modules/betweenBidAdapter.js index 7ac1e4edf15..b2f63488e12 100644 --- a/modules/betweenBidAdapter.js +++ b/modules/betweenBidAdapter.js @@ -3,12 +3,14 @@ import { getAdUnitSizes, parseSizesInput } from '../src/utils.js'; import { getRefererInfo } from '../src/refererDetection.js'; const BIDDER_CODE = 'between'; -const ENDPOINT = 'https://ads.betweendigital.com/adjson?t=prebid'; +let ENDPOINT = 'https://ads.betweendigital.com/adjson?t=prebid'; +const CODE_TYPES = ['inpage', 'preroll', 'midroll', 'postroll']; +const includes = require('core-js-pure/features/array/includes.js'); export const spec = { code: BIDDER_CODE, aliases: ['btw'], - supportedMediaTypes: ['banner'], + supportedMediaTypes: ['banner', 'video'], /** * Determines whether or not the given bid request is valid. * @@ -30,6 +32,8 @@ export const spec = { const refInfo = getRefererInfo(); validBidRequests.forEach((i) => { + const video = i.mediaTypes && i.mediaTypes.video; + let params = { eids: getUsersIds(i), sizes: parseSizesInput(getAdUnitSizes(i)), @@ -38,12 +42,21 @@ export const spec = { tz: getTz(), fl: getFl(), rr: getRr(), - s: i.params.s, + s: i.params && i.params.s, bidid: i.bidId, transactionid: i.transactionId, auctionid: i.auctionId }; + if (video) { + params.mediaType = 2; + params.maxd = video.maxd; + params.mind = video.mind; + params.pos = 'atf'; + ENDPOINT += '&jst=pvc'; + params.codeType = includes(CODE_TYPES, video.codeType) ? video.codeType : 'inpage'; + } + if (i.params.itu !== undefined) { params.itu = i.params.itu; } @@ -94,12 +107,15 @@ export const spec = { */ interpretResponse: function(serverResponse, bidRequest) { const bidResponses = []; + for (var i = 0; i < serverResponse.body.length; i++) { let bidResponse = { requestId: serverResponse.body[i].bidid, cpm: serverResponse.body[i].cpm || 0, width: serverResponse.body[i].w, height: serverResponse.body[i].h, + vastXml: serverResponse.body[i].vastXml, + mediaType: serverResponse.body[i].mediaType, ttl: serverResponse.body[i].ttl, creativeId: serverResponse.body[i].creativeid, currency: serverResponse.body[i].currency || 'RUB', @@ -109,6 +125,7 @@ export const spec = { advertiserDomains: serverResponse.body[i].adomain ? serverResponse.body[i].adomain : [] } }; + bidResponses.push(bidResponse); } return bidResponses; diff --git a/modules/bidViewability.js b/modules/bidViewability.js index c3b72cda8d4..545d57940da 100644 --- a/modules/bidViewability.js +++ b/modules/bidViewability.js @@ -4,7 +4,7 @@ import { config } from '../src/config.js'; import * as events from '../src/events.js'; -import { EVENTS } from '../src/constants.json'; +import CONSTANTS from '../src/constants.json'; import { logWarn, isFn, triggerPixel } from '../src/utils.js'; import { getGlobal } from '../src/prebidGlobal.js'; import adapterManager, { gdprDataHandler, uspDataHandler } from '../src/adapterManager.js'; @@ -70,12 +70,12 @@ export let impressionViewableHandler = (globalModuleConfig, slot, event) => { // trigger respective bidder's onBidViewable handler adapterManager.callBidViewableBidder(respectiveBid.bidder, respectiveBid); // emit the BID_VIEWABLE event with bid details, this event can be consumed by bidders and analytics pixels - events.emit(EVENTS.BID_VIEWABLE, respectiveBid); + events.emit(CONSTANTS.EVENTS.BID_VIEWABLE, respectiveBid); } }; export let init = () => { - events.on(EVENTS.AUCTION_INIT, () => { + events.on(CONSTANTS.EVENTS.AUCTION_INIT, () => { // read the config for the module const globalModuleConfig = config.getConfig(MODULE_NAME) || {}; // do nothing if module-config.enabled is not set to true diff --git a/modules/bidViewabilityIO.js b/modules/bidViewabilityIO.js index d936fb4aeec..ff7ec70e32c 100644 --- a/modules/bidViewabilityIO.js +++ b/modules/bidViewabilityIO.js @@ -1,7 +1,7 @@ import { logMessage } from '../src/utils.js'; import { config } from '../src/config.js'; import * as events from '../src/events.js'; -import { EVENTS } from '../src/constants.json'; +import CONSTANTS from '../src/constants.json'; const MODULE_NAME = 'bidViewabilityIO'; const CONFIG_ENABLED = 'enabled'; @@ -42,7 +42,7 @@ export let getViewableOptions = (bid) => { export let markViewed = (bid, entry, observer) => { return () => { observer.unobserve(entry.target); - events.emit(EVENTS.BID_VIEWABLE, bid); + events.emit(CONSTANTS.EVENTS.BID_VIEWABLE, bid); _logMessage(`id: ${entry.target.getAttribute('id')} code: ${bid.adUnitCode} was viewed`); } } @@ -77,7 +77,7 @@ export let init = () => { if (conf[MODULE_NAME][CONFIG_ENABLED] && CLIENT_SUPPORTS_IO) { // if the module is enabled and the browser supports Intersection Observer, // then listen to AD_RENDER_SUCCEEDED to setup IO's for supported mediaTypes - events.on(EVENTS.AD_RENDER_SUCCEEDED, ({doc, bid, id}) => { + events.on(CONSTANTS.EVENTS.AD_RENDER_SUCCEEDED, ({doc, bid, id}) => { if (isSupportedMediaType(bid)) { let viewable = new IntersectionObserver(viewCallbackFactory(bid), getViewableOptions(bid)); let element = document.getElementById(bid.adUnitCode); diff --git a/modules/bliinkBidAdapter.js b/modules/bliinkBidAdapter.js index 2f4cb9beac6..45b6c46c2df 100644 --- a/modules/bliinkBidAdapter.js +++ b/modules/bliinkBidAdapter.js @@ -1,7 +1,6 @@ // eslint-disable-next-line prebid/validate-imports // eslint-disable-next-line prebid/validate-imports -import {registerBidder} from 'src/adapters/bidderFactory.js' - +import {registerBidder} from '../src/adapters/bidderFactory.js' export const BIDDER_CODE = 'bliink' export const BLIINK_ENDPOINT_ENGINE = 'https://engine.bliink.io/delivery' export const BLIINK_ENDPOINT_ENGINE_VAST = 'https://engine.bliink.io/vast' @@ -10,10 +9,9 @@ export const META_KEYWORDS = 'keywords' export const META_DESCRIPTION = 'description' const VIDEO = 'video' -const NATIVE = 'native' const BANNER = 'banner' -const supportedMediaTypes = [BANNER, VIDEO, NATIVE] +const supportedMediaTypes = [BANNER, VIDEO] const aliasBidderCode = ['bk'] export function getMetaList(name) { @@ -90,7 +88,11 @@ export const parseXML = (content) => { if (typeof content !== 'string' || content.length === 0) return null const parser = new DOMParser() - const xml = parser.parseFromString(content, 'text/xml') + let xml; + + try { + xml = parser.parseFromString(content, 'text/xml') + } catch (e) {} if (xml && xml.getElementsByTagName('VAST')[0] && @@ -104,19 +106,19 @@ export const parseXML = (content) => { /** * @param bidRequest * @param bliinkCreative - * @return {{cpm, netRevenue: boolean, ad: string, requestId, width: number, currency: string, mediaType: string, vastXml, ttl: number, height: number}|null} + * @return {{cpm, netRevenue: boolean, requestId, width: (*|number), currency, ttl: number, creativeId, height: (*|number)} & {mediaType: string, vastXml}} */ export const buildBid = (bidRequest, bliinkCreative) => { if (!bidRequest && !bliinkCreative) return null const body = { requestId: bidRequest.bidId, + currency: bliinkCreative.currency, cpm: bliinkCreative.price, creativeId: bliinkCreative.creativeId, - currency: 'EUR', + width: (bidRequest.sizes && bidRequest.sizes[0][0]) || 1, + height: (bidRequest.sizes && bidRequest.sizes[0][1]) || 1, netRevenue: false, - width: 1, - height: 1, ttl: 3600, } @@ -131,14 +133,20 @@ export const buildBid = (bidRequest, bliinkCreative) => { delete bidRequest['bids'] - return Object.assign(body, { - currency: bliinkCreative.currency, - width: 1, - height: 1, - mediaType: VIDEO, - ad: '', - vastXml: bliinkCreative.content, - }) + switch (bliinkCreative.media_type) { + case VIDEO: + return Object.assign(body, { + mediaType: VIDEO, + vastXml: bliinkCreative.content, + }) + case BANNER: + return Object.assign(body, { + mediaType: BANNER, + ad: (bliinkCreative && bliinkCreative.content && bliinkCreative.content.creative && bliinkCreative.content.creative.adm) || '', + }) + default: + break; + } } /** @@ -165,6 +173,8 @@ export const buildRequests = (_, bidderRequest) => { pageUrl: bidderRequest.refererInfo.referer, pageDescription: getMetaValue(META_DESCRIPTION), keywords: getKeywords().join(','), + gdpr: false, + gdpr_consent: '', pageTitle: document.title, } @@ -209,7 +219,7 @@ export const buildRequests = (_, bidderRequest) => { * @return */ const interpretResponse = (serverResponse, request) => { - if ((serverResponse && serverResponse.mode === 'no-ad') && (!request.params)) { + if ((serverResponse && serverResponse.mode === 'no-ad')) { return [] } @@ -218,23 +228,40 @@ const interpretResponse = (serverResponse, request) => { const xml = parseXML(body) - if (xml) { - const price = xml.getElementsByTagName('Price') && xml.getElementsByTagName('Price')[0] - const currency = xml.getElementsByTagName('Currency') && xml.getElementsByTagName('Currency')[0] - const creativeId = xml.getElementsByTagName('CreativeId') && xml.getElementsByTagName('CreativeId')[0] - - const creative = { - content: body, - price: (price && price.textContent) || 0, - currency: (currency && currency.textContent) || 'EUR', - creativeId: creativeId || 0, - media_type: 'video', - } - - return buildBid(serverBody.bids[0], creative); + let creative; + + switch (serverBody.bids[0].params.placement) { + case xml && VIDEO: + const price = xml.getElementsByTagName('Price') && xml.getElementsByTagName('Price')[0] + const currency = xml.getElementsByTagName('Currency') && xml.getElementsByTagName('Currency')[0] + const creativeId = xml.getElementsByTagName('CreativeId') && xml.getElementsByTagName('CreativeId')[0] + + creative = { + content: body, + price: (price && price.textContent) || 0, + currency: (currency && currency.textContent) || 'EUR', + creativeId: creativeId || 0, + media_type: 'video', + } + + return buildBid(serverBody.bids[0], creative) + case BANNER: + if (body) { + creative = { + content: body, + price: body.price, + currency: body.currency, + creativeId: 0, + media_type: 'banner', + } + + return buildBid(serverBody.bids[0], creative) + } + + break + default: + break } - - return [] } /** diff --git a/modules/bliinkBidAdapter.md b/modules/bliinkBidAdapter.md index ae0d4275396..af7aee3a1ae 100644 --- a/modules/bliinkBidAdapter.md +++ b/modules/bliinkBidAdapter.md @@ -31,7 +31,7 @@ const adUnits = [ bidder: 'bliink', params: { placement: 'banner', - tagId: '14f30eca-85d2-11e8-9eed-0242ac120007' + tagId: '41' } } ] @@ -50,11 +50,34 @@ const adUnits = [ mediaTypes: { video: { context: 'instream', - playerSize: [640, 480], - mimes: ['video/mp4'], - protocols: [1, 2, 3, 4, 5, 6, 7, 8], - playbackmethod: [2], - skip: 1 + playerSize: [[640,480]], + } + }, + bids: [ + { + bidder: 'bliink', + params: { + tagId: '41', + placement: 'video', + } + } + ] + } +] +``` + +## Sample outstream Video Ad Unit + +```js +const adUnits = [ + { + code: '/19968336/prebid_cache_video_adunit', + sizes: [[640,480]], + mediaType: 'video', + mediaTypes: { + video: { + context: 'outstream', + playerSize: [[640,480]], } }, bids: [ diff --git a/modules/brandmetricsRtdProvider.js b/modules/brandmetricsRtdProvider.js new file mode 100644 index 00000000000..60d3c98f15e --- /dev/null +++ b/modules/brandmetricsRtdProvider.js @@ -0,0 +1,168 @@ +/** + * This module adds brandmetrics provider to the real time data module + * The {@link module:modules/realTimeData} module is required + * The module will load load the brandmetrics script and set survey- targeting to ad units of specific bidders. + * @module modules/brandmetricsRtdProvider + * @requires module:modules/realTimeData + */ +import { config } from '../src/config.js' +import { submodule } from '../src/hook.js' +import { deepSetValue, mergeDeep, logError, deepAccess } from '../src/utils.js' +import {loadExternalScript} from '../src/adloader.js' +const MODULE_NAME = 'brandmetrics' +const MODULE_CODE = MODULE_NAME +const RECEIVED_EVENTS = [] +const GVL_ID = 422 +const TCF_PURPOSES = [1, 7] + +function init (config, userConsent) { + const hasConsent = checkConsent(userConsent) + + if (hasConsent) { + const moduleConfig = getMergedConfig(config) + initializeBrandmetrics(moduleConfig.params.scriptId) + } + return hasConsent +} + +/** + * Checks TCF and USP consents + * @param {Object} userConsent + * @returns {boolean} + */ +function checkConsent (userConsent) { + let consent = false + + if (userConsent && userConsent.gdpr && userConsent.gdpr.gdprApplies) { + const gdpr = userConsent.gdpr + + if (gdpr.vendorData) { + const vendor = gdpr.vendorData.vendor + const purpose = gdpr.vendorData.purpose + + let vendorConsent = false + if (vendor.consents) { + vendorConsent = vendor.consents[GVL_ID] + } + + if (vendor.legitimateInterests) { + vendorConsent = vendorConsent || vendor.legitimateInterests[GVL_ID] + } + + const purposes = TCF_PURPOSES.map(id => { + return (purpose.consents && purpose.consents[id]) || (purpose.legitimateInterests && purpose.legitimateInterests[id]) + }) + const purposesValid = purposes.filter(p => p === true).length === TCF_PURPOSES.length + consent = vendorConsent && purposesValid + } + } else if (userConsent.usp) { + const usp = userConsent.usp + consent = usp[1] !== 'N' && usp[2] !== 'Y' + } + + return consent +} + +/** +* Add event- listeners to hook in to brandmetrics events +* @param {Object} reqBidsConfigObj +* @param {function} callback +*/ +function processBrandmetricsEvents (reqBidsConfigObj, moduleConfig, callback) { + const callBidTargeting = (event) => { + if (event.available && event.conf) { + const targetingConf = event.conf.displayOption || {} + if (targetingConf.type === 'pbjs') { + setBidderTargeting(reqBidsConfigObj, moduleConfig, targetingConf.targetKey || 'brandmetrics_survey', event.survey.measurementId) + } + } + callback() + } + + if (RECEIVED_EVENTS.length > 0) { + callBidTargeting(RECEIVED_EVENTS[RECEIVED_EVENTS.length - 1]) + } else { + window._brandmetrics = window._brandmetrics || [] + window._brandmetrics.push({ + cmd: '_addeventlistener', + val: { + event: 'surveyloaded', + reEmitLast: true, + handler: (ev) => { + RECEIVED_EVENTS.push(ev) + if (RECEIVED_EVENTS.length === 1) { + // Call bid targeting only for the first received event, if called subsequently, last event from the RECEIVED_EVENTS array is used + callBidTargeting(ev) + } + }, + } + }) + } +} + +/** + * Sets bid targeting of specific bidders + * @param {Object} reqBidsConfigObj + * @param {string} key Targeting key + * @param {string} val Targeting value + */ +function setBidderTargeting (reqBidsConfigObj, moduleConfig, key, val) { + const bidders = deepAccess(moduleConfig, 'params.bidders') + if (bidders && bidders.length > 0) { + const ortb2 = {} + deepSetValue(ortb2, 'ortb2.user.ext.data.' + key, val) + config.setBidderConfig({ + bidders: bidders, + config: ortb2 + }) + } +} + +/** + * Add the brandmetrics script to the page. + * @param {string} scriptId - The script- id provided by brandmetrics or brandmetrics partner + */ +function initializeBrandmetrics(scriptId) { + if (scriptId) { + const path = 'https://cdn.brandmetrics.com/survey/script/' + const file = scriptId + '.js' + const url = path + file + + loadExternalScript(url, MODULE_CODE) + } +} + +/** + * Merges a provided config with default values + * @param {Object} customConfig + * @returns + */ +function getMergedConfig(customConfig) { + return mergeDeep({ + waitForIt: false, + params: { + bidders: [], + scriptId: undefined, + } + }, customConfig) +} + +/** @type {RtdSubmodule} */ +export const brandmetricsSubmodule = { + name: MODULE_NAME, + getBidRequestData: function (reqBidsConfigObj, callback, customConfig) { + try { + const moduleConfig = getMergedConfig(customConfig) + if (moduleConfig.waitForIt) { + processBrandmetricsEvents(reqBidsConfigObj, moduleConfig, callback) + } else { + callback() + } + } catch (e) { + logError(e) + } + }, + init: init +} + +submodule('realTimeData', brandmetricsSubmodule) diff --git a/modules/brandmetricsRtdProvider.md b/modules/brandmetricsRtdProvider.md new file mode 100644 index 00000000000..89ee6bb75cf --- /dev/null +++ b/modules/brandmetricsRtdProvider.md @@ -0,0 +1,40 @@ +# Brandmetrics Real-time Data Submodule +This module is intended to be used by brandmetrics (https://brandmetrics.com) partners and sets targeting keywords to bids if the browser is eligeble to see a brandmetrics survey. +The module hooks in to brandmetrics events and requires a brandmetrics script to be running. The module can optionally load and initialize brandmetrics by providing the 'scriptId'- parameter. + +## Usage +Compile the Brandmetrics RTD module into your Prebid build: +``` +gulp build --modules=rtdModule,brandmetricsRtdProvider +``` + +> Note that the global RTD module, `rtdModule`, is a prerequisite of the Brandmetrics RTD module. + +Enable the Brandmetrics RTD in your Prebid configuration, using the below format: + +```javascript +pbjs.setConfig({ + ..., + realTimeData: { + auctionDelay: 500, // auction delay + dataProviders: [{ + name: 'brandmetrics', + waitForIt: true // should be true if there's an `auctionDelay`, + params: { + scriptId: '00000000-0000-0000-0000-000000000000', + bidders: ['ozone'] + } + }] + }, + ... +}) +``` + +## Parameters +| Name | Type | Description | Default | +| ----------------- | -------------------- | ------------------ | ------------------ | +| name | String | This should always be `brandmetrics` | - | +| waitForIt | Boolean | Should be `true` if there's an `auctionDelay` defined (recommended) | `false` | +| params | Object | | - | +| params.bidders | String[] | An array of bidders which should receive targeting keys. | `[]` | +| params.scriptId | String | A script- id GUID if the brandmetrics- script should be initialized. | `undefined` | diff --git a/modules/browsiRtdProvider.js b/modules/browsiRtdProvider.js index d527223964e..a1943afda8d 100644 --- a/modules/browsiRtdProvider.js +++ b/modules/browsiRtdProvider.js @@ -15,21 +15,27 @@ * @property {?string} keyName */ -import { deepClone, logError, isGptPubadsDefined } from '../src/utils.js'; +import { deepClone, logError, isGptPubadsDefined, isNumber, isFn, deepSetValue } from '../src/utils.js'; import {submodule} from '../src/hook.js'; import {ajaxBuilder} from '../src/ajax.js'; import {loadExternalScript} from '../src/adloader.js'; import {getStorageManager} from '../src/storageManager.js'; import find from 'core-js-pure/features/array/find.js'; +import {getGlobal} from '../src/prebidGlobal.js'; +import includes from 'core-js-pure/features/array/includes.js'; const storage = getStorageManager(); /** @type {ModuleParams} */ let _moduleParams = {}; /** @type {null|Object} */ -let _predictionsData = null; +let _browsiData = null; /** @type {string} */ const DEF_KEYNAME = 'browsiViewability'; +/** @type {null | function} */ +let _dataReadyCallback = null; +/** @type {null|Object} */ +let _ic = {}; /** * add browsi script to page @@ -78,29 +84,49 @@ export function collectData() { getPredictionsFromServer(`//${_moduleParams.url}/prebid?${toUrlParams(predictorData)}`); } +/** + * wait for data from server + * call callback when data is ready + * @param {function} callback + */ +function waitForData(callback) { + if (_browsiData) { + _dataReadyCallback = null; + callback(_browsiData); + } else { + _dataReadyCallback = callback; + } +} + export function setData(data) { - _predictionsData = data; + _browsiData = data; + if (isFn(_dataReadyCallback)) { + _dataReadyCallback(_browsiData); + _dataReadyCallback = null; + } } -function sendDataToModule(adUnitsCodes) { +function getRTD(auc) { try { - const _predictions = (_predictionsData && _predictionsData.p) || {}; - return adUnitsCodes.reduce((rp, adUnitCode) => { - if (!adUnitCode) { + const _bp = (_browsiData && _browsiData.p) || {}; + return auc.reduce((rp, uc) => { + _ic[uc] = _ic[uc] || 0; + const _c = _ic[uc]; + if (!uc) { return rp } - const adSlot = getSlotByCode(adUnitCode); - const identifier = adSlot ? getMacroId(_predictionsData['pmd'], adSlot) : adUnitCode; - const predictionData = _predictions[identifier]; - rp[adUnitCode] = getKVObject(-1, _predictionsData['kn']); - if (!predictionData) { + const adSlot = getSlotByCode(uc); + const identifier = adSlot ? getMacroId(_browsiData['pmd'], adSlot) : uc; + const _pd = _bp[identifier]; + rp[uc] = getKVObject(-1); + if (!_pd) { return rp } - if (predictionData.p) { - if (!isIdMatchingAdUnit(adSlot, predictionData.w)) { + if (_pd.ps) { + if (!isIdMatchingAdUnit(adSlot, _pd.w)) { return rp; } - rp[adUnitCode] = getKVObject(predictionData.p, _predictionsData.kn); + rp[uc] = getKVObject(getCurrentData(_pd.ps, _c)); } return rp; }, {}); @@ -109,6 +135,31 @@ function sendDataToModule(adUnitsCodes) { } } +/** + * get prediction + * return -1 if prediction not found + * @param {object} predictionObject + * @param {number} _c + * @return {number} + */ +export function getCurrentData(predictionObject, _c) { + if (!predictionObject || !isNumber(_c)) { + return -1; + } + if (isNumber(predictionObject[_c])) { + return predictionObject[_c]; + } + if (Object.keys(predictionObject).length > 1) { + while (_c > 0) { + _c--; + if (isNumber(predictionObject[_c])) { + return predictionObject[_c]; + } + } + } + return -1; +} + /** * get all slots on page * @return {Object[]} slot GoogleTag slots @@ -122,12 +173,16 @@ function getAllSlots() { * @param {string?} keyName * @return {Object} key:value */ -function getKVObject(p, keyName) { +function getKVObject(p) { const prValue = p < 0 ? 'NA' : (Math.floor(p * 10) / 10).toFixed(2); let prObject = {}; - prObject[((_moduleParams['keyName'] || keyName || DEF_KEYNAME).toString())] = prValue.toString(); + prObject[getKey()] = prValue.toString(); return prObject; } + +function getKey() { + return ((_moduleParams['keyName'] || (_browsiData && _browsiData['kn']) || DEF_KEYNAME).toString()) +} /** * check if placement id matches one of given ad units * @param {Object} slot google slot @@ -238,6 +293,28 @@ function toUrlParams(data) { .join('&'); } +function setBidRequestsData(bidObj, callback) { + let adUnitCodes = bidObj.adUnitCodes; + let adUnits = bidObj.adUnits || getGlobal().adUnits || []; + if (adUnitCodes) { + adUnits = adUnits.filter(au => includes(adUnitCodes, au.code)); + } else { + adUnitCodes = adUnits.map(au => au.code); + } + waitForData(() => { + const data = getRTD(adUnitCodes); + if (data) { + adUnits.forEach(adUnit => { + const adUnitCode = adUnit.code; + if (data[adUnitCode]) { + deepSetValue(adUnit, 'ortb2Imp.ext.data.browsi', {[getKey()]: data[adUnitCode][getKey()]}); + } + }); + } + callback(); + }) +} + /** @type {RtdSubmodule} */ export const browsiSubmodule = { /** @@ -250,10 +327,21 @@ export const browsiSubmodule = { * @function * @param {string[]} adUnitsCodes */ - getTargetingData: sendDataToModule, + getTargetingData: getTargetingData, init: init, + getBidRequestData: setBidRequestsData }; +function getTargetingData(uc) { + const targetingData = getRTD(uc); + uc.forEach(auc => { + if (isNumber(_ic[auc])) { + _ic[auc] = _ic[auc] + 1; + } + }); + return targetingData; +} + function init(moduleConfig) { _moduleParams = moduleConfig.params; if (_moduleParams && _moduleParams.siteKey && _moduleParams.pubKey && _moduleParams.url) { diff --git a/modules/cleanioRtdProvider.js b/modules/cleanioRtdProvider.js new file mode 100644 index 00000000000..b9fdcef768e --- /dev/null +++ b/modules/cleanioRtdProvider.js @@ -0,0 +1,192 @@ +/** + * This module adds clean.io provider to the real time data module + * The {@link module:modules/realTimeData} module is required + * The module will wrap bid responses markup in clean.io agent script for protection + * @module modules/cleanioRtdProvider + * @requires module:modules/realTimeData + */ + +import { submodule } from '../src/hook.js'; +import { logError, generateUUID, insertElement } from '../src/utils.js'; + +// ============================ MODULE STATE =============================== + +/** + * @type {function(): void} + * Page-wide initialization step / strategy + */ +let onModuleInit = () => {}; + +/** + * @type {function(Object): void} + * Bid response mutation step / strategy. + */ +let onBidResponse = () => {}; + +/** + * @type {number} + * 0 for unknown, 1 for preloaded, -1 for error. + */ +let preloadStatus = 0; + +// ============================ MODULE LOGIC =============================== + +/** + * Page initialization step which just preloads the script, to be available whenever we start processing the bids. + * @param {string} scriptURL The script URL to preload + */ +function pageInitStepPreloadScript(scriptURL) { + const linkElement = document.createElement('link'); + linkElement.rel = 'preload'; + linkElement.as = 'script'; + linkElement.href = scriptURL; + linkElement.onload = () => { preloadStatus = 1; }; + linkElement.onerror = () => { preloadStatus = -1; }; + insertElement(linkElement); +} + +/** + * Page initialization step which adds the protector script to the whole page. With that, there is no need wrapping bids, and the coverage is better. + * @param {string} scriptURL The script URL to add to the page for protection + */ +function pageInitStepProtectPage(scriptURL) { + const scriptElement = document.createElement('script'); + scriptElement.type = 'text/javascript'; + scriptElement.src = scriptURL; + insertElement(scriptElement); +} + +/** + * Bid processing step which alters the ad HTML to contain bid-specific information, which can be used to identify the creative later. + * @param {Object} bidResponse Bid response data + */ +function bidWrapStepAugmentHtml(bidResponse) { + bidResponse.ad = `\n${bidResponse.ad}`; +} + +/** + * Bid processing step which applies creative protection by wrapping the ad HTML. + * @param {string} scriptURL + * @param {number} requiredPreload + * @param {Object} bidResponse + */ +function bidWrapStepProtectByWrapping(scriptURL, requiredPreload, bidResponse) { + // Still prepend bid info, it's always helpful to have creative data in its payload + bidWrapStepAugmentHtml(bidResponse); + + // If preloading failed, or if configuration requires us to finish preloading - + // we should not process this bid any further + if (preloadStatus < requiredPreload) { + return; + } + + const sid = generateUUID(); + bidResponse.ad = ` + + + `; +} + +/** + * Custom error class to differentiate validation errors + */ +class ConfigError extends Error { } + +/** + * The function to be called upon module init. Depending on the passed config, initializes properly init/bid steps or throws ConfigError. + * @param {Object} config + */ +function readConfig(config) { + if (!config.params) { + throw new ConfigError('Missing config parameters for clean.io RTD module provider.'); + } + + if (typeof config.params.cdnUrl !== 'string' || !/^https?:\/\//.test(config.params.cdnUrl)) { + throw new ConfigError('Parameter "cdnUrl" is a required string parameter, which should start with "http(s)://".'); + } + + if (typeof config.params.protectionMode !== 'string') { + throw new ConfigError('Parameter "protectionMode" is a required string parameter.'); + } + + const scriptURL = config.params.cdnUrl; + + switch (config.params.protectionMode) { + case 'full': + onModuleInit = () => pageInitStepProtectPage(scriptURL); + onBidResponse = (bidResponse) => bidWrapStepAugmentHtml(bidResponse); + break; + + case 'bids': + onModuleInit = () => pageInitStepPreloadScript(scriptURL); + onBidResponse = (bidResponse) => bidWrapStepProtectByWrapping(scriptURL, 0, bidResponse); + break; + + case 'bids-nowait': + onModuleInit = () => pageInitStepPreloadScript(scriptURL); + onBidResponse = (bidResponse) => bidWrapStepProtectByWrapping(scriptURL, 1, bidResponse); + break; + + default: + throw new ConfigError('Parameter "protectionMode" must be one of "full" | "bids" | "bids-nowait".'); + } +} + +// ============================ MODULE REGISTRATION =============================== + +/** + * The function which performs submodule registration. + */ +function beforeInit() { + submodule('realTimeData', /** @type {RtdSubmodule} */ ({ + name: 'clean.io', + + init: (config, userConsent) => { + try { + readConfig(config); + onModuleInit(); + return true; + } catch (err) { + if (err instanceof ConfigError) { + logError(err.message); + } + return false; + } + }, + + onBidResponseEvent: (bidResponse, config, userConsent) => { + onBidResponse(bidResponse); + } + })); +} + +/** + * Exporting local (and otherwise encapsulated to this module) functions + * for testing purposes + */ +export const __TEST__ = { + pageInitStepPreloadScript, + pageInitStepProtectPage, + bidWrapStepAugmentHtml, + bidWrapStepProtectByWrapping, + ConfigError, + readConfig, + beforeInit, +} + +beforeInit(); diff --git a/modules/cleanioRtdProvider.md b/modules/cleanioRtdProvider.md new file mode 100644 index 00000000000..7870a2719b6 --- /dev/null +++ b/modules/cleanioRtdProvider.md @@ -0,0 +1,59 @@ +# Overview + +``` +Module Name: clean.io Rtd provider +Module Type: Rtd Provider +Maintainer: nick@clean.io +``` + +The clean.io Realtime module provides effective anti-malvertising solution for publishers, including, but not limited to, +blocking unwanted 0- and 1-click redirects, deceptive ads or those with malicious landing pages, and various types of affiliate fraud. + +Using this module requires prior agreement with [clean.io](https://clean.io) to obtain the necessary distribution key. + + +# Integration + +clean.io Realtime module can be built just like any other prebid module: + +``` +gulp build --modules=cleanioRtdProvider,... +``` + + +# Configuration + +When built into prebid.js, this module can be configured through the following `pbjs.setConfig` call: + +```javascript +pbjs.setConfig({ + realTimeData: { + dataProviders: [{ + name: 'clean.io', + params: { + cdnUrl: 'https://abc1234567890.cloudfront.net/script.js', ///< Contact clean.io to get your own CDN URL + protectionMode: 'full', ///< Supported modes are 'full', 'bids' and 'bids-nowait', see below. + } + }] + } +}); +``` + + +## Configuration parameters + +{: .table .table-bordered .table-striped } +| Name | Type | Scope | Description | +| :------------ | :------------ | :------------ |:------------ | +| ``cdnUrl`` | ``string`` | Required | CDN URL of the script, which is to be used for protection. | +| ``protectionMode`` | ``'full' \| 'bids' \| 'bids-nowait'`` | Required | Integration mode. Please refer to the "Integration modes" section for details. | + + +## Integration modes + +{: .table .table-bordered .table-striped } +| Integration Mode | Parameter Value | Description | +| :------------ | :------------ | :------------ | +| Full page protection | ``'full'`` | Preferred mode. The module will add the protector agent script directly to the page, and it will protect all placements. This mode will make the most out of various behavioral detection mechanisms, and will also prevent typical malicious behaviors. Please note that in this mode, depending on Prebid library naming, Chrome may mistakenly tag non-ad-related content as ads: https://chromium.googlesource.com/chromium/src/+/refs/heads/main/docs/ad_tagging.md. | +| Bids-only protection | ``'bids'`` | The module will protect specific bid responses, more specifically, the HTML representing ad payload, by wrapping it into the agent script. Please note that in this mode, ads delivered directly, outside of Prebid integration, will not be protected, since the module can only access the ads coming through Prebid. | +| Bids-only protection with no delay on bid rendering | ``'bids-nowait'`` | Same as above, but in this mode, the script will also *not* wrap those bid responses, which arrived prior to successful preloading of agent script. | diff --git a/modules/codefuelBidAdapter.js b/modules/codefuelBidAdapter.js new file mode 100644 index 00000000000..b9da86ac24e --- /dev/null +++ b/modules/codefuelBidAdapter.js @@ -0,0 +1,181 @@ +import { deepAccess, isArray } from '../src/utils.js'; +import {registerBidder} from '../src/adapters/bidderFactory.js'; +import { BANNER } from '../src/mediaTypes.js'; +const BIDDER_CODE = 'codefuel'; +const CURRENCY = 'USD'; + +export const spec = { + code: BIDDER_CODE, + supportedMediaTypes: [ BANNER ], + aliases: ['ex'], // short code + /** + * Determines whether or not the given bid request is valid. + * + * @param {BidRequest} bid The bid params to validate. + * @return boolean True if this is a valid bid, and false otherwise. + */ + isBidRequestValid: function(bid) { + if (bid.nativeParams) { + return false; + } + return !!(bid.params.placementId || (bid.params.member && bid.params.invCode)); + }, + /** + * Make a server request from the list of BidRequests. + * + * @param {validBidRequests[]} - an array of bids + * @return ServerRequest Info describing the request to the server. + */ + buildRequests: function(validBidRequests, bidderRequest) { + const page = bidderRequest.refererInfo.referer; + const domain = getDomainFromURL(page) + const ua = navigator.userAgent; + const devicetype = getDeviceType() + const publisher = setOnAny(validBidRequests, 'params.publisher'); + const cur = CURRENCY; + const endpointUrl = 'https://ai-p-codefuel-ds-rtb-us-east-1-k8s.seccint.com/prebid' + const timeout = bidderRequest.timeout; + + validBidRequests.forEach(bid => bid.netRevenue = 'net'); + + const imps = validBidRequests.map((bid, idx) => { + const imp = { + id: idx + 1 + '' + } + + if (bid.params.tagid) { + imp.tagid = bid.params.tagid + } + + if (bid.sizes) { + imp.banner = { + format: transformSizes(bid.sizes) + } + } + + return imp; + }); + + const request = { + id: bidderRequest.auctionId, + site: { page, domain, publisher }, + device: { ua, devicetype }, + source: { fd: 1 }, + cur: [cur], + tmax: timeout, + imp: imps, + }; + + return { + method: 'POST', + url: endpointUrl, + data: request, + bids: validBidRequests, + options: { + withCredentials: false + } + }; + }, + /** + * Unpack the response from the server into a list of bids. + * + * @param {ServerResponse} serverResponse A successful response from the server. + * @return {Bid[]} An array of bids which were nested inside the server. + */ + interpretResponse: (serverResponse, { bids }) => { + if (!serverResponse.body) { + return []; + } + const { seatbid, cur } = serverResponse.body; + + const bidResponses = flatten(seatbid.map(seat => seat.bid)).reduce((result, bid) => { + result[bid.impid - 1] = bid; + return result; + }, []); + + return bids.map((bid, id) => { + const bidResponse = bidResponses[id]; + if (bidResponse) { + const bidObject = { + requestId: bid.bidId, + cpm: bidResponse.price, + creativeId: bidResponse.crid, + ttl: 360, + netRevenue: true, + currency: cur, + mediaType: BANNER, + ad: bidResponse.adm, + width: bidResponse.w, + height: bidResponse.h, + meta: { advertiserDomains: bid.adomain ? bid.adomain : [] } + }; + return bidObject; + } + }).filter(Boolean); + }, + + /** + * Register the user sync pixels which should be dropped after the auction. + * + * @param {SyncOptions} syncOptions Which user syncs are allowed? + * @param {ServerResponse[]} serverResponses List of server's responses. + * @return {UserSync[]} The user syncs which should be dropped. + */ + getUserSyncs: function(syncOptions, serverResponses, gdprConsent, uspConsent) { + return []; + } + +} +registerBidder(spec); + +function getDomainFromURL(url) { + let anchor = document.createElement('a'); + anchor.href = url; + return anchor.hostname; +} + +function getDeviceType() { + if ((/ipad|android 3.0|xoom|sch-i800|playbook|tablet|kindle/i.test(navigator.userAgent.toLowerCase()))) { + return 5; // 'tablet' + } + if ((/iphone|ipod|android|blackberry|opera|mini|windows\sce|palm|smartphone|iemobile/i.test(navigator.userAgent.toLowerCase()))) { + return 4; // 'mobile' + } + return 2; // 'desktop' +} + +function setOnAny(collection, key) { + for (let i = 0, result; i < collection.length; i++) { + result = deepAccess(collection[i], key); + if (result) { + return result; + } + } +} + +function flatten(arr) { + return [].concat(...arr); +} + +/* Turn bid request sizes into ut-compatible format */ +function transformSizes(requestSizes) { + if (!isArray(requestSizes)) { + return []; + } + + if (requestSizes.length === 2 && !isArray(requestSizes[0])) { + return [{ + w: parseInt(requestSizes[0], 10), + h: parseInt(requestSizes[1], 10) + }]; + } else if (isArray(requestSizes[0])) { + return requestSizes.map(item => + ({ + w: parseInt(item[0], 10), + h: parseInt(item[1], 10) + }) + ); + } + + return []; +} diff --git a/modules/codefuelBidAdapter.md b/modules/codefuelBidAdapter.md new file mode 100644 index 00000000000..4e1a0dd6409 --- /dev/null +++ b/modules/codefuelBidAdapter.md @@ -0,0 +1,111 @@ +# Overview + +``` +Module Name: Codefuel Adapter +Module Type: Bidder Adapter +Maintainer: hayimm@codefuel.com +``` + +# Description + +Module that connects to Codefuel bidder to fetch bids. +Display format is supported but not native format. Using OpenRTB standard. + +# Configuration + +## Bidder and usersync URLs + +The Codefuel adapter does not work without setting the correct bidder. +You will receive the URLs when contacting us. + +``` +pbjs.setConfig({ + codefuel: { + bidderUrl: 'https://ai-p-codefuel-ds-rtb-us-east-1-k8s.seccint.com/prebid', + usersyncUrl: 'https://usersync-url.com' + } +}); +``` + + +# Test Native Parameters +``` + var adUnits = [ + code: '/19968336/prebid_native_example_1', + mediaTypes: { + native: { + image: { + required: false, + sizes: [100, 50] + }, + title: { + required: false, + len: 140 + }, + sponsoredBy: { + required: false + }, + clickUrl: { + required: false + }, + body: { + required: false + }, + icon: { + required: false, + sizes: [50, 50] + } + } + }, + bids: [{ + bidder: 'codefuel', + params: { + publisher: { + id: '2706', // required + name: 'Publishers Name', + domain: 'publisher.com' + }, + tagid: 'tag-id', + bcat: ['IAB1-1'], + badv: ['example.com'] + } + }] + ]; + + pbjs.setConfig({ + codefuel: { + bidderUrl: 'https://ai-p-codefuel-ds-rtb-us-east-1-k8s.seccint.com/prebid' + } + }); +``` + +# Test Display Parameters +``` + var adUnits = [ + code: '/19968336/prebid_display_example_1', + mediaTypes: { + banner: { + sizes: [[300, 250]] + } + }, + bids: [{ + bidder: 'codefuel', + params: { + publisher: { + id: '2706', // required + name: 'Publishers Name', + domain: 'publisher.com' + }, + tagid: 'tag-id', + bcat: ['IAB1-1'], + badv: ['example.com'] + }, + }] + ]; + + pbjs.setConfig({ + codefuel: { + bidderUrl: 'https://ai-p-codefuel-ds-rtb-us-east-1-k8s.seccint.com/prebid' + } + }); +``` diff --git a/modules/colossussspBidAdapter.js b/modules/colossussspBidAdapter.js index 5e7c58f28ad..72df7c7b465 100644 --- a/modules/colossussspBidAdapter.js +++ b/modules/colossussspBidAdapter.js @@ -1,6 +1,7 @@ import { getWindowTop, deepAccess, logMessage } from '../src/utils.js'; import { registerBidder } from '../src/adapters/bidderFactory.js'; import { BANNER, NATIVE, VIDEO } from '../src/mediaTypes.js'; +import { ajax } from '../src/ajax.js'; const BIDDER_CODE = 'colossusssp'; const G_URL = 'https://colossusssp.com/?c=o&m=multi'; @@ -46,7 +47,10 @@ export const spec = { * @return boolean True if this is a valid bid, and false otherwise. */ isBidRequestValid: (bid) => { - return Boolean(bid.bidId && bid.params && !isNaN(bid.params.placement_id)); + const validPlacamentId = bid.params && !isNaN(bid.params.placement_id); + const validGroupId = bid.params && !isNaN(bid.params.group_id); + + return Boolean(bid.bidId && (validPlacamentId || validGroupId)); }, /** @@ -60,13 +64,13 @@ export const spec = { const location = winTop.location; let placements = []; let request = { - 'deviceWidth': winTop.screen.width, - 'deviceHeight': winTop.screen.height, - 'language': (navigator && navigator.language) ? navigator.language : '', - 'secure': location.protocol === 'https:' ? 1 : 0, - 'host': location.host, - 'page': location.pathname, - 'placements': placements, + deviceWidth: winTop.screen.width, + deviceHeight: winTop.screen.height, + language: (navigator && navigator.language) ? navigator.language : '', + secure: location.protocol === 'https:' ? 1 : 0, + host: location.host, + page: location.pathname, + placements: placements, }; if (bidderRequest) { @@ -84,6 +88,7 @@ export const spec = { let traff = bid.params.traffic || BANNER let placement = { placementId: bid.params.placement_id, + groupId: bid.params.group_id, bidId: bid.bidId, sizes: bid.mediaTypes[traff].sizes, traffic: traff, @@ -175,6 +180,12 @@ export const spec = { type: 'image', url: G_URL_SYNC }]; + }, + + onBidWon: (bid) => { + if (bid.nurl) { + ajax(bid.nurl, null); + } } }; diff --git a/modules/colossussspBidAdapter.md b/modules/colossussspBidAdapter.md index 8797c648c95..4187dfbf36e 100644 --- a/modules/colossussspBidAdapter.md +++ b/modules/colossussspBidAdapter.md @@ -26,5 +26,19 @@ Module that connects to Colossus SSP demand sources traffic: 'banner' } }] - ]; + }, { + code: 'placementid_1', + mediaTypes: { + banner: { + sizes: [[300, 250], [300,600]] + } + }, + bids: [{ + bidder: 'colossusssp', + params: { + group_id: 0, + traffic: 'banner' + } + }] + }]; ``` diff --git a/modules/compassBidAdapter.js b/modules/compassBidAdapter.js new file mode 100644 index 00000000000..77f918276bc --- /dev/null +++ b/modules/compassBidAdapter.js @@ -0,0 +1,208 @@ +import { isFn, deepAccess, logMessage, logError } from '../src/utils.js'; + +import { registerBidder } from '../src/adapters/bidderFactory.js'; +import { BANNER, NATIVE, VIDEO } from '../src/mediaTypes.js'; +import { config } from '../src/config.js'; + +const BIDDER_CODE = 'compass'; +const AD_URL = 'https://sa-lb.deliverimp.com/pbjs'; +const SYNC_URL = 'https://sa-cs.deliverimp.com'; + +function isBidResponseValid(bid) { + if (!bid.requestId || !bid.cpm || !bid.creativeId || !bid.ttl || !bid.currency) { + return false; + } + + switch (bid.mediaType) { + case BANNER: + return Boolean(bid.width && bid.height && bid.ad); + case VIDEO: + return Boolean(bid.vastUrl || bid.vastXml); + case NATIVE: + return Boolean(bid.native && bid.native.impressionTrackers && bid.native.impressionTrackers.length); + default: + return false; + } +} + +function getPlacementReqData(bid) { + const { params, bidId, mediaTypes } = bid; + const schain = bid.schain || {}; + const { placementId, endpointId } = params; + const bidfloor = getBidFloor(bid); + + const placement = { + bidId, + schain, + bidfloor + }; + + if (placementId) { + placement.placementId = placementId; + placement.type = 'publisher'; + } else if (endpointId) { + placement.endpointId = endpointId; + placement.type = 'network'; + } + + if (mediaTypes && mediaTypes[BANNER]) { + placement.adFormat = BANNER; + placement.sizes = mediaTypes[BANNER].sizes; + } else if (mediaTypes && mediaTypes[VIDEO]) { + placement.adFormat = VIDEO; + placement.playerSize = mediaTypes[VIDEO].playerSize; + placement.minduration = mediaTypes[VIDEO].minduration; + placement.maxduration = mediaTypes[VIDEO].maxduration; + placement.mimes = mediaTypes[VIDEO].mimes; + placement.protocols = mediaTypes[VIDEO].protocols; + placement.startdelay = mediaTypes[VIDEO].startdelay; + placement.placement = mediaTypes[VIDEO].placement; + placement.skip = mediaTypes[VIDEO].skip; + placement.skipafter = mediaTypes[VIDEO].skipafter; + placement.minbitrate = mediaTypes[VIDEO].minbitrate; + placement.maxbitrate = mediaTypes[VIDEO].maxbitrate; + placement.delivery = mediaTypes[VIDEO].delivery; + placement.playbackmethod = mediaTypes[VIDEO].playbackmethod; + placement.api = mediaTypes[VIDEO].api; + placement.linearity = mediaTypes[VIDEO].linearity; + } else if (mediaTypes && mediaTypes[NATIVE]) { + placement.native = mediaTypes[NATIVE]; + placement.adFormat = NATIVE; + } + + return placement; +} + +function getBidFloor(bid) { + if (!isFn(bid.getFloor)) { + return deepAccess(bid, 'params.bidfloor', 0); + } + + try { + const bidFloor = bid.getFloor({ + currency: 'USD', + mediaType: '*', + size: '*', + }); + return bidFloor.floor; + } catch (err) { + logError(err); + return 0; + } +} + +export const spec = { + code: BIDDER_CODE, + supportedMediaTypes: [BANNER, VIDEO, NATIVE], + + isBidRequestValid: (bid = {}) => { + const { params, bidId, mediaTypes } = bid; + let valid = Boolean(bidId && params && (params.placementId || params.endpointId)); + + if (mediaTypes && mediaTypes[BANNER]) { + valid = valid && Boolean(mediaTypes[BANNER] && mediaTypes[BANNER].sizes); + } else if (mediaTypes && mediaTypes[VIDEO]) { + valid = valid && Boolean(mediaTypes[VIDEO] && mediaTypes[VIDEO].playerSize); + } else if (mediaTypes && mediaTypes[NATIVE]) { + valid = valid && Boolean(mediaTypes[NATIVE]); + } else { + valid = false; + } + return valid; + }, + + buildRequests: (validBidRequests = [], bidderRequest = {}) => { + let deviceWidth = 0; + let deviceHeight = 0; + + let winLocation; + try { + const winTop = window.top; + deviceWidth = winTop.screen.width; + deviceHeight = winTop.screen.height; + winLocation = winTop.location; + } catch (e) { + logMessage(e); + winLocation = window.location; + } + + const refferUrl = bidderRequest.refererInfo && bidderRequest.refererInfo.referer; + let refferLocation; + try { + refferLocation = refferUrl && new URL(refferUrl); + } catch (e) { + logMessage(e); + } + + let location = refferLocation || winLocation; + const language = (navigator && navigator.language) ? navigator.language.split('-')[0] : ''; + const host = location.host; + const page = location.pathname; + const secure = location.protocol === 'https:' ? 1 : 0; + const placements = []; + const request = { + deviceWidth, + deviceHeight, + language, + secure, + host, + page, + placements, + coppa: config.getConfig('coppa') === true ? 1 : 0, + ccpa: bidderRequest.uspConsent || undefined, + gdpr: bidderRequest.gdprConsent || undefined, + tmax: config.getConfig('bidderTimeout') + }; + + const len = validBidRequests.length; + for (let i = 0; i < len; i++) { + const bid = validBidRequests[i]; + placements.push(getPlacementReqData(bid)); + } + + return { + method: 'POST', + url: AD_URL, + data: request + }; + }, + + interpretResponse: (serverResponse) => { + let response = []; + for (let i = 0; i < serverResponse.body.length; i++) { + let resItem = serverResponse.body[i]; + if (isBidResponseValid(resItem)) { + const advertiserDomains = resItem.adomain && resItem.adomain.length ? resItem.adomain : []; + resItem.meta = { ...resItem.meta, advertiserDomains }; + + response.push(resItem); + } + } + return response; + }, + + getUserSyncs: (syncOptions, serverResponses, gdprConsent, uspConsent) => { + let syncType = syncOptions.iframeEnabled ? 'iframe' : 'image'; + let syncUrl = SYNC_URL + `/${syncType}?pbjs=1`; + if (gdprConsent && gdprConsent.consentString) { + if (typeof gdprConsent.gdprApplies === 'boolean') { + syncUrl += `&gdpr=${Number(gdprConsent.gdprApplies)}&gdpr_consent=${gdprConsent.consentString}`; + } else { + syncUrl += `&gdpr=0&gdpr_consent=${gdprConsent.consentString}`; + } + } + if (uspConsent && uspConsent.consentString) { + syncUrl += `&ccpa_consent=${uspConsent.consentString}`; + } + + const coppa = config.getConfig('coppa') ? 1 : 0; + syncUrl += `&coppa=${coppa}`; + + return [{ + type: syncType, + url: syncUrl + }]; + } +}; + +registerBidder(spec); diff --git a/modules/compassBidAdapter.md b/modules/compassBidAdapter.md new file mode 100644 index 00000000000..18d52c12384 --- /dev/null +++ b/modules/compassBidAdapter.md @@ -0,0 +1,79 @@ +# Overview + +``` +Module Name: Compass Bidder Adapter +Module Type: Compass Bidder Adapter +Maintainer: sa-support@brightcom.com +``` + +# Description + +Connects to Compass exchange for bids. +Compass bid adapter supports Banner, Video (instream and outstream) and Native. + +# Test Parameters +``` + var adUnits = [ + // Will return static test banner + { + code: 'adunit1', + mediaTypes: { + banner: { + sizes: [ [300, 250], [320, 50] ], + } + }, + bids: [ + { + bidder: 'compass', + params: { + placementId: 'testBanner', + } + } + ] + }, + { + code: 'addunit2', + mediaTypes: { + video: { + playerSize: [ [640, 480] ], + context: 'instream', + minduration: 5, + maxduration: 60, + } + }, + bids: [ + { + bidder: 'compass', + params: { + placementId: 'testVideo', + } + } + ] + }, + { + code: 'addunit3', + mediaTypes: { + native: { + title: { + required: true + }, + body: { + required: true + }, + icon: { + required: true, + size: [64, 64] + } + } + }, + bids: [ + { + bidder: 'compass', + params: { + placementId: 'testNative', + } + } + ] + } + ]; +``` \ No newline at end of file diff --git a/modules/connectIdSystem.js b/modules/connectIdSystem.js new file mode 100644 index 00000000000..ac44a8b5a2d --- /dev/null +++ b/modules/connectIdSystem.js @@ -0,0 +1,104 @@ +/** + * This module adds support for Yahoo ConnectID to the user ID module system. + * The {@link module:modules/userId} module is required + * @module modules/connectIdSystem + * @requires module:modules/userId + */ + +import {ajax} from '../src/ajax.js'; +import {submodule} from '../src/hook.js'; +import {logError, formatQS} from '../src/utils.js'; +import includes from 'core-js-pure/features/array/includes.js'; + +const MODULE_NAME = 'connectId'; +const VENDOR_ID = 25; +const PLACEHOLDER = '__PIXEL_ID__'; +const UPS_ENDPOINT = `https://ups.analytics.yahoo.com/ups/${PLACEHOLDER}/fed`; + +function isEUConsentRequired(consentData) { + return !!(consentData && consentData.gdpr && consentData.gdpr.gdprApplies); +} + +/** @type {Submodule} */ +export const connectIdSubmodule = { + /** + * used to link submodule with config + * @type {string} + */ + name: MODULE_NAME, + /** + * @type {Number} + */ + gvlid: VENDOR_ID, + /** + * decode the stored id value for passing to bid requests + * @function + * @returns {{connectId: string} | undefined} + */ + decode(value) { + return (typeof value === 'object' && value.connectid) + ? {connectId: value.connectid} : undefined; + }, + /** + * Gets the Yahoo ConnectID + * @function + * @param {SubmoduleConfig} [config] + * @param {ConsentData} [consentData] + * @returns {IdResponse|undefined} + */ + getId(config, consentData) { + const params = config.params || {}; + if (!params || typeof params.he !== 'string' || + (typeof params.pixelId === 'undefined' && typeof params.endpoint === 'undefined')) { + logError('The connectId submodule requires the \'he\' and \'pixelId\' parameters to be defined.'); + return; + } + + const data = { + '1p': includes([1, '1', true], params['1p']) ? '1' : '0', + he: params.he, + gdpr: isEUConsentRequired(consentData) ? '1' : '0', + gdpr_consent: isEUConsentRequired(consentData) ? consentData.gdpr.consentString : '', + us_privacy: consentData && consentData.uspConsent ? consentData.uspConsent : '' + }; + + if (params.pixelId) { + data.pixelId = params.pixelId + } + + const resp = function (callback) { + const callbacks = { + success: response => { + let responseObj; + if (response) { + try { + responseObj = JSON.parse(response); + } catch (error) { + logError(error); + } + } + callback(responseObj); + }, + error: error => { + logError(`${MODULE_NAME}: ID fetch encountered an error`, error); + callback(); + } + }; + const endpoint = UPS_ENDPOINT.replace(PLACEHOLDER, params.pixelId); + let url = `${params.endpoint || endpoint}?${formatQS(data)}`; + connectIdSubmodule.getAjaxFn()(url, callbacks, null, {method: 'GET', withCredentials: true}); + }; + return {callback: resp}; + }, + + /** + * Return the function used to perform XHR calls. + * Utilised for each of testing. + * @returns {Function} + */ + getAjaxFn() { + return ajax; + } +}; + +submodule('userId', connectIdSubmodule); diff --git a/modules/connectIdSystem.md b/modules/connectIdSystem.md new file mode 100644 index 00000000000..f2153e1b6cb --- /dev/null +++ b/modules/connectIdSystem.md @@ -0,0 +1,33 @@ +## Yahoo ConnectID User ID Submodule + +Yahoo ConnectID user ID Module. + +### Prebid Params + +``` +pbjs.setConfig({ + userSync: { + userIds: [{ + name: 'connectId', + storage: { + name: 'connectId', + type: 'html5', + expires: 15 + }, + params: { + pixelId: 58776, + he: '0bef996248d63cea1529cb86de31e9547a712d9f380146e98bbd39beec70355a' + } + }] + } +}); +``` +## Parameter Descriptions for the `usersync` Configuration Section +The below parameters apply only to the Yahoo ConnectID user ID Module. + +| Param under usersync.userIds[] | Scope | Type | Description | Example | +| --- | --- | --- | --- | --- | +| name | Required | String | ID value for the Yahoo ConnectID module - `"connectId"` | `"connectId"` | +| params | Required | Object | Data for Yahoo ConnectID initialization. | | +| params.pixelId | Required | Number | The Yahoo supplied publisher specific pixel Id | `8976` | +| params.he | Required | String | The SHA-256 hashed user email address | `"529cb86de31e9547a712d9f380146e98bbd39beec"` | diff --git a/modules/consentManagement.js b/modules/consentManagement.js index 65ffc9a4def..ecd0c0eec4b 100644 --- a/modules/consentManagement.js +++ b/modules/consentManagement.js @@ -371,7 +371,13 @@ function processCmpData(consentObject, hookConfig) { * General timeout callback when interacting with CMP takes too long. */ function cmpTimedOut(hookConfig) { - cmpFailed('CMP workflow exceeded timeout threshold.', hookConfig); + if (cmpVersion === 2) { + logWarn(`No response from CMP, continuing auction...`) + storeConsentData(undefined); + exitModule(null, hookConfig) + } else { + cmpFailed('CMP workflow exceeded timeout threshold.', hookConfig); + } } /** diff --git a/modules/consumableBidAdapter.md b/modules/consumableBidAdapter.md index 2189494ebd4..ba472899c49 100644 --- a/modules/consumableBidAdapter.md +++ b/modules/consumableBidAdapter.md @@ -4,7 +4,7 @@ Module Name: Consumable Bid Adapter Module Type: Consumable Adapter -Maintainer: naffis@consumable.com +Maintainer: prebid@consumable.com # Description diff --git a/modules/craftBidAdapter.js b/modules/craftBidAdapter.js index b98b72b59ad..812ec53d686 100644 --- a/modules/craftBidAdapter.js +++ b/modules/craftBidAdapter.js @@ -5,6 +5,7 @@ import { auctionManager } from '../src/auctionManager.js'; import find from 'core-js-pure/features/array/find.js'; import includes from 'core-js-pure/features/array/includes.js'; import { getStorageManager } from '../src/storageManager.js'; +import {ajax} from '../src/ajax.js'; const BIDDER_CODE = 'craft'; const URL_BASE = 'https://gacraft.jp/prebid-v3'; @@ -110,9 +111,10 @@ export const spec = { }, onBidWon: function(bid) { - var xhr = new XMLHttpRequest(); - xhr.open('POST', bid._prebidWon); - xhr.send(); + ajax(bid._prebidWon, null, null, { + method: 'POST', + contentType: 'application/json' + }); } }; diff --git a/modules/criteoBidAdapter.js b/modules/criteoBidAdapter.js index a4ec99e4fa8..0285ab6be5b 100644 --- a/modules/criteoBidAdapter.js +++ b/modules/criteoBidAdapter.js @@ -24,7 +24,7 @@ const LOG_PREFIX = 'Criteo: '; Unminified source code can be found in the privately shared repo: https://github.com/Prebid-org/prebid-js-external-js-criteo/blob/master/dist/prod.js */ const FAST_BID_VERSION_PLACEHOLDER = '%FAST_BID_VERSION%'; -export const FAST_BID_VERSION_CURRENT = 113; +export const FAST_BID_VERSION_CURRENT = 117; const FAST_BID_VERSION_LATEST = 'latest'; const FAST_BID_VERSION_NONE = 'none'; const PUBLISHER_TAG_URL_TEMPLATE = 'https://static.criteo.net/js/ld/publishertag.prebid' + FAST_BID_VERSION_PLACEHOLDER + '.js'; @@ -281,6 +281,7 @@ function checkNativeSendId(bidRequest) { */ function buildCdbRequest(context, bidRequests, bidderRequest) { let networkId; + let schain; const request = { publisher: { url: context.url, @@ -288,6 +289,7 @@ function buildCdbRequest(context, bidRequests, bidderRequest) { }, slots: bidRequests.map(bidRequest => { networkId = bidRequest.params.networkId || networkId; + schain = bidRequest.schain || schain; const slot = { impid: bidRequest.adUnitCode, transactionid: bidRequest.transactionId, @@ -344,6 +346,13 @@ function buildCdbRequest(context, bidRequests, bidderRequest) { if (networkId) { request.publisher.networkid = networkId; } + if (schain) { + request.source = { + ext: { + schain: schain + } + } + }; request.user = { ext: bidderRequest.userExt }; diff --git a/modules/criteoIdSystem.js b/modules/criteoIdSystem.js index c716a3c9cd6..ecf7b3aaac4 100644 --- a/modules/criteoIdSystem.js +++ b/modules/criteoIdSystem.js @@ -33,15 +33,37 @@ function getFromAllStorages(key) { return storage.getCookie(key) || storage.getDataFromLocalStorage(key); } -function saveOnAllStorages(key, value) { +function saveOnAllStorages(key, value, hostname) { if (key && value) { - storage.setCookie(key, value, expirationString); storage.setDataInLocalStorage(key, value); + setCookieOnAllDomains(key, value, expirationString, hostname, true); } } -function deleteFromAllStorages(key) { - storage.setCookie(key, '', pastDateString); +function setCookieOnAllDomains(key, value, expiration, hostname, stopOnSuccess) { + const subDomains = hostname.split('.'); + for (let i = 0; i < subDomains.length; ++i) { + // Try to write the cookie on this subdomain (we want it to be stored only on the TLD+1) + const domain = subDomains.slice(subDomains.length - i - 1, subDomains.length).join('.'); + + try { + storage.setCookie(key, value, expiration, null, '.' + domain); + + if (stopOnSuccess) { + // Try to read the cookie to check if we wrote it + const ck = storage.getCookie(key); + if (ck && ck === value) { + break; + } + } + } catch (error) { + + } + } +} + +function deleteFromAllStorages(key, hostname) { + setCookieOnAllDomains(key, '', pastDateString, hostname, true); storage.removeDataFromLocalStorage(key); } @@ -89,15 +111,15 @@ function callCriteoUserSync(parsedCriteoData, gdprString, callback) { const urlsToCall = typeof jsonResponse.acwsUrl === 'string' ? [jsonResponse.acwsUrl] : jsonResponse.acwsUrl; urlsToCall.forEach(url => triggerPixel(url)); } else if (jsonResponse.bundle) { - saveOnAllStorages(bundleStorageKey, jsonResponse.bundle); + saveOnAllStorages(bundleStorageKey, jsonResponse.bundle, domain); } if (jsonResponse.bidId) { - saveOnAllStorages(bididStorageKey, jsonResponse.bidId); + saveOnAllStorages(bididStorageKey, jsonResponse.bidId, domain); const criteoId = { criteoId: jsonResponse.bidId }; callback(criteoId); } else { - deleteFromAllStorages(bididStorageKey); + deleteFromAllStorages(bididStorageKey, domain); callback(); } }, diff --git a/modules/currency.js b/modules/currency.js index dc77ee21430..5f27e49798a 100644 --- a/modules/currency.js +++ b/modules/currency.js @@ -1,7 +1,7 @@ import { logInfo, logWarn, logError, logMessage } from '../src/utils.js'; import { getGlobal } from '../src/prebidGlobal.js'; import { createBid } from '../src/bidfactory.js'; -import { STATUS } from '../src/constants.json'; +import CONSTANTS from '../src/constants.json'; import { ajax } from '../src/ajax.js'; import { config } from '../src/config.js'; import { getHook } from '../src/hook.js'; @@ -20,6 +20,25 @@ export var currencyRates = {}; var bidderCurrencyDefault = {}; var defaultRates; +export const ready = (() => { + let isDone, resolver, promise; + function reset() { + isDone = false; + resolver = null; + promise = new Promise((resolve) => { + resolver = resolve; + if (isDone) resolve(); + }) + } + function done() { + isDone = true; + if (resolver != null) { resolver() } + } + reset(); + + return {done, reset, promise: () => promise} +})(); + /** * Configuration function for currency * @param {string} [config.adServerCurrency = 'USD'] @@ -138,11 +157,15 @@ function initCurrency(url) { logInfo('currencyRates set to ' + JSON.stringify(currencyRates)); currencyRatesLoaded = true; processBidResponseQueue(); + ready.done(); } catch (e) { errorSettingsRates('Failed to parse currencyRates response: ' + response); } }, - error: errorSettingsRates + error: function (...args) { + errorSettingsRates(...args); + ready.done(); + } } ); } @@ -197,6 +220,8 @@ export function addBidResponseHook(fn, adUnitCode, bid) { bidResponseQueue.push(wrapFunction(fn, this, [adUnitCode, bid])); if (!currencySupportEnabled || currencyRatesLoaded) { processBidResponseQueue(); + } else { + fn.bail(ready.promise()); } } @@ -219,7 +244,7 @@ function wrapFunction(fn, context, params) { } } catch (e) { logWarn('Returning NO_BID, getCurrencyConversion threw error: ', e); - params[1] = createBid(STATUS.NO_BID, { + params[1] = createBid(CONSTANTS.STATUS.NO_BID, { bidder: bid.bidderCode || bid.bidder, bidId: bid.requestId }); diff --git a/modules/cwireBidAdapter.js b/modules/cwireBidAdapter.js new file mode 100644 index 00000000000..c9caa78e5e7 --- /dev/null +++ b/modules/cwireBidAdapter.js @@ -0,0 +1,275 @@ +import {registerBidder} from '../src/adapters/bidderFactory.js'; +import { getRefererInfo } from '../src/refererDetection.js'; +import { getStorageManager } from '../src/storageManager.js'; +import {BANNER, VIDEO} from '../src/mediaTypes.js'; +import { OUTSTREAM } from '../src/video.js'; +import { + isArray, + isNumber, + generateUUID, + parseSizesInput, + deepAccess, + getParameterByName, + getValue, + getBidIdParameter, + logError, + logWarn, +} from '../src/utils.js'; +import { Renderer } from '../src/Renderer.js'; +import find from 'core-js-pure/features/array/find.js'; + +// ------------------------------------ +const BIDDER_CODE = 'cwire'; +export const ENDPOINT_URL = 'https://embed.cwi.re/delivery/prebid'; +export const RENDERER_URL = 'https://cdn.cwi.re/prebid/renderer/LATEST/renderer.min.js'; +// ------------------------------------ +export const CW_PAGE_VIEW_ID = generateUUID(); +const LS_CWID_KEY = 'cw_cwid'; +const CW_GROUPS_QUERY = 'cwgroups'; +const CW_CREATIVE_QUERY = 'cwcreative'; + +const storage = getStorageManager(); + +/** + * ------------------------------------ + * ------------------------------------ + * @param bid + * @returns {Array} + */ +export function getSlotSizes(bid) { + return parseSizesInput(getAllMediaSizes(bid)); +} + +/** + * ------------------------------------ + * ------------------------------------ + * @param bid + * @returns {*[]} + */ +export function getAllMediaSizes(bid) { + let playerSizes = deepAccess(bid, 'mediaTypes.video.playerSize'); + let videoSizes = deepAccess(bid, 'mediaTypes.video.sizes'); + let bannerSizes = deepAccess(bid, 'mediaTypes.banner.sizes'); + + const sizes = []; + + if (isArray(playerSizes)) { + playerSizes.forEach((s) => { + sizes.push(s); + }) + } + + if (isArray(videoSizes)) { + videoSizes.forEach((s) => { + sizes.push(s); + }) + } + + if (isArray(bannerSizes)) { + bannerSizes.forEach((s) => { + sizes.push(s); + }) + } + return sizes; +} + +const getQueryVariable = (variable) => { + let value = getParameterByName(variable); + if (value === '') { + value = null; + } + return value; +}; + +/** + * ------------------------------------ + * ------------------------------------ + * @param validBidRequests + * @returns {*[]} + */ +export const mapSlotsData = function(validBidRequests) { + const slots = []; + validBidRequests.forEach(bid => { + const bidObj = {}; + // get the pacement and page ids + let placementId = getValue(bid.params, 'placementId'); + let pageId = getValue(bid.params, 'pageId'); + let adUnitElementId = getValue(bid.params, 'adUnitElementId'); + // get the rest of the auction/bid/transaction info + bidObj.auctionId = getBidIdParameter('auctionId', bid); + bidObj.adUnitCode = getBidIdParameter('adUnitCode', bid); + bidObj.adUnitElementId = adUnitElementId; + bidObj.bidId = getBidIdParameter('bidId', bid); + bidObj.bidderRequestId = getBidIdParameter('bidderRequestId', bid); + bidObj.placementId = placementId; + bidObj.pageId = pageId; + bidObj.mediaTypes = getBidIdParameter('mediaTypes', bid); + bidObj.transactionId = getBidIdParameter('transactionId', bid); + bidObj.sizes = getSlotSizes(bid); + slots.push(bidObj); + }); + + return slots; +}; + +export const spec = { + code: BIDDER_CODE, + supportedMediaTypes: [BANNER, VIDEO], + /** + * Determines whether or not the given bid request is valid. + * + * @param {BidRequest} bid The bid params to validate. + * @return boolean True if this is a valid bid, and false otherwise. + */ + isBidRequestValid: function(bid) { + bid.params = bid.params || {}; + + // if ad unit elemt id not provided - use adUnitCode by default + if (!bid.params.adUnitElementId) { + bid.params.adUnitElementId = bid.code; + } + + if (!bid.params.placementId || !isNumber(bid.params.placementId)) { + logError('placementId not provided or invalid'); + return false; + } + + if (!bid.params.pageId || !isNumber(bid.params.pageId)) { + logError('pageId not provided'); + return false; + } + + return true; + }, + + /** + * ------------------------------------ + * Make a server request from the + * list of BidRequests. + * ------------------------------------ + * @param {validBidRequests[]} - an array of bids + * @return ServerRequest Info describing the request to the server. + */ + buildRequests: function(validBidRequests, bidderRequest) { + let slots = []; + let referer; + try { + referer = getRefererInfo().referer; + slots = mapSlotsData(validBidRequests); + } catch (e) { + logWarn(e); + } + + let refgroups = []; + + const cwCreativeId = getQueryVariable(CW_CREATIVE_QUERY); + const rgQuery = getQueryVariable(CW_GROUPS_QUERY); + if (rgQuery !== null) { + refgroups = rgQuery.split(','); + } + + const localStorageCWID = storage.localStorageIsEnabled() ? storage.getDataFromLocalStorage(LS_CWID_KEY) : null; + + const payload = { + cwid: localStorageCWID, + refgroups, + cwcreative: cwCreativeId, + slots: slots, + httpRef: referer || '', + pageViewId: CW_PAGE_VIEW_ID, + }; + + return { + method: 'POST', + url: ENDPOINT_URL, + data: payload + }; + }, + + /** + * Unpack the response from the server into a list of bids. + * + * @param {ServerResponse} serverResponse A successful response from the server. + * @return {Bid[]} An array of bids which were nested inside the server. + */ + interpretResponse: function(serverResponse, bidRequest) { + const bidResponses = []; + + try { + if (typeof bidRequest.data === 'string') { + bidRequest.data = JSON.parse(bidRequest.data); + } + const serverBody = serverResponse.body; + serverBody.bids.forEach((br) => { + const bidReq = find(bidRequest.data.slots, bid => bid.bidId === br.requestId); + + let mediaType = BANNER; + + const bidResponse = { + requestId: br.requestId, + cpm: br.cpm, + bidderCode: BIDDER_CODE, + width: br.dimensions[0], + height: br.dimensions[1], + creativeId: br.creativeId, + currency: br.currency, + netRevenue: br.netRevenue, + ttl: br.ttl, + meta: { + advertiserDomains: br.adomains ? br.advertiserDomains : [], + }, + + }; + + // ------------------------------------ + // IF BANNER + // ------------------------------------ + + if (deepAccess(bidReq, 'mediaTypes.banner')) { + bidResponse.ad = br.html; + } + // ------------------------------------ + // IF VIDEO + // ------------------------------------ + if (deepAccess(bidReq, 'mediaTypes.video')) { + mediaType = VIDEO; + bidResponse.vastXml = br.vastXml; + bidResponse.videoScript = br.html; + const mediaTypeContext = deepAccess(bidReq, 'mediaTypes.video.context'); + if (mediaTypeContext === OUTSTREAM) { + const r = Renderer.install({ + id: bidResponse.requestId, + adUnitCode: bidReq.adUnitCode, + url: RENDERER_URL, + loaded: false, + config: { + ...deepAccess(bidReq, 'mediaTypes.video'), + ...deepAccess(br, 'outstream', {}) + } + }); + + // set renderer + try { + bidResponse.renderer = r; + bidResponse.renderer.setRender(function(bid) { + if (window.CWIRE && window.CWIRE.outstream) { + window.CWIRE.outstream.renderAd(bid); + } + }); + } catch (err) { + logWarn('Prebid Error calling setRender on newRenderer', err); + } + } + } + + bidResponse.mediaType = mediaType; + bidResponses.push(bidResponse); + }); + } catch (e) { + logWarn(e); + } + + return bidResponses; + }, +} +registerBidder(spec); diff --git a/modules/cwireBidAdapter.md b/modules/cwireBidAdapter.md new file mode 100644 index 00000000000..fc0889e05ad --- /dev/null +++ b/modules/cwireBidAdapter.md @@ -0,0 +1,43 @@ +# Overview + +Module Name: C-WIRE Bid Adapter +Module Type: Adagio Adapter +Maintainer: dragan@cwire.ch + +## Description + +Connects to C-WIRE demand source to fetch bids. + +## Configuration + + +Below, the list of C-WIRE params and where they can be set. + +| Param name | Global config | AdUnit config | Type | Required | +| ---------- | ------------- | ------------- | ---- | ---------| +| pageId | | x | number | YES | +| placementId | | x | number | YES | +| adUnitElementId | | x | string | NO | + +### adUnit configuration + +```javascript +var adUnits = [ + { + code: 'target_div_id', // REQUIRED + bids: [{ + bidder: 'cwire', + mediaTypes: { + banner: { + sizes: [[1, 1]], + } + }, + params: { + pageId: 1422, // required - number + placementId: 2211521, // required - number + adUnitElementId: 'other_div', // optional, div id to write to, if not set it will default to ad unit code + } + }] + } +]; +``` \ No newline at end of file diff --git a/modules/dailyhuntBidAdapter.js b/modules/dailyhuntBidAdapter.js new file mode 100644 index 00000000000..cdcc9f1d038 --- /dev/null +++ b/modules/dailyhuntBidAdapter.js @@ -0,0 +1,435 @@ +import { registerBidder } from '../src/adapters/bidderFactory.js'; +import * as mediaTypes from '../src/mediaTypes.js'; +import {deepAccess, _map, isEmpty} from '../src/utils.js'; +import { ajax } from '../src/ajax.js'; +import find from 'core-js-pure/features/array/find.js'; +import { OUTSTREAM, INSTREAM } from '../src/video.js'; + +const BIDDER_CODE = 'dailyhunt'; +const BIDDER_ALIAS = 'dh'; +const SUPPORTED_MEDIA_TYPES = [mediaTypes.BANNER, mediaTypes.NATIVE, mediaTypes.VIDEO]; + +const PROD_PREBID_ENDPOINT_URL = 'https://pbs.dailyhunt.in/openrtb2/auction?partner='; +const PROD_PREBID_TEST_ENDPOINT_URL = 'https://qa-pbs-van.dailyhunt.in/openrtb2/auction?partner='; + +const ORTB_NATIVE_TYPE_MAPPING = { + img: { + '3': 'image', + '1': 'icon' + }, + data: { + '1': 'sponsoredBy', + '2': 'body', + '3': 'rating', + '4': 'likes', + '5': 'downloads', + '6': 'price', + '7': 'salePrice', + '8': 'phone', + '9': 'address', + '10': 'body2', + '11': 'displayUrl', + '12': 'cta' + } +} + +const ORTB_NATIVE_PARAMS = { + title: { + id: 0, + name: 'title' + }, + icon: { + id: 1, + type: 1, + name: 'img' + }, + image: { + id: 2, + type: 3, + name: 'img' + }, + sponsoredBy: { + id: 3, + name: 'data', + type: 1 + }, + body: { + id: 4, + name: 'data', + type: 2 + }, + cta: { + id: 5, + type: 12, + name: 'data' + }, + body2: { + id: 4, + name: 'data', + type: 10 + }, +}; + +// Encode URI. +const _encodeURIComponent = function (a) { + let b = window.encodeURIComponent(a); + b = b.replace(/'/g, '%27'); + return b; +} + +// Extract key from collections. +const extractKeyInfo = (collection, key) => { + for (let i = 0, result; i < collection.length; i++) { + result = deepAccess(collection[i].params, key); + if (result) { + return result; + } + } + return undefined +} + +// Flattern Array. +const flatten = (arr) => { + return [].concat(...arr); +} + +const createOrtbRequest = (validBidRequests, bidderRequest) => { + let device = createOrtbDeviceObj(validBidRequests); + let user = createOrtbUserObj(validBidRequests) + let site = createOrtbSiteObj(validBidRequests, bidderRequest.refererInfo.referer) + return { + id: bidderRequest.auctionId, + imp: [], + site, + device, + user, + }; +} + +const createOrtbDeviceObj = (validBidRequests) => { + let device = { ...extractKeyInfo(validBidRequests, `device`) }; + device.ua = navigator.userAgent; + return device; +} + +const createOrtbUserObj = (validBidRequests) => ({ ...extractKeyInfo(validBidRequests, `user`) }) + +const createOrtbSiteObj = (validBidRequests, page) => { + let site = { ...extractKeyInfo(validBidRequests, `site`), page }; + let publisher = createOrtbPublisherObj(validBidRequests); + if (!site.publisher) { + site.publisher = publisher + } + return site +} + +const createOrtbPublisherObj = (validBidRequests) => ({ ...extractKeyInfo(validBidRequests, `publisher`) }) + +// get bidFloor Function for different creatives +function getBidFloor(bid, creative) { + let floorInfo = typeof (bid.getFloor) == 'function' ? bid.getFloor({ currency: 'USD', mediaType: creative, size: '*' }) : {}; + return Math.floor(floorInfo.floor || (bid.params.bidfloor ? bid.params.bidfloor : 0.0)); +} + +const createOrtbImpObj = (bid) => { + let params = bid.params + let testMode = !!bid.params.test_mode + + // Validate Banner Request. + let bannerObj = deepAccess(bid.mediaTypes, `banner`); + let nativeObj = deepAccess(bid.mediaTypes, `native`); + let videoObj = deepAccess(bid.mediaTypes, `video`); + + let imp = { + id: bid.bidId, + ext: { + dailyhunt: { + placement_id: params.placement_id, + publisher_id: params.publisher_id, + partner: params.partner_name + } + } + }; + + // Test Mode Campaign. + if (testMode) { + imp.ext.test_mode = testMode; + } + + if (bannerObj) { + imp.banner = { + ...createOrtbImpBannerObj(bid, bannerObj) + } + imp.bidfloor = getBidFloor(bid, 'banner'); + } else if (nativeObj) { + imp.native = { + ...createOrtbImpNativeObj(bid, nativeObj) + } + imp.bidfloor = getBidFloor(bid, 'native'); + } else if (videoObj) { + imp.video = { + ...createOrtbImpVideoObj(bid, videoObj) + } + imp.bidfloor = getBidFloor(bid, 'video'); + } + return imp; +} + +const createOrtbImpBannerObj = (bid, bannerObj) => { + let format = []; + bannerObj.sizes.forEach(size => format.push({ w: size[0], h: size[1] })) + + return { + id: 'banner-' + bid.bidId, + format + } +} + +const createOrtbImpNativeObj = (bid, nativeObj) => { + const assets = _map(bid.nativeParams, (bidParams, key) => { + const props = ORTB_NATIVE_PARAMS[key]; + const asset = { + required: bidParams.required & 1, + }; + if (props) { + let h = 0; + let w = 0; + + asset.id = props.id; + + if (bidParams.sizes) { + const sizes = flatten(bidParams.sizes); + w = sizes[0]; + h = sizes[1]; + } + + asset[props.name] = { + len: bidParams.len ? bidParams.len : 20, + type: props.type, + w, + h + }; + + return asset; + } + }).filter(Boolean); + let request = { + assets, + ver: '1,0' + } + return { request: JSON.stringify(request) }; +} + +const createOrtbImpVideoObj = (bid, videoObj) => { + let obj = {}; + let params = bid.params + if (!isEmpty(bid.params.video)) { + obj = { + topframe: 1, + skip: params.video.skippable || 0, + linearity: params.video.linearity || 1, + minduration: params.video.minduration || 5, + maxduration: params.video.maxduration || 60, + mimes: params.video.mimes || ['video/mp4'], + protocols: getProtocols(params.video), + w: params.video.playerSize[0][0], + h: params.video.playerSize[0][1], + }; + } else { + obj = { + mimes: ['video/mp4'], + }; + } + obj.ext = { + ...videoObj, + } + return obj; +} + +export function getProtocols({protocols}) { + let defaultValue = [2, 3, 5, 6, 7, 8]; + let listProtocols = [ + {key: 'VAST_1_0', value: 1}, + {key: 'VAST_2_0', value: 2}, + {key: 'VAST_3_0', value: 3}, + {key: 'VAST_1_0_WRAPPER', value: 4}, + {key: 'VAST_2_0_WRAPPER', value: 5}, + {key: 'VAST_3_0_WRAPPER', value: 6}, + {key: 'VAST_4_0', value: 7}, + {key: 'VAST_4_0_WRAPPER', value: 8} + ]; + if (protocols) { + return listProtocols.filter(p => { + return protocols.indexOf(p.key) !== -1 + }).map(p => p.value); + } else { + return defaultValue; + } +} + +const createServerRequest = (ortbRequest, validBidRequests, isTestMode = 'false') => ({ + method: 'POST', + url: isTestMode === 'true' ? PROD_PREBID_TEST_ENDPOINT_URL + validBidRequests[0].params.partner_name : PROD_PREBID_ENDPOINT_URL + validBidRequests[0].params.partner_name, + data: JSON.stringify(ortbRequest), + options: { + contentType: 'application/json', + withCredentials: true + }, + bids: validBidRequests +}) + +const createPrebidBannerBid = (bid, bidResponse) => ({ + requestId: bid.bidId, + cpm: bidResponse.price.toFixed(2), + creativeId: bidResponse.crid, + width: bidResponse.w, + height: bidResponse.h, + ttl: 360, + netRevenue: bid.netRevenue === 'net', + currency: 'USD', + ad: bidResponse.adm, + mediaType: 'banner', + winUrl: bidResponse.nurl, + adomain: bidResponse.adomain +}) + +const createPrebidNativeBid = (bid, bidResponse) => ({ + requestId: bid.bidId, + cpm: bidResponse.price.toFixed(2), + creativeId: bidResponse.crid, + currency: 'USD', + ttl: 360, + netRevenue: bid.netRevenue === 'net', + native: parseNative(bidResponse), + mediaType: 'native', + winUrl: bidResponse.nurl, + width: bidResponse.w, + height: bidResponse.h, + adomain: bidResponse.adomain +}) + +const parseNative = (bid) => { + let adm = JSON.parse(bid.adm) + const { assets, link, imptrackers, jstracker } = adm.native; + const result = { + clickUrl: _encodeURIComponent(link.url), + clickTrackers: link.clicktrackers || [], + impressionTrackers: imptrackers || [], + javascriptTrackers: jstracker ? [ jstracker ] : [] + }; + assets.forEach(asset => { + if (!isEmpty(asset.title)) { + result.title = asset.title.text + } else if (!isEmpty(asset.img)) { + result[ORTB_NATIVE_TYPE_MAPPING.img[asset.img.type]] = { + url: asset.img.url, + height: asset.img.h, + width: asset.img.w + } + } else if (!isEmpty(asset.data)) { + result[ORTB_NATIVE_TYPE_MAPPING.data[asset.data.type]] = asset.data.value + } + }); + + return result; +} + +const createPrebidVideoBid = (bid, bidResponse) => { + let videoBid = { + requestId: bid.bidId, + cpm: bidResponse.price.toFixed(2), + creativeId: bidResponse.crid, + width: bidResponse.w, + height: bidResponse.h, + ttl: 360, + netRevenue: bid.netRevenue === 'net', + currency: 'USD', + mediaType: 'video', + winUrl: bidResponse.nurl, + adomain: bidResponse.adomain + }; + + let videoContext = bid.mediaTypes.video.context; + switch (videoContext) { + case OUTSTREAM: + videoBid.vastXml = bidResponse.adm; + break; + case INSTREAM: + videoBid.videoCacheKey = bidResponse.ext.bidder.cacheKey; + videoBid.vastUrl = bidResponse.ext.bidder.vastUrl; + break; + } + return videoBid; +} + +const getQueryVariable = (variable) => { + let query = window.location.search.substring(1); + let vars = query.split('&'); + for (var i = 0; i < vars.length; i++) { + let pair = vars[i].split('='); + if (decodeURIComponent(pair[0]) == variable) { + return decodeURIComponent(pair[1]); + } + } + return false; +} + +export const spec = { + code: BIDDER_CODE, + + aliases: [BIDDER_ALIAS], + + supportedMediaTypes: SUPPORTED_MEDIA_TYPES, + + isBidRequestValid: bid => !!bid.params.placement_id && !!bid.params.publisher_id && !!bid.params.partner_name, + + buildRequests: function (validBidRequests, bidderRequest) { + let serverRequests = []; + + // ORTB Request. + let ortbReq = createOrtbRequest(validBidRequests, bidderRequest); + + validBidRequests.forEach((bid) => { + let imp = createOrtbImpObj(bid) + ortbReq.imp.push(imp); + }); + + serverRequests.push({ ...createServerRequest(ortbReq, validBidRequests, getQueryVariable('dh_test')) }); + + return serverRequests; + }, + + interpretResponse: function (serverResponse, request) { + const { seatbid } = serverResponse.body; + let bids = request.bids; + let prebidResponse = []; + + let seatBids = seatbid[0].bid; + + seatBids.forEach(ortbResponseBid => { + let bidId = ortbResponseBid.impid; + let actualBid = find(bids, (bid) => bid.bidId === bidId); + let bidMediaType = ortbResponseBid.ext.prebid.type + switch (bidMediaType) { + case mediaTypes.BANNER: + prebidResponse.push(createPrebidBannerBid(actualBid, ortbResponseBid)); + break; + case mediaTypes.NATIVE: + prebidResponse.push(createPrebidNativeBid(actualBid, ortbResponseBid)); + break; + case mediaTypes.VIDEO: + prebidResponse.push(createPrebidVideoBid(actualBid, ortbResponseBid)); + break; + } + }) + return prebidResponse; + }, + + onBidWon: function(bid) { + ajax(bid.winUrl, null, null, { + method: 'GET' + }) + } +} + +registerBidder(spec); diff --git a/modules/dailyhuntBidAdapter.md b/modules/dailyhuntBidAdapter.md index acfd20a4de0..a08b66fb826 100644 --- a/modules/dailyhuntBidAdapter.md +++ b/modules/dailyhuntBidAdapter.md @@ -99,3 +99,7 @@ Dailyhunt bid adapter supports Banner, Native and Video. } ]; ``` + +## latest commit has all the required support for latest version of prebid above 6.x +## Dailyhunt adapter was there till 4.x and then removed in version 5.x of prebid. +## this doc has been also submitted to https://github.com/prebid/prebid.github.io \ No newline at end of file diff --git a/modules/datablocksBidAdapter.js b/modules/datablocksBidAdapter.js index 197ba19b1d6..43039e070c3 100644 --- a/modules/datablocksBidAdapter.js +++ b/modules/datablocksBidAdapter.js @@ -94,7 +94,7 @@ export const spec = { code: 'datablocks', // DATABLOCKS SCOPED OBJECT - db_obj: {metrics_host: 'prebid.datablocks.net', metrics: [], metrics_timer: null, metrics_queue_time: 1000, vis_optout: false, source_id: 0}, + db_obj: {metrics_host: 'prebid.dblks.net', metrics: [], metrics_timer: null, metrics_queue_time: 1000, vis_optout: false, source_id: 0}, // STORE THE DATABLOCKS BUYERID IN STORAGE store_dbid: function(dbid) { @@ -388,12 +388,12 @@ export const spec = { }; let sourceId = validRequests[0].params.source_id || 0; - let host = validRequests[0].params.host || 'prebid.datablocks.net'; + let host = validRequests[0].params.host || 'prebid.dblks.net'; // RETURN WITH THE REQUEST AND PAYLOAD return { method: 'POST', - url: `https://${sourceId}.${host}/openrtb/?sid=${sourceId}`, + url: `https://${host}/openrtb/?sid=${sourceId}`, data: { id: bidderRequest.auctionId, imp: imps, diff --git a/modules/dchain.js b/modules/dchain.js new file mode 100644 index 00000000000..6a1bd1ebf70 --- /dev/null +++ b/modules/dchain.js @@ -0,0 +1,149 @@ +import includes from 'core-js-pure/features/array/includes.js'; +import { config } from '../src/config.js'; +import { getHook } from '../src/hook.js'; +import { _each, isStr, isArray, isPlainObject, hasOwn, deepClone, deepAccess, logWarn, logError } from '../src/utils.js'; + +const shouldBeAString = ' should be a string'; +const shouldBeAnObject = ' should be an object'; +const shouldBeAnArray = ' should be an Array'; +const shouldBeValid = ' is not a valid dchain property'; +const MODE = { + STRICT: 'strict', + RELAXED: 'relaxed', + OFF: 'off' +}; +const MODES = []; // an array of modes +_each(MODE, mode => MODES.push(mode)); + +export function checkDchainSyntax(bid, mode) { + let dchainObj = deepClone(bid.meta.dchain); + let failPrefix = 'Detected something wrong in bid.meta.dchain object for bid:'; + let failMsg = ''; + const dchainPropList = ['ver', 'complete', 'nodes', 'ext']; + + function appendFailMsg(msg) { + failMsg += '\n' + msg; + } + + function printFailMsg() { + if (mode === MODE.STRICT) { + logError(failPrefix, bid, '\n', dchainObj, failMsg); + } else { + logWarn(failPrefix, bid, `\n`, dchainObj, failMsg); + } + } + + let dchainProps = Object.keys(dchainObj); + dchainProps.forEach(prop => { + if (!includes(dchainPropList, prop)) { + appendFailMsg(`dchain.${prop}` + shouldBeValid); + } + }); + + if (dchainObj.complete !== 0 && dchainObj.complete !== 1) { + appendFailMsg(`dchain.complete should be 0 or 1`); + } + + if (!isStr(dchainObj.ver)) { + appendFailMsg(`dchain.ver` + shouldBeAString); + } + + if (hasOwn(dchainObj, 'ext')) { + if (!isPlainObject(dchainObj.ext)) { + appendFailMsg(`dchain.ext` + shouldBeAnObject); + } + } + + if (!isArray(dchainObj.nodes)) { + appendFailMsg(`dchain.nodes` + shouldBeAnArray); + printFailMsg(); + if (mode === MODE.STRICT) return false; + } else { + const nodesPropList = ['asi', 'bsid', 'rid', 'name', 'domain', 'ext']; + dchainObj.nodes.forEach((node, index) => { + if (!isPlainObject(node)) { + appendFailMsg(`dchain.nodes[${index}]` + shouldBeAnObject); + } else { + let nodeProps = Object.keys(node); + nodeProps.forEach(prop => { + if (!includes(nodesPropList, prop)) { + appendFailMsg(`dchain.nodes[${index}].${prop}` + shouldBeValid); + } + + if (prop === 'ext') { + if (!isPlainObject(node.ext)) { + appendFailMsg(`dchain.nodes[${index}].ext` + shouldBeAnObject); + } + } else { + if (!isStr(node[prop])) { + appendFailMsg(`dchain.nodes[${index}].${prop}` + shouldBeAString); + } + } + }); + } + }); + } + + if (failMsg.length > 0) { + printFailMsg(); + if (mode === MODE.STRICT) { + return false; + } + } + return true; +} + +function isValidDchain(bid) { + let mode = MODE.STRICT; + const dchainConfig = config.getConfig('dchain'); + + if (dchainConfig && isStr(dchainConfig.validation) && MODES.indexOf(dchainConfig.validation) != -1) { + mode = dchainConfig.validation; + } + + if (mode === MODE.OFF) { + return true; + } else { + return checkDchainSyntax(bid, mode); + } +} + +export function addBidResponseHook(fn, adUnitCode, bid) { + const basicDchain = { + ver: '1.0', + complete: 0, + nodes: [] + }; + + if (deepAccess(bid, 'meta.networkId') && deepAccess(bid, 'meta.networkName')) { + basicDchain.nodes.push({ name: bid.meta.networkName, bsid: bid.meta.networkId.toString() }); + } + basicDchain.nodes.push({ name: bid.bidderCode }); + + let bidDchain = deepAccess(bid, 'meta.dchain'); + if (bidDchain && isPlainObject(bidDchain)) { + let result = isValidDchain(bid); + + if (result) { + // extra check in-case mode is OFF and there is a setup issue + if (isArray(bidDchain.nodes)) { + bid.meta.dchain.nodes.push({ asi: bid.bidderCode }); + } else { + logWarn('bid.meta.dchain.nodes did not exist or was not an array; did not append prebid dchain.', bid); + } + } else { + // remove invalid dchain + delete bid.meta.dchain; + } + } else { + bid.meta.dchain = basicDchain; + } + + fn(adUnitCode, bid); +} + +export function init() { + getHook('addBidResponse').before(addBidResponseHook, 35); +} + +init(); diff --git a/modules/dchain.md b/modules/dchain.md new file mode 100644 index 00000000000..f01b3483f3c --- /dev/null +++ b/modules/dchain.md @@ -0,0 +1,45 @@ +# dchain module + +Refer: +- https://iabtechlab.com/buyers-json-demand-chain/ + +## Sample code for dchain setConfig and dchain object +``` +pbjs.setConfig({ + "dchain": { + "validation": "strict" + } +}); +``` + +``` +bid.meta.dchain: { + "complete": 0, + "ver": "1.0", + "ext": {...}, + "nodes": [{ + "asi": "abc", + "bsid": "123", + "rid": "d4e5f6", + "name": "xyz", + "domain": "mno", + "ext": {...} + }, ...] +} +``` + +## Workflow +The dchain module is not enabled by default as it may not be necessary for all publishers. +If required, dchain module can be included as following +``` + $ gulp build --modules=dchain,pubmaticBidAdapter,openxBidAdapter,rubiconBidAdapter,sovrnBidAdapter +``` + +The dchain module will validate a bidder's dchain object (if it was defined). Bidders should assign their dchain object into `bid.meta` field. If the dchain object is valid, it will remain in the bid object for later use. + +If it was not defined, the dchain will create a default dchain object for prebid. + +## Validation modes +- ```strict```: It is the default validation mode. In this mode, dchain object will not be accpeted from adapters if it is invalid. Errors are thrown for invalid dchain object. +- ```relaxed```: In this mode, errors are thrown for an invalid dchain object but the invalid dchain object is still accepted from adapters. +- ```off```: In this mode, no validations are performed and dchain object is accepted as is from adapters. \ No newline at end of file diff --git a/modules/deepintentBidAdapter.js b/modules/deepintentBidAdapter.js index f2314454ab9..94167b92bb0 100644 --- a/modules/deepintentBidAdapter.js +++ b/modules/deepintentBidAdapter.js @@ -1,13 +1,38 @@ -import { generateUUID, deepSetValue, deepAccess, isArray } from '../src/utils.js'; +import { generateUUID, deepSetValue, deepAccess, isArray, isInteger, logError, logWarn } from '../src/utils.js'; import {registerBidder} from '../src/adapters/bidderFactory.js'; -import {BANNER} from '../src/mediaTypes.js'; +import {BANNER, VIDEO} from '../src/mediaTypes.js'; const BIDDER_CODE = 'deepintent'; const BIDDER_ENDPOINT = 'https://prebid.deepintent.com/prebid'; const USER_SYNC_URL = 'https://cdn.deepintent.com/syncpixel.html'; const DI_M_V = '1.0.0'; +export const ORTB_VIDEO_PARAMS = { + 'mimes': (value) => Array.isArray(value) && value.length > 0 && value.every(v => typeof v === 'string'), + 'minduration': (value) => isInteger(value), + 'maxduration': (value) => isInteger(value), + 'protocols': (value) => Array.isArray(value) && value.every(v => v >= 1 && v <= 10), + 'w': (value) => isInteger(value), + 'h': (value) => isInteger(value), + 'startdelay': (value) => isInteger(value), + 'placement': (value) => Array.isArray(value) && value.every(v => v >= 1 && v <= 5), + 'linearity': (value) => [1, 2].indexOf(value) !== -1, + 'skip': (value) => [0, 1].indexOf(value) !== -1, + 'skipmin': (value) => isInteger(value), + 'skipafter': (value) => isInteger(value), + 'sequence': (value) => isInteger(value), + 'battr': (value) => Array.isArray(value) && value.every(v => v >= 1 && v <= 17), + 'maxextended': (value) => isInteger(value), + 'minbitrate': (value) => isInteger(value), + 'maxbitrate': (value) => isInteger(value), + 'boxingallowed': (value) => [0, 1].indexOf(value) !== -1, + 'playbackmethod': (value) => Array.isArray(value) && value.every(v => v >= 1 && v <= 6), + 'playbackend': (value) => [1, 2, 3].indexOf(value) !== -1, + 'delivery': (value) => [1, 2, 3].indexOf(value) !== -1, + 'pos': (value) => [0, 1, 2, 3, 4, 5, 6, 7].indexOf(value) !== -1, + 'api': (value) => Array.isArray(value) && value.every(v => v >= 1 && v <= 6) +}; export const spec = { code: BIDDER_CODE, - supportedMediaTypes: [BANNER], + supportedMediaTypes: [BANNER, VIDEO], aliases: [], // tagId is mandatory param @@ -15,16 +40,38 @@ export const spec = { let valid = false; if (bid && bid.params && bid.params.tagId) { if (typeof bid.params.tagId === 'string' || bid.params.tagId instanceof String) { - valid = true; + if (bid.hasOwnProperty('mediaTypes') && bid.mediaTypes.hasOwnProperty(VIDEO)) { + if (bid.mediaTypes[VIDEO].hasOwnProperty('context')) { + valid = true; + } + } else { + valid = true; + } } } return valid; }, - interpretResponse: function(bidResponse, request) { + interpretResponse: function(bidResponse, bidRequest) { let responses = []; if (bidResponse && bidResponse.body) { - let bids = bidResponse.body.seatbid && bidResponse.body.seatbid[0] ? bidResponse.body.seatbid[0].bid : []; - responses = bids.map(bid => formatResponse(bid)) + try { + let bids = bidResponse.body.seatbid && bidResponse.body.seatbid[0] ? bidResponse.body.seatbid[0].bid : []; + if (bids) { + bids.forEach(bidObj => { + let newBid = formatResponse(bidObj); + let mediaType = _checkMediaType(bidObj); + if (mediaType === BANNER) { + newBid.mediaType = BANNER; + } else if (mediaType === VIDEO) { + newBid.mediaType = VIDEO; + newBid.vastXml = bidObj.adm; + } + responses.push(newBid); + }); + } + } catch (err) { + logError(err); + } } return responses; }, @@ -73,6 +120,17 @@ export const spec = { } }; +function _checkMediaType(bid) { + let videoRegex = new RegExp(/VAST\s+version/); + let mediaType; + if (bid.adm && bid.adm.indexOf('deepintent_wrapper') >= 0) { + mediaType = BANNER; + } else if (videoRegex.test(bid.adm)) { + mediaType = VIDEO; + } + return mediaType; +} + function clean(obj) { for (let propName in obj) { if (obj[propName] === null || obj[propName] === undefined) { @@ -100,16 +158,55 @@ function formatResponse(bid) { } function buildImpression(bid) { - return { + let impression = {}; + impression = { id: bid.bidId, tagid: bid.params.tagId || '', - secure: window.location.protocol === 'https' ? 1 : 0, - banner: buildBanner(bid), + secure: window.location.protocol === 'https:' ? 1 : 0, displaymanager: 'di_prebid', displaymanagerver: DI_M_V, ext: buildCustomParams(bid) }; + if (deepAccess(bid, 'mediaTypes.banner')) { + impression['banner'] = buildBanner(bid); + } + if (deepAccess(bid, 'mediaTypes.video')) { + impression['video'] = _buildVideo(bid); + } + return impression; } + +function _buildVideo(bid) { + const videoObj = {}; + const videoAdUnitParams = deepAccess(bid, 'mediaTypes.video', {}); + const videoBidderParams = deepAccess(bid, 'params.video', {}); + const computedParams = {}; + + if (Array.isArray(videoAdUnitParams.playerSize)) { + const tempSize = (Array.isArray(videoAdUnitParams.playerSize[0])) ? videoAdUnitParams.playerSize[0] : videoAdUnitParams.playerSize; + computedParams.w = tempSize[0]; + computedParams.h = tempSize[1]; + } + + const videoParams = { + ...computedParams, + ...videoAdUnitParams, + ...videoBidderParams + }; + + Object.keys(ORTB_VIDEO_PARAMS).forEach(paramName => { + if (videoParams.hasOwnProperty(paramName)) { + if (ORTB_VIDEO_PARAMS[paramName](videoParams[paramName])) { + videoObj[paramName] = videoParams[paramName]; + } else { + logWarn(`The OpenRTB video param ${paramName} has been skipped due to misformating. Please refer to OpenRTB 2.5 spec.`); + } + } + }); + + return videoObj; +}; + function buildCustomParams(bid) { if (bid.params && bid.params.custom) { return { diff --git a/modules/deepintentBidAdapter.md b/modules/deepintentBidAdapter.md index 79a6a1679e2..84c375d69a4 100644 --- a/modules/deepintentBidAdapter.md +++ b/modules/deepintentBidAdapter.md @@ -8,7 +8,7 @@ Maintainer: prebid@deepintent.com # Description -Deepintent currently supports the BANNER type ads through prebid js +Deepintent currently supports the BANNER and VIDEO type ads through prebid js Module that connects to Deepintent's demand sources. @@ -40,6 +40,41 @@ Module that connects to Deepintent's demand sources. ]; ``` +# Sample Video Ad Unit +``` +var adVideoAdUnits = [ +{ + code: 'test-div-video', + mediaTypes: { + video: { + playerSize: [640, 480], // required + context: 'instream' //required + } + }, + bids: [{ + bidder: 'deepintent', + params: { + tagId: '1300', // Required parameter // required + video: { + mimes: ['video/mp4','video/x-flv'], // required + skippable: true, // optional + minduration: 5, // optional + maxduration: 30, // optional + startdelay: 5, // optional + playbackmethod: [1,3], // optional + api: [ 1, 2 ], // optional + protocols: [ 2, 3 ], // optional + battr: [ 13, 14 ], // optional + linearity: 1, // optional + placement: 2, // optional + minbitrate: 10, // optional + maxbitrate: 10 // optional + } + } + }] +}] +``` + ###Recommended User Sync Configuration ```javascript diff --git a/modules/deltaprojectsBidAdapter.js b/modules/deltaprojectsBidAdapter.js new file mode 100644 index 00000000000..33df5bd252e --- /dev/null +++ b/modules/deltaprojectsBidAdapter.js @@ -0,0 +1,252 @@ +import { registerBidder } from '../src/adapters/bidderFactory.js'; +import { BANNER } from '../src/mediaTypes.js'; +import { + _each, _map, isFn, isNumber, createTrackPixelHtml, deepAccess, parseUrl, logWarn, logError +} from '../src/utils.js'; +import {config} from '../src/config.js'; + +export const BIDDER_CODE = 'deltaprojects'; +export const BIDDER_ENDPOINT_URL = 'https://d5p.de17a.com/dogfight/prebid'; +export const USERSYNC_URL = 'https://userservice.de17a.com/getuid/prebid'; + +/** -- isBidRequestValid --**/ +function isBidRequestValid(bid) { + if (!bid) return false; + + if (bid.bidder !== BIDDER_CODE) return false; + + // publisher id is required + const publisherId = deepAccess(bid, 'params.publisherId') + if (!publisherId) { + logError('Invalid bid request, missing publisher id in params'); + return false; + } + + return true; +} + +/** -- Build requests --**/ +function buildRequests(validBidRequests, bidderRequest) { + /** == shared ==**/ + // -- build id + const id = bidderRequest.auctionId; + + // -- build site + const loc = parseUrl(bidderRequest.refererInfo.referer); + const publisherId = setOnAny(validBidRequests, 'params.publisherId'); + const siteId = setOnAny(validBidRequests, 'params.siteId'); + const site = { + id: siteId, + domain: loc.hostname, + page: loc.href, + ref: loc.href, + publisher: { id: publisherId }, + }; + + // -- build device + const ua = navigator.userAgent; + const device = { + ua, + w: screen.width, + h: screen.height + } + + // -- build user, reg + let user = { ext: {} }; + const regs = { ext: {} }; + const gdprConsent = bidderRequest && bidderRequest.gdprConsent; + if (gdprConsent) { + user.ext = { consent: gdprConsent.consentString }; + if (typeof gdprConsent.gdprApplies == 'boolean') { + regs.ext.gdpr = gdprConsent.gdprApplies ? 1 : 0 + } + } + + // -- build tmax + let tmax = (bidderRequest && bidderRequest.timeout > 0) ? bidderRequest.timeout : undefined; + + // build bid specific + return validBidRequests.map(validBidRequest => { + const openRTBRequest = buildOpenRTBRequest(validBidRequest, id, site, device, user, tmax, regs); + return { + method: 'POST', + url: BIDDER_ENDPOINT_URL, + data: openRTBRequest, + options: { contentType: 'application/json' }, + bids: [validBidRequest], + }; + }); +} + +function buildOpenRTBRequest(validBidRequest, id, site, device, user, tmax, regs) { + // build cur + const currency = config.getConfig('currency.adServerCurrency') || deepAccess(validBidRequest, 'params.currency'); + const cur = currency && [currency]; + + // build impression + const impression = buildImpression(validBidRequest, currency); + + // build test + const test = deepAccess(validBidRequest, 'params.test') ? 1 : 0 + + const at = 1 + + // build source + const source = { + tid: validBidRequest.transactionId, + fd: 1, + } + + return { + id, + at, + imp: [impression], + site, + device, + user, + test, + tmax, + cur, + source, + regs, + ext: {}, + }; +} + +function buildImpression(bid, currency) { + const impression = { + id: bid.bidId, + tagid: bid.params.tagId, + ext: {}, + }; + + const bannerMediaType = deepAccess(bid, `mediaTypes.${BANNER}`); + impression.banner = buildImpressionBanner(bid, bannerMediaType); + + // bid floor + const bidFloor = getBidFloor(bid, BANNER, '*', currency); + if (bidFloor) { + impression.bidfloor = bidFloor.floor; + impression.bidfloorcur = bidFloor.currency; + } + + return impression; +} + +function buildImpressionBanner(bid, bannerMediaType) { + const bannerSizes = (bannerMediaType && bannerMediaType.sizes) || bid.sizes; + return { + format: _map(bannerSizes, ([width, height]) => ({ w: width, h: height })), + }; +} + +/** -- Interpret response --**/ +function interpretResponse(serverResponse) { + if (!serverResponse.body) { + logWarn('Response body is invalid, return !!'); + return []; + } + + const { body: { id, seatbid, cur } } = serverResponse; + if (!id || !seatbid) { + logWarn('Id / seatbid of response is invalid, return !!'); + return []; + } + + const bidResponses = []; + + _each(seatbid, seatbid => { + _each(seatbid.bid, bid => { + const bidObj = { + requestId: bid.impid, + cpm: parseFloat(bid.price), + width: parseInt(bid.w), + height: parseInt(bid.h), + creativeId: bid.crid || bid.id, + dealId: bid.dealid || null, + currency: cur, + netRevenue: true, + ttl: 60, + }; + + bidObj.mediaType = BANNER; + bidObj.ad = bid.adm; + if (bid.nurl) { + bidObj.ad += createTrackPixelHtml(decodeURIComponent(bid.nurl)); + } + if (bid.ext) { + bidObj[BIDDER_CODE] = bid.ext; + } + bidResponses.push(bidObj); + }); + }); + return bidResponses; +} + +/** -- On Bid Won -- **/ +function onBidWon(bid) { + let cpm = bid.cpm; + if (bid.currency && bid.currency !== bid.originalCurrency && typeof bid.getCpmInNewCurrency === 'function') { + cpm = bid.getCpmInNewCurrency(bid.originalCurrency); + } + const wonPrice = Math.round(cpm * 1000000); + const wonPriceMacroPatten = /\$\{AUCTION_PRICE:B64\}/g; + bid.ad = bid.ad.replace(wonPriceMacroPatten, wonPrice); +} + +/** -- Get user syncs --**/ +function getUserSyncs(syncOptions, serverResponses, gdprConsent) { + const syncs = [] + + if (syncOptions.pixelEnabled) { + let gdprParams; + if (gdprConsent) { + if (typeof gdprConsent.gdprApplies === 'boolean') { + gdprParams = `?gdpr=${Number(gdprConsent.gdprApplies)}&gdpr_consent=${gdprConsent.consentString}`; + } else { + gdprParams = `?gdpr_consent=${gdprConsent.consentString}`; + } + } else { + gdprParams = ''; + } + syncs.push({ + type: 'image', + url: USERSYNC_URL + gdprParams + }); + } + return syncs; +} + +/** -- Get bid floor --**/ +export function getBidFloor(bid, mediaType, size, currency) { + if (isFn(bid.getFloor)) { + const bidFloorCurrency = currency || 'USD'; + const bidFloor = bid.getFloor({currency: bidFloorCurrency, mediaType: mediaType, size: size}); + if (isNumber(bidFloor.floor)) { + return bidFloor; + } + } +} + +/** -- Helper methods --**/ +function setOnAny(collection, key) { + for (let i = 0, result; i < collection.length; i++) { + result = deepAccess(collection[i], key); + if (result) { + return result; + } + } +} + +/** -- Register -- */ +export const spec = { + code: BIDDER_CODE, + supportedMediaTypes: [BANNER], + isBidRequestValid, + buildRequests, + interpretResponse, + onBidWon, + getUserSyncs, +}; + +registerBidder(spec); diff --git a/modules/deltaprojectsBidAdapter.md b/modules/deltaprojectsBidAdapter.md new file mode 100644 index 00000000000..97cef4dd228 --- /dev/null +++ b/modules/deltaprojectsBidAdapter.md @@ -0,0 +1,32 @@ +# Overview + +``` +Module Name: Delta Projects Bid Adapter +Module Type: Bidder Adapter +Maintainer: dev@deltaprojects.com +``` + +# Description + +Connects to Delta Projects DSP for bids. + +# Test Parameters +``` +// define banner unit +var bannerUnit = { + code: 'div-gpt-ad-1460505748561-0', + mediaTypes: { + banner: { + sizes: [[300, 250], [300,600]], + } + }, + // Replace this object to test a new Adapter! + bids: [{ + bidder: 'deltaprojects', + params: { + publisherId: '4' //required + } + }] +}; +``` + diff --git a/modules/dgkeywordRtdProvider.js b/modules/dgkeywordRtdProvider.js index a086091d464..26a8257077a 100644 --- a/modules/dgkeywordRtdProvider.js +++ b/modules/dgkeywordRtdProvider.js @@ -78,7 +78,6 @@ export function getDgKeywordsAndSet(reqBidsConfigObj, callback, moduleConfig, us } }, null, { withCredentials: true, - contentType: 'application/json', }); setTimeout(function () { if (!isFinish) { diff --git a/modules/displayioBidAdapter.js b/modules/displayioBidAdapter.js new file mode 100644 index 00000000000..55a2f4a8604 --- /dev/null +++ b/modules/displayioBidAdapter.js @@ -0,0 +1,157 @@ +import {registerBidder} from '../src/adapters/bidderFactory.js'; +import {BANNER, VIDEO} from '../src/mediaTypes.js'; + +const BIDDER_VERSION = '1.0.0'; +const BIDDER_CODE = 'displayio'; +const GVLID = 999; +const BID_TTL = 300; +const SUPPORTED_AD_TYPES = [BANNER, VIDEO]; +const DEFAULT_CURRENCY = 'USD'; + +export const spec = { + code: BIDDER_CODE, + gvlid: GVLID, + supportedMediaTypes: SUPPORTED_AD_TYPES, + isBidRequestValid: function(bid) { + return !!(bid.params && bid.params.placementId && bid.params.siteId && + bid.params.adsSrvDomain && bid.params.cdnDomain); + }, + buildRequests: function (bidRequests, bidderRequest) { + return bidRequests.map(bid => { + let url = '//' + bid.params.adsSrvDomain + '/srv?method=getPlacement&app=' + + bid.params.siteId + '&placement=' + bid.params.placementId; + const data = this._getPayload(bid, bidderRequest); + return { + method: 'POST', + headers: {'Content-Type': 'application/json;charset=utf-8'}, + url, + data + }; + }); + }, + interpretResponse: function (serverResponse, serverRequest) { + const ads = serverResponse.body.data.ads; + const bidResponses = []; + const { data } = serverRequest.data; + if (ads.length) { + const adData = ads[0].ad.data; + const bidResponse = { + requestId: data.id, + cpm: adData.ecpm, + width: adData.w, + height: adData.h, + netRevenue: true, + ttl: BID_TTL, + creativeId: adData.adId || 0, + currency: DEFAULT_CURRENCY, + referrer: data.data.ref, + mediaType: ads[0].ad.subtype, + ad: adData.markup, + placement: data.placement, + adData: adData + }; + if (bidResponse.vastUrl === 'videoVast') { + bidResponse.vastUrl = adData.videos[0].url + } + bidResponses.push(bidResponse); + } + return bidResponses; + }, + _getPayload: function (bid, bidderRequest) { + const connection = navigator.connection || navigator.mozConnection || navigator.webkitConnection; + const userSession = 'us_web_xxxxxxxxxxxx'.replace(/[x]/g, c => { + let r = Math.random() * 16 | 0; + let v = c === 'x' ? r : (r & 0x3 | 0x8); + return v.toString(16); + }); + const { params } = bid; + const { siteId, placementId } = params; + const { refererInfo, uspConsent, gdprConsent } = bidderRequest; + const mediation = {consent: '-1', gdpr: '-1'}; + if (gdprConsent) { + if (gdprConsent.consentString !== undefined) { + mediation.consent = gdprConsent.consentString; + } + if (gdprConsent.gdprApplies !== undefined) { + mediation.gdpr = gdprConsent.gdprApplies ? '1' : '0'; + } + } + const payload = { + userSession, + data: { + id: bid.bidId, + action: 'getPlacement', + app: siteId, + placement: placementId, + data: { + pagecat: params.pageCategory ? params.pageCategory.split(',').map(k => k.trim()) : [], + keywords: params.keywords ? params.keywords.split(',').map(k => k.trim()) : [], + lang_content: document.documentElement.lang, + lang: window.navigator.language, + domain: window.location.hostname, + page: window.location.href, + ref: refererInfo.referer, + userids: _getUserIDs(), + geo: '', + }, + complianceData: { + child: '-1', + us_privacy: uspConsent, + dnt: window.navigator.doNotTrack, + iabConsent: {}, + mediation: { + consent: mediation.consent, + gdpr: mediation.gdpr, + } + }, + integration: 'JS', + omidpn: 'Displayio', + mediationPlatform: 0, + prebidVersion: BIDDER_VERSION, + device: { + w: window.screen.width, + h: window.screen.height, + connection_type: connection ? connection.effectiveType : '', + } + } + } + if (navigator.permissions) { + navigator.permissions.query({ name: 'geolocation' }) + .then((result) => { + if (result.state === 'granted') { + payload.data.data.geo = _getGeoData(); + } + }); + } + return payload + } +}; + +function _getUserIDs () { + let ids = {}; + try { + ids = window.owpbjs.getUserIdsAsEids(); + } catch (e) {} + return ids; +} + +async function _getGeoData () { + let geoData = null; + const getCurrentPosition = () => { + return new Promise((resolve, reject) => + navigator.geolocation.getCurrentPosition(resolve, reject) + ); + } + try { + const position = await getCurrentPosition(); + let {latitude, longitude, accuracy} = position.coords; + geoData = { + 'lat': latitude, + 'lng': longitude, + 'precision': accuracy + }; + } catch (e) {} + return geoData +} + +registerBidder(spec); diff --git a/modules/displayioBidAdapter.md b/modules/displayioBidAdapter.md new file mode 100644 index 00000000000..41505ee966e --- /dev/null +++ b/modules/displayioBidAdapter.md @@ -0,0 +1,148 @@ +# Overview + +``` +Module Name: DisplayIO Bidder Adapter +Module Type: Bidder Adapter +``` + +# Description + +Module that connects to display.io's demand sources. +Web mobile (not relevant for web desktop). + + +#Features +| Feature | | Feature | | +|---------------|---------------------------------------------------------|-----------------------|-----| +| Bidder Code | displayio | Prebid member | no | +| Media Types | Banner, video.
Sizes (display 320x480 / vertical video) | GVL ID | no | +| GDPR Support | yes | Prebid.js Adapter | yes | +| USP Support | yes | Prebid Server Adapter | no | + + +#Global configuration +```javascript + + + + + + + + + + +

Basic Prebid.js Example

+
Div-1
+
+ +
+ +
+ +
Div-2
+
+ +
+ + + + diff --git a/modules/playwireBidAdapter.md b/modules/playwireBidAdapter.md new file mode 100644 index 00000000000..dddb57c9bc1 --- /dev/null +++ b/modules/playwireBidAdapter.md @@ -0,0 +1,61 @@ +# Overview + +Module Name: Playwire Bidder Adapter +Module Type: Bidder Adapter +Maintainer: grid-tech@themediagrid.com + +# Description + +Module that connects to Grid demand source to fetch bids. +The adapter is GDPR compliant and supports banner and video (instream and outstream). + +# Test Parameters +``` + var adUnits = [ + { + code: 'test-div', + sizes: [[300, 250]], + bids: [ + { + bidder: "playwire", + params: { + uid: '1', + bidFloor: 0.5 + } + } + ] + },{ + code: 'test-div', + sizes: [[728, 90]], + bids: [ + { + bidder: "playwire", + params: { + uid: 2, + keywords: { + brandsafety: ['disaster'], + topic: ['stress', 'fear'] + } + } + } + ] + }, + { + code: 'test-div', + sizes: [[728, 90]], + mediaTypes: { video: { + context: 'instream', + playerSize: [728, 90], + mimes: ['video/mp4'] + }, + bids: [ + { + bidder: "playwire", + params: { + uid: 11 + } + } + ] + } + ]; +``` diff --git a/modules/prebidServerBidAdapter/index.js b/modules/prebidServerBidAdapter/index.js index 245cb7fc0b0..efe2576efd5 100644 --- a/modules/prebidServerBidAdapter/index.js +++ b/modules/prebidServerBidAdapter/index.js @@ -56,6 +56,7 @@ let eidPermissions; * @property {string} [adapter='prebidServer'] adapter code to use for S2S * @property {boolean} [enabled=false] enables S2S bidding * @property {number} [timeout=1000] timeout for S2S bidders - should be lower than `pbjs.requestBids({timeout})` + * @property {number} [syncTimeout=1000] timeout for cookie sync iframe / image rendering * @property {number} [maxBids=1] * @property {AdapterOptions} [adapterOptions] adds arguments to resulting OpenRTB payload to Prebid Server * @property {Object} [syncUrlModifier] @@ -77,6 +78,7 @@ let eidPermissions; */ const s2sDefaultConfig = { timeout: 1000, + syncTimeout: 1000, maxBids: 1, adapter: 'prebidServer', adapterOptions: {}, @@ -274,11 +276,9 @@ function doAllSyncs(bidders, s2sConfig) { */ function doPreBidderSync(type, url, bidder, done, s2sConfig) { if (s2sConfig.syncUrlModifier && typeof s2sConfig.syncUrlModifier[bidder] === 'function') { - const newSyncUrl = s2sConfig.syncUrlModifier[bidder](type, url, bidder); - doBidderSync(type, newSyncUrl, bidder, done) - } else { - doBidderSync(type, url, bidder, done) + url = s2sConfig.syncUrlModifier[bidder](type, url, bidder); } + doBidderSync(type, url, bidder, done, s2sConfig.syncTimeout) } /** @@ -288,17 +288,18 @@ function doPreBidderSync(type, url, bidder, done, s2sConfig) { * @param {string} url the url to sync * @param {string} bidder name of bidder doing sync for * @param {function} done an exit callback; to signify this pixel has either: finished rendering or something went wrong + * @param {number} timeout: maximum time to wait for rendering in milliseconds */ -function doBidderSync(type, url, bidder, done) { +function doBidderSync(type, url, bidder, done, timeout) { if (!url) { logError(`No sync url for bidder "${bidder}": ${url}`); done(); } else if (type === 'image' || type === 'redirect') { logMessage(`Invoking image pixel user sync for bidder: "${bidder}"`); - triggerPixel(url, done); - } else if (type == 'iframe') { + triggerPixel(url, done, timeout); + } else if (type === 'iframe') { logMessage(`Invoking iframe user sync for bidder: "${bidder}"`); - insertUserSyncIframe(url, done); + insertUserSyncIframe(url, done, timeout); } else { logError(`User sync type "${type}" not supported for bidder: "${bidder}"`); done(); @@ -539,10 +540,13 @@ const OPEN_RTB_PROTOCOL = { } if (Array.isArray(params.aspect_ratios)) { // pass aspect_ratios as ext data I guess? - asset.ext = { - aspectratios: params.aspect_ratios.map( - ratio => `${ratio.ratio_width}:${ratio.ratio_height}` - ) + const aspectRatios = params.aspect_ratios + .filter((ar) => ar.ratio_width && ar.ratio_height) + .map(ratio => `${ratio.ratio_width}:${ratio.ratio_height}`); + if (aspectRatios.length > 0) { + asset.ext = { + aspectratios: aspectRatios + } } } assets.push(newAsset({ @@ -731,7 +735,7 @@ const OPEN_RTB_PROTOCOL = { return; } const request = { - id: s2sBidRequest.tid, + id: firstBidRequest.auctionId, source: {tid: s2sBidRequest.tid}, tmax: s2sConfig.timeout, imp: imps, @@ -751,7 +755,7 @@ const OPEN_RTB_PROTOCOL = { } }; - // Sets pbjs version, can be overwritten below if channel exists in s2sConfig.extPrebid + // This is no longer overwritten unless name and version explicitly overwritten by extPrebid (mergeDeep) request.ext.prebid = Object.assign(request.ext.prebid, {channel: {name: 'pbjs', version: $$PREBID_GLOBAL$$.version}}) // set debug flag if in debug mode @@ -761,7 +765,7 @@ const OPEN_RTB_PROTOCOL = { // s2sConfig video.ext.prebid is passed through openrtb to PBS if (s2sConfig.extPrebid && typeof s2sConfig.extPrebid === 'object') { - request.ext.prebid = Object.assign(request.ext.prebid, s2sConfig.extPrebid); + request.ext.prebid = mergeDeep(request.ext.prebid, s2sConfig.extPrebid); } /** @@ -799,7 +803,7 @@ const OPEN_RTB_PROTOCOL = { if (requestedBidders && isArray(requestedBidders)) { eidPermissions.forEach(i => { if (i.bidders) { - i.bidders = i.bidders.filter(bidder => requestedBidders.includes(bidder)) + i.bidders = i.bidders.filter(bidder => includes(requestedBidders, bidder)) } }); } diff --git a/modules/proxistoreBidAdapter.js b/modules/proxistoreBidAdapter.js index 68151f820dd..42a98bcdb09 100644 --- a/modules/proxistoreBidAdapter.js +++ b/modules/proxistoreBidAdapter.js @@ -4,7 +4,8 @@ import { registerBidder } from '../src/adapters/bidderFactory.js'; const BIDDER_CODE = 'proxistore'; const PROXISTORE_VENDOR_ID = 418; const COOKIE_BASE_URL = 'https://abs.proxistore.com/v3/rtb/prebid/multi'; -const COOKIE_LESS_URL = 'https://abs.cookieless-proxistore.com/v3/rtb/prebid/multi'; +const COOKIE_LESS_URL = + 'https://abs.cookieless-proxistore.com/v3/rtb/prebid/multi'; function _createServerRequest(bidRequests, bidderRequest) { var sizeIds = []; @@ -171,8 +172,6 @@ function interpretResponse(serverResponse, bidRequest) { function _assignFloor(bid) { if (!isFn(bid.getFloor)) { - // eslint-disable-next-line no-console - console.log(bid.params.bidFloor); return bid.params.bidFloor ? bid.params.bidFloor : null; } const floor = bid.getFloor({ @@ -181,11 +180,7 @@ function _assignFloor(bid) { size: '*', }); - if ( - isPlainObject(floor) && - !isNaN(floor.floor) && - floor.currency === 'EUR' - ) { + if (isPlainObject(floor) && !isNaN(floor.floor) && floor.currency === 'EUR') { return floor.floor; } return null; @@ -196,6 +191,7 @@ export const spec = { isBidRequestValid: isBidRequestValid, buildRequests: buildRequests, interpretResponse: interpretResponse, + gvlid: PROXISTORE_VENDOR_ID, }; registerBidder(spec); diff --git a/modules/pubgeniusBidAdapter.js b/modules/pubgeniusBidAdapter.js index 89dea545434..28c4fdefd42 100644 --- a/modules/pubgeniusBidAdapter.js +++ b/modules/pubgeniusBidAdapter.js @@ -16,7 +16,7 @@ import { } from '../src/utils.js'; const BIDDER_VERSION = '1.1.0'; -const BASE_URL = 'https://ortb.adpearl.io'; +const BASE_URL = 'https://auction.adpearl.io'; export const spec = { code: 'pubgenius', diff --git a/modules/publinkIdSystem.js b/modules/publinkIdSystem.js index 299158a175e..990227e7cfe 100644 --- a/modules/publinkIdSystem.js +++ b/modules/publinkIdSystem.js @@ -81,13 +81,18 @@ function getlocalValue() { } if (typeof value === 'string') { - try { - const obj = JSON.parse(value); - if (obj && obj.exp && obj.exp > Date.now()) { - return obj.publink; + // if it's a json object parse it and return the publink value, otherwise assume the value is the id + if (value.charAt(0) === '{') { + try { + const obj = JSON.parse(value); + if (obj) { + return obj.publink; + } + } catch (e) { + logError(e); } - } catch (e) { - logError(e); + } else { + return value; } } } diff --git a/modules/pubmaticAnalyticsAdapter.js b/modules/pubmaticAnalyticsAdapter.js index 2477ab4c0a3..f69fb20e5d5 100755 --- a/modules/pubmaticAnalyticsAdapter.js +++ b/modules/pubmaticAnalyticsAdapter.js @@ -200,6 +200,21 @@ function getAdapterNameForAlias(aliasName) { return adapterManager.aliasRegistry[aliasName] || aliasName; } +function getAdDomain(bidResponse) { + if (bidResponse.meta && bidResponse.meta.advertiserDomains) { + let adomain = bidResponse.meta.advertiserDomains[0] + if (adomain) { + try { + let hostname = (new URL(adomain)); + return hostname.hostname.replace('www.', ''); + } catch (e) { + logWarn(LOG_PRE_FIX + 'Adomain URL (Not a proper URL):', adomain); + return adomain.replace('www.', ''); + } + } + } +} + function gatherPartnerBidsForAdUnitForLogger(adUnit, adUnitId, highestBid) { highestBid = (highestBid && highestBid.length > 0) ? highestBid[0] : null; return Object.keys(adUnit.bids).reduce(function(partnerBids, bidId) { @@ -218,6 +233,7 @@ function gatherPartnerBidsForAdUnitForLogger(adUnit, adUnitId, highestBid) { 'dc': bid.bidResponse ? (bid.bidResponse.dealChannel || EMPTY_STRING) : EMPTY_STRING, 'l1': bid.bidResponse ? bid.clientLatencyTimeMs : 0, 'l2': 0, + 'adv': bid.bidResponse ? getAdDomain(bid.bidResponse) || undefined : undefined, 'ss': (s2sBidders.indexOf(bid.bidder) > -1) ? 1 : 0, 't': (bid.status == ERROR && bid.error.code == TIMEOUT_ERROR) ? 1 : 0, 'wb': (highestBid && highestBid.requestId === bid.bidId ? 1 : 0), diff --git a/modules/pubmaticBidAdapter.js b/modules/pubmaticBidAdapter.js index 4c182c214a3..366a0326054 100644 --- a/modules/pubmaticBidAdapter.js +++ b/modules/pubmaticBidAdapter.js @@ -11,12 +11,14 @@ const USER_SYNC_URL_IFRAME = 'https://ads.pubmatic.com/AdServer/js/user_sync.htm const USER_SYNC_URL_IMAGE = 'https://image8.pubmatic.com/AdServer/ImgSync?p='; const DEFAULT_CURRENCY = 'USD'; const AUCTION_TYPE = 1; +const GROUPM_ALIAS = {code: 'groupm', gvlid: 98}; const UNDEFINED = undefined; const DEFAULT_WIDTH = 0; const DEFAULT_HEIGHT = 0; const PREBID_NATIVE_HELP_LINK = 'http://prebid.org/dev-docs/show-native-ads.html'; const PUBLICATION = 'pubmatic'; // Your publication on Blue Billywig, potentially with environment (e.g. publication.bbvms.com or publication.test.bbvms.com) const RENDERER_URL = 'https://pubmatic.bbvms.com/r/'.concat('$RENDERER', '.js'); // URL of the renderer application +const MSG_VIDEO_PLACEMENT_MISSING = 'Video.Placement param missing'; const CUSTOM_PARAMS = { 'kadpageurl': '', // Custom page url 'gender': '', // User gender @@ -537,12 +539,20 @@ function _createBannerRequest(bid) { return bannerObj; } +export function checkVideoPlacement(videoData, adUnitCode) { + // Check for video.placement property. If property is missing display log message. + if (!deepAccess(videoData, 'placement')) { + logWarn(MSG_VIDEO_PLACEMENT_MISSING + ' for ' + adUnitCode); + }; +} + function _createVideoRequest(bid) { var videoData = mergeDeep(deepAccess(bid.mediaTypes, 'video'), bid.params.video); var videoObj; if (videoData !== UNDEFINED) { videoObj = {}; + checkVideoPlacement(videoData, bid.adUnitCode); for (var key in VIDEO_CUSTOM_PARAMS) { if (videoData.hasOwnProperty(key)) { videoObj[key] = _checkParamDataType(key, videoData[key], VIDEO_CUSTOM_PARAMS[key]); @@ -857,10 +867,10 @@ function _handleEids(payload, validBidRequests) { function _checkMediaType(bid, newBid) { // Create a regex here to check the strings - if (bid.ext && bid.ext['BidType'] != undefined) { - newBid.mediaType = MEDIATYPE[bid.ext.BidType]; + if (bid.ext && bid.ext['bidtype'] != undefined) { + newBid.mediaType = MEDIATYPE[bid.ext.bidtype]; } else { - logInfo(LOG_WARN_PREFIX + 'bid.ext.BidType does not exist, checking alternatively for mediaType') + logInfo(LOG_WARN_PREFIX + 'bid.ext.bidtype does not exist, checking alternatively for mediaType') var adm = bid.adm; var admStr = ''; var videoRegex = new RegExp(/VAST\s+version/); @@ -996,6 +1006,7 @@ export const spec = { code: BIDDER_CODE, gvlid: 76, supportedMediaTypes: [BANNER, VIDEO, NATIVE], + aliases: [GROUPM_ALIAS], /** * Determines whether or not the given bid request is valid. Valid bid request must have placementId and hbid * diff --git a/modules/pubmaticBidAdapter.md b/modules/pubmaticBidAdapter.md index e9d93d79758..baf58177505 100644 --- a/modules/pubmaticBidAdapter.md +++ b/modules/pubmaticBidAdapter.md @@ -25,7 +25,7 @@ var adUnits = [ bidder: 'pubmatic', params: { publisherId: '156209', // required, must be a string, not an integer or other js type. - oustreamAU: 'renderer_test_pubmatic', // required if mediaTypes-> video-> context is 'outstream' and optional if renderer is defined in adUnits or in mediaType video. This value can be get by BlueBillyWig Team. + outstreamAU: 'renderer_test_pubmatic', // required if mediaTypes-> video-> context is 'outstream' and optional if renderer is defined in adUnits or in mediaType video. This value can be get by BlueBillyWig Team. adSlot: 'pubmatic_test2', // optional, must be a string, not an integer or other js type. pmzoneid: 'zone1, zone11', // optional lat: '40.712775', // optional diff --git a/modules/pubxaiAnalyticsAdapter.js b/modules/pubxaiAnalyticsAdapter.js index 3232d34ccba..0031ff5539b 100644 --- a/modules/pubxaiAnalyticsAdapter.js +++ b/modules/pubxaiAnalyticsAdapter.js @@ -21,6 +21,14 @@ let events = { deviceDetail: {} }; +function getStorage() { + try { + return window.top['sessionStorage']; + } catch (e) { + return null; + } +} + var pubxaiAnalyticsAdapter = Object.assign(adapter( { emptyUrl, @@ -132,6 +140,7 @@ pubxaiAnalyticsAdapter.shouldFireEventRequest = function (samplingRate = 1) { function send(data, status) { if (pubxaiAnalyticsAdapter.shouldFireEventRequest(initOptions.samplingRate)) { let location = getWindowLocation(); + const storage = getStorage(); data.initOptions = initOptions; if (typeof data !== 'undefined' && typeof data.auctionInit !== 'undefined') { Object.assign(data.pageDetail, { @@ -142,6 +151,13 @@ function send(data, status) { }); data.initOptions.auctionId = data.auctionInit.auctionId; delete data.auctionInit; + + data.pmcDetail = {} + Object.assign(data.pmcDetail, { + bidDensity: storage ? storage.getItem('pbx:dpbid') : null, + maxBid: storage ? storage.getItem('pbx:mxbid') : null, + auctionId: storage ? storage.getItem('pbx:aucid') : null, + }); } data.deviceDetail = {}; Object.assign(data.deviceDetail, { @@ -150,6 +166,7 @@ function send(data, status) { deviceOS: getOS(), browser: getBrowser() }); + let pubxaiAnalyticsRequestUrl = buildUrl({ protocol: 'https', hostname: (initOptions && initOptions.hostName) || defaultHost, diff --git a/modules/relaidoBidAdapter.js b/modules/relaidoBidAdapter.js index 16e01f80819..128fd72996b 100644 --- a/modules/relaidoBidAdapter.js +++ b/modules/relaidoBidAdapter.js @@ -6,7 +6,7 @@ import { getStorageManager } from '../src/storageManager.js'; const BIDDER_CODE = 'relaido'; const BIDDER_DOMAIN = 'api.relaido.jp'; -const ADAPTER_VERSION = '1.0.6'; +const ADAPTER_VERSION = '1.0.7'; const DEFAULT_TTL = 300; const UUID_KEY = 'relaido_uuid'; @@ -31,14 +31,14 @@ function isBidRequestValid(bid) { } function buildRequests(validBidRequests, bidderRequest) { - let bidRequests = []; + const bids = []; + let imuid = null; + let bidDomain = null; + let bidder = null; + let count = null; for (let i = 0; i < validBidRequests.length; i++) { const bidRequest = validBidRequests[i]; - const placementId = getBidIdParameter('placementId', bidRequest.params); - const bidDomain = bidRequest.params.domain || BIDDER_DOMAIN; - const bidUrl = `https://${bidDomain}/bid/v1/prebid/${placementId}`; - const uuid = getUuid(); let mediaType = ''; let width = 0; let height = 0; @@ -55,46 +55,63 @@ function buildRequests(validBidRequests, bidderRequest) { mediaType = BANNER; } - let payload = { - version: ADAPTER_VERSION, - timeout_ms: bidderRequest.timeout, - ad_unit_code: bidRequest.adUnitCode, - auction_id: bidRequest.auctionId, - bidder: bidRequest.bidder, - bidder_request_id: bidRequest.bidderRequestId, - bid_requests_count: bidRequest.bidRequestsCount, - bid_id: bidRequest.bidId, - transaction_id: bidRequest.transactionId, - media_type: mediaType, - uuid: uuid, - width: width, - height: height, - pv: '$prebid.version$' - }; + if (!imuid) { + const pickImuid = deepAccess(bidRequest, 'userId.imuid'); + if (pickImuid) { + imuid = pickImuid; + } + } + + if (!bidDomain) { + bidDomain = bidRequest.params.domain; + } + + if (!bidder) { + bidder = bidRequest.bidder + } + + if (!bidder) { + bidder = bidRequest.bidder + } - const imuid = deepAccess(bidRequest, 'userId.imuid'); - if (imuid) { - payload.imuid = imuid; + if (!count) { + count = bidRequest.bidRequestsCount; } - // It may not be encoded, so add it at the end of the payload - payload.ref = bidderRequest.refererInfo.referer; - - bidRequests.push({ - method: 'GET', - url: bidUrl, - data: payload, - options: { - withCredentials: true - }, - bidId: bidRequest.bidId, + bids.push({ + bid_id: bidRequest.bidId, + placement_id: getBidIdParameter('placementId', bidRequest.params), + transaction_id: bidRequest.transactionId, + bidder_request_id: bidRequest.bidderRequestId, + ad_unit_code: bidRequest.adUnitCode, + auction_id: bidRequest.auctionId, player: bidRequest.params.player, - width: payload.width, - height: payload.height, - mediaType: payload.media_type + width: width, + height: height, + media_type: mediaType }); } - return bidRequests; + + const data = JSON.stringify({ + version: ADAPTER_VERSION, + bids: bids, + timeout_ms: bidderRequest.timeout, + bidder: bidder, + bid_requests_count: count, + uuid: getUuid(), + pv: '$prebid.version$', + imuid: imuid, + ref: bidderRequest.refererInfo.referer + }) + + return { + method: 'POST', + url: `https://${bidDomain || BIDDER_DOMAIN}/bid/v1/sprebid`, + options: { + withCredentials: true + }, + data: data + }; } function interpretResponse(serverResponse, bidRequest) { @@ -105,34 +122,38 @@ function interpretResponse(serverResponse, bidRequest) { } const playerUrl = bidRequest.player || body.playerUrl; - const mediaType = bidRequest.mediaType || VIDEO; - - let bidResponse = { - requestId: bidRequest.bidId, - width: bidRequest.width, - height: bidRequest.height, - cpm: body.price, - currency: body.currency, - creativeId: body.creativeId, - dealId: body.dealId || '', - ttl: body.ttl || DEFAULT_TTL, - netRevenue: true, - mediaType: mediaType, - meta: { - advertiserDomains: body.adomain || [], - mediaType: VIDEO + + for (const res of body.ads) { + let bidResponse = { + requestId: res.bidId, + width: res.width, + height: res.height, + cpm: res.price, + currency: res.currency, + creativeId: res.creativeId, + dealId: body.dealId || '', + ttl: body.ttl || DEFAULT_TTL, + netRevenue: true, + mediaType: res.mediaType || VIDEO, + meta: { + advertiserDomains: res.adomain || [], + mediaType: VIDEO + } + }; + + if (bidResponse.mediaType === VIDEO) { + bidResponse.vastXml = res.vast; + bidResponse.renderer = newRenderer(res.bidId, playerUrl); + } else { + const playerTag = createPlayerTag(playerUrl); + const renderTag = createRenderTag(res.width, res.height, res.vast); + bidResponse.ad = `
${playerTag}${renderTag}
`; } - }; - if (mediaType === VIDEO) { - bidResponse.vastXml = body.vast; - bidResponse.renderer = newRenderer(bidRequest.bidId, playerUrl); - } else { - const playerTag = createPlayerTag(playerUrl); - const renderTag = createRenderTag(bidRequest.width, bidRequest.height, body.vast); - bidResponse.ad = `
${playerTag}${renderTag}
`; + bidResponses.push(bidResponse); } - bidResponses.push(bidResponse); + // eslint-disable-next-line no-console + console.log(JSON.stringify(bidResponses)); return bidResponses; } diff --git a/modules/richaudienceBidAdapter.js b/modules/richaudienceBidAdapter.js index eace460eb22..0f62e988249 100755 --- a/modules/richaudienceBidAdapter.js +++ b/modules/richaudienceBidAdapter.js @@ -1,4 +1,4 @@ -import { isEmpty, deepAccess, isStr } from '../src/utils.js'; +import {isEmpty, deepAccess, isStr} from '../src/utils.js'; import {registerBidder} from '../src/adapters/bidderFactory.js'; import {config} from '../src/config.js'; import {BANNER, VIDEO} from '../src/mediaTypes.js'; @@ -52,17 +52,17 @@ export const spec = { videoData: raiGetVideoInfo(bid), scr_rsl: raiGetResolution(), cpuc: (typeof window.navigator != 'undefined' ? window.navigator.hardwareConcurrency : null), - kws: (!isEmpty(bid.params.keywords) ? bid.params.keywords : null) + kws: (!isEmpty(bid.params.keywords) ? bid.params.keywords : null), + schain: bid.schain }; REFERER = (typeof bidderRequest.refererInfo.referer != 'undefined' ? encodeURIComponent(bidderRequest.refererInfo.referer) : null) payload.gdpr_consent = ''; - payload.gdpr = null; + payload.gdpr = bidderRequest.gdprConsent.gdprApplies; if (bidderRequest && bidderRequest.gdprConsent) { payload.gdpr_consent = bidderRequest.gdprConsent.consentString; - payload.gdpr = bidderRequest.gdprConsent.gdprApplies; } var payloadString = JSON.stringify(payload); @@ -296,7 +296,7 @@ function raiGetFloor(bid, config) { } else if (typeof bid.getFloor == 'function') { let floorSpec = bid.getFloor({ currency: config.getConfig('currency.adServerCurrency'), - mediaType: bid.mediaType.banner ? 'banner' : 'video', + mediaType: typeof bid.mediaTypes['banner'] == 'object' ? 'banner' : 'video', size: '*' }) diff --git a/modules/riseBidAdapter.js b/modules/riseBidAdapter.js index 7f84dbd3344..70777fe03f2 100644 --- a/modules/riseBidAdapter.js +++ b/modules/riseBidAdapter.js @@ -159,7 +159,7 @@ function getSupplyChain(schainObject) { scStr += '!'; scStr += `${getEncodedValIfNotEmpty(node.asi)},`; scStr += `${getEncodedValIfNotEmpty(node.sid)},`; - scStr += `${getEncodedValIfNotEmpty(node.hp)},`; + scStr += `${node.hp ? encodeURIComponent(node.hp) : ''},`; scStr += `${getEncodedValIfNotEmpty(node.rid)},`; scStr += `${getEncodedValIfNotEmpty(node.name)},`; scStr += `${getEncodedValIfNotEmpty(node.domain)}`; diff --git a/modules/rtbhouseBidAdapter.js b/modules/rtbhouseBidAdapter.js index 2c562e5841a..40389b1e1e0 100644 --- a/modules/rtbhouseBidAdapter.js +++ b/modules/rtbhouseBidAdapter.js @@ -165,16 +165,26 @@ function mapBanner(slot) { * @returns {object} Site by OpenRTB 2.5 §3.2.13 */ function mapSite(slot, bidderRequest) { - const pubId = slot && slot.length > 0 - ? slot[0].params.publisherId - : 'unknown'; - return { + let pubId = 'unknown'; + let channel = null; + if (slot && slot.length > 0) { + pubId = slot[0].params.publisherId; + channel = slot[0].params.channel && + slot[0].params.channel + .toString() + .slice(0, 50); + } + let siteData = { publisher: { id: pubId.toString(), }, page: bidderRequest.refererInfo.referer, name: getOrigin() + }; + if (channel) { + siteData.channel = channel; } + return siteData; } /** diff --git a/modules/rtdModule/index.js b/modules/rtdModule/index.js index 7dce09f0d1d..6c88d66999a 100644 --- a/modules/rtdModule/index.js +++ b/modules/rtdModule/index.js @@ -42,10 +42,10 @@ * @function? * @summary modify bid request data * @name RtdSubmodule#getBidRequestData - * @param {SubmoduleConfig} config - * @param {UserConsentData} userConsent * @param {Object} reqBidsConfigObj * @param {function} callback + * @param {SubmoduleConfig} config + * @param {UserConsentData} userConsent */ /** @@ -98,6 +98,15 @@ * @param {UserConsentData} userConsent */ +/** + * @function? + * @summary on bid requested event + * @name RtdSubmodule#onBidRequestEvent + * @param {Object} data + * @param {SubmoduleConfig} config + * @param {UserConsentData} userConsent + */ + /** * @interface ModuleConfig */ @@ -143,7 +152,7 @@ import {config} from '../../src/config.js'; import {module} from '../../src/hook.js'; -import { logError, logWarn } from '../../src/utils.js'; +import {logError, logInfo, logWarn} from '../../src/utils.js'; import events from '../../src/events.js'; import CONSTANTS from '../../src/constants.json'; import {gdprDataHandler, uspDataHandler} from '../../src/adapterManager.js'; @@ -164,13 +173,51 @@ let _dataProviders = []; let _userConsent; /** - * enable submodule in User ID + * Register a RTD submodule. + * * @param {RtdSubmodule} submodule + * @returns {function()} a de-registration function that will unregister the module when called. */ export function attachRealTimeDataProvider(submodule) { registeredSubModules.push(submodule); + return function detach() { + const idx = registeredSubModules.indexOf(submodule) + if (idx >= 0) { + registeredSubModules.splice(idx, 1); + initSubModules(); + } + } } +/** + * call each sub module event function by config order + */ +const setEventsListeners = (function () { + let registered = false; + return function setEventsListeners() { + if (!registered) { + Object.entries({ + [CONSTANTS.EVENTS.AUCTION_INIT]: ['onAuctionInitEvent'], + [CONSTANTS.EVENTS.AUCTION_END]: ['onAuctionEndEvent', getAdUnitTargeting], + [CONSTANTS.EVENTS.BID_RESPONSE]: ['onBidResponseEvent'], + [CONSTANTS.EVENTS.BID_REQUESTED]: ['onBidRequestEvent'] + }).forEach(([ev, [handler, preprocess]]) => { + events.on(ev, (args) => { + preprocess && preprocess(args); + subModules.forEach(sm => { + try { + sm[handler] && sm[handler](args, sm.config, _userConsent) + } catch (e) { + logError(`RTD provider '${sm.name}': error in '${handler}':`, e); + } + }); + }) + }); + registered = true; + } + } +})(); + export function init(config) { const confListener = config.getConfig(MODULE_NAME, ({realTimeData}) => { if (!realTimeData.dataProviders) { @@ -209,22 +256,7 @@ function initSubModules() { } }); subModules = subModulesByOrder; -} - -/** - * call each sub module event function by config order - */ -function setEventsListeners() { - events.on(CONSTANTS.EVENTS.AUCTION_INIT, (args) => { - subModules.forEach(sm => { sm.onAuctionInitEvent && sm.onAuctionInitEvent(args, sm.config, _userConsent) }) - }); - events.on(CONSTANTS.EVENTS.AUCTION_END, (args) => { - getAdUnitTargeting(args); - subModules.forEach(sm => { sm.onAuctionEndEvent && sm.onAuctionEndEvent(args, sm.config, _userConsent) }) - }); - events.on(CONSTANTS.EVENTS.BID_RESPONSE, (args) => { - subModules.forEach(sm => { sm.onBidResponseEvent && sm.onBidResponseEvent(args, sm.config, _userConsent) }) - }); + logInfo(`Real time data module enabled, using submodules: ${subModules.map((m) => m.name).join(', ')}`); } /** @@ -259,18 +291,12 @@ export function setBidRequestsData(fn, reqBidsConfigObj) { return exitHook(); } - if (shouldDelayAuction) { - waitTimeout = setTimeout(exitHook, _moduleConfig.auctionDelay); - } + waitTimeout = setTimeout(exitHook, shouldDelayAuction ? _moduleConfig.auctionDelay : 0); relevantSubModules.forEach(sm => { sm.getBidRequestData(reqBidsConfigObj, onGetBidRequestDataCallback.bind(sm), sm.config, _userConsent) }); - if (!shouldDelayAuction) { - return exitHook(); - } - function onGetBidRequestDataCallback() { if (isDone) { return; @@ -278,12 +304,15 @@ export function setBidRequestsData(fn, reqBidsConfigObj) { if (this.config && this.config.waitForIt) { callbacksExpected--; } - if (callbacksExpected <= 0) { - return exitHook(); + if (callbacksExpected === 0) { + setTimeout(exitHook, 0); } } function exitHook() { + if (isDone) { + return; + } isDone = true; clearTimeout(waitTimeout); fn.call(this, reqBidsConfigObj); diff --git a/modules/rubiconAnalyticsAdapter.js b/modules/rubiconAnalyticsAdapter.js index 5b17048cbd3..ed24598e986 100644 --- a/modules/rubiconAnalyticsAdapter.js +++ b/modules/rubiconAnalyticsAdapter.js @@ -234,6 +234,9 @@ function sendMessage(auctionId, bidWonId, trigger) { let auction = { clientTimeoutMillis: auctionCache.timeout, + auctionStart: auctionCache.timestamp, + auctionEnd: auctionCache.endTs, + bidderOrder: auctionCache.bidderOrder, samplingFactor, accountId, adUnits: Object.keys(adUnitMap).map(i => adUnitMap[i]), @@ -384,8 +387,9 @@ export function parseBidResponse(bid, previousBidResponse, auctionFloorData) { 'floorRuleValue', () => deepAccess(bid, 'floorData.floorRuleValue'), 'floorRule', () => debugTurnedOn() ? deepAccess(bid, 'floorData.floorRule') : undefined, 'adomains', () => { - let adomains = deepAccess(bid, 'meta.advertiserDomains'); - return Array.isArray(adomains) && adomains.length > 0 ? adomains.slice(0, 10) : undefined + const adomains = deepAccess(bid, 'meta.advertiserDomains'); + const validAdomains = Array.isArray(adomains) && adomains.filter(domain => typeof domain === 'string'); + return validAdomains && validAdomains.length > 0 ? validAdomains.slice(0, 10) : undefined } ]); } @@ -582,6 +586,7 @@ let rubiconAdapter = Object.assign({}, baseAdapter, { cacheEntry.bids = {}; cacheEntry.bidsWon = {}; cacheEntry.gamHasRendered = {}; + cacheEntry.bidderOrder = []; cacheEntry.referrer = deepAccess(args, 'bidderRequests.0.refererInfo.referer'); const floorData = deepAccess(args, 'bidderRequests.0.bids.0.floorData'); if (floorData) { @@ -607,6 +612,7 @@ let rubiconAdapter = Object.assign({}, baseAdapter, { } break; case BID_REQUESTED: + cache.auctions[args.auctionId].bidderOrder.push(args.bidderCode); Object.assign(cache.auctions[args.auctionId].bids, args.bids.reduce((memo, bid) => { // mark adUnits we expect bidWon events for cache.auctions[args.auctionId].bidsWon[bid.adUnitCode] = false; diff --git a/modules/rubiconBidAdapter.js b/modules/rubiconBidAdapter.js index 501ccc98e33..28e491900f8 100644 --- a/modules/rubiconBidAdapter.js +++ b/modules/rubiconBidAdapter.js @@ -1,4 +1,4 @@ -import { mergeDeep, _each, logError, deepAccess, deepSetValue, isStr, isNumber, logWarn, convertTypes, isArray, parseSizesInput, logMessage } from '../src/utils.js'; +import { mergeDeep, _each, logError, deepAccess, deepSetValue, isStr, isNumber, logWarn, convertTypes, isArray, parseSizesInput, logMessage, formatQS } from '../src/utils.js'; import {registerBidder} from '../src/adapters/bidderFactory.js'; import {config} from '../src/config.js'; import {BANNER, VIDEO} from '../src/mediaTypes.js'; @@ -765,21 +765,23 @@ export const spec = { getUserSyncs: function (syncOptions, responses, gdprConsent, uspConsent) { if (!hasSynced && syncOptions.iframeEnabled) { // data is only assigned if params are available to pass to syncEndpoint - let params = ''; + let params = {}; - if (gdprConsent && typeof gdprConsent.consentString === 'string') { - // add 'gdpr' only if 'gdprApplies' is defined + if (gdprConsent) { if (typeof gdprConsent.gdprApplies === 'boolean') { - params += `?gdpr=${Number(gdprConsent.gdprApplies)}&gdpr_consent=${gdprConsent.consentString}`; - } else { - params += `?gdpr_consent=${gdprConsent.consentString}`; + params['gdpr'] = Number(gdprConsent.gdprApplies); + } + if (typeof gdprConsent.consentString === 'string') { + params['gdpr_consent'] = gdprConsent.consentString; } } if (uspConsent) { - params += `${params ? '&' : '?'}us_privacy=${encodeURIComponent(uspConsent)}`; + params['us_privacy'] = encodeURIComponent(uspConsent); } + params = Object.keys(params).length ? `?${formatQS(params)}` : ''; + hasSynced = true; return { type: 'iframe', @@ -992,6 +994,8 @@ function applyFPD(bidRequest, mediaType, data) { let fpd = mergeDeep({}, config.getConfig('ortb2') || {}, BID_FPD); let impData = deepAccess(bidRequest.ortb2Imp, 'ext.data') || {}; + + const gpid = deepAccess(bidRequest, 'ortb2Imp.ext.gpid'); const SEGTAX = {user: [4], site: [1, 2, 5, 6]}; const MAP = {user: 'tg_v.', site: 'tg_i.', adserver: 'tg_i.dfp_ad_unit_code', pbadslot: 'tg_i.pbadslot', keywords: 'kw'}; const validate = function(prop, key, parentName) { @@ -1020,16 +1024,6 @@ function applyFPD(bidRequest, mediaType, data) { data[loc] = (data[loc]) ? data[loc].concat(',', val) : val; } - Object.keys(impData).forEach((key) => { - if (key === 'adserver') { - ['name', 'adslot'].forEach(prop => { - if (impData[key][prop]) impData[key][prop] = impData[key][prop].toString().replace(/^\/+/, ''); - }); - } else if (key === 'pbadslot') { - impData[key] = impData[key].toString().replace(/^\/+/, ''); - } - }); - if (mediaType === BANNER) { ['site', 'user'].forEach(name => { Object.keys(fpd[name]).forEach((key) => { @@ -1045,12 +1039,30 @@ function applyFPD(bidRequest, mediaType, data) { }); }); Object.keys(impData).forEach((key) => { - (key === 'adserver') ? addBannerData(impData[key].adslot, name, key) : addBannerData(impData[key], 'site', key); + if (key !== 'adserver') { + addBannerData(impData[key], 'site', key); + } else if (impData[key].name === 'gam') { + addBannerData(impData[key].adslot, name, key) + } }); + + // add in gpid + if (gpid) { + data['p_gpid'] = gpid; + } + + // only send one of pbadslot or dfp adunit code (prefer pbadslot) + if (data['tg_i.pbadslot']) { + delete data['tg_i.dfp_ad_unit_code']; + } } else { if (Object.keys(impData).length) { mergeDeep(data.imp[0].ext, {data: impData}); } + // add in gpid + if (gpid) { + data.imp[0].ext.gpid = gpid; + } mergeDeep(data, fpd); } diff --git a/modules/saambaaBidAdapter.js b/modules/saambaaBidAdapter.js new file mode 100644 index 00000000000..2810853532d --- /dev/null +++ b/modules/saambaaBidAdapter.js @@ -0,0 +1,420 @@ +import { deepAccess, isFn, generateUUID, parseUrl, isEmpty, parseSizesInput } from '../src/utils.js'; +import { config } from '../src/config.js'; +import { registerBidder } from '../src/adapters/bidderFactory.js'; +import { VIDEO, BANNER } from '../src/mediaTypes.js'; +import find from 'core-js-pure/features/array/find.js'; +import includes from 'core-js-pure/features/array/includes.js'; + +const ADAPTER_VERSION = '1.0'; +const BIDDER_CODE = 'saambaa'; + +export const VIDEO_ENDPOINT = 'https://nep.advangelists.com/xp/get?pubid='; +export const BANNER_ENDPOINT = 'https://nep.advangelists.com/xp/get?pubid='; +export const OUTSTREAM_SRC = 'https://player-cdn.beachfrontmedia.com/playerapi/loader/outstream.js'; +export const VIDEO_TARGETING = ['mimes', 'playbackmethod', 'maxduration', 'skip', 'playerSize', 'context']; +export const DEFAULT_MIMES = ['video/mp4', 'application/javascript']; + +let pubid = ''; + +export const spec = { + code: BIDDER_CODE, + supportedMediaTypes: [BANNER, VIDEO], + + isBidRequestValid(bidRequest) { + if (typeof bidRequest != 'undefined') { + if (bidRequest.bidder !== BIDDER_CODE && typeof bidRequest.params === 'undefined') { return false; } + if (bidRequest === '' || bidRequest.params.placement === '' || bidRequest.params.pubid === '') { return false; } + return true; + } else { return false; } + }, + + buildRequests(bids, bidderRequest) { + let requests = []; + let videoBids = bids.filter(bid => isVideoBidValid(bid)); + let bannerBids = bids.filter(bid => isBannerBidValid(bid)); + videoBids.forEach(bid => { + pubid = getVideoBidParam(bid, 'pubid'); + requests.push({ + method: 'POST', + url: VIDEO_ENDPOINT + pubid, + data: createVideoRequestData(bid, bidderRequest), + bidRequest: bid + }); + }); + + bannerBids.forEach(bid => { + pubid = getBannerBidParam(bid, 'pubid'); + + requests.push({ + method: 'POST', + url: BANNER_ENDPOINT + pubid, + data: createBannerRequestData(bid, bidderRequest), + bidRequest: bid + }); + }); + return requests; + }, + + interpretResponse(serverResponse, {bidRequest}) { + let response = serverResponse.body; + if (response !== null && isEmpty(response) == false) { + if (isVideoBid(bidRequest)) { + let bidResponse = { + requestId: response.id, + bidderCode: BIDDER_CODE, + cpm: response.seatbid[0].bid[0].price, + width: response.seatbid[0].bid[0].w, + height: response.seatbid[0].bid[0].h, + ttl: response.seatbid[0].bid[0].ttl || 60, + creativeId: response.seatbid[0].bid[0].crid, + currency: response.cur, + meta: { 'advertiserDomains': response.seatbid[0].bid[0].adomain }, + mediaType: VIDEO, + netRevenue: true + } + + if (response.seatbid[0].bid[0].adm) { + bidResponse.vastXml = response.seatbid[0].bid[0].adm; + bidResponse.adResponse = { + content: response.seatbid[0].bid[0].adm + }; + } else { + bidResponse.vastUrl = response.seatbid[0].bid[0].nurl; + } + + return bidResponse; + } else { + return { + requestId: response.id, + bidderCode: BIDDER_CODE, + cpm: response.seatbid[0].bid[0].price, + width: response.seatbid[0].bid[0].w, + height: response.seatbid[0].bid[0].h, + ad: response.seatbid[0].bid[0].adm, + ttl: response.seatbid[0].bid[0].ttl || 60, + creativeId: response.seatbid[0].bid[0].crid, + currency: response.cur, + meta: { 'advertiserDomains': response.seatbid[0].bid[0].adomain }, + mediaType: BANNER, + netRevenue: true + } + } + } + } +}; + +function isBannerBid(bid) { + return deepAccess(bid, 'mediaTypes.banner') || !isVideoBid(bid); +} + +function isVideoBid(bid) { + return deepAccess(bid, 'mediaTypes.video'); +} + +function getBannerBidFloor(bid) { + let floorInfo = isFn(bid.getFloor) ? bid.getFloor({ currency: 'USD', mediaType: 'banner', size: '*' }) : {}; + return floorInfo.floor || getBannerBidParam(bid, 'bidfloor'); +} + +function getVideoBidFloor(bid) { + let floorInfo = isFn(bid.getFloor) ? bid.getFloor({ currency: 'USD', mediaType: 'video', size: '*' }) : {}; + return floorInfo.floor || getVideoBidParam(bid, 'bidfloor'); +} + +function isVideoBidValid(bid) { + return isVideoBid(bid) && getVideoBidParam(bid, 'pubid') && getVideoBidParam(bid, 'placement'); +} + +function isBannerBidValid(bid) { + return isBannerBid(bid) && getBannerBidParam(bid, 'pubid') && getBannerBidParam(bid, 'placement'); +} + +function getVideoBidParam(bid, key) { + return deepAccess(bid, 'params.video.' + key) || deepAccess(bid, 'params.' + key); +} + +function getBannerBidParam(bid, key) { + return deepAccess(bid, 'params.banner.' + key) || deepAccess(bid, 'params.' + key); +} + +function isMobile() { + return (/(ios|ipod|ipad|iphone|android)/i).test(navigator.userAgent); +} + +function isConnectedTV() { + return (/(smart[-]?tv|hbbtv|appletv|googletv|hdmi|netcast\.tv|viera|nettv|roku|\bdtv\b|sonydtv|inettvbrowser|\btv\b)/i).test(navigator.userAgent); +} + +function getDoNotTrack() { + return navigator.doNotTrack === '1' || window.doNotTrack === '1' || navigator.msDoNoTrack === '1' || navigator.doNotTrack === 'yes'; +} + +function findAndFillParam(o, key, value) { + try { + if (typeof value === 'function') { + o[key] = value(); + } else { + o[key] = value; + } + } catch (ex) {} +} + +function getOsVersion() { + let clientStrings = [ + { s: 'Android', r: /Android/ }, + { s: 'iOS', r: /(iPhone|iPad|iPod)/ }, + { s: 'Mac OS X', r: /Mac OS X/ }, + { s: 'Mac OS', r: /(MacPPC|MacIntel|Mac_PowerPC|Macintosh)/ }, + { s: 'Linux', r: /(Linux|X11)/ }, + { s: 'Windows 10', r: /(Windows 10.0|Windows NT 10.0)/ }, + { s: 'Windows 8.1', r: /(Windows 8.1|Windows NT 6.3)/ }, + { s: 'Windows 8', r: /(Windows 8|Windows NT 6.2)/ }, + { s: 'Windows 7', r: /(Windows 7|Windows NT 6.1)/ }, + { s: 'Windows Vista', r: /Windows NT 6.0/ }, + { s: 'Windows Server 2003', r: /Windows NT 5.2/ }, + { s: 'Windows XP', r: /(Windows NT 5.1|Windows XP)/ }, + { s: 'UNIX', r: /UNIX/ }, + { s: 'Search Bot', r: /(nuhk|Googlebot|Yammybot|Openbot|Slurp|MSNBot|Ask Jeeves\/Teoma|ia_archiver)/ } + ]; + let cs = find(clientStrings, cs => cs.r.test(navigator.userAgent)); + return cs ? cs.s : 'unknown'; +} + +function getFirstSize(sizes) { + return (sizes && sizes.length) ? sizes[0] : { w: undefined, h: undefined }; +} + +function parseSizes(sizes) { + return parseSizesInput(sizes).map(size => { + let [ width, height ] = size.split('x'); + return { + w: parseInt(width, 10) || undefined, + h: parseInt(height, 10) || undefined + }; + }); +} + +function getVideoSizes(bid) { + return parseSizes(deepAccess(bid, 'mediaTypes.video.playerSize') || bid.sizes); +} + +function getBannerSizes(bid) { + return parseSizes(deepAccess(bid, 'mediaTypes.banner.sizes') || bid.sizes); +} + +function getTopWindowReferrer() { + try { + return window.top.document.referrer; + } catch (e) { + return ''; + } +} + +function getVideoTargetingParams(bid) { + const result = {}; + const excludeProps = ['playerSize', 'context', 'w', 'h']; + Object.keys(Object(bid.mediaTypes.video)) + .filter(key => !includes(excludeProps, key)) + .forEach(key => { + result[ key ] = bid.mediaTypes.video[ key ]; + }); + Object.keys(Object(bid.params.video)) + .filter(key => includes(VIDEO_TARGETING, key)) + .forEach(key => { + result[ key ] = bid.params.video[ key ]; + }); + return result; +} + +function createVideoRequestData(bid, bidderRequest) { + let topLocation = getTopWindowLocation(bidderRequest); + let topReferrer = getTopWindowReferrer(); + + // if size is explicitly given via adapter params + let paramSize = getVideoBidParam(bid, 'size'); + let sizes = []; + let coppa = config.getConfig('coppa'); + + if (typeof paramSize !== 'undefined' && paramSize != '') { + sizes = parseSizes(paramSize); + } else { + sizes = getVideoSizes(bid); + } + const firstSize = getFirstSize(sizes); + let floor = (getVideoBidFloor(bid) == null || typeof getVideoBidFloor(bid) == 'undefined') ? 0.5 : getVideoBidFloor(bid); + let video = getVideoTargetingParams(bid); + const o = { + 'device': { + 'langauge': (global.navigator.language).split('-')[0], + 'dnt': (global.navigator.doNotTrack === 1 ? 1 : 0), + 'devicetype': isMobile() ? 4 : isConnectedTV() ? 3 : 2, + 'js': 1, + 'os': getOsVersion() + }, + 'at': 2, + 'site': {}, + 'tmax': 3000, + 'cur': ['USD'], + 'id': bid.bidId, + 'imp': [], + 'regs': { + 'ext': { + } + }, + 'user': { + 'ext': { + } + } + }; + + o.site['page'] = topLocation.href; + o.site['domain'] = topLocation.hostname; + o.site['search'] = topLocation.search; + o.site['domain'] = topLocation.hostname; + o.site['ref'] = topReferrer; + o.site['mobile'] = isMobile() ? 1 : 0; + const secure = topLocation.protocol.indexOf('https') === 0 ? 1 : 0; + + o.device['dnt'] = getDoNotTrack() ? 1 : 0; + + findAndFillParam(o.site, 'name', function() { + return global.top.document.title; + }); + + findAndFillParam(o.device, 'h', function() { + return global.screen.height; + }); + findAndFillParam(o.device, 'w', function() { + return global.screen.width; + }); + + let placement = getVideoBidParam(bid, 'placement'); + + for (let j = 0; j < sizes.length; j++) { + o.imp.push({ + 'id': '' + j, + 'displaymanager': '' + BIDDER_CODE, + 'displaymanagerver': '' + ADAPTER_VERSION, + 'tagId': placement, + 'bidfloor': floor, + 'bidfloorcur': 'USD', + 'secure': secure, + 'video': Object.assign({ + 'id': generateUUID(), + 'pos': 0, + 'w': firstSize.w, + 'h': firstSize.h, + 'mimes': DEFAULT_MIMES + }, video) + + }); + } + if (coppa) { + o.regs.ext = {'coppa': 1}; + } + if (bidderRequest && bidderRequest.gdprConsent) { + let { gdprApplies, consentString } = bidderRequest.gdprConsent; + o.regs.ext = {'gdpr': gdprApplies ? 1 : 0}; + o.user.ext = {'consent': consentString}; + } + + return o; +} + +function getTopWindowLocation(bidderRequest) { + let url = bidderRequest && bidderRequest.refererInfo && bidderRequest.refererInfo.referer; + return parseUrl(config.getConfig('pageUrl') || url, { decodeSearchAsString: true }); +} + +function createBannerRequestData(bid, bidderRequest) { + let topLocation = getTopWindowLocation(bidderRequest); + let topReferrer = getTopWindowReferrer(); + + // if size is explicitly given via adapter params + + let paramSize = getBannerBidParam(bid, 'size'); + let sizes = []; + let coppa = config.getConfig('coppa'); + if (typeof paramSize !== 'undefined' && paramSize != '') { + sizes = parseSizes(paramSize); + } else { + sizes = getBannerSizes(bid); + } + + let floor = (getBannerBidFloor(bid) == null || typeof getBannerBidFloor(bid) == 'undefined') ? 0.1 : getBannerBidFloor(bid); + const o = { + 'device': { + 'langauge': (global.navigator.language).split('-')[0], + 'dnt': (global.navigator.doNotTrack === 1 ? 1 : 0), + 'devicetype': isMobile() ? 4 : isConnectedTV() ? 3 : 2, + 'js': 1 + }, + 'at': 2, + 'site': {}, + 'tmax': 3000, + 'cur': ['USD'], + 'id': bid.bidId, + 'imp': [], + 'regs': { + 'ext': { + } + }, + 'user': { + 'ext': { + } + } + }; + + o.site['page'] = topLocation.href; + o.site['domain'] = topLocation.hostname; + o.site['search'] = topLocation.search; + o.site['domain'] = topLocation.hostname; + o.site['ref'] = topReferrer; + o.site['mobile'] = isMobile() ? 1 : 0; + const secure = topLocation.protocol.indexOf('https') === 0 ? 1 : 0; + + o.device['dnt'] = getDoNotTrack() ? 1 : 0; + + findAndFillParam(o.site, 'name', function() { + return global.top.document.title; + }); + + findAndFillParam(o.device, 'h', function() { + return global.screen.height; + }); + findAndFillParam(o.device, 'w', function() { + return global.screen.width; + }); + + let placement = getBannerBidParam(bid, 'placement'); + for (let j = 0; j < sizes.length; j++) { + let size = sizes[j]; + + o.imp.push({ + 'id': '' + j, + 'displaymanager': '' + BIDDER_CODE, + 'displaymanagerver': '' + ADAPTER_VERSION, + 'tagId': placement, + 'bidfloor': floor, + 'bidfloorcur': 'USD', + 'secure': secure, + 'banner': { + 'id': generateUUID(), + 'pos': 0, + 'w': size['w'], + 'h': size['h'] + } + }); + } + if (coppa) { + o.regs.ext = {'coppa': 1}; + } + if (bidderRequest && bidderRequest.gdprConsent) { + let { gdprApplies, consentString } = bidderRequest.gdprConsent; + o.regs.ext = {'gdpr': gdprApplies ? 1 : 0}; + o.user.ext = {'consent': consentString}; + } + + return o; +} +registerBidder(spec); diff --git a/modules/saambaaBidAdapter.md b/modules/saambaaBidAdapter.md index 2d391da7628..d58e3f0abfa 100755 --- a/modules/saambaaBidAdapter.md +++ b/modules/saambaaBidAdapter.md @@ -1,69 +1,66 @@ -# Overview - -``` -Module Name: Saambaa Bidder Adapter -Module Type: Bidder Adapter -Maintainer: matt.voigt@saambaa.com -``` - -# Description - -Connects to Saambaa exchange for bids. - -Saambaa bid adapter supports Banner and Video ads currently. - -For more informatio - -# Sample Display Ad Unit: For Publishers -```javascript - -var displayAdUnit = [ -{ - code: 'display', - mediaTypes: { - banner: { - sizes: [[300, 250],[320, 50]] - } - } - bids: [{ - bidder: 'saambaa', - params: { - pubid: '121ab139faf7ac67428a23f1d0a9a71b', - placement: 1234, - size: '320x50' - } - }] -}]; -``` - -# Sample Video Ad Unit: For Publishers -```javascript - -var videoAdUnit = { - code: 'video', - sizes: [320,480], - mediaTypes: { - video: { - playerSize : [[320, 480]], - context: 'instream' - } - }, - bids: [ - { - bidder: 'saambaa', - params: { - pubid: '121ab139faf7ac67428a23f1d0a9a71b', - placement: 1234, - size: "320x480", - video: { - id: 123, - skip: 1, - mimes : ['video/mp4', 'application/javascript'], - playbackmethod : [2,6], - maxduration: 30 - } - } - } - ] - }; +# Overview + +``` +Module Name: Saambaa Bidder Adapter +Module Type: Bidder Adapter +Maintainer: matt.voigt@saambaa.com +``` + +# Description + +Connects to Saambaa exchange for bids. + +Saambaa bid adapter supports Banner and Video ads currently. + +For more informatio + +# Sample Display Ad Unit: For Publishers +```javascript + +var displayAdUnit = [ +{ + code: 'display', + mediaTypes: { + banner: { + sizes: [[300, 250],[320, 50]] + } + } + bids: [{ + bidder: 'saambaa', + params: { + pubid: '121ab139faf7ac67428a23f1d0a9a71b', + placement: 1234, + size: '320x50' + } + }] +}]; +``` + +# Sample Video Ad Unit: For Publishers +```javascript + +var videoAdUnit = { + code: 'video', + sizes: [320,480], + mediaTypes: { + video: { + playerSize : [[320, 480]], + context: 'instream', + skip: 1, + mimes : ['video/mp4', 'application/javascript'], + playbackmethod : [2,6], + maxduration: 30 + } + }, + bids: [ + { + bidder: 'saambaa', + params: { + pubid: '121ab139faf7ac67428a23f1d0a9a71b', + placement: 1234, + size: "320x480" + } + } + ] + }; ``` \ No newline at end of file diff --git a/modules/seedingAllianceBidAdapter.js b/modules/seedingAllianceBidAdapter.js index 00f3b64fb44..b7aec0f8881 100755 --- a/modules/seedingAllianceBidAdapter.js +++ b/modules/seedingAllianceBidAdapter.js @@ -152,10 +152,10 @@ export const spec = { const { seatbid, cur } = serverResponse.body; - const bidResponses = flatten(seatbid.map(seat => seat.bid)).reduce((result, bid) => { + const bidResponses = (typeof seatbid != 'undefined') ? flatten(seatbid.map(seat => seat.bid)).reduce((result, bid) => { result[bid.impid - 1] = bid; return result; - }, []); + }, []) : []; return bids .map((bid, id) => { @@ -167,7 +167,7 @@ export const spec = { cpm: bidResponse.price, creativeId: bidResponse.crid, ttl: 1000, - netRevenue: bid.netRevenue === 'net', + netRevenue: (!bid.netRevenue || bid.netRevenue === 'net'), currency: cur, mediaType: NATIVE, bidderCode: BIDDER_CODE, diff --git a/modules/seedtagBidAdapter.js b/modules/seedtagBidAdapter.js index cb646fe10c3..bae27d41028 100644 --- a/modules/seedtagBidAdapter.js +++ b/modules/seedtagBidAdapter.js @@ -13,6 +13,11 @@ const ALLOWED_PLACEMENTS = { banner: true, video: true } + +// Global Vendor List Id +// https://iabeurope.eu/vendor-list-tcf-v2-0/ +const GVLID = 157; + const mediaTypesMap = { [BANNER]: 'display', [VIDEO]: 'video' @@ -158,6 +163,7 @@ export function getTimeoutUrl (data) { export const spec = { code: BIDDER_CODE, + gvlid: GVLID, aliases: [SEEDTAG_ALIAS], supportedMediaTypes: [BANNER, VIDEO], /** diff --git a/modules/sharedIdSystem.js b/modules/sharedIdSystem.js index 0c25a1747f6..32a96100d43 100644 --- a/modules/sharedIdSystem.js +++ b/modules/sharedIdSystem.js @@ -11,7 +11,7 @@ import { coppaDataHandler } from '../src/adapterManager.js'; import {getStorageManager} from '../src/storageManager.js'; const GVLID = 887; -const storage = getStorageManager(GVLID, 'pubCommonId'); +export const storage = getStorageManager(GVLID, 'pubCommonId'); const COOKIE = 'cookie'; const LOCAL_STORAGE = 'html5'; const OPTOUT_NAME = '_pubcid_optout'; @@ -171,7 +171,33 @@ export const sharedIdSystemSubmodule = { return {id: storedId}; } } + }, + + domainOverride: function () { + const domainElements = document.domain.split('.'); + const cookieName = `_gd${Date.now()}`; + for (let i = 0, topDomain, testCookie; i < domainElements.length; i++) { + const nextDomain = domainElements.slice(i).join('.'); + + // write test cookie + storage.setCookie(cookieName, '1', undefined, undefined, nextDomain); + + // read test cookie to verify domain was valid + testCookie = storage.getCookie(cookieName); + + // delete test cookie + storage.setCookie(cookieName, '', 'Thu, 01 Jan 1970 00:00:01 GMT', undefined, nextDomain); + + if (testCookie === '1') { + // cookie was written successfully using test domain so the topDomain is updated + topDomain = nextDomain; + } else { + // cookie failed to write using test domain so exit by returning the topDomain + return topDomain; + } + } } + }; submodule('userId', sharedIdSystemSubmodule); diff --git a/modules/sharethroughBidAdapter.js b/modules/sharethroughBidAdapter.js index 36d1a94e93d..06cc81324cf 100644 --- a/modules/sharethroughBidAdapter.js +++ b/modules/sharethroughBidAdapter.js @@ -1,10 +1,10 @@ -import { generateUUID, deepAccess, inIframe } from '../src/utils.js'; +import { deepAccess, generateUUID, inIframe } from '../src/utils.js'; import { registerBidder } from '../src/adapters/bidderFactory.js'; import { config } from '../src/config.js'; import { BANNER, VIDEO } from '../src/mediaTypes.js'; import { createEidsArray } from './userId/eids.js'; -const VERSION = '4.0.1'; +const VERSION = '4.1.0'; const BIDDER_CODE = 'sharethrough'; const SUPPLY_ID = 'WYu2BXv1'; @@ -23,6 +23,7 @@ export const sharethroughAdapterSpec = { buildRequests: (bidRequests, bidderRequest) => { const timeout = config.getConfig('bidderTimeout'); + const firstPartyData = config.getConfig('ortb2') || {}; const nonHttp = sharethroughInternal.getProtocol().indexOf('http') < 0; const secure = nonHttp || (sharethroughInternal.getProtocol().indexOf('https') > -1); @@ -35,12 +36,8 @@ export const sharethroughAdapterSpec = { site: { domain: window.location.hostname, page: window.location.href, - ref: bidderRequest.refererInfo ? bidderRequest.refererInfo.referer || null : null, - }, - user: { - ext: { - eids: userIdAsEids(bidRequests[0]), - }, + ref: deepAccess(bidderRequest, 'refererInfo.referer'), + ...firstPartyData.site, }, device: { ua: navigator.userAgent, @@ -66,6 +63,10 @@ export const sharethroughAdapterSpec = { test: 0, }; + req.user = nullish(firstPartyData.user, {}); + if (!req.user.ext) req.user.ext = {}; + req.user.ext.eids = userIdAsEids(bidRequests[0]); + if (bidderRequest.gdprConsent) { const gdprApplies = bidderRequest.gdprConsent.gdprApplies === true; req.regs.ext.gdpr = gdprApplies ? 1 : 0; @@ -86,15 +87,9 @@ export const sharethroughAdapterSpec = { impression.ext = { gpid: gpid }; } - // if request is for video, we only support instream - if (bidReq.mediaTypes && bidReq.mediaTypes.video && bidReq.mediaTypes.video.context === 'outstream') { - // return null so we can easily remove this imp from the array of imps that we send to adserver - return null; - } - - if (bidReq.mediaTypes && bidReq.mediaTypes.video) { - const videoRequest = bidReq.mediaTypes.video; + const videoRequest = deepAccess(bidReq, 'mediaTypes.video'); + if (videoRequest) { // default playerSize, only change this if we know width and height are properly defined in the request let [w, h] = [640, 360]; if (videoRequest.playerSize && videoRequest.playerSize[0] && videoRequest.playerSize[1]) { @@ -117,9 +112,9 @@ export const sharethroughAdapterSpec = { startdelay: nullish(videoRequest.startdelay, 0), skipmin: nullish(videoRequest.skipmin, 0), skipafter: nullish(videoRequest.skipafter, 0), + placement: videoRequest.context === 'instream' ? 1 : +deepAccess(videoRequest, 'placement', 4), }; - if (videoRequest.placement) impression.video.placement = videoRequest.placement; if (videoRequest.delivery) impression.video.delivery = videoRequest.delivery; if (videoRequest.companiontype) impression.video.companiontype = videoRequest.companiontype; if (videoRequest.companionad) impression.video.companionad = videoRequest.companionad; diff --git a/modules/showheroes-bsBidAdapter.js b/modules/showheroes-bsBidAdapter.js index 99378b494df..4c8fb812edc 100644 --- a/modules/showheroes-bsBidAdapter.js +++ b/modules/showheroes-bsBidAdapter.js @@ -61,15 +61,24 @@ export const spec = { } } + const consentData = bidderRequest.gdprConsent || {}; + + const gdprConsent = { + apiVersion: consentData.apiVersion || 2, + gdprApplies: consentData.gdprApplies || 0, + consentString: consentData.consentString || '', + } + return { type: streamType, + adUnitCode: bid.adUnitCode, bidId: bid.bidId, mediaType: type, context: context, playerId: getBidIdParameter('playerId', bid.params), auctionId: bidderRequest.auctionId, bidderCode: BIDDER_CODE, - gdprConsent: bidderRequest.gdprConsent, + gdprConsent: gdprConsent, start: +new Date(), timeout: 3000, size: { @@ -159,6 +168,7 @@ function createBids(bidRes, reqData) { let bidUnit = {}; bidUnit.cpm = bid.cpm; bidUnit.requestId = bid.bidId; + bidUnit.adUnitCode = reqBid.adUnitCode; bidUnit.currency = bid.currency; bidUnit.mediaType = bid.mediaType || VIDEO; bidUnit.ttl = TTL; @@ -183,7 +193,8 @@ function createBids(bidRes, reqData) { } else if (bid.context === 'outstream') { const renderer = Renderer.install({ id: bid.bidId, - url: '//', + url: 'https://static.showheroes.com/renderer.js', + adUnitCode: reqBid.adUnitCode, config: { playerId: reqBid.playerId, width: bid.size.width, diff --git a/modules/smaatoBidAdapter.js b/modules/smaatoBidAdapter.js index dd389b42098..68334aed0ab 100644 --- a/modules/smaatoBidAdapter.js +++ b/modules/smaatoBidAdapter.js @@ -5,7 +5,7 @@ import {ADPOD, BANNER, VIDEO} from '../src/mediaTypes.js'; const BIDDER_CODE = 'smaato'; const SMAATO_ENDPOINT = 'https://prebid.ad.smaato.net/oapi/prebid'; -const SMAATO_CLIENT = 'prebid_js_$prebid.version$_1.4' +const SMAATO_CLIENT = 'prebid_js_$prebid.version$_1.6' const CURRENCY = 'USD'; const buildOpenRtbBidRequest = (bidRequest, bidderRequest) => { @@ -37,6 +37,11 @@ const buildOpenRtbBidRequest = (bidRequest, bidderRequest) => { user: { ext: {} }, + source: { + ext: { + schain: bidRequest.schain + } + }, ext: { client: SMAATO_CLIENT } @@ -299,6 +304,7 @@ function createBannerImp(bidRequest) { id: bidRequest.bidId, tagid: deepAccess(bidRequest, 'params.adspaceId'), bidfloor: getBidFloor(bidRequest, BANNER, adUnitSizes), + instl: deepAccess(bidRequest.ortb2Imp, 'instl'), banner: { w: sizes[0].w, h: sizes[0].h, @@ -314,6 +320,7 @@ function createVideoImp(bidRequest, videoMediaType) { id: bidRequest.bidId, tagid: deepAccess(bidRequest, 'params.adspaceId'), bidfloor: getBidFloor(bidRequest, VIDEO, videoMediaType.playerSize), + instl: deepAccess(bidRequest.ortb2Imp, 'instl'), video: { mimes: videoMediaType.mimes, minduration: videoMediaType.minduration, @@ -341,6 +348,7 @@ function createAdPodImp(bidRequest, videoMediaType) { id: bidRequest.bidId, tagid: tagid, bidfloor: getBidFloor(bidRequest, VIDEO, videoMediaType.playerSize), + instl: deepAccess(bidRequest.ortb2Imp, 'instl'), video: { w: videoMediaType.playerSize[0][0], h: videoMediaType.playerSize[0][1], diff --git a/modules/smartxBidAdapter.js b/modules/smartxBidAdapter.js index da63331cd0f..00c962445d9 100644 --- a/modules/smartxBidAdapter.js +++ b/modules/smartxBidAdapter.js @@ -161,11 +161,20 @@ export const spec = { domain: domain, publisher: { id: publisherId + }, + content: { + ext: { + prebid: { + name: 'pbjs', + version: '$prebid.version$' + } + } } }, device: device, at: at, - cur: cur + cur: cur, + ext: {} }; const userExt = {}; @@ -194,6 +203,8 @@ export const spec = { }; } + // requestPayload.user.ext.ver = pbjs.version; + // Targeting if (getBidIdParameter('data', bid.params.user)) { var targetingarr = []; @@ -336,6 +347,7 @@ function createOutstreamConfig(bid) { let confTitle = getBidIdParameter('title', bid.renderer.config.outstream_options); let confSkipOffset = getBidIdParameter('skipOffset', bid.renderer.config.outstream_options); let confDesiredBitrate = getBidIdParameter('desiredBitrate', bid.renderer.config.outstream_options); + let confVisibilityThreshold = getBidIdParameter('visibilityThreshold', bid.renderer.config.outstream_options); let elementId = getBidIdParameter('slot', bid.renderer.config.outstream_options) || bid.adUnitCode; logMessage('[SMARTX][renderer] Handle SmartX outstream renderer'); @@ -384,6 +396,10 @@ function createOutstreamConfig(bid) { smartPlayObj.desiredBitrate = confDesiredBitrate; } + if (confVisibilityThreshold) { + smartPlayObj.visibilityThreshold = confVisibilityThreshold; + } + smartPlayObj.adResponse = bid.vastContent; const divID = '[id="' + elementId + '"]'; diff --git a/modules/sonobiBidAdapter.js b/modules/sonobiBidAdapter.js index 01966f3d6b1..c5fc07320d8 100644 --- a/modules/sonobiBidAdapter.js +++ b/modules/sonobiBidAdapter.js @@ -83,8 +83,15 @@ export const spec = { 'lib_name': 'prebid', 'lib_v': '$prebid.version$', 'us': 0, + }; + const fpd = config.getConfig('ortb2'); + + if (fpd) { + payload.fpd = JSON.stringify(fpd); + } + if (config.getConfig('userSync') && config.getConfig('userSync').syncsPerBidder) { payload.us = config.getConfig('userSync').syncsPerBidder; } diff --git a/modules/sortableBidAdapter.js b/modules/sortableBidAdapter.js index c1cb607e5ab..15246a10eab 100644 --- a/modules/sortableBidAdapter.js +++ b/modules/sortableBidAdapter.js @@ -227,6 +227,7 @@ export const spec = { rv.ext[partner] = params; }); } + rv.ext.gpid = deepAccess(bid, 'ortb2Imp.ext.data.pbadslot'); return rv; }); const gdprConsent = bidderRequest && bidderRequest.gdprConsent; diff --git a/modules/sovrnBidAdapter.js b/modules/sovrnBidAdapter.js index 38788036ce0..afdc354c230 100644 --- a/modules/sovrnBidAdapter.js +++ b/modules/sovrnBidAdapter.js @@ -1,12 +1,38 @@ -import { _each, getBidIdParameter, isArray, deepClone, parseUrl, getUniqueIdentifierStr, deepSetValue, logError, deepAccess } from '../src/utils.js'; +import { _each, getBidIdParameter, isArray, deepClone, parseUrl, getUniqueIdentifierStr, deepSetValue, logError, deepAccess, isInteger, logWarn } from '../src/utils.js'; import { registerBidder } from '../src/adapters/bidderFactory.js' -import { BANNER } from '../src/mediaTypes.js' +import { ADPOD, BANNER, VIDEO } from '../src/mediaTypes.js' import { createEidsArray } from './userId/eids.js'; import {config} from '../src/config.js'; +const ORTB_VIDEO_PARAMS = { + 'mimes': (value) => Array.isArray(value) && value.length > 0 && value.every(v => typeof v === 'string'), + 'minduration': (value) => isInteger(value), + 'maxduration': (value) => isInteger(value), + 'protocols': (value) => Array.isArray(value) && value.every(v => v >= 1 && v <= 10), + 'w': (value) => isInteger(value), + 'h': (value) => isInteger(value), + 'startdelay': (value) => isInteger(value), + 'placement': (value) => Array.isArray(value) && value.every(v => v >= 1 && v <= 5), + 'linearity': (value) => [1, 2].indexOf(value) !== -1, + 'skip': (value) => [0, 1].indexOf(value) !== -1, + 'skipmin': (value) => isInteger(value), + 'skipafter': (value) => isInteger(value), + 'sequence': (value) => isInteger(value), + 'battr': (value) => Array.isArray(value) && value.every(v => v >= 1 && v <= 17), + 'maxextended': (value) => isInteger(value), + 'minbitrate': (value) => isInteger(value), + 'maxbitrate': (value) => isInteger(value), + 'boxingallowed': (value) => [0, 1].indexOf(value) !== -1, + 'playbackmethod': (value) => Array.isArray(value) && value.every(v => v >= 1 && v <= 6), + 'playbackend': (value) => [1, 2, 3].indexOf(value) !== -1, + 'delivery': (value) => [1, 2, 3].indexOf(value) !== -1, + 'pos': (value) => Array.isArray(value) && value.every(v => v >= 0 && v <= 7), + 'api': (value) => Array.isArray(value) && value.every(v => v >= 1 && v <= 6) +} + export const spec = { code: 'sovrn', - supportedMediaTypes: [BANNER], + supportedMediaTypes: [BANNER, VIDEO], gvlid: 13, /** @@ -15,7 +41,12 @@ export const spec = { * @return boolean for whether or not a bid is valid */ isBidRequestValid: function(bid) { - return !!(bid.params.tagid && !isNaN(parseFloat(bid.params.tagid)) && isFinite(bid.params.tagid)) + return !!( + bid.params.tagid && + !isNaN(parseFloat(bid.params.tagid)) && + isFinite(bid.params.tagid) && + deepAccess(bid, 'mediaTypes.video.context') !== ADPOD + ) }, /** @@ -50,13 +81,9 @@ export const spec = { } iv = iv || getBidIdParameter('iv', bid.params) - let bidSizes = (bid.mediaTypes && bid.mediaTypes.banner && bid.mediaTypes.banner.sizes) || bid.sizes - bidSizes = ((isArray(bidSizes) && isArray(bidSizes[0])) ? bidSizes : [bidSizes]) - bidSizes = bidSizes.filter(size => isArray(size)) - const processedSizes = bidSizes.map(size => ({w: parseInt(size[0], 10), h: parseInt(size[1], 10)})) const floorInfo = (bid.getFloor && typeof bid.getFloor === 'function') ? bid.getFloor({ currency: 'USD', - mediaType: 'banner', + mediaType: bid.mediaTypes && bid.mediaTypes.banner ? 'banner' : 'video', size: '*' }) : {} floorInfo.floor = floorInfo.floor || getBidIdParameter('bidfloor', bid.params) @@ -64,13 +91,24 @@ export const spec = { const imp = { adunitcode: bid.adUnitCode, id: bid.bidId, - banner: { + tagid: String(getBidIdParameter('tagid', bid.params)), + bidfloor: floorInfo.floor + } + + if (deepAccess(bid, 'mediaTypes.banner')) { + let bidSizes = deepAccess(bid, 'mediaTypes.banner.sizes') || bid.sizes + bidSizes = (isArray(bidSizes) && isArray(bidSizes[0])) ? bidSizes : [bidSizes] + bidSizes = bidSizes.filter(size => isArray(size)) + const processedSizes = bidSizes.map(size => ({w: parseInt(size[0], 10), h: parseInt(size[1], 10)})) + + imp.banner = { format: processedSizes, w: 1, h: 1, - }, - tagid: String(getBidIdParameter('tagid', bid.params)), - bidfloor: floorInfo.floor + }; + } + if (deepAccess(bid, 'mediaTypes.video')) { + imp.video = _buildVideoRequestObj(bid); } imp.ext = getBidIdParameter('ext', bid.ortb2Imp) || undefined @@ -149,7 +187,7 @@ export const spec = { seatbid[0].bid && seatbid[0].bid.length > 0) { seatbid[0].bid.map(sovrnBid => { - sovrnBidResponses.push({ + const bid = { requestId: sovrnBid.impid, cpm: parseFloat(sovrnBid.price), width: parseInt(sovrnBid.w), @@ -158,11 +196,18 @@ export const spec = { dealId: sovrnBid.dealid || null, currency: 'USD', netRevenue: true, - mediaType: BANNER, - ad: decodeURIComponent(`${sovrnBid.adm}`), ttl: sovrnBid.ext ? (sovrnBid.ext.ttl || 90) : 90, meta: { advertiserDomains: sovrnBid && sovrnBid.adomain ? sovrnBid.adomain : [] } - }); + } + + if (!sovrnBid.nurl) { + bid.mediaType = VIDEO + bid.vastXml = decodeURIComponent(sovrnBid.adm) + } else { + bid.mediaType = BANNER + bid.ad = decodeURIComponent(`${sovrnBid.adm}`) + } + sovrnBidResponses.push(bid); }); } return sovrnBidResponses @@ -209,4 +254,34 @@ export const spec = { }, } -registerBidder(spec); +function _buildVideoRequestObj(bid) { + const videoObj = {} + const videoAdUnitParams = deepAccess(bid, 'mediaTypes.video', {}) + const videoBidderParams = deepAccess(bid, 'params.video', {}) + const computedParams = {} + + if (Array.isArray(videoAdUnitParams.playerSize)) { + const sizes = (Array.isArray(videoAdUnitParams.playerSize[0])) ? videoAdUnitParams.playerSize[0] : videoAdUnitParams.playerSize + computedParams.w = sizes[0] + computedParams.h = sizes[1] + } + + const videoParams = { + ...computedParams, + ...videoAdUnitParams, + ...videoBidderParams + }; + + Object.keys(ORTB_VIDEO_PARAMS).forEach(paramName => { + if (videoParams.hasOwnProperty(paramName)) { + if (ORTB_VIDEO_PARAMS[paramName](videoParams[paramName])) { + videoObj[paramName] = videoParams[paramName] + } else { + logWarn(`The OpenRTB video param ${paramName} has been skipped due to misformating. Please refer to OpenRTB 2.5 spec.`); + } + } + }) + return videoObj +} + +registerBidder(spec) diff --git a/modules/sovrnBidAdapter.md b/modules/sovrnBidAdapter.md index 2b5d21d5515..53e3158024d 100644 --- a/modules/sovrnBidAdapter.md +++ b/modules/sovrnBidAdapter.md @@ -10,9 +10,9 @@ Maintainer: jrosendahl@sovrn.com Sovrn's adapter integration to the Prebid library. Posts plain-text JSON to the /rtb/bid endpoint. -# Test Parameters +# Banner Test Parameters -``` +```js var adUnits = [ { code: 'test-leaderboard', @@ -45,3 +45,81 @@ var adUnits = [ } ] ``` + +# Video Test Parameters +### Instream +```js +var videoAdUnit = { + code: 'video1', + sizes: [640, 480], + mediaTypes: { + video: { + context: 'instream', + playerSize: [640, 480], + mimes: ['video/mp4'], + protocols: [1, 2, 3, 4, 5, 6, 7, 8], + playbackmethod: [2], + skip: 1, + }, + }, + bids: [ + { + bidder: 'sovrn', + // Prebid Server Bidder Params https://docs.prebid.org/dev-docs/pbs-bidders.html#sovrn + params: { + tagid: '315045', + bidfloor: '0.04', + }, + }, + ], +} +``` +### Outstream +```js +var adUnits = [ + { + code: videoId, + mediaTypes: { + video: { + context: 'outstream', + playerSize: [640, 360], + mimes: ['video/mp4'], + protocols: [1, 2, 3, 4, 5, 6, 7, 8], + playbackmethod: [2], + skip: 1, + }, + }, + bids: [ + { + bidder: 'sovrn', + // Prebid Server Bidder Params https://docs.prebid.org/dev-docs/pbs-bidders.html#sovrn + params: { + tagid: '315045', + bidfloor: '0.04', + }, + }, + ], + renderer: { + url: 'https://acdn.adnxs.com/video/outstream/ANOutstreamVideo.js', + render: function (bid) { + adResponse = { + ad: { + video: { + content: bid.vastXml, + player_height: bid.height, + player_width: bid.width, + }, + }, + } + // push to render queue because ANOutstreamVideo may not be loaded yet. + bid.renderer.push(() => { + ANOutstreamVideo.renderAd({ + targetId: bid.adUnitCode, + adResponse: adResponse, + }) + }) + }, + }, + }, +] +``` diff --git a/modules/sspBCBidAdapter.js b/modules/sspBCBidAdapter.js index b4caf1db1d0..c8f3c138ce4 100644 --- a/modules/sspBCBidAdapter.js +++ b/modules/sspBCBidAdapter.js @@ -9,11 +9,13 @@ const BIDDER_URL = 'https://ssp.wp.pl/bidder/'; const SYNC_URL = 'https://ssp.wp.pl/bidder/usersync'; const NOTIFY_URL = 'https://ssp.wp.pl/bidder/notify'; const TMAX = 450; -const BIDDER_VERSION = '5.2'; +const BIDDER_VERSION = '5.3'; const W = window; const { navigator } = W; const oneCodeDetection = {}; +const adUnitsCalled = {}; const adSizesCalled = {}; +const pageView = {}; var consentApiVersion; /** @@ -70,6 +72,7 @@ const cookieSupport = () => { }; const applyClientHints = ortbRequest => { + const { location } = document; const { connection = {}, deviceMemory, userAgentData = {} } = navigator; const viewport = W.visualViewport || false; const segments = []; @@ -85,6 +88,16 @@ const applyClientHints = ortbRequest => { 'CH-isMobile': userAgentData.mobile, }; + /** + Check / generate page view id + Should be generated dureing first call to applyClientHints(), + and re-generated if pathname has changed + */ + if (!pageView.id || location.pathname !== pageView.path) { + pageView.path = location.pathname; + pageView.id = Math.floor(1E20 * Math.random()); + } + Object.keys(hints).forEach(key => { const hint = hints[key]; @@ -100,6 +113,14 @@ const applyClientHints = ortbRequest => { id: '12', name: 'NetInfo', segment: segments, + }, { + id: '7', + name: 'pvid', + segment: [ + { + value: `${pageView.id}` + } + ] }]; ortbRequest.user = Object.assign(ortbRequest.user, { data }); @@ -129,6 +150,7 @@ const setOnAny = (collection, key) => collection.reduce((prev, next) => prev || */ const sendNotification = payload => { ajax(NOTIFY_URL, null, JSON.stringify(payload), { + contentType: 'application/json', withCredentials: false, method: 'POST', crossOrigin: true @@ -267,8 +289,14 @@ const mapImpression = slot => { send this info as ext.pbsize */ const slotSize = slot.sizes.length ? slot.sizes.reduce((prev, next) => prev[0] * prev[1] <= next[0] * next[1] ? next : prev).join('x') : '1x1'; - adSizesCalled[slotSize] = adSizesCalled[slotSize] ? adSizesCalled[slotSize] += 1 : 1; - ext.data = Object.assign({ pbsize: `${slotSize}_${adSizesCalled[slotSize]}` }, ext.data); + + if (!adUnitsCalled[adUnitCode]) { + // this is a new adunit - assign & save pbsize + adSizesCalled[slotSize] = adSizesCalled[slotSize] ? adSizesCalled[slotSize] += 1 : 1; + adUnitsCalled[adUnitCode] = `${slotSize}_${adSizesCalled[slotSize]}` + } + + ext.data = Object.assign({ pbsize: adUnitsCalled[adUnitCode] }, ext.data); const imp = { id: id && siteId ? id : 'bidid-' + bidId, @@ -325,6 +353,9 @@ const parseNative = nativeData => { case 0: result.title = asset.title.text; break; + case 1: + result.cta = asset.data.value; + break; case 2: result.icon = { url: asset.img.url, diff --git a/modules/synacormediaBidAdapter.js b/modules/synacormediaBidAdapter.js index 7694f5d838e..96616bb6a48 100644 --- a/modules/synacormediaBidAdapter.js +++ b/modules/synacormediaBidAdapter.js @@ -1,6 +1,6 @@ 'use strict'; -import { getAdUnitSizes, logWarn, deepSetValue } from '../src/utils.js'; +import { getAdUnitSizes, logWarn, deepSetValue, isFn, isPlainObject } from '../src/utils.js'; import { registerBidder } from '../src/adapters/bidderFactory.js'; import { BANNER, VIDEO } from '../src/mediaTypes.js'; import includes from 'core-js-pure/features/array/includes.js'; @@ -67,11 +67,7 @@ export const spec = { } else { seatId = bid.params.seatId; } - const tagIdOrplacementId = bid.params.tagId || bid.params.placementId; - const bidFloor = bid.params.bidfloor ? parseFloat(bid.params.bidfloor) : null; - if (isNaN(bidFloor)) { - logWarn(`Synacormedia: there is an invalid bid floor: ${bid.params.bidfloor}`); - } + const tagIdOrPlacementId = bid.params.tagId || bid.params.placementId; let pos = parseInt(bid.params.pos, 10); if (isNaN(pos)) { logWarn(`Synacormedia: there is an invalid POS: ${bid.params.pos}`); @@ -83,9 +79,9 @@ export const spec = { let imps = []; if (videoOrBannerKey === 'banner') { - imps = this.buildBannerImpressions(adSizes, bid, tagIdOrplacementId, pos, bidFloor, videoOrBannerKey); + imps = this.buildBannerImpressions(adSizes, bid, tagIdOrPlacementId, pos, videoOrBannerKey); } else if (videoOrBannerKey === 'video') { - imps = this.buildVideoImpressions(adSizes, bid, tagIdOrplacementId, pos, bidFloor, videoOrBannerKey); + imps = this.buildVideoImpressions(adSizes, bid, tagIdOrPlacementId, pos, videoOrBannerKey); } if (imps.length > 0) { imps.forEach(i => openRtbBidRequest.imp.push(i)); @@ -128,7 +124,7 @@ export const spec = { return eids; }, - buildBannerImpressions: function (adSizes, bid, tagIdOrPlacementId, pos, bidFloor, videoOrBannerKey) { + buildBannerImpressions: function (adSizes, bid, tagIdOrPlacementId, pos, videoOrBannerKey) { let format = []; let imps = []; adSizes.forEach((size, i) => { @@ -151,6 +147,10 @@ export const spec = { }, tagid: tagIdOrPlacementId, }; + const bidFloor = getBidFloor(bid, 'banner', '*'); + if (isNaN(bidFloor)) { + logWarn(`Synacormedia: there is an invalid bid floor: ${bid.params.bidfloor}`); + } if (bidFloor !== null && !isNaN(bidFloor)) { imp.bidfloor = bidFloor; } @@ -159,7 +159,7 @@ export const spec = { return imps; }, - buildVideoImpressions: function(adSizes, bid, tagIdOrPlacementId, pos, bidFloor, videoOrBannerKey) { + buildVideoImpressions: function(adSizes, bid, tagIdOrPlacementId, pos, videoOrBannerKey) { let imps = []; adSizes.forEach((size, i) => { if (!size || size.length != 2) { @@ -171,6 +171,11 @@ export const spec = { id: `${videoOrBannerKey.substring(0, 1)}${bid.bidId}-${size0}x${size1}`, tagid: tagIdOrPlacementId }; + const bidFloor = getBidFloor(bid, 'video', size); + if (isNaN(bidFloor)) { + logWarn(`Synacormedia: there is an invalid bid floor: ${bid.params.bidfloor}`); + } + if (bidFloor !== null && !isNaN(bidFloor)) { imp.bidfloor = bidFloor; } @@ -287,4 +292,20 @@ export const spec = { } }; +function getBidFloor(bid, mediaType, size) { + if (!isFn(bid.getFloor)) { + return bid.params.bidfloor ? parseFloat(bid.params.bidfloor) : null; + } + let floor = bid.getFloor({ + currency: 'USD', + mediaType, + size + }); + + if (isPlainObject(floor) && !isNaN(floor.floor) && floor.currency === 'USD') { + return floor.floor; + } + return null; +} + registerBidder(spec); diff --git a/modules/talkadsBidAdapter.js b/modules/talkadsBidAdapter.js new file mode 100644 index 00000000000..f95456b5c54 --- /dev/null +++ b/modules/talkadsBidAdapter.js @@ -0,0 +1,129 @@ +import { registerBidder } from '../src/adapters/bidderFactory.js'; +import { NATIVE, BANNER } from '../src/mediaTypes.js'; +import * as utils from '../src/utils.js'; +import {ajax} from '../src/ajax.js'; + +const CURRENCY = 'EUR'; +const BIDDER_CODE = 'talkads'; + +export const spec = { + code: BIDDER_CODE, + supportedMediaTypes: [ NATIVE, BANNER ], + params: null, + + /** + * Determines whether or not the given bid request is valid. + * + * @param poBid The bid params to validate. + * @return boolean True if this is a valid bid, and false otherwise. + */ + isBidRequestValid: (poBid) => { + utils.logInfo('isBidRequestValid : ', poBid); + if (poBid.params === undefined) { + utils.logError('VALIDATION FAILED : the parameters must be defined'); + return false; + } + if (poBid.params.tag_id === undefined) { + utils.logError('VALIDATION FAILED : the parameter "tag_id" must be defined'); + return false; + } + if (poBid.params.bidder_url === undefined) { + utils.logError('VALIDATION FAILED : the parameter "bidder_url" must be defined'); + return false; + } + this.params = poBid.params; + return !!(poBid.nativeParams || poBid.sizes); + }, // isBidRequestValid + + /** + * Make a server request from the list of BidRequests. + * + * @param paValidBidRequests An array of bids + * @param poBidderRequest + * @return ServerRequest Info describing the request to the server. + */ + buildRequests: (paValidBidRequests, poBidderRequest) => { + utils.logInfo('buildRequests : ', paValidBidRequests, poBidderRequest); + const laBids = paValidBidRequests.map((poBid, piId) => { + const loOne = { id: piId, ad_unit: poBid.adUnitCode, bid_id: poBid.bidId, type: '', size: [] }; + if (poBid.nativeParams) { + loOne.type = 'native'; + } else { + loOne.type = 'banner'; + loOne.size = poBid.sizes; + } + return loOne; + }); + const loServerRequest = { + cur: CURRENCY, + timeout: poBidderRequest.timeout, + auction_id: paValidBidRequests[0].auctionId, + transaction_id: paValidBidRequests[0].transactionId, + bids: laBids, + gdpr: { applies: false, consent: false }, + }; + if (poBidderRequest && poBidderRequest.gdprConsent) { + const loGdprConsent = poBidderRequest.gdprConsent; + if ((typeof loGdprConsent.gdprApplies === 'boolean') && loGdprConsent.gdprApplies) { + loServerRequest.gdpr.applies = true; + } + if ((typeof loGdprConsent.consentString === 'string') && loGdprConsent.consentString) { + loServerRequest.gdpr.consent = poBidderRequest.gdprConsent.consentString; + } + } + const lsUrl = this.params.bidder_url + '/' + this.params.tag_id; + return { + method: 'POST', + url: lsUrl, + data: JSON.stringify(loServerRequest), + }; + }, // buildRequests + + /** + * Unpack the response from the server into a list of bids. + * + * @param poServerResponse A successful response from the server. + * @param poPidRequest Request original server request + * @return An array of bids which were nested inside the server. + */ + interpretResponse: (poServerResponse, poPidRequest) => { + utils.logInfo('interpretResponse : ', poServerResponse); + if (!poServerResponse.body) { + return []; + } + let laResponse = []; + if (poServerResponse.body.status !== 'ok') { + utils.logInfo('Error : ', poServerResponse.body.error); + return laResponse; + } + poServerResponse.body.bids.forEach((poResponse) => { + laResponse[laResponse.length] = { + requestId: poResponse.requestId, + cpm: poResponse.cpm, + currency: poResponse.currency, + width: poResponse.width, + height: poResponse.height, + ad: poResponse.ad, + ttl: poResponse.ttl, + creativeId: poResponse.creativeId, + netRevenue: poResponse.netRevenue, + pbid: poServerResponse.body.pbid, + }; + }); + return laResponse; + }, // interpretResponse + + /** + * Register bidder specific code, which will execute if a bid from this bidder won the auction. + * + * @param poBid The bid that won the auction + */ + onBidWon: (poBid) => { + utils.logInfo('onBidWon : ', poBid); + if (poBid.pbid) { + ajax(this.params.bidder_url + 'won/' + poBid.pbid); + } + }, // onBidWon +}; + +registerBidder(spec); diff --git a/modules/talkadsBidAdapter.md b/modules/talkadsBidAdapter.md new file mode 100644 index 00000000000..3ab8aaa8354 --- /dev/null +++ b/modules/talkadsBidAdapter.md @@ -0,0 +1,60 @@ +# Overview + +``` +Module Name: TalkAds Adapter +Module Type: Bidder Adapter +Maintainer: technical_team@natexo.com +``` + +# Description + +Module that connects to TalkAds bidder to fetch bids. +Both native and banner formats are supported but not at the same time. +The only currently supported currency is EUR. + +This adapter requires setup and approval from the Natexo programmatic team. + +# Configuration + + + + +# Test parameters + +## Test banner Parameters + +``` + var adUnits = [ + code: 'prebid_banner_test', + mediaTypes: { + banner: { + sizes: [[300, 250], [300, 600]], + } + }, + bids: [{ + bidder: 'talkads', + params: { + tag_id: 0, + bidder_url: 'https://d.natexo-programmatic.com/tad/tag/testbid', + }, + }] + ]; +``` + +## Test native parameters + +``` + var adUnits = [ + code: 'prebid_native_test', + mediaTypes: { + native: {} + }, + bids: [{ + bidder: 'talkads', + params: { + tag_id: 0, + bidder_url: 'https://d.natexo-programmatic.com/tad/tag/testbid', + }, + }] + ]; +``` diff --git a/modules/tappxBidAdapter.js b/modules/tappxBidAdapter.js index a026b2cd6a6..769e8f73565 100644 --- a/modules/tappxBidAdapter.js +++ b/modules/tappxBidAdapter.js @@ -7,9 +7,10 @@ import { config } from '../src/config.js'; import { Renderer } from '../src/Renderer.js'; const BIDDER_CODE = 'tappx'; +const GVLID_CODE = 628; const TTL = 360; const CUR = 'USD'; -const TAPPX_BIDDER_VERSION = '0.1.1004'; +const TAPPX_BIDDER_VERSION = '0.1.1005'; const TYPE_CNN = 'prebidjs'; const LOG_PREFIX = '[TAPPX]: '; const VIDEO_SUPPORT = ['instream', 'outstream']; @@ -42,6 +43,7 @@ var hostDomain; export const spec = { code: BIDDER_CODE, + gvlid: GVLID_CODE, supportedMediaTypes: [BANNER, VIDEO], /** @@ -51,7 +53,14 @@ export const spec = { * @return boolean True if this is a valid bid, and false otherwise. */ isBidRequestValid: function(bid) { - return validBasic(bid) && validMediaType(bid) + // bid.params.host + if ((new RegExp(`^(vz.*|zz.*)\\.*$`, 'i')).test(bid.params.host)) { // New endpoint + if ((new RegExp(`^(zz.*)\\.*$`, 'i')).test(bid.params.host)) return validBasic(bid) + else return validBasic(bid) && validMediaType(bid) + } else { // This is backward compatible feature. It will be remove in the future + if ((new RegExp(`^(ZZ.*)\\.*$`, 'i')).test(bid.params.endpoint)) return validBasic(bid) + else return validBasic(bid) && validMediaType(bid) + } }, /** @@ -167,10 +176,6 @@ function validMediaType(bid) { logWarn(LOG_PREFIX, 'Please review the mandatory Tappx parameters for Video. Video context not supported.'); return false; } - if (typeof video.mimes == 'undefined') { - logWarn(LOG_PREFIX, 'Please review the mandatory Tappx parameters for Video. Mimes param is mandatory.'); - return false; - } } return true; diff --git a/modules/targetVideoBidAdapter.js b/modules/targetVideoBidAdapter.js new file mode 100644 index 00000000000..5714916c131 --- /dev/null +++ b/modules/targetVideoBidAdapter.js @@ -0,0 +1,187 @@ +import find from 'core-js-pure/features/array/find.js'; +import { getBidRequest } from '../src/utils.js'; +import { BANNER, VIDEO } from '../src/mediaTypes.js'; +import { registerBidder } from '../src/adapters/bidderFactory.js'; + +const SOURCE = 'pbjs'; +const BIDDER_CODE = 'targetVideo'; +const ENDPOINT_URL = 'https://ib.adnxs.com/ut/v3/prebid'; +const MARGIN = 1.35; + +export const spec = { + + code: BIDDER_CODE, + supportedMediaTypes: [BANNER], + + /** + * Determines whether or not the given bid request is valid. + * + * @param {object} bid The bid to validate. + * @return boolean True if this is a valid bid, and false otherwise. + */ + isBidRequestValid: function(bid) { + return !!(bid && bid.params && bid.params.placementId); + }, + + /** + * Make a server request from the list of BidRequests. + * + * @param {BidRequest[]} bidRequests A non-empty list of bid requests which should be sent to the Server. + * @return ServerRequest Info describing the request to the server. + */ + buildRequests: function(bidRequests, bidderRequest) { + const tags = bidRequests.map(createVideoTag); + const schain = bidRequests[0].schain; + const payload = { + tags: tags, + sdk: { + source: SOURCE, + version: '$prebid.version$' + }, + schain: schain + }; + return formatRequest(payload, bidderRequest); + }, + + /** + * Unpack the response from the server into a list of bids. + * + * @param {*} serverResponse A successful response from the server. + * @return {Bid[]} An array of bids which were nested inside the server. + */ + interpretResponse: function(serverResponse, { bidderRequest }) { + serverResponse = serverResponse.body; + const bids = []; + + if (serverResponse.tags) { + serverResponse.tags.forEach(serverBid => { + const rtbBid = getRtbBid(serverBid); + if (rtbBid && rtbBid.cpm !== 0 && rtbBid.ad_type == VIDEO) { + bids.push(newBid(serverBid, rtbBid, bidderRequest)); + } + }); + } + + return bids; + } + +} + +function getSizes(request) { + let sizes = request.sizes; + if (!sizes && request.mediaTypes && request.mediaTypes.banner && request.mediaTypes.banner.sizes) { + sizes = request.mediaTypes.banner.sizes; + } + if (Array.isArray(sizes) && !Array.isArray(sizes[0])) { + sizes = [sizes[0], sizes[1]]; + } + if (!Array.isArray(sizes) || !Array.isArray(sizes[0])) { + sizes = [[0, 0]]; + } + + return sizes; +} + +function formatRequest(payload, bidderRequest) { + const options = { + withCredentials: true + }; + const request = { + method: 'POST', + url: ENDPOINT_URL, + data: JSON.stringify(payload), + bidderRequest, + options + }; + + return request; +} + +/** + * Create video auction. + * + * @param {*} serverResponse A successful response from the server. + * @return {Bid[]} An array of bids which were nested inside the server. + */ +function createVideoTag(bid) { + const tag = {}; + tag.id = parseInt(bid.params.placementId, 10); + tag.gpid = 'targetVideo'; + tag.sizes = getSizes(bid); + tag.primary_size = tag.sizes[0]; + tag.ad_types = [VIDEO]; + tag.uuid = bid.bidId; + tag.allow_smaller_sizes = false; + tag.use_pmt_rule = false + tag.prebid = true; + tag.disable_psa = true; + tag.hb_source = 1; + tag.require_asset_url = true; + tag.video = { + playback_method: 2, + skippable: true + }; + + return tag; +} + +/** + * Unpack the Server's Bid into a Prebid-compatible one. + * @param serverBid + * @param rtbBid + * @param bidderRequest + * @return Bid + */ +function newBid(serverBid, rtbBid, bidderRequest) { + const bidRequest = getBidRequest(serverBid.uuid, [bidderRequest]); + const sizes = getSizes(bidRequest); + const bid = { + requestId: serverBid.uuid, + cpm: rtbBid.cpm * MARGIN, + creativeId: rtbBid.creative_id, + dealId: rtbBid.deal_id, + currency: 'USD', + netRevenue: true, + width: sizes[0][0], + height: sizes[0][1], + ttl: 300, + adUnitCode: bidRequest.adUnitCode, + appnexus: { + buyerMemberId: rtbBid.buyer_member_id, + dealPriority: rtbBid.deal_priority, + dealCode: rtbBid.deal_code + } + }; + + if (rtbBid.rtb.video) { + Object.assign(bid, { + vastImpUrl: rtbBid.notify_url, + ad: getBannerHtml(rtbBid.notify_url + '&redir=' + encodeURIComponent(rtbBid.rtb.video.asset_url)), + ttl: 3600 + }); + } + + return bid; +} + +function getRtbBid(tag) { + return tag && tag.ads && tag.ads.length && find(tag.ads, ad => ad.rtb); +} + +function getBannerHtml(vastUrl) { + return ` + + + + + + + +
+ + + + `; +} + +registerBidder(spec); diff --git a/modules/targetVideoBidAdapter.md b/modules/targetVideoBidAdapter.md new file mode 100644 index 00000000000..557c9f94410 --- /dev/null +++ b/modules/targetVideoBidAdapter.md @@ -0,0 +1,34 @@ +# Overview + +``` +Module Name: Target Video Bid Adapter +Module Type: Bidder Adapter +Maintainer: grajzer@gmail.com +``` + +# Description + +Connects to Appnexus exchange for bids. + +TargetVideo bid adapter supports Banner. + +# Test Parameters +``` +var adUnits = [ + // Banner adUnit + { + code: 'banner-div', + mediaTypes: { + banner: { + sizes: [[640, 480], [300, 250]], + } + }, + bids: [{ + bidder: 'targetVideo', + params: { + placementId: 13232361 + } + }] + } +]; +``` diff --git a/modules/teadsBidAdapter.js b/modules/teadsBidAdapter.js index 3edca828bf0..4bea8858d98 100644 --- a/modules/teadsBidAdapter.js +++ b/modules/teadsBidAdapter.js @@ -190,6 +190,7 @@ function buildRequestObject(bid) { const reqObj = {}; let placementId = getValue(bid.params, 'placementId'); let pageId = getValue(bid.params, 'pageId'); + const gpid = deepAccess(bid, 'ortb2Imp.ext.gpid'); reqObj.sizes = getSizes(bid); reqObj.bidId = getBidIdParameter('bidId', bid); @@ -199,6 +200,7 @@ function buildRequestObject(bid) { reqObj.adUnitCode = getBidIdParameter('adUnitCode', bid); reqObj.auctionId = getBidIdParameter('auctionId', bid); reqObj.transactionId = getBidIdParameter('transactionId', bid); + if (gpid) { reqObj.gpid = gpid; } return reqObj; } diff --git a/modules/telariaBidAdapter.js b/modules/telariaBidAdapter.js index 560bf762394..c55ecdcfbc6 100644 --- a/modules/telariaBidAdapter.js +++ b/modules/telariaBidAdapter.js @@ -2,7 +2,7 @@ import { logError, isEmpty, deepAccess, triggerPixel, logWarn, isArray } from '. import {createBid as createBidFactory} from '../src/bidfactory.js'; import {registerBidder} from '../src/adapters/bidderFactory.js'; import {VIDEO} from '../src/mediaTypes.js'; -import {STATUS} from '../src/constants.json'; +import CONSTANTS from '../src/constants.json'; const BIDDER_CODE = 'telaria'; const DOMAIN = 'tremorhub.com'; @@ -86,7 +86,7 @@ export const spec = { logError(errorMessage); } else if (!isEmpty(bidResult.seatbid)) { bidResult.seatbid[0].bid.forEach(tag => { - bids.push(createBid(STATUS.GOOD, bidderRequest, tag, width, height, BIDDER_CODE)); + bids.push(createBid(CONSTANTS.STATUS.GOOD, bidderRequest, tag, width, height, BIDDER_CODE)); }); } diff --git a/modules/tripleliftBidAdapter.js b/modules/tripleliftBidAdapter.js index 01df88f62ee..215769e9812 100644 --- a/modules/tripleliftBidAdapter.js +++ b/modules/tripleliftBidAdapter.js @@ -1,4 +1,4 @@ -import { tryAppendQueryString, logMessage, isEmpty } from '../src/utils.js'; +import { tryAppendQueryString, logMessage, isEmpty, isStr, isPlainObject, isArray, logWarn } from '../src/utils.js'; import { BANNER, VIDEO } from '../src/mediaTypes.js'; import { registerBidder } from '../src/adapters/bidderFactory.js'; import { config } from '../src/config.js'; @@ -262,19 +262,36 @@ function getPubCommonEids(bidRequest) { function getEids(bidRequest, type, source, rtiPartner) { return bidRequest .map(getUserId(type)) // bids -> userIds of a certain type - .filter((x) => !!x) // filter out null userIds + .filter(filterEids(type)) // filter out unqualified userIds .map(formatEid(source, rtiPartner)); // userIds -> eid objects } +const filterEids = type => (userId, i, arr) => { + let isValidUserId = + !!userId && // is not null nor empty + (isStr(userId) + ? !!userId + : isPlainObject(userId) && // or, is object + !isArray(userId) && // not an array + !isEmpty(userId) && // is not empty + userId.id && // contains nested id field + isStr(userId.id) && // nested id field is a string + !!userId.id); // that is not empty + if (!isValidUserId && arr[0] !== undefined) { + logWarn(`Triplelift: invalid ${type} userId format`); + } + return isValidUserId; +}; + function getUserId(type) { - return (bid) => (bid && bid.userId && bid.userId[type]); + return bid => bid && bid.userId && bid.userId[type]; } function formatEid(source, rtiPartner) { - return (id) => ({ + return (userId) => ({ source, uids: [{ - id, + id: userId.id ? userId.id : userId, ext: { rtiPartner } }] }); diff --git a/modules/trustxBidAdapter.js b/modules/trustxBidAdapter.js index e74dbd6b4d8..31be2814bba 100644 --- a/modules/trustxBidAdapter.js +++ b/modules/trustxBidAdapter.js @@ -6,9 +6,8 @@ import { config } from '../src/config.js'; const BIDDER_CODE = 'trustx'; const ENDPOINT_URL = 'https://grid.bidswitch.net/hbjson?sp=trustx'; -const ADDITIONAL_SYNC_URL = 'https://x.bidswitch.net/sync?ssp=themediagrid'; const TIME_TO_LIVE = 360; -const ADAPTER_SYNC_URL = 'https://sofia.trustx.org/push_sync'; +const ADAPTER_SYNC_URL = 'https://x.bidswitch.net/sync?ssp=themediagrid'; const RENDERER_URL = 'https://acdn.adnxs.com/video/outstream/ANOutstreamVideo.js'; const LOG_ERROR_MESS = { @@ -76,7 +75,7 @@ export const spec = { if (!userIdAsEids) { userIdAsEids = bid.userIdAsEids; } - const {params: {uid, keywords}, mediaTypes, bidId, adUnitCode, rtd} = bid; + const {params: {uid, keywords}, mediaTypes, bidId, adUnitCode, rtd, ortb2Imp} = bid; bidsMap[bidId] = bid; const bidFloor = _getFloor(mediaTypes || {}, bid); if (rtd) { @@ -102,6 +101,15 @@ export const spec = { } }; + if (ortb2Imp && ortb2Imp.ext && ortb2Imp.ext.data) { + impObj.ext.data = ortb2Imp.ext.data; + if (impObj.ext.data.adserver && impObj.ext.data.adserver.adslot) { + impObj.ext.gpid = impObj.ext.data.adserver.adslot.toString(); + } else { + impObj.ext.gpid = ortb2Imp.ext.data.pbadslot && ortb2Imp.ext.data.pbadslot.toString(); + } + } + if (!isEmpty(keywords)) { if (!pageKeywords) { pageKeywords = keywords; @@ -272,12 +280,12 @@ export const spec = { }, getUserSyncs: function(syncOptions, responses, gdprConsent, uspConsent) { if (syncOptions.pixelEnabled) { - const syncsPerBidder = config.getConfig('userSync.syncsPerBidder'); let params = []; - if (gdprConsent && typeof gdprConsent.consentString === 'string') { + if (gdprConsent) { if (typeof gdprConsent.gdprApplies === 'boolean') { - params.push(`gdpr=${Number(gdprConsent.gdprApplies)}&gdpr_consent=${gdprConsent.consentString}`); - } else { + params.push(`gdpr=${Number(gdprConsent.gdprApplies)}`); + } + if (typeof gdprConsent.consentString === 'string') { params.push(`gdpr_consent=${gdprConsent.consentString}`); } } @@ -285,17 +293,10 @@ export const spec = { params.push(`us_privacy=${uspConsent}`); } const stringParams = params.join('&'); - const syncs = [{ + return { type: 'image', - url: ADAPTER_SYNC_URL + (stringParams ? `?${stringParams}` : '') - }]; - if (syncsPerBidder > 1) { - syncs.push({ - type: 'image', - url: ADDITIONAL_SYNC_URL + (stringParams ? `&${stringParams}` : '') - }); - } - return syncs; + url: ADAPTER_SYNC_URL + stringParams + }; } } } @@ -429,9 +430,10 @@ function addSegments(name, segName, segments, data, bidConfigName) { if (segments && segments.length) { data.push({ name: name, - segment: segments.map((seg) => { - return {name: segName, value: seg}; - }) + segment: segments + .map((seg) => seg && (seg.id || seg)) + .filter((seg) => seg && (typeof seg === 'string' || typeof seg === 'number')) + .map((seg) => ({ name: segName, value: seg.toString() })) }); } else if (bidConfigName) { const configData = config.getConfig('ortb2.user.data'); @@ -445,9 +447,10 @@ function addSegments(name, segName, segments, data, bidConfigName) { if (segData && segData.length) { data.push({ name: name, - segment: segData.map((seg) => { - return {name: segName, value: seg}; - }) + segment: segData + .map((seg) => seg && (seg.id || seg)) + .filter((seg) => seg && (typeof seg === 'string' || typeof seg === 'number')) + .map((seg) => ({ name: segName, value: seg.toString() })) }); } } diff --git a/modules/turktelekomBidAdapter.md b/modules/turktelekomBidAdapter.md deleted file mode 100644 index 360e7f95230..00000000000 --- a/modules/turktelekomBidAdapter.md +++ /dev/null @@ -1,49 +0,0 @@ -# Overview - -Module Name: Türk Telekom Bidder Adapter -Module Type: Bidder Adapter -Maintainer: turktelssp@gmail.com - -# Description - -Module that connects to Türk Telekom demand source to fetch bids. -Türk Telekom Bid Adapter supports Banner and Video (instream and outstream). - -# Test Parameters -``` - var adUnits = [ - { - code: 'test-div', - mediaTypes: { - banner: { - sizes: [[300, 250], [300,600]] - } - }, - bids: [ - { - bidder: "turktelekom", - params: { - uid: 17, - priceType: 'gross' // by default is 'net' - } - } - ] - },{ - code: 'test-div', - mediaTypes: { - video: { - playerSize: [[640, 360]], - context: 'instream' - } - }, - bids: [ - { - bidder: "turktelekom", - params: { - uid: 19 - } - } - ] - } - ]; -``` \ No newline at end of file diff --git a/modules/undertoneBidAdapter.js b/modules/undertoneBidAdapter.js index 14a765206b6..f86faf3fe4d 100644 --- a/modules/undertoneBidAdapter.js +++ b/modules/undertoneBidAdapter.js @@ -12,6 +12,20 @@ const FRAME_USER_SYNC = 'https://cdn.undertone.com/js/usersync.html'; const PIXEL_USER_SYNC_1 = 'https://usr.undertone.com/userPixel/syncOne?id=1&of=2'; const PIXEL_USER_SYNC_2 = 'https://usr.undertone.com/userPixel/syncOne?id=2&of=2'; +function getBidFloor(bidRequest, mediaType) { + if (typeof bidRequest.getFloor !== 'function') { + return 0; + } + + const floor = bidRequest.getFloor({ + currency: 'USD', + mediaType: mediaType, + size: '*' + }); + + return (floor && floor.currency === 'USD' && floor.floor) || 0; +} + function getCanonicalUrl() { try { let doc = window.top.document; @@ -85,18 +99,29 @@ export const spec = { const vw = Math.max(document.documentElement.clientWidth, window.innerWidth || 0); const vh = Math.max(document.documentElement.clientHeight, window.innerHeight || 0); const pageSizeArray = vw == 0 || vh == 0 ? null : [vw, vh]; + const commons = { + 'adapterVersion': '$prebid.version$', + 'uids': validBidRequests[0].userId, + 'pageSize': pageSizeArray + }; + if (validBidRequests[0].schain) { + commons.schain = validBidRequests[0].schain; + } const payload = { 'x-ut-hb-params': [], - 'commons': { - 'adapterVersion': '$prebid.version$', - 'uids': validBidRequests[0].userId, - 'pageSize': pageSizeArray - } + 'commons': commons }; const referer = bidderRequest.refererInfo.referer; + const canonicalUrl = getCanonicalUrl(); + if (referer) { + commons.referrer = referer; + } + if (canonicalUrl) { + commons.canonicalUrl = canonicalUrl; + } const hostname = parseUrl(referer).hostname; let domain = extractDomainFromHost(hostname); - const pageUrl = getCanonicalUrl() || referer; + const pageUrl = canonicalUrl || referer; const pubid = validBidRequests[0].params.publisherId; let reqUrl = `${URL}?pid=${pubid}&domain=${domain}`; @@ -123,6 +148,9 @@ export const spec = { params: bidReq.params }; const videoMediaType = deepAccess(bidReq, 'mediaTypes.video'); + const mediaType = videoMediaType ? VIDEO : BANNER; + bid.mediaType = mediaType; + bid.bidfloor = getBidFloor(bidReq, mediaType); if (videoMediaType) { bid.video = { playerSize: deepAccess(bidReq, 'mediaTypes.video.playerSize') || null, @@ -131,7 +159,6 @@ export const spec = { maxDuration: deepAccess(bidReq, 'params.video.maxDuration') || null, skippable: deepAccess(bidReq, 'params.video.skippable') || null }; - bid.mediaType = 'video'; } payload['x-ut-hb-params'].push(bid); }); diff --git a/modules/undertoneBidAdapter.md b/modules/undertoneBidAdapter.md index 8ac84b77bd8..8e0b234fd7a 100644 --- a/modules/undertoneBidAdapter.md +++ b/modules/undertoneBidAdapter.md @@ -1,9 +1,13 @@ # Overview ``` -Module Name: Example Bidder Adapter +Module Name: Undertone Bidder Adapter Module Type: Bidder Adapter Maintainer: RampProgrammatic@perion.com +gdpr_supported: true +usp_supported: true +schain_supported: true +media_types: video, native ``` # Description diff --git a/modules/userId/eids.js b/modules/userId/eids.js index 0f159c4f33b..a290371b36d 100644 --- a/modules/userId/eids.js +++ b/modules/userId/eids.js @@ -181,15 +181,18 @@ const USER_IDS_CONFIG = { source: 'neustar.biz', atype: 1 }, + // MediaWallah OpenLink 'mwOpenLinkId': { source: 'mediawallahscript.com', atype: 1 }, + 'tapadId': { source: 'tapad.com', atype: 1 }, + // Novatiq Snowflake 'novatiq': { getValue: function(data) { @@ -198,6 +201,7 @@ const USER_IDS_CONFIG = { source: 'novatiq.com', atype: 1 }, + 'uid2': { source: 'uidapi.com', atype: 3, @@ -205,41 +209,61 @@ const USER_IDS_CONFIG = { return data.id; } }, + // Akamai Data Activation Platform (DAP) 'dapId': { source: 'akamai.com', atype: 1 }, + 'deepintentId': { source: 'deepintent.com', atype: 3 }, + // Admixer Id 'admixerId': { source: 'admixer.net', atype: 3 }, + // Adtelligent Id 'adtelligentId': { source: 'adtelligent.com', atype: 3 }, + amxId: { source: 'amxrtb.com', atype: 1, }, + 'publinkId': { source: 'epsilon.com', atype: 3 }, + 'kpuid': { source: 'kpuid.com', atype: 3 }, + 'imuid': { source: 'intimatemerger.com', atype: 1 - } + }, + + // Yahoo ConnectID + 'connectId': { + source: 'yahoo.com', + atype: 3 + }, + + // Adquery ID + 'qid': { + source: 'adquery.io', + atype: 1 + }, }; // this function will create an eid object for the given UserId sub-module diff --git a/modules/userId/eids.md b/modules/userId/eids.md index a8e73216327..679bf5ffe27 100644 --- a/modules/userId/eids.md +++ b/modules/userId/eids.md @@ -210,6 +210,13 @@ userIdAsEids = [ id: 'some-random-id-value', atype: 3 }] + }, + { + source: 'yahoo.com', + uids: [{ + id: 'some-random-id-value', + atype: 3 + }] } ] ``` diff --git a/modules/userId/index.js b/modules/userId/index.js index b0293a9c26a..42fff2cd16c 100644 --- a/modules/userId/index.js +++ b/modules/userId/index.js @@ -141,7 +141,7 @@ import { createEidsArray, buildEidPermissions } from './eids.js'; import { getCoreStorageManager } from '../../src/storageManager.js'; import { getPrebidInternal, isPlainObject, logError, isArray, cyrb53Hash, deepAccess, timestamp, delayExecution, logInfo, isFn, - logWarn, isEmptyStr, isNumber + logWarn, isEmptyStr, isNumber, isGptPubadsDefined } from '../../src/utils.js'; import includes from 'core-js-pure/features/array/includes.js'; @@ -184,6 +184,9 @@ export let syncDelay; /** @type {(number|undefined)} */ export let auctionDelay; +/** @type {(string|undefined)} */ +let ppidSource; + /** @param {Submodule[]} submodules */ export function setSubmoduleRegistry(submodules) { submoduleRegistry = submodules; @@ -557,6 +560,26 @@ export function requestBidsHook(fn, reqBidsConfigObj) { initializeSubmodulesAndExecuteCallbacks(function () { // pass available user id data to bid adapters addIdDataToAdUnitBids(reqBidsConfigObj.adUnits || getGlobal().adUnits, initializedSubmodules); + + // userSync.ppid should be one of the 'source' values in getUserIdsAsEids() eg pubcid.org or id5-sync.com + const matchingUserId = ppidSource && (getUserIdsAsEids() || []).find(userID => userID.source === ppidSource); + if (matchingUserId && typeof deepAccess(matchingUserId, 'uids.0.id') === 'string') { + const ppidValue = matchingUserId.uids[0].id.replace(/[\W_]/g, ''); + if (ppidValue.length >= 32 && ppidValue.length <= 150) { + if (isGptPubadsDefined()) { + window.googletag.pubads().setPublisherProvidedId(ppidValue); + } else { + window.googletag = window.googletag || {}; + window.googletag.cmd = window.googletag.cmd || []; + window.googletag.cmd.push(function() { + window.googletag.pubads().setPublisherProvidedId(ppidValue); + }); + } + } else { + logWarn(`User ID - Googletag Publisher Provided ID for ${ppidSource} is not between 32 and 150 characters - ${ppidValue}`); + } + } + // calling fn allows prebid to continue processing fn.call(this, reqBidsConfigObj); }); @@ -817,6 +840,7 @@ export function attachIdSystem(submodule) { * @param {{getConfig:function}} config */ export function init(config) { + ppidSource = undefined; submodules = []; configRegistry = []; addedUserIdHook = false; @@ -839,9 +863,10 @@ export function init(config) { } // listen for config userSyncs to be set - config.getConfig(conf => { + config.getConfig('userSync', conf => { // Note: support for 'usersync' was dropped as part of Prebid.js 4.0 const userSync = conf.userSync; + ppidSource = userSync.ppid; if (userSync && userSync.userIds) { configRegistry = userSync.userIds; syncDelay = isNumber(userSync.syncDelay) ? userSync.syncDelay : DEFAULT_SYNC_DELAY; diff --git a/modules/userId/userId.md b/modules/userId/userId.md index 88460093c63..6364de8ea79 100644 --- a/modules/userId/userId.md +++ b/modules/userId/userId.md @@ -285,6 +285,26 @@ pbjs.setConfig({ params: { cid: 5126 // Set your Intimate Merger Customer ID here for production } + }, + { + name: 'connectId', + params: { + pixelId: 58776, + he: '0bef996248d63cea1529cb86de31e9547a712d9f380146e98bbd39beec70355a' + }, + storage: { + name: 'connectId', + type: 'html5', + expires: 15 + } + } + { + name: "qid", + storage: { + type: "html5", + name: "qid", + expires: 365 + } }], syncDelay: 5000 } diff --git a/modules/ventes.md b/modules/ventes.md new file mode 100644 index 00000000000..5f0f571ecd8 --- /dev/null +++ b/modules/ventes.md @@ -0,0 +1,71 @@ +--- +layout: bidder +title: ventes +description: Prebid ventes Bidder Adapter +pbjs: false +biddercode: ventes +gdpr_supported: false +usp_supported: false +media_types: banner +coppa_supported: false +schain_supported: false +dchain_supported: false +prebid_member: false +--- + +### BidParams +{: .table .table-bordered .table-striped } +| Name | Scope | Description | Example | Type | +|-----------------|----------|-----------------------------------------------------------|----------------------------------------------|---------------| +| `placementId` | required | Placement ID from Ventes Avenues | `'VA-062-0013-0183'` | `string` | +| `publisherId` | required | Publisher ID from Ventes Avenues | `'VA-062'` | `string` | +| `user` | optional | Object that specifies information about an external user. | `user: { age: 25, gender: 0, dnt: true}` | `object` | +| `app` | optional | Object containing mobile app parameters. | `app : { id: 'app-id'}` | `object` | + +#### User Object + +{: .table .table-bordered .table-striped } +| Name | Description | Example | Type | +|-------------------|-------------------------------------------------------------------------------------------|-----------------------|-----------------------| +| `age` | The age of the user. | `35` | `integer` | +| `externalUid` | Specifies a string that corresponds to an external user ID for this user. | `'1234567890abcdefg'` | `string` | +| `segments` | Specifies the segments to which the user belongs. | `[1, 2]` | `Array` | +| `gender` | Specifies the gender of the user. Allowed values: Unknown: `0`; Male: `1`; Female: `2` | `1` | `integer` | +| `dnt` | Do not track flag. Indicates if tracking cookies should be disabled for this auction | `true` | `boolean` | +| `language` | Two-letter ANSI code for this user's language. | `EN` | `string` | + + +### Ad Unit Setup for Banner +```javascript +var adUnits = [ +{ + code: 'test-hb-ad-11111-1', + mediaTypes: { + banner: { + sizes: [ + [300, 250] + ] + } + }, + bids: [{ + bidder: 'ventes', + params: { + placementId: 'VA-062-0013-0183', + publisherId: '5cebea3c9eea646c7b623d5e', + IABCategories: "['IAB1', 'IAB5']", + device:{ + ip: '123.145.167.189', + ifa:"AEBE52E7-03EE-455A-B3C4-E57283966239", + }, + app: { + id: "agltb3B1Yi1pbmNyDAsSA0FwcBiJkfIUDA", + name: "Yahoo Weather", + bundle: 'com.kiloo.subwaysurf', + storeurl: 'https://play.google.com/store/apps/details?id=com.kiloo.subwaysurf&hl=en', + domain: 'somoaudience.com', + } + } + }] + } +] +``` diff --git a/modules/ventesBidAdapter.js b/modules/ventesBidAdapter.js new file mode 100644 index 00000000000..a9de52a86ba --- /dev/null +++ b/modules/ventesBidAdapter.js @@ -0,0 +1,410 @@ +import { + BANNER, + NATIVE, + VIDEO +} from '../src/mediaTypes.js'; +import { + convertCamelToUnderscore, + isStr, + isArray, + isNumber, + isPlainObject, + replaceAuctionPrice +} from '../src/utils.js'; +import find from 'core-js-pure/features/array/find.js'; +import { + registerBidder +} from '../src/adapters/bidderFactory.js'; + +const BID_METHOD = 'POST'; +const BIDDER_URL = 'http://13.234.201.146:8088/va/ad'; + +const DOMAIN_REGEX = new RegExp('//([^/]*)'); + +function groupBy(values, key) { + const groups = values.reduce((acc, value) => { + const groupId = value[key]; + + if (!acc[groupId]) acc[groupId] = []; + acc[groupId].push(value); + + return acc; + }, {}); + + return Object + .keys(groups) + .map(id => ({ + id, + key, + values: groups[id] + })); +} + +function validateMediaTypes(mediaTypes, allowedMediaTypes) { + if (!isPlainObject(mediaTypes)) return false; + if (!allowedMediaTypes.some(mediaType => mediaType in mediaTypes)) return false; + + if (isBanner(mediaTypes)) { + if (!validateBanner(mediaTypes.banner)) return false; + } + return true; +} + +function isBanner(mediaTypes) { + return isPlainObject(mediaTypes) && isPlainObject(mediaTypes.banner); +} + +function validateBanner(banner) { + return isPlainObject(banner) && + isArray(banner.sizes) && + (banner.sizes.length > 0) && + banner.sizes.every(validateMediaSizes); +} + +function validateMediaSizes(mediaSize) { + return isArray(mediaSize) && + (mediaSize.length === 2) && + mediaSize.every(size => (isNumber(size) && size >= 0)); +} + +function hasUserInfo(bid) { + return !!bid.params.user; +} + +function validateParameters(parameters) { + if (!(parameters.placementId)) { + return false; + } + if (!(parameters.publisherId)) { + return false; + } + + return true; +} + +function extractSiteDomainFromURL(url) { + if (!url || !isStr(url)) return null; + + const domain = url.match(DOMAIN_REGEX); + + if (isArray(domain) && domain.length === 2) return domain[1]; + + return null; +} + +function generateSiteFromAdUnitContext(bidRequests, adUnitContext) { + if (!adUnitContext || !adUnitContext.refererInfo) return null; + + const domain = extractSiteDomainFromURL(adUnitContext.refererInfo.referer); + const publisherId = bidRequests[0].params.publisherId; + + if (!domain) return null; + + return { + page: adUnitContext.refererInfo.referer, + domain: domain, + name: domain, + publisher: { + id: publisherId + } + }; +} + +function validateServerRequest(serverRequest) { + return isPlainObject(serverRequest) && + isPlainObject(serverRequest.data) && + isArray(serverRequest.data.imp) +} + +function createServerRequestFromAdUnits(adUnits, bidRequestId, adUnitContext) { + return { + method: BID_METHOD, + url: BIDDER_URL, + data: generateBidRequestsFromAdUnits(adUnits, bidRequestId, adUnitContext), + options: { + contentType: 'application/json', + withCredentials: false, + } + } +} + +function generateBidRequestsFromAdUnits(bidRequests, bidRequestId, adUnitContext) { + const userObjBid = find(bidRequests, hasUserInfo); + let userObj = {}; + if (userObjBid) { + Object.keys(userObjBid.params.user) + .forEach((param) => { + let uparam = convertCamelToUnderscore(param); + if (param === 'segments' && isArray(userObjBid.params.user[param])) { + let segs = []; + userObjBid.params.user[param].forEach(val => { + if (isNumber(val)) { + segs.push({ + 'id': val + }); + } else if (isPlainObject(val)) { + segs.push(val); + } + }); + userObj[uparam] = segs; + } else if (param !== 'segments') { + userObj[uparam] = userObjBid.params.user[param]; + } + }); + } + + const deviceObjBid = find(bidRequests, hasDeviceInfo); + let deviceObj; + if (deviceObjBid && deviceObjBid.params && deviceObjBid.params.device) { + deviceObj = {}; + Object.keys(deviceObjBid.params.device) + .forEach(param => deviceObj[param] = deviceObjBid.params.device[param]); + if (!deviceObjBid.hasOwnProperty('ua')) { + deviceObj.ua = navigator.userAgent; + } + if (!deviceObjBid.hasOwnProperty('language')) { + deviceObj.language = navigator.language; + } + } else { + deviceObj = {}; + deviceObj.ua = navigator.userAgent; + deviceObj.language = navigator.language; + } + + const payload = {} + payload.id = bidRequestId + payload.at = 1 + payload.cur = ['USD'] + payload.imp = bidRequests.reduce(generateImpressionsFromAdUnit, []) + const appDeviceObjBid = find(bidRequests, hasAppInfo); + if (!appDeviceObjBid) { + payload.site = generateSiteFromAdUnitContext(bidRequests, adUnitContext) + } else { + let appIdObj; + if (appDeviceObjBid && appDeviceObjBid.params && appDeviceObjBid.params.app && appDeviceObjBid.params.app.id) { + appIdObj = {}; + Object.keys(appDeviceObjBid.params.app) + .forEach(param => appIdObj[param] = appDeviceObjBid.params.app[param]); + } + payload.app = appIdObj; + } + payload.device = deviceObj; + payload.user = userObj + return payload +} + +function generateImpressionsFromAdUnit(acc, adUnit) { + const { + bidId, + mediaTypes, + params + } = adUnit; + const { + placementId + } = params; + const pmp = {}; + + if (placementId) pmp.deals = [{ id: placementId }] + + const imps = Object + .keys(mediaTypes) + .reduce((acc, mediaType) => { + const data = mediaTypes[mediaType]; + const impId = `${bidId}`; + + if (mediaType === 'banner') return acc.concat(generateBannerFromAdUnit(impId, data, params)); + }, []); + + return acc.concat(imps); +} + +function generateBannerFromAdUnit(impId, data, params) { + const { + position, + placementId + } = params; + const pos = position || 0; + const pmp = {}; + const ext = { + placementId + }; + + if (placementId) pmp.deals = [{ id: placementId }] + + return data.sizes.map(([w, h]) => ({ + id: `${impId}`, + banner: { + format: [{ + w, + h + }], + w, + h, + pos + }, + pmp, + ext, + tagid: placementId + })); +} + +function validateServerResponse(serverResponse) { + return isPlainObject(serverResponse) && + isPlainObject(serverResponse.body) && + isStr(serverResponse.body.cur) && + isArray(serverResponse.body.seatbid); +} + +function seatBidsToAds(seatBid, bidResponse, serverRequest) { + return seatBid.bid + .filter(bid => validateBids(bid)) + .map(bid => generateAdFromBid(bid, bidResponse)); +} + +function validateBids(bid) { + if (!isPlainObject(bid)) return false; + if (!isStr(bid.impid)) return false; + if (!isStr(bid.crid)) return false; + if (!isNumber(bid.price)) return false; + if (!isNumber(bid.w)) return false; + if (!isNumber(bid.h)) return false; + if (!bid.adm) return false; + if (bid.adm) { + if (!isStr(bid.adm)) return false; + } + return true; +} + +function getMediaType(adm) { + const videoRegex = new RegExp(/VAST\s+version/); + + if (videoRegex.test(adm)) { + return VIDEO; + } + + const markup = safeJSONparse(adm.replace(/\\/g, '')); + + if (markup && isPlainObject(markup.native)) { + return NATIVE; + } + + return BANNER; +} + +function safeJSONparse(...args) { + try { + return JSON.parse(...args); + } catch (_) { + return undefined; + } +} + +function generateAdFromBid(bid, bidResponse) { + const mediaType = getMediaType(bid.adm); + const base = { + requestId: bid.impid, + cpm: bid.price, + currency: bidResponse.cur, + ttl: 10, + creativeId: bid.crid, + mediaType: mediaType, + netRevenue: true + }; + + if (bid.adomain) { + base.meta = { + advertiserDomains: bid.adomain + }; + } + + const size = getSizeFromBid(bid); + const creative = getCreativeFromBid(bid); + + return { + ...base, + height: size.height, + width: size.width, + ad: creative.markup, + adUrl: creative.markupUrl, + renderer: creative.renderer + }; +} + +function getSizeFromBid(bid) { + if (isNumber(bid.w) && isNumber(bid.h)) { + return { + width: bid.w, + height: bid.h + }; + } + return { + width: null, + height: null + }; +} + +function getCreativeFromBid(bid) { + const shouldUseAdMarkup = !!bid.adm; + const price = bid.price; + return { + markup: shouldUseAdMarkup ? replaceAuctionPrice(bid.adm, price) : null, + markupUrl: !shouldUseAdMarkup ? replaceAuctionPrice(bid.nurl, price) : null + }; +} + +function hasDeviceInfo(bid) { + if (bid.params) { + return !!bid.params.device + } +} + +function hasAppInfo(bid) { + if (bid.params) { + return !!bid.params.app + } +} + +const venavenBidderSpec = { + code: 'ventes', + supportedMediaTypes: [BANNER], + isBidRequestValid(adUnit) { + const allowedBidderCodes = [this.code]; + + return isPlainObject(adUnit) && + allowedBidderCodes.indexOf(adUnit.bidder) !== -1 && + isStr(adUnit.adUnitCode) && + isStr(adUnit.bidderRequestId) && + isStr(adUnit.bidId) && + validateMediaTypes(adUnit.mediaTypes, this.supportedMediaTypes) && + validateParameters(adUnit.params); + }, + buildRequests(bidRequests, bidderRequest) { + if (!bidRequests) return null; + + return groupBy(bidRequests, 'bidderRequestId').map(group => { + const bidRequestId = group.id; + const adUnits = groupBy(group.values, 'bidId').map((group) => { + const length = group.values.length; + return length > 0 && group.values[length - 1] + }); + + return createServerRequestFromAdUnits(adUnits, bidRequestId, bidderRequest) + }); + }, + interpretResponse(serverResponse, serverRequest) { + if (!validateServerRequest(serverRequest)) return []; + if (!validateServerResponse(serverResponse)) return []; + + const bidResponse = serverResponse.body; + + return bidResponse.seatbid + .filter(seatBid => isPlainObject(seatBid) && isArray(seatBid.bid)) + .reduce((acc, seatBid) => acc.concat(seatBidsToAds(seatBid, bidResponse, serverRequest)), []); + } +}; + +registerBidder(venavenBidderSpec); + +export { + venavenBidderSpec as spec +}; diff --git a/modules/ventesBidAdapter.md b/modules/ventesBidAdapter.md new file mode 100644 index 00000000000..c79ef080cd1 --- /dev/null +++ b/modules/ventesBidAdapter.md @@ -0,0 +1,93 @@ +--- +layout: bidder +title: ventes +description: Prebid ventes Bidder Adapter +pbjs: false +biddercode: ventes +gdpr_supported: false +usp_supported: false +media_types: banner +coppa_supported: false +schain_supported: false +dchain_supported: false +prebid_member: false +--- + +### BidParams +{: .table .table-bordered .table-striped } +| Name | Scope | Description | Example | Type | +|-----------------|----------|-----------------------------------------------------------|----------------------------------------------|---------------| +| `placementId` | required | Placement ID from Ventes Avenues | `'VA-062-0013-0183'` | `string` | +| `publisherId` | required | Publisher ID from Ventes Avenues | `'VA-062'` | `string` | +| `user` | optional | Object that specifies information about an external user. | `user: { age: 25, gender: 0, dnt: true}` | `object` | +| `app` | required | Object containing mobile app parameters. | `app : { id: 'app-id'}` | `object` | +| `device` | required | Object containing device info mandatory for mobile devices| `device : { ifa: 'device-id'}` | `object` | + +#### User Object + +{: .table .table-bordered .table-striped } +| Name | Description | Example | Type | +|-------------------|-------------------------------------------------------------------------------------------|-----------------------|-----------------------| +| `age` | The age of the user. | `35` | `integer` | +| `externalUid` | Specifies a string that corresponds to an external user ID for this user. | `'1234567890abcdefg'` | `string` | +| `segments` | Specifies the segments to which the user belongs. | `[1, 2]` | `Array` | +| `gender` | Specifies the gender of the user. Allowed values: Unknown: `0`; Male: `1`; Female: `2` | `1` | `integer` | +| `dnt` | Do not track flag. Indicates if tracking cookies should be disabled for this auction | `true` | `boolean` | +| `language` | Two-letter ANSI code for this user's language. | `EN` | `string` | + + +### Ad Unit Setup for Banner through mobile devices +```javascript +var adUnits = [ +{ + code: 'test-hb-ad-11111-1', + mediaTypes: { + banner: { + sizes: [ + [300, 250] + ] + } + }, + bids: [{ + bidder: 'ventes', + params: { + placementId: 'VA-062-0013-0183', + publisherId: '5cebea3c9eea646c7b623d5e', + IABCategories: "['IAB1', 'IAB5']", + device:{ + ifa:"AEBE52E7-03EE-455A-B3C4-E57283966239", + }, + app: { + id: "agltb3B1Yi1pbmNyDAsSA0FwcBiJkfIUDA", + name: "Yahoo Weather", + bundle: 'com.kiloo.subwaysurf', + storeurl: 'https://play.google.com/store/apps/details?id=com.kiloo.subwaysurf&hl=en', + domain: 'somoaudience.com', + } + } + }] + } +] +``` + +### Ad Unit Setup for Banner through Websites +```javascript +var adUnits = [ +{ + code: 'test-hb-ad-11111-1', + mediaTypes: { + banner: { + sizes: [ + [300, 250] + ] + } + }, + bids: [{ + bidder: 'ventes', + params: { + placementId: 'VA-002-0007-0799', + publisherId: '5cebea3c9eea646c7b623d5e', + } + }] + } +] diff --git a/modules/videobyteBidAdapter.js b/modules/videobyteBidAdapter.js index 172e0c763f4..6e99b5bc42a 100644 --- a/modules/videobyteBidAdapter.js +++ b/modules/videobyteBidAdapter.js @@ -27,7 +27,7 @@ export const spec = { code: BIDDER_CODE, supportedMediaTypes: [VIDEO], VERSION: '1.0.0', - ENDPOINT: 'https://x.videobyte.com/ortb/', + ENDPOINT: 'https://x.videobyte.com/ortbhb', /** * Determines whether or not the given bid request is valid. @@ -53,13 +53,22 @@ export const spec = { return bidRequests.map(bidRequest => { const {params} = bidRequest; let pubId = params.pubId; + const placementId = params.placementId; + const nId = params.nid; if (bidRequest.params.video && bidRequest.params.video.e2etest) { logMessage('E2E test mode enabled'); pubId = 'e2etest' } + let baseEndpoint = spec.ENDPOINT + '?pid=' + pubId; + if (placementId) { + baseEndpoint += '&placementId=' + placementId + } + if (nId) { + baseEndpoint += '&nid=' + nId + } return { method: 'POST', - url: spec.ENDPOINT + pubId, + url: baseEndpoint, data: JSON.stringify(buildRequestData(bidRequest, bidderRequest)), } }); @@ -97,8 +106,6 @@ export const spec = { }; bidResponses.push(bidResponse) } - } else { - logError('invalid server response received'); } return bidResponses; }, @@ -228,7 +235,7 @@ function buildRequestData(bidRequest, bidderRequest) { // content if (videoParams.content && isPlainObject(videoParams.content)) { openrtbRequest.site.content = {}; - const contentStringKeys = ['id', 'title', 'series', 'season', 'genre', 'contentrating', 'language']; + const contentStringKeys = ['id', 'title', 'series', 'season', 'genre', 'contentrating', 'language', 'url']; const contentNumberkeys = ['episode', 'prodq', 'context', 'livestream', 'len']; const contentArrayKeys = ['cat']; const contentObjectKeys = ['ext']; @@ -271,7 +278,7 @@ function validateVideo(bidRequest) { } if (!bidRequest.params.pubId) { - logError('failed validation: publisher id not declared'); + logError('failed validation: pubId not declared'); return false; } diff --git a/modules/videobyteBidAdapter.md b/modules/videobyteBidAdapter.md index fc2b0bce4b5..d0037b0972a 100644 --- a/modules/videobyteBidAdapter.md +++ b/modules/videobyteBidAdapter.md @@ -49,6 +49,55 @@ Module that connects to VideoByte's demand sources ] ``` +## Instream Video adUnit with placement, nid and content params +``` + var adUnits = [ + { + code: 'video1', + mediaTypes: { + video: { + context: 'instream', + playerSize: [640, 480], + mimes: ['video/mp4', 'application/javascript'], + protocols: [2,5], + api: [2], + position: 1, + delivery: [2], + minduration: 10, + maxduration: 30, + placement: 1, + playbackmethod: [1,5], + protocols: [2,5], + api: [2], + } + }, + bids: [ + { + bidder: 'videobyte', + params: { + bidfloor: 0.5, + pubId: 'e2etest', + placementId: '1234567', + nid: '1234', + video: { + content:{ + id: "uuid", + url: "https://videobyte.com/awesome-video.mp4", + title: "Awesome video", + genre: "Comedy", + language: "en", + season: "1", + series: "1", + + } + } + } + } + ] + } + ] +``` + # End To End testing mode By passing bid.params.video.e2etest = true you will be able to receive a test creative diff --git a/modules/vidoomyBidAdapter.js b/modules/vidoomyBidAdapter.js index e69386f264a..64145b2c6b4 100644 --- a/modules/vidoomyBidAdapter.js +++ b/modules/vidoomyBidAdapter.js @@ -1,4 +1,4 @@ -import { logError, deepAccess } from '../src/utils.js'; +import { logError, deepAccess, parseSizesInput } from '../src/utils.js'; import {registerBidder} from '../src/adapters/bidderFactory.js'; import {BANNER, VIDEO} from '../src/mediaTypes.js'; import {config} from '../src/config.js'; @@ -9,7 +9,17 @@ const ENDPOINT = `https://d.vidoomy.com/api/rtbserver/prebid/`; const BIDDER_CODE = 'vidoomy'; const GVLID = 380; -const COOKIE_SYNC_JSON = 'https://vpaid.vidoomy.com/sync/urls.json'; +const COOKIE_SYNC_FALLBACK_URLS = [ + 'https://x.bidswitch.net/sync?ssp=vidoomy', + 'https://ib.adnxs.com/getuid?https%3A%2F%2Fa-prebid.vidoomy.com%2Fsetuid%3Fbidder%3Dadnxs%26gdpr%3D{{GDPR}}%26gdpr_consent%3D{{GDPR_CONSENT}}%26uid%3D%24UID', + 'https://pixel-sync.sitescout.com/dmp/pixelSync?nid=120&redir=https%3A%2F%2Fa.vidoomy.com%2Fapi%2Frtbserver%2Fcookie%3Fi%3DCEN%26uid%3D%7BuserId%7D', + 'https://sync.1rx.io/usersync2/vidoomy?redir=https%3A%2F%2Fa.vidoomy.com%2Fapi%2Frtbserver%2Fcookie%3Fi%3DUN%26uid%3D%5BRX_UUID%5D', + 'https://rtb.openx.net/sync/prebid?gdpr={{GDPR}}&gdpr_consent={{GDPR_CONSENT}}&r=https%3A%2F%2Fa-prebid.vidoomy.com%2Fsetuid%3Fbidder%3Dopenx%26uid%3D$%7BUID%7D', + 'https://ads.pubmatic.com/AdServer/js/user_sync.html?gdpr={{GDPR}}&gdpr_consent={{GDPR_CONSENT}}&us_privacy=&predirect=https%3A%2F%2Fa-prebid.vidoomy.com%2Fsetuid%3Fbidder%3Dpubmatic%26gdpr%3D{{GDPR}}%26gdpr_consent%3D{{GDPR_CONSENT}}%26uid%3D', + 'https://cm.adform.net/cookie?redirect_url=https%3A%2F%2Fa-prebid.vidoomy.com%2Fsetuid%3Fbidder%3Dadf%26gdpr%3D{{GDPR}}%26gdpr_consent%3D{{GDPR_CONSENT}}%26uid%3D%24UID', + 'https://ups.analytics.yahoo.com/ups/58531/occ?gdpr={{GDPR}}&gdpr_consent={{GDPR_CONSENT}}', + 'https://ap.lijit.com/pixel?redir=https%3A%2F%2Fa-prebid.vidoomy.com%2Fsetuid%3Fbidder%3Dsovrn%26gdpr%3D{{GDPR}}%26gdpr_consent%3D{{GDPR_CONSENT}}%26uid%3D%24UID' +]; const isBidRequestValid = bid => { if (!bid.params) { @@ -36,14 +46,14 @@ const isBidRequestValid = bid => { }; const isBidResponseValid = bid => { - if (!bid.requestId || !bid.cpm || !bid.ttl || !bid.currency) { + if (!bid || !bid.requestId || !bid.cpm || !bid.ttl || !bid.currency) { return false; } switch (bid.mediaType) { case BANNER: return Boolean(bid.width && bid.height && bid.ad); case VIDEO: - return Boolean(bid.vastUrl); + return Boolean(bid.vastUrl || bid.vastXml); default: return false; } @@ -52,14 +62,15 @@ const isBidResponseValid = bid => { const buildRequests = (validBidRequests, bidderRequest) => { const serverRequests = validBidRequests.map(bid => { let adType = BANNER; - let w, h; + let sizes; if (bid.mediaTypes && bid.mediaTypes[BANNER] && bid.mediaTypes[BANNER].sizes) { - [w, h] = bid.mediaTypes[BANNER].sizes[0]; + sizes = bid.mediaTypes[BANNER].sizes; adType = BANNER; } else if (bid.mediaTypes && bid.mediaTypes[VIDEO] && bid.mediaTypes[VIDEO].playerSize) { - [w, h] = bid.mediaTypes[VIDEO].playerSize; + sizes = bid.mediaTypes[VIDEO].playerSize; adType = VIDEO; } + const [w, h] = (parseSizesInput(sizes)[0] || '0x0').split('x'); const aElement = document.createElement('a'); aElement.href = (bidderRequest.refererInfo && bidderRequest.refererInfo.referer) || top.location.href; @@ -67,35 +78,34 @@ const buildRequests = (validBidRequests, bidderRequest) => { const videoContext = deepAccess(bid, 'mediaTypes.video.context'); - const queryParams = []; - queryParams.push(['id', bid.params.id]); - queryParams.push(['adtype', adType]); - queryParams.push(['w', w]); - queryParams.push(['h', h]); - queryParams.push(['pos', parseInt(bid.params.position) || 1]); - queryParams.push(['ua', navigator.userAgent]); - queryParams.push(['l', navigator.language && navigator.language.indexOf('-') !== -1 ? navigator.language.split('-')[0] : '']); - queryParams.push(['dt', /Mobi/.test(navigator.userAgent) ? 2 : 1]); - queryParams.push(['pid', bid.params.pid]); - queryParams.push(['requestId', bid.bidId]); - queryParams.push(['d', getDomainWithoutSubdomain(hostname)]); - queryParams.push(['sp', encodeURIComponent(aElement.href)]); + const queryParams = { + id: bid.params.id, + adtype: adType, + auc: bid.adUnitCode, + w, + h, + pos: parseInt(bid.params.position) || 1, + ua: navigator.userAgent, + l: navigator.language && navigator.language.indexOf('-') !== -1 ? navigator.language.split('-')[0] : '', + dt: /Mobi/.test(navigator.userAgent) ? 2 : 1, + pid: bid.params.pid, + requestId: bid.bidId, + d: getDomainWithoutSubdomain(hostname), // 'vidoomy.com', + sp: encodeURIComponent(aElement.href), + usp: bidderRequest.uspConsent || '', + coppa: !!config.getConfig('coppa'), + videoContext: videoContext || '' + }; + if (bidderRequest.gdprConsent) { - queryParams.push(['gdpr', bidderRequest.gdprConsent.gdprApplies]); - queryParams.push(['gdprcs', bidderRequest.gdprConsent.consentString]); + queryParams.gdpr = bidderRequest.gdprConsent.gdprApplies; + queryParams.gdprcs = bidderRequest.gdprConsent.consentString; } - queryParams.push(['usp', bidderRequest.uspConsent || '']); - queryParams.push(['coppa', !!config.getConfig('coppa')]); - - const rawQueryParams = queryParams.map(qp => qp.join('=')).join('&'); - cookieSync(bidderRequest) - - const url = `${ENDPOINT}?${rawQueryParams}`; return { method: 'GET', - url: url, - data: {videoContext} + url: ENDPOINT, + data: queryParams } }); return serverRequests; @@ -117,8 +127,9 @@ const render = (bid) => { const interpretResponse = (serverResponse, bidRequest) => { try { let responseBody = serverResponse.body; + if (!responseBody) return; if (responseBody.mediaType === 'video') { - responseBody.ad = responseBody.vastUrl; + responseBody.ad = responseBody.vastUrl || responseBody.vastXml; const videoContext = bidRequest.data.videoContext; if (videoContext === OUTSTREAM) { @@ -134,13 +145,12 @@ const interpretResponse = (serverResponse, bidRequest) => { responseBody.renderer = renderer; } catch (e) { - responseBody.ad = responseBody.vastUrl; + responseBody.ad = responseBody.vastUrl || responseBody.vastXml; logError(BIDDER_CODE + ': error while installing renderer to show outstream ad'); } } } const bid = { - vastUrl: responseBody.vastUrl, ad: responseBody.ad, renderer: responseBody.renderer, mediaType: responseBody.mediaType, @@ -169,6 +179,11 @@ const interpretResponse = (serverResponse, bidRequest) => { secondaryCatIds: responseBody.meta.secondaryCatIds } }; + if (responseBody.vastUrl) { + bid.vastUrl = responseBody.vastUrl; + } else if (responseBody.vastXml) { + bid.vastXml = responseBody.vastXml; + } const bids = []; @@ -185,6 +200,21 @@ const interpretResponse = (serverResponse, bidRequest) => { } }; +function getUserSyncs (syncOptions, responses, gdprConsent, uspConsent) { + if (syncOptions.iframeEnabled || syncOptions.pixelEnabled) { + const pixelType = syncOptions.pixelEnabled ? 'image' : 'iframe'; + const urls = deepAccess(responses, '0.body.pixels') || COOKIE_SYNC_FALLBACK_URLS; + + return [].concat(urls).map(url => ({ + type: pixelType, + url: url + .replace('{{GDPR}}', gdprConsent ? (gdprConsent.gdprApplies ? '1' : '0') : '0') + .replace('{{GDPR_CONSENT}}', gdprConsent ? encodeURIComponent(gdprConsent.consentString) : '') + .replace('{{USP_CONSENT}}', uspConsent ? encodeURIComponent(uspConsent) : '') + })); + } +}; + export const spec = { code: BIDDER_CODE, supportedMediaTypes: [BANNER, VIDEO], @@ -192,84 +222,11 @@ export const spec = { buildRequests, interpretResponse, gvlid: GVLID, + getUserSyncs, }; registerBidder(spec); -let cookieSynced = false; -function cookieSync(bidderRequest) { - if (cookieSynced) return; - const xhr = new XMLHttpRequest(); - xhr.open('GET', COOKIE_SYNC_JSON) - xhr.addEventListener('load', function () { - const macro = Macro({ - gpdr: bidderRequest.gdprConsent ? bidderRequest.gdprConsent.gdprApplies : '0', - gpdr_consent: bidderRequest.gdprConsent ? bidderRequest.gdprConsent.consentString : '', - }); - JSON.parse(this.responseText).filter(Boolean).forEach(url => { - firePixel(macro.replace(url)) - }) - }) - xhr.send() - cookieSynced = true; -} - -function firePixel(url) { - const img = document.createElement('img'); - img.width = 1; - img.height = 1; - img.src = url; - document.body.appendChild(img); - setTimeout(() => { - img.remove(); - }, 10000) -} - -function normalizeKey (x) { - return x.replace(/_/g, '').toLowerCase(); -} - -function Macro (obj) { - const macros = {}; - for (const key in obj) { - macros[normalizeKey(key)] = obj[key]; - } - - const set = (key, value) => { - macros[normalizeKey(key)] = typeof value === 'function' ? value : String(value); - }; - - return { - set, - setAll (obj) { - for (const key in obj) { - macros[normalizeKey(key)] = set(obj[key]); - } - }, - replace (string, extraMacros = {}) { - const allMacros = { - ...macros, - ...extraMacros, - }; - const regexes = [ - /{{\s*([a-zA-Z0-9_]+)\s*}}/g, - /\$\$\s*([a-zA-Z0-9_]+)\s*\$\$/g, - /\[\s*([a-zA-Z0-9_]+)\s*\]/g, - /\{\s*([a-zA-Z0-9_]+)\s*\}/g, - ]; - regexes.forEach(regex => { - string = string.replace(regex, (str, x) => { - x = normalizeKey(x); - let value = allMacros[x]; - value = typeof value === 'function' ? value(allMacros) : value; - return !value && value !== 0 ? '' : value; - }); - }); - return string; - }, - }; -} - function getDomainWithoutSubdomain (hostname) { const parts = hostname.split('.'); const newParts = []; diff --git a/modules/viewability.js b/modules/viewability.js new file mode 100644 index 00000000000..b12c53b7f59 --- /dev/null +++ b/modules/viewability.js @@ -0,0 +1,177 @@ +import { logWarn, logInfo, isStr, isFn, triggerPixel, insertHtmlIntoIframe } from '../src/utils.js'; +import { getGlobal } from '../src/prebidGlobal.js'; +import find from 'core-js-pure/features/array/find.js'; + +export const MODULE_NAME = 'viewability'; + +export function init() { + (getGlobal()).viewability = { + startMeasurement: startMeasurement, + stopMeasurement: stopMeasurement, + }; + + listenMessagesFromCreative(); +} + +const observers = {}; + +function isValid(vid, element, tracker, criteria) { + if (!element) { + logWarn(`${MODULE_NAME}: no html element provided`); + return false; + } + + let validTracker = tracker && + ((tracker.method === 'img' && isStr(tracker.value)) || + (tracker.method === 'js' && isStr(tracker.value)) || + (tracker.method === 'callback' && isFn(tracker.value))); + + if (!validTracker) { + logWarn(`${MODULE_NAME}: invalid tracker`, tracker); + return false; + } + + if (!criteria || !criteria.inViewThreshold || !criteria.timeInView) { + logWarn(`${MODULE_NAME}: missing criteria`, criteria); + return false; + } + + if (!vid || observers[vid]) { + logWarn(`${MODULE_NAME}: must provide an unregistered vid`, vid); + return false; + } + + return true; +} + +function stopObserving(observer, vid, element) { + observer.unobserve(element); + observers[vid].done = true; +} + +function fireViewabilityTracker(element, tracker) { + switch (tracker.method) { + case 'img': + triggerPixel(tracker.value, () => { + logInfo(`${MODULE_NAME}: viewability pixel fired`, tracker.value); + }); + break; + case 'js': + insertHtmlIntoIframe(``); + break; + case 'callback': + tracker.value(element); + break; + } +} + +function viewabilityCriteriaMet(observer, vid, element, tracker) { + stopObserving(observer, vid, element); + fireViewabilityTracker(element, tracker); +} + +/** + * Start measuring viewability of an element + * @typedef {{ method: string='img','js','callback', value: string|function }} ViewabilityTracker { method: 'img', value: 'http://my.tracker/123' } + * @typedef {{ inViewThreshold: number, timeInView: number }} ViewabilityCriteria { inViewThreshold: 0.5, timeInView: 1000 } + * @param {string} vid unique viewability identifier + * @param {HTMLElement} element + * @param {ViewabilityTracker} tracker + * @param {ViewabilityCriteria} criteria + */ +export function startMeasurement(vid, element, tracker, criteria) { + if (!isValid(vid, element, tracker, criteria)) { + return; + } + + const options = { + root: null, + rootMargin: '0px', + threshold: criteria.inViewThreshold, + }; + + let observer; + let viewable = false; + let stateChange = (entries) => { + viewable = entries[0].isIntersecting; + + if (viewable) { + observers[vid].timeoutId = window.setTimeout(() => { + viewabilityCriteriaMet(observer, vid, element, tracker); + }, criteria.timeInView); + } else if (observers[vid].timeoutId) { + window.clearTimeout(observers[vid].timeoutId); + } + }; + + observer = new IntersectionObserver(stateChange, options); + observers[vid] = { + observer: observer, + element: element, + timeoutId: null, + done: false, + }; + + observer.observe(element); + + logInfo(`${MODULE_NAME}: startMeasurement called with:`, arguments); +} + +/** + * Stop measuring viewability of an element + * @param {string} vid unique viewability identifier + */ +export function stopMeasurement(vid) { + if (!vid || !observers[vid]) { + logWarn(`${MODULE_NAME}: must provide a registered vid`, vid); + return; + } + + observers[vid].observer.unobserve(observers[vid].element); + if (observers[vid].timeoutId) { + window.clearTimeout(observers[vid].timeoutId); + } + + // allow the observer under this vid to be created again + if (!observers[vid].done) { + delete observers[vid]; + } +} + +function listenMessagesFromCreative() { + window.addEventListener('message', receiveMessage, false); +} + +/** + * Recieve messages from creatives + * @param {MessageEvent} evt + */ +export function receiveMessage(evt) { + var key = evt.message ? 'message' : 'data'; + var data = {}; + try { + data = JSON.parse(evt[key]); + } catch (e) { + return; + } + + if (!data || data.message !== 'Prebid Viewability') { + return; + } + + switch (data.action) { + case 'startMeasurement': + let element = data.elementId && document.getElementById(data.elementId); + if (!element) { + element = find(document.getElementsByTagName('IFRAME'), iframe => (iframe.contentWindow || iframe.contentDocument.defaultView) == evt.source); + } + + startMeasurement(data.vid, element, data.tracker, data.criteria); + break; + case 'stopMeasurement': + stopMeasurement(data.vid); + break; + } +} + +init(); diff --git a/modules/viewability.md b/modules/viewability.md new file mode 100644 index 00000000000..df93b5c40db --- /dev/null +++ b/modules/viewability.md @@ -0,0 +1,87 @@ +# Overview + +Module Name: Viewability + +Purpose: Track when a given HTML element becomes viewable + +Maintainer: atrajkovic@magnite.com + +# Configuration + +Module does not need any configuration, as long as you include it in your PBJS bundle. +Viewability module has only two functions `startMeasurement` and `stopMeasurement` which can be used to enable more complex viewability measurements. Since it allows tracking from within creative (possibly inside a safe frame) this module registers a message listener, for messages with a format that is described bellow. + +## `startMeasurement` + +| startMeasurement Arg Object | Scope | Type | Description | Example | +| --------------------- | -------- | ------------ | -------------------------------------------------------------------------------- | --------- | +| vid | Required | String | Unique viewability identifier, used to reference particular observer | `"ae0f9"` | +| element | Required | HTMLElement | Reference to an HTML element that needs to be tracked | `document.getElementById('test_div')` | +| tracker | Required | ViewabilityTracker | How viewaility event is communicated back to the parties of interest | `{ method: 'img', value: 'http://my.tracker/123' }` | +| criteria | Required | ViewabilityCriteria| Defines custom viewability criteria using the threshold and duration provided | `{ inViewThreshold: 0.5, timeInView: 1000 }` | + +| ViewabilityTracker | Scope | Type | Description | Example | +| --------------------- | -------- | ------------ | -------------------------------------------------------------------------------- | --------- | +| method | Required | String | Type of method for Tracker | `'img' OR 'js' OR 'callback'` | +| value | Required | String | URL string for 'img' and 'js' Trackers, or a function for 'callback' Tracker | `'http://my.tracker/123'` | + +| ViewabilityCriteria | Scope | Type | Description | Example | +| --------------------- | -------- | ------------ | -------------------------------------------------------------------------------- | --------- | +| inViewThreshold | Required | Number | Represents a percentage threshold for the Element to be registered as in view | `0.5` | +| timeInView | Required | Number | Number of milliseconds that a given element needs to be in view continuously, above the threshold | `1000` | + +## Please Note: +- `vid` allows for multiple trackers, with different criteria to be registered for a given HTML element, independently. It's not autogenerated by `startMeasurement()`, it needs to be provided by the caller so that it doesn't have to be posted back to the source iframe (in case viewability is started from within the creative). +- In case of 'callback' method, HTML element is being passed back to the callback function. +- When a tracker needs to be started, without direct access to pbjs, postMessage mechanism can be used to invoke `startMeasurement`, with a following payload: `vid`, `tracker` and `criteria` as described above, but also with `message: 'Prebid Viewability'` and `action: 'startMeasurement'`. Optionally payload can provide `elementId`, if available at that time (for ad servers where name of the iframe is known, or adservers that render outside an iframe). If `elementId` is not provided, viewability module will try to find the iframe that corresponds to the message source. + + +## `stopMeasurement` + +| stopMeasurement Arg Object | Scope | Type | Description | Example | +| --------------------- | -------- | ------------ | -------------------------------------------------------------------------------- | --------- | +| vid | Required | String | Unique viewability identifier, referencing an already started viewability tracker. | `"ae0f9"` | + +## Please Note: +- When a tracker needs to be stopped, without direct access to pbjs, postMessage mechanism can be used here as well. To invoke `stopMeasurement`, you provide the payload with `vid`, `message: 'Prebid Viewability'` and `action: 'stopMeasurement`. Check the example bellow. + +# Examples + +## Example of starting a viewability measurement, when you have direct access to pbjs +``` +pbjs.viewability.startMeasurement( + 'ae0f9', + document.getElementById('test_div'), + { method: 'img', value: 'http://my.tracker/123' }, + { inViewThreshold: 0.5, timeInView: 1000 } +); +``` + +## Example of starting a viewability measurement from within a rendered creative +``` +let viewabilityRecord = { + vid: 'ae0f9', + tracker: { method: 'img', value: 'http://my.tracker/123'}, + criteria: { inViewThreshold: 0.5, timeInView: 1000 }, + message: 'Prebid Viewability', + action: 'startMeasurement' +} + +window.parent.postMessage(JSON.stringify(viewabilityRecord), '*'); +``` + +## Example of stopping the viewability measurement, when you have direct access to pbjs +``` +pbjs.viewability.stopMeasurement('ae0f9'); +``` + +## Example of stopping the viewability measurement from within a rendered creative +``` +let viewabilityRecord = { + vid: 'ae0f9', + message: 'Prebid Viewability', + action: 'stopMeasurement' +} + +window.parent.postMessage(JSON.stringify(viewabilityRecord), '*'); +``` diff --git a/modules/visxBidAdapter.js b/modules/visxBidAdapter.js index 3c7431409d7..af8672ea233 100644 --- a/modules/visxBidAdapter.js +++ b/modules/visxBidAdapter.js @@ -1,4 +1,4 @@ -import { triggerPixel, parseSizesInput, deepAccess, logError } from '../src/utils.js'; +import { triggerPixel, parseSizesInput, deepAccess, logError, getGptSlotInfoForAdUnitCode } from '../src/utils.js'; import { registerBidder } from '../src/adapters/bidderFactory.js'; import { config } from '../src/config.js'; import { BANNER, VIDEO } from '../src/mediaTypes.js'; @@ -168,16 +168,21 @@ export const spec = { return bidResponses; }, getUserSyncs: function(syncOptions, serverResponses, gdprConsent) { - if (syncOptions.pixelEnabled) { - var query = []; - if (gdprConsent) { - if (gdprConsent.consentString) { - query.push('gdpr_consent=' + encodeURIComponent(gdprConsent.consentString)); - } - query.push('gdpr_applies=' + encodeURIComponent( - (typeof gdprConsent.gdprApplies === 'boolean') - ? Number(gdprConsent.gdprApplies) : 1)); + var query = []; + if (gdprConsent) { + if (gdprConsent.consentString) { + query.push('gdpr_consent=' + encodeURIComponent(gdprConsent.consentString)); } + query.push('gdpr_applies=' + encodeURIComponent( + (typeof gdprConsent.gdprApplies === 'boolean') + ? Number(gdprConsent.gdprApplies) : 1)); + } + if (syncOptions.iframeEnabled) { + return [{ + type: 'iframe', + url: buildUrl(ADAPTER_SYNC_PATH) + '?iframe=1' + (query.length ? '&' + query.join('&') : '') + }]; + } else if (syncOptions.pixelEnabled) { return [{ type: 'image', url: buildUrl(ADAPTER_SYNC_PATH) + (query.length ? '?' + query.join('&') : '') @@ -198,7 +203,7 @@ export const spec = { }, onTimeout: function(timeoutData) { // Call '/track/bid_timeout' with timeout data - triggerPixel(buildUrl(TRACK_TIMEOUT_PATH) + '?data=' + JSON.stringify(timeoutData)); + triggerPixel(buildUrl(TRACK_TIMEOUT_PATH) + '//' + JSON.stringify(timeoutData)); } }; @@ -230,13 +235,13 @@ function makeVideo(videoParams = {}) { return result; }, { w: deepAccess(videoParams, 'playerSize.0.0'), h: deepAccess(videoParams, 'playerSize.0.1') }); - if (video.w && video.h && video.mimes) { + if (video.w && video.h) { return video; } } function buildImpObject(bid) { - const { params: { uid }, bidId, mediaTypes, sizes } = bid; + const { params: { uid }, bidId, mediaTypes, sizes, adUnitCode } = bid; const video = mediaTypes && _isVideoBid(bid) && _isValidVideoBid(bid) && makeVideo(mediaTypes.video); const banner = makeBanner((mediaTypes && mediaTypes.banner) || (!video && { sizes })); const impObject = { @@ -248,6 +253,10 @@ function buildImpObject(bid) { } }; + if (impObject.banner) { + impObject.ext.bidder.adslotExists = _isAdSlotExists(adUnitCode); + } + if (impObject.ext.bidder.uid && (impObject.banner || impObject.video)) { return impObject; } @@ -347,19 +356,20 @@ function _isValidVideoBid(bid, logErrors = false) { } result = false; } - if (!videoMediaType.mimes) { - if (logErrors) { - logError(LOG_ERROR_MESS.videoMissing + 'mimes'); - } - result = false; + return result; +} + +function _isAdSlotExists(adUnitCode) { + if (document.getElementById(adUnitCode)) { + return true; } - if (!videoMediaType.protocols) { - if (logErrors) { - logError(LOG_ERROR_MESS.videoMissing + 'protocols'); - } - result = false; + + const gptAdSlot = getGptSlotInfoForAdUnitCode(adUnitCode); + if (gptAdSlot && gptAdSlot.divId && document.getElementById(gptAdSlot.divId)) { + return true; } - return result; + + return false; } registerBidder(spec); diff --git a/modules/visxBidAdapter.md b/modules/visxBidAdapter.md index 9578f7cc4a7..34ebe9bb937 100644 --- a/modules/visxBidAdapter.md +++ b/modules/visxBidAdapter.md @@ -3,7 +3,7 @@ ``` Module Name: YOC VIS.X Bidder Adapter Module Type: Bidder Adapter -Maintainer: service@yoc.com +Maintainer: supply.partners@yoc.com ``` # Description @@ -47,16 +47,14 @@ var adUnits = [ } ] }, - // YOC In-stream adUnit + // In-stream video adUnit { code: 'instream-test-div', mediaTypes: { video: { context: 'instream', - playerSize: [400, 300], - mimes: ['video/mp4'], - protocols: [3, 6] - }, + playerSize: [400, 300] + } }, bids: [ { diff --git a/modules/weboramaRtdProvider.js b/modules/weboramaRtdProvider.js index 01086ad129f..70f258e1698 100644 --- a/modules/weboramaRtdProvider.js +++ b/modules/weboramaRtdProvider.js @@ -2,90 +2,326 @@ * This module adds Weborama provider to the real time data module * The {@link module:modules/realTimeData} module is required * The module will fetch contextual data (page-centric) from Weborama server + * and may access user-centric data from local storage * @module modules/weboramaRtdProvider * @requires module:modules/realTimeData */ /** -* @typedef {Object} ModuleParams -* @property {WeboCtxConf} weboCtxConf -*/ + * @typedef {Object} ModuleParams + * @property {WeboCtxConf} weboCtxConf + * @property {WeboUserDataConf} weboUserDataConf + */ /** -* @typedef {Object} WeboCtxConf -* @property {string} token required token to be used on bigsea contextual API requests -* @property {?string} targetURL specify the target url instead use the referer -* @property {?boolean} setTargeting if true will set the GAM targeting -* @property {?object} defaultProfile to be used if the profile is not found -*/ - -import { deepSetValue, logError, tryAppendQueryString, logMessage } from '../src/utils.js'; -import {submodule} from '../src/hook.js'; -import {ajax} from '../src/ajax.js'; -import {config} from '../src/config.js'; + * @typedef {Object} WeboCtxConf + * @property {string} token required token to be used on bigsea contextual API requests + * @property {?string} targetURL specify the target url instead use the referer + * @property {?Boolean} setPrebidTargeting if true will set the GAM targeting (default true) + * @property {?Boolean} sendToBidders if true, will send the contextual profile to all bidders (default true) + * @property {?object} defaultProfile to be used if the profile is not found + */ + +/** + * @typedef {Object} WeboUserDataConf + * @property {?string} localStorageProfileKey can be used to customize the local storage key (default is 'webo_wam2gam_entry') + * @property {?Boolean} setPrebidTargeting if true will set the GAM targeting (default true) + * @property {?Boolean} sendToBidders if true, will send the contextual profile to all bidders (default true) + * @property {?object} defaultProfile to be used if the profile is not found + */ + +import { + getGlobal +} from '../src/prebidGlobal.js'; +import { + deepSetValue, + deepAccess, + isEmpty, + mergeDeep, + logError, + tryAppendQueryString, + logMessage +} from '../src/utils.js'; +import { + submodule +} from '../src/hook.js'; +import { + ajax +} from '../src/ajax.js'; +import { + getStorageManager +} from '../src/storageManager.js'; + +const adapterManager = require('../src/adapterManager.js').default; /** @type {string} */ const MODULE_NAME = 'realTimeData'; /** @type {string} */ const SUBMODULE_NAME = 'weborama'; /** @type {string} */ -const WEBO_CTX = 'webo_ctx'; +export const DEFAULT_LOCAL_STORAGE_USER_PROFILE_KEY = 'webo_wam2gam_entry'; /** @type {string} */ -const WEBO_DS = 'webo_ds'; +const LOCAL_STORAGE_USER_TARGETING_SECTION = 'targeting'; +/** @type {number} */ +const GVLID = 284; +/** @type {object} */ +export const storage = getStorageManager(GVLID, SUBMODULE_NAME); /** @type {null|Object} */ -let _bigseaContextualProfile = null; +let _weboContextualProfile = null; -/** function that provides ad server targeting data to RTD-core -* @param {Array} adUnitsCodes -* @param {Object} moduleConfig -* @returns {Object} target data +/** @type {Boolean} */ +let _weboCtxInitialized = false; + +/** @type {null|Object} */ +let _weboUserDataUserProfile = null; + +/** @type {Boolean} */ +let _weboUserDataInitialized = false; + +/** Initialize module + * @param {object} moduleConfig + * @return {Boolean} true if module was initialized with success */ -function getTargetingData(adUnitsCodes, moduleConfig) { +function init(moduleConfig) { moduleConfig = moduleConfig || {}; const moduleParams = moduleConfig.params || {}; const weboCtxConf = moduleParams.weboCtxConf || {}; - const defaultContextualProfiles = weboCtxConf.defaultProfile || {} - const profile = _bigseaContextualProfile || defaultContextualProfiles; + const weboUserDataConf = moduleParams.weboUserDataConf; - if (weboCtxConf.setOrtb2) { - const ortb2 = config.getConfig('ortb2') || {}; - if (profile[WEBO_CTX]) { - deepSetValue(ortb2, 'site.ext.data.webo_ctx', profile[WEBO_CTX]); - } - if (profile[WEBO_DS]) { - deepSetValue(ortb2, 'site.ext.data.webo_ds', profile[WEBO_DS]); - } - config.setConfig({ortb2: ortb2}); - } + _weboCtxInitialized = initWeboCtx(weboCtxConf); + _weboUserDataInitialized = initWeboUserData(weboUserDataConf); - if (weboCtxConf.setTargeting === false) { - return {}; + return _weboCtxInitialized || _weboUserDataInitialized; +} + +/** Initialize contextual sub module + * @param {WeboCtxConf} weboCtxConf + * @return {Boolean} true if sub module was initialized with success + */ +function initWeboCtx(weboCtxConf) { + _weboCtxInitialized = false; + _weboContextualProfile = null; + + if (!weboCtxConf.token) { + logError('missing param "token" for weborama contextual sub module initialization'); + return false; } + return true; +} + +/** Initialize weboUserData sub module + * @param {WeboUserDataConf} weboUserDataConf + * @return {Boolean} true if sub module was initialized with success + */ +function initWeboUserData(weboUserDataConf) { + _weboUserDataInitialized = false; + _weboUserDataUserProfile = null; + + return !!weboUserDataConf; +} + +/** function that provides ad server targeting data to RTD-core + * @param {Array} adUnitsCodes + * @param {Object} moduleConfig + * @returns {Object} target data + */ +function getTargetingData(adUnitsCodes, moduleConfig) { + moduleConfig = moduleConfig || {}; + const moduleParams = moduleConfig.params || {}; + const weboCtxConf = moduleParams.weboCtxConf || {}; + const weboUserDataConf = moduleParams.weboUserDataConf || {}; + const weboCtxConfTargeting = weboCtxConf.setPrebidTargeting !== false; + const weboUserDataConfTargeting = weboUserDataConf.setPrebidTargeting !== false; + try { - const formattedProfile = profile; - const r = adUnitsCodes.reduce((rp, adUnitCode) => { + const profile = getCompleteProfile(moduleParams, weboCtxConfTargeting, weboUserDataConfTargeting); + + if (isEmpty(profile)) { + return {}; + } + + const td = adUnitsCodes.reduce((data, adUnitCode) => { if (adUnitCode) { - rp[adUnitCode] = formattedProfile; + data[adUnitCode] = profile; } - return rp; + return data; }, {}); - return r; + + return td; } catch (e) { logError('unable to format weborama rtd targeting data', e); return {}; } } +/** function that provides complete profile formatted to be used + * @param {ModuleParams} moduleParams + * @param {Boolean} weboCtxConfTargeting + * @param {Boolean} weboUserDataConfTargeting + * @returns {Object} complete profile + */ +function getCompleteProfile(moduleParams, weboCtxConfTargeting, weboUserDataConfTargeting) { + const profile = {}; + + if (weboCtxConfTargeting) { + const contextualProfile = getContextualProfile(moduleParams.weboCtxConf || {}); + mergeDeep(profile, contextualProfile); + } + + if (weboUserDataConfTargeting) { + const weboUserDataProfile = getWeboUserDataProfile(moduleParams.weboUserDataConf || {}); + mergeDeep(profile, weboUserDataProfile); + } + + return profile; +} + +/** return contextual profile + * @param {WeboCtxConf} weboCtxConf + * @returns {Object} contextual profile + */ +function getContextualProfile(weboCtxConf) { + const defaultContextualProfile = weboCtxConf.defaultProfile || {}; + return _weboContextualProfile || defaultContextualProfile; +} + +/** return weboUserData profile + * @param {WeboUserDataConf} weboUserDataConf + * @returns {Object} weboUserData profile + */ +function getWeboUserDataProfile(weboUserDataConf) { + const weboUserDataDefaultUserProfile = weboUserDataConf.defaultProfile || {}; + + if (storage.localStorageIsEnabled() && !_weboUserDataUserProfile) { + const localStorageProfileKey = weboUserDataConf.localStorageProfileKey || DEFAULT_LOCAL_STORAGE_USER_PROFILE_KEY; + + const entry = storage.getDataFromLocalStorage(localStorageProfileKey); + if (entry) { + const data = JSON.parse(entry); + if (data && Object.keys(data).length > 0) { + _weboUserDataUserProfile = data[LOCAL_STORAGE_USER_TARGETING_SECTION]; + } + } + } + + return _weboUserDataUserProfile || weboUserDataDefaultUserProfile; +} + +/** function that will allow RTD sub-modules to modify the AdUnit object for each auction + * @param {Object} reqBidsConfigObj + * @param {doneCallback} onDone + * @param {Object} moduleConfig + * @returns {void} + */ +export function getBidRequestData(reqBidsConfigObj, onDone, moduleConfig) { + moduleConfig = moduleConfig || {}; + const moduleParams = moduleConfig.params || {}; + const weboCtxConf = moduleParams.weboCtxConf || {}; + + const adUnits = reqBidsConfigObj.adUnits || getGlobal().adUnits; + + if (!_weboCtxInitialized) { + handleBidRequestData(adUnits, moduleParams); + + onDone(); + + return; + } + + fetchContextualProfile(weboCtxConf, (data) => { + logMessage('fetchContextualProfile on getBidRequestData is done'); + + setWeboContextualProfile(data); + }, () => { + handleBidRequestData(adUnits, moduleParams); + + onDone(); + }); +} + +/** function that handles bid request data + * @param {Object[]} adUnits + * @param {ModuleParams} moduleParams + * @returns {void} + */ + +function handleBidRequestData(adUnits, moduleParams) { + const weboCtxConf = moduleParams.weboCtxConf || {}; + const weboUserDataConf = moduleParams.weboUserDataConf || {}; + const weboCtxConfTargeting = weboCtxConf.sendToBidders !== false; + const weboUserDataConfTargeting = weboUserDataConf.sendToBidders !== false; + const profile = getCompleteProfile(moduleParams, weboCtxConfTargeting, weboUserDataConfTargeting); + + if (isEmpty(profile)) { + return; + } + + adUnits.forEach(adUnit => { + if (adUnit.hasOwnProperty('bids')) { + adUnit.bids.forEach(bid => handleBid(adUnit, profile, bid)); + } + }); +} + +/** @type {string} */ +const SMARTADSERVER = 'smartadserver'; + +/** @type {Object} */ +const bidderAliasRegistry = adapterManager.aliasRegistry || {}; + +/** handle individual bid + * @param {Object} adUnit + * @param {Object} profile + * @param {Object} bid + * @returns {void} + */ +function handleBid(adUnit, profile, bid) { + const bidder = bidderAliasRegistry[bid.bidder] || bid.bidder; + + logMessage('handle bidder', bidder, bid); + + switch (bidder) { + case SMARTADSERVER: + handleSmartadserverBid(adUnit, profile, bid); + + break; + } +} + +/** handle smartadserver bid + * @param {Object} adUnit + * @param {Object} profile + * @param {Object} bid + * @returns {void} + */ +function handleSmartadserverBid(adUnit, profile, bid) { + const target = []; + + if (deepAccess(bid, 'params.target')) { + target.push(bid.params.target.split(';')); + } + + Object.keys(profile).forEach(key => { + profile[key].forEach(value => { + const keyword = `${key}=${value}`; + if (target.indexOf(keyword) === -1) { + target.push(keyword); + } + }); + }); + + deepSetValue(bid, 'params.target', target.join(';')); +} + /** set bigsea contextual profile on module state - * if the profile is empty, will store the default profile * @param {null|Object} data * @returns {void} */ -export function setBigseaContextualProfile(data) { +export function setWeboContextualProfile(data) { if (data && Object.keys(data).length > 0) { - _bigseaContextualProfile = data; + _weboContextualProfile = data; } } @@ -96,9 +332,9 @@ export function setBigseaContextualProfile(data) { */ /** onDone callback type - * @callback doneCallback - * @returns {void} - */ + * @callback doneCallback + * @returns {void} + */ /** Fetch Bigsea Contextual Profile * @param {WeboCtxConf} weboCtxConf @@ -117,7 +353,7 @@ function fetchContextualProfile(weboCtxConf, onSuccess, onDone) { const url = 'https://ctx.weborama.com/api/profile?' + queryString; ajax(url, { - success: function (response, req) { + success: function(response, req) { if (req.status === 200) { try { const data = JSON.parse(response); @@ -126,49 +362,28 @@ function fetchContextualProfile(weboCtxConf, onSuccess, onDone) { } catch (e) { onDone(); logError('unable to parse weborama data', e); + throw e; } } else if (req.status === 204) { onDone(); } }, - error: function () { + error: function() { onDone(); logError('unable to get weborama data'); } }, - null, - { + null, { method: 'GET', withCredentials: false, }); } -/** Initialize module - * @param {object} moduleConfig - * @return {boolean} true if module was initialized with success - */ -function init(moduleConfig) { - _bigseaContextualProfile = null; - - moduleConfig = moduleConfig || {}; - const moduleParams = moduleConfig.params || {}; - const weboCtxConf = moduleParams.weboCtxConf || {}; - - if (weboCtxConf.token) { - fetchContextualProfile(weboCtxConf, setBigseaContextualProfile, - () => logMessage('fetchContextualProfile on init is done')); - } else { - logError('missing param "token" for weborama rtd module initialization'); - return false; - } - - return true; -} - export const weboramaSubmodule = { name: SUBMODULE_NAME, init: init, getTargetingData: getTargetingData, + getBidRequestData: getBidRequestData, }; submodule(MODULE_NAME, weboramaSubmodule); diff --git a/modules/weboramaRtdProvider.md b/modules/weboramaRtdProvider.md index e7b9b96d668..06e5b4fb43b 100644 --- a/modules/weboramaRtdProvider.md +++ b/modules/weboramaRtdProvider.md @@ -10,8 +10,6 @@ Maintainer: prebid-support@weborama.com Weborama provides a Semantic AI Contextual API that classifies in Real-time a web page seen by a web user within generic and custom topics. It enables publishers to better monetize their inventory and unlock it to programmatic. -ORTB2 compliant and FPD support for Prebid versions < 4.29 - Contact prebid-support@weborama.com for information. ### Publisher Usage @@ -32,17 +30,31 @@ pbjs.setConfig( name: "weborama", waitForIt: true, params: { - weboCtxConf: { - setTargeting: true, - token: "<>", - targetURL: "..." // default is document.URL + weboCtxConf: { // contextual configuration + token: "<>", // mandatory + targetURL: "...", // default is document.URL + setPrebidTargeting: true, // default + sendToBidders: true, // default + defaultProfile: { // optional, default is none + webo_ctx: ['foo'], + webo_ds: ['bar'] + } + }, + weboUserDataConf: { // user-centric configuration + setPrebidTargeting: true, // default + sendToBidders: true, // default + defaultProfile: { // optional, default is none + webo_cs: ['baz'], + webo_audiences: ['bam'] + }, + localStorageProfileKey: 'webo_wam2gam_entry' // default } } } ] } ... -} +); ``` ### Parameter Descriptions for the Weborama Configuration Section @@ -55,15 +67,20 @@ pbjs.setConfig( | params.weboCtxConf | Object | Weborama Contextual Configuration | Optional | | params.weboCtxConf.token | String | Security Token provided by Weborama, unique per client | Mandatory | | params.weboCtxConf.targetURL | String | Url to be profiled in the contextual api | Optional. Defaults to `document.URL` | +| params.weboCtxConf.setPrebidTargeting|Boolean|If true, will use the contextual profile to set the prebid (GPT/GAM or AST) targeting of all adunits managed by prebid.js| Optional. Default is *true*.| +| params.weboCtxConf.sendToBidders|Boolean|If true, will send the contextual profile to all bidders (only smartadserver is supported now)| Optional. Default is *true*.| | params.weboCtxConf.defaultProfile | Object | default value of the profile to be used when there are no response from contextual api (such as timeout)| Optional. Default is `{}` | -| params.weboCtxConf.setTargeting|Boolean|If true, will use the contextual profile to set the gam targeting of all adunits managed by prebid.js| Optional. Default is *true*.| -| params.weboCtxConf.setOrtb2|Boolean|If true, will use the contextual profile to set the ortb2 configuration on `site.ext.data`| Optional. Default is *false*.| +| params.weboUserDataConf | Object | WeboUserData Configuration | Optional | +| params.weboUserDataConf.setPrebidTargeting|Boolean|If true, will use the contextual profile to set the prebid (GPT/GAM or AST) targeting of all adunits managed by prebid.js| Optional. Default is *true*.| +| params.weboUserDataConf.sendToBidders|Boolean|If true, will send the contextual profile to all bidders (only smartadserver is supported now)| Optional. Default is *true*.| +| params.weboUserDataConf.defaultProfile | Object | default value of the profile to be used when there are no response from contextual api (such as timeout)| Optional. Default is `{}` | +| params.weboUserDataConf.localStorageProfileKey| String | can be used to customize the local storage key | Optional | ### Testing To view an example of available segments returned by Weborama's backends: -`gulp serve --modules=rtdModule,weboramaRtdProvider,appnexusBidAdapter` +`gulp serve --modules=rtdModule,weboramaRtdProvider,smartadserverBidAdapter` and then point your browser at: diff --git a/modules/welectBidAdapter.js b/modules/welectBidAdapter.js new file mode 100644 index 00000000000..d88a3f4c3e2 --- /dev/null +++ b/modules/welectBidAdapter.js @@ -0,0 +1,106 @@ +import { deepAccess } from '../src/utils.js'; +import { registerBidder } from '../src/adapters/bidderFactory.js'; + +const BIDDER_CODE = 'welect'; +const DEFAULT_DOMAIN = 'www.welect.de'; + +export const spec = { + code: BIDDER_CODE, + aliases: ['wlt'], + gvlid: 282, + supportedMediaTypes: ['video'], + + // short code + /** + * Determines whether or not the given bid request is valid. + * + * @param {BidRequest} bid The bid params to validate. + * @return boolean True if this is a valid bid, and false otherwise. + */ + isBidRequestValid: function (bid) { + return ( + deepAccess(bid, 'mediaTypes.video.context') === 'instream' && + !!bid.params.placementId + ); + }, + /** + * Make a server request from the list of BidRequests. + * + * @param {validBidRequests[]} - an array of bids + * @return ServerRequest Info describing the request to the server. + */ + buildRequests: function (validBidRequests) { + return validBidRequests.map((bidRequest) => { + let rawSizes = + deepAccess(bidRequest, 'mediaTypes.video.playerSize') || + bidRequest.sizes; + let size = rawSizes[0]; + + let domain = bidRequest.params.domain || DEFAULT_DOMAIN; + + let url = `https://${domain}/api/v2/preflight/${bidRequest.params.placementId}`; + + let gdprConsent = null; + + if (bidRequest && bidRequest.gdprConsent) { + gdprConsent = { + gdpr_consent: { + gdprApplies: bidRequest.gdprConsent.gdprApplies, + tcString: bidRequest.gdprConsent.gdprConsent, + }, + }; + } + + const data = { + width: size[0], + height: size[1], + bid_id: bidRequest.bidId, + ...gdprConsent, + }; + + return { + method: 'POST', + url: url, + data: data, + options: { + contentType: 'application/json', + withCredentials: false, + crossOrigin: true, + }, + }; + }); + }, + /** + * Unpack the response from the server into a list of bids. + * + * @param {ServerResponse} serverResponse A successful response from the server. + * @return {Bid[]} An array of bids which were nested inside the server. + */ + interpretResponse: function (serverResponse, bidRequest) { + const responseBody = serverResponse.body; + + if (typeof responseBody !== 'object' || responseBody.available !== true) { + return []; + } + + const bidResponses = []; + const bidResponse = { + requestId: responseBody.bidResponse.requestId, + cpm: responseBody.bidResponse.cpm, + width: responseBody.bidResponse.width, + height: responseBody.bidResponse.height, + creativeId: responseBody.bidResponse.creativeId, + currency: responseBody.bidResponse.currency, + netRevenue: responseBody.bidResponse.netRevenue, + ttl: responseBody.bidResponse.ttl, + ad: responseBody.bidResponse.ad, + vastUrl: responseBody.bidResponse.vastUrl, + meta: { + advertiserDomains: responseBody.bidResponse.meta.advertiserDomains + } + }; + bidResponses.push(bidResponse); + return bidResponses; + }, +}; +registerBidder(spec); diff --git a/modules/yahoosspBidAdapter.js b/modules/yahoosspBidAdapter.js new file mode 100644 index 00000000000..a9533234d1c --- /dev/null +++ b/modules/yahoosspBidAdapter.js @@ -0,0 +1,643 @@ +import { registerBidder } from '../src/adapters/bidderFactory.js'; +import { BANNER, VIDEO } from '../src/mediaTypes.js'; +import { deepAccess, isFn, isStr, isNumber, isArray, isEmpty, isPlainObject, generateUUID, logInfo, logWarn } from '../src/utils.js'; +import { config } from '../src/config.js'; +import { Renderer } from '../src/Renderer.js'; + +const INTEGRATION_METHOD = 'prebid.js'; +const BIDDER_CODE = 'yahoossp'; +const GVLID = 25; +const ADAPTER_VERSION = '1.0.2'; +const PREBID_VERSION = '$prebid.version$'; +const DEFAULT_BID_TTL = 300; +const TEST_MODE_DCN = '8a969516017a7a396ec539d97f540011'; +const TEST_MODE_PUBID_DCN = '1234567'; +const TEST_MODE_BANNER_POS = '8a969978017a7aaabab4ab0bc01a0009'; +const TEST_MODE_VIDEO_POS = '8a96958a017a7a57ac375d50c0c700cc'; +const DEFAULT_RENDERER_TIMEOUT = 700; +const DEFAULT_CURRENCY = 'USD'; +const SSP_ENDPOINT_DCN_POS = 'https://c2shb.pubgw.yahoo.com/bidRequest'; +const SSP_ENDPOINT_PUBID = 'https://c2shb.pubgw.yahoo.com/admax/bid/partners/PBJS'; +const SUPPORTED_USER_ID_SOURCES = [ + 'admixer.net', + 'adserver.org', + 'adtelligent.com', + 'akamai.com', + 'amxrtb.com', + 'audigent.com', + 'britepool.com', + 'criteo.com', + 'crwdcntrl.net', + 'deepintent.com', + 'hcn.health', + 'id5-sync.com', + 'idx.lat', + 'intentiq.com', + 'intimatemerger.com', + 'liveintent.com', + 'liveramp.com', + 'mediawallahscript.com', + 'merkleinc.com', + 'netid.de', + 'neustar.biz', + 'nextroll.com', + 'novatiq.com', + 'parrable.com', + 'pubcid.org', + 'quantcast.com', + 'quantcast.com', + 'tapad.com', + 'uidapi.com', + 'verizonmedia.com', + 'yahoo.com', + 'zeotap.com' +]; + +/* Utility functions */ +function hasPurpose1Consent(bidderRequest) { + if (bidderRequest && bidderRequest.gdprConsent) { + if (bidderRequest.gdprConsent.gdprApplies && bidderRequest.gdprConsent.apiVersion === 2) { + return !!false; + } + } + return true; +} + +function getSize(size) { + return { + w: parseInt(size[0]), + h: parseInt(size[1]) + } +} + +function transformSizes(sizes) { + if (isArray(sizes) && sizes.length === 2 && !isArray(sizes[0])) { + return [ getSize(sizes) ]; + } + return sizes.map(getSize); +} + +function extractUserSyncUrls(syncOptions, pixels) { + let itemsRegExp = /(img|iframe)[\s\S]*?src\s*=\s*("|')(.*?)\2/gi; + let tagNameRegExp = /\w*(?=\s)/; + let srcRegExp = /src=("|')(.*?)\1/; + let userSyncObjects = []; + + if (pixels) { + let matchedItems = pixels.match(itemsRegExp); + if (matchedItems) { + matchedItems.forEach(item => { + let tagName = item.match(tagNameRegExp)[0]; + let url = item.match(srcRegExp)[2]; + + if (tagName && url) { + let tagType = tagName.toLowerCase() === 'img' ? 'image' : 'iframe'; + if ((!syncOptions.iframeEnabled && tagType === 'iframe') || + (!syncOptions.pixelEnabled && tagType === 'image')) { + return; + } + userSyncObjects.push({ + type: tagType, + url: url + }); + } + }); + } + } + + return userSyncObjects; +} + +function getSupportedEids(bid) { + if (isArray(deepAccess(bid, 'userIdAsEids'))) { + return bid.userIdAsEids.filter(eid => { + return SUPPORTED_USER_ID_SOURCES.indexOf(eid.source) !== -1; + }); + } + return []; +} + +function isSecure(bid) { + return deepAccess(bid, 'params.bidOverride.imp.secure') || (document.location.protocol === 'https:') ? 1 : 0; +}; + +function getPubIdMode(bid) { + let pubIdMode; + if (deepAccess(bid, 'params.pubId')) { + pubIdMode = true; + } else if (deepAccess(bid, 'params.dcn') && deepAccess(bid, 'params.pos')) { + pubIdMode = false; + }; + return pubIdMode; +}; + +function getAdapterMode() { + let adapterMode = config.getConfig('yahoossp.mode'); + adapterMode = adapterMode ? adapterMode.toLowerCase() : undefined; + if (typeof adapterMode === 'undefined' || adapterMode === BANNER) { + return BANNER; + } else if (adapterMode === VIDEO) { + return VIDEO; + } else if (adapterMode === 'all') { + return '*'; + } +}; + +function getResponseFormat(bid) { + const adm = bid.adm; + if (adm.indexOf('o2playerSettings') !== -1 || adm.indexOf('YAHOO.VideoPlatform.VideoPlayer') !== -1 || adm.indexOf('AdPlacement') !== -1) { + return BANNER; + } else if (adm.indexOf('VAST') !== -1) { + return VIDEO; + } +}; + +function getFloorModuleData(bid) { + const adapterMode = getAdapterMode(); + const getFloorRequestObject = { + currency: deepAccess(bid, 'params.bidOverride.cur') || DEFAULT_CURRENCY, + mediaType: adapterMode, + size: '*' + }; + return (isFn(bid.getFloor)) ? bid.getFloor(getFloorRequestObject) : false; +}; + +function filterBidRequestByMode(validBidRequests) { + const mediaTypesMode = getAdapterMode(); + let result = []; + if (mediaTypesMode === BANNER) { + result = validBidRequests.filter(bid => { + return Object.keys(bid.mediaTypes).some(item => item === BANNER); + }); + } else if (mediaTypesMode === VIDEO) { + result = validBidRequests.filter(bid => { + return Object.keys(bid.mediaTypes).some(item => item === VIDEO); + }); + } else if (mediaTypesMode === '*') { + result = validBidRequests.filter(bid => { + return Object.keys(bid.mediaTypes).some(item => item === BANNER || item === VIDEO); + }); + }; + return result; +}; + +function validateAppendObject(validationType, allowedKeys, inputObject, appendToObject) { + const outputObject = { + ...appendToObject + }; + + for (const objectKey in inputObject) { + switch (validationType) { + case 'string': + if (allowedKeys.indexOf(objectKey) !== -1 && isStr(inputObject[objectKey])) { + outputObject[objectKey] = inputObject[objectKey]; + }; + break; + case 'number': + if (allowedKeys.indexOf(objectKey) !== -1 && isNumber(inputObject[objectKey])) { + outputObject[objectKey] = inputObject[objectKey]; + }; + break; + + case 'array': + if (allowedKeys.indexOf(objectKey) !== -1 && isArray(inputObject[objectKey])) { + outputObject[objectKey] = inputObject[objectKey]; + }; + break; + case 'object': + if (allowedKeys.indexOf(objectKey) !== -1 && isPlainObject(inputObject[objectKey])) { + outputObject[objectKey] = inputObject[objectKey]; + }; + break; + case 'objectAllKeys': + if (isPlainObject(inputObject)) { + outputObject[objectKey] = inputObject[objectKey]; + }; + break; + }; + }; + return outputObject; +}; + +function getTtl(bidderRequest) { + const globalTTL = config.getConfig('yahoossp.ttl'); + return globalTTL ? validateTTL(globalTTL) : validateTTL(deepAccess(bidderRequest, 'params.ttl')); +}; + +function validateTTL(ttl) { + return (isNumber(ttl) && ttl > 0 && ttl < 3600) ? ttl : DEFAULT_BID_TTL +}; + +function isNotEmptyStr(value) { + return (isStr(value) && value.length > 0); +}; + +function generateOpenRtbObject(bidderRequest, bid) { + if (bidderRequest) { + let outBoundBidRequest = { + id: generateUUID(), + cur: [getFloorModuleData(bidderRequest).currency || deepAccess(bid, 'params.bidOverride.cur') || DEFAULT_CURRENCY], + imp: [], + site: { + page: deepAccess(bidderRequest, 'refererInfo.referer'), + }, + device: { + dnt: 0, + ua: navigator.userAgent, + ip: deepAccess(bid, 'params.bidOverride.device.ip') || deepAccess(bid, 'params.ext.ip') || undefined + }, + regs: { + ext: { + 'us_privacy': bidderRequest.uspConsent ? bidderRequest.uspConsent : '', + gdpr: bidderRequest.gdprConsent && bidderRequest.gdprConsent.gdprApplies ? 1 : 0 + } + }, + source: { + ext: { + hb: 1, + adapterver: ADAPTER_VERSION, + prebidver: PREBID_VERSION, + integration: { + name: INTEGRATION_METHOD, + ver: PREBID_VERSION + } + }, + fd: 1 + }, + user: { + ext: { + consent: bidderRequest.gdprConsent && bidderRequest.gdprConsent.gdprApplies + ? bidderRequest.gdprConsent.consentString : '', + eids: getSupportedEids(bid) + } + } + }; + + if (getPubIdMode(bid) === true) { + outBoundBidRequest.site.publisher = { + id: bid.params.pubId + } + if (deepAccess(bid, 'params.bidOverride.site.id') || deepAccess(bid, 'params.siteId')) { + outBoundBidRequest.site.id = deepAccess(bid, 'params.bidOverride.site.id') || bid.params.siteId; + } + } else { + outBoundBidRequest.site.id = bid.params.dcn; + }; + + if (config.getConfig('ortb2')) { + outBoundBidRequest = appendFirstPartyData(outBoundBidRequest, bid); + }; + + if (deepAccess(bid, 'schain')) { + outBoundBidRequest.source.ext.schain = bid.schain; + outBoundBidRequest.source.ext.schain.nodes[0].rid = outBoundBidRequest.id; + }; + + return outBoundBidRequest; + }; +}; + +function appendImpObject(bid, openRtbObject) { + const mediaTypeMode = getAdapterMode(); + + if (openRtbObject && bid) { + const impObject = { + id: bid.bidId, + secure: isSecure(bid), + bidfloor: getFloorModuleData(bid).floor || deepAccess(bid, 'params.bidOverride.imp.bidfloor') + }; + + if (bid.mediaTypes.banner && (typeof mediaTypeMode === 'undefined' || mediaTypeMode === BANNER || mediaTypeMode === '*')) { + impObject.banner = { + mimes: bid.mediaTypes.banner.mimes || ['text/html', 'text/javascript', 'application/javascript', 'image/jpg'], + format: transformSizes(bid.sizes) + }; + if (bid.mediaTypes.banner.pos) { + impObject.banner.pos = bid.mediaTypes.banner.pos; + }; + }; + + if (bid.mediaTypes.video && (mediaTypeMode === VIDEO || mediaTypeMode === '*')) { + const playerSize = transformSizes(bid.mediaTypes.video.playerSize); + impObject.video = { + mimes: deepAccess(bid, 'params.bidOverride.imp.video.mimes') || bid.mediaTypes.video.mimes || ['video/mp4', 'application/javascript'], + w: deepAccess(bid, 'params.bidOverride.imp.video.w') || playerSize[0].w, + h: deepAccess(bid, 'params.bidOverride.imp.video.h') || playerSize[0].h, + maxbitrate: deepAccess(bid, 'params.bidOverride.imp.video.maxbitrate') || bid.mediaTypes.video.maxbitrate || undefined, + maxduration: deepAccess(bid, 'params.bidOverride.imp.video.maxduration') || bid.mediaTypes.video.maxduration || undefined, + minduration: deepAccess(bid, 'params.bidOverride.imp.video.minduration') || bid.mediaTypes.video.minduration || undefined, + api: deepAccess(bid, 'params.bidOverride.imp.video.api') || bid.mediaTypes.video.api || [2], + delivery: deepAccess(bid, 'params.bidOverride.imp.video.delivery') || bid.mediaTypes.video.delivery || undefined, + pos: deepAccess(bid, 'params.bidOverride.imp.video.pos') || bid.mediaTypes.video.pos || undefined, + playbackmethod: deepAccess(bid, 'params.bidOverride.imp.video.playbackmethod') || bid.mediaTypes.video.playbackmethod || undefined, + placement: deepAccess(bid, 'params.bidOverride.imp.video.placement') || bid.mediaTypes.video.placement || undefined, + linearity: deepAccess(bid, 'params.bidOverride.imp.video.linearity') || bid.mediaTypes.video.linearity || 1, + protocols: deepAccess(bid, 'params.bidOverride.imp.video.protocols') || bid.mediaTypes.video.protocols || [2, 5], + startdelay: deepAccess(bid, 'params.bidOverride.imp.video.startdelay') || bid.mediaTypes.video.startdelay || 0, + rewarded: deepAccess(bid, 'params.bidOverride.imp.video.rewarded') || undefined, + } + } + + impObject.ext = { + dfp_ad_unit_code: bid.adUnitCode + }; + + if (deepAccess(bid, 'params.kvp') && isPlainObject(bid.params.kvp)) { + impObject.ext.kvs = {}; + for (const key in bid.params.kvp) { + if (isStr(bid.params.kvp[key]) || isNumber(bid.params.kvp[key])) { + impObject.ext.kvs[key] = bid.params.kvp[key]; + } else if (isArray(bid.params.kvp[key])) { + const array = bid.params.kvp[key]; + if (array.every(value => isStr(value)) || array.every(value => isNumber(value))) { + impObject.ext.kvs[key] = bid.params.kvp[key]; + } + } + } + }; + + if (deepAccess(bid, 'ortb2Imp.ext.data') && isPlainObject(bid.ortb2Imp.ext.data)) { + impObject.ext.data = bid.ortb2Imp.ext.data; + }; + + if (deepAccess(bid, 'ortb2Imp.instl') && isNumber(bid.ortb2Imp.instl) && (bid.ortb2Imp.instl === 1)) { + impObject.instl = bid.ortb2Imp.instl; + }; + + if (getPubIdMode(bid) === false) { + impObject.tagid = bid.params.pos; + impObject.ext.pos = bid.params.pos; + } else if (deepAccess(bid, 'params.placementId')) { + impObject.tagid = bid.params.placementId + }; + + openRtbObject.imp.push(impObject); + }; +}; + +function appendFirstPartyData(outBoundBidRequest, bid) { + const ortb2Object = config.getConfig('ortb2'); + const siteObject = deepAccess(ortb2Object, 'site') || undefined; + const siteContentObject = deepAccess(siteObject, 'content') || undefined; + const siteContentDataArray = deepAccess(siteObject, 'content.data') || undefined; + const appContentObject = deepAccess(ortb2Object, 'app.content') || undefined; + const appContentDataArray = deepAccess(ortb2Object, 'app.content.data') || undefined; + const userObject = deepAccess(ortb2Object, 'user') || undefined; + + if (siteObject && isPlainObject(siteObject)) { + const allowedSiteStringKeys = ['name', 'domain', 'page', 'ref', 'keywords', 'search']; + const allowedSiteArrayKeys = ['cat', 'sectioncat', 'pagecat']; + const allowedSiteObjectKeys = ['ext']; + outBoundBidRequest.site = validateAppendObject('string', allowedSiteStringKeys, siteObject, outBoundBidRequest.site); + outBoundBidRequest.site = validateAppendObject('array', allowedSiteArrayKeys, siteObject, outBoundBidRequest.site); + outBoundBidRequest.site = validateAppendObject('object', allowedSiteObjectKeys, siteObject, outBoundBidRequest.site); + }; + + if (siteContentObject && isPlainObject(siteContentObject)) { + const allowedContentStringKeys = ['id', 'title', 'series', 'season', 'genre', 'contentrating', 'language']; + const allowedContentNumberkeys = ['episode', 'prodq', 'context', 'livestream', 'len']; + const allowedContentArrayKeys = ['cat']; + const allowedContentObjectKeys = ['ext']; + outBoundBidRequest.site.content = validateAppendObject('string', allowedContentStringKeys, siteContentObject, outBoundBidRequest.site.content); + outBoundBidRequest.site.content = validateAppendObject('number', allowedContentNumberkeys, siteContentObject, outBoundBidRequest.site.content); + outBoundBidRequest.site.content = validateAppendObject('array', allowedContentArrayKeys, siteContentObject, outBoundBidRequest.site.content); + outBoundBidRequest.site.content = validateAppendObject('object', allowedContentObjectKeys, siteContentObject, outBoundBidRequest.site.content); + + if (siteContentDataArray && isArray(siteContentDataArray)) { + siteContentDataArray.every(dataObject => { + let newDataObject = {}; + const allowedContentDataStringKeys = ['id', 'name']; + const allowedContentDataArrayKeys = ['segment']; + const allowedContentDataObjectKeys = ['ext']; + newDataObject = validateAppendObject('string', allowedContentDataStringKeys, dataObject, newDataObject); + newDataObject = validateAppendObject('array', allowedContentDataArrayKeys, dataObject, newDataObject); + newDataObject = validateAppendObject('object', allowedContentDataObjectKeys, dataObject, newDataObject); + outBoundBidRequest.site.content.data = []; + outBoundBidRequest.site.content.data.push(newDataObject); + }) + }; + }; + + if (appContentObject && isPlainObject(appContentObject)) { + if (appContentDataArray && isArray(appContentDataArray)) { + appContentDataArray.every(dataObject => { + let newDataObject = {}; + const allowedContentDataStringKeys = ['id', 'name']; + const allowedContentDataArrayKeys = ['segment']; + const allowedContentDataObjectKeys = ['ext']; + newDataObject = validateAppendObject('string', allowedContentDataStringKeys, dataObject, newDataObject); + newDataObject = validateAppendObject('array', allowedContentDataArrayKeys, dataObject, newDataObject); + newDataObject = validateAppendObject('object', allowedContentDataObjectKeys, dataObject, newDataObject); + outBoundBidRequest.app = { + content: { + data: [] + } + }; + outBoundBidRequest.app.content.data.push(newDataObject); + }) + }; + }; + + if (userObject && isPlainObject(userObject)) { + const allowedUserStrings = ['id', 'buyeruid', 'gender', 'keywords', 'customdata']; + const allowedUserNumbers = ['yob']; + const allowedUserArrays = ['data']; + const allowedUserObjects = ['ext']; + outBoundBidRequest.user = validateAppendObject('string', allowedUserStrings, userObject, outBoundBidRequest.user); + outBoundBidRequest.user = validateAppendObject('number', allowedUserNumbers, userObject, outBoundBidRequest.user); + outBoundBidRequest.user = validateAppendObject('array', allowedUserArrays, userObject, outBoundBidRequest.user); + outBoundBidRequest.user.ext = validateAppendObject('object', allowedUserObjects, userObject, outBoundBidRequest.user.ext); + }; + + return outBoundBidRequest; +}; + +function generateServerRequest({payload, requestOptions, bidderRequest}) { + const pubIdMode = getPubIdMode(bidderRequest); + let sspEndpoint = config.getConfig('yahoossp.endpoint') || SSP_ENDPOINT_DCN_POS; + + if (pubIdMode === true) { + sspEndpoint = config.getConfig('yahoossp.endpoint') || SSP_ENDPOINT_PUBID; + }; + + if (deepAccess(bidderRequest, 'params.testing.e2etest') === true) { + logInfo('yahoossp adapter e2etest mode is active'); + requestOptions.withCredentials = false; + + if (pubIdMode === true) { + payload.site.id = TEST_MODE_PUBID_DCN; + } else { + const mediaTypeMode = getAdapterMode(); + payload.site.id = TEST_MODE_DCN; + payload.imp.forEach(impObject => { + impObject.ext.e2eTestMode = true; + if (mediaTypeMode === BANNER) { + impObject.tagid = TEST_MODE_BANNER_POS; // banner passback + } else if (mediaTypeMode === VIDEO) { + impObject.tagid = TEST_MODE_VIDEO_POS; // video passback + } else { + logWarn('yahoossp adapter e2etest mode does not support yahoossp.mode="all". \n Please specify either "banner" or "video"'); + logWarn('yahoossp adapter e2etest mode: Please make sure your adUnit matches the yahoossp.mode video or banner'); + } + }); + } + }; + logWarn('yahoossp adapter endpoint override enabled. Pointing requests to: ', sspEndpoint); + + return { + url: sspEndpoint, + method: 'POST', + data: payload, + options: requestOptions, + bidderRequest: bidderRequest + }; +}; + +function createRenderer(bidderRequest, bidResponse) { + const renderer = Renderer.install({ + url: 'https://cdn.vidible.tv/prod/hb-outstream-renderer/renderer.js', + loaded: false, + adUnitCode: bidderRequest.adUnitCode + }) + + try { + renderer.setRender(function(bidResponse) { + setTimeout(function() { + // eslint-disable-next-line no-undef + o2PlayerRender(bidResponse); + }, deepAccess(bidderRequest, 'params.testing.renderer.setTimeout') || DEFAULT_RENDERER_TIMEOUT); + }); + } catch (error) { + logWarn('yahoossp renderer error: setRender() failed', error); + } + return renderer; +} +/* Utility functions */ + +export const spec = { + code: BIDDER_CODE, + gvlid: GVLID, + aliases: [], + supportedMediaTypes: [BANNER, VIDEO], + + isBidRequestValid: function(bid) { + const params = bid.params; + if (deepAccess(params, 'testing.e2etest') === true) { + return true; + } else if ( + isPlainObject(params) && + (isNotEmptyStr(params.pubId) || (isNotEmptyStr(params.dcn) && isNotEmptyStr(params.pos))) + ) { + return true; + } else { + logWarn('yahoossp bidder params missing or incorrect, please pass object with either: dcn & pos OR pubId'); + return false; + } + }, + + buildRequests: function(validBidRequests, bidderRequest) { + if (isEmpty(validBidRequests) || isEmpty(bidderRequest)) { + logWarn('yahoossp Adapter: buildRequests called with either empty "validBidRequests" or "bidderRequest"'); + return undefined; + }; + + const requestOptions = { + contentType: 'application/json', + customHeaders: { + 'x-openrtb-version': '2.5' + } + }; + + requestOptions.withCredentials = hasPurpose1Consent(bidderRequest); + + const filteredBidRequests = filterBidRequestByMode(validBidRequests); + + if (config.getConfig('yahoossp.singleRequestMode') === true) { + const payload = generateOpenRtbObject(bidderRequest, filteredBidRequests[0]); + filteredBidRequests.forEach(bid => { + appendImpObject(bid, payload); + }); + + return generateServerRequest({payload, requestOptions, bidderRequest}); + } + + return filteredBidRequests.map(bid => { + const payloadClone = generateOpenRtbObject(bidderRequest, bid); + appendImpObject(bid, payloadClone); + return generateServerRequest({payload: payloadClone, requestOptions, bidderRequest: bid}); + }); + }, + + interpretResponse: function(serverResponse, { data, bidderRequest }) { + const response = []; + if (!serverResponse.body || !Array.isArray(serverResponse.body.seatbid)) { + return response; + } + + let seatbids = serverResponse.body.seatbid; + seatbids.forEach(seatbid => { + let bid; + + try { + bid = seatbid.bid[0]; + } catch (e) { + return response; + } + + let cpm = (bid.ext && bid.ext.encp) ? bid.ext.encp : bid.price; + + let bidResponse = { + adId: deepAccess(bid, 'adId') ? bid.adId : bid.impid || bid.crid, + adUnitCode: bidderRequest.adUnitCode, + requestId: bid.impid, + bidderCode: spec.code, + cpm: cpm, + width: bid.w, + height: bid.h, + creativeId: bid.crid || 0, + currency: bid.cur || DEFAULT_CURRENCY, + dealId: bid.dealid ? bid.dealid : null, + netRevenue: true, + ttl: getTtl(bidderRequest), + meta: { + advertiserDomains: bid.adomain, + } + }; + + const responseAdmFormat = getResponseFormat(bid); + if (responseAdmFormat === BANNER) { + bidResponse.mediaType = BANNER; + bidResponse.ad = bid.adm; + bidResponse.meta.mediaType = BANNER; + } else if (responseAdmFormat === VIDEO) { + bidResponse.mediaType = VIDEO; + bidResponse.meta.mediaType = VIDEO; + bidResponse.vastXml = bid.adm; + + if (bid.nurl) { + bidResponse.vastUrl = bid.nurl; + }; + } + + if (deepAccess(bidderRequest, 'mediaTypes.video.context') === 'outstream' && !bidderRequest.renderer) { + bidResponse.renderer = createRenderer(bidderRequest, bidResponse) || undefined; + } + + response.push(bidResponse); + }); + + return response; + }, + + getUserSyncs: function(syncOptions, serverResponses, gdprConsent, uspConsent) { + const bidResponse = !isEmpty(serverResponses) && serverResponses[0].body; + + if (bidResponse && bidResponse.ext && bidResponse.ext.pixels) { + return extractUserSyncUrls(syncOptions, bidResponse.ext.pixels); + } + + return []; + } +}; + +registerBidder(spec); diff --git a/modules/yahoosspBidAdapter.md b/modules/yahoosspBidAdapter.md new file mode 100644 index 00000000000..7fb7a307192 --- /dev/null +++ b/modules/yahoosspBidAdapter.md @@ -0,0 +1,795 @@ +# Overview +**Module Name:** yahoossp Bid Adapter +**Module Type:** Bidder Adapter +**Maintainer:** hb-fe-tech@yahooinc.com + +# Description +The Yahoo SSP Bid Adapter is an OpenRTB interface that consolidates all previous "Oath.inc" adapters such as: "aol", "oneMobile", "oneDisplay" & "oneVideo" supply-side platforms. + +# Supported Features: +* Media Types: Banner & Video +* Outstream renderer +* Multi-format adUnits +* Schain module +* Price floors module +* Advertiser domains +* End-2-End self-served testing mode +* Outstream renderer/Player +* User ID Modules - ConnectId and others +* First Party Data (ortb2 & ortb2Imp) +* Custom TTL (time to live) + + +# Adapter Request mode +Since the yahoossp adapter now supports both Banner and Video adUnits a controller was needed to allow you to define when the adapter should generate a bid-requests to our Yahoo SSP. + +**Important!** By default the adapter mode is set to "banner" only. +This means that you do not need to explicitly declare the yahoossp.mode in the Global config to initiate banner adUnit requests. + +## Request modes: +* **undefined** - (Default) Will generate bid-requests for "Banner" formats only. +* **banner** - Will generate bid-requests for "Banner" formats only (Explicit declaration). +* **video** - Will generate bid-requests for "Video" formats only (Explicit declaration). +* **all** - Will generate bid-requests for both "Banner" & "Video" formats + +**Important!** When setting yahoossp.mode = 'all' Make sure your Yahoo SSP Placement (pos id) supports both Banner & Video placements. +If it does not, the Yahoo SSP will respond only in the format it is set too. + +```javascript +pbjs.setConfig({ + yahoossp: { + mode: 'banner' // 'all', 'video', 'banner' (default) + } +}); +``` +# Integration Options +The `yahoossp` bid adapter supports 2 types of integration: +1. **dcn & pos** DEFAULT (Site/App & Position targeting) - For Display partners/publishers. +2. **pubId** (Publisher ID) - For legacy "oneVideo" AND New partners/publishers. +**Important:** pubId integration (option 2) is only possible when your Seller account is setup for "Inventory Mapping". + +**Please Note:** Most examples in this file are using dcn & pos. + +## Who is currently eligible for "pubId" integration +At this time, only the following partners/publishers are eligble for pubId integration: +1. New partners/publishers that do not have any existing accounts on Yahoo SSP (aka: aol, oneMobile, oneDisplay). +2. Video SSP (oneVideo) partners/publishers that + A. Do not have any display/banner inventory. + B. Do not have any existing accounts on Yahoo SSP (aka: aol, oneMobile, oneDisplay). + +# Mandatory Bidder Parameters +## dcn & pos (DEFAULT) +The minimal requirements for the 'yahoossp' bid adapter to generate an outbound bid-request to our Yahoo SSP are: +1. At least 1 adUnit including mediaTypes: banner or video +2. **bidder.params** object must include: + A. **dcn:** Yahoo SSP Site/App inventory parameter. + B. **pos:** Yahoo SSP position inventory parameter. + +### Example: dcn & pos Mandatory Parameters (Single banner adUnit) +```javascript +const adUnits = [{ + code: 'your-placement', + mediaTypes: { + banner: { + sizes: [[300, 250]] + } + }, + bids: [ + { + bidder: 'yahoossp', + params: { + dcn: '8a969516017a7a396ec539d97f540011', // Site/App ID provided from SSP + pos: '8a969978017a7aaabab4ab0bc01a0009' // Placement ID provided from SSP + } + } + ] +}]; +``` + +## pubId +The minimal requirements for the 'yahoossp' bid adapter to generate an outbound bid-request to our Yahoo SSP are: +1. At least 1 adUnit including mediaTypes: banner or video +2. **bidder.params** object must include: + A. **pubId:** Yahoo SSP Publisher ID (AKA oneVideo pubId/Exchange name) + +### Example: pubId Mandatory Parameters (Single banner adUnit) +```javascript +const adUnits = [{ + code: 'your-placement', + mediaTypes: { + banner: { + sizes: [[300, 250]] + } + }, + bids: [ + { + bidder: 'yahoossp', + params: { + pubId: 'DemoPublisher', // Publisher External ID provided from Yahoo SSP. + } + } + ] +}]; +``` +# Advanced adUnit Examples: +## Banner +```javascript +const adUnits = [{ + code: 'banner-adUnit', + mediaTypes: { + banner: { + sizes: [ + [300, 250] + ] + } + }, + bids: [{ + bidder: 'yahoossp', + params: { + dcn: '8a969516017a7a396ec539d97f540011', // Site/App ID provided from Yahoo SSP + pos: '8a969978017a7aaabab4ab0bc01a0009', // Placement ID provided from Yahoo SSP + } + } + }] +}]; +``` +## Video Instream +**Important!** Make sure that the Yahoo SSP Placement type (in-stream) matches the adUnit video inventory type. +**Note:** Make sure to set the adapter mode to allow video requests by setting it to mode: 'video' OR mode: 'all'. +```javascript +pbjs.setConfig({ + yahoossp: { + mode: 'video' + } +}); + +const adUnits = [{ + code: 'video-adUnit', + mediaTypes: { + video: { + context: 'instream', + playerSize: [ + [300, 250] + ], + mimes: ['video/mp4','application/javascript'], + api: [2] + } + }, + bids: [{ + bidder: 'yahoossp', + params: { + dcn: '8a969516017a7a396ec539d97f540011', // Site/App ID provided from Yahoo SSP + pos: '8a96958a017a7a57ac375d50c0c700cc', // Placement ID provided from Yahoo SSP + } + }] +}]; +``` +## Video Outstream +**Important!** Make sure that the Yahoo SSP Placement type (in-feed/ in-article) matches the adUnit video inventory type. +**Note:** Make sure to set the adapter mode to allow video requests by setting it to mode: 'video' OR mode: 'all' +```javascript +pbjs.setConfig({ + yahoossp: { + mode: 'video' + } +}); + +const adUnits = [{ + code: 'video-adUnit', + mediaTypes: { + video: { + context: 'outstream', + playerSize: [ + [300, 250] + ], + mimes: ['video/mp4','application/javascript'], + api: [2] + } + }, + bids: [{ + bidder: 'yahoossp', + params: { + dcn: '8a969516017a7a396ec539d97f540011', // Site/App ID provided from Yahoo SSP + pos: '8a96958a017a7a57ac375d50c0c700cc', // Placement ID provided from Yahoo SSP + } + }] +}]; +``` +## Multi-Format +**Important!** If you intend to use the yahoossp bidder for both Banner and Video formats please make sure: +1. Set the adapter as mode: 'all' - to call the Yahoo SSP for both banner & video formats. +2. Make sure the Yahoo SSP placement (pos id) supports both banner & video format requests. + +```javascript +pbjs.setConfig({ + yahoossp: { + mode: 'all' + } +}); + +const adUnits = [{ + code: 'video-adUnit', + mediaTypes: { + banner: { + sizes: [[300, 250]] + }, + video: { + context: 'outstream', + playerSize: [ + [300, 250] + ], + mimes: ['video/mp4','application/javascript'], + api: [2] + } + }, + bids: [{ + bidder: 'yahoossp', + params: { + dcn: '8a969516017a7a396ec539d97f540011', // Site/App ID provided from Yahoo SSP + pos: '8a96958a017a7a57ac375d50c0c700cc', // Placement ID provided from Yahoo SSP + } + }] +}]; +``` + +# Optional: Schain module support +The yahoossp adapter supports the Prebid.org Schain module and will pass it through to our Yahoo SSP +For further details please see, https://docs.prebid.org/dev-docs/modules/schain.html + +## Global Schain Example: +```javascript + pbjs.setConfig({ + "schain": { + "validation": "off", + "config": { + "ver": "1.0", + "complete": 1, + "nodes": [{ + "asi": "some-platform.com", + "sid": "111111", + "hp": 1 + }] + } + } + }); +``` +## Bidder Specific Schain Example: +```javascript + pbjs.setBidderConfig({ + "bidders": ['yahoossp'], // can list more bidders here if they share the same config + "config": { + "schain": { + "validation": "strict", + "config": { + "ver": "1.0", + "complete": 1, + "nodes": [{ + "asi": "other-platform.com", + "sid": "222222", + "hp": 0 + }] + } + } + } + }); +``` + +# Optional: Price floors module & bidfloor +The yahoossp adapter supports the Prebid.org Price Floors module and will use it to define the outbound bidfloor and currency. +By default the adapter will always check the existance of Module price floor. +If a module price floor does not exist you can set a custom bid floor for your impression using "params.bidOverride.imp.bidfloor". + +**Note:** All override params apply to all requests generated using this configuration regardless of format type. + +```javascript +const adUnits = [{ + code: 'override-pricefloor', + mediaTypes: { + banner: { + sizes: [ + [300, 250] + ] + } + }, + bids: [{ + bidder: 'yahoossp', + params: { + dcn: '8a969516017a7a396ec539d97f540011', // Site/App ID provided from Yahoo SSP + pos: '8a969978017a7aaabab4ab0bc01a0009', // Placement ID provided from Yahoo SSP + bidOverride :{ + imp: { + bidfloor: 5.00 // bidOverride priceFloor + } + } + } + } + }] +}]; +``` + +For further details please see, https://docs.prebid.org/dev-docs/modules/floors.html + +# Optional: Self-served E2E testing mode +If you want to see how the yahoossp adapter works and loads you are invited to try it out using our testing mode. +This is useful for integration testing and response parsing when checking banner vs video capabilities. + +## How to use E2E test mode: +1. Set the yahoossp global config mode to either 'banner' or 'video' - depending on the adUnit you want to test. +2. Add params.testing.e2etest: true to your adUnit bidder config - See examples below. + +**Note:** When using E2E Test Mode you do not need to pass mandatory bidder params dcn or pos. + +**Important!** E2E Testing Mode only works when the Bidder Request Mode is set explicitly to either 'banner' or 'video'. + +## Activating E2E Test for "Banner" + ```javascript +pbjs.setConfig({ + yahoossp: { + mode: 'banner' // select 'banner' or 'video' to define what response to load + } +}); + +const adUnits = [{ + code: 'your-placement', + mediaTypes: { + banner: { + sizes: [[300, 250]] + } + }, + bids: [ + { + bidder: 'yahoossp', + params: { + testing: { + e2etest: true // Activate E2E Test mode + } + } + } + ] +}]; + +``` +## Activating E2E Test for "Video" +**Note:** We recommend using Video Outstream as it would load the video response using our Outstream Renderer feature + ```javascript +pbjs.setConfig({ + yahoossp: { + mode: 'video' + } +}); + +const adUnits = [{ + code: 'video-adUnit', + mediaTypes: { + video: { + context: 'outstream', + playerSize: [ + [300, 250] + ], + mimes: ['video/mp4','application/javascript'], + api: [2] + } + }, + bids: [{ + bidder: 'yahoossp', + params: { + testing: { + e2etest: true // Activate E2E Test mode + } + } + }] +}]; +``` + +# Optional: First Party Data +The yahoossp adapter now supports first party data passed via: +1. Global ortb2 object using pbjs.setConfig() +2. adUnit ortb2Imp object declared within an adUnit. +For further details please see, https://docs.prebid.org/features/firstPartyData.html +## Global First Party Data "ortb2" +### Passing First Party "site" data: +```javascript +pbjs.setConfig({ + ortb2: { + site: { + name: 'yahooAdTech', + domain: 'yahooadtech.com', + cat: ['IAB2'], + sectioncat: ['IAB2-2'], + pagecat: ['IAB2-2'], + page: 'https://page.yahooadtech.com/here.html', + ref: 'https://ref.yahooadtech.com/there.html', + keywords:'yahoo, ad, tech', + search: 'SSP', + content: { + id: '1234', + title: 'Title', + series: 'Series', + season: 'Season', + episode: 1, + cat: ['IAB1', 'IAB1-1', 'IAB1-2', 'IAB2', 'IAB2-1'], + genre: 'Fantase', + contentrating: 'C-Rating', + language: 'EN', + prodq: 1, + context: 1, + len: 200, + data: [{ + name: "www.dataprovider1.com", + ext: { segtax: 4 }, + segment: [ + { id: "687" }, + { id: "123" } + ] + }], + ext: { + network: 'ext-network', + channel: 'ext-channel', + data: { + pageType: "article", + category: "repair" + } + } + } + } + } + }); +``` +### Passing First Party "user" data: +```javascript +pbjs.setConfig({ + ortb2: { + user: { + yob: 1985, + gender: 'm', + keywords: 'a,b', + data: [{ + name: "www.dataprovider1.com", + ext: { segtax: 4 }, + segment: [ + { id: "687" }, + { id: "123" } + ] + }], + ext: { + data: { + registered: true, + interests: ['cars'] + } + } + } + } + }); +``` +### Passing First Party "app.content.data" data: +```javascript +pbjs.setConfig({ + ortb2: { + app: { + content: { + data: [{ + name: "www.dataprovider1.com", + ext: { segtax: 4 }, + segment: [ + { id: "687" }, + { id: "123" } + ] + }], + } + } + } + }); +``` + + +## AdUnit First Party Data "ortb2Imp" +Most DSPs are adopting the Global Placement ID (GPID). +Please pass your placement specific GPID value to Yahoo SSP using `adUnit.ortb2Imp.ext.data.pbadslot`. +```javascript +const adUnits = [{ + code: 'placement', + mediaTypes: { + banner: { + sizes: [ + [300, 250] + ] + }, + }, + ortb2Imp: { + ext: { + data: { + pbadslot: "homepage-top-rect", + adUnitSpecificAttribute: "123" + } + } + }, + bids: [{ + bidder: 'yahoossp', + params: { + pubdId: 'DemoPublisher' + } + }] + } + ] +``` + +# Optional: Bidder bidOverride Parameters +The yahoossp adapter allows passing override data to the outbound bid-request in that overrides First Party Data. +**Important!** We highly recommend using prebid modules to pass data instead of bidder speicifc overrides. +The use of these parameters are a last resort to force a specific feature or use case in your implementation. + +Currently the bidOverride object only accepts the following: +* imp + * video + * mimes + * w + * h + * maxbitrate + * maxduration + * minduration + * api + * delivery + * pos + * playbackmethod + * placement + * linearity + * protocols + * rewarded +* site + * page +* device + * ip + + +```javascript +const adUnits = [{ + code: 'bidOverride-adUnit', + mediaTypes: { + video: { + context: 'outstream', + playerSize: [ + [300, 250] + ], + } + }, + bids: [{ + bidder: 'yahoossp', + params: { + dcn: '8a969516017a7a396ec539d97f540011', + pos: '8a96958a017a7a57ac375d50c0c700cc', + bidOverride: { + imp: { + video: { + mimes: ['video/mp4', 'javascript/application'], + w: 300, + h: 200, + maxbitrate: 4000, + maxduration: 30, + minduration: 10, + api: [1,2], + delivery: 1, + pos: 1, + playbackmethod: 0, + placement: 1, + linearity: 1, + protocols: [2,5], + startdelay: 0, + rewarded: 0 + } + }, + site: { + page: 'https://yahoossp-bid-adapter.com', + }, + device: { + ip: "1.2.3.4" + } + } + } + }] +}] +``` + +# Optional: Custom Key-Value Pairs +Custom key-value paris can be used for both inventory targeting and reporting. +You must set up key-value pairs in the Yahoo SSP before sending them via the adapter otherwise the Ad Server will not be listening and picking them up. + +Important! Key-value pairs can only contain values of types: String, Number, Array of strings OR Array of numbers + +```javascript +const adUnits = [{ + code: 'key-value-pairs', + mediaTypes: { + video: { + context: 'outstream', + playerSize: [ + [300, 250] + ], + } + }, + bids: [{ + bidder: 'yahoossp', + params: { + dcn: '8a969516017a7a396ec539d97f540011', + pos: '8a96958a017a7a57ac375d50c0c700cc', + kvp: { + key1: 'value', // String + key2: 123456, // Number + key3: ['string1','string2', 'string3'], // Array of strings + key4: [1, 23, 456, 7890] // Array of Numbers + } + } + }] +}] +``` + +# Optional: Custom Cache Time To Live (ttl): +The yahoossp adapter supports passing of "Time To Live" (ttl) that indicates to prebid chache for how long to keep the chaced winning bid alive. Value is Number in seconds and you can enter any number between 1 - 3600 (seconds). +The setting can be defined globally using setConfig or within the adUnit.params. +Global level setConfig overrides adUnit.params. +If no value is being passed default is 300 seconds. +## Global TTL +```javascript +pbjs.setConfig({ + yahoossp: { + ttl: 300 + } +}); + +const adUnits = [{ + code: 'global-ttl', + mediaTypes: { + video: { + context: 'outstream', + playerSize: [ + [300, 250] + ], + } + }, + bids: [{ + bidder: 'yahoossp', + params: { + dcn: '8a969516017a7a396ec539d97f540011', + pos: '8a96958a017a7a57ac375d50c0c700cc', + } + }] +}] +``` + +## Ad Unit Params TTL +```javascript +const adUnits = [{ + code: 'adUnit-ttl', + mediaTypes: { + banner: { + sizes: [[300, 250]], + } + }, + bids: [{ + bidder: 'yahoossp', + params: { + dcn: '8a969516017a7a396ec539d97f540011', + pos: '8a96958a017a7a57ac375d50c0c700cc', + ttl: 300, + } + }] +}] +``` +# Optional: Video Features +## Rewarded video flag +To indicate to Yahoo SSP that this adUnit is a rewarded video you can pass the following in the params.bidOverride.imp.video.rewarded: 1 + +```javascript +const adUnits = [{ + code: 'rewarded-video-adUnit', + mediaTypes: { + video: { + context: 'outstream', + playerSize: [ + [300, 250] + ], + } + }, + bids: [{ + bidder: 'yahoossp', + params: { + dcn: '8a969516017a7a396ec539d97f540011', + pos: '8a96958a017a7a57ac375d50c0c700cc', + bidOverride: { + imp: { + video: { + rewarded: 1 + } + } + } + } + }] +}] +``` + +## Site/App Targeting for "pubId" Inventory Mapping +To target your adUnit explicitly to a specific Site/App Object in Yahoo SSP, you can pass one of the following: +1. params.siteId = External Site ID || Video SSP RTBIS Id (in String format). +2. params.bidOverride.site.id = External Site ID || Video SSP RTBIS Id (in String format). +**Important:** Site override is a only supported when using "pubId" mode. +**Important:** If you are switching from the oneVideo adapter, please make sure to pass inventoryid as a String instead of Integer. + +```javascript +const adUnits = [{ + code: 'pubId-site-targeting-adUnit', + mediaTypes: { + video: { + context: 'outstream', + playerSize: [ + [300, 250] + ], + } + }, + bids: [{ + bidder: 'yahoossp', + params: { + pubId: 'DemoPublisher', + siteId: '1234567'; + } + }] +}] +``` + +## Placement Targeting for "pubId" Inventory Mapping +To target your adUnit explicitly to a specific Placement within a Site/App Object in Yahoo SSP, you can pass the following params.placementId = External Placement ID || Placement Alias + +**Important!** Placement override is a only supported when using "pubId" mode. +**Important!** It is highly recommended that you pass both `siteId` AND `placementId` together to avoid inventory miss matching. + +### Site & Placement override +**Important!** If the placement ID does not reside under the defined Site/App object, the request will not resolve and no response will be sent back from the ad-server. +```javascript +const adUnits = [{ + code: 'pubId-site-targeting-adUnit', + mediaTypes: { + video: { + context: 'outstream', + playerSize: [ + [300, 250] + ], + } + }, + bids: [{ + bidder: 'yahoossp', + params: { + pubId: 'DemoPublisher', + siteId: '1234567', + placementId: 'header-250x300' + } + }] +}] +``` +### Placement only override +**Important!** Using this method is not advised if you have multiple Site/Apps that are broken out of a Run Of Network (RON) Site/App. If the placement ID does not reside under a matching Site/App object, the request will not resolve and no response will be sent back from the ad-server. +```javascript +const adUnits = [{ + code: 'pubId-site-targeting-adUnit', + mediaTypes: { + video: { + context: 'outstream', + playerSize: [ + [300, 250] + ], + } + }, + bids: [{ + bidder: 'yahoossp', + params: { + pubId: 'DemoPublisher', + placementId: 'header-250x300' + } + }] +}] +``` + +# Optional: Legacy override Parameters +This adapter does not support passing legacy overrides via 'bidder.params.ext' since most of the data should be passed using prebid modules (First Party Data, Schain, Price Floors etc.). +If you do not know how to pass a custom parameter that you previously used, please contact us using the information provided above. + +Thanks you, +Yahoo SSP + diff --git a/modules/yieldlabBidAdapter.js b/modules/yieldlabBidAdapter.js index 9578f1aaa25..c2f2b79a3b7 100644 --- a/modules/yieldlabBidAdapter.js +++ b/modules/yieldlabBidAdapter.js @@ -1,8 +1,9 @@ import { _each, isPlainObject, isArray, deepAccess } from '../src/utils.js'; import { registerBidder } from '../src/adapters/bidderFactory.js' import find from 'core-js-pure/features/array/find.js' -import { VIDEO, BANNER } from '../src/mediaTypes.js' +import { VIDEO, BANNER, NATIVE } from '../src/mediaTypes.js' import { Renderer } from '../src/Renderer.js' +import { config } from '../src/config.js'; const ENDPOINT = 'https://ad.yieldlab.net' const BIDDER_CODE = 'yieldlab' @@ -14,7 +15,7 @@ const GVLID = 70 export const spec = { code: BIDDER_CODE, gvlid: GVLID, - supportedMediaTypes: [VIDEO, BANNER], + supportedMediaTypes: [VIDEO, BANNER, NATIVE], isBidRequestValid: function (bid) { if (bid && bid.params && bid.params.adslotId && bid.params.supplyId) { @@ -52,6 +53,11 @@ export const spec = { if (bid.schain && isPlainObject(bid.schain) && Array.isArray(bid.schain.nodes)) { query.schain = createSchainString(bid.schain) } + + const iabContent = getContentObject(bid) + if (iabContent) { + query.iab_content = createIabContentString(iabContent) + } }) if (bidderRequest) { @@ -105,6 +111,7 @@ export const spec = { const gdprApplies = reqParams.gdpr ? '&gdpr=' + reqParams.gdpr : '' const gdprConsent = reqParams.consent ? '&consent=' + reqParams.consent : '' const pvId = matchedBid.pvid !== undefined ? '&pvid=' + matchedBid.pvid : '' + const iabContent = reqParams.iab_content ? '&iab_content=' + reqParams.iab_content : '' const bidResponse = { requestId: bidRequest.bidId, @@ -117,7 +124,7 @@ export const spec = { netRevenue: false, ttl: BID_RESPONSE_TTL_SEC, referrer: '', - ad: ``, + ad: ``, meta: { advertiserDomains: (matchedBid.advertiser) ? matchedBid.advertiser : 'n/a' } @@ -130,7 +137,7 @@ export const spec = { bidResponse.height = playersize[1] } bidResponse.mediaType = VIDEO - bidResponse.vastUrl = `${ENDPOINT}/d/${matchedBid.id}/${bidRequest.params.supplyId}/?ts=${timestamp}${extId}${gdprApplies}${gdprConsent}${pvId}` + bidResponse.vastUrl = `${ENDPOINT}/d/${matchedBid.id}/${bidRequest.params.supplyId}/?ts=${timestamp}${extId}${gdprApplies}${gdprConsent}${pvId}${iabContent}` if (isOutstream(bidRequest)) { const renderer = Renderer.install({ id: bidRequest.bidId, @@ -142,6 +149,27 @@ export const spec = { } } + if (isNative(bidRequest, adType)) { + const url = `${ENDPOINT}/d/${matchedBid.id}/${bidRequest.params.supplyId}/?ts=${timestamp}${extId}${gdprApplies}${gdprConsent}${pvId}` + bidResponse.adUrl = url + bidResponse.mediaType = NATIVE + const nativeImageAssetObj = find(matchedBid.native.assets, e => e.id === 2) + const nativeImageAsset = nativeImageAssetObj ? nativeImageAssetObj.img : {url: '', w: 0, h: 0}; + const nativeTitleAsset = find(matchedBid.native.assets, e => e.id === 1) + const nativeBodyAsset = find(matchedBid.native.assets, e => e.id === 3) + bidResponse.native = { + title: nativeTitleAsset ? nativeTitleAsset.title.text : '', + body: nativeBodyAsset ? nativeBodyAsset.data.value : '', + image: { + url: nativeImageAsset.url, + width: nativeImageAsset.w, + height: nativeImageAsset.h, + }, + clickUrl: matchedBid.native.link.url, + impressionTrackers: matchedBid.native.imptrackers, + }; + } + bidResponses.push(bidResponse) } }) @@ -155,16 +183,26 @@ export const spec = { * @param {String} adtype * @returns {Boolean} */ -function isVideo (format, adtype) { +function isVideo(format, adtype) { return deepAccess(format, 'mediaTypes.video') && adtype.toLowerCase() === 'video' } +/** + * Is this a native format? + * @param {Object} format + * @param {String} adtype + * @returns {Boolean} + */ +function isNative(format, adtype) { + return deepAccess(format, 'mediaTypes.native') && adtype.toLowerCase() === 'native' +} + /** * Is this an outstream context? * @param {Object} format * @returns {Boolean} */ -function isOutstream (format) { +function isOutstream(format) { let context = deepAccess(format, 'mediaTypes.video.context') return (context === 'outstream') } @@ -174,7 +212,7 @@ function isOutstream (format) { * @param {Object} format * @returns {Array} */ -function getPlayerSize (format) { +function getPlayerSize(format) { let playerSize = deepAccess(format, 'mediaTypes.video.playerSize') return (playerSize && isArray(playerSize[0])) ? playerSize[0] : playerSize } @@ -184,7 +222,7 @@ function getPlayerSize (format) { * @param {String} size * @returns {Array} */ -function parseSize (size) { +function parseSize(size) { return size.split('x').map(Number) } @@ -193,7 +231,7 @@ function parseSize (size) { * @param {Array} eids * @returns {String} */ -function createUserIdString (eids) { +function createUserIdString(eids) { let str = [] for (let i = 0; i < eids.length; i++) { str.push(eids[i].source + ':' + eids[i].uids[0].id) @@ -206,12 +244,12 @@ function createUserIdString (eids) { * @param {Object} obj * @returns {String} */ -function createQueryString (obj) { +function createQueryString(obj) { let str = [] for (var p in obj) { if (obj.hasOwnProperty(p)) { let val = obj[p] - if (p !== 'schain') { + if (p !== 'schain' && p !== 'iab_content') { str.push(encodeURIComponent(p) + '=' + encodeURIComponent(val)) } else { str.push(p + '=' + val) @@ -226,7 +264,7 @@ function createQueryString (obj) { * @param {Object} obj * @returns {String} */ -function createTargetingString (obj) { +function createTargetingString(obj) { let str = [] for (var p in obj) { if (obj.hasOwnProperty(p)) { @@ -243,7 +281,7 @@ function createTargetingString (obj) { * @param {Object} schain * @returns {String} */ -function createSchainString (schain) { +function createSchainString(schain) { const ver = schain.ver || '' const complete = (schain.complete === 1 || schain.complete === 0) ? schain.complete : '' const keys = ['asi', 'sid', 'hp', 'rid', 'name', 'domain', 'ext'] @@ -253,6 +291,44 @@ function createSchainString (schain) { return `${ver},${complete}${nodesString}` } +/** + * Get content object from bid request + * First get content from bidder params; + * If not provided in bidder params, get from first party data under 'ortb2.site.content' or 'ortb2.app.content' + * @param {Object} bid + * @returns {Object} + */ +function getContentObject(bid) { + if (bid.params.iabContent && isPlainObject(bid.params.iabContent)) { + return bid.params.iabContent + } + + const globalContent = config.getConfig('ortb2.site') ? config.getConfig('ortb2.site.content') + : config.getConfig('ortb2.app.content') + if (globalContent && isPlainObject(globalContent)) { + return globalContent + } + return undefined +} + +/** + * Creates a string for iab_content object + * @param {Object} iabContent + * @returns {String} + */ +function createIabContentString(iabContent) { + const arrKeys = ['keywords', 'cat'] + let str = [] + for (let key in iabContent) { + if (iabContent.hasOwnProperty(key)) { + const value = (arrKeys.indexOf(key) !== -1 && Array.isArray(iabContent[key])) + ? iabContent[key].map(node => encodeURIComponent(node)).join('|') : encodeURIComponent(iabContent[key]) + str.push(''.concat(key, ':', value)) + } + } + return encodeURIComponent(str.join(',')) +} + /** * Encodes URI Component with exlamation mark included. Needed for schain object. * @param {String} str diff --git a/modules/yieldlabBidAdapter.md b/modules/yieldlabBidAdapter.md index a7a3f2715dc..1f52e26f5c7 100644 --- a/modules/yieldlabBidAdapter.md +++ b/modules/yieldlabBidAdapter.md @@ -11,38 +11,96 @@ Maintainer: solutions@yieldlab.de Module that connects to Yieldlab's demand sources # Test Parameters + +```javascript +const adUnits = [ + { + code: 'banner', + sizes: [ [ 728, 90 ] ], + bids: [{ + bidder: 'yieldlab', + params: { + adslotId: '5220336', + supplyId: '1381604', + targeting: { + key1: 'value1', + key2: 'value2' + }, + extId: 'abc', + iabContent: { + id: 'some_id', + episode: '1', + title: 'some title', + series: 'some series', + season: 's1', + artist: 'John Doe', + genre: 'some genre', + isrc: 'CC-XXX-YY-NNNNN', + url: 'http://foo_url.de', + cat: [ 'IAB1-1', 'IAB1-2', 'IAB2-10' ], + context: '7', + keywords: ['k1', 'k2'], + live: '0' + } + } + }] + }, + { + code: 'video', + sizes: [ [ 640, 480 ] ], + mediaTypes: { + video: { + context: 'instream' // or 'outstream' + } + }, + bids: [{ + bidder: 'yieldlab', + params: { + adslotId: '5220339', + supplyId: '1381604' + } + }] + }, + { + code: 'native', + mediaTypes: { + native: { + // native config + } + }, + bids: [{ + bidder: 'yieldlab', + params: { + adslotId: '5220339', + supplyId: '1381604' + } + }] + } +]; ``` - var adUnits = [ - { - code: "banner", - sizes: [[728, 90]], - bids: [{ - bidder: "yieldlab", - params: { - adslotId: "5220336", - supplyId: "1381604", - targeting: { - key1: "value1", - key2: "value2" - }, - extId: "abc" - } - }] - }, { - code: "video", - sizes: [[640, 480]], - mediaTypes: { - video: { - context: "instream" // or "outstream" - } - }, - bids: [{ - bidder: "yieldlab", - params: { - adslotId: "5220339", - supplyId: "1381604" - } - }] - } - ]; + +# Multi-Format Setup + +A general overview of how to set up multi-format ads can be found in the offical Prebid.js docs. See: [show multi-format ads](https://docs.prebid.org/dev-docs/show-multi-format-ads.html) + +When setting up multi-format ads with Yieldlab make sure to always add at least one eligible Adslot per given media type in the ad unit configuration. + +```javascript +const adUnit = { + code: 'multi-format-adslot', + mediaTypes: { + banner: { + sizes: [ [ 728, 90 ] ] + }, + native: { + // native config + } + }, + bids: [ + // banner Adslot + { bidder: 'yieldlab', params: { adslotId: '1234', supplyId: '42' } }, + // native Adslot + { bidder: 'yieldlab', params: { adslotId: '2345', supplyId: '42' } } + ] +}; ``` diff --git a/modules/yieldmoBidAdapter.js b/modules/yieldmoBidAdapter.js index ed73a541b8b..6642d1e9b83 100644 --- a/modules/yieldmoBidAdapter.js +++ b/modules/yieldmoBidAdapter.js @@ -55,13 +55,8 @@ export const spec = { p: [], page_url: bidderRequest.refererInfo.referer, bust: new Date().getTime().toString(), - pr: (LOCAL_WINDOW.document && LOCAL_WINDOW.document.referrer) || '', - scrd: LOCAL_WINDOW.devicePixelRatio || 0, dnt: getDNT(), description: getPageDescription(), - title: LOCAL_WINDOW.document.title || '', - w: LOCAL_WINDOW.innerWidth, - h: LOCAL_WINDOW.innerHeight, userConsent: JSON.stringify({ // case of undefined, stringify will remove param gdprApplies: deepAccess(bidderRequest, 'gdprConsent.gdprApplies') || '', @@ -70,6 +65,14 @@ export const spec = { us_privacy: deepAccess(bidderRequest, 'uspConsent') || '' }; + if (canAccessTopWindow()) { + serverRequest.pr = (LOCAL_WINDOW.document && LOCAL_WINDOW.document.referrer) || ''; + serverRequest.scrd = LOCAL_WINDOW.devicePixelRatio || 0; + serverRequest.title = LOCAL_WINDOW.document.title || ''; + serverRequest.w = LOCAL_WINDOW.innerWidth; + serverRequest.h = LOCAL_WINDOW.innerHeight; + } + const mtp = window.navigator.maxTouchPoints; if (mtp) { serverRequest.mtp = mtp; @@ -609,3 +612,18 @@ function getEids(bidRequest) { return createEidsArray(bidRequest.userId) || []; } }; + +/** + * Check if top window can be accessed + * + * @return {boolean} true if can access top window otherwise false + */ +function canAccessTopWindow() { + try { + if (getWindowTop().location.href) { + return true; + } + } catch (error) { + return false; + } +} diff --git a/modules/yieldmoSyntheticInventoryModule.js b/modules/yieldmoSyntheticInventoryModule.js new file mode 100644 index 00000000000..bca778a7b43 --- /dev/null +++ b/modules/yieldmoSyntheticInventoryModule.js @@ -0,0 +1,46 @@ +import { config } from '../src/config.js'; +import { isGptPubadsDefined } from '../src/utils.js'; + +export const MODULE_NAME = 'Yieldmo Synthetic Inventory Module'; + +export function init(config) { + validateConfig(config); + + if (!isGptPubadsDefined()) { + window.googletag = window.googletag || {}; + window.googletag.cmd = window.googletag.cmd || []; + } + + const googletag = window.googletag; + const containerName = 'ym_sim_container_' + config.placementId; + + googletag.cmd.push(() => { + if (window.document.body) { + googletagCmd(config, containerName, googletag); + } else { + window.document.addEventListener('DOMContentLoaded', () => googletagCmd(config, containerName, googletag)); + } + }); +} + +export function validateConfig(config) { + if (!('placementId' in config)) { + throw new Error(`${MODULE_NAME}: placementId required`); + } + if (!('adUnitPath' in config)) { + throw new Error(`${MODULE_NAME}: adUnitPath required`); + } +} + +function googletagCmd(config, containerName, googletag) { + const gamContainer = window.document.createElement('div'); + gamContainer.id = containerName; + window.document.body.appendChild(gamContainer); + googletag.defineSlot(config.adUnitPath, [1, 1], containerName) + .addService(googletag.pubads()) + .setTargeting('ym_sim_p_id', config.placementId); + googletag.enableServices(); + googletag.display(containerName); +} + +config.getConfig('yieldmo_synthetic_inventory', config => init(config.yieldmo_synthetic_inventory)); diff --git a/modules/yieldmoSyntheticInventoryModule.md b/modules/yieldmoSyntheticInventoryModule.md new file mode 100644 index 00000000000..dd6f0acf884 --- /dev/null +++ b/modules/yieldmoSyntheticInventoryModule.md @@ -0,0 +1,68 @@ +# Yieldmo Synthetic Inventory Module + +## Overview + +This module enables publishers to set up Yieldmo Synthetic Outstream ads on their pages. + +If publishers will enable this module and provide placementId and Google Ad Manager ad unit path, this module will create a placement on the page and inject Yieldmo SDK into this placement. Publisher will then need to get a placement id from their Yieldmo account manager (accounts email) and setup corresponding ad units on the GAM ad server. + +## Integration + +Build the Yieldmo Synthetic Inventory Module into the Prebid.js package with: + +``` +gulp build --modules=yieldmoSyntheticInventoryModule,... +``` + +## Module Configuration + +```js +pbjs.que.push(function() { + pbjs.setConfig({ + yieldmo_synthetic_inventory: { + placementId: '1234567890', + adUnitPath: '/1234567/ad_unit_name_used_in_gam' + } + }); +}); +``` + +### Configuration Parameters + +|Name |Scope |Description | Example| Type +| :------------ | :------------ | :------------ | :------------ | :------------ | +|placementId | required | Yieldmo placement ID | '1234567890' | string +|adUnitPath | required | Google Ad Manager ad unit path | '/6355419/ad_unit_name_used_in_gam' | string + +### How to get ad unit path + +Ad unit path follows the format /network-code/[parent-ad-unit-code/.../]ad-unit-code, where: + +- network-code is a unique identifier for the Ad Manager network the ad unit belongs to +- parent-ad-unit-code are the codes of all parent ad units (only applies to non-top level ad units) +- ad-unit-code is the code for the ad unit to be displayed + +Note that all ad unit codes included in the ad unit path must adhere to the [formatting rules](https://support.google.com/admanager/answer/1628457#ad-unit-codes) specified by Ad Manager. + +Another and probably the easiest way to get an ad unit path is to get it from the google ad manager ad unit document header generated tag: + +```js +googletag.defineSlot('/1234567/ad_unit_name_used_in_gam', [1, 1], 'ad-container-id').addService(googletag.pubads()); +``` + +### How to get Yieldmo placement id + +Please reach out to your Yieldmo account's person or email to support@yieldmo.com + +### Google Ad Manager setup + +Yieldmo Synthetic Inventory Module is designed to be used along with Google Ad Manager. GAM should be set as usual, but there are a few requirements: + +- Ad unit size should be 1x1 +- Creative should NOT be served into a SafeFrame and also should have 1x1 size +- Synthetic Inventory Universal Tag should be used as 3rd party creative code +### Synthetic Inventory Universal Tag + +```js +
+``` \ No newline at end of file diff --git a/modules/yieldoneBidAdapter.js b/modules/yieldoneBidAdapter.js index b12f314da2e..fe5a63cab51 100644 --- a/modules/yieldoneBidAdapter.js +++ b/modules/yieldoneBidAdapter.js @@ -1,4 +1,4 @@ -import { deepAccess, isEmpty, parseSizesInput, isStr, logWarn } from '../src/utils.js'; +import {deepAccess, isEmpty, isStr, logWarn, parseSizesInput} from '../src/utils.js'; import {config} from '../src/config.js'; import {registerBidder} from '../src/adapters/bidderFactory.js'; import { Renderer } from '../src/Renderer.js'; @@ -11,6 +11,8 @@ const VIDEO_PLAYER_URL = 'https://img.ak.impact-ad.jp/ic/pone/ivt/firstview/js/d const CMER_PLAYER_URL = 'https://an.cmertv.com/hb/renderer/cmertv-video-yone-prebid.min.js'; const VIEWABLE_PERCENTAGE_URL = 'https://img.ak.impact-ad.jp/ic/pone/ivt/firstview/js/prebid-adformat-config.js'; +const DEFAULT_VIDEO_SIZE = {w: 640, h: 360}; + export const spec = { code: BIDDER_CODE, aliases: ['y1'], @@ -40,16 +42,18 @@ export const spec = { t: 'i' }; - const videoMediaType = deepAccess(bidRequest, 'mediaTypes.video'); - if ((isEmpty(bidRequest.mediaType) && isEmpty(bidRequest.mediaTypes)) || - (bidRequest.mediaType === BANNER || (bidRequest.mediaTypes && bidRequest.mediaTypes[BANNER]))) { - const sizes = deepAccess(bidRequest, 'mediaTypes.banner.sizes') || bidRequest.sizes; - payload.sz = parseSizesInput(sizes).join(','); - } else if (bidRequest.mediaType === VIDEO || videoMediaType) { - const sizes = deepAccess(bidRequest, 'mediaTypes.video.playerSize') || bidRequest.sizes; - const size = parseSizesInput(sizes)[0]; - payload.w = size.split('x')[0]; - payload.h = size.split('x')[1]; + const mediaType = getMediaType(bidRequest); + switch (mediaType) { + case BANNER: + payload.sz = getBannerSizes(bidRequest); + break; + case VIDEO: + const videoSize = getVideoSize(bidRequest); + payload.w = videoSize.w; + payload.h = videoSize.h; + break; + default: + break; } // LiveRampID @@ -167,6 +171,106 @@ export const spec = { }, } +/** + * NOTE: server side does not yet support multiple formats. + * @param {Object} bidRequest - + * @param {boolean} [enabledOldFormat = true] - default: `true`. + * @return {string|null} - `"banner"` or `"video"` or `null`. + */ +function getMediaType(bidRequest, enabledOldFormat = true) { + let hasBannerType = Boolean(deepAccess(bidRequest, 'mediaTypes.banner')); + let hasVideoType = Boolean(deepAccess(bidRequest, 'mediaTypes.video')); + + if (enabledOldFormat) { + hasBannerType = hasBannerType || bidRequest.mediaType === BANNER || + (isEmpty(bidRequest.mediaTypes) && isEmpty(bidRequest.mediaType)); + hasVideoType = hasVideoType || bidRequest.mediaType === VIDEO; + } + + if (hasBannerType && hasVideoType) { + const playerParams = deepAccess(bidRequest, 'params.playerParams') + if (playerParams) { + return VIDEO; + } else { + return BANNER; + } + } else if (hasBannerType) { + return BANNER; + } else if (hasVideoType) { + return VIDEO; + } + + return null; +} + +/** + * NOTE: + * If `mediaTypes.banner` exists, then `mediaTypes.banner.sizes` must also exist. + * The reason for this is that Prebid.js will perform the verification and + * if `mediaTypes.banner.sizes` is inappropriate, it will delete the entire `mediaTypes.banner`. + * @param {Object} bidRequest - + * @param {Object} bidRequest.banner - + * @param {Array} bidRequest.banner.sizes - + * @param {boolean} [enabledOldFormat = true] - default: `true`. + * @return {string} - strings like `"300x250"` or `"300x250,728x90"`. + */ +function getBannerSizes(bidRequest, enabledOldFormat = true) { + let sizes = deepAccess(bidRequest, 'mediaTypes.banner.sizes'); + + if (enabledOldFormat) { + sizes = sizes || bidRequest.sizes; + } + + return parseSizesInput(sizes).join(','); +} + +/** + * @param {Object} bidRequest - + * @param {boolean} [enabledOldFormat = true] - default: `true`. + * @param {boolean} [enabledFlux = true] - default: `true`. + * @return {{w: number, h: number}} - + */ +function getVideoSize(bidRequest, enabledOldFormat = true, enabledFlux = true) { + /** + * @param {Array | Array>} sizes - + * @return {{w: number, h: number} | null} - + */ + const _getPlayerSize = (sizes) => { + let result = null; + + const size = parseSizesInput(sizes)[0]; + if (isEmpty(size)) { + return result; + } + + const splited = size.split('x'); + const sizeObj = {w: parseInt(splited[0], 10), h: parseInt(splited[1], 10)}; + const _isValidPlayerSize = !(isEmpty(sizeObj)) && (isFinite(sizeObj.w) && isFinite(sizeObj.h)); + if (!_isValidPlayerSize) { + return result; + } + + result = sizeObj; + return result; + } + + let playerSize = _getPlayerSize(deepAccess(bidRequest, 'mediaTypes.video.playerSize')); + + if (enabledOldFormat) { + playerSize = playerSize || _getPlayerSize(bidRequest.sizes); + } + + if (enabledFlux) { + // NOTE: `video.playerSize` in Flux is always [1,1]. + if (playerSize && (playerSize.w === 1 && playerSize.h === 1)) { + // NOTE: `params.playerSize` is a specific object to support `FLUX`. + playerSize = _getPlayerSize(deepAccess(bidRequest, 'params.playerSize')); + } + } + + return playerSize || DEFAULT_VIDEO_SIZE; +} + function newRenderer(response) { const renderer = Renderer.install({ id: response.uid, diff --git a/modules/zetaSspBidAdapter.md b/modules/zetaSspBidAdapter.md index d2950bce6b9..00d8663586c 100644 --- a/modules/zetaSspBidAdapter.md +++ b/modules/zetaSspBidAdapter.md @@ -10,7 +10,7 @@ Maintainer: miakovlev@zetaglobal.com Module that connects to Zeta's SSP -# Test Parameters +# Banner Ad Unit: For Publishers ``` var adUnits = [ { @@ -40,3 +40,35 @@ Module that connects to Zeta's SSP } ]; ``` + +# Video Ad Unit: For Publishers +``` + var adUnits = [ + { + mediaTypes: { + video: { + playerSize: [640, 480], + context: 'outstream' + } + }, + bids: [ + { + bidder: 'zeta_global_ssp', + bidId: 12345, + params: { + placement: 12345, + user: { + uid: 12345, + buyeruid: 12345 + }, + tags: { + someTag: 123, + sid: 'publisherId' + }, + test: 1 + } + } + ] + } + ]; +``` diff --git a/modules/zeta_global_sspAnalyticsAdapter.js b/modules/zeta_global_sspAnalyticsAdapter.js new file mode 100644 index 00000000000..906e6e19cc2 --- /dev/null +++ b/modules/zeta_global_sspAnalyticsAdapter.js @@ -0,0 +1,97 @@ +import { logInfo, logError } from '../src/utils.js'; +import { ajax } from '../src/ajax.js'; +import adapterManager from '../src/adapterManager.js'; +import CONSTANTS from '../src/constants.json'; + +import adapter from '../src/AnalyticsAdapter.js'; + +const ZETA_GVL_ID = 833; +const ADAPTER_CODE = 'zeta_global_ssp'; +const BASE_URL = 'https://ssp.disqus.com/prebid/event'; +const LOG_PREFIX = 'ZetaGlobalSsp-Analytics: '; + +/// /////////// VARIABLES //////////////////////////////////// + +let publisherId; // int + +/// /////////// HELPER FUNCTIONS ///////////////////////////// + +function sendEvent(eventType, event) { + ajax( + BASE_URL + '/' + eventType, + null, + JSON.stringify(event) + ); +} + +/// /////////// ADAPTER EVENT HANDLER FUNCTIONS ////////////// + +function adRenderSucceededHandler(args) { + let eventType = CONSTANTS.EVENTS.AD_RENDER_SUCCEEDED + logInfo(LOG_PREFIX + 'handle ' + eventType + ' event'); + + sendEvent(eventType, args); +} + +function auctionEndHandler(args) { + let eventType = CONSTANTS.EVENTS.AUCTION_END; + logInfo(LOG_PREFIX + 'handle ' + eventType + ' event'); + + sendEvent(eventType, args); +} + +/// /////////// ADAPTER DEFINITION /////////////////////////// + +let baseAdapter = adapter({ analyticsType: 'endpoint' }); +let zetaAdapter = Object.assign({}, baseAdapter, { + + enableAnalytics(config = {}) { + let error = false; + + if (typeof config.options === 'object') { + if (config.options.sid) { + publisherId = Number(config.options.sid); + } + } else { + logError(LOG_PREFIX + 'Config not found'); + error = true; + } + + if (!publisherId) { + logError(LOG_PREFIX + 'Missing sid (publisher id)'); + error = true; + } + + if (error) { + logError(LOG_PREFIX + 'Analytics is disabled due to error(s)'); + } else { + baseAdapter.enableAnalytics.call(this, config); + } + }, + + disableAnalytics() { + publisherId = undefined; + baseAdapter.disableAnalytics.apply(this, arguments); + }, + + track({ eventType, args }) { + switch (eventType) { + case CONSTANTS.EVENTS.AD_RENDER_SUCCEEDED: + adRenderSucceededHandler(args); + break; + case CONSTANTS.EVENTS.AUCTION_END: + auctionEndHandler(args); + break; + } + } +}); + +/// /////////// ADAPTER REGISTRATION ///////////////////////// + +adapterManager.registerAnalyticsAdapter({ + adapter: zetaAdapter, + code: ADAPTER_CODE, + gvlid: ZETA_GVL_ID +}); + +export default zetaAdapter; diff --git a/modules/zeta_global_sspAnalyticsAdapter.md b/modules/zeta_global_sspAnalyticsAdapter.md new file mode 100644 index 00000000000..d586d0069b1 --- /dev/null +++ b/modules/zeta_global_sspAnalyticsAdapter.md @@ -0,0 +1,24 @@ +# Zeta Global SSP Analytics Adapter + +## Overview + +Module Name: Zeta Global SSP Analytics Adapter\ +Module Type: Analytics Adapter\ +Maintainer: abermanov@zetaglobal.com + +## Description + +Analytics Adapter which sends auctionEnd and adRenderSucceeded events to Zeta Global SSP analytics endpoints + +## How to configure +``` +pbjs.enableAnalytics({ + provider: 'zeta_global_ssp', + options: { + sid: 111, + tags: { + ... + } + } +}); +``` diff --git a/modules/zeta_global_sspBidAdapter.js b/modules/zeta_global_sspBidAdapter.js index f526a50e098..77542252aeb 100644 --- a/modules/zeta_global_sspBidAdapter.js +++ b/modules/zeta_global_sspBidAdapter.js @@ -1,4 +1,4 @@ -import { logWarn, deepSetValue, deepAccess, isArray, isNumber, isBoolean, isStr } from '../src/utils.js'; +import {deepAccess, deepSetValue, isArray, isBoolean, isNumber, isStr, logWarn} from '../src/utils.js'; import {registerBidder} from '../src/adapters/bidderFactory.js'; import {BANNER, VIDEO} from '../src/mediaTypes.js'; import {config} from '../src/config.js'; @@ -11,6 +11,8 @@ const DEFAULT_CUR = 'USD'; const TTL = 200; const NET_REV = true; +const VIDEO_REGEX = new RegExp(/VAST\s+version/); + const DATA_TYPES = { 'NUMBER': 'number', 'STRING': 'string', @@ -161,6 +163,7 @@ export const spec = { advertiserDomains: zetaBid.adomain }; } + provideMediaType(zetaBid, bid); bidResponses.push(bid); }) }) @@ -284,4 +287,22 @@ function isConnectedTV() { return /(smart[-]?tv|hbbtv|appletv|googletv|hdmi|netcast\.tv|viera|nettv|roku|\bdtv\b|sonydtv|inettvbrowser|\btv\b)/i.test(navigator.userAgent); } +function provideMediaType(zetaBid, bid) { + if (zetaBid.ext && zetaBid.ext.bidtype) { + if (zetaBid.ext.bidtype === VIDEO) { + bid.mediaType = VIDEO; + bid.vastXml = bid.ad; + } else { + bid.mediaType = BANNER; + } + } else { + if (VIDEO_REGEX.test(bid.ad)) { + bid.mediaType = VIDEO; + bid.vastXml = bid.ad; + } else { + bid.mediaType = BANNER; + } + } +} + registerBidder(spec); diff --git a/package.json b/package.json index 9c6cb061c36..0f60f1a5c6e 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "prebid.js", - "version": "5.18.0-pre", + "version": "6.9.0-pre", "description": "Header Bidding Management Library", "main": "src/prebid.js", "scripts": { @@ -11,6 +11,11 @@ "type": "git", "url": "https://github.com/prebid/Prebid.js.git" }, + "browserslist": [ + "> 0.25%", + "not IE 11", + "not op_mini all" + ], "keywords": [ "advertising", "auction", diff --git a/plugins/pbjsGlobals.js b/plugins/pbjsGlobals.js index bf3c9033ee6..73912d8126e 100644 --- a/plugins/pbjsGlobals.js +++ b/plugins/pbjsGlobals.js @@ -1,11 +1,13 @@ let t = require('@babel/core').types; let prebid = require('../package.json'); +const path = require('path'); module.exports = function(api, options) { + const pbGlobal = options.globalVarName || prebid.globalVarName; let replace = { '$prebid.version$': prebid.version, - '$$PREBID_GLOBAL$$': options.globalVarName || prebid.globalVarName, + '$$PREBID_GLOBAL$$': pbGlobal, '$$REPO_AND_VERSION$$': `${prebid.repository.url.split('/')[3]}_prebid_${prebid.version}` }; @@ -13,8 +15,33 @@ module.exports = function(api, options) { '$$REPO_AND_VERSION$$' ]; + const PREBID_ROOT = path.resolve(__dirname, '..'); + + function getModuleName(filename) { + const modPath = path.parse(path.relative(PREBID_ROOT, filename)); + if (modPath.ext.toLowerCase() !== '.js') { + return null; + } + if (modPath.dir === 'modules') { + // modules/moduleName.js -> moduleName + return modPath.name; + } + if (modPath.name.toLowerCase() === 'index' && path.dirname(modPath.dir) === 'modules') { + // modules/moduleName/index.js -> moduleName + return path.basename(modPath.dir); + } + return null; + } + return { visitor: { + Program(path, state) { + const modName = getModuleName(state.filename); + if (modName != null) { + // append "registration" of module file to $$PREBID_GLOBAL$$.installedModules + path.node.body.push(...api.parse(`window.${pbGlobal}.installedModules.push('${modName}');`).program.body); + } + }, StringLiteral(path) { Object.keys(replace).forEach(name => { if (path.node.value.includes(name)) { diff --git a/src/adRendering.js b/src/adRendering.js new file mode 100644 index 00000000000..3bd4b0b918f --- /dev/null +++ b/src/adRendering.js @@ -0,0 +1,38 @@ +import {logError} from './utils.js'; +import events from './events.js'; +import CONSTANTS from './constants.json'; + +const {AD_RENDER_FAILED, AD_RENDER_SUCCEEDED} = CONSTANTS.EVENTS; + +/** + * Emit the AD_RENDER_FAILED event. + * + * @param reason one of the values in CONSTANTS.AD_RENDER_FAILED_REASON + * @param message failure description + * @param bid? bid response object that failed to render + * @param id? adId that failed to render + */ +export function emitAdRenderFail({ reason, message, bid, id }) { + const data = { reason, message }; + if (bid) data.bid = bid; + if (id) data.adId = id; + + logError(message); + events.emit(AD_RENDER_FAILED, data); +} + +/** + * Emit the AD_RENDER_SUCCEEDED event. + * + * @param doc document object that was used to `.write` the ad. Should be `null` if unavailable (e.g. for documents in + * a cross-origin frame). + * @param bid bid response object for the ad that was rendered + * @param id adId that was rendered. + */ +export function emitAdRenderSucceeded({ doc, bid, id }) { + const data = { doc }; + if (bid) data.bid = bid; + if (id) data.adId = id; + + events.emit(AD_RENDER_SUCCEEDED, data); +} diff --git a/src/adapterManager.js b/src/adapterManager.js index 9a3d05a4321..9a041543cf8 100644 --- a/src/adapterManager.js +++ b/src/adapterManager.js @@ -262,14 +262,16 @@ adapterManager.makeBidRequests = hook('sync', function (adUnits, auctionStart, a } let adUnitsS2SCopy = getAdUnitCopyForPrebidServer(adUnits, s2sConfig); - let tid = generateUUID(); + + // uniquePbsTid is so we know which server to send which bids to during the callBids function + let uniquePbsTid = generateUUID(); adaptersServerSide.forEach(bidderCode => { const bidderRequestId = getUniqueIdentifierStr(); const bidderRequest = { bidderCode, auctionId, bidderRequestId, - tid, + uniquePbsTid, bids: hookedGetBids({bidderCode, auctionId, bidderRequestId, 'adUnits': deepClone(adUnitsS2SCopy), labels, src: CONSTANTS.S2S.SRC}), auctionStart: auctionStart, timeout: s2sConfig.timeout, @@ -350,7 +352,7 @@ adapterManager.callBids = (adUnits, bidRequests, addBidResponse, doneCb, request serverBidRequests.forEach(serverBidRequest => { var index = -1; for (var i = 0; i < uniqueServerBidRequests.length; ++i) { - if (serverBidRequest.tid === uniqueServerBidRequests[i].tid) { + if (serverBidRequest.uniquePbsTid === uniqueServerBidRequests[i].uniquePbsTid) { index = i; break; } @@ -360,7 +362,10 @@ adapterManager.callBids = (adUnits, bidRequests, addBidResponse, doneCb, request } }); - let counter = 0 + let counter = 0; + + // $.source.tid MUST be a unique UUID and also THE SAME between all PBS Requests for a given Auction + const sourceTid = generateUUID(); _s2sConfigs.forEach((s2sConfig) => { if (s2sConfig && uniqueServerBidRequests[counter] && includes(s2sConfig.bidders, uniqueServerBidRequests[counter].bidderCode)) { // s2s should get the same client side timeout as other client side requests. @@ -370,13 +375,13 @@ adapterManager.callBids = (adUnits, bidRequests, addBidResponse, doneCb, request } : undefined); let adaptersServerSide = s2sConfig.bidders; const s2sAdapter = _bidderRegistry[s2sConfig.adapter]; - let tid = uniqueServerBidRequests[counter].tid; + let uniquePbsTid = uniqueServerBidRequests[counter].uniquePbsTid; let adUnitsS2SCopy = uniqueServerBidRequests[counter].adUnitsS2SCopy; - let uniqueServerRequests = serverBidRequests.filter(serverBidRequest => serverBidRequest.tid === tid) + let uniqueServerRequests = serverBidRequests.filter(serverBidRequest => serverBidRequest.uniquePbsTid === uniquePbsTid); if (s2sAdapter) { - let s2sBidRequest = {tid, 'ad_units': adUnitsS2SCopy, s2sConfig}; + let s2sBidRequest = {tid: sourceTid, 'ad_units': adUnitsS2SCopy, s2sConfig}; if (s2sBidRequest.ad_units.length) { let doneCbs = uniqueServerRequests.map(bidRequest => { bidRequest.start = timestamp(); @@ -391,7 +396,8 @@ adapterManager.callBids = (adUnits, bidRequests, addBidResponse, doneCb, request // fire BID_REQUESTED event for each s2s bidRequest uniqueServerRequests.forEach(bidRequest => { - events.emit(CONSTANTS.EVENTS.BID_REQUESTED, bidRequest); + // add the new sourceTid + events.emit(CONSTANTS.EVENTS.BID_REQUESTED, {...bidRequest, tid: sourceTid}); }); // make bid requests @@ -621,4 +627,9 @@ adapterManager.callBidViewableBidder = function(bidder, bid) { tryCallBidderMethod(bidder, 'onBidViewable', bid); }; +adapterManager.callBidderError = function(bidder, error, bidderRequest) { + const param = { error, bidderRequest }; + tryCallBidderMethod(bidder, 'onBidderError', param); +}; + export default adapterManager; diff --git a/src/adapters/bidderFactory.js b/src/adapters/bidderFactory.js index fa0ad36e7ff..fc736926dd7 100644 --- a/src/adapters/bidderFactory.js +++ b/src/adapters/bidderFactory.js @@ -335,10 +335,11 @@ export function newBidder(spec) { // If the server responds with an error, there's not much we can do. Log it, and make sure to // call onResponse() so that we're one step closer to calling done(). - function onFailure(err) { + function onFailure(errorMessage, error) { onTimelyResponse(spec.code); - - logError(`Server call for ${spec.code} failed: ${err}. Continuing without bids.`); + adapterManager.callBidderError(spec.code, error, bidderRequest) + events.emit(CONSTANTS.EVENTS.BIDDER_ERROR, { error, bidderRequest }); + logError(`Server call for ${spec.code} failed: ${errorMessage} ${error.status}. Continuing without bids.`); onResponse(); } } diff --git a/src/auction.js b/src/auction.js index 498f08d6c73..2172a865b62 100644 --- a/src/auction.js +++ b/src/auction.js @@ -59,7 +59,7 @@ import { flatten, timestamp, adUnitsFilter, deepAccess, getBidRequest, getValue, parseUrl, generateUUID, - logMessage, bind, logError, logInfo, logWarn, isEmpty, _each, isFn, isEmptyStr + logMessage, bind, logError, logInfo, logWarn, isEmpty, _each, isFn, isEmptyStr, isAllowZeroCpmBidsEnabled } from './utils.js'; import { getPriceBucketString } from './cpmBucketManager.js'; import { getNativeTargeting } from './native.js'; @@ -250,10 +250,7 @@ export function newAuction({adUnits, adUnitCodes, callback, cbTimeout, labels, a let callbacks = auctionCallbacks(auctionDone, this); adapterManager.callBids(_adUnits, bidRequests, function(...args) { - addBidResponse.apply({ - dispatch: callbacks.addBidResponse, - bidderRequest: this - }, args) + callbacks.addBidResponse.apply(this, args); }, callbacks.adapterDone, { request(source, origin) { increment(outstandingRequests, origin); @@ -344,6 +341,7 @@ export function newAuction({adUnits, adUnitCodes, callback, cbTimeout, labels, a addWinningBid, setBidTargeting, getWinningBids: () => _winningBids, + getAuctionStart: () => _auctionStart, getTimeout: () => _timeout, getAuctionId: () => _auctionId, getAuctionStatus: () => _auctionStatus, @@ -355,8 +353,12 @@ export function newAuction({adUnits, adUnitCodes, callback, cbTimeout, labels, a } } -export const addBidResponse = hook('async', function(adUnitCode, bid) { - this.dispatch.call(this.bidderRequest, adUnitCode, bid); +/** + * addBidResponse may return a Promise; if it does, the auction will attempt to + * wait for it to complete (successfully or not) before closing. + */ +export const addBidResponse = hook('sync', function(adUnitCode, bid) { + return this.dispatch.call(this.bidderRequest, adUnitCode, bid); }, 'addBidResponse'); export const addBidderRequests = hook('sync', function(bidderRequests) { @@ -374,6 +376,32 @@ export function auctionCallbacks(auctionDone, auctionInstance) { let allAdapterCalledDone = false; let bidderRequestsDone = new Set(); let bidResponseMap = {}; + const ready = {}; + + function waitFor(bidderRequest, result) { + const id = bidderRequest.bidderRequestId; + if (ready[id] == null) { + ready[id] = Promise.resolve(); + } + ready[id] = ready[id].then(() => Promise.resolve(result).catch(() => {})) + } + + function guard(bidderRequest, fn) { + let timeout = bidderRequest.timeout; + if (timeout == null || timeout > auctionInstance.getTimeout()) { + timeout = auctionInstance.getTimeout(); + } + const timeRemaining = auctionInstance.getAuctionStart() + timeout - Date.now(); + const wait = ready[bidderRequest.bidderRequestId]; + if (wait != null && timeRemaining > 0) { + Promise.race([ + new Promise((resolve) => setTimeout(resolve, timeRemaining)), + wait + ]).then(fn); + } else { + fn(); + } + } function afterBidAdded() { outstandingBidsAdded--; @@ -382,7 +410,7 @@ export function auctionCallbacks(auctionDone, auctionInstance) { } } - function addBidResponse(adUnitCode, bid) { + function handleBidResponse(adUnitCode, bid) { let bidderRequest = this; bidResponseMap[bid.requestId] = true; @@ -429,8 +457,15 @@ export function auctionCallbacks(auctionDone, auctionInstance) { } return { - addBidResponse, - adapterDone + addBidResponse: function (...args) { + waitFor(this, addBidResponse.apply({ + dispatch: handleBidResponse, + bidderRequest: this + }, args)); + }, + adapterDone: function () { + guard(this, adapterDone.bind(this)) + } } } @@ -567,8 +602,9 @@ function getPreparedBidForAuction({adUnitCode, bid, bidderRequest, auctionId}) { function setupBidTargeting(bidObject, bidderRequest) { let keyValues; - if (bidObject.bidderCode && (bidObject.cpm > 0 || bidObject.dealId)) { - let bidReq = find(bidderRequest.bids, bid => bid.adUnitCode === bidObject.adUnitCode); + const cpmCheck = (isAllowZeroCpmBidsEnabled(bidObject.bidderCode)) ? bidObject.cpm >= 0 : bidObject.cpm > 0; + if (bidObject.bidderCode && (cpmCheck || bidObject.dealId)) { + let bidReq = find(bidderRequest.bids, bid => bid.adUnitCode === bidObject.adUnitCode && bid.bidId === bidObject.requestId); keyValues = getKeyValueTargetingPairs(bidObject.bidderCode, bidObject, bidReq); } @@ -613,7 +649,8 @@ export const getPriceGranularity = (mediaType, bidReq) => { * @returns {function} */ export const getPriceByGranularity = (granularity) => { - return (bid) => { + return (bid, bidReq) => { + granularity = granularity || getPriceGranularity(bid.mediaType, bidReq); if (granularity === CONSTANTS.GRANULARITY_OPTIONS.AUTO) { return bid.pbAg; } else if (granularity === CONSTANTS.GRANULARITY_OPTIONS.DENSE) { @@ -646,14 +683,14 @@ export const getAdvertiserDomain = () => { * @param {BidRequest} bidReq * @returns {*} */ -export function getStandardBidderSettings(mediaType, bidderCode, bidReq) { +export function getStandardBidderSettings(mediaType, bidderCode) { // factory for key value objs function createKeyVal(key, value) { return { key, val: (typeof value === 'function') - ? function (bidResponse) { - return value(bidResponse); + ? function (bidResponse, bidReq) { + return value(bidResponse, bidReq); } : function (bidResponse) { return getValue(bidResponse, value); @@ -661,7 +698,6 @@ export function getStandardBidderSettings(mediaType, bidderCode, bidReq) { }; } const TARGETING_KEYS = CONSTANTS.TARGETING_KEYS; - const granularity = getPriceGranularity(mediaType, bidReq); let bidderSettings = $$PREBID_GLOBAL$$.bidderSettings; if (!bidderSettings[CONSTANTS.JSON_MAPPING.BD_SETTING_STANDARD]) { @@ -671,7 +707,7 @@ export function getStandardBidderSettings(mediaType, bidderCode, bidReq) { bidderSettings[CONSTANTS.JSON_MAPPING.BD_SETTING_STANDARD][CONSTANTS.JSON_MAPPING.ADSERVER_TARGETING] = [ createKeyVal(TARGETING_KEYS.BIDDER, 'bidderCode'), createKeyVal(TARGETING_KEYS.AD_ID, 'adId'), - createKeyVal(TARGETING_KEYS.PRICE_BUCKET, getPriceByGranularity(granularity)), + createKeyVal(TARGETING_KEYS.PRICE_BUCKET, getPriceByGranularity()), createKeyVal(TARGETING_KEYS.SIZE, 'size'), createKeyVal(TARGETING_KEYS.DEAL, 'dealId'), createKeyVal(TARGETING_KEYS.SOURCE, 'source'), @@ -716,12 +752,12 @@ export function getKeyValueTargetingPairs(bidderCode, custBidObj, bidReq) { // 1) set the keys from "standard" setting or from prebid defaults if (bidderSettings) { // initialize default if not set - const standardSettings = getStandardBidderSettings(custBidObj.mediaType, bidderCode, bidReq); - setKeys(keyValues, standardSettings, custBidObj); + const standardSettings = getStandardBidderSettings(custBidObj.mediaType, bidderCode); + setKeys(keyValues, standardSettings, custBidObj, bidReq); // 2) set keys from specific bidder setting override if they exist if (bidderCode && bidderSettings[bidderCode] && bidderSettings[bidderCode][CONSTANTS.JSON_MAPPING.ADSERVER_TARGETING]) { - setKeys(keyValues, bidderSettings[bidderCode], custBidObj); + setKeys(keyValues, bidderSettings[bidderCode], custBidObj, bidReq); custBidObj.sendStandardTargeting = bidderSettings[bidderCode].sendStandardTargeting; } } @@ -734,7 +770,7 @@ export function getKeyValueTargetingPairs(bidderCode, custBidObj, bidReq) { return keyValues; } -function setKeys(keyValues, bidderSettings, custBidObj) { +function setKeys(keyValues, bidderSettings, custBidObj, bidReq) { var targeting = bidderSettings[CONSTANTS.JSON_MAPPING.ADSERVER_TARGETING]; custBidObj.size = custBidObj.getSize(); @@ -743,12 +779,12 @@ function setKeys(keyValues, bidderSettings, custBidObj) { var value = kvPair.val; if (keyValues[key]) { - logWarn('The key: ' + key + ' is getting ovewritten'); + logWarn('The key: ' + key + ' is being overwritten'); } if (isFn(value)) { try { - value = value(custBidObj); + value = value(custBidObj, bidReq); } catch (e) { logError('bidmanager', 'ERROR', e); } diff --git a/src/config.js b/src/config.js index 72d760c5b87..ddf3b79a6af 100644 --- a/src/config.js +++ b/src/config.js @@ -1,6 +1,6 @@ /* * Module for getting and setting Prebid configuration. - */ +*/ /** * @typedef {Object} MediaTypePriceGranularity @@ -580,7 +580,7 @@ export function newConfig() { .forEach(listener => listener.callback(options)); } - function setBidderConfig(config) { + function setBidderConfig(config, mergeFlag = false) { try { check(config); config.bidders.forEach(bidder => { @@ -592,7 +592,8 @@ export function newConfig() { let option = (topic === 'fpd') ? convertFpd(config.config[topic]) : config.config[topic]; if (isPlainObject(option)) { - bidderConfig[bidder][prop] = Object.assign({}, bidderConfig[bidder][prop] || {}, option); + const func = mergeFlag ? mergeDeep : Object.assign; + bidderConfig[bidder][prop] = func({}, bidderConfig[bidder][prop] || {}, option); } else { bidderConfig[bidder][prop] = option; } @@ -601,6 +602,7 @@ export function newConfig() { } catch (e) { logError(e); } + function check(obj) { if (!isPlainObject(obj)) { throw 'setBidderConfig bidder options must be an object'; @@ -614,6 +616,26 @@ export function newConfig() { } } + function mergeConfig(obj) { + if (!isPlainObject(obj)) { + logError('mergeConfig input must be an object'); + return; + } + + const mergedConfig = Object.keys(obj).reduce((accum, key) => { + const prevConf = _getConfig(key)[key] || {}; + accum[key] = mergeDeep(prevConf, obj[key]); + return accum; + }, {}); + + setConfig({ ...mergedConfig }); + return mergedConfig; + } + + function mergeBidderConfig(obj) { + return setBidderConfig(obj, true); + } + /** * Internal functions for core to execute some synchronous code while having an active bidder set. */ @@ -653,12 +675,14 @@ export function newConfig() { getConfig, readConfig, setConfig, + mergeConfig, setDefaults, resetConfig, runWithBidder, callbackWithBidder, setBidderConfig, getBidderConfig, + mergeBidderConfig, convertAdUnitFpd, getLegacyFpd, getLegacyImpFpd diff --git a/src/constants.json b/src/constants.json index 114cedf112b..055ccd8aacb 100644 --- a/src/constants.json +++ b/src/constants.json @@ -32,6 +32,7 @@ "NO_BID": "noBid", "BID_WON": "bidWon", "BIDDER_DONE": "bidderDone", + "BIDDER_ERROR": "bidderError", "SET_TARGETING": "setTargeting", "BEFORE_REQUEST_BIDS": "beforeRequestBids", "BEFORE_BIDDER_HTTP": "beforeBidderHttp", diff --git a/src/hook.js b/src/hook.js index 9050bf2f7dc..2c8e4c7a6e7 100644 --- a/src/hook.js +++ b/src/hook.js @@ -13,14 +13,18 @@ export function setupBeforeHookFnOnce(baseFn, hookFn, priority = 15) { baseFn.before(hookFn, priority); } } +const submoduleInstallMap = {}; -export function module(name, install) { +export function module(name, install, {postInstallAllowed = false} = {}) { hook('async', function (submodules) { submodules.forEach(args => install(...args)); + if (postInstallAllowed) submoduleInstallMap[name] = install; }, name)([]); // will be queued until hook.ready() called in pbjs.processQueue(); } export function submodule(name, ...args) { + const install = submoduleInstallMap[name]; + if (install) return install(...args); getHook(name).before((next, modules) => { modules.push(args); next(modules); diff --git a/src/prebid.js b/src/prebid.js index 855d53d7de0..e27d73c40b0 100644 --- a/src/prebid.js +++ b/src/prebid.js @@ -19,6 +19,7 @@ import { adunitCounter } from './adUnits.js'; import { executeRenderer, isRendererRequired } from './Renderer.js'; import { createBid } from './bidfactory.js'; import { storageCallbacks } from './storageManager.js'; +import { emitAdRenderSucceeded, emitAdRenderFail } from './adRendering.js'; const $$PREBID_GLOBAL$$ = getGlobal(); const CONSTANTS = require('./constants.json'); @@ -27,7 +28,7 @@ const events = require('./events.js'); const { triggerUserSyncs } = userSync; /* private variables */ -const { ADD_AD_UNITS, BID_WON, REQUEST_BIDS, SET_TARGETING, AD_RENDER_FAILED, AD_RENDER_SUCCEEDED, STALE_RENDER } = CONSTANTS.EVENTS; +const { ADD_AD_UNITS, BID_WON, REQUEST_BIDS, SET_TARGETING, STALE_RENDER } = CONSTANTS.EVENTS; const { PREVENT_WRITING_ON_MAIN_DOCUMENT, NO_AD, EXCEPTION, CANNOT_FIND_AD, MISSING_DOC_OR_ADID } = CONSTANTS.AD_RENDER_FAILED_REASON; const eventValidators = { @@ -47,8 +48,7 @@ $$PREBID_GLOBAL$$.libLoaded = true; $$PREBID_GLOBAL$$.version = 'v$prebid.version$'; logInfo('Prebid.js v$prebid.version$ loaded'); -// modules list generated from build -$$PREBID_GLOBAL$$.installedModules = ['v$prebid.modulesList$']; +$$PREBID_GLOBAL$$.installedModules = $$PREBID_GLOBAL$$.installedModules || []; // create adUnit array $$PREBID_GLOBAL$$.adUnits = $$PREBID_GLOBAL$$.adUnits || []; @@ -147,7 +147,7 @@ function validateNativeMediaType(adUnit) { function validateAdUnitPos(adUnit, mediaType) { let pos = deepAccess(adUnit, `mediaTypes.${mediaType}.pos`); - if (!pos || !isNumber(pos) || !isFinite(pos)) { + if (!isNumber(pos) || isNaN(pos) || !isFinite(pos)) { let warning = `Value of property 'pos' on ad unit ${adUnit.code} should be of type: Number`; logWarn(warning); @@ -386,27 +386,23 @@ $$PREBID_GLOBAL$$.setTargetingForAst = function (adUnitCodes) { events.emit(SET_TARGETING, targeting.getAllTargeting()); }; -function emitAdRenderFail({ reason, message, bid, id }) { - const data = { reason, message }; - if (bid) data.bid = bid; - if (id) data.adId = id; - - logError(message); - events.emit(AD_RENDER_FAILED, data); -} - -function emitAdRenderSucceeded({ doc, bid, id }) { - const data = { doc }; - if (bid) data.bid = bid; - if (id) data.adId = id; - - events.emit(AD_RENDER_SUCCEEDED, data); +/** + * This function will check for presence of given node in given parent. If not present - will inject it. + * @param {Node} node node, whose existance is in question + * @param {Document} doc document element do look in + * @param {string} tagName tag name to look in + */ +function reinjectNodeIfRemoved(node, doc, tagName) { + const injectionNode = doc.querySelector(tagName); + if (!node.parentNode || node.parentNode !== injectionNode) { + insertElement(node, doc, tagName); + } } /** * This function will render the ad (based on params) in the given iframe document passed through. * Note that doc SHOULD NOT be the parent document page as we can't doc.write() asynchronously - * @param {HTMLDocument} doc document + * @param {Document} doc document * @param {string} id bid id to locate the ad * @alias module:pbjs.renderAd */ @@ -450,10 +446,11 @@ $$PREBID_GLOBAL$$.renderAd = hook('async', function (doc, id, options) { const {height, width, ad, mediaType, adUrl, renderer} = bid; const creativeComment = document.createComment(`Creative ${bid.creativeId} served by ${bid.bidder} Prebid.js Header Bidding`); + insertElement(creativeComment, doc, 'html'); if (isRendererRequired(renderer)) { executeRenderer(renderer, bid); - insertElement(creativeComment, doc, 'html'); + reinjectNodeIfRemoved(creativeComment, doc, 'html'); emitAdRenderSucceeded({ doc, bid, id }); } else if ((doc === document && !inIframe()) || mediaType === 'video') { const message = `Error trying to write ad. Ad render call ad id ${id} was prevented from writing to the main document.`; @@ -472,7 +469,7 @@ $$PREBID_GLOBAL$$.renderAd = hook('async', function (doc, id, options) { doc.write(ad); doc.close(); setRenderSize(doc, width, height); - insertElement(creativeComment, doc, 'html'); + reinjectNodeIfRemoved(creativeComment, doc, 'html'); callBurl(bid); emitAdRenderSucceeded({ doc, bid, id }); } else if (adUrl) { @@ -485,7 +482,7 @@ $$PREBID_GLOBAL$$.renderAd = hook('async', function (doc, id, options) { insertElement(iframe, doc, 'body'); setRenderSize(doc, width, height); - insertElement(creativeComment, doc, 'html'); + reinjectNodeIfRemoved(creativeComment, doc, 'html'); callBurl(bid); emitAdRenderSucceeded({ doc, bid, id }); } else { @@ -902,6 +899,8 @@ $$PREBID_GLOBAL$$.markWinningBidAsUsed = function (markBidRequest) { */ $$PREBID_GLOBAL$$.getConfig = config.getConfig; $$PREBID_GLOBAL$$.readConfig = config.readConfig; +$$PREBID_GLOBAL$$.mergeConfig = config.mergeConfig; +$$PREBID_GLOBAL$$.mergeBidderConfig = config.mergeBidderConfig; /** * Set Prebid config options. diff --git a/src/secureCreatives.js b/src/secureCreatives.js index 60e60688c64..8622274a9d3 100644 --- a/src/secureCreatives.js +++ b/src/secureCreatives.js @@ -4,18 +4,25 @@ */ import events from './events.js'; -import { fireNativeTrackers, getAssetMessage, getAllAssetsMessage } from './native.js'; +import {fireNativeTrackers, getAllAssetsMessage, getAssetMessage} from './native.js'; import constants from './constants.json'; -import { logWarn, replaceAuctionPrice, deepAccess, isGptPubadsDefined, isApnGetTagDefined } from './utils.js'; -import { auctionManager } from './auctionManager.js'; +import {deepAccess, isApnGetTagDefined, isGptPubadsDefined, logError, logWarn, replaceAuctionPrice} from './utils.js'; +import {auctionManager} from './auctionManager.js'; import find from 'core-js-pure/features/array/find.js'; -import { isRendererRequired, executeRenderer } from './Renderer.js'; +import {executeRenderer, isRendererRequired} from './Renderer.js'; import includes from 'core-js-pure/features/array/includes.js'; -import { config } from './config.js'; +import {config} from './config.js'; +import {emitAdRenderFail, emitAdRenderSucceeded} from './adRendering.js'; const BID_WON = constants.EVENTS.BID_WON; const STALE_RENDER = constants.EVENTS.STALE_RENDER; +const HANDLER_MAP = { + 'Prebid Request': handleRenderRequest, + 'Prebid Native': handleNativeRequest, + 'Prebid Event': handleEventRequest, +} + export function listenMessagesFromCreative() { window.addEventListener('message', receiveMessage, false); } @@ -29,53 +36,114 @@ export function receiveMessage(ev) { return; } - if (data && data.adId) { + if (data && data.adId && data.message) { const adObject = find(auctionManager.getBidsReceived(), function (bid) { return bid.adId === data.adId; }); + if (HANDLER_MAP.hasOwnProperty(data.message)) { + HANDLER_MAP[data.message](ev, data, adObject); + } + } +} - if (adObject && data.message === 'Prebid Request') { - if (adObject.status === constants.BID_STATUS.RENDERED) { - logWarn(`Ad id ${adObject.adId} has been rendered before`); - events.emit(STALE_RENDER, adObject); - if (deepAccess(config.getConfig('auctionOptions'), 'suppressStaleRender')) { - return; - } - } +function handleRenderRequest(ev, data, adObject) { + if (adObject == null) { + emitAdRenderFail({ + reason: constants.AD_RENDER_FAILED_REASON.CANNOT_FIND_AD, + message: `Cannot find ad '${data.adId}' for cross-origin render request`, + id: data.adId + }); + return; + } + if (adObject.status === constants.BID_STATUS.RENDERED) { + logWarn(`Ad id ${adObject.adId} has been rendered before`); + events.emit(STALE_RENDER, adObject); + if (deepAccess(config.getConfig('auctionOptions'), 'suppressStaleRender')) { + return; + } + } - _sendAdToCreative(adObject, ev); + try { + _sendAdToCreative(adObject, ev); + } catch (e) { + emitAdRenderFail({ + reason: constants.AD_RENDER_FAILED_REASON.EXCEPTION, + message: e.message, + id: data.adId, + bid: adObject + }); + return; + } - // save winning bids - auctionManager.addWinningBid(adObject); + // save winning bids + auctionManager.addWinningBid(adObject); - events.emit(BID_WON, adObject); - } + events.emit(BID_WON, adObject); +} - // handle this script from native template in an ad server - // window.parent.postMessage(JSON.stringify({ - // message: 'Prebid Native', - // adId: '%%PATTERN:hb_adid%%' - // }), '*'); - if (adObject && data.message === 'Prebid Native') { - if (data.action === 'assetRequest') { - const message = getAssetMessage(data, adObject); - ev.source.postMessage(JSON.stringify(message), ev.origin); +function handleNativeRequest(ev, data, adObject) { + // handle this script from native template in an ad server + // window.parent.postMessage(JSON.stringify({ + // message: 'Prebid Native', + // adId: '%%PATTERN:hb_adid%%' + // }), '*'); + if (adObject == null) { + logError(`Cannot find ad '${data.adId}' for x-origin event request`); + return; + } + switch (data.action) { + case 'assetRequest': + reply(getAssetMessage(data, adObject)); + break; + case 'allAssetRequest': + reply(getAllAssetsMessage(data, adObject)); + break; + case 'resizeNativeHeight': + adObject.height = data.height; + adObject.width = data.width; + resizeRemoteCreative(adObject); + break; + default: + const trackerType = fireNativeTrackers(data, adObject); + if (trackerType === 'click') { return; - } else if (data.action === 'allAssetRequest') { - const message = getAllAssetsMessage(data, adObject); - ev.source.postMessage(JSON.stringify(message), ev.origin); - } else if (data.action === 'resizeNativeHeight') { - adObject.height = data.height; - adObject.width = data.width; - resizeRemoteCreative(adObject); } - - const trackerType = fireNativeTrackers(data, adObject); - if (trackerType === 'click') { return; } - auctionManager.addWinningBid(adObject); events.emit(BID_WON, adObject); - } + } + + function reply(message) { + ev.source.postMessage(JSON.stringify(message), ev.origin); + } +} + +function handleEventRequest(ev, data, adObject) { + if (adObject == null) { + logError(`Cannot find ad '${data.adId}' for x-origin event request`); + return; + } + if (adObject.status !== constants.BID_STATUS.RENDERED) { + logWarn(`Received x-origin event request without corresponding render request for ad '${data.adId}'`); + return; + } + switch (data.event) { + case constants.EVENTS.AD_RENDER_FAILED: + emitAdRenderFail({ + bid: adObject, + id: data.adId, + reason: data.info.reason, + message: data.info.message + }); + break; + case constants.EVENTS.AD_RENDER_SUCCEEDED: + emitAdRenderSucceeded({ + doc: null, + bid: adObject, + id: data.adId + }); + break; + default: + logError(`Received x-origin event request for unsupported event: '${data.event}' (adId: '${data.adId}')`) } } @@ -128,11 +196,12 @@ function resizeRemoteCreative({ adId, adUnitCode, width, height }) { } function getDfpElementId(adId) { - return find(window.googletag.pubads().getSlots(), slot => { + const slot = find(window.googletag.pubads().getSlots(), slot => { return find(slot.getTargetingKeys(), key => { return includes(slot.getTargeting(key), adId); }); - }).getSlotElementId(); + }); + return slot ? slot.getSlotElementId() : null; } function getAstElementId(adUnitCode) { diff --git a/src/targeting.js b/src/targeting.js index 61a9f9a76b7..423e946896e 100644 --- a/src/targeting.js +++ b/src/targeting.js @@ -1,6 +1,6 @@ import { uniques, isGptPubadsDefined, getHighestCpm, getOldestHighestCpmBid, groupBy, isAdUnitCodeMatchingSlot, timestamp, - deepAccess, deepClone, logError, logWarn, logInfo, isFn, isArray, logMessage, isStr + deepAccess, deepClone, logError, logWarn, logInfo, isFn, isArray, logMessage, isStr, isAllowZeroCpmBidsEnabled } from './utils.js'; import { config } from './config.js'; import { NATIVE_TARGETING_KEYS } from './native.js'; @@ -18,6 +18,10 @@ var pbTargetingKeys = []; const MAX_DFP_KEYLENGTH = 20; const TTL_BUFFER = 1000; +const CFG_ALLOW_TARGETING_KEYS = `targetingControls.allowTargetingKeys`; +const CFG_ADD_TARGETING_KEYS = `targetingControls.addTargetingKeys`; +const TARGETING_KEY_CONFIGURATION_ERROR_MSG = `Only one of "${CFG_ALLOW_TARGETING_KEYS}" or "${CFG_ADD_TARGETING_KEYS}" can be set`; + export const TARGETING_KEYS = Object.keys(CONSTANTS.TARGETING_KEYS).map( key => CONSTANTS.TARGETING_KEYS[key] ); @@ -123,17 +127,19 @@ export function newTargeting(auctionManager) { if (isGptPubadsDefined()) { const adUnitCodes = getAdUnitCodes(adUnitCode); const adUnits = auctionManager.getAdUnits().filter(adUnit => includes(adUnitCodes, adUnit.code)); + let unsetKeys = pbTargetingKeys.reduce((reducer, key) => { + reducer[key] = null; + return reducer; + }, {}); window.googletag.pubads().getSlots().forEach(slot => { let customSlotMatchingFunc = isFn(customSlotMatching) && customSlotMatching(slot); - pbTargetingKeys.forEach(function(key) { - // reset only registered adunits - adUnits.forEach(function(unit) { - if (unit.code === slot.getAdUnitPath() || - unit.code === slot.getSlotElementId() || - (isFn(customSlotMatchingFunc) && customSlotMatchingFunc(unit.code))) { - slot.setTargeting(key, null); - } - }); + // reset only registered adunits + adUnits.forEach(unit => { + if (unit.code === slot.getAdUnitPath() || + unit.code === slot.getSlotElementId() || + (isFn(customSlotMatchingFunc) && customSlotMatchingFunc(unit.code))) { + slot.updateTargetingFromMap(unsetKeys); + } }); }); } @@ -259,7 +265,17 @@ export function newTargeting(auctionManager) { }); const defaultKeys = Object.keys(Object.assign({}, CONSTANTS.DEFAULT_TARGETING_KEYS, CONSTANTS.NATIVE_KEYS)); - const allowedKeys = config.getConfig('targetingControls.allowTargetingKeys') || defaultKeys; + let allowedKeys = config.getConfig(CFG_ALLOW_TARGETING_KEYS); + const addedKeys = config.getConfig(CFG_ADD_TARGETING_KEYS); + + if (addedKeys != null && allowedKeys != null) { + throw new Error(TARGETING_KEY_CONFIGURATION_ERROR_MSG); + } else if (addedKeys != null) { + allowedKeys = defaultKeys.concat(addedKeys); + } else { + allowedKeys = allowedKeys || defaultKeys; + } + if (Array.isArray(allowedKeys) && allowedKeys.length > 0) { targeting = getAllowedTargetingKeyValues(targeting, allowedKeys); } @@ -282,6 +298,13 @@ export function newTargeting(auctionManager) { return targeting; }; + // warn about conflicting configuration + config.getConfig('targetingControls', function (config) { + if (deepAccess(config, CFG_ALLOW_TARGETING_KEYS) != null && deepAccess(config, CFG_ADD_TARGETING_KEYS) != null) { + logError(TARGETING_KEY_CONFIGURATION_ERROR_MSG); + } + }); + // create an encoded string variant based on the keypairs of the provided object // - note this will encode the characters between the keys (ie = and &) function convertKeysToQueryForm(keyMap) { @@ -436,7 +459,7 @@ export function newTargeting(auctionManager) { const adUnitCodes = getAdUnitCodes(adUnitCode); return bidsReceived .filter(bid => includes(adUnitCodes, bid.adUnitCode)) - .filter(bid => bid.cpm > 0) + .filter(bid => (isAllowZeroCpmBidsEnabled(bid.bidderCode)) ? bid.cpm >= 0 : bid.cpm > 0) .map(bid => bid.adUnitCode) .filter(uniques) .map(adUnitCode => bidsReceived diff --git a/src/utils.js b/src/utils.js index 03c76529ddf..0f3709487f0 100644 --- a/src/utils.js +++ b/src/utils.js @@ -1,5 +1,6 @@ /* eslint-disable no-console */ import { config } from './config.js'; +import { getGlobal } from './prebidGlobal.js'; import clone from 'just-clone'; import find from 'core-js-pure/features/array/find.js'; import includes from 'core-js-pure/features/array/includes.js'; @@ -21,7 +22,17 @@ let consoleLogExists = Boolean(consoleExists && window.console.log); let consoleInfoExists = Boolean(consoleExists && window.console.info); let consoleWarnExists = Boolean(consoleExists && window.console.warn); let consoleErrorExists = Boolean(consoleExists && window.console.error); -var events = require('./events.js'); + +const emitEvent = (function () { + // lazy load events to avoid circular import + let ev; + return function() { + if (ev == null) { + ev = require('./events.js'); + } + return ev.emit.apply(ev, arguments); + } +})(); // this allows stubbing of utility functions that are used internally by other utility functions export const internal = { @@ -264,14 +275,14 @@ export function logWarn() { if (debugTurnedOn() && consoleWarnExists) { console.warn.apply(console, decorateLog(arguments, 'WARNING:')); } - events.emit(CONSTANTS.EVENTS.AUCTION_DEBUG, {type: 'WARNING', arguments: arguments}); + emitEvent(CONSTANTS.EVENTS.AUCTION_DEBUG, {type: 'WARNING', arguments: arguments}); } export function logError() { if (debugTurnedOn() && consoleErrorExists) { console.error.apply(console, decorateLog(arguments, 'ERROR:')); } - events.emit(CONSTANTS.EVENTS.AUCTION_DEBUG, {type: 'ERROR', arguments: arguments}); + emitEvent(CONSTANTS.EVENTS.AUCTION_DEBUG, {type: 'ERROR', arguments: arguments}); } function decorateLog(args, prefix) { @@ -481,16 +492,43 @@ export function insertElement(elm, doc, target, asLastChildChild) { } catch (e) {} } +/** + * Returns a promise that completes when the given element triggers a 'load' or 'error' DOM event, or when + * `timeout` milliseconds have elapsed. + * + * @param {HTMLElement} element + * @param {Number} [timeout] + * @returns {Promise} + */ +export function waitForElementToLoad(element, timeout) { + let timer = null; + return new Promise((resolve) => { + const onLoad = function() { + element.removeEventListener('load', onLoad); + element.removeEventListener('error', onLoad); + if (timer != null) { + window.clearTimeout(timer); + } + resolve(); + }; + element.addEventListener('load', onLoad); + element.addEventListener('error', onLoad); + if (timeout != null) { + timer = window.setTimeout(onLoad, timeout); + } + }); +} + /** * Inserts an image pixel with the specified `url` for cookie sync * @param {string} url URL string of the image pixel to load * @param {function} [done] an optional exit callback, used when this usersync pixel is added during an async process + * @param {Number} [timeout] an optional timeout in milliseconds for the image to load before calling `done` */ -export function triggerPixel(url, done) { +export function triggerPixel(url, done, timeout) { const img = new Image(); if (done && internal.isFn(done)) { - img.addEventListener('load', done); - img.addEventListener('error', done); + waitForElementToLoad(img, timeout).then(done); } img.src = url; } @@ -538,18 +576,18 @@ export function insertHtmlIntoIframe(htmlCode) { * @param {string} url URL to be requested * @param {string} encodeUri boolean if URL should be encoded before inserted. Defaults to true * @param {function} [done] an optional exit callback, used when this usersync pixel is added during an async process + * @param {Number} [timeout] an optional timeout in milliseconds for the iframe to load before calling `done` */ -export function insertUserSyncIframe(url, done) { +export function insertUserSyncIframe(url, done, timeout) { let iframeHtml = internal.createTrackPixelIframeHtml(url, false, 'allow-scripts allow-same-origin'); let div = document.createElement('div'); div.innerHTML = iframeHtml; let iframe = div.firstChild; if (done && internal.isFn(done)) { - iframe.addEventListener('load', done); - iframe.addEventListener('error', done); + waitForElementToLoad(iframe, timeout).then(done); } internal.insertElement(iframe, document, 'html', true); -}; +} /** * Creates a snippet of HTML that retrieves the specified `url` @@ -1244,7 +1282,18 @@ export function mergeDeep(target, ...sources) { if (!target[key]) { Object.assign(target, { [key]: source[key] }); } else if (isArray(target[key])) { - target[key] = target[key].concat(source[key]); + source[key].forEach(obj => { + let addItFlag = 1; + for (let i = 0; i < target[key].length; i++) { + if (deepEqual(target[key][i], obj)) { + addItFlag = 0; + break; + } + } + if (addItFlag) { + target[key].push(obj); + } + }); } } else { Object.assign(target, { [key]: source[key] }); @@ -1294,3 +1343,9 @@ export function cyrb53Hash(str, seed = 0) { h2 = imul(h2 ^ (h2 >>> 16), 2246822507) ^ imul(h1 ^ (h1 >>> 13), 3266489909); return (4294967296 * (2097151 & h2) + (h1 >>> 0)).toString(); } + +export function isAllowZeroCpmBidsEnabled(bidderCode) { + const bidderSettings = getGlobal().bidderSettings; + return ((bidderSettings[bidderCode] && bidderSettings[bidderCode].allowZeroCpmBids === true) || + (bidderSettings.standard && bidderSettings.standard.allowZeroCpmBids === true)); +} diff --git a/test/helpers/prebidGlobal.js b/test/helpers/prebidGlobal.js index 597076ab0db..94776a5242b 100644 --- a/test/helpers/prebidGlobal.js +++ b/test/helpers/prebidGlobal.js @@ -1,3 +1,4 @@ window.$$PREBID_GLOBAL$$ = (window.$$PREBID_GLOBAL$$ || {}); +window.$$PREBID_GLOBAL$$.installedModules = (window.$$PREBID_GLOBAL$$.installedModules || []); window.$$PREBID_GLOBAL$$.cmd = window.$$PREBID_GLOBAL$$.cmd || []; window.$$PREBID_GLOBAL$$.que = window.$$PREBID_GLOBAL$$.que || []; diff --git a/test/helpers/syncPromise.js b/test/helpers/syncPromise.js new file mode 100644 index 00000000000..99361bd716e --- /dev/null +++ b/test/helpers/syncPromise.js @@ -0,0 +1,71 @@ +const orig = {}; +['resolve', 'reject', 'all', 'race', 'allSettled'].forEach((k) => orig[k] = Promise[k].bind(Promise)) + +// Callbacks attached through Promise.resolve(value).then(...) will usually +// not execute immediately even if `value` is immediately available. This +// breaks tests that were written before promises even though they are semantically still valid. +// They can be made to work by making promises quasi-synchronous. + +export function SyncPromise(value, fail = false) { + if (value instanceof SyncPromise) { + return value; + } else if (typeof value === 'object' && typeof value.then === 'function') { + return orig.resolve(value); + } else { + Object.assign(this, { + then: function (cb, err) { + const handler = fail ? err : cb; + if (handler != null) { + return new SyncPromise(handler(value)); + } else { + return this; + } + }, + catch: function (cb) { + if (fail) { + return new SyncPromise(cb(value)) + } else { + return this; + } + }, + finally: function (cb) { + cb(); + return this; + }, + __value: fail ? {status: 'rejected', reason: value} : {status: 'fulfilled', value} + }) + } +} + +Object.assign(SyncPromise, { + resolve: (val) => new SyncPromise(val), + reject: (val) => new SyncPromise(val, true), + race: (promises) => promises.find((p) => p instanceof SyncPromise) || orig.race(promises), + allSettled: (promises) => { + if (promises.every((p) => p instanceof SyncPromise)) { + return new SyncPromise(promises.map((p) => p.__value)) + } else { + return orig.allSettled(promises); + } + }, + all: (promises) => { + if (promises.every((p) => p instanceof SyncPromise)) { + return SyncPromise.allSettled(promises).then((result) => { + const err = result.find((r) => r.status === 'rejected'); + if (err != null) { + return new SyncPromise(err.reason, true); + } else { + return new SyncPromise(result.map((r) => r.value)) + } + }) + } else { + return orig.all(promises); + } + } +}) + +export function synchronizePromise(sandbox) { + Object.keys(orig).forEach((k) => { + sandbox.stub(window.Promise, k).callsFake(SyncPromise[k]); + }) +} diff --git a/test/spec/auctionmanager_spec.js b/test/spec/auctionmanager_spec.js index 69e34c4a07a..0b335674a6b 100644 --- a/test/spec/auctionmanager_spec.js +++ b/test/spec/auctionmanager_spec.js @@ -3,7 +3,7 @@ import { auctionCallbacks, AUCTION_COMPLETED, adjustBids, - getMediaTypeGranularity, + getMediaTypeGranularity, addBidResponse, } from 'src/auction.js'; import CONSTANTS from 'src/constants.json'; import * as auctionModule from 'src/auction.js'; @@ -14,6 +14,10 @@ import * as store from 'src/videoCache.js'; import * as ajaxLib from 'src/ajax.js'; import find from 'core-js-pure/features/array/find.js'; import { server } from 'test/mocks/xhr.js'; +import {expect} from 'chai'; +import {hook} from '../../src/hook.js'; +import 'src/debugging.js' +import {synchronizePromise} from '../helpers/syncPromise.js'; // some of these tests require debugging hooks to be loaded var assert = require('assert'); @@ -124,6 +128,27 @@ function mockAjaxBuilder() { } describe('auctionmanager.js', function () { + let promiseSandbox; + + before(() => { + // hooks are global and their side effects depend on what has been loaded... not ideal for unit tests + [ + auctionModule.addBidResponse, + auctionModule.addBidderRequests, + auctionModule.bidsBackCallback + ].forEach((h) => h.getHooks().remove()) + hook.ready(); + }); + + beforeEach(() => { + promiseSandbox = sinon.createSandbox(); + synchronizePromise(promiseSandbox); + }); + + afterEach(() => { + promiseSandbox.restore(); + }) + describe('getKeyValueTargetingPairs', function () { const DEFAULT_BID = { cpm: 5.578, @@ -1259,12 +1284,7 @@ describe('auctionmanager.js', function () { let bids = TEST_BIDS; let bidRequests; let doneSpy; - let auction = { - getBidRequests: () => bidRequests, - getAuctionId: () => '1', - addBidReceived: () => true, - getTimeout: () => 1000 - } + let auction; beforeEach(() => { doneSpy = sinon.spy(); @@ -1272,12 +1292,21 @@ describe('auctionmanager.js', function () { cache: { url: 'https://prebid.adnxs.com/pbc/v1/cache' } - }) + }); + const start = Date.now(); + auction = { + getBidRequests: () => bidRequests, + getAuctionId: () => '1', + addBidReceived: () => true, + getTimeout: () => 1000, + getAuctionStart: () => start, + } }); afterEach(() => { doneSpy.resetHistory(); config.resetConfig(); + bidRequests = null; }); it('should call auction done after bid is added to auction for mediaType banner', function () { @@ -1328,19 +1357,114 @@ describe('auctionmanager.js', function () { const responseBody = `{"responses":[{"uuid":"${uuid}"}]}`; server.requests[0].respond(200, { 'Content-Type': 'application/json' }, responseBody); assert.equal(doneSpy.callCount, 1); - }) + }); + + describe('when addBidResponse hook returns promises', () => { + let resolvers, callbacks, bids; + + function hook(next, ...args) { + next.bail(new Promise((resolve, reject) => { + resolvers.resolve.push(resolve); + resolvers.reject.push(reject); + }).finally(() => next(...args))); + } + + function invokeCallbacks() { + bids.forEach((bid, i) => callbacks.addBidResponse.call(bidRequests[i], ADUNIT_CODE, bid)); + bidRequests.forEach(bidRequest => callbacks.adapterDone.call(bidRequest)); + } + + function delay(ms = 0) { + return new Promise((resolve) => { + setTimeout(resolve, ms) + }); + } + + beforeEach(() => { + promiseSandbox.restore(); + bids = [ + mockBid({bidderCode: BIDDER_CODE1}), + mockBid({bidderCode: BIDDER_CODE}) + ] + bidRequests = bids.map((b) => mockBidRequest(b)); + resolvers = {resolve: [], reject: []}; + addBidResponse.before(hook); + callbacks = auctionCallbacks(doneSpy, auction); + Object.assign(auction, { + addNoBid: sinon.spy() + }); + }); + + afterEach(() => { + addBidResponse.getHooks({hook: hook}).remove(); + }); + + Object.entries({ + 'all succeed': ['resolve', 'resolve'], + 'some fail': ['resolve', 'reject'], + 'all fail': ['reject', 'reject'] + }).forEach(([test, results]) => { + describe(`(and ${test})`, () => { + it('should wait for them to complete before calling auctionDone', () => { + invokeCallbacks(); + return delay().then(() => { + expect(doneSpy.called).to.be.false; + expect(auction.addNoBid.called).to.be.false; + resolvers[results[0]][0](); + return delay(); + }).then(() => { + expect(doneSpy.called).to.be.false; + expect(auction.addNoBid.called).to.be.false; + resolvers[results[1]][1](); + return delay(); + }).then(() => { + expect(doneSpy.called).to.be.true; + }); + }); + }); + }); + + Object.entries({ + bidder: (timeout) => { + bidRequests.forEach((r) => r.timeout = timeout); + auction.getTimeout = () => timeout + 10000 + }, + auction: (timeout) => { + auction.getTimeout = () => timeout; + bidRequests.forEach((r) => r.timeout = timeout + 10000) + } + }).forEach(([test, setTimeout]) => { + it(`should respect ${test} timeout if they never complete`, () => { + const start = Date.now() - 2900; + auction.getAuctionStart = () => start; + setTimeout(3000); + invokeCallbacks(); + return delay().then(() => { + expect(doneSpy.called).to.be.false; + return delay(100); + }).then(() => { + expect(doneSpy.called).to.be.true; + }); + }); + + it(`should not wait if ${test} has already timed out`, () => { + const start = Date.now() - 2000; + auction.getAuctionStart = () => start; + setTimeout(1000); + invokeCallbacks(); + return delay().then(() => { + expect(doneSpy.called).to.be.true; + }); + }); + }) + }); }); describe('auctionOptions', function() { let bidRequests; let doneSpy; let clock; - let auction = { - getBidRequests: () => bidRequests, - getAuctionId: () => '1', - addBidReceived: () => true, - getTimeout: () => 1000 - } + let auction; let requiredBidder = BIDDER_CODE; let requiredBidder1 = BIDDER_CODE1; let secondaryBidder = 'doNotWaitForMe'; @@ -1352,7 +1476,15 @@ describe('auctionmanager.js', function () { 'auctionOptions': { secondaryBidders: [ secondaryBidder ] } - }) + }); + const start = Date.now(); + auction = { + getBidRequests: () => bidRequests, + getAuctionId: () => '1', + addBidReceived: () => true, + getTimeout: () => 1000, + getAuctionStart: () => start, + } }); afterEach(() => { diff --git a/test/spec/config_spec.js b/test/spec/config_spec.js index 6cc1bd557d5..04501ccb5b5 100644 --- a/test/spec/config_spec.js +++ b/test/spec/config_spec.js @@ -7,8 +7,10 @@ const utils = require('src/utils'); let getConfig; let setConfig; let readConfig; +let mergeConfig; let getBidderConfig; let setBidderConfig; +let mergeBidderConfig; let setDefaults; describe('config API', function () { @@ -19,8 +21,10 @@ describe('config API', function () { getConfig = config.getConfig; setConfig = config.setConfig; readConfig = config.readConfig; + mergeConfig = config.mergeConfig; getBidderConfig = config.getBidderConfig; setBidderConfig = config.setBidderConfig; + mergeBidderConfig = config.mergeBidderConfig; setDefaults = config.setDefaults; logErrorSpy = sinon.spy(utils, 'logError'); logWarnSpy = sinon.spy(utils, 'logWarn'); @@ -345,4 +349,279 @@ describe('config API', function () { const warning = 'Auction Options given an incorrect param: testing'; assert.ok(logWarnSpy.calledWith(warning), 'expected warning was logged'); }); + + it('should merge input with existing global config', function () { + const obj = { + ortb2: { + site: { + name: 'example', + domain: 'page.example.com', + cat: ['IAB2'], + sectioncat: ['IAB2-2'], + pagecat: ['IAB2-2'] + } + } + }; + setConfig({ ortb2: { + user: { + ext: { + data: { + registered: true, + interests: ['cars'] + } + } + } + } + }); + mergeConfig(obj); + const expected = { + site: { + name: 'example', + domain: 'page.example.com', + cat: ['IAB2'], + sectioncat: ['IAB2-2'], + pagecat: ['IAB2-2'] + }, + user: { + ext: { + data: { + registered: true, + interests: ['cars'] + } + } + } + } + expect(getConfig('ortb2')).to.deep.equal(expected); + }); + + it('input should take precedence over existing config if keys are the same', function() { + const input = { + ortb2: { + user: { + ext: { + data: { + registered: true, + interests: ['cars'] + } + } + } + } + } + setConfig({ ortb2: { + user: { + ext: { + data: { + registered: false + } + } + } + }}); + mergeConfig(input); + const expected = { + user: { + ext: { + data: { + registered: true, + interests: ['cars'] + } + } + } + } + expect(getConfig('ortb2')).to.deep.equal(expected); + }); + + it('should log error for a non-object value passed in', function () { + mergeConfig('invalid object'); + expect(logErrorSpy.calledOnce).to.equal(true); + const error = 'mergeConfig input must be an object'; + assert.ok(logErrorSpy.calledWith(error), 'expected error was logged'); + }); + + it('should merge input with existing bidder config', function () { + const input = { + bidders: ['rubicon', 'appnexus'], + config: { + ortb2: { + site: { + name: 'example', + domain: 'page.example.com', + cat: ['IAB2'], + sectioncat: ['IAB2-2'], + pagecat: ['IAB2-2'] + }, + user: { + ext: { + ssp: 'magnite', + data: { + registered: false, + interests: ['sports'] + } + } + } + } + } + }; + setBidderConfig({ + bidders: ['rubicon'], + config: { + ortb2: { + user: { + ext: { + data: { + registered: true, + interests: ['cars'] + } + } + } + } + } + }); + mergeBidderConfig(input); + const expected = { + rubicon: { + ortb2: { + site: { + name: 'example', + domain: 'page.example.com', + cat: ['IAB2'], + sectioncat: ['IAB2-2'], + pagecat: ['IAB2-2'] + }, + user: { + ext: { + ssp: 'magnite', + data: { + registered: false, + interests: ['cars', 'sports'] + } + } + } + } + }, + appnexus: { + ortb2: { + site: { + name: 'example', + domain: 'page.example.com', + cat: ['IAB2'], + sectioncat: ['IAB2-2'], + pagecat: ['IAB2-2'] + }, + user: { + ext: { + ssp: 'magnite', + data: { + registered: false, + interests: ['sports'] + } + } + } + } + } + } + expect(getBidderConfig()).to.deep.equal(expected); + }); + + it('should log error for a non-object value passed in', function () { + mergeBidderConfig('invalid object'); + expect(logErrorSpy.calledOnce).to.equal(true); + const error = 'setBidderConfig bidder options must be an object'; + assert.ok(logErrorSpy.calledWith(error), 'expected error was logged'); + }); + + it('should log error for empty bidders array', function () { + mergeBidderConfig({ + bidders: [], + config: { + ortb2: { + site: { + name: 'example', + domain: 'page.example.com', + cat: ['IAB2'], + sectioncat: ['IAB2-2'], + pagecat: ['IAB2-2'] + } + } + } + }); + expect(logErrorSpy.calledOnce).to.equal(true); + const error = 'setBidderConfig bidder options must contain a bidders list with at least 1 bidder'; + assert.ok(logErrorSpy.calledWith(error), 'expected error was logged'); + }); + + it('should log error for nonexistent config object', function () { + mergeBidderConfig({ + bidders: ['appnexus'] + }); + expect(logErrorSpy.calledOnce).to.equal(true); + const error = 'setBidderConfig bidder options must contain a config object'; + assert.ok(logErrorSpy.calledWith(error), 'expected error was logged'); + }); + + it('should merge without array duplication', function() { + const userObj1 = { + name: 'www.dataprovider1.com', + ext: { taxonomyname: 'iab_audience_taxonomy' }, + segment: [{ + id: '1776' + }] + }; + + const userObj2 = { + name: 'www.dataprovider2.com', + ext: { taxonomyname: 'iab_audience_taxonomy' }, + segment: [{ + id: '1914' + }] + }; + + const siteObj1 = { + name: 'www.dataprovider3.com', + ext: { + taxonomyname: 'iab_audience_taxonomy' + }, + segment: [ + { + id: '1812' + }, + { + id: '1955' + } + ] + } + + setConfig({ + ortb2: { + user: { + data: [userObj1, userObj2] + }, + site: { + content: { + data: [siteObj1] + } + } + } + }); + + const rtd = { + ortb2: { + user: { + data: [userObj1] + }, + site: { + content: { + data: [siteObj1] + } + } + } + }; + mergeConfig(rtd); + + let ortb2Config = getConfig('ortb2'); + + expect(ortb2Config.user.data).to.deep.include.members([userObj1, userObj2]); + expect(ortb2Config.site.content.data).to.deep.include.members([siteObj1]); + expect(ortb2Config.user.data).to.have.lengthOf(2); + expect(ortb2Config.site.content.data).to.have.lengthOf(1); + }); }); diff --git a/test/spec/integration/faker/googletag.js b/test/spec/integration/faker/googletag.js index 1d1a7512153..a8676b500a5 100644 --- a/test/spec/integration/faker/googletag.js +++ b/test/spec/integration/faker/googletag.js @@ -51,9 +51,15 @@ export function enable() { window.googletag = { _slots: [], _callbackMap: {}, + _ppid: undefined, + cmd: [], pubads: function () { var self = this; return { + setPublisherProvidedId: function (ppid) { + self._ppid = ppid; + }, + getSlots: function () { return self._slots; }, diff --git a/test/spec/modules/33acrossBidAdapter_spec.js b/test/spec/modules/33acrossBidAdapter_spec.js index b5443cdd5c2..141edc1e61c 100644 --- a/test/spec/modules/33acrossBidAdapter_spec.js +++ b/test/spec/modules/33acrossBidAdapter_spec.js @@ -16,20 +16,21 @@ function validateBuiltServerRequest(builtReq, expectedReq) { describe('33acrossBidAdapter:', function () { const BIDDER_CODE = '33across'; const SITE_ID = 'sample33xGUID123456789'; - const PRODUCT_ID = 'siab'; const END_POINT = 'https://ssc.33across.com/api/v1/hb'; let element, win; let bidRequests; let sandbox; - function TtxRequestBuilder() { + function TtxRequestBuilder(siteId = SITE_ID) { const ttxRequest = { - imp: [{}], + imp: [{ + id: 'b1' + }], site: { - id: SITE_ID + id: siteId }, - id: 'b1', + id: 'r1', regs: { ext: { gdpr: 0 @@ -46,66 +47,83 @@ describe('33acrossBidAdapter:', function () { } }; + this.addImp = (id = 'b2') => { + ttxRequest.imp.push({ id }); + + return this; + } + this.withBanner = () => { - Object.assign(ttxRequest.imp[0], { - banner: { - format: [ - { - w: 300, - h: 250 - }, - { - w: 728, - h: 90 - } - ], - ext: { - ttx: { - viewability: { - amount: 100 + ttxRequest.imp.forEach((imp) => { + Object.assign(imp, { + banner: { + format: [ + { + w: 300, + h: 250 + }, + { + w: 728, + h: 90 + } + ], + ext: { + ttx: { + viewability: { + amount: 100 + } } } } - } + }); }); - return this; }; this.withBannerSizes = this.withSizes = sizes => { - Object.assign(ttxRequest.imp[0].banner, { format: sizes }); + ttxRequest.imp.forEach((imp) => { + Object.assign(imp.banner, { format: sizes }); + }); + return this; }; this.withVideo = (params = {}) => { - Object.assign(ttxRequest.imp[0], { - video: { - w: 300, - h: 250, - placement: 2, - ...params - } + ttxRequest.imp.forEach((imp) => { + Object.assign(imp, { + video: { + w: 300, + h: 250, + placement: 2, + ...params + } + }); }); return this; }; this.withViewability = (viewability, format = 'banner') => { - Object.assign(ttxRequest.imp[0][format], { - ext: { - ttx: { viewability } - } + ttxRequest.imp.forEach((imp) => { + Object.assign(imp[format], { + ext: { + ttx: { viewability } + } + }); }); + return this; }; - this.withProduct = (prod = PRODUCT_ID) => { - Object.assign(ttxRequest.imp[0], { - ext: { - ttx: { - prod + this.withProduct = (prod = 'siab') => { + ttxRequest.imp.forEach((imp) => { + Object.assign(imp, { + ext: { + ttx: { + prod + } } - } + }); }); return this; @@ -249,7 +267,7 @@ describe('33acrossBidAdapter:', function () { bidderRequestId: 'b1a', params: { siteId: SITE_ID, - productId: PRODUCT_ID + productId: 'siab' }, adUnitCode: 'div-id', auctionId: 'r1', @@ -258,35 +276,61 @@ describe('33acrossBidAdapter:', function () { } ]; + this.addBid = (bidParams = {}) => { + bidRequests.push({ + bidId: 'b2', + bidder: '33across', + bidderRequestId: 'b1b', + params: { + siteId: SITE_ID, + productId: 'siab' + }, + adUnitCode: 'div-id', + auctionId: 'r1', + mediaTypes: {}, + transactionId: 't2', + ...bidParams + }); + + return this; + }; + this.withBanner = () => { - bidRequests[0].mediaTypes.banner = { - sizes: [ - [300, 250], - [728, 90] - ] - }; + bidRequests.forEach((bid) => { + bid.mediaTypes.banner = { + sizes: [ + [300, 250], + [728, 90] + ] + }; + }); return this; }; this.withProduct = (prod) => { - bidRequests[0].params.productId = prod; - + bidRequests.forEach((bid) => { + bid.params.productId = prod; + }); return this; }; this.withVideo = (params) => { - bidRequests[0].mediaTypes.video = { - playerSize: [[300, 250]], - context: 'outstream', - ...params - }; + bidRequests.forEach((bid) => { + bid.mediaTypes.video = { + playerSize: [[300, 250]], + context: 'outstream', + ...params + }; + }); return this; } this.withUserIds = (eids) => { - bidRequests[0].userIdAsEids = eids; + bidRequests.forEach((bid) => { + bid.userIdAsEids = eids; + }); return this; }; @@ -315,6 +359,7 @@ describe('33acrossBidAdapter:', function () { } }; win = { + parent: null, document: { visibilityState: 'visible' }, @@ -331,7 +376,7 @@ describe('33acrossBidAdapter:', function () { sandbox = sinon.sandbox.create(); sandbox.stub(Date, 'now').returns(1); - sandbox.stub(document, 'getElementById').withArgs('div-id').returns(element); + sandbox.stub(document, 'getElementById').returns(element); sandbox.stub(utils, 'getWindowTop').returns(win); sandbox.stub(utils, 'getWindowSelf').returns(win); }); @@ -1376,10 +1421,146 @@ describe('33acrossBidAdapter:', function () { }); }); }); + + context('when SRA mode is enabled', function() { + it('builds a single request with multiple imps corresponding to each group {siteId, productId}', function() { + sandbox.stub(config, 'getConfig').callsFake(() => { + return { + enableSRAMode: true + } + }); + + const bidRequests = new BidRequestsBuilder() + .addBid() + .addBid({ + bidId: 'b3', + adUnitCode: 'div-id', + params: { + siteId: 'sample33xGUID123456780', + productId: 'siab' + } + }) + .addBid({ + bidId: 'b4', + adUnitCode: 'div-id', + params: { + siteId: 'sample33xGUID123456780', + productId: 'inview' + } + }) + .withBanner() + .withVideo({context: 'outstream'}) + .build(); + + const req1 = new TtxRequestBuilder() + .addImp() + .withProduct('siab') + .withBanner() + .withVideo() + .build(); + + const req2 = new TtxRequestBuilder('sample33xGUID123456780') + .withProduct('siab') + .withBanner() + .withVideo() + .build(); + + req2.imp[0].id = 'b3'; + + const req3 = new TtxRequestBuilder('sample33xGUID123456780') + .withProduct('inview') + .withBanner() + .withVideo() + .build(); + + req3.imp[0].id = 'b4'; + + const serverReq1 = new ServerRequestBuilder() + .withData(req1) + .build(); + + const serverReq2 = new ServerRequestBuilder() + .withData(req2) + .withUrl('https://ssc.33across.com/api/v1/hb?guid=sample33xGUID123456780') + .build(); + + const serverReq3 = new ServerRequestBuilder() + .withData(req3) + .withUrl('https://ssc.33across.com/api/v1/hb?guid=sample33xGUID123456780') + .build(); + + const builtServerRequests = spec.buildRequests(bidRequests, {}); + + expect(builtServerRequests).to.deep.equal([serverReq1, serverReq2, serverReq3]); + }); + }); + + context('when SRA mode is not enabled', function() { + it('builds multiple requests, one corresponding to each Ad Unit', function() { + const bidRequests = new BidRequestsBuilder() + .addBid() + .addBid({ + bidId: 'b3', + adUnitCode: 'div-id', + params: { + siteId: 'sample33xGUID123456780', + productId: 'siab' + } + }) + .withBanner() + .withVideo({context: 'outstream'}) + .build(); + + const req1 = new TtxRequestBuilder() + .withProduct('siab') + .withBanner() + .withVideo() + .build(); + + const req2 = new TtxRequestBuilder() + .withProduct('siab') + .withBanner() + .withVideo() + .build(); + + req2.imp[0].id = 'b2'; + + const req3 = new TtxRequestBuilder('sample33xGUID123456780') + .withProduct('siab') + .withBanner() + .withVideo() + .build(); + + req3.imp[0].id = 'b3'; + + const serverReq1 = new ServerRequestBuilder() + .withData(req1) + .build(); + + const serverReq2 = new ServerRequestBuilder() + .withData(req2) + .build(); + + const serverReq3 = new ServerRequestBuilder() + .withData(req3) + .withUrl('https://ssc.33across.com/api/v1/hb?guid=sample33xGUID123456780') + .build(); + + const builtServerRequests = spec.buildRequests(bidRequests, {}); + + expect(builtServerRequests) + .to.deep.equal([ + serverReq1, + serverReq2, + serverReq3 + ]); + }); + }); }); describe('interpretResponse', function() { let ttxRequest, serverRequest; + const videoBid = ''; beforeEach(function() { ttxRequest = new TtxRequestBuilder() @@ -1390,6 +1571,7 @@ describe('33acrossBidAdapter:', function () { page: 'https://test-url.com' }) .build(); + serverRequest = new ServerRequestBuilder() .withUrl('https://staging-ssc.33across.com/api/v1/hb') .withData(ttxRequest) @@ -1405,11 +1587,12 @@ describe('33acrossBidAdapter:', function () { const serverResponse = { cur: 'USD', ext: {}, - id: 'b1', + id: 'r1', seatbid: [ { bid: [{ id: '1', + impid: 'b1', adm: '

I am an ad

', crid: 1, h: 250, @@ -1441,15 +1624,15 @@ describe('33acrossBidAdapter:', function () { }); it('interprets and returns the single video bid response', function() { - const videoBid = ''; const serverResponse = { cur: 'USD', ext: {}, - id: 'b1', + id: 'r1', seatbid: [ { bid: [{ id: '1', + impid: 'b1', adm: videoBid, ext: { ttx: { @@ -1497,6 +1680,7 @@ describe('33acrossBidAdapter:', function () { { bid: [{ id: '1', + impid: 'b1', adm: '

I am an ad

', crid: 1, h: 250, @@ -1533,7 +1717,7 @@ describe('33acrossBidAdapter:', function () { const serverResponse = { cur: 'USD', ext: {}, - id: 'b1', + id: 'r1', seatbid: [] }; @@ -1542,15 +1726,16 @@ describe('33acrossBidAdapter:', function () { }); context('when more than one bids are returned', function() { - it('interprets and returns the the first bid of the first seatbid', function() { + it('interprets and returns all bids', function() { const serverResponse = { cur: 'USD', ext: {}, - id: 'b1', + id: 'r1', seatbid: [ { bid: [{ id: '1', + impid: 'b1', adm: '

I am an ad

', crid: 1, h: 250, @@ -1559,6 +1744,7 @@ describe('33acrossBidAdapter:', function () { }, { id: '2', + impid: 'b2', adm: '

I am an ad

', crid: 2, h: 250, @@ -1570,7 +1756,14 @@ describe('33acrossBidAdapter:', function () { { bid: [{ id: '3', - adm: '

I am an ad

', + impid: 'b3', + adm: videoBid, + ext: { + ttx: { + mediaType: 'video', + vastType: 'xml' + } + }, crid: 3, h: 250, w: 300, @@ -1579,21 +1772,50 @@ describe('33acrossBidAdapter:', function () { } ] }; - const bidResponse = { - requestId: 'b1', - bidderCode: BIDDER_CODE, - cpm: 0.0940, - width: 300, - height: 250, - ad: '

I am an ad

', - ttl: 60, - creativeId: 1, - mediaType: 'banner', - currency: 'USD', - netRevenue: true - }; + const bidResponse = [ + { + requestId: 'b1', + bidderCode: BIDDER_CODE, + cpm: 0.0940, + width: 300, + height: 250, + ad: '

I am an ad

', + ttl: 60, + creativeId: 1, + mediaType: 'banner', + currency: 'USD', + netRevenue: true + }, + { + requestId: 'b2', + bidderCode: BIDDER_CODE, + cpm: 0.0938, + width: 300, + height: 250, + ad: '

I am an ad

', + ttl: 60, + creativeId: 2, + mediaType: 'banner', + currency: 'USD', + netRevenue: true + }, + { + requestId: 'b3', + bidderCode: BIDDER_CODE, + cpm: 0.0938, + width: 300, + height: 250, + ad: videoBid, + vastXml: '', + ttl: 60, + creativeId: 3, + mediaType: 'video', + currency: 'USD', + netRevenue: true + } + ]; - expect(spec.interpretResponse({ body: serverResponse }, serverRequest)).to.deep.equal([bidResponse]); + expect(spec.interpretResponse({ body: serverResponse }, serverRequest)).to.deep.equal(bidResponse); }); }); }); diff --git a/test/spec/modules/adagioBidAdapter_spec.js b/test/spec/modules/adagioBidAdapter_spec.js index ed3d26c1f4d..945cd0565a7 100644 --- a/test/spec/modules/adagioBidAdapter_spec.js +++ b/test/spec/modules/adagioBidAdapter_spec.js @@ -244,16 +244,6 @@ describe('Adagio bid adapter', () => { expect(spec.isBidRequestValid(bid03)).to.equal(false); expect(spec.isBidRequestValid(bid04)).to.equal(false); }); - - it('should return false when refererInfo.reachedTop is false', function() { - sandbox.spy(utils, 'logWarn'); - sandbox.stub(adagio, 'getRefererInfo').returns({ reachedTop: false }); - const bid = new BidRequestBuilder().withParams().build(); - - expect(spec.isBidRequestValid(bid)).to.equal(false); - sinon.assert.callCount(utils.logWarn, 1); - sinon.assert.calledWith(utils.logWarn, 'Adagio: the main page url is unreachabled.'); - }); }); describe('buildRequests()', function() { diff --git a/test/spec/modules/adbookpspBidAdapter_spec.js b/test/spec/modules/adbookpspBidAdapter_spec.js index a6b8a794eeb..3a49f25edb6 100755 --- a/test/spec/modules/adbookpspBidAdapter_spec.js +++ b/test/spec/modules/adbookpspBidAdapter_spec.js @@ -504,9 +504,11 @@ describe('adbookpsp bid adapter', () => { ad: '
ad
', adId: '5', adserverTargeting: { + hb_ad_ord_adbookpsp: '0_0', // the value to the left of the underscore represents the index of the ad id and the number to the right represents the order index hb_adid_c_adbookpsp: '5', hb_deal_adbookpsp: 'werwetwerw', hb_liid_adbookpsp: '2342345', + hb_ordid_adbookpsp: '567843', }, referrer: 'http://prebid-test-page.io:8080/banner.html', lineItemId: '2342345', @@ -516,9 +518,11 @@ describe('adbookpsp bid adapter', () => { adId: '10', adUnitCode: 'div-gpt-ad-837465923534-0', adserverTargeting: { + hb_ad_ord_adbookpsp: '0_0', hb_adid_c_adbookpsp: '10', hb_deal_adbookpsp: 'dsfxcxcvxc', hb_liid_adbookpsp: '2121221', + hb_ordid_adbookpsp: '5678234', }, bidId: 'bid4321', bidderRequestId: '999ccceeee11', @@ -556,14 +560,18 @@ describe('adbookpsp bid adapter', () => { expect(bids).to.have.length(2); expect(bids[0].adserverTargeting).to.deep.equal({ + hb_ad_ord_adbookpsp: '0_0', + hb_adid_c_adbookpsp: '5', hb_deal_adbookpsp: 'werwetwerw', hb_liid_adbookpsp: '2342345', - hb_adid_c_adbookpsp: '5', + hb_ordid_adbookpsp: '567843', }); expect(bids[1].adserverTargeting).to.deep.equal({ + hb_ad_ord_adbookpsp: '0_0', + hb_adid_c_adbookpsp: '10', hb_deal_adbookpsp: 'dsfxcxcvxc', hb_liid_adbookpsp: '2121221', - hb_adid_c_adbookpsp: '10', + hb_ordid_adbookpsp: '5678234', }); }); @@ -580,9 +588,11 @@ describe('adbookpsp bid adapter', () => { expect(bids).to.have.length(2); for (const bid of bids) { expect(bid.adserverTargeting).to.deep.equal({ + hb_ad_ord_adbookpsp: '0_0,1_0', + hb_adid_c_adbookpsp: '5,10', hb_deal_adbookpsp: 'werwetwerw,dsfxcxcvxc', hb_liid_adbookpsp: '2342345,2121221', - hb_adid_c_adbookpsp: '5,10', + hb_ordid_adbookpsp: '567843,5678234', }); } }); @@ -670,9 +680,11 @@ describe('adbookpsp bid adapter', () => { ); expect(bids[0].adserverTargeting).to.deep.equal({ + hb_ad_ord_adbookpsp: '0_0', hb_adid_c_adbookpsp: '10', hb_deal_adbookpsp: 'dsfxcxcvxc', hb_liid_adbookpsp: '2121221', + hb_ordid_adbookpsp: '5678234', }); }); @@ -1279,6 +1291,7 @@ const exchangeResponse = { nurl: 'http://win.example.url', ext: { liid: '2342345', + ordid: '567843', }, cat: ['IAB2-1', 'IAB2-2', 'IAB2-3'], adomain: ['advertiser.com'], @@ -1301,6 +1314,7 @@ const exchangeResponse = { nurl: 'http://win.example.url', ext: { liid: '2121221', + ordid: '5678234', }, cat: ['IAB2-3'], adomain: ['advertiser.com', 'campaign.advertiser.com'], diff --git a/test/spec/modules/adfBidAdapter_spec.js b/test/spec/modules/adfBidAdapter_spec.js index c8697032b3f..ed096e7189d 100644 --- a/test/spec/modules/adfBidAdapter_spec.js +++ b/test/spec/modules/adfBidAdapter_spec.js @@ -29,11 +29,34 @@ describe('Adf adapter', function () { it('should return true when required params found', function () { assert(spec.isBidRequestValid(bid)); + + bid.params = { + inv: 1234, + mname: 'some-placement' + }; + assert(spec.isBidRequestValid(bid)); + + bid.params = { + mid: 4332, + inv: 1234, + mname: 'some-placement' + }; + assert(spec.isBidRequestValid(bid)); }); it('should return false when required params are missing', function () { bid.params = { adxDomain: 'adx.adform.net' }; assert.isFalse(spec.isBidRequestValid(bid)); + + bid.params = { + mname: 'some-placement' + }; + assert.isFalse(spec.isBidRequestValid(bid)); + + bid.params = { + inv: 1234 + }; + assert.isFalse(spec.isBidRequestValid(bid)); }); }); @@ -331,6 +354,30 @@ describe('Adf adapter', function () { } }); + describe('dynamic placement tag', function () { + it('should add imp parameters correctly', function () { + const validBidRequests = [ + { bidId: 'bidId', params: { inv: 1000, mname: 'placement' }, mediaTypes: {video: {}} }, + { bidId: 'bidId', params: { mid: 1234, inv: 1002, mname: 'placement2' }, mediaTypes: {video: {}} }, + { bidId: 'bidId', params: { mid: 1234 }, mediaTypes: {video: {}} } + ]; + const [ imp1, imp2, imp3 ] = getRequestImps(validBidRequests); + + assert.equal(imp1.ext.bidder.inv, 1000); + assert.equal(imp1.ext.bidder.mname, 'placement'); + assert.equal('tagid' in imp1, false); + + assert.equal(imp2.ext.bidder.inv, 1002); + assert.equal(imp2.ext.bidder.mname, 'placement2'); + assert.equal(imp2.tagid, 1234); + + assert.ok(imp3.ext.bidder); + assert.equal('inv' in imp3.ext.bidder, false); + assert.equal('mname' in imp3.ext.bidder, false); + assert.equal(imp3.tagid, 1234); + }); + }); + describe('price floors', function () { it('should not add if floors module not configured', function () { const validBidRequests = [{ bidId: 'bidId', params: {mid: 1000}, mediaTypes: {video: {}} }]; @@ -376,18 +423,14 @@ describe('Adf adapter', function () { return { currency: currency, floor - } + }; } }; } - - function getRequestImps(validBidRequests) { - return JSON.parse(spec.buildRequests(validBidRequests, { refererInfo: { referer: 'page' } }).data).imp; - } }); describe('multiple media types', function () { - it('should use single media type for bidding', function () { + it('should use all configured media types for bidding', function () { let validBidRequests = [{ bidId: 'bidId', params: { mid: 1000 }, @@ -414,20 +457,23 @@ describe('Adf adapter', function () { banner: { sizes: [[100, 100], [200, 300]] }, - native: {} + native: {}, + video: {} } }]; - let [ banner, video, native ] = JSON.parse(spec.buildRequests(validBidRequests, { refererInfo: { referer: 'page' } }).data).imp; - - assert.ok(banner.banner); - assert.equal(banner.video, undefined); - assert.equal(banner.native, undefined); - assert.ok(video.video); - assert.equal(video.banner, undefined); - assert.equal(video.native, undefined); - assert.ok(native.native); - assert.equal(native.video, undefined); - assert.equal(native.banner, undefined); + let [ first, second, third ] = JSON.parse(spec.buildRequests(validBidRequests, { refererInfo: { referer: 'page' } }).data).imp; + + assert.ok(first.banner); + assert.ok(first.video); + assert.equal(first.native, undefined); + + assert.ok(second.video); + assert.equal(second.banner, undefined); + assert.equal(second.native, undefined); + + assert.ok(third.native); + assert.ok(third.video); + assert.ok(third.banner); }); }); @@ -626,6 +672,10 @@ describe('Adf adapter', function () { }); }); }); + + function getRequestImps(validBidRequests) { + return JSON.parse(spec.buildRequests(validBidRequests, { refererInfo: { referer: 'page' } }).data).imp; + } }); describe('interpretResponse', function () { @@ -754,7 +804,12 @@ describe('Adf adapter', function () { imptrackers: ['imptrackers url1', 'imptrackers url2'] }, dealid: 'deal-id', - adomain: [ 'demo.com' ] + adomain: [ 'demo.com' ], + ext: { + prebid: { + type: 'native' + } + } } ] }], @@ -767,7 +822,6 @@ describe('Adf adapter', function () { { bidId: 'bidId1', params: { mid: 1000 }, - mediaType: 'native', nativeParams: { title: { required: true, len: 140 }, image: { required: false, wmin: 836, hmin: 627, w: 325, h: 300, mimes: ['image/jpg', 'image/gif'] }, @@ -899,7 +953,7 @@ describe('Adf adapter', function () { let serverResponse = { body: { seatbid: [{ - bid: [{ impid: '1', adm: '' }] + bid: [{ impid: '1', adm: '', ext: { prebid: { type: 'banner' } } }] }] } }; @@ -908,8 +962,7 @@ describe('Adf adapter', function () { bids: [ { bidId: 'bidId1', - params: { mid: 1000 }, - mediaType: 'banner' + params: { mid: 1000 } } ] }; @@ -917,6 +970,8 @@ describe('Adf adapter', function () { bids = spec.interpretResponse(serverResponse, bidRequest); assert.equal(bids.length, 1); assert.equal(bids[0].ad, ''); + assert.equal(bids[0].mediaType, 'banner'); + assert.equal(bids[0].meta.mediaType, 'banner'); }); }); @@ -925,7 +980,7 @@ describe('Adf adapter', function () { let serverResponse = { body: { seatbid: [{ - bid: [{ impid: '1', adm: '' }] + bid: [{ impid: '1', adm: '', ext: { prebid: { type: 'video' } } }] }] } }; @@ -934,8 +989,7 @@ describe('Adf adapter', function () { bids: [ { bidId: 'bidId1', - params: { mid: 1000 }, - mediaType: 'video' + params: { mid: 1000 } } ] }; @@ -943,13 +997,15 @@ describe('Adf adapter', function () { bids = spec.interpretResponse(serverResponse, bidRequest); assert.equal(bids.length, 1); assert.equal(bids[0].vastXml, ''); + assert.equal(bids[0].mediaType, 'video'); + assert.equal(bids[0].meta.mediaType, 'video'); }); it('should add renderer for outstream bids', function () { let serverResponse = { body: { seatbid: [{ - bid: [{ impid: '1', adm: '' }, { impid: '2', adm: '' }] + bid: [{ impid: '1', adm: '', ext: { prebid: { type: 'video' } } }, { impid: '2', adm: '', ext: { prebid: { type: 'video' } } }] }] } }; @@ -959,7 +1015,6 @@ describe('Adf adapter', function () { { bidId: 'bidId1', params: { mid: 1000 }, - mediaType: 'video', mediaTypes: { video: { context: 'outstream' @@ -969,7 +1024,6 @@ describe('Adf adapter', function () { { bidId: 'bidId2', params: { mid: 1000 }, - mediaType: 'video', mediaTypes: { video: { constext: 'instream' diff --git a/test/spec/modules/adgenerationBidAdapter_spec.js b/test/spec/modules/adgenerationBidAdapter_spec.js index 7eacba2f7d4..3ff5e1fb302 100644 --- a/test/spec/modules/adgenerationBidAdapter_spec.js +++ b/test/spec/modules/adgenerationBidAdapter_spec.js @@ -92,9 +92,9 @@ describe('AdgenerationAdapter', function () { } }; const data = { - banner: `posall=SSPLOC&id=58278&sdktype=0&hb=true&t=json3&sizes=300x250%2C320x100¤cy=JPY&pbver=${prebid.version}&sdkname=prebidjs&adapterver=1.1.0&imark=1&tp=https%3A%2F%2Fexample.com`, - bannerUSD: `posall=SSPLOC&id=58278&sdktype=0&hb=true&t=json3&sizes=300x250%2C320x100¤cy=USD&pbver=${prebid.version}&sdkname=prebidjs&adapterver=1.1.0&imark=1&tp=https%3A%2F%2Fexample.com`, - native: 'posall=SSPLOC&id=58278&sdktype=0&hb=true&t=json3&sizes=1x1¤cy=JPY&pbver=' + prebid.version + '&sdkname=prebidjs&adapterver=1.1.0&tp=https%3A%2F%2Fexample.com' + banner: `posall=SSPLOC&id=58278&sdktype=0&hb=true&t=json3&sizes=300x250%2C320x100¤cy=JPY&pbver=${prebid.version}&sdkname=prebidjs&adapterver=1.2.0&imark=1&tp=https%3A%2F%2Fexample.com`, + bannerUSD: `posall=SSPLOC&id=58278&sdktype=0&hb=true&t=json3&sizes=300x250%2C320x100¤cy=USD&pbver=${prebid.version}&sdkname=prebidjs&adapterver=1.2.0&imark=1&tp=https%3A%2F%2Fexample.com`, + native: 'posall=SSPLOC&id=58278&sdktype=0&hb=true&t=json3&sizes=1x1¤cy=JPY&pbver=' + prebid.version + '&sdkname=prebidjs&adapterver=1.2.0&tp=https%3A%2F%2Fexample.com' }; it('sends bid request to ENDPOINT via GET', function () { const request = spec.buildRequests(bidRequests, bidderRequest)[0]; @@ -192,6 +192,21 @@ describe('AdgenerationAdapter', function () { transactionTd: 'f76f6dfd-d64f-4645-a29f-682bac7f431a' }, }, + upperBillboard: { + bidRequest: { + bidder: 'adg', + params: { + id: '143038', // banner + marginTop: '50', + }, + adUnitCode: 'adunit-code', + sizes: [[320, 180]], + bidId: '2f6ac468a9c15e', + bidderRequestId: '14a9f773e30243', + auctionId: '4aae9f05-18c6-4fcd-80cf-282708cd584a', + transactionTd: 'f76f6dfd-d64f-4645-a29f-682bac7f431a' + }, + }, }; const serverResponse = { @@ -216,12 +231,12 @@ describe('AdgenerationAdapter', function () { dealid: 'fd5sa5fa7f', ttl: 1000, results: [ - {ad: '
'}, + {ad: '<\!DOCTYPE html>
'}, ], adomain: ['advertiserdomain.com'] }, native: { - ad: '↵ ↵ ↵ ↵ ↵
↵ ', + ad: '<\!DOCTYPE html>↵ ↵ ↵ ↵ ↵
↵ ', beacon: '', cpm: 36.0008, displaytype: '1', @@ -311,6 +326,78 @@ describe('AdgenerationAdapter', function () { creativeid: '1k2kv35vsa5r', dealid: 'fd5sa5fa7f', ttl: 1000 + }, + upperBillboard: { + 'ad': '<\!DOCTYPE html>\n \n \n \n \n \n \n \n
\n \n
\n \n', + 'beacon': '', + 'beaconurl': 'https://tg.socdm.com/bc/v3?b=Y2hzbT0yOTQsNGZiM2NkNWVpZD0xNDMwMzgmcG9zPVNTUExPQyZhZD0xMjMzMzIzLzI2MTA2MS4yNjU3OTkuMTIzMzMyMy8yMTY2NDY2LzE1NDQxMC8xNDMwMzg6U1NQTE9DOiovaWR4PTA7ZHNwaWQ9MTtkaTI9MjEzNC0xMzI4NjRfbmV3Zm9ybWF0X3Rlc3Q7ZnR5cGU9Mztwcj15TXB3O3ByYj15UTtwcm89eVE7cHJvYz1KUFk7Y3JkMnk9MTExLjkyO2NyeTJkPTAuMDA4OTM0OTUzNTM4MjQxNjAxMztwcnY9aWp6QVZtWW9wbmJUV1B0cWhtZEN1ZWRXNDd0MjU1MEtmYjFWYmI3SzsmZXg9MTYzMzMyNzU4MyZjdD0xNjMzMzI3NTgzODAzJnNyPWh0dHA-&xuid=Xm8Q8cCo5r8AAHCCMg0AAAAA&ctsv=m-ad240&seqid=be38bdb4-74a7-14a7-3439-b7f1ea8b4f33&seqtime=1633327583803&seqctx=gat3by5hZy5mdHlwZaEz&t=.gif', + 'cpm': 80, + 'creative_params': {}, + 'creativeid': 'ScaleOut_2146187', + 'dealid': '2134-132864_newformat_test', + 'displaytype': '1', + 'h': 180, + 'ids': { + 'anid': '', + 'diid': '', + 'idfa': '', + 'soc': 'Xm8Q8cCo5r8AAHCCMg0AAAAA' + }, + 'location_params': { + 'option': { + 'ad_type': 'upper_billboard' + } + }, + 'locationid': '143038', + 'results': [ + { + 'ad': '<\!DOCTYPE html>\n \n \n \n \n \n \n \n
\n \n
\n \n', + 'beacon': '', + 'beaconurl': 'https://tg.socdm.com/bc/v3?b=Y2hzbT0yOTQsNGZiM2NkNWVpZD0xNDMwMzgmcG9zPVNTUExPQyZhZD0xMjMzMzIzLzI2MTA2MS4yNjU3OTkuMTIzMzMyMy8yMTY2NDY2LzE1NDQxMC8xNDMwMzg6U1NQTE9DOiovaWR4PTA7ZHNwaWQ9MTtkaTI9MjEzNC0xMzI4NjRfbmV3Zm9ybWF0X3Rlc3Q7ZnR5cGU9Mztwcj15TXB3O3ByYj15UTtwcm89eVE7cHJvYz1KUFk7Y3JkMnk9MTExLjkyO2NyeTJkPTAuMDA4OTM0OTUzNTM4MjQxNjAxMztwcnY9aWp6QVZtWW9wbmJUV1B0cWhtZEN1ZWRXNDd0MjU1MEtmYjFWYmI3SzsmZXg9MTYzMzMyNzU4MyZjdD0xNjMzMzI3NTgzODAzJnNyPWh0dHA-&xuid=Xm8Q8cCo5r8AAHCCMg0AAAAA&ctsv=m-ad240&seqid=be38bdb4-74a7-14a7-3439-b7f1ea8b4f33&seqtime=1633327583803&seqctx=gat3by5hZy5mdHlwZaEz&t=.gif', + 'cpm': 80, + 'creative_params': {}, + 'creativeid': 'ScaleOut_2146187', + 'dealid': '2134-132864_newformat_test', + 'h': 180, + 'landing_url': 'https://supership.jp/', + 'rparams': {}, + 'scheduleid': '1233323', + 'trackers': { + 'imp': [ + 'https://tg.socdm.com/bc/v3?b=Y2hzbT0yOTQsNGZiM2NkNWVpZD0xNDMwMzgmcG9zPVNTUExPQyZhZD0xMjMzMzIzLzI2MTA2MS4yNjU3OTkuMTIzMzMyMy8yMTY2NDY2LzE1NDQxMC8xNDMwMzg6U1NQTE9DOiovaWR4PTA7ZHNwaWQ9MTtkaTI9MjEzNC0xMzI4NjRfbmV3Zm9ybWF0X3Rlc3Q7ZnR5cGU9Mztwcj15TXB3O3ByYj15UTtwcm89eVE7cHJvYz1KUFk7Y3JkMnk9MTExLjkyO2NyeTJkPTAuMDA4OTM0OTUzNTM4MjQxNjAxMztwcnY9aWp6QVZtWW9wbmJUV1B0cWhtZEN1ZWRXNDd0MjU1MEtmYjFWYmI3SzsmZXg9MTYzMzMyNzU4MyZjdD0xNjMzMzI3NTgzODAzJnNyPWh0dHA-&xuid=Xm8Q8cCo5r8AAHCCMg0AAAAA&ctsv=m-ad240&seqid=be38bdb4-74a7-14a7-3439-b7f1ea8b4f33&seqtime=1633327583803&seqctx=gat3by5hZy5mdHlwZaEz&t=.gif' + ], + 'viewable_imp': [ + 'https://tg.socdm.com/aux/inview?creative_id=2166466&ctsv=m-ad240&extra_field=idx%3D0%3Bdspid%3D1%3Bdi2%3D2134-132864_newformat_test%3Bftype%3D3%3Bprb%3D0%3Bpro%3D0%3Bproc%3DJPY%3Bcrd2y%3D111.92%3Bcry2d%3D0.0089349535382416013%3Bsspm%3D0%3Bsom%3D0.2%3Borgm%3D0%3Btechm%3D0%3Bssp_margin%3D0%3Bso_margin%3D0.2%3Borg_margin%3D0%3Btech_margin%3D0%3Bbs%3Dclassic%3B&family_id=1233323&id=143038&loglocation_id=154410&lookupname=143038%3ASSPLOC%3A*&pos=SSPLOC&schedule_id=261061.265799.1233323&seqid=be38bdb4-74a7-14a7-3439-b7f1ea8b4f33&seqtime=1633327583803&xuid=Xm8Q8cCo5r8AAHCCMg0AAAAA' + ], + 'viewable_measured': [ + 'https://tg.socdm.com/aux/measured?creative_id=2166466&ctsv=m-ad240&extra_field=idx%3D0%3Bdspid%3D1%3Bdi2%3D2134-132864_newformat_test%3Bftype%3D3%3Bprb%3D0%3Bpro%3D0%3Bproc%3DJPY%3Bcrd2y%3D111.92%3Bcry2d%3D0.0089349535382416013%3Bsspm%3D0%3Bsom%3D0.2%3Borgm%3D0%3Btechm%3D0%3Bssp_margin%3D0%3Bso_margin%3D0.2%3Borg_margin%3D0%3Btech_margin%3D0%3Bbs%3Dclassic%3B&family_id=1233323&id=143038&loglocation_id=154410&lookupname=143038%3ASSPLOC%3A*&pos=SSPLOC&schedule_id=261061.265799.1233323&seqid=be38bdb4-74a7-14a7-3439-b7f1ea8b4f33&seqtime=1633327583803&xuid=Xm8Q8cCo5r8AAHCCMg0AAAAA' + ] + }, + 'ttl': 1000, + 'vastxml': '\n \n \n SOADS\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n\n \n \n \n \n \n \n \n \n \n 00:00:15\n \n \n \n \n \n \n \n \n \n \n \n \n https://i.socdm.com/a/2/2095/2091787/20210810043037-de3e74aec30f36.mp4\n https://i.socdm.com/a/2/2095/2091788/20210810043037-6dd368dc91d507.mp4\n https://i.socdm.com/a/2/2095/2091789/20210810043037-c8eb814ddd85c4.webm\n https://i.socdm.com/a/2/2095/2091790/20210810043037-0a7f74c40268ab.webm\n \n \n \n \n \n \n', + 'vcpm': 0, + 'w': 320, + 'weight': 1 + } + ], + 'rotation': '0', + 'scheduleid': '1233323', + 'sdktype': '0', + 'trackers': { + 'imp': [ + 'https://tg.socdm.com/bc/v3?b=Y2hzbT0yOTQsNGZiM2NkNWVpZD0xNDMwMzgmcG9zPVNTUExPQyZhZD0xMjMzMzIzLzI2MTA2MS4yNjU3OTkuMTIzMzMyMy8yMTY2NDY2LzE1NDQxMC8xNDMwMzg6U1NQTE9DOiovaWR4PTA7ZHNwaWQ9MTtkaTI9MjEzNC0xMzI4NjRfbmV3Zm9ybWF0X3Rlc3Q7ZnR5cGU9Mztwcj15TXB3O3ByYj15UTtwcm89eVE7cHJvYz1KUFk7Y3JkMnk9MTExLjkyO2NyeTJkPTAuMDA4OTM0OTUzNTM4MjQxNjAxMztwcnY9aWp6QVZtWW9wbmJUV1B0cWhtZEN1ZWRXNDd0MjU1MEtmYjFWYmI3SzsmZXg9MTYzMzMyNzU4MyZjdD0xNjMzMzI3NTgzODAzJnNyPWh0dHA-&xuid=Xm8Q8cCo5r8AAHCCMg0AAAAA&ctsv=m-ad240&seqid=be38bdb4-74a7-14a7-3439-b7f1ea8b4f33&seqtime=1633327583803&seqctx=gat3by5hZy5mdHlwZaEz&t=.gif' + ], + 'viewable_imp': [ + 'https://tg.socdm.com/aux/inview?creative_id=2166466&ctsv=m-ad240&extra_field=idx%3D0%3Bdspid%3D1%3Bdi2%3D2134-132864_newformat_test%3Bftype%3D3%3Bprb%3D0%3Bpro%3D0%3Bproc%3DJPY%3Bcrd2y%3D111.92%3Bcry2d%3D0.0089349535382416013%3Bsspm%3D0%3Bsom%3D0.2%3Borgm%3D0%3Btechm%3D0%3Bssp_margin%3D0%3Bso_margin%3D0.2%3Borg_margin%3D0%3Btech_margin%3D0%3Bbs%3Dclassic%3B&family_id=1233323&id=143038&loglocation_id=154410&lookupname=143038%3ASSPLOC%3A*&pos=SSPLOC&schedule_id=261061.265799.1233323&seqid=be38bdb4-74a7-14a7-3439-b7f1ea8b4f33&seqtime=1633327583803&xuid=Xm8Q8cCo5r8AAHCCMg0AAAAA' + ], + 'viewable_measured': [ + 'https://tg.socdm.com/aux/measured?creative_id=2166466&ctsv=m-ad240&extra_field=idx%3D0%3Bdspid%3D1%3Bdi2%3D2134-132864_newformat_test%3Bftype%3D3%3Bprb%3D0%3Bpro%3D0%3Bproc%3DJPY%3Bcrd2y%3D111.92%3Bcry2d%3D0.0089349535382416013%3Bsspm%3D0%3Bsom%3D0.2%3Borgm%3D0%3Btechm%3D0%3Bssp_margin%3D0%3Bso_margin%3D0.2%3Borg_margin%3D0%3Btech_margin%3D0%3Bbs%3Dclassic%3B&family_id=1233323&id=143038&loglocation_id=154410&lookupname=143038%3ASSPLOC%3A*&pos=SSPLOC&schedule_id=261061.265799.1233323&seqid=be38bdb4-74a7-14a7-3439-b7f1ea8b4f33&seqtime=1633327583803&xuid=Xm8Q8cCo5r8AAHCCMg0AAAAA' + ] + }, + 'ttl': 1000, + 'vastxml': '\n \n \n SOADS\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n\n \n \n \n \n \n \n \n \n \n 00:00:15\n \n \n \n \n \n \n \n \n \n \n \n \n https://i.socdm.com/a/2/2095/2091787/20210810043037-de3e74aec30f36.mp4\n https://i.socdm.com/a/2/2095/2091788/20210810043037-6dd368dc91d507.mp4\n https://i.socdm.com/a/2/2095/2091789/20210810043037-c8eb814ddd85c4.webm\n https://i.socdm.com/a/2/2095/2091790/20210810043037-0a7f74c40268ab.webm\n \n \n \n \n \n \n', + 'vcpm': 0, + 'w': 320, } }, emptyAdomain: { @@ -331,12 +418,12 @@ describe('AdgenerationAdapter', function () { dealid: 'fd5sa5fa7f', ttl: 1000, results: [ - {ad: '
'}, + {ad: '<\!DOCTYPE html>
'}, ], adomain: [] }, native: { - ad: '↵ ↵ ↵ ↵ ↵
↵ ', + ad: '<\!DOCTYPE html>↵ ↵ ↵ ↵ ↵
↵ ', beacon: '', cpm: 36.0008, displaytype: '1', @@ -446,11 +533,11 @@ describe('AdgenerationAdapter', function () { dealid: 'fd5sa5fa7f', ttl: 1000, results: [ - {ad: '
'}, + {ad: '<\!DOCTYPE html>
'}, ], }, native: { - ad: '↵ ↵ ↵ ↵ ↵
↵ ', + ad: '<\!DOCTYPE html>↵ ↵ ↵ ↵ ↵
↵ ', beacon: '', cpm: 36.0008, displaytype: '1', @@ -591,7 +678,20 @@ describe('AdgenerationAdapter', function () { impressionTrackers: ['https://adg-dummy-dsp.s3-ap-northeast-1.amazonaws.com/1x1.gif'] }, mediaType: NATIVE - } + }, + upperBillboard: { + requestId: '2f6ac468a9c15e', + cpm: 80, + width: 320, + height: 180, + creativeId: 'ScaleOut_2146187', + dealId: '2134-132864_newformat_test', + currency: 'JPY', + netRevenue: true, + ttl: 1000, + ad: ``, + adomain: ['advertiserdomain.com'] + }, }, emptyAdomain: { banner: { @@ -696,46 +796,17 @@ describe('AdgenerationAdapter', function () { expect(result.length).to.equal(0); }); - it('handles banner responses', function () { - const result = spec.interpretResponse({body: serverResponse.normal.banner}, bidRequests.banner)[0]; - expect(result.requestId).to.equal(bidResponses.normal.banner.requestId); - expect(result.width).to.equal(bidResponses.normal.banner.width); - expect(result.height).to.equal(bidResponses.normal.banner.height); - expect(result.creativeId).to.equal(bidResponses.normal.banner.creativeId); - expect(result.dealId).to.equal(bidResponses.normal.banner.dealId); - expect(result.currency).to.equal(bidResponses.normal.banner.currency); - expect(result.netRevenue).to.equal(bidResponses.normal.banner.netRevenue); - expect(result.ttl).to.equal(bidResponses.normal.banner.ttl); - expect(result.ad).to.equal(bidResponses.normal.banner.ad); - expect(result.meta.advertiserDomains).deep.equal(bidResponses.normal.banner.adomain); - }); - - it('handles native responses', function () { - const result = spec.interpretResponse({body: serverResponse.normal.native}, bidRequests.native)[0]; - expect(result.requestId).to.equal(bidResponses.normal.native.requestId); - expect(result.width).to.equal(bidResponses.normal.native.width); - expect(result.height).to.equal(bidResponses.normal.native.height); - expect(result.creativeId).to.equal(bidResponses.normal.native.creativeId); - expect(result.dealId).to.equal(bidResponses.normal.native.dealId); - expect(result.currency).to.equal(bidResponses.normal.native.currency); - expect(result.netRevenue).to.equal(bidResponses.normal.native.netRevenue); - expect(result.ttl).to.equal(bidResponses.normal.native.ttl); - expect(result.native.title).to.equal(bidResponses.normal.native.native.title); - expect(result.native.image.url).to.equal(bidResponses.normal.native.native.image.url); - expect(result.native.image.height).to.equal(bidResponses.normal.native.native.image.height); - expect(result.native.image.width).to.equal(bidResponses.normal.native.native.image.width); - expect(result.native.icon.url).to.equal(bidResponses.normal.native.native.icon.url); - expect(result.native.icon.width).to.equal(bidResponses.normal.native.native.icon.width); - expect(result.native.icon.height).to.equal(bidResponses.normal.native.native.icon.height); - expect(result.native.sponsoredBy).to.equal(bidResponses.normal.native.native.sponsoredBy); - expect(result.native.body).to.equal(bidResponses.normal.native.native.body); - expect(result.native.cta).to.equal(bidResponses.normal.native.native.cta); - expect(decodeURIComponent(result.native.privacyLink)).to.equal(bidResponses.normal.native.native.privacyLink); - expect(result.native.clickUrl).to.equal(bidResponses.normal.native.native.clickUrl); - expect(result.native.impressionTrackers[0]).to.equal(bidResponses.normal.native.native.impressionTrackers[0]); - expect(result.native.clickTrackers[0]).to.equal(bidResponses.normal.native.native.clickTrackers[0]); - expect(result.mediaType).to.equal(bidResponses.normal.native.mediaType); - expect(result.meta.advertiserDomains).deep.equal(bidResponses.normal.native.adomain); + it('handles ADGBrowserM responses', function () { + const result = spec.interpretResponse({body: serverResponse.normal.upperBillboard}, bidRequests.upperBillboard)[0]; + expect(result.requestId).to.equal(bidResponses.normal.upperBillboard.requestId); + expect(result.width).to.equal(bidResponses.normal.upperBillboard.width); + expect(result.height).to.equal(bidResponses.normal.upperBillboard.height); + expect(result.creativeId).to.equal(bidResponses.normal.upperBillboard.creativeId); + expect(result.dealId).to.equal(bidResponses.normal.upperBillboard.dealId); + expect(result.currency).to.equal(bidResponses.normal.upperBillboard.currency); + expect(result.netRevenue).to.equal(bidResponses.normal.upperBillboard.netRevenue); + expect(result.ttl).to.equal(bidResponses.normal.upperBillboard.ttl); + expect(result.ad).to.equal(bidResponses.normal.upperBillboard.ad); }); it('handles banner responses for empty adomain', function () { diff --git a/test/spec/modules/adhashBidAdapter_spec.js b/test/spec/modules/adhashBidAdapter_spec.js index eda164da852..6c214b84928 100644 --- a/test/spec/modules/adhashBidAdapter_spec.js +++ b/test/spec/modules/adhashBidAdapter_spec.js @@ -77,7 +77,7 @@ describe('adhashBidAdapter', function () { ); expect(result.length).to.equal(1); expect(result[0].method).to.equal('POST'); - expect(result[0].url).to.equal('https://bidder.adhash.org/rtb?version=1.0&prebid=true'); + expect(result[0].url).to.equal('https://bidder.adhash.com/rtb?version=1.0&prebid=true&publisher=0xc3b09b27e9c6ef73957901aa729b9e69e5bbfbfb'); expect(result[0].bidRequest).to.equal(bidRequest); expect(result[0].data).to.have.property('timezone'); expect(result[0].data).to.have.property('location'); @@ -93,7 +93,7 @@ describe('adhashBidAdapter', function () { const result = spec.buildRequests([ bidRequest ], { gdprConsent: true }); expect(result.length).to.equal(1); expect(result[0].method).to.equal('POST'); - expect(result[0].url).to.equal('https://bidder.adhash.org/rtb?version=1.0&prebid=true'); + expect(result[0].url).to.equal('https://bidder.adhash.com/rtb?version=1.0&prebid=true&publisher=0xc3b09b27e9c6ef73957901aa729b9e69e5bbfbfb'); expect(result[0].bidRequest).to.equal(bidRequest); expect(result[0].data).to.have.property('timezone'); expect(result[0].data).to.have.property('location'); diff --git a/test/spec/modules/adheseBidAdapter_spec.js b/test/spec/modules/adheseBidAdapter_spec.js index 78ca25d6be2..3fe0a62b2a0 100644 --- a/test/spec/modules/adheseBidAdapter_spec.js +++ b/test/spec/modules/adheseBidAdapter_spec.js @@ -1,5 +1,6 @@ import {expect} from 'chai'; import {spec} from 'modules/adheseBidAdapter.js'; +import {config} from 'src/config.js'; const BID_ID = 456; const TTL = 360; @@ -131,12 +132,21 @@ describe('AdheseAdapter', function () { expect(JSON.parse(req.data)).to.not.have.key('eids'); }); - it('should request vast content as url', function () { + it('should request vast content as url by default', function () { let req = spec.buildRequests([ minimalBid() ], bidderRequest); expect(JSON.parse(req.data).vastContentAsUrl).to.equal(true); }); + it('should request vast content as markup when configured', function () { + sinon.stub(config, 'getConfig').withArgs('adhese').returns({ vastContentAsUrl: false }); + + let req = spec.buildRequests([ minimalBid() ], bidderRequest); + + expect(JSON.parse(req.data).vastContentAsUrl).to.equal(false); + config.getConfig.restore(); + }); + it('should include bids', function () { let bid = minimalBid(); let req = spec.buildRequests([ bid ], bidderRequest); @@ -155,6 +165,22 @@ describe('AdheseAdapter', function () { expect(req.url).to.equal('https://ads-demo.adhese.com/json'); }); + + it('should include params specified in the config', function () { + sinon.stub(config, 'getConfig').withArgs('adhese').returns({ globalTargets: { 'tl': [ 'all' ] } }); + let req = spec.buildRequests([ minimalBid() ], bidderRequest); + + expect(JSON.parse(req.data).parameters).to.deep.include({ 'tl': [ 'all' ] }); + config.getConfig.restore(); + }); + + it('should give priority to bid params over config params', function () { + sinon.stub(config, 'getConfig').withArgs('adhese').returns({ globalTargets: { 'xt': ['CONFIG_CONSENT_STRING'] } }); + let req = spec.buildRequests([ minimalBid() ], bidderRequest); + + expect(JSON.parse(req.data).parameters).to.deep.include({ 'xt': [ 'CONSENT_STRING' ] }); + config.getConfig.restore(); + }); }); describe('interpretResponse', () => { diff --git a/test/spec/modules/adlooxAnalyticsAdapter_spec.js b/test/spec/modules/adlooxAnalyticsAdapter_spec.js index 01ab3afcf2f..b87fd20adc2 100644 --- a/test/spec/modules/adlooxAnalyticsAdapter_spec.js +++ b/test/spec/modules/adlooxAnalyticsAdapter_spec.js @@ -3,7 +3,7 @@ import analyticsAdapter, { command as analyticsCommand, COMMAND } from 'modules/ import { AUCTION_COMPLETED } from 'src/auction.js'; import { expect } from 'chai'; import events from 'src/events.js'; -import { EVENTS } from 'src/constants.json'; +import CONSTANTS from 'src/constants.json'; import * as utils from 'src/utils.js'; import { loadExternalScriptStub } from 'test/mocks/adloaderStub.js'; @@ -143,10 +143,10 @@ describe('Adloox Analytics Adapter', function () { return arg.tagName === 'LINK' && arg.getAttribute('rel') === 'preload' && arg.getAttribute('as') === 'script' && href_uri.href === uri.href; }; - events.emit(EVENTS.AUCTION_END, auctionDetails); + events.emit(CONSTANTS.EVENTS.AUCTION_END, auctionDetails); expect(insertElementStub.calledWith(sinon.match(isLinkPreloadAsScript))).to.true; - events.emit(EVENTS.AUCTION_END, auctionDetails); + events.emit(CONSTANTS.EVENTS.AUCTION_END, auctionDetails); expect(insertElementStub.callCount).to.equal(1); done(); @@ -167,7 +167,7 @@ describe('Adloox Analytics Adapter', function () { const querySelectorStub = sandbox.stub(document, 'querySelector'); querySelectorStub.withArgs(`#${bid.adUnitCode}`).returns(slot); - events.emit(EVENTS.BID_WON, bid); + events.emit(CONSTANTS.EVENTS.BID_WON, bid); const [urlInserted, moduleCode] = loadExternalScriptStub.getCall(0).args; @@ -196,7 +196,7 @@ describe('Adloox Analytics Adapter', function () { const querySelectorStub = sandbox.stub(document, 'querySelector'); querySelectorStub.withArgs(`#${bid.adUnitCode}`).returns(slot); - events.emit(EVENTS.BID_WON, bidIgnore); + events.emit(CONSTANTS.EVENTS.BID_WON, bidIgnore); expect(parent.querySelector('script')).is.null; @@ -238,7 +238,7 @@ describe('Adloox Analytics Adapter', function () { it('should inject tracking event', function (done) { const data = { - eventType: EVENTS.BID_WON, + eventType: CONSTANTS.EVENTS.BID_WON, args: bid }; diff --git a/test/spec/modules/admanBidAdapter_spec.js b/test/spec/modules/admanBidAdapter_spec.js index fd467f074ac..33e6a8a89b9 100644 --- a/test/spec/modules/admanBidAdapter_spec.js +++ b/test/spec/modules/admanBidAdapter_spec.js @@ -175,13 +175,13 @@ describe('AdmanAdapter', function () { }); describe('getUserSyncs', function () { - let userSync = spec.getUserSyncs(); + let userSync = spec.getUserSyncs({}); it('Returns valid URL and type', function () { expect(userSync).to.be.an('array').with.lengthOf(1); expect(userSync[0].type).to.exist; expect(userSync[0].url).to.exist; expect(userSync[0].type).to.be.equal('image'); - expect(userSync[0].url).to.be.equal('https://pub.admanmedia.com/?c=o&m=sync'); + expect(userSync[0].url).to.be.equal('https://pub.admanmedia.com/image?pbjs=1&coppa=0'); }); }); }); diff --git a/test/spec/modules/adnuntiusBidAdapter_spec.js b/test/spec/modules/adnuntiusBidAdapter_spec.js index 20dbaad1cc6..3b6ad8d0912 100644 --- a/test/spec/modules/adnuntiusBidAdapter_spec.js +++ b/test/spec/modules/adnuntiusBidAdapter_spec.js @@ -19,6 +19,7 @@ describe('adnuntiusBidAdapter', function () { }); const tzo = new Date().getTimezoneOffset(); const ENDPOINT_URL = `${URL}${tzo}&format=json&userId=${usi}`; + const ENDPOINT_URL_NOCOOKIE = `${URL}${tzo}&format=json&userId=${usi}&noCookies=true`; const ENDPOINT_URL_SEGMENTS = `${URL}${tzo}&format=json&segments=segment1,segment2,segment3&userId=${usi}`; const ENDPOINT_URL_CONSENT = `${URL}${tzo}&format=json&consentString=consentString&userId=${usi}`; const adapter = newBidder(spec); @@ -251,6 +252,24 @@ describe('adnuntiusBidAdapter', function () { expect(request[0]).to.have.property('url') expect(request[0].url).to.equal(ENDPOINT_URL_SEGMENTS); }); + + it('should user user ID if present in ortb2.user.id field', function () { + config.setBidderConfig({ + bidders: ['adnuntius', 'other'], + config: { + ortb2: { + user: { + id: usi + } + } + } + }); + + const request = config.runWithBidder('adnuntius', () => spec.buildRequests(bidRequests)); + expect(request.length).to.equal(1); + expect(request[0]).to.have.property('url') + expect(request[0].url).to.equal(ENDPOINT_URL); + }); }); describe('user privacy', function () { @@ -269,6 +288,22 @@ describe('adnuntiusBidAdapter', function () { }); }); + describe('use cookie', function () { + it('should send noCookie in url if set to false.', function () { + config.setBidderConfig({ + bidders: ['adnuntius'], + config: { + useCookie: false + } + }); + + const request = config.runWithBidder('adnuntius', () => spec.buildRequests(bidRequests)); + expect(request.length).to.equal(1); + expect(request[0]).to.have.property('url') + expect(request[0].url).to.equal(ENDPOINT_URL_NOCOOKIE); + }); + }); + describe('interpretResponse', function () { it('should return valid response when passed valid server response', function () { const interpretedResponse = spec.interpretResponse(serverResponse, singleBidRequest); diff --git a/test/spec/modules/adomikAnalyticsAdapter_spec.js b/test/spec/modules/adomikAnalyticsAdapter_spec.js index 1414b2402b9..93822d04c88 100644 --- a/test/spec/modules/adomikAnalyticsAdapter_spec.js +++ b/test/spec/modules/adomikAnalyticsAdapter_spec.js @@ -1,5 +1,6 @@ import adomikAnalytics from 'modules/adomikAnalyticsAdapter.js'; -import {expect} from 'chai'; +import { expect } from 'chai'; + let events = require('src/events'); let adapterManager = require('src/adapterManager').default; let constants = require('src/constants.json'); @@ -8,6 +9,7 @@ describe('Adomik Prebid Analytic', function () { let sendEventStub; let sendWonEventStub; let clock; + before(function () { clock = sinon.useFakeTimers(); }); @@ -43,6 +45,8 @@ describe('Adomik Prebid Analytic', function () { const initOptions = { id: '123456', url: 'testurl', + testId: '12345', + testValue: '1000' }; const bid = { @@ -69,6 +73,9 @@ describe('Adomik Prebid Analytic', function () { expect(adomikAnalytics.currentContext).to.deep.equal({ uid: '123456', url: 'testurl', + sampling: undefined, + testId: '12345', + testValue: '1000', id: '', timeouted: false }); @@ -79,6 +86,9 @@ describe('Adomik Prebid Analytic', function () { expect(adomikAnalytics.currentContext).to.deep.equal({ uid: '123456', url: 'testurl', + sampling: undefined, + testId: '12345', + testValue: '1000', id: 'test-test-test', timeouted: false }); @@ -91,7 +101,7 @@ describe('Adomik Prebid Analytic', function () { type: 'request', event: { bidder: 'BIDDERTEST', - placementCode: 'placementtest', + placementCode: '0000', } }); diff --git a/test/spec/modules/adplusBidAdapter_spec.js b/test/spec/modules/adplusBidAdapter_spec.js new file mode 100644 index 00000000000..840d86c80f1 --- /dev/null +++ b/test/spec/modules/adplusBidAdapter_spec.js @@ -0,0 +1,213 @@ +import {expect} from 'chai'; +import {spec, BIDDER_CODE, ADPLUS_ENDPOINT, } from 'modules/adplusBidAdapter.js'; +import {newBidder} from 'src/adapters/bidderFactory.js'; + +describe('AplusBidAdapter', function () { + const adapter = newBidder(spec); + + describe('inherited functions', function () { + it('exists and is a function', function () { + expect(adapter.callBids).to.be.exist.and.to.be.a('function'); + }); + }); + + describe('isBidRequestValid', function () { + it('should return true when required params found', function () { + let validRequest = { + mediaTypes: { + banner: { + sizes: [[300, 250]] + } + }, + params: { + inventoryId: '30', + adUnitId: '1', + } + }; + expect(spec.isBidRequestValid(validRequest)).to.equal(true); + }); + + it('should return false when required params are not passed', function () { + let validRequest = { + mediaTypes: { + banner: { + sizes: [[300, 250]] + } + }, + params: { + inventoryId: '30', + } + }; + expect(spec.isBidRequestValid(validRequest)).to.equal(false); + }); + + it('should return false when required param types are wrong', function () { + let validRequest = { + mediaTypes: { + banner: { + sizes: [[300, 250]] + } + }, + params: { + inventoryId: 30, + adUnitId: '1', + } + }; + expect(spec.isBidRequestValid(validRequest)).to.equal(false); + }); + + it('should return false when size is not exists', function () { + let validRequest = { + params: { + inventoryId: 30, + adUnitId: '1', + } + }; + expect(spec.isBidRequestValid(validRequest)).to.equal(false); + }); + + it('should return false when size is wrong', function () { + let validRequest = { + mediaTypes: { + banner: { + sizes: [[300]] + } + }, + params: { + inventoryId: 30, + adUnitId: '1', + } + }; + expect(spec.isBidRequestValid(validRequest)).to.equal(false); + }); + }); + + describe('buildRequests', function () { + let validRequest = [ + { + bidder: BIDDER_CODE, + mediaTypes: { + banner: { + sizes: [[300, 250]] + } + }, + params: { + inventoryId: '-1', + adUnitId: '-3', + }, + bidId: '2bdcb0b203c17d' + }, + ]; + + let bidderRequest = { + refererInfo: { + referer: 'https://test.domain' + } + }; + + it('bidRequest HTTP method', function () { + const request = spec.buildRequests(validRequest, bidderRequest); + expect(request[0].method).to.equal('GET'); + }); + + it('bidRequest url', function () { + const request = spec.buildRequests(validRequest, bidderRequest); + expect(request[0].url).to.equal(ADPLUS_ENDPOINT); + }); + + it('tests bidRequest data is clean and has the right values', function () { + const request = spec.buildRequests(validRequest, bidderRequest); + + expect(request[0].data.bidId).to.equal('2bdcb0b203c17d'); + expect(request[0].data.inventoryId).to.equal('-1'); + expect(request[0].data.adUnitId).to.equal('-3'); + expect(request[0].data.adUnitWidth).to.equal(300); + expect(request[0].data.adUnitHeight).to.equal(250); + expect(request[0].data.sdkVersion).to.equal('1'); + expect(typeof request[0].data.session).to.equal('string'); + expect(request[0].data.session).length(36); + expect(request[0].data.interstitial).to.equal(0); + expect(request[0].data).to.not.have.deep.property('extraData'); + expect(request[0].data).to.not.have.deep.property('yearOfBirth'); + expect(request[0].data).to.not.have.deep.property('gender'); + expect(request[0].data).to.not.have.deep.property('categories'); + expect(request[0].data).to.not.have.deep.property('latitude'); + expect(request[0].data).to.not.have.deep.property('longitude'); + }); + }); + + describe('interpretResponse', function () { + const requestData = { + language: window.navigator.language, + screenWidth: 1440, + screenHeight: 900, + sdkVersion: '1', + inventoryId: '-1', + adUnitId: '-3', + adUnitWidth: 300, + adUnitHeight: 250, + domain: 'tassandigi.com', + pageUrl: 'https%3A%2F%2Ftassandigi.com%2Fserafettin%2Fads.html', + interstitial: 0, + session: '1c02db03-5289-932a-93af-7b4022611fec', + token: '1c02db03-5289-937a-93df-7b4022611fec', + secure: 1, + bidId: '2bdcb0b203c17d', + }; + const bidRequest = { + 'method': 'GET', + 'url': ADPLUS_ENDPOINT, + 'data': requestData, + }; + + const bidResponse = { + body: [ + { + 'ad': '
ad
', + 'advertiserDomains': [ + 'advertiser.com' + ], + 'categoryIDs': [ + 'IAB-111' + ], + 'cpm': 3.57, + 'creativeID': '1', + 'currency': 'TRY', + 'dealID': '1', + 'height': 300, + 'mediaType': 'banner', + 'netRevenue': true, + 'requestID': '2bdcb0b203c17d', + 'ttl': 300, + 'width': 250 + } + ], + headers: {} + }; + + const emptyBidResponse = { + body: null, + }; + + it('returns an empty array when the result body is not valid', function () { + const result = spec.interpretResponse(emptyBidResponse, bidRequest); + expect(result).to.deep.equal([]); + }); + + it('result is correct', function () { + const result = spec.interpretResponse(bidResponse, bidRequest); + expect(result[0].requestId).to.equal('2bdcb0b203c17d'); + expect(result[0].cpm).to.equal(3.57); + expect(result[0].width).to.equal(250); + expect(result[0].height).to.equal(300); + expect(result[0].creativeId).to.equal('1'); + expect(result[0].currency).to.equal('TRY'); + expect(result[0].dealId).to.equal('1'); + expect(result[0].mediaType).to.equal('banner'); + expect(result[0].netRevenue).to.equal(true); + expect(result[0].ttl).to.equal(300); + expect(result[0].meta.advertiserDomains).to.deep.equal(['advertiser.com']); + expect(result[0].meta.secondaryCatIds).to.deep.equal(['IAB-111']); + }); + }); +}); diff --git a/test/spec/modules/adqueryIdSystem_spec.js b/test/spec/modules/adqueryIdSystem_spec.js new file mode 100644 index 00000000000..ab98b253b33 --- /dev/null +++ b/test/spec/modules/adqueryIdSystem_spec.js @@ -0,0 +1,74 @@ +import { adqueryIdSubmodule, storage } from 'modules/adqueryIdSystem.js'; +import { server } from 'test/mocks/xhr.js'; +import {amxIdSubmodule} from '../../../modules/amxIdSystem'; +import * as utils from '../../../src/utils'; + +const config = { + storage: { + type: 'html5', + }, +}; + +describe('AdqueryIdSystem', function () { + describe('qid submodule', () => { + it('should expose a "name" property containing qid', () => { + expect(adqueryIdSubmodule.name).to.equal('qid'); + }); + + it('should expose a "gvlid" property containing the GVL ID 902', () => { + expect(adqueryIdSubmodule.gvlid).to.equal(902); + }); + }); + + describe('getId', function() { + let getDataFromLocalStorageStub; + + beforeEach(function() { + getDataFromLocalStorageStub = sinon.stub(storage, 'getDataFromLocalStorage'); + }); + + afterEach(function () { + getDataFromLocalStorageStub.restore(); + }); + + it('gets a adqueryId', function() { + const config = { + params: {} + }; + const callbackSpy = sinon.spy(); + const callback = adqueryIdSubmodule.getId(config).callback; + callback(callbackSpy); + const request = server.requests[0]; + expect(request.url).to.eq(`https://bidder.adquery.io/prebid/qid`); + request.respond(200, { 'Content-Type': 'application/json' }, JSON.stringify({ qid: 'qid' })); + expect(callbackSpy.lastCall.lastArg).to.deep.equal({qid: 'qid'}); + }); + + it('gets a cached adqueryId', function() { + const config = { + params: {} + }; + getDataFromLocalStorageStub.withArgs('qid').returns('qid'); + + const callbackSpy = sinon.spy(); + const callback = adqueryIdSubmodule.getId(config).callback; + callback(callbackSpy); + expect(callbackSpy.lastCall.lastArg).to.deep.equal({qid: 'qid'}); + }); + + it('allows configurable id url', function() { + const config = { + params: { + url: 'https://bidder.adquery.io' + } + }; + const callbackSpy = sinon.spy(); + const callback = adqueryIdSubmodule.getId(config).callback; + callback(callbackSpy); + const request = server.requests[0]; + expect(request.url).to.eq('https://bidder.adquery.io'); + request.respond(200, { 'Content-Type': 'application/json' }, JSON.stringify({ qid: 'testqid' })); + expect(callbackSpy.lastCall.lastArg).to.deep.equal({qid: 'testqid'}); + }); + }); +}); diff --git a/test/spec/modules/adtelligentBidAdapter_spec.js b/test/spec/modules/adtelligentBidAdapter_spec.js index 4cfb367efb3..a004d888268 100644 --- a/test/spec/modules/adtelligentBidAdapter_spec.js +++ b/test/spec/modules/adtelligentBidAdapter_spec.js @@ -17,6 +17,7 @@ const aliasEP = { mediafuse: 'https://ghb.hbmp.mediafuse.com/v2/auction/', navelix: 'https://ghb.hb.navelix.com/v2/auction/', bidsxchange: 'https://ghb.hbd.bidsxchange.com/v2/auction/', + streamkey: 'https://ghb.hb.streamkey.net/v2/auction/', }; const DEFAULT_ADATPER_REQ = { bidderCode: 'adtelligent' }; diff --git a/test/spec/modules/adxcgBidAdapter_spec.js b/test/spec/modules/adxcgBidAdapter_spec.js index 551d50b60e7..7721295572c 100644 --- a/test/spec/modules/adxcgBidAdapter_spec.js +++ b/test/spec/modules/adxcgBidAdapter_spec.js @@ -1,653 +1,902 @@ -import {expect} from 'chai'; +// jshint esversion: 6, es3: false, node: true +import {assert} from 'chai'; import {spec} from 'modules/adxcgBidAdapter.js'; -import {deepClone, parseUrl} from 'src/utils.js'; -import * as utils from '../../../src/utils.js'; - -describe('AdxcgAdapter', function () { - let bidBanner = { - bidder: 'adxcg', - params: { - adzoneid: '1' - }, - adUnitCode: 'adunit-code', - mediaTypes: { - banner: { - sizes: [ - [300, 250], - [640, 360], - [1, 1] - ] - } - }, - bidId: '84ab500420319d', - bidderRequestId: '7101db09af0db2', - auctionId: '1d1a030790a475' - }; - - let bidVideo = { - bidder: 'adxcg', - params: { - adzoneid: '20', - video: { - api: [2], - maxduration: 30 - } - }, - mediaTypes: { - video: { - context: 'instream', - playerSize: [[640, 480]], - protocols: [1, 2], - mimes: ['video/mp4'], - } - }, - adUnitCode: 'adunit-code', - bidId: '84ab500420319d', - bidderRequestId: '7101db09af0db2', - auctionId: '1d1a030790a475' - }; - - let bidNative = { - bidder: 'adxcg', - params: { - adzoneid: '2379' - }, - mediaTypes: { - native: { - image: { - sendId: false, - required: true, - sizes: [80, 80] - }, - title: { - required: true, - len: 75 - }, - body: { - required: true, - len: 200 - }, - sponsoredBy: { - required: false, - len: 20 - } - } - }, - adUnitCode: 'adunit-code', - bidId: '84ab500420319d', - bidderRequestId: '7101db09af0db2', - auctionId: '1d1a030790a475' - }; +import {config} from 'src/config.js'; +import {createEidsArray} from 'modules/userId/eids.js'; +const utils = require('src/utils'); - describe('isBidRequestValid', function () { - it('should return true when required params found bidNative', function () { - expect(spec.isBidRequestValid(bidNative)).to.equal(true); - }); +describe('Adxcg adapter', function () { + let bids = []; - it('should return true when required params found bidVideo', function () { - expect(spec.isBidRequestValid(bidVideo)).to.equal(true); - }); + describe('isBidRequestValid', function () { + let bid = { + 'bidder': 'adxcg', + 'params': { + 'adzoneid': '19910113' + } + }; - it('should return true when required params found bidBanner', function () { - expect(spec.isBidRequestValid(bidBanner)).to.equal(true); - }); + it('should return true when required params found', function () { + assert(spec.isBidRequestValid(bid)); - it('should return false when required params not found', function () { - expect(spec.isBidRequestValid({})).to.be.false; + bid.params = { + adzoneid: 4332, + }; + assert(spec.isBidRequestValid(bid)); }); - it('should return false when required params are not passed', function () { - let bid = Object.assign({}, bidBanner); - delete bid.params; + it('should return false when required params are missing', function () { bid.params = {}; - expect(spec.isBidRequestValid(bid)).to.equal(false); - }); + assert.isFalse(spec.isBidRequestValid(bid)); - it('should return true when required video params not found', function () { - const simpleVideo = JSON.parse(JSON.stringify(bidVideo)); - simpleVideo.params.adzoneid = 123; - expect(spec.isBidRequestValid(simpleVideo)).to.be.false; + bid.params = { + mname: 'some-placement' + }; + assert.isFalse(spec.isBidRequestValid(bid)); + + bid.params = { + inv: 1234 + }; + assert.isFalse(spec.isBidRequestValid(bid)); }); }); - describe('request function http', function () { - it('creates a valid adxcg request url bidBanner', function () { - let request = spec.buildRequests([bidBanner]); - expect(request).to.exist; - expect(request.method).to.equal('GET'); - let parsedRequestUrl = parseUrl(request.url); - expect(parsedRequestUrl.hostname).to.equal('hbps.adxcg.net'); - expect(parsedRequestUrl.pathname).to.equal('/get/adi'); - - let query = parsedRequestUrl.search; - expect(query.renderformat).to.equal('javascript'); - expect(query.ver).to.equal('r20210330PB40'); - expect(query.source).to.equal('pbjs10'); - expect(query.pbjs).to.equal('$prebid.version$'); - expect(query.adzoneid).to.equal('1'); - expect(query.format).to.equal('300x250|640x360|1x1'); - expect(query.jsonp).to.be.undefined; - expect(query.prebidBidIds).to.equal('84ab500420319d'); - expect(query.bidfloors).to.equal('0'); - - expect(query).to.have.property('secure'); - expect(query).to.have.property('uw'); - expect(query).to.have.property('uh'); - expect(query).to.have.property('dpr'); - expect(query).to.have.property('bt'); - expect(query).to.have.property('cookies'); - expect(query).to.have.property('tz'); - expect(query).to.have.property('dt'); - expect(query).to.have.property('iob'); - expect(query).to.have.property('rndid'); - expect(query).to.have.property('ref'); - expect(query).to.have.property('url'); + describe('buildRequests', function () { + beforeEach(function () { + config.resetConfig(); }); + it('should send request with correct structure', function () { + let validBidRequests = [{ + bidId: 'bidId', + params: { + adzoneid: '19910113' + } + }]; + let request = spec.buildRequests(validBidRequests, {refererInfo: {referer: 'page'}}); - it('creates a valid adxcg request url bidVideo', function () { - let request = spec.buildRequests([bidVideo]); - expect(request).to.exist; - expect(request.method).to.equal('GET'); - let parsedRequestUrl = parseUrl(request.url); - expect(parsedRequestUrl.hostname).to.equal('hbps.adxcg.net'); - expect(parsedRequestUrl.pathname).to.equal('/get/adi'); - - let query = parsedRequestUrl.search; - // general part - expect(query.renderformat).to.equal('javascript'); - expect(query.ver).to.equal('r20210330PB40'); - expect(query.source).to.equal('pbjs10'); - expect(query.pbjs).to.equal('$prebid.version$'); - expect(query.adzoneid).to.equal('20'); - expect(query.format).to.equal('640x480'); - expect(query.jsonp).to.be.undefined; - expect(query.prebidBidIds).to.equal('84ab500420319d'); - expect(query.bidfloors).to.equal('0'); - - expect(query).to.have.property('secure'); - expect(query).to.have.property('uw'); - expect(query).to.have.property('uh'); - expect(query).to.have.property('dpr'); - expect(query).to.have.property('bt'); - expect(query).to.have.property('cookies'); - expect(query).to.have.property('tz'); - expect(query).to.have.property('dt'); - expect(query).to.have.property('iob'); - expect(query).to.have.property('rndid'); - expect(query).to.have.property('ref'); - expect(query).to.have.property('url'); - - // video specific part - expect(query['video.maxduration.0']).to.equal('30'); - expect(query['video.mimes.0']).to.equal('video/mp4'); - expect(query['video.context.0']).to.equal('instream'); + assert.equal(request.method, 'POST'); + assert.equal(request.url, 'https://pbc.adxcg.net/rtb/ortb/pbc?adExchangeId=1'); + assert.deepEqual(request.options, {contentType: 'application/json'}); + assert.ok(request.data); }); - it('creates a valid adxcg request url bidNative', function () { - let request = spec.buildRequests([bidNative]); - expect(request).to.exist; - expect(request.method).to.equal('GET'); - let parsedRequestUrl = parseUrl(request.url); - expect(parsedRequestUrl.hostname).to.equal('hbps.adxcg.net'); - expect(parsedRequestUrl.pathname).to.equal('/get/adi'); - - let query = parsedRequestUrl.search; - expect(query.renderformat).to.equal('javascript'); - expect(query.ver).to.equal('r20210330PB40'); - expect(query.source).to.equal('pbjs10'); - expect(query.pbjs).to.equal('$prebid.version$'); - expect(query.adzoneid).to.equal('2379'); - expect(query.format).to.equal('0x0'); - expect(query.jsonp).to.be.undefined; - expect(query.prebidBidIds).to.equal('84ab500420319d'); - expect(query.bidfloors).to.equal('0'); - - expect(query).to.have.property('secure'); - expect(query).to.have.property('uw'); - expect(query).to.have.property('uh'); - expect(query).to.have.property('dpr'); - expect(query).to.have.property('bt'); - expect(query).to.have.property('cookies'); - expect(query).to.have.property('tz'); - expect(query).to.have.property('dt'); - expect(query).to.have.property('iob'); - expect(query).to.have.property('rndid'); - expect(query).to.have.property('ref'); - expect(query).to.have.property('url'); - }); - }); + describe('user privacy', function () { + it('should send GDPR Consent data to exchange if gdprApplies', function () { + let validBidRequests = [{bidId: 'bidId', params: {test: 1}}]; + let bidderRequest = { + gdprConsent: {gdprApplies: true, consentString: 'consentDataString'}, + refererInfo: {referer: 'page'} + }; + let request = JSON.parse(spec.buildRequests(validBidRequests, bidderRequest).data); + + assert.equal(request.user.ext.consent, bidderRequest.gdprConsent.consentString); + assert.equal(request.regs.ext.gdpr, bidderRequest.gdprConsent.gdprApplies); + assert.equal(typeof request.regs.ext.gdpr, 'number'); + }); - describe('gdpr compliance', function () { - it('should send GDPR Consent data if gdprApplies', function () { - let request = spec.buildRequests([bidBanner], { - gdprConsent: { - gdprApplies: true, - consentString: 'consentDataString' - } + it('should send gdpr as number', function () { + let validBidRequests = [{bidId: 'bidId', params: {test: 1}}]; + let bidderRequest = { + gdprConsent: {gdprApplies: true, consentString: 'consentDataString'}, + refererInfo: {referer: 'page'} + }; + let request = JSON.parse(spec.buildRequests(validBidRequests, bidderRequest).data); + + assert.equal(typeof request.regs.ext.gdpr, 'number'); + assert.equal(request.regs.ext.gdpr, 1); }); - let parsedRequestUrl = parseUrl(request.url); - let query = parsedRequestUrl.search; - expect(query.gdpr).to.equal('1'); - expect(query.gdpr_consent).to.equal('consentDataString'); - }); + it('should send CCPA Consent data to exchange', function () { + let validBidRequests = [{bidId: 'bidId', params: {test: 1}}]; + let bidderRequest = {uspConsent: '1YA-', refererInfo: {referer: 'page'}}; + let request = JSON.parse(spec.buildRequests(validBidRequests, bidderRequest).data); - it('should not send GDPR Consent data if gdprApplies is false or undefined', function () { - let request = spec.buildRequests([bidBanner], { - gdprConsent: { - gdprApplies: false, - consentString: 'consentDataString' - } + assert.equal(request.regs.ext.us_privacy, '1YA-'); + + bidderRequest = { + uspConsent: '1YA-', + gdprConsent: {gdprApplies: true, consentString: 'consentDataString'}, + refererInfo: {referer: 'page'} + }; + request = JSON.parse(spec.buildRequests(validBidRequests, bidderRequest).data); + + assert.equal(request.regs.ext.us_privacy, '1YA-'); + assert.equal(request.user.ext.consent, 'consentDataString'); + assert.equal(request.regs.ext.gdpr, 1); }); - let parsedRequestUrl = parseUrl(request.url); - let query = parsedRequestUrl.search; - expect(query.gdpr).to.be.undefined; - expect(query.gdpr_consent).to.be.undefined; + it('should not send GDPR Consent data to adxcg if gdprApplies is undefined', function () { + let validBidRequests = [{ + bidId: 'bidId', + params: {siteId: 'siteId'} + }]; + let bidderRequest = { + gdprConsent: {gdprApplies: false, consentString: 'consentDataString'}, + refererInfo: {referer: 'page'} + }; + let request = JSON.parse(spec.buildRequests(validBidRequests, bidderRequest).data); + + assert.equal(request.user.ext.consent, 'consentDataString'); + assert.equal(request.regs.ext.gdpr, 0); + + bidderRequest = {gdprConsent: {consentString: 'consentDataString'}, refererInfo: {referer: 'page'}}; + request = JSON.parse(spec.buildRequests(validBidRequests, bidderRequest).data); + + assert.equal(request.user, undefined); + assert.equal(request.regs, undefined); + }); + it('should send default GDPR Consent data to exchange', function () { + let validBidRequests = [{ + bidId: 'bidId', + params: {siteId: 'siteId'} + }]; + let request = JSON.parse(spec.buildRequests(validBidRequests, {refererInfo: {referer: 'page'}}).data); + + assert.equal(request.user, undefined); + assert.equal(request.regs, undefined); + }); }); - }); - describe('userid pubcid should be passed to querystring', function () { - let bidderRequests = {}; - let bid = deepClone([bidBanner]); - bid[0].userId = {pubcid: 'pubcidabcd'}; + it('should add test and is_debug to request, if test is set in parameters', function () { + let validBidRequests = [{ + bidId: 'bidId', + params: {test: 1} + }]; + let request = JSON.parse(spec.buildRequests(validBidRequests, {refererInfo: {referer: 'page'}}).data); - it('should send pubcid if available', function () { - let request = spec.buildRequests(bid, bidderRequests); - let parsedRequestUrl = parseUrl(request.url); - let query = parsedRequestUrl.search; - expect(query.pubcid).to.equal('pubcidabcd'); + assert.ok(request.is_debug); + assert.equal(request.test, 1); }); - }); - describe('userid tdid should be passed to querystring', function () { - let bid = deepClone([bidBanner]); - let bidderRequests = {}; + it('should have default request structure', function () { + let keys = 'site,geo,device,source,ext,imp'.split(','); + let validBidRequests = [{ + bidId: 'bidId', + params: {siteId: 'siteId'} + }]; + let request = JSON.parse(spec.buildRequests(validBidRequests, {refererInfo: {referer: 'page'}}).data); + let data = Object.keys(request); + + assert.deepEqual(keys, data); + }); - bid[0].userId = {tdid: 'tdidabcd'}; + it('should set request keys correct values', function () { + let validBidRequests = [{ + bidId: 'bidId', + params: {siteId: 'siteId'}, + transactionId: 'transactionId' + }]; + let request = JSON.parse(spec.buildRequests(validBidRequests, {refererInfo: {referer: 'page'}}).data); - it('should send pubcid if available', function () { - let request = spec.buildRequests(bid, bidderRequests); - let parsedRequestUrl = parseUrl(request.url); - let query = parsedRequestUrl.search; - expect(query.tdid).to.equal('tdidabcd'); + assert.equal(request.source.tid, validBidRequests[0].transactionId); + assert.equal(request.source.fd, 1); + }); + + it('should send info about device', function () { + config.setConfig({ + device: {w: 100, h: 100} + }); + let validBidRequests = [{ + bidId: 'bidId', + params: {adzoneid: '1000'} + }]; + let request = JSON.parse(spec.buildRequests(validBidRequests, {refererInfo: {referer: 'page'}}).data); + + assert.equal(request.device.ua, navigator.userAgent); + assert.equal(request.device.w, 100); + assert.equal(request.device.h, 100); }); - }); - describe('userid id5id should be passed to querystring', function () { - let bid = deepClone([bidBanner]); - let bidderRequests = {}; + it('should send app info', function () { + config.setConfig({ + app: {id: 'appid'}, + ortb2: {app: {name: 'appname'}} + }); + let validBidRequests = [{ + bidId: 'bidId', + params: {adzoneid: '1000'} + }]; + let request = JSON.parse(spec.buildRequests(validBidRequests, {refererInfo: {referer: 'page'}}).data); + + assert.equal(request.app.id, 'appid'); + assert.equal(request.app.name, 'appname'); + assert.equal(request.site, undefined); + }); - bid[0].userId = {id5id: {uid: 'id5idsample'}}; + it('should send info about the site', function () { + config.setConfig({ + site: { + id: '123123', + publisher: { + domain: 'publisher.domain.com' + } + }, + ortb2: { + site: { + publisher: { + id: 4441, + name: 'publisher\'s name' + } + } + } + }); + let validBidRequests = [{ + bidId: 'bidId', + params: {adzoneid: '1000'} + }]; + let refererInfo = {referer: 'page'}; + let request = JSON.parse(spec.buildRequests(validBidRequests, {refererInfo}).data); + + assert.deepEqual(request.site, { + domain: 'localhost', + id: '123123', + page: refererInfo.referer, + publisher: { + domain: 'publisher.domain.com', + id: 4441, + name: 'publisher\'s name' + } + }); + }); - it('should send pubcid if available', function () { - let request = spec.buildRequests(bid, bidderRequests); - let parsedRequestUrl = parseUrl(request.url); - let query = parsedRequestUrl.search; - expect(query.id5id).to.equal('id5idsample'); + it('should pass extended ids', function () { + let validBidRequests = [{ + bidId: 'bidId', + params: {}, + userIdAsEids: createEidsArray({ + tdid: 'TTD_ID_FROM_USER_ID_MODULE', + pubcid: 'pubCommonId_FROM_USER_ID_MODULE' + }) + }]; + + let request = JSON.parse(spec.buildRequests(validBidRequests, {refererInfo: {referer: 'page'}}).data); + assert.deepEqual(request.user.ext.eids, [ + {source: 'adserver.org', uids: [{id: 'TTD_ID_FROM_USER_ID_MODULE', atype: 1, ext: {rtiPartner: 'TDID'}}]}, + {source: 'pubcid.org', uids: [{id: 'pubCommonId_FROM_USER_ID_MODULE', atype: 1}]} + ]); }); - }); - describe('userid idl_env should be passed to querystring', function () { - let bid = deepClone([bidBanner]); - let bidderRequests = {}; + it('should send currency if defined', function () { + config.setConfig({currency: {adServerCurrency: 'EUR'}}); + let validBidRequests = [{params: {}}]; + let refererInfo = {referer: 'page'}; + let request = JSON.parse(spec.buildRequests(validBidRequests, {refererInfo}).data); + + assert.deepEqual(request.cur, ['EUR']); + }); - bid[0].userId = {idl_env: 'idl_envsample'}; + it('should pass supply chain object', function () { + let validBidRequests = [{ + bidId: 'bidId', + params: {}, + schain: { + validation: 'strict', + config: { + ver: '1.0' + } + } + }]; - it('should send pubcid if available', function () { - let request = spec.buildRequests(bid, bidderRequests); - let parsedRequestUrl = parseUrl(request.url); - let query = parsedRequestUrl.search; - expect(query.idl_env).to.equal('idl_envsample'); + let request = JSON.parse(spec.buildRequests(validBidRequests, {refererInfo: {referer: 'page'}}).data); + assert.deepEqual(request.source.ext.schain, { + validation: 'strict', + config: { + ver: '1.0' + } + }); }); - }); - describe('response handler', function () { - let BIDDER_REQUEST = { - bidder: 'adxcg', - params: { - adzoneid: '1' - }, - adUnitCode: 'adunit-code', - mediaTypes: { - banner: { - sizes: [ - [300, 250], - [640, 360], - [1, 1] - ] + describe('bids', function () { + it('should add more than one bid to the request', function () { + let validBidRequests = [{ + bidId: 'bidId', + params: {siteId: 'siteId'} + }, { + bidId: 'bidId2', + params: {siteId: 'siteId'} + }]; + let request = JSON.parse(spec.buildRequests(validBidRequests, {refererInfo: {referer: 'page'}}).data); + + assert.equal(request.imp.length, 2); + }); + it('should add incrementing values of id', function () { + let validBidRequests = [{ + bidId: 'bidId', + params: {adzoneid: '1000'}, + mediaTypes: {video: {}} + }, { + bidId: 'bidId2', + params: {adzoneid: '1000'}, + mediaTypes: {video: {}} + }, { + bidId: 'bidId3', + params: {adzoneid: '1000'}, + mediaTypes: {video: {}} + }]; + let imps = JSON.parse(spec.buildRequests(validBidRequests, {refererInfo: {referer: 'page'}}).data).imp; + + for (let i = 0; i < 3; i++) { + assert.equal(imps[i].id, i + 1); } - }, - bidId: '84ab500420319d', - bidderRequestId: '7101db09af0db2', - auctionId: '1d1a030790a475' - }; + }); - let BANNER_RESPONSE = { - body: { - id: 'auctionid', - bidid: '84ab500420319d', - seatbid: [{ - bid: [ - { - impid: '84ab500420319d', - price: 0.45, - crid: '42', - adm: '', - w: 300, - h: 250, - adomain: ['adomain.com'], - cat: ['IAB1-4', 'IAB8-16', 'IAB25-5'], - ext: { - crType: 'banner', - advertiser_id: '777', - advertiser_name: 'advertiser', - agency_name: 'agency' - } + it('should add adzoneid', function () { + let validBidRequests = [{bidId: 'bidId', params: {adzoneid: 1000}, mediaTypes: {video: {}}}, + {bidId: 'bidId2', params: {adzoneid: 1001}, mediaTypes: {video: {}}}, + {bidId: 'bidId3', params: {adzoneid: 1002}, mediaTypes: {video: {}}}]; + let imps = JSON.parse(spec.buildRequests(validBidRequests, {refererInfo: {referer: 'page'}}).data).imp; + for (let i = 0; i < 3; i++) { + assert.equal(imps[i].tagid, validBidRequests[i].params.adzoneid); + } + }); + + describe('price floors', function () { + it('should not add if floors module not configured', function () { + const validBidRequests = [{bidId: 'bidId', params: {adzoneid: 1000}, mediaTypes: {video: {}}}]; + let imp = getRequestImps(validBidRequests)[0]; + + assert.equal(imp.bidfloor, undefined); + assert.equal(imp.bidfloorcur, undefined); + }); + + it('should not add if floor price not defined', function () { + const validBidRequests = [getBidWithFloor()]; + let imp = getRequestImps(validBidRequests)[0]; + + assert.equal(imp.bidfloor, undefined); + assert.equal(imp.bidfloorcur, 'USD'); + }); + + it('should request floor price in adserver currency', function () { + config.setConfig({currency: {adServerCurrency: 'DKK'}}); + const validBidRequests = [getBidWithFloor()]; + let imp = getRequestImps(validBidRequests)[0]; + + assert.equal(imp.bidfloor, undefined); + assert.equal(imp.bidfloorcur, 'DKK'); + }); + + it('should add correct floor values', function () { + const expectedFloors = [1, 1.3, 0.5]; + const validBidRequests = expectedFloors.map(getBidWithFloor); + let imps = getRequestImps(validBidRequests); + + expectedFloors.forEach((floor, index) => { + assert.equal(imps[index].bidfloor, floor); + assert.equal(imps[index].bidfloorcur, 'USD'); + }); + }); + + function getBidWithFloor(floor) { + return { + params: {adzoneid: 1}, + mediaTypes: {video: {}}, + getFloor: ({currency}) => { + return { + currency: currency, + floor + }; } - ] - }], - cur: 'USD' - }, - headers: {someheader: 'fakedata'} - }; + }; + } + }); - let BANNER_RESPONSE_WITHDEALID = { - body: { - id: 'auctionid', - bidid: '84ab500420319d', - seatbid: [{ - bid: [ - { - impid: '84ab500420319d', - price: 0.45, - crid: '42', - dealid: '7722', - adm: '', - w: 300, - h: 250, - adomain: ['adomain.com'], - ext: { - crType: 'banner' - } + describe('multiple media types', function () { + it('should use all configured media types for bidding', function () { + let validBidRequests = [{ + bidId: 'bidId', + params: {adzoneid: 1000}, + mediaTypes: { + banner: { + sizes: [[100, 100], [200, 300]] + }, + video: {} } - ] - }], - cur: 'USD' - } - }; + }, { + bidId: 'bidId1', + params: {adzoneid: 1000}, + mediaTypes: { + video: {}, + native: {} + } + }, { + bidId: 'bidId2', + params: {adzoneid: 1000}, + nativeParams: { + title: {required: true, len: 140} + }, + mediaTypes: { + banner: { + sizes: [[100, 100], [200, 300]] + }, + native: {}, + video: {} + } + }]; + let [first, second, third] = JSON.parse(spec.buildRequests(validBidRequests, {refererInfo: {referer: 'page'}}).data).imp; - let VIDEO_RESPONSE = { - body: { - id: 'auctionid', - bidid: '84ab500420319d', - seatbid: [{ - bid: [ - { - impid: '84ab500420319d', - price: 0.45, - crid: '42', - nurl: 'vastContentUrl', - adomain: ['adomain.com'], - w: 640, - h: 360, - ext: { - crType: 'video' + assert.ok(first.banner); + assert.ok(first.video); + assert.equal(first.native, undefined); + + assert.ok(second.video); + assert.equal(second.banner, undefined); + assert.equal(second.native, undefined); + + assert.ok(third.native); + assert.ok(third.video); + assert.ok(third.banner); + }); + }); + + describe('banner', function () { + it('should convert sizes to openrtb format', function () { + let validBidRequests = [{ + bidId: 'bidId', + params: {adzoneid: 1000}, + mediaTypes: { + banner: { + sizes: [[100, 100], [200, 300]] } } - ] - }], - cur: 'USD' - }, - headers: {someheader: 'fakedata'} - }; - - let NATIVE_RESPONSEob = { - assets: [ - { - id: 1, - required: 0, - title: { - text: 'titleContent' - } - }, - { - id: 2, - required: 0, - img: { - url: 'imageContent', - w: 600, - h: 600 - } - }, - { - id: 3, - required: 0, - data: { - label: 'DESC', - value: 'descriptionContent' - } - }, - { - id: 0, - required: 0, - data: { - label: 'SPONSORED', - value: 'sponsoredByContent' - } - }, - { - id: 5, - required: 0, - icon: { - url: 'iconContent', - w: 400, - h: 400 - } - } - ], - link: { - url: 'linkContent' - }, - imptrackers: ['impressionTracker1', 'impressionTracker2'] - } + }]; + let {banner} = JSON.parse(spec.buildRequests(validBidRequests, {refererInfo: {referer: 'page'}}).data).imp[0]; + assert.deepEqual(banner, { + format: [{w: 100, h: 100}, {w: 200, h: 300}] + }); + }); + }); - let NATIVE_RESPONSE = { - body: { - id: 'auctionid', - bidid: '84ab500420319d', - seatbid: [{ - bid: [ - { - impid: '84ab500420319d', - price: 0.45, - crid: '42', - w: 0, - h: 0, - adm: JSON.stringify(NATIVE_RESPONSEob), - adomain: ['adomain.com'], - ext: { - crType: 'native' + describe('video', function () { + it('should pass video mediatype config', function () { + let validBidRequests = [{ + bidId: 'bidId', + params: {adzoneid: 1000}, + mediaTypes: { + video: { + playerSize: [640, 480], + context: 'outstream', + mimes: ['video/mp4'] } } - ] - }], - cur: 'USD' - }, - headers: {someheader: 'fakedata'} - }; + }]; + let {video} = JSON.parse(spec.buildRequests(validBidRequests, {refererInfo: {referer: 'page'}}).data).imp[0]; + assert.deepEqual(video, { + playerSize: [640, 480], + context: 'outstream', + mimes: ['video/mp4'] + }); + }); + }); - it('handles regular responses', function () { - expect(BANNER_RESPONSE).to.exist; - expect(BANNER_RESPONSE.body).to.exist; - expect(BANNER_RESPONSE.body.id).to.exist; - expect(BANNER_RESPONSE.body.seatbid[0]).to.exist; - let result = spec.interpretResponse(BANNER_RESPONSE, BIDDER_REQUEST); - - expect(result).to.have.lengthOf(1); - - expect(result[0]).to.exist; - expect(result[0].width).to.equal(300); - expect(result[0].height).to.equal(250); - expect(result[0].creativeId).to.equal(42); - expect(result[0].cpm).to.be.within(0.45, 0.46); - expect(result[0].ad).to.equal(''); - expect(result[0].currency).to.equal('USD'); - expect(result[0].netRevenue).to.equal(true); - expect(result[0].ttl).to.equal(300); - expect(result[0].dealId).to.not.exist; - expect(result[0].meta.advertiserDomains[0]).to.equal('adomain.com'); - expect(result[0].meta.advertiserId).to.be.eql('777'); - expect(result[0].meta.advertiserName).to.be.eql('advertiser'); - expect(result[0].meta.agencyName).to.be.eql('agency'); - expect(result[0].meta.advertiserDomains).to.be.eql(['adomain.com']); - expect(result[0].meta.secondaryCatIds).to.be.eql(['IAB1-4', 'IAB8-16', 'IAB25-5']); + describe('native', function () { + describe('assets', function () { + it('should set correct asset id', function () { + let validBidRequests = [{ + bidId: 'bidId', + params: {adzoneid: 1000}, + nativeParams: { + title: {required: true, len: 140}, + image: {required: false, wmin: 836, hmin: 627, w: 325, h: 300, mimes: ['image/jpg', 'image/gif']}, + body: {len: 140} + } + }]; + let nativeRequest = JSON.parse(spec.buildRequests(validBidRequests, {refererInfo: {referer: 'page'}}).data).imp[0].native.request; + let assets = JSON.parse(nativeRequest).assets; + + assert.equal(assets[0].id, 0); + assert.equal(assets[1].id, 3); + assert.equal(assets[2].id, 4); + }); + it('should add required key if it is necessary', function () { + let validBidRequests = [{ + bidId: 'bidId', + params: {adzoneid: 1000}, + nativeParams: { + title: {required: true, len: 140}, + image: {required: false, wmin: 836, hmin: 627, w: 325, h: 300, mimes: ['image/jpg', 'image/gif']}, + body: {len: 140}, + sponsoredBy: {required: true, len: 140} + } + }]; + + let nativeRequest = JSON.parse(spec.buildRequests(validBidRequests, {refererInfo: {referer: 'page'}}).data).imp[0].native.request; + let assets = JSON.parse(nativeRequest).assets; + + assert.equal(assets[0].required, 1); + assert.ok(!assets[1].required); + assert.ok(!assets[2].required); + assert.equal(assets[3].required, 1); + }); + + it('should map img and data assets', function () { + let validBidRequests = [{ + bidId: 'bidId', + params: {adzoneid: 1000}, + nativeParams: { + title: {required: true, len: 140}, + image: {required: true, sizes: [150, 50]}, + icon: {required: false, sizes: [50, 50]}, + body: {required: false, len: 140}, + sponsoredBy: {required: true}, + cta: {required: false}, + clickUrl: {required: false} + } + }]; + + let nativeRequest = JSON.parse(spec.buildRequests(validBidRequests, {refererInfo: {referer: 'page'}}).data).imp[0].native.request; + let assets = JSON.parse(nativeRequest).assets; + assert.ok(assets[0].title); + assert.equal(assets[0].title.len, 140); + assert.deepEqual(assets[1].img, {type: 3, w: 150, h: 50}); + assert.deepEqual(assets[2].img, {type: 1, w: 50, h: 50}); + assert.deepEqual(assets[3].data, {type: 2, len: 140}); + assert.deepEqual(assets[4].data, {type: 1}); + assert.deepEqual(assets[5].data, {type: 12}); + assert.ok(!assets[6]); + }); + + describe('icon/image sizing', function () { + it('should flatten sizes and utilise first pair', function () { + const validBidRequests = [{ + bidId: 'bidId', + params: {adzoneid: 1000}, + nativeParams: { + image: { + sizes: [[200, 300], [100, 200]] + }, + } + }]; + + let nativeRequest = JSON.parse(spec.buildRequests(validBidRequests, {refererInfo: {referer: 'page'}}).data).imp[0].native.request; + let assets = JSON.parse(nativeRequest).assets; + assert.ok(assets[0].img); + assert.equal(assets[0].img.w, 200); + assert.equal(assets[0].img.h, 300); + }); + }); + + it('should utilise aspect_ratios', function () { + const validBidRequests = [{ + bidId: 'bidId', + params: {adzoneid: 1000}, + nativeParams: { + image: { + aspect_ratios: [{ + min_width: 100, + ratio_height: 3, + ratio_width: 1 + }] + }, + icon: { + aspect_ratios: [{ + min_width: 10, + ratio_height: 5, + ratio_width: 2 + }] + } + } + }]; + + let nativeRequest = JSON.parse(spec.buildRequests(validBidRequests, {refererInfo: {referer: 'page'}}).data).imp[0].native.request; + let assets = JSON.parse(nativeRequest).assets; + assert.ok(assets[0].img); + assert.equal(assets[0].img.wmin, 100); + assert.equal(assets[0].img.hmin, 300); + + assert.ok(assets[1].img); + assert.equal(assets[1].img.wmin, 10); + assert.equal(assets[1].img.hmin, 25); + }); + + it('should not throw error if aspect_ratios config is not defined', function () { + const validBidRequests = [{ + bidId: 'bidId', + params: {adzoneid: 1000}, + nativeParams: { + image: { + aspect_ratios: [] + }, + icon: { + aspect_ratios: [] + } + } + }]; + + assert.doesNotThrow(() => spec.buildRequests(validBidRequests, {refererInfo: {referer: 'page'}})); + }); + }); + + it('should expect any dimensions if min_width not passed', function () { + const validBidRequests = [{ + bidId: 'bidId', + params: {adzoneid: 1000}, + nativeParams: { + image: { + aspect_ratios: [{ + ratio_height: 3, + ratio_width: 1 + }] + } + } + }]; + + let nativeRequest = JSON.parse(spec.buildRequests(validBidRequests, {refererInfo: {referer: 'page'}}).data).imp[0].native.request; + let assets = JSON.parse(nativeRequest).assets; + assert.ok(assets[0].img); + assert.equal(assets[0].img.wmin, 0); + assert.equal(assets[0].img.hmin, 0); + assert.ok(!assets[1]); + }); + }); }); - it('handles regular responses with dealid', function () { - let result = spec.interpretResponse(BANNER_RESPONSE_WITHDEALID); + function getRequestImps(validBidRequests) { + return JSON.parse(spec.buildRequests(validBidRequests, {refererInfo: {referer: 'page'}}).data).imp; + } + }); - expect(result).to.have.lengthOf(1); + describe('interpretResponse', function () { + it('should return if no body in response', function () { + let serverResponse = {}; + let bidRequest = {}; - expect(result[0].width).to.equal(300); - expect(result[0].height).to.equal(250); - expect(result[0].creativeId).to.equal(42); - // expect(result[0].cpm).to.equal(0.45); - expect(result[0].cpm).to.be.within(0.45, 0.46); - expect(result[0].ad).to.equal(''); - expect(result[0].currency).to.equal('USD'); - expect(result[0].netRevenue).to.equal(true); - expect(result[0].ttl).to.equal(300); + assert.ok(!spec.interpretResponse(serverResponse, bidRequest)); }); + it('should return more than one bids', function () { + let serverResponse = { + body: { + seatbid: [{ + bid: [{ + impid: '1', + native: {ver: '1.1', link: {url: 'link'}, assets: [{id: 1, title: {text: 'Asset title text'}}]} + }] + }, { + bid: [{ + impid: '2', + native: {ver: '1.1', link: {url: 'link'}, assets: [{id: 1, data: {value: 'Asset title text'}}]} + }] + }] + } + }; + let bidRequest = { + data: {}, + bids: [ + { + bidId: 'bidId1', + params: {adzoneid: 1000}, + nativeParams: { + title: {required: true, len: 140}, + image: {required: false, wmin: 836, hmin: 627, w: 325, h: 300, mimes: ['image/jpg', 'image/gif']}, + body: {len: 140} + } + }, + { + bidId: 'bidId2', + params: {adzoneid: 1000}, + nativeParams: { + title: {required: true, len: 140}, + image: {required: false, wmin: 836, hmin: 627, w: 325, h: 300, mimes: ['image/jpg', 'image/gif']}, + body: {len: 140} + } + } + ] + }; - it('handles video responses', function () { - let result = spec.interpretResponse(VIDEO_RESPONSE); - expect(result).to.have.lengthOf(1); - - expect(result[0].width).to.equal(640); - expect(result[0].height).to.equal(360); - expect(result[0].mediaType).to.equal('video'); - expect(result[0].creativeId).to.equal(42); - expect(result[0].cpm).to.equal(0.45); - expect(result[0].vastUrl).to.equal('vastContentUrl'); - expect(result[0].currency).to.equal('USD'); - expect(result[0].netRevenue).to.equal(true); - expect(result[0].ttl).to.equal(300); + bids = spec.interpretResponse(serverResponse, bidRequest); + assert.equal(spec.interpretResponse(serverResponse, bidRequest).length, 2); }); - it('handles native responses', function () { - let result = spec.interpretResponse(NATIVE_RESPONSE); - - expect(result[0].width).to.equal(0); - expect(result[0].height).to.equal(0); - - expect(result[0].creativeId).to.equal(42); - expect(result[0].cpm).to.equal(0.45); - expect(result[0].currency).to.equal('USD'); - expect(result[0].netRevenue).to.equal(true); - expect(result[0].ttl).to.equal(300); + it('should set correct values to bid', function () { + let nativeExample1 = { + assets: [], + link: {url: 'link'}, + imptrackers: ['imptrackers url1', 'imptrackers url2'] + } - expect(result[0].mediaType).to.equal('native'); + let serverResponse = { + body: { + id: null, + bidid: null, + seatbid: [{ + bid: [ + { + impid: '1', + price: 93.1231, + crid: '12312312', + adm: JSON.stringify(nativeExample1), + dealid: 'deal-id', + adomain: ['demo.com'], + ext: { + crType: 'native', + advertiser_id: 'adv1', + advertiser_name: 'advname', + agency_name: 'agname', + mediaType: 'native' + } + } + ] + }], + cur: 'EUR' + } + }; + let bidRequest = { + data: {}, + bids: [ + { + bidId: 'bidId1', + params: {adzoneid: 1000}, + nativeParams: { + title: {required: true, len: 140}, + image: {required: false, wmin: 836, hmin: 627, w: 325, h: 300, mimes: ['image/jpg', 'image/gif']}, + body: {len: 140} + } + } + ] + }; - expect(result[0].native.clickUrl).to.equal('linkContent'); - expect(result[0].native.impressionTrackers).to.deep.equal([ - 'impressionTracker1', - 'impressionTracker2' - ]); - expect(result[0].native.title).to.equal('titleContent'); + const bids = spec.interpretResponse(serverResponse, bidRequest); + const bid = serverResponse.body.seatbid[0].bid[0]; + assert.deepEqual(bids[0].requestId, bidRequest.bids[0].bidId); + assert.deepEqual(bids[0].cpm, bid.price); + assert.deepEqual(bids[0].creativeId, bid.crid); + assert.deepEqual(bids[0].ttl, 300); + assert.deepEqual(bids[0].netRevenue, false); + assert.deepEqual(bids[0].currency, serverResponse.body.cur); + assert.deepEqual(bids[0].mediaType, 'native'); + assert.deepEqual(bids[0].meta.mediaType, 'native'); + assert.deepEqual(bids[0].meta.advertiserDomains, ['demo.com']); + + assert.deepEqual(bids[0].meta.advertiserName, 'advname'); + assert.deepEqual(bids[0].meta.agencyName, 'agname'); + + assert.deepEqual(bids[0].dealId, 'deal-id'); + }); - expect(result[0].native.image.url).to.equal('imageContent'); - expect(result[0].native.image.height).to.equal(600); - expect(result[0].native.image.width).to.equal(600); + it('should return empty when there is no bids in response', function () { + const serverResponse = { + body: { + id: null, + bidid: null, + seatbid: [{bid: []}], + cur: 'EUR' + } + }; + let bidRequest = { + data: {}, + bids: [{bidId: 'bidId1'}] + }; + const result = spec.interpretResponse(serverResponse, bidRequest)[0]; + assert.ok(!result); + }); - expect(result[0].native.icon.url).to.equal('iconContent'); - expect(result[0].native.icon.height).to.equal(400); - expect(result[0].native.icon.width).to.equal(400); + describe('banner', function () { + it('should set ad content on response', function () { + let serverResponse = { + body: { + seatbid: [{ + bid: [{impid: '1', adm: '', ext: {crType: 'banner'}}] + }] + } + }; + let bidRequest = { + data: {}, + bids: [ + { + bidId: 'bidId1', + params: {adzoneid: 1000} + } + ] + }; - expect(result[0].native.body).to.equal('descriptionContent'); - expect(result[0].native.sponsoredBy).to.equal('sponsoredByContent'); + bids = spec.interpretResponse(serverResponse, bidRequest); + assert.equal(bids.length, 1); + assert.equal(bids[0].ad, ''); + assert.equal(bids[0].mediaType, 'banner'); + assert.equal(bids[0].meta.mediaType, 'banner'); + }); }); - it('handles nobid responses', function () { - let response = []; - let bidderRequest = BIDDER_REQUEST; + describe('video', function () { + it('should set vastXml on response', function () { + let serverResponse = { + body: { + seatbid: [{ + bid: [{impid: '1', adm: '', ext: {crType: 'video'}}] + }] + } + }; + let bidRequest = { + data: {}, + bids: [ + { + bidId: 'bidId1', + params: {adzoneid: 1000} + } + ] + }; - let result = spec.interpretResponse(response, bidderRequest); - expect(result.length).to.equal(0); + bids = spec.interpretResponse(serverResponse, bidRequest); + assert.equal(bids.length, 1); + assert.equal(bids[0].vastXml, ''); + assert.equal(bids[0].mediaType, 'video'); + assert.equal(bids[0].meta.mediaType, 'video'); + }); }); }); describe('getUserSyncs', function () { - let syncoptionsIframe = { - iframeEnabled: 'true' - }; + const usersyncUrl = 'https://usersync-url.com'; + beforeEach(() => { + config.setConfig( + { + adxcg: { + usersyncUrl: usersyncUrl, + } + } + ) + }) + after(() => { + config.resetConfig() + }) + + it('should return user sync if pixel enabled with adxcg config', function () { + const ret = spec.getUserSyncs({pixelEnabled: true}) + expect(ret).to.deep.equal([{type: 'image', url: usersyncUrl}]) + }) + + it('should not return user sync if pixel disabled', function () { + const ret = spec.getUserSyncs({pixelEnabled: false}) + expect(ret).to.be.an('array').that.is.empty + }) + + it('should not return user sync if url is not set', function () { + config.resetConfig() + const ret = spec.getUserSyncs({pixelEnabled: true}) + expect(ret).to.be.an('array').that.is.empty + }) + + it('should pass GDPR consent', function() { + expect(spec.getUserSyncs({ pixelEnabled: true }, {}, {gdprApplies: true, consentString: 'foo'}, undefined)).to.deep.equal([{ + type: 'image', url: `${usersyncUrl}?gdpr=1&gdpr_consent=foo` + }]); + expect(spec.getUserSyncs({ pixelEnabled: true }, {}, {gdprApplies: false, consentString: 'foo'}, undefined)).to.deep.equal([{ + type: 'image', url: `${usersyncUrl}?gdpr=0&gdpr_consent=foo` + }]); + expect(spec.getUserSyncs({ pixelEnabled: true }, {}, {gdprApplies: true, consentString: undefined}, undefined)).to.deep.equal([{ + type: 'image', url: `${usersyncUrl}?gdpr=1&gdpr_consent=` + }]); + }); + + it('should pass US consent', function() { + expect(spec.getUserSyncs({ pixelEnabled: true }, {}, undefined, '1NYN')).to.deep.equal([{ + type: 'image', url: `${usersyncUrl}?us_privacy=1NYN` + }]); + }); - it('should return iframe sync option', function () { - expect(spec.getUserSyncs(syncoptionsIframe)[0].type).to.equal('iframe'); - expect(spec.getUserSyncs(syncoptionsIframe)[0].url).to.equal( - 'https://cdn.adxcg.net/pb-sync.html' - ); + it('should pass GDPR and US consent', function() { + expect(spec.getUserSyncs({ pixelEnabled: true }, {}, {gdprApplies: true, consentString: 'foo'}, '1NYN')).to.deep.equal([{ + type: 'image', url: `${usersyncUrl}?gdpr=1&gdpr_consent=foo&us_privacy=1NYN` + }]); }); }); - describe('on bidWon', function () { - beforeEach(function () { + describe('onBidWon', function() { + beforeEach(function() { sinon.stub(utils, 'triggerPixel'); }); - afterEach(function () { + afterEach(function() { utils.triggerPixel.restore(); }); - it('should replace burl for banner', function () { - const burl = 'burl=${' + 'AUCTION_PRICE}'; + + it('Should trigger pixel if bid nurl', function() { const bid = { - 'bidderCode': 'adxcg', - 'width': 0, - 'height': 0, - 'statusMessage': 'Bid available', - 'adId': '3d0b6ff1dda89', - 'requestId': '2a423489e058a1', - 'mediaType': 'banner', - 'source': 'client', - 'ad': burl, - 'cpm': 0.66, - 'creativeId': '353538_591471', - 'currency': 'USD', - 'dealId': '', - 'netRevenue': true, - 'ttl': 300, - // 'nurl': nurl, - 'burl': burl, - 'isBurl': true, - 'auctionId': 'a92bffce-14d2-4f8f-a78a-7b9b5e4d28fa', - 'responseTimestamp': 1556867386065, - 'requestTimestamp': 1556867385916, - 'bidder': 'adxcg', - 'adUnitCode': 'div-gpt-ad-1555415275793-0', - 'timeToRespond': 149, - 'pbLg': '0.50', - 'pbMg': '0.60', - 'pbHg': '0.66', - 'pbAg': '0.65', - 'pbDg': '0.66', - 'pbCg': '', - 'size': '0x0', - 'adserverTargeting': { - 'hb_bidder': 'mgid', - 'hb_adid': '3d0b6ff1dda89', - 'hb_pb': '0.66', - 'hb_size': '0x0', - 'hb_source': 'client', - 'hb_format': 'banner', - 'hb_banner_title': 'TITLE', - 'hb_banner_image': 'hb_banner_image:3d0b6ff1dda89', - 'hb_banner_icon': 'IconURL', - 'hb_banner_linkurl': 'hb_banner_linkurl:3d0b6ff1dda89' - }, - 'status': 'targetingSet', - 'params': [{'adzoneid': '20'}] - }; + nurl: 'http://example.com/win/${AUCTION_PRICE}', + cpm: 2.1, + originalCpm: 1.1, + } spec.onBidWon(bid); - expect(bid.burl).to.deep.equal(burl); - }); - }); + expect(utils.triggerPixel.callCount).to.equal(1) + }) + }) }); diff --git a/test/spec/modules/adyoulikeBidAdapter_spec.js b/test/spec/modules/adyoulikeBidAdapter_spec.js index befc95e5f24..4a13fbf1232 100644 --- a/test/spec/modules/adyoulikeBidAdapter_spec.js +++ b/test/spec/modules/adyoulikeBidAdapter_spec.js @@ -95,6 +95,40 @@ describe('Adyoulike Adapter', function () { } ]; + const bidRequestWithMultipleMediatype = [ + { + 'bidId': 'bid_id_0', + 'bidder': 'adyoulike', + 'placementCode': 'adunit/hb-0', + 'params': { + 'placement': 'placement_0' + }, + 'sizes': '300x250', + 'mediaTypes': { + 'banner': { + 'sizes': ['640x480'] + }, + 'video': { + 'playerSize': [640, 480], + 'context': 'outstream' + }, + 'native': { + 'image': { + 'required': true, + }, + 'title': { + 'required': true, + 'len': 80 + }, + 'cta': { + 'required': false + }, + } + }, + 'transactionId': 'bid_id_0_transaction_id' + } + ]; + const bidRequestWithNativeImageType = [ { 'bidId': 'bid_id_0', @@ -590,6 +624,32 @@ describe('Adyoulike Adapter', function () { expect(payload.gdprConsent.consentRequired).to.be.null; }); + it('should add userid eids information to the request', function () { + let bidderRequest = { + 'auctionId': '1d1a030790a475', + 'bidderRequestId': '22edbae2733bf6', + 'timeout': 3000, + 'userId': { + pubcid: '01EAJWWNEPN3CYMM5N8M5VXY22', + unsuported: '666' + } + }; + + bidderRequest.bids = bidRequestWithSinglePlacement; + + const request = spec.buildRequests(bidRequestWithSinglePlacement, bidderRequest); + const payload = JSON.parse(request.data); + + expect(payload.userId).to.exist; + expect(payload.userId).to.deep.equal([{ + 'source': 'pubcid.org', + 'uids': [{ + 'atype': 1, + 'id': '01EAJWWNEPN3CYMM5N8M5VXY22' + }] + }]); + }); + it('sends bid request to endpoint with single placement', function () { const request = spec.buildRequests(bidRequestWithSinglePlacement, bidderRequest); const payload = JSON.parse(request.data); @@ -621,6 +681,21 @@ describe('Adyoulike Adapter', function () { expect(payload.Bids['bid_id_0'].TransactionID).to.be.equal('bid_id_0_transaction_id'); }); + it('sends bid request to endpoint with single placement multiple mediatype', function () { + canonicalQuery.restore(); + const request = spec.buildRequests(bidRequestWithMultipleMediatype, bidderRequest); + const payload = JSON.parse(request.data); + + expect(request.url).to.contain(getEndpoint()); + expect(request.method).to.equal('POST'); + + expect(request.url).to.not.contains('CanonicalUrl=' + encodeURIComponent(canonicalUrl)); + expect(payload.Version).to.equal('1.0'); + expect(payload.Bids['bid_id_0'].PlacementID).to.be.equal('placement_0'); + expect(payload.PageRefreshed).to.equal(false); + expect(payload.Bids['bid_id_0'].TransactionID).to.be.equal('bid_id_0_transaction_id'); + }); + it('sends bid request to endpoint with multiple placements', function () { const request = spec.buildRequests(bidRequestMultiPlacements, bidderRequest); const payload = JSON.parse(request.data); diff --git a/test/spec/modules/akamaiDapRtdProvider_spec.js b/test/spec/modules/akamaiDapRtdProvider_spec.js new file mode 100644 index 00000000000..b350c2bb529 --- /dev/null +++ b/test/spec/modules/akamaiDapRtdProvider_spec.js @@ -0,0 +1,246 @@ +import {config} from 'src/config.js'; +import {SEGMENTS_STORAGE_KEY, TOKEN_STORAGE_KEY, dapUtils, addRealTimeData, getRealTimeData, akamaiDapRtdSubmodule, storage} from 'modules/akamaiDapRtdProvider.js'; +import {server} from 'test/mocks/xhr.js'; +import logMessage from 'src/utils.js' +const responseHeader = {'Content-Type': 'application/json'}; + +describe('akamaiDapRtdProvider', function() { + let getDataFromLocalStorageStub; + let getDapTokenStub; + + const testReqBidsConfigObj = { + adUnits: [ + { + bids: ['bid1', 'bid2'] + } + ] + }; + + const onDone = function() { return true }; + + const onSuccess = function() { return ('request', 200, 'success') }; + + const cmoduleConfig = { + 'name': 'dap', + 'waitForIt': true, + 'params': { + 'apiHostname': 'prebid.dap.akadns.net', + 'apiVersion': 'x1', + 'domain': 'prebid.org', + 'identityType': 'dap-signature:1.0.0', + 'segtax': 503, + 'tokenTtl': 5 + } + } + + const sampleConfig = { + 'api_hostname': 'prebid.dap.akadns.net', + 'api_version': 'x1', + 'domain': 'prebid.org', + 'segtax': 503 + } + const sampleIdentity = { + type: 'dap-signature:1.0.0' + }; + + beforeEach(function() { + config.resetConfig(); + getDataFromLocalStorageStub = sinon.stub(storage, 'getDataFromLocalStorage') + }); + + afterEach(function () { + getDataFromLocalStorageStub.restore(); + }); + + describe('akamaiDapRtdSubmodule', function() { + it('successfully instantiates', function () { + expect(akamaiDapRtdSubmodule.init()).to.equal(true); + }); + }); + + describe('Get Real-Time Data', function() { + it('gets rtd from local storage cache', function() { + const rtdConfig = { + params: { + segmentCache: true + } + }; + + const bidConfig = {}; + + const rtdUserObj1 = { + name: 'www.dataprovider3.com', + ext: { + taxonomyname: 'iab_audience_taxonomy' + }, + segment: [ + { + id: '1918' + }, + { + id: '1939' + } + ] + }; + + const cachedRtd = { + rtd: { + ortb2: { + user: { + data: [rtdUserObj1] + } + } + } + }; + + getDataFromLocalStorageStub.withArgs(SEGMENTS_STORAGE_KEY).returns(JSON.stringify(cachedRtd)); + + expect(config.getConfig().ortb2).to.be.undefined; + getRealTimeData(bidConfig, () => {}, rtdConfig, {}); + expect(config.getConfig().ortb2.user.data).to.deep.include.members([rtdUserObj1]); + }); + + it('should initalise and return with config', function () { + expect(getRealTimeData(testReqBidsConfigObj, onDone, cmoduleConfig)).to.equal(undefined) + }); + }); + + describe('dapTokenize', function () { + it('dapTokenize error callback', function () { + let configAsync = JSON.parse(JSON.stringify(sampleConfig)); + let submoduleCallback = dapUtils.dapTokenize(configAsync, sampleIdentity, + function(token, status, xhr) { + }, + function(xhr, status, error) { + } + ); + let request = server.requests[0]; + request.respond(400, responseHeader, JSON.stringify('error')); + expect(submoduleCallback).to.equal(undefined); + }); + + it('dapTokenize success callback', function () { + let configAsync = JSON.parse(JSON.stringify(sampleConfig)); + let submoduleCallback = dapUtils.dapTokenize(configAsync, sampleIdentity, + function(token, status, xhr) { + }, + function(xhr, status, error) { + } + ); + let request = server.requests[0]; + request.respond(200, responseHeader, JSON.stringify('success')); + expect(submoduleCallback).to.equal(undefined); + }); + }); + + describe('dapTokenize and dapMembership incorrect params', function () { + it('Onerror and config are null', function () { + expect(dapUtils.dapTokenize(null, 'identity', null, null)).to.be.equal(undefined); + expect(dapUtils.dapMembership(null, 'identity', null, null)).to.be.equal(undefined); + const config = { + 'api_hostname': 'prebid.dap.akadns.net', + 'api_version': 1, + 'domain': '', + 'segtax': 503 + }; + let identity = { + type: 'dap-signature:1.0.0' + }; + expect(dapUtils.dapTokenize(config, identity, null, null)).to.be.equal(undefined); + expect(dapUtils.dapMembership(config, 'token', null, null)).to.be.equal(undefined); + }); + + it('dapGetToken success', function () { + let dapTokenizeStub = sinon.stub(dapUtils, 'dapTokenize').returns(onSuccess); + expect(dapUtils.dapGetToken(sampleConfig, 'token', + function(token, status, xhr) { + }, + function(xhr, status, error) { + } + )).to.be.equal(null); + dapTokenizeStub.restore(); + }); + }); + + describe('dapMembership', function () { + it('dapMembership success callback', function () { + let configAsync = JSON.parse(JSON.stringify(sampleConfig)); + let submoduleCallback = dapUtils.dapMembership(configAsync, 'token', + function(token, status, xhr) { + }, + function(xhr, status, error) { + } + ); + let request = server.requests[0]; + request.respond(200, responseHeader, JSON.stringify('success')); + expect(submoduleCallback).to.equal(undefined); + }); + + it('dapMembership error callback', function () { + let configAsync = JSON.parse(JSON.stringify(sampleConfig)); + let submoduleCallback = dapUtils.dapMembership(configAsync, 'token', + function(token, status, xhr) { + }, + function(xhr, status, error) { + } + ); + let request = server.requests[0]; + request.respond(400, responseHeader, JSON.stringify('error')); + expect(submoduleCallback).to.equal(undefined); + }); + }); + + describe('dapMembership', function () { + it('should invoke the getDapToken and getDapMembership', function () { + let config = { + api_hostname: cmoduleConfig.params.apiHostname, + api_version: cmoduleConfig.params.apiVersion, + domain: cmoduleConfig.params.domain, + segtax: cmoduleConfig.params.segtax + }; + let identity = { + type: cmoduleConfig.params.identityType + }; + + let membership = { + said: 'item.said1', + cohorts: 'item.cohorts', + attributes: null + }; + + let getDapTokenStub = sinon.stub(dapUtils, 'dapGetToken').returns('token3'); + let getDapMembershipStub = sinon.stub(dapUtils, 'dapGetMembership').returns(membership); + let dapTokenizeStub = sinon.stub(dapUtils, 'dapTokenize').returns('response', 200, 'request'); + getRealTimeData(testReqBidsConfigObj, onDone, cmoduleConfig); + expect(getDapTokenStub.calledOnce).to.be.equal(true); + expect(getDapMembershipStub.calledOnce).to.be.equal(true); + getDapTokenStub.restore(); + getDapMembershipStub.restore(); + dapTokenizeStub.restore(); + }); + }); + + describe('dapMembershipToRtbSegment', function () { + it('dapMembershipToRtbSegment', function () { + let membership1 = { + said: 'item.said1', + cohorts: 'item.cohorts', + attributes: null + }; + const config = { + apiHostname: 'prebid.dap.akadns.net', + apiVersion: 'x1', + domain: 'prebid.org', + tokenTtl: 5, + segtax: 503 + }; + let identity = { + type: 'dap-signature:1.0.0' + }; + + expect(dapUtils.dapGetMembership(config, 'token')).to.equal(null) + const membership = {cohorts: ['1', '5', '7']} + expect(dapUtils.dapMembershipToRtbSegment(membership, config)).to.not.equal(undefined); + }); + }); +}); diff --git a/test/spec/modules/appnexusBidAdapter_spec.js b/test/spec/modules/appnexusBidAdapter_spec.js index 9396c1e1928..2cf65bc8a59 100644 --- a/test/spec/modules/appnexusBidAdapter_spec.js +++ b/test/spec/modules/appnexusBidAdapter_spec.js @@ -556,6 +556,33 @@ describe('AppNexusAdapter', function () { config.getConfig.restore(); }); + it('adds auction level keywords to request when set', function() { + let bidRequest = Object.assign({}, bidRequests[0]); + sinon + .stub(config, 'getConfig') + .withArgs('appnexusAuctionKeywords') + .returns({ + gender: 'm', + music: ['rock', 'pop'], + test: '' + }); + + const request = spec.buildRequests([bidRequest]); + const payload = JSON.parse(request.data); + + expect(payload.keywords).to.deep.equal([{ + 'key': 'gender', + 'value': ['m'] + }, { + 'key': 'music', + 'value': ['rock', 'pop'] + }, { + 'key': 'test' + }]); + + config.getConfig.restore(); + }); + it('should attach native params to the request', function () { let bidRequest = Object.assign({}, bidRequests[0], @@ -1004,12 +1031,16 @@ describe('AppNexusAdapter', function () { describe('interpretResponse', function () { let bfStub; + let bidderSettingsStorage; + before(function() { bfStub = sinon.stub(bidderFactory, 'getIabSubCategory'); + bidderSettingsStorage = $$PREBID_GLOBAL$$.bidderSettings; }); after(function() { bfStub.restore(); + $$PREBID_GLOBAL$$.bidderSettings = bidderSettingsStorage; }); let response = { @@ -1047,7 +1078,8 @@ describe('AppNexusAdapter', function () { 'trackers': [ { 'impression_urls': [ - 'https://lax1-ib.adnxs.com/impression' + 'https://lax1-ib.adnxs.com/impression', + 'https://www.test.com/tracker' ], 'video_events': {} } @@ -1076,6 +1108,15 @@ describe('AppNexusAdapter', function () { 'adUnitCode': 'code', 'appnexus': { 'buyerMemberId': 958 + }, + 'meta': { + 'dchain': { + 'ver': '1.0', + 'complete': 0, + 'nodes': [{ + 'bsid': '958' + }] + } } } ]; @@ -1084,11 +1125,46 @@ describe('AppNexusAdapter', function () { bidId: '3db3773286ee59', adUnitCode: 'code' }] - } + }; let result = spec.interpretResponse({ body: response }, {bidderRequest}); expect(Object.keys(result[0])).to.have.members(Object.keys(expectedResponse[0])); }); + it('should reject 0 cpm bids', function () { + let zeroCpmResponse = deepClone(response); + zeroCpmResponse.tags[0].ads[0].cpm = 0; + + let bidderRequest = { + bidderCode: 'appnexus' + }; + + let result = spec.interpretResponse({ body: zeroCpmResponse }, { bidderRequest }); + expect(result.length).to.equal(0); + }); + + it('should allow 0 cpm bids if allowZeroCpmBids setConfig is true', function () { + $$PREBID_GLOBAL$$.bidderSettings = { + appnexus: { + allowZeroCpmBids: true + } + }; + + let zeroCpmResponse = deepClone(response); + zeroCpmResponse.tags[0].ads[0].cpm = 0; + + let bidderRequest = { + bidderCode: 'appnexus', + bids: [{ + bidId: '3db3773286ee59', + adUnitCode: 'code' + }] + }; + + let result = spec.interpretResponse({ body: zeroCpmResponse }, { bidderRequest }); + expect(result.length).to.equal(1); + expect(result[0].cpm).to.equal(0); + }); + it('handles nobid responses', function () { let response = { 'version': '0.0.1', @@ -1334,6 +1410,20 @@ describe('AppNexusAdapter', function () { expect(Object.keys(result[0].meta)).to.include.members(['advertiserId']); }); + it('should add brand id', function() { + let responseBrandId = deepClone(response); + responseBrandId.tags[0].ads[0].brand_id = 123; + + let bidderRequest = { + bids: [{ + bidId: '3db3773286ee59', + adUnitCode: 'code' + }] + } + let result = spec.interpretResponse({ body: responseBrandId }, {bidderRequest}); + expect(Object.keys(result[0].meta)).to.include.members(['brandId']); + }); + it('should add advertiserDomains', function() { let responseAdvertiserId = deepClone(response); responseAdvertiserId.tags[0].ads[0].adomain = ['123']; diff --git a/test/spec/modules/asealBidAdapter_spec.js b/test/spec/modules/asealBidAdapter_spec.js new file mode 100644 index 00000000000..ef74dd3fc05 --- /dev/null +++ b/test/spec/modules/asealBidAdapter_spec.js @@ -0,0 +1,144 @@ +import { expect } from 'chai'; +import { spec, BIDDER_CODE, API_ENDPOINT, HEADER_AOTTER_VERSION } from 'modules/asealBidAdapter.js'; +import { newBidder } from 'src/adapters/bidderFactory.js'; +import { config } from 'src/config.js'; + +const TEST_CLIENT_ID = 'TEST_CLIENT_ID' + +describe('asealBidAdapter', () => { + const adapter = newBidder(spec); + + describe('inherited functions', () => { + it('exists and is a function', () => { + expect(adapter.callBids).to.exist.and.to.be.a('function') + }) + }); + + describe('isBidRequestValid', () => { + const bid = { + bidder: 'aseal', + params: { + placeUid: '123' + } + }; + + it('should return true when required params found', () => { + expect(spec.isBidRequestValid(bid)).to.equal(true) + }); + + it('should return false when required param placeUid is not passed', () => { + bid.params = { + placeUid: '' + } + expect(spec.isBidRequestValid(bid)).to.equal(false) + }); + + it('should return false when required param placeUid is wrong type', () => { + bid.params = { + placeUid: null + } + expect(spec.isBidRequestValid(bid)).to.equal(false) + }); + + it('should return false when required params are not passed', () => { + let bid = Object.assign({}, bid); + delete bid.params; + bid.params = {}; + expect(spec.isBidRequestValid(bid)).to.equal(false) + }) + }); + + describe('buildRequests', () => { + afterEach(() => { + config.resetConfig(); + }); + + it('should return an empty array when there are no bid requests', () => { + const bidRequests = [] + const request = spec.buildRequests(bidRequests) + + expect(request).to.be.an('array').that.is.empty + }); + + it('should send `x-aotter-clientid` header as empty string when user not set config `clientId`', () => { + const bidRequests = [{ + bidder: BIDDER_CODE, + params: { + placeUid: '123' + } + }] + + const bidderRequest = {} + const request = spec.buildRequests(bidRequests, bidderRequest)[0]; + + expect(request.options.customHeaders['x-aotter-clientid']).equal('') + }) + + it('should send bid requests to ENDPOINT via POST', () => { + const bidRequests = [{ + bidder: BIDDER_CODE, + params: { + placeUid: '123' + } + }] + + const bidderRequest = { + refererInfo: { + referer: 'https://aseal.in/', + } + } + + config.setConfig({ + aseal: { + clientId: TEST_CLIENT_ID + } + }); + const request = spec.buildRequests(bidRequests, bidderRequest)[0]; + + expect(request.url).to.equal(API_ENDPOINT); + expect(request.method).to.equal('POST') + expect(request.options).deep.equal({ + contentType: 'application/json', + withCredentials: true, + customHeaders: { + 'x-aotter-clientid': TEST_CLIENT_ID, + 'x-aotter-version': HEADER_AOTTER_VERSION, + } + }); + expect(request.data).deep.equal({ + bids: bidRequests, + refererInfo: bidderRequest.refererInfo, + }); + }); + }); + + describe('interpretResponse', () => { + it('should return an empty array when there are no bids', () => { + const serverResponse = {} + const response = spec.interpretResponse(serverResponse); + + expect(response).is.an('array').that.is.empty; + }); + + it('should get correct bid response', () => { + const serverResponse = { + body: [{ + requestId: '2ef08f145b7a4f', + cpm: 3, + width: 300, + height: 250, + creativeId: '123abc', + dealId: '123abc', + currency: 'USD', + netRevenue: false, + mediaType: 'banner', + ttl: 300, + ad: '' + }] + } + const response = spec.interpretResponse(serverResponse); + + expect(response).deep.equal(serverResponse.body); + }); + }) +}); diff --git a/test/spec/modules/atsAnalyticsAdapter_spec.js b/test/spec/modules/atsAnalyticsAdapter_spec.js index 7f662ffd06d..cae90a19223 100644 --- a/test/spec/modules/atsAnalyticsAdapter_spec.js +++ b/test/spec/modules/atsAnalyticsAdapter_spec.js @@ -12,11 +12,15 @@ let constants = require('src/constants.json'); export const storage = getStorageManager(); let sandbox; +let clock; +let now = new Date(); + describe('ats analytics adapter', function () { beforeEach(function () { sinon.stub(events, 'getEvents').returns([]); storage.setCookie('_lr_env_src_ats', 'true', 'Thu, 01 Jan 1970 00:00:01 GMT'); sandbox = sinon.sandbox.create(); + clock = sandbox.useFakeTimers(now.getTime()); }); afterEach(function () { @@ -25,18 +29,20 @@ describe('ats analytics adapter', function () { atsAnalyticsAdapter.disableAnalytics(); Math.random.restore(); sandbox.restore(); + clock.restore(); }); describe('track', function () { it('builds and sends request and response data', function () { sinon.stub(Math, 'random').returns(0.99); - sinon.stub(atsAnalyticsAdapter, 'shouldFireRequest').returns(true); sinon.stub(atsAnalyticsAdapter, 'getUserAgent').returns('Mozilla/5.0 (Macintosh; Intel Mac OS X 10_7_5) AppleWebKit/536.25 (KHTML, like Gecko) Version/6.0 Safari/536.25'); - let now = new Date(); + now.setTime(now.getTime() + 3600000); storage.setCookie('_lr_env_src_ats', 'true', now.toUTCString()); storage.setCookie('_lr_sampling_rate', '10', now.toUTCString()); + this.timeout(2100); + let initOptions = { pid: '10433394' }; @@ -62,7 +68,7 @@ describe('ats analytics adapter', function () { 'refererInfo': { 'referer': 'https://example.com/dev' }, - 'auctionId': 'a5b849e5-87d7-4205-8300-d063084fcfb7', + 'auctionId': 'a5b849e5-87d7-4205-8300-d063084fcfb7' }; // prepare general auction - response let bidResponse = { @@ -90,7 +96,7 @@ describe('ats analytics adapter', function () { let expectedAfterBid = { 'Data': [{ 'has_envelope': true, - 'adapter_version': 2, + 'adapter_version': 3, 'bidder': 'appnexus', 'bid_id': '30c77d079cdf17', 'auction_id': 'a5b849e5-87d7-4205-8300-d063084fcfb7', @@ -103,10 +109,30 @@ describe('ats analytics adapter', function () { 'response_time_stamp': '2020-02-03T14:23:11.978Z', 'currency': 'USD', 'cpm': 0.5, - 'net_revenue': true + 'net_revenue': true, + 'bid_won': true }] }; + let wonRequest = { + 'adId': '2eddfdc0c791dc', + 'mediaType': 'banner', + 'requestId': '30c77d079cdf17', + 'cpm': 0.5, + 'creativeId': 29681110, + 'currency': 'USD', + 'netRevenue': true, + 'ttl': 300, + 'auctionId': 'a5b849e5-87d7-4205-8300-d063084fcfb7', + 'statusMessage': 'Bid available', + 'responseTimestamp': 1633525319061, + 'requestTimestamp': 1633525319258, + 'bidder': 'appnexus', + 'adUnitCode': 'div-gpt-ad-1438287399331-0', + 'size': '300x250', + 'status': 'rendered' + }; + // lets simulate that some bidders timeout let bidTimeoutArgsV1 = [ { @@ -148,6 +174,14 @@ describe('ats analytics adapter', function () { // Step 5: Send auction end event events.emit(constants.EVENTS.AUCTION_END, {}); + // Step 6: Send bid won event + events.emit(constants.EVENTS.BID_WON, wonRequest); + + sandbox.stub($$PREBID_GLOBAL$$, 'getAllWinningBids').callsFake((key) => { + return [wonRequest] + }); + + clock.tick(2000); let requests = server.requests.filter(req => { return req.url.indexOf(analyticsUrl) > -1; @@ -156,13 +190,12 @@ describe('ats analytics adapter', function () { expect(requests.length).to.equal(1); let realAfterBid = JSON.parse(requests[0].requestBody); - // Step 6: assert real data after bid and expected data + + // Step 7: assert real data after bid and expected data expect(realAfterBid['Data']).to.deep.equal(expectedAfterBid['Data']); // check that the publisher ID is configured via options expect(atsAnalyticsAdapter.context.pid).to.equal(initOptions.pid); - - atsAnalyticsAdapter.shouldFireRequest.restore(); }) it('check browser is safari', function () { sinon.stub(atsAnalyticsAdapter, 'getUserAgent').returns('Mozilla/5.0 (Macintosh; Intel Mac OS X 10_7_5) AppleWebKit/536.25 (KHTML, like Gecko) Version/6.0 Safari/536.25'); @@ -204,7 +237,7 @@ describe('ats analytics adapter', function () { sinon.stub(atsAnalyticsAdapter, 'getUserAgent').returns('Mozilla/5.0 (Macintosh; Intel Mac OS X 10_7_5) AppleWebKit/536.25 (KHTML, like Gecko) Version/6.0 Safari/536.25'); sinon.stub(Math, 'random').returns(0.99); // publisher can try to pass anything they want but we will set sampling rate to 100, which means we will have 1% of requests - let result = atsAnalyticsAdapter.shouldFireRequest(10); + let result = atsAnalyticsAdapter.shouldFireRequest(8); expect(result).to.equal(true); }) it('should not fire analytics request if math random is something other then 0.99', function () { diff --git a/test/spec/modules/beachfrontBidAdapter_spec.js b/test/spec/modules/beachfrontBidAdapter_spec.js index e29994eba44..d9b8cac10b4 100644 --- a/test/spec/modules/beachfrontBidAdapter_spec.js +++ b/test/spec/modules/beachfrontBidAdapter_spec.js @@ -1,6 +1,7 @@ import { expect } from 'chai'; import { spec, VIDEO_ENDPOINT, BANNER_ENDPOINT, OUTSTREAM_SRC, DEFAULT_MIMES } from 'modules/beachfrontBidAdapter.js'; -import { parseUrl } from 'src/utils.js'; +import { config } from 'src/config.js'; +import { parseUrl, deepAccess } from 'src/utils.js'; describe('BeachfrontAdapter', function () { let bidRequests; @@ -556,6 +557,69 @@ describe('BeachfrontAdapter', function () { }); }); + describe('with first-party data', function () { + let sandbox + + beforeEach(function () { + sandbox = sinon.sandbox.create(); + }); + + afterEach(function () { + sandbox.restore(); + }); + + it('must add first-party data to the video bid request', function () { + sandbox.stub(config, 'getConfig').callsFake(key => { + const cfg = { + ortb2: { + site: { + keywords: 'test keyword' + }, + user: { + data: 'some user data' + } + } + }; + return deepAccess(cfg, key); + }); + const bidRequest = bidRequests[0]; + bidRequest.mediaTypes = { video: {} }; + const bidderRequest = { + refererInfo: { + referer: 'http://example.com/page.html' + } + }; + const requests = spec.buildRequests([ bidRequest ], bidderRequest); + const data = requests[0].data; + expect(data.user.data).to.equal('some user data'); + expect(data.site.keywords).to.equal('test keyword'); + expect(data.site.page).to.equal('http://example.com/page.html'); + expect(data.site.domain).to.equal('example.com'); + }); + + it('must add first-party data to the banner bid request', function () { + sandbox.stub(config, 'getConfig').callsFake(key => { + const cfg = { + ortb2: { + site: { + keywords: 'test keyword' + }, + user: { + data: 'some user data' + } + } + }; + return deepAccess(cfg, key); + }); + const bidRequest = bidRequests[0]; + bidRequest.mediaTypes = { banner: {} }; + const requests = spec.buildRequests([ bidRequest ]); + const data = requests[0].data; + expect(data.ortb2.user.data).to.equal('some user data'); + expect(data.ortb2.site.keywords).to.equal('test keyword'); + }); + }); + describe('for multi-format bids', function () { it('should create a POST request for each bid format', function () { const width = 300; diff --git a/test/spec/modules/beopBidAdapter_spec.js b/test/spec/modules/beopBidAdapter_spec.js index b68adb8f196..832ad2707d3 100644 --- a/test/spec/modules/beopBidAdapter_spec.js +++ b/test/spec/modules/beopBidAdapter_spec.js @@ -124,7 +124,7 @@ describe('BeOp Bid Adapter tests', () => { expect(payload.tc_string).to.exist; expect(payload.tc_string).to.equal('BOJ8RZsOJ8RZsABAB8AAAAAZ+A=='); expect(payload.url).to.exist; - expect(payload.url).to.equal('http://test.te'); + expect(payload.url).to.equal('http://localhost:9876/context.html'); }); }); diff --git a/test/spec/modules/betweenBidAdapter_spec.js b/test/spec/modules/betweenBidAdapter_spec.js index 7f8e69669a8..65c200748e4 100644 --- a/test/spec/modules/betweenBidAdapter_spec.js +++ b/test/spec/modules/betweenBidAdapter_spec.js @@ -23,6 +23,32 @@ describe('betweenBidAdapterTests', function () { let req_data = JSON.parse(request.data)[0].data; expect(req_data.bidid).to.equal('bid1234'); }); + + it('validate_video_params', function () { + let bidRequestData = [{ + bidId: 'bid1234', + bidder: 'between', + params: {w: 240, h: 400, s: 1112}, + mediaTypes: { + video: { + context: 'outstream', + playerSize: [970, 250], + maxd: 123, + mind: 234, + codeType: 'unknown code type' + } + }, + }]; + let request = spec.buildRequests(bidRequestData); + let req_data = JSON.parse(request.data)[0].data; + + expect(req_data.mediaType).to.equal(2); + expect(req_data.maxd).to.equal(123); + expect(req_data.mind).to.equal(234); + expect(req_data.pos).to.equal('atf'); + expect(req_data.codeType).to.equal('inpage'); + }); + it('validate itu param', function() { let bidRequestData = [{ bidId: 'bid1234', @@ -230,6 +256,21 @@ describe('betweenBidAdapterTests', function () { expect(bid.requestId).to.equal('bid1234'); expect(bid.ad).to.equal('Ad html'); }); + + it('validate_response_video_params', function () { + let serverResponse = { + body: [{ + mediaType: 2, + vastXml: 'vastXml', + }] + }; + let bids = spec.interpretResponse(serverResponse); + expect(bids).to.have.lengthOf(1); + let bid = bids[0]; + expect(bid.mediaType).to.equal(2); + expect(bid.vastXml).to.equal('vastXml'); + }); + it('validate response params without currency', function () { let serverResponse = { body: [{ diff --git a/test/spec/modules/bidViewabilityIO_spec.js b/test/spec/modules/bidViewabilityIO_spec.js index b59dbc867c1..5b4944082bc 100644 --- a/test/spec/modules/bidViewabilityIO_spec.js +++ b/test/spec/modules/bidViewabilityIO_spec.js @@ -3,7 +3,7 @@ import * as events from 'src/events.js'; import * as utils from 'src/utils.js'; import * as sinon from 'sinon'; import { expect } from 'chai'; -import { EVENTS } from 'src/constants.json'; +import CONSTANTS from 'src/constants.json'; describe('#bidViewabilityIO', function() { const makeElement = (id) => { @@ -97,7 +97,7 @@ describe('#bidViewabilityIO', function() { expect(mockObserver.unobserve.calledOnce).to.be.true; expect(emitSpy.calledOnce).to.be.true; // expect(emitSpy.firstCall.args).to.be.false; - expect(emitSpy.firstCall.args[0]).to.eq(EVENTS.BID_VIEWABLE); + expect(emitSpy.firstCall.args[0]).to.eq(CONSTANTS.EVENTS.BID_VIEWABLE); }); }) diff --git a/test/spec/modules/bidViewability_spec.js b/test/spec/modules/bidViewability_spec.js index 211dec090a5..a822d86f852 100644 --- a/test/spec/modules/bidViewability_spec.js +++ b/test/spec/modules/bidViewability_spec.js @@ -5,7 +5,7 @@ import * as utils from 'src/utils.js'; import * as sinon from 'sinon'; import {expect, spy} from 'chai'; import * as prebidGlobal from 'src/prebidGlobal.js'; -import { EVENTS } from 'src/constants.json'; +import CONSTANTS from 'src/constants.json'; import adapterManager, { gdprDataHandler, uspDataHandler } from 'src/adapterManager.js'; import parse from 'url-parse'; @@ -279,9 +279,9 @@ describe('#bidViewability', function() { let call = callBidViewableBidderSpy.getCall(0); expect(call.args[0]).to.equal(PBJS_WINNING_BID.bidder); expect(call.args[1]).to.deep.equal(PBJS_WINNING_BID); - // EVENTS.BID_VIEWABLE is triggered + // CONSTANTS.EVENTS.BID_VIEWABLE is triggered call = eventsEmitSpy.getCall(0); - expect(call.args[0]).to.equal(EVENTS.BID_VIEWABLE); + expect(call.args[0]).to.equal(CONSTANTS.EVENTS.BID_VIEWABLE); expect(call.args[1]).to.deep.equal(PBJS_WINNING_BID); }); @@ -290,7 +290,7 @@ describe('#bidViewability', function() { expect(triggerPixelSpy.callCount).to.equal(0); // adapterManager.callBidViewableBidder is NOT called expect(callBidViewableBidderSpy.callCount).to.equal(0); - // EVENTS.BID_VIEWABLE is NOT triggered + // CONSTANTS.EVENTS.BID_VIEWABLE is NOT triggered expect(eventsEmitSpy.callCount).to.equal(0); }); }); diff --git a/test/spec/modules/bliinkBidAdapter_spec.js b/test/spec/modules/bliinkBidAdapter_spec.js index 4fbd0978552..729605f7db8 100644 --- a/test/spec/modules/bliinkBidAdapter_spec.js +++ b/test/spec/modules/bliinkBidAdapter_spec.js @@ -20,7 +20,7 @@ import { spec, buildBid, BLIINK_ENDPOINT_ENGINE, parseXML, getMetaList } from 'm * crumbs: {pubcid: string}, * ortb2Imp: {ext: {data: {pbadslot: string}}}}} */ -const getConfigBid = () => { +const getConfigBid = (placement) => { return { adUnitCode: '/19968336/test', auctionId: '6752b51c-dcd4-4001-85dc-885ab5c504cf', @@ -48,7 +48,7 @@ const getConfigBid = () => { } }, params: { - placement: 'banner', + placement: placement, tagId: '14f30eca-85d2-11e8-9eed-0242ac120007' }, sizes: [ @@ -66,24 +66,35 @@ const getConfigBid = () => { * viewability_percent_in_view: number, * viewability_duration: number, * ad_id: number, - * adm: string, * id: number, * category: number, - * type: number + * type: number, + * content: { + * creative: { + * adm: string + * } + * } + * } * } * } */ const getConfigCreative = () => { return { ad_id: 5648, - adm: '', price: 1, currency: 'EUR', + media_type: 'banner', category: 1, id: 2825, + creativeId: 2825, type: 1, viewability_duration: 1, viewability_percent_in_view: 30, + content: { + creative: { + adm: '', + } + } } } @@ -92,6 +103,7 @@ const getConfigCreativeVideo = () => { ad_id: 5648, price: 1, currency: 'EUR', + media_type: 'video', category: 1, creativeId: 2825, content: '' @@ -102,14 +114,11 @@ const getConfigCreativeVideo = () => { * @description Mockup BuildRequest function * @return {{bidderRequestId: string, bidderCode: string, bids: {bidderWinsCount: number, adUnitCode: string, bidder: string, src: string, bidRequestsCount: number, params: {tagId: string, placement: string}, bidId: string, transactionId: string, auctionId: string, bidderRequestId: string, bidderRequestsCount: number, mediaTypes: {banner: {sizes: number[][]}}, sizes: number[][], crumbs: {pubcid: string}, ortb2Imp: {ext: {data: {pbadslot: string}}}}[], refererInfo: {referer: string, canonicalUrl: null, isAmp: boolean, reachedTop: boolean, numIframes: number}}} */ -const getConfigBuildRequest = () => { - return { +const getConfigBuildRequest = (placement) => { + let buildRequest = { bidderRequestId: '164ddfd207e94d', bidderCode: 'bliink', - bids: [getConfigBid()], - params: { - bids: [getConfigBid()], - }, + bids: [getConfigBid(placement)], refererInfo: { canonicalUrl: null, isAmp: false, @@ -118,6 +127,17 @@ const getConfigBuildRequest = () => { referer: 'http://localhost:9999/integrationExamples/gpt/bliink-adapter.html?pbjs_debug=true', }, } + + if (!placement) { + return buildRequest + } + + return Object.assign(buildRequest, { + params: { + bids: [getConfigBid(placement)], + placement: placement + }, + }) } /** @@ -157,7 +177,19 @@ const getConfigInterpretResponseRTB = (noAd = false) => { } return { - body: '' + body: ` + + + + BLIINK + https://vast.bliink.io/p/508379d0-9f65-4198-8ba5-f61f2b51224f.xml + https://e.api.bliink.io/e?name=vast-error&token=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJleHAiOjE2MzQwMzA1MjcsImlhdCI6MTYzMzQyNTcyNywiaXNzIjoiYmxpaW5rIiwiZGF0YSI6eyJ0eXBlIjoiYWQtc2VydmVyIiwidHJhbnNhY3Rpb25JZCI6ImE2NjJjZGJmLTkzNDYtNDI0MS1iMTU0LTJhOTc2OTg0NjNmOSIsIm5ldHdvcmtJZCI6MjUsInNpdGVJZCI6MTQzLCJ0YWdJZCI6MTI3MSwiY29va2llSWQiOiIwNWFhN2UwMi05MzgzLTQ1NGYtOTJmZC1jOTE2YWNlMmUyZjYiLCJldmVudElkIjozLCJ0YXJnZXRpbmciOnsicGxhdGZvcm0iOiJXZWJzaXRlIiwicmVmZXJyZXIiOiJodHRwOi8vbG9jYWxob3N0OjgxODEvaW50ZWdyYXRpb25FeGFtcGxlcy9ncHQvYmxpaW5rLWluc3RyZWFtLmh0bWwiLCJwYWdlVXJsIjoiaHR0cDovL2xvY2FsaG9zdDo4MTgxL2ludGVncmF0aW9uRXhhbXBsZXMvZ3B0L2JsaWluay1pbnN0cmVhbS5odG1sIiwiaXAiOiIzMS4zOS4xNDEuMTQwIiwidGltZSI6MTYzMzQyNTcyNywibG9jYXRpb24iOnsibGF0aXR1ZGUiOjQ4Ljk0MjIsImxvbmdpdHVkZSI6Mi41MDM5LCJyZWdpb24iOiJJREYiLCJjb3VudHJ5IjoiRlIiLCJjaXR5IjoiQXVsbmF5LXNvdXMtQm9pcyIsInppcENvZGUiOiI5MzYwMCIsImRlcGFydG1lbnQiOiI5MyJ9LCJjaXR5IjoiQXVsbmF5LXNvdXMtQm9pcyIsImNvdW50cnkiOiJGUiIsImRldmljZU9zIjoibWFjT1MiLCJkZXZpY2VQbGF0Zm9ybSI6IldlYnNpdGUiLCJyYXdVc2VyQWdlbnQiOiJNb3ppbGxhLzUuMCAoTWFjaW50b3NoOyBJbnRlbCBNYWMgT1MgWCAxMF8xNV83KSBBcHBsZVdlYktpdC81MzcuMzYgKEtIVE1MLCBsaWtlIEdlY2tvKSBDaHJvbWUvOTMuMC40NTc3LjYzIFNhZmFyaS81MzcuMzYiLCJjb250ZW50Q2xhc3NpZmljYXRpb24iOnsiYnJhbmRzYWZlIjpmYWxzZX19LCJnZHByIjp7Imhhc0NvbnNlbnQiOnRydWV9LCJ3aW4iOmZhbHNlLCJhZElkIjo1NzkzLCJhZHZlcnRpc2VySWQiOjEsImNhbXBhaWduSWQiOjEsImNyZWF0aXZlSWQiOjExOTQsImVycm9yIjpmYWxzZX19.nJSJPKovg0_jSHtLdrMPDqesAIlFKCuXPXYxpsyWBDw + https://e.api.bliink.io/e?name=impression&token=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJleHAiOjE2MzQwMzA1MjcsImlhdCI6MTYzMzQyNTcyNywiaXNzIjoiYmxpaW5rIiwiZGF0YSI6eyJ0eXBlIjoiYWQtc2VydmVyIiwidHJhbnNhY3Rpb25JZCI6ImE2NjJjZGJmLTkzNDYtNDI0MS1iMTU0LTJhOTc2OTg0NjNmOSIsIm5ldHdvcmtJZCI6MjUsInNpdGVJZCI6MTQzLCJ0YWdJZCI6MTI3MSwiY29va2llSWQiOiIwNWFhN2UwMi05MzgzLTQ1NGYtOTJmZC1jOTE2YWNlMmUyZjYiLCJldmVudElkIjozLCJ0YXJnZXRpbmciOnsicGxhdGZvcm0iOiJXZWJzaXRlIiwicmVmZXJyZXIiOiJodHRwOi8vbG9jYWxob3N0OjgxODEvaW50ZWdyYXRpb25FeGFtcGxlcy9ncHQvYmxpaW5rLWluc3RyZWFtLmh0bWwiLCJwYWdlVXJsIjoiaHR0cDovL2xvY2FsaG9zdDo4MTgxL2ludGVncmF0aW9uRXhhbXBsZXMvZ3B0L2JsaWluay1pbnN0cmVhbS5odG1sIiwiaXAiOiIzMS4zOS4xNDEuMTQwIiwidGltZSI6MTYzMzQyNTcyNywibG9jYXRpb24iOnsibGF0aXR1ZGUiOjQ4Ljk0MjIsImxvbmdpdHVkZSI6Mi41MDM5LCJyZWdpb24iOiJJREYiLCJjb3VudHJ5IjoiRlIiLCJjaXR5IjoiQXVsbmF5LXNvdXMtQm9pcyIsInppcENvZGUiOiI5MzYwMCIsImRlcGFydG1lbnQiOiI5MyJ9LCJjaXR5IjoiQXVsbmF5LXNvdXMtQm9pcyIsImNvdW50cnkiOiJGUiIsImRldmljZU9zIjoibWFjT1MiLCJkZXZpY2VQbGF0Zm9ybSI6IldlYnNpdGUiLCJyYXdVc2VyQWdlbnQiOiJNb3ppbGxhLzUuMCAoTWFjaW50b3NoOyBJbnRlbCBNYWMgT1MgWCAxMF8xNV83KSBBcHBsZVdlYktpdC81MzcuMzYgKEtIVE1MLCBsaWtlIEdlY2tvKSBDaHJvbWUvOTMuMC40NTc3LjYzIFNhZmFyaS81MzcuMzYiLCJjb250ZW50Q2xhc3NpZmljYXRpb24iOnsiYnJhbmRzYWZlIjpmYWxzZX19LCJnZHByIjp7Imhhc0NvbnNlbnQiOnRydWV9LCJ3aW4iOmZhbHNlLCJhZElkIjo1NzkzLCJhZHZlcnRpc2VySWQiOjEsImNhbXBhaWduSWQiOjEsImNyZWF0aXZlSWQiOjExOTQsImVycm9yIjpmYWxzZX19.nJSJPKovg0_jSHtLdrMPDqesAIlFKCuXPXYxpsyWBDw + 1EUR + + + + ` } } @@ -284,7 +316,7 @@ const testsIsBidRequestValid = [ { title: 'isBidRequestValid Receive a valid bid', args: { - fn: spec.isBidRequestValid(getConfigBid()) + fn: spec.isBidRequestValid(getConfigBid('banner')) }, want: true, } @@ -303,14 +335,13 @@ const testsInterpretResponse = [ { title: 'Should construct bid for video instream', args: { - fn: spec.interpretResponse(getConfigInterpretResponseRTB(false), getConfigBuildRequest()) + fn: spec.interpretResponse(getConfigInterpretResponseRTB(false), getConfigBuildRequest('video')) }, want: { - ad: '', cpm: 0, currency: 'EUR', - height: 1, - width: 1, + height: 250, + width: 300, creativeId: 0, mediaType: 'video', netRevenue: false, @@ -322,7 +353,7 @@ const testsInterpretResponse = [ { title: 'ServerResponse with message: invalid tag, return empty array', args: { - fn: spec.interpretResponse(getConfigInterpretResponse(true), getConfigBuildRequest()) + fn: spec.interpretResponse(getConfigInterpretResponse(true), getConfigBuildRequest('banner')) }, want: [] }, @@ -332,7 +363,10 @@ describe('BLIINK Adapter interpretResponse', function() { for (const test of testsInterpretResponse) { it(test.title, () => { const res = test.args.fn - expect(res).to.eql(test.want) + + if (res) { + expect(res).to.eql(test.want) + } }) } }) @@ -372,21 +406,38 @@ const testsBuildBid = [ { title: 'input data respect the output model for video', args: { - fn: buildBid(getConfigBid(), getConfigCreativeVideo()) + fn: buildBid(getConfigBid('video'), getConfigCreativeVideo()) }, want: { - requestId: getConfigBid().bidId, + requestId: getConfigBid('video').bidId, cpm: 1, currency: 'EUR', mediaType: 'video', - width: 1, - height: 1, + width: 300, + height: 250, creativeId: getConfigCreativeVideo().creativeId, netRevenue: false, vastXml: getConfigCreativeVideo().content, - ad: getConfigCreative().adm, ttl: 3600, } + }, + { + title: 'input data respect the output model for banner', + args: { + fn: buildBid(getConfigBid('banner'), getConfigCreative()) + }, + want: { + requestId: getConfigBid('banner').bidId, + cpm: 1, + currency: 'EUR', + mediaType: 'banner', + width: 300, + height: 250, + creativeId: getConfigCreative().id, + ad: getConfigCreative().content.creative.adm, + ttl: 3600, + netRevenue: false, + } } ] @@ -414,18 +465,20 @@ const testsBuildRequests = [ { title: 'Should build request if bidderRequest exist', args: { - fn: spec.buildRequests([], getConfigBuildRequest()) + fn: spec.buildRequests([], getConfigBuildRequest('banner')) }, want: { method: 'GET', - url: `${BLIINK_ENDPOINT_ENGINE}/${getConfigBuildRequest().bids[0].params.tagId}`, + url: `${BLIINK_ENDPOINT_ENGINE}/${getConfigBuildRequest('banner').bids[0].params.tagId}`, params: { - bidderRequestId: getConfigBuildRequest().bidderRequestId, - bidderCode: getConfigBuildRequest().bidderCode, - bids: getConfigBuildRequest().bids, - refererInfo: getConfigBuildRequest().refererInfo + bidderRequestId: getConfigBuildRequest('banner').bidderRequestId, + bidderCode: getConfigBuildRequest('banner').bidderCode, + bids: getConfigBuildRequest('banner').bids, + refererInfo: getConfigBuildRequest('banner').refererInfo }, data: { + gdpr: false, + gdpr_consent: '', height: 250, width: 300, keywords: '', @@ -438,7 +491,7 @@ const testsBuildRequests = [ { title: 'Should build request width GDPR configuration', args: { - fn: spec.buildRequests([], Object.assign(getConfigBuildRequest(), { + fn: spec.buildRequests([], Object.assign(getConfigBuildRequest('banner'), { gdprConsent: { gdprApplies: true, consentString: 'XXXX' @@ -447,12 +500,12 @@ const testsBuildRequests = [ }, want: { method: 'GET', - url: `${BLIINK_ENDPOINT_ENGINE}/${getConfigBuildRequest().bids[0].params.tagId}`, + url: `${BLIINK_ENDPOINT_ENGINE}/${getConfigBuildRequest('banner').bids[0].params.tagId}`, params: { - bidderRequestId: getConfigBuildRequest().bidderRequestId, - bidderCode: getConfigBuildRequest().bidderCode, - bids: getConfigBuildRequest().bids, - refererInfo: getConfigBuildRequest().refererInfo + bidderRequestId: getConfigBuildRequest('banner').bidderRequestId, + bidderCode: getConfigBuildRequest('banner').bidderCode, + bids: getConfigBuildRequest('banner').bids, + refererInfo: getConfigBuildRequest('banner').refererInfo }, data: { gdpr: true, diff --git a/test/spec/modules/brandmetricsRtdProvider_spec.js b/test/spec/modules/brandmetricsRtdProvider_spec.js new file mode 100644 index 00000000000..3cac5a3d559 --- /dev/null +++ b/test/spec/modules/brandmetricsRtdProvider_spec.js @@ -0,0 +1,191 @@ +import * as brandmetricsRTD from '../../../modules/brandmetricsRtdProvider.js'; +import {config} from 'src/config.js'; + +const VALID_CONFIG = { + name: 'brandmetrics', + waitForIt: true, + params: { + scriptId: '00000000-0000-0000-0000-000000000000', + bidders: ['ozone'] + } +}; + +const NO_BIDDERS_CONFIG = { + name: 'brandmetrics', + waitForIt: true, + params: { + scriptId: '00000000-0000-0000-0000-000000000000' + } +}; + +const NO_SCRIPTID_CONFIG = { + name: 'brandmetrics', + waitForIt: true +}; + +const USER_CONSENT = { + gdpr: { + vendorData: { + vendor: { + consents: { + 422: true + } + }, + purpose: { + consents: { + 1: true, + 7: true + } + } + }, + gdprApplies: true + } +}; + +const NO_TCF_CONSENT = { + gdpr: { + vendorData: { + vendor: { + consents: { + 422: false + } + }, + purpose: { + consents: { + 1: false, + 7: false + } + } + }, + gdprApplies: true + } +}; + +const NO_USP_CONSENT = { + usp: '1NYY' +}; + +function mockSurveyLoaded(surveyConf) { + const commands = window._brandmetrics || []; + commands.forEach(command => { + if (command.cmd === '_addeventlistener') { + const conf = command.val; + if (conf.event === 'surveyloaded') { + conf.handler(surveyConf); + } + } + }); +} + +function scriptTagExists(url) { + const tags = document.getElementsByTagName('script'); + for (let i = 0; i < tags.length; i++) { + if (tags[i].src === url) { + return true; + } + } + return false; +} + +describe('BrandmetricsRTD module', () => { + beforeEach(function () { + const scriptTags = document.getElementsByTagName('script'); + for (let i = 0; i < scriptTags.length; i++) { + if (scriptTags[i].src.indexOf('brandmetrics') !== -1) { + scriptTags[i].remove(); + } + } + }); + + it('should init and return true', () => { + expect(brandmetricsRTD.brandmetricsSubmodule.init(VALID_CONFIG, USER_CONSENT)).to.equal(true); + }); + + it('should init and return true even if bidders is not included', () => { + expect(brandmetricsRTD.brandmetricsSubmodule.init(NO_BIDDERS_CONFIG, USER_CONSENT)).to.equal(true); + }); + + it('should init even if script- id is not configured', () => { + expect(brandmetricsRTD.brandmetricsSubmodule.init(NO_SCRIPTID_CONFIG, USER_CONSENT)).to.equal(true); + }); + + it('should not init when there is no TCF- consent', () => { + expect(brandmetricsRTD.brandmetricsSubmodule.init(VALID_CONFIG, NO_TCF_CONSENT)).to.equal(false); + }); + + it('should not init when there is no usp- consent', () => { + expect(brandmetricsRTD.brandmetricsSubmodule.init(VALID_CONFIG, NO_USP_CONSENT)).to.equal(false); + }); +}); + +describe('getBidRequestData', () => { + beforeEach(function () { + config.resetConfig() + }) + + it('should set targeting keys for specified bidders', () => { + brandmetricsRTD.brandmetricsSubmodule.getBidRequestData({}, () => { + const bidderConfig = config.getBidderConfig() + const expected = VALID_CONFIG.params.bidders + + expected.forEach(exp => { + expect(bidderConfig[exp].ortb2.user.ext.data.mockTargetKey).to.equal('mockMeasurementId') + }) + }, VALID_CONFIG); + + mockSurveyLoaded({ + available: true, + conf: { + displayOption: { + type: 'pbjs', + targetKey: 'mockTargetKey' + } + }, + survey: { + measurementId: 'mockMeasurementId' + } + }); + }); + + it('should only set targeting keys when the brandmetrics survey- type is "pbjs"', () => { + mockSurveyLoaded({ + available: true, + conf: { + displayOption: { + type: 'dfp', + targetKey: 'mockTargetKey' + } + }, + survey: { + measurementId: 'mockMeasurementId' + } + }); + + brandmetricsRTD.brandmetricsSubmodule.getBidRequestData({}, () => {}, VALID_CONFIG); + const bidderConfig = config.getBidderConfig() + expect(Object.keys(bidderConfig).length).to.equal(0) + }); + + it('should use a default targeting key name if the brandmetrics- configuration does not include one', () => { + mockSurveyLoaded({ + available: true, + conf: { + displayOption: { + type: 'pbjs', + } + }, + survey: { + measurementId: 'mockMeasurementId' + } + }); + + brandmetricsRTD.brandmetricsSubmodule.getBidRequestData({}, () => {}, VALID_CONFIG); + + const bidderConfig = config.getBidderConfig() + const expected = VALID_CONFIG.params.bidders + + expected.forEach(exp => { + expect(bidderConfig[exp].ortb2.user.ext.data.brandmetrics_survey).to.equal('mockMeasurementId') + }) + }); +}); diff --git a/test/spec/modules/browsiRtdProvider_spec.js b/test/spec/modules/browsiRtdProvider_spec.js index ee37d16905b..32e1c7fe795 100644 --- a/test/spec/modules/browsiRtdProvider_spec.js +++ b/test/spec/modules/browsiRtdProvider_spec.js @@ -1,5 +1,6 @@ import * as browsiRTD from '../../../modules/browsiRtdProvider.js'; import {makeSlot} from '../integration/faker/googletag.js'; +import * as utils from '../../../src/utils' describe('browsi Real time data sub module', function () { const conf = { @@ -29,11 +30,11 @@ describe('browsi Real time data sub module', function () { }); it('should match placement with ad unit', function () { - const slot = makeSlot({code: '/57778053/Browsi_Demo_300x250', divId: 'browsiAd_1'}); + const slot = makeSlot({code: '/123/abc', divId: 'browsiAd_1'}); - const test1 = browsiRTD.isIdMatchingAdUnit(slot, ['/57778053/Browsi_Demo_300x250']); // true - const test2 = browsiRTD.isIdMatchingAdUnit(slot, ['/57778053/Browsi_Demo_300x250', '/57778053/Browsi']); // true - const test3 = browsiRTD.isIdMatchingAdUnit(slot, ['/57778053/Browsi_Demo_Low']); // false + const test1 = browsiRTD.isIdMatchingAdUnit(slot, ['/123/abc']); // true + const test2 = browsiRTD.isIdMatchingAdUnit(slot, ['/123/abc', '/456/def']); // true + const test3 = browsiRTD.isIdMatchingAdUnit(slot, ['/123/def']); // false const test4 = browsiRTD.isIdMatchingAdUnit(slot, []); // true expect(test1).to.equal(true); @@ -43,12 +44,12 @@ describe('browsi Real time data sub module', function () { }); it('should return correct macro values', function () { - const slot = makeSlot({code: '/57778053/Browsi_Demo_300x250', divId: 'browsiAd_1'}); + const slot = makeSlot({code: '/123/abc', divId: 'browsiAd_1'}); slot.setTargeting('test', ['test', 'value']); // slot getTargeting doesn't act like GPT so we can't expect real value const macroResult = browsiRTD.getMacroId({p: '/'}, slot); - expect(macroResult).to.equal('/57778053/Browsi_Demo_300x250/NA'); + expect(macroResult).to.equal('/123/abc/NA'); const macroResultB = browsiRTD.getMacroId({}, slot); expect(macroResultB).to.equal('browsiAd_1'); @@ -72,7 +73,7 @@ describe('browsi Real time data sub module', function () { it('should return prediction from server', function () { makeSlot({code: 'hasPrediction', divId: 'hasPrediction'}); const data = { - p: {'hasPrediction': {p: 0.234}}, + p: {'hasPrediction': {ps: {0: 0.234}}}, kn: 'bv', pmd: undefined }; @@ -80,4 +81,58 @@ describe('browsi Real time data sub module', function () { expect(browsiRTD.browsiSubmodule.getTargetingData(['hasPrediction'])).to.eql({hasPrediction: {bv: '0.20'}}); }) }) + + describe('should return matching prediction', function () { + const predictions = { + 0: 0.123, + 1: 0.254, + 3: 0, + 4: 0.8 + } + const singlePrediction = { + 0: 0.123 + } + it('should return raw value if valid', function () { + expect(browsiRTD.getCurrentData(predictions, 0)).to.equal(0.123); + expect(browsiRTD.getCurrentData(predictions, 1)).to.equal(0.254); + }) + it('should return 0 for prediction = 0', function () { + expect(browsiRTD.getCurrentData(predictions, 3)).to.equal(0); + }) + it('should return -1 for invalid params', function () { + expect(browsiRTD.getCurrentData(null, 3)).to.equal(-1); + expect(browsiRTD.getCurrentData(predictions, null)).to.equal(-1); + }) + it('should return prediction according to object keys length ', function () { + expect(browsiRTD.getCurrentData(singlePrediction, 0)).to.equal(0.123); + expect(browsiRTD.getCurrentData(singlePrediction, 1)).to.equal(-1); + expect(browsiRTD.getCurrentData(singlePrediction, 2)).to.equal(-1); + expect(browsiRTD.getCurrentData(predictions, 4)).to.equal(0.8); + expect(browsiRTD.getCurrentData(predictions, 5)).to.equal(0.8); + expect(browsiRTD.getCurrentData(predictions, 8)).to.equal(0.8); + }) + }) + describe('should set bid request data', function () { + const data = { + p: { + 'adUnit1': {ps: {0: 0.234}}, + 'adUnit2': {ps: {0: 0.134}}}, + kn: 'bv', + pmd: undefined + }; + browsiRTD.setData(data); + const fakeAdUnits = [ + { + code: 'adUnit1' + }, + { + code: 'adUnit2' + } + ] + browsiRTD.browsiSubmodule.getBidRequestData({adUnits: fakeAdUnits}, () => {}, {}, null); + it('should set ad unit params with prediction values', function () { + expect(utils.deepAccess(fakeAdUnits[0], 'ortb2Imp.ext.data.browsi')).to.eql({bv: '0.20'}); + expect(utils.deepAccess(fakeAdUnits[1], 'ortb2Imp.ext.data.browsi')).to.eql({bv: '0.10'}); + }) + }) }); diff --git a/test/spec/modules/cleanioRtdProvider_spec.js b/test/spec/modules/cleanioRtdProvider_spec.js new file mode 100644 index 00000000000..47c4b1b4961 --- /dev/null +++ b/test/spec/modules/cleanioRtdProvider_spec.js @@ -0,0 +1,188 @@ +import * as utils from '../../../src/utils.js'; +import * as hook from '../../../src/hook.js' + +import { __TEST__ } from '../../../modules/cleanioRtdProvider.js'; + +const { + readConfig, + ConfigError, + pageInitStepPreloadScript, + pageInitStepProtectPage, + bidWrapStepAugmentHtml, + bidWrapStepProtectByWrapping, + beforeInit, +} = __TEST__; + +sinon.assert.expose(chai.assert, { prefix: 'sinon' }); + +const fakeScriptURL = 'https://example.com/script.js'; + +function makeFakeBidResponse() { + return { + ad: 'hello ad', + bidderCode: 'BIDDER', + creativeId: 'CREATIVE', + cpm: 1.23, + }; +} + +describe('clean.io RTD module', function () { + describe('readConfig()', function() { + it('should throw ConfigError on invalid configurations', function() { + expect(() => readConfig({})).to.throw(ConfigError); + expect(() => readConfig({ params: {} })).to.throw(ConfigError); + expect(() => readConfig({ params: { protectionMode: 'bids' } })).to.throw(ConfigError); + expect(() => readConfig({ params: { cdnUrl: 'abc' } })).to.throw(ConfigError); + expect(() => readConfig({ params: { cdnUrl: 'abc', protectionMode: 'bids' } })).to.throw(ConfigError); + expect(() => readConfig({ params: { cdnUrl: 'https://abc1234567890.cloudfront.net/script.js', protectionMode: '123' } })).to.throw(ConfigError); + }); + + it('should accept valid configurations', function() { + expect(() => readConfig({ params: { cdnUrl: 'https://abc1234567890.cloudfront.net/script.js', protectionMode: 'full' } })).to.not.throw(); + expect(() => readConfig({ params: { cdnUrl: 'https://abc1234567890.cloudfront.net/script.js', protectionMode: 'bids' } })).to.not.throw(); + expect(() => readConfig({ params: { cdnUrl: 'https://abc1234567890.cloudfront.net/script.js', protectionMode: 'bids-nowait' } })).to.not.throw(); + }); + }); + + describe('Module initialization step', function() { + let insertElementStub; + beforeEach(function() { + insertElementStub = sinon.stub(utils, 'insertElement'); + }); + afterEach(function() { + utils.insertElement.restore(); + }); + + it('pageInitStepPreloadScript() should insert link/preload element', function() { + pageInitStepPreloadScript(fakeScriptURL); + + sinon.assert.calledOnce(insertElementStub); + sinon.assert.calledWith(insertElementStub, sinon.match(elem => elem.tagName === 'LINK')); + sinon.assert.calledWith(insertElementStub, sinon.match(elem => elem.rel === 'preload')); + sinon.assert.calledWith(insertElementStub, sinon.match(elem => elem.as === 'script')); + sinon.assert.calledWith(insertElementStub, sinon.match(elem => elem.href === fakeScriptURL)); + }); + + it('pageInitStepProtectPage() should insert script element', function() { + pageInitStepProtectPage(fakeScriptURL); + + sinon.assert.calledOnce(insertElementStub); + sinon.assert.calledWith(insertElementStub, sinon.match(elem => elem.tagName === 'SCRIPT')); + sinon.assert.calledWith(insertElementStub, sinon.match(elem => elem.type === 'text/javascript')); + sinon.assert.calledWith(insertElementStub, sinon.match(elem => elem.src === fakeScriptURL)); + }); + }); + + function ensurePrependToBidResponse(fakeBidResponse) { + expect(fakeBidResponse).to.have.own.property('ad').which.is.a('string'); + expect(fakeBidResponse.ad).to.contain(''); + } + + function ensureWrapBidResponse(fakeBidResponse, scriptUrl) { + expect(fakeBidResponse).to.have.own.property('ad').which.is.a('string'); + expect(fakeBidResponse.ad).to.contain(`src="${scriptUrl}"`); + expect(fakeBidResponse.ad).to.contain('agent.put(ad)'); + } + + describe('Bid processing step', function() { + it('bidWrapStepAugmentHtml() should prepend bid-specific information in a comment', function() { + const fakeBidResponse = makeFakeBidResponse(); + bidWrapStepAugmentHtml(fakeBidResponse); + ensurePrependToBidResponse(fakeBidResponse); + }); + + it('bidWrapStepProtectByWrapping() should wrap payload into a script tag', function() { + const fakeBidResponse = makeFakeBidResponse(); + bidWrapStepProtectByWrapping(fakeScriptURL, 0, fakeBidResponse); + ensureWrapBidResponse(fakeBidResponse, fakeScriptURL); + }); + }); + + describe('Sumbodule execution', function() { + let submoduleStub; + let insertElementStub; + beforeEach(function () { + submoduleStub = sinon.stub(hook, 'submodule'); + insertElementStub = sinon.stub(utils, 'insertElement'); + }); + afterEach(function () { + utils.insertElement.restore(); + submoduleStub.restore(); + }); + + function getModule() { + beforeInit(); + + expect(submoduleStub.calledOnceWith('realTimeData')).to.equal(true); + + const registeredSubmoduleDefinition = submoduleStub.getCall(0).args[1]; + expect(registeredSubmoduleDefinition).to.be.an('object'); + expect(registeredSubmoduleDefinition).to.have.own.property('name', 'clean.io'); + expect(registeredSubmoduleDefinition).to.have.own.property('init').that.is.a('function'); + expect(registeredSubmoduleDefinition).to.have.own.property('onBidResponseEvent').that.is.a('function'); + + return registeredSubmoduleDefinition; + } + + it('should register clean.io RTD submodule provider', function () { + getModule(); + }); + + it('should refuse initialization with incorrect parameters', function () { + const { init } = getModule(); + expect(init({ params: { cdnUrl: 'abc', protectionMode: 'full' } }, {})).to.equal(false); // too short distribution name + sinon.assert.notCalled(insertElementStub); + }); + + it('should iniitalize in full (page) protection mode', function () { + const { init, onBidResponseEvent } = getModule(); + expect(init({ params: { cdnUrl: 'https://abc1234567890.cloudfront.net/script.js', protectionMode: 'full' } }, {})).to.equal(true); + sinon.assert.calledOnce(insertElementStub); + sinon.assert.calledWith(insertElementStub, sinon.match(elem => elem.tagName === 'SCRIPT')); + + const fakeBidResponse = makeFakeBidResponse(); + onBidResponseEvent(fakeBidResponse, {}, {}); + ensurePrependToBidResponse(fakeBidResponse); + }); + + it('should iniitalize in bids (frame) protection mode', function () { + const { init, onBidResponseEvent } = getModule(); + expect(init({ params: { cdnUrl: 'https://abc1234567890.cloudfront.net/script.js', protectionMode: 'bids' } }, {})).to.equal(true); + sinon.assert.calledOnce(insertElementStub); + sinon.assert.calledWith(insertElementStub, sinon.match(elem => elem.tagName === 'LINK')); + + const fakeBidResponse = makeFakeBidResponse(); + onBidResponseEvent(fakeBidResponse, {}, {}); + ensureWrapBidResponse(fakeBidResponse, 'https://abc1234567890.cloudfront.net/script.js'); + }); + + it('should respect preload status in bids-nowait protection mode', function () { + const { init, onBidResponseEvent } = getModule(); + expect(init({ params: { cdnUrl: 'https://abc1234567890.cloudfront.net/script.js', protectionMode: 'bids-nowait' } }, {})).to.equal(true); + sinon.assert.calledOnce(insertElementStub); + sinon.assert.calledWith(insertElementStub, sinon.match(elem => elem.tagName === 'LINK')); + const preloadLink = insertElementStub.getCall(0).args[0]; + expect(preloadLink).to.have.property('onload').which.is.a('function'); + expect(preloadLink).to.have.property('onerror').which.is.a('function'); + + const fakeBidResponse1 = makeFakeBidResponse(); + onBidResponseEvent(fakeBidResponse1, {}, {}); + ensurePrependToBidResponse(fakeBidResponse1); + + // Simulate successful preloading + preloadLink.onload(); + + const fakeBidResponse2 = makeFakeBidResponse(); + onBidResponseEvent(fakeBidResponse2, {}, {}); + ensureWrapBidResponse(fakeBidResponse2, 'https://abc1234567890.cloudfront.net/script.js'); + + // Simulate error + preloadLink.onerror(); + + // Now we should fallback to just prepending + const fakeBidResponse3 = makeFakeBidResponse(); + onBidResponseEvent(fakeBidResponse3, {}, {}); + ensurePrependToBidResponse(fakeBidResponse3); + }); + }); +}); diff --git a/test/spec/modules/codefuelBidAdapter_spec.js b/test/spec/modules/codefuelBidAdapter_spec.js new file mode 100644 index 00000000000..a2549012d84 --- /dev/null +++ b/test/spec/modules/codefuelBidAdapter_spec.js @@ -0,0 +1,316 @@ +import {expect} from 'chai'; +import {spec} from 'modules/codefuelBidAdapter.js'; +import {config} from 'src/config.js'; +import {server} from 'test/mocks/xhr'; + +const USER_AGENT = 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) HeadlessChrome/92.0.4515.159 Safari/537.36'; +const DEFAULT_USER_AGENT = window.navigator.userAgent; +const setUADefault = () => { window.navigator.__defineGetter__('userAgent', function () { return DEFAULT_USER_AGENT }) }; +const setUAMock = () => { window.navigator.__defineGetter__('userAgent', function () { return USER_AGENT }) }; + +describe('Codefuel Adapter', function () { + describe('Bid request and response', function () { + const commonBidRequest = { + bidder: 'codefuel', + params: { + publisher: { + id: 'publisher-id' + }, + }, + bidId: '2d6815a92ba1ba', + auctionId: '12043683-3254-4f74-8934-f941b085579e', + } + const nativeBidRequestParams = { + nativeParams: { + image: { + required: true, + sizes: [ + 120, + 100 + ], + sendId: true + }, + title: { + required: true, + sendId: true + }, + sponsoredBy: { + required: false + } + }, + } + + const displayBidRequestParams = { + sizes: [ + [ + 300, + 250 + ] + ] + } + + describe('isBidRequestValid', function () { + before(() => { + config.setConfig({ + codefuel: { + bidderUrl: 'https://bidder-url.com', + } + } + ) + }) + after(() => { + config.resetConfig() + }) + + it('should fail when bid is invalid', function () { + const bid = { + bidder: 'codefuel', + params: { + publisher: { + id: 'publisher-id', + } + }, + } + expect(spec.isBidRequestValid(bid)).to.equal(false) + }) + it('should not succeed when bid contains native params', function () { + const bid = { + bidder: 'codefuel', + params: { + publisher: { + id: 'publisher-id', + } + }, + ...nativeBidRequestParams, + } + expect(spec.isBidRequestValid(bid)).to.equal(false) + }) + it('should not succeed when bid contains only sizes', function () { + const bid = { + bidder: 'codefuel', + params: { + publisher: { + id: 'publisher-id', + } + }, + ...displayBidRequestParams, + } + expect(spec.isBidRequestValid(bid)).to.equal(false) + }) + it('should fail if publisher id is not set', function () { + const bid = { + bidder: 'codefuel', + ...nativeBidRequestParams, + } + expect(spec.isBidRequestValid(bid)).to.equal(false) + }) + + it('should fail if bidder url is not set', function () { + const bid = { + bidder: 'codefuel', + params: { + publisher: { + id: 'publisher-id', + } + }, + ...nativeBidRequestParams, + } + config.resetConfig() + expect(spec.isBidRequestValid(bid)).to.equal(false) + }) + }) + + describe('buildRequests', function () { + before(() => { + setUAMock() + config.setConfig({ + codefuel: { + bidderUrl: 'https://bidder-url.com', + } + } + ) + }) + after(() => { + config.resetConfig() + setUADefault() + }) + + const commonBidderRequest = { + timeout: 500, + auctionId: '12043683-3254-4f74-8934-f941b085579e', + refererInfo: { + referer: 'https://example.com/', + } + } + + it('should build display request', function () { + const bidRequest = { + ...commonBidRequest, + ...displayBidRequestParams, + } + const expectedData = { + cur: [ + 'USD' + ], + device: { + devicetype: 2, + ua: 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) HeadlessChrome/92.0.4515.159 Safari/537.36' + }, + id: '12043683-3254-4f74-8934-f941b085579e', + imp: [ + { + banner: { + format: [ + { + h: 250, + w: 300 + } + ] + }, + id: '1' + } + ], + site: { + domain: 'example.com', + page: 'https://example.com/', + publisher: { + id: 'publisher-id' + } + }, + source: { + fd: 1 + }, + tmax: 500 + } + const res = spec.buildRequests([bidRequest], commonBidderRequest) + expect(res.url).to.equal('https://ai-p-codefuel-ds-rtb-us-east-1-k8s.seccint.com/prebid') + expect(res.data).to.deep.equal(expectedData) + }) + + it('should pass bidder timeout', function () { + const bidRequest = { + ...commonBidRequest, + } + const bidderRequest = { + ...commonBidderRequest, + timeout: 500 + } + const res = spec.buildRequests([bidRequest], bidderRequest) + const resData = res.data + expect(resData.tmax).to.equal(500) + }); + }) + + describe('interpretResponse', function () { + it('should return empty array if no valid bids', function () { + const res = spec.interpretResponse({}, []) + expect(res).to.be.an('array').that.is.empty + }); + + it('should interpret display response', function () { + const serverResponse = { + body: { + id: '6b2eedc8-8ff5-46ef-adcf-e701b508943e', + seatbid: [ + { + bid: [ + { + id: 'd90fe7fa-28d7-11eb-8ce4-462a842a7cf9', + impid: '1', + price: 1.1, + nurl: 'http://example.com/win/${AUCTION_PRICE}', + adm: '
ad
', + adomain: [ + 'example.com' + ], + cid: '3865084', + crid: '29998660', + cat: [ + 'IAB10-2' + ], + w: 300, + h: 250 + } + ], + seat: 'acc-6536' + } + ], + bidid: 'd90fe7fa-28d7-11eb-8ce4-13d94bfa26f9', + cur: 'USD' + } + } + const request = { + bids: [ + { + ...commonBidRequest, + ...displayBidRequestParams + } + ] + } + const expectedRes = [ + { + requestId: request.bids[0].bidId, + cpm: 1.1, + creativeId: '29998660', + ttl: 360, + netRevenue: true, + currency: 'USD', + mediaType: 'banner', + ad: '
ad
', + width: 300, + height: 250, + meta: {'advertiserDomains': []} + } + ] + + const res = spec.interpretResponse(serverResponse, request) + expect(res).to.deep.equal(expectedRes) + }); + }) + }) + + describe('getUserSyncs', function () { + const usersyncUrl = 'https://usersync-url.com'; + beforeEach(() => { + config.setConfig({ + codefuel: { + usersyncUrl: usersyncUrl, + } + } + ) + }) + after(() => { + config.resetConfig() + }) + + it('should not return user sync if pixel enabled with codefuel config', function () { + const ret = spec.getUserSyncs({pixelEnabled: true}) + expect(ret).to.be.an('array').that.is.empty + }) + + it('should not return user sync if pixel disabled', function () { + const ret = spec.getUserSyncs({pixelEnabled: false}) + expect(ret).to.be.an('array').that.is.empty + }) + + it('should not return user sync if url is not set', function () { + config.resetConfig() + const ret = spec.getUserSyncs({pixelEnabled: true}) + expect(ret).to.be.an('array').that.is.empty + }) + + it('should not pass GDPR consent', function() { + expect(spec.getUserSyncs({ pixelEnabled: true }, {}, {gdprApplies: true, consentString: 'foo'}, undefined)).to.to.be.an('array').that.is.empty + expect(spec.getUserSyncs({ pixelEnabled: true }, {}, {gdprApplies: false, consentString: 'foo'}, undefined)).to.be.an('array').that.is.empty + expect(spec.getUserSyncs({ pixelEnabled: true }, {}, {gdprApplies: true, consentString: undefined}, undefined)).to.be.an('array').that.is.empty + }); + + it('should not pass US consent', function() { + expect(spec.getUserSyncs({ pixelEnabled: true }, {}, undefined, '1NYN')).to.be.an('array').that.is.empty + }); + + it('should pass GDPR and US consent', function() { + expect(spec.getUserSyncs({ pixelEnabled: true }, {}, {gdprApplies: true, consentString: 'foo'}, '1NYN')).to.be.an('array').that.is.empty + }); + }) +}) diff --git a/test/spec/modules/colossussspBidAdapter_spec.js b/test/spec/modules/colossussspBidAdapter_spec.js index 0fe4d5b358e..91f900d6279 100644 --- a/test/spec/modules/colossussspBidAdapter_spec.js +++ b/test/spec/modules/colossussspBidAdapter_spec.js @@ -7,7 +7,8 @@ describe('ColossussspAdapter', function () { bidder: 'colossusssp', bidderRequestId: '145e1d6a7837c9', params: { - placement_id: 0 + placement_id: 0, + group_id: 0 }, placementCode: 'placementid_0', auctionId: '74f78609-a92d-4cf1-869f-1b244bbfb5d2', @@ -60,6 +61,7 @@ describe('ColossussspAdapter', function () { }); it('Should return false when placement_id is not a number', function () { bid.params.placement_id = 'aaa'; + delete bid.params.group_id; expect(spec.isBidRequestValid(bid)).to.be.false; }); }); @@ -95,9 +97,10 @@ describe('ColossussspAdapter', function () { let placements = data['placements']; for (let i = 0; i < placements.length; i++) { let placement = placements[i]; - expect(placement).to.have.all.keys('placementId', 'eids', 'bidId', 'traffic', 'sizes', 'schain', 'floor', 'gpid'); + expect(placement).to.have.all.keys('placementId', 'groupId', 'eids', 'bidId', 'traffic', 'sizes', 'schain', 'floor', 'gpid'); expect(placement.schain).to.be.an('object') expect(placement.placementId).to.be.a('number'); + expect(placement.groupId).to.be.a('number'); expect(placement.bidId).to.be.a('string'); expect(placement.traffic).to.be.a('string'); expect(placement.sizes).to.be.an('array'); @@ -186,6 +189,15 @@ describe('ColossussspAdapter', function () { }); }); + describe('onBidWon', function () { + it('should make an ajax call', function () { + const bid = { + nurl: 'http://example.com/win', + }; + expect(spec.onBidWon(bid)).to.equals(undefined); + }); + }) + describe('getUserSyncs', function () { let userSync = spec.getUserSyncs(); it('Returns valid URL and type', function () { diff --git a/test/spec/modules/compassBidAdapter_spec.js b/test/spec/modules/compassBidAdapter_spec.js new file mode 100644 index 00000000000..28021c4f7c0 --- /dev/null +++ b/test/spec/modules/compassBidAdapter_spec.js @@ -0,0 +1,398 @@ +import { expect } from 'chai'; +import { spec } from '../../../modules/compassBidAdapter.js'; +import { BANNER, VIDEO, NATIVE } from '../../../src/mediaTypes.js'; +import { getUniqueIdentifierStr } from '../../../src/utils.js'; + +const bidder = 'compass' + +describe('CompassBidAdapter', function () { + const bids = [ + { + bidId: getUniqueIdentifierStr(), + bidder: bidder, + mediaTypes: { + [BANNER]: { + sizes: [[300, 250]] + } + }, + params: { + placementId: 'testBanner', + } + }, + { + bidId: getUniqueIdentifierStr(), + bidder: bidder, + mediaTypes: { + [VIDEO]: { + playerSize: [[300, 300]], + minduration: 5, + maxduration: 60 + } + }, + params: { + placementId: 'testVideo', + } + }, + { + bidId: getUniqueIdentifierStr(), + bidder: bidder, + mediaTypes: { + [NATIVE]: { + native: { + title: { + required: true + }, + body: { + required: true + }, + icon: { + required: true, + size: [64, 64] + } + } + } + }, + params: { + placementId: 'testNative', + } + } + ]; + + const invalidBid = { + bidId: getUniqueIdentifierStr(), + bidder: bidder, + mediaTypes: { + [BANNER]: { + sizes: [[300, 250]] + } + }, + params: { + + } + } + + const bidderRequest = { + uspConsent: '1---', + gdprConsent: 'COvFyGBOvFyGBAbAAAENAPCAAOAAAAAAAAAAAEEUACCKAAA.IFoEUQQgAIQwgIwQABAEAAAAOIAACAIAAAAQAIAgEAACEAAAAAgAQBAAAAAAAGBAAgAAAAAAAFAAECAAAgAAQARAEQAAAAAJAAIAAgAAAYQEAAAQmAgBC3ZAYzUw', + refererInfo: { + referer: 'https://test.com' + } + }; + + describe('isBidRequestValid', function () { + it('Should return true if there are bidId, params and key parameters present', function () { + expect(spec.isBidRequestValid(bids[0])).to.be.true; + }); + it('Should return false if at least one of parameters is not present', function () { + expect(spec.isBidRequestValid(invalidBid)).to.be.false; + }); + }); + + describe('buildRequests', function () { + let serverRequest = spec.buildRequests(bids, bidderRequest); + + it('Creates a ServerRequest object with method, URL and data', function () { + expect(serverRequest).to.exist; + expect(serverRequest.method).to.exist; + expect(serverRequest.url).to.exist; + expect(serverRequest.data).to.exist; + }); + + it('Returns POST method', function () { + expect(serverRequest.method).to.equal('POST'); + }); + + it('Returns valid URL', function () { + expect(serverRequest.url).to.equal('https://sa-lb.deliverimp.com/pbjs'); + }); + + it('Returns general data valid', function () { + let data = serverRequest.data; + expect(data).to.be.an('object'); + expect(data).to.have.all.keys('deviceWidth', + 'deviceHeight', + 'language', + 'secure', + 'host', + 'page', + 'placements', + 'coppa', + 'ccpa', + 'gdpr', + 'tmax' + ); + expect(data.deviceWidth).to.be.a('number'); + expect(data.deviceHeight).to.be.a('number'); + expect(data.language).to.be.a('string'); + expect(data.secure).to.be.within(0, 1); + expect(data.host).to.be.a('string'); + expect(data.page).to.be.a('string'); + expect(data.coppa).to.be.a('number'); + expect(data.gdpr).to.be.a('string'); + expect(data.ccpa).to.be.a('string'); + expect(data.tmax).to.be.a('number'); + expect(data.placements).to.have.lengthOf(3); + }); + + it('Returns valid placements', function () { + const { placements } = serverRequest.data; + for (let i = 0, len = placements.length; i < len; i++) { + const placement = placements[i]; + expect(placement.placementId).to.be.oneOf(['testBanner', 'testVideo', 'testNative']); + expect(placement.adFormat).to.be.oneOf([BANNER, VIDEO, NATIVE]); + expect(placement.bidId).to.be.a('string'); + expect(placement.schain).to.be.an('object'); + expect(placement.bidfloor).to.exist.and.to.equal(0); + expect(placement.type).to.exist.and.to.equal('publisher'); + + if (placement.adFormat === BANNER) { + expect(placement.sizes).to.be.an('array'); + } + switch (placement.adFormat) { + case BANNER: + expect(placement.sizes).to.be.an('array'); + break; + case VIDEO: + expect(placement.playerSize).to.be.an('array'); + expect(placement.minduration).to.be.an('number'); + expect(placement.maxduration).to.be.an('number'); + break; + case NATIVE: + expect(placement.native).to.be.an('object'); + break; + } + } + }); + + it('Returns data with gdprConsent and without uspConsent', function () { + delete bidderRequest.uspConsent; + serverRequest = spec.buildRequests(bids, bidderRequest); + let data = serverRequest.data; + expect(data.gdpr).to.exist; + expect(data.gdpr).to.be.a('string'); + expect(data.gdpr).to.equal(bidderRequest.gdprConsent); + expect(data.ccpa).to.not.exist; + delete bidderRequest.gdprConsent; + }); + + it('Returns data with uspConsent and without gdprConsent', function () { + bidderRequest.uspConsent = '1---'; + delete bidderRequest.gdprConsent; + serverRequest = spec.buildRequests(bids, bidderRequest); + let data = serverRequest.data; + expect(data.ccpa).to.exist; + expect(data.ccpa).to.be.a('string'); + expect(data.ccpa).to.equal(bidderRequest.uspConsent); + expect(data.gdpr).to.not.exist; + }); + + it('Returns empty data if no valid requests are passed', function () { + serverRequest = spec.buildRequests([], bidderRequest); + let data = serverRequest.data; + expect(data.placements).to.be.an('array').that.is.empty; + }); + }); + + describe('interpretResponse', function () { + it('Should interpret banner response', function () { + const banner = { + body: [{ + mediaType: 'banner', + width: 300, + height: 250, + cpm: 0.4, + ad: 'Test', + requestId: '23fhj33i987f', + ttl: 120, + creativeId: '2', + netRevenue: true, + currency: 'USD', + dealId: '1', + meta: { + advertiserDomains: ['google.com'], + advertiserId: 1234 + } + }] + }; + let bannerResponses = spec.interpretResponse(banner); + expect(bannerResponses).to.be.an('array').that.is.not.empty; + let dataItem = bannerResponses[0]; + expect(dataItem).to.have.all.keys('requestId', 'cpm', 'width', 'height', 'ad', 'ttl', 'creativeId', + 'netRevenue', 'currency', 'dealId', 'mediaType', 'meta'); + expect(dataItem.requestId).to.equal(banner.body[0].requestId); + expect(dataItem.cpm).to.equal(banner.body[0].cpm); + expect(dataItem.width).to.equal(banner.body[0].width); + expect(dataItem.height).to.equal(banner.body[0].height); + expect(dataItem.ad).to.equal(banner.body[0].ad); + expect(dataItem.ttl).to.equal(banner.body[0].ttl); + expect(dataItem.creativeId).to.equal(banner.body[0].creativeId); + expect(dataItem.netRevenue).to.be.true; + expect(dataItem.currency).to.equal(banner.body[0].currency); + expect(dataItem.meta).to.be.an('object').that.has.any.key('advertiserDomains'); + }); + it('Should interpret video response', function () { + const video = { + body: [{ + vastUrl: 'test.com', + mediaType: 'video', + cpm: 0.5, + requestId: '23fhj33i987f', + ttl: 120, + creativeId: '2', + netRevenue: true, + currency: 'USD', + dealId: '1', + meta: { + advertiserDomains: ['google.com'], + advertiserId: 1234 + } + }] + }; + let videoResponses = spec.interpretResponse(video); + expect(videoResponses).to.be.an('array').that.is.not.empty; + + let dataItem = videoResponses[0]; + expect(dataItem).to.have.all.keys('requestId', 'cpm', 'vastUrl', 'ttl', 'creativeId', + 'netRevenue', 'currency', 'dealId', 'mediaType', 'meta'); + expect(dataItem.requestId).to.equal('23fhj33i987f'); + expect(dataItem.cpm).to.equal(0.5); + expect(dataItem.vastUrl).to.equal('test.com'); + expect(dataItem.ttl).to.equal(120); + expect(dataItem.creativeId).to.equal('2'); + expect(dataItem.netRevenue).to.be.true; + expect(dataItem.currency).to.equal('USD'); + expect(dataItem.meta).to.be.an('object').that.has.any.key('advertiserDomains'); + }); + it('Should interpret native response', function () { + const native = { + body: [{ + mediaType: 'native', + native: { + clickUrl: 'test.com', + title: 'Test', + image: 'test.com', + impressionTrackers: ['test.com'], + }, + ttl: 120, + cpm: 0.4, + requestId: '23fhj33i987f', + creativeId: '2', + netRevenue: true, + currency: 'USD', + meta: { + advertiserDomains: ['google.com'], + advertiserId: 1234 + } + }] + }; + let nativeResponses = spec.interpretResponse(native); + expect(nativeResponses).to.be.an('array').that.is.not.empty; + + let dataItem = nativeResponses[0]; + expect(dataItem).to.have.keys('requestId', 'cpm', 'ttl', 'creativeId', 'netRevenue', 'currency', 'mediaType', 'native', 'meta'); + expect(dataItem.native).to.have.keys('clickUrl', 'impressionTrackers', 'title', 'image') + expect(dataItem.requestId).to.equal('23fhj33i987f'); + expect(dataItem.cpm).to.equal(0.4); + expect(dataItem.native.clickUrl).to.equal('test.com'); + expect(dataItem.native.title).to.equal('Test'); + expect(dataItem.native.image).to.equal('test.com'); + expect(dataItem.native.impressionTrackers).to.be.an('array').that.is.not.empty; + expect(dataItem.native.impressionTrackers[0]).to.equal('test.com'); + expect(dataItem.ttl).to.equal(120); + expect(dataItem.creativeId).to.equal('2'); + expect(dataItem.netRevenue).to.be.true; + expect(dataItem.currency).to.equal('USD'); + expect(dataItem.meta).to.be.an('object').that.has.any.key('advertiserDomains'); + }); + it('Should return an empty array if invalid banner response is passed', function () { + const invBanner = { + body: [{ + width: 300, + cpm: 0.4, + ad: 'Test', + requestId: '23fhj33i987f', + ttl: 120, + creativeId: '2', + netRevenue: true, + currency: 'USD', + dealId: '1' + }] + }; + + let serverResponses = spec.interpretResponse(invBanner); + expect(serverResponses).to.be.an('array').that.is.empty; + }); + it('Should return an empty array if invalid video response is passed', function () { + const invVideo = { + body: [{ + mediaType: 'video', + cpm: 0.5, + requestId: '23fhj33i987f', + ttl: 120, + creativeId: '2', + netRevenue: true, + currency: 'USD', + dealId: '1' + }] + }; + let serverResponses = spec.interpretResponse(invVideo); + expect(serverResponses).to.be.an('array').that.is.empty; + }); + it('Should return an empty array if invalid native response is passed', function () { + const invNative = { + body: [{ + mediaType: 'native', + clickUrl: 'test.com', + title: 'Test', + impressionTrackers: ['test.com'], + ttl: 120, + requestId: '23fhj33i987f', + creativeId: '2', + netRevenue: true, + currency: 'USD', + }] + }; + let serverResponses = spec.interpretResponse(invNative); + expect(serverResponses).to.be.an('array').that.is.empty; + }); + it('Should return an empty array if invalid response is passed', function () { + const invalid = { + body: [{ + ttl: 120, + creativeId: '2', + netRevenue: true, + currency: 'USD', + dealId: '1' + }] + }; + let serverResponses = spec.interpretResponse(invalid); + expect(serverResponses).to.be.an('array').that.is.empty; + }); + }); + + describe('getUserSyncs', function() { + it('Should return array of objects with proper sync config , include GDPR', function() { + const syncData = spec.getUserSyncs({}, {}, { + consentString: 'ALL', + gdprApplies: true, + }, {}); + expect(syncData).to.be.an('array').which.is.not.empty; + expect(syncData[0]).to.be.an('object') + expect(syncData[0].type).to.be.a('string') + expect(syncData[0].type).to.equal('image') + expect(syncData[0].url).to.be.a('string') + expect(syncData[0].url).to.equal('https://sa-cs.deliverimp.com/image?pbjs=1&gdpr=1&gdpr_consent=ALL&coppa=0') + }); + it('Should return array of objects with proper sync config , include CCPA', function() { + const syncData = spec.getUserSyncs({}, {}, {}, { + consentString: '1---' + }); + expect(syncData).to.be.an('array').which.is.not.empty; + expect(syncData[0]).to.be.an('object') + expect(syncData[0].type).to.be.a('string') + expect(syncData[0].type).to.equal('image') + expect(syncData[0].url).to.be.a('string') + expect(syncData[0].url).to.equal('https://sa-cs.deliverimp.com/image?pbjs=1&ccpa_consent=1---&coppa=0') + }); + }); +}); diff --git a/test/spec/modules/connectIdSystem_spec.js b/test/spec/modules/connectIdSystem_spec.js new file mode 100644 index 00000000000..41135a74515 --- /dev/null +++ b/test/spec/modules/connectIdSystem_spec.js @@ -0,0 +1,189 @@ +import {expect} from 'chai'; +import {parseQS} from 'src/utils.js'; +import {connectIdSubmodule} from 'modules/connectIdSystem.js'; + +describe('Yahoo ConnectID Submodule', () => { + const HASHED_EMAIL = '6bda6f2fa268bf0438b5423a9861a2cedaa5dec163c03f743cfe05c08a8397b2'; + const PIXEL_ID = '1234'; + const PROD_ENDPOINT = `https://ups.analytics.yahoo.com/ups/${PIXEL_ID}/fed`; + const OVERRIDE_ENDPOINT = 'https://foo/bar'; + + it('should have the correct module name declared', () => { + expect(connectIdSubmodule.name).to.equal('connectId'); + }); + + it('should have the correct TCFv2 Vendor ID declared', () => { + expect(connectIdSubmodule.gvlid).to.equal(25); + }); + + describe('getId()', () => { + let ajaxStub; + let getAjaxFnStub; + let consentData; + beforeEach(() => { + ajaxStub = sinon.stub(); + getAjaxFnStub = sinon.stub(connectIdSubmodule, 'getAjaxFn'); + getAjaxFnStub.returns(ajaxStub); + + consentData = { + gdpr: { + gdprApplies: 1, + consentString: 'GDPR_CONSENT_STRING' + }, + uspConsent: 'USP_CONSENT_STRING' + }; + }); + + afterEach(() => { + getAjaxFnStub.restore(); + }); + + function invokeGetIdAPI(configParams, consentData) { + let result = connectIdSubmodule.getId({ + params: configParams + }, consentData); + if (typeof result === 'object') { + result.callback(sinon.stub()); + } + return result; + } + + it('returns undefined if he and pixelId params are not passed', () => { + expect(invokeGetIdAPI({}, consentData)).to.be.undefined; + expect(ajaxStub.callCount).to.equal(0); + }); + + it('returns undefined if the pixelId param is not passed', () => { + expect(invokeGetIdAPI({ + he: HASHED_EMAIL + }, consentData)).to.be.undefined; + expect(ajaxStub.callCount).to.equal(0); + }); + + it('returns undefined if the he param is not passed', () => { + expect(invokeGetIdAPI({ + pixelId: PIXEL_ID + }, consentData)).to.be.undefined; + expect(ajaxStub.callCount).to.equal(0); + }); + + it('returns an object with the callback function if the correct params are passed', () => { + let result = invokeGetIdAPI({ + he: HASHED_EMAIL, + pixelId: PIXEL_ID + }, consentData); + expect(result).to.be.an('object').that.has.all.keys('callback'); + expect(result.callback).to.be.a('function'); + }); + + it('Makes an ajax GET request to the production API endpoint with query params', () => { + invokeGetIdAPI({ + he: HASHED_EMAIL, + pixelId: PIXEL_ID + }, consentData); + + const expectedParams = { + he: HASHED_EMAIL, + pixelId: PIXEL_ID, + '1p': '0', + gdpr: '1', + gdpr_consent: consentData.gdpr.consentString, + us_privacy: consentData.uspConsent + }; + const requestQueryParams = parseQS(ajaxStub.firstCall.args[0].split('?')[1]); + + expect(ajaxStub.firstCall.args[0].indexOf(`${PROD_ENDPOINT}?`)).to.equal(0); + expect(requestQueryParams).to.deep.equal(expectedParams); + expect(ajaxStub.firstCall.args[3]).to.deep.equal({method: 'GET', withCredentials: true}); + }); + + it('Makes an ajax GET request to the specified override API endpoint with query params', () => { + invokeGetIdAPI({ + he: HASHED_EMAIL, + endpoint: OVERRIDE_ENDPOINT + }, consentData); + + const expectedParams = { + he: HASHED_EMAIL, + '1p': '0', + gdpr: '1', + gdpr_consent: consentData.gdpr.consentString, + us_privacy: consentData.uspConsent + }; + const requestQueryParams = parseQS(ajaxStub.firstCall.args[0].split('?')[1]); + + expect(ajaxStub.firstCall.args[0].indexOf(`${OVERRIDE_ENDPOINT}?`)).to.equal(0); + expect(requestQueryParams).to.deep.equal(expectedParams); + expect(ajaxStub.firstCall.args[3]).to.deep.equal({method: 'GET', withCredentials: true}); + }); + + it('sets the callbacks param of the ajax function call correctly', () => { + invokeGetIdAPI({ + he: HASHED_EMAIL, + pixelId: PIXEL_ID, + }, consentData); + + expect(ajaxStub.firstCall.args[1]).to.be.an('object').that.has.all.keys(['success', 'error']); + }); + + it('sets GDPR consent data flag correctly when call is under GDPR jurisdiction.', () => { + invokeGetIdAPI({ + he: HASHED_EMAIL, + pixelId: PIXEL_ID, + }, consentData); + + const requestQueryParams = parseQS(ajaxStub.firstCall.args[0].split('?')[1]); + expect(requestQueryParams.gdpr).to.equal('1'); + expect(requestQueryParams.gdpr_consent).to.equal(consentData.gdpr.consentString); + }); + + it('sets GDPR consent data flag correctly when call is NOT under GDPR jurisdiction.', () => { + consentData.gdpr.gdprApplies = false; + + invokeGetIdAPI({ + he: HASHED_EMAIL, + pixelId: PIXEL_ID, + }, consentData); + + const requestQueryParams = parseQS(ajaxStub.firstCall.args[0].split('?')[1]); + expect(requestQueryParams.gdpr).to.equal('0'); + expect(requestQueryParams.gdpr_consent).to.equal(''); + }); + + [1, '1', true].forEach(firstPartyParamValue => { + it(`sets 1p payload property to '1' for a config value of ${firstPartyParamValue}`, () => { + invokeGetIdAPI({ + '1p': firstPartyParamValue, + he: HASHED_EMAIL, + pixelId: PIXEL_ID, + }, consentData); + + const requestQueryParams = parseQS(ajaxStub.firstCall.args[0].split('?')[1]); + expect(requestQueryParams['1p']).to.equal('1'); + }); + }); + }); + + describe('decode()', () => { + const VALID_API_RESPONSES = [{ + key: 'connectid', + expected: '4567', + payload: { + connectid: '4567' + } + }]; + VALID_API_RESPONSES.forEach(responseData => { + it('should return a newly constructed object with the connect ID for a payload with ${responseData.key} key(s)', () => { + expect(connectIdSubmodule.decode(responseData.payload)).to.deep.equal( + {connectId: responseData.expected} + ); + }); + }); + + [{}, '', {foo: 'bar'}].forEach((response) => { + it(`should return undefined for an invalid response "${JSON.stringify(response)}"`, () => { + expect(connectIdSubmodule.decode(response)).to.be.undefined; + }); + }); + }); +}); diff --git a/test/spec/modules/consentManagement_spec.js b/test/spec/modules/consentManagement_spec.js index 5e9b0f07f46..d7ae3c58a85 100644 --- a/test/spec/modules/consentManagement_spec.js +++ b/test/spec/modules/consentManagement_spec.js @@ -715,6 +715,26 @@ describe('consentManagement', function () { expect(consent).to.be.null; }); + it('allows the auction when CMP is unresponsive', (done) => { + setConsentConfig({ + cmpApi: 'iab', + timeout: 10, + defaultGdprScope: true + }); + + requestBidsHook(() => { + didHookReturn = true; + }, {}); + + setTimeout(() => { + expect(didHookReturn).to.be.true; + const consent = gdprDataHandler.getConsentData(); + expect(consent.gdprApplies).to.be.true; + expect(consent.consentString).to.be.undefined; + done(); + }, 20); + }); + it('It still considers it a valid cmp response if gdprApplies is not a boolean', function () { // gdprApplies is undefined, should just still store consent response but use whatever defaultGdprScope was let testConsentData = { diff --git a/test/spec/modules/criteoBidAdapter_spec.js b/test/spec/modules/criteoBidAdapter_spec.js index aa995b3c9a0..1e5f9e613d2 100755 --- a/test/spec/modules/criteoBidAdapter_spec.js +++ b/test/spec/modules/criteoBidAdapter_spec.js @@ -663,6 +663,27 @@ describe('The Criteo bidding adapter', function () { expect(request.data.user.uspIab).to.equal('1YNY'); }); + it('should properly build a request with schain object', function () { + const expectedSchain = { + someProperty: 'someValue' + }; + const bidRequests = [ + { + bidder: 'criteo', + schain: expectedSchain, + adUnitCode: 'bid-123', + transactionId: 'transaction-123', + sizes: [[728, 90]], + params: { + zoneId: 123, + }, + }, + ]; + + const request = spec.buildRequests(bidRequests, bidderRequest); + expect(request.data.source.ext.schain).to.equal(expectedSchain); + }); + it('should properly build a request with if ccpa consent field is not provided', function () { const bidRequests = [ { diff --git a/test/spec/modules/criteoIdSystem_spec.js b/test/spec/modules/criteoIdSystem_spec.js index 828b8401af1..d50eadebb55 100644 --- a/test/spec/modules/criteoIdSystem_spec.js +++ b/test/spec/modules/criteoIdSystem_spec.js @@ -116,16 +116,19 @@ describe('CriteoId module', function () { expect(setCookieStub.calledWith('cto_bundle')).to.be.false; expect(setLocalStorageStub.calledWith('cto_bundle')).to.be.false; } else if (response.bundle) { - expect(setCookieStub.calledWith('cto_bundle', response.bundle, expirationTs)).to.be.true; + expect(setCookieStub.calledWith('cto_bundle', response.bundle, expirationTs, null, '.com')).to.be.true; + expect(setCookieStub.calledWith('cto_bundle', response.bundle, expirationTs, null, '.testdev.com')).to.be.true; expect(setLocalStorageStub.calledWith('cto_bundle', response.bundle)).to.be.true; expect(triggerPixelStub.called).to.be.false; } if (response.bidId) { - expect(setCookieStub.calledWith('cto_bidid', response.bidId, expirationTs)).to.be.true; + expect(setCookieStub.calledWith('cto_bidid', response.bidId, expirationTs, null, '.com')).to.be.true; + expect(setCookieStub.calledWith('cto_bidid', response.bidId, expirationTs, null, '.testdev.com')).to.be.true; expect(setLocalStorageStub.calledWith('cto_bidid', response.bidId)).to.be.true; } else { - expect(setCookieStub.calledWith('cto_bidid', '', pastDateString)).to.be.true; + expect(setCookieStub.calledWith('cto_bidid', '', pastDateString, null, '.com')).to.be.true; + expect(setCookieStub.calledWith('cto_bidid', '', pastDateString, null, '.testdev.com')).to.be.true; expect(removeFromLocalStorageStub.calledWith('cto_bidid')).to.be.true; } }); diff --git a/test/spec/modules/currency_spec.js b/test/spec/modules/currency_spec.js index ccd205964a9..77215cea7f4 100644 --- a/test/spec/modules/currency_spec.js +++ b/test/spec/modules/currency_spec.js @@ -9,7 +9,8 @@ import { setConfig, addBidResponseHook, currencySupportEnabled, - currencyRates + currencyRates, + ready } from 'modules/currency.js'; var assert = require('chai').assert; @@ -24,6 +25,7 @@ describe('currency', function () { beforeEach(function () { fakeCurrencyFileServer = sinon.fakeServer.create(); + ready.reset(); }); afterEach(function () { @@ -286,7 +288,7 @@ describe('currency', function () { }); describe('currency.addBidResponseDecorator bidResponseQueue', function () { - it('not run until currency rates file is loaded', function () { + it('not run until currency rates file is loaded', function (done) { setConfig({}); fakeCurrencyFileServer.respondWith(JSON.stringify(getCurrencyRates())); @@ -296,14 +298,27 @@ describe('currency', function () { setConfig({ 'adServerCurrency': 'JPY' }); var marker = false; - addBidResponseHook(function() { + let promiseResolved = false; + addBidResponseHook(Object.assign(function() { marker = true; - }, 'elementId', bid); + }, { + bail: function (promise) { + promise.then(() => promiseResolved = true); + } + }), 'elementId', bid); expect(marker).to.equal(false); - fakeCurrencyFileServer.respond(); - expect(marker).to.equal(true); + setTimeout(() => { + expect(promiseResolved).to.be.false; + fakeCurrencyFileServer.respond(); + + setTimeout(() => { + expect(marker).to.equal(true); + expect(promiseResolved).to.be.true; + done(); + }); + }); }); }); diff --git a/test/spec/modules/cwireBidAdapter_spec.js b/test/spec/modules/cwireBidAdapter_spec.js new file mode 100644 index 00000000000..3f922fc1471 --- /dev/null +++ b/test/spec/modules/cwireBidAdapter_spec.js @@ -0,0 +1,248 @@ +import { expect } from 'chai'; +import * as utils from '../../../src/utils.js'; +import { + spec, + CW_PAGE_VIEW_ID, + ENDPOINT_URL, + RENDERER_URL, +} from '../../../modules/cwireBidAdapter.js'; +import * as prebidGlobal from 'src/prebidGlobal.js'; + +// ------------------------------------ +// Bid Request Builder +// ------------------------------------ + +const BID_DEFAULTS = { + request: { + bidder: 'cwire', + auctionId: 'aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee', + transactionId: 'txaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee', + bidId: 'bid123445', + bidderRequestId: 'brid12345', + code: 'original-div', + }, + params: { + placementId: 123456, + pageId: 777, + adUnitElementId: 'target-div' + }, + sizes: [[300, 250], [1, 1]], +}; + +const BidderRequestBuilder = function BidderRequestBuilder(options) { + const defaults = { + bidderCode: 'cwire', + auctionId: BID_DEFAULTS.request.auctionId, + bidderRequestId: BID_DEFAULTS.request.bidderRequestId, + transactionId: BID_DEFAULTS.request.transactionId, + timeout: 3000, + }; + + const request = { + ...defaults, + ...options + }; + + this.build = () => request; +}; + +const BidRequestBuilder = function BidRequestBuilder(options, deleteKeys) { + const defaults = JSON.parse(JSON.stringify(BID_DEFAULTS)); + + const request = { + ...defaults.request, + ...options + }; + + if (request && utils.isArray(deleteKeys)) { + deleteKeys.forEach((k) => { + delete request[k]; + }) + } + + this.withParams = (options, deleteKeys) => { + request.params = { + ...defaults.params, + ...options + }; + if (request && utils.isArray(deleteKeys)) { + deleteKeys.forEach((k) => { + delete request.params[k]; + }) + } + return this; + }; + + this.build = () => request; +}; + +describe('C-WIRE bid adapter', () => { + let sandbox; + + beforeEach(() => { + sandbox = sinon.createSandbox(); + }); + + afterEach(() => { + sandbox.restore(); + }); + + // START TESTING + describe('C-WIRE - isBidRequestValid', function () { + it('should return true when required params found', function () { + const bid01 = new BidRequestBuilder().withParams().build(); + expect(spec.isBidRequestValid(bid01)).to.equal(true); + }); + + it('should fail if there is no placementId', function () { + const bid01 = new BidRequestBuilder().withParams().build(); + delete bid01.params.placementId + expect(spec.isBidRequestValid(bid01)).to.equal(false); + }); + + it('should fail if invalid placementId type', function () { + const bid01 = new BidRequestBuilder().withParams().build(); + delete bid01.params.placementId; + bid01.placementId = '322'; + expect(spec.isBidRequestValid(bid01)).to.equal(false); + }); + + it('should fail if there is no pageId', function () { + const bid01 = new BidRequestBuilder().withParams().build(); + delete bid01.params.pageId + expect(spec.isBidRequestValid(bid01)).to.equal(false); + }); + + it('should fail if invalid pageId type', function () { + const bid01 = new BidRequestBuilder().withParams().build(); + delete bid01.params.pageId; + bid01.params.pageId = '3320'; + expect(spec.isBidRequestValid(bid01)).to.equal(false); + }); + + it('should use params.adUnitElementId if provided', function () { + const bid01 = new BidRequestBuilder().withParams().build(); + + expect(spec.isBidRequestValid(bid01)).to.equal(true); + expect(bid01.params.adUnitElementId).to.exist; + expect(bid01.params.adUnitElementId).to.equal('target-div'); + }); + + it('should use default adUnitCode if no adUnitElementId provided', function () { + const bid01 = new BidRequestBuilder().withParams({}, ['adUnitElementId']).build(); + expect(spec.isBidRequestValid(bid01)).to.equal(true); + expect(bid01.params.adUnitElementId).to.exist; + expect(bid01.params.adUnitElementId).to.equal('original-div'); + }); + }); + + describe('C-WIRE - buildRequests()', function () { + it('creates a valid request', function () { + // for whatever reason stub for getWindowLocation does not work + // so this was the closest way to test for get params + const params = sandbox.stub(utils, 'getParameterByName'); + params.withArgs('cwgroups').returns('group_1'); + params.withArgs('cwcreative').returns('54321'); + + const bid01 = new BidRequestBuilder({ + mediaTypes: { + banner: { + sizes: [[1, 1]], + } + } + }).withParams().build(); + + const bidderRequest01 = new BidderRequestBuilder().build(); + + const requests = spec.buildRequests([bid01], bidderRequest01); + + expect(requests.data.slots.length).to.equal(1); + expect(requests.data.cwid).to.be.null; + expect(requests.data.slots[0].sizes[0]).to.equal('1x1'); + expect(requests.data.cwcreative).to.equal('54321'); + expect(requests.data.refgroups[0]).to.equal('group_1'); + }); + }); + + describe('C-WIRE - interpretResponse()', function () { + const serverResponse = { + body: { + bids: [{ + html: '

AD CONTENT

', + currency: 'CHF', + cpm: 43.37, + dimensions: [1, 1], + netRevenue: true, + creativeId: '1337', + requestId: BID_DEFAULTS.request.bidId, + ttl: 3500, + }], + } + }; + + const expectedResponse = [{ + ad: JSON.parse(JSON.stringify(serverResponse.body.bids[0].html)), + bidderCode: BID_DEFAULTS.request.bidder, + cpm: JSON.parse(JSON.stringify(serverResponse.body.bids[0].cpm)), + creativeId: JSON.parse(JSON.stringify(serverResponse.body.bids[0].creativeId)), + currency: JSON.parse(JSON.stringify(serverResponse.body.bids[0].currency)), + height: JSON.parse(JSON.stringify(serverResponse.body.bids[0].dimensions[0])), + width: JSON.parse(JSON.stringify(serverResponse.body.bids[0].dimensions[1])), + netRevenue: JSON.parse(JSON.stringify(serverResponse.body.bids[0].netRevenue)), + requestId: JSON.parse(JSON.stringify(serverResponse.body.bids[0].requestId)), + ttl: JSON.parse(JSON.stringify(serverResponse.body.bids[0].ttl)), + meta: { + advertiserDomains: [], + }, + mediaType: 'banner', + }] + + it('correctly parses response', function () { + const bid01 = new BidRequestBuilder({ + mediaTypes: { + banner: { + sizes: [[1, 1]], + } + } + }).withParams().build(); + + const bidderRequest01 = new BidderRequestBuilder().build(); + const requests = spec.buildRequests([bid01], bidderRequest01); + + const response = spec.interpretResponse(serverResponse, requests); + expect(response).to.deep.equal(expectedResponse); + }); + + it('attaches renderer', function () { + const bid01 = new BidRequestBuilder({ + mediaTypes: { + video: { + playerSize: [[640, 480]], + context: 'outstream', + } + } + }).withParams().build(); + const bidderRequest01 = new BidderRequestBuilder().build(); + + const _serverResponse = utils.deepClone(serverResponse); + _serverResponse.body.bids[0].vastXml = ''; + + const _expectedResponse = utils.deepClone(expectedResponse); + _expectedResponse[0].mediaType = 'video'; + _expectedResponse[0].videoScript = JSON.parse(JSON.stringify(_serverResponse.body.bids[0].html)); + _expectedResponse[0].vastXml = JSON.parse(JSON.stringify(_serverResponse.body.bids[0].vastXml)); + delete _expectedResponse[0].ad; + + const requests = spec.buildRequests([bid01], bidderRequest01); + expect(requests.data.slots[0].sizes).to.deep.equal(['640x480']); + + const response = spec.interpretResponse(_serverResponse, requests); + expect(response[0].renderer).to.exist; + expect(response[0].renderer.url).to.equals(RENDERER_URL); + expect(response[0].renderer.loaded).to.equals(false); + + delete response[0].renderer; + expect(response).to.deep.equal(_expectedResponse); + }); + }); +}); diff --git a/test/spec/modules/dailyhuntBidAdapter_spec.js b/test/spec/modules/dailyhuntBidAdapter_spec.js new file mode 100644 index 00000000000..f347d6cec5b --- /dev/null +++ b/test/spec/modules/dailyhuntBidAdapter_spec.js @@ -0,0 +1,404 @@ +import { expect } from 'chai'; +import { spec } from 'modules/dailyhuntBidAdapter.js'; + +const PROD_PREBID_ENDPOINT_URL = 'https://pbs.dailyhunt.in/openrtb2/auction?partner=dailyhunt'; +const PROD_PREBID_TEST_ENDPOINT_URL = 'https://qa-pbs-van.dailyhunt.in/openrtb2/auction?partner=dailyhunt'; + +const _encodeURIComponent = function (a) { + if (!a) { return } + let b = window.encodeURIComponent(a); + b = b.replace(/'/g, '%27'); + return b; +} + +describe('DailyhuntAdapter', function () { + describe('isBidRequestValid', function () { + let bid = { + 'bidder': 'dailyhunt', + 'params': { + placement_id: 1, + publisher_id: 1, + partner_name: 'dailyhunt' + } + }; + + it('should return true when required params found', function () { + expect(spec.isBidRequestValid(bid)).to.equal(true); + }); + + it('should return false when required params are not passed', function () { + let bid = Object.assign({}, bid); + delete bid.params; + bid.params = {}; + expect(spec.isBidRequestValid(bid)).to.equal(false); + }); + }); + describe('buildRequests', function() { + let bidRequests = [ + { + bidder: 'dailyhunt', + params: { + placement_id: 1, + publisher_id: 1, + partner_name: 'dailyhunt', + bidfloor: 0.1, + device: { + ip: '47.9.247.217' + }, + site: { + cat: ['1', '2', '3'] + } + }, + mediaTypes: { + banner: { + sizes: [[300, 250]] + } + }, + adUnitCode: 'adunit-code', + sizes: [[300, 50]], + bidId: '30b31c1838de1e', + bidderRequestId: '22edbae2733bf6', + auctionId: '1d1a030790a475', + transactionId: '04f2659e-c005-4eb1-a57c-fa93145e3843' + } + ]; + let nativeBidRequests = [ + { + bidder: 'dailyhunt', + params: { + placement_id: 1, + publisher_id: 1, + partner_name: 'dailyhunt', + }, + nativeParams: { + title: { + required: true, + len: 80 + }, + image: { + required: true, + sizes: [150, 50] + }, + }, + mediaTypes: { + native: { + title: { + required: true + }, + } + }, + adUnitCode: 'adunit-code', + sizes: [[300, 250], [300, 50]], + bidId: '30b31c1838de1e', + bidderRequestId: '22edbae2733bf6', + auctionId: '1d1a030790a475', + transactionId: '04f2659e-c005-4eb1-a57c-fa93145e3843' + } + ]; + let videoBidRequests = [ + { + bidder: 'dailyhunt', + params: { + placement_id: 1, + publisher_id: 1, + partner_name: 'dailyhunt' + }, + nativeParams: { + video: { + context: 'instream' + } + }, + mediaTypes: { + video: { + context: 'instream' + } + }, + adUnitCode: 'adunit-code', + sizes: [[300, 250], [300, 50]], + bidId: '30b31c1838de1e', + bidderRequestId: '22edbae2733bf6', + auctionId: '1d1a030790a475', + transactionId: '04f2659e-c005-4eb1-a57c-fa93145e3843' + } + ]; + let bidderRequest = { + 'bidderRequestId': '22edbae2733bf6', + 'auctionId': '1d1a030790a475', + 'bidderCode': 'dailyhunt', + 'bids': [ + { + ...bidRequests[0] + } + ], + 'refererInfo': { + 'referer': 'http://m.dailyhunt.in/' + } + }; + let nativeBidderRequest = { + 'bidderRequestId': '22edbae2733bf6', + 'auctionId': '1d1a030790a475', + 'bidderCode': 'dailyhunt', + 'bids': [ + { + ...nativeBidRequests[0] + } + ], + 'refererInfo': { + 'referer': 'http://m.dailyhunt.in/' + } + }; + let videoBidderRequest = { + 'bidderRequestId': '22edbae2733bf6', + 'auctionId': '1d1a030790a475', + 'bidderCode': 'dailyhunt', + 'bids': [ + { + ...videoBidRequests[0] + } + ], + 'refererInfo': { + 'referer': 'http://m.dailyhunt.in/' + } + }; + + it('sends display bid request to ENDPOINT via POST', function () { + const request = spec.buildRequests(bidRequests, bidderRequest)[0]; + expect(request.url).to.equal(PROD_PREBID_ENDPOINT_URL); + expect(request.method).to.equal('POST'); + }); + + it('sends native bid request to ENDPOINT via POST', function () { + const request = spec.buildRequests(nativeBidRequests, nativeBidderRequest)[0]; + expect(request.url).to.equal(PROD_PREBID_ENDPOINT_URL); + expect(request.method).to.equal('POST'); + }); + + it('sends video bid request to ENDPOINT via POST', function () { + const request = spec.buildRequests(videoBidRequests, videoBidderRequest)[0]; + expect(request.url).to.equal(PROD_PREBID_ENDPOINT_URL); + expect(request.method).to.equal('POST'); + }); + }); + describe('interpretResponse', function () { + let bidResponses = { + id: 'da32def7-6779-403c-ada7-0b201dbc9744', + seatbid: [ + { + bid: [ + { + id: 'id1', + impid: 'banner-impid', + price: 1.4, + adm: 'adm', + adid: '66658', + crid: 'asd5ddbf014cac993.66466212', + dealid: 'asd5ddbf014cac993.66466212', + w: 300, + h: 250, + nurl: 'winUrl', + ext: { + prebid: { + type: 'banner' + } + } + }, + { + id: '5caccc1f-94a6-4230-a1f9-6186ee65da99', + impid: 'video-impid', + price: 1.4, + nurl: 'winUrl', + adm: 'adm', + adid: '980', + crid: '2394', + w: 300, + h: 250, + ext: { + prebid: { + 'type': 'video' + }, + bidder: { + cacheKey: 'cache_key', + vastUrl: 'vastUrl' + } + } + }, + { + id: '74973faf-cce7-4eff-abd0-b59b8e91ca87', + impid: 'native-impid', + price: 50, + nurl: 'winUrl', + adm: '{"native":{"link":{"url":"url","clicktrackers":[]},"assets":[{"id":1,"required":1,"img":{},"video":{},"data":{},"title":{"text":"TITLE"},"link":{}},{"id":1,"required":1,"img":{},"video":{},"data":{"type":2,"value":"Lorem Ipsum Lorem Ipsum Lorem Ipsum."},"title":{},"link":{}},{"id":1,"required":1,"img":{},"video":{},"data":{"type":12,"value":"Install Here"},"title":{},"link":{}},{"id":1,"required":1,"img":{"type":3,"url":"urk","w":990,"h":505},"video":{},"data":{},"title":{},"link":{}}],"imptrackers":[]}}', + adid: '968', + crid: '2370', + w: 300, + h: 250, + ext: { + prebid: { + type: 'native' + }, + bidder: null + } + }, + { + id: '5caccc1f-94a6-4230-a1f9-6186ee65da99', + impid: 'video-outstream-impid', + price: 1.4, + nurl: 'winUrl', + adm: 'adm', + adid: '980', + crid: '2394', + w: 300, + h: 250, + ext: { + prebid: { + 'type': 'video' + }, + bidder: { + cacheKey: 'cache_key', + vastUrl: 'vastUrl' + } + } + }, + ], + seat: 'dailyhunt' + } + ], + ext: { + responsetimemillis: { + dailyhunt: 119 + } + } + }; + + it('should get correct bid response', function () { + let expectedResponse = [ + { + requestId: '1', + cpm: 1.4, + creativeId: 'asd5ddbf014cac993.66466212', + width: 300, + height: 250, + ttl: 360, + netRevenue: true, + currency: 'USD', + ad: 'adm', + mediaType: 'banner', + winUrl: 'winUrl', + adomain: 'dailyhunt' + }, + { + requestId: '2', + cpm: 1.4, + creativeId: '2394', + width: 300, + height: 250, + ttl: 360, + netRevenue: true, + currency: 'USD', + mediaType: 'video', + winUrl: 'winUrl', + adomain: 'dailyhunt', + videoCacheKey: 'cache_key', + vastUrl: 'vastUrl', + }, + { + requestId: '3', + cpm: 1.4, + creativeId: '2370', + width: 300, + height: 250, + ttl: 360, + netRevenue: true, + currency: 'USD', + mediaType: 'native', + winUrl: 'winUrl', + adomain: 'dailyhunt', + native: { + clickUrl: 'https%3A%2F%2Fmontu1996.github.io%2F', + clickTrackers: [], + impressionTrackers: [], + javascriptTrackers: [], + title: 'TITLE', + body: 'Lorem Ipsum Lorem Ipsum Lorem Ipsum.', + cta: 'Install Here', + image: { + url: 'url', + height: 505, + width: 990 + } + } + }, + { + requestId: '4', + cpm: 1.4, + creativeId: '2394', + width: 300, + height: 250, + ttl: 360, + netRevenue: true, + currency: 'USD', + mediaType: 'video', + winUrl: 'winUrl', + adomain: 'dailyhunt', + vastXml: 'adm', + }, + ]; + let bidderRequest = { + bids: [ + { + bidId: 'banner-impid', + adUnitCode: 'code1', + requestId: '1' + }, + { + bidId: 'video-impid', + adUnitCode: 'code2', + requestId: '2', + mediaTypes: { + video: { + context: 'instream' + } + } + }, + { + bidId: 'native-impid', + adUnitCode: 'code3', + requestId: '3' + }, + { + bidId: 'video-outstream-impid', + adUnitCode: 'code4', + requestId: '4', + mediaTypes: { + video: { + context: 'outstream' + } + } + }, + ] + } + let result = spec.interpretResponse({ body: bidResponses }, bidderRequest); + result.forEach((r, i) => { + expect(Object.keys(r)).to.have.members(Object.keys(expectedResponse[i])); + }); + }); + }) + describe('onBidWon', function () { + it('should hit win url when bid won', function () { + let bid = { + requestId: '1', + cpm: 1.4, + creativeId: 'asd5ddbf014cac993.66466212', + width: 300, + height: 250, + ttl: 360, + netRevenue: true, + currency: 'USD', + ad: 'adm', + mediaType: 'banner', + winUrl: 'winUrl' + }; + expect(spec.onBidWon(bid)).to.equal(undefined); + }); + }) +}) diff --git a/test/spec/modules/datablocksBidAdapter_spec.js b/test/spec/modules/datablocksBidAdapter_spec.js index 0ec12905430..ff7b0aad48c 100644 --- a/test/spec/modules/datablocksBidAdapter_spec.js +++ b/test/spec/modules/datablocksBidAdapter_spec.js @@ -96,8 +96,8 @@ const bidderRequest = { refererInfo: { numIframes: 0, reachedTop: true, - referer: 'https://v5demo.datablocks.net/test', - stack: ['https://v5demo.datablocks.net/test'] + referer: 'https://7560.v5demo.datablocks.net/test', + stack: ['https://7560.v5demo.datablocks.net/test'] }, start: Date.now(), timeout: 10000 @@ -452,7 +452,7 @@ describe('DatablocksAdapter', function() { it('Returns valid URL', function() { expect(request.url).to.exist; - expect(request.url).to.equal('https://7560.v5demo.datablocks.net/openrtb/?sid=7560'); + expect(request.url).to.equal('https://v5demo.datablocks.net/openrtb/?sid=7560'); }); it('Creates an array of request objects', function() { diff --git a/test/spec/modules/dchain_spec.js b/test/spec/modules/dchain_spec.js new file mode 100644 index 00000000000..45061c539c1 --- /dev/null +++ b/test/spec/modules/dchain_spec.js @@ -0,0 +1,329 @@ +import { checkDchainSyntax, addBidResponseHook } from '../../../modules/dchain.js'; +import { config } from '../../../src/config.js'; +import { expect } from 'chai'; + +describe('dchain module', function () { + const STRICT = 'strict'; + const RELAX = 'relaxed'; + const OFF = 'off'; + + describe('checkDchainSyntax', function () { + let bid; + + beforeEach(function () { + bid = { + meta: { + dchain: { + 'ver': '1.0', + 'complete': 0, + 'ext': {}, + 'nodes': [{ + 'asi': 'domain.com', + 'bsid': '12345', + }, { + 'name': 'bidder', + 'domain': 'bidder.com', + 'ext': {} + }] + } + } + }; + }); + + it('Returns false if complete param is not 0 or 1', function () { + let dchainConfig = bid.meta.dchain; + dchainConfig.complete = 0; // integer + expect(checkDchainSyntax(bid, STRICT)).to.true; + dchainConfig.complete = 1; // integer + expect(checkDchainSyntax(bid, STRICT)).to.true; + dchainConfig.complete = '1'; // string + expect(checkDchainSyntax(bid, STRICT)).to.false; + dchainConfig.complete = 1.1; // float + expect(checkDchainSyntax(bid, STRICT)).to.false; + dchainConfig.complete = {}; // object + expect(checkDchainSyntax(bid, STRICT)).to.false; + delete dchainConfig.complete; // undefined + expect(checkDchainSyntax(bid, STRICT)).to.false; + dchainConfig.complete = true; // boolean + expect(checkDchainSyntax(bid, STRICT)).to.false; + dchainConfig.complete = []; // array + expect(checkDchainSyntax(bid, STRICT)).to.false; + }); + + it('Returns false if ver param is not a String', function () { + let dchainConfig = bid.meta.dchain; + dchainConfig.ver = 1; // integer + expect(checkDchainSyntax(bid, STRICT)).to.false; + dchainConfig.ver = '1'; // string + expect(checkDchainSyntax(bid, STRICT)).to.true; + dchainConfig.ver = 1.1; // float + expect(checkDchainSyntax(bid, STRICT)).to.false; + dchainConfig.ver = {}; // object + expect(checkDchainSyntax(bid, STRICT)).to.false; + delete dchainConfig.ver; // undefined + expect(checkDchainSyntax(bid, STRICT)).to.false; + dchainConfig.ver = true; // boolean + expect(checkDchainSyntax(bid, STRICT)).to.false; + dchainConfig.ver = []; // array + expect(checkDchainSyntax(bid, STRICT)).to.false; + }); + + it('Returns false if ext param is not an Object', function () { + let dchainConfig = bid.meta.dchain; + dchainConfig.ext = 1; // integer + expect(checkDchainSyntax(bid, STRICT)).to.false; + dchainConfig.ext = '1'; // string + expect(checkDchainSyntax(bid, STRICT)).to.false; + dchainConfig.ext = 1.1; // float + expect(checkDchainSyntax(bid, STRICT)).to.false; + dchainConfig.ext = {}; // object + expect(checkDchainSyntax(bid, STRICT)).to.true; + delete dchainConfig.ext; // undefined + expect(checkDchainSyntax(bid, STRICT)).to.true; + dchainConfig.ext = true; // boolean + expect(checkDchainSyntax(bid, STRICT)).to.false; + dchainConfig.ext = []; // array + expect(checkDchainSyntax(bid, STRICT)).to.false; + }); + + it('Returns false if nodes param is not an Array', function () { + let dchainConfig = bid.meta.dchain; + expect(checkDchainSyntax(bid, STRICT)).to.true; + dchainConfig.nodes = 1; // integer + expect(checkDchainSyntax(bid, STRICT)).to.false; + dchainConfig.nodes = '1'; // string + expect(checkDchainSyntax(bid, STRICT)).to.false; + dchainConfig.nodes = 1.1; // float + expect(checkDchainSyntax(bid, STRICT)).to.false; + dchainConfig.nodes = {}; // object + expect(checkDchainSyntax(bid, STRICT)).to.false; + delete dchainConfig.nodes; // undefined + expect(checkDchainSyntax(bid, STRICT)).to.false; + dchainConfig.nodes = true; // boolean + expect(checkDchainSyntax(bid, STRICT)).to.false; + }); + + it('Returns false if unknown field is used in main dchain', function () { + let dchainConfig = bid.meta.dchain; + dchainConfig.test = '1'; // String + expect(checkDchainSyntax(bid, STRICT)).to.false; + }); + + it('Returns false if nodes[].asi is not a String', function () { + let dchainConfig = bid.meta.dchain; + expect(checkDchainSyntax(bid, STRICT)).to.true; + dchainConfig.nodes[0].asi = 1; // Integer + expect(checkDchainSyntax(bid, STRICT)).to.false; + dchainConfig.nodes[0].asi = 1.1; // float + expect(checkDchainSyntax(bid, STRICT)).to.false; + dchainConfig.nodes[0].asi = {}; // object + expect(checkDchainSyntax(bid, STRICT)).to.false; + delete dchainConfig.nodes[0].asi; // undefined + expect(checkDchainSyntax(bid, STRICT)).to.true; + dchainConfig.nodes[0].asi = true; // boolean + expect(checkDchainSyntax(bid, STRICT)).to.false; + dchainConfig.nodes[0].asi = []; // array + expect(checkDchainSyntax(bid, STRICT)).to.false; + }); + + it('Returns false if nodes[].bsid is not a String', function () { + let dchainConfig = bid.meta.dchain; + expect(checkDchainSyntax(bid, STRICT)).to.true; + dchainConfig.nodes[0].bsid = 1; // Integer + expect(checkDchainSyntax(bid, STRICT)).to.false; + dchainConfig.nodes[0].bsid = 1.1; // float + expect(checkDchainSyntax(bid, STRICT)).to.false; + dchainConfig.nodes[0].bsid = {}; // object + expect(checkDchainSyntax(bid, STRICT)).to.false; + delete dchainConfig.nodes[0].bsid; // undefined + expect(checkDchainSyntax(bid, STRICT)).to.true; + dchainConfig.nodes[0].bsid = true; // boolean + expect(checkDchainSyntax(bid, STRICT)).to.false; + dchainConfig.nodes[0].bsid = []; // array + expect(checkDchainSyntax(bid, STRICT)).to.false; + }); + + it('Returns false if nodes[].rid is not a String', function () { + let dchainConfig = bid.meta.dchain; + expect(checkDchainSyntax(bid, STRICT)).to.true; + dchainConfig.nodes[0].rid = 1; // Integer + expect(checkDchainSyntax(bid, STRICT)).to.false; + dchainConfig.nodes[0].rid = 1.1; // float + expect(checkDchainSyntax(bid, STRICT)).to.false; + dchainConfig.nodes[0].rid = {}; // object + expect(checkDchainSyntax(bid, STRICT)).to.false; + delete dchainConfig.nodes[0].rid; // undefined + expect(checkDchainSyntax(bid, STRICT)).to.true; + dchainConfig.nodes[0].rid = true; // boolean + expect(checkDchainSyntax(bid, STRICT)).to.false; + dchainConfig.nodes[0].rid = []; // array + expect(checkDchainSyntax(bid, STRICT)).to.false; + }); + + it('Returns false if nodes[].name is not a String', function () { + let dchainConfig = bid.meta.dchain; + expect(checkDchainSyntax(bid, STRICT)).to.true; + dchainConfig.nodes[0].name = 1; // Integer + expect(checkDchainSyntax(bid, STRICT)).to.false; + dchainConfig.nodes[0].name = 1.1; // float + expect(checkDchainSyntax(bid, STRICT)).to.false; + dchainConfig.nodes[0].name = {}; // object + expect(checkDchainSyntax(bid, STRICT)).to.false; + delete dchainConfig.nodes[0].name; // undefined + expect(checkDchainSyntax(bid, STRICT)).to.true; + dchainConfig.nodes[0].name = true; // boolean + expect(checkDchainSyntax(bid, STRICT)).to.false; + dchainConfig.nodes[0].name = []; // array + expect(checkDchainSyntax(bid, STRICT)).to.false; + }); + + it('Returns false if nodes[].domain is not a String', function () { + let dchainConfig = bid.meta.dchain; + expect(checkDchainSyntax(bid, STRICT)).to.true; + dchainConfig.nodes[0].domain = 1; // Integer + expect(checkDchainSyntax(bid, STRICT)).to.false; + dchainConfig.nodes[0].domain = 1.1; // float + expect(checkDchainSyntax(bid, STRICT)).to.false; + dchainConfig.nodes[0].domain = {}; // object + expect(checkDchainSyntax(bid, STRICT)).to.false; + delete dchainConfig.nodes[0].domain; // undefined + expect(checkDchainSyntax(bid, STRICT)).to.true; + dchainConfig.nodes[0].domain = true; // boolean + expect(checkDchainSyntax(bid, STRICT)).to.false; + dchainConfig.nodes[0].domain = []; // array + expect(checkDchainSyntax(bid, STRICT)).to.false; + }); + + it('Returns false if nodes[].ext is not an Object', function () { + let dchainConfig = bid.meta.dchain; + dchainConfig.nodes[0].ext = '1'; // String + expect(checkDchainSyntax(bid, STRICT)).to.false; + dchainConfig.nodes[0].ext = 1; // Integer + expect(checkDchainSyntax(bid, STRICT)).to.false; + dchainConfig.nodes[0].ext = 1.1; // float + expect(checkDchainSyntax(bid, STRICT)).to.false; + dchainConfig.nodes[0].ext = {}; // object + expect(checkDchainSyntax(bid, STRICT)).to.true; + delete dchainConfig.nodes[0].ext; // undefined + expect(checkDchainSyntax(bid, STRICT)).to.true; + dchainConfig.nodes[0].ext = true; // boolean + expect(checkDchainSyntax(bid, STRICT)).to.false; + dchainConfig.nodes[0].ext = []; // array + expect(checkDchainSyntax(bid, STRICT)).to.false; + }); + + it('Returns false if unknown field is used in nodes[]', function () { + let dchainConfig = bid.meta.dchain; + dchainConfig.nodes[0].test = '1'; // String + expect(checkDchainSyntax(bid, STRICT)).to.false; + }); + + it('Relaxed mode: returns true even for invalid config', function () { + bid.meta.dchain = { + ver: 1.1, + complete: '0', + nodes: [{ + name: 'asdf', + domain: ['domain.com'] + }] + }; + + expect(checkDchainSyntax(bid, RELAX)).to.true; + }); + }); + + describe('addBidResponseHook', function () { + let bid; + let adUnitCode = 'adUnit1'; + + beforeEach(function () { + bid = { + bidderCode: 'bidderA', + meta: { + dchain: { + 'ver': '1.0', + 'complete': 0, + 'ext': {}, + 'nodes': [{ + 'asi': 'domain.com', + 'bsid': '12345', + }, { + 'name': 'bidder', + 'domain': 'bidder.com', + 'ext': {} + }] + }, + networkName: 'myNetworkName', + networkId: 8475 + } + }; + }); + + afterEach(function () { + config.resetConfig(); + }); + + it('good strict config should append a node object to existing bid.meta.dchain object', function () { + function testCallback(adUnitCode, bid) { + expect(bid.meta.dchain).to.exist; + expect(bid.meta.dchain.nodes[1]).to.exist.and.to.deep.equal({ + 'name': 'bidder', + 'domain': 'bidder.com', + 'ext': {} + }); + expect(bid.meta.dchain.nodes[2]).to.exist.and.to.deep.equal({ asi: 'bidderA' }); + } + + config.setConfig({ dchain: { validation: STRICT } }); + addBidResponseHook(testCallback, adUnitCode, bid); + }); + + it('bad strict config should delete the bid.meta.dchain object', function () { + function testCallback(adUnitCode, bid) { + expect(bid.meta.dchain).to.not.exist; + } + + config.setConfig({ dchain: { validation: STRICT } }); + bid.meta.dchain.complete = 3; + addBidResponseHook(testCallback, adUnitCode, bid); + }); + + it('relaxed config should allow bid.meta.dchain to proceed, even with bad values', function () { + function testCallback(adUnitCode, bid) { + expect(bid.meta.dchain).to.exist; + expect(bid.meta.dchain.complete).to.exist.and.to.equal(3); + expect(bid.meta.dchain.nodes[2]).to.exist.and.to.deep.equal({ asi: 'bidderA' }); + } + + config.setConfig({ dchain: { validation: RELAX } }); + bid.meta.dchain.complete = 3; + addBidResponseHook(testCallback, adUnitCode, bid); + }); + + it('off config should allow the bid.meta.dchain to proceed', function () { + // check for missing nodes + function testCallback(adUnitCode, bid) { + expect(bid.meta.dchain).to.exist; + expect(bid.meta.dchain.complete).to.exist.and.to.equal(0); + expect(bid.meta.dchain.nodes).to.exist.and.to.deep.equal({ test: 123 }); + } + + config.setConfig({ dchain: { validation: OFF } }); + bid.meta.dchain.nodes = { test: 123 }; + addBidResponseHook(testCallback, adUnitCode, bid); + }); + + it('no bidder dchain', function () { + function testCallback(adUnitCode, bid) { + expect(bid.meta.dchain).to.exist; + expect(bid.meta.dchain.ver).to.exist.and.to.equal('1.0'); + expect(bid.meta.dchain.complete).to.exist.and.to.equal(0); + expect(bid.meta.dchain.nodes).to.exist.and.to.deep.equal([{ name: 'myNetworkName', bsid: '8475' }, { name: 'bidderA' }]); + } + + delete bid.meta.dchain; + config.setConfig({ dchain: { validation: RELAX } }); + addBidResponseHook(testCallback, adUnitCode, bid); + }); + }); +}); diff --git a/test/spec/modules/deepintentBidAdapter_spec.js b/test/spec/modules/deepintentBidAdapter_spec.js index fcf7056fb3f..d2a351b4089 100644 --- a/test/spec/modules/deepintentBidAdapter_spec.js +++ b/test/spec/modules/deepintentBidAdapter_spec.js @@ -3,8 +3,8 @@ import {spec} from 'modules/deepintentBidAdapter.js'; import * as utils from '../../../src/utils.js'; describe('Deepintent adapter', function () { - let request; - let bannerResponse; + let request, videoBidRequests; + let bannerResponse, videoBidResponse, invalidResponse; beforeEach(function () { request = [ @@ -32,6 +32,38 @@ describe('Deepintent adapter', function () { } } ]; + videoBidRequests = + [ + { + code: 'video1', + mediaTypes: { + video: { + playerSize: [640, 480], + context: 'instream' + } + }, + bidder: 'deepintent', + bidId: '22bddb28db77d', + params: { + tagId: '100013', + video: { + mimes: ['video/mp4', 'video/x-flv'], + skippable: true, + testwrongparam: 3, + testwrongparam1: 'wrong', + minduration: 5, + maxduration: 30, + startdelay: 5, + playbackmethod: [1, 3], + api: [1, 2], + protocols: [2, 3], + battr: [13, 14], + minbitrate: 10, + maxbitrate: 10 + } + } + } + ]; bannerResponse = { 'body': { 'id': '303e1fae-9677-41e2-9a92-15a23445363f', @@ -53,7 +85,47 @@ describe('Deepintent adapter', function () { }], 'bidid': '0b08b09f-aaa1-4c14-b1c8-7debb1a7c1cd' } - } + }; + invalidResponse = { + 'body': { + 'id': '303e1fae-9677-41e2-9a92-15a23445363f', + 'seatbid': [{ + 'bid': [{ + 'id': '11447bb1-a266-470d-b0d7-8810f5b1b75f', + 'impid': 'a7e92b9b-d9db-4de8-9c3f-f90737335445', + 'price': 0.6, + 'adid': '10001', + 'adm': 'invalid response', + 'adomain': ['deepintent.com'], + 'cid': '103389', + 'crid': '13665', + 'w': 300, + 'h': 250, + 'dealId': 'dee_12312stdszzsx' + }], + 'seat': '10000' + }], + 'bidid': '0b08b09f-aaa1-4c14-b1c8-7debb1a7c1cd' + } + }; + videoBidResponse = { + 'body': { + 'id': '93D3BAD6-E2E2-49FB-9D89-920B1761C865', + 'seatbid': [{ + 'bid': [{ + 'id': '74858439-49D7-4169-BA5D-44A046315B2F', + 'impid': '22bddb28db77d', + 'price': 1.3, + 'adm': 'Acudeo CompatibleVAST 2.0 Instream Test 1VAST 2.0 Instream Test 1https://dsptracker.com/{PSPM}00:00:04https://www.deepintent.com', + 'h': 250, + 'w': 300, + 'ext': { + 'deal_channel': 6 + } + }] + }] + } + }; }); describe('validations', function () { @@ -88,6 +160,45 @@ describe('Deepintent adapter', function () { isValid = spec.isBidRequestValid(bid); expect(isValid).to.equals(false); }); + it('should check for context if video is present', function() { + let bid = { + bidder: 'deepintent', + params: { + tagId: '12345', + video: { + mimes: ['video/mp4', 'video/x-flv'], + skippable: true, + } + }, + mediaTypes: { + video: { + playerSize: [640, 480], + context: 'instream' + } + }, + }, + isValid = spec.isBidRequestValid(bid); + expect(isValid).to.equal(true); + }); + it('should error out if context is not present and is Video', function() { + let bid = { + bidder: 'deepintent', + params: { + tagId: '12345', + video: { + mimes: ['video/mp4', 'video/x-flv'], + skippable: true, + } + }, + mediaTypes: { + video: { + playerSize: [640, 480] + } + }, + }, + isValid = spec.isBidRequestValid(bid); + expect(isValid).to.equal(false); + }) }); describe('request check', function () { it('unmutaable bid request check', function () { @@ -179,6 +290,28 @@ describe('Deepintent adapter', function () { expect(data2.regs).to.equal(undefined); expect(data2.user.ext).to.equal(undefined); }); + it('bid request check: Video params check ', function() { + let bRequest = spec.buildRequests(videoBidRequests); + let data = JSON.parse(bRequest.data); + expect(data.imp[0].video).to.be.a('object'); + expect(data.imp[0].video.minduration).to.be.a('number'); + expect(data.imp[0].video.maxduration).to.be.a('number'); + expect(data.imp[0].video.startdelay).to.be.a('number'); + expect(data.imp[0].video.playbackmethod).to.be.an('array'); + expect(data.imp[0].video.api).to.be.an('array'); + expect(data.imp[0].video.protocols).to.be.an('array'); + expect(data.imp[0].video.battr).to.be.an('array'); + expect(data.imp[0].video.minbitrate).to.be.a('number'); + expect(data.imp[0].video.maxbitrate).to.be.a('number'); + expect(data.imp[0].video.w).to.be.a('number'); + }); + it('bid request param check : invalid video params', function() { + let bRequest = spec.buildRequests(videoBidRequests); + let data = JSON.parse(bRequest.data); + expect(data.imp[0].video).to.be.a('object'); + expect(data.imp[0].video.testwrongparam).to.equal(undefined); + expect(data.imp[0].video.testwrongparam1).to.equal(undefined); + }); }); describe('user sync check', function () { it('user sync url check', function () { @@ -202,10 +335,27 @@ describe('Deepintent adapter', function () { expect(bResponse[0].height).to.equal(bannerResponse.body.seatbid[0].bid[0].h); expect(bResponse[0].currency).to.equal('USD'); expect(bResponse[0].netRevenue).to.equal(false); + expect(bResponse[0].mediaType).to.equal('banner'); expect(bResponse[0].meta.advertiserDomains).to.deep.equal(['deepintent.com']); expect(bResponse[0].ttl).to.equal(300); expect(bResponse[0].creativeId).to.equal(bannerResponse.body.seatbid[0].bid[0].crid); expect(bResponse[0].dealId).to.equal(bannerResponse.body.seatbid[0].bid[0].dealId); }); + it('bid response check: valid video bid response', function() { + let request = spec.buildRequests(videoBidRequests); + let response = spec.interpretResponse(videoBidResponse, request); + expect(response[0].mediaType).to.equal('video'); + expect(response[0].vastXml).to.not.equal(undefined); + }); + it('invalid bid response check ', function() { + let bRequest = spec.buildRequests(request); + let response = spec.interpretResponse(invalidResponse, bRequest); + expect(response[0].mediaType).to.equal(undefined); + }); + it('invalid bid response check ', function() { + let bRequest = spec.buildRequests(videoBidRequests); + let response = spec.interpretResponse(invalidResponse, bRequest); + expect(response[0].mediaType).to.equal(undefined); + }); }) }); diff --git a/test/spec/modules/deltaprojectsBidAdapter_spec.js b/test/spec/modules/deltaprojectsBidAdapter_spec.js new file mode 100644 index 00000000000..382415eab62 --- /dev/null +++ b/test/spec/modules/deltaprojectsBidAdapter_spec.js @@ -0,0 +1,399 @@ +import { expect } from 'chai'; +import { + BIDDER_CODE, + BIDDER_ENDPOINT_URL, + spec, USERSYNC_URL, + getBidFloor +} from 'modules/deltaprojectsBidAdapter.js'; + +const BID_REQ_REFER = 'http://example.com/page?param=val'; + +describe('deltaprojectsBidAdapter', function() { + describe('isBidRequestValid', function () { + function makeBid() { + return { + bidder: BIDDER_CODE, + params: { + publisherId: '12345' + }, + adUnitCode: 'adunit-code', + sizes: [ + [300, 250], + ], + bidId: '30b31c1838de1e', + bidderRequestId: '22edbae2733bf6', + auctionId: '1d1a030790a475', + }; + } + + it('should return true when bidder set correctly', function () { + expect(spec.isBidRequestValid(makeBid())).to.equal(true); + }); + + it('should return false when bid request is null', function () { + expect(spec.isBidRequestValid(undefined)).to.equal(false); + }); + + it('should return false when bidder not set correctly', function () { + let bid = makeBid(); + delete bid.bidder; + expect(spec.isBidRequestValid(bid)).to.equal(false); + }); + + it('should return false when publisher id is not set', function () { + let bid = makeBid(); + delete bid.params.publisherId; + expect(spec.isBidRequestValid(bid)).to.equal(false); + }); + }); + + describe('buildRequests', function () { + const BIDREQ = { + bidder: BIDDER_CODE, + params: { + tagId: '403370', + siteId: 'example.com', + }, + sizes: [ + [300, 250], + ], + bidId: '30b31c1838de1e', + bidderRequestId: '22edbae2733bf6', + auctionId: '1d1a030790a475', + } + const bidRequests = [BIDREQ]; + const bannerRequest = spec.buildRequests(bidRequests, {refererInfo: { referer: BID_REQ_REFER }})[0]; + const bannerRequestBody = bannerRequest.data; + + it('send bid request with test tag if it is set in the param', function () { + const TEST_TAG = 1; + const bidRequest = Object.assign({}, BIDREQ, { + params: { ...BIDREQ.params, test: TEST_TAG }, + }); + const bidderRequest = { refererInfo: { referer: BID_REQ_REFER } }; + const request = spec.buildRequests([bidRequest], bidderRequest)[0]; + expect(request.data.test).to.equal(TEST_TAG); + }); + + it('send bid request with correct timeout', function () { + const TMAX = 10; + const bidderRequest = { refererInfo: { referer: BID_REQ_REFER }, timeout: TMAX }; + const request = spec.buildRequests(bidRequests, bidderRequest)[0]; + expect(request.data.tmax).to.equal(TMAX); + }); + + it('send bid request to the correct endpoint URL', function () { + expect(bannerRequest.url).to.equal(BIDDER_ENDPOINT_URL); + }); + + it('sends bid request to our endpoint via POST', function () { + expect(bannerRequest.method).to.equal('POST'); + }); + + it('sends screen dimensions', function () { + expect(bannerRequestBody.device.w).to.equal(screen.width); + expect(bannerRequestBody.device.h).to.equal(screen.height); + }); + + it('includes the ad size in the bid request', function () { + expect(bannerRequestBody.imp[0].banner.format[0].w).to.equal(BIDREQ.sizes[0][0]); + expect(bannerRequestBody.imp[0].banner.format[0].h).to.equal(BIDREQ.sizes[0][1]); + }); + + it('sets domain and href correctly', function () { + expect(bannerRequestBody.site.domain).to.equal(BIDREQ.params.siteId); + expect(bannerRequestBody.site.page).to.equal(BID_REQ_REFER); + }); + + const gdprBidRequests = [{ + bidder: BIDDER_CODE, + params: { + tagId: '403370', + siteId: 'example.com' + }, + sizes: [ + [300, 250] + ], + bidId: '30b31c1838de1e', + bidderRequestId: '22edbae2733bf6', + auctionId: '1d1a030790a475' + }]; + const consentString = 'BOJ/P2HOJ/P2HABABMAAAAAZ+A=='; + + const GDPR_REQ_REFERER = 'http://localhost:9876/' + function getGdprRequestBody(gdprApplies, consentString) { + const gdprRequest = spec.buildRequests(gdprBidRequests, { + gdprConsent: { + gdprApplies: gdprApplies, + consentString: consentString, + }, + refererInfo: { + referer: GDPR_REQ_REFERER, + }, + })[0]; + return gdprRequest.data; + } + + it('should handle gdpr applies being present and true', function() { + const gdprRequestBody = getGdprRequestBody(true, consentString); + expect(gdprRequestBody.regs.ext.gdpr).to.equal(1); + expect(gdprRequestBody.user.ext.consent).to.equal(consentString); + }) + + it('should handle gdpr applies being present and false', function() { + const gdprRequestBody = getGdprRequestBody(false, consentString); + expect(gdprRequestBody.regs.ext.gdpr).to.equal(0); + expect(gdprRequestBody.user.ext.consent).to.equal(consentString); + }) + + it('should handle gdpr applies being undefined', function() { + const gdprRequestBody = getGdprRequestBody(undefined, consentString); + expect(gdprRequestBody.regs).to.deep.equal({ext: {}}); + expect(gdprRequestBody.user.ext.consent).to.equal(consentString); + }) + + it('should handle gdpr consent being undefined', function() { + const gdprRequest = spec.buildRequests(gdprBidRequests, {refererInfo: { referer: GDPR_REQ_REFERER }})[0]; + const gdprRequestBody = gdprRequest.data; + expect(gdprRequestBody.regs).to.deep.equal({ ext: {} }); + expect(gdprRequestBody.user).to.deep.equal({ ext: {} }); + }) + }); + + describe('interpretResponse', function () { + const bidRequests = [ + { + bidder: BIDDER_CODE, + params: { + tagId: '403370', + siteId: 'example.com', + currency: 'USD', + }, + sizes: [ + [300, 250], + ], + bidId: '30b31c1838de1e', + bidderRequestId: '22edbae2733bf6', + auctionId: '1d1a030790a475', + }, + ]; + const request = spec.buildRequests(bidRequests, {refererInfo: { referer: BID_REQ_REFER }})[0]; + function makeResponse() { + return { + body: { + id: '5e5c23a5ba71e78', + seatbid: [ + { + bid: [ + { + id: '6vmb3isptf', + crid: 'deltaprojectscreative', + impid: '322add653672f68', + price: 1.22, + adm: '', + attr: [5], + h: 90, + nurl: 'http://nurl', + w: 728, + } + ], + seat: 'MOCK' + } + ], + bidid: '5e5c23a5ba71e78', + cur: 'USD' + } + }; + } + const expectedBid = { + requestId: '322add653672f68', + cpm: 1.22, + width: 728, + height: 90, + creativeId: 'deltaprojectscreative', + dealId: null, + currency: 'USD', + netRevenue: true, + mediaType: 'banner', + ttl: 60, + ad: '
' + }; + + it('should get incorrect bid response if response body is missing', function () { + let response = makeResponse(); + delete response.body; + let result = spec.interpretResponse(response, request); + expect(result.length).to.equal(0); + }); + + it('should get incorrect bid response if id or seat id of response body is missing', function () { + let response1 = makeResponse(); + delete response1.body.id; + let result1 = spec.interpretResponse(response1, request); + expect(result1.length).to.equal(0); + + let response2 = makeResponse(); + delete response2.body.seatbid; + let result2 = spec.interpretResponse(response2, request); + expect(result2.length).to.equal(0); + }); + + it('should get the correct bid response', function () { + let result = spec.interpretResponse(makeResponse(), request); + expect(result.length).to.equal(1); + expect(result[0]).to.deep.equal(expectedBid); + }); + + it('should handle a missing crid', function () { + let noCridResponse = makeResponse(); + delete noCridResponse.body.seatbid[0].bid[0].crid; + const fallbackCrid = noCridResponse.body.seatbid[0].bid[0].id; + let noCridResult = Object.assign({}, expectedBid, {'creativeId': fallbackCrid}); + let result = spec.interpretResponse(noCridResponse, request); + expect(result.length).to.equal(1); + expect(result[0]).to.deep.equal(noCridResult); + }); + + it('should handle a missing nurl', function () { + let noNurlResponse = makeResponse(); + delete noNurlResponse.body.seatbid[0].bid[0].nurl; + let noNurlResult = Object.assign({}, expectedBid); + noNurlResult.ad = ''; + let result = spec.interpretResponse(noNurlResponse, request); + expect(result.length).to.equal(1); + expect(result[0]).to.deep.equal(noNurlResult); + }); + + it('handles empty bid response', function () { + let response = { + body: { + id: '5e5c23a5ba71e78', + seatbid: [] + } + }; + let result = spec.interpretResponse(response, request); + expect(result.length).to.equal(0); + }); + + it('should keep custom properties', () => { + const customProperties = {test: 'a test message', param: {testParam: 1}}; + const expectedResult = Object.assign({}, expectedBid, {[spec.code]: customProperties}); + const response = makeResponse(); + response.body.seatbid[0].bid[0].ext = customProperties; + const result = spec.interpretResponse(response, request); + expect(result.length).to.equal(1); + expect(result[0]).to.deep.equal(expectedResult); + }); + }); + + describe('onBidWon', function () { + const OPEN_RTB_RESP = { + body: { + id: 'abc', + seatbid: [ + { + bid: [ + { + 'id': 'abc*123*456', + 'impid': 'xxxxxxx', + 'price': 46.657196, + 'adm': '', + 'adid': '56380110', + 'cid': '44724710', + 'crid': '443801010', + 'w': 300, + 'h': 250, + 'ext': { + 'prebid': { + 'targeting': { + 'hb_bidder': 'luponmedia', + 'hb_pb': '0.40', + 'hb_size': '300x250' + }, + 'type': 'banner' + } + } + } + ], + 'seat': 'luponmedia' + } + ], + 'cur': 'USD', + 'ext': { + 'responsetimemillis': { + 'luponmedia': 233 + }, + 'tmaxrequest': 1500, + 'usersyncs': { + 'status': 'ok', + 'bidder_status': [] + } + } + }; + + let expectedResponse = [ + { + 'requestId': '2a122246ef72ea', + 'cpm': '0.43', + 'width': 300, + 'height': 250, + 'creativeId': '443801010', + 'currency': 'USD', + 'dealId': '23425', + 'netRevenue': false, + 'ttl': 300, + 'referrer': '', + 'ad': ' ' + } + ]; + + let bidderRequest = { + 'data': '{"site":{"page":"https://novi.ba/clanak/176067/fast-car-beginner-s-guide-to-tuning-turbo-engines"}}' + }; + + let result = spec.interpretResponse({ body: response }, bidderRequest); + expect(Object.keys(result[0])).to.have.members(Object.keys(expectedResponse[0])); + }); + + it('handles nobid responses', function () { + let noBidResponse = []; + + let noBidBidderRequest = { + 'data': '{"site":{"page":""}}' + } + let noBidResult = spec.interpretResponse({ body: noBidResponse }, noBidBidderRequest); + expect(noBidResult.length).to.equal(0); + }); + }); + + describe('getUserSyncs', function () { + const bidResponse1 = { + 'body': { + 'ext': { + 'responsetimemillis': { + 'luponmedia': 233 + }, + 'tmaxrequest': 1500, + 'usersyncs': { + 'status': 'ok', + 'bidder_status': [ + { + 'bidder': 'luponmedia', + 'no_cookie': true, + 'usersync': { + 'url': 'https://adxpremium.services/api/usersync', + 'type': 'redirect' + } + }, + { + 'bidder': 'luponmedia', + 'no_cookie': true, + 'usersync': { + 'url': 'https://adxpremium.services/api/iframeusersync', + 'type': 'iframe' + } + } + ] + } + } + } + }; + + const bidResponse2 = { + 'body': { + 'ext': { + 'responsetimemillis': { + 'luponmedia': 233 + }, + 'tmaxrequest': 1500, + 'usersyncs': { + 'status': 'no_cookie', + 'bidder_status': [] + } + } + } + }; + + it('should use a sync url from first response (pixel and iframe)', function () { + const syncs = spec.getUserSyncs({ pixelEnabled: true, iframeEnabled: true }, [bidResponse1, bidResponse2]); + expect(syncs).to.deep.equal([ + { + type: 'image', + url: 'https://adxpremium.services/api/usersync' + }, + { + type: 'iframe', + url: 'https://adxpremium.services/api/iframeusersync' + } + ]); + }); + + it('handle empty response (e.g. timeout)', function () { + const syncs = spec.getUserSyncs({ pixelEnabled: true, iframeEnabled: true }, []); + expect(syncs).to.deep.equal([]); + }); + + it('returns empty syncs when not pixel enabled and not iframe enabled', function () { + const syncs = spec.getUserSyncs({ pixelEnabled: false, iframeEnabled: false }, [bidResponse1]); + expect(syncs).to.deep.equal([]); + }); + + it('returns pixel syncs when pixel enabled and not iframe enabled', function() { + resetUserSync(); + + const syncs = spec.getUserSyncs({ pixelEnabled: true, iframeEnabled: false }, [bidResponse1]); + expect(syncs).to.deep.equal([ + { + type: 'image', + url: 'https://adxpremium.services/api/usersync' + } + ]); + }); + + it('returns iframe syncs when not pixel enabled and iframe enabled', function() { + resetUserSync(); + + const syncs = spec.getUserSyncs({ pixelEnabled: false, iframeEnabled: true }, [bidResponse1]); + expect(syncs).to.deep.equal([ + { + type: 'iframe', + url: 'https://adxpremium.services/api/iframeusersync' + } + ]); + }); + }); + + describe('hasValidSupplyChainParams', function () { + it('returns true if schain is valid', function () { + const schain = { + 'ver': '1.0', + 'complete': 1, + 'nodes': [ + { + 'asi': 'novi.ba', + 'sid': '199424', + 'hp': 1 + } + ] + }; + + const checkSchain = hasValidSupplyChainParams(schain); + expect(checkSchain).to.equal(true); + }); + + it('returns false if schain is invalid', function () { + const schain = { + 'ver': '1.0', + 'complete': 1, + 'nodes': [ + { + 'invalid': 'novi.ba' + } + ] + }; + + const checkSchain = hasValidSupplyChainParams(schain); + expect(checkSchain).to.equal(false); + }); + }); + + describe('onBidWon', function () { + const bidWonEvent = { + 'bidderCode': 'luponmedia', + 'width': 300, + 'height': 250, + 'statusMessage': 'Bid available', + 'adId': '105bbf8c54453ff', + 'requestId': '934b8752185955', + 'mediaType': 'banner', + 'source': 'client', + 'cpm': 0.364, + 'creativeId': '443801010', + 'currency': 'USD', + 'netRevenue': false, + 'ttl': 300, + 'referrer': '', + 'ad': '', + 'auctionId': '926a8ea3-3dd4-4bf2-95ab-c85c2ce7e99b', + 'responseTimestamp': 1598527728026, + 'requestTimestamp': 1598527727629, + 'bidder': 'luponmedia', + 'adUnitCode': 'div-gpt-ad-1533155193780-5', + 'timeToRespond': 397, + 'size': '300x250', + 'status': 'rendered' + }; + + let ajaxStub; + + beforeEach(() => { + ajaxStub = sinon.stub(spec, 'sendWinningsToServer') + }) + + afterEach(() => { + ajaxStub.restore() + }) + + it('calls luponmedia\'s callback endpoint', () => { + const result = spec.onBidWon(bidWonEvent); + expect(result).to.equal(undefined); + expect(ajaxStub.calledOnce).to.equal(true); + expect(ajaxStub.firstCall.args[0]).to.deep.equal(JSON.stringify(bidWonEvent)); + }); + }); +}); diff --git a/test/spec/modules/mediasquareBidAdapter_spec.js b/test/spec/modules/mediasquareBidAdapter_spec.js index f3f09a8ddf8..862b74f4f97 100644 --- a/test/spec/modules/mediasquareBidAdapter_spec.js +++ b/test/spec/modules/mediasquareBidAdapter_spec.js @@ -167,7 +167,7 @@ describe('MediaSquare bid adapter tests', function () { }); it('Verifies user sync without cookie in bid response', function () { var syncs = spec.getUserSyncs({}, [BID_RESPONSE], DEFAULT_OPTIONS.gdprConsent, DEFAULT_OPTIONS.uspConsent); - expect(syncs).to.have.property('type').and.to.equal('iframe'); + expect(syncs).to.have.lengthOf(0); }); it('Verifies user sync with cookies in bid response', function () { BID_RESPONSE.body.cookies = [{'type': 'image', 'url': 'http://www.cookie.sync.org/'}]; @@ -178,13 +178,13 @@ describe('MediaSquare bid adapter tests', function () { }); it('Verifies user sync with no bid response', function() { var syncs = spec.getUserSyncs({}, null, DEFAULT_OPTIONS.gdprConsent, DEFAULT_OPTIONS.uspConsent); - expect(syncs).to.have.property('type').and.to.equal('iframe'); + expect(syncs).to.have.lengthOf(0); }); it('Verifies user sync with no bid body response', function() { var syncs = spec.getUserSyncs({}, [], DEFAULT_OPTIONS.gdprConsent, DEFAULT_OPTIONS.uspConsent); - expect(syncs).to.have.property('type').and.to.equal('iframe'); + expect(syncs).to.have.lengthOf(0); var syncs = spec.getUserSyncs({}, [{}], DEFAULT_OPTIONS.gdprConsent, DEFAULT_OPTIONS.uspConsent); - expect(syncs).to.have.property('type').and.to.equal('iframe'); + expect(syncs).to.have.lengthOf(0); }); it('Verifies native in bid response', function () { const request = spec.buildRequests(NATIVE_PARAMS, DEFAULT_OPTIONS); diff --git a/test/spec/modules/merkleIdSystem_spec.js b/test/spec/modules/merkleIdSystem_spec.js index 64dfc0d463c..63a2791ba3c 100644 --- a/test/spec/modules/merkleIdSystem_spec.js +++ b/test/spec/modules/merkleIdSystem_spec.js @@ -192,6 +192,24 @@ describe('Merkle System', function () { expect(id.id).to.exist.and.to.equal(storedId); }); + it('extendId() should warn on missing endpoint', function () { + let config = { + params: { + ...CONFIG_PARAMS, + endpoint: undefined + }, + storage: STORAGE_PARAMS + }; + + let yesterday = new Date(Date.now() - 86400000).toUTCString(); + let storedId = {value: 'Merkle_Stored_ID', date: yesterday}; + + let submoduleCallback = merkleIdSubmodule.extendId(config, undefined, + storedId).callback; + submoduleCallback(callbackSpy); + expect(callbackSpy.calledOnce).to.be.true; + expect(utils.logWarn.args[0][0]).to.exist.and.to.equal('User ID - merkleId submodule endpoint string is not defined'); + }); it('extendId() callback on configured storageParam.refreshInSeconds', function () { let config = { diff --git a/test/spec/modules/missenaBidAdapter_spec.js b/test/spec/modules/missenaBidAdapter_spec.js new file mode 100644 index 00000000000..86b967cca5b --- /dev/null +++ b/test/spec/modules/missenaBidAdapter_spec.js @@ -0,0 +1,134 @@ +import { expect } from 'chai'; +import { spec, _getPlatform } from 'modules/missenaBidAdapter.js'; +import { newBidder } from 'src/adapters/bidderFactory.js'; + +describe('Missena Adapter', function () { + const adapter = newBidder(spec); + + const bidId = 'abc'; + + const bid = { + bidder: 'missena', + bidId: bidId, + sizes: [[1, 1]], + params: { + apiKey: 'PA-34745704', + }, + }; + + describe('codes', function () { + it('should return a bidder code of missena', function () { + expect(spec.code).to.equal('missena'); + }); + }); + + describe('isBidRequestValid', function () { + it('should return true if the apiKey param is present', function () { + expect(spec.isBidRequestValid(bid)).to.equal(true); + }); + + it('should return false if the apiKey is missing', function () { + expect( + spec.isBidRequestValid(Object.assign(bid, { params: {} })) + ).to.equal(false); + }); + + it('should return false if the apiKey is an empty string', function () { + expect( + spec.isBidRequestValid(Object.assign(bid, { params: { apiKey: '' } })) + ).to.equal(false); + }); + }); + + describe('buildRequests', function () { + const consentString = 'AAAAAAAAA=='; + + const bidderRequest = { + gdprConsent: { + consentString: consentString, + gdprApplies: true, + }, + refererInfo: { + referer: 'https://referer', + canonicalUrl: 'https://canonical', + }, + }; + + const requests = spec.buildRequests([bid, bid], bidderRequest); + const request = requests[0]; + const payload = JSON.parse(request.data); + + it('should return as many server requests as bidder requests', function () { + expect(requests.length).to.equal(2); + }); + + it('should have a post method', function () { + expect(request.method).to.equal('POST'); + }); + + it('should send the bidder id', function () { + expect(payload.request_id).to.equal(bidId); + }); + + it('should send referer information to the request', function () { + expect(payload.referer).to.equal('https://referer'); + expect(payload.referer_canonical).to.equal('https://canonical'); + }); + + it('should send gdpr consent information to the request', function () { + expect(payload.consent_string).to.equal(consentString); + expect(payload.consent_required).to.equal(true); + }); + }); + + describe('interpretResponse', function () { + const serverResponse = { + requestId: bidId, + cpm: 0.5, + currency: 'USD', + ad: '', + meta: { + advertiserDomains: ['missena.com'] + }, + }; + + const serverTimeoutResponse = { + requestId: bidId, + timeout: true, + ad: '', + }; + + const serverEmptyAdResponse = { + requestId: bidId, + cpm: 0.5, + currency: 'USD', + ad: '', + }; + + it('should return a proper bid response', function () { + const result = spec.interpretResponse({ body: serverResponse }, bid); + + expect(result.length).to.equal(1); + + expect(Object.keys(result[0])).to.have.members( + Object.keys(serverResponse) + ); + }); + + it('should return an empty response when the server answers with a timeout', function () { + const result = spec.interpretResponse( + { body: serverTimeoutResponse }, + bid + ); + expect(result).to.deep.equal([]); + }); + + it('should return an empty response when the server answers with an empty ad', function () { + const result = spec.interpretResponse( + { body: serverEmptyAdResponse }, + bid + ); + expect(result).to.deep.equal([]); + }); + }); +}); diff --git a/test/spec/modules/multibid_spec.js b/test/spec/modules/multibid_spec.js index e849392ee4b..86365eb520f 100644 --- a/test/spec/modules/multibid_spec.js +++ b/test/spec/modules/multibid_spec.js @@ -82,7 +82,7 @@ describe('multibid adapter', function () { 'sizes': [[300, 250]] } }, - 'adUnitCode': 'test-div', + 'adUnitCode': 'test.div', 'transactionId': 'c153f3da-84f0-4be8-95cb-0647c458bc60', 'sizes': [[300, 250]], 'bidId': '2408ef83b84c9d', @@ -106,7 +106,7 @@ describe('multibid adapter', function () { 'sizes': [[300, 250]] } }, - 'adUnitCode': 'test-div', + 'adUnitCode': 'test.div', 'transactionId': 'c153f3da-84f0-4be8-95cb-0647c458bc60', 'sizes': [[300, 250]], 'bidId': '2408ef83b84c9d', @@ -197,14 +197,14 @@ describe('multibid adapter', function () { }); it('adds original bids and does not modify', function () { - let adUnitCode = 'test-div'; + let adUnitCode = 'test.div'; let bids = [{...bidArray[0]}, {...bidArray[1]}]; addBidResponseHook(callbackFn, adUnitCode, {...bids[0]}); expect(result).to.not.equal(null); expect(result.adUnitCode).to.not.equal(null); - expect(result.adUnitCode).to.equal('test-div'); + expect(result.adUnitCode).to.equal('test.div'); expect(result.bid).to.not.equal(null); expect(result.bid).to.deep.equal(bids[0]); @@ -214,13 +214,13 @@ describe('multibid adapter', function () { expect(result).to.not.equal(null); expect(result.adUnitCode).to.not.equal(null); - expect(result.adUnitCode).to.equal('test-div'); + expect(result.adUnitCode).to.equal('test.div'); expect(result.bid).to.not.equal(null); expect(result.bid).to.deep.equal(bids[1]); }); it('modifies and adds both bids based on multibid configuration', function () { - let adUnitCode = 'test-div'; + let adUnitCode = 'test.div'; let bids = [{...bidArray[0]}, {...bidArray[1]}]; config.setConfig({multibid: [{bidder: 'bidderA', maxBids: 2, targetBiddercodePrefix: 'bidA'}]}); @@ -232,7 +232,7 @@ describe('multibid adapter', function () { expect(result).to.not.equal(null); expect(result.adUnitCode).to.not.equal(null); - expect(result.adUnitCode).to.equal('test-div'); + expect(result.adUnitCode).to.equal('test.div'); expect(result.bid).to.not.equal(null); expect(result.bid).to.deep.equal(bids[0]); @@ -250,13 +250,13 @@ describe('multibid adapter', function () { expect(result).to.not.equal(null); expect(result.adUnitCode).to.not.equal(null); - expect(result.adUnitCode).to.equal('test-div'); + expect(result.adUnitCode).to.equal('test.div'); expect(result.bid).to.not.equal(null); expect(result.bid).to.deep.equal(bids[1]); }); it('only modifies bids defined in the multibid configuration', function () { - let adUnitCode = 'test-div'; + let adUnitCode = 'test.div'; let bids = [{...bidArray[0]}, {...bidArray[1]}]; bids.push({ @@ -276,7 +276,7 @@ describe('multibid adapter', function () { expect(result).to.not.equal(null); expect(result.adUnitCode).to.not.equal(null); - expect(result.adUnitCode).to.equal('test-div'); + expect(result.adUnitCode).to.equal('test.div'); expect(result.bid).to.not.equal(null); expect(result.bid).to.deep.equal(bids[0]); @@ -292,7 +292,7 @@ describe('multibid adapter', function () { expect(result).to.not.equal(null); expect(result.adUnitCode).to.not.equal(null); - expect(result.adUnitCode).to.equal('test-div'); + expect(result.adUnitCode).to.equal('test.div'); expect(result.bid).to.not.equal(null); expect(result.bid).to.deep.equal(bids[1]); @@ -302,13 +302,13 @@ describe('multibid adapter', function () { expect(result).to.not.equal(null); expect(result.adUnitCode).to.not.equal(null); - expect(result.adUnitCode).to.equal('test-div'); + expect(result.adUnitCode).to.equal('test.div'); expect(result.bid).to.not.equal(null); expect(result.bid).to.deep.equal(bids[2]); }); it('only modifies and returns bids under limit for a specifc bidder in the multibid configuration', function () { - let adUnitCode = 'test-div'; + let adUnitCode = 'test.div'; let bids = [{...bidArray[0]}, {...bidArray[1]}]; bids.push({ @@ -328,7 +328,7 @@ describe('multibid adapter', function () { expect(result).to.not.equal(null); expect(result.adUnitCode).to.not.equal(null); - expect(result.adUnitCode).to.equal('test-div'); + expect(result.adUnitCode).to.equal('test.div'); expect(result.bid).to.not.equal(null); expect(result.bid).to.deep.equal(bids[0]); @@ -344,7 +344,7 @@ describe('multibid adapter', function () { expect(result).to.not.equal(null); expect(result.adUnitCode).to.not.equal(null); - expect(result.adUnitCode).to.equal('test-div'); + expect(result.adUnitCode).to.equal('test.div'); expect(result.bid).to.not.equal(null); expect(result.bid).to.deep.equal(bids[1]); @@ -356,7 +356,7 @@ describe('multibid adapter', function () { }); it('if no prefix in multibid configuration, modifies and returns bids under limit without preifx property', function () { - let adUnitCode = 'test-div'; + let adUnitCode = 'test.div'; let bids = [{...bidArray[0]}, {...bidArray[1]}]; bids.push({ @@ -375,7 +375,7 @@ describe('multibid adapter', function () { expect(result).to.not.equal(null); expect(result.adUnitCode).to.not.equal(null); - expect(result.adUnitCode).to.equal('test-div'); + expect(result.adUnitCode).to.equal('test.div'); expect(result.bid).to.not.equal(null); expect(result.bid).to.deep.equal(bids[0]); @@ -389,7 +389,7 @@ describe('multibid adapter', function () { expect(result).to.not.equal(null); expect(result.adUnitCode).to.not.equal(null); - expect(result.adUnitCode).to.equal('test-div'); + expect(result.adUnitCode).to.equal('test.div'); expect(result.bid).to.not.equal(null); expect(result.bid).to.deep.equal(bids[1]); @@ -401,7 +401,7 @@ describe('multibid adapter', function () { }); it('does not include extra bids if cpm is less than floor value', function () { - let adUnitCode = 'test-div'; + let adUnitCode = 'test.div'; let bids = [{...bidArrayAlt[1]}, {...bidArrayAlt[0]}, {...bidArrayAlt[2]}, {...bidArrayAlt[3]}]; bids.map(bid => { @@ -418,7 +418,7 @@ describe('multibid adapter', function () { floorRuleValue: 65, floorValue: 65, matchedFields: { - gptSlot: 'test-div', + gptSlot: 'test.div', mediaType: 'banner' } } @@ -435,7 +435,7 @@ describe('multibid adapter', function () { expect(result).to.not.equal(null); expect(result.adUnitCode).to.not.equal(null); - expect(result.adUnitCode).to.equal('test-div'); + expect(result.adUnitCode).to.equal('test.div'); expect(result.bid).to.not.equal(null); expect(result.bid.bidder).to.equal('bidderA'); expect(result.bid.targetingBidder).to.equal(undefined); @@ -452,7 +452,7 @@ describe('multibid adapter', function () { expect(result).to.not.equal(null); expect(result.adUnitCode).to.not.equal(null); - expect(result.adUnitCode).to.equal('test-div'); + expect(result.adUnitCode).to.equal('test.div'); expect(result.bid).to.not.equal(null); expect(result.bid.bidder).to.equal('bidderB'); expect(result.bid.targetingBidder).to.equal(undefined); @@ -463,14 +463,14 @@ describe('multibid adapter', function () { expect(result).to.not.equal(null); expect(result.adUnitCode).to.not.equal(null); - expect(result.adUnitCode).to.equal('test-div'); + expect(result.adUnitCode).to.equal('test.div'); expect(result.bid).to.not.equal(null); expect(result.bid.bidder).to.equal('bidderC'); expect(result.bid.targetingBidder).to.equal(undefined); }); it('does include extra bids if cpm is not less than floor value', function () { - let adUnitCode = 'test-div'; + let adUnitCode = 'test.div'; let bids = [{...bidArrayAlt[1]}, {...bidArrayAlt[0]}]; bids.map(bid => { @@ -487,7 +487,7 @@ describe('multibid adapter', function () { floorRuleValue: 25, floorValue: 25, matchedFields: { - gptSlot: 'test-div', + gptSlot: 'test.div', mediaType: 'banner' } } @@ -504,7 +504,7 @@ describe('multibid adapter', function () { expect(result).to.not.equal(null); expect(result.adUnitCode).to.not.equal(null); - expect(result.adUnitCode).to.equal('test-div'); + expect(result.adUnitCode).to.equal('test.div'); expect(result.bid).to.not.equal(null); expect(result.bid.bidder).to.equal('bidderA'); expect(result.bid.targetingBidder).to.equal(undefined); @@ -518,7 +518,7 @@ describe('multibid adapter', function () { expect(result).to.not.equal(null); expect(result.adUnitCode).to.not.equal(null); - expect(result.adUnitCode).to.equal('test-div'); + expect(result.adUnitCode).to.equal('test.div'); expect(result.bid).to.not.equal(null); expect(result.bid.bidder).to.equal('bidderA'); expect(result.bid.targetingBidder).to.equal('bidA2'); @@ -578,7 +578,7 @@ describe('multibid adapter', function () { it('it sorts and creates dynamic alias on bidsReceived if multibid configuration found with prefix', function () { let modifiedBids = [{...bidArray[1]}, {...bidArray[0]}].map(bid => { - addBidResponseHook(bidResponseCallback, 'test-div', {...bid}); + addBidResponseHook(bidResponseCallback, 'test.div', {...bid}); return bidResult; }); @@ -603,7 +603,7 @@ describe('multibid adapter', function () { it('it sorts by cpm treating dynamic alias as unique bid when no bid limit defined', function () { let modifiedBids = [{...bidArrayAlt[0]}, {...bidArrayAlt[2]}, {...bidArrayAlt[3]}, {...bidArrayAlt[1]}].map(bid => { - addBidResponseHook(bidResponseCallback, 'test-div', {...bid}); + addBidResponseHook(bidResponseCallback, 'test.div', {...bid}); return bidResult; }); @@ -636,7 +636,7 @@ describe('multibid adapter', function () { it('it should filter out dynamic bid when bid limit is less than unique bid pool', function () { let modifiedBids = [{...bidArrayAlt[0]}, {...bidArrayAlt[2]}, {...bidArrayAlt[3]}, {...bidArrayAlt[1]}].map(bid => { - addBidResponseHook(bidResponseCallback, 'test-div', {...bid}); + addBidResponseHook(bidResponseCallback, 'test.div', {...bid}); return bidResult; }); @@ -662,7 +662,7 @@ describe('multibid adapter', function () { config.setConfig({ multibid: [{bidder: 'bidderA', maxBids: 2, targetBiddercodePrefix: 'bidA'}] }); let modifiedBids = [{...bidArrayAlt[0]}, {...bidArrayAlt[2]}, {...bidArrayAlt[3]}, {...bidArrayAlt[1]}].map(bid => { - addBidResponseHook(bidResponseCallback, 'test-div', {...bid}); + addBidResponseHook(bidResponseCallback, 'test.div', {...bid}); return bidResult; }); 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) }) }) diff --git a/test/spec/modules/nextMillenniumBidAdapter_spec.js b/test/spec/modules/nextMillenniumBidAdapter_spec.js index 15256d6c382..0dfee96d0ea 100644 --- a/test/spec/modules/nextMillenniumBidAdapter_spec.js +++ b/test/spec/modules/nextMillenniumBidAdapter_spec.js @@ -4,20 +4,57 @@ import { spec } from 'modules/nextMillenniumBidAdapter.js'; describe('nextMillenniumBidAdapterTests', function() { const bidRequestData = [ { + adUnitCode: 'test-div', bidId: 'bid1234', auctionId: 'b06c5141-fe8f-4cdf-9d7d-54415490a917', bidder: 'nextMillennium', params: { placement_id: '-1' }, - sizes: [[300, 250]] + sizes: [[300, 250]], + uspConsent: '1---', + gdprConsent: { + consentString: 'kjfdniwjnifwenrif3', + gdprApplies: true + } } ]; + it('Request params check with GDPR and USP Consent', function () { + const request = spec.buildRequests(bidRequestData, bidRequestData[0]); + expect(JSON.parse(request[0].data).user.ext.consent).to.equal('kjfdniwjnifwenrif3'); + expect(JSON.parse(request[0].data).regs.ext.us_privacy).to.equal('1---'); + expect(JSON.parse(request[0].data).regs.ext.gdpr).to.equal(1); + }); + + it('Request params check without GDPR Consent', function () { + delete bidRequestData[0].gdprConsent + const request = spec.buildRequests(bidRequestData, bidRequestData[0]); + expect(JSON.parse(request[0].data).regs.ext.gdpr).to.be.undefined; + expect(JSON.parse(request[0].data).regs.ext.us_privacy).to.equal('1---'); + }); + it('validate_generated_params', function() { const request = spec.buildRequests(bidRequestData); expect(request[0].bidId).to.equal('bid1234'); expect(JSON.parse(request[0].data).id).to.equal('b06c5141-fe8f-4cdf-9d7d-54415490a917'); }); + it('Check if refresh_count param is incremented', function() { + const request = spec.buildRequests(bidRequestData); + expect(JSON.parse(request[0].data).ext.nextMillennium.refresh_count).to.equal(3); + }); + + it('Test getUserSyncs function', function () { + const syncOptions = { + 'iframeEnabled': true + } + const userSync = spec.getUserSyncs(syncOptions); + expect(userSync).to.be.an('array').with.lengthOf(1); + expect(userSync[0].type).to.exist; + expect(userSync[0].url).to.exist; + expect(userSync[0].type).to.be.equal('iframe'); + expect(userSync[0].url).to.be.equal('https://statics.nextmillmedia.com/load-cookie.html?v=4'); + }); + it('validate_response_params', function() { const serverResponse = { body: { diff --git a/test/spec/modules/oguryBidAdapter_spec.js b/test/spec/modules/oguryBidAdapter_spec.js index d693e8fd3cc..225d0cd30ef 100644 --- a/test/spec/modules/oguryBidAdapter_spec.js +++ b/test/spec/modules/oguryBidAdapter_spec.js @@ -1,8 +1,9 @@ import { expect } from 'chai'; import { spec } from 'modules/oguryBidAdapter'; -import { deepClone } from 'src/utils.js'; +import * as utils from 'src/utils.js'; -const BID_HOST = 'https://mweb-hb.presage.io/api/header-bidding-request'; +const BID_URL = 'https://mweb-hb.presage.io/api/header-bidding-request'; +const TIMEOUT_URL = 'https://ms-ads-monitoring-events.presage.io/bid_timeout' describe('OguryBidAdapter', function () { let bidRequests; @@ -67,14 +68,14 @@ describe('OguryBidAdapter', function () { describe('isBidRequestValid', function () { it('should validate correct bid', () => { - let validBid = deepClone(bidRequests[0]); + let validBid = utils.deepClone(bidRequests[0]); let isValid = spec.isBidRequestValid(validBid); expect(isValid).to.equal(true); }); it('should not validate incorrect bid', () => { - let invalidBid = deepClone(bidRequests[0]); + let invalidBid = utils.deepClone(bidRequests[0]); delete invalidBid.sizes; delete invalidBid.mediaTypes; @@ -83,7 +84,7 @@ describe('OguryBidAdapter', function () { }); it('should not validate bid if adunit is not present', () => { - let invalidBid = deepClone(bidRequests[0]); + let invalidBid = utils.deepClone(bidRequests[0]); delete invalidBid.params.adUnitId; let isValid = spec.isBidRequestValid(invalidBid); @@ -91,7 +92,7 @@ describe('OguryBidAdapter', function () { }); it('should not validate bid if assetKet is not present', () => { - let invalidBid = deepClone(bidRequests[0]); + let invalidBid = utils.deepClone(bidRequests[0]); delete invalidBid.params.assetKey; let isValid = spec.isBidRequestValid(invalidBid); @@ -99,7 +100,7 @@ describe('OguryBidAdapter', function () { }); it('should validate bid if getFloor is not present', () => { - let invalidBid = deepClone(bidRequests[1]); + let invalidBid = utils.deepClone(bidRequests[1]); delete invalidBid.getFloor; let isValid = spec.isBidRequestValid(invalidBid); @@ -118,22 +119,26 @@ describe('OguryBidAdapter', function () { }; }); - it('should return syncs array with an element of type image', () => { + it('should return syncs array with two elements of type image', () => { const userSyncs = spec.getUserSyncs(syncOptions, [], gdprConsent); - expect(userSyncs).to.have.lengthOf(1); + expect(userSyncs).to.have.lengthOf(2); expect(userSyncs[0].type).to.equal('image'); expect(userSyncs[0].url).to.contain('https://ms-cookie-sync.presage.io/v1/init-sync/bid-switch'); + expect(userSyncs[1].type).to.equal('image'); + expect(userSyncs[1].url).to.contain('https://ms-cookie-sync.presage.io/ttd/init-sync'); }); it('should set the source as query param', () => { const userSyncs = spec.getUserSyncs(syncOptions, [], gdprConsent); expect(userSyncs[0].url).to.contain('source=prebid'); + expect(userSyncs[1].url).to.contain('source=prebid'); }); it('should set the tcString as query param', () => { const userSyncs = spec.getUserSyncs(syncOptions, [], gdprConsent); expect(userSyncs[0].url).to.contain(`iab_string=${gdprConsent.consentString}`); + expect(userSyncs[1].url).to.contain(`iab_string=${gdprConsent.consentString}`); }); it('should return an empty array when pixel is disable', () => { @@ -141,70 +146,82 @@ describe('OguryBidAdapter', function () { expect(spec.getUserSyncs(syncOptions, [], gdprConsent)).to.have.lengthOf(0); }); - it('should return syncs array with an element of type image when consentString is undefined', () => { + it('should return syncs array with two elements of type image when consentString is undefined', () => { gdprConsent = { gdprApplies: true, consentString: undefined }; const userSyncs = spec.getUserSyncs(syncOptions, [], gdprConsent); - expect(userSyncs).to.have.lengthOf(1); + expect(userSyncs).to.have.lengthOf(2); expect(userSyncs[0].type).to.equal('image'); - expect(userSyncs[0].url).to.equal('https://ms-cookie-sync.presage.io/v1/init-sync/bid-switch?iab_string=&source=prebid'); + expect(userSyncs[0].url).to.equal('https://ms-cookie-sync.presage.io/v1/init-sync/bid-switch?iab_string=&source=prebid') + expect(userSyncs[1].type).to.equal('image'); + expect(userSyncs[1].url).to.equal('https://ms-cookie-sync.presage.io/ttd/init-sync?iab_string=&source=prebid') }); - it('should return syncs array with an element of type image when consentString is null', () => { + it('should return syncs array with two elements of type image when consentString is null', () => { gdprConsent = { gdprApplies: true, consentString: null }; const userSyncs = spec.getUserSyncs(syncOptions, [], gdprConsent); - expect(userSyncs).to.have.lengthOf(1); + expect(userSyncs).to.have.lengthOf(2); expect(userSyncs[0].type).to.equal('image'); - expect(userSyncs[0].url).to.equal('https://ms-cookie-sync.presage.io/v1/init-sync/bid-switch?iab_string=&source=prebid'); + expect(userSyncs[0].url).to.equal('https://ms-cookie-sync.presage.io/v1/init-sync/bid-switch?iab_string=&source=prebid') + expect(userSyncs[1].type).to.equal('image'); + expect(userSyncs[1].url).to.equal('https://ms-cookie-sync.presage.io/ttd/init-sync?iab_string=&source=prebid') }); - it('should return syncs array with an element of type image when gdprConsent is undefined', () => { + it('should return syncs array with two elements of type image when gdprConsent is undefined', () => { gdprConsent = undefined; const userSyncs = spec.getUserSyncs(syncOptions, [], gdprConsent); - expect(userSyncs).to.have.lengthOf(1); + expect(userSyncs).to.have.lengthOf(2); expect(userSyncs[0].type).to.equal('image'); - expect(userSyncs[0].url).to.equal('https://ms-cookie-sync.presage.io/v1/init-sync/bid-switch?iab_string=&source=prebid'); + expect(userSyncs[0].url).to.equal('https://ms-cookie-sync.presage.io/v1/init-sync/bid-switch?iab_string=&source=prebid') + expect(userSyncs[1].type).to.equal('image'); + expect(userSyncs[1].url).to.equal('https://ms-cookie-sync.presage.io/ttd/init-sync?iab_string=&source=prebid') }); - it('should return syncs array with an element of type image when gdprConsent is null', () => { + it('should return syncs array with two elements of type image when gdprConsent is null', () => { gdprConsent = null; const userSyncs = spec.getUserSyncs(syncOptions, [], gdprConsent); - expect(userSyncs).to.have.lengthOf(1); + expect(userSyncs).to.have.lengthOf(2); expect(userSyncs[0].type).to.equal('image'); - expect(userSyncs[0].url).to.equal('https://ms-cookie-sync.presage.io/v1/init-sync/bid-switch?iab_string=&source=prebid'); + expect(userSyncs[0].url).to.equal('https://ms-cookie-sync.presage.io/v1/init-sync/bid-switch?iab_string=&source=prebid') + expect(userSyncs[1].type).to.equal('image'); + expect(userSyncs[1].url).to.equal('https://ms-cookie-sync.presage.io/ttd/init-sync?iab_string=&source=prebid') }); - it('should return syncs array with an element of type image when gdprConsent is null and gdprApplies is false', () => { + it('should return syncs array with two elements of type image when gdprConsent is null and gdprApplies is false', () => { gdprConsent = { gdprApplies: false, consentString: null }; const userSyncs = spec.getUserSyncs(syncOptions, [], gdprConsent); - expect(userSyncs).to.have.lengthOf(1); + expect(userSyncs).to.have.lengthOf(2); expect(userSyncs[0].type).to.equal('image'); - expect(userSyncs[0].url).to.equal('https://ms-cookie-sync.presage.io/v1/init-sync/bid-switch?iab_string=&source=prebid'); + expect(userSyncs[0].url).to.equal('https://ms-cookie-sync.presage.io/v1/init-sync/bid-switch?iab_string=&source=prebid') + expect(userSyncs[1].type).to.equal('image'); + expect(userSyncs[1].url).to.equal('https://ms-cookie-sync.presage.io/ttd/init-sync?iab_string=&source=prebid') }); - it('should return syncs array with an element of type image when gdprConsent is empty string and gdprApplies is false', () => { + it('should return syncs array with two elements of type image when gdprConsent is empty string and gdprApplies is false', () => { gdprConsent = { gdprApplies: false, consentString: '' }; const userSyncs = spec.getUserSyncs(syncOptions, [], gdprConsent); - expect(userSyncs).to.have.lengthOf(1); + expect(userSyncs).to.have.lengthOf(2); expect(userSyncs[0].type).to.equal('image'); - expect(userSyncs[0].url).to.equal('https://ms-cookie-sync.presage.io/v1/init-sync/bid-switch?iab_string=&source=prebid'); + expect(userSyncs[0].url).to.equal('https://ms-cookie-sync.presage.io/v1/init-sync/bid-switch?iab_string=&source=prebid') + expect(userSyncs[1].type).to.equal('image'); + expect(userSyncs[1].url).to.equal('https://ms-cookie-sync.presage.io/ttd/init-sync?iab_string=&source=prebid') }); }); @@ -223,7 +240,8 @@ describe('OguryBidAdapter', function () { w: 300, h: 250 }] - } + }, + ext: bidRequests[0].params }, { id: bidRequests[1].bidId, tagid: bidRequests[1].params.adUnitId, @@ -233,7 +251,8 @@ describe('OguryBidAdapter', function () { w: 600, h: 500 }] - } + }, + ext: bidRequests[1].params }], regs: { ext: { @@ -243,24 +262,29 @@ describe('OguryBidAdapter', function () { site: { id: bidRequests[0].params.assetKey, domain: window.location.hostname, + page: window.location.href }, user: { ext: { consent: bidderRequest.gdprConsent.consentString }, + }, + ext: { + prebidversion: '$prebid.version$', + adapterversion: '1.2.7' } }; it('sends bid request to ENDPOINT via POST', function () { - const validBidRequests = deepClone(bidRequests) + const validBidRequests = utils.deepClone(bidRequests) const request = spec.buildRequests(validBidRequests, bidderRequest); - expect(request.url).to.equal(BID_HOST); + expect(request.url).to.equal(BID_URL); expect(request.method).to.equal('POST'); }); it('bid request object should be conform', function () { - const validBidRequests = deepClone(bidRequests) + const validBidRequests = utils.deepClone(bidRequests) const request = spec.buildRequests(validBidRequests, bidderRequest); expect(request.data).to.deep.equal(expectedRequestObject); @@ -298,7 +322,7 @@ describe('OguryBidAdapter', function () { ...expectedRequestObject }; - const validBidRequests = deepClone(bidRequests); + const validBidRequests = utils.deepClone(bidRequests); validBidRequests[1] = { ...validBidRequests[1], getFloor: undefined @@ -313,7 +337,7 @@ describe('OguryBidAdapter', function () { ...expectedRequestObject }; - let validBidRequests = deepClone(bidRequests); + let validBidRequests = utils.deepClone(bidRequests); validBidRequests[1] = { ...validBidRequests[1], getFloor: 'getFloor' @@ -324,9 +348,9 @@ describe('OguryBidAdapter', function () { }); it('should handle bidFloor when currency is not USD', () => { - const expectedRequestWithUnsupportedFloorCurrency = deepClone(expectedRequestObject) + const expectedRequestWithUnsupportedFloorCurrency = utils.deepClone(expectedRequestObject) expectedRequestWithUnsupportedFloorCurrency.imp[0].bidfloor = 0; - let validBidRequests = deepClone(bidRequests); + let validBidRequests = utils.deepClone(bidRequests); validBidRequests[0] = { ...validBidRequests[0], getFloor: ({ size, currency, mediaType }) => { @@ -405,7 +429,9 @@ describe('OguryBidAdapter', function () { meta: { advertiserDomains: openRtbBidResponse.body.seatbid[0].bid[0].adomain }, - nurl: openRtbBidResponse.body.seatbid[0].bid[0].nurl + nurl: openRtbBidResponse.body.seatbid[0].bid[0].nurl, + adapterVersion: '1.2.7', + prebidVersion: '$prebid.version$' }, { requestId: openRtbBidResponse.body.seatbid[0].bid[1].impid, cpm: openRtbBidResponse.body.seatbid[0].bid[1].price, @@ -420,7 +446,9 @@ describe('OguryBidAdapter', function () { meta: { advertiserDomains: openRtbBidResponse.body.seatbid[0].bid[1].adomain }, - nurl: openRtbBidResponse.body.seatbid[0].bid[1].nurl + nurl: openRtbBidResponse.body.seatbid[0].bid[1].nurl, + adapterVersion: '1.2.7', + prebidVersion: '$prebid.version$' }] let request = spec.buildRequests(bidRequests, bidderRequest); @@ -453,11 +481,11 @@ describe('OguryBidAdapter', function () { }) afterEach(function() { - xhr.restore(); + xhr.restore() }) it('Should not create nurl request if bid is undefined', function() { - spec.onBidWon(); + spec.onBidWon() expect(requests.length).to.equal(0); }) @@ -472,5 +500,75 @@ describe('OguryBidAdapter', function () { expect(requests[0].url).to.equal(nurl); expect(requests[0].method).to.equal('GET') }) + + it('Should trigger getWindowContext method', function() { + const bidSample = { + id: 'advertId', + impid: 'bidId', + price: 100, + nurl: 'url', + adm: `test creative
cookies
`, + adomain: ['renault.fr'], + ext: { + adcontent: 'sample_creative', + advertid: '1a278c48-b79a-4bbf-b69f-3824803e7d87', + campaignid: '31724', + mediatype: 'image', + userid: 'ab4aabed-5230-49d9-9f1a-f06280d28366', + usersync: true, + advertiserid: '1', + isomidcompliant: false + }, + w: 180, + h: 101 + } + spec.onBidWon(bidSample) + expect(window.top.OG_PREBID_BID_OBJECT).to.deep.equal(bidSample) + }) + }) + + describe('getWindowContext', function() { + it('Should return top window if exist', function() { + const res = spec.getWindowContext() + expect(res).to.equal(window.top) + expect(res).to.not.be.undefined; + }) + + it('Should return self window if getting top window throw an error', function() { + const stub = sinon.stub(utils, 'getWindowTop') + stub.throws() + const res = spec.getWindowContext() + expect(res).to.equal(window.self) + utils.getWindowTop.restore() + }) }) + + describe('onTimeout', function () { + let xhr; + let requests; + + beforeEach(function() { + xhr = sinon.useFakeXMLHttpRequest(); + requests = []; + xhr.onCreate = (xhr) => { + requests.push(xhr); + }; + }) + + afterEach(function() { + xhr.restore() + }) + + it('should send on bid timeout notification', function() { + const bid = { + ad: 'cookies', + cpm: 3 + } + spec.onTimeout(bid); + expect(requests).to.not.be.undefined; + expect(requests.length).to.equal(1); + expect(requests[0].url).to.equal(TIMEOUT_URL); + expect(requests[0].method).to.equal('POST'); + }) + }); }); diff --git a/test/spec/modules/openxBidAdapter_spec.js b/test/spec/modules/openxBidAdapter_spec.js index 96e545d70fa..3bc53e30eb8 100644 --- a/test/spec/modules/openxBidAdapter_spec.js +++ b/test/spec/modules/openxBidAdapter_spec.js @@ -1085,7 +1085,7 @@ describe('OpenxAdapter', function () { intentIqId: '1111-intentiqid', lipb: {lipbid: '1111-lipb'}, lotamePanoramaId: '1111-lotameid', - merkleId: '1111-merkleid', + merkleId: {id: '1111-merkleid'}, netId: 'fH5A3n2O8_CZZyPoJVD-eabc6ECb7jhxCicsds7qSg', parrableId: { eid: 'eidVersion.encryptionKeyReference.encryptedValue' }, pubcid: '1111-pubcid', @@ -1102,6 +1102,11 @@ describe('OpenxAdapter', function () { mwOpenLinkId: '1111-mwopenlinkid', dapId: '1111-dapId', amxId: '1111-amxid', + kpuid: '1111-kpuid', + publinkId: '1111-publinkid', + naveggId: '1111-naveggid', + imuid: '1111-imuid', + adtelligentId: '1111-adtelligentid' }; // generates the same set of tests for each id provider @@ -1139,6 +1144,9 @@ describe('OpenxAdapter', function () { let userIdValue; // handle cases where userId key refers to an object switch (userIdProviderKey) { + case 'merkleId': + userIdValue = EXAMPLE_DATA_BY_ATTR.merkleId.id; + break; case 'flocId': userIdValue = EXAMPLE_DATA_BY_ATTR.flocId.id; break; @@ -1545,7 +1553,7 @@ describe('OpenxAdapter', function () { describe('with segments', function () { const TESTS = [ { - name: 'should send proprietary segment data from first party config', + name: 'should send proprietary segment data from ortb2.user.data', config: { ortb2: { user: { @@ -1556,10 +1564,51 @@ describe('OpenxAdapter', function () { } } }, - expect: 'dmp1/4:foo|bar,dmp2:baz', + expect: {sm: 'dmp1/4:foo|bar,dmp2:baz'}, }, { - name: 'should combine same provider segment data from first party config', + name: 'should send proprietary segment data from ortb2.site.content.data', + config: { + ortb2: { + site: { + content: { + data: [ + {name: 'dmp1', ext: {segtax: 4}, segment: [{id: 'foo'}, {id: 'bar'}]}, + {name: 'dmp2', segment: [{id: 'baz'}]}, + ] + } + } + } + }, + expect: {scsm: 'dmp1/4:foo|bar,dmp2:baz'}, + }, + { + name: 'should send proprietary segment data from both ortb2.site.content.data and ortb2.user.data', + config: { + ortb2: { + user: { + data: [ + {name: 'dmp1', ext: {segtax: 4}, segment: [{id: 'foo'}, {id: 'bar'}]}, + {name: 'dmp2', segment: [{id: 'baz'}]}, + ] + }, + site: { + content: { + data: [ + {name: 'dmp3', ext: {segtax: 5}, segment: [{id: 'foo2'}, {id: 'bar2'}]}, + {name: 'dmp4', segment: [{id: 'baz2'}]}, + ] + } + } + } + }, + expect: { + sm: 'dmp1/4:foo|bar,dmp2:baz', + scsm: 'dmp3/5:foo2|bar2,dmp4:baz2' + }, + }, + { + name: 'should combine same provider segment data from ortb2.user.data', config: { ortb2: { user: { @@ -1570,7 +1619,23 @@ describe('OpenxAdapter', function () { } } }, - expect: 'dmp1/4:foo|bar,dmp1:baz', + expect: {sm: 'dmp1/4:foo|bar,dmp1:baz'}, + }, + { + name: 'should combine same provider segment data from ortb2.site.content.data', + config: { + ortb2: { + site: { + content: { + data: [ + {name: 'dmp1', ext: {segtax: 4}, segment: [{id: 'foo'}, {id: 'bar'}]}, + {name: 'dmp1', ext: {}, segment: [{id: 'baz'}]}, + ] + } + } + } + }, + expect: {scsm: 'dmp1/4:foo|bar,dmp1:baz'}, }, { name: 'should not send any segment data if first party config is incomplete', @@ -1595,6 +1660,14 @@ describe('OpenxAdapter', function () { {name: 'dmp1', segment: [{id: 'foo'}, {id: 'bar'}]}, {name: 'dmp2', segment: [{id: 'baz'}]}, ] + }, + site: { + content: { + data: [ + {name: 'dmp3', ext: {segtax: 5}, segment: [{id: 'foo2'}, {id: 'bar2'}]}, + {name: 'dmp4', segment: [{id: 'baz2'}]}, + ] + } } } }, @@ -1606,7 +1679,10 @@ describe('OpenxAdapter', function () { }, }, }, - expect: 'dmp1:foo|bar,dmp2:baz,liveintent:l1|l2', + expect: { + sm: 'dmp1:foo|bar,dmp2:baz,liveintent:l1|l2', + scsm: 'dmp3/5:foo2|bar2,dmp4:baz2' + }, }, { name: 'should send just liveintent segment from request if no first party config', @@ -1619,7 +1695,7 @@ describe('OpenxAdapter', function () { }, }, }, - expect: 'liveintent:l1|l2', + expect: {sm: 'liveintent:l1|l2'}, }, { name: 'should send nothing if lipb section does not contain segments', @@ -1636,13 +1712,11 @@ describe('OpenxAdapter', function () { utils._each(TESTS, (t) => { context('in ortb2.user.data', function () { let bidRequests; - let configStub; - beforeEach(function () { let fpdConfig = t.config - configStub = sinon + sinon .stub(config, 'getConfig') - .withArgs('ortb2.user.data') + .withArgs(sinon.match(/^ortb2\.user\.data$|^ortb2\.site\.content\.data$/)) .callsFake((key) => { return utils.deepAccess(fpdConfig, key); }); @@ -1658,10 +1732,13 @@ describe('OpenxAdapter', function () { const request = spec.buildRequests(bidRequests, mockBidderRequest) expect(request.length).to.equal(1); if (t.expect) { - expect(request[0].data.sm).to.exist; - expect(request[0].data.sm).to.equal(encodeURIComponent(t.expect)); + for (const key in t.expect) { + expect(request[0].data[key]).to.exist; + expect(request[0].data[key]).to.equal(t.expect[key]); + } } else { expect(request[0].data.sm).to.not.exist; + expect(request[0].data.scsm).to.not.exist; } }); }); diff --git a/test/spec/modules/operaadsBidAdapter_spec.js b/test/spec/modules/operaadsBidAdapter_spec.js index c225835fcc2..849a3eada3f 100644 --- a/test/spec/modules/operaadsBidAdapter_spec.js +++ b/test/spec/modules/operaadsBidAdapter_spec.js @@ -369,7 +369,7 @@ describe('Opera Ads Bid Adapter', function () { requestData = JSON.parse(reqs[0].data); }).to.not.throw(); - expect(requestData.user.id).to.equal(bidRequests[0].userId.sharedid.id); + expect(requestData.user.buyeruid).to.equal(bidRequests[0].userId.sharedid.id); }); it('pubcid should be used when sharedid is empty', function () { @@ -406,7 +406,7 @@ describe('Opera Ads Bid Adapter', function () { requestData = JSON.parse(reqs[0].data); }).to.not.throw(); - expect(requestData.user.id).to.equal(bidRequests[0].userId.pubcid); + expect(requestData.user.buyeruid).to.equal(bidRequests[0].userId.pubcid); }); it('random uid will be generate when userId is empty', function () { @@ -433,7 +433,7 @@ describe('Opera Ads Bid Adapter', function () { requestData = JSON.parse(reqs[0].data); }).to.not.throw(); - expect(requestData.user.id).to.not.be.empty; + expect(requestData.user.buyeruid).to.not.be.empty; }) }); @@ -679,9 +679,41 @@ describe('Opera Ads Bid Adapter', function () { }); }); - describe('Test getUserSyncs', function () { - it('getUserSyncs should return empty array', function () { - expect(spec.getUserSyncs()).to.be.an('array').that.is.empty; + describe('Test getUserSyncs with both iframe and pixel disabled', function () { + it('getUserSyncs should return an empty array', function () { + const syncOptions = {}; + expect(spec.getUserSyncs(syncOptions)).to.be.an('array').that.is.empty; + }); + }); + + describe('Test getUserSyncs with iframe enabled', function () { + it('getUserSyncs should return array', function () { + const syncOptions = { + iframeEnabled: true + } + const userSyncPixels = spec.getUserSyncs(syncOptions) + expect(userSyncPixels).to.have.lengthOf(1); + expect(userSyncPixels[0].url).to.equal('https://s.adx.opera.com/usersync/page') + }); + }); + + describe('Test getUserSyncs with pixel enabled', function () { + it('getUserSyncs should return array', function () { + const serverResponse = { + body: { + 'pixels': [ + 'https://b1.com/usersync', + 'https://b2.com/usersync' + ] + } + }; + const syncOptions = { + pixelEnabled: true + } + const userSyncPixels = spec.getUserSyncs(syncOptions, [serverResponse]) + expect(userSyncPixels).to.have.lengthOf(2); + expect(userSyncPixels[0].url).to.equal('https://b1.com/usersync') + expect(userSyncPixels[1].url).to.equal('https://b2.com/usersync') }); }); diff --git a/test/spec/modules/optimeraRtdProvider_spec.js b/test/spec/modules/optimeraRtdProvider_spec.js index 8b1866d044a..7883412ab70 100644 --- a/test/spec/modules/optimeraRtdProvider_spec.js +++ b/test/spec/modules/optimeraRtdProvider_spec.js @@ -35,17 +35,30 @@ describe('Optimera RTD score file properly sets targeting values', () => { 'de': { 'div-0': ['A5', 'A6'], 'div-1': ['A7', 'A8'], + 'insights': { + 'ilv': ['div-0'], + 'miv': ['div-4'], + } }, 'mo': { 'div-0': ['A9', 'B0'], 'div-1': ['B1', 'B2'], + 'insights': { + 'ilv': ['div-1'], + 'miv': ['div-2'], + } } + }, + 'insights': { + 'ilv': ['div-5'], + 'miv': ['div-6'], } }; - it('Properly set the score file url', () => { + it('Properly set the score file url and scores', () => { optimeraRTD.setScores(JSON.stringify(scores)); expect(optimeraRTD.optimeraTargeting['div-0']).to.include.ordered.members(['A5', 'A6']); expect(optimeraRTD.optimeraTargeting['div-1']).to.include.ordered.members(['A7', 'A8']); + expect(window.optimeraInsights.data['ilv']).to.include.ordered.members(['div-0']); }); }); diff --git a/test/spec/modules/otmBidAdapter_spec.js b/test/spec/modules/otmBidAdapter_spec.js new file mode 100644 index 00000000000..6eb5768c3af --- /dev/null +++ b/test/spec/modules/otmBidAdapter_spec.js @@ -0,0 +1,67 @@ +import {expect} from 'chai'; +import {spec} from 'modules/otmBidAdapter'; + +describe('otmBidAdapter', function () { + it('validate_pub_params', function () { + expect(spec.isBidRequestValid({ + bidder: 'otm', + params: { + tid: '123', + bidfloor: 20 + } + })).to.equal(true); + }); + + it('validate_generated_params', function () { + let bidRequestData = [{ + bidId: 'bid1234', + bidder: 'otm', + params: { + tid: '123', + bidfloor: 20 + }, + sizes: [[240, 400]] + }]; + + let request = spec.buildRequests(bidRequestData); + let req_data = request[0].data; + + expect(req_data.bidid).to.equal('bid1234'); + }); + + it('validate_response_params', function () { + let bidRequestData = { + data: { + bidId: 'bid1234' + } + }; + + let serverResponse = { + body: [ + { + 'auctionid': '3c6f8e22-541b-485c-9214-e974d9fb1b6f', + 'cpm': 847.097, + 'ad': 'test html', + 'w': 240, + 'h': 400, + 'currency': 'RUB', + 'ttl': 300, + 'creativeid': '1_7869053', + 'bidid': '101f211def7c99', + 'transactionid': 'transaction_id_1' + } + ] + }; + + let bids = spec.interpretResponse(serverResponse, bidRequestData); + expect(bids).to.have.lengthOf(1); + let bid = bids[0]; + expect(bid.cpm).to.equal(847.097); + expect(bid.currency).to.equal('RUB'); + expect(bid.width).to.equal(240); + expect(bid.height).to.equal(400); + expect(bid.netRevenue).to.equal(true); + expect(bid.requestId).to.equal('101f211def7c99'); + expect(bid.ad).to.equal('test html'); + }); +}); diff --git a/test/spec/modules/outbrainBidAdapter_spec.js b/test/spec/modules/outbrainBidAdapter_spec.js index a5f23240a7c..4bc163aefe6 100644 --- a/test/spec/modules/outbrainBidAdapter_spec.js +++ b/test/spec/modules/outbrainBidAdapter_spec.js @@ -2,6 +2,7 @@ import {expect} from 'chai'; import {spec} from 'modules/outbrainBidAdapter.js'; import {config} from 'src/config.js'; import {server} from 'test/mocks/xhr'; +import { createEidsArray } from 'modules/userId/eids.js'; describe('Outbrain Adapter', function () { describe('Bid request and response', function () { @@ -344,6 +345,23 @@ describe('Outbrain Adapter', function () { config.resetConfig() }); + + it('should pass extended ids', function () { + let bidRequest = { + bidId: 'bidId', + params: {}, + userIdAsEids: createEidsArray({ + idl_env: 'id-value', + }), + ...commonBidRequest, + }; + + let res = spec.buildRequests([bidRequest], commonBidderRequest); + const resData = JSON.parse(res.data) + expect(resData.user.ext.eids).to.deep.equal([ + {source: 'liveramp.com', uids: [{id: 'id-value', atype: 3}]} + ]); + }); }) describe('interpretResponse', function () { diff --git a/test/spec/modules/ozoneBidAdapter_spec.js b/test/spec/modules/ozoneBidAdapter_spec.js index f9941b41189..658af310ea5 100644 --- a/test/spec/modules/ozoneBidAdapter_spec.js +++ b/test/spec/modules/ozoneBidAdapter_spec.js @@ -69,8 +69,6 @@ var validBidRequestsMulti = [ transactionId: '2e63c0ed-b10c-4008-aed5-84582cecfe87' } ]; -// use 'pubcid', 'tdid', 'id5id', 'parrableId', 'idl_env', 'criteoId' -// see http://prebid.org/dev-docs/modules/userId.html var validBidRequestsWithUserIdData = [ { adUnitCode: 'div-gpt-ad-1460505748561-0', @@ -291,7 +289,6 @@ var validBidRequests1OutstreamVideo2020 = [ } ]; -// WHEN sent as bidderRequest to buildRequests you should send the child: .bidderRequest var validBidderRequest1OutstreamVideo2020 = { bidderRequest: { auctionId: '27dcb421-95c6-4024-a624-3c03816c5f99', @@ -394,7 +391,6 @@ var validBidderRequest1OutstreamVideo2020 = { timeout: 3000 } }; -// WHEN sent as bidderRequest to buildRequests you should send the child: .bidderRequest var validBidderRequest = { bidderRequest: { auctionId: '27dcb421-95c6-4024-a624-3c03816c5f99', @@ -419,11 +415,6 @@ var validBidderRequest = { } }; -// bidder request with GDPR - change the values for testing: -// gdprConsent.gdprApplies (true/false) -// gdprConsent.vendorData.purposeConsents (make empty, make null, remove it) -// gdprConsent.vendorData.vendorConsents (remove 524, remove all, make the element null, remove it) -// WHEN sent as bidderRequest to buildRequests you should send the child: .bidderRequest var bidderRequestWithFullGdpr = { bidderRequest: { auctionId: '27dcb421-95c6-4024-a624-3c03816c5f99', @@ -512,7 +503,6 @@ var gdpr1 = { 'gdprApplies': true }; -// simulating the Mirror var bidderRequestWithPartialGdpr = { bidderRequest: { auctionId: '27dcb421-95c6-4024-a624-3c03816c5f99', @@ -558,7 +548,6 @@ var bidderRequestWithPartialGdpr = { } }; -// make sure the impid matches the request bidId var validResponse = { 'body': { 'id': 'd6198807-7a53-4141-b2db-d2cb754d68ba', @@ -1113,7 +1102,6 @@ var multiRequest1 = [ } ]; -// WHEN sent as bidderRequest to buildRequests you should send the child: .bidderRequest var multiBidderRequest1 = { bidderRequest: { 'bidderCode': 'ozone', @@ -1507,7 +1495,6 @@ var multiResponse1 = { describe('ozone Adapter', function () { describe('isBidRequestValid', function () { - // A test ad unit that will consistently return test creatives let validBidReq = { bidder: BIDDER_CODE, params: { @@ -1941,7 +1928,6 @@ describe('ozone Adapter', function () { const request = spec.buildRequests(validBidRequestsNoSizes, validBidderRequest.bidderRequest); expect(request).to.be.a('array'); expect(request[0]).to.have.all.keys(['bidderRequest', 'data', 'method', 'url']); - // need to reset the singleRequest config flag: config.setConfig({'ozone': {'singleRequest': true}}); }); @@ -1965,7 +1951,6 @@ describe('ozone Adapter', function () { expect(payload.user.ext.consent).to.equal(consentString); }); - // mirror it('should add gdpr consent information to the request when vendorData is missing vendorConsents (Mirror)', function () { let consentString = 'BOcocyaOcocyaAfEYDENCD-AAAAjx7_______9______9uz_Ov_v_f__33e8__9v_l_7_-___u_-33d4-_1vf99yfm1-7ftr3tp_87ues2_Xur__59__3z3_NphLgA=='; let bidderRequest = validBidderRequest.bidderRequest; @@ -2018,7 +2003,6 @@ describe('ozone Adapter', function () { }; let bidRequests = validBidRequests; - // values from http://prebid.org/dev-docs/modules/userId.html#pubcommon-id bidRequests[0]['userId'] = { 'digitrustid': {data: {id: 'DTID', keyv: 4, privacy: {optout: false}, producer: 'ABC', version: 2}}, 'id5id': { uid: '1111', ext: { linkType: 2, abTestingControlGroup: false } }, @@ -2038,13 +2022,11 @@ describe('ozone Adapter', function () { it('should pick up the value of pubcid when built using the pubCommonId module (not userId)', function () { let bidRequests = validBidRequests; - // values from http://prebid.org/dev-docs/modules/userId.html#pubcommon-id bidRequests[0]['userId'] = { 'digitrustid': {data: {id: 'DTID', keyv: 4, privacy: {optout: false}, producer: 'ABC', version: 2}}, 'id5id': { uid: '1111', ext: { linkType: 2, abTestingControlGroup: false } }, 'idl_env': '3333', 'parrableid': 'eidVersion.encryptionKeyReference.encryptedValue', - // 'pubcid': '5555', // remove pubcid from here to emulate the OLD module & cause the failover code to kick in 'tdid': '6666', 'sharedid': {'id': '01EAJWWNEPN3CYMM5N8M5VXY22', 'third': '01EAJWWNEPN3CYMM5N8M5VXY22'} }; @@ -2170,7 +2152,6 @@ describe('ozone Adapter', function () { }); it('should use oztestmode GET value if set', function() { var specMock = utils.deepClone(spec); - // mock the getGetParametersAsObject function to simulate GET parameters for oztestmode: specMock.getGetParametersAsObject = function() { return {'oztestmode': 'mytestvalue_123'}; }; @@ -2181,7 +2162,6 @@ describe('ozone Adapter', function () { }); it('should pass through GET params if present: ozf, ozpf, ozrp, ozip', function() { var specMock = utils.deepClone(spec); - // mock the getGetParametersAsObject function to simulate GET parameters : specMock.getGetParametersAsObject = function() { return {ozf: '1', ozpf: '0', ozrp: '2', ozip: '123'}; }; @@ -2194,7 +2174,6 @@ describe('ozone Adapter', function () { }); it('should pass through GET params if present: ozf, ozpf, ozrp, ozip with alternative values', function() { var specMock = utils.deepClone(spec); - // mock the getGetParametersAsObject function to simulate GET parameters : specMock.getGetParametersAsObject = function() { return {ozf: 'false', ozpf: 'true', ozrp: 'xyz', ozip: 'hello'}; }; @@ -2207,7 +2186,6 @@ describe('ozone Adapter', function () { }); it('should use oztestmode GET value if set, even if there is no customdata in config', function() { var specMock = utils.deepClone(spec); - // mock the getGetParametersAsObject function to simulate GET parameters for oztestmode: specMock.getGetParametersAsObject = function() { return {'oztestmode': 'mytestvalue_123'}; }; @@ -2217,7 +2195,6 @@ describe('ozone Adapter', function () { expect(data.imp[0].ext.ozone.customData[0].targeting.oztestmode).to.equal('mytestvalue_123'); }); it('should use GET values auction=dev & cookiesync=dev if set', function() { - // mock the getGetParametersAsObject function to simulate GET parameters for oztestmode: var specMock = utils.deepClone(spec); specMock.getGetParametersAsObject = function() { return {}; @@ -2228,8 +2205,6 @@ describe('ozone Adapter', function () { let cookieUrl = specMock.getCookieSyncUrl(); expect(cookieUrl).to.equal('https://elb.the-ozone-project.com/static/load-cookie.html'); - // now mock the response from getGetParametersAsObject & do the request again - specMock = utils.deepClone(spec); specMock.getGetParametersAsObject = function() { return {'auction': 'dev', 'cookiesync': 'dev'}; @@ -2241,7 +2216,6 @@ describe('ozone Adapter', function () { expect(cookieUrl).to.equal('https://test.ozpr.net/static/load-cookie.html'); }); it('should use a valid ozstoredrequest GET value if set to override the placementId values, and set oz_rw if we find it', function() { - // mock the getGetParametersAsObject function to simulate GET parameters for ozstoredrequest: var specMock = utils.deepClone(spec); specMock.getGetParametersAsObject = function() { return {'ozstoredrequest': '1122334455'}; // 10 digits are valid @@ -2252,7 +2226,6 @@ describe('ozone Adapter', function () { expect(data.imp[0].ext.prebid.storedrequest.id).to.equal('1122334455'); }); it('should NOT use an invalid ozstoredrequest GET value if set to override the placementId values, and set oz_rw to 0', function() { - // mock the getGetParametersAsObject function to simulate GET parameters for ozstoredrequest: var specMock = utils.deepClone(spec); specMock.getGetParametersAsObject = function() { return {'ozstoredrequest': 'BADVAL'}; // 10 digits are valid @@ -2424,6 +2397,26 @@ describe('ozone Adapter', function () { expect(utils.deepAccess(payload, 'imp.0.floor.banner.floor')).to.equal(0.8); config.resetConfig(); }); + + it('handles schain object in each bidrequest (will be the same in each br)', function () { + let br = JSON.parse(JSON.stringify(validBidRequests)); + let schainConfigObject = { + 'ver': '1.0', + 'complete': 1, + 'nodes': [ + { + 'asi': 'bidderA.com', + 'sid': '00001', + 'hp': 1 + } + ] + }; + br[0]['schain'] = schainConfigObject; + const request = spec.buildRequests(br, validBidderRequest.bidderRequest); + const data = JSON.parse(request.data); + expect(data.source.ext).to.haveOwnProperty('schain'); + expect(data.source.ext.schain).to.deep.equal(schainConfigObject); // .deep.equal() : Target object deeply (but not strictly) equals `{a: 1}` + }); }); describe('interpretResponse', function () { @@ -2624,7 +2617,6 @@ describe('ozone Adapter', function () { expect(result[1]['impid']).to.equal('3025f169863b7f8'); expect(result[1]['id']).to.equal('18552976939844999'); expect(result[1]['adserverTargeting']['oz_ozappnexus_adId']).to.equal('3025f169863b7f8-0-oz-2'); - // change the bid values so a different second bid for an impid by the same bidder gets dropped validres = JSON.parse(JSON.stringify(multiResponse1)); validres.body.seatbid[0].bid[1].price = 1.1; validres.body.seatbid[0].bid[1].cpm = 1.1; @@ -2647,7 +2639,6 @@ describe('ozone Adapter', function () { expect(result).to.be.empty; }); it('should append the various values if they exist', function() { - // get the cookie bag populated spec.buildRequests(validBidRequests, validBidderRequest.bidderRequest); const result = spec.getUserSyncs({iframeEnabled: true}, 'good server response', gdpr1); expect(result).to.be.an('array'); @@ -2657,14 +2648,12 @@ describe('ozone Adapter', function () { expect(result[0].url).to.include('gdpr_consent=BOh7mtYOh7mtYAcABBENCU-AAAAncgPIXJiiAoao0PxBFkgCAC8ACIAAQAQQAAIAAAIAAAhBGAAAQAQAEQgAAAAAAABAAAAAAAAAAAAAAACAAAAAAAACgAAAAABAAAAQAAAAAAA'); }); it('should append ccpa (usp data)', function() { - // get the cookie bag populated spec.buildRequests(validBidRequests, validBidderRequest.bidderRequest); const result = spec.getUserSyncs({iframeEnabled: true}, 'good server response', gdpr1, '1YYN'); expect(result).to.be.an('array'); expect(result[0].url).to.include('usp_consent=1YYN'); }); it('should use "" if no usp is sent to cookieSync', function() { - // get the cookie bag populated spec.buildRequests(validBidRequests, validBidderRequest.bidderRequest); const result = spec.getUserSyncs({iframeEnabled: true}, 'good server response', gdpr1); expect(result).to.be.an('array'); @@ -2887,4 +2876,27 @@ describe('ozone Adapter', function () { expect(response[1].bid.length).to.equal(2); }); }); + /** + * spec.getWhitelabelConfigItem test - get a config value for a whitelabelled bidder, + * from a standard ozone.oz_xxxx_yyy string + */ + describe('getWhitelabelConfigItem', function() { + it('should fetch the whitelabelled equivalent config value correctly', function () { + var specMock = utils.deepClone(spec); + config.setConfig({'ozone': {'oz_omp_floor': 'ozone-floor-value'}}); + config.setConfig({'markbidder': {'mb_omp_floor': 'markbidder-floor-value'}}); + specMock.propertyBag.whitelabel = {bidder: 'ozone', keyPrefix: 'oz'}; + let testKey = 'ozone.oz_omp_floor'; + let ozone_value = specMock.getWhitelabelConfigItem(testKey); + expect(ozone_value).to.equal('ozone-floor-value'); + specMock.propertyBag.whitelabel = {bidder: 'markbidder', keyPrefix: 'mb'}; + let markbidder_config = specMock.getWhitelabelConfigItem(testKey); + expect(markbidder_config).to.equal('markbidder-floor-value'); + config.setConfig({'markbidder': {'singleRequest': 'markbidder-singlerequest-value'}}); + let testKey2 = 'ozone.singleRequest'; + let markbidder_config2 = specMock.getWhitelabelConfigItem(testKey2); + expect(markbidder_config2).to.equal('markbidder-singlerequest-value'); + config.resetConfig(); + }); + }); }); diff --git a/test/spec/modules/pilotxBidAdapter_spec.js b/test/spec/modules/pilotxBidAdapter_spec.js new file mode 100644 index 00000000000..2ef31c0a8f5 --- /dev/null +++ b/test/spec/modules/pilotxBidAdapter_spec.js @@ -0,0 +1,244 @@ +// import or require modules necessary for the test, e.g.: +import { expect } from 'chai'; // may prefer 'assert' in place of 'expect' +import { spec } from '../../../modules/pilotxBidAdapter.js'; + +describe('pilotxAdapter', function () { + describe('isBidRequestValid', function () { + let banner; + beforeEach(function () { + banner = { + bidder: 'pilotx', + adUnitCode: 'adunit-test', + mediaTypes: { banner: {} }, + sizes: [[300, 250], [468, 60]], + bidId: '2de8c82e30665a', + params: { + placementId: '1' + } + }; + }); + + it('should return false if sizes is empty', function () { + banner.sizes = [] + expect(spec.isBidRequestValid(banner)).to.equal(false); + }); + it('should return true if all is valid/ is not empty', function () { + expect(spec.isBidRequestValid(banner)).to.equal(true); + }); + it('should return false if there is no placement id found', function () { + banner.params = {} + expect(spec.isBidRequestValid(banner)).to.equal(false); + }); + it('should return false if sizes is empty', function () { + banner.sizes = [] + expect(spec.isBidRequestValid(banner)).to.equal(false); + }); + it('should return false for no size and empty params', function() { + const emptySizes = { + bidder: 'pilotx', + adUnitCode: 'adunit-test', + mediaTypes: { banner: {} }, + bidId: '2de8c82e30665a', + params: { + placementId: '1', + sizes: [] + } + }; + expect(spec.isBidRequestValid(emptySizes)).to.equal(false); + }) + it('should return true for no size and valid size params', function() { + const emptySizes = { + bidder: 'pilotx', + adUnitCode: 'adunit-test', + mediaTypes: { banner: {} }, + bidId: '2de8c82e30665a', + params: { + placementId: '1', + sizes: [[300, 250], [468, 60]] + } + }; + expect(spec.isBidRequestValid(emptySizes)).to.equal(true); + }) + it('should return false for no size items', function() { + const emptySizes = { + bidder: 'pilotx', + adUnitCode: 'adunit-test', + mediaTypes: { banner: {} }, + bidId: '2de8c82e30665a', + params: { + placementId: '1' + } + }; + expect(spec.isBidRequestValid(emptySizes)).to.equal(false); + }) + }); + + describe('buildRequests', function () { + const mockRequest = { refererInfo: {} }; + const mockRequestGDPR = { + refererInfo: {}, + gdprConsent: { + consentString: 'BOJ/P2HOJ/P2HABABMAAAAAZ+A==', + gdprApplies: true + } + + } + const mockVideo1 = [{ + adUnitCode: 'video1', + auctionId: '01618029-7ae9-4e98-a73a-1ed0c817f414', + bidId: '2a59588c0114fa', + bidRequestsCount: 1, + bidder: 'pilotx', + bidderRequestId: '1f6b4ba2039726', + bidderRequestsCount: 1, + bidderWinsCount: 0, + crumbs: { pubcid: 'de5240ef-ff80-4b55-8837-26a11cfbf64c' }, + mediaTypes: { + video: { + context: 'instream', + mimes: ['video/mp4'], + playbackmethod: [2], + playerSize: [[640, 480]], + protocols: [1, 2, 3, 4, 5, 6, 7, 8], + skip: 1 + } + }, + ortb2Imp: { + ext: { + data: { + pbadslot: 'video1' + } + } + }, + params: { placementId: '379' }, + sizes: [[640, 480]], + src: 'client', + transactionId: 'fec9f2ff-da13-4921-8437-8d679c2be7fe', + }]; + const mockVideo2 = [{ + adUnitCode: 'video1', + auctionId: '01618029-7ae9-4e98-a73a-1ed0c817f414', + bidId: '2a59588c0114fa', + bidRequestsCount: 1, + bidder: 'pilotx', + bidderRequestId: '1f6b4ba2039726', + bidderRequestsCount: 1, + bidderWinsCount: 0, + crumbs: { pubcid: 'de5240ef-ff80-4b55-8837-26a11cfbf64c' }, + mediaTypes: { + video: { + context: 'instream', + mimes: ['video/mp4'], + playbackmethod: [2], + playerSize: [[640, 480]], + protocols: [1, 2, 3, 4, 5, 6, 7, 8], + skip: 1 + } + }, + ortb2Imp: { + ext: { + data: { + pbadslot: 'video1' + } + } + }, + params: { placementId: '379' }, + sizes: [640, 480], + src: 'client', + transactionId: 'fec9f2ff-da13-4921-8437-8d679c2be7fe', + }]; + it('should return correct response', function () { + const builtRequest = spec.buildRequests(mockVideo1, mockRequest) + let builtRequestData = builtRequest.data + let data = JSON.parse(builtRequestData) + expect(data['379'].bidId).to.equal(mockVideo1[0].bidId) + }); + it('should return correct response for only array of size', function () { + const builtRequest = spec.buildRequests(mockVideo2, mockRequest) + let builtRequestData = builtRequest.data + let data = JSON.parse(builtRequestData) + expect(data['379'].sizes[0][0]).to.equal(mockVideo2[0].sizes[0]) + expect(data['379'].sizes[0][1]).to.equal(mockVideo2[0].sizes[1]) + }); + it('should be valid and pass gdpr items correctly', function () { + const builtRequest = spec.buildRequests(mockVideo2, mockRequestGDPR) + let builtRequestData = builtRequest.data + let data = JSON.parse(builtRequestData) + expect(data['379'].gdprConsentString).to.equal(mockRequestGDPR.gdprConsent.consentString) + expect(data['379'].gdprConsentRequired).to.equal(mockRequestGDPR.gdprConsent.gdprApplies) + }); + }); + describe('interpretResponse', function () { + const bidRequest = {} + const serverResponse = { + cpm: 2.5, + creativeId: 'V9060', + currency: 'US', + height: 480, + mediaType: 'video', + netRevenue: false, + requestId: '273b39c74069cb', + ttl: 3000, + vastUrl: 'http://testadserver.com/ads?&k=60cd901ad8ab70c9cedf373cb17b93b8&pid=379&tid=91342717', + width: 640 + } + const serverResponseVideo = { + body: serverResponse + } + const serverResponse2 = { + cpm: 2.5, + creativeId: 'V9060', + currency: 'US', + height: 480, + mediaType: 'banner', + netRevenue: false, + requestId: '273b39c74069cb', + ttl: 3000, + vastUrl: 'http://testadserver.com/ads?&k=60cd901ad8ab70c9cedf373cb17b93b8&pid=379&tid=91342717', + width: 640 + } + const serverResponseBanner = { + body: serverResponse2 + } + it('should be valid from bidRequest for video', function () { + const bidResponses = spec.interpretResponse(serverResponseVideo, bidRequest) + expect(bidResponses[0].requestId).to.equal(serverResponse.requestId) + expect(bidResponses[0].cpm).to.equal(serverResponse.cpm) + expect(bidResponses[0].width).to.equal(serverResponse.width) + expect(bidResponses[0].height).to.equal(serverResponse.height) + expect(bidResponses[0].creativeId).to.equal(serverResponse.creativeId) + expect(bidResponses[0].currency).to.equal(serverResponse.currency) + expect(bidResponses[0].netRevenue).to.equal(serverResponse.netRevenue) + expect(bidResponses[0].ttl).to.equal(serverResponse.ttl) + expect(bidResponses[0].vastUrl).to.equal(serverResponse.vastUrl) + expect(bidResponses[0].mediaType).to.equal(serverResponse.mediaType) + expect(bidResponses[0].meta.mediaType).to.equal(serverResponse.mediaType) + }); + it('should be valid from bidRequest for banner', function () { + const bidResponses = spec.interpretResponse(serverResponseBanner, bidRequest) + expect(bidResponses[0].requestId).to.equal(serverResponse2.requestId) + expect(bidResponses[0].cpm).to.equal(serverResponse2.cpm) + expect(bidResponses[0].width).to.equal(serverResponse2.width) + expect(bidResponses[0].height).to.equal(serverResponse2.height) + expect(bidResponses[0].creativeId).to.equal(serverResponse2.creativeId) + expect(bidResponses[0].currency).to.equal(serverResponse2.currency) + expect(bidResponses[0].netRevenue).to.equal(serverResponse2.netRevenue) + expect(bidResponses[0].ttl).to.equal(serverResponse2.ttl) + expect(bidResponses[0].ad).to.equal(serverResponse2.ad) + expect(bidResponses[0].mediaType).to.equal(serverResponse2.mediaType) + expect(bidResponses[0].meta.mediaType).to.equal(serverResponse2.mediaType) + }); + }); + describe('setPlacementID', function () { + const multiplePlacementIds = ['380', '381'] + it('should be valid with an array of placement ids passed', function () { + const placementID = spec.setPlacementID(multiplePlacementIds) + expect(placementID).to.equal('380#381') + }); + it('should be valid with single placement ID passed', function () { + const placementID = spec.setPlacementID('381') + expect(placementID).to.equal('381') + }); + }); + // Add other `describe` or `it` blocks as necessary +}); diff --git a/test/spec/modules/prebidServerBidAdapter_spec.js b/test/spec/modules/prebidServerBidAdapter_spec.js index a06d4dbcd68..73a531b30bf 100644 --- a/test/spec/modules/prebidServerBidAdapter_spec.js +++ b/test/spec/modules/prebidServerBidAdapter_spec.js @@ -8,6 +8,7 @@ import events from 'src/events.js'; import CONSTANTS from 'src/constants.json'; import { server } from 'test/mocks/xhr.js'; import { createEidsArray } from 'modules/userId/eids.js'; +import {deepAccess, deepClone} from 'src/utils.js'; let CONFIG = { accountId: '1', @@ -507,6 +508,17 @@ describe('S2S Adapter', function () { resetSyncedStatus(); }); + it('should set id to auction ID and source.tid to tid', function () { + config.setConfig({ s2sConfig: CONFIG }); + + adapter.callBids(OUTSTREAM_VIDEO_REQUEST, BID_REQUESTS, addBidResponse, done, ajax); + + const requestBid = JSON.parse(server.requests[0].requestBody); + expect(requestBid.id).to.equal('173afb6d132ba3'); + expect(requestBid.source).to.be.an('object'); + expect(requestBid.source.tid).to.equal('437fbbf5-33f5-487a-8e16-a7112903cfe5'); + }); + it('should block request if config did not define p1Consent URL in endpoint object config', function() { let badConfig = utils.deepClone(CONFIG); badConfig.endpoint = { noP1Consent: 'https://prebid.adnxs.com/pbs/v1/openrtb2/auction' }; @@ -1081,6 +1093,18 @@ describe('S2S Adapter', function () { }); }); + it('should not include ext.aspectratios if adunit\'s aspect_ratios do not define radio_width and ratio_height', () => { + const req = deepClone(REQUEST); + req.ad_units[0].mediaTypes.native.icon.aspect_ratios[0] = {'min_width': 1, 'min_height': 2}; + adapter.callBids(req, BID_REQUESTS, addBidResponse, done, ajax); + const nativeReq = JSON.parse(JSON.parse(server.requests[0].requestBody).imp[0].native.request); + const icons = nativeReq.assets.map((a) => a.img).filter((img) => img && img.type === 1); + expect(icons).to.have.length(1); + expect(icons[0].hmin).to.equal(2); + expect(icons[0].wmin).to.equal(1); + expect(deepAccess(icons[0], 'ext.aspectratios')).to.be.undefined; + }) + it('adds site if app is not present', function () { const _config = { s2sConfig: CONFIG, @@ -1705,7 +1729,7 @@ describe('S2S Adapter', function () { expect(parsedRequestBody.ext.prebid.channel).to.deep.equal({name: 'pbjs', version: 'v$prebid.version$'}); }); - it('does not set pbjs version in request if channel does exist in s2sConfig', () => { + it('extPrebid is now mergedDeep -> should include default channel as well', () => { const s2sBidRequest = utils.deepClone(REQUEST); const bidRequests = utils.deepClone(BID_REQUESTS); @@ -1714,7 +1738,13 @@ describe('S2S Adapter', function () { adapter.callBids(s2sBidRequest, bidRequests, addBidResponse, done, ajax); const parsedRequestBody = JSON.parse(server.requests[0].requestBody); - expect(parsedRequestBody.ext.prebid.channel).to.deep.equal({test: 1}); + + // extPrebid is now deep merged with + expect(parsedRequestBody.ext.prebid.channel).to.deep.equal({ + name: 'pbjs', + test: 1, + version: 'v$prebid.version$' + }); }); it('passes first party data in request', () => { @@ -2403,6 +2433,36 @@ describe('S2S Adapter', function () { utils.getBidRequest.restore(); }); + + describe('on sync requested with no cookie', () => { + let cfg, req, csRes; + + beforeEach(() => { + cfg = utils.deepClone(CONFIG); + req = utils.deepClone(REQUEST); + cfg.syncEndpoint = { p1Consent: 'https://prebid.adnxs.com/pbs/v1/cookie_sync' }; + req.s2sConfig = cfg; + config.setConfig({ s2sConfig: cfg }); + csRes = utils.deepClone(RESPONSE_NO_COOKIE); + }); + + afterEach(() => { + resetSyncedStatus(); + }) + + Object.entries({ + iframe: () => utils.insertUserSyncIframe, + image: () => utils.triggerPixel, + }).forEach(([type, syncer]) => { + it(`passes timeout to ${type} syncs`, () => { + cfg.syncTimeout = 123; + csRes.bidder_status[0].usersync.type = type; + adapter.callBids(req, BID_REQUESTS, addBidResponse, done, ajax); + server.requests[0].respond(200, {}, JSON.stringify(csRes)); + expect(syncer().args[0]).to.include.members([123]); + }); + }); + }); }); describe('bid won events', function () { diff --git a/test/spec/modules/pubgeniusBidAdapter_spec.js b/test/spec/modules/pubgeniusBidAdapter_spec.js index 6568f7aa782..4599eb2a6fa 100644 --- a/test/spec/modules/pubgeniusBidAdapter_spec.js +++ b/test/spec/modules/pubgeniusBidAdapter_spec.js @@ -173,7 +173,7 @@ describe('pubGENIUS adapter', () => { expectedRequest = { method: 'POST', - url: 'https://ortb.adpearl.io/prebid/auction', + url: 'https://auction.adpearl.io/prebid/auction', data: { id: 'fake-auction-id', imp: [ @@ -493,7 +493,7 @@ describe('pubGENIUS adapter', () => { }; expectedSync = { type: 'iframe', - url: 'https://ortb.adpearl.io/usersync/pixels.html?', + url: 'https://auction.adpearl.io/usersync/pixels.html?', }; }); @@ -551,7 +551,7 @@ describe('pubGENIUS adapter', () => { onTimeout(timeoutData); expect(server.requests[0].method).to.equal('POST'); - expect(server.requests[0].url).to.equal('https://ortb.adpearl.io/prebid/events?type=timeout'); + expect(server.requests[0].url).to.equal('https://auction.adpearl.io/prebid/events?type=timeout'); expect(JSON.parse(server.requests[0].requestBody)).to.deep.equal(timeoutData); }); }); diff --git a/test/spec/modules/publinkIdSystem_spec.js b/test/spec/modules/publinkIdSystem_spec.js index aa6b669d773..cfb5f8ed135 100644 --- a/test/spec/modules/publinkIdSystem_spec.js +++ b/test/spec/modules/publinkIdSystem_spec.js @@ -47,12 +47,6 @@ describe('PublinkIdSystem', () => { expect(result.id).to.equal(LOCAL_VALUE.publink); storage.removeDataFromLocalStorage(PUBLINK_COOKIE); }); - it('ignore expired cookie', () => { - storage.setDataInLocalStorage(PUBLINK_COOKIE, JSON.stringify({publink: 'value', exp: Date.now() - 60 * 60 * 24 * 1000})); - const result = publinkIdSubmodule.getId(); - expect(result.id).to.be.undefined; - storage.removeDataFromLocalStorage(PUBLINK_COOKIE); - }); it('priority goes to publink_srv cookie', () => { storage.setCookie(PUBLINK_SRV_COOKIE, JSON.stringify(COOKIE_VALUE), COOKIE_EXPIRATION); storage.setDataInLocalStorage(PUBLINK_COOKIE, JSON.stringify(LOCAL_VALUE)); @@ -61,6 +55,12 @@ describe('PublinkIdSystem', () => { storage.setCookie(PUBLINK_SRV_COOKIE, '', DELETE_COOKIE); storage.removeDataFromLocalStorage(PUBLINK_COOKIE); }); + it('publink non-json cookie', () => { + storage.setCookie(PUBLINK_COOKIE, COOKIE_VALUE.publink, COOKIE_EXPIRATION); + const result = publinkIdSubmodule.getId(); + expect(result.id).to.equal(COOKIE_VALUE.publink); + storage.setCookie(PUBLINK_COOKIE, '', DELETE_COOKIE); + }); }); describe('getId', () => { diff --git a/test/spec/modules/pubmaticAnalyticsAdapter_spec.js b/test/spec/modules/pubmaticAnalyticsAdapter_spec.js index c6496ee7fe1..b6268548ccc 100755 --- a/test/spec/modules/pubmaticAnalyticsAdapter_spec.js +++ b/test/spec/modules/pubmaticAnalyticsAdapter_spec.js @@ -95,6 +95,9 @@ const BID2 = Object.assign({}, BID, { 'hb_pb': '1.500', 'hb_size': '728x90', 'hb_source': 'server' + }, + meta: { + advertiserDomains: ['example.com'] } }); @@ -382,6 +385,7 @@ describe('pubmatic analytics adapter', function () { expect(data.s[1].ps[0].di).to.equal('the-deal-id'); expect(data.s[1].ps[0].dc).to.equal('PMP'); expect(data.s[1].ps[0].mi).to.equal('matched-impression'); + expect(data.s[1].ps[0].adv).to.equal('example.com'); expect(data.s[1].ps[0].l1).to.equal(3214); expect(data.s[1].ps[0].l2).to.equal(0); expect(data.s[1].ps[0].ss).to.equal(1); @@ -651,6 +655,7 @@ describe('pubmatic analytics adapter', function () { expect(data.s[1].ps[0].di).to.equal('the-deal-id'); expect(data.s[1].ps[0].dc).to.equal('PMP'); expect(data.s[1].ps[0].mi).to.equal('matched-impression'); + expect(data.s[1].ps[0].adv).to.equal('example.com'); expect(data.s[1].ps[0].l1).to.equal(3214); expect(data.s[1].ps[0].l2).to.equal(0); expect(data.s[1].ps[0].ss).to.equal(1); @@ -708,6 +713,7 @@ describe('pubmatic analytics adapter', function () { expect(data.s[1].ps[0].di).to.equal('the-deal-id'); expect(data.s[1].ps[0].dc).to.equal('PMP'); expect(data.s[1].ps[0].mi).to.equal('matched-impression'); + expect(data.s[1].ps[0].adv).to.equal('example.com'); expect(data.s[1].ps[0].l1).to.equal(3214); expect(data.s[1].ps[0].l2).to.equal(0); expect(data.s[1].ps[0].ss).to.equal(1); @@ -754,6 +760,7 @@ describe('pubmatic analytics adapter', function () { expect(data.s[1].ps[0].di).to.equal('the-deal-id'); expect(data.s[1].ps[0].dc).to.equal('PMP'); expect(data.s[1].ps[0].mi).to.equal('matched-impression'); + expect(data.s[1].ps[0].adv).to.equal('example.com'); expect(data.s[1].ps[0].l1).to.equal(3214); expect(data.s[1].ps[0].l2).to.equal(0); expect(data.s[1].ps[0].ss).to.equal(1); @@ -771,9 +778,10 @@ describe('pubmatic analytics adapter', function () { expect(data.kgpv).to.equal('*'); }); - it('Logger: regexPattern in bid.bidResponse', function() { + it('Logger: regexPattern in bid.bidResponse and url in adomain', function() { const BID2_COPY = utils.deepClone(BID2); BID2_COPY.regexPattern = '*'; + BID2_COPY.meta.advertiserDomains = ['https://www.example.com/abc/223'] events.emit(AUCTION_INIT, MOCK.AUCTION_INIT); events.emit(AUCTION_INIT, MOCK.AUCTION_INIT); events.emit(BID_REQUESTED, MOCK.BID_REQUESTED); @@ -808,6 +816,7 @@ describe('pubmatic analytics adapter', function () { expect(data.s[1].ps[0].di).to.equal('the-deal-id'); expect(data.s[1].ps[0].dc).to.equal('PMP'); expect(data.s[1].ps[0].mi).to.equal('matched-impression'); + expect(data.s[1].ps[0].adv).to.equal('example.com'); expect(data.s[1].ps[0].l1).to.equal(3214); expect(data.s[1].ps[0].l2).to.equal(0); expect(data.s[1].ps[0].ss).to.equal(1); @@ -859,6 +868,7 @@ describe('pubmatic analytics adapter', function () { expect(data.s[1].ps[0].di).to.equal('the-deal-id'); expect(data.s[1].ps[0].dc).to.equal('PMP'); expect(data.s[1].ps[0].mi).to.equal('matched-impression'); + expect(data.s[1].ps[0].adv).to.equal('example.com'); expect(data.s[1].ps[0].l1).to.equal(3214); expect(data.s[1].ps[0].l2).to.equal(0); expect(data.s[1].ps[0].ss).to.equal(1); @@ -912,6 +922,7 @@ describe('pubmatic analytics adapter', function () { expect(data.s[1].ps[0].di).to.equal('the-deal-id'); expect(data.s[1].ps[0].dc).to.equal('PMP'); expect(data.s[1].ps[0].mi).to.equal('matched-impression'); + expect(data.s[1].ps[0].adv).to.equal('example.com'); expect(data.s[1].ps[0].l1).to.equal(3214); expect(data.s[1].ps[0].l2).to.equal(0); expect(data.s[1].ps[0].ss).to.equal(1); @@ -1011,6 +1022,7 @@ describe('pubmatic analytics adapter', function () { expect(data.s[1].ps[0].di).to.equal('the-deal-id'); expect(data.s[1].ps[0].dc).to.equal('PMP'); expect(data.s[1].ps[0].mi).to.equal('matched-impression'); + expect(data.s[1].ps[0].adv).to.equal('example.com'); expect(data.s[1].ps[0].l1).to.equal(3214); expect(data.s[1].ps[0].l2).to.equal(0); expect(data.s[1].ps[0].ss).to.equal(1); diff --git a/test/spec/modules/pubmaticBidAdapter_spec.js b/test/spec/modules/pubmaticBidAdapter_spec.js index 47b9c984ff5..9696501437b 100644 --- a/test/spec/modules/pubmaticBidAdapter_spec.js +++ b/test/spec/modules/pubmaticBidAdapter_spec.js @@ -1,5 +1,5 @@ import {expect} from 'chai'; -import {spec} from 'modules/pubmaticBidAdapter.js'; +import {spec, checkVideoPlacement} from 'modules/pubmaticBidAdapter.js'; import * as utils from 'src/utils.js'; import {config} from 'src/config.js'; import { createEidsArray } from 'modules/userId/eids.js'; @@ -3457,7 +3457,7 @@ describe('PubMatic adapter', function () { 'h': 0, 'dealId': 'ASEA-MS-KLY-TTD-DESKTOP-ID-VID-6S-030420', 'ext': { - 'BidType': 1 + 'bidtype': 1 } }], 'ext': { @@ -3822,5 +3822,43 @@ describe('PubMatic adapter', function () { expect(bidRequests[0].params.dctr).to.equal('key1:val1,val2|key2:val1'); }); }) + + describe('Checking for Video.Placement property', function() { + let sandbox, utilsMock; + const adUnit = 'Div1'; + const msg_placement_missing = 'Video.Placement param missing for Div1'; + let videoData = { + battr: [6, 7], + skipafter: 15, + maxduration: 50, + context: 'instream', + playerSize: [640, 480], + skip: 0, + connectiontype: [1, 2, 6], + skipmin: 10, + minduration: 10, + mimes: ['video/mp4', 'video/x-flv'], + } + beforeEach(() => { + utilsMock = sinon.mock(utils); + sandbox = sinon.sandbox.create(); + sandbox.spy(utils, 'logWarn'); + }); + + afterEach(() => { + utilsMock.restore(); + sandbox.restore(); + }) + + it('should log Video.Placement param missing', function() { + checkVideoPlacement(videoData, adUnit); + sinon.assert.calledWith(utils.logWarn, msg_placement_missing); + }) + it('shoud not log Video.Placement param missing', function() { + videoData['placement'] = 1; + checkVideoPlacement(videoData, adUnit); + sinon.assert.neverCalledWith(utils.logWarn, msg_placement_missing); + }) + }); }); }); diff --git a/test/spec/modules/pubxaiAnalyticsAdapter_spec.js b/test/spec/modules/pubxaiAnalyticsAdapter_spec.js index 3d9be082be3..e6a1c063a86 100644 --- a/test/spec/modules/pubxaiAnalyticsAdapter_spec.js +++ b/test/spec/modules/pubxaiAnalyticsAdapter_spec.js @@ -28,6 +28,7 @@ describe('pubxai analytics adapter', function() { }; let location = utils.getWindowLocation(); + let storage = window.top['sessionStorage']; let prebidEvent = { 'auctionInit': { @@ -514,6 +515,11 @@ describe('pubxai analytics adapter', function() { 'path': location.pathname, 'search': location.search }, + 'pmcDetail': { + 'bidDensity': storage.getItem('pbx:dpbid'), + 'maxBid': storage.getItem('pbx:mxbid'), + 'auctionId': storage.getItem('pbx:aucid') + } }; let expectedAfterBid = { @@ -577,6 +583,11 @@ describe('pubxai analytics adapter', function() { 'deviceOS': getOS(), 'browser': getBrowser() }, + 'pmcDetail': { + 'bidDensity': storage.getItem('pbx:dpbid'), + 'maxBid': storage.getItem('pbx:mxbid'), + 'auctionId': storage.getItem('pbx:aucid') + }, 'initOptions': initOptions }; diff --git a/test/spec/modules/realTimeDataModule_spec.js b/test/spec/modules/realTimeDataModule_spec.js index b84aef15feb..11639733661 100644 --- a/test/spec/modules/realTimeDataModule_spec.js +++ b/test/spec/modules/realTimeDataModule_spec.js @@ -1,6 +1,9 @@ import * as rtdModule from 'modules/rtdModule/index.js'; -import { config } from 'src/config.js'; +import {config} from 'src/config.js'; import * as sinon from 'sinon'; +import {default as CONSTANTS} from '../../../src/constants.json'; +import {default as events} from '../../../src/events.js'; +import 'src/prebid.js'; const getBidRequestDataSpy = sinon.spy(); @@ -58,33 +61,136 @@ const conf = { }; describe('Real time module', function () { - before(function () { - rtdModule.attachRealTimeDataProvider(validSM); - rtdModule.attachRealTimeDataProvider(invalidSM); - rtdModule.attachRealTimeDataProvider(failureSM); - rtdModule.attachRealTimeDataProvider(nonConfSM); - rtdModule.attachRealTimeDataProvider(validSMWait); - }); + let eventHandlers; + let sandbox; - after(function () { - config.resetConfig(); - }); + function mockEmitEvent(event, ...args) { + (eventHandlers[event] || []).forEach((h) => h(...args)); + } - beforeEach(function () { - config.setConfig(conf); + before(() => { + eventHandlers = {}; + sandbox = sinon.sandbox.create(); + sandbox.stub(events, 'on').callsFake((event, handler) => { + if (!eventHandlers.hasOwnProperty(event)) { + eventHandlers[event] = []; + } + eventHandlers[event].push(handler); + }); }); - it('should use only valid modules', function () { - rtdModule.init(config); - expect(rtdModule.subModules).to.eql([validSMWait, validSM]); + after(() => { + sandbox.restore(); }); - it('should be able to modify bid request', function (done) { - rtdModule.setBidRequestsData(() => { - assert(getBidRequestDataSpy.calledTwice); - assert(getBidRequestDataSpy.calledWith({bidRequest: {}})); + describe('', () => { + const PROVIDERS = [validSM, invalidSM, failureSM, nonConfSM, validSMWait]; + let _detachers; + + beforeEach(function () { + _detachers = PROVIDERS.map(rtdModule.attachRealTimeDataProvider); + rtdModule.init(config); + config.setConfig(conf); + }); + + afterEach(function () { + _detachers.forEach((f) => f()); + config.resetConfig(); + }); + + it('should use only valid modules', function () { + expect(rtdModule.subModules).to.eql([validSMWait, validSM]); + }); + + it('should be able to modify bid request', function (done) { + rtdModule.setBidRequestsData(() => { + assert(getBidRequestDataSpy.calledTwice); + assert(getBidRequestDataSpy.calledWith({bidRequest: {}})); + done(); + }, {bidRequest: {}}) + }); + + it('sould place targeting on adUnits', function (done) { + const auction = { + adUnitCodes: ['ad1', 'ad2'], + adUnits: [ + { + code: 'ad1' + }, + { + code: 'ad2', + adserverTargeting: {preKey: 'preValue'} + } + ] + }; + + const expectedAdUnits = [ + { + code: 'ad1', + adserverTargeting: {key: 'validSMWait'} + }, + { + code: 'ad2', + adserverTargeting: { + preKey: 'preValue', + key: 'validSM' + } + } + ]; + + const adUnits = rtdModule.getAdUnitTargeting(auction); + assert.deepEqual(expectedAdUnits, adUnits) done(); - }, {bidRequest: {}}) + }); + + describe('setBidRequestData', () => { + let withWait, withoutWait; + + function runSetBidRequestData() { + return new Promise((resolve) => { + rtdModule.setBidRequestsData(resolve, {bidRequest: {}}); + }); + } + + beforeEach(() => { + withWait = { + submod: validSMWait, + cbTime: 0, + cbRan: false + }; + withoutWait = { + submod: validSM, + cbTime: 0, + cbRan: false + }; + + [withWait, withoutWait].forEach((c) => { + c.submod.getBidRequestData = sinon.stub().callsFake((_, cb) => { + setTimeout(() => { + c.cbRan = true; + cb(); + }, c.cbTime); + }); + }); + }); + + it('should allow non-priority submodules to run synchronously', () => { + withWait.cbTime = withoutWait.cbTime = 0; + return runSetBidRequestData().then(() => { + expect(withWait.cbRan).to.be.true; + expect(withoutWait.cbRan).to.be.true; + }) + }); + + it('should not wait for non-priority submodules if priority ones complete first', () => { + withWait.cbTime = 10; + withoutWait.cbTime = 100; + return runSetBidRequestData().then(() => { + expect(withWait.cbRan).to.be.true; + expect(withoutWait.cbRan).to.be.false; + }); + }); + }); }); it('deep merge object', function () { @@ -125,36 +231,78 @@ describe('Real time module', function () { assert.deepEqual(expected, merged); }); - it('sould place targeting on adUnits', function (done) { - const auction = { - adUnitCodes: ['ad1', 'ad2'], - adUnits: [ - { - code: 'ad1' - }, - { - code: 'ad2', - adserverTargeting: {preKey: 'preValue'} - } - ] + describe('event', () => { + const EVENTS = { + [CONSTANTS.EVENTS.AUCTION_INIT]: 'onAuctionInitEvent', + [CONSTANTS.EVENTS.AUCTION_END]: 'onAuctionEndEvent', + [CONSTANTS.EVENTS.BID_RESPONSE]: 'onBidResponseEvent', + [CONSTANTS.EVENTS.BID_REQUESTED]: 'onBidRequestEvent' + } + const conf = { + 'realTimeData': { + dataProviders: [ + { + 'name': 'tp1', + }, + { + 'name': 'tp2', + } + ] + } }; + let providers; + let _detachers; - const expectedAdUnits = [ - { - code: 'ad1', - adserverTargeting: {key: 'validSMWait'} - }, - { - code: 'ad2', - adserverTargeting: { - preKey: 'preValue', - key: 'validSM' - } + function eventHandlingProvider(name) { + const provider = { + name: name, + init: () => true, } - ]; + Object.values(EVENTS).forEach((ev) => provider[ev] = sinon.spy()); + return provider; + } + + beforeEach(() => { + providers = [eventHandlingProvider('tp1'), eventHandlingProvider('tp2')]; + _detachers = providers.map(rtdModule.attachRealTimeDataProvider); + rtdModule.init(config); + config.setConfig(conf); + }); - const adUnits = rtdModule.getAdUnitTargeting(auction); - assert.deepEqual(expectedAdUnits, adUnits) - done(); - }) + afterEach(() => { + _detachers.forEach((d) => d()) + config.resetConfig(); + }); + + it('should set targeting for auctionEnd', () => { + providers.forEach(p => p.getTargetingData = sinon.spy()); + const auction = { + adUnitCodes: ['a1'], + adUnits: [{code: 'a1'}] + }; + mockEmitEvent(CONSTANTS.EVENTS.AUCTION_END, auction); + providers.forEach(p => { + expect(p.getTargetingData.calledWith(auction.adUnitCodes)).to.be.true; + }); + }); + + Object.entries(EVENTS).forEach(([event, hook]) => { + it(`'${event}' should be propagated to providers through '${hook}'`, () => { + const eventArg = {}; + mockEmitEvent(event, eventArg); + providers.forEach((provider) => { + const providerConf = conf.realTimeData.dataProviders.find((cfg) => cfg.name === provider.name); + expect(provider[hook].called).to.be.true; + expect(provider[hook].args).to.have.length(1); + expect(provider[hook].args[0]).to.include.members([eventArg, providerConf]) + }) + }); + + it(`${event} should not fail to propagate elsewhere if a provider throws in its event handler`, () => { + providers[0][hook] = function () { throw new Error() }; + mockEmitEvent(event); + expect(providers[1][hook].called).to.be.true; + }); + }); + }); }); diff --git a/test/spec/modules/relaidoBidAdapter_spec.js b/test/spec/modules/relaidoBidAdapter_spec.js index 868b1856c34..51850e5f357 100644 --- a/test/spec/modules/relaidoBidAdapter_spec.js +++ b/test/spec/modules/relaidoBidAdapter_spec.js @@ -57,23 +57,34 @@ describe('RelaidoAdapter', function () { serverResponse = { body: { status: 'ok', - price: 500, - model: 'vcpm', - currency: 'JPY', - creativeId: 1000, - uuid: relaido_uuid, - vast: '', + ads: [{ + placementId: 100000, + width: 640, + height: 360, + bidId: '2ed93003f7bb99', + price: 500, + model: 'vcpm', + currency: 'JPY', + creativeId: 1000, + vast: '', + syncUrl: 'https://relaido/sync.html', + adomain: ['relaido.co.jp', 'www.cmertv.co.jp'], + mediaType: 'video' + }], playerUrl: 'https://relaido/player.js', - syncUrl: 'https://relaido/sync.html', - adomain: ['relaido.co.jp', 'www.cmertv.co.jp'] + syncUrl: 'https://api-dev.ulizaex.com/tr/v1/prebid/sync.html', + uuid: relaido_uuid, } }; serverRequest = { - method: 'GET', - bidId: bidRequest.bidId, - width: bidRequest.mediaTypes.video.playerSize[0][0], - height: bidRequest.mediaTypes.video.playerSize[0][1], - mediaType: 'video', + method: 'POST', + data: { + bids: [{ + bidId: bidRequest.bidId, + width: bidRequest.mediaTypes.video.playerSize[0][0], + height: bidRequest.mediaTypes.video.playerSize[0][1], + mediaType: 'video'}] + } }; }); @@ -195,28 +206,23 @@ describe('RelaidoAdapter', function () { describe('spec.buildRequests', function () { it('should build bid requests by video', function () { const bidRequests = spec.buildRequests([bidRequest], bidderRequest); - expect(bidRequests).to.have.lengthOf(1); - const request = bidRequests[0]; - expect(request.method).to.equal('GET'); - expect(request.url).to.equal('https://api.relaido.jp/bid/v1/prebid/100000'); - expect(request.bidId).to.equal(bidRequest.bidId); - expect(request.width).to.equal(bidRequest.mediaTypes.video.playerSize[0][0]); - expect(request.height).to.equal(bidRequest.mediaTypes.video.playerSize[0][1]); - expect(request.mediaType).to.equal('video'); - expect(request.data.ref).to.equal(bidderRequest.refererInfo.referer); - expect(request.data.timeout_ms).to.equal(bidderRequest.timeout); - expect(request.data.ad_unit_code).to.equal(bidRequest.adUnitCode); - expect(request.data.auction_id).to.equal(bidRequest.auctionId); - expect(request.data.bidder).to.equal(bidRequest.bidder); - expect(request.data.bidder_request_id).to.equal(bidRequest.bidderRequestId); - expect(request.data.bid_requests_count).to.equal(bidRequest.bidRequestsCount); - expect(request.data.bid_id).to.equal(bidRequest.bidId); - expect(request.data.transaction_id).to.equal(bidRequest.transactionId); - expect(request.data.media_type).to.equal('video'); - expect(request.data.uuid).to.equal(relaido_uuid); - expect(request.data.width).to.equal(bidRequest.mediaTypes.video.playerSize[0][0]); - expect(request.data.height).to.equal(bidRequest.mediaTypes.video.playerSize[0][1]); - expect(request.data.pv).to.equal('$prebid.version$'); + const data = JSON.parse(bidRequests.data); + expect(data.bids).to.have.lengthOf(1); + const request = data.bids[0]; + expect(bidRequests.method).to.equal('POST'); + expect(bidRequests.url).to.equal('https://api.relaido.jp/bid/v1/sprebid'); + expect(data.ref).to.equal(bidderRequest.refererInfo.referer); + expect(data.timeout_ms).to.equal(bidderRequest.timeout); + expect(request.ad_unit_code).to.equal(bidRequest.adUnitCode); + expect(request.auction_id).to.equal(bidRequest.auctionId); + expect(data.bidder).to.equal(bidRequest.bidder); + expect(request.bidder_request_id).to.equal(bidRequest.bidderRequestId); + expect(data.bid_requests_count).to.equal(bidRequest.bidRequestsCount); + expect(request.bid_id).to.equal(bidRequest.bidId); + expect(request.transaction_id).to.equal(bidRequest.transactionId); + expect(request.media_type).to.equal('video'); + expect(data.uuid).to.equal(relaido_uuid); + expect(data.pv).to.equal('$prebid.version$'); }); it('should build bid requests by banner', function () { @@ -236,9 +242,10 @@ describe('RelaidoAdapter', function () { } }; const bidRequests = spec.buildRequests([bidRequest], bidderRequest); - expect(bidRequests).to.have.lengthOf(1); - const request = bidRequests[0]; - expect(request.mediaType).to.equal('banner'); + const data = JSON.parse(bidRequests.data); + expect(data.bids).to.have.lengthOf(1); + const request = data.bids[0]; + expect(request.media_type).to.equal('banner'); }); it('should take 1x1 size', function () { @@ -258,17 +265,18 @@ describe('RelaidoAdapter', function () { } }; const bidRequests = spec.buildRequests([bidRequest], bidderRequest); - expect(bidRequests).to.have.lengthOf(1); - const request = bidRequests[0]; + const data = JSON.parse(bidRequests.data); + expect(data.bids).to.have.lengthOf(1); + const request = data.bids[0]; expect(request.width).to.equal(1); }); it('The referrer should be the last', function () { const bidRequests = spec.buildRequests([bidRequest], bidderRequest); - expect(bidRequests).to.have.lengthOf(1); - const request = bidRequests[0]; - const keys = Object.keys(request.data); + const data = JSON.parse(bidRequests.data); + expect(data.bids).to.have.lengthOf(1); + const keys = Object.keys(data); expect(keys[0]).to.equal('version'); expect(keys[keys.length - 1]).to.equal('ref'); }); @@ -277,9 +285,9 @@ describe('RelaidoAdapter', function () { bidRequest.userId = {} bidRequest.userId.imuid = 'i.tjHcK_7fTcqnbrS_YA2vaw'; const bidRequests = spec.buildRequests([bidRequest], bidderRequest); - expect(bidRequests).to.have.lengthOf(1); - const request = bidRequests[0]; - expect(request.data.imuid).to.equal('i.tjHcK_7fTcqnbrS_YA2vaw'); + const data = JSON.parse(bidRequests.data); + expect(data.bids).to.have.lengthOf(1); + expect(data.imuid).to.equal('i.tjHcK_7fTcqnbrS_YA2vaw'); }); }); @@ -288,29 +296,29 @@ describe('RelaidoAdapter', function () { const bidResponses = spec.interpretResponse(serverResponse, serverRequest); expect(bidResponses).to.have.lengthOf(1); const response = bidResponses[0]; - expect(response.requestId).to.equal(serverRequest.bidId); - expect(response.width).to.equal(serverRequest.width); - expect(response.height).to.equal(serverRequest.height); - expect(response.cpm).to.equal(serverResponse.body.price); - expect(response.currency).to.equal(serverResponse.body.currency); - expect(response.creativeId).to.equal(serverResponse.body.creativeId); - expect(response.vastXml).to.equal(serverResponse.body.vast); - expect(response.meta.advertiserDomains).to.equal(serverResponse.body.adomain); + expect(response.requestId).to.equal(serverRequest.data.bids[0].bidId); + expect(response.width).to.equal(serverRequest.data.bids[0].width); + expect(response.height).to.equal(serverRequest.data.bids[0].height); + expect(response.cpm).to.equal(serverResponse.body.ads[0].price); + expect(response.currency).to.equal(serverResponse.body.ads[0].currency); + expect(response.creativeId).to.equal(serverResponse.body.ads[0].creativeId); + expect(response.vastXml).to.equal(serverResponse.body.ads[0].vast); + expect(response.meta.advertiserDomains).to.equal(serverResponse.body.ads[0].adomain); expect(response.meta.mediaType).to.equal(VIDEO); expect(response.ad).to.be.undefined; }); it('should build bid response by banner', function () { - serverRequest.mediaType = 'banner'; + serverResponse.body.ads[0].mediaType = 'banner'; const bidResponses = spec.interpretResponse(serverResponse, serverRequest); expect(bidResponses).to.have.lengthOf(1); const response = bidResponses[0]; - expect(response.requestId).to.equal(serverRequest.bidId); - expect(response.width).to.equal(serverRequest.width); - expect(response.height).to.equal(serverRequest.height); - expect(response.cpm).to.equal(serverResponse.body.price); - expect(response.currency).to.equal(serverResponse.body.currency); - expect(response.creativeId).to.equal(serverResponse.body.creativeId); + expect(response.requestId).to.equal(serverRequest.data.bids[0].bidId); + expect(response.width).to.equal(serverRequest.data.bids[0].width); + expect(response.height).to.equal(serverRequest.data.bids[0].height); + expect(response.cpm).to.equal(serverResponse.body.ads[0].price); + expect(response.currency).to.equal(serverResponse.body.ads[0].currency); + expect(response.creativeId).to.equal(serverResponse.body.ads[0].creativeId); expect(response.vastXml).to.be.undefined; expect(response.ad).to.include(`
`); expect(response.ad).to.include(``); @@ -370,8 +378,8 @@ describe('RelaidoAdapter', function () { it('Should create nurl pixel if bid nurl', function () { let bid = { bidder: bidRequest.bidder, - creativeId: serverResponse.body.creativeId, - cpm: serverResponse.body.price, + creativeId: serverResponse.body.ads[0].creativeId, + cpm: serverResponse.body.ads[0].price, params: [bidRequest.params], auctionId: bidRequest.auctionId, requestId: bidRequest.bidId, diff --git a/test/spec/modules/richaudienceBidAdapter_spec.js b/test/spec/modules/richaudienceBidAdapter_spec.js index 00ae55823b0..9e9366072be 100644 --- a/test/spec/modules/richaudienceBidAdapter_spec.js +++ b/test/spec/modules/richaudienceBidAdapter_spec.js @@ -791,6 +791,46 @@ describe('Richaudience adapter tests', function () { })).to.equal(true); }); + it('should pass schain', function() { + let schain = { + 'ver': '1.0', + 'complete': 1, + 'nodes': [{ + 'asi': 'richaudience.com', + 'sid': '00001', + 'hp': 1 + }, { + 'asi': 'richaudience-2.com', + 'sid': '00002', + 'hp': 1 + }] + } + + DEFAULT_PARAMS_NEW_SIZES[0].schain = { + 'ver': '1.0', + 'complete': 1, + 'nodes': [{ + 'asi': 'richaudience.com', + 'sid': '00001', + 'hp': 1 + }, { + 'asi': 'richaudience-2.com', + 'sid': '00002', + 'hp': 1 + }] + } + + const request = spec.buildRequests(DEFAULT_PARAMS_NEW_SIZES, { + gdprConsent: { + consentString: 'BOZcQl_ObPFjWAeABAESCD-AAAAjx7_______9______9uz_Ov_v_f__33e8__9v_l_7_-___u_-33d4-_1vf99yfm1-7ftr3tp_87ues2_Xur__59__3z3_NohBgA', + gdprApplies: true + }, + refererInfo: {} + }) + const requestContent = JSON.parse(request[0].data); + expect(requestContent).to.have.property('schain').to.deep.equal(schain); + }) + describe('userSync', function () { it('Verifies user syncs iframe include', function () { config.setConfig({ @@ -905,7 +945,7 @@ describe('Richaudience adapter tests', function () { }, [], { consentString: null, referer: 'http://domain.com', - gdprApplies: true + gdprApplies: false }) expect(syncs).to.have.lengthOf(1); expect(syncs[0].type).to.equal('image'); @@ -942,7 +982,7 @@ describe('Richaudience adapter tests', function () { }, [], { consentString: null, referer: 'http://domain.com', - gdprApplies: true + gdprApplies: false }) expect(syncs).to.have.lengthOf(0); }); diff --git a/test/spec/modules/riseBidAdapter_spec.js b/test/spec/modules/riseBidAdapter_spec.js index 61b307eef21..211c36a254a 100644 --- a/test/spec/modules/riseBidAdapter_spec.js +++ b/test/spec/modules/riseBidAdapter_spec.js @@ -257,7 +257,7 @@ describe('riseAdapter', function () { const requests = spec.buildRequests(bidRequests, bidderRequest); for (const request of requests) { expect(request.data).to.be.an('object'); - expect(request.data).to.have.property('schain', '1.0,1!indirectseller.com,00001,,,,'); + expect(request.data).to.have.property('schain', '1.0,1!indirectseller.com,00001,1,,,'); } }); diff --git a/test/spec/modules/rtbhouseBidAdapter_spec.js b/test/spec/modules/rtbhouseBidAdapter_spec.js index d6bee26d73b..f4bcb48474a 100644 --- a/test/spec/modules/rtbhouseBidAdapter_spec.js +++ b/test/spec/modules/rtbhouseBidAdapter_spec.js @@ -68,6 +68,7 @@ describe('RTBHouseAdapter', () => { 'params': { 'publisherId': 'PREBID_TEST', 'region': 'prebid-eu', + 'channel': 'Partner_Site - news', 'test': 1 }, 'adUnitCode': 'adunit-code', @@ -101,6 +102,25 @@ describe('RTBHouseAdapter', () => { expect(JSON.parse(builtTestRequest).test).to.equal(1); }); + it('should build channel param into request.site', () => { + let builtTestRequest = spec.buildRequests(bidRequests, bidderRequest).data; + expect(JSON.parse(builtTestRequest).site.channel).to.equal('Partner_Site - news'); + }) + + it('should not build channel param into request.site if no value is passed', () => { + let bidRequest = Object.assign([], bidRequests); + bidRequest[0].params.channel = undefined; + let builtTestRequest = spec.buildRequests(bidRequest, bidderRequest).data; + expect(JSON.parse(builtTestRequest).site.channel).to.be.undefined + }) + + it('should cap the request.site.channel length to 50', () => { + let bidRequest = Object.assign([], bidRequests); + bidRequest[0].params.channel = 'Lorem ipsum dolor sit amet, consectetur adipiscing elit. Praesent scelerisque ipsum eu purus lobortis iaculis.'; + let builtTestRequest = spec.buildRequests(bidRequest, bidderRequest).data; + expect(JSON.parse(builtTestRequest).site.channel.length).to.equal(50) + }) + it('should build valid OpenRTB banner object', () => { const request = JSON.parse(spec.buildRequests(bidRequests, bidderRequest).data); const imp = request.imp[0]; diff --git a/test/spec/modules/rubiconAnalyticsAdapter_spec.js b/test/spec/modules/rubiconAnalyticsAdapter_spec.js index 798e98bb97d..584fe718c36 100644 --- a/test/spec/modules/rubiconAnalyticsAdapter_spec.js +++ b/test/spec/modules/rubiconAnalyticsAdapter_spec.js @@ -246,7 +246,7 @@ const MOCK = { } }, BID_REQUESTED: { - 'bidder': 'rubicon', + 'bidderCode': 'rubicon', 'auctionId': '25c6d7f5-699a-4bfc-87c9-996f915341fa', 'bidderRequestId': '1be65d7958826a', 'bids': [ @@ -384,6 +384,10 @@ const ANALYTICS_MESSAGE = { 'referrerHostname': 'www.test.com', 'auctions': [ { + + 'auctionEnd': 1519767013781, + 'auctionStart': 1519767010567, + 'bidderOrder': ['rubicon'], 'requestId': '25c6d7f5-699a-4bfc-87c9-996f915341fa', 'clientTimeoutMillis': 3000, 'serverTimeoutMillis': 1000, @@ -853,6 +857,32 @@ describe('rubicon analytics adapter', function () { expect(message).to.deep.equal(ANALYTICS_MESSAGE); }); + it('should pass along bidderOrder correctly', function () { + const appnexusBid = utils.deepClone(MOCK.BID_REQUESTED); + appnexusBid.bidderCode = 'appnexus'; + const pubmaticBid = utils.deepClone(MOCK.BID_REQUESTED); + pubmaticBid.bidderCode = 'pubmatic'; + const indexBid = utils.deepClone(MOCK.BID_REQUESTED); + indexBid.bidderCode = 'ix'; + events.emit(AUCTION_INIT, MOCK.AUCTION_INIT); + events.emit(BID_REQUESTED, pubmaticBid); + events.emit(BID_REQUESTED, MOCK.BID_REQUESTED); + events.emit(BID_REQUESTED, indexBid); + events.emit(BID_REQUESTED, appnexusBid); + events.emit(BIDDER_DONE, MOCK.BIDDER_DONE); + events.emit(AUCTION_END, MOCK.AUCTION_END); + events.emit(SET_TARGETING, MOCK.SET_TARGETING); + clock.tick(SEND_TIMEOUT + 1000); + + let message = JSON.parse(server.requests[0].requestBody); + expect(message.auctions[0].bidderOrder).to.deep.equal([ + 'pubmatic', + 'rubicon', + 'ix', + 'appnexus' + ]); + }); + it('should pass along user ids', function () { let auctionInit = utils.deepClone(MOCK.AUCTION_INIT); auctionInit.bidderRequests[0].bids[0].userId = { @@ -987,6 +1017,36 @@ describe('rubicon analytics adapter', function () { expect(message.auctions[0].adUnits[1].bids[0].bidResponse.adomains).to.be.undefined; }); + it('should NOT pass along adomians with other edge cases', function () { + events.emit(AUCTION_INIT, MOCK.AUCTION_INIT); + events.emit(BID_REQUESTED, MOCK.BID_REQUESTED); + + // should filter out non string values and pass valid ones + let bidResponse1 = utils.deepClone(MOCK.BID_RESPONSE[0]); + bidResponse1.meta = { + advertiserDomains: [123, 'prebid.org', false, true, [], 'magnite.com', {}] + } + + // array of arrays (as seen when passed by kargo bid adapter) + let bidResponse2 = utils.deepClone(MOCK.BID_RESPONSE[1]); + bidResponse2.meta = { + advertiserDomains: [['prebid.org']] + } + + events.emit(BID_RESPONSE, bidResponse1); + events.emit(BID_RESPONSE, bidResponse2); + events.emit(BIDDER_DONE, MOCK.BIDDER_DONE); + events.emit(AUCTION_END, MOCK.AUCTION_END); + events.emit(SET_TARGETING, MOCK.SET_TARGETING); + events.emit(BID_WON, MOCK.BID_WON[0]); + events.emit(BID_WON, MOCK.BID_WON[1]); + + let message = JSON.parse(server.requests[0].requestBody); + validate(message); + expect(message.auctions[0].adUnits[0].bids[0].bidResponse.adomains).to.deep.equal(['prebid.org', 'magnite.com']); + expect(message.auctions[0].adUnits[1].bids[0].bidResponse.adomains).to.be.undefined; + }); + it('should not pass empty adServerTargeting values', function () { events.emit(AUCTION_INIT, MOCK.AUCTION_INIT); events.emit(BID_REQUESTED, MOCK.BID_REQUESTED); diff --git a/test/spec/modules/rubiconBidAdapter_spec.js b/test/spec/modules/rubiconBidAdapter_spec.js index 6210640f79f..ea839bc7ae3 100644 --- a/test/spec/modules/rubiconBidAdapter_spec.js +++ b/test/spec/modules/rubiconBidAdapter_spec.js @@ -1408,7 +1408,7 @@ describe('the rubicon adapter', function () { expect(data['tg_i.pbadslot']).to.equal('abc'); }); - it('should send \"tg_i.pbadslot\" if \"ortb2Imp.ext.data.pbadslot\" value is a valid string, but all leading slash characters should be removed', function () { + it('should send \"tg_i.pbadslot\" if \"ortb2Imp.ext.data.pbadslot\" value is a valid string', function () { bidderRequest.bids[0].ortb2Imp = { ext: { data: { @@ -1422,7 +1422,45 @@ describe('the rubicon adapter', function () { expect(data).to.be.an('Object'); expect(data).to.have.property('tg_i.pbadslot'); - expect(data['tg_i.pbadslot']).to.equal('a/b/c'); + expect(data['tg_i.pbadslot']).to.equal('/a/b/c'); + }); + + it('should send gpid as p_gpid if valid', function () { + bidderRequest.bids[0].ortb2Imp = { + ext: { + gpid: '/1233/sports&div1' + } + } + + const [request] = spec.buildRequests(bidderRequest.bids, bidderRequest); + const data = parseQuery(request.data); + + expect(data).to.be.an('Object'); + expect(data).to.have.property('p_gpid'); + expect(data['p_gpid']).to.equal('/1233/sports&div1'); + }); + + it('should send gpid and pbadslot since it is prefered over dfp code', function () { + bidderRequest.bids[0].ortb2Imp = { + ext: { + gpid: '/1233/sports&div1', + data: { + pbadslot: 'pb_slot', + adserver: { + adslot: '/1234/sports', + name: 'gam' + } + } + } + } + + const [request] = spec.buildRequests(bidderRequest.bids, bidderRequest); + const data = parseQuery(request.data); + + expect(data).to.be.an('Object'); + expect(data['p_gpid']).to.equal('/1233/sports&div1'); + expect(data).to.not.have.property('tg_i.dfp_ad_unit_code'); + expect(data['tg_i.pbadslot']).to.equal('pb_slot'); }); }); @@ -1470,12 +1508,13 @@ describe('the rubicon adapter', function () { expect(data).to.not.have.property('tg_i.dfp_ad_unit_code'); }); - it('should send \"tg_i.dfp_ad_unit_code\" if \"ortb2Imp.ext.data.adServer.adslot\" value is a valid string', function () { + it('should send NOT \"tg_i.dfp_ad_unit_code\" if \"ortb2Imp.ext.data.adServer.adslot\" value is a valid string but not gam', function () { bidderRequest.bids[0].ortb2Imp = { ext: { data: { adserver: { - adslot: 'abc' + adslot: '/a/b/c', + name: 'not gam' } } } @@ -1485,16 +1524,16 @@ describe('the rubicon adapter', function () { const data = parseQuery(request.data); expect(data).to.be.an('Object'); - expect(data).to.have.property('tg_i.dfp_ad_unit_code'); - expect(data['tg_i.dfp_ad_unit_code']).to.equal('abc'); + expect(data).to.not.have.property('tg_i.dfp_ad_unit_code'); }); - it('should send \"tg_i.dfp_ad_unit_code\" if \"ortb2Imp.ext.data.adServer.adslot\" value is a valid string, but all leading slash characters should be removed', function () { + it('should send \"tg_i.dfp_ad_unit_code\" if \"ortb2Imp.ext.data.adServer.adslot\" value is a valid string and name is gam', function () { bidderRequest.bids[0].ortb2Imp = { ext: { data: { adserver: { - adslot: 'a/b/c' + name: 'gam', + adslot: '/a/b/c' } } } @@ -1505,7 +1544,7 @@ describe('the rubicon adapter', function () { expect(data).to.be.an('Object'); expect(data).to.have.property('tg_i.dfp_ad_unit_code'); - expect(data['tg_i.dfp_ad_unit_code']).to.equal('a/b/c'); + expect(data['tg_i.dfp_ad_unit_code']).to.equal('/a/b/c'); }); }); }); @@ -3343,6 +3382,23 @@ describe('the rubicon adapter', function () { type: 'iframe', url: `${emilyUrl}?gdpr_consent=foo&us_privacy=1NYN` }); }); + + it('should pass gdprApplies', function () { + expect(spec.getUserSyncs({iframeEnabled: true}, {}, { + gdprApplies: true + }, '1NYN')).to.deep.equal({ + type: 'iframe', url: `${emilyUrl}?gdpr=1&us_privacy=1NYN` + }); + }); + + it('should pass all correctly', function () { + expect(spec.getUserSyncs({iframeEnabled: true}, {}, { + gdprApplies: true, + consentString: 'foo' + }, '1NYN')).to.deep.equal({ + type: 'iframe', url: `${emilyUrl}?gdpr=1&gdpr_consent=foo&us_privacy=1NYN` + }); + }); }); describe('get price granularity', function () { diff --git a/test/spec/modules/seedtagBidAdapter_spec.js b/test/spec/modules/seedtagBidAdapter_spec.js index 3aa378379dd..159645ff6d6 100644 --- a/test/spec/modules/seedtagBidAdapter_spec.js +++ b/test/spec/modules/seedtagBidAdapter_spec.js @@ -274,6 +274,9 @@ describe('Seedtag Adapter', function() { expect(data.ga).to.equal(true) expect(data.cd).to.equal('consentString') }) + it('should expose gvlid', function() { + expect(spec.gvlid).to.equal(157) + }) }) }) diff --git a/test/spec/modules/sharedIdSystem_spec.js b/test/spec/modules/sharedIdSystem_spec.js index 534d0b3f381..8ef34a1599e 100644 --- a/test/spec/modules/sharedIdSystem_spec.js +++ b/test/spec/modules/sharedIdSystem_spec.js @@ -50,10 +50,10 @@ describe('SharedId System', function () { expect(callbackSpy.calledOnce).to.be.true; expect(callbackSpy.lastCall.lastArg).to.equal(UUID); }); - it('should log message if coppa is set', function () { + it('should abort if coppa is set', function () { coppaDataHandlerDataStub.returns('true'); - sharedIdSystemSubmodule.getId({}); - expect(utils.logInfo.args[0][0]).to.exist.and.to.equal('PubCommonId: IDs not provided for coppa requests, exiting PubCommonId'); + const result = sharedIdSystemSubmodule.getId({}); + expect(result).to.be.undefined; }); }); describe('SharedId System extendId()', function () { @@ -85,10 +85,56 @@ describe('SharedId System', function () { let pubcommId = sharedIdSystemSubmodule.extendId(config, undefined, 'TestId').id; expect(pubcommId).to.equal('TestId'); }); - it('should log message if coppa is set', function () { + it('should abort if coppa is set', function () { coppaDataHandlerDataStub.returns('true'); - sharedIdSystemSubmodule.extendId({}, undefined, 'TestId'); - expect(utils.logInfo.args[0][0]).to.exist.and.to.equal('PubCommonId: IDs not provided for coppa requests, exiting PubCommonId'); + const result = sharedIdSystemSubmodule.extendId({params: {extend: true}}, undefined, 'TestId'); + expect(result).to.be.undefined; + }); + }); + + describe('SharedID System domainOverride', () => { + let sandbox, domain, cookies, rejectCookiesFor; + + beforeEach(() => { + sandbox = sinon.createSandbox(); + sandbox.stub(document, 'domain').get(() => domain); + cookies = {}; + sandbox.stub(storage, 'getCookie').callsFake((key) => cookies[key]); + rejectCookiesFor = null; + sandbox.stub(storage, 'setCookie').callsFake((key, value, expires, sameSite, domain) => { + if (domain !== rejectCookiesFor) { + if (expires != null) { + expires = new Date(expires); + } + if (expires == null || expires > Date.now()) { + cookies[key] = value; + } else { + delete cookies[key]; + } + } + }); + }); + + afterEach(() => { + sandbox.restore(); + }); + + it('should return TLD if cookies can be set there', () => { + domain = 'sub.domain.com'; + rejectCookiesFor = 'com'; + expect(sharedIdSystemSubmodule.domainOverride()).to.equal('domain.com'); + }); + + it('should return undefined when cookies cannot be set', () => { + domain = 'sub.domain.com'; + rejectCookiesFor = 'sub.domain.com'; + expect(sharedIdSystemSubmodule.domainOverride()).to.be.undefined; + }); + + it('should return half-way domain if parent domain rejects cookies', () => { + domain = 'inner.outer.domain.com'; + rejectCookiesFor = 'domain.com'; + expect(sharedIdSystemSubmodule.domainOverride()).to.equal('outer.domain.com'); }); }); }); diff --git a/test/spec/modules/sharethroughBidAdapter_spec.js b/test/spec/modules/sharethroughBidAdapter_spec.js index db21af5f6b3..39238cc877f 100644 --- a/test/spec/modules/sharethroughBidAdapter_spec.js +++ b/test/spec/modules/sharethroughBidAdapter_spec.js @@ -1,4 +1,5 @@ import { expect } from 'chai'; +import * as sinon from 'sinon'; import { sharethroughAdapterSpec, sharethroughInternal } from 'modules/sharethroughBidAdapter.js'; import { newBidder } from 'src/adapters/bidderFactory.js'; import { config } from 'src/config'; @@ -157,7 +158,6 @@ describe('sharethrough adapter spec', function () { startdelay: 42, skipmin: 10, skipafter: 20, - placement: 1, delivery: 1, companiontype: 'companion type', companionad: 'companion ad', @@ -205,6 +205,7 @@ describe('sharethrough adapter spec', function () { expect(openRtbReq.cur).to.deep.equal(['USD']); expect(openRtbReq.tmax).to.equal(242); + expect(Object.keys(openRtbReq.site)).to.have.length(3); expect(openRtbReq.site.domain).not.to.be.undefined; expect(openRtbReq.site.page).not.to.be.undefined; expect(openRtbReq.site.ref).to.equal('https://referer.com'); @@ -256,6 +257,17 @@ describe('sharethrough adapter spec', function () { }); }); + describe('no referer provided', () => { + beforeEach(() => { + bidderRequest = {}; + }); + + it('should set referer to undefined', () => { + const openRtbReq = spec.buildRequests(bidRequests, bidderRequest)[0].data; + expect(openRtbReq.site.ref).to.be.undefined; + }); + }); + describe('regulation', () => { describe('gdpr', () => { it('should populate request accordingly when gdpr applies', () => { @@ -419,17 +431,90 @@ describe('sharethrough adapter spec', function () { expect(videoImp.startdelay).to.equal(0); expect(videoImp.skipmin).to.equal(0); expect(videoImp.skipafter).to.equal(0); - expect(videoImp.placement).to.be.undefined; + expect(videoImp.placement).to.equal(1); expect(videoImp.delivery).to.be.undefined; expect(videoImp.companiontype).to.be.undefined; expect(videoImp.companionad).to.be.undefined; }); - it('should not return a video impression if context is outstream', () => { - bidRequests[1].mediaTypes.video.context = 'outstream'; - const builtRequest = spec.buildRequests(bidRequests, bidderRequest)[1]; + describe('outstream', () => { + it('should use placement value if provided', () => { + bidRequests[1].mediaTypes.video.context = 'outstream'; + bidRequests[1].mediaTypes.video.placement = 3; + + const builtRequest = spec.buildRequests(bidRequests, bidderRequest)[1]; + const videoImp = builtRequest.data.imp[0].video; + + expect(videoImp.placement).to.equal(3); + }); + + it('should default placement to 4 if not provided', () => { + bidRequests[1].mediaTypes.video.context = 'outstream'; + + const builtRequest = spec.buildRequests(bidRequests, bidderRequest)[1]; + const videoImp = builtRequest.data.imp[0].video; + + expect(videoImp.placement).to.equal(4); + }); + }); + }); + + describe('first party data', () => { + const firstPartyData = { + site: { + name: 'example', + keywords: 'power tools, drills', + search: 'drill', + content: { + userrating: '4', + }, + ext: { + data: { + pageType: 'article', + category: 'repair', + }, + }, + }, + user: { + yob: 1985, + gender: 'm', + ext: { + data: { + registered: true, + interests: ['cars'], + }, + }, + }, + }; + + let configStub; + + beforeEach(() => { + configStub = sinon.stub(config, 'getConfig'); + configStub.withArgs('ortb2').returns(firstPartyData); + }); + + afterEach(() => { + configStub.restore(); + }); + + it('should include first party data in open rtb request, site section', () => { + const openRtbReq = spec.buildRequests(bidRequests, bidderRequest)[0].data; + + expect(openRtbReq.site.name).to.equal(firstPartyData.site.name); + expect(openRtbReq.site.keywords).to.equal(firstPartyData.site.keywords); + expect(openRtbReq.site.search).to.equal(firstPartyData.site.search); + expect(openRtbReq.site.content).to.deep.equal(firstPartyData.site.content); + expect(openRtbReq.site.ext).to.deep.equal(firstPartyData.site.ext); + }); + + it('should include first party data in open rtb request, user section', () => { + const openRtbReq = spec.buildRequests(bidRequests, bidderRequest)[0].data; - expect(builtRequest).to.be.undefined; + expect(openRtbReq.user.yob).to.equal(firstPartyData.user.yob); + expect(openRtbReq.user.gender).to.equal(firstPartyData.user.gender); + expect(openRtbReq.user.ext.data).to.deep.equal(firstPartyData.user.ext.data); + expect(openRtbReq.user.ext.eids).not.to.be.undefined; }); }); }); diff --git a/test/spec/modules/showheroes-bsBidAdapter_spec.js b/test/spec/modules/showheroes-bsBidAdapter_spec.js index ad2210c18c6..69e8343dfc9 100644 --- a/test/spec/modules/showheroes-bsBidAdapter_spec.js +++ b/test/spec/modules/showheroes-bsBidAdapter_spec.js @@ -13,6 +13,7 @@ const adomain = ['showheroes.com']; const gdpr = { 'gdprConsent': { + 'apiVersion': 2, 'consentString': 'BOEFEAyOEFEAyAHABDENAI4AAAB9vABAASA', 'gdprApplies': true } @@ -332,6 +333,7 @@ describe('shBidAdapter', function () { { 'cpm': 5, 'creativeId': 'c_38b373e1e31c18', + 'adUnitCode': 'adunit-code-1', 'currency': 'EUR', 'width': 640, 'height': 480, diff --git a/test/spec/modules/smaatoBidAdapter_spec.js b/test/spec/modules/smaatoBidAdapter_spec.js index faa288306a5..9803d1df103 100644 --- a/test/spec/modules/smaatoBidAdapter_spec.js +++ b/test/spec/modules/smaatoBidAdapter_spec.js @@ -287,6 +287,23 @@ describe('smaatoBidAdapterTest', () => { expect(req.regs.ext.us_privacy).to.equal('uspConsentString'); }); + it('sends no schain if no schain exists', () => { + const reqs = spec.buildRequests([singleBannerBidRequest], defaultBidderRequest); + + const req = extractPayloadOfFirstAndOnlyRequest(reqs); + expect(req.source.ext.schain).to.not.exist; + }); + + it('sends instl if instl exists', () => { + const instl = { instl: 1 }; + const bidRequestWithInstl = Object.assign({}, singleBannerBidRequest, {ortb2Imp: instl}); + + const reqs = spec.buildRequests([bidRequestWithInstl], defaultBidderRequest); + + const req = extractPayloadOfFirstAndOnlyRequest(reqs); + expect(req.imp[0].instl).to.equal(1); + }); + it('sends tmax', () => { const reqs = spec.buildRequests([singleBannerBidRequest], defaultBidderRequest); @@ -435,6 +452,16 @@ describe('smaatoBidAdapterTest', () => { expect(req.imp[0].bidfloor).to.be.equal(0.456); }); + it('sends instl if instl exists', () => { + const instl = { instl: 1 }; + const bidRequestWithInstl = Object.assign({}, singleVideoBidRequest, {ortb2Imp: instl}); + + const reqs = spec.buildRequests([bidRequestWithInstl], defaultBidderRequest); + + const req = extractPayloadOfFirstAndOnlyRequest(reqs); + expect(req.imp[0].instl).to.equal(1); + }); + it('splits multi format bid requests', () => { const combinedBannerAndVideoBidRequest = { bidder: 'smaato', @@ -516,6 +543,17 @@ describe('smaatoBidAdapterTest', () => { expect(req.imp[1].video.sequence).to.be.equal(2); }); + it('sends instl if instl exists', () => { + const instl = { instl: 1 }; + const bidRequestWithInstl = Object.assign({}, longFormVideoBidRequest, {ortb2Imp: instl}); + + const reqs = spec.buildRequests([bidRequestWithInstl], defaultBidderRequest); + + const req = extractPayloadOfFirstAndOnlyRequest(reqs); + expect(req.imp[0].instl).to.equal(1); + expect(req.imp[1].instl).to.equal(1); + }); + it('sends bidfloor when configured', () => { const longFormVideoBidRequestWithFloor = Object.assign({}, longFormVideoBidRequest); longFormVideoBidRequestWithFloor.getFloor = function(arg) { @@ -854,6 +892,29 @@ describe('smaatoBidAdapterTest', () => { expect(req.user.ext.eids).to.have.length(2); }); }); + + describe('schain in request', () => { + it('schain is added to source.ext.schain', () => { + const schain = { + ver: '1.0', + complete: 1, + nodes: [ + { + 'asi': 'asi', + 'sid': 'sid', + 'rid': 'rid', + 'hp': 1 + } + ] + }; + const bidRequestWithSchain = Object.assign({}, singleBannerBidRequest, {schain: schain}); + + const reqs = spec.buildRequests([bidRequestWithSchain], defaultBidderRequest); + + const req = extractPayloadOfFirstAndOnlyRequest(reqs); + expect(req.source.ext.schain).to.deep.equal(schain); + }); + }); }); describe('interpretResponse', () => { diff --git a/test/spec/modules/smartxBidAdapter_spec.js b/test/spec/modules/smartxBidAdapter_spec.js index 4e560c87df3..ddee2fa3347 100644 --- a/test/spec/modules/smartxBidAdapter_spec.js +++ b/test/spec/modules/smartxBidAdapter_spec.js @@ -189,6 +189,14 @@ describe('The smartx adapter', function () { domain: '', publisher: { id: '__name__' + }, + content: { + ext: { + prebid: { + name: 'pbjs', + version: '$prebid.version$' + } + } } }); }); @@ -525,6 +533,7 @@ describe('The smartx adapter', function () { bidderRequestObj.bidRequest.bids[0].params.outstream_options.title = 'abc'; bidderRequestObj.bidRequest.bids[0].params.outstream_options.skipOffset = 2; bidderRequestObj.bidRequest.bids[0].params.outstream_options.desiredBitrate = 123; + bidderRequestObj.bidRequest.bids[0].params.outstream_options.visibilityThreshold = 30; responses[0].renderer.render(responses[0]); diff --git a/test/spec/modules/sonobiBidAdapter_spec.js b/test/spec/modules/sonobiBidAdapter_spec.js index 05ba3f0897b..f56f4e0c12b 100644 --- a/test/spec/modules/sonobiBidAdapter_spec.js +++ b/test/spec/modules/sonobiBidAdapter_spec.js @@ -238,14 +238,17 @@ describe('SonobiBidAdapter', function () { }); describe('.buildRequests', function () { + let sandbox; beforeEach(function() { sinon.stub(userSync, 'canBidderRegisterSync'); sinon.stub(utils, 'getGptSlotInfoForAdUnitCode') - .onFirstCall().returns({gptSlot: '/123123/gpt_publisher/adunit-code-3', divId: 'adunit-code-3-div-id'}) + .onFirstCall().returns({gptSlot: '/123123/gpt_publisher/adunit-code-3', divId: 'adunit-code-3-div-id'}); + sandbox = sinon.createSandbox(); }); afterEach(function() { userSync.canBidderRegisterSync.restore(); utils.getGptSlotInfoForAdUnitCode.restore(); + sandbox.restore(); }); let bidRequest = [{ 'schain': { @@ -333,6 +336,36 @@ describe('SonobiBidAdapter', function () { uspConsent: 'someCCPAString' }; + it('should set fpd if there is any data in ortb2', function() { + const ortb2 = { + site: { + ext: { + data: { + pageType: 'article', + category: 'tools' + } + } + }, + user: { + ext: { + data: { + registered: true, + interests: ['cars'] + } + } + } + }; + + sandbox.stub(config, 'getConfig').callsFake(key => { + const config = { + ortb2: ortb2 + }; + return utils.deepAccess(config, key); + }); + const bidRequests = spec.buildRequests(bidRequest, bidderRequests); + expect(bidRequests.data.fpd).to.equal(JSON.stringify(ortb2)); + }); + it('should populate coppa as 1 if set in config', function () { config.setConfig({coppa: true}); const bidRequests = spec.buildRequests(bidRequest, bidderRequests); diff --git a/test/spec/modules/sortableBidAdapter_spec.js b/test/spec/modules/sortableBidAdapter_spec.js index 17f77e96d51..7357fa77952 100644 --- a/test/spec/modules/sortableBidAdapter_spec.js +++ b/test/spec/modules/sortableBidAdapter_spec.js @@ -112,6 +112,13 @@ describe('sortableBidAdapter', function() { 'key2': 'val2' } }, + 'ortb2Imp': { + 'ext': { + 'data': { + 'pbadslot': 'abc/123' + } + } + }, 'sizes': [ [300, 250] ], @@ -176,6 +183,10 @@ describe('sortableBidAdapter', function() { expect(requestBody.imp[0].floor).to.equal(0.21); }); + it('includes pbadslot in the bid request', function () { + expect(requestBody.imp[0].ext.gpid).to.equal('abc/123'); + }); + it('sets domain and href correctly', function () { expect(requestBody.site.domain).to.equal('example.com'); expect(requestBody.site.page).to.equal('http://example.com/page?param=val'); diff --git a/test/spec/modules/sovrnBidAdapter_spec.js b/test/spec/modules/sovrnBidAdapter_spec.js index 729c48c28f4..09a61c82b6c 100644 --- a/test/spec/modules/sovrnBidAdapter_spec.js +++ b/test/spec/modules/sovrnBidAdapter_spec.js @@ -5,7 +5,7 @@ import * as utils from 'src/utils.js' const ENDPOINT = `https://ap.lijit.com/rtb/bid?src=$$REPO_AND_VERSION$$`; -const adUnitBidRequest = { +const baseBidRequest = { 'bidder': 'sovrn', 'params': { 'tagid': 403370 @@ -19,7 +19,7 @@ const adUnitBidRequest = { 'bidderRequestId': '22edbae2733bf6', 'auctionId': '1d1a030790a475', } -const bidderRequest = { +const baseBidderRequest = { refererInfo: { referer: 'http://example.com/page.html', } @@ -28,28 +28,35 @@ const bidderRequest = { describe('sovrnBidAdapter', function() { describe('isBidRequestValid', function () { it('should return true when required params found', function () { - expect(spec.isBidRequestValid(adUnitBidRequest)).to.equal(true); + expect(spec.isBidRequestValid(baseBidRequest)).to.equal(true); }); it('should return false when tagid not passed correctly', function () { - const bid = {...adUnitBidRequest} - const params = adUnitBidRequest.params - bid.params = {...params} - bid.params.tagid = 'ABCD' - expect(spec.isBidRequestValid(bid)).to.equal(false) + const bidRequest = { + ...baseBidRequest, + 'params': { + ...baseBidRequest.params, + 'tagid': 'ABCD' + }, + }; + + expect(spec.isBidRequestValid(bidRequest)).to.equal(false); }); it('should return false when require params are not passed', function () { - const bid = {...adUnitBidRequest} - bid.params = {}; - expect(spec.isBidRequestValid(bid)).to.equal(false); + const bidRequest = { + ...baseBidRequest, + 'params': {} + } + + expect(spec.isBidRequestValid(bidRequest)).to.equal(false); }); }); describe('buildRequests', function () { describe('basic bid parameters', function() { - const bidRequests = [adUnitBidRequest]; - const request = spec.buildRequests(bidRequests, bidderRequest); + const request = spec.buildRequests([baseBidRequest], baseBidderRequest); + const payload = JSON.parse(request.data); it('sends bid request to our endpoint via POST', function () { expect(request.method).to.equal('POST'); @@ -60,15 +67,65 @@ describe('sovrnBidAdapter', function() { }); it('sets the proper banner object', function() { + const bannerBidRequest = { + ...baseBidRequest, + 'mediaTypes': { + banner: {} + } + } + const request = spec.buildRequests([bannerBidRequest], baseBidderRequest) + + const payload = JSON.parse(request.data) + const impression = payload.imp[0] + + expect(impression.banner.format).to.deep.equal([{w: 300, h: 250}, {w: 300, h: 600}]) + expect(impression.banner.w).to.equal(1) + expect(impression.banner.h).to.equal(1) + }) + + it('sets the proper video object', function() { + const width = 640 + const height = 480 + const mimes = ['video/mp4', 'application/javascript'] + const protocols = [2, 5] + const minduration = 5 + const maxduration = 60 + const startdelay = 0 + const videoBidRequest = { + ...baseBidRequest, + 'mediaTypes': { + video: { + mimes, + protocols, + playerSize: [[width, height], [360, 240]], + minduration, + maxduration, + startdelay + } + } + } + const request = spec.buildRequests([videoBidRequest], baseBidderRequest) + const payload = JSON.parse(request.data) - expect(payload.imp[0].banner.format).to.deep.equal([{w: 300, h: 250}, {w: 300, h: 600}]) - expect(payload.imp[0].banner.w).to.equal(1) - expect(payload.imp[0].banner.h).to.equal(1) + const impression = payload.imp[0] + + expect(impression.video.w).to.equal(width) + expect(impression.video.h).to.equal(height) + expect(impression.video.mimes).to.have.same.members(mimes) + expect(impression.video.protocols).to.have.same.members(protocols) + expect(impression.video.minduration).to.equal(minduration) + expect(impression.video.maxduration).to.equal(maxduration) + expect(impression.video.startdelay).to.equal(startdelay) }) - it('includes the ad unit code int the request', function() { - const payload = JSON.parse(request.data); - expect(payload.imp[0].adunitcode).to.equal('adunit-code') + it('gets correct site info', function() { + expect(payload.site.page).to.equal('http://example.com/page.html'); + expect(payload.site.domain).to.equal('example.com'); + }); + + it('includes the ad unit code in the request', function() { + const impression = payload.imp[0] + expect(impression.adunitcode).to.equal('adunit-code') }) it('converts tagid to string', function () { @@ -77,109 +134,79 @@ describe('sovrnBidAdapter', function() { }) it('accepts a single array as a size', function() { - const singleSize = [{ - 'bidder': 'sovrn', + const singleSizeBidRequest = { + ...baseBidRequest, 'params': { - 'tagid': '403370', 'iv': 'vet' }, - 'adUnitCode': 'adunit-code', 'sizes': [300, 250], - 'bidId': '30b31c1838de1e', - 'bidderRequestId': '22edbae2733bf6', - 'auctionId': '1d1a030790a475' - }] - const request = spec.buildRequests(singleSize, bidderRequest) + 'mediaTypes': { + banner: {} + }, + } + const request = spec.buildRequests([singleSizeBidRequest], baseBidderRequest) + const payload = JSON.parse(request.data) - expect(payload.imp[0].banner.format).to.deep.equal([{w: 300, h: 250}]) - expect(payload.imp[0].banner.w).to.equal(1) - expect(payload.imp[0].banner.h).to.equal(1) + const impression = payload.imp[0] + + expect(impression.banner.format).to.deep.equal([{w: 300, h: 250}]) + expect(impression.banner.w).to.equal(1) + expect(impression.banner.h).to.equal(1) }) it('sends \'iv\' as query param if present', function () { - const ivBidRequests = [{ - 'bidder': 'sovrn', - 'params': { - 'tagid': '403370', - 'iv': 'vet' - }, - 'adUnitCode': 'adunit-code', - 'sizes': [ - [300, 250], - [300, 600] - ], - 'bidId': '30b31c1838de1e', - 'bidderRequestId': '22edbae2733bf6', - 'auctionId': '1d1a030790a475' - }]; - const bidderRequest = { - refererInfo: { - referer: 'http://example.com/page.html', + const ivBidRequest = { + ...baseBidRequest, + params: { + iv: 'vet' } - }; - const request = spec.buildRequests(ivBidRequests, bidderRequest); + } + const request = spec.buildRequests([ivBidRequest], baseBidderRequest) expect(request.url).to.contain('iv=vet') }); it('sends gdpr info if exists', function () { - let consentString = 'BOJ8RZsOJ8RZsABAB8AAAAAZ+A=='; const bidderRequest = { + ...baseBidderRequest, 'bidderCode': 'sovrn', 'auctionId': '1d1a030790a475', 'bidderRequestId': '22edbae2733bf6', 'timeout': 3000, - gdprConsent: { - consentString: consentString, - gdprApplies: true + 'gdprConsent': { + 'consentString': 'BOJ8RZsOJ8RZsABAB8AAAAAZ+A==', + 'gdprApplies': true }, - refererInfo: { - referer: 'http://example.com/page.html', - } + 'bids': [baseBidRequest] }; - bidderRequest.bids = [adUnitBidRequest]; - const data = JSON.parse(spec.buildRequests([adUnitBidRequest], bidderRequest).data); + const { regs, user } = JSON.parse(spec.buildRequests([baseBidRequest], bidderRequest).data) - expect(data.regs.ext.gdpr).to.exist.and.to.be.a('number'); - expect(data.regs.ext.gdpr).to.equal(1); - expect(data.user.ext.consent).to.exist.and.to.be.a('string'); - expect(data.user.ext.consent).to.equal(consentString); - }); + expect(regs.ext.gdpr).to.exist.and.to.be.a('number') + expect(regs.ext.gdpr).to.equal(1) + expect(user.ext.consent).to.exist.and.to.be.a('string') + expect(user.ext.consent).to.equal(bidderRequest.gdprConsent.consentString) + }) it('should send us_privacy if bidderRequest has a value for uspConsent', function () { - const uspString = '1NYN'; const bidderRequest = { + ...baseBidderRequest, 'bidderCode': 'sovrn', 'auctionId': '1d1a030790a475', 'bidderRequestId': '22edbae2733bf6', 'timeout': 3000, - uspConsent: uspString, - refererInfo: { - referer: 'http://example.com/page.html', - } - }; - bidderRequest.bids = [adUnitBidRequest]; + 'uspConsent': '1NYN', + 'bids': [baseBidRequest] + } - const data = JSON.parse(spec.buildRequests([adUnitBidRequest], bidderRequest).data); + const data = JSON.parse(spec.buildRequests([baseBidRequest], bidderRequest).data) - expect(data.regs.ext['us_privacy']).to.equal(uspString); - }); + expect(data.regs.ext['us_privacy']).to.equal(bidderRequest.uspConsent) + }) it('should add schain if present', function() { - const schainRequests = [{ - 'bidder': 'sovrn', - 'params': { - 'tagid': 403370 - }, - 'adUnitCode': 'adunit-code', - 'sizes': [ - [300, 250], - [300, 600] - ], - 'bidId': '30b31c1838de1e', - 'bidderRequestId': '22edbae2733bf6', - 'auctionId': '1d1a030790a475', + const schainRequest = { + ...baseBidRequest, 'schain': { 'ver': '1.0', 'complete': 1, @@ -192,100 +219,89 @@ describe('sovrnBidAdapter', function() { } ] } - }].concat(adUnitBidRequest); - const bidderRequest = { - refererInfo: { - referer: 'http://example.com/page.html', - } - }; - const data = JSON.parse(spec.buildRequests(schainRequests, bidderRequest).data); + } + const schainRequests = [schainRequest, baseBidRequest] + + const data = JSON.parse(spec.buildRequests(schainRequests, baseBidderRequest).data) expect(data.source.ext.schain.nodes.length).to.equal(1) - }); + }) - it('should add ids to the bid request', function() { - const criteoIdRequest = [{ - 'bidder': 'sovrn', - 'params': { - 'tagid': 403370 - }, - 'adUnitCode': 'adunit-code', - 'sizes': [ - [300, 250], - [300, 600] - ], - 'bidId': '30b31c1838de1e', - 'bidderRequestId': '22edbae2733bf6', - 'auctionId': '1d1a030790a475', - 'userId': { - 'criteoId': 'A_CRITEO_ID', - 'tdid': 'SOMESORTOFID', - } - }].concat(adUnitBidRequest); - const bidderRequest = { - refererInfo: { - referer: 'http://example.com/page.html', + it('should add eds to the bid request', function() { + const criteoIdRequest = { + ...baseBidRequest, + userId: { + criteoId: 'A_CRITEO_ID', + tdid: 'SOMESORTOFID', } - }; - - const data = JSON.parse(spec.buildRequests(criteoIdRequest, bidderRequest).data); - expect(data.user.ext.eids[0].source).to.equal('criteo.com') - expect(data.user.ext.eids[0].uids[0].id).to.equal('A_CRITEO_ID') - expect(data.user.ext.eids[0].uids[0].atype).to.equal(1) - expect(data.user.ext.eids[1].source).to.equal('adserver.org') - expect(data.user.ext.eids[1].uids[0].id).to.equal('SOMESORTOFID') - expect(data.user.ext.eids[1].uids[0].ext.rtiPartner).to.equal('TDID') - expect(data.user.ext.eids[1].uids[0].atype).to.equal(1) - expect(data.user.ext.tpid[0].source).to.equal('criteo.com') - expect(data.user.ext.tpid[0].uid).to.equal('A_CRITEO_ID') - expect(data.user.ext.prebid_criteoid).to.equal('A_CRITEO_ID') - }); + } + const criteoIdRequests = [criteoIdRequest, baseBidRequest] + + const ext = JSON.parse(spec.buildRequests(criteoIdRequests, baseBidderRequest).data).user.ext + const firstEID = ext.eids[0] + const secondEID = ext.eids[1] + + expect(firstEID.source).to.equal('criteo.com') + expect(firstEID.uids[0].id).to.equal('A_CRITEO_ID') + expect(firstEID.uids[0].atype).to.equal(1) + expect(secondEID.source).to.equal('adserver.org') + expect(secondEID.uids[0].id).to.equal('SOMESORTOFID') + expect(secondEID.uids[0].ext.rtiPartner).to.equal('TDID') + expect(secondEID.uids[0].atype).to.equal(1) + expect(ext.tpid[0].source).to.equal('criteo.com') + expect(ext.tpid[0].uid).to.equal('A_CRITEO_ID') + expect(ext.prebid_criteoid).to.equal('A_CRITEO_ID') + }) it('should ignore empty segments', function() { - const request = spec.buildRequests([adUnitBidRequest], bidderRequest) + const request = spec.buildRequests([baseBidRequest], baseBidderRequest) const payload = JSON.parse(request.data) + expect(payload.imp[0].ext).to.be.undefined }) it('should pass the segments param value as trimmed deal ids array', function() { - const segmentsRequests = [{ - 'bidder': 'sovrn', - 'params': { - 'segments': ' test1,test2 ' - }, - 'adUnitCode': 'adunit-code', - 'sizes': [ - [300, 250], - [300, 600] - ], - 'bidId': '30b31c1838de1e', - 'bidderRequestId': '22edbae2733bf6', - 'auctionId': '1d1a030790a475' - }] - const request = spec.buildRequests(segmentsRequests, bidderRequest) - const payload = JSON.parse(request.data) - expect(payload.imp[0].ext.deals[0]).to.equal('test1') - expect(payload.imp[0].ext.deals[1]).to.equal('test2') + const segmentsRequest = { + ...baseBidRequest, + params: { + segments: ' test1,test2 ' + } + } + const request = spec.buildRequests([segmentsRequest], baseBidderRequest) + const deals = JSON.parse(request.data).imp[0].ext.deals + + expect(deals[0]).to.equal('test1') + expect(deals[1]).to.equal('test2') }) it('should use the floor provided from the floor module if present', function() { - const floorBid = {...adUnitBidRequest, getFloor: () => ({currency: 'USD', floor: 1.10})} - floorBid.params = { - tagid: 1234, - bidfloor: 2.00 + const floorBid = { + ...baseBidRequest, + getFloor: () => ({currency: 'USD', floor: 1.10}), + params: { + tagid: 1234, + bidfloor: 2.00 + } } - const request = spec.buildRequests([floorBid], bidderRequest) + + const request = spec.buildRequests([floorBid], baseBidderRequest) const payload = JSON.parse(request.data) + expect(payload.imp[0].bidfloor).to.equal(1.10) }) it('should use the floor from the param if there is no floor from the floor module', function() { - const floorBid = {...adUnitBidRequest, getFloor: () => ({})} + const floorBid = { + ...baseBidRequest, + getFloor: () => ({}) + } floorBid.params = { tagid: 1234, bidfloor: 2.00 } - const request = spec.buildRequests([floorBid], bidderRequest) - const payload = JSON.parse(request.data) - expect(payload.imp[0].bidfloor).to.equal(2.00) + + const request = spec.buildRequests([floorBid], baseBidderRequest) + const impression = JSON.parse(request.data).imp[0] + + expect(impression.bidfloor).to.equal(2.00) }) describe('First Party Data', function () { let sandbox @@ -307,54 +323,79 @@ describe('sovrnBidAdapter', function() { data: 'some user data' } } - }; - return utils.deepAccess(cfg, key); - }); - const request = spec.buildRequests([adUnitBidRequest], bidderRequest) - const payload = JSON.parse(request.data) - expect(payload.user.data).to.equal('some user data') - expect(payload.site.keywords).to.equal('test keyword') - expect(payload.site.page).to.equal('http://example.com/page.html') - expect(payload.site.domain).to.equal('example.com') + } + return utils.deepAccess(cfg, key) + }) + + const request = spec.buildRequests([baseBidRequest], baseBidderRequest) + const { user, site } = JSON.parse(request.data) + + expect(user.data).to.equal('some user data') + expect(site.keywords).to.equal('test keyword') + expect(site.page).to.equal('http://example.com/page.html') + expect(site.domain).to.equal('example.com') }) it('should append impression first party data', function () { - const fpdBid = {...adUnitBidRequest} - fpdBid.ortb2Imp = { - ext: { - data: { - pbadslot: 'homepage-top-rect', - adUnitSpecificAttribute: '123' + const fpdBidRequest = { + ...baseBidRequest, + ortb2Imp: { + ext: { + data: { + pbadslot: 'homepage-top-rect', + adUnitSpecificAttribute: '123' + } } } } - const request = spec.buildRequests([fpdBid], bidderRequest) + + const request = spec.buildRequests([fpdBidRequest], baseBidderRequest) const payload = JSON.parse(request.data) + expect(payload.imp[0].ext.data.pbadslot).to.equal('homepage-top-rect') expect(payload.imp[0].ext.data.adUnitSpecificAttribute).to.equal('123') }) it('should not overwrite deals when impression fpd is present', function() { - const fpdBid = {...adUnitBidRequest} - fpdBid.params = {...adUnitBidRequest.params} - fpdBid.params.segments = 'seg1, seg2' - fpdBid.ortb2Imp = { - ext: { - data: { - pbadslot: 'homepage-top-rect', - adUnitSpecificAttribute: '123' + const fpdBid = { + ...baseBidRequest, + params: { + segments: 'seg1, seg2' + }, + ortb2Imp: { + ext: { + data: { + pbadslot: 'homepage-top-rect', + adUnitSpecificAttribute: '123' + } } } } - const request = spec.buildRequests([fpdBid], bidderRequest) - const payload = JSON.parse(request.data) - expect(payload.imp[0].ext.data.pbadslot).to.equal('homepage-top-rect') - expect(payload.imp[0].ext.data.adUnitSpecificAttribute).to.equal('123') - expect(payload.imp[0].ext.deals).to.deep.equal(['seg1', 'seg2']) + + const request = spec.buildRequests([fpdBid], baseBidderRequest) + const impression = JSON.parse(request.data).imp[0] + + expect(impression.ext.data.pbadslot).to.equal('homepage-top-rect') + expect(impression.ext.data.adUnitSpecificAttribute).to.equal('123') + expect(impression.ext.deals).to.deep.equal(['seg1', 'seg2']) }) }) }); describe('interpretResponse', function () { let response; + const baseResponse = { + 'requestId': '263c448586f5a1', + 'cpm': 0.45882675, + 'width': 728, + 'height': 90, + 'creativeId': 'creativelycreatedcreativecreative', + 'dealId': null, + 'currency': 'USD', + 'netRevenue': true, + 'mediaType': 'banner', + 'ad': decodeURIComponent(``), + 'ttl': 90, + 'meta': { advertiserDomains: [] } + } beforeEach(function () { response = { body: { @@ -376,106 +417,175 @@ describe('sovrnBidAdapter', function() { }); it('should get the correct bid response', function () { - let expectedResponse = [{ - 'requestId': '263c448586f5a1', - 'cpm': 0.45882675, - 'width': 728, - 'height': 90, - 'creativeId': 'creativelycreatedcreativecreative', - 'dealId': null, - 'currency': 'USD', - 'netRevenue': true, - 'mediaType': 'banner', + const expectedResponse = { + ...baseResponse, 'ad': decodeURIComponent(`>`), 'ttl': 60000, - 'meta': { advertiserDomains: [] } - }]; + }; + + const result = spec.interpretResponse(response); - let result = spec.interpretResponse(response); - expect(Object.keys(result[0])).to.deep.equal(Object.keys(expectedResponse[0])); + expect(result[0]).to.have.deep.keys(expectedResponse) }); it('crid should default to the bid id if not on the response', function () { delete response.body.seatbid[0].bid[0].crid; - let expectedResponse = [{ - 'requestId': '263c448586f5a1', - 'cpm': 0.45882675, - 'width': 728, - 'height': 90, + + const expectedResponse = { + ...baseResponse, 'creativeId': response.body.seatbid[0].bid[0].id, - 'dealId': null, - 'currency': 'USD', - 'netRevenue': true, - 'mediaType': 'banner', 'ad': decodeURIComponent(``), - 'ttl': 90, - 'meta': { advertiserDomains: [] } - }]; + } - let result = spec.interpretResponse(response); - expect(result[0]).to.deep.equal(expectedResponse[0]); + const result = spec.interpretResponse(response); + + expect(result[0]).to.deep.equal(expectedResponse); }); it('should get correct bid response when dealId is passed', function () { response.body.seatbid[0].bid[0].dealid = 'baking'; - - let expectedResponse = [{ - 'requestId': '263c448586f5a1', - 'cpm': 0.45882675, - 'width': 728, - 'height': 90, - 'creativeId': 'creativelycreatedcreativecreative', + const expectedResponse = { + ...baseResponse, 'dealId': 'baking', - 'currency': 'USD', - 'netRevenue': true, - 'mediaType': 'banner', - 'ad': decodeURIComponent(``), - 'ttl': 90, - 'meta': { advertiserDomains: [] } - }]; + } + + const result = spec.interpretResponse(response) - let result = spec.interpretResponse(response); - expect(result[0]).to.deep.equal(expectedResponse[0]); + expect(result[0]).to.deep.equal(expectedResponse); }); it('should get correct bid response when ttl is set', function () { - response.body.seatbid[0].bid[0].ext = { 'ttl': 480 }; - - let expectedResponse = [{ - 'requestId': '263c448586f5a1', - 'cpm': 0.45882675, - 'width': 728, - 'height': 90, - 'creativeId': 'creativelycreatedcreativecreative', - 'dealId': null, - 'currency': 'USD', - 'netRevenue': true, - 'mediaType': 'banner', - 'ad': decodeURIComponent(``), + response.body.seatbid[0].bid[0].ext = { 'ttl': 480 } + + const expectedResponse = { + ...baseResponse, 'ttl': 480, - 'meta': { advertiserDomains: [] } - }]; + } + + const result = spec.interpretResponse(response) + + expect(result[0]).to.deep.equal(expectedResponse) + }) - let result = spec.interpretResponse(response); - expect(result[0]).to.deep.equal(expectedResponse[0]); + it('handles empty bid response', function () { + const response = { + body: { + 'id': '37386aade21a71', + 'seatbid': [] + } + }; + + const result = spec.interpretResponse(response) + + expect(result.length).to.equal(0); + }); + }); + + describe('interpretResponse video', function () { + let videoResponse; + const bidAdm = 'key%3Dvalue'; + const decodedBidAdm = decodeURIComponent(bidAdm); + const baseVideoResponse = { + 'requestId': '263c448586f5a1', + 'cpm': 0.45882675, + 'width': 640, + 'height': 480, + 'creativeId': 'creativelycreatedcreativecreative', + 'dealId': null, + 'currency': 'USD', + 'netRevenue': true, + 'mediaType': 'video', + 'ttl': 90, + 'meta': { advertiserDomains: [] }, + 'vastXml': decodedBidAdm + } + beforeEach(function () { + videoResponse = { + body: { + 'id': '37386aade21a71', + 'seatbid': [{ + 'bid': [{ + 'id': 'a_403370_332fdb9b064040ddbec05891bd13ab28', + 'crid': 'creativelycreatedcreativecreative', + 'impid': '263c448586f5a1', + 'price': 0.45882675, + 'nurl': '', + 'adm': bidAdm, + 'h': 480, + 'w': 640 + }] + }] + } + }; }); + it('should get the correct bid response', function () { + const expectedResponse = { + ...baseVideoResponse, + 'ttl': 60000, + }; + + const result = spec.interpretResponse(videoResponse); + + expect(result[0]).to.have.deep.keys(expectedResponse) + }); + + it('crid should default to the bid id if not on the response', function () { + delete videoResponse.body.seatbid[0].bid[0].crid; + + const expectedResponse = { + ...baseVideoResponse, + 'creativeId': videoResponse.body.seatbid[0].bid[0].id, + } + + const result = spec.interpretResponse(videoResponse); + + expect(result[0]).to.deep.equal(expectedResponse); + }); + + it('should get correct bid response when dealId is passed', function () { + videoResponse.body.seatbid[0].bid[0].dealid = 'baking'; + const expectedResponse = { + ...baseVideoResponse, + 'dealId': 'baking', + } + + const result = spec.interpretResponse(videoResponse) + + expect(result[0]).to.deep.equal(expectedResponse); + }); + + it('should get correct bid response when ttl is set', function () { + videoResponse.body.seatbid[0].bid[0].ext = { 'ttl': 480 } + + const expectedResponse = { + ...baseVideoResponse, + 'ttl': 480, + } + + const result = spec.interpretResponse(videoResponse) + + expect(result[0]).to.deep.equal(expectedResponse) + }) + it('handles empty bid response', function () { - let response = { + const response = { body: { 'id': '37386aade21a71', 'seatbid': [] } }; - let result = spec.interpretResponse(response); + + const result = spec.interpretResponse(response) + expect(result.length).to.equal(0); }); }); describe('getUserSyncs ', function() { - let syncOptions = { iframeEnabled: true, pixelEnabled: false }; - let iframeDisabledSyncOptions = { iframeEnabled: false, pixelEnabled: false }; - let serverResponse = [ + const syncOptions = { iframeEnabled: true, pixelEnabled: false }; + const iframeDisabledSyncOptions = { iframeEnabled: false, pixelEnabled: false }; + const serverResponse = [ { 'body': { 'id': '546956d68c757f', @@ -524,14 +634,14 @@ describe('sovrnBidAdapter', function() { ]; it('should return if iid present on server response & iframe syncs enabled', function() { - const expectedReturnStatement = [ - { - 'type': 'iframe', - 'url': 'https://ap.lijit.com/beacon?informer=13487408', - } - ]; + const expectedReturnStatement = { + 'type': 'iframe', + 'url': 'https://ap.lijit.com/beacon?informer=13487408', + } + const returnStatement = spec.getUserSyncs(syncOptions, serverResponse); - expect(returnStatement[0]).to.deep.equal(expectedReturnStatement[0]); + + expect(returnStatement[0]).to.deep.equal(expectedReturnStatement); }); it('should include gdpr consent string if present', function() { @@ -539,26 +649,26 @@ describe('sovrnBidAdapter', function() { gdprApplies: 1, consentString: 'BOJ8RZsOJ8RZsABAB8AAAAAZ+A==' } - const expectedReturnStatement = [ - { - 'type': 'iframe', - 'url': `https://ap.lijit.com/beacon?gdpr_consent=${gdprConsent.consentString}&informer=13487408`, - } - ]; + const expectedReturnStatement = { + 'type': 'iframe', + 'url': `https://ap.lijit.com/beacon?gdpr_consent=${gdprConsent.consentString}&informer=13487408`, + } + const returnStatement = spec.getUserSyncs(syncOptions, serverResponse, gdprConsent, ''); - expect(returnStatement[0]).to.deep.equal(expectedReturnStatement[0]); + + expect(returnStatement[0]).to.deep.equal(expectedReturnStatement); }); it('should include us privacy string if present', function() { const uspString = '1NYN'; - const expectedReturnStatement = [ - { - 'type': 'iframe', - 'url': `https://ap.lijit.com/beacon?us_privacy=${uspString}&informer=13487408`, - } - ]; + const expectedReturnStatement = { + 'type': 'iframe', + 'url': `https://ap.lijit.com/beacon?us_privacy=${uspString}&informer=13487408`, + } + const returnStatement = spec.getUserSyncs(syncOptions, serverResponse, null, uspString); - expect(returnStatement[0]).to.deep.equal(expectedReturnStatement[0]); + + expect(returnStatement[0]).to.deep.equal(expectedReturnStatement); }); it('should include all privacy strings if present', function() { @@ -567,57 +677,35 @@ describe('sovrnBidAdapter', function() { consentString: 'BOJ8RZsOJ8RZsABAB8AAAAAZ+A==' } const uspString = '1NYN'; - const expectedReturnStatement = [ - { - 'type': 'iframe', - 'url': `https://ap.lijit.com/beacon?gdpr_consent=${gdprConsent.consentString}&us_privacy=${uspString}&informer=13487408`, - } - ]; - const returnStatement = spec.getUserSyncs(syncOptions, serverResponse, gdprConsent, uspString); - expect(returnStatement[0]).to.deep.equal(expectedReturnStatement[0]); + const expectedReturnStatement = { + 'type': 'iframe', + 'url': `https://ap.lijit.com/beacon?gdpr_consent=${gdprConsent.consentString}&us_privacy=${uspString}&informer=13487408`, + } + + const returnStatement = spec.getUserSyncs(syncOptions, serverResponse, gdprConsent, uspString) + + expect(returnStatement[0]).to.deep.equal(expectedReturnStatement) }); it('should not return if iid missing on server response', function() { const returnStatement = spec.getUserSyncs(syncOptions, []); + expect(returnStatement).to.be.empty; }); it('should not return if iframe syncs disabled', function() { const returnStatement = spec.getUserSyncs(iframeDisabledSyncOptions, serverResponse); + expect(returnStatement).to.be.empty; }); it('should include pixel syncs', function() { - let pixelEnabledOptions = { iframeEnabled: false, pixelEnabled: true }; - const resp2 = { + const pixelEnabledOptions = { iframeEnabled: false, pixelEnabled: true } + + const otherResponce = { + ...serverResponse, 'body': { - 'id': '546956d68c757f-2', - 'seatbid': [ - { - 'bid': [ - { - 'id': 'a_448326_16c2ada014224bee815a90d2248322f5-2', - 'impid': '2a3826aae345f4', - 'price': 1.0099999904632568, - 'nurl': 'http://localhost/rtb/impression?bannerid=220958&campaignid=3890&rtb_tid=15588614-75d2-40ab-b27e-13d2127b3c2e&rpid=1295&seatid=seat1&zoneid=448326&cb=26900712&tid=a_448326_16c2ada014224bee815a90d2248322f5', - 'adm': 'yo a creative', - 'crid': 'cridprebidrtb', - 'w': 160, - 'h': 600 - }, - { - 'id': 'a_430392_beac4c1515da4576acf6cb9c5340b40c-2', - 'impid': '3cf96fd26ed4c5', - 'price': 1.0099999904632568, - 'nurl': 'http://localhost/rtb/impression?bannerid=220957&campaignid=3890&rtb_tid=5bc0e68b-3492-448d-a6f9-26fa3fd0b646&rpid=1295&seatid=seat1&zoneid=430392&cb=62735099&tid=a_430392_beac4c1515da4576acf6cb9c5340b40c', - 'adm': 'yo a creative', - 'crid': 'cridprebidrtb', - 'w': 300, - 'h': 250 - }, - ] - } - ], + ...serverResponse.body, 'ext': { 'iid': 13487408, sync: { @@ -631,10 +719,11 @@ describe('sovrnBidAdapter', function() { ] } } - }, - 'headers': {} + } } - const returnStatement = spec.getUserSyncs(pixelEnabledOptions, [...serverResponse, resp2]); + + const returnStatement = spec.getUserSyncs(pixelEnabledOptions, [...serverResponse, otherResponce]) + expect(returnStatement.length).to.equal(4); expect(returnStatement).to.deep.include.members([ { type: 'image', url: 'http://idprovider1.com' }, @@ -642,34 +731,25 @@ describe('sovrnBidAdapter', function() { { type: 'image', url: 'http://idprovider3.com' }, { type: 'image', url: 'http://idprovider4.com' } ]); - }); - }); + }) + }) describe('prebid 3 upgrade', function() { - const bidRequests = [{ - 'bidder': 'sovrn', + const bidRequest = { + ...baseBidRequest, 'params': { 'tagid': '403370' }, - 'adUnitCode': 'adunit-code', - mediaTypes: { - banner: { - sizes: [ + 'mediaTypes': { + 'banner': { + 'sizes': [ [300, 250], [300, 600] ] } }, - 'bidId': '30b31c1838de1e', - 'bidderRequestId': '22edbae2733bf6', - 'auctionId': '1d1a030790a475' - }]; - const bidderRequest = { - refererInfo: { - referer: 'http://example.com/page.html', - } }; - const request = spec.buildRequests(bidRequests, bidderRequest); + const request = spec.buildRequests([bidRequest], baseBidderRequest); const payload = JSON.parse(request.data); it('gets sizes from mediaTypes.banner', function() { diff --git a/test/spec/modules/sspBCBidAdapter_spec.js b/test/spec/modules/sspBCBidAdapter_spec.js index 75a269c93f7..d9f3ca84a3a 100644 --- a/test/spec/modules/sspBCBidAdapter_spec.js +++ b/test/spec/modules/sspBCBidAdapter_spec.js @@ -518,6 +518,33 @@ describe('SSPBC adapter', function () { expect(payload.user).to.be.an('object').and.to.have.property('[ortb_extensions.consent]', bidRequest.gdprConsent.consentString); }); + it('should send net info and pvid', function () { + expect(payload.user).to.be.an('object').and.to.have.property('data').that.is.an('array'); + + const userData = payload.user.data; + expect(userData.length).to.equal(2); + + const netInfo = userData[0]; + expect(netInfo.id).to.equal('12'); + expect(netInfo.name).to.equal('NetInfo'); + expect(netInfo).to.have.property('segment').that.is.an('array'); + + const pvid = userData[1]; + expect(pvid.id).to.equal('7'); + expect(pvid.name).to.equal('pvid'); + expect(pvid).to.have.property('segment').that.is.an('array'); + expect(pvid.segment[0]).to.have.property('value'); + }); + + it('pvid should be constant on a single page view', function () { + const userData1 = payload.user.data; + const userData2 = payloadNative.user.data; + const pvid1 = userData1[1]; + const pvid2 = userData2[1]; + + expect(pvid1.segment[0].value).to.equal(pvid2.segment[0].value); + }); + it('should build correct native payload', function () { const nativeAssets = payloadNative.imp && payloadNative.imp[0].native.request; @@ -543,13 +570,16 @@ describe('SSPBC adapter', function () { expect(videoAssets).to.have.property('api').that.is.an('array'); }); - it('should create auxilary placement identifier (size_numUsed)', function () { + it('should create auxilary placement identifier (size_numUsed), that is constant for a given adUnit', function () { const extAssets1 = payload.imp && payload.imp[0].ext.data; const extAssets2 = payloadSingle.imp && payloadSingle.imp[0].ext.data; - // note that payload comes from first, and payloadSingle from second auction in the test run + /* + note that payload comes from first, and payloadSingle from second auction in the test run + also, since both have same adUnitName, value of pbsize property should be the same + */ expect(extAssets1).to.have.property('pbsize').that.equals('750x200_1') - expect(extAssets2).to.have.property('pbsize').that.equals('750x200_2') + expect(extAssets2).to.have.property('pbsize').that.equals('750x200_1') }); }); diff --git a/test/spec/modules/synacormediaBidAdapter_spec.js b/test/spec/modules/synacormediaBidAdapter_spec.js index 5f3633ec311..c053771c296 100644 --- a/test/spec/modules/synacormediaBidAdapter_spec.js +++ b/test/spec/modules/synacormediaBidAdapter_spec.js @@ -1231,4 +1231,74 @@ describe('synacormediaBidAdapter ', function () { expect(usersyncs).to.be.an('array').that.is.empty; }); }); + + describe('Bid Requests with price module should use if available', function () { + let validVideoBidRequest = { + bidder: 'synacormedia', + params: { + bidfloor: '0.50', + seatId: 'prebid', + placementId: 'demo1', + pos: 1, + video: {} + }, + renderer: { + url: '../syncOutstreamPlayer.js' + }, + mediaTypes: { + video: { + playerSize: [[300, 250]], + context: 'outstream' + } + }, + adUnitCode: 'div-1', + transactionId: '0869f34e-090b-4b20-84ee-46ff41405a39', + sizes: [[300, 250]], + bidId: '22b3a2268d9f0e', + bidderRequestId: '1d195910597e13', + auctionId: '3375d336-2aea-4ee7-804c-6d26b621ad20', + src: 'client', + bidRequestsCount: 1, + bidderRequestsCount: 1, + bidderWinsCount: 0 + }; + + let validBannerBidRequest = { + bidId: '9876abcd', + sizes: [[300, 250]], + params: { + bidfloor: '0.50', + seatId: 'prebid', + placementId: '1234', + } + }; + + let bidderRequest = { + refererInfo: { + referer: 'http://localhost:9999/' + }, + bidderCode: 'synacormedia', + auctionId: 'f8a75621-d672-4cbb-9275-3db7d74fb110' + }; + + it('should return valid bidfloor using price module for banner/video impression', function () { + let bannerRequest = spec.buildRequests([validBannerBidRequest], bidderRequest); + let videoRequest = spec.buildRequests([validVideoBidRequest], bidderRequest); + + expect(bannerRequest.data.imp[0].bidfloor).to.equal(0.5); + expect(videoRequest.data.imp[0].bidfloor).to.equal(0.5); + + let priceModuleFloor = 3; + let floorResponse = { currency: 'USD', floor: priceModuleFloor }; + + validBannerBidRequest.getFloor = () => { return floorResponse; }; + validVideoBidRequest.getFloor = () => { return floorResponse; }; + + bannerRequest = spec.buildRequests([validBannerBidRequest], bidderRequest); + videoRequest = spec.buildRequests([validVideoBidRequest], bidderRequest); + + expect(bannerRequest.data.imp[0].bidfloor).to.equal(priceModuleFloor); + expect(videoRequest.data.imp[0].bidfloor).to.equal(priceModuleFloor); + }); + }); }); diff --git a/test/spec/modules/talkadsBidAdapter_spec.js b/test/spec/modules/talkadsBidAdapter_spec.js new file mode 100644 index 00000000000..00f52ba7b6a --- /dev/null +++ b/test/spec/modules/talkadsBidAdapter_spec.js @@ -0,0 +1,231 @@ +import {expect} from 'chai'; +import {spec} from 'modules/talkadsBidAdapter.js'; +import {newBidder} from 'src/adapters/bidderFactory.js'; +import {config} from '../../../src/config'; +import {server} from '../../mocks/xhr'; + +describe('TalkAds adapter', function () { + const commonBidderRequest = { + refererInfo: { + referer: 'https://example.com/' + }, + timeout: 3000, + } + const commonBidRequest = { + bidder: 'talkads', + params: { + tag_id: 999999, + bidder_url: 'https://test.natexo-programmatic.com/tad/tag/prebid', + }, + bidId: '1a2b3c4d56e7f0', + auctionId: '12345678-1234-1a2b-3c4d-1a2b3c4d56e7', + transactionId: '4f68b713-04ba-4d7f-8df9-643bcdab5efb', + }; + const nativeBidRequestParams = { + nativeParams: {}, + }; + const bannerBidRequestParams = { + sizes: [[300, 250], [300, 600]], + }; + + /** + * isBidRequestValid + */ + describe('isBidRequestValid1', function() { + it('should fail when config is invalid', function () { + const bidRequest = { + ...commonBidRequest, + ...bannerBidRequestParams, + }; + bidRequest.params = Object.assign({}, bidRequest.params); + delete bidRequest.params; + expect(spec.isBidRequestValid(bidRequest)).to.equal(false); + }); + }); // isBidRequestValid1 + describe('isBidRequestValid2', function() { + it('should fail when config is invalid', function () { + const bidRequest = { + ...commonBidRequest, + ...bannerBidRequestParams, + }; + bidRequest.params = Object.assign({}, bidRequest.params); + delete bidRequest.params.bidder_url; + expect(spec.isBidRequestValid(bidRequest)).to.equal(false); + }); + }); // isBidRequestValid2 + describe('isBidRequestValid3', function() { + it('should fail when config is invalid', function () { + const bidRequest = { + ...commonBidRequest, + ...bannerBidRequestParams, + }; + bidRequest.params = Object.assign({}, bidRequest.params); + delete bidRequest.params.tag_id; + expect(spec.isBidRequestValid(bidRequest)).to.equal(false); + }); + }); // isBidRequestValid3 + describe('isBidRequestValid4', function() { + let bidRequest = { + ...commonBidRequest, + ...bannerBidRequestParams, + }; + it('should succeed when a banner bid is valid', function () { + expect(spec.isBidRequestValid(bidRequest)).to.equal(true); + }); + bidRequest = { + ...commonBidRequest, + ...nativeBidRequestParams, + }; + it('should succeed when a native bid is valid', function () { + expect(spec.isBidRequestValid(bidRequest)).to.equal(true); + }); + }); // isBidRequestValid4 + + /** + * buildRequests + */ + describe('buildRequests1', function() { + let bidRequest = { + ...commonBidRequest, + ...bannerBidRequestParams, + }; + const loServerRequest = { + cur: 'EUR', + timeout: commonBidderRequest.timeout, + auction_id: commonBidRequest.auctionId, + transaction_id: commonBidRequest.transactionId, + bids: [{ id: 0, bid_id: commonBidRequest.bidId, type: 'banner', size: bannerBidRequestParams.sizes }], + gdpr: { applies: false, consent: false }, + }; + it('should generate a valid banner bid request', function () { + let laResponse = spec.buildRequests([bidRequest], commonBidderRequest); + expect(laResponse.method).to.equal('POST'); + expect(laResponse.url).to.equal('https://test.natexo-programmatic.com/tad/tag/prebid/999999'); + expect(laResponse.data).to.equal(JSON.stringify(loServerRequest)); + }); + }); // buildRequests1 + describe('buildRequests2', function() { + let bidRequest = { + ...commonBidRequest, + ...nativeBidRequestParams, + }; + const loServerRequest = { + cur: 'EUR', + timeout: commonBidderRequest.timeout, + auction_id: commonBidRequest.auctionId, + transaction_id: commonBidRequest.transactionId, + bids: [{ id: 0, bid_id: commonBidRequest.bidId, type: 'native', size: [] }], + gdpr: { applies: false, consent: false }, + }; + it('should generate a valid native bid request', function () { + let laResponse = spec.buildRequests([bidRequest], commonBidderRequest); + expect(laResponse.method).to.equal('POST'); + expect(laResponse.url).to.equal('https://test.natexo-programmatic.com/tad/tag/prebid/999999'); + expect(laResponse.data).to.equal(JSON.stringify(loServerRequest)); + }); + const bidderRequest = { + ...commonBidderRequest, + gdprConsent: { gdprApplies: true, consentString: 'yes' } + }; + const loServerRequest2 = { + ...loServerRequest, + gdpr: { applies: true, consent: 'yes' }, + }; + it('should generate a valid native bid request', function () { + let laResponse = spec.buildRequests([bidRequest], bidderRequest); + expect(laResponse.method).to.equal('POST'); + expect(laResponse.url).to.equal('https://test.natexo-programmatic.com/tad/tag/prebid/999999'); + expect(laResponse.data).to.equal(JSON.stringify(loServerRequest2)); + }); + }); // buildRequests2 + + /** + * interpretResponse + */ + describe('interpretResponse1', function() { + it('should return empty array if no valid bids', function () { + const laResult = spec.interpretResponse({}, []) + expect(laResult).to.be.an('array').that.is.empty; + }); + const loServerResult = { + body: { status: 'error', error: 'aie' } + }; + it('should return empty array if there is an error', function () { + const laResult = spec.interpretResponse(loServerResult, []) + expect(laResult).to.be.an('array').that.is.empty; + }); + }); // interpretResponse1 + describe('interpretResponse2', function() { + const loServerResult = { + body: { + status: 'ok', + error: '', + pbid: '6147833a65749742875ace47', + bids: [{ + requestId: commonBidRequest.bidId, + cpm: 0.10, + currency: 'EUR', + width: 300, + height: 250, + ad: 'test ad', + ttl: 60, + creativeId: 'c123a456', + netRevenue: false, + }] + } + }; + const loExpected = [{ + requestId: '1a2b3c4d56e7f0', + cpm: 0.1, + currency: 'EUR', + width: 300, + height: 250, + ad: 'test ad', + ttl: 60, + creativeId: 'c123a456', + netRevenue: false, + pbid: '6147833a65749742875ace47' + }]; + it('should return a correct bid response', function () { + const laResult = spec.interpretResponse(loServerResult, []) + expect(JSON.stringify(laResult)).to.equal(JSON.stringify(loExpected)); + }); + }); // interpretResponse2 + + /** + * onBidWon + */ + describe('onBidWon', function() { + it('should not make an ajax call if pbid is null', function () { + const loBid = { + requestId: '1a2b3c4d56e7f0', + cpm: 0.1, + currency: 'EUR', + width: 300, + height: 250, + ad: 'test ad', + ttl: 60, + creativeId: 'c123a456', + netRevenue: false, + } + spec.onBidWon(loBid) + expect(server.requests.length).to.equals(0); + }); + it('should make an ajax call', function () { + const loBid = { + requestId: '1a2b3c4d56e7f0', + cpm: 0.1, + currency: 'EUR', + width: 300, + height: 250, + ad: 'test ad', + ttl: 60, + creativeId: 'c123a456', + netRevenue: false, + pbid: '6147833a65749742875ace47' + } + spec.onBidWon(loBid) + expect(server.requests[0].url).to.equals('https://test.natexo-programmatic.com/tad/tag/prebidwon/6147833a65749742875ace47'); + }); + }); // onBidWon +}); diff --git a/test/spec/modules/tappxBidAdapter_spec.js b/test/spec/modules/tappxBidAdapter_spec.js index 3fe691847b6..8866670df77 100644 --- a/test/spec/modules/tappxBidAdapter_spec.js +++ b/test/spec/modules/tappxBidAdapter_spec.js @@ -134,12 +134,6 @@ describe('Tappx bid adapter', function () { assert.isTrue(spec.isBidRequestValid(c_BIDREQUEST.bids[0]), JSON.stringify(c_BIDREQUEST)); }); - it('should return false when params are missing', function () { - let badBidRequestParam = JSON.parse(JSON.stringify(c_BIDREQUEST)); - delete badBidRequestParam.bids[0].params; - assert.isFalse(spec.isBidRequestValid(badBidRequestParam.bids[0])); - }); - it('should return false when tappxkey is missing', function () { let badBidRequestTpxkey = JSON.parse(JSON.stringify(c_BIDREQUEST)); ; delete badBidRequestTpxkey.bids[0].params.tappxkey; @@ -165,24 +159,18 @@ describe('Tappx bid adapter', function () { assert.isTrue(spec.isBidRequestValid(badBidRequestNwEp.bids[0])); }); - it('should return false mimes param is missing', function () { - let badBidRequest_mimes = c_BIDDERREQUEST_V; - delete badBidRequest_mimes.bids.mediaTypes.video; - badBidRequest_mimes.bids.mediaTypes.video = {}; - badBidRequest_mimes.bids.mediaTypes.video.context = 'instream'; - badBidRequest_mimes.bids.mediaTypes.video.playerSize = [320, 250]; - assert.isFalse(spec.isBidRequestValid(badBidRequest_mimes.bids), badBidRequest_mimes); - }); - it('should return false for not instream/outstream requests', function () { let badBidRequest_v = c_BIDDERREQUEST_V; delete badBidRequest_v.bids.mediaTypes.banner; badBidRequest_v.bids.mediaTypes.video = {}; badBidRequest_v.bids.mediaTypes.video.context = ''; - badBidRequest_v.bids.mediaTypes.video.mimes = [ 'video/mp4', 'application/javascript' ]; badBidRequest_v.bids.mediaTypes.video.playerSize = [320, 250]; assert.isFalse(spec.isBidRequestValid(badBidRequest_v.bids)); }); + + it('should export the TCF vendor ID', function () { + expect(spec.gvlid).to.equal(628); + }) }); /** @@ -228,7 +216,6 @@ describe('Tappx bid adapter', function () { validBidRequests_V[0].mediaTypes.video = {}; validBidRequests_V[0].mediaTypes.video.playerSize = [640, 480]; validBidRequests_V[0].mediaTypes.video.context = 'instream'; - validBidRequests_V[0].mediaTypes.video.mimes = [ 'video/mp4', 'application/javascript' ]; bidderRequest_V.bids.mediaTypes.context = 'instream'; @@ -248,7 +235,6 @@ describe('Tappx bid adapter', function () { validBidRequests_Voutstream[0].mediaTypes.video = {}; validBidRequests_Voutstream[0].mediaTypes.video.playerSize = [640, 480]; validBidRequests_Voutstream[0].mediaTypes.video.context = 'outstream'; - validBidRequests_Voutstream[0].mediaTypes.video.mimes = [ 'video/mp4', 'application/javascript' ]; bidderRequest_VOutstream.bids.mediaTypes.context = 'outstream'; @@ -269,7 +255,6 @@ describe('Tappx bid adapter', function () { validBidRequests_Voutstream[0].mediaTypes.video.rewarded = 1; validBidRequests_Voutstream[0].mediaTypes.video.playerSize = [640, 480]; validBidRequests_Voutstream[0].mediaTypes.video.context = 'outstream'; - validBidRequests_Voutstream[0].mediaTypes.video.mimes = [ 'video/mp4', 'application/javascript' ]; bidderRequest_VOutstream.bids.mediaTypes.context = 'outstream'; diff --git a/test/spec/modules/targetVideoBidAdapter_spec.js b/test/spec/modules/targetVideoBidAdapter_spec.js new file mode 100644 index 00000000000..0ce6f0fb70d --- /dev/null +++ b/test/spec/modules/targetVideoBidAdapter_spec.js @@ -0,0 +1,96 @@ +import { spec } from '../../../modules/targetVideoBidAdapter.js' + +describe('TargetVideo Bid Adapter', function() { + const bannerRequest = [{ + bidder: 'targetVideo', + mediaTypes: { + banner: { + sizes: [[300, 250]], + } + }, + params: { + placementId: 12345, + } + }]; + + it('Test the bid validation function', function() { + const validBid = spec.isBidRequestValid(bannerRequest[0]); + const invalidBid = spec.isBidRequestValid(null); + + expect(validBid).to.be.true; + expect(invalidBid).to.be.false; + }); + + it('Test the request processing function', function () { + const request = spec.buildRequests(bannerRequest, bannerRequest[0]); + expect(request).to.not.be.empty; + + const payload = JSON.parse(request.data); + expect(payload).to.not.be.empty; + expect(payload.sdk).to.deep.equal({ + source: 'pbjs', + version: '$prebid.version$' + }); + expect(payload.tags[0].id).to.equal(12345); + expect(payload.tags[0].gpid).to.equal('targetVideo'); + expect(payload.tags[0].ad_types[0]).to.equal('video'); + }); + + it('Handle nobid responses', function () { + const responseBody = { + 'version': '0.0.1', + 'tags': [{ + 'uuid': '84ab500420319d', + 'tag_id': 5976557, + 'auction_id': '297492697822162468', + 'nobid': true + }] + }; + const bidderRequest = null; + + const bidResponse = spec.interpretResponse({ body: responseBody }, {bidderRequest}); + expect(bidResponse.length).to.equal(0); + }); + + it('Test the response parsing function', function () { + const responseBody = { + 'tags': [{ + 'uuid': '84ab500420319d', + 'ads': [{ + 'ad_type': 'video', + 'cpm': 0.500000, + 'notify_url': 'https://www.target-video.com/', + 'rtb': { + 'video': { + 'player_width': 640, + 'player_height': 360, + 'asset_url': 'https://www.target-video.com/' + } + } + }] + }] + }; + const bidderRequest = { + bids: [{ + bidId: '84ab500420319d', + adUnitCode: 'code', + mediaTypes: { + banner: { + sizes: [[300, 250]] + } + } + }] + }; + + const bidResponse = spec.interpretResponse({ body: responseBody }, {bidderRequest}); + expect(bidResponse).to.not.be.empty; + + const bid = bidResponse[0]; + expect(bid).to.not.be.empty; + expect(bid.cpm).to.equal(0.675); + expect(bid.width).to.equal(300); + expect(bid.height).to.equal(250); + expect(bid.ad).to.include('') + expect(bid.ad).to.include('initPlayer') + }); +}); diff --git a/test/spec/modules/teadsBidAdapter_spec.js b/test/spec/modules/teadsBidAdapter_spec.js index 7fd3b70398b..75ed7452b57 100644 --- a/test/spec/modules/teadsBidAdapter_spec.js +++ b/test/spec/modules/teadsBidAdapter_spec.js @@ -582,6 +582,93 @@ describe('teadsBidAdapter', () => { }); }); + describe('Global Placement Id', function () { + let bidRequests = [ + { + 'bidder': 'teads', + 'params': { + 'placementId': 10433394, + 'pageId': 1234 + }, + 'adUnitCode': 'adunit-code-1', + 'sizes': [[300, 250], [300, 600]], + 'bidId': '30b31c1838de1e', + 'bidderRequestId': '22edbae2733bf6', + 'auctionId': '1d1a030790a475', + 'creativeId': 'er2ee', + 'deviceWidth': 1680 + }, + { + 'bidder': 'teads', + 'params': { + 'placementId': 10433395, + 'pageId': 1234 + }, + 'adUnitCode': 'adunit-code-2', + 'sizes': [[300, 250], [300, 600]], + 'bidId': '30b31c1838de1f', + 'bidderRequestId': '22edbae2733bf6', + 'auctionId': '1d1a030790a475', + 'creativeId': 'er2ef', + 'deviceWidth': 1680 + } + ]; + + it('should add gpid if ortb2Imp.ext.gpid is present and is non empty', function () { + const updatedBidRequests = bidRequests.map(function(bidRequest, index) { + return { + ...bidRequest, + ortb2Imp: { + ext: { + gpid: '1111/home-left-' + index + } + } + }; + } + ); + const request = spec.buildRequests(updatedBidRequests, bidderResquestDefault); + const payload = JSON.parse(request.data); + + expect(payload.data[0].gpid).to.equal('1111/home-left-0'); + expect(payload.data[1].gpid).to.equal('1111/home-left-1'); + }); + + it('should not add gpid if ortb2Imp.ext.gpid is present but empty', function () { + const updatedBidRequests = bidRequests.map(bidRequest => ({ + ...bidRequest, + ortb2Imp: { + ext: { + gpid: '' + } + } + })); + + const request = spec.buildRequests(updatedBidRequests, bidderResquestDefault); + const payload = JSON.parse(request.data); + + return payload.data.forEach(bid => { + expect(bid).not.to.have.property('gpid'); + }); + }); + + it('should not add gpid if ortb2Imp.ext.gpid is not present', function () { + const updatedBidRequests = bidRequests.map(bidRequest => ({ + ...bidRequest, + ortb2Imp: { + ext: { + } + } + })); + + const request = spec.buildRequests(updatedBidRequests, bidderResquestDefault); + const payload = JSON.parse(request.data); + + return payload.data.forEach(bid => { + expect(bid).not.to.have.property('gpid'); + }); + }); + }); + function checkMediaTypesSizes(mediaTypes, expectedSizes) { const bidRequestWithBannerSizes = Object.assign(bidRequests[0], mediaTypes); const requestWithBannerSizes = spec.buildRequests([bidRequestWithBannerSizes], bidderResquestDefault); diff --git a/test/spec/modules/tripleliftBidAdapter_spec.js b/test/spec/modules/tripleliftBidAdapter_spec.js index d523976826e..6f2674dadc5 100644 --- a/test/spec/modules/tripleliftBidAdapter_spec.js +++ b/test/spec/modules/tripleliftBidAdapter_spec.js @@ -568,6 +568,134 @@ describe('triplelift adapter', function () { expect(payload.user.ext.eids).to.have.lengthOf(4); }); + it('should remove malformed ids that would otherwise break call', function () { + let tdidId = '6bca7f6b-a98a-46c0-be05-6020f7604598'; + let idlEnvId = null; // fail; can't be null + let criteoId = '53e30ea700424f7bbdd793b02abc5d7'; + let pubcid = ''; // fail; can't be empty string + + let bidRequestsMultiple = [ + { ...bidRequests[0], userId: { tdid: tdidId, idl_env: idlEnvId, criteoId, pubcid } }, + { ...bidRequests[0], userId: { tdid: tdidId, idl_env: idlEnvId, criteoId, pubcid } }, + { ...bidRequests[0], userId: { tdid: tdidId, idl_env: idlEnvId, criteoId, pubcid } } + ]; + + let request = tripleliftAdapterSpec.buildRequests(bidRequestsMultiple, bidderRequest); + let payload = request.data; + + expect(payload.user).to.deep.equal({ + ext: { + eids: [ + { + source: 'adserver.org', + uids: [ + { + id: tdidId, + ext: { rtiPartner: 'TDID' } + } + ], + }, + { + source: 'criteo.com', + uids: [ + { + id: criteoId, + ext: { rtiPartner: 'criteoId' } + } + ] + } + ] + } + }); + + expect(payload.user.ext.eids).to.be.an('array'); + expect(payload.user.ext.eids).to.have.lengthOf(2); + + tdidId = {}; // fail; can't be empty object + idlEnvId = { id: '987654' }; // pass + criteoId = [{ id: '123456' }]; // fail; can't be an array + pubcid = '3261d8ad-435d-481d-abd1-9f1a9ec99f0e'; // pass + + bidRequestsMultiple = [ + { ...bidRequests[0], userId: { tdid: tdidId, idl_env: idlEnvId, criteoId, pubcid } }, + { ...bidRequests[0], userId: { tdid: tdidId, idl_env: idlEnvId, criteoId, pubcid } }, + { ...bidRequests[0], userId: { tdid: tdidId, idl_env: idlEnvId, criteoId, pubcid } } + ]; + + request = tripleliftAdapterSpec.buildRequests(bidRequestsMultiple, bidderRequest); + payload = request.data; + + expect(payload.user).to.deep.equal({ + ext: { + eids: [ + { + source: 'liveramp.com', + uids: [ + { + id: '987654', + ext: { rtiPartner: 'idl' } + } + ] + }, + { + source: 'pubcid.org', + uids: [ + { + id: pubcid, + ext: { rtiPartner: 'pubcid' } + } + ] + } + ] + } + }); + + expect(payload.user.ext.eids).to.be.an('array'); + expect(payload.user.ext.eids).to.have.lengthOf(2); + + tdidId = { id: '987654' }; // pass + idlEnvId = { id: 987654 }; // fail; can't be an int + criteoId = '53e30ea700424f7bbdd793b02abc5d7'; // pass + pubcid = { id: '' }; // fail; can't be an empty string + + bidRequestsMultiple = [ + { ...bidRequests[0], userId: { tdid: tdidId, idl_env: idlEnvId, criteoId, pubcid } }, + { ...bidRequests[0], userId: { tdid: tdidId, idl_env: idlEnvId, criteoId, pubcid } }, + { ...bidRequests[0], userId: { tdid: tdidId, idl_env: idlEnvId, criteoId, pubcid } } + ]; + + request = tripleliftAdapterSpec.buildRequests(bidRequestsMultiple, bidderRequest); + payload = request.data; + + expect(payload.user).to.deep.equal({ + ext: { + eids: [ + { + source: 'adserver.org', + uids: [ + { + id: '987654', + ext: { rtiPartner: 'TDID' } + } + ], + }, + { + source: 'criteo.com', + uids: [ + { + id: criteoId, + ext: { rtiPartner: 'criteoId' } + } + ] + } + ] + } + }); + + expect(payload.user.ext.eids).to.be.an('array'); + expect(payload.user.ext.eids).to.have.lengthOf(2); + }); + it('should return a query string for TL call', function () { const request = tripleliftAdapterSpec.buildRequests(bidRequests, bidderRequest); const url = request.url; diff --git a/test/spec/modules/trustxBidAdapter_spec.js b/test/spec/modules/trustxBidAdapter_spec.js index 435f0402f3a..e710bd6d00f 100644 --- a/test/spec/modules/trustxBidAdapter_spec.js +++ b/test/spec/modules/trustxBidAdapter_spec.js @@ -402,7 +402,7 @@ describe('TrustXAdapter', function () { }); it('if segment is present in permutive targeting, payload must have right params', function () { - const permSegments = ['test_perm_1', 'test_perm_2']; + const permSegments = [{id: 'test_perm_1'}, {id: 'test_perm_2'}]; const bidRequestsWithPermutiveTargeting = bidRequests.map((bid) => { return Object.assign({ rtd: { @@ -421,8 +421,8 @@ describe('TrustXAdapter', function () { expect(payload.user.data).to.deep.equal([{ name: 'permutive', segment: [ - {name: 'p_standard', value: permSegments[0]}, - {name: 'p_standard', value: permSegments[1]} + {name: 'p_standard', value: permSegments[0].id}, + {name: 'p_standard', value: permSegments[1].id} ] }]); }); @@ -519,6 +519,48 @@ describe('TrustXAdapter', function () { expect(payload.tmax).to.equal(3000); getConfigStub.restore(); }); + it('should contain imp[].ext.data.adserver if available', function() { + const ortb2Imp = [{ + ext: { + data: { + adserver: { + name: 'ad_server_name', + adslot: '/111111/slot' + }, + pbadslot: '/111111/slot' + } + } + }, { + ext: { + data: { + adserver: { + name: 'ad_server_name', + adslot: '/222222/slot' + }, + pbadslot: '/222222/slot' + } + } + }]; + const bidRequestsWithOrtb2Imp = bidRequests.slice(0, 3).map((bid, ind) => { + return Object.assign(ortb2Imp[ind] ? { ortb2Imp: ortb2Imp[ind] } : {}, bid); + }); + const request = spec.buildRequests(bidRequestsWithOrtb2Imp, bidderRequest); + expect(request.data).to.be.an('string'); + const payload = parseRequest(request.data); + expect(payload.imp[0].ext).to.deep.equal({ + divid: bidRequests[0].adUnitCode, + data: ortb2Imp[0].ext.data, + gpid: ortb2Imp[0].ext.data.adserver.adslot + }); + expect(payload.imp[1].ext).to.deep.equal({ + divid: bidRequests[1].adUnitCode, + data: ortb2Imp[1].ext.data, + gpid: ortb2Imp[1].ext.data.adserver.adslot + }); + expect(payload.imp[2].ext).to.deep.equal({ + divid: bidRequests[2].adUnitCode + }); + }); it('all id like request fields must be a string', function () { const bidderRequestWithNumId = Object.assign({}, bidderRequest, { bidderRequestId: 123123, auctionId: 345345543 }); diff --git a/test/spec/modules/undertoneBidAdapter_spec.js b/test/spec/modules/undertoneBidAdapter_spec.js index 0faa321be5f..c24f63c0b99 100644 --- a/test/spec/modules/undertoneBidAdapter_spec.js +++ b/test/spec/modules/undertoneBidAdapter_spec.js @@ -1,5 +1,7 @@ -import { expect } from 'chai'; -import { spec } from 'modules/undertoneBidAdapter.js'; +import {expect} from 'chai'; +import {spec} from 'modules/undertoneBidAdapter.js'; +import {BANNER, VIDEO} from '../../../src/mediaTypes'; +import {deepClone} from '../../../src/utils'; const URL = 'https://hb.undertone.com/hb'; const BIDDER_CODE = 'undertone'; @@ -60,6 +62,23 @@ const videoBidReq = [{ bidId: '263be71e91dd9d', auctionId: '9ad1fa8d-2297-4660-a018-b39945054746' }]; +const schainObj = { + 'ver': '1.0', + 'complete': 1, + 'nodes': [ + { + 'asi': 'indirectseller.com', + 'sid': '00001', + 'hp': 1 + }, + + { + 'asi': 'indirectseller-2.com', + 'sid': '00002', + 'hp': 2 + } + ] +}; const bidReq = [{ adUnitCode: 'div-gpt-ad-1460505748561-0', bidder: BIDDER_CODE, @@ -72,6 +91,29 @@ const bidReq = [{ auctionId: '9ad1fa8d-2297-4660-a018-b39945054746' }, { + adUnitCode: 'div-gpt-ad-1460505748561-0', + bidder: BIDDER_CODE, + params: { + publisherId: 12345 + }, + sizes: [[1, 1]], + bidId: '453cf42d72bb3c', + auctionId: '6c22f5a5-59df-4dc6-b92c-f433bcf0a874', + schain: schainObj +}]; + +const supplyChainedBidReqs = [{ + adUnitCode: 'div-gpt-ad-1460505748561-0', + bidder: BIDDER_CODE, + params: { + placementId: '10433394', + publisherId: 12345, + }, + sizes: [[300, 250], [300, 600]], + bidId: '263be71e91dd9d', + auctionId: '9ad1fa8d-2297-4660-a018-b39945054746', + schain: schainObj +}, { adUnitCode: 'div-gpt-ad-1460505748561-0', bidder: BIDDER_CODE, params: { @@ -236,6 +278,69 @@ describe('Undertone Adapter', () => { sandbox.restore(); }); + describe('getFloor', function () { + it('should send 0 floor when getFloor is undefined', function() { + const request = spec.buildRequests(videoBidReq, bidderReq); + const bidReq = JSON.parse(request.data)['x-ut-hb-params'][0]; + expect(bidReq.mediaType).to.deep.equal(VIDEO); + expect(bidReq.bidfloor).to.deep.equal(0); + }); + it('should send mocked floor when defined on video media-type', function() { + const clonedVideoBidReqArr = deepClone(videoBidReq); + const mockedFloorResponse = { + currency: 'USD', + floor: 2.3 + }; + clonedVideoBidReqArr[1].getFloor = () => mockedFloorResponse; + + const request = spec.buildRequests(clonedVideoBidReqArr, bidderReq); + const bidReq1 = JSON.parse(request.data)['x-ut-hb-params'][0]; + const bidReq2 = JSON.parse(request.data)['x-ut-hb-params'][1]; + expect(bidReq1.mediaType).to.deep.equal(VIDEO); + expect(bidReq1.bidfloor).to.deep.equal(0); + + expect(bidReq2.mediaType).to.deep.equal(VIDEO); + expect(bidReq2.bidfloor).to.deep.equal(mockedFloorResponse.floor); + }); + it('should send mocked floor on banner media-type', function() { + const clonedValidBidReqArr = [deepClone(validBidReq)]; + const mockedFloorResponse = { + currency: 'USD', + floor: 2.3 + }; + clonedValidBidReqArr[0].getFloor = () => mockedFloorResponse; + + const request = spec.buildRequests(clonedValidBidReqArr, bidderReq); + const bidReq = JSON.parse(request.data)['x-ut-hb-params'][0]; + expect(bidReq.mediaType).to.deep.equal(BANNER); + expect(bidReq.bidfloor).to.deep.equal(mockedFloorResponse.floor); + }); + it('should send 0 floor on invalid currency', function() { + const clonedValidBidReqArr = [deepClone(validBidReq)]; + const mockedFloorResponse = { + currency: 'EUR', + floor: 2.3 + }; + clonedValidBidReqArr[0].getFloor = () => mockedFloorResponse; + + const request = spec.buildRequests(clonedValidBidReqArr, bidderReq); + const bidReq = JSON.parse(request.data)['x-ut-hb-params'][0]; + expect(bidReq.mediaType).to.deep.equal(BANNER); + expect(bidReq.bidfloor).to.deep.equal(0); + }); + }); + describe('supply chain', function () { + it('should send supply chain if found on first bid', function () { + const request = spec.buildRequests(supplyChainedBidReqs, bidderReq); + const commons = JSON.parse(request.data)['commons']; + expect(commons.schain).to.deep.equal(schainObj); + }); + it('should not send supply chain if not found on first bid', function () { + const request = spec.buildRequests(bidReq, bidderReq); + const commons = JSON.parse(request.data)['commons']; + expect(commons.schain).to.not.exist; + }); + }); it('should send request to correct url via POST not in GDPR or CCPA', function () { const request = spec.buildRequests(bidReq, bidderReq); const domainStart = bidderReq.refererInfo.referer.indexOf('//'); diff --git a/test/spec/modules/userId_spec.js b/test/spec/modules/userId_spec.js index 0190bceca70..8ddb02138f5 100644 --- a/test/spec/modules/userId_spec.js +++ b/test/spec/modules/userId_spec.js @@ -49,6 +49,8 @@ import {flocIdSubmodule} from 'modules/flocIdSystem.js' import {amxIdSubmodule} from '../../../modules/amxIdSystem.js'; import {akamaiDAPIdSubmodule} from 'modules/akamaiDAPIdSystem.js' import {kinessoIdSubmodule} from 'modules/kinessoIdSystem.js' +import {adqueryIdSubmodule} from 'modules/adqueryIdSystem.js'; +import * as mockGpt from '../integration/faker/googletag.js'; let assert = require('chai').assert; let expect = require('chai').expect; @@ -114,15 +116,21 @@ describe('User ID', function () { describe('Decorate Ad Units', function () { beforeEach(function () { + // reset mockGpt so nothing else interferes + mockGpt.disable(); + mockGpt.enable(); coreStorage.setCookie('pubcid', '', EXPIRED_COOKIE_DATE); coreStorage.setCookie('pubcid_alt', 'altpubcid200000', (new Date(Date.now() + 5000).toUTCString())); sinon.spy(coreStorage, 'setCookie'); + sinon.stub(utils, 'logWarn'); }); afterEach(function () { + mockGpt.enable(); $$PREBID_GLOBAL$$.requestBids.removeAll(); config.resetConfig(); coreStorage.setCookie.restore(); + utils.logWarn.restore(); }); after(function () { @@ -321,6 +329,50 @@ describe('User ID', function () { expect((getGlobal()).getUserIdsAsEids()).to.deep.equal(createEidsArray((getGlobal()).getUserIds())); }); + it('should set googletag ppid correctly', function () { + let adUnits = [getAdUnitMock()]; + setSubmoduleRegistry([amxIdSubmodule, sharedIdSystemSubmodule, identityLinkSubmodule]); + init(config); + + config.setConfig({ + userSync: { + ppid: 'pubcid.org', + userIds: [ + { name: 'amxId', value: {'amxId': 'amx-id-value-amx-id-value-amx-id-value'} }, + { name: 'pubCommonId', value: {'pubcid': 'pubCommon-id-value-pubCommon-id-value'} }, + { name: 'identityLink', value: {'idl_env': 'identityLink-id-value-identityLink-id-value'} }, + ] + } + }); + // before ppid should not be set + expect(window.googletag._ppid).to.equal(undefined); + requestBidsHook(() => {}, {adUnits}); + // ppid should have been set without dashes and stuff + expect(window.googletag._ppid).to.equal('pubCommonidvaluepubCommonidvalue'); + }); + + it('should log a warning if PPID too big or small', function () { + let adUnits = [getAdUnitMock()]; + setSubmoduleRegistry([sharedIdSystemSubmodule]); + init(config); + + config.setConfig({ + userSync: { + ppid: 'pubcid.org', + userIds: [ + { name: 'pubCommonId', value: {'pubcid': 'pubcommonIdValue'} }, + ] + } + }); + // before ppid should not be set + expect(window.googletag._ppid).to.equal(undefined); + requestBidsHook(() => {}, {adUnits}); + // ppid should NOT have been set + expect(window.googletag._ppid).to.equal(undefined); + // a warning should have been emmited + expect(utils.logWarn.args[0][0]).to.exist.and.to.contain('User ID - Googletag Publisher Provided ID for pubcid.org is not between 32 and 150 characters - pubcommonIdValue'); + }); + it('pbjs.refreshUserIds refreshes', function() { let sandbox = sinon.createSandbox(); @@ -508,7 +560,7 @@ describe('User ID', function () { }); it('handles config with no usersync object', function () { - setSubmoduleRegistry([sharedIdSystemSubmodule, unifiedIdSubmodule, id5IdSubmodule, identityLinkSubmodule, merkleIdSubmodule, netIdSubmodule, intentIqIdSubmodule, zeotapIdPlusSubmodule, pubProvidedIdSubmodule, criteoIdSubmodule, mwOpenLinkIdSubModule, tapadIdSubmodule, uid2IdSubmodule, admixerIdSubmodule, deepintentDpesSubmodule, flocIdSubmodule, akamaiDAPIdSubmodule, amxIdSubmodule, kinessoIdSubmodule]); + setSubmoduleRegistry([sharedIdSystemSubmodule, unifiedIdSubmodule, id5IdSubmodule, identityLinkSubmodule, merkleIdSubmodule, netIdSubmodule, intentIqIdSubmodule, zeotapIdPlusSubmodule, pubProvidedIdSubmodule, criteoIdSubmodule, mwOpenLinkIdSubModule, tapadIdSubmodule, uid2IdSubmodule, admixerIdSubmodule, deepintentDpesSubmodule, flocIdSubmodule, akamaiDAPIdSubmodule, amxIdSubmodule, kinessoIdSubmodule, adqueryIdSubmodule]); init(config); config.setConfig({}); // usersync is undefined, and no logInfo message for 'User ID - usersync config updated' @@ -516,14 +568,14 @@ describe('User ID', function () { }); it('handles config with empty usersync object', function () { - setSubmoduleRegistry([sharedIdSystemSubmodule, unifiedIdSubmodule, id5IdSubmodule, identityLinkSubmodule, merkleIdSubmodule, netIdSubmodule, intentIqIdSubmodule, zeotapIdPlusSubmodule, pubProvidedIdSubmodule, criteoIdSubmodule, mwOpenLinkIdSubModule, tapadIdSubmodule, uid2IdSubmodule, admixerIdSubmodule, deepintentDpesSubmodule, flocIdSubmodule, akamaiDAPIdSubmodule, amxIdSubmodule, kinessoIdSubmodule]); + setSubmoduleRegistry([sharedIdSystemSubmodule, unifiedIdSubmodule, id5IdSubmodule, identityLinkSubmodule, merkleIdSubmodule, netIdSubmodule, intentIqIdSubmodule, zeotapIdPlusSubmodule, pubProvidedIdSubmodule, criteoIdSubmodule, mwOpenLinkIdSubModule, tapadIdSubmodule, uid2IdSubmodule, admixerIdSubmodule, deepintentDpesSubmodule, flocIdSubmodule, akamaiDAPIdSubmodule, amxIdSubmodule, kinessoIdSubmodule, adqueryIdSubmodule]); init(config); config.setConfig({userSync: {}}); expect(typeof utils.logInfo.args[0]).to.equal('undefined'); }); it('handles config with usersync and userIds that are empty objs', function () { - setSubmoduleRegistry([sharedIdSystemSubmodule, unifiedIdSubmodule, id5IdSubmodule, identityLinkSubmodule, merkleIdSubmodule, netIdSubmodule, nextrollIdSubmodule, intentIqIdSubmodule, zeotapIdPlusSubmodule, pubProvidedIdSubmodule, criteoIdSubmodule, mwOpenLinkIdSubModule, tapadIdSubmodule, uid2IdSubmodule, admixerIdSubmodule, deepintentDpesSubmodule, dmdIdSubmodule, flocIdSubmodule, akamaiDAPIdSubmodule, amxIdSubmodule, kinessoIdSubmodule]); + setSubmoduleRegistry([sharedIdSystemSubmodule, unifiedIdSubmodule, id5IdSubmodule, identityLinkSubmodule, merkleIdSubmodule, netIdSubmodule, nextrollIdSubmodule, intentIqIdSubmodule, zeotapIdPlusSubmodule, pubProvidedIdSubmodule, criteoIdSubmodule, mwOpenLinkIdSubModule, tapadIdSubmodule, uid2IdSubmodule, admixerIdSubmodule, deepintentDpesSubmodule, dmdIdSubmodule, flocIdSubmodule, akamaiDAPIdSubmodule, amxIdSubmodule, kinessoIdSubmodule, adqueryIdSubmodule]); init(config); config.setConfig({ userSync: { @@ -534,7 +586,7 @@ describe('User ID', function () { }); it('handles config with usersync and userIds with empty names or that dont match a submodule.name', function () { - setSubmoduleRegistry([sharedIdSystemSubmodule, unifiedIdSubmodule, id5IdSubmodule, identityLinkSubmodule, merkleIdSubmodule, netIdSubmodule, nextrollIdSubmodule, intentIqIdSubmodule, zeotapIdPlusSubmodule, pubProvidedIdSubmodule, criteoIdSubmodule, mwOpenLinkIdSubModule, tapadIdSubmodule, uid2IdSubmodule, admixerIdSubmodule, deepintentDpesSubmodule, dmdIdSubmodule, flocIdSubmodule, akamaiDAPIdSubmodule, amxIdSubmodule, kinessoIdSubmodule]); + setSubmoduleRegistry([sharedIdSystemSubmodule, unifiedIdSubmodule, id5IdSubmodule, identityLinkSubmodule, merkleIdSubmodule, netIdSubmodule, nextrollIdSubmodule, intentIqIdSubmodule, zeotapIdPlusSubmodule, pubProvidedIdSubmodule, criteoIdSubmodule, mwOpenLinkIdSubModule, tapadIdSubmodule, uid2IdSubmodule, admixerIdSubmodule, deepintentDpesSubmodule, dmdIdSubmodule, flocIdSubmodule, akamaiDAPIdSubmodule, amxIdSubmodule, kinessoIdSubmodule, adqueryIdSubmodule]); init(config); config.setConfig({ userSync: { @@ -551,7 +603,7 @@ describe('User ID', function () { }); it('config with 1 configurations should create 1 submodules', function () { - setSubmoduleRegistry([sharedIdSystemSubmodule, unifiedIdSubmodule, id5IdSubmodule, identityLinkSubmodule, netIdSubmodule, nextrollIdSubmodule, intentIqIdSubmodule, zeotapIdPlusSubmodule, pubProvidedIdSubmodule, criteoIdSubmodule, mwOpenLinkIdSubModule, tapadIdSubmodule, uid2IdSubmodule, admixerIdSubmodule, deepintentDpesSubmodule, flocIdSubmodule, akamaiDAPIdSubmodule, amxIdSubmodule, kinessoIdSubmodule]); + setSubmoduleRegistry([sharedIdSystemSubmodule, unifiedIdSubmodule, id5IdSubmodule, identityLinkSubmodule, netIdSubmodule, nextrollIdSubmodule, intentIqIdSubmodule, zeotapIdPlusSubmodule, pubProvidedIdSubmodule, criteoIdSubmodule, mwOpenLinkIdSubModule, tapadIdSubmodule, uid2IdSubmodule, admixerIdSubmodule, deepintentDpesSubmodule, flocIdSubmodule, akamaiDAPIdSubmodule, amxIdSubmodule, kinessoIdSubmodule, adqueryIdSubmodule]); init(config); config.setConfig(getConfigMock(['unifiedId', 'unifiedid', 'cookie'])); @@ -572,8 +624,8 @@ describe('User ID', function () { expect(utils.logInfo.args[0][0]).to.exist.and.to.contain('User ID - usersync config updated for 1 submodules'); }); - it('config with 23 configurations should result in 23 submodules add', function () { - setSubmoduleRegistry([sharedIdSystemSubmodule, unifiedIdSubmodule, id5IdSubmodule, identityLinkSubmodule, liveIntentIdSubmodule, britepoolIdSubmodule, netIdSubmodule, nextrollIdSubmodule, intentIqIdSubmodule, zeotapIdPlusSubmodule, haloIdSubmodule, pubProvidedIdSubmodule, criteoIdSubmodule, mwOpenLinkIdSubModule, tapadIdSubmodule, uid2IdSubmodule, admixerIdSubmodule, deepintentDpesSubmodule, dmdIdSubmodule, flocIdSubmodule, akamaiDAPIdSubmodule, amxIdSubmodule, kinessoIdSubmodule]); + it('config with 24 configurations should result in 24 submodules add', function () { + setSubmoduleRegistry([sharedIdSystemSubmodule, unifiedIdSubmodule, id5IdSubmodule, identityLinkSubmodule, liveIntentIdSubmodule, britepoolIdSubmodule, netIdSubmodule, nextrollIdSubmodule, intentIqIdSubmodule, zeotapIdPlusSubmodule, haloIdSubmodule, pubProvidedIdSubmodule, criteoIdSubmodule, mwOpenLinkIdSubModule, tapadIdSubmodule, uid2IdSubmodule, admixerIdSubmodule, deepintentDpesSubmodule, dmdIdSubmodule, flocIdSubmodule, akamaiDAPIdSubmodule, amxIdSubmodule, kinessoIdSubmodule, adqueryIdSubmodule]); init(config); config.setConfig({ userSync: { @@ -638,14 +690,17 @@ describe('User ID', function () { }, { name: 'kpuid', storage: {name: 'kpuid', type: 'cookie'} + }, { + name: 'qid', + storage: {name: 'qid', type: 'html5'} }] } }); - expect(utils.logInfo.args[0][0]).to.exist.and.to.contain('User ID - usersync config updated for 23 submodules'); + expect(utils.logInfo.args[0][0]).to.exist.and.to.contain('User ID - usersync config updated for 24 submodules'); }); it('config syncDelay updates module correctly', function () { - setSubmoduleRegistry([sharedIdSystemSubmodule, unifiedIdSubmodule, id5IdSubmodule, identityLinkSubmodule, netIdSubmodule, nextrollIdSubmodule, intentIqIdSubmodule, zeotapIdPlusSubmodule, haloIdSubmodule, pubProvidedIdSubmodule, criteoIdSubmodule, mwOpenLinkIdSubModule, tapadIdSubmodule, uid2IdSubmodule, admixerIdSubmodule, deepintentDpesSubmodule, dmdIdSubmodule, flocIdSubmodule, akamaiDAPIdSubmodule, amxIdSubmodule, kinessoIdSubmodule]); + setSubmoduleRegistry([sharedIdSystemSubmodule, unifiedIdSubmodule, id5IdSubmodule, identityLinkSubmodule, netIdSubmodule, nextrollIdSubmodule, intentIqIdSubmodule, zeotapIdPlusSubmodule, haloIdSubmodule, pubProvidedIdSubmodule, criteoIdSubmodule, mwOpenLinkIdSubModule, tapadIdSubmodule, uid2IdSubmodule, admixerIdSubmodule, deepintentDpesSubmodule, dmdIdSubmodule, flocIdSubmodule, akamaiDAPIdSubmodule, amxIdSubmodule, kinessoIdSubmodule, adqueryIdSubmodule]); init(config); config.setConfig({ @@ -661,7 +716,7 @@ describe('User ID', function () { }); it('config auctionDelay updates module correctly', function () { - setSubmoduleRegistry([sharedIdSystemSubmodule, unifiedIdSubmodule, id5IdSubmodule, identityLinkSubmodule, netIdSubmodule, nextrollIdSubmodule, intentIqIdSubmodule, zeotapIdPlusSubmodule, haloIdSubmodule, pubProvidedIdSubmodule, criteoIdSubmodule, mwOpenLinkIdSubModule, tapadIdSubmodule, uid2IdSubmodule, admixerIdSubmodule, deepintentDpesSubmodule, dmdIdSubmodule, flocIdSubmodule, akamaiDAPIdSubmodule, amxIdSubmodule, kinessoIdSubmodule]); + setSubmoduleRegistry([sharedIdSystemSubmodule, unifiedIdSubmodule, id5IdSubmodule, identityLinkSubmodule, netIdSubmodule, nextrollIdSubmodule, intentIqIdSubmodule, zeotapIdPlusSubmodule, haloIdSubmodule, pubProvidedIdSubmodule, criteoIdSubmodule, mwOpenLinkIdSubModule, tapadIdSubmodule, uid2IdSubmodule, admixerIdSubmodule, deepintentDpesSubmodule, dmdIdSubmodule, flocIdSubmodule, akamaiDAPIdSubmodule, amxIdSubmodule, kinessoIdSubmodule, adqueryIdSubmodule]); init(config); config.setConfig({ userSync: { @@ -676,7 +731,7 @@ describe('User ID', function () { }); it('config auctionDelay defaults to 0 if not a number', function () { - setSubmoduleRegistry([sharedIdSystemSubmodule, unifiedIdSubmodule, id5IdSubmodule, identityLinkSubmodule, netIdSubmodule, nextrollIdSubmodule, intentIqIdSubmodule, zeotapIdPlusSubmodule, haloIdSubmodule, pubProvidedIdSubmodule, criteoIdSubmodule, mwOpenLinkIdSubModule, tapadIdSubmodule, uid2IdSubmodule, admixerIdSubmodule, deepintentDpesSubmodule, dmdIdSubmodule, flocIdSubmodule, akamaiDAPIdSubmodule, amxIdSubmodule, kinessoIdSubmodule]); + setSubmoduleRegistry([sharedIdSystemSubmodule, unifiedIdSubmodule, id5IdSubmodule, identityLinkSubmodule, netIdSubmodule, nextrollIdSubmodule, intentIqIdSubmodule, zeotapIdPlusSubmodule, haloIdSubmodule, pubProvidedIdSubmodule, criteoIdSubmodule, mwOpenLinkIdSubModule, tapadIdSubmodule, uid2IdSubmodule, admixerIdSubmodule, deepintentDpesSubmodule, dmdIdSubmodule, flocIdSubmodule, akamaiDAPIdSubmodule, amxIdSubmodule, kinessoIdSubmodule, adqueryIdSubmodule]); init(config); config.setConfig({ userSync: { @@ -1772,7 +1827,38 @@ describe('User ID', function () { }, {adUnits}); }); - it('test hook when pubCommonId, unifiedId, id5Id, identityLink, britepoolId, intentIqId, zeotapIdPlus, netId, haloId, Criteo, UID 2.0, admixerId, amxId, dmdId, kpuid and mwOpenLinkId have data to pass', function (done) { + it('test hook from qid html5', (done) => { + // simulate existing localStorage values + localStorage.setItem('qid', 'testqid'); + localStorage.setItem('qid_exp', ''); + + setSubmoduleRegistry([adqueryIdSubmodule]); + init(config); + config.setConfig(getConfigMock(['qid', 'qid', 'html5'])); + + requestBidsHook(() => { + adUnits.forEach((adUnit) => { + adUnit.bids.forEach((bid) => { + expect(bid).to.have.deep.nested.property('userId.qid'); + expect(bid.userId.qid).to.equal('testqid'); + expect(bid.userIdAsEids[0]).to.deep.equal({ + source: 'adquery.io', + uids: [{ + id: 'testqid', + atype: 1, + }] + }); + }); + }); + + // clear LS + localStorage.removeItem('qid'); + localStorage.removeItem('qid_exp'); + done(); + }, {adUnits}); + }); + + it('test hook when pubCommonId, unifiedId, id5Id, identityLink, britepoolId, intentIqId, zeotapIdPlus, netId, haloId, Criteo, UID 2.0, admixerId, amxId, dmdId, kpuid, qid and mwOpenLinkId have data to pass', function (done) { coreStorage.setCookie('pubcid', 'testpubcid', (new Date(Date.now() + 5000).toUTCString())); coreStorage.setCookie('unifiedid', JSON.stringify({'TDID': 'testunifiedid'}), (new Date(Date.now() + 5000).toUTCString())); coreStorage.setCookie('id5id', JSON.stringify({'universal_uid': 'testid5id'}), (new Date(Date.now() + 5000).toUTCString())); @@ -1789,12 +1875,13 @@ describe('User ID', function () { coreStorage.setCookie('admixerId', 'testadmixerId', (new Date(Date.now() + 5000).toUTCString())); coreStorage.setCookie('deepintentId', 'testdeepintentId', (new Date(Date.now() + 5000).toUTCString())); coreStorage.setCookie('kpuid', 'KINESSO_ID', (new Date(Date.now() + 5000).toUTCString())); - // amxId only supports localStorage localStorage.setItem('amxId', 'test_amxid_id'); localStorage.setItem('amxId_exp', (new Date(Date.now() + 5000)).toUTCString()); - - setSubmoduleRegistry([sharedIdSystemSubmodule, unifiedIdSubmodule, id5IdSubmodule, identityLinkSubmodule, britepoolIdSubmodule, netIdSubmodule, intentIqIdSubmodule, zeotapIdPlusSubmodule, haloIdSubmodule, criteoIdSubmodule, mwOpenLinkIdSubModule, tapadIdSubmodule, uid2IdSubmodule, admixerIdSubmodule, deepintentDpesSubmodule, dmdIdSubmodule, amxIdSubmodule, kinessoIdSubmodule]); + // qid only supports localStorage + localStorage.setItem('qid', 'testqid'); + localStorage.setItem('qid_exp', (new Date(Date.now() + 5000)).toUTCString()); + setSubmoduleRegistry([sharedIdSystemSubmodule, unifiedIdSubmodule, id5IdSubmodule, identityLinkSubmodule, britepoolIdSubmodule, netIdSubmodule, intentIqIdSubmodule, zeotapIdPlusSubmodule, haloIdSubmodule, criteoIdSubmodule, mwOpenLinkIdSubModule, tapadIdSubmodule, uid2IdSubmodule, admixerIdSubmodule, deepintentDpesSubmodule, dmdIdSubmodule, amxIdSubmodule, kinessoIdSubmodule, adqueryIdSubmodule]); init(config); config.setConfig(getConfigMock(['pubCommonId', 'pubcid', 'cookie'], ['unifiedId', 'unifiedid', 'cookie'], @@ -1813,7 +1900,8 @@ describe('User ID', function () { ['admixerId', 'admixerId', 'cookie'], ['amxId', 'amxId', 'html5'], ['deepintentId', 'deepintentId', 'cookie'], - ['kpuid', 'kpuid', 'cookie'])); + ['kpuid', 'kpuid', 'cookie'], + ['qid', 'qid', 'html5'])); requestBidsHook(function () { adUnits.forEach(unit => { @@ -1871,7 +1959,10 @@ describe('User ID', function () { expect(bid).to.have.deep.nested.property('userId.kpuid'); expect(bid.userId.kpuid).to.equal('KINESSO_ID'); - expect(bid.userIdAsEids.length).to.equal(17); + expect(bid).to.have.deep.nested.property('userId.qid'); + expect(bid.userId.qid).to.equal('testqid'); + + expect(bid.userIdAsEids.length).to.equal(18); }); }); coreStorage.setCookie('pubcid', '', EXPIRED_COOKIE_DATE); @@ -1892,6 +1983,8 @@ describe('User ID', function () { coreStorage.setCookie('kpuid', EXPIRED_COOKIE_DATE); localStorage.removeItem('amxId'); localStorage.removeItem('amxId_exp'); + localStorage.removeItem('qid'); + localStorage.removeItem('qid_exp'); done(); }, {adUnits}); }); @@ -2081,8 +2174,10 @@ describe('User ID', function () { localStorage.setItem('amxId', 'test_amxid_id'); localStorage.setItem('amxId_exp', new Date(Date.now() + 5000).toUTCString()) coreStorage.setCookie('kpuid', 'KINESSO_ID', (new Date(Date.now() + 5000).toUTCString())); + localStorage.setItem('qid', 'testqid'); + localStorage.setItem('qid_exp', new Date(Date.now() + 5000).toUTCString()) - setSubmoduleRegistry([sharedIdSystemSubmodule, unifiedIdSubmodule, id5IdSubmodule, identityLinkSubmodule, britepoolIdSubmodule, netIdSubmodule, intentIqIdSubmodule, zeotapIdPlusSubmodule, haloIdSubmodule, uid2IdSubmodule, admixerIdSubmodule, deepintentDpesSubmodule, dmdIdSubmodule, akamaiDAPIdSubmodule, amxIdSubmodule, kinessoIdSubmodule]); + setSubmoduleRegistry([sharedIdSystemSubmodule, unifiedIdSubmodule, id5IdSubmodule, identityLinkSubmodule, britepoolIdSubmodule, netIdSubmodule, intentIqIdSubmodule, zeotapIdPlusSubmodule, haloIdSubmodule, uid2IdSubmodule, admixerIdSubmodule, deepintentDpesSubmodule, dmdIdSubmodule, akamaiDAPIdSubmodule, amxIdSubmodule, kinessoIdSubmodule, adqueryIdSubmodule]); init(config); config.setConfig({ @@ -2120,6 +2215,8 @@ describe('User ID', function () { name: 'amxId', storage: {name: 'amxId', type: 'html5'} }, { name: 'kpuid', storage: {name: 'kpuid', type: 'cookie'} + }, { + name: 'qid', storage: {name: 'qid', type: 'html5'} }] } }); @@ -2191,7 +2288,10 @@ describe('User ID', function () { expect(bid).to.have.deep.nested.property('userId.kpuid'); expect(bid.userId.kpuid).to.equal('KINESSO_ID'); - expect(bid.userIdAsEids.length).to.equal(15); + + expect(bid).to.have.deep.nested.property('userId.qid'); + expect(bid.userId.qid).to.equal('testqid'); + expect(bid.userIdAsEids.length).to.equal(16); }); }); coreStorage.setCookie('pubcid', '', EXPIRED_COOKIE_DATE); diff --git a/test/spec/modules/ventesBidAdapter_spec.js b/test/spec/modules/ventesBidAdapter_spec.js new file mode 100644 index 00000000000..219c24deced --- /dev/null +++ b/test/spec/modules/ventesBidAdapter_spec.js @@ -0,0 +1,845 @@ +import { expect } from 'chai'; +import * as utils from 'src/utils.js'; +import { spec } from 'modules/ventesBidAdapter.js'; + +const BIDDER_URL = 'http://13.234.201.146:8088/va/ad'; + +describe('Ventes Adapter', function () { + const examples = { + adUnit_banner: { + adUnitCode: 'ad_unit_banner', + bidder: 'ventes', + bidderRequestId: 'bid_request_id', + bidId: 'bid_id', + params: { + publisherId: 'agltb3B1Yi1pbmNyDAsSA0FwcBiJkfTUCV', + placementId: 'VA-062-0013-0183', + device: { + ip: '123.145.167.189', + ifa: 'AEBE52E7-03EE-455A-B3C4-E57283966239', + } + }, + mediaTypes: { + banner: { + sizes: [[300, 250]] + } + } + }, + + adUnitContext: { + refererInfo: { + referer: 'https://ventesavenues.in', + } + }, + + serverRequest_banner: { + method: 'POST', + url: 'http://13.234.201.146:8088/va/ad', + data: { + id: 'bid_request_id', + imp: [ + { + id: 'imp_id_banner', + banner: { + format: [{ + w: 300, + h: 200 + }] + } + } + ], + site: { + page: 'https://ventesavenues.in', + domain: 'ventesavenues.in', + name: 'ventesavenues.in' + }, + device: { + ua: '', + ip: '123.145.167.189', + ifa: 'AEBE52E7-03EE-455A-B3C4-E57283966239', + language: 'en' + }, + user: null, + regs: null, + at: 1 + } + }, + serverResponse_banner: { + body: { + cur: 'USD', + seatbid: [ + { + seat: '4', + bid: [ + { + id: 'id', + impid: 'imp_id_banner', + cid: 'campaign_id', + crid: 'creative_id', + adm: '..', + price: 1.5, + w: 300, + h: 200 + } + ] + } + ] + } + } + }; + + describe('isBidRequestValid', function () { + describe('General', function () { + it('should return false when not given an ad unit', function () { + const adUnit = undefined; + + expect(spec.isBidRequestValid(adUnit)).to.equal(false); + }); + + it('should return false when given an invalid ad unit', function () { + const adUnit = 'bad_bid'; + + expect(spec.isBidRequestValid(adUnit)).to.equal(false); + }); + + it('should return false when given an ad unit without bidder code', function () { + const adUnit = utils.deepClone(examples.adUnit_banner); + adUnit.bidder = undefined; + + expect(spec.isBidRequestValid(adUnit)).to.equal(false); + }); + + it('should return false when given an ad unit with a bad bidder code', function () { + const adUnit = utils.deepClone(examples.adUnit_banner); + adUnit.bidder = 'unknownBidder'; + + expect(spec.isBidRequestValid(adUnit)).to.equal(false); + }); + + it('should return false when given an ad unit without ad unit code', function () { + const adUnit = utils.deepClone(examples.adUnit_banner); + adUnit.adUnitCode = undefined; + + expect(spec.isBidRequestValid(adUnit)).to.equal(false); + }); + + it('should return false when given an ad unit with an invalid ad unit code', function () { + const adUnit = utils.deepClone(examples.adUnit_banner); + adUnit.adUnitCode = {}; + + expect(spec.isBidRequestValid(adUnit)).to.equal(false); + }); + + it('should return false when given an ad unit without bid request identifier', function () { + const adUnit = utils.deepClone(examples.adUnit_banner); + adUnit.bidderRequestId = undefined; + + expect(spec.isBidRequestValid(adUnit)).to.equal(false); + }); + + it('should return false when given an ad unit with an invalid bid request identifier', function () { + const adUnit = utils.deepClone(examples.adUnit_banner); + adUnit.bidderRequestId = {}; + + expect(spec.isBidRequestValid(adUnit)).to.equal(false); + }); + + it('should return false when given an ad unit without impression identifier', function () { + const adUnit = utils.deepClone(examples.adUnit_banner); + adUnit.bidId = undefined; + + expect(spec.isBidRequestValid(adUnit)).to.equal(false); + }); + + it('should return false when given an ad unit with an invalid impression identifier', function () { + const adUnit = utils.deepClone(examples.adUnit_banner); + adUnit.bidId = {}; + + expect(spec.isBidRequestValid(adUnit)).to.equal(false); + }); + + it('should return false when given an ad unit without media types', function () { + const adUnit = utils.deepClone(examples.adUnit_banner); + adUnit.mediaTypes = undefined; + + expect(spec.isBidRequestValid(adUnit)).to.equal(false); + }); + + it('should return false when given an ad unit with empty media types', function () { + const adUnit = utils.deepClone(examples.adUnit_banner); + adUnit.mediaTypes = {}; + + expect(spec.isBidRequestValid(adUnit)).to.equal(false); + }); + + it('should return false when given an ad unit with invalid media types', function () { + const adUnit = utils.deepClone(examples.adUnit_banner); + adUnit.mediaTypes = 'bad_media_types'; + + expect(spec.isBidRequestValid(adUnit)).to.equal(false); + }); + }); + + describe('Banner', function () { + it('should return true when given a valid ad unit', function () { + const adUnit = examples.adUnit_banner; + + expect(spec.isBidRequestValid(adUnit)).to.equal(true); + }); + + it('should return true when given a valid ad unit with invalid publisher id', function () { + const adUnit = utils.deepClone(examples.adUnit_banner); + adUnit.params = {}; + adUnit.params.publisherId = undefined; + + expect(spec.isBidRequestValid(adUnit)).to.equal(false); + }); + + it('should return true when given a valid ad unit without placement id', function () { + const adUnit = utils.deepClone(examples.adUnit_banner); + adUnit.params = {}; + adUnit.params.publisherId = 'agltb3B1Yi1pbmNyDAsSA0FwcBiJkfTUCV'; + + expect(spec.isBidRequestValid(adUnit)).to.equal(false); + }); + + it('should return true when given a valid ad unit with invalid placement id', function () { + const adUnit = utils.deepClone(examples.adUnit_banner); + adUnit.params = {}; + adUnit.params.publisherId = 'agltb3B1Yi1pbmNyDAsSA0FwcBiJkfTUCV'; + adUnit.params.placementId = undefined; + + expect(spec.isBidRequestValid(adUnit)).to.equal(false); + }); + + it('should return false when given an ad unit without size', function () { + const adUnit = utils.deepClone(examples.adUnit_banner); + adUnit.mediaTypes.banner.sizes = undefined; + + expect(spec.isBidRequestValid(adUnit)).to.equal(false); + }); + + it('should return false when given an ad unit with an invalid size', function () { + const adUnit = utils.deepClone(examples.adUnit_banner); + adUnit.mediaTypes.banner.sizes = 'bad_banner_size'; + + expect(spec.isBidRequestValid(adUnit)).to.equal(false); + }); + + it('should return false when given an ad unit with an empty size', function () { + const adUnit = utils.deepClone(examples.adUnit_banner); + adUnit.mediaTypes.banner.sizes = []; + + expect(spec.isBidRequestValid(adUnit)).to.equal(false); + }); + + it('should return false when given an ad unit with an invalid size value', function () { + const adUnit = utils.deepClone(examples.adUnit_banner); + adUnit.mediaTypes.banner.sizes = ['bad_banner_size_value']; + + expect(spec.isBidRequestValid(adUnit)).to.equal(false); + }); + + it('should return false when given an ad unit with a size value with less than 2 dimensions', function () { + const adUnit = utils.deepClone(examples.adUnit_banner); + adUnit.mediaTypes.banner.sizes = [[300]]; + + expect(spec.isBidRequestValid(adUnit)).to.equal(false); + }); + + it('should return false when given an ad unit with a size value with more than 2 dimensions', function () { + const adUnit = utils.deepClone(examples.adUnit_banner); + adUnit.mediaTypes.banner.sizes = [[300, 250, 30]]; + + expect(spec.isBidRequestValid(adUnit)).to.equal(false); + }); + + it('should return false when given an ad unit with a negative width value', function () { + const adUnit = utils.deepClone(examples.adUnit_banner); + adUnit.mediaTypes.banner.sizes = [[-300, 250]]; + + expect(spec.isBidRequestValid(adUnit)).to.equal(false); + }); + + it('should return false when given an ad unit with a negative height value', function () { + const adUnit = utils.deepClone(examples.adUnit_banner); + adUnit.mediaTypes.banner.sizes = [[300, -250]]; + + expect(spec.isBidRequestValid(adUnit)).to.equal(false); + }); + + it('should return false when given an ad unit with an invalid width value', function () { + const adUnit = utils.deepClone(examples.adUnit_banner); + adUnit.mediaTypes.banner.sizes = [[false, 250]]; + + expect(spec.isBidRequestValid(adUnit)).to.equal(false); + }); + + it('should return false when given an ad unit with an invalid height value', function () { + const adUnit = utils.deepClone(examples.adUnit_banner); + adUnit.mediaTypes.banner.sizes = [[300, {}]]; + + expect(spec.isBidRequestValid(adUnit)).to.equal(false); + }); + }); + }); + + describe('buildRequests', function () { + describe('ServerRequest', function () { + it('should return a server request when given a valid ad unit and a valid ad unit context', function () { + const adUnits = [examples.adUnit_banner]; + const serverRequests = spec.buildRequests(adUnits, examples.adUnitContext); + expect(serverRequests).to.be.an('array').and.to.have.length(1); + expect(serverRequests[0].method).to.exist.and.to.be.a('string').and.to.equal('POST'); + expect(serverRequests[0].url).to.exist.and.to.be.a('string').and.to.equal(BIDDER_URL); + expect(serverRequests[0].data).to.exist.and.to.be.an('object'); + }); + + it('should return an empty server request list when given an empty ad unit list and a valid ad unit context', function () { + const adUnits = []; + const serverRequests = spec.buildRequests(adUnits, examples.adUnitContext); + expect(serverRequests).to.be.an('array').and.to.have.length(0); + }); + + it('should not return a server request when given no ad unit and a valid ad unit context', function () { + const serverRequests = spec.buildRequests(null, examples.adUnitContext); + expect(serverRequests).to.equal(null); + }); + + it('should not return a server request when given a valid ad unit and no ad unit context', function () { + const adUnits = [examples.adUnit_banner]; + const serverRequests = spec.buildRequests(adUnits, null); + expect(serverRequests).to.be.an('array').and.to.have.length(1); + }); + + it('should not return a server request when given a valid ad unit and an invalid ad unit context', function () { + const adUnits = [examples.adUnit_banner]; + const serverRequests = spec.buildRequests(adUnits, {}); + expect(serverRequests).to.be.an('array').and.to.have.length(1); + }); + }); + + describe('BidRequest', function () { + it('should return a valid server request when given a valid ad unit', function () { + const adUnits = [examples.adUnit_banner]; + const serverRequests = spec.buildRequests(adUnits, examples.adUnitContext); + expect(serverRequests).to.be.an('array').and.to.have.lengthOf(1); + expect(serverRequests[0].data.id).to.exist.and.to.be.a('string').and.to.equal(adUnits[0].bidderRequestId); + expect(serverRequests[0].data.at).to.exist.and.to.be.a('number').and.to.equal(1); + }); + + it('should return one server request when given one valid ad unit', function () { + const adUnits = [examples.adUnit_banner]; + const serverRequests = spec.buildRequests(adUnits, examples.adUnitContext); + expect(serverRequests).to.be.an('array').and.to.have.lengthOf(1); + expect(serverRequests[0].data.id).to.exist.and.to.be.a('string').and.to.equal(adUnits[0].bidderRequestId); + }); + }); + + describe('Impression', function () { + describe('Banner', function () { + it('should return a server request with one impression when given a valid ad unit', function () { + const adUnits = [examples.adUnit_banner]; + + const serverRequests = spec.buildRequests(adUnits, examples.adUnitContext); + + expect(serverRequests).to.be.an('array').and.to.have.lengthOf(1); + + expect(serverRequests[0].data).to.exist.and.to.be.an('object'); + expect(serverRequests[0].data.imp).to.exist.and.to.be.an('array').and.to.have.lengthOf(1); + expect(serverRequests[0].data.imp[0]).to.exist.and.to.be.an('object'); + expect(serverRequests[0].data.imp[0].id).to.exist.and.to.be.a('string').and.to.equal(`${adUnits[0].bidId}`); + expect(serverRequests[0].data.imp[0].banner).to.exist.and.to.be.an('object'); + expect(serverRequests[0].data.imp[0].banner.w).to.exist.and.to.be.a('number').and.to.equal(adUnits[0].mediaTypes.banner.sizes[0][0]); + expect(serverRequests[0].data.imp[0].banner.h).to.exist.and.to.be.a('number').and.to.equal(adUnits[0].mediaTypes.banner.sizes[0][1]); + expect(serverRequests[0].data.imp[0].banner.format).to.exist.and.to.be.an('array').and.to.have.lengthOf(1); + expect(serverRequests[0].data.imp[0].banner.format[0]).to.exist.and.to.be.an('object').and.to.deep.equal({ + w: adUnits[0].mediaTypes.banner.sizes[0][0], + h: adUnits[0].mediaTypes.banner.sizes[0][1] + }); + }); + }); + }); + + describe('Site', function () { + it('should return a server request with site information when given a valid ad unit and a valid ad unit context', function () { + const adUnits = [examples.adUnit_banner]; + + const adUnitContext = examples.adUnitContext; + + const serverRequests = spec.buildRequests(adUnits, adUnitContext); + + expect(serverRequests).to.be.an('array').and.to.have.lengthOf(1); + expect(serverRequests[0].data).to.exist.and.to.be.an('object'); + expect(serverRequests[0].data.id).to.exist.and.to.be.an('string').and.to.equal(adUnits[0].bidderRequestId); + expect(serverRequests[0].data.site).to.exist.and.to.be.an('object'); + expect(serverRequests[0].data.site.page).to.exist.and.to.be.an('string').and.to.equal(adUnitContext.refererInfo.referer); + expect(serverRequests[0].data.site.domain).to.exist.and.to.be.an('string').and.to.equal('ventesavenues.in'); + expect(serverRequests[0].data.site.name).to.exist.and.to.be.an('string').and.to.equal('ventesavenues.in'); + }); + + it('should return a server request without site information when given an ad unit context without referer information', function () { + const adUnits = [examples.adUnit_banner]; + + const adUnitContext = utils.deepClone(examples.adUnitContext); + adUnitContext.refererInfo = undefined; + + const serverRequests = spec.buildRequests(adUnits, adUnitContext); + + expect(serverRequests).to.be.an('array').and.to.have.lengthOf(1); + expect(serverRequests[0].data).to.exist.and.to.be.an('object'); + expect(serverRequests[0].data.id).to.exist.and.to.be.an('string').and.to.equal(adUnits[0].bidderRequestId); + }); + + it('should return a server request without site information when given an ad unit context with invalid referer information', function () { + const adUnits = [examples.adUnit_banner]; + + const adUnitContext = utils.deepClone(examples.adUnitContext); + adUnitContext.refererInfo = 'bad_referer_information'; + + const serverRequests = spec.buildRequests(adUnits, adUnitContext); + + expect(serverRequests).to.be.an('array').and.to.have.lengthOf(1); + expect(serverRequests[0].data).to.exist.and.to.be.an('object'); + expect(serverRequests[0].data.id).to.exist.and.to.be.an('string').and.to.equal(adUnits[0].bidderRequestId); + }); + + it('should return a server request without site information when given an ad unit context without referer', function () { + const adUnits = [examples.adUnit_banner]; + + const adUnitContext = utils.deepClone(examples.adUnitContext); + adUnitContext.refererInfo.referer = undefined; + + const serverRequests = spec.buildRequests(adUnits, adUnitContext); + + expect(serverRequests).to.be.an('array').and.to.have.lengthOf(1); + expect(serverRequests[0].data).to.exist.and.to.be.an('object'); + expect(serverRequests[0].data.id).to.exist.and.to.be.an('string').and.to.equal(adUnits[0].bidderRequestId); + }); + + it('should return a server request without site information when given an ad unit context with an invalid referer', function () { + const adUnits = [examples.adUnit_banner]; + + const adUnitContext = utils.deepClone(examples.adUnitContext); + adUnitContext.refererInfo.referer = {}; + + const serverRequests = spec.buildRequests(adUnits, adUnitContext); + + expect(serverRequests).to.be.an('array').and.to.have.lengthOf(1); + expect(serverRequests[0].data).to.exist.and.to.be.an('object'); + expect(serverRequests[0].data.id).to.exist.and.to.be.an('string').and.to.equal(adUnits[0].bidderRequestId); + }); + + it('should return a server request without site information when given an ad unit context with a misformatted referer', function () { + const adUnits = [examples.adUnit_banner]; + + const adUnitContext = utils.deepClone(examples.adUnitContext); + adUnitContext.refererInfo.referer = 'we-are-adot'; + + const serverRequests = spec.buildRequests(adUnits, adUnitContext); + + expect(serverRequests).to.be.an('array').and.to.have.lengthOf(1); + expect(serverRequests[0].data).to.exist.and.to.be.an('object'); + expect(serverRequests[0].data.id).to.exist.and.to.be.an('string').and.to.equal(adUnits[0].bidderRequestId); + }); + }); + + describe('Device', function () { + it('should return a server request with device information when given a valid ad unit and a valid ad unit context', function () { + const adUnits = [examples.adUnit_banner]; + + const serverRequests = spec.buildRequests(adUnits, examples.adUnitContext); + + expect(serverRequests).to.be.an('array').and.to.have.lengthOf(1); + expect(serverRequests[0].data).to.exist.and.to.be.an('object'); + expect(serverRequests[0].data.id).to.exist.and.to.be.an('string').and.to.equal(adUnits[0].bidderRequestId); + expect(serverRequests[0].data.device).to.exist.and.to.be.an('object'); + expect(serverRequests[0].data.device.ua).to.exist.and.to.be.a('string'); + expect(serverRequests[0].data.device.language).to.exist.and.to.be.a('string'); + }); + }); + + describe('User', function () { + it('should return a server request with user information when given a valid ad unit and a valid ad unit context', function () { + const adUnits = [examples.adUnit_banner]; + + const adUnitContext = examples.adUnitContext; + + const serverRequests = spec.buildRequests(adUnits, adUnitContext); + + expect(serverRequests).to.be.an('array').and.to.have.lengthOf(1); + expect(serverRequests[0].data).to.exist.and.to.be.an('object'); + expect(serverRequests[0].data.id).to.exist.and.to.be.an('string').and.to.equal(adUnits[0].bidderRequestId); + expect(serverRequests[0].data.user).to.exist.and.to.be.an('object'); + }); + + it('should return a server request without user information when not given an ad unit context', function () { + const adUnits = [examples.adUnit_banner]; + + const adUnitContext = undefined; + + const serverRequests = spec.buildRequests(adUnits, adUnitContext); + + expect(serverRequests).to.be.an('array').and.to.have.lengthOf(1); + expect(serverRequests[0].data).to.exist.and.to.be.an('object'); + expect(serverRequests[0].data.id).to.exist.and.to.be.an('string').and.to.equal(adUnits[0].bidderRequestId); + }); + }); + }); + + describe('interpretResponse', function () { + describe('General', function () { + it('should return an ad when given a valid server response with one bid with USD currency', function () { + const serverRequest = examples.serverRequest_banner; + + const serverResponse = utils.deepClone(examples.serverResponse_banner); + serverResponse.body.cur = 'USD'; + const ads = spec.interpretResponse(serverResponse, serverRequest); + + expect(ads).to.be.an('array').and.to.have.length(1); + expect(ads[0].creativeId).to.exist.and.to.be.a('string').and.to.equal(serverResponse.body.seatbid[0].bid[0].crid); + expect(ads[0].cpm).to.exist.and.to.be.a('number').and.to.equal(serverResponse.body.seatbid[0].bid[0].price); + expect(ads[0].currency).to.exist.and.to.be.a('string').and.to.equal(serverResponse.body.cur); + expect(ads[0].netRevenue).to.exist.and.to.be.a('boolean').and.to.equal(true); + expect(ads[0].ttl).to.exist.and.to.be.a('number').and.to.equal(10); + expect(ads[0].height).to.exist.and.to.be.a('number').and.to.equal(serverResponse.body.seatbid[0].bid[0].h); + expect(ads[0].width).to.exist.and.to.be.a('number').and.to.equal(serverResponse.body.seatbid[0].bid[0].w); + expect(ads[0].mediaType).to.exist.and.to.be.a('string').and.to.equal('banner'); + }); + + it('should return no ad when not given a server response', function () { + const ads = spec.interpretResponse(null); + + expect(ads).to.be.an('array').and.to.have.length(0); + }); + + it('should return no ad when not given a server response body', function () { + const serverRequest = examples.serverRequest_banner; + + const serverResponse = utils.deepClone(examples.serverResponse_banner); + serverResponse.body = undefined; + + const ads = spec.interpretResponse(serverResponse, serverRequest); + + expect(ads).to.be.an('array').and.to.have.length(0); + }); + + it('should return no ad when given an invalid server response body', function () { + const serverRequest = examples.serverRequest_banner; + + const serverResponse = utils.deepClone(examples.serverResponse_banner); + serverResponse.body = 'invalid_body'; + + const ads = spec.interpretResponse(serverResponse, serverRequest); + + expect(ads).to.be.an('array').and.to.have.length(0); + }); + + it('should return no ad when given a server response without seat bids', function () { + const serverRequest = examples.serverRequest_banner; + + const serverResponse = utils.deepClone(examples.serverResponse_banner); + serverResponse.body.seatbid = undefined; + + const ads = spec.interpretResponse(serverResponse, serverRequest); + + expect(ads).to.be.an('array').and.to.have.length(0); + }); + + it('should return no ad when given a server response with invalid seat bids', function () { + const serverRequest = examples.serverRequest_banner; + + const serverResponse = utils.deepClone(examples.serverResponse_banner); + serverResponse.body.seatbid = 'invalid_seat_bids'; + + const ads = spec.interpretResponse(serverResponse, serverRequest); + + expect(ads).to.be.an('array').and.to.have.length(0); + }); + + it('should return no ad when given a server response with an empty seat bids array', function () { + const serverRequest = examples.serverRequest_banner; + + const serverResponse = utils.deepClone(examples.serverResponse_banner); + serverResponse.body.seatbid = []; + + const ads = spec.interpretResponse(serverResponse, serverRequest); + + expect(ads).to.be.an('array').and.to.have.length(0); + }); + + it('should return no ad when given a server response with an invalid seat bid', function () { + const serverRequest = examples.serverRequest_banner; + + const serverResponse = utils.deepClone(examples.serverResponse_banner); + serverResponse.body.seatbid[0].bid = 'invalid_bids'; + + const ads = spec.interpretResponse(serverResponse, serverRequest); + + expect(ads).to.be.an('array').and.to.have.length(0); + }); + + it('should return no ad when given a server response with an empty bids array', function () { + const serverRequest = examples.serverRequest_banner; + + const serverResponse = utils.deepClone(examples.serverResponse_banner); + serverResponse.body.seatbid[0].bid = []; + + const ads = spec.interpretResponse(serverResponse, serverRequest); + + expect(ads).to.be.an('array').and.to.have.length(0); + }); + + it('should return no ad when given a server response with an invalid bid', function () { + const serverRequest = examples.serverRequest_banner; + + const serverResponse = utils.deepClone(examples.serverResponse_banner); + serverResponse.body.seatbid[0].bid = ['invalid_bid']; + + const ads = spec.interpretResponse(serverResponse, serverRequest); + + expect(ads).to.be.an('array').and.to.have.length(0); + }); + + it('should return no ad when given a server response with a bid without currency', function () { + const serverRequest = examples.serverRequest_banner; + + const serverResponse = utils.deepClone(examples.serverResponse_banner); + serverResponse.body.cur = undefined; + + const ads = spec.interpretResponse(serverResponse, serverRequest); + + expect(ads).to.be.an('array').and.to.have.length(0); + }); + + it('should return no ad when given a server response with a bid with an invalid currency', function () { + const serverRequest = examples.serverRequest_banner; + + const serverResponse = utils.deepClone(examples.serverResponse_banner); + serverResponse.body.cur = {}; + + const ads = spec.interpretResponse(serverResponse, serverRequest); + + expect(ads).to.be.an('array').and.to.have.length(0); + }); + + it('should return no ad when given a server response with a bid without impression identifier', function () { + const serverRequest = examples.serverRequest_banner; + + const serverResponse = utils.deepClone(examples.serverResponse_banner); + serverResponse.body.seatbid[0].bid[0].impid = undefined; + + const ads = spec.interpretResponse(serverResponse, serverRequest); + + expect(ads).to.be.an('array').and.to.have.length(0); + }); + + it('should return no ad when given a server response with a bid with an invalid impression identifier', function () { + const serverRequest = examples.serverRequest_banner; + + const serverResponse = utils.deepClone(examples.serverResponse_banner); + serverResponse.body.seatbid[0].bid[0].impid = {}; + + const ads = spec.interpretResponse(serverResponse, serverRequest); + + expect(ads).to.be.an('array').and.to.have.length(0); + }); + + it('should return no ad when given a server response with a bid without creative identifier', function () { + const serverRequest = examples.serverRequest_banner; + + const serverResponse = utils.deepClone(examples.serverResponse_banner); + serverResponse.body.seatbid[0].bid[0].crid = undefined; + + const ads = spec.interpretResponse(serverResponse, serverRequest); + + expect(ads).to.be.an('array').and.to.have.length(0); + }); + + it('should return no ad when given a server response with a bid with an invalid creative identifier', function () { + const serverRequest = examples.serverRequest_banner; + + const serverResponse = utils.deepClone(examples.serverResponse_banner); + serverResponse.body.seatbid[0].bid[0].crid = {}; + + const ads = spec.interpretResponse(serverResponse, serverRequest); + + expect(ads).to.be.an('array').and.to.have.length(0); + }); + + it('should return no ad when given a server response with a bid without ad markup and ad serving URL', function () { + const serverRequest = examples.serverRequest_banner; + + const serverResponse = utils.deepClone(examples.serverResponse_banner); + serverResponse.body.seatbid[0].bid[0].adm = undefined; + serverResponse.body.seatbid[0].bid[0].nurl = undefined; + + const ads = spec.interpretResponse(serverResponse, serverRequest); + + expect(ads).to.be.an('array').and.to.have.length(0); + }); + + it('should return no ad when given a server response with a bid with an invalid ad markup', function () { + const serverRequest = examples.serverRequest_banner; + + const serverResponse = utils.deepClone(examples.serverResponse_banner); + serverResponse.body.seatbid[0].bid[0].adm = {}; + + const ads = spec.interpretResponse(serverResponse, serverRequest); + + expect(ads).to.be.an('array').and.to.have.length(0); + }); + + it('should return no ad when given a server response with a bid without bid price', function () { + const serverRequest = examples.serverRequest_banner; + + const serverResponse = utils.deepClone(examples.serverResponse_banner); + serverResponse.body.seatbid[0].bid[0].price = undefined; + + const ads = spec.interpretResponse(serverResponse, serverRequest); + + expect(ads).to.be.an('array').and.to.have.length(0); + }); + + it('should return no ad when given a server response with a bid with an invalid bid price', function () { + const serverRequest = examples.serverRequest_banner; + + const serverResponse = utils.deepClone(examples.serverResponse_banner); + serverResponse.body.seatbid[0].bid[0].price = {}; + + const ads = spec.interpretResponse(serverResponse, serverRequest); + + expect(ads).to.be.an('array').and.to.have.length(0); + }); + + it('should return no ad when given a valid server response and no server request', function () { + const serverRequest = undefined; + + const serverResponse = examples.serverResponse_banner; + + const ads = spec.interpretResponse(serverResponse, serverRequest); + + expect(ads).to.be.an('array').and.to.have.length(0); + }); + + it('should return no ad when given a valid server response and an invalid server request', function () { + const serverRequest = 'bad_server_request'; + + const serverResponse = examples.serverResponse_banner; + + const ads = spec.interpretResponse(serverResponse, serverRequest); + + expect(ads).to.be.an('array').and.to.have.length(0); + }); + + it('should return no ad when given a valid server response and a server request without bid request', function () { + const serverRequest = utils.deepClone(examples.serverRequest_banner); + serverRequest.data = undefined; + + const serverResponse = examples.serverResponse_banner; + + const ads = spec.interpretResponse(serverResponse, serverRequest); + + expect(ads).to.be.an('array').and.to.have.length(0); + }); + + it('should return no ad when given a valid server response and a server request with an invalid bid request', function () { + const serverRequest = utils.deepClone(examples.serverRequest_banner); + serverRequest.data = 'bad_bid_request'; + + const serverResponse = examples.serverResponse_banner; + + const ads = spec.interpretResponse(serverResponse, serverRequest); + + expect(ads).to.be.an('array').and.to.have.length(0); + }); + + it('should return no ad when given a valid server response and a server request without impression', function () { + const serverRequest = utils.deepClone(examples.serverRequest_banner); + serverRequest.data.imp = undefined; + + const serverResponse = examples.serverResponse_banner; + + const ads = spec.interpretResponse(serverResponse, serverRequest); + + expect(ads).to.be.an('array').and.to.have.length(0); + }); + + it('should return no ad when given a valid server response and a server request with an invalid impression field', function () { + const serverRequest = utils.deepClone(examples.serverRequest_banner); + serverRequest.data.imp = 'invalid_impressions'; + + const serverResponse = examples.serverResponse_banner; + + const ads = spec.interpretResponse(serverResponse, serverRequest); + + expect(ads).to.be.an('array').and.to.have.length(0); + }); + }); + + describe('Banner', function () { + it('should return an ad when given a valid server response with one bid', function () { + const serverRequest = examples.serverRequest_banner; + + const serverResponse = examples.serverResponse_banner; + + const ads = spec.interpretResponse(serverResponse, serverRequest); + + expect(ads).to.be.an('array').and.to.have.length(1); + expect(ads[0].adUrl).to.equal(null); + expect(ads[0].creativeId).to.exist.and.to.be.a('string').and.to.equal(serverResponse.body.seatbid[0].bid[0].crid); + expect(ads[0].cpm).to.exist.and.to.be.a('number').and.to.equal(serverResponse.body.seatbid[0].bid[0].price); + expect(ads[0].currency).to.exist.and.to.be.a('string').and.to.equal(serverResponse.body.cur); + expect(ads[0].netRevenue).to.exist.and.to.be.a('boolean').and.to.equal(true); + expect(ads[0].ttl).to.exist.and.to.be.a('number').and.to.equal(10); + expect(ads[0].height).to.exist.and.to.be.a('number').and.to.equal(serverResponse.body.seatbid[0].bid[0].h); + expect(ads[0].width).to.exist.and.to.be.a('number').and.to.equal(serverResponse.body.seatbid[0].bid[0].w); + expect(ads[0].mediaType).to.exist.and.to.be.a('string').and.to.equal('banner'); + }); + + it('should return no ad when given a server response with a bid without height', function () { + const serverRequest = examples.serverRequest_banner; + + const serverResponse = utils.deepClone(examples.serverResponse_banner); + serverResponse.body.seatbid[0].bid[0].h = undefined; + + const ads = spec.interpretResponse(serverResponse, serverRequest); + + expect(ads).to.be.an('array').and.to.have.length(0); + }); + + it('should return no ad when given a server response with a bid with an invalid height', function () { + const serverRequest = examples.serverRequest_banner; + + const serverResponse = utils.deepClone(examples.serverResponse_banner); + serverResponse.body.seatbid[0].bid[0].h = {}; + + const ads = spec.interpretResponse(serverResponse, serverRequest); + + expect(ads).to.be.an('array').and.to.have.length(0); + }); + + it('should return no ad when given a server response with a bid without width', function () { + const serverRequest = examples.serverRequest_banner; + + const serverResponse = utils.deepClone(examples.serverResponse_banner); + serverResponse.body.seatbid[0].bid[0].w = undefined; + + const ads = spec.interpretResponse(serverResponse, serverRequest); + + expect(ads).to.be.an('array').and.to.have.length(0); + }); + + it('should return no ad when given a server response with a bid with an invalid width', function () { + const serverRequest = examples.serverRequest_banner; + + const serverResponse = utils.deepClone(examples.serverResponse_banner); + serverResponse.body.seatbid[0].bid[0].w = {}; + + const ads = spec.interpretResponse(serverResponse, serverRequest); + + expect(ads).to.be.an('array').and.to.have.length(0); + }); + }); + }); +}); diff --git a/test/spec/modules/videobyteBidAdapter_spec.js b/test/spec/modules/videobyteBidAdapter_spec.js index b8e41829031..f7ea0698956 100644 --- a/test/spec/modules/videobyteBidAdapter_spec.js +++ b/test/spec/modules/videobyteBidAdapter_spec.js @@ -153,7 +153,7 @@ describe('VideoByteBidAdapter', function () { it('should create a POST request for every bid', function () { const requests = spec.buildRequests([bidRequest], bidderRequest); expect(requests[0].method).to.equal('POST'); - expect(requests[0].url).to.equal(spec.ENDPOINT + bidRequest.params.pubId); + expect(requests[0].url).to.equal(spec.ENDPOINT + '?pid=' + bidRequest.params.pubId); }); it('should attach request data', function () { @@ -172,7 +172,7 @@ describe('VideoByteBidAdapter', function () { bidRequest.params.video.e2etest = true; const requests = spec.buildRequests([bidRequest], bidderRequest); expect(requests[0].method).to.equal('POST'); - expect(requests[0].url).to.equal(spec.ENDPOINT + 'e2etest'); + expect(requests[0].url).to.equal(spec.ENDPOINT + '?pid=e2etest'); }); it('should attach End 2 End test data', function () { diff --git a/test/spec/modules/vidoomyBidAdapter_spec.js b/test/spec/modules/vidoomyBidAdapter_spec.js index 37452914e79..52f522bdcfc 100644 --- a/test/spec/modules/vidoomyBidAdapter_spec.js +++ b/test/spec/modules/vidoomyBidAdapter_spec.js @@ -4,6 +4,7 @@ import { newBidder } from 'src/adapters/bidderFactory.js'; import { INSTREAM } from '../../../src/video'; const ENDPOINT = `https://d.vidoomy.com/api/rtbserver/prebid/`; +const PIXELS = ['/test.png', '/test2.png?gdpr={{GDPR}}&gdpr_consent={{GDPR_CONSENT}}'] describe('vidoomyBidAdapter', function() { const adapter = newBidder(spec); @@ -101,24 +102,24 @@ describe('vidoomyBidAdapter', function() { }); it('attaches source and version to endpoint URL as query params', function () { - expect(request[0].url).to.include(ENDPOINT); - expect(request[1].url).to.include(ENDPOINT); + expect(request[0].url).to.equal(ENDPOINT); + expect(request[1].url).to.equal(ENDPOINT); }); it('only accepts first width and height sizes', function () { - expect(request[0].url).to.include('w=300'); - expect(request[0].url).to.include('h=250'); - expect(request[0].url).to.not.include('w=200'); - expect(request[0].url).to.not.include('h=100'); - expect(request[1].url).to.include('w=400'); - expect(request[1].url).to.include('h=225'); + expect('' + request[0].data.w).to.equal('300'); + expect('' + request[0].data.h).to.equal('250'); + expect('' + request[0].data.w).to.not.equal('200'); + expect('' + request[0].data.h).to.not.equal('100'); + expect('' + request[1].data.w).to.equal('400'); + expect('' + request[1].data.h).to.equal('225'); }); it('should send id and pid parameters', function () { - expect(request[0].url).to.include('id=123123'); - expect(request[0].url).to.include('pid=123123'); - expect(request[1].url).to.include('id=456456'); - expect(request[1].url).to.include('pid=456456'); + expect('' + request[0].data.id).to.equal('123123'); + expect('' + request[0].data.pid).to.equal('123123'); + expect('' + request[1].data.id).to.equal('456456'); + expect('' + request[1].data.pid).to.equal('456456'); }); }); @@ -182,7 +183,8 @@ describe('vidoomyBidAdapter', function() { 'networkName': null, 'primaryCatId': 'IAB3-1', 'secondaryCatIds': null - } + }, + 'pixels': PIXELS } } @@ -206,5 +208,22 @@ describe('vidoomyBidAdapter', function() { expect(result[0].requestId).to.equal(serverResponseBanner.body.requestId); }); + + it('should sync user cookies', function () { + const GDPR_CONSENT = 'GDPR_TEST' + const result = spec.getUserSyncs({ + pixelEnabled: true + }, [serverResponseBanner], { consentString: GDPR_CONSENT, gdprApplies: 1 }, null) + expect(result).to.eql([ + { + type: 'image', + url: PIXELS[0] + }, + { + type: 'image', + url: `/test2.png?gdpr=1&gdpr_consent=${GDPR_CONSENT}` + } + ]) + }); }); }); diff --git a/test/spec/modules/viewability_spec.js b/test/spec/modules/viewability_spec.js new file mode 100644 index 00000000000..ab2753daf53 --- /dev/null +++ b/test/spec/modules/viewability_spec.js @@ -0,0 +1,280 @@ +import { expect } from 'chai'; +import * as sinon from 'sinon'; +import * as utils from 'src/utils.js'; +import * as viewability from 'modules/viewability.js'; + +describe('viewability test', () => { + describe('start measurement', () => { + let sandbox; + let intersectionObserverStub; + let setTimeoutStub; + let observeCalled; + let unobserveCalled; + let ti = 1; + beforeEach(() => { + observeCalled = false; + unobserveCalled = false; + sandbox = sinon.sandbox.create(); + + let fakeIntersectionObserver = (stateChange, options) => { + return { + observe: (element) => { + observeCalled = true; + stateChange([{ isIntersecting: true }]); + }, + unobserve: (element) => { + unobserveCalled = true; + }, + }; + }; + + intersectionObserverStub = sandbox.stub(window, 'IntersectionObserver').callsFake(fakeIntersectionObserver); + setTimeoutStub = sandbox.stub(window, 'setTimeout').callsFake((callback, timeout) => { + callback(); + return ti++; + }); + }); + + afterEach(() => { + sandbox.restore(); + }); + + it('should trigger appropriate callbacks', () => { + viewability.startMeasurement('0', {}, { method: 'img', value: 'http://my.tracker/123' }, { inViewThreshold: 0.5, timeInView: 1000 }); + + sinon.assert.called(intersectionObserverStub); + sinon.assert.called(setTimeoutStub); + expect(observeCalled).to.equal(true); + expect(unobserveCalled).to.equal(true); + }); + + it('should trigger img tracker', () => { + let triggerPixelSpy = sandbox.spy(utils, ['triggerPixel']); + viewability.startMeasurement('1', {}, { method: 'img', value: 'http://my.tracker/123' }, { inViewThreshold: 0.5, timeInView: 1000 }); + expect(triggerPixelSpy.callCount).to.equal(1); + }); + + it('should trigger js tracker', () => { + let insertHtmlIntoIframeSpy = sandbox.spy(utils, ['insertHtmlIntoIframe']); + viewability.startMeasurement('2', {}, { method: 'js', value: 'http://my.tracker/123.js' }, { inViewThreshold: 0.5, timeInView: 1000 }); + expect(insertHtmlIntoIframeSpy.callCount).to.equal(1); + }); + + it('should trigger callback tracker', () => { + let callbackFired = false; + viewability.startMeasurement('3', {}, { method: 'callback', value: () => { callbackFired = true; } }, { inViewThreshold: 0.5, timeInView: 1000 }); + expect(callbackFired).to.equal(true); + }); + + it('should check for vid uniqueness', () => { + let logWarnSpy = sandbox.spy(utils, 'logWarn'); + viewability.startMeasurement('4', {}, { method: 'js', value: 'http://my.tracker/123.js' }, { inViewThreshold: 0.5, timeInView: 1000 }); + expect(logWarnSpy.callCount).to.equal(0); + + viewability.startMeasurement('4', {}, { method: 'js', value: 'http://my.tracker/123.js' }, { inViewThreshold: 0.5, timeInView: 1000 }); + expect(logWarnSpy.callCount).to.equal(1); + expect(logWarnSpy.calledWith(`${viewability.MODULE_NAME}: must provide an unregistered vid`, '4')).to.equal(true); + }); + + it('should check for valid criteria', () => { + let logWarnSpy = sandbox.spy(utils, 'logWarn'); + viewability.startMeasurement('5', {}, { method: 'js', value: 'http://my.tracker/123.js' }, { timeInView: 1000 }); + expect(logWarnSpy.callCount).to.equal(1); + expect(logWarnSpy.calledWith(`${viewability.MODULE_NAME}: missing criteria`, { timeInView: 1000 })).to.equal(true); + }); + + it('should check for valid tracker', () => { + let logWarnSpy = sandbox.spy(utils, 'logWarn'); + viewability.startMeasurement('6', {}, { method: 'callback', value: 'string' }, { inViewThreshold: 0.5, timeInView: 1000 }); + expect(logWarnSpy.callCount).to.equal(1); + expect(logWarnSpy.calledWith(`${viewability.MODULE_NAME}: invalid tracker`, { method: 'callback', value: 'string' })).to.equal(true); + }); + + it('should check if element provided', () => { + let logWarnSpy = sandbox.spy(utils, 'logWarn'); + viewability.startMeasurement('7', undefined, { method: 'js', value: 'http://my.tracker/123.js' }, { timeInView: 1000 }); + expect(logWarnSpy.callCount).to.equal(1); + expect(logWarnSpy.calledWith(`${viewability.MODULE_NAME}: no html element provided`)).to.equal(true); + }); + }); + + describe('stop measurement', () => { + let sandbox; + let intersectionObserverStub; + let setTimeoutStub; + let clearTimeoutStub; + let observeCalled; + let unobserveCalled; + let stateChangeBackup; + let ti = 1; + beforeEach(() => { + observeCalled = false; + unobserveCalled = false; + sandbox = sinon.sandbox.create(); + + let fakeIntersectionObserver = (stateChange, options) => { + return { + observe: (element) => { + stateChangeBackup = stateChange; + observeCalled = true; + stateChange([{ isIntersecting: true }]); + }, + unobserve: (element) => { + unobserveCalled = true; + }, + }; + }; + + intersectionObserverStub = sandbox.stub(window, 'IntersectionObserver').callsFake(fakeIntersectionObserver); + setTimeoutStub = sandbox.stub(window, 'setTimeout').callsFake((callback, timeout) => { + // skipping the callback + return ti++; + }); + clearTimeoutStub = sandbox.stub(window, 'clearTimeout').callsFake((timeoutId) => { }); + }); + + afterEach(() => { + sandbox.restore(); + }); + + it('should clear the timeout', () => { + viewability.startMeasurement('10', {}, { method: 'img', value: 'http://my.tracker/123' }, { inViewThreshold: 0.5, timeInView: 1000 }); + stateChangeBackup([{ isIntersecting: false }]); + sinon.assert.called(intersectionObserverStub); + sinon.assert.called(setTimeoutStub); + sinon.assert.called(clearTimeoutStub); + expect(observeCalled).to.equal(true); + }); + + it('should unobserve', () => { + viewability.startMeasurement('11', {}, { method: 'img', value: 'http://my.tracker/123' }, { inViewThreshold: 0.5, timeInView: 1000 }); + sinon.assert.called(intersectionObserverStub); + sinon.assert.called(setTimeoutStub); + expect(observeCalled).to.equal(true); + expect(unobserveCalled).to.equal(false); + + viewability.stopMeasurement('11'); + expect(unobserveCalled).to.equal(true); + sinon.assert.called(clearTimeoutStub); + }); + + it('should check for vid existence', () => { + let logWarnSpy = sandbox.spy(utils, 'logWarn'); + viewability.stopMeasurement('100'); + expect(logWarnSpy.callCount).to.equal(1); + expect(logWarnSpy.calledWith(`${viewability.MODULE_NAME}: must provide a registered vid`, '100')).to.equal(true); + }); + }); + + describe('handle creative messages', () => { + let sandbox; + let intersectionObserverStub; + let setTimeoutStub; + let observeCalled; + let unobserveCalled; + let ti = 1; + let getElementsByTagStub; + let getElementByIdStub; + + let fakeContentWindow = {}; + beforeEach(() => { + observeCalled = false; + unobserveCalled = false; + sandbox = sinon.sandbox.create(); + + let fakeIntersectionObserver = (stateChange, options) => { + return { + observe: (element) => { + observeCalled = true; + stateChange([{ isIntersecting: true }]); + }, + unobserve: (element) => { + unobserveCalled = true; + }, + }; + }; + + intersectionObserverStub = sandbox.stub(window, 'IntersectionObserver').callsFake(fakeIntersectionObserver); + setTimeoutStub = sandbox.stub(window, 'setTimeout').callsFake((callback, timeout) => { + callback(); + return ti++; + }); + + getElementsByTagStub = sandbox.stub(document, 'getElementsByTagName').callsFake((tagName) => { + return [{ + contentWindow: fakeContentWindow, + }]; + }); + getElementByIdStub = sandbox.stub(document, 'getElementById').callsFake((id) => { + return {}; + }); + }); + + afterEach(() => { + sandbox.restore(); + }); + + it('should find element by contentWindow', () => { + let viewabilityRecord = { + vid: 1000, + tracker: { + value: 'http://my.tracker/123', + method: 'img', + }, + criteria: { inViewThreshold: 0.5, timeInView: 1000 }, + message: 'Prebid Viewability', + action: 'startMeasurement', + }; + let data = JSON.stringify(viewabilityRecord); + + viewability.receiveMessage({ + data: data, + source: fakeContentWindow, + }); + + sinon.assert.called(getElementsByTagStub); + sinon.assert.called(intersectionObserverStub); + sinon.assert.called(setTimeoutStub); + expect(observeCalled).to.equal(true); + expect(unobserveCalled).to.equal(true); + }); + + it('should find element by id', () => { + let viewabilityRecord = { + vid: 1001, + tracker: { + value: 'http://my.tracker/123', + method: 'img', + }, + criteria: { inViewThreshold: 0.5, timeInView: 1000 }, + message: 'Prebid Viewability', + action: 'startMeasurement', + elementId: '1', + }; + let data = JSON.stringify(viewabilityRecord); + viewability.receiveMessage({ + data: data, + }); + + sinon.assert.called(getElementByIdStub); + sinon.assert.called(intersectionObserverStub); + sinon.assert.called(setTimeoutStub); + expect(observeCalled).to.equal(true); + expect(unobserveCalled).to.equal(true); + }); + + it('should stop measurement', () => { + let viewabilityRecord = { + vid: 1001, + message: 'Prebid Viewability', + action: 'stopMeasurement', + }; + let data = JSON.stringify(viewabilityRecord); + viewability.receiveMessage({ + data: data, + }); + + expect(unobserveCalled).to.equal(true); + }); + }); +}); diff --git a/test/spec/modules/visxBidAdapter_spec.js b/test/spec/modules/visxBidAdapter_spec.js index 99ab327eda4..d29eb6c25dc 100755 --- a/test/spec/modules/visxBidAdapter_spec.js +++ b/test/spec/modules/visxBidAdapter_spec.js @@ -3,6 +3,7 @@ import { spec } from 'modules/visxBidAdapter.js'; import { config } from 'src/config.js'; import { newBidder } from 'src/adapters/bidderFactory.js'; import * as utils from 'src/utils.js'; +import { makeSlot } from '../integration/faker/googletag.js'; describe('VisxAdapter', function () { const adapter = newBidder(spec); @@ -44,7 +45,8 @@ describe('VisxAdapter', function () { videoBid.mediaTypes = { video: { context: 'instream', - playerSize: [[400, 300]] + mimes: ['video/mp4'], + protocols: [3, 6] } }; expect(spec.isBidRequestValid(videoBid)).to.equal(false); @@ -55,9 +57,7 @@ describe('VisxAdapter', function () { videoBid.mediaTypes = { video: { context: 'instream', - playerSize: [[400, 300]], - mimes: ['video/mp4'], - protocols: [3, 6] + playerSize: [[400, 300]] } }; expect(spec.isBidRequestValid(videoBid)).to.equal(true); @@ -146,17 +146,17 @@ describe('VisxAdapter', function () { const expectedFullImps = [{ 'id': '30b31c1838de1e', 'banner': {'format': [{'w': 300, 'h': 250}, {'w': 300, 'h': 600}]}, - 'ext': {'bidder': {'uid': 903535}} + 'ext': {'bidder': {'uid': 903535, 'adslotExists': false}} }, { 'id': '3150ccb55da321', 'banner': {'format': [{'w': 728, 'h': 90}, {'w': 300, 'h': 250}]}, - 'ext': {'bidder': {'uid': 903535}} + 'ext': {'bidder': {'uid': 903535, 'adslotExists': false}} }, { 'id': '42dbe3a7168a6a', 'banner': {'format': [{'w': 300, 'h': 250}, {'w': 300, 'h': 600}]}, - 'ext': {'bidder': {'uid': 903536}} + 'ext': {'bidder': {'uid': 903536, 'adslotExists': false}} }, { 'id': '39a4e3a7168a6a', @@ -453,7 +453,122 @@ describe('VisxAdapter', function () { 'imp': [{ 'id': '39aff3a7169a6a', 'banner': {'format': [{'w': 300, 'h': 250}, {'w': 300, 'h': 600}]}, - 'ext': {'bidder': {'uid': 903538}} + 'ext': {'bidder': {'uid': 903538, 'adslotExists': false}} + }], + 'tmax': 3000, + 'cur': ['EUR'], + 'source': { + 'ext': { + 'wrapperType': 'Prebid_js', + 'wrapperVersion': '$prebid.version$' + } + }, + 'site': {'page': referrer} + }); + }); + }); + + describe('buildRequests (check ad slot exists)', function () { + function parseRequest(url) { + const res = {}; + (url.split('?')[1] || '').split('&').forEach((it) => { + const couple = it.split('='); + res[couple[0]] = decodeURIComponent(couple[1]); + }); + return res; + } + const bidderRequest = { + timeout: 3000, + refererInfo: { + referer: 'https://example.com' + } + }; + const referrer = bidderRequest.refererInfo.referer; + const bidRequests = [ + { + 'bidder': 'visx', + 'params': { + 'uid': 903535 + }, + 'adUnitCode': 'visx-adunit-code-1', + 'sizes': [[300, 250], [300, 600]], + 'bidId': '30b31c1838de1e', + 'bidderRequestId': '22edbae2733bf6', + 'auctionId': '1d1a030790a475', + }, + { + 'bidder': 'visx', + 'params': { + 'uid': 903535 + }, + 'adUnitCode': 'visx-adunit-code-2', + 'sizes': [[300, 250], [300, 600]], + 'bidId': '30b31c1838de1e', + 'bidderRequestId': '22edbae2733bf6', + 'auctionId': '1d1a030790a475', + } + ]; + let sandbox; + let documentStub; + + before(function() { + sandbox = sinon.sandbox.create(); + documentStub = sandbox.stub(document, 'getElementById'); + documentStub.withArgs('visx-adunit-code-1').returns({ + id: 'visx-adunit-code-1' + }); + documentStub.withArgs('visx-adunit-element-2').returns({ + id: 'visx-adunit-element-2' + }); + }); + + after(function() { + sandbox.restore(); + }); + + it('should find ad slot by ad unit code as element id', function () { + const request = spec.buildRequests([bidRequests[0]], bidderRequest); + const payload = parseRequest(request.url); + expect(payload).to.be.an('object'); + expect(payload).to.have.property('auids', '903535'); + + const postData = request.data; + expect(postData).to.be.an('object'); + expect(postData).to.deep.equal({ + 'id': '22edbae2733bf6', + 'imp': [{ + 'id': '30b31c1838de1e', + 'banner': {'format': [{'w': 300, 'h': 250}, {'w': 300, 'h': 600}]}, + 'ext': {'bidder': {'uid': 903535, 'adslotExists': true}} + }], + 'tmax': 3000, + 'cur': ['EUR'], + 'source': { + 'ext': { + 'wrapperType': 'Prebid_js', + 'wrapperVersion': '$prebid.version$' + } + }, + 'site': {'page': referrer} + }); + }); + + it('should find ad slot by ad unit code as adUnitPath', function () { + makeSlot({code: 'visx-adunit-code-2', divId: 'visx-adunit-element-2'}); + + const request = spec.buildRequests([bidRequests[1]], bidderRequest); + const payload = parseRequest(request.url); + expect(payload).to.be.an('object'); + expect(payload).to.have.property('auids', '903535'); + + const postData = request.data; + expect(postData).to.be.an('object'); + expect(postData).to.deep.equal({ + 'id': '22edbae2733bf6', + 'imp': [{ + 'id': '30b31c1838de1e', + 'banner': {'format': [{'w': 300, 'h': 250}, {'w': 300, 'h': 600}]}, + 'ext': {'bidder': {'uid': 903535, 'adslotExists': true}} }], 'tmax': 3000, 'cur': ['EUR'], @@ -1150,7 +1265,52 @@ describe('VisxAdapter', function () { it('onTimeout', function () { const data = { timeout: 3000, bidId: '23423', params: { uid: 1 } }; spec.onTimeout(data); - expect(utils.triggerPixel.calledOnceWith('https://t.visx.net/track/bid_timeout?data=' + JSON.stringify(data))).to.equal(true); + expect(utils.triggerPixel.calledOnceWith('https://t.visx.net/track/bid_timeout//' + JSON.stringify(data))).to.equal(true); + }); + }); + + describe('user sync', function () { + function parseUrl(url) { + const [, path, querySt] = url.match(/^https?:\/\/[^\/]+(?:\/([^?]+)?)?(?:\?(.+)?)?$/) || []; + const query = {}; + (querySt || '').split('&').forEach((q) => { + var kv = q.split('='); + if (kv[0]) { + query[kv[0]] = decodeURIComponent(kv[1] || ''); + } + }); + return { path, query }; + } + it('should call iframe', function () { + let syncs = spec.getUserSyncs({ + iframeEnabled: true + }); + + expect(Array.isArray(syncs)).to.equal(true); + expect(syncs.length).to.equal(1); + expect(syncs[0]).to.have.property('type', 'iframe'); + expect(syncs[0]).to.have.property('url'); + expect(syncs[0].url).to.be.an('string'); + + const { path, query } = parseUrl(syncs[0].url); + expect(path).to.equal('push_sync'); + expect(query).to.deep.equal({iframe: '1'}); + }); + + it('should call image', function () { + let syncs = spec.getUserSyncs({ + pixelEnabled: true + }); + + expect(Array.isArray(syncs)).to.equal(true); + expect(syncs.length).to.equal(1); + expect(syncs[0]).to.have.property('type', 'image'); + expect(syncs[0]).to.have.property('url'); + expect(syncs[0].url).to.be.an('string'); + + const { path, query } = parseUrl(syncs[0].url); + expect(path).to.equal('push_sync'); + expect(query).to.deep.equal({}); }); }); }); diff --git a/test/spec/modules/weboramaRtdProvider_spec.js b/test/spec/modules/weboramaRtdProvider_spec.js index 155f26990a7..081a6f06876 100644 --- a/test/spec/modules/weboramaRtdProvider_spec.js +++ b/test/spec/modules/weboramaRtdProvider_spec.js @@ -1,14 +1,21 @@ -import { setBigseaContextualProfile, weboramaSubmodule } from 'modules/weboramaRtdProvider.js'; -import { server } from 'test/mocks/xhr.js'; -import {config} from 'src/config.js'; - -const responseHeader = {'Content-Type': 'application/json'}; - -// TODO fix it +import { + weboramaSubmodule +} from 'modules/weboramaRtdProvider.js'; +import { + server +} from 'test/mocks/xhr.js'; +import { + storage, + DEFAULT_LOCAL_STORAGE_USER_PROFILE_KEY +} from '../../../modules/weboramaRtdProvider.js'; + +const responseHeader = { + 'Content-Type': 'application/json' +}; describe('weboramaRtdProvider', function() { describe('weboramaSubmodule', function() { - it('successfully instantiates and call contextual api', function () { + it('successfully instantiates and call contextual api', function() { const moduleConfig = { params: { weboCtxConf: { @@ -18,271 +25,458 @@ describe('weboramaRtdProvider', function() { } }; - expect(weboramaSubmodule.init(moduleConfig)).to.equal(true); - - let request = server.requests[0]; - - expect(request.url).to.equal('https://ctx.weborama.com/api/profile?token=foo&url=https%3A%2F%2Fprebid.org&'); - expect(request.method).to.equal('GET') + expect(weboramaSubmodule.init(moduleConfig)).to.equal(true); }); - it('instantiate without token should fail', function () { + + it('instantiate without contextual token should fail', function() { const moduleConfig = { params: { weboCtxConf: {} } }; - expect(weboramaSubmodule.init(moduleConfig)).to.equal(false); + expect(weboramaSubmodule.init(moduleConfig)).to.equal(false); + }); + + it('instantiate with empty weboUserData conf should return true', function() { + const moduleConfig = { + params: { + weboUserDataConf: {} + } + }; + expect(weboramaSubmodule.init(moduleConfig)).to.equal(true); }); }); - describe('Add Contextual Data', function() { + describe('Handle Set Targeting', function() { + let sandbox; + beforeEach(function() { - let conf = { - site: { - ext: { - data: { - inventory: ['value1'] + sandbox = sinon.sandbox.create(); + + storage.removeDataFromLocalStorage('webo_wam2gam_entry'); + }); + + afterEach(function() { + sandbox.restore(); + }); + + describe('Add Contextual Data', function() { + it('should set gam targeting and send to bidders by default', function() { + const moduleConfig = { + params: { + weboCtxConf: { + token: 'foo', + targetURL: 'https://prebid.org', } } - }, - user: { - ext: { - data: { - visitor: ['value2'] + }; + const data = { + webo_ctx: ['foo', 'bar'], + webo_ds: ['baz'], + }; + const adUnitsCodes = ['adunit1', 'adunit2']; + const reqBidsConfigObj = { + adUnits: [{ + bids: [{ + bidder: 'smartadserver' + }] + }] + }; + const onDoneSpy = sinon.spy(); + + expect(weboramaSubmodule.init(moduleConfig)).to.be.true; + weboramaSubmodule.getBidRequestData(reqBidsConfigObj, onDoneSpy, moduleConfig); + + let request = server.requests[0]; + + expect(request.method).to.equal('GET'); + expect(request.url).to.equal('https://ctx.weborama.com/api/profile?token=foo&url=https%3A%2F%2Fprebid.org&'); + expect(request.withCredentials).to.be.false; + + request.respond(200, responseHeader, JSON.stringify(data)); + + expect(onDoneSpy.calledOnce).to.be.true; + + const targeting = weboramaSubmodule.getTargetingData(adUnitsCodes, moduleConfig); + + expect(targeting).to.deep.equal({ + 'adunit1': data, + 'adunit2': data, + }); + + expect(reqBidsConfigObj.adUnits[0].bids[0].params.target).to.equal('webo_ctx=foo;webo_ctx=bar;webo_ds=baz'); + }); + + it('should set gam targeting but not send to bidders with setPrebidTargeting=true/sendToBidders=false', function() { + const moduleConfig = { + params: { + weboCtxConf: { + token: 'foo', + targetURL: 'https://prebid.org', + setPrebidTargeting: true, + sendToBidders: false, } } - }, - cur: ['USD'] - }; + }; + const data = { + webo_ctx: ['foo', 'bar'], + webo_ds: ['baz'], + }; + const adUnitsCodes = ['adunit1', 'adunit2']; + const reqBidsConfigObj = { + adUnits: [{ + bids: [{ + bidder: 'smartadserver', + params: { + target: 'foo=bar' + } + }] + }] + }; + const onDoneSpy = sinon.spy(); + + expect(weboramaSubmodule.init(moduleConfig)).to.be.true; + weboramaSubmodule.getBidRequestData(reqBidsConfigObj, onDoneSpy, moduleConfig); + + let request = server.requests[0]; + + expect(request.method).to.equal('GET'); + expect(request.url).to.equal('https://ctx.weborama.com/api/profile?token=foo&url=https%3A%2F%2Fprebid.org&'); + expect(request.withCredentials).to.be.false; + + request.respond(200, responseHeader, JSON.stringify(data)); + + expect(onDoneSpy.calledOnce).to.be.true; + + const targeting = weboramaSubmodule.getTargetingData(adUnitsCodes, moduleConfig); + + expect(targeting).to.deep.equal({ + 'adunit1': data, + 'adunit2': data, + }); + + expect(reqBidsConfigObj.adUnits[0].bids[0].params.target).to.equal('foo=bar'); + }); - config.setConfig({ortb2: conf}); - }); - it('should set targeting and ortb2 if omit setTargeting', function() { - const moduleConfig = { - params: { - weboCtxConf: { - token: 'foo', - targetURL: 'https://prebid.org', - setOrtb2: true, + it('should not set gam targeting with setPrebidTargeting=false but send to bidders', function() { + const moduleConfig = { + params: { + weboCtxConf: { + token: 'foo', + targetURL: 'https://prebid.org', + setPrebidTargeting: false, + } } + }; + const data = { + webo_ctx: ['foo', 'bar'], + webo_ds: ['baz'], + }; + const adUnitsCodes = ['adunit1', 'adunit2']; + const reqBidsConfigObj = { + adUnits: [{ + bids: [{ + bidder: 'smartadserver', + params: { + target: 'foo=bar' + } + }] + }] } - }; - const data = { - webo_ctx: ['foo', 'bar'], - webo_ds: ['baz'], - }; - const adUnitsCodes = ['adunit1', 'adunit2']; - weboramaSubmodule.init(moduleConfig); + const onDoneSpy = sinon.spy(); - let request = server.requests[0]; - request.respond(200, responseHeader, JSON.stringify(data)); + expect(weboramaSubmodule.init(moduleConfig)).to.be.true; + weboramaSubmodule.getBidRequestData(reqBidsConfigObj, onDoneSpy, moduleConfig); - const targeting = weboramaSubmodule.getTargetingData(adUnitsCodes, moduleConfig); + let request = server.requests[0]; - expect(targeting).to.deep.equal({ - 'adunit1': data, - 'adunit2': data, - }); + expect(request.method).to.equal('GET'); + expect(request.url).to.equal('https://ctx.weborama.com/api/profile?token=foo&url=https%3A%2F%2Fprebid.org&'); + expect(request.withCredentials).to.be.false; - const ortb2 = config.getConfig('ortb2'); + request.respond(200, responseHeader, JSON.stringify(data)); - expect(ortb2.site.ext.data.webo_ctx).to.deep.equal(data.webo_ctx); - expect(ortb2.site.ext.data.webo_ds).to.deep.equal(data.webo_ds); - }); - - it('should set targeting and ortb2 with setTargeting=true', function() { - const moduleConfig = { - params: { - weboCtxConf: { - token: 'foo', - targetURL: 'https://prebid.org', - setTargeting: true, - setOrtb2: true, - } - } - }; - const data = { - webo_ctx: ['foo', 'bar'], - webo_ds: ['baz'], - }; - const adUnitsCodes = ['adunit1', 'adunit2']; - weboramaSubmodule.init(moduleConfig); + expect(onDoneSpy.calledOnce).to.be.true; - let request = server.requests[0]; - request.respond(200, responseHeader, JSON.stringify(data)); + const targeting = weboramaSubmodule.getTargetingData(adUnitsCodes, moduleConfig); - const targeting = weboramaSubmodule.getTargetingData(adUnitsCodes, moduleConfig); + expect(targeting).to.deep.equal({}); - expect(targeting).to.deep.equal({ - 'adunit1': data, - 'adunit2': data, + expect(reqBidsConfigObj.adUnits[0].bids[0].params.target).to.equal('foo=bar;webo_ctx=foo;webo_ctx=bar;webo_ds=baz'); }); - const ortb2 = config.getConfig('ortb2'); - - expect(ortb2.site.ext.data.webo_ctx).to.deep.equal(data.webo_ctx); - expect(ortb2.site.ext.data.webo_ds).to.deep.equal(data.webo_ds); - }); - it('should set targeting and ortb2 only webo_ctx with setTargeting=true', function() { - const moduleConfig = { - params: { - weboCtxConf: { - token: 'foo', - targetURL: 'https://prebid.org', - setTargeting: true, - setOrtb2: true, + it('should use default profile in case of api error', function() { + const defaultProfile = { + webo_ctx: ['baz'], + }; + const moduleConfig = { + params: { + weboCtxConf: { + token: 'foo', + targetURL: 'https://prebid.org', + setPrebidTargeting: true, + defaultProfile: defaultProfile, + } } - } - }; - const data = { - webo_ctx: ['foo', 'bar'], - }; + }; - const adUnitsCodes = ['adunit1', 'adunit2']; - weboramaSubmodule.init(moduleConfig); + const adUnitsCodes = ['adunit1', 'adunit2']; + const reqBidsConfigObj = { + adUnits: [{ + bids: [{ + bidder: 'smartadserver' + }] + }] + }; + const onDoneSpy = sinon.spy(); - let request = server.requests[0]; - request.respond(200, responseHeader, JSON.stringify(data)); + expect(weboramaSubmodule.init(moduleConfig)).to.be.true; + weboramaSubmodule.getBidRequestData(reqBidsConfigObj, onDoneSpy, moduleConfig); - const targeting = weboramaSubmodule.getTargetingData(adUnitsCodes, moduleConfig); + let request = server.requests[0]; - expect(targeting).to.deep.equal({ - 'adunit1': data, - 'adunit2': data, - }); + expect(request.method).to.equal('GET'); + expect(request.url).to.equal('https://ctx.weborama.com/api/profile?token=foo&url=https%3A%2F%2Fprebid.org&'); + expect(request.withCredentials).to.be.false; - const ortb2 = config.getConfig('ortb2'); + request.respond(500, responseHeader); - expect(ortb2.site.ext.data.webo_ctx).to.deep.equal(data.webo_ctx); - expect(ortb2.site.ext.data).to.not.have.property('webo_ds'); - }); - it('should set only targeting and not ortb2 with setTargeting=true and setOrtb2=false', function() { - const moduleConfig = { - params: { - weboCtxConf: { - token: 'foo', - targetURL: 'https://prebid.org', - setTargeting: true, - setOrtb2: false, - } - } - }; - const data = { - webo_ctx: ['foo', 'bar'], - }; + expect(onDoneSpy.calledOnce).to.be.true; - const adUnitsCodes = ['adunit1', 'adunit2']; - weboramaSubmodule.init(moduleConfig); + const targeting = weboramaSubmodule.getTargetingData(adUnitsCodes, moduleConfig); - let request = server.requests[0]; - request.respond(200, responseHeader, JSON.stringify(data)); + expect(targeting).to.deep.equal({ + 'adunit1': defaultProfile, + 'adunit2': defaultProfile, + }); - const targeting = weboramaSubmodule.getTargetingData(adUnitsCodes, moduleConfig); + expect(reqBidsConfigObj.adUnits[0].bids[0].params.target).to.equal('webo_ctx=baz'); + }); + }); - expect(targeting).to.deep.equal({ - 'adunit1': data, - 'adunit2': data, + describe('Add WAM2GAM Data', function() { + it('should set gam targeting from local storage and send to bidders by default', function() { + const moduleConfig = { + params: { + weboUserDataConf: {} + } + }; + const data = { + webo_cs: ['foo', 'bar'], + webo_audiences: ['baz'], + }; + + const entry = { + targeting: data, + }; + + sandbox.stub(storage, 'localStorageIsEnabled').returns(true); + sandbox.stub(storage, 'getDataFromLocalStorage') + .withArgs(DEFAULT_LOCAL_STORAGE_USER_PROFILE_KEY) + .returns(JSON.stringify(entry)); + + const adUnitsCodes = ['adunit1', 'adunit2']; + const reqBidsConfigObj = { + adUnits: [{ + bids: [{ + bidder: 'smartadserver' + }] + }] + }; + const onDoneSpy = sinon.spy(); + + expect(weboramaSubmodule.init(moduleConfig)).to.be.true; + weboramaSubmodule.getBidRequestData(reqBidsConfigObj, onDoneSpy, moduleConfig); + + expect(onDoneSpy.calledOnce).to.be.true; + + const targeting = weboramaSubmodule.getTargetingData(adUnitsCodes, moduleConfig); + + expect(targeting).to.deep.equal({ + 'adunit1': data, + 'adunit2': data, + }); + + expect(reqBidsConfigObj.adUnits[0].bids[0].params.target).to.equal('webo_cs=foo;webo_cs=bar;webo_audiences=baz'); }); - const ortb2 = config.getConfig('ortb2'); + it('should set gam targeting but not send to bidders with setPrebidTargeting=true/sendToBidders=false', function() { + const moduleConfig = { + params: { + weboUserDataConf: { + setPrebidTargeting: true, + sendToBidders: false + } + } + }; + const data = { + webo_cs: ['foo', 'bar'], + webo_audiences: ['baz'], + }; + + const entry = { + targeting: data, + }; + + sandbox.stub(storage, 'localStorageIsEnabled').returns(true); + sandbox.stub(storage, 'getDataFromLocalStorage') + .withArgs(DEFAULT_LOCAL_STORAGE_USER_PROFILE_KEY) + .returns(JSON.stringify(entry)); + + const adUnitsCodes = ['adunit1', 'adunit2']; + const reqBidsConfigObj = { + adUnits: [{ + bids: [{ + bidder: 'smartadserver', + params: { + target: 'foo=bar' + } + }] + }] + }; + const onDoneSpy = sinon.spy(); + + expect(weboramaSubmodule.init(moduleConfig)).to.be.true; + weboramaSubmodule.getBidRequestData(reqBidsConfigObj, onDoneSpy, moduleConfig); + + expect(onDoneSpy.calledOnce).to.be.true; + + const targeting = weboramaSubmodule.getTargetingData(adUnitsCodes, moduleConfig); + + expect(targeting).to.deep.equal({ + 'adunit1': data, + 'adunit2': data, + }); + + expect(reqBidsConfigObj.adUnits[0].bids[0].params.target).to.equal('foo=bar'); + }); - expect(ortb2.site.ext.data).to.not.have.property('webo_ctx'); - expect(ortb2.site.ext.data).to.not.have.property('webo_ds'); - }); - it('should set only targeting and not ortb2 with setTargeting=true and omit setOrtb2', function() { - const moduleConfig = { - params: { - weboCtxConf: { - token: 'foo', - targetURL: 'https://prebid.org', - setTargeting: true, + it('should not set gam targeting with setPrebidTargeting=false but send to bidders', function() { + const moduleConfig = { + params: { + weboUserDataConf: { + setPrebidTargeting: false, + } } - } - }; - const data = { - webo_ctx: ['foo', 'bar'], - }; + }; + const data = { + webo_cs: ['foo', 'bar'], + webo_audiences: ['baz'], + }; + + const entry = { + targeting: data, + }; + + sandbox.stub(storage, 'localStorageIsEnabled').returns(true); + sandbox.stub(storage, 'getDataFromLocalStorage') + .withArgs(DEFAULT_LOCAL_STORAGE_USER_PROFILE_KEY) + .returns(JSON.stringify(entry)); + + const adUnitsCodes = ['adunit1', 'adunit2']; + const reqBidsConfigObj = { + adUnits: [{ + bids: [{ + bidder: 'smartadserver', + params: { + target: 'foo=bar' + } + }] + }] + }; + const onDoneSpy = sinon.spy(); + + expect(weboramaSubmodule.init(moduleConfig)).to.be.true; + weboramaSubmodule.getBidRequestData(reqBidsConfigObj, onDoneSpy, moduleConfig); + + expect(onDoneSpy.calledOnce).to.be.true; + + const targeting = weboramaSubmodule.getTargetingData(adUnitsCodes, moduleConfig); + + expect(targeting).to.deep.equal({}); + + expect(reqBidsConfigObj.adUnits[0].bids[0].params.target).to.equal('foo=bar;webo_cs=foo;webo_cs=bar;webo_audiences=baz'); + }); - const adUnitsCodes = ['adunit1', 'adunit2']; - weboramaSubmodule.init(moduleConfig); + it('should use default profile in case of nothing on local storage', function() { + const defaultProfile = { + webo_audiences: ['baz'] + }; + const moduleConfig = { + params: { + weboUserDataConf: { + setPrebidTargeting: true, + defaultProfile: defaultProfile, + } + } + }; - let request = server.requests[0]; - request.respond(200, responseHeader, JSON.stringify(data)); + sandbox.stub(storage, 'localStorageIsEnabled').returns(true); - const targeting = weboramaSubmodule.getTargetingData(adUnitsCodes, moduleConfig); + const adUnitsCodes = ['adunit1', 'adunit2']; + const reqBidsConfigObj = { + adUnits: [{ + bids: [{ + bidder: 'smartadserver' + }] + }] + }; + const onDoneSpy = sinon.spy(); - expect(targeting).to.deep.equal({ - 'adunit1': data, - 'adunit2': data, - }); + expect(weboramaSubmodule.init(moduleConfig)).to.be.true; + weboramaSubmodule.getBidRequestData(reqBidsConfigObj, onDoneSpy, moduleConfig); - const ortb2 = config.getConfig('ortb2'); + expect(onDoneSpy.calledOnce).to.be.true; - expect(ortb2.site.ext.data).to.not.have.property('webo_ctx'); - expect(ortb2.site.ext.data).to.not.have.property('webo_ds'); - }); + const targeting = weboramaSubmodule.getTargetingData(adUnitsCodes, moduleConfig); - it('should set only ortb2 with setTargeting=false', function() { - const moduleConfig = { - params: { - weboCtxConf: { - token: 'foo', - targetURL: 'https://prebid.org', - setTargeting: false, - setOrtb2: true, - } - } - }; - const data = { - webo_ctx: ['foo', 'bar'], - }; - const adUnitsCodes = ['adunit1', 'adunit2']; - weboramaSubmodule.init(moduleConfig); + expect(targeting).to.deep.equal({ + 'adunit1': defaultProfile, + 'adunit2': defaultProfile, + }); - let request = server.requests[0]; - request.respond(200, responseHeader, JSON.stringify(data)); + expect(reqBidsConfigObj.adUnits[0].bids[0].params.target).to.equal('webo_audiences=baz'); + }); - const targeting = weboramaSubmodule.getTargetingData(adUnitsCodes, moduleConfig); + it('should use default profile if cant read from local storage', function() { + const defaultProfile = { + webo_audiences: ['baz'] + }; + const moduleConfig = { + params: { + weboUserDataConf: { + setPrebidTargeting: true, + defaultProfile: defaultProfile, + } + } + }; - expect(targeting).to.deep.equal({}); + sandbox.stub(storage, 'localStorageIsEnabled').returns(false); - const ortb2 = config.getConfig('ortb2'); + const adUnitsCodes = ['adunit1', 'adunit2']; + const reqBidsConfigObj = { + adUnits: [{ + bids: [{ + bidder: 'smartadserver' + }] + }] + }; + const onDoneSpy = sinon.spy(); - expect(ortb2.site.ext.data.webo_ctx).to.deep.equal(data.webo_ctx); - expect(ortb2.site.ext.data).to.not.have.property('webo_ds'); - }); - it('should use default profile in case of api error', function() { - const defaultProfile = { - webo_ctx: ['baz'], - }; - const moduleConfig = { - params: { - weboCtxConf: { - token: 'foo', - targetURL: 'https://prebid.org', - setTargeting: true, - defaultProfile: defaultProfile, - } - } - }; + expect(weboramaSubmodule.init(moduleConfig)).to.be.true; + weboramaSubmodule.getBidRequestData(reqBidsConfigObj, onDoneSpy, moduleConfig); - const adUnitsCodes = ['adunit1', 'adunit2']; - weboramaSubmodule.init(moduleConfig); + expect(onDoneSpy.calledOnce).to.be.true; - let request = server.requests[0]; - request.respond(500, responseHeader); + const targeting = weboramaSubmodule.getTargetingData(adUnitsCodes, moduleConfig); - const targeting = weboramaSubmodule.getTargetingData(adUnitsCodes, moduleConfig); + expect(targeting).to.deep.equal({ + 'adunit1': defaultProfile, + 'adunit2': defaultProfile, + }); - expect(targeting).to.deep.equal({ - 'adunit1': defaultProfile, - 'adunit2': defaultProfile, + expect(reqBidsConfigObj.adUnits[0].bids[0].params.target).to.equal('webo_audiences=baz'); }); - - const ortb2 = config.getConfig('ortb2'); - - expect(ortb2.site.ext.data).to.not.have.property('webo_ctx'); - expect(ortb2.site.ext.data).to.not.have.property('webo_ds'); }); }); }); diff --git a/test/spec/modules/welectBidAdapter_spec.js b/test/spec/modules/welectBidAdapter_spec.js new file mode 100644 index 00000000000..2f2af35eaec --- /dev/null +++ b/test/spec/modules/welectBidAdapter_spec.js @@ -0,0 +1,211 @@ +import { expect } from 'chai'; +import { spec as adapter } from 'modules/welectBidAdapter.js'; + +describe('WelectAdapter', function () { + describe('Check methods existance', function () { + it('exists and is a function', function () { + expect(adapter.isBidRequestValid).to.exist.and.to.be.a('function'); + }); + it('exists and is a function', function () { + expect(adapter.buildRequests).to.exist.and.to.be.a('function'); + }); + it('exists and is a function', function () { + expect(adapter.interpretResponse).to.exist.and.to.be.a('function'); + }); + }); + + describe('Check method isBidRequestValid return', function () { + let bid = { + bidder: 'welect', + params: { + placementId: 'exampleAlias', + domain: 'www.welect.de' + }, + sizes: [[640, 360]], + mediaTypes: { + video: { + context: 'instream' + } + }, + }; + let bid2 = { + bidder: 'welect', + params: { + domain: 'www.welect.de' + }, + mediaTypes: { + video: { + context: 'instream', + playerSize: [640, 360] + } + }, + }; + + it('should be true', function () { + expect(adapter.isBidRequestValid(bid)).to.be.true; + }); + + it('should be false because the placementId is missing', function () { + expect(adapter.isBidRequestValid(bid2)).to.be.false; + }); + }); + + describe('Check buildRequests method', function () { + // Bids to be formatted + let bid1 = { + bidder: 'welect', + params: { + placementId: 'exampleAlias' + }, + sizes: [[640, 360]], + mediaTypes: { + video: { + context: 'instream' + } + }, + bidId: 'abdc' + }; + let bid2 = { + bidder: 'welect', + params: { + placementId: 'exampleAlias', + domain: 'www.welect2.de' + }, + sizes: [[640, 360]], + mediaTypes: { + video: { + context: 'instream' + } + }, + bidId: 'abdc', + gdprConsent: { + gdprApplies: 1, + gdprConsent: 'some_string' + } + }; + + let data1 = { + bid_id: 'abdc', + width: 640, + height: 360 + } + + let data2 = { + bid_id: 'abdc', + width: 640, + height: 360, + gdpr_consent: { + gdprApplies: 1, + tcString: 'some_string' + } + } + + // Formatted requets + let request1 = { + method: 'POST', + url: 'https://www.welect.de/api/v2/preflight/exampleAlias', + data: data1, + options: { + contentType: 'application/json', + withCredentials: false, + crossOrigin: true, + } + }; + + let request2 = { + method: 'POST', + url: 'https://www.welect2.de/api/v2/preflight/exampleAlias', + data: data2, + options: { + contentType: 'application/json', + withCredentials: false, + crossOrigin: true, + } + } + + it('defaults to www.welect.de, without gdpr object', function () { + expect(adapter.buildRequests([bid1])).to.deep.equal([request1]); + }) + + it('must return the right formatted requests, with gdpr object', function () { + expect(adapter.buildRequests([bid2])).to.deep.equal([request2]); + }); + }); + + describe('Check interpretResponse method return', function () { + // invalid server response + let unavailableResponse = { + body: { + available: false + } + }; + + let availableResponse = { + body: { + available: true, + bidResponse: { + ad: { + video: 'some vast url' + }, + meta: { + advertiserDomains: [], + }, + cpm: 17, + creativeId: 'svmpreview', + currency: 'EUR', + netRevenue: true, + requestId: 'some bid id', + ttl: 120, + vastUrl: 'some vast url', + height: 640, + width: 320 + } + } + } + // bid Request + let bid = { + data: { + bid_id: 'some bid id', + width: 640, + height: 320, + gdpr_consent: { + gdprApplies: 1, + tcString: 'some_string' + } + }, + method: 'POST', + url: 'https://www.welect.de/api/v2/preflight/exampleAlias', + options: { + contentType: 'application/json', + withCredentials: false, + crossOrigin: true, + } + }; + // Formatted reponse + let result = { + ad: { + video: 'some vast url' + }, + meta: { + advertiserDomains: [] + }, + cpm: 17, + creativeId: 'svmpreview', + currency: 'EUR', + height: 640, + netRevenue: true, + requestId: 'some bid id', + ttl: 120, + vastUrl: 'some vast url', + width: 320 + } + + it('if response reflects unavailability, should be empty', function () { + expect(adapter.interpretResponse(unavailableResponse, bid)).to.deep.equal([]); + }); + + it('if response reflects availability, should equal result', function () { + expect(adapter.interpretResponse(availableResponse, bid)).to.deep.equal([result]) + }) + }); +}); diff --git a/test/spec/modules/yahoosspBidAdapter_spec.js b/test/spec/modules/yahoosspBidAdapter_spec.js new file mode 100644 index 00000000000..50424d41d4b --- /dev/null +++ b/test/spec/modules/yahoosspBidAdapter_spec.js @@ -0,0 +1,1369 @@ +import { expect } from 'chai'; +import { config } from 'src/config.js'; +import { BANNER, VIDEO } from 'src/mediaTypes.js'; +import { spec } from 'modules/yahoosspBidAdapter.js'; + +const DEFAULT_BID_ID = '84ab500420319d'; +const DEFAULT_BID_DCN = '2093845709823475'; +const DEFAULT_BID_POS = 'header'; +const DEFAULT_PUBID = 'PubId'; +const DEFAULT_AD_UNIT_CODE = '/19968336/header-bid-tag-1'; +const DEFAULT_AD_UNIT_TYPE = 'banner'; +const DEFAULT_PARAMS_BID_OVERRIDE = {}; +const DEFAULT_VIDEO_CONTEXT = 'instream'; +const ADAPTER_VERSION = '1.0.2'; +const PREBID_VERSION = '$prebid.version$'; +const INTEGRATION_METHOD = 'prebid.js'; + +// Utility functions +const generateBidRequest = ({bidId, pos, adUnitCode, adUnitType, bidOverrideObject, videoContext, pubIdMode}) => { + const bidRequest = { + adUnitCode, + auctionId: 'b06c5141-fe8f-4cdf-9d7d-54415490a917', + bidId, + bidderRequestsCount: 1, + bidder: 'yahoossp', + bidderRequestId: '7101db09af0db2', + bidderWinsCount: 0, + mediaTypes: {}, + params: { + bidOverride: bidOverrideObject + }, + src: 'client', + transactionId: '5b17b67d-7704-4732-8cc9-5b1723e9bcf9' + }; + + const bannerObj = { + sizes: [[300, 250], [300, 600]] + }; + + const videoObject = { + context: videoContext, + playerSize: [[300, 250]], + mimes: ['video/mp4', 'application/javascript'], + api: [2] + }; + + if (videoContext === 'outstream') { + bidRequest.renderer = undefined + }; + + if (adUnitType === 'banner' || adUnitType === 'dap-o2' || adUnitType === 'dap-up') { + bidRequest.mediaTypes.banner = bannerObj; + bidRequest.sizes = [[300, 250], [300, 600]]; + } else if (adUnitType === 'video') { + bidRequest.mediaTypes.video = videoObject; + } else if (adUnitType === 'multi-format') { + bidRequest.mediaTypes.banner = bannerObj; + bidRequest.sizes = [[300, 250], [300, 600]]; + bidRequest.mediaTypes.video = videoObject; + } else if (adUnitType === 'native') { + bidRequest.mediaTypes.native = {a: 123, b: 456}; + } + + if (pubIdMode === true) { + bidRequest.params.pubId = DEFAULT_PUBID; + } else { + bidRequest.params.dcn = DEFAULT_BID_DCN; + bidRequest.params.pos = pos || DEFAULT_BID_POS; + }; + + return bidRequest; +} + +let generateBidderRequest = (bidRequestArray, adUnitCode) => { + const bidderRequest = { + adUnitCode: adUnitCode || 'default-adUnitCode', + auctionId: 'd4c83a3b-18e4-4208-b98b-63848449c7aa', + auctionStart: new Date().getTime(), + bidderCode: 'yahoossp', + bidderRequestId: '112f1c7c5d399a', + bids: bidRequestArray, + refererInfo: { + referer: 'https://publisher-test.com', + reachedTop: true, + isAmp: false, + numIframes: 0, + stack: ['https://publisher-test.com'], + }, + gdprConsent: { + consentString: 'BOtmiBKOtmiBKABABAENAFAAAAACeAAA', + vendorData: {}, + gdprApplies: true + }, + start: new Date().getTime(), + timeout: 1000, + }; + + return bidderRequest; +}; + +const generateBuildRequestMock = ({bidId, pos, adUnitCode, adUnitType, bidOverrideObject, videoContext, pubIdMode}) => { + const bidRequestConfig = { + bidId: bidId || DEFAULT_BID_ID, + pos: pos || DEFAULT_BID_POS, + adUnitCode: adUnitCode || DEFAULT_AD_UNIT_CODE, + adUnitType: adUnitType || DEFAULT_AD_UNIT_TYPE, + bidOverrideObject: bidOverrideObject || DEFAULT_PARAMS_BID_OVERRIDE, + videoContext: videoContext || DEFAULT_VIDEO_CONTEXT, + pubIdMode: pubIdMode || false + }; + const bidRequest = generateBidRequest(bidRequestConfig); + const validBidRequests = [bidRequest]; + const bidderRequest = generateBidderRequest(validBidRequests, adUnitCode); + + return { bidRequest, validBidRequests, bidderRequest } +}; + +const generateAdmPayload = (admPayloadType) => { + let ADM_PAYLOAD; + switch (admPayloadType) { + case 'banner': + ADM_PAYLOAD = ''; // banner + break; + case 'video': + ADM_PAYLOAD = ''; // VAST xml + break; + case 'multi-format': + const admPayloads = [ + '', // banner + '' // VAST xml + ] + const random = Math.floor(Math.random() * admPayloads.length); + ADM_PAYLOAD = admPayloads[random] + break; + case 'dap-o2': + ADM_PAYLOAD = ''; // O2 player + break; + case 'dap-up': + ADM_PAYLOAD = ''; // Unified Player + break; + }; + return ADM_PAYLOAD; +}; + +const generateResponseMock = (admPayloadType, vastVersion, videoContext) => { + const bidResponse = { + id: 'fc0c35df-21fb-4f93-9ebd-88759dbe31f9', + impid: '274395c06a24e5', + adm: generateAdmPayload(admPayloadType), + price: 1, + w: 300, + h: 250, + crid: 'ssp-placement-name', + adomain: ['advertiser-domain.com'] + }; + + if (vastVersion === 'vast') { + bidResponse.nurl = 'https://yahoo.com?event=adAttempt'; + }; + + const serverResponse = { + body: { + id: 'fc0c35df-21fb-4f93-9ebd-88759dbe31f9', + seatbid: [{ bid: [ bidResponse ], seat: 13107 }] + } + }; + const { validBidRequests, bidderRequest } = generateBuildRequestMock({adUnitType: admPayloadType, videoContext: videoContext}); + const data = spec.buildRequests(validBidRequests, bidderRequest)[0].data; + + return {serverResponse, data, bidderRequest}; +} + +// Unit tests +describe('YahooSSP Bid Adapter:', () => { + it('PLACEHOLDER TO PASS GULP', () => { + const obj = {}; + expect(obj).to.be.an('object'); + }); + + describe('Validate basic properties', () => { + it('should define the correct bidder code', () => { + expect(spec.code).to.equal('yahoossp') + }); + + it('should define the correct vendor ID', () => { + expect(spec.gvlid).to.equal(25) + }); + }); + + describe('getUserSyncs()', () => { + const IMAGE_PIXEL_URL = 'http://image-pixel.com/foo/bar?1234&baz=true'; + const IFRAME_ONE_URL = 'http://image-iframe.com/foo/bar?1234&baz=true'; + const IFRAME_TWO_URL = 'http://image-iframe-two.com/foo/bar?1234&baz=true'; + + let serverResponses = []; + beforeEach(() => { + serverResponses[0] = { + body: { + ext: { + pixels: `` + } + } + } + }); + + after(() => { + serverResponses = undefined; + }); + + it('for only iframe enabled syncs', () => { + let syncOptions = { + iframeEnabled: true, + pixelEnabled: false + }; + let pixelsObjects = spec.getUserSyncs(syncOptions, serverResponses); + expect(pixelsObjects.length).to.equal(2); + expect(pixelsObjects).to.deep.equal( + [ + {type: 'iframe', 'url': IFRAME_ONE_URL}, + {type: 'iframe', 'url': IFRAME_TWO_URL} + ] + ) + }); + + it('for only pixel enabled syncs', () => { + let syncOptions = { + iframeEnabled: false, + pixelEnabled: true + }; + let pixelsObjects = spec.getUserSyncs(syncOptions, serverResponses); + expect(pixelsObjects.length).to.equal(1); + expect(pixelsObjects).to.deep.equal( + [ + {type: 'image', 'url': IMAGE_PIXEL_URL} + ] + ) + }); + + it('for both pixel and iframe enabled syncs', () => { + let syncOptions = { + iframeEnabled: true, + pixelEnabled: true + }; + let pixelsObjects = spec.getUserSyncs(syncOptions, serverResponses); + expect(pixelsObjects.length).to.equal(3); + expect(pixelsObjects).to.deep.equal( + [ + {type: 'iframe', 'url': IFRAME_ONE_URL}, + {type: 'image', 'url': IMAGE_PIXEL_URL}, + {type: 'iframe', 'url': IFRAME_TWO_URL} + ] + ) + }); + }); + + // Validate Bid Requests + describe('isBidRequestValid()', () => { + const INVALID_INPUT = [ + {}, + {params: {}}, + {params: {dcn: '2c9d2b50015a5aa95b70a9b0b5b10012'}}, + {params: {dcn: 1234, pos: 'header'}}, + {params: {dcn: '2c9d2b50015a5aa95b70a9b0b5b10012', pos: 1234}}, + {params: {dcn: '2c9d2b50015a5aa95b70a9b0b5b10012', pos: ''}}, + {params: {dcn: '', pos: 'header'}}, + ]; + + INVALID_INPUT.forEach(input => { + it(`should determine that the bid is INVALID for the input ${JSON.stringify(input)}`, () => { + expect(spec.isBidRequestValid(input)).to.be.false; + }); + }); + + it('should determine that the bid is VALID if dcn and pos are present on the params object', () => { + const validBid = { + params: { + dcn: '2c9d2b50015a5aa95b70a9b0b5b10012', + pos: 'header' + } + }; + expect(spec.isBidRequestValid(validBid)).to.be.true; + }); + + it('should mark bid as VALID if bid.params.testing.e2etest = "true" (dcn & pos not required)', () => { + const validBid = { + params: { + dcn: 8888, + pos: 9999, + testing: { + e2etest: true + } + } + }; + expect(spec.isBidRequestValid(validBid)).to.be.true; + }); + + it('should mark bid ad VALID if pubId exists instead of dcn & pos', () => { + const validBid = { + params: { + pubId: DEFAULT_PUBID + } + }; + expect(spec.isBidRequestValid(validBid)).to.be.true; + }); + }); + + describe('Price Floor module support:', () => { + it('should get bidfloor from getFloor method', () => { + const { bidRequest, validBidRequests, bidderRequest } = generateBuildRequestMock({}); + bidRequest.params.bidOverride = {cur: 'EUR'}; + bidRequest.getFloor = (floorObj) => { + return { + floor: bidRequest.floors.values[floorObj.mediaType + '|640x480'], + currency: floorObj.currency, + mediaType: floorObj.mediaType + } + }; + bidRequest.floors = { + currency: 'EUR', + values: { + 'banner|640x480': 5.55 + } + }; + const data = spec.buildRequests(validBidRequests, bidderRequest)[0].data; + expect(data.cur).to.deep.equal(['EUR']); + expect(data.imp[0].bidfloor).is.a('number'); + expect(data.imp[0].bidfloor).to.equal(5.55); + }); + }); + + describe('Schain module support:', () => { + it('should send Global or Bidder specific schain', function () { + const { bidRequest, validBidRequests, bidderRequest } = generateBuildRequestMock({}); + const globalSchain = { + ver: '1.0', + complete: 1, + nodes: [{ + asi: 'some-platform.com', + sid: '111111', + rid: bidRequest.bidId, + hp: 1 + }] + }; + bidRequest.schain = globalSchain; + const data = spec.buildRequests(validBidRequests, bidderRequest)[0].data; + const schain = data.source.ext.schain; + expect(schain.nodes.length).to.equal(1); + expect(schain).to.equal(globalSchain); + }); + }); + + describe('First party data module - "Site" support (ortb2):', () => { + // Should not allow invalid "site" data types + const INVALID_ORTB2_TYPES = [ null, [], 123, 'unsupportedKeyName', true, false, undefined ]; + INVALID_ORTB2_TYPES.forEach(param => { + const ortb2 = { site: param } + config.setConfig({ortb2}); + it(`should not allow invalid site types to be added to bid-request: ${JSON.stringify(param)}`, () => { + const { validBidRequests, bidderRequest } = generateBuildRequestMock({}); + const data = spec.buildRequests(validBidRequests, bidderRequest)[0].data; + expect(data.site[param]).to.be.undefined; + }); + }); + + // Should add valid "site" params + const VALID_SITE_STRINGS = ['name', 'domain', 'page', 'ref', 'keywords', 'search']; + const VALID_SITE_ARRAYS = ['cat', 'sectioncat', 'pagecat']; + + VALID_SITE_STRINGS.forEach(param => { + it(`should allow supported site keys to be added bid-request: ${JSON.stringify(param)}`, () => { + const ortb2 = { + site: { + [param]: 'something' + } + }; + config.setConfig({ortb2}); + const { validBidRequests, bidderRequest } = generateBuildRequestMock({}); + const data = spec.buildRequests(validBidRequests, bidderRequest)[0].data; + expect(data.site[param]).to.exist; + expect(data.site[param]).to.be.a('string'); + expect(data.site[param]).to.be.equal(ortb2.site[param]); + }); + }); + + VALID_SITE_ARRAYS.forEach(param => { + it(`should determine that the ortb2.site Array key is valid and append to the bid-request: ${JSON.stringify(param)}`, () => { + const ortb2 = { + site: { + [param]: ['something'] + } + }; + config.setConfig({ortb2}); + const { validBidRequests, bidderRequest } = generateBuildRequestMock({}); + const data = spec.buildRequests(validBidRequests, bidderRequest)[0].data; + expect(data.site[param]).to.exist; + expect(data.site[param]).to.be.a('array'); + expect(data.site[param]).to.be.equal(ortb2.site[param]); + }); + }); + + // Should not allow invalid "site.content" data types + INVALID_ORTB2_TYPES.forEach(param => { + it(`should determine that the ortb2.site.content key is invalid and should not be added to bid-request: ${JSON.stringify(param)}`, () => { + const ortb2 = { + site: { + content: param + } + }; + config.setConfig({ortb2}); + const { validBidRequests, bidderRequest } = generateBuildRequestMock({}); + const data = spec.buildRequests(validBidRequests, bidderRequest)[0].data; + expect(data.site.content).to.be.undefined; + }); + }); + + // Should not allow invalid "site.content" keys + it(`should not allow invalid ortb2.site.content object keys to be added to bid-request: {custom object}`, () => { + const ortb2 = { + site: { + content: { + fake: 'news', + unreal: 'param', + counterfit: 'data' + } + } + }; + config.setConfig({ortb2}); + const { validBidRequests, bidderRequest } = generateBuildRequestMock({}); + const data = spec.buildRequests(validBidRequests, bidderRequest)[0].data; + expect(data.site.content).to.be.a('object'); + }); + + // Should append valid "site.content" keys + const VALID_CONTENT_STRINGS = ['id', 'title', 'series', 'season', 'genre', 'contentrating', 'language']; + VALID_CONTENT_STRINGS.forEach(param => { + it(`should determine that the ortb2.site String key is valid and append to the bid-request: ${JSON.stringify(param)}`, () => { + const ortb2 = { + site: { + content: { + [param]: 'something' + } + } + }; + config.setConfig({ortb2}); + const { validBidRequests, bidderRequest } = generateBuildRequestMock({}); + const data = spec.buildRequests(validBidRequests, bidderRequest)[0].data; + expect(data.site.content[param]).to.exist; + expect(data.site.content[param]).to.be.a('string'); + expect(data.site.content[param]).to.be.equal(ortb2.site.content[param]); + }); + }); + + const VALID_CONTENT_NUMBERS = ['episode', 'prodq', 'context', 'livestream', 'len']; + VALID_CONTENT_NUMBERS.forEach(param => { + it(`should determine that the ortb2.site.content Number key is valid and append to the bid-request: ${JSON.stringify(param)}`, () => { + const ortb2 = { + site: { + content: { + [param]: 1234 + } + } + }; + config.setConfig({ortb2}); + const { validBidRequests, bidderRequest } = generateBuildRequestMock({}); + const data = spec.buildRequests(validBidRequests, bidderRequest)[0].data; + expect(data.site.content[param]).to.be.a('number'); + expect(data.site.content[param]).to.be.equal(ortb2.site.content[param]); + }); + }); + + const VALID_CONTENT_ARRAYS = ['cat']; + VALID_CONTENT_ARRAYS.forEach(param => { + it(`should determine that the ortb2.site Array key is valid and append to the bid-request: ${JSON.stringify(param)}`, () => { + const ortb2 = { + site: { + content: { + [param]: ['something', 'something-else'] + } + } + }; + config.setConfig({ortb2}); + const { validBidRequests, bidderRequest } = generateBuildRequestMock({}); + const data = spec.buildRequests(validBidRequests, bidderRequest)[0].data; + expect(data.site.content[param]).to.be.a('array'); + expect(data.site.content[param]).to.be.equal(ortb2.site.content[param]); + }); + }); + + const VALID_CONTENT_OBJECTS = ['ext']; + VALID_CONTENT_OBJECTS.forEach(param => { + it(`should determine that the ortb2.site.content Object key is valid and append to the bid-request: ${JSON.stringify(param)}`, () => { + const ortb2 = { + site: { + content: { + [param]: {a: '123', b: '456'} + } + } + }; + config.setConfig({ortb2}); + const { validBidRequests, bidderRequest } = generateBuildRequestMock({}); + const data = spec.buildRequests(validBidRequests, bidderRequest)[0].data; + expect(data.site.content[param]).to.be.a('object'); + expect(data.site.content[param]).to.be.equal(ortb2.site.content[param]); + config.setConfig({ortb2: {}}); + }); + }); + }); + + describe('First party data module - "User" support (ortb2):', () => { + // Global ortb2.user validations + // Should not allow invalid "user" data types + const INVALID_ORTB2_TYPES = [ null, [], 'unsupportedKeyName', true, false, undefined ]; + INVALID_ORTB2_TYPES.forEach(param => { + const ortb2 = { user: param } + config.setConfig({ortb2}); + it(`should not allow invalid site types to be added to bid-request: ${JSON.stringify(param)}`, () => { + const { validBidRequests, bidderRequest } = generateBuildRequestMock({}); + const data = spec.buildRequests(validBidRequests, bidderRequest)[0].data; + expect(data.user[param]).to.be.undefined; + }); + }); + + // Should add valid "user" params + const VALID_USER_STRINGS = ['id', 'buyeruid', 'gender', 'keywords', 'customdata']; + VALID_USER_STRINGS.forEach(param => { + it(`should allow supported user string keys to be added bid-request: ${JSON.stringify(param)}`, () => { + const ortb2 = { + user: { + [param]: 'something' + } + }; + config.setConfig({ortb2}); + const { validBidRequests, bidderRequest } = generateBuildRequestMock({}); + const data = spec.buildRequests(validBidRequests, bidderRequest)[0].data; + expect(data.user[param]).to.exist; + expect(data.user[param]).to.be.a('string'); + expect(data.user[param]).to.be.equal(ortb2.user[param]); + }); + }); + + const VALID_USER_NUMBERS = ['yob']; + VALID_USER_NUMBERS.forEach(param => { + it(`should allow supported user number keys to be added bid-request: ${JSON.stringify(param)}`, () => { + const ortb2 = { + user: { + [param]: 1982 + } + }; + config.setConfig({ortb2}); + const { validBidRequests, bidderRequest } = generateBuildRequestMock({}); + const data = spec.buildRequests(validBidRequests, bidderRequest)[0].data; + expect(data.user[param]).to.exist; + expect(data.user[param]).to.be.a('number'); + expect(data.user[param]).to.be.equal(ortb2.user[param]); + }); + }); + + const VALID_USER_ARRAYS = ['data']; + VALID_USER_ARRAYS.forEach(param => { + it(`should allow supported user Array keys to be added to the bid-request: ${JSON.stringify(param)}`, () => { + const ortb2 = { + user: { + [param]: ['something'] + } + }; + config.setConfig({ortb2}); + const { validBidRequests, bidderRequest } = generateBuildRequestMock({}); + const data = spec.buildRequests(validBidRequests, bidderRequest)[0].data; + expect(data.user[param]).to.exist; + expect(data.user[param]).to.be.a('array'); + expect(data.user[param]).to.be.equal(ortb2.user[param]); + }); + }); + + const VALID_USER_OBJECTS = ['ext']; + VALID_USER_OBJECTS.forEach(param => { + it(`should allow supported user extObject keys to be added to the bid-request: ${JSON.stringify(param)}`, () => { + const ortb2 = { + user: { + [param]: {a: '123', b: '456'} + } + }; + config.setConfig({ortb2}); + const { validBidRequests, bidderRequest } = generateBuildRequestMock({}); + const data = spec.buildRequests(validBidRequests, bidderRequest)[0].data; + expect(data.user[param]).to.be.a('object'); + expect(data.user[param]).to.be.deep.include({[param]: {a: '123', b: '456'}}); + config.setConfig({ortb2: {}}); + }); + }); + + // Should allow valid user.data && site.content.data + const VALID_USER_DATA_STRINGS = ['id', 'name']; + VALID_USER_DATA_STRINGS.forEach(param => { + it(`should allow supported user.data & site.content.data strings to be added to the bid-request: ${JSON.stringify(param)}`, () => { + const ortb2 = { + user: { + data: [{[param]: 'string'}] + }, + site: { + content: { + data: [{[param]: 'string'}] + } + } + }; + config.setConfig({ortb2}); + const { validBidRequests, bidderRequest } = generateBuildRequestMock({}); + const data = spec.buildRequests(validBidRequests, bidderRequest)[0].data; + expect(data.user.data[0][param]).to.exist; + expect(data.user.data[0][param]).to.be.a('string'); + expect(data.user.data[0][param]).to.be.equal(ortb2.user.data[0][param]); + expect(data.site.content.data[0][param]).to.exist; + expect(data.site.content.data[0][param]).to.be.a('string'); + expect(data.site.content.data[0][param]).to.be.equal(ortb2.site.content.data[0][param]); + }); + }); + + const VALID_USER_DATA_ARRAYS = ['segment'] + VALID_USER_DATA_ARRAYS.forEach(param => { + it(`should allow supported user data arrays to be added to the bid-request: ${JSON.stringify(param)}`, () => { + const ortb2 = { + user: { + data: [{[param]: [{id: 1}]}] + } + }; + config.setConfig({ortb2}); + const { validBidRequests, bidderRequest } = generateBuildRequestMock({}); + const data = spec.buildRequests(validBidRequests, bidderRequest)[0].data; + expect(data.user.data[0][param]).to.exist; + expect(data.user.data[0][param]).to.be.a('array'); + expect(data.user.data[0][param]).to.be.equal(ortb2.user.data[0][param]); + }); + }); + + const VALID_USER_DATA_OBJECTS = ['ext']; + VALID_USER_DATA_OBJECTS.forEach(param => { + it(`should allow supported user data objects to be added to the bid-request: ${JSON.stringify(param)}`, () => { + const ortb2 = { + user: { + data: [{[param]: {id: 'ext'}}] + } + }; + config.setConfig({ortb2}); + const { validBidRequests, bidderRequest } = generateBuildRequestMock({}); + const data = spec.buildRequests(validBidRequests, bidderRequest)[0].data; + expect(data.user.data[0][param]).to.exist; + expect(data.user.data[0][param]).to.be.a('object'); + expect(data.user.data[0][param]).to.be.equal(ortb2.user.data[0][param]); + config.setConfig({ortb2: {}}); + }); + }); + + // adUnit.ortb2Imp.ext.data + it(`should allow adUnit.ortb2Imp.ext.data object to be added to the bid-request`, () => { + let { validBidRequests, bidderRequest } = generateBuildRequestMock({}) + validBidRequests[0].ortb2Imp = { + ext: { + data: { + pbadslot: 'homepage-top-rect', + adUnitSpecificAttribute: '123' + } + } + }; + const data = spec.buildRequests(validBidRequests, bidderRequest)[0].data; + expect(data.imp[0].ext.data).to.deep.equal(validBidRequests[0].ortb2Imp.ext.data); + }); + // adUnit.ortb2Imp.instl + it(`should allow adUnit.ortb2Imp.instl numeric boolean "1" to be added to the bid-request`, () => { + let { validBidRequests, bidderRequest } = generateBuildRequestMock({}) + validBidRequests[0].ortb2Imp = { + instl: 1 + }; + const data = spec.buildRequests(validBidRequests, bidderRequest)[0].data; + expect(data.imp[0].instl).to.deep.equal(validBidRequests[0].ortb2Imp.instl); + }); + + it(`should prevent adUnit.ortb2Imp.instl boolean "true" to be added to the bid-request`, () => { + let { validBidRequests, bidderRequest } = generateBuildRequestMock({}) + validBidRequests[0].ortb2Imp = { + instl: true + }; + const data = spec.buildRequests(validBidRequests, bidderRequest)[0].data; + expect(data.imp[0].instl).to.not.exist; + }); + + it(`should prevent adUnit.ortb2Imp.instl boolean "false" to be added to the bid-request`, () => { + let { validBidRequests, bidderRequest } = generateBuildRequestMock({}) + validBidRequests[0].ortb2Imp = { + instl: false + }; + const data = spec.buildRequests(validBidRequests, bidderRequest)[0].data; + expect(data.imp[0].instl).to.not.exist; + }); + }); + + describe('e2etest mode support:', () => { + it(`should override DCN & POS when params.testing.e2etest = "true".`, () => { + const { bidRequest, validBidRequests, bidderRequest } = generateBuildRequestMock({}); + const params = { + dcn: '1234', + pos: '5678', + testing: { + e2etest: true + } + } + bidRequest.params = params; + const data = spec.buildRequests(validBidRequests, bidderRequest)[0].data; + expect(data.site.id).to.be.equal('8a969516017a7a396ec539d97f540011'); + expect(data.imp[0].tagid).to.be.equal('8a969978017a7aaabab4ab0bc01a0009'); + expect(data.imp[0].ext.e2eTestMode).to.be.true; + }); + }); + + describe('GDPR & Consent:', () => { + it('should return request objects that do not send cookies if purpose 1 consent is not provided', () => { + const { validBidRequests, bidderRequest } = generateBuildRequestMock({}); + bidderRequest.gdprConsent = { + consentString: 'BOtmiBKOtmiBKABABAENAFAAAAACeAAA', + apiVersion: 2, + vendorData: { + purpose: { + consents: { + '1': false + } + } + }, + gdprApplies: true + }; + const options = spec.buildRequests(validBidRequests, bidderRequest)[0].options; + expect(options.withCredentials).to.be.false; + }); + }); + + describe('Endpoint & Impression Request Mode:', () => { + it('should route request to config override endpoint', () => { + const { validBidRequests, bidderRequest } = generateBuildRequestMock({}); + const testOverrideEndpoint = 'http://foo.bar.baz.com/bidderRequest'; + config.setConfig({ + yahoossp: { + endpoint: testOverrideEndpoint + } + }); + const response = spec.buildRequests(validBidRequests, bidderRequest)[0]; + expect(response).to.deep.include( + { + method: 'POST', + url: testOverrideEndpoint + }); + }); + + it('should route request to /bidRequest endpoint when dcn & pos present', () => { + config.setConfig({ + yahoossp: {} + }); + const { validBidRequests, bidderRequest } = generateBuildRequestMock({}); + const response = spec.buildRequests(validBidRequests, bidderRequest); + expect(response[0]).to.deep.include({ + method: 'POST', + url: 'https://c2shb.pubgw.yahoo.com/bidRequest' + }); + }); + + it('should route request to /partners endpoint when pubId is present', () => { + const { validBidRequests, bidderRequest } = generateBuildRequestMock({pubIdMode: true}); + const response = spec.buildRequests(validBidRequests, bidderRequest); + expect(response[0]).to.deep.include({ + method: 'POST', + url: 'https://c2shb.pubgw.yahoo.com/admax/bid/partners/PBJS' + }); + }); + + it('should return a single request object for single request mode', () => { + let { bidRequest, validBidRequests, bidderRequest } = generateBuildRequestMock({}); + const BID_ID_2 = '84ab50xxxxx'; + const BID_POS_2 = 'footer'; + const AD_UNIT_CODE_2 = 'test-ad-unit-code-123'; + const { bidRequest: bidRequest2 } = generateBuildRequestMock({bidId: BID_ID_2, pos: BID_POS_2, adUnitCode: AD_UNIT_CODE_2}); + validBidRequests = [bidRequest, bidRequest2]; + bidderRequest.bids = validBidRequests; + + config.setConfig({ + yahoossp: { + singleRequestMode: true + } + }); + + const data = spec.buildRequests(validBidRequests, bidderRequest).data; + expect(data.imp).to.be.an('array').with.lengthOf(2); + + expect(data.imp[0]).to.deep.include({ + id: DEFAULT_BID_ID, + ext: { + pos: DEFAULT_BID_POS, + dfp_ad_unit_code: DEFAULT_AD_UNIT_CODE + } + }); + + expect(data.imp[1]).to.deep.include({ + id: BID_ID_2, + ext: { + pos: BID_POS_2, + dfp_ad_unit_code: AD_UNIT_CODE_2 + } + }); + }); + }); + + describe('Validate request filtering:', () => { + it('should not return request when no bids are present', function () { + let request = spec.buildRequests([]); + expect(request).to.be.undefined; + }); + + it('buildRequests(): should return an array with the correct amount of request objects', () => { + const { validBidRequests, bidderRequest } = generateBuildRequestMock({}); + const response = spec.buildRequests(validBidRequests, bidderRequest).bidderRequest; + expect(response.bids).to.be.an('array').to.have.lengthOf(1); + }); + }); + + describe('Request Headers validation:', () => { + it('should return request objects with the relevant custom headers and content type declaration', () => { + const { validBidRequests, bidderRequest } = generateBuildRequestMock({}); + const options = spec.buildRequests(validBidRequests, bidderRequest).options; + expect(options).to.deep.equal( + { + contentType: 'application/json', + customHeaders: { + 'x-openrtb-version': '2.5' + }, + withCredentials: true + }); + }); + }); + + describe('Request Payload oRTB bid validation:', () => { + it('should generate a valid openRTB bid-request object in the data field', () => { + const { validBidRequests, bidderRequest } = generateBuildRequestMock({}); + const data = spec.buildRequests(validBidRequests, bidderRequest).data; + expect(data.site).to.deep.equal({ + id: bidderRequest.bids[0].params.dcn, + page: bidderRequest.refererInfo.referer + }); + + expect(data.device).to.deep.equal({ + dnt: 0, + ua: navigator.userAgent, + ip: undefined + }); + + expect(data.regs).to.deep.equal({ + ext: { + 'us_privacy': '', + gdpr: 1 + } + }); + + expect(data.source).to.deep.equal({ + ext: { + hb: 1, + adapterver: ADAPTER_VERSION, + prebidver: PREBID_VERSION, + integration: { + name: INTEGRATION_METHOD, + ver: PREBID_VERSION + } + }, + fd: 1 + }); + + expect(data.user).to.deep.equal({ + ext: { + consent: bidderRequest.gdprConsent.consentString, + eids: [] + } + }); + + expect(data.cur).to.deep.equal(['USD']); + }); + + it('should generate a valid openRTB imp.ext object in the bid-request', () => { + const { validBidRequests, bidderRequest } = generateBuildRequestMock({}); + const bid = validBidRequests[0]; + const data = spec.buildRequests(validBidRequests, bidderRequest).data; + expect(data.imp[0].ext).to.deep.equal({ + pos: bid.params.pos, + dfp_ad_unit_code: DEFAULT_AD_UNIT_CODE + }); + }); + + it('should use siteId value as site.id in the outbound bid-request when using "pubId" integration mode', () => { + let { validBidRequests, bidderRequest } = generateBuildRequestMock({pubIdMode: true}); + validBidRequests[0].params.siteId = '1234567'; + const data = spec.buildRequests(validBidRequests, bidderRequest).data; + expect(data.site.id).to.equal('1234567'); + }); + + it('should use placementId value as imp.tagid in the outbound bid-request when using "pubId" integration mode', () => { + let { validBidRequests, bidderRequest } = generateBuildRequestMock({pubIdMode: true}); + validBidRequests[0].params.placementId = 'header-300x250'; + const data = spec.buildRequests(validBidRequests, bidderRequest).data; + expect(data.imp[0].tagid).to.deep.equal('header-300x250'); + }); + }); + + describe('Request Payload oRTB bid.imp validation:', () => { + // Validate Banner imp imp when yahoossp.mode=undefined + it('should generate a valid "Banner" imp object', () => { + config.setConfig({ + yahoossp: {} + }); + const { validBidRequests, bidderRequest } = generateBuildRequestMock({}); + const data = spec.buildRequests(validBidRequests, bidderRequest)[0].data; + expect(data.imp[0].video).to.not.exist; + expect(data.imp[0].banner).to.deep.equal({ + mimes: ['text/html', 'text/javascript', 'application/javascript', 'image/jpg'], + format: [{w: 300, h: 250}, {w: 300, h: 600}] + }); + }); + + // Validate Banner imp when yahoossp.mode="banner" + it('should generate a valid "Banner" imp object', () => { + config.setConfig({ + yahoossp: { mode: 'banner' } + }); + const { validBidRequests, bidderRequest } = generateBuildRequestMock({}); + const data = spec.buildRequests(validBidRequests, bidderRequest)[0].data; + expect(data.imp[0].video).to.not.exist; + expect(data.imp[0].banner).to.deep.equal({ + mimes: ['text/html', 'text/javascript', 'application/javascript', 'image/jpg'], + format: [{w: 300, h: 250}, {w: 300, h: 600}] + }); + }); + + // Validate Video imp + it('should generate a valid "Video" only imp object', () => { + config.setConfig({ + yahoossp: { mode: 'video' } + }); + const { validBidRequests, bidderRequest } = generateBuildRequestMock({adUnitType: 'video'}); + const data = spec.buildRequests(validBidRequests, bidderRequest)[0].data; + expect(data.imp[0].banner).to.not.exist; + expect(data.imp[0].video).to.deep.equal({ + mimes: ['video/mp4', 'application/javascript'], + w: 300, + h: 250, + api: [2], + protocols: [2, 5], + startdelay: 0, + linearity: 1, + maxbitrate: undefined, + maxduration: undefined, + minduration: undefined, + delivery: undefined, + pos: undefined, + playbackmethod: undefined, + rewarded: undefined, + placement: undefined + }); + }); + + // Validate multi-format Video+banner imp + it('should generate a valid multi-format "Video + Banner" imp object', () => { + config.setConfig({ + yahoossp: { mode: 'all' } + }); + const { validBidRequests, bidderRequest } = generateBuildRequestMock({adUnitType: 'multi-format'}); + const data = spec.buildRequests(validBidRequests, bidderRequest)[0].data; + expect(data.imp[0].banner).to.deep.equal({ + mimes: ['text/html', 'text/javascript', 'application/javascript', 'image/jpg'], + format: [{w: 300, h: 250}, {w: 300, h: 600}] + }); + expect(data.imp[0].video).to.deep.equal({ + mimes: ['video/mp4', 'application/javascript'], + w: 300, + h: 250, + api: [2], + protocols: [2, 5], + startdelay: 0, + linearity: 1, + maxbitrate: undefined, + maxduration: undefined, + minduration: undefined, + delivery: undefined, + pos: undefined, + playbackmethod: undefined, + rewarded: undefined, + placement: undefined + }); + }); + + // Validate Key-Value Pairs + it('should generate supported String, Number, Array of Strings, Array of Numbers key-value pairs and append to imp.ext.kvs', () => { + let { validBidRequests, bidderRequest } = generateBuildRequestMock({}) + validBidRequests[0].params.kvp = { + key1: 'String', + key2: 123456, + key3: ['String', 'String', 'String'], + key4: [1, 2, 3], + invalidKey1: true, + invalidKey2: null, + invalidKey3: ['string', 1234], + invalidKey4: {a: 1, b: 2}, + invalidKey5: undefined + }; + const data = spec.buildRequests(validBidRequests, bidderRequest)[0].data; + + expect(data.imp[0].ext.kvs).to.deep.equal({ + key1: 'String', + key2: 123456, + key3: ['String', 'String', 'String'], + key4: [1, 2, 3] + }); + }); + }); + + describe('Multiple adUnit validations:', () => { + // Multiple banner adUnits + it('should generate multiple bid-requests for each adUnit - 2 banner only', () => { + config.setConfig({ + yahoossp: { mode: 'banner' } + }); + + const BID_ID_2 = '84ab50xxxxx'; + const BID_POS_2 = 'footer'; + const AD_UNIT_CODE_2 = 'test-ad-unit-code-123'; + const BID_ID_3 = '84ab50yyyyy'; + const BID_POS_3 = 'hero'; + const AD_UNIT_CODE_3 = 'video-ad-unit'; + + let { bidRequest, validBidRequests, bidderRequest } = generateBuildRequestMock({}); // banner + const { bidRequest: bidRequest2 } = generateBuildRequestMock({bidId: BID_ID_2, pos: BID_POS_2, adUnitCode: AD_UNIT_CODE_2}); // banner + const { bidRequest: bidRequest3 } = generateBuildRequestMock({bidId: BID_ID_3, pos: BID_POS_3, adUnitCode: AD_UNIT_CODE_3, adUnitType: 'video'}); // video (should be filtered) + validBidRequests = [bidRequest, bidRequest2, bidRequest3]; + bidderRequest.bids = validBidRequests; + + const response = spec.buildRequests(validBidRequests, bidderRequest) + expect(response).to.be.a('array'); + expect(response.length).to.equal(2); + response.forEach((obj) => { + expect(obj.data.imp[0].video).to.not.exist + expect(obj.data.imp[0].banner).to.deep.equal({ + mimes: ['text/html', 'text/javascript', 'application/javascript', 'image/jpg'], + format: [{w: 300, h: 250}, {w: 300, h: 600}] + }); + }); + }); + + // Multiple video adUnits + it('should generate multiple bid-requests for each adUnit - 2 video only', () => { + config.setConfig({ + yahoossp: { mode: 'video' } + }); + const BID_ID_2 = '84ab50xxxxx'; + const BID_POS_2 = 'footer'; + const AD_UNIT_CODE_2 = 'test-ad-unit-code-123'; + const BID_ID_3 = '84ab50yyyyy'; + const BID_POS_3 = 'hero'; + const AD_UNIT_CODE_3 = 'video-ad-unit'; + + let { bidRequest, validBidRequests, bidderRequest } = generateBuildRequestMock({adUnitType: 'video'}); // video + const { bidRequest: bidRequest2 } = generateBuildRequestMock({bidId: BID_ID_2, pos: BID_POS_2, adUnitCode: AD_UNIT_CODE_2, adUnitType: 'video'}); // video + const { bidRequest: bidRequest3 } = generateBuildRequestMock({bidId: BID_ID_3, pos: BID_POS_3, adUnitCode: AD_UNIT_CODE_3}); // banner (should be filtered) + validBidRequests = [bidRequest, bidRequest2, bidRequest3]; + bidderRequest.bids = validBidRequests; + + const response = spec.buildRequests(validBidRequests, bidderRequest) + expect(response).to.be.a('array'); + expect(response.length).to.equal(2); + response.forEach((obj) => { + expect(obj.data.imp[0].banner).to.not.exist + expect(obj.data.imp[0].video).to.deep.equal({ + mimes: ['video/mp4', 'application/javascript'], + w: 300, + h: 250, + api: [2], + protocols: [2, 5], + startdelay: 0, + linearity: 1, + maxbitrate: undefined, + maxduration: undefined, + minduration: undefined, + delivery: undefined, + pos: undefined, + playbackmethod: undefined, + rewarded: undefined, + placement: undefined + }); + }); + }); + // Mixed adUnits 1-banner, 1-video, 1-native (should filter out native) + it('should generate multiple bid-requests for both "video & banner" adUnits', () => { + config.setConfig({ + yahoossp: { mode: 'all' } + }); + const BID_ID_2 = '84ab50xxxxx'; + const BID_POS_2 = 'footer'; + const AD_UNIT_CODE_2 = 'video-ad-unit'; + const BID_ID_3 = '84ab50yyyyy'; + const BID_POS_3 = 'hero'; + const AD_UNIT_CODE_3 = 'native-ad-unit'; + + let { bidRequest, validBidRequests, bidderRequest } = generateBuildRequestMock({adUnitType: 'banner'}); // banner + const { bidRequest: bidRequest2 } = generateBuildRequestMock({bidId: BID_ID_2, pos: BID_POS_2, adUnitCode: AD_UNIT_CODE_2, adUnitType: 'video'}); // video + const { bidRequest: bidRequest3 } = generateBuildRequestMock({bidId: BID_ID_3, pos: BID_POS_3, adUnitCode: AD_UNIT_CODE_3, adUnitType: 'native'}); // native (should be filtered) + validBidRequests = [bidRequest, bidRequest2, bidRequest3]; + bidderRequest.bids = validBidRequests; + + const response = spec.buildRequests(validBidRequests, bidderRequest); + expect(response).to.be.a('array'); + expect(response.length).to.equal(2); + response.forEach((obj) => { + expect(obj.data.imp[0].native).to.not.exist; + }); + + const data1 = response[0].data; + expect(data1.imp[0].video).to.not.exist; + expect(data1.imp[0].banner).to.deep.equal({ + mimes: ['text/html', 'text/javascript', 'application/javascript', 'image/jpg'], + format: [{w: 300, h: 250}, {w: 300, h: 600}] + }); + + const data2 = response[1].data; + expect(data2.imp[0].banner).to.not.exist; + expect(data2.imp[0].video).to.deep.equal({ + mimes: ['video/mp4', 'application/javascript'], + w: 300, + h: 250, + api: [2], + protocols: [2, 5], + startdelay: 0, + linearity: 1, + maxbitrate: undefined, + maxduration: undefined, + minduration: undefined, + delivery: undefined, + pos: undefined, + playbackmethod: undefined, + rewarded: undefined, + placement: undefined + }); + }); + }); + + describe('Video params firstlook & bidOverride validations:', () => { + it('should first look at params.bidOverride for video placement data', () => { + config.setConfig({ + yahoossp: { mode: 'video' } + }); + const bidOverride = { + imp: { + video: { + mimes: ['video/mp4'], + w: 400, + h: 350, + api: [1], + protocols: [1, 3], + startdelay: 0, + linearity: 1, + maxbitrate: 400000, + maxduration: 3600, + minduration: 1500, + delivery: 1, + pos: 123456, + playbackmethod: 1, + rewarded: 1, + placement: 1 + } + } + } + const { validBidRequests, bidderRequest } = generateBuildRequestMock({adUnitType: 'video', bidOverrideObject: bidOverride}); + const data = spec.buildRequests(validBidRequests, bidderRequest)[0].data; + expect(data.imp[0].video).to.deep.equal(bidOverride.imp.video); + }); + + it('should second look at bid.mediaTypes.video for video placement data', () => { + config.setConfig({ + yahoossp: { mode: 'video' } + }); + let { bidRequest, bidderRequest } = generateBuildRequestMock({adUnitType: 'video'}); + bidRequest.mediaTypes.video = { + mimes: ['video/mp4'], + playerSize: [400, 350], + api: [1], + protocols: [1, 3], + startdelay: 0, + linearity: 1, + maxbitrate: 400000, + maxduration: 3600, + minduration: 1500, + delivery: 1, + pos: 123456, + playbackmethod: 1, + placement: 1 + } + const validBidRequests = [bidRequest]; + bidderRequest.bids = validBidRequests; + const data = spec.buildRequests(validBidRequests, bidderRequest)[0].data; + expect(data.imp[0].video).to.deep.equal({ + mimes: ['video/mp4'], + w: 400, + h: 350, + api: [1], + protocols: [1, 3], + startdelay: 0, + linearity: 1, + maxbitrate: 400000, + maxduration: 3600, + minduration: 1500, + delivery: 1, + pos: 123456, + playbackmethod: 1, + placement: 1, + rewarded: undefined + }); + }); + + it('should use params.bidOverride.device.ip override', () => { + config.setConfig({ + yahoossp: { mode: 'all' } + }); + const bidOverride = { + device: { + ip: '1.2.3.4' + } + } + const { validBidRequests, bidderRequest } = generateBuildRequestMock({adUnitType: 'video', bidOverrideObject: bidOverride}); + const data = spec.buildRequests(validBidRequests, bidderRequest)[0].data; + expect(data.device.ip).to.deep.equal(bidOverride.device.ip); + }); + }); + // #endregion buildRequests(): + + describe('interpretResponse()', () => { + describe('for mediaTypes: "banner"', () => { + it('should insert banner payload into response[0].ad', () => { + const { serverResponse, bidderRequest } = generateResponseMock('banner'); + const response = spec.interpretResponse(serverResponse, {bidderRequest}); + expect(response[0].ad).to.equal(''); + expect(response[0].mediaType).to.equal('banner'); + }) + }); + + describe('for mediaTypes: "video"', () => { + it('should insert video VPAID payload into vastXml', () => { + const { serverResponse, bidderRequest } = generateResponseMock('video'); + const response = spec.interpretResponse(serverResponse, {bidderRequest}); + expect(response[0].ad).to.be.undefined; + expect(response[0].vastXml).to.equal(''); + expect(response[0].mediaType).to.equal('video'); + }) + + it('should insert video VAST win notification into vastUrl', () => { + const { serverResponse, bidderRequest } = generateResponseMock('video', 'vast'); + const response = spec.interpretResponse(serverResponse, {bidderRequest}); + expect(response[0].ad).to.be.undefined; + expect(response[0].vastUrl).to.equal('https://yahoo.com?event=adAttempt'); + expect(response[0].vastXml).to.equal(''); + expect(response[0].mediaType).to.equal('video'); + }) + + it('should insert video DAP O2 Player into ad', () => { + const { serverResponse, bidderRequest } = generateResponseMock('dap-o2', 'vpaid'); + const response = spec.interpretResponse(serverResponse, {bidderRequest}); + expect(response[0].ad).to.equal(''); + expect(response[0].vastUrl).to.be.undefined; + expect(response[0].vastXml).to.be.undefined; + expect(response[0].mediaType).to.equal('banner'); + }); + + it('should insert video DAP Unified Player into ad', () => { + const { serverResponse, bidderRequest } = generateResponseMock('dap-up', 'vpaid'); + const response = spec.interpretResponse(serverResponse, {bidderRequest}); + expect(response[0].ad).to.equal(''); + expect(response[0].vastUrl).to.be.undefined; + expect(response[0].vastXml).to.be.undefined; + expect(response[0].mediaType).to.equal('banner'); + }) + }); + + describe('Support Advertiser domains', () => { + it('should append bid-response adomain to meta.advertiserDomains', () => { + const { serverResponse, bidderRequest } = generateResponseMock('video', 'vpaid'); + const response = spec.interpretResponse(serverResponse, {bidderRequest}); + expect(response[0].meta.advertiserDomains).to.be.a('array'); + expect(response[0].meta.advertiserDomains[0]).to.equal('advertiser-domain.com'); + }) + }); + + describe('bid response Ad ID / Creative ID', () => { + it('should use adId if it exists in the bid-response', () => { + const { serverResponse, bidderRequest } = generateResponseMock('banner'); + const adId = 'bid-response-adId'; + serverResponse.body.seatbid[0].bid[0].adId = adId; + const response = spec.interpretResponse(serverResponse, {bidderRequest}); + expect(response[0].adId).to.equal(adId); + }); + + it('should use impid if adId does not exist in the bid-response', () => { + const { serverResponse, bidderRequest } = generateResponseMock('banner'); + const impid = '25b6c429c1f52f'; + serverResponse.body.seatbid[0].bid[0].impid = impid; + const response = spec.interpretResponse(serverResponse, {bidderRequest}); + expect(response[0].adId).to.equal(impid); + }); + + it('should use crid if adId & impid do not exist in the bid-response', () => { + const { serverResponse, bidderRequest } = generateResponseMock('banner'); + const crid = 'passback-12579'; + serverResponse.body.seatbid[0].bid[0].impid = undefined; + serverResponse.body.seatbid[0].bid[0].crid = crid; + const response = spec.interpretResponse(serverResponse, {bidderRequest}); + expect(response[0].adId).to.equal(crid); + }); + }); + + describe('Time To Live (ttl)', () => { + const UNSUPPORTED_TTL_FORMATS = ['string', [1, 2, 3], true, false, null, undefined]; + UNSUPPORTED_TTL_FORMATS.forEach(param => { + it('should not allow unsupported global yahoossp.ttl formats and default to 300', () => { + const { serverResponse, bidderRequest } = generateResponseMock('banner'); + config.setConfig({ + yahoossp: { ttl: param } + }); + const response = spec.interpretResponse(serverResponse, {bidderRequest}); + expect(response[0].ttl).to.equal(300); + }); + + it('should not allow unsupported params.ttl formats and default to 300', () => { + const { serverResponse, bidderRequest } = generateResponseMock('banner'); + bidderRequest.bids[0].params.ttl = param; + const response = spec.interpretResponse(serverResponse, {bidderRequest}); + expect(response[0].ttl).to.equal(300); + }); + }); + + const UNSUPPORTED_TTL_VALUES = [-1, 3601]; + UNSUPPORTED_TTL_VALUES.forEach(param => { + it('should not allow invalid global yahoossp.ttl values 3600 < ttl < 0 and default to 300', () => { + const { serverResponse, bidderRequest } = generateResponseMock('banner'); + config.setConfig({ + yahoossp: { ttl: param } + }); + const response = spec.interpretResponse(serverResponse, {bidderRequest}); + expect(response[0].ttl).to.equal(300); + }); + + it('should not allow invalid params.ttl values 3600 < ttl < 0 and default to 300', () => { + const { serverResponse, bidderRequest } = generateResponseMock('banner'); + bidderRequest.bids[0].params.ttl = param; + const response = spec.interpretResponse(serverResponse, {bidderRequest}); + expect(response[0].ttl).to.equal(300); + }); + }); + + it('should give presedence to Gloabl ttl over params.ttl ', () => { + const { serverResponse, bidderRequest } = generateResponseMock('banner'); + config.setConfig({ + yahoossp: { ttl: 500 } + }); + bidderRequest.bids[0].params.ttl = 400; + const response = spec.interpretResponse(serverResponse, {bidderRequest}); + expect(response[0].ttl).to.equal(500); + }); + }); + }); +}); diff --git a/test/spec/modules/yieldlabBidAdapter_spec.js b/test/spec/modules/yieldlabBidAdapter_spec.js index d150646851f..f80cad46d50 100644 --- a/test/spec/modules/yieldlabBidAdapter_spec.js +++ b/test/spec/modules/yieldlabBidAdapter_spec.js @@ -1,3 +1,4 @@ +import { config } from 'src/config.js'; import { expect } from 'chai' import { spec } from 'modules/yieldlabBidAdapter.js' import { newBidder } from 'src/adapters/bidderFactory.js' @@ -16,7 +17,22 @@ const REQUEST = { 'extraParam': true, 'foo': 'bar' }, - 'extId': 'abc' + 'extId': 'abc', + 'iabContent': { + 'id': 'foo_id', + 'episode': '99', + 'title': 'foo_title,bar_title', + 'series': 'foo_series', + 'season': 's1', + 'artist': 'foo bar', + 'genre': 'baz', + 'isrc': 'CC-XXX-YY-NNNNN', + 'url': 'http://foo_url.de', + 'cat': ['cat1', 'cat2,ppp', 'cat3|||//'], + 'context': '7', + 'keywords': ['k1,', 'k2..'], + 'live': '0' + } }, 'bidderRequestId': '143346cf0f1731', 'auctionId': '2e41f65424c87c', @@ -57,6 +73,12 @@ const VIDEO_REQUEST = Object.assign({}, REQUEST, { } }) +const NATIVE_REQUEST = Object.assign({}, REQUEST, { + 'mediaTypes': { + 'native': { } + } +}) + const RESPONSE = { advertiser: 'yieldlab', curl: 'https://www.yieldlab.de', @@ -68,6 +90,42 @@ const RESPONSE = { adtype: 'BANNER' } +const NATIVE_RESPONSE = Object.assign({}, RESPONSE, { + 'adtype': 'NATIVE', + 'native': { + 'link': { + 'url': 'https://www.yieldlab.de' + }, + 'assets': [ + { + 'id': 1, + 'title': { + 'text': 'This is a great headline' + } + }, + { + 'id': 2, + 'img': { + 'url': 'https://localhost:8080/yl-logo100x100.jpg', + 'w': 100, + 'h': 100 + } + }, + { + 'id': 3, + 'data': { + 'value': 'Native body value' + } + } + ], + 'imptrackers': [ + 'http://localhost:8080/ve?d=ODE9ZSY2MTI1MjAzNjMzMzYxPXN0JjA0NWUwZDk0NTY5Yi05M2FiLWUwZTQtOWFjNy1hYWY0MzFiZj1kaXQmMj12', + 'http://localhost:8080/md/1111/9efa4e76-2030-4f04-bb9f-322541f8d611?mdata=false&pvid=false&ids=x:1', + 'http://localhost:8080/imp?s=13216&d=2171514&a=12548955&ts=1633363025216&tid=fb134faa-7ca9-4e0e-ba39-b96549d0e540&l=0' + ] + } +}) + const VIDEO_RESPONSE = Object.assign({}, RESPONSE, { 'adtype': 'VIDEO' }) @@ -86,6 +144,10 @@ const REQPARAMS_GDPR = Object.assign({}, REQPARAMS, { consent: 'BN5lERiOMYEdiAKAWXEND1AAAAE6DABACMA' }) +const REQPARAMS_IAB_CONTENT = Object.assign({}, REQPARAMS, { + iab_content: 'id%3Afoo_id%2Cepisode%3A99%2Ctitle%3Afoo_title%252Cbar_title%2Cseries%3Afoo_series%2Cseason%3As1%2Cartist%3Afoo%2520bar%2Cgenre%3Abaz%2Cisrc%3ACC-XXX-YY-NNNNN%2Curl%3Ahttp%253A%252F%252Ffoo_url.de%2Ccat%3Acat1%7Ccat2%252Cppp%7Ccat3%257C%257C%257C%252F%252F%2Ccontext%3A7%2Ckeywords%3Ak1%252C%7Ck2..%2Clive%3A0' +}) + describe('yieldlabBidAdapter', function () { const adapter = newBidder(spec) @@ -139,6 +201,40 @@ describe('yieldlabBidAdapter', function () { expect(request.url).to.include('schain=1.0,1!indirectseller.com,1,1,,,,!indirectseller2.com,2,1,,indirectseller2%20name%20with%20comma%20%2C%20and%20bang%20%21,,') }) + it('passes iab_content string to bid request', function () { + expect(request.url).to.include('iab_content=id%3Afoo_id%2Cepisode%3A99%2Ctitle%3Afoo_title%252Cbar_title%2Cseries%3Afoo_series%2Cseason%3As1%2Cartist%3Afoo%2520bar%2Cgenre%3Abaz%2Cisrc%3ACC-XXX-YY-NNNNN%2Curl%3Ahttp%253A%252F%252Ffoo_url.de%2Ccat%3Acat1%7Ccat2%252Cppp%7Ccat3%257C%257C%257C%252F%252F%2Ccontext%3A7%2Ckeywords%3Ak1%252C%7Ck2..%2Clive%3A0') + }) + + const siteConfig = { + 'ortb2': { + 'site': { + 'content': { + 'id': 'id_from_config' + } + } + } + } + + it('generates iab_content string from bidder params', function () { + config.setConfig(siteConfig); + const request = spec.buildRequests([REQUEST]) + expect(request.url).to.include('iab_content=id%3Afoo_id%2Cepisode%3A99%2Ctitle%3Afoo_title%252Cbar_title%2Cseries%3Afoo_series%2Cseason%3As1%2Cartist%3Afoo%2520bar%2Cgenre%3Abaz%2Cisrc%3ACC-XXX-YY-NNNNN%2Curl%3Ahttp%253A%252F%252Ffoo_url.de%2Ccat%3Acat1%7Ccat2%252Cppp%7Ccat3%257C%257C%257C%252F%252F%2Ccontext%3A7%2Ckeywords%3Ak1%252C%7Ck2..%2Clive%3A0') + config.resetConfig(); + }) + + it('generates iab_content string from first party data if not provided in bidder params', function () { + const requestWithoutIabContent = { + 'params': { + 'adslotId': '1111', + 'supplyId': '2222' + } + } + config.setConfig(siteConfig); + const request = spec.buildRequests([requestWithoutIabContent]) + expect(request.url).to.include('iab_content=id%3Aid_from_config') + config.resetConfig(); + }) + const refererRequest = spec.buildRequests(bidRequests, { refererInfo: { canonicalUrl: undefined, @@ -203,6 +299,11 @@ describe('yieldlabBidAdapter', function () { expect(result[0].ad).to.include('&consent=BN5lERiOMYEdiAKAWXEND1AAAAE6DABACMA') }) + it('should append iab_content to adtag', function () { + const result = spec.interpretResponse({body: [RESPONSE]}, {validBidRequests: [REQUEST], queryParams: REQPARAMS_IAB_CONTENT}) + expect(result[0].ad).to.include('&iab_content=id%3Afoo_id%2Cepisode%3A99%2Ctitle%3Afoo_title%252Cbar_title%2Cseries%3Afoo_series%2Cseason%3As1%2Cartist%3Afoo%2520bar%2Cgenre%3Abaz%2Cisrc%3ACC-XXX-YY-NNNNN%2Curl%3Ahttp%253A%252F%252Ffoo_url.de%2Ccat%3Acat1%7Ccat2%252Cppp%7Ccat3%257C%257C%257C%252F%252F%2Ccontext%3A7%2Ckeywords%3Ak1%252C%7Ck2..%2Clive%3A0') + }) + it('should get correct bid response when passing more than one size', function () { const REQUEST2 = Object.assign({}, REQUEST, { 'sizes': [ @@ -238,6 +339,45 @@ describe('yieldlabBidAdapter', function () { expect(result[0].vastUrl).to.include('&id=abc') }) + it('should add adUrl and native assets when type is Native', function () { + const result = spec.interpretResponse({body: [NATIVE_RESPONSE]}, {validBidRequests: [NATIVE_REQUEST], queryParams: REQPARAMS}) + + expect(result[0].requestId).to.equal('2d925f27f5079f') + expect(result[0].cpm).to.equal(0.01) + expect(result[0].mediaType).to.equal('native') + expect(result[0].adUrl).to.include('https://ad.yieldlab.net/d/1111/2222/?ts=') + expect(result[0].native.title).to.equal('This is a great headline') + expect(result[0].native.body).to.equal('Native body value') + expect(result[0].native.image.url).to.equal('https://localhost:8080/yl-logo100x100.jpg') + expect(result[0].native.image.width).to.equal(100) + expect(result[0].native.image.height).to.equal(100) + expect(result[0].native.clickUrl).to.equal('https://www.yieldlab.de') + expect(result[0].native.impressionTrackers.length).to.equal(3) + }) + + it('should add adUrl and default native assets when type is Native', function () { + const NATIVE_RESPONSE_2 = Object.assign({}, NATIVE_RESPONSE, { + 'native': { + 'link': { + 'url': 'https://www.yieldlab.de' + }, + 'assets': [], + 'imptrackers': [] + } + }) + const result = spec.interpretResponse({body: [NATIVE_RESPONSE_2]}, {validBidRequests: [NATIVE_REQUEST], queryParams: REQPARAMS}) + + expect(result[0].requestId).to.equal('2d925f27f5079f') + expect(result[0].cpm).to.equal(0.01) + expect(result[0].mediaType).to.equal('native') + expect(result[0].adUrl).to.include('https://ad.yieldlab.net/d/1111/2222/?ts=') + expect(result[0].native.title).to.equal('') + expect(result[0].native.body).to.equal('') + expect(result[0].native.image.url).to.equal('') + expect(result[0].native.image.width).to.equal(0) + expect(result[0].native.image.height).to.equal(0) + }) + it('should append gdpr parameters to vastUrl', function () { const result = spec.interpretResponse({body: [VIDEO_RESPONSE]}, {validBidRequests: [VIDEO_REQUEST], queryParams: REQPARAMS_GDPR}) @@ -268,5 +408,10 @@ describe('yieldlabBidAdapter', function () { expect(result[0].ad).to.include('&pvid=43513f11-55a0-4a83-94e5-0ebc08f54a2c') expect(result[0].vastUrl).to.include('&pvid=43513f11-55a0-4a83-94e5-0ebc08f54a2c') }) + + it('should append iab_content to vastUrl', function () { + const result = spec.interpretResponse({body: [VIDEO_RESPONSE]}, {validBidRequests: [VIDEO_REQUEST], queryParams: REQPARAMS_IAB_CONTENT}) + expect(result[0].vastUrl).to.include('&iab_content=id%3Afoo_id%2Cepisode%3A99%2Ctitle%3Afoo_title%252Cbar_title%2Cseries%3Afoo_series%2Cseason%3As1%2Cartist%3Afoo%2520bar%2Cgenre%3Abaz%2Cisrc%3ACC-XXX-YY-NNNNN%2Curl%3Ahttp%253A%252F%252Ffoo_url.de%2Ccat%3Acat1%7Ccat2%252Cppp%7Ccat3%257C%257C%257C%252F%252F%2Ccontext%3A7%2Ckeywords%3Ak1%252C%7Ck2..%2Clive%3A0') + }) }) }) diff --git a/test/spec/modules/yieldmoSyntheticInventoryModule_spec.js b/test/spec/modules/yieldmoSyntheticInventoryModule_spec.js new file mode 100644 index 00000000000..55b4e7255f7 --- /dev/null +++ b/test/spec/modules/yieldmoSyntheticInventoryModule_spec.js @@ -0,0 +1,89 @@ +import { expect } from 'chai'; +import { + init, + MODULE_NAME, + validateConfig +} from 'modules/yieldmoSyntheticInventoryModule'; + +const mockedYmConfig = { + placementId: '123456', + adUnitPath: '/6355419/ad_unit_name_used_in_gam' +}; + +const setGoogletag = () => { + window.googletag = { + cmd: [], + defineSlot: sinon.stub(), + addService: sinon.stub(), + pubads: sinon.stub(), + setTargeting: sinon.stub(), + enableServices: sinon.stub(), + display: sinon.stub(), + }; + window.googletag.defineSlot.returns(window.googletag); + window.googletag.addService.returns(window.googletag); + window.googletag.pubads.returns({getSlots: sinon.stub()}); + return window.googletag; +} + +describe('Yieldmo Synthetic Inventory Module', function() { + let config = Object.assign({}, mockedYmConfig); + let googletagBkp; + + beforeEach(function () { + googletagBkp = window.googletag; + delete window.googletag; + }); + + afterEach(function () { + window.googletag = googletagBkp; + }); + + it('should be enabled with valid required params', function() { + expect(function () { + init(mockedYmConfig); + }).not.to.throw() + }); + + it('should throw an error if placementId is missed', function() { + const {placementId, ...config} = mockedYmConfig; + + expect(function () { + validateConfig(config); + }).throw(`${MODULE_NAME}: placementId required`) + }); + + it('should throw an error if adUnitPath is missed', function() { + const {adUnitPath, ...config} = mockedYmConfig; + + expect(function () { + validateConfig(config); + }).throw(`${MODULE_NAME}: adUnitPath required`) + }); + + it('should add correct googletag.cmd', function() { + const containerName = 'ym_sim_container_' + mockedYmConfig.placementId; + const gtag = setGoogletag(); + + init(mockedYmConfig); + + expect(gtag.cmd.length).to.equal(1); + + gtag.cmd[0](); + + expect(gtag.addService.getCall(0)).to.not.be.null; + expect(gtag.setTargeting.getCall(0)).to.not.be.null; + expect(gtag.setTargeting.getCall(0).args[0]).to.exist.and.to.equal('ym_sim_p_id'); + expect(gtag.setTargeting.getCall(0).args[1]).to.exist.and.to.equal(mockedYmConfig.placementId); + expect(gtag.defineSlot.getCall(0)).to.not.be.null; + expect(gtag.enableServices.getCall(0)).to.not.be.null; + expect(gtag.display.getCall(0)).to.not.be.null; + expect(gtag.display.getCall(0).args[0]).to.exist.and.to.equal(containerName); + expect(gtag.pubads.getCall(0)).to.not.be.null; + + const gamContainerEl = window.document.getElementById(containerName); + expect(gamContainerEl).to.not.be.null; + + gamContainerEl.parentNode.removeChild(gamContainerEl); + }); +}); diff --git a/test/spec/modules/yieldoneBidAdapter_spec.js b/test/spec/modules/yieldoneBidAdapter_spec.js index 4186c5da41a..1b38bb94a8c 100644 --- a/test/spec/modules/yieldoneBidAdapter_spec.js +++ b/test/spec/modules/yieldoneBidAdapter_spec.js @@ -7,6 +7,8 @@ const ENDPOINT = 'https://y.one.impact-ad.jp/h_bid'; const USER_SYNC_URL = 'https://y.one.impact-ad.jp/push_sync'; const VIDEO_PLAYER_URL = 'https://img.ak.impact-ad.jp/ic/pone/ivt/firstview/js/dac-video-prebid.min.js'; +const DEFAULT_VIDEO_SIZE = {w: 640, h: 360}; + describe('yieldoneBidAdapter', function() { const adapter = newBidder(spec); @@ -40,32 +42,7 @@ describe('yieldoneBidAdapter', function() { }); describe('buildRequests', function () { - let bidRequests = [ - { - 'bidder': 'yieldone', - 'params': { - placementId: '36891' - }, - 'adUnitCode': 'adunit-code1', - 'sizes': [[300, 250], [336, 280]], - 'bidId': '23beaa6af6cdde', - 'bidderRequestId': '19c0c1efdf37e7', - 'auctionId': '61466567-d482-4a16-96f0-fe5f25ffbdf1', - }, - { - 'bidder': 'yieldone', - 'params': { - placementId: '47919' - }, - 'adUnitCode': 'adunit-code2', - 'sizes': [[300, 250]], - 'bidId': '382091349b149f"', - 'bidderRequestId': '"1f9c98192de251"', - 'auctionId': '61466567-d482-4a16-96f0-fe5f25ffbdf1', - } - ]; - - let bidderRequest = { + const bidderRequest = { refererInfo: { numIframes: 0, reachedTop: true, @@ -74,49 +51,318 @@ describe('yieldoneBidAdapter', function() { } }; - const request = spec.buildRequests(bidRequests, bidderRequest); + describe('Basic', function () { + const bidRequests = [ + { + 'bidder': 'yieldone', + 'params': {placementId: '36891'}, + 'adUnitCode': 'adunit-code1', + 'bidId': '23beaa6af6cdde', + 'bidderRequestId': '19c0c1efdf37e7', + 'auctionId': '61466567-d482-4a16-96f0-fe5f25ffbdf1', + }, + { + 'bidder': 'yieldone', + 'params': {placementId: '47919'}, + 'adUnitCode': 'adunit-code2', + 'bidId': '382091349b149f"', + 'bidderRequestId': '"1f9c98192de251"', + 'auctionId': '61466567-d482-4a16-96f0-fe5f25ffbdf1', + } + ]; + const request = spec.buildRequests(bidRequests, bidderRequest); - it('sends bid request to our endpoint via GET', function () { - expect(request[0].method).to.equal('GET'); - expect(request[1].method).to.equal('GET'); + it('sends bid request to our endpoint via GET', function () { + expect(request[0].method).to.equal('GET'); + expect(request[1].method).to.equal('GET'); + }); + it('attaches source and version to endpoint URL as query params', function () { + expect(request[0].url).to.equal(ENDPOINT); + expect(request[1].url).to.equal(ENDPOINT); + }); + it('adUnitCode should be sent as uc parameters on any requests', function () { + expect(request[0].data.uc).to.equal('adunit-code1'); + expect(request[1].data.uc).to.equal('adunit-code2'); + }); }); - it('attaches source and version to endpoint URL as query params', function () { - expect(request[0].url).to.equal(ENDPOINT); - expect(request[1].url).to.equal(ENDPOINT); - }); + describe('Old Format', function () { + const bidRequests = [ + { + params: {placementId: '0'}, + mediaType: 'banner', + sizes: [[300, 250], [336, 280]], + }, + { + params: {placementId: '1'}, + mediaType: 'banner', + sizes: [[336, 280]], + }, + { + // It doesn't actually exist. + params: {placementId: '2'}, + }, + { + params: {placementId: '3'}, + mediaType: 'video', + sizes: [[1280, 720], [1920, 1080]], + }, + { + params: {placementId: '4'}, + mediaType: 'video', + sizes: [[1920, 1080]], + }, + { + params: {placementId: '5'}, + mediaType: 'video', + }, + ]; + const request = spec.buildRequests(bidRequests, bidderRequest); - it('parameter sz has more than one size on banner requests', function () { - expect(request[0].data.sz).to.equal('300x250,336x280'); - expect(request[1].data.sz).to.equal('300x250'); + it('parameter sz has more than one size on banner requests', function () { + expect(request[0].data.sz).to.equal('300x250,336x280'); + expect(request[1].data.sz).to.equal('336x280'); + expect(request[2].data.sz).to.equal(''); + expect(request[3].data).to.not.have.property('sz'); + expect(request[4].data).to.not.have.property('sz'); + expect(request[5].data).to.not.have.property('sz'); + }); + + it('width and height should be set as separate parameters on outstream requests', function () { + expect(request[0].data).to.not.have.property('w'); + expect(request[1].data).to.not.have.property('w'); + expect(request[2].data).to.not.have.property('w'); + expect(request[3].data.w).to.equal(1280); + expect(request[3].data.h).to.equal(720); + expect(request[4].data.w).to.equal(1920); + expect(request[4].data.h).to.equal(1080); + expect(request[5].data.w).to.equal(DEFAULT_VIDEO_SIZE.w); + expect(request[5].data.h).to.equal(DEFAULT_VIDEO_SIZE.h); + }); }); - it('width and height should be set as separate parameters on outstream requests', function () { - const bidRequest = Object.assign({}, bidRequests[0]); - bidRequest.mediaTypes = {}; - bidRequest.mediaTypes.video = {context: 'outstream'}; - const request = spec.buildRequests([bidRequest], bidderRequest); - expect(request[0].data.w).to.equal('300'); - expect(request[0].data.h).to.equal('250'); + describe('New Format', function () { + const bidRequests = [ + { + params: {placementId: '0'}, + mediaTypes: { + banner: { + sizes: [[300, 250], [336, 280]], + }, + }, + }, + { + params: {placementId: '1'}, + mediaTypes: { + banner: { + sizes: [[336, 280]], + }, + }, + }, + { + // It doesn't actually exist. + params: {placementId: '2'}, + mediaTypes: { + banner: { + }, + }, + }, + { + params: {placementId: '3'}, + mediaTypes: { + video: { + context: 'outstream', + playerSize: [[1280, 720], [1920, 1080]], + }, + }, + }, + { + params: {placementId: '4'}, + mediaTypes: { + video: { + context: 'outstream', + playerSize: [1920, 1080], + }, + }, + }, + { + params: {placementId: '5'}, + mediaTypes: { + video: { + context: 'outstream', + }, + }, + }, + ]; + const request = spec.buildRequests(bidRequests, bidderRequest); + + it('parameter sz has more than one size on banner requests', function () { + expect(request[0].data.sz).to.equal('300x250,336x280'); + expect(request[1].data.sz).to.equal('336x280'); + expect(request[2].data.sz).to.equal(''); + expect(request[3].data).to.not.have.property('sz'); + expect(request[4].data).to.not.have.property('sz'); + expect(request[5].data).to.not.have.property('sz'); + }); + + it('width and height should be set as separate parameters on outstream requests', function () { + expect(request[0].data).to.not.have.property('w'); + expect(request[1].data).to.not.have.property('w'); + expect(request[2].data).to.not.have.property('w'); + expect(request[3].data.w).to.equal(1280); + expect(request[3].data.h).to.equal(720); + expect(request[4].data.w).to.equal(1920); + expect(request[4].data.h).to.equal(1080); + expect(request[5].data.w).to.equal(DEFAULT_VIDEO_SIZE.w); + expect(request[5].data.h).to.equal(DEFAULT_VIDEO_SIZE.h); + }); }); - it('adUnitCode should be sent as uc parameters on any requests', function () { - expect(request[0].data.uc).to.equal('adunit-code1'); - expect(request[1].data.uc).to.equal('adunit-code2'); + describe('Multiple Format', function () { + const bidRequests = [ + { + // It will be treated as a banner. + params: { + placementId: '0', + }, + mediaTypes: { + banner: { + sizes: [[300, 250], [336, 280]], + }, + video: { + context: 'outstream', + playerSize: [1920, 1080], + }, + }, + }, + { + // It will be treated as a video. + params: { + placementId: '1', + playerParams: {}, + }, + mediaTypes: { + banner: { + sizes: [[300, 250], [336, 280]], + }, + video: { + context: 'outstream', + playerSize: [1920, 1080], + }, + }, + }, + ]; + const request = spec.buildRequests(bidRequests, bidderRequest); + + it('parameter sz has more than one size on banner requests', function () { + expect(request[0].data.sz).to.equal('300x250,336x280'); + expect(request[1].data).to.not.have.property('sz'); + }); + + it('width and height should be set as separate parameters on outstream requests', function () { + expect(request[0].data).to.not.have.property('w'); + expect(request[1].data.w).to.equal(1920); + expect(request[1].data.h).to.equal(1080); + }); }); - describe('userid idl_env should be passed to querystring', function () { - const bid = deepClone([bidRequests[0]]); + describe('FLUX Format', function () { + const bidRequests = [ + { + // It will be treated as a banner. + params: { + placementId: '0', + }, + mediaTypes: { + banner: { + sizes: [[300, 250], [336, 280]], + }, + video: { + context: 'outstream', + playerSize: [[1, 1]], + }, + }, + }, + { + // It will be treated as a video. + params: { + placementId: '1', + playerParams: {}, + playerSize: [1920, 1080], + }, + mediaTypes: { + banner: { + sizes: [[300, 250], [336, 280]], + }, + video: { + context: 'outstream', + playerSize: [[1, 1]], + }, + }, + }, + { + // It will be treated as a video. + params: { + placementId: '2', + playerParams: {}, + }, + mediaTypes: { + banner: { + sizes: [[300, 250], [336, 280]], + }, + video: { + context: 'outstream', + playerSize: [[1, 1]], + }, + }, + }, + ]; + const request = spec.buildRequests(bidRequests, bidderRequest); + + it('parameter sz has more than one size on banner requests', function () { + expect(request[0].data.sz).to.equal('300x250,336x280'); + expect(request[1].data).to.not.have.property('sz'); + expect(request[2].data).to.not.have.property('sz'); + }); + + it('width and height should be set as separate parameters on outstream requests', function () { + expect(request[0].data).to.not.have.property('w'); + expect(request[1].data.w).to.equal(1920); + expect(request[1].data.h).to.equal(1080); + expect(request[2].data.w).to.equal(DEFAULT_VIDEO_SIZE.w); + expect(request[2].data.h).to.equal(DEFAULT_VIDEO_SIZE.h); + }); + }); + describe('LiveRampID', function () { it('dont send LiveRampID if undefined', function () { - bid[0].userId = {}; - const request = spec.buildRequests(bid, bidderRequest); + const bidRequests = [ + { + params: {placementId: '0'}, + }, + { + params: {placementId: '1'}, + userId: {}, + }, + { + params: {placementId: '2'}, + userId: undefined, + }, + ]; + const request = spec.buildRequests(bidRequests, bidderRequest); expect(request[0].data).to.not.have.property('lr_env'); + expect(request[1].data).to.not.have.property('lr_env'); + expect(request[2].data).to.not.have.property('lr_env'); }); it('should send LiveRampID if available', function () { - bid[0].userId = {idl_env: 'idl_env_sample'}; - const request = spec.buildRequests(bid, bidderRequest); + const bidRequests = [ + { + params: {placementId: '0'}, + userId: {idl_env: 'idl_env_sample'}, + }, + ]; + const request = spec.buildRequests(bidRequests, bidderRequest); expect(request[0].data.lr_env).to.equal('idl_env_sample'); }); }); diff --git a/test/spec/modules/zeta_global_sspAnalyticsAdapter_spec.js b/test/spec/modules/zeta_global_sspAnalyticsAdapter_spec.js new file mode 100644 index 00000000000..15a1155f378 --- /dev/null +++ b/test/spec/modules/zeta_global_sspAnalyticsAdapter_spec.js @@ -0,0 +1,427 @@ +import zetaAnalyticsAdapter from 'modules/zeta_global_sspAnalyticsAdapter.js'; +import {config} from 'src/config'; +import CONSTANTS from 'src/constants.json'; +import {logError} from '../../../src/utils'; + +let utils = require('src/utils'); +let events = require('src/events'); + +const MOCK = { + STUB: { + 'auctionId': '25c6d7f5-699a-4bfc-87c9-996f915341fa' + }, + AUCTION_END: { + 'auctionId': '75e394d9-ccce-4978-9238-91e6a1ac88a1', + 'timestamp': 1638441234544, + 'auctionEnd': 1638441234784, + 'auctionStatus': 'completed', + 'adUnits': [ + { + 'code': '/19968336/header-bid-tag-0', + 'mediaTypes': { + 'banner': { + 'sizes': [ + [ + 300, + 250 + ], + [ + 300, + 600 + ] + ] + } + }, + 'bids': [ + { + 'bidder': 'zeta_global_ssp', + 'params': { + 'sid': 111, + 'tags': { + 'shortname': 'prebid_analytics_event_test_shortname', + 'position': 'test_position' + } + } + }, + { + 'bidder': 'appnexus', + 'params': { + 'placementId': 13232385 + } + } + ], + 'sizes': [ + [ + 300, + 250 + ], + [ + 300, + 600 + ] + ], + 'transactionId': '6b29369c-0c2e-414e-be1f-5867aec18d83' + } + ], + 'adUnitCodes': [ + '/19968336/header-bid-tag-0' + ], + 'bidderRequests': [ + { + 'bidderCode': 'zeta_global_ssp', + 'auctionId': '75e394d9-ccce-4978-9238-91e6a1ac88a1', + 'bidderRequestId': '1207cb49191887', + 'bids': [ + { + 'bidder': 'zeta_global_ssp', + 'params': { + 'sid': 111, + 'tags': { + 'shortname': 'prebid_analytics_event_test_shortname', + 'position': 'test_position' + } + }, + 'mediaTypes': { + 'banner': { + 'sizes': [ + [ + 300, + 250 + ], + [ + 300, + 600 + ] + ] + } + }, + 'adUnitCode': '/19968336/header-bid-tag-0', + 'transactionId': '6b29369c-0c2e-414e-be1f-5867aec18d83', + 'sizes': [ + [ + 300, + 250 + ], + [ + 300, + 600 + ] + ], + 'bidId': '206be9a13236af', + 'bidderRequestId': '1207cb49191887', + 'auctionId': '75e394d9-ccce-4978-9238-91e6a1ac88a1', + 'src': 'client', + 'bidRequestsCount': 1, + 'bidderRequestsCount': 1, + 'bidderWinsCount': 0 + } + ], + 'auctionStart': 1638441234544, + 'timeout': 400, + 'refererInfo': { + 'referer': 'http://test-zeta-ssp.net:63342/zeta-ssp/ssp/_dev/examples/page_banner.html', + 'reachedTop': true, + 'isAmp': false, + 'numIframes': 0, + 'stack': [ + 'http://test-zeta-ssp.net:63342/zeta-ssp/ssp/_dev/examples/page_banner.html' + ], + 'canonicalUrl': null + }, + 'start': 1638441234547 + }, + { + 'bidderCode': 'appnexus', + 'auctionId': '75e394d9-ccce-4978-9238-91e6a1ac88a1', + 'bidderRequestId': '32b97f0a935422', + 'bids': [ + { + 'bidder': 'appnexus', + 'params': { + 'placementId': 13232385 + }, + 'mediaTypes': { + 'banner': { + 'sizes': [ + [ + 300, + 250 + ], + [ + 300, + 600 + ] + ] + } + }, + 'adUnitCode': '/19968336/header-bid-tag-0', + 'transactionId': '6b29369c-0c2e-414e-be1f-5867aec18d83', + 'sizes': [ + [ + 300, + 250 + ], + [ + 300, + 600 + ] + ], + 'bidId': '41badc0e164c758', + 'bidderRequestId': '32b97f0a935422', + 'auctionId': '75e394d9-ccce-4978-9238-91e6a1ac88a1', + 'src': 'client', + 'bidRequestsCount': 1, + 'bidderRequestsCount': 1, + 'bidderWinsCount': 0 + } + ], + 'auctionStart': 1638441234544, + 'timeout': 400, + 'refererInfo': { + 'referer': 'http://test-zeta-ssp.net:63342/zeta-ssp/ssp/_dev/examples/page_banner.html', + 'reachedTop': true, + 'isAmp': false, + 'numIframes': 0, + 'stack': [ + 'http://test-zeta-ssp.net:63342/zeta-ssp/ssp/_dev/examples/page_banner.html' + ], + 'canonicalUrl': null + }, + 'start': 1638441234550 + } + ], + 'noBids': [ + { + 'bidder': 'appnexus', + 'params': { + 'placementId': 13232385 + }, + 'mediaTypes': { + 'banner': { + 'sizes': [ + [ + 300, + 250 + ], + [ + 300, + 600 + ] + ] + } + }, + 'adUnitCode': '/19968336/header-bid-tag-0', + 'transactionId': '6b29369c-0c2e-414e-be1f-5867aec18d83', + 'sizes': [ + [ + 300, + 250 + ], + [ + 300, + 600 + ] + ], + 'bidId': '41badc0e164c758', + 'bidderRequestId': '32b97f0a935422', + 'auctionId': '75e394d9-ccce-4978-9238-91e6a1ac88a1', + 'src': 'client', + 'bidRequestsCount': 1, + 'bidderRequestsCount': 1, + 'bidderWinsCount': 0 + } + ], + 'bidsReceived': [ + { + 'bidderCode': 'zeta_global_ssp', + 'width': 480, + 'height': 320, + 'statusMessage': 'Bid available', + 'adId': '5759bb3ef7be1e8', + 'requestId': '206be9a13236af', + 'mediaType': 'banner', + 'source': 'client', + 'cpm': 2.258302852806723, + 'currency': 'USD', + 'ad': 'test_ad', + 'ttl': 200, + 'creativeId': '456456456', + 'netRevenue': true, + 'meta': { + 'advertiserDomains': [ + 'viaplay.fi' + ] + }, + 'originalCpm': 2.258302852806723, + 'originalCurrency': 'USD', + 'auctionId': '75e394d9-ccce-4978-9238-91e6a1ac88a1', + 'responseTimestamp': 1638441234670, + 'requestTimestamp': 1638441234547, + 'bidder': 'zeta_global_ssp', + 'adUnitCode': '/19968336/header-bid-tag-0', + 'timeToRespond': 123, + 'pbLg': '2.00', + 'pbMg': '2.20', + 'pbHg': '2.25', + 'pbAg': '2.25', + 'pbDg': '2.25', + 'pbCg': '', + 'size': '480x320', + 'adserverTargeting': { + 'hb_bidder': 'zeta_global_ssp', + 'hb_adid': '5759bb3ef7be1e8', + 'hb_pb': '2.20', + 'hb_size': '480x320', + 'hb_source': 'client', + 'hb_format': 'banner', + 'hb_adomain': 'viaplay.fi' + } + } + ], + 'winningBids': [], + 'timeout': 400 + }, + AD_RENDER_SUCCEEDED: { + 'doc': { + 'location': { + 'href': 'http://test-zeta-ssp.net:63342/zeta-ssp/ssp/_dev/examples/page_banner.html', + 'protocol': 'http:', + 'host': 'localhost:63342', + 'hostname': 'localhost', + 'port': '63342', + 'pathname': '/zeta-ssp/ssp/_dev/examples/page_banner.html', + 'hash': '', + 'origin': 'http://test-zeta-ssp.net:63342', + 'ancestorOrigins': { + '0': 'http://test-zeta-ssp.net:63342' + } + } + }, + 'bid': { + 'bidderCode': 'zeta_global_ssp', + 'width': 480, + 'height': 320, + 'statusMessage': 'Bid available', + 'adId': '5759bb3ef7be1e8', + 'requestId': '206be9a13236af', + 'mediaType': 'banner', + 'source': 'client', + 'cpm': 2.258302852806723, + 'currency': 'USD', + 'ad': 'test_ad', + 'ttl': 200, + 'creativeId': '456456456', + 'netRevenue': true, + 'meta': { + 'advertiserDomains': [ + 'viaplay.fi' + ] + }, + 'originalCpm': 2.258302852806723, + 'originalCurrency': 'USD', + 'auctionId': '75e394d9-ccce-4978-9238-91e6a1ac88a1', + 'responseTimestamp': 1638441234670, + 'requestTimestamp': 1638441234547, + 'bidder': 'zeta_global_ssp', + 'adUnitCode': '/19968336/header-bid-tag-0', + 'timeToRespond': 123, + 'pbLg': '2.00', + 'pbMg': '2.20', + 'pbHg': '2.25', + 'pbAg': '2.25', + 'pbDg': '2.25', + 'pbCg': '', + 'size': '480x320', + 'adserverTargeting': { + 'hb_bidder': 'zeta_global_ssp', + 'hb_adid': '5759bb3ef7be1e8', + 'hb_pb': '2.20', + 'hb_size': '480x320', + 'hb_source': 'client', + 'hb_format': 'banner', + 'hb_adomain': 'viaplay.fi' + }, + 'status': 'rendered', + 'params': [ + { + 'sid': 111, + 'tags': { + 'shortname': 'prebid_analytics_event_test_shortname', + 'position': 'test_position' + } + } + ] + }, + 'adId': '5759bb3ef7be1e8' + } +} + +describe('Zeta Global SSP Analytics Adapter', function() { + let sandbox; + let xhr; + let requests; + + beforeEach(function() { + sandbox = sinon.sandbox.create(); + requests = []; + xhr = sandbox.useFakeXMLHttpRequest(); + xhr.onCreate = request => requests.push(request); + sandbox.stub(events, 'getEvents').returns([]); + }); + + afterEach(function () { + sandbox.restore(); + config.resetConfig(); + }); + + it('should require publisherId', function () { + sandbox.stub(utils, 'logError'); + zetaAnalyticsAdapter.enableAnalytics({ + options: {} + }); + expect(utils.logError.called).to.equal(true); + }); + + describe('handle events', function() { + beforeEach(function() { + zetaAnalyticsAdapter.enableAnalytics({ + options: { + sid: 111 + } + }); + }); + + afterEach(function () { + zetaAnalyticsAdapter.disableAnalytics(); + }); + + it('events are sent', function() { + this.timeout(5000); + events.emit(CONSTANTS.EVENTS.AUCTION_INIT, MOCK.STUB); + events.emit(CONSTANTS.EVENTS.AUCTION_END, MOCK.AUCTION_END); + events.emit(CONSTANTS.EVENTS.BID_ADJUSTMENT, MOCK.STUB); + events.emit(CONSTANTS.EVENTS.BID_TIMEOUT, MOCK.STUB); + events.emit(CONSTANTS.EVENTS.BID_REQUESTED, MOCK.STUB); + events.emit(CONSTANTS.EVENTS.BID_RESPONSE, MOCK.STUB); + events.emit(CONSTANTS.EVENTS.NO_BID, MOCK.STUB); + events.emit(CONSTANTS.EVENTS.BID_WON, MOCK.STUB); + events.emit(CONSTANTS.EVENTS.BIDDER_DONE, MOCK.STUB); + events.emit(CONSTANTS.EVENTS.BIDDER_ERROR, MOCK.STUB); + events.emit(CONSTANTS.EVENTS.SET_TARGETING, MOCK.STUB); + events.emit(CONSTANTS.EVENTS.BEFORE_REQUEST_BIDS, MOCK.STUB); + events.emit(CONSTANTS.EVENTS.BEFORE_BIDDER_HTTP, MOCK.STUB); + events.emit(CONSTANTS.EVENTS.REQUEST_BIDS, MOCK.STUB); + events.emit(CONSTANTS.EVENTS.ADD_AD_UNITS, MOCK.STUB); + events.emit(CONSTANTS.EVENTS.AD_RENDER_FAILED, MOCK.STUB); + events.emit(CONSTANTS.EVENTS.AD_RENDER_SUCCEEDED, MOCK.AD_RENDER_SUCCEEDED); + events.emit(CONSTANTS.EVENTS.TCF2_ENFORCEMENT, MOCK.STUB); + events.emit(CONSTANTS.EVENTS.AUCTION_DEBUG, MOCK.STUB); + events.emit(CONSTANTS.EVENTS.BID_VIEWABLE, MOCK.STUB); + events.emit(CONSTANTS.EVENTS.STALE_RENDER, MOCK.STUB); + + expect(requests.length).to.equal(2); + expect(JSON.parse(requests[0].requestBody)).to.deep.equal(MOCK.AUCTION_END); + expect(JSON.parse(requests[1].requestBody)).to.deep.equal(MOCK.AD_RENDER_SUCCEEDED); + }); + }); +}); diff --git a/test/spec/modules/zeta_global_sspBidAdapter_spec.js b/test/spec/modules/zeta_global_sspBidAdapter_spec.js index dcb0183fb4c..7d4a115958e 100644 --- a/test/spec/modules/zeta_global_sspBidAdapter_spec.js +++ b/test/spec/modules/zeta_global_sspBidAdapter_spec.js @@ -1,4 +1,5 @@ import {spec} from '../../../modules/zeta_global_sspBidAdapter.js' +import {BANNER, VIDEO} from '../../../src/mediaTypes'; describe('Zeta Ssp Bid Adapter', function () { const eids = [ @@ -143,7 +144,22 @@ describe('Zeta Ssp Bid Adapter', function () { 'https://example2.com' ], h: 150, - w: 200 + w: 200, + ext: { + bidtype: 'video' + } + }, + { + id: 'auctionId3', + impid: 'impId3', + price: 0.2, + adm: '', + crid: 'creativeId3', + adomain: [ + 'https://example3.com' + ], + h: 400, + w: 300 } ] } @@ -159,6 +175,8 @@ describe('Zeta Ssp Bid Adapter', function () { const receivedBid1 = response.body.seatbid[0].bid[0]; expect(bid1).to.not.be.empty; expect(bid1.ad).to.equal(receivedBid1.adm); + expect(bid1.vastXml).to.be.undefined; + expect(bid1.mediaType).to.equal(BANNER); expect(bid1.cpm).to.equal(receivedBid1.price); expect(bid1.height).to.equal(receivedBid1.h); expect(bid1.width).to.equal(receivedBid1.w); @@ -169,11 +187,25 @@ describe('Zeta Ssp Bid Adapter', function () { const receivedBid2 = response.body.seatbid[0].bid[1]; expect(bid2).to.not.be.empty; expect(bid2.ad).to.equal(receivedBid2.adm); + expect(bid2.vastXml).to.equal(receivedBid2.adm); + expect(bid2.mediaType).to.equal(VIDEO); expect(bid2.cpm).to.equal(receivedBid2.price); expect(bid2.height).to.equal(receivedBid2.h); expect(bid2.width).to.equal(receivedBid2.w); expect(bid2.requestId).to.equal(receivedBid2.impid); expect(bid2.meta.advertiserDomains).to.equal(receivedBid2.adomain); + + const bid3 = bidResponse[2]; + const receivedBid3 = response.body.seatbid[0].bid[2]; + expect(bid3).to.not.be.empty; + expect(bid3.ad).to.equal(receivedBid3.adm); + expect(bid3.vastXml).to.equal(receivedBid3.adm); + expect(bid3.mediaType).to.equal(VIDEO); + expect(bid3.cpm).to.equal(receivedBid3.price); + expect(bid3.height).to.equal(receivedBid3.h); + expect(bid3.width).to.equal(receivedBid3.w); + expect(bid3.requestId).to.equal(receivedBid3.impid); + expect(bid3.meta.advertiserDomains).to.equal(receivedBid3.adomain); }); it('Different cases for user syncs', function () { diff --git a/test/spec/unit/core/adapterManager_spec.js b/test/spec/unit/core/adapterManager_spec.js index f33c9139e5f..f414febcebe 100644 --- a/test/spec/unit/core/adapterManager_spec.js +++ b/test/spec/unit/core/adapterManager_spec.js @@ -162,7 +162,7 @@ describe('adapterManager tests', function () { 'bidderCode': 'appnexus', 'auctionId': '1863e370099523', 'bidderRequestId': '2946b569352ef2', - 'tid': '34566b569352ef2', + 'uniquePbsTid': '34566b569352ef2', 'bids': [ { 'bidder': 'appnexus', @@ -436,6 +436,38 @@ describe('adapterManager tests', function () { }); }); // end onBidViewable + describe('onBidderError', function () { + const bidder = 'appnexus'; + const appnexusSpec = { onBidderError: sinon.stub() }; + const appnexusAdapter = { + bidder, + getSpec: function() { return appnexusSpec; }, + } + before(function () { + config.setConfig({s2sConfig: { enabled: false }}); + }); + + beforeEach(function () { + adapterManager.bidderRegistry[bidder] = appnexusAdapter; + }); + + afterEach(function () { + delete adapterManager.bidderRegistry[bidder]; + }); + + it('should call spec\'s onBidderError callback when callBidderError is called', function () { + const bidRequests = getBidRequests(); + const bidderRequest = find(bidRequests, bidRequest => bidRequest.bidderCode === bidder); + const xhrErrorMock = { + status: 500, + statusText: 'Internal Server Error' + }; + adapterManager.callBidderError(bidder, xhrErrorMock, bidderRequest); + sinon.assert.calledOnce(appnexusSpec.onBidderError); + sinon.assert.calledWithExactly(appnexusSpec.onBidderError, { error: xhrErrorMock, bidderRequest }); + }); + }); // end onBidderError + describe('S2S tests', function () { beforeEach(function () { config.setConfig({s2sConfig: CONFIG}); @@ -447,7 +479,7 @@ describe('adapterManager tests', function () { 'bidderCode': 'appnexus', 'auctionId': '1863e370099523', 'bidderRequestId': '2946b569352ef2', - 'tid': '34566b569352ef2', + 'uniquePbsTid': '34566b569352ef2', 'timeout': 1000, 'src': 's2s', 'adUnitsS2SCopy': [ @@ -676,7 +708,7 @@ describe('adapterManager tests', function () { 'bidderCode': 'appnexus', 'auctionId': '1863e370099523', 'bidderRequestId': '2946b569352ef2', - 'tid': '34566b569352ef2', + 'uniquePbsTid': '34566b569352ef2', 'timeout': 1000, 'src': 's2s', 'adUnitsS2SCopy': [ @@ -812,7 +844,7 @@ describe('adapterManager tests', function () { 'bidderCode': 'pubmatic', 'auctionId': '1863e370099523', 'bidderRequestId': '2946b569352ef2', - 'tid': '2342342342lfi23', + 'uniquePbsTid': '2342342342lfi23', 'timeout': 1000, 'src': 's2s', 'adUnitsS2SCopy': [ @@ -1009,6 +1041,21 @@ describe('adapterManager tests', function () { sinon.assert.calledTwice(prebidServerAdapterMock.callBids); }); + it('should have one tid for ALL s2s bidRequests', function () { + let adUnits = utils.deepClone(getAdUnits()).map(adUnit => { + adUnit.bids = adUnit.bids.filter(bid => includes(['appnexus', 'pubmatic'], bid.bidder)); + return adUnit; + }) + let bidRequests = adapterManager.makeBidRequests(adUnits, 1111, 2222, 1000); + adapterManager.callBids(adUnits, bidRequests, () => {}, () => {}); + sinon.assert.calledTwice(prebidServerAdapterMock.callBids); + const firstBid = prebidServerAdapterMock.callBids.firstCall.args[0]; + const secondBid = prebidServerAdapterMock.callBids.secondCall.args[0]; + + // TIDS should be the same + expect(firstBid.tid).to.equal(secondBid.tid); + }); + it('should fire for simultaneous s2s and client requests', function () { adapterManager.bidderRegistry['adequant'] = adequantAdapterMock; let adUnits = utils.deepClone(getAdUnits()).map(adUnit => { @@ -1665,14 +1712,17 @@ describe('adapterManager tests', function () { }); describe('sizeMapping', function () { + let sandbox; beforeEach(function () { + sandbox = sinon.sandbox.create(); allS2SBidders.length = 0; clientTestAdapters.length = 0; - sinon.stub(window, 'matchMedia').callsFake(() => ({matches: true})); + // always have matchMedia return true for us + sandbox.stub(utils.getWindowTop(), 'matchMedia').callsFake(() => ({matches: true})); }); afterEach(function () { - matchMedia.restore(); + sandbox.restore(); config.resetConfig(); setSizeConfig([]); }); diff --git a/test/spec/unit/core/bidderFactory_spec.js b/test/spec/unit/core/bidderFactory_spec.js index 48c17a90398..4dc79deaf85 100644 --- a/test/spec/unit/core/bidderFactory_spec.js +++ b/test/spec/unit/core/bidderFactory_spec.js @@ -552,17 +552,27 @@ describe('bidders created by newBidder', function () { describe('when the ajax call fails', function () { let ajaxStub; + let callBidderErrorStub; + let eventEmitterStub; + let xhrErrorMock = { + status: 500, + statusText: 'Internal Server Error' + }; beforeEach(function () { ajaxStub = sinon.stub(ajax, 'ajax').callsFake(function(url, callbacks) { - callbacks.error('ajax call failed.'); + callbacks.error('ajax call failed.', xhrErrorMock); }); + callBidderErrorStub = sinon.stub(adapterManager, 'callBidderError'); + eventEmitterStub = sinon.stub(events, 'emit'); addBidResponseStub.reset(); doneStub.reset(); }); afterEach(function () { ajaxStub.restore(); + callBidderErrorStub.restore(); + eventEmitterStub.restore(); }); it('should not spec.interpretResponse()', function () { @@ -580,6 +590,14 @@ describe('bidders created by newBidder', function () { expect(spec.interpretResponse.called).to.equal(false); expect(doneStub.calledOnce).to.equal(true); + expect(callBidderErrorStub.calledOnce).to.equal(true); + expect(callBidderErrorStub.firstCall.args[0]).to.equal(CODE); + expect(callBidderErrorStub.firstCall.args[1]).to.equal(xhrErrorMock); + expect(callBidderErrorStub.firstCall.args[2]).to.equal(MOCK_BIDS_REQUEST); + sinon.assert.calledWith(eventEmitterStub, CONSTANTS.EVENTS.BIDDER_ERROR, { + error: xhrErrorMock, + bidderRequest: MOCK_BIDS_REQUEST + }); }); it('should not add bids for each adunit code into the auction', function () { @@ -598,6 +616,14 @@ describe('bidders created by newBidder', function () { expect(addBidResponseStub.callCount).to.equal(0); expect(doneStub.calledOnce).to.equal(true); + expect(callBidderErrorStub.calledOnce).to.equal(true); + expect(callBidderErrorStub.firstCall.args[0]).to.equal(CODE); + expect(callBidderErrorStub.firstCall.args[1]).to.equal(xhrErrorMock); + expect(callBidderErrorStub.firstCall.args[2]).to.equal(MOCK_BIDS_REQUEST); + sinon.assert.calledWith(eventEmitterStub, CONSTANTS.EVENTS.BIDDER_ERROR, { + error: xhrErrorMock, + bidderRequest: MOCK_BIDS_REQUEST + }); }); it('should call spec.getUserSyncs() with no responses', function () { @@ -616,6 +642,40 @@ describe('bidders created by newBidder', function () { expect(spec.getUserSyncs.calledOnce).to.equal(true); expect(spec.getUserSyncs.firstCall.args[1]).to.deep.equal([]); expect(doneStub.calledOnce).to.equal(true); + expect(callBidderErrorStub.calledOnce).to.equal(true); + expect(callBidderErrorStub.firstCall.args[0]).to.equal(CODE); + expect(callBidderErrorStub.firstCall.args[1]).to.equal(xhrErrorMock); + expect(callBidderErrorStub.firstCall.args[2]).to.equal(MOCK_BIDS_REQUEST); + sinon.assert.calledWith(eventEmitterStub, CONSTANTS.EVENTS.BIDDER_ERROR, { + error: xhrErrorMock, + bidderRequest: MOCK_BIDS_REQUEST + }); + }); + + it('should call spec.getUserSyncs() with no responses', function () { + const bidder = newBidder(spec); + + spec.isBidRequestValid.returns(true); + spec.buildRequests.returns({ + method: 'POST', + url: 'test.url.com', + data: {} + }); + spec.getUserSyncs.returns([]); + + bidder.callBids(MOCK_BIDS_REQUEST, addBidResponseStub, doneStub, ajaxStub, onTimelyResponseStub, wrappedCallback); + + expect(spec.getUserSyncs.calledOnce).to.equal(true); + expect(spec.getUserSyncs.firstCall.args[1]).to.deep.equal([]); + expect(doneStub.calledOnce).to.equal(true); + expect(callBidderErrorStub.calledOnce).to.equal(true); + expect(callBidderErrorStub.firstCall.args[0]).to.equal(CODE); + expect(callBidderErrorStub.firstCall.args[1]).to.equal(xhrErrorMock); + expect(callBidderErrorStub.firstCall.args[2]).to.equal(MOCK_BIDS_REQUEST); + sinon.assert.calledWith(eventEmitterStub, CONSTANTS.EVENTS.BIDDER_ERROR, { + error: xhrErrorMock, + bidderRequest: MOCK_BIDS_REQUEST + }); }); }); }); diff --git a/test/spec/unit/core/targeting_spec.js b/test/spec/unit/core/targeting_spec.js index 1064d7c0f7d..4eaf414bf85 100644 --- a/test/spec/unit/core/targeting_spec.js +++ b/test/spec/unit/core/targeting_spec.js @@ -5,6 +5,7 @@ import { createBidReceived } from 'test/fixtures/fixtures.js'; import CONSTANTS from 'src/constants.json'; import { auctionManager } from 'src/auctionManager.js'; import * as utils from 'src/utils.js'; +import {deepClone} from 'src/utils.js'; const bid1 = { 'bidderCode': 'rubicon', @@ -461,6 +462,50 @@ describe('targeting tests', function () { }); }); + describe('targetingControls.allowZeroCpmBids', function () { + let bid4; + let bidderSettingsStorage; + + before(function() { + bidderSettingsStorage = $$PREBID_GLOBAL$$.bidderSettings; + }); + + beforeEach(function () { + bid4 = utils.deepClone(bid1); + bid4.adserverTargeting = { + hb_pb: '0.0', + hb_adid: '567891011', + hb_bidder: 'appnexus', + }; + bid4.bidder = bid4.bidderCode = 'appnexus'; + bid4.cpm = 0; + bidsReceived = [bid4]; + }); + + after(function() { + bidsReceived = [bid1, bid2, bid3]; + $$PREBID_GLOBAL$$.bidderSettings = bidderSettingsStorage; + }) + + it('targeting should not include a 0 cpm by default', function() { + bid4.adserverTargeting = {}; + const targeting = targetingInstance.getAllTargeting(['/123456/header-bid-tag-0']); + expect(targeting['/123456/header-bid-tag-0']).to.deep.equal({}); + }); + + it('targeting should allow a 0 cpm with targetingControls.allowZeroCpmBids set to true', function () { + $$PREBID_GLOBAL$$.bidderSettings = { + standard: { + allowZeroCpmBids: true + } + }; + + const targeting = targetingInstance.getAllTargeting(['/123456/header-bid-tag-0']); + expect(targeting['/123456/header-bid-tag-0']).to.include.all.keys('hb_pb', 'hb_bidder', 'hb_adid', 'hb_bidder_appnexus', 'hb_adid_appnexus', 'hb_pb_appnexus'); + expect(targeting['/123456/header-bid-tag-0']['hb_pb']).to.equal('0.0') + }); + }); + describe('targetingControls.allowTargetingKeys', function () { let bid4; @@ -501,6 +546,77 @@ describe('targeting tests', function () { }); }); + describe('targetingControls.addTargetingKeys', function () { + let winningBid = null; + + beforeEach(function () { + bidsReceived = [bid1, bid2, nativeBid1, nativeBid2].map(deepClone); + bidsReceived.forEach((bid) => { + bid.adserverTargeting[CONSTANTS.TARGETING_KEYS.SOURCE] = 'test-source'; + bid.adUnitCode = 'adunit'; + if (winningBid == null || bid.cpm > winningBid.cpm) { + winningBid = bid; + } + }); + enableSendAllBids = true; + }); + + const expandKey = function (key) { + const keys = new Set(); + if (winningBid.adserverTargeting[key] != null) { + keys.add(key); + } + bidsReceived + .filter((bid) => bid.adserverTargeting[key] != null) + .map((bid) => bid.bidderCode) + .forEach((code) => keys.add(`${key}_${code}`.substr(0, 20))); + return new Array(...keys); + } + + const targetingResult = function () { + return targetingInstance.getAllTargeting(['adunit'])['adunit']; + } + + it('should include added keys', function () { + config.setConfig({ + targetingControls: { + addTargetingKeys: ['SOURCE'] + } + }); + expect(targetingResult()).to.include.all.keys(...expandKey(CONSTANTS.TARGETING_KEYS.SOURCE)); + }); + + it('should keep default and native keys', function() { + config.setConfig({ + targetingControls: { + addTargetingKeys: ['SOURCE'] + } + }); + const defaultKeys = new Set(Object.values(CONSTANTS.DEFAULT_TARGETING_KEYS)); + Object.values(CONSTANTS.NATIVE_KEYS).forEach((k) => defaultKeys.add(k)); + + const expectedKeys = new Set(); + bidsReceived + .map((bid) => Object.keys(bid.adserverTargeting)) + .reduce((left, right) => left.concat(right), []) + .filter((key) => defaultKeys.has(key)) + .map(expandKey) + .reduce((left, right) => left.concat(right), []) + .forEach((k) => expectedKeys.add(k)); + expect(targetingResult()).to.include.all.keys(...expectedKeys); + }); + + it('should not be allowed together with allowTargetingKeys', function () { + config.setConfig({ + targetingControls: { + allowTargetingKeys: [CONSTANTS.TARGETING_KEYS.BIDDER], + addTargetingKeys: [CONSTANTS.TARGETING_KEYS.SOURCE] + } + }); + expect(targetingResult).to.throw(); + }); + }); + describe('targetingControls.allowSendAllBidsTargetingKeys', function () { let bid4; diff --git a/test/spec/unit/pbjs_api_spec.js b/test/spec/unit/pbjs_api_spec.js index cc75b95a7ac..09aacdebd0c 100644 --- a/test/spec/unit/pbjs_api_spec.js +++ b/test/spec/unit/pbjs_api_spec.js @@ -16,6 +16,7 @@ import * as auctionModule from 'src/auction.js'; import { registerBidder } from 'src/adapters/bidderFactory.js'; import { _sendAdToCreative } from 'src/secureCreatives.js'; import find from 'core-js-pure/features/array/find.js'; +import {synchronizePromise} from '../../helpers/syncPromise.js'; var assert = require('chai').assert; var expect = require('chai').expect; @@ -98,7 +99,6 @@ var createSlotArrayScenario2 = function createSlotArrayScenario2() { var slot1 = new Slot(config.adUnitElementIDs[0], config.adUnitCodes[0]); slot1.setTargeting('pos1', '750x350'); var slot2 = new Slot(config.adUnitElementIDs[1], config.adUnitCodes[0]); - slot2.setTargeting('gender', ['male', 'female']); return [ slot1, slot2 @@ -191,13 +191,16 @@ window.apntag = { } describe('Unit: Prebid Module', function () { - let bidExpiryStub; + let bidExpiryStub, promiseSandbox; beforeEach(function () { + promiseSandbox = sinon.createSandbox(); + synchronizePromise(promiseSandbox); bidExpiryStub = sinon.stub(filters, 'isBidNotExpired').callsFake(() => true); configObj.setConfig({ useBidCache: true }); }); afterEach(function() { + promiseSandbox.restore(); $$PREBID_GLOBAL$$.adUnits = []; bidExpiryStub.restore(); configObj.setConfig({ useBidCache: false }); @@ -862,6 +865,9 @@ describe('Unit: Prebid Module', function () { it('should set googletag targeting keys after calling setTargetingForGPTAsync function', function () { var slots = createSlotArrayScenario2(); + + // explicitly setting some PBJS key value pairs to verify whether these are removed befor new keys are set + window.googletag.pubads().setSlots(slots); $$PREBID_GLOBAL$$.setTargetingForGPTAsync([config.adUnitCodes[0]]); @@ -869,7 +875,7 @@ describe('Unit: Prebid Module', function () { // googletag's targeting structure // googletag setTargeting will override old value if invoked with same key - const targeting = []; + let targeting = []; slots[1].getTargetingKeys().map(function (key) { const value = slots[1].getTargeting(key); targeting.push([key, value]); @@ -887,6 +893,39 @@ describe('Unit: Prebid Module', function () { invokedTargeting.push([key, value]); }); assert.deepEqual(targeting, invokedTargeting, 'google tag targeting options not matching'); + + // resetPresetTargeting: initiate a new auction with no winning bids, now old targeting should be removed + + resetAuction(); + auction.getBidsReceived = function() { return [] }; + + var slots = createSlotArrayScenario2(); + window.googletag.pubads().setSlots(slots); + + $$PREBID_GLOBAL$$.setTargetingForGPTAsync([config.adUnitCodes[0]]); + + targeting = []; + slots[1].getTargetingKeys().map(function (key) { + const value = slots[1].getTargeting(key); + targeting.push([key, value]); + }); + + invokedTargetingMap = {}; + slots[1].spySetTargeting.args.map(function (entry) { + invokedTargetingMap[entry[0]] = entry[1]; + }); + + var invokedTargeting = []; + + Object.getOwnPropertyNames(invokedTargetingMap).map(function (key) { + const value = Array.isArray(invokedTargetingMap[key]) ? invokedTargetingMap[key] : [invokedTargetingMap[key]]; // values are always returned as array in googletag + invokedTargeting.push([key, value]); + }); + assert.deepEqual(targeting, invokedTargeting, 'google tag targeting options not matching'); + targeting.forEach(function(e) { + // here e[0] is key and e[1] is value in array that should be [null] as we are un-setting prebid keys in resetPresetTargeting + assert.deepEqual(e[1], [null], 'resetPresetTargeting: the value of the key ' + e[0] + ' should be [null]'); + }); }); it('should set googletag targeting keys to specific slot with customSlotMatching', function () { @@ -1100,13 +1139,15 @@ describe('Unit: Prebid Module', function () { height: 0 } }, - getElementsByTagName: sinon.stub() + getElementsByTagName: sinon.stub(), + querySelector: sinon.stub() }; elStub = { insertBefore: sinon.stub() }; doc.getElementsByTagName.returns([elStub]); + doc.querySelector.returns(elStub); spyLogError = sinon.spy(utils, 'logError'); spyLogMessage = sinon.spy(utils, 'logMessage'); @@ -1817,11 +1858,23 @@ describe('Unit: Prebid Module', function () { pos: 2 } } + }, + { + code: 'test6', + bids: [], + sizes: [300, 250], + mediaTypes: { + banner: { + sizes: [300, 250], + pos: 0 + } + } }]; $$PREBID_GLOBAL$$.requestBids({ adUnits: adUnit }); expect(auctionArgs.adUnits[0].mediaTypes.banner.pos).to.equal(2); + expect(auctionArgs.adUnits[1].mediaTypes.banner.pos).to.equal(0); }); }); diff --git a/test/spec/unit/secureCreatives_spec.js b/test/spec/unit/secureCreatives_spec.js index eca00e8c8fa..a6c7d48b6e1 100644 --- a/test/spec/unit/secureCreatives_spec.js +++ b/test/spec/unit/secureCreatives_spec.js @@ -1,6 +1,7 @@ import { _sendAdToCreative, receiveMessage } from 'src/secureCreatives.js'; +import * as secureCreatives from 'src/secureCreatives.js'; import * as utils from 'src/utils.js'; import {getAdUnits, getBidRequests, getBidResponses} from 'test/fixtures/fixtures.js'; import {auctionManager} from 'src/auctionManager.js'; @@ -9,6 +10,7 @@ import * as native from 'src/native.js'; import {fireNativeTrackers, getAllAssetsMessage} from 'src/native.js'; import events from 'src/events.js'; import { config as configObj } from 'src/config.js'; +import 'src/prebid.js'; import { expect } from 'chai'; @@ -116,7 +118,7 @@ describe('secureCreatives', () => { beforeEach(function() { spyAddWinningBid = sinon.spy(auctionManager, 'addWinningBid'); spyLogWarn = sinon.spy(utils, 'logWarn'); - stubFireNativeTrackers = sinon.stub(native, 'fireNativeTrackers'); + stubFireNativeTrackers = sinon.stub(native, 'fireNativeTrackers').callsFake(message => { return message.action; }); stubGetAllAssetsMessage = sinon.stub(native, 'getAllAssetsMessage'); stubEmit = sinon.stub(events, 'emit'); }); @@ -237,6 +239,38 @@ describe('secureCreatives', () => { configObj.setConfig({'auctionOptions': {}}); }); + + it('should emit AD_RENDER_FAILED if requested missing adId', () => { + const ev = { + data: JSON.stringify({ + message: 'Prebid Request', + adId: 'missing' + }) + }; + receiveMessage(ev); + sinon.assert.calledWith(stubEmit, CONSTANTS.EVENTS.AD_RENDER_FAILED, sinon.match({ + reason: CONSTANTS.AD_RENDER_FAILED_REASON.CANNOT_FIND_AD, + adId: 'missing' + })); + }); + + it('should emit AD_RENDER_FAILED if creative can\'t be sent to rendering frame', () => { + pushBidResponseToAuction({}); + const ev = { + source: { + postMessage: sinon.stub().callsFake(() => { throw new Error(); }) + }, + data: JSON.stringify({ + message: 'Prebid Request', + adId: bidId + }) + } + receiveMessage(ev) + sinon.assert.calledWith(stubEmit, CONSTANTS.EVENTS.AD_RENDER_FAILED, sinon.match({ + reason: CONSTANTS.AD_RENDER_FAILED_REASON.EXCEPTION, + adId: bidId + })); + }); }); describe('Prebid Native', function() { @@ -263,10 +297,9 @@ describe('secureCreatives', () => { sinon.assert.calledOnce(stubGetAllAssetsMessage); sinon.assert.calledWith(stubGetAllAssetsMessage, data, adResponse); sinon.assert.calledOnce(ev.source.postMessage); - sinon.assert.calledOnce(stubFireNativeTrackers); - sinon.assert.calledWith(stubFireNativeTrackers, data, adResponse); - sinon.assert.calledOnce(spyAddWinningBid); - sinon.assert.calledWith(stubEmit, CONSTANTS.EVENTS.BID_WON, adResponse); + sinon.assert.notCalled(stubFireNativeTrackers); + sinon.assert.neverCalledWith(stubEmit, CONSTANTS.EVENTS.BID_WON); + sinon.assert.notCalled(spyAddWinningBid); sinon.assert.neverCalledWith(stubEmit, CONSTANTS.EVENTS.STALE_RENDER); }); @@ -293,14 +326,11 @@ describe('secureCreatives', () => { sinon.assert.calledOnce(stubGetAllAssetsMessage); sinon.assert.calledWith(stubGetAllAssetsMessage, data, adResponse); sinon.assert.calledOnce(ev.source.postMessage); - sinon.assert.calledOnce(stubFireNativeTrackers); - sinon.assert.calledWith(stubFireNativeTrackers, data, adResponse); - sinon.assert.calledOnce(spyAddWinningBid); - sinon.assert.calledWith(stubEmit, CONSTANTS.EVENTS.BID_WON, adResponse); + sinon.assert.notCalled(stubFireNativeTrackers); + sinon.assert.neverCalledWith(stubEmit, CONSTANTS.EVENTS.BID_WON); + sinon.assert.notCalled(spyAddWinningBid); sinon.assert.neverCalledWith(stubEmit, CONSTANTS.EVENTS.STALE_RENDER); - expect(adResponse).to.have.property('status', CONSTANTS.BID_STATUS.RENDERED); - resetHistories(ev.source.postMessage); receiveMessage(ev); @@ -309,10 +339,9 @@ describe('secureCreatives', () => { sinon.assert.calledOnce(stubGetAllAssetsMessage); sinon.assert.calledWith(stubGetAllAssetsMessage, data, adResponse); sinon.assert.calledOnce(ev.source.postMessage); - sinon.assert.calledOnce(stubFireNativeTrackers); - sinon.assert.calledWith(stubFireNativeTrackers, data, adResponse); - sinon.assert.calledOnce(spyAddWinningBid); - sinon.assert.calledWith(stubEmit, CONSTANTS.EVENTS.BID_WON, adResponse); + sinon.assert.notCalled(stubFireNativeTrackers); + sinon.assert.neverCalledWith(stubEmit, CONSTANTS.EVENTS.BID_WON); + sinon.assert.notCalled(spyAddWinningBid); sinon.assert.neverCalledWith(stubEmit, CONSTANTS.EVENTS.STALE_RENDER); }); @@ -341,14 +370,11 @@ describe('secureCreatives', () => { sinon.assert.calledOnce(stubGetAllAssetsMessage); sinon.assert.calledWith(stubGetAllAssetsMessage, data, adResponse); sinon.assert.calledOnce(ev.source.postMessage); - sinon.assert.calledOnce(stubFireNativeTrackers); - sinon.assert.calledWith(stubFireNativeTrackers, data, adResponse); - sinon.assert.calledOnce(spyAddWinningBid); - sinon.assert.calledWith(stubEmit, CONSTANTS.EVENTS.BID_WON, adResponse); + sinon.assert.notCalled(stubFireNativeTrackers); + sinon.assert.neverCalledWith(stubEmit, CONSTANTS.EVENTS.BID_WON); + sinon.assert.notCalled(spyAddWinningBid); sinon.assert.neverCalledWith(stubEmit, CONSTANTS.EVENTS.STALE_RENDER); - expect(adResponse).to.have.property('status', CONSTANTS.BID_STATUS.RENDERED); - resetHistories(ev.source.postMessage); receiveMessage(ev); @@ -357,14 +383,102 @@ describe('secureCreatives', () => { sinon.assert.calledOnce(stubGetAllAssetsMessage); sinon.assert.calledWith(stubGetAllAssetsMessage, data, adResponse); sinon.assert.calledOnce(ev.source.postMessage); - sinon.assert.calledOnce(stubFireNativeTrackers); - sinon.assert.calledWith(stubFireNativeTrackers, data, adResponse); - sinon.assert.calledOnce(spyAddWinningBid); - sinon.assert.calledWith(stubEmit, CONSTANTS.EVENTS.BID_WON, adResponse); + sinon.assert.notCalled(stubFireNativeTrackers); + sinon.assert.neverCalledWith(stubEmit, CONSTANTS.EVENTS.BID_WON); + sinon.assert.notCalled(spyAddWinningBid); sinon.assert.neverCalledWith(stubEmit, CONSTANTS.EVENTS.STALE_RENDER); configObj.setConfig({'auctionOptions': {}}); }); + + it('Prebid native should fire trackers', function () { + pushBidResponseToAuction({}); + + const data = { + adId: bidId, + message: 'Prebid Native', + action: 'click', + }; + + const ev = { + data: JSON.stringify(data), + source: { + postMessage: sinon.stub() + }, + origin: 'any origin' + }; + + receiveMessage(ev); + + sinon.assert.neverCalledWith(spyLogWarn, warning); + sinon.assert.calledOnce(stubFireNativeTrackers); + sinon.assert.neverCalledWith(stubEmit, CONSTANTS.EVENTS.BID_WON); + sinon.assert.notCalled(spyAddWinningBid); + + resetHistories(ev.source.postMessage); + + delete data.action; + ev.data = JSON.stringify(data); + receiveMessage(ev); + + sinon.assert.neverCalledWith(spyLogWarn, warning); + sinon.assert.calledOnce(stubFireNativeTrackers); + sinon.assert.calledWith(stubEmit, CONSTANTS.EVENTS.BID_WON, adResponse); + sinon.assert.calledOnce(spyAddWinningBid); + + expect(adResponse).to.have.property('status', CONSTANTS.BID_STATUS.RENDERED); + }); + }); + + describe('Prebid Event', () => { + Object.entries({ + 'unrendered': [false, (bid) => { delete bid.status; }], + 'rendered': [true, (bid) => { bid.status = CONSTANTS.BID_STATUS.RENDERED }] + }).forEach(([test, [shouldEmit, prepBid]]) => { + describe(`for ${test} bids`, () => { + beforeEach(() => { + prepBid(adResponse); + pushBidResponseToAuction(adResponse); + }); + + it(`should${shouldEmit ? ' ' : ' not '}emit AD_RENDER_FAILED`, () => { + const event = { + data: JSON.stringify({ + message: 'Prebid Event', + event: CONSTANTS.EVENTS.AD_RENDER_FAILED, + adId: bidId, + info: { + reason: 'Fail reason', + message: 'Fail message', + }, + }) + }; + receiveMessage(event); + expect(stubEmit.calledWith(CONSTANTS.EVENTS.AD_RENDER_FAILED, { + adId: bidId, + bid: adResponse, + reason: 'Fail reason', + message: 'Fail message' + })).to.equal(shouldEmit); + }); + + it(`should${shouldEmit ? ' ' : ' not '}emit AD_RENDER_SUCCEEDED`, () => { + const event = { + data: JSON.stringify({ + message: 'Prebid Event', + event: CONSTANTS.EVENTS.AD_RENDER_SUCCEEDED, + adId: bidId, + }) + }; + receiveMessage(event); + expect(stubEmit.calledWith(CONSTANTS.EVENTS.AD_RENDER_SUCCEEDED, { + adId: bidId, + bid: adResponse, + doc: null + })).to.equal(shouldEmit); + }); + }); + }); }); }); }); diff --git a/test/spec/utils_spec.js b/test/spec/utils_spec.js index 6494ead78e7..898b79cdcb5 100644 --- a/test/spec/utils_spec.js +++ b/test/spec/utils_spec.js @@ -2,6 +2,7 @@ import { getAdServerTargeting } from 'test/fixtures/fixtures.js'; import { expect } from 'chai'; import CONSTANTS from 'src/constants.json'; import * as utils from 'src/utils.js'; +import {waitForElementToLoad} from 'src/utils.js'; var assert = require('assert'); @@ -1198,4 +1199,41 @@ describe('Utils', function () { }); }); }); + + describe('waitForElementToLoad', () => { + let element; + let callbacks; + + function callback() { + callbacks++; + } + + function delay(delay = 0) { + return new Promise((resolve) => { + window.setTimeout(resolve, delay); + }) + } + + beforeEach(() => { + callbacks = 0; + element = window.document.createElement('div'); + }); + + it('should respect timeout if set', () => { + waitForElementToLoad(element, 50).then(callback); + return delay(60).then(() => { + expect(callbacks).to.equal(1); + }); + }); + + ['load', 'error'].forEach((event) => { + it(`should complete on '${event} event'`, () => { + waitForElementToLoad(element).then(callback); + element.dispatchEvent(new Event(event)); + return delay().then(() => { + expect(callbacks).to.equal(1); + }) + }); + }); + }); }); diff --git a/test/test_deps.js b/test/test_deps.js new file mode 100644 index 00000000000..e535154b799 --- /dev/null +++ b/test/test_deps.js @@ -0,0 +1,3 @@ +require('test/helpers/prebidGlobal.js'); +require('test/mocks/adloaderStub.js'); +require('test/mocks/xhr.js'); diff --git a/test/test_index.js b/test/test_index.js index 53d75e36176..883f4d0590c 100644 --- a/test/test_index.js +++ b/test/test_index.js @@ -1,6 +1,4 @@ -require('test/helpers/prebidGlobal.js'); -require('test/mocks/adloaderStub.js'); -require('test/mocks/xhr.js'); +require('./test_deps.js'); var testsContext = require.context('.', true, /_spec$/); testsContext.keys().forEach(testsContext); diff --git a/wdio.conf.js b/wdio.conf.js index c9aaa3f160d..4865c03f339 100644 --- a/wdio.conf.js +++ b/wdio.conf.js @@ -9,7 +9,7 @@ function getCapabilities() { return platformMap[os]; } - // only IE 11, Chrome 80 & Firefox 73 run as part of functional tests + // only Chrome 80 & Firefox 73 run as part of functional tests // rest of the browsers are discarded. delete browsers['bs_chrome_79_windows_10']; delete browsers['bs_firefox_72_windows_10']; diff --git a/yarn.lock b/yarn.lock deleted file mode 100644 index 2e081e121db..00000000000 --- a/yarn.lock +++ /dev/null @@ -1,13122 +0,0 @@ -# THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY. -# yarn lockfile v1 - - -"@babel/code-frame@7.12.11": - version "7.12.11" - resolved "https://registry.yarnpkg.com/@babel/code-frame/-/code-frame-7.12.11.tgz#f4ad435aa263db935b8f10f2c552d23fb716a63f" - integrity sha512-Zt1yodBx1UcyiePMSkWnU4hPqhwq7hGi2nFL1LeA3EUl+q2LQx16MISgJ0+z7dnmgvP9QtIleuETGOiOH1RcIw== - dependencies: - "@babel/highlight" "^7.10.4" - -"@babel/code-frame@^7.0.0", "@babel/code-frame@^7.10.4", "@babel/code-frame@^7.12.13", "@babel/code-frame@^7.14.5": - version "7.14.5" - resolved "https://registry.yarnpkg.com/@babel/code-frame/-/code-frame-7.14.5.tgz#23b08d740e83f49c5e59945fbf1b43e80bbf4edb" - integrity sha512-9pzDqyc6OLDaqe+zbACgFkb6fKMNG6CObKpnYXChRsvYGyEdc7CA2BaqeOM+vOtCS5ndmJicPJhKAwYRI6UfFw== - dependencies: - "@babel/highlight" "^7.14.5" - -"@babel/compat-data@^7.13.11", "@babel/compat-data@^7.15.0": - version "7.15.0" - resolved "https://registry.yarnpkg.com/@babel/compat-data/-/compat-data-7.15.0.tgz#2dbaf8b85334796cafbb0f5793a90a2fc010b176" - integrity sha512-0NqAC1IJE0S0+lL1SWFMxMkz1pKCNCjI4tr2Zx4LJSXxCLAdr6KyArnY+sno5m3yH9g737ygOyPABDsnXkpxiA== - -"@babel/core@7.12.3": - version "7.12.3" - resolved "https://registry.yarnpkg.com/@babel/core/-/core-7.12.3.tgz#1b436884e1e3bff6fb1328dc02b208759de92ad8" - integrity sha512-0qXcZYKZp3/6N2jKYVxZv0aNCsxTSVCiK72DTiTYZAu7sjg73W0/aynWjMbiGd87EQL4WyA8reiJVh92AVla9g== - dependencies: - "@babel/code-frame" "^7.10.4" - "@babel/generator" "^7.12.1" - "@babel/helper-module-transforms" "^7.12.1" - "@babel/helpers" "^7.12.1" - "@babel/parser" "^7.12.3" - "@babel/template" "^7.10.4" - "@babel/traverse" "^7.12.1" - "@babel/types" "^7.12.1" - convert-source-map "^1.7.0" - debug "^4.1.0" - gensync "^1.0.0-beta.1" - json5 "^2.1.2" - lodash "^4.17.19" - resolve "^1.3.2" - semver "^5.4.1" - source-map "^0.5.0" - -"@babel/core@^7.7.5", "@babel/core@^7.8.4": - version "7.15.5" - resolved "https://registry.yarnpkg.com/@babel/core/-/core-7.15.5.tgz#f8ed9ace730722544609f90c9bb49162dc3bf5b9" - integrity sha512-pYgXxiwAgQpgM1bNkZsDEq85f0ggXMA5L7c+o3tskGMh2BunCI9QUwB9Z4jpvXUOuMdyGKiGKQiRe11VS6Jzvg== - dependencies: - "@babel/code-frame" "^7.14.5" - "@babel/generator" "^7.15.4" - "@babel/helper-compilation-targets" "^7.15.4" - "@babel/helper-module-transforms" "^7.15.4" - "@babel/helpers" "^7.15.4" - "@babel/parser" "^7.15.5" - "@babel/template" "^7.15.4" - "@babel/traverse" "^7.15.4" - "@babel/types" "^7.15.4" - convert-source-map "^1.7.0" - debug "^4.1.0" - gensync "^1.0.0-beta.2" - json5 "^2.1.2" - semver "^6.3.0" - source-map "^0.5.0" - -"@babel/generator@7.12.1": - version "7.12.1" - resolved "https://registry.yarnpkg.com/@babel/generator/-/generator-7.12.1.tgz#0d70be32bdaa03d7c51c8597dda76e0df1f15468" - integrity sha512-DB+6rafIdc9o72Yc3/Ph5h+6hUjeOp66pF0naQBgUFFuPqzQwIlPTm3xZR7YNvduIMtkDIj2t21LSQwnbCrXvg== - dependencies: - "@babel/types" "^7.12.1" - jsesc "^2.5.1" - source-map "^0.5.0" - -"@babel/generator@^7.12.1", "@babel/generator@^7.15.4": - version "7.15.4" - resolved "https://registry.yarnpkg.com/@babel/generator/-/generator-7.15.4.tgz#85acb159a267ca6324f9793986991ee2022a05b0" - integrity sha512-d3itta0tu+UayjEORPNz6e1T3FtvWlP5N4V5M+lhp/CxT4oAA7/NcScnpRyspUMLK6tu9MNHmQHxRykuN2R7hw== - dependencies: - "@babel/types" "^7.15.4" - jsesc "^2.5.1" - source-map "^0.5.0" - -"@babel/helper-annotate-as-pure@^7.14.5", "@babel/helper-annotate-as-pure@^7.15.4": - version "7.15.4" - resolved "https://registry.yarnpkg.com/@babel/helper-annotate-as-pure/-/helper-annotate-as-pure-7.15.4.tgz#3d0e43b00c5e49fdb6c57e421601a7a658d5f835" - integrity sha512-QwrtdNvUNsPCj2lfNQacsGSQvGX8ee1ttrBrcozUP2Sv/jylewBP/8QFe6ZkBsC8T/GYWonNAWJV4aRR9AL2DA== - dependencies: - "@babel/types" "^7.15.4" - -"@babel/helper-builder-binary-assignment-operator-visitor@^7.14.5": - version "7.15.4" - resolved "https://registry.yarnpkg.com/@babel/helper-builder-binary-assignment-operator-visitor/-/helper-builder-binary-assignment-operator-visitor-7.15.4.tgz#21ad815f609b84ee0e3058676c33cf6d1670525f" - integrity sha512-P8o7JP2Mzi0SdC6eWr1zF+AEYvrsZa7GSY1lTayjF5XJhVH0kjLYUZPvTMflP7tBgZoe9gIhTa60QwFpqh/E0Q== - dependencies: - "@babel/helper-explode-assignable-expression" "^7.15.4" - "@babel/types" "^7.15.4" - -"@babel/helper-compilation-targets@^7.13.0", "@babel/helper-compilation-targets@^7.15.4": - version "7.15.4" - resolved "https://registry.yarnpkg.com/@babel/helper-compilation-targets/-/helper-compilation-targets-7.15.4.tgz#cf6d94f30fbefc139123e27dd6b02f65aeedb7b9" - integrity sha512-rMWPCirulnPSe4d+gwdWXLfAXTTBj8M3guAf5xFQJ0nvFY7tfNAFnWdqaHegHlgDZOCT4qvhF3BYlSJag8yhqQ== - dependencies: - "@babel/compat-data" "^7.15.0" - "@babel/helper-validator-option" "^7.14.5" - browserslist "^4.16.6" - semver "^6.3.0" - -"@babel/helper-create-class-features-plugin@^7.14.5", "@babel/helper-create-class-features-plugin@^7.15.4": - version "7.15.4" - resolved "https://registry.yarnpkg.com/@babel/helper-create-class-features-plugin/-/helper-create-class-features-plugin-7.15.4.tgz#7f977c17bd12a5fba363cb19bea090394bf37d2e" - integrity sha512-7ZmzFi+DwJx6A7mHRwbuucEYpyBwmh2Ca0RvI6z2+WLZYCqV0JOaLb+u0zbtmDicebgKBZgqbYfLaKNqSgv5Pw== - dependencies: - "@babel/helper-annotate-as-pure" "^7.15.4" - "@babel/helper-function-name" "^7.15.4" - "@babel/helper-member-expression-to-functions" "^7.15.4" - "@babel/helper-optimise-call-expression" "^7.15.4" - "@babel/helper-replace-supers" "^7.15.4" - "@babel/helper-split-export-declaration" "^7.15.4" - -"@babel/helper-create-regexp-features-plugin@^7.14.5": - version "7.14.5" - resolved "https://registry.yarnpkg.com/@babel/helper-create-regexp-features-plugin/-/helper-create-regexp-features-plugin-7.14.5.tgz#c7d5ac5e9cf621c26057722fb7a8a4c5889358c4" - integrity sha512-TLawwqpOErY2HhWbGJ2nZT5wSkR192QpN+nBg1THfBfftrlvOh+WbhrxXCH4q4xJ9Gl16BGPR/48JA+Ryiho/A== - dependencies: - "@babel/helper-annotate-as-pure" "^7.14.5" - regexpu-core "^4.7.1" - -"@babel/helper-define-polyfill-provider@^0.2.2": - version "0.2.3" - resolved "https://registry.yarnpkg.com/@babel/helper-define-polyfill-provider/-/helper-define-polyfill-provider-0.2.3.tgz#0525edec5094653a282688d34d846e4c75e9c0b6" - integrity sha512-RH3QDAfRMzj7+0Nqu5oqgO5q9mFtQEVvCRsi8qCEfzLR9p2BHfn5FzhSB2oj1fF7I2+DcTORkYaQ6aTR9Cofew== - dependencies: - "@babel/helper-compilation-targets" "^7.13.0" - "@babel/helper-module-imports" "^7.12.13" - "@babel/helper-plugin-utils" "^7.13.0" - "@babel/traverse" "^7.13.0" - debug "^4.1.1" - lodash.debounce "^4.0.8" - resolve "^1.14.2" - semver "^6.1.2" - -"@babel/helper-explode-assignable-expression@^7.15.4": - version "7.15.4" - resolved "https://registry.yarnpkg.com/@babel/helper-explode-assignable-expression/-/helper-explode-assignable-expression-7.15.4.tgz#f9aec9d219f271eaf92b9f561598ca6b2682600c" - integrity sha512-J14f/vq8+hdC2KoWLIQSsGrC9EFBKE4NFts8pfMpymfApds+fPqR30AOUWc4tyr56h9l/GA1Sxv2q3dLZWbQ/g== - dependencies: - "@babel/types" "^7.15.4" - -"@babel/helper-function-name@^7.14.5", "@babel/helper-function-name@^7.15.4": - version "7.15.4" - resolved "https://registry.yarnpkg.com/@babel/helper-function-name/-/helper-function-name-7.15.4.tgz#845744dafc4381a4a5fb6afa6c3d36f98a787ebc" - integrity sha512-Z91cOMM4DseLIGOnog+Z8OI6YseR9bua+HpvLAQ2XayUGU+neTtX+97caALaLdyu53I/fjhbeCnWnRH1O3jFOw== - dependencies: - "@babel/helper-get-function-arity" "^7.15.4" - "@babel/template" "^7.15.4" - "@babel/types" "^7.15.4" - -"@babel/helper-get-function-arity@^7.15.4": - version "7.15.4" - resolved "https://registry.yarnpkg.com/@babel/helper-get-function-arity/-/helper-get-function-arity-7.15.4.tgz#098818934a137fce78b536a3e015864be1e2879b" - integrity sha512-1/AlxSF92CmGZzHnC515hm4SirTxtpDnLEJ0UyEMgTMZN+6bxXKg04dKhiRx5Enel+SUA1G1t5Ed/yQia0efrA== - dependencies: - "@babel/types" "^7.15.4" - -"@babel/helper-hoist-variables@^7.15.4": - version "7.15.4" - resolved "https://registry.yarnpkg.com/@babel/helper-hoist-variables/-/helper-hoist-variables-7.15.4.tgz#09993a3259c0e918f99d104261dfdfc033f178df" - integrity sha512-VTy085egb3jUGVK9ycIxQiPbquesq0HUQ+tPO0uv5mPEBZipk+5FkRKiWq5apuyTE9FUrjENB0rCf8y+n+UuhA== - dependencies: - "@babel/types" "^7.15.4" - -"@babel/helper-member-expression-to-functions@^7.15.4": - version "7.15.4" - resolved "https://registry.yarnpkg.com/@babel/helper-member-expression-to-functions/-/helper-member-expression-to-functions-7.15.4.tgz#bfd34dc9bba9824a4658b0317ec2fd571a51e6ef" - integrity sha512-cokOMkxC/BTyNP1AlY25HuBWM32iCEsLPI4BHDpJCHHm1FU2E7dKWWIXJgQgSFiu4lp8q3bL1BIKwqkSUviqtA== - dependencies: - "@babel/types" "^7.15.4" - -"@babel/helper-module-imports@^7.12.13", "@babel/helper-module-imports@^7.14.5", "@babel/helper-module-imports@^7.15.4": - version "7.15.4" - resolved "https://registry.yarnpkg.com/@babel/helper-module-imports/-/helper-module-imports-7.15.4.tgz#e18007d230632dea19b47853b984476e7b4e103f" - integrity sha512-jeAHZbzUwdW/xHgHQ3QmWR4Jg6j15q4w/gCfwZvtqOxoo5DKtLHk8Bsf4c5RZRC7NmLEs+ohkdq8jFefuvIxAA== - dependencies: - "@babel/types" "^7.15.4" - -"@babel/helper-module-transforms@^7.12.1", "@babel/helper-module-transforms@^7.14.5", "@babel/helper-module-transforms@^7.15.4": - version "7.15.7" - resolved "https://registry.yarnpkg.com/@babel/helper-module-transforms/-/helper-module-transforms-7.15.7.tgz#7da80c8cbc1f02655d83f8b79d25866afe50d226" - integrity sha512-ZNqjjQG/AuFfekFTY+7nY4RgBSklgTu970c7Rj3m/JOhIu5KPBUuTA9AY6zaKcUvk4g6EbDXdBnhi35FAssdSw== - dependencies: - "@babel/helper-module-imports" "^7.15.4" - "@babel/helper-replace-supers" "^7.15.4" - "@babel/helper-simple-access" "^7.15.4" - "@babel/helper-split-export-declaration" "^7.15.4" - "@babel/helper-validator-identifier" "^7.15.7" - "@babel/template" "^7.15.4" - "@babel/traverse" "^7.15.4" - "@babel/types" "^7.15.6" - -"@babel/helper-optimise-call-expression@^7.15.4": - version "7.15.4" - resolved "https://registry.yarnpkg.com/@babel/helper-optimise-call-expression/-/helper-optimise-call-expression-7.15.4.tgz#f310a5121a3b9cc52d9ab19122bd729822dee171" - integrity sha512-E/z9rfbAOt1vDW1DR7k4SzhzotVV5+qMciWV6LaG1g4jeFrkDlJedjtV4h0i4Q/ITnUu+Pk08M7fczsB9GXBDw== - dependencies: - "@babel/types" "^7.15.4" - -"@babel/helper-plugin-utils@^7.0.0", "@babel/helper-plugin-utils@^7.10.4", "@babel/helper-plugin-utils@^7.12.13", "@babel/helper-plugin-utils@^7.13.0", "@babel/helper-plugin-utils@^7.14.5", "@babel/helper-plugin-utils@^7.8.0", "@babel/helper-plugin-utils@^7.8.3": - version "7.14.5" - resolved "https://registry.yarnpkg.com/@babel/helper-plugin-utils/-/helper-plugin-utils-7.14.5.tgz#5ac822ce97eec46741ab70a517971e443a70c5a9" - integrity sha512-/37qQCE3K0vvZKwoK4XU/irIJQdIfCJuhU5eKnNxpFDsOkgFaUAwbv+RYw6eYgsC0E4hS7r5KqGULUogqui0fQ== - -"@babel/helper-remap-async-to-generator@^7.14.5", "@babel/helper-remap-async-to-generator@^7.15.4": - version "7.15.4" - resolved "https://registry.yarnpkg.com/@babel/helper-remap-async-to-generator/-/helper-remap-async-to-generator-7.15.4.tgz#2637c0731e4c90fbf58ac58b50b2b5a192fc970f" - integrity sha512-v53MxgvMK/HCwckJ1bZrq6dNKlmwlyRNYM6ypaRTdXWGOE2c1/SCa6dL/HimhPulGhZKw9W0QhREM583F/t0vQ== - dependencies: - "@babel/helper-annotate-as-pure" "^7.15.4" - "@babel/helper-wrap-function" "^7.15.4" - "@babel/types" "^7.15.4" - -"@babel/helper-replace-supers@^7.14.5", "@babel/helper-replace-supers@^7.15.4": - version "7.15.4" - resolved "https://registry.yarnpkg.com/@babel/helper-replace-supers/-/helper-replace-supers-7.15.4.tgz#52a8ab26ba918c7f6dee28628b07071ac7b7347a" - integrity sha512-/ztT6khaXF37MS47fufrKvIsiQkx1LBRvSJNzRqmbyeZnTwU9qBxXYLaaT/6KaxfKhjs2Wy8kG8ZdsFUuWBjzw== - dependencies: - "@babel/helper-member-expression-to-functions" "^7.15.4" - "@babel/helper-optimise-call-expression" "^7.15.4" - "@babel/traverse" "^7.15.4" - "@babel/types" "^7.15.4" - -"@babel/helper-simple-access@^7.15.4": - version "7.15.4" - resolved "https://registry.yarnpkg.com/@babel/helper-simple-access/-/helper-simple-access-7.15.4.tgz#ac368905abf1de8e9781434b635d8f8674bcc13b" - integrity sha512-UzazrDoIVOZZcTeHHEPYrr1MvTR/K+wgLg6MY6e1CJyaRhbibftF6fR2KU2sFRtI/nERUZR9fBd6aKgBlIBaPg== - dependencies: - "@babel/types" "^7.15.4" - -"@babel/helper-skip-transparent-expression-wrappers@^7.14.5", "@babel/helper-skip-transparent-expression-wrappers@^7.15.4": - version "7.15.4" - resolved "https://registry.yarnpkg.com/@babel/helper-skip-transparent-expression-wrappers/-/helper-skip-transparent-expression-wrappers-7.15.4.tgz#707dbdba1f4ad0fa34f9114fc8197aec7d5da2eb" - integrity sha512-BMRLsdh+D1/aap19TycS4eD1qELGrCBJwzaY9IE8LrpJtJb+H7rQkPIdsfgnMtLBA6DJls7X9z93Z4U8h7xw0A== - dependencies: - "@babel/types" "^7.15.4" - -"@babel/helper-split-export-declaration@^7.15.4": - version "7.15.4" - resolved "https://registry.yarnpkg.com/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.15.4.tgz#aecab92dcdbef6a10aa3b62ab204b085f776e257" - integrity sha512-HsFqhLDZ08DxCpBdEVtKmywj6PQbwnF6HHybur0MAnkAKnlS6uHkwnmRIkElB2Owpfb4xL4NwDmDLFubueDXsw== - dependencies: - "@babel/types" "^7.15.4" - -"@babel/helper-validator-identifier@^7.14.5", "@babel/helper-validator-identifier@^7.14.9", "@babel/helper-validator-identifier@^7.15.7": - version "7.15.7" - resolved "https://registry.yarnpkg.com/@babel/helper-validator-identifier/-/helper-validator-identifier-7.15.7.tgz#220df993bfe904a4a6b02ab4f3385a5ebf6e2389" - integrity sha512-K4JvCtQqad9OY2+yTU8w+E82ywk/fe+ELNlt1G8z3bVGlZfn/hOcQQsUhGhW/N+tb3fxK800wLtKOE/aM0m72w== - -"@babel/helper-validator-option@^7.14.5": - version "7.14.5" - resolved "https://registry.yarnpkg.com/@babel/helper-validator-option/-/helper-validator-option-7.14.5.tgz#6e72a1fff18d5dfcb878e1e62f1a021c4b72d5a3" - integrity sha512-OX8D5eeX4XwcroVW45NMvoYaIuFI+GQpA2a8Gi+X/U/cDUIRsV37qQfF905F0htTRCREQIB4KqPeaveRJUl3Ow== - -"@babel/helper-wrap-function@^7.15.4": - version "7.15.4" - resolved "https://registry.yarnpkg.com/@babel/helper-wrap-function/-/helper-wrap-function-7.15.4.tgz#6f754b2446cfaf3d612523e6ab8d79c27c3a3de7" - integrity sha512-Y2o+H/hRV5W8QhIfTpRIBwl57y8PrZt6JM3V8FOo5qarjshHItyH5lXlpMfBfmBefOqSCpKZs/6Dxqp0E/U+uw== - dependencies: - "@babel/helper-function-name" "^7.15.4" - "@babel/template" "^7.15.4" - "@babel/traverse" "^7.15.4" - "@babel/types" "^7.15.4" - -"@babel/helpers@^7.12.1", "@babel/helpers@^7.15.4": - version "7.15.4" - resolved "https://registry.yarnpkg.com/@babel/helpers/-/helpers-7.15.4.tgz#5f40f02050a3027121a3cf48d497c05c555eaf43" - integrity sha512-V45u6dqEJ3w2rlryYYXf6i9rQ5YMNu4FLS6ngs8ikblhu2VdR1AqAd6aJjBzmf2Qzh6KOLqKHxEN9+TFbAkAVQ== - dependencies: - "@babel/template" "^7.15.4" - "@babel/traverse" "^7.15.4" - "@babel/types" "^7.15.4" - -"@babel/highlight@^7.10.4", "@babel/highlight@^7.14.5": - version "7.14.5" - resolved "https://registry.yarnpkg.com/@babel/highlight/-/highlight-7.14.5.tgz#6861a52f03966405001f6aa534a01a24d99e8cd9" - integrity sha512-qf9u2WFWVV0MppaL877j2dBtQIDgmidgjGk5VIMw3OadXvYaXn66U1BFlH2t4+t3i+8PhedppRv+i40ABzd+gg== - dependencies: - "@babel/helper-validator-identifier" "^7.14.5" - chalk "^2.0.0" - js-tokens "^4.0.0" - -"@babel/parser@7.12.3": - version "7.12.3" - resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.12.3.tgz#a305415ebe7a6c7023b40b5122a0662d928334cd" - integrity sha512-kFsOS0IbsuhO5ojF8Hc8z/8vEIOkylVBrjiZUbLTE3XFe0Qi+uu6HjzQixkFaqr0ZPAMZcBVxEwmsnsLPZ2Xsw== - -"@babel/parser@^7.10.5", "@babel/parser@^7.12.3", "@babel/parser@^7.15.0", "@babel/parser@^7.15.4", "@babel/parser@^7.15.5": - version "7.15.7" - resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.15.7.tgz#0c3ed4a2eb07b165dfa85b3cc45c727334c4edae" - integrity sha512-rycZXvQ+xS9QyIcJ9HXeDWf1uxqlbVFAUq0Rq0dbc50Zb/+wUe/ehyfzGfm9KZZF0kBejYgxltBXocP+gKdL2g== - -"@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining@^7.15.4": - version "7.15.4" - resolved "https://registry.yarnpkg.com/@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining/-/plugin-bugfix-v8-spread-parameters-in-optional-chaining-7.15.4.tgz#dbdeabb1e80f622d9f0b583efb2999605e0a567e" - integrity sha512-eBnpsl9tlhPhpI10kU06JHnrYXwg3+V6CaP2idsCXNef0aeslpqyITXQ74Vfk5uHgY7IG7XP0yIH8b42KSzHog== - dependencies: - "@babel/helper-plugin-utils" "^7.14.5" - "@babel/helper-skip-transparent-expression-wrappers" "^7.15.4" - "@babel/plugin-proposal-optional-chaining" "^7.14.5" - -"@babel/plugin-proposal-async-generator-functions@^7.15.4": - version "7.15.4" - resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-async-generator-functions/-/plugin-proposal-async-generator-functions-7.15.4.tgz#f82aabe96c135d2ceaa917feb9f5fca31635277e" - integrity sha512-2zt2g5vTXpMC3OmK6uyjvdXptbhBXfA77XGrd3gh93zwG8lZYBLOBImiGBEG0RANu3JqKEACCz5CGk73OJROBw== - dependencies: - "@babel/helper-plugin-utils" "^7.14.5" - "@babel/helper-remap-async-to-generator" "^7.15.4" - "@babel/plugin-syntax-async-generators" "^7.8.4" - -"@babel/plugin-proposal-class-properties@^7.14.5": - version "7.14.5" - resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-class-properties/-/plugin-proposal-class-properties-7.14.5.tgz#40d1ee140c5b1e31a350f4f5eed945096559b42e" - integrity sha512-q/PLpv5Ko4dVc1LYMpCY7RVAAO4uk55qPwrIuJ5QJ8c6cVuAmhu7I/49JOppXL6gXf7ZHzpRVEUZdYoPLM04Gg== - dependencies: - "@babel/helper-create-class-features-plugin" "^7.14.5" - "@babel/helper-plugin-utils" "^7.14.5" - -"@babel/plugin-proposal-class-static-block@^7.15.4": - version "7.15.4" - resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-class-static-block/-/plugin-proposal-class-static-block-7.15.4.tgz#3e7ca6128453c089e8b477a99f970c63fc1cb8d7" - integrity sha512-M682XWrrLNk3chXCjoPUQWOyYsB93B9z3mRyjtqqYJWDf2mfCdIYgDrA11cgNVhAQieaq6F2fn2f3wI0U4aTjA== - dependencies: - "@babel/helper-create-class-features-plugin" "^7.15.4" - "@babel/helper-plugin-utils" "^7.14.5" - "@babel/plugin-syntax-class-static-block" "^7.14.5" - -"@babel/plugin-proposal-dynamic-import@^7.14.5": - version "7.14.5" - resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-dynamic-import/-/plugin-proposal-dynamic-import-7.14.5.tgz#0c6617df461c0c1f8fff3b47cd59772360101d2c" - integrity sha512-ExjiNYc3HDN5PXJx+bwC50GIx/KKanX2HiggnIUAYedbARdImiCU4RhhHfdf0Kd7JNXGpsBBBCOm+bBVy3Gb0g== - dependencies: - "@babel/helper-plugin-utils" "^7.14.5" - "@babel/plugin-syntax-dynamic-import" "^7.8.3" - -"@babel/plugin-proposal-export-namespace-from@^7.14.5": - version "7.14.5" - resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-export-namespace-from/-/plugin-proposal-export-namespace-from-7.14.5.tgz#dbad244310ce6ccd083072167d8cea83a52faf76" - integrity sha512-g5POA32bXPMmSBu5Dx/iZGLGnKmKPc5AiY7qfZgurzrCYgIztDlHFbznSNCoQuv57YQLnQfaDi7dxCtLDIdXdA== - dependencies: - "@babel/helper-plugin-utils" "^7.14.5" - "@babel/plugin-syntax-export-namespace-from" "^7.8.3" - -"@babel/plugin-proposal-json-strings@^7.14.5": - version "7.14.5" - resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-json-strings/-/plugin-proposal-json-strings-7.14.5.tgz#38de60db362e83a3d8c944ac858ddf9f0c2239eb" - integrity sha512-NSq2fczJYKVRIsUJyNxrVUMhB27zb7N7pOFGQOhBKJrChbGcgEAqyZrmZswkPk18VMurEeJAaICbfm57vUeTbQ== - dependencies: - "@babel/helper-plugin-utils" "^7.14.5" - "@babel/plugin-syntax-json-strings" "^7.8.3" - -"@babel/plugin-proposal-logical-assignment-operators@^7.14.5": - version "7.14.5" - resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-logical-assignment-operators/-/plugin-proposal-logical-assignment-operators-7.14.5.tgz#6e6229c2a99b02ab2915f82571e0cc646a40c738" - integrity sha512-YGn2AvZAo9TwyhlLvCCWxD90Xq8xJ4aSgaX3G5D/8DW94L8aaT+dS5cSP+Z06+rCJERGSr9GxMBZ601xoc2taw== - dependencies: - "@babel/helper-plugin-utils" "^7.14.5" - "@babel/plugin-syntax-logical-assignment-operators" "^7.10.4" - -"@babel/plugin-proposal-nullish-coalescing-operator@^7.14.5": - version "7.14.5" - resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-nullish-coalescing-operator/-/plugin-proposal-nullish-coalescing-operator-7.14.5.tgz#ee38589ce00e2cc59b299ec3ea406fcd3a0fdaf6" - integrity sha512-gun/SOnMqjSb98Nkaq2rTKMwervfdAoz6NphdY0vTfuzMfryj+tDGb2n6UkDKwez+Y8PZDhE3D143v6Gepp4Hg== - dependencies: - "@babel/helper-plugin-utils" "^7.14.5" - "@babel/plugin-syntax-nullish-coalescing-operator" "^7.8.3" - -"@babel/plugin-proposal-numeric-separator@^7.14.5": - version "7.14.5" - resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-numeric-separator/-/plugin-proposal-numeric-separator-7.14.5.tgz#83631bf33d9a51df184c2102a069ac0c58c05f18" - integrity sha512-yiclALKe0vyZRZE0pS6RXgjUOt87GWv6FYa5zqj15PvhOGFO69R5DusPlgK/1K5dVnCtegTiWu9UaBSrLLJJBg== - dependencies: - "@babel/helper-plugin-utils" "^7.14.5" - "@babel/plugin-syntax-numeric-separator" "^7.10.4" - -"@babel/plugin-proposal-object-rest-spread@^7.15.6": - version "7.15.6" - resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-object-rest-spread/-/plugin-proposal-object-rest-spread-7.15.6.tgz#ef68050c8703d07b25af402cb96cf7f34a68ed11" - integrity sha512-qtOHo7A1Vt+O23qEAX+GdBpqaIuD3i9VRrWgCJeq7WO6H2d14EK3q11urj5Te2MAeK97nMiIdRpwd/ST4JFbNg== - dependencies: - "@babel/compat-data" "^7.15.0" - "@babel/helper-compilation-targets" "^7.15.4" - "@babel/helper-plugin-utils" "^7.14.5" - "@babel/plugin-syntax-object-rest-spread" "^7.8.3" - "@babel/plugin-transform-parameters" "^7.15.4" - -"@babel/plugin-proposal-optional-catch-binding@^7.14.5": - version "7.14.5" - resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-optional-catch-binding/-/plugin-proposal-optional-catch-binding-7.14.5.tgz#939dd6eddeff3a67fdf7b3f044b5347262598c3c" - integrity sha512-3Oyiixm0ur7bzO5ybNcZFlmVsygSIQgdOa7cTfOYCMY+wEPAYhZAJxi3mixKFCTCKUhQXuCTtQ1MzrpL3WT8ZQ== - dependencies: - "@babel/helper-plugin-utils" "^7.14.5" - "@babel/plugin-syntax-optional-catch-binding" "^7.8.3" - -"@babel/plugin-proposal-optional-chaining@^7.14.5": - version "7.14.5" - resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-optional-chaining/-/plugin-proposal-optional-chaining-7.14.5.tgz#fa83651e60a360e3f13797eef00b8d519695b603" - integrity sha512-ycz+VOzo2UbWNI1rQXxIuMOzrDdHGrI23fRiz/Si2R4kv2XZQ1BK8ccdHwehMKBlcH/joGW/tzrUmo67gbJHlQ== - dependencies: - "@babel/helper-plugin-utils" "^7.14.5" - "@babel/helper-skip-transparent-expression-wrappers" "^7.14.5" - "@babel/plugin-syntax-optional-chaining" "^7.8.3" - -"@babel/plugin-proposal-private-methods@^7.14.5": - version "7.14.5" - resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-private-methods/-/plugin-proposal-private-methods-7.14.5.tgz#37446495996b2945f30f5be5b60d5e2aa4f5792d" - integrity sha512-838DkdUA1u+QTCplatfq4B7+1lnDa/+QMI89x5WZHBcnNv+47N8QEj2k9I2MUU9xIv8XJ4XvPCviM/Dj7Uwt9g== - dependencies: - "@babel/helper-create-class-features-plugin" "^7.14.5" - "@babel/helper-plugin-utils" "^7.14.5" - -"@babel/plugin-proposal-private-property-in-object@^7.15.4": - version "7.15.4" - resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-private-property-in-object/-/plugin-proposal-private-property-in-object-7.15.4.tgz#55c5e3b4d0261fd44fe637e3f624cfb0f484e3e5" - integrity sha512-X0UTixkLf0PCCffxgu5/1RQyGGbgZuKoI+vXP4iSbJSYwPb7hu06omsFGBvQ9lJEvwgrxHdS8B5nbfcd8GyUNA== - dependencies: - "@babel/helper-annotate-as-pure" "^7.15.4" - "@babel/helper-create-class-features-plugin" "^7.15.4" - "@babel/helper-plugin-utils" "^7.14.5" - "@babel/plugin-syntax-private-property-in-object" "^7.14.5" - -"@babel/plugin-proposal-unicode-property-regex@^7.14.5", "@babel/plugin-proposal-unicode-property-regex@^7.4.4": - version "7.14.5" - resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-unicode-property-regex/-/plugin-proposal-unicode-property-regex-7.14.5.tgz#0f95ee0e757a5d647f378daa0eca7e93faa8bbe8" - integrity sha512-6axIeOU5LnY471KenAB9vI8I5j7NQ2d652hIYwVyRfgaZT5UpiqFKCuVXCDMSrU+3VFafnu2c5m3lrWIlr6A5Q== - dependencies: - "@babel/helper-create-regexp-features-plugin" "^7.14.5" - "@babel/helper-plugin-utils" "^7.14.5" - -"@babel/plugin-syntax-async-generators@^7.8.4": - version "7.8.4" - resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-async-generators/-/plugin-syntax-async-generators-7.8.4.tgz#a983fb1aeb2ec3f6ed042a210f640e90e786fe0d" - integrity sha512-tycmZxkGfZaxhMRbXlPXuVFpdWlXpir2W4AMhSJgRKzk/eDlIXOhb2LHWoLpDF7TEHylV5zNhykX6KAgHJmTNw== - dependencies: - "@babel/helper-plugin-utils" "^7.8.0" - -"@babel/plugin-syntax-class-properties@^7.12.13": - version "7.12.13" - resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-class-properties/-/plugin-syntax-class-properties-7.12.13.tgz#b5c987274c4a3a82b89714796931a6b53544ae10" - integrity sha512-fm4idjKla0YahUNgFNLCB0qySdsoPiZP3iQE3rky0mBUtMZ23yDJ9SJdg6dXTSDnulOVqiF3Hgr9nbXvXTQZYA== - dependencies: - "@babel/helper-plugin-utils" "^7.12.13" - -"@babel/plugin-syntax-class-static-block@^7.14.5": - version "7.14.5" - resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-class-static-block/-/plugin-syntax-class-static-block-7.14.5.tgz#195df89b146b4b78b3bf897fd7a257c84659d406" - integrity sha512-b+YyPmr6ldyNnM6sqYeMWE+bgJcJpO6yS4QD7ymxgH34GBPNDM/THBh8iunyvKIZztiwLH4CJZ0RxTk9emgpjw== - dependencies: - "@babel/helper-plugin-utils" "^7.14.5" - -"@babel/plugin-syntax-dynamic-import@^7.8.3": - version "7.8.3" - resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-dynamic-import/-/plugin-syntax-dynamic-import-7.8.3.tgz#62bf98b2da3cd21d626154fc96ee5b3cb68eacb3" - integrity sha512-5gdGbFon+PszYzqs83S3E5mpi7/y/8M9eC90MRTZfduQOYW76ig6SOSPNe41IG5LoP3FGBn2N0RjVDSQiS94kQ== - dependencies: - "@babel/helper-plugin-utils" "^7.8.0" - -"@babel/plugin-syntax-export-namespace-from@^7.8.3": - version "7.8.3" - resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-export-namespace-from/-/plugin-syntax-export-namespace-from-7.8.3.tgz#028964a9ba80dbc094c915c487ad7c4e7a66465a" - integrity sha512-MXf5laXo6c1IbEbegDmzGPwGNTsHZmEy6QGznu5Sh2UCWvueywb2ee+CCE4zQiZstxU9BMoQO9i6zUFSY0Kj0Q== - dependencies: - "@babel/helper-plugin-utils" "^7.8.3" - -"@babel/plugin-syntax-json-strings@^7.8.3": - version "7.8.3" - resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-json-strings/-/plugin-syntax-json-strings-7.8.3.tgz#01ca21b668cd8218c9e640cb6dd88c5412b2c96a" - integrity sha512-lY6kdGpWHvjoe2vk4WrAapEuBR69EMxZl+RoGRhrFGNYVK8mOPAW8VfbT/ZgrFbXlDNiiaxQnAtgVCZ6jv30EA== - dependencies: - "@babel/helper-plugin-utils" "^7.8.0" - -"@babel/plugin-syntax-logical-assignment-operators@^7.10.4": - version "7.10.4" - resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-logical-assignment-operators/-/plugin-syntax-logical-assignment-operators-7.10.4.tgz#ca91ef46303530448b906652bac2e9fe9941f699" - integrity sha512-d8waShlpFDinQ5MtvGU9xDAOzKH47+FFoney2baFIoMr952hKOLp1HR7VszoZvOsV/4+RRszNY7D17ba0te0ig== - dependencies: - "@babel/helper-plugin-utils" "^7.10.4" - -"@babel/plugin-syntax-nullish-coalescing-operator@^7.8.3": - version "7.8.3" - resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-nullish-coalescing-operator/-/plugin-syntax-nullish-coalescing-operator-7.8.3.tgz#167ed70368886081f74b5c36c65a88c03b66d1a9" - integrity sha512-aSff4zPII1u2QD7y+F8oDsz19ew4IGEJg9SVW+bqwpwtfFleiQDMdzA/R+UlWDzfnHFCxxleFT0PMIrR36XLNQ== - dependencies: - "@babel/helper-plugin-utils" "^7.8.0" - -"@babel/plugin-syntax-numeric-separator@^7.10.4": - version "7.10.4" - resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-numeric-separator/-/plugin-syntax-numeric-separator-7.10.4.tgz#b9b070b3e33570cd9fd07ba7fa91c0dd37b9af97" - integrity sha512-9H6YdfkcK/uOnY/K7/aA2xpzaAgkQn37yzWUMRK7OaPOqOpGS1+n0H5hxT9AUw9EsSjPW8SVyMJwYRtWs3X3ug== - dependencies: - "@babel/helper-plugin-utils" "^7.10.4" - -"@babel/plugin-syntax-object-rest-spread@^7.8.3": - version "7.8.3" - resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-object-rest-spread/-/plugin-syntax-object-rest-spread-7.8.3.tgz#60e225edcbd98a640332a2e72dd3e66f1af55871" - integrity sha512-XoqMijGZb9y3y2XskN+P1wUGiVwWZ5JmoDRwx5+3GmEplNyVM2s2Dg8ILFQm8rWM48orGy5YpI5Bl8U1y7ydlA== - dependencies: - "@babel/helper-plugin-utils" "^7.8.0" - -"@babel/plugin-syntax-optional-catch-binding@^7.8.3": - version "7.8.3" - resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-optional-catch-binding/-/plugin-syntax-optional-catch-binding-7.8.3.tgz#6111a265bcfb020eb9efd0fdfd7d26402b9ed6c1" - integrity sha512-6VPD0Pc1lpTqw0aKoeRTMiB+kWhAoT24PA+ksWSBrFtl5SIRVpZlwN3NNPQjehA2E/91FV3RjLWoVTglWcSV3Q== - dependencies: - "@babel/helper-plugin-utils" "^7.8.0" - -"@babel/plugin-syntax-optional-chaining@^7.8.3": - version "7.8.3" - resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-optional-chaining/-/plugin-syntax-optional-chaining-7.8.3.tgz#4f69c2ab95167e0180cd5336613f8c5788f7d48a" - integrity sha512-KoK9ErH1MBlCPxV0VANkXW2/dw4vlbGDrFgz8bmUsBGYkFRcbRwMh6cIJubdPrkxRwuGdtCk0v/wPTKbQgBjkg== - dependencies: - "@babel/helper-plugin-utils" "^7.8.0" - -"@babel/plugin-syntax-private-property-in-object@^7.14.5": - version "7.14.5" - resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-private-property-in-object/-/plugin-syntax-private-property-in-object-7.14.5.tgz#0dc6671ec0ea22b6e94a1114f857970cd39de1ad" - integrity sha512-0wVnp9dxJ72ZUJDV27ZfbSj6iHLoytYZmh3rFcxNnvsJF3ktkzLDZPy/mA17HGsaQT3/DQsWYX1f1QGWkCoVUg== - dependencies: - "@babel/helper-plugin-utils" "^7.14.5" - -"@babel/plugin-syntax-top-level-await@^7.14.5": - version "7.14.5" - resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-top-level-await/-/plugin-syntax-top-level-await-7.14.5.tgz#c1cfdadc35a646240001f06138247b741c34d94c" - integrity sha512-hx++upLv5U1rgYfwe1xBQUhRmU41NEvpUvrp8jkrSCdvGSnM5/qdRMtylJ6PG5OFkBaHkbTAKTnd3/YyESRHFw== - dependencies: - "@babel/helper-plugin-utils" "^7.14.5" - -"@babel/plugin-transform-arrow-functions@^7.14.5": - version "7.14.5" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-arrow-functions/-/plugin-transform-arrow-functions-7.14.5.tgz#f7187d9588a768dd080bf4c9ffe117ea62f7862a" - integrity sha512-KOnO0l4+tD5IfOdi4x8C1XmEIRWUjNRV8wc6K2vz/3e8yAOoZZvsRXRRIF/yo/MAOFb4QjtAw9xSxMXbSMRy8A== - dependencies: - "@babel/helper-plugin-utils" "^7.14.5" - -"@babel/plugin-transform-async-to-generator@^7.14.5": - version "7.14.5" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-async-to-generator/-/plugin-transform-async-to-generator-7.14.5.tgz#72c789084d8f2094acb945633943ef8443d39e67" - integrity sha512-szkbzQ0mNk0rpu76fzDdqSyPu0MuvpXgC+6rz5rpMb5OIRxdmHfQxrktL8CYolL2d8luMCZTR0DpIMIdL27IjA== - dependencies: - "@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@^7.14.5": - version "7.14.5" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-block-scoped-functions/-/plugin-transform-block-scoped-functions-7.14.5.tgz#e48641d999d4bc157a67ef336aeb54bc44fd3ad4" - integrity sha512-dtqWqdWZ5NqBX3KzsVCWfQI3A53Ft5pWFCT2eCVUftWZgjc5DpDponbIF1+c+7cSGk2wN0YK7HGL/ezfRbpKBQ== - dependencies: - "@babel/helper-plugin-utils" "^7.14.5" - -"@babel/plugin-transform-block-scoping@^7.15.3": - version "7.15.3" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-block-scoping/-/plugin-transform-block-scoping-7.15.3.tgz#94c81a6e2fc230bcce6ef537ac96a1e4d2b3afaf" - integrity sha512-nBAzfZwZb4DkaGtOes1Up1nOAp9TDRRFw4XBzBBSG9QK7KVFmYzgj9o9sbPv7TX5ofL4Auq4wZnxCoPnI/lz2Q== - dependencies: - "@babel/helper-plugin-utils" "^7.14.5" - -"@babel/plugin-transform-classes@^7.15.4": - version "7.15.4" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-classes/-/plugin-transform-classes-7.15.4.tgz#50aee17aaf7f332ae44e3bce4c2e10534d5d3bf1" - integrity sha512-Yjvhex8GzBmmPQUvpXRPWQ9WnxXgAFuZSrqOK/eJlOGIXwvv8H3UEdUigl1gb/bnjTrln+e8bkZUYCBt/xYlBg== - dependencies: - "@babel/helper-annotate-as-pure" "^7.15.4" - "@babel/helper-function-name" "^7.15.4" - "@babel/helper-optimise-call-expression" "^7.15.4" - "@babel/helper-plugin-utils" "^7.14.5" - "@babel/helper-replace-supers" "^7.15.4" - "@babel/helper-split-export-declaration" "^7.15.4" - globals "^11.1.0" - -"@babel/plugin-transform-computed-properties@^7.14.5": - version "7.14.5" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-computed-properties/-/plugin-transform-computed-properties-7.14.5.tgz#1b9d78987420d11223d41195461cc43b974b204f" - integrity sha512-pWM+E4283UxaVzLb8UBXv4EIxMovU4zxT1OPnpHJcmnvyY9QbPPTKZfEj31EUvG3/EQRbYAGaYEUZ4yWOBC2xg== - dependencies: - "@babel/helper-plugin-utils" "^7.14.5" - -"@babel/plugin-transform-destructuring@^7.14.7": - version "7.14.7" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-destructuring/-/plugin-transform-destructuring-7.14.7.tgz#0ad58ed37e23e22084d109f185260835e5557576" - integrity sha512-0mDE99nK+kVh3xlc5vKwB6wnP9ecuSj+zQCa/n0voENtP/zymdT4HH6QEb65wjjcbqr1Jb/7z9Qp7TF5FtwYGw== - dependencies: - "@babel/helper-plugin-utils" "^7.14.5" - -"@babel/plugin-transform-dotall-regex@^7.14.5", "@babel/plugin-transform-dotall-regex@^7.4.4": - version "7.14.5" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-dotall-regex/-/plugin-transform-dotall-regex-7.14.5.tgz#2f6bf76e46bdf8043b4e7e16cf24532629ba0c7a" - integrity sha512-loGlnBdj02MDsFaHhAIJzh7euK89lBrGIdM9EAtHFo6xKygCUGuuWe07o1oZVk287amtW1n0808sQM99aZt3gw== - dependencies: - "@babel/helper-create-regexp-features-plugin" "^7.14.5" - "@babel/helper-plugin-utils" "^7.14.5" - -"@babel/plugin-transform-duplicate-keys@^7.14.5": - version "7.14.5" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-duplicate-keys/-/plugin-transform-duplicate-keys-7.14.5.tgz#365a4844881bdf1501e3a9f0270e7f0f91177954" - integrity sha512-iJjbI53huKbPDAsJ8EmVmvCKeeq21bAze4fu9GBQtSLqfvzj2oRuHVx4ZkDwEhg1htQ+5OBZh/Ab0XDf5iBZ7A== - dependencies: - "@babel/helper-plugin-utils" "^7.14.5" - -"@babel/plugin-transform-exponentiation-operator@^7.14.5": - version "7.14.5" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-exponentiation-operator/-/plugin-transform-exponentiation-operator-7.14.5.tgz#5154b8dd6a3dfe6d90923d61724bd3deeb90b493" - integrity sha512-jFazJhMBc9D27o9jDnIE5ZErI0R0m7PbKXVq77FFvqFbzvTMuv8jaAwLZ5PviOLSFttqKIW0/wxNSDbjLk0tYA== - dependencies: - "@babel/helper-builder-binary-assignment-operator-visitor" "^7.14.5" - "@babel/helper-plugin-utils" "^7.14.5" - -"@babel/plugin-transform-for-of@^7.15.4": - version "7.15.4" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-for-of/-/plugin-transform-for-of-7.15.4.tgz#25c62cce2718cfb29715f416e75d5263fb36a8c2" - integrity sha512-DRTY9fA751AFBDh2oxydvVm4SYevs5ILTWLs6xKXps4Re/KG5nfUkr+TdHCrRWB8C69TlzVgA9b3RmGWmgN9LA== - dependencies: - "@babel/helper-plugin-utils" "^7.14.5" - -"@babel/plugin-transform-function-name@^7.14.5": - version "7.14.5" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-function-name/-/plugin-transform-function-name-7.14.5.tgz#e81c65ecb900746d7f31802f6bed1f52d915d6f2" - integrity sha512-vbO6kv0fIzZ1GpmGQuvbwwm+O4Cbm2NrPzwlup9+/3fdkuzo1YqOZcXw26+YUJB84Ja7j9yURWposEHLYwxUfQ== - dependencies: - "@babel/helper-function-name" "^7.14.5" - "@babel/helper-plugin-utils" "^7.14.5" - -"@babel/plugin-transform-literals@^7.14.5": - version "7.14.5" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-literals/-/plugin-transform-literals-7.14.5.tgz#41d06c7ff5d4d09e3cf4587bd3ecf3930c730f78" - integrity sha512-ql33+epql2F49bi8aHXxvLURHkxJbSmMKl9J5yHqg4PLtdE6Uc48CH1GS6TQvZ86eoB/ApZXwm7jlA+B3kra7A== - dependencies: - "@babel/helper-plugin-utils" "^7.14.5" - -"@babel/plugin-transform-member-expression-literals@^7.14.5": - version "7.14.5" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-member-expression-literals/-/plugin-transform-member-expression-literals-7.14.5.tgz#b39cd5212a2bf235a617d320ec2b48bcc091b8a7" - integrity sha512-WkNXxH1VXVTKarWFqmso83xl+2V3Eo28YY5utIkbsmXoItO8Q3aZxN4BTS2k0hz9dGUloHK26mJMyQEYfkn/+Q== - dependencies: - "@babel/helper-plugin-utils" "^7.14.5" - -"@babel/plugin-transform-modules-amd@^7.14.5": - version "7.14.5" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-modules-amd/-/plugin-transform-modules-amd-7.14.5.tgz#4fd9ce7e3411cb8b83848480b7041d83004858f7" - integrity sha512-3lpOU8Vxmp3roC4vzFpSdEpGUWSMsHFreTWOMMLzel2gNGfHE5UWIh/LN6ghHs2xurUp4jRFYMUIZhuFbody1g== - dependencies: - "@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@^7.15.4": - version "7.15.4" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-modules-commonjs/-/plugin-transform-modules-commonjs-7.15.4.tgz#8201101240eabb5a76c08ef61b2954f767b6b4c1" - integrity sha512-qg4DPhwG8hKp4BbVDvX1s8cohM8a6Bvptu4l6Iingq5rW+yRUAhe/YRup/YcW2zCOlrysEWVhftIcKzrEZv3sA== - dependencies: - "@babel/helper-module-transforms" "^7.15.4" - "@babel/helper-plugin-utils" "^7.14.5" - "@babel/helper-simple-access" "^7.15.4" - babel-plugin-dynamic-import-node "^2.3.3" - -"@babel/plugin-transform-modules-systemjs@^7.15.4": - version "7.15.4" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-modules-systemjs/-/plugin-transform-modules-systemjs-7.15.4.tgz#b42890c7349a78c827719f1d2d0cd38c7d268132" - integrity sha512-fJUnlQrl/mezMneR72CKCgtOoahqGJNVKpompKwzv3BrEXdlPspTcyxrZ1XmDTIr9PpULrgEQo3qNKp6dW7ssw== - dependencies: - "@babel/helper-hoist-variables" "^7.15.4" - "@babel/helper-module-transforms" "^7.15.4" - "@babel/helper-plugin-utils" "^7.14.5" - "@babel/helper-validator-identifier" "^7.14.9" - babel-plugin-dynamic-import-node "^2.3.3" - -"@babel/plugin-transform-modules-umd@^7.14.5": - version "7.14.5" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-modules-umd/-/plugin-transform-modules-umd-7.14.5.tgz#fb662dfee697cce274a7cda525190a79096aa6e0" - integrity sha512-RfPGoagSngC06LsGUYyM9QWSXZ8MysEjDJTAea1lqRjNECE3y0qIJF/qbvJxc4oA4s99HumIMdXOrd+TdKaAAA== - dependencies: - "@babel/helper-module-transforms" "^7.14.5" - "@babel/helper-plugin-utils" "^7.14.5" - -"@babel/plugin-transform-named-capturing-groups-regex@^7.14.9": - version "7.14.9" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-named-capturing-groups-regex/-/plugin-transform-named-capturing-groups-regex-7.14.9.tgz#c68f5c5d12d2ebaba3762e57c2c4f6347a46e7b2" - integrity sha512-l666wCVYO75mlAtGFfyFwnWmIXQm3kSH0C3IRnJqWcZbWkoihyAdDhFm2ZWaxWTqvBvhVFfJjMRQ0ez4oN1yYA== - dependencies: - "@babel/helper-create-regexp-features-plugin" "^7.14.5" - -"@babel/plugin-transform-new-target@^7.14.5": - version "7.14.5" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-new-target/-/plugin-transform-new-target-7.14.5.tgz#31bdae8b925dc84076ebfcd2a9940143aed7dbf8" - integrity sha512-Nx054zovz6IIRWEB49RDRuXGI4Gy0GMgqG0cII9L3MxqgXz/+rgII+RU58qpo4g7tNEx1jG7rRVH4ihZoP4esQ== - dependencies: - "@babel/helper-plugin-utils" "^7.14.5" - -"@babel/plugin-transform-object-super@^7.14.5": - version "7.14.5" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-object-super/-/plugin-transform-object-super-7.14.5.tgz#d0b5faeac9e98597a161a9cf78c527ed934cdc45" - integrity sha512-MKfOBWzK0pZIrav9z/hkRqIk/2bTv9qvxHzPQc12RcVkMOzpIKnFCNYJip00ssKWYkd8Sf5g0Wr7pqJ+cmtuFg== - dependencies: - "@babel/helper-plugin-utils" "^7.14.5" - "@babel/helper-replace-supers" "^7.14.5" - -"@babel/plugin-transform-parameters@^7.15.4": - version "7.15.4" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-parameters/-/plugin-transform-parameters-7.15.4.tgz#5f2285cc3160bf48c8502432716b48504d29ed62" - integrity sha512-9WB/GUTO6lvJU3XQsSr6J/WKvBC2hcs4Pew8YxZagi6GkTdniyqp8On5kqdK8MN0LMeu0mGbhPN+O049NV/9FQ== - dependencies: - "@babel/helper-plugin-utils" "^7.14.5" - -"@babel/plugin-transform-property-literals@^7.14.5": - version "7.14.5" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-property-literals/-/plugin-transform-property-literals-7.14.5.tgz#0ddbaa1f83db3606f1cdf4846fa1dfb473458b34" - integrity sha512-r1uilDthkgXW8Z1vJz2dKYLV1tuw2xsbrp3MrZmD99Wh9vsfKoob+JTgri5VUb/JqyKRXotlOtwgu4stIYCmnw== - dependencies: - "@babel/helper-plugin-utils" "^7.14.5" - -"@babel/plugin-transform-regenerator@^7.14.5": - version "7.14.5" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-regenerator/-/plugin-transform-regenerator-7.14.5.tgz#9676fd5707ed28f522727c5b3c0aa8544440b04f" - integrity sha512-NVIY1W3ITDP5xQl50NgTKlZ0GrotKtLna08/uGY6ErQt6VEQZXla86x/CTddm5gZdcr+5GSsvMeTmWA5Ii6pkg== - dependencies: - regenerator-transform "^0.14.2" - -"@babel/plugin-transform-reserved-words@^7.14.5": - version "7.14.5" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-reserved-words/-/plugin-transform-reserved-words-7.14.5.tgz#c44589b661cfdbef8d4300dcc7469dffa92f8304" - integrity sha512-cv4F2rv1nD4qdexOGsRQXJrOcyb5CrgjUH9PKrrtyhSDBNWGxd0UIitjyJiWagS+EbUGjG++22mGH1Pub8D6Vg== - dependencies: - "@babel/helper-plugin-utils" "^7.14.5" - -"@babel/plugin-transform-shorthand-properties@^7.14.5": - version "7.14.5" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-shorthand-properties/-/plugin-transform-shorthand-properties-7.14.5.tgz#97f13855f1409338d8cadcbaca670ad79e091a58" - integrity sha512-xLucks6T1VmGsTB+GWK5Pl9Jl5+nRXD1uoFdA5TSO6xtiNjtXTjKkmPdFXVLGlK5A2/or/wQMKfmQ2Y0XJfn5g== - dependencies: - "@babel/helper-plugin-utils" "^7.14.5" - -"@babel/plugin-transform-spread@^7.14.6": - version "7.14.6" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-spread/-/plugin-transform-spread-7.14.6.tgz#6bd40e57fe7de94aa904851963b5616652f73144" - integrity sha512-Zr0x0YroFJku7n7+/HH3A2eIrGMjbmAIbJSVv0IZ+t3U2WUQUA64S/oeied2e+MaGSjmt4alzBCsK9E8gh+fag== - dependencies: - "@babel/helper-plugin-utils" "^7.14.5" - "@babel/helper-skip-transparent-expression-wrappers" "^7.14.5" - -"@babel/plugin-transform-sticky-regex@^7.14.5": - version "7.14.5" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-sticky-regex/-/plugin-transform-sticky-regex-7.14.5.tgz#5b617542675e8b7761294381f3c28c633f40aeb9" - integrity sha512-Z7F7GyvEMzIIbwnziAZmnSNpdijdr4dWt+FJNBnBLz5mwDFkqIXU9wmBcWWad3QeJF5hMTkRe4dAq2sUZiG+8A== - dependencies: - "@babel/helper-plugin-utils" "^7.14.5" - -"@babel/plugin-transform-template-literals@^7.14.5": - version "7.14.5" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-template-literals/-/plugin-transform-template-literals-7.14.5.tgz#a5f2bc233937d8453885dc736bdd8d9ffabf3d93" - integrity sha512-22btZeURqiepOfuy/VkFr+zStqlujWaarpMErvay7goJS6BWwdd6BY9zQyDLDa4x2S3VugxFb162IZ4m/S/+Gg== - dependencies: - "@babel/helper-plugin-utils" "^7.14.5" - -"@babel/plugin-transform-typeof-symbol@^7.14.5": - version "7.14.5" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-typeof-symbol/-/plugin-transform-typeof-symbol-7.14.5.tgz#39af2739e989a2bd291bf6b53f16981423d457d4" - integrity sha512-lXzLD30ffCWseTbMQzrvDWqljvZlHkXU+CnseMhkMNqU1sASnCsz3tSzAaH3vCUXb9PHeUb90ZT1BdFTm1xxJw== - dependencies: - "@babel/helper-plugin-utils" "^7.14.5" - -"@babel/plugin-transform-unicode-escapes@^7.14.5": - version "7.14.5" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-unicode-escapes/-/plugin-transform-unicode-escapes-7.14.5.tgz#9d4bd2a681e3c5d7acf4f57fa9e51175d91d0c6b" - integrity sha512-crTo4jATEOjxj7bt9lbYXcBAM3LZaUrbP2uUdxb6WIorLmjNKSpHfIybgY4B8SRpbf8tEVIWH3Vtm7ayCrKocA== - dependencies: - "@babel/helper-plugin-utils" "^7.14.5" - -"@babel/plugin-transform-unicode-regex@^7.14.5": - version "7.14.5" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-unicode-regex/-/plugin-transform-unicode-regex-7.14.5.tgz#4cd09b6c8425dd81255c7ceb3fb1836e7414382e" - integrity sha512-UygduJpC5kHeCiRw/xDVzC+wj8VaYSoKl5JNVmbP7MadpNinAm3SvZCxZ42H37KZBKztz46YC73i9yV34d0Tzw== - dependencies: - "@babel/helper-create-regexp-features-plugin" "^7.14.5" - "@babel/helper-plugin-utils" "^7.14.5" - -"@babel/preset-env@^7.8.4": - version "7.15.6" - resolved "https://registry.yarnpkg.com/@babel/preset-env/-/preset-env-7.15.6.tgz#0f3898db9d63d320f21b17380d8462779de57659" - integrity sha512-L+6jcGn7EWu7zqaO2uoTDjjMBW+88FXzV8KvrBl2z6MtRNxlsmUNRlZPaNNPUTgqhyC5DHNFk/2Jmra+ublZWw== - dependencies: - "@babel/compat-data" "^7.15.0" - "@babel/helper-compilation-targets" "^7.15.4" - "@babel/helper-plugin-utils" "^7.14.5" - "@babel/helper-validator-option" "^7.14.5" - "@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining" "^7.15.4" - "@babel/plugin-proposal-async-generator-functions" "^7.15.4" - "@babel/plugin-proposal-class-properties" "^7.14.5" - "@babel/plugin-proposal-class-static-block" "^7.15.4" - "@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.15.6" - "@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.15.4" - "@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.15.3" - "@babel/plugin-transform-classes" "^7.15.4" - "@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.15.4" - "@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.15.4" - "@babel/plugin-transform-modules-systemjs" "^7.15.4" - "@babel/plugin-transform-modules-umd" "^7.14.5" - "@babel/plugin-transform-named-capturing-groups-regex" "^7.14.9" - "@babel/plugin-transform-new-target" "^7.14.5" - "@babel/plugin-transform-object-super" "^7.14.5" - "@babel/plugin-transform-parameters" "^7.15.4" - "@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.15.6" - 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.16.0" - semver "^6.3.0" - -"@babel/preset-modules@^0.1.4": - version "0.1.4" - resolved "https://registry.yarnpkg.com/@babel/preset-modules/-/preset-modules-0.1.4.tgz#362f2b68c662842970fdb5e254ffc8fc1c2e415e" - integrity sha512-J36NhwnfdzpmH41M1DrnkkgAqhZaqr/NBdPfQ677mLzlaXo+oDiv1deyCDtgAhz8p328otdob0Du7+xgHGZbKg== - dependencies: - "@babel/helper-plugin-utils" "^7.0.0" - "@babel/plugin-proposal-unicode-property-regex" "^7.4.4" - "@babel/plugin-transform-dotall-regex" "^7.4.4" - "@babel/types" "^7.4.4" - esutils "^2.0.2" - -"@babel/runtime-corejs3@^7.10.2": - version "7.15.4" - resolved "https://registry.yarnpkg.com/@babel/runtime-corejs3/-/runtime-corejs3-7.15.4.tgz#403139af262b9a6e8f9ba04a6fdcebf8de692bf1" - integrity sha512-lWcAqKeB624/twtTc3w6w/2o9RqJPaNBhPGK6DKLSiwuVWC7WFkypWyNg+CpZoyJH0jVzv1uMtXZ/5/lQOLtCg== - dependencies: - core-js-pure "^3.16.0" - regenerator-runtime "^0.13.4" - -"@babel/runtime@^7.10.2", "@babel/runtime@^7.8.4": - version "7.15.4" - resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.15.4.tgz#fd17d16bfdf878e6dd02d19753a39fa8a8d9c84a" - integrity sha512-99catp6bHCaxr4sJ/DbTGgHS4+Rs2RVd2g7iOap6SLGPDknRK9ztKNsE/Fg6QhSeh1FGE5f6gHGQmvvn3I3xhw== - dependencies: - regenerator-runtime "^0.13.4" - -"@babel/template@^7.10.4", "@babel/template@^7.15.4": - version "7.15.4" - resolved "https://registry.yarnpkg.com/@babel/template/-/template-7.15.4.tgz#51898d35dcf3faa670c4ee6afcfd517ee139f194" - integrity sha512-UgBAfEa1oGuYgDIPM2G+aHa4Nlo9Lh6mGD2bDBGMTbYnc38vulXPuC1MGjYILIEmlwl6Rd+BPR9ee3gm20CBtg== - dependencies: - "@babel/code-frame" "^7.14.5" - "@babel/parser" "^7.15.4" - "@babel/types" "^7.15.4" - -"@babel/traverse@^7.10.5", "@babel/traverse@^7.12.1", "@babel/traverse@^7.13.0", "@babel/traverse@^7.15.4": - version "7.15.4" - resolved "https://registry.yarnpkg.com/@babel/traverse/-/traverse-7.15.4.tgz#ff8510367a144bfbff552d9e18e28f3e2889c22d" - integrity sha512-W6lQD8l4rUbQR/vYgSuCAE75ADyyQvOpFVsvPPdkhf6lATXAsQIG9YdtOcu8BB1dZ0LKu+Zo3c1wEcbKeuhdlA== - dependencies: - "@babel/code-frame" "^7.14.5" - "@babel/generator" "^7.15.4" - "@babel/helper-function-name" "^7.15.4" - "@babel/helper-hoist-variables" "^7.15.4" - "@babel/helper-split-export-declaration" "^7.15.4" - "@babel/parser" "^7.15.4" - "@babel/types" "^7.15.4" - debug "^4.1.0" - globals "^11.1.0" - -"@babel/types@^7.12.1", "@babel/types@^7.15.4", "@babel/types@^7.15.6", "@babel/types@^7.4.4": - version "7.15.6" - resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.15.6.tgz#99abdc48218b2881c058dd0a7ab05b99c9be758f" - integrity sha512-BPU+7QhqNjmWyDO0/vitH/CuhpV8ZmK1wpKva8nuyNF5MJfuRNWMc+hc14+u9xT93kvykMdncrJT19h74uB1Ig== - dependencies: - "@babel/helper-validator-identifier" "^7.14.9" - to-fast-properties "^2.0.0" - -"@eslint/eslintrc@^0.4.3": - version "0.4.3" - resolved "https://registry.yarnpkg.com/@eslint/eslintrc/-/eslintrc-0.4.3.tgz#9e42981ef035beb3dd49add17acb96e8ff6f394c" - integrity sha512-J6KFFz5QCYUJq3pf0mjEcCJVERbzv71PUIDczuh9JkwGEzced6CO5ADLHB1rbf/+oPBtoPfMYNOpGDzCANlbXw== - dependencies: - ajv "^6.12.4" - debug "^4.1.1" - espree "^7.3.0" - globals "^13.9.0" - ignore "^4.0.6" - import-fresh "^3.2.1" - js-yaml "^3.13.1" - minimatch "^3.0.4" - strip-json-comments "^3.1.1" - -"@gulp-sourcemaps/identity-map@^2.0.1": - version "2.0.1" - resolved "https://registry.yarnpkg.com/@gulp-sourcemaps/identity-map/-/identity-map-2.0.1.tgz#a6e8b1abec8f790ec6be2b8c500e6e68037c0019" - integrity sha512-Tb+nSISZku+eQ4X1lAkevcQa+jknn/OVUgZ3XCxEKIsLsqYuPoJwJOPQeaOk75X3WPftb29GWY1eqE7GLsXb1Q== - dependencies: - acorn "^6.4.1" - normalize-path "^3.0.0" - postcss "^7.0.16" - source-map "^0.6.0" - through2 "^3.0.1" - -"@gulp-sourcemaps/map-sources@^1.0.0": - version "1.0.0" - resolved "https://registry.yarnpkg.com/@gulp-sourcemaps/map-sources/-/map-sources-1.0.0.tgz#890ae7c5d8c877f6d384860215ace9d7ec945bda" - integrity sha1-iQrnxdjId/bThIYCFazp1+yUW9o= - dependencies: - normalize-path "^2.0.1" - through2 "^2.0.3" - -"@humanwhocodes/config-array@^0.5.0": - version "0.5.0" - resolved "https://registry.yarnpkg.com/@humanwhocodes/config-array/-/config-array-0.5.0.tgz#1407967d4c6eecd7388f83acf1eaf4d0c6e58ef9" - integrity sha512-FagtKFz74XrTl7y6HCzQpwDfXP0yhxe9lHLD1UZxjvZIcbyRz8zTFF/yYNfSfzU414eDwZ1SrO0Qvtyf+wFMQg== - dependencies: - "@humanwhocodes/object-schema" "^1.2.0" - debug "^4.1.1" - minimatch "^3.0.4" - -"@humanwhocodes/object-schema@^1.2.0": - version "1.2.0" - resolved "https://registry.yarnpkg.com/@humanwhocodes/object-schema/-/object-schema-1.2.0.tgz#87de7af9c231826fdd68ac7258f77c429e0e5fcf" - integrity sha512-wdppn25U8z/2yiaT6YGquE6X8sSv7hNMWSXYSSU1jGv/yd6XqjXgTDJ8KP4NgjTXfJ3GbRjeeb8RTV7a/VpM+w== - -"@hutson/parse-repository-url@^3.0.0": - version "3.0.2" - resolved "https://registry.yarnpkg.com/@hutson/parse-repository-url/-/parse-repository-url-3.0.2.tgz#98c23c950a3d9b6c8f0daed06da6c3af06981340" - integrity sha512-H9XAx3hc0BQHY6l+IFSWHDySypcXsvsuLhgYLUGywmJ5pswRVQJUHpOsobnLYp2ZUaUlKiKDrgWWhosOwAEM8Q== - -"@istanbuljs/schema@^0.1.2": - version "0.1.3" - resolved "https://registry.yarnpkg.com/@istanbuljs/schema/-/schema-0.1.3.tgz#e45e384e4b8ec16bce2fd903af78450f6bf7ec98" - integrity sha512-ZXRY4jNvVgSVQ8DL3LTcakaAtXwTVUxE81hslsyD2AtoXW/wVob10HkOJ1X/pAlcI7D+2YoZKg5do8G/w6RYgA== - -"@jest/types@^27.2.4": - version "27.2.4" - resolved "https://registry.yarnpkg.com/@jest/types/-/types-27.2.4.tgz#2430042a66e00dc5b140c3636f4474d464c21ee8" - integrity sha512-IDO2ezTxeMvQAHxzG/ZvEyA47q0aVfzT95rGFl7bZs/Go0aIucvfDbS2rmnoEdXxlLQhcolmoG/wvL/uKx4tKA== - dependencies: - "@types/istanbul-lib-coverage" "^2.0.0" - "@types/istanbul-reports" "^3.0.0" - "@types/node" "*" - "@types/yargs" "^16.0.0" - chalk "^4.0.0" - -"@jsdevtools/coverage-istanbul-loader@^3.0.3": - version "3.0.5" - resolved "https://registry.yarnpkg.com/@jsdevtools/coverage-istanbul-loader/-/coverage-istanbul-loader-3.0.5.tgz#2a4bc65d0271df8d4435982db4af35d81754ee26" - integrity sha512-EUCPEkaRPvmHjWAAZkWMT7JDzpw7FKB00WTISaiXsbNOd5hCHg77XLA8sLYLFDo1zepYLo2w7GstN8YBqRXZfA== - dependencies: - convert-source-map "^1.7.0" - istanbul-lib-instrument "^4.0.3" - loader-utils "^2.0.0" - merge-source-map "^1.1.0" - schema-utils "^2.7.0" - -"@sindresorhus/is@^4.0.0": - version "4.2.0" - resolved "https://registry.yarnpkg.com/@sindresorhus/is/-/is-4.2.0.tgz#667bfc6186ae7c9e0b45a08960c551437176e1ca" - integrity sha512-VkE3KLBmJwcCaVARtQpfuKcKv8gcBmUubrfHGF84dXuuW6jgsRYxPtzcIhPyK9WAPpRt2/xY6zkD9MnRaJzSyw== - -"@sinonjs/commons@^1", "@sinonjs/commons@^1.3.0", "@sinonjs/commons@^1.7.0": - version "1.8.3" - resolved "https://registry.yarnpkg.com/@sinonjs/commons/-/commons-1.8.3.tgz#3802ddd21a50a949b6721ddd72da36e67e7f1b2d" - integrity sha512-xkNcLAn/wZaX14RPlwizcKicDk9G3F8m2nU3L7Ukm5zBgTwiT0wsoFAHx9Jq56fJA1z/7uKGtCRu16sOUCLIHQ== - dependencies: - type-detect "4.0.8" - -"@sinonjs/formatio@^2.0.0": - version "2.0.0" - resolved "https://registry.yarnpkg.com/@sinonjs/formatio/-/formatio-2.0.0.tgz#84db7e9eb5531df18a8c5e0bfb6e449e55e654b2" - integrity sha512-ls6CAMA6/5gG+O/IdsBcblvnd8qcO/l1TYoNeAzp3wcISOxlPXQEus0mLcdwazEkWjaBdaJ3TaxmNgCLWwvWzg== - dependencies: - samsam "1.3.0" - -"@sinonjs/formatio@^3.2.1": - version "3.2.2" - resolved "https://registry.yarnpkg.com/@sinonjs/formatio/-/formatio-3.2.2.tgz#771c60dfa75ea7f2d68e3b94c7e888a78781372c" - integrity sha512-B8SEsgd8gArBLMD6zpRw3juQ2FVSsmdd7qlevyDqzS9WTCtvF55/gAL+h6gue8ZvPYcdiPdvueM/qm//9XzyTQ== - dependencies: - "@sinonjs/commons" "^1" - "@sinonjs/samsam" "^3.1.0" - -"@sinonjs/samsam@^3.1.0": - version "3.3.3" - resolved "https://registry.yarnpkg.com/@sinonjs/samsam/-/samsam-3.3.3.tgz#46682efd9967b259b81136b9f120fd54585feb4a" - integrity sha512-bKCMKZvWIjYD0BLGnNrxVuw4dkWCYsLqFOUWw8VgKF/+5Y+mE7LfHWPIYoDXowH+3a9LsWDMo0uAP8YDosPvHQ== - dependencies: - "@sinonjs/commons" "^1.3.0" - array-from "^2.1.1" - lodash "^4.17.15" - -"@sinonjs/text-encoding@^0.7.1": - version "0.7.1" - resolved "https://registry.yarnpkg.com/@sinonjs/text-encoding/-/text-encoding-0.7.1.tgz#8da5c6530915653f3a1f38fd5f101d8c3f8079c5" - integrity sha512-+iTbntw2IZPb/anVDbypzfQa+ay64MW0Zo8aJ8gZPWMMK6/OubMVb6lUPMagqjOPnmtauXnFCACVl3O7ogjeqQ== - -"@szmarczak/http-timer@^4.0.5": - version "4.0.6" - resolved "https://registry.yarnpkg.com/@szmarczak/http-timer/-/http-timer-4.0.6.tgz#b4a914bb62e7c272d4e5989fe4440f812ab1d807" - integrity sha512-4BAffykYOgO+5nzBWYwE3W90sBgLJoUPRWWcL8wlyiM8IB8ipJz3UMJ9KXQd1RKQXpKp8Tutn80HZtWsu2u76w== - dependencies: - defer-to-connect "^2.0.0" - -"@types/aria-query@^4.2.1": - version "4.2.2" - resolved "https://registry.yarnpkg.com/@types/aria-query/-/aria-query-4.2.2.tgz#ed4e0ad92306a704f9fb132a0cfcf77486dbe2bc" - integrity sha512-HnYpAE1Y6kRyKM/XkEuiRQhTHvkzMBurTHnpFLYLBGPIylZNPs9jJcuOOYWxPLJCSEtmZT0Y8rHDokKN7rRTig== - -"@types/cacheable-request@^6.0.1": - version "6.0.2" - resolved "https://registry.yarnpkg.com/@types/cacheable-request/-/cacheable-request-6.0.2.tgz#c324da0197de0a98a2312156536ae262429ff6b9" - integrity sha512-B3xVo+dlKM6nnKTcmm5ZtY/OL8bOAOd2Olee9M1zft65ox50OzjEHW91sDiU9j6cvW8Ejg1/Qkf4xd2kugApUA== - dependencies: - "@types/http-cache-semantics" "*" - "@types/keyv" "*" - "@types/node" "*" - "@types/responselike" "*" - -"@types/component-emitter@^1.2.10": - version "1.2.10" - resolved "https://registry.yarnpkg.com/@types/component-emitter/-/component-emitter-1.2.10.tgz#ef5b1589b9f16544642e473db5ea5639107ef3ea" - integrity sha512-bsjleuRKWmGqajMerkzox19aGbscQX5rmmvvXl3wlIp5gMG1HgkiwPxsN5p070fBDKTNSPgojVbuY1+HWMbFhg== - -"@types/cookie@^0.4.0": - version "0.4.1" - resolved "https://registry.yarnpkg.com/@types/cookie/-/cookie-0.4.1.tgz#bfd02c1f2224567676c1545199f87c3a861d878d" - integrity sha512-XW/Aa8APYr6jSVVA1y/DEIZX0/GMKLEVekNG727R8cs56ahETkRAy/3DR7+fJyh7oUgGwNQaRfXCun0+KbWY7Q== - -"@types/cors@^2.8.8": - version "2.8.12" - resolved "https://registry.yarnpkg.com/@types/cors/-/cors-2.8.12.tgz#6b2c510a7ad7039e98e7b8d3d6598f4359e5c080" - integrity sha512-vt+kDhq/M2ayberEtJcIN/hxXy1Pk+59g2FV/ZQceeaTyCtCucjL2Q7FXlFjtWn4n15KCr1NE2lNNFhp0lEThw== - -"@types/diff@^5.0.0": - version "5.0.1" - resolved "https://registry.yarnpkg.com/@types/diff/-/diff-5.0.1.tgz#9c9b9a331d4e41ccccff553f5d7ef964c6cf4042" - integrity sha512-XIpxU6Qdvp1ZE6Kr3yrkv1qgUab0fyf4mHYvW8N3Bx3PCsbN6or1q9/q72cv5jIFWolaGH08U9XyYoLLIykyKQ== - -"@types/easy-table@^0.0.33": - version "0.0.33" - resolved "https://registry.yarnpkg.com/@types/easy-table/-/easy-table-0.0.33.tgz#b1f7ec29014ec24906b4f28d8368e2e99b399313" - integrity sha512-/vvqcJPmZUfQwCgemL0/34G7bIQnCuvgls379ygRlcC1FqNqk3n+VZ15dAO51yl6JNDoWd8vsk+kT8zfZ1VZSw== - -"@types/ejs@^3.0.5": - version "3.1.0" - resolved "https://registry.yarnpkg.com/@types/ejs/-/ejs-3.1.0.tgz#ab8109208106b5e764e5a6c92b2ba1c625b73020" - integrity sha512-DCg+Ka+uDQ31lJ/UtEXVlaeV3d6t81gifaVWKJy4MYVVgvJttyX/viREy+If7fz+tK/gVxTGMtyrFPnm4gjrVA== - -"@types/expect@^1.20.4": - version "1.20.4" - resolved "https://registry.yarnpkg.com/@types/expect/-/expect-1.20.4.tgz#8288e51737bf7e3ab5d7c77bfa695883745264e5" - integrity sha512-Q5Vn3yjTDyCMV50TB6VRIbQNxSE4OmZR86VSbGaNpfUolm0iePBB4KdEEHmxoY5sT2+2DIvXW0rvMDP2nHZ4Mg== - -"@types/fibers@^3.1.0": - version "3.1.1" - resolved "https://registry.yarnpkg.com/@types/fibers/-/fibers-3.1.1.tgz#b714d357eebf6aec0bc5d70512e573b89bc84f20" - integrity sha512-yHoUi46uika0snoTpNcVqUSvgbRndaIps4TUCotrXjtc0DHDoPQckmyXEZ2bX3e4mpJmyEW3hRhCwQa/ISCPaA== - -"@types/fs-extra@^9.0.4": - version "9.0.13" - resolved "https://registry.yarnpkg.com/@types/fs-extra/-/fs-extra-9.0.13.tgz#7594fbae04fe7f1918ce8b3d213f74ff44ac1f45" - integrity sha512-nEnwB++1u5lVDM2UI4c1+5R+FYaKfaAzS4OococimjVm3nQw3TuzH5UNsocrcTBbhnerblyHj4A49qXbIiZdpA== - dependencies: - "@types/node" "*" - -"@types/http-cache-semantics@*": - version "4.0.1" - resolved "https://registry.yarnpkg.com/@types/http-cache-semantics/-/http-cache-semantics-4.0.1.tgz#0ea7b61496902b95890dc4c3a116b60cb8dae812" - integrity sha512-SZs7ekbP8CN0txVG2xVRH6EgKmEm31BOxA07vkFaETzZz1xh+cbt8BcI0slpymvwhx5dlFnQG2rTlPVQn+iRPQ== - -"@types/inquirer@^8.1.2": - version "8.1.3" - resolved "https://registry.yarnpkg.com/@types/inquirer/-/inquirer-8.1.3.tgz#dfda4c97cdbe304e4dceb378a80f79448ea5c8fe" - integrity sha512-AayK4ZL5ssPzR1OtnOLGAwpT0Dda3Xi/h1G0l1oJDNrowp7T1423q4Zb8/emr7tzRlCy4ssEri0LWVexAqHyKQ== - dependencies: - "@types/through" "*" - rxjs "^7.2.0" - -"@types/istanbul-lib-coverage@*", "@types/istanbul-lib-coverage@^2.0.0": - version "2.0.3" - resolved "https://registry.yarnpkg.com/@types/istanbul-lib-coverage/-/istanbul-lib-coverage-2.0.3.tgz#4ba8ddb720221f432e443bd5f9117fd22cfd4762" - integrity sha512-sz7iLqvVUg1gIedBOvlkxPlc8/uVzyS5OwGz1cKjXzkl3FpL3al0crU8YGU1WoHkxn0Wxbw5tyi6hvzJKNzFsw== - -"@types/istanbul-lib-report@*": - version "3.0.0" - resolved "https://registry.yarnpkg.com/@types/istanbul-lib-report/-/istanbul-lib-report-3.0.0.tgz#c14c24f18ea8190c118ee7562b7ff99a36552686" - integrity sha512-plGgXAPfVKFoYfa9NpYDAkseG+g6Jr294RqeqcqDixSbU34MZVJRi/P+7Y8GDpzkEwLaGZZOpKIEmeVZNtKsrg== - dependencies: - "@types/istanbul-lib-coverage" "*" - -"@types/istanbul-reports@^3.0.0": - version "3.0.1" - resolved "https://registry.yarnpkg.com/@types/istanbul-reports/-/istanbul-reports-3.0.1.tgz#9153fe98bba2bd565a63add9436d6f0d7f8468ff" - integrity sha512-c3mAZEuK0lvBp8tmuL74XRKn1+y2dcwOUpH7x4WrF6gk1GIgiluDRgMYQtw2OFcBvAJWlt6ASU3tSqxp0Uu0Aw== - dependencies: - "@types/istanbul-lib-report" "*" - -"@types/json-schema@^7.0.5": - version "7.0.9" - resolved "https://registry.yarnpkg.com/@types/json-schema/-/json-schema-7.0.9.tgz#97edc9037ea0c38585320b28964dde3b39e4660d" - integrity sha512-qcUXuemtEu+E5wZSJHNxUXeCZhAfXKQ41D+duX+VYPde7xyEVZci+/oXKJL13tnRs9lR2pr4fod59GT6/X1/yQ== - -"@types/json5@^0.0.29": - version "0.0.29" - resolved "https://registry.yarnpkg.com/@types/json5/-/json5-0.0.29.tgz#ee28707ae94e11d2b827bcbe5270bcea7f3e71ee" - integrity sha1-7ihweulOEdK4J7y+UnC86n8+ce4= - -"@types/keyv@*": - version "3.1.3" - resolved "https://registry.yarnpkg.com/@types/keyv/-/keyv-3.1.3.tgz#1c9aae32872ec1f20dcdaee89a9f3ba88f465e41" - integrity sha512-FXCJgyyN3ivVgRoml4h94G/p3kY+u/B86La+QptcqJaWtBWtmc6TtkNfS40n9bIvyLteHh7zXOtgbobORKPbDg== - dependencies: - "@types/node" "*" - -"@types/lodash.flattendeep@^4.4.6": - version "4.4.6" - resolved "https://registry.yarnpkg.com/@types/lodash.flattendeep/-/lodash.flattendeep-4.4.6.tgz#2686d9161ae6c3d56d6745fa118308d88562ae53" - integrity sha512-uLm2MaRVlqJSGsMK0RZpP5T3KqReq+9WbYDHCUhBhp98v56hMG/Yht52bsoTSui9xz2mUvQ9NfG3LrNGDL92Ng== - dependencies: - "@types/lodash" "*" - -"@types/lodash.pickby@^4.6.6": - version "4.6.6" - resolved "https://registry.yarnpkg.com/@types/lodash.pickby/-/lodash.pickby-4.6.6.tgz#3dc39c2b38432f7a0c5e5627b0d5c0e3878b4f35" - integrity sha512-NFa13XxlMd9eFi0UFZFWIztpMpXhozbijrx3Yb1viYZphT7jyopIFVoIRF4eYMjruWNEG1rnyrRmg/8ej9T8Iw== - dependencies: - "@types/lodash" "*" - -"@types/lodash.union@^4.6.6": - version "4.6.6" - resolved "https://registry.yarnpkg.com/@types/lodash.union/-/lodash.union-4.6.6.tgz#2f77f2088326ed147819e9e384182b99aae8d4b0" - integrity sha512-Wu0ZEVNcyCz8eAn6TlUbYWZoGbH9E+iOHxAZbwUoCEXdUiy6qpcz5o44mMXViM4vlPLLCPlkAubEP1gokoSZaw== - dependencies: - "@types/lodash" "*" - -"@types/lodash@*": - version "4.14.175" - resolved "https://registry.yarnpkg.com/@types/lodash/-/lodash-4.14.175.tgz#b78dfa959192b01fae0ad90e166478769b215f45" - integrity sha512-XmdEOrKQ8a1Y/yxQFOMbC47G/V2VDO1GvMRnl4O75M4GW/abC5tnfzadQYkqEveqRM1dEJGFFegfPNA2vvx2iw== - -"@types/mdast@^3.0.0", "@types/mdast@^3.0.3": - version "3.0.10" - resolved "https://registry.yarnpkg.com/@types/mdast/-/mdast-3.0.10.tgz#4724244a82a4598884cbbe9bcfd73dff927ee8af" - integrity sha512-W864tg/Osz1+9f4lrGTZpCSO5/z4608eUp19tbozkq2HJK6i3z1kT0H9tlADXuYIb1YYOBByU4Jsqkk75q48qA== - dependencies: - "@types/unist" "*" - -"@types/minimist@^1.2.0": - version "1.2.2" - resolved "https://registry.yarnpkg.com/@types/minimist/-/minimist-1.2.2.tgz#ee771e2ba4b3dc5b372935d549fd9617bf345b8c" - integrity sha512-jhuKLIRrhvCPLqwPcx6INqmKeiA5EWrsCOPhrlFSrbrmU4ZMPjj5Ul/oLCMDO98XRUIwVm78xICz4EPCektzeQ== - -"@types/mocha@^9.0.0": - version "9.0.0" - resolved "https://registry.yarnpkg.com/@types/mocha/-/mocha-9.0.0.tgz#3205bcd15ada9bc681ac20bef64e9e6df88fd297" - integrity sha512-scN0hAWyLVAvLR9AyW7HoFF5sJZglyBsbPuHO4fv7JRvfmPBMfp1ozWqOf/e4wwPNxezBZXRfWzMb6iFLgEVRA== - -"@types/node@*", "@types/node@>=10.0.0": - version "16.10.2" - resolved "https://registry.yarnpkg.com/@types/node/-/node-16.10.2.tgz#5764ca9aa94470adb4e1185fe2e9f19458992b2e" - integrity sha512-zCclL4/rx+W5SQTzFs9wyvvyCwoK9QtBpratqz2IYJ3O8Umrn0m3nsTv0wQBk9sRGpvUe9CwPDrQFB10f1FIjQ== - -"@types/node@^14.14.41": - version "14.17.20" - resolved "https://registry.yarnpkg.com/@types/node/-/node-14.17.20.tgz#74cc80438fd0467dc4377ee5bbad89a886df3c10" - integrity sha512-gI5Sl30tmhXsqkNvopFydP7ASc4c2cLfGNQrVKN3X90ADFWFsPEsotm/8JHSUJQKTHbwowAHtcJPeyVhtKv0TQ== - -"@types/node@^15.12.5": - version "15.14.9" - resolved "https://registry.yarnpkg.com/@types/node/-/node-15.14.9.tgz#bc43c990c3c9be7281868bbc7b8fdd6e2b57adfa" - integrity sha512-qjd88DrCxupx/kJD5yQgZdcYKZKSIGBVDIBE1/LTGcNm3d2Np/jxojkdePDdfnBHJc5W7vSMpbJ1aB7p/Py69A== - -"@types/normalize-package-data@^2.4.0": - version "2.4.1" - resolved "https://registry.yarnpkg.com/@types/normalize-package-data/-/normalize-package-data-2.4.1.tgz#d3357479a0fdfdd5907fe67e17e0a85c906e1301" - integrity sha512-Gj7cI7z+98M282Tqmp2K5EIsoouUEzbBJhQQzDE3jSIRk6r9gsz0oUokqIUR4u1R3dMHo0pDHM7sNOHyhulypw== - -"@types/object-inspect@^1.8.0": - version "1.8.1" - resolved "https://registry.yarnpkg.com/@types/object-inspect/-/object-inspect-1.8.1.tgz#7c08197ad05cc0e513f529b1f3919cc99f720e1f" - integrity sha512-0JTdf3CGV0oWzE6Wa40Ayv2e2GhpP3pEJMcrlM74vBSJPuuNkVwfDnl0SZxyFCXETcB4oKA/MpTVfuYSMOelBg== - -"@types/puppeteer@^5.4.0": - version "5.4.4" - resolved "https://registry.yarnpkg.com/@types/puppeteer/-/puppeteer-5.4.4.tgz#e92abeccc4f46207c3e1b38934a1246be080ccd0" - integrity sha512-3Nau+qi69CN55VwZb0ATtdUAlYlqOOQ3OfQfq0Hqgc4JMFXiQT/XInlwQ9g6LbicDslE6loIFsXFklGh5XmI6Q== - dependencies: - "@types/node" "*" - -"@types/recursive-readdir@^2.2.0": - version "2.2.0" - resolved "https://registry.yarnpkg.com/@types/recursive-readdir/-/recursive-readdir-2.2.0.tgz#b39cd5474fd58ea727fe434d5c68b7a20ba9121c" - integrity sha512-HGk753KRu2N4mWduovY4BLjYq4jTOL29gV2OfGdGxHcPSWGFkC5RRIdk+VTs5XmYd7MVAD+JwKrcb5+5Y7FOCg== - dependencies: - "@types/node" "*" - -"@types/responselike@*", "@types/responselike@^1.0.0": - version "1.0.0" - resolved "https://registry.yarnpkg.com/@types/responselike/-/responselike-1.0.0.tgz#251f4fe7d154d2bad125abe1b429b23afd262e29" - integrity sha512-85Y2BjiufFzaMIlvJDvTTB8Fxl2xfLo4HgmHzVBz08w4wDePCTjYw66PdrolO0kzli3yam/YCgRufyo1DdQVTA== - dependencies: - "@types/node" "*" - -"@types/stack-utils@^2.0.0": - version "2.0.1" - resolved "https://registry.yarnpkg.com/@types/stack-utils/-/stack-utils-2.0.1.tgz#20f18294f797f2209b5f65c8e3b5c8e8261d127c" - integrity sha512-Hl219/BT5fLAaz6NDkSuhzasy49dwQS/DSdu4MdggFB8zcXv7vflBI3xp7FEmkmdDkBUI2bPUNeMttp2knYdxw== - -"@types/stream-buffers@^3.0.3": - version "3.0.4" - resolved "https://registry.yarnpkg.com/@types/stream-buffers/-/stream-buffers-3.0.4.tgz#bf128182da7bc62722ca0ddf5458a9c65f76e648" - integrity sha512-qU/K1tb2yUdhXkLIATzsIPwbtX6BpZk0l3dPW6xqWyhfzzM1ECaQ/8faEnu3CNraLiQ9LHyQQPBGp7N9Fbs25w== - dependencies: - "@types/node" "*" - -"@types/supports-color@^8.1.0": - version "8.1.1" - resolved "https://registry.yarnpkg.com/@types/supports-color/-/supports-color-8.1.1.tgz#1b44b1b096479273adf7f93c75fc4ecc40a61ee4" - integrity sha512-dPWnWsf+kzIG140B8z2w3fr5D03TLWbOAFQl45xUpI3vcizeXriNR5VYkWZ+WTMsUHqZ9Xlt3hrxGNANFyNQfw== - -"@types/through@*": - version "0.0.30" - resolved "https://registry.yarnpkg.com/@types/through/-/through-0.0.30.tgz#e0e42ce77e897bd6aead6f6ea62aeb135b8a3895" - integrity sha512-FvnCJljyxhPM3gkRgWmxmDZyAQSiBQQWLI0A0VFL0K7W1oRUrPJSqNO0NvTnLkBcotdlp3lKvaT0JrnyRDkzOg== - dependencies: - "@types/node" "*" - -"@types/unist@*", "@types/unist@^2.0.0", "@types/unist@^2.0.2", "@types/unist@^2.0.3": - version "2.0.6" - resolved "https://registry.yarnpkg.com/@types/unist/-/unist-2.0.6.tgz#250a7b16c3b91f672a24552ec64678eeb1d3a08d" - integrity sha512-PBjIUxZHOuj0R15/xuwJYjFi+KZdNFrehocChv4g5hu6aFroHue8m0lBP0POdK2nKzbw0cgV1mws8+V/JAcEkQ== - -"@types/vinyl@^2.0.4": - version "2.0.6" - resolved "https://registry.yarnpkg.com/@types/vinyl/-/vinyl-2.0.6.tgz#b2d134603557a7c3d2b5d3dc23863ea2b5eb29b0" - integrity sha512-ayJ0iOCDNHnKpKTgBG6Q6JOnHTj9zFta+3j2b8Ejza0e4cvRyMn0ZoLEmbPrTHe5YYRlDYPvPWVdV4cTaRyH7g== - dependencies: - "@types/expect" "^1.20.4" - "@types/node" "*" - -"@types/which@^1.3.2": - version "1.3.2" - resolved "https://registry.yarnpkg.com/@types/which/-/which-1.3.2.tgz#9c246fc0c93ded311c8512df2891fb41f6227fdf" - integrity sha512-8oDqyLC7eD4HM307boe2QWKyuzdzWBj56xI/imSl2cpL+U3tCMaTAkMJ4ee5JBZ/FsOJlvRGeIShiZDAl1qERA== - -"@types/yargs-parser@*": - version "20.2.1" - resolved "https://registry.yarnpkg.com/@types/yargs-parser/-/yargs-parser-20.2.1.tgz#3b9ce2489919d9e4fea439b76916abc34b2df129" - integrity sha512-7tFImggNeNBVMsn0vLrpn1H1uPrUBdnARPTpZoitY37ZrdJREzf7I16tMrlK3hen349gr1NYh8CmZQa7CTG6Aw== - -"@types/yargs@^16.0.0": - version "16.0.4" - resolved "https://registry.yarnpkg.com/@types/yargs/-/yargs-16.0.4.tgz#26aad98dd2c2a38e421086ea9ad42b9e51642977" - integrity sha512-T8Yc9wt/5LbJyCaLiHPReJa0kApcIgJ7Bn735GjItUfh08Z1pJvu8QZqb9s+mMvKV6WUQRV7K2R46YbjMXTTJw== - dependencies: - "@types/yargs-parser" "*" - -"@types/yauzl@^2.9.1": - version "2.9.2" - resolved "https://registry.yarnpkg.com/@types/yauzl/-/yauzl-2.9.2.tgz#c48e5d56aff1444409e39fa164b0b4d4552a7b7a" - integrity sha512-8uALY5LTvSuHgloDVUvWP3pIauILm+8/0pDMokuDYIoNsOkSwd5AiHBTSEJjKTDcZr5z8UpgOWZkxBF4iJftoA== - dependencies: - "@types/node" "*" - -"@ungap/promise-all-settled@1.1.2": - version "1.1.2" - resolved "https://registry.yarnpkg.com/@ungap/promise-all-settled/-/promise-all-settled-1.1.2.tgz#aa58042711d6e3275dd37dc597e5d31e8c290a44" - integrity sha512-sL/cEvJWAnClXw0wHk85/2L0G6Sj8UB0Ctc1TEMbKSsmpRosqhwj9gWgFRZSrBr2f9tiXISwNhCPmlfqUqyb9Q== - -"@vue/compiler-core@3.2.19": - version "3.2.19" - resolved "https://registry.yarnpkg.com/@vue/compiler-core/-/compiler-core-3.2.19.tgz#b537dd377ce51fdb64e9b30ebfbff7cd70a64cb9" - integrity sha512-8dOPX0YOtaXol0Zf2cfLQ4NU/yHYl2H7DCKsLEZ7gdvPK6ZSEwGLJ7IdghhY2YEshEpC5RB9QKdC5I07z8Dtjg== - dependencies: - "@babel/parser" "^7.15.0" - "@vue/shared" "3.2.19" - estree-walker "^2.0.2" - source-map "^0.6.1" - -"@vue/compiler-dom@3.2.19": - version "3.2.19" - resolved "https://registry.yarnpkg.com/@vue/compiler-dom/-/compiler-dom-3.2.19.tgz#0607bc90de6af55fde73b09b3c4d0bf8cb597ed8" - integrity sha512-WzQoE8rfkFjPtIioc7SSgTsnz9g2oG61DU8KHnzPrRS7fW/lji6H2uCYJfp4Z6kZE8GjnHc1Ljwl3/gxDes0cw== - dependencies: - "@vue/compiler-core" "3.2.19" - "@vue/shared" "3.2.19" - -"@vue/compiler-sfc@^3.0.11": - version "3.2.19" - resolved "https://registry.yarnpkg.com/@vue/compiler-sfc/-/compiler-sfc-3.2.19.tgz#d412195a98ebd49b84602f171719294a1d9549be" - integrity sha512-pLlbgkO1UHTO02MSpa/sFOXUwIDxSMiKZ1ozE5n71CY4DM+YmI+G3gT/ZHZ46WBId7f3VTF/D8pGwMygcQbrQA== - dependencies: - "@babel/parser" "^7.15.0" - "@vue/compiler-core" "3.2.19" - "@vue/compiler-dom" "3.2.19" - "@vue/compiler-ssr" "3.2.19" - "@vue/ref-transform" "3.2.19" - "@vue/shared" "3.2.19" - estree-walker "^2.0.2" - magic-string "^0.25.7" - postcss "^8.1.10" - source-map "^0.6.1" - -"@vue/compiler-ssr@3.2.19": - version "3.2.19" - resolved "https://registry.yarnpkg.com/@vue/compiler-ssr/-/compiler-ssr-3.2.19.tgz#3e91ecf70f8f961c5f63eacd2139bcdab9a7a07c" - integrity sha512-oLon0Cn3O7WEYzzmzZavGoqXH+199LT+smdjBT3Uf3UX4HwDNuBFCmvL0TsqV9SQnIgKvBRbQ7lhbpnd4lqM3w== - dependencies: - "@vue/compiler-dom" "3.2.19" - "@vue/shared" "3.2.19" - -"@vue/ref-transform@3.2.19": - version "3.2.19" - resolved "https://registry.yarnpkg.com/@vue/ref-transform/-/ref-transform-3.2.19.tgz#cf0f986486bb26838fbd09749e927bab19745600" - integrity sha512-03wwUnoIAeKti5IGGx6Vk/HEBJ+zUcm5wrUM3+PQsGf7IYnXTbeIfHHpx4HeSeWhnLAjqZjADQwW8uA4rBmVbg== - dependencies: - "@babel/parser" "^7.15.0" - "@vue/compiler-core" "3.2.19" - "@vue/shared" "3.2.19" - estree-walker "^2.0.2" - magic-string "^0.25.7" - -"@vue/shared@3.2.19": - version "3.2.19" - resolved "https://registry.yarnpkg.com/@vue/shared/-/shared-3.2.19.tgz#111ec3da18337d86274446984c49925b1b2b2dd7" - integrity sha512-Knqhx7WieLdVgwCAZgTVrDCXZ50uItuecLh9JdLC8O+a5ayaSyIQYveUK3hCRNC7ws5zalHmZwfdLMGaS8r4Ew== - -"@wdio/browserstack-service@^6.1.4": - version "6.12.1" - resolved "https://registry.yarnpkg.com/@wdio/browserstack-service/-/browserstack-service-6.12.1.tgz#648cbf419f434f7e7dd31f04f25fe9178e2733a8" - integrity sha512-B4zYlaE8q1Jxb6ctcuUPlKL3inwloETwks+cB9fFtVMDf/HH2Cau3Pi0CoIs8435EI+J4/1LxLHQV2uhzbBSlQ== - dependencies: - "@wdio/logger" "6.10.10" - browserstack-local "^1.4.5" - got "^11.0.2" - -"@wdio/cli@^7.5.2": - version "7.14.0" - resolved "https://registry.yarnpkg.com/@wdio/cli/-/cli-7.14.0.tgz#bb852e716dc3f35f5be5d569b061841ed13b76cc" - integrity sha512-9s2gzy4VI3DII3VGAb4AABm1dC05QN2sZU5TgPtqTUn9Cub5Rjht5wCGHhn8C3SiIfoR6fzuQezl1FpIzL3+yQ== - dependencies: - "@types/ejs" "^3.0.5" - "@types/fs-extra" "^9.0.4" - "@types/inquirer" "^8.1.2" - "@types/lodash.flattendeep" "^4.4.6" - "@types/lodash.pickby" "^4.6.6" - "@types/lodash.union" "^4.6.6" - "@types/node" "^15.12.5" - "@types/recursive-readdir" "^2.2.0" - "@wdio/config" "7.13.2" - "@wdio/logger" "7.7.0" - "@wdio/types" "7.13.2" - "@wdio/utils" "7.13.2" - async-exit-hook "^2.0.1" - chalk "^4.0.0" - chokidar "^3.0.0" - cli-spinners "^2.1.0" - ejs "^3.0.1" - fs-extra "^10.0.0" - inquirer "8.1.5" - 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.13.2" - yargs "^17.0.0" - yarn-install "^1.0.0" - -"@wdio/concise-reporter@^7.5.2": - version "7.13.2" - resolved "https://registry.yarnpkg.com/@wdio/concise-reporter/-/concise-reporter-7.13.2.tgz#4244c21c7c8c5b63782a8ec67cbd9405e7bd9f19" - integrity sha512-ndRWq9JUq8XXPpDQ+PbZGlrD5wejvTqfCMNtHpA9VHl5p1fhe7ZHOxzSEbz6UmHK7yZwgdplHdxxsOVoGaWRZA== - dependencies: - "@wdio/reporter" "7.13.2" - "@wdio/types" "7.13.2" - chalk "^4.0.0" - pretty-ms "^7.0.0" - -"@wdio/config@7.13.2": - version "7.13.2" - resolved "https://registry.yarnpkg.com/@wdio/config/-/config-7.13.2.tgz#5abdd8ce71caefd013ec62bb2e885a0a23861d37" - integrity sha512-EKrckwuUIABeOX9ju8t+Dj74urrXUko8aNj8TpxPdRL/J3cBQVOb1k/ffm2NSOga9RaLKhbKWzWeWfUULSYsiQ== - dependencies: - "@wdio/logger" "7.7.0" - "@wdio/types" "7.13.2" - deepmerge "^4.0.0" - glob "^7.1.2" - -"@wdio/local-runner@^7.5.2": - version "7.13.2" - resolved "https://registry.yarnpkg.com/@wdio/local-runner/-/local-runner-7.13.2.tgz#10b559d9c4090a694cc89ef7427d33f97f8c1985" - integrity sha512-wegCw6d/Omm3zFek1hhFmW363hW/gnzf1pTvWa91rC4jlWjMSEp1HkEVnjLIdLeiaPcya5JuB0gSKTQnsl9EpA== - dependencies: - "@types/stream-buffers" "^3.0.3" - "@wdio/logger" "7.7.0" - "@wdio/repl" "7.13.2" - "@wdio/runner" "7.13.2" - "@wdio/types" "7.13.2" - async-exit-hook "^2.0.1" - split2 "^3.2.2" - stream-buffers "^3.0.2" - -"@wdio/logger@6.10.10": - version "6.10.10" - resolved "https://registry.yarnpkg.com/@wdio/logger/-/logger-6.10.10.tgz#1e07cf32a69606ddb94fa9fd4b0171cb839a5980" - integrity sha512-2nh0hJz9HeZE0VIEMI+oPgjr/Q37ohrR9iqsl7f7GW5ik+PnKYCT9Eab5mR1GNMG60askwbskgGC1S9ygtvrSw== - dependencies: - chalk "^4.0.0" - loglevel "^1.6.0" - loglevel-plugin-prefix "^0.8.4" - strip-ansi "^6.0.0" - -"@wdio/logger@7.7.0": - version "7.7.0" - resolved "https://registry.yarnpkg.com/@wdio/logger/-/logger-7.7.0.tgz#cac834008b7570f3b6ae30ed731545618c51da40" - integrity sha512-XX/OkC8NlvsBdhKsb9j7ZbuQtF/Vuo0xf38PXdqYtVezOrYbDuba0hPG++g/IGNuAF34ZbSi+49cvz4u5w92kQ== - dependencies: - chalk "^4.0.0" - loglevel "^1.6.0" - loglevel-plugin-prefix "^0.8.4" - strip-ansi "^6.0.0" - -"@wdio/mocha-framework@^7.5.2": - version "7.13.2" - resolved "https://registry.yarnpkg.com/@wdio/mocha-framework/-/mocha-framework-7.13.2.tgz#59600bc823b249d8cd2cb91d6d00204294a44ded" - integrity sha512-NR73KcZrKZYIgly26jTDUIOhC0YvYDdBUgKN/LsIbp63gQsOmx0/QGuh2O58OxMT5Z1ri7vfcnJhf8H73Adu2Q== - dependencies: - "@types/mocha" "^9.0.0" - "@wdio/logger" "7.7.0" - "@wdio/types" "7.13.2" - "@wdio/utils" "7.13.2" - expect-webdriverio "^3.0.0" - mocha "^9.0.0" - -"@wdio/protocols@7.13.2": - version "7.13.2" - resolved "https://registry.yarnpkg.com/@wdio/protocols/-/protocols-7.13.2.tgz#639cb0d9863e8d899c51642e9f1980aa1e713f86" - integrity sha512-GUbYbV2IjPlPhlz457nMD6C0GA9yPfVtZQAwgqaKXf9yR2cuNGHHkidWivfXJNG3zws2uFm/9I1+K9OaYIKVkQ== - -"@wdio/repl@7.13.2": - version "7.13.2" - resolved "https://registry.yarnpkg.com/@wdio/repl/-/repl-7.13.2.tgz#f9a1b1f7f98e170c5af2cc6a8d5f19b9365583df" - integrity sha512-gEnC39RANX2lMip95lpCle0hkdQn2qVvLbUFAE3RzkTO8vW3Cs8Rkg905AfJdVUlsnAc3NpzINq+UBWxpk3EQw== - dependencies: - "@wdio/utils" "7.13.2" - -"@wdio/reporter@7.13.2": - version "7.13.2" - resolved "https://registry.yarnpkg.com/@wdio/reporter/-/reporter-7.13.2.tgz#039de4ec8d57b29363d1bc3c51ebf891ff1269ea" - integrity sha512-R5tqFybwfXyvopAcmYXZOb3pG0I8b3sB9S3OYLGGVeYhv21aFuEn3+2LBth65cVpRQ+vlqoYBxmcJ7A4BvoS5g== - dependencies: - "@types/diff" "^5.0.0" - "@types/node" "^15.12.5" - "@types/object-inspect" "^1.8.0" - "@types/supports-color" "^8.1.0" - "@wdio/types" "7.13.2" - diff "^5.0.0" - fs-extra "^10.0.0" - object-inspect "^1.10.3" - supports-color "8.1.1" - -"@wdio/runner@7.13.2": - version "7.13.2" - resolved "https://registry.yarnpkg.com/@wdio/runner/-/runner-7.13.2.tgz#fcb0368c71cea1949181f6c8c73a95ad211899d4" - integrity sha512-IlhObs9bH+cSs2gH/RzYuiKEXZ7HL65cBIgRhuFKVjunrAiZZUGugk2MJHQltYXBaydSkk2kJCj0noHzEBR/CA== - dependencies: - "@wdio/config" "7.13.2" - "@wdio/logger" "7.7.0" - "@wdio/types" "7.13.2" - "@wdio/utils" "7.13.2" - deepmerge "^4.0.0" - gaze "^1.1.2" - webdriver "7.13.2" - webdriverio "7.13.2" - -"@wdio/spec-reporter@^7.5.2": - version "7.13.2" - resolved "https://registry.yarnpkg.com/@wdio/spec-reporter/-/spec-reporter-7.13.2.tgz#5de5e6645183309679f061c8090f2d43676d5662" - integrity sha512-ZrQGRvAox8egDIbJyNGIC8YDpAqEaRHOzcgGlZlTpoW81Ufmn1/iX2t+Dlhu/Zku1qT23uhtV1T3PqKDW4JDHQ== - dependencies: - "@types/easy-table" "^0.0.33" - "@wdio/reporter" "7.13.2" - "@wdio/types" "7.13.2" - chalk "^4.0.0" - easy-table "^1.1.1" - pretty-ms "^7.0.0" - -"@wdio/sync@^7.5.2": - version "7.13.2" - resolved "https://registry.yarnpkg.com/@wdio/sync/-/sync-7.13.2.tgz#5e11532bc3e8d101b2b956288cad6c68fde09e08" - integrity sha512-KRGwDeFVeDrojtB2ptPQe0MZCVF+43gIjo98TLxmO+ZdruDt8j6v4bTmg55kTBhVVgiFIbN1dE6WyH1JllUcgw== - dependencies: - "@types/fibers" "^3.1.0" - "@types/puppeteer" "^5.4.0" - "@wdio/logger" "7.7.0" - "@wdio/types" "7.13.2" - fibers "^5.0.0" - webdriverio "7.13.2" - -"@wdio/types@7.13.2": - version "7.13.2" - resolved "https://registry.yarnpkg.com/@wdio/types/-/types-7.13.2.tgz#7ad775145f3af26225349838ac893ef09008f701" - integrity sha512-uTU9e4QjOIME0z4HIEwefitGNjvgeekA4G8EnOGPpgI9JCoR6kjl3X6T58tilDtZVpTC54XwjpjHESz5MwQt2w== - dependencies: - "@types/node" "^15.12.5" - got "^11.8.1" - -"@wdio/utils@7.13.2": - version "7.13.2" - resolved "https://registry.yarnpkg.com/@wdio/utils/-/utils-7.13.2.tgz#1e3aa228451365ee1b4812d6fd5298b11455f238" - integrity sha512-FvOMpwDu56PadYuHyd0GFOHQzdm/zK1A2DC2ZRalVOgaZ6adG3HrtYtFfbbVFio9XaQxoefO9OOiRphGjg8tzg== - dependencies: - "@wdio/logger" "7.7.0" - "@wdio/types" "7.13.2" - p-iteration "^1.1.8" - -JSONStream@^1.0.3, JSONStream@^1.0.4: - version "1.3.5" - resolved "https://registry.yarnpkg.com/JSONStream/-/JSONStream-1.3.5.tgz#3208c1f08d3a4d99261ab64f92302bc15e111ca0" - integrity sha512-E+iruNOY8VV9s4JEbe1aNEm6MiszPRr/UfcHMz0TQh1BXSxHK+ASV1R6W4HpjBhSeS+54PIsAMCBmwD06LLsqQ== - dependencies: - jsonparse "^1.2.0" - through ">=2.2.7 <3" - -abbrev@1: - version "1.1.1" - resolved "https://registry.yarnpkg.com/abbrev/-/abbrev-1.1.1.tgz#f8f2c887ad10bf67f634f005b6987fed3179aac8" - integrity sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q== - -abbrev@1.0.x: - version "1.0.9" - resolved "https://registry.yarnpkg.com/abbrev/-/abbrev-1.0.9.tgz#91b4792588a7738c25f35dd6f63752a2f8776135" - integrity sha1-kbR5JYinc4wl813W9jdSovh3YTU= - -accepts@~1.3.4, accepts@~1.3.7: - version "1.3.7" - resolved "https://registry.yarnpkg.com/accepts/-/accepts-1.3.7.tgz#531bc726517a3b2b41f850021c6cc15eaab507cd" - integrity sha512-Il80Qs2WjYlJIBNzNkK6KYqlVMTbZLXgHx2oT0pU/fjRHyEp+PEfEPY0R3WCwAGVOtauxh1hOxNgIf5bv7dQpA== - dependencies: - mime-types "~2.1.24" - negotiator "0.6.2" - -acorn-dynamic-import@^2.0.0: - version "2.0.2" - resolved "https://registry.yarnpkg.com/acorn-dynamic-import/-/acorn-dynamic-import-2.0.2.tgz#c752bd210bef679501b6c6cb7fc84f8f47158cc4" - integrity sha1-x1K9IQvvZ5UBtsbLf8hPj0cVjMQ= - dependencies: - acorn "^4.0.3" - -acorn-jsx@^3.0.0: - version "3.0.1" - resolved "https://registry.yarnpkg.com/acorn-jsx/-/acorn-jsx-3.0.1.tgz#afdf9488fb1ecefc8348f6fb22f464e32a58b36b" - integrity sha1-r9+UiPsezvyDSPb7IvRk4ypYs2s= - dependencies: - acorn "^3.0.4" - -acorn-jsx@^5.3.1: - version "5.3.2" - resolved "https://registry.yarnpkg.com/acorn-jsx/-/acorn-jsx-5.3.2.tgz#7ed5bb55908b3b2f1bc55c6af1653bada7f07937" - integrity sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ== - -acorn-node@^1.6.1: - version "1.8.2" - resolved "https://registry.yarnpkg.com/acorn-node/-/acorn-node-1.8.2.tgz#114c95d64539e53dede23de8b9d96df7c7ae2af8" - integrity sha512-8mt+fslDufLYntIoPAaIMUe/lrbrehIiwmR3t2k9LljIzoigEPF27eLk2hy8zSGzmR/ogr7zbRKINMo1u0yh5A== - dependencies: - acorn "^7.0.0" - acorn-walk "^7.0.0" - xtend "^4.0.2" - -acorn-walk@^7.0.0, acorn-walk@^7.1.1: - version "7.2.0" - resolved "https://registry.yarnpkg.com/acorn-walk/-/acorn-walk-7.2.0.tgz#0de889a601203909b0fbe07b8938dc21d2e967bc" - integrity sha512-OPdCF6GsMIP+Az+aWfAAOEt2/+iVDKE7oy6lJ098aoe59oAmK76qV6Gw60SbZ8jHuG2wH058GF4pLFbYamYrVA== - -acorn@^3.0.0, acorn@^3.0.4: - version "3.3.0" - resolved "https://registry.yarnpkg.com/acorn/-/acorn-3.3.0.tgz#45e37fb39e8da3f25baee3ff5369e2bb5f22017a" - integrity sha1-ReN/s56No/JbruP/U2niu18iAXo= - -acorn@^4.0.3: - version "4.0.13" - resolved "https://registry.yarnpkg.com/acorn/-/acorn-4.0.13.tgz#105495ae5361d697bd195c825192e1ad7f253787" - integrity sha1-EFSVrlNh1pe9GVyCUZLhrX8lN4c= - -acorn@^5.0.0, acorn@^5.5.0: - version "5.7.4" - resolved "https://registry.yarnpkg.com/acorn/-/acorn-5.7.4.tgz#3e8d8a9947d0599a1796d10225d7432f4a4acf5e" - integrity sha512-1D++VG7BhrtvQpNbBzovKNc1FLGGEE/oGe7b9xJm/RFHMBeUaUGpluV9RLjZa47YFdPcDAenEYuq9pQPcMdLJg== - -acorn@^6.4.1: - version "6.4.2" - resolved "https://registry.yarnpkg.com/acorn/-/acorn-6.4.2.tgz#35866fd710528e92de10cf06016498e47e39e1e6" - integrity sha512-XtGIhXwF8YM8bJhGxG5kXgjkEuNGLTkoYqVE+KMR+aspr4KGYmKYg7yUe3KghyQ9yheNwLnjmzh/7+gfDBmHCQ== - -acorn@^7.0.0, acorn@^7.1.1, acorn@^7.4.0: - version "7.4.1" - resolved "https://registry.yarnpkg.com/acorn/-/acorn-7.4.1.tgz#feaed255973d2e77555b83dbc08851a6c63520fa" - integrity sha512-nQyp0o1/mNdbTO1PO6kHkwSrmgZ0MT/jCCpNiwbUjGoRN4dlBhqJtoQuCnEOKzgTVwg0ZWiCoQy6SxMebQVh8A== - -add-stream@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/add-stream/-/add-stream-1.0.0.tgz#6a7990437ca736d5e1288db92bd3266d5f5cb2aa" - integrity sha1-anmQQ3ynNtXhKI25K9MmbV9csqo= - -agent-base@5: - version "5.1.1" - resolved "https://registry.yarnpkg.com/agent-base/-/agent-base-5.1.1.tgz#e8fb3f242959db44d63be665db7a8e739537a32c" - integrity sha512-TMeqbNl2fMW0nMjTEPOwe3J/PRFP4vqeoNuQMG0HlMrtm5QxKqdvAkZ1pRBQ/ulIyDD5Yq0nJ7YbdD8ey0TO3g== - -agent-base@6: - version "6.0.2" - resolved "https://registry.yarnpkg.com/agent-base/-/agent-base-6.0.2.tgz#49fff58577cfee3f37176feab4c22e00f86d7f77" - integrity sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ== - dependencies: - debug "4" - -agent-base@^4.3.0: - version "4.3.0" - resolved "https://registry.yarnpkg.com/agent-base/-/agent-base-4.3.0.tgz#8165f01c436009bccad0b1d122f05ed770efc6ee" - integrity sha512-salcGninV0nPrwpGNn4VTXBb1SOuXQBiqbrNXoeizJsHrsL6ERFM2Ne3JUSBWRE6aeNJI2ROP/WEEIDUiDe3cg== - dependencies: - es6-promisify "^5.0.0" - -ajv-keywords@^2.1.0: - version "2.1.1" - resolved "https://registry.yarnpkg.com/ajv-keywords/-/ajv-keywords-2.1.1.tgz#617997fc5f60576894c435f940d819e135b80762" - integrity sha1-YXmX/F9gV2iUxDX5QNgZ4TW4B2I= - -ajv-keywords@^3.1.0, ajv-keywords@^3.5.2: - version "3.5.2" - resolved "https://registry.yarnpkg.com/ajv-keywords/-/ajv-keywords-3.5.2.tgz#31f29da5ab6e00d1c2d329acf7b5929614d5014d" - integrity sha512-5p6WTN0DdTGVQk6VjcEju19IgaHudalcfabD7yhDGeA6bcQnmL+CpveLJq/3hvfwd1aof6L386Ougkx6RfyMIQ== - -ajv@5.5.2, ajv@^5.2.3, ajv@^5.3.0: - version "5.5.2" - resolved "https://registry.yarnpkg.com/ajv/-/ajv-5.5.2.tgz#73b5eeca3fab653e3d3f9422b341ad42205dc965" - integrity sha1-c7Xuyj+rZT49P5Qis0GtQiBdyWU= - dependencies: - co "^4.6.0" - fast-deep-equal "^1.0.0" - fast-json-stable-stringify "^2.0.0" - json-schema-traverse "^0.3.0" - -ajv@^6.1.0, ajv@^6.10.0, ajv@^6.12.3, ajv@^6.12.4: - version "6.12.6" - resolved "https://registry.yarnpkg.com/ajv/-/ajv-6.12.6.tgz#baf5a62e802b07d977034586f8c3baf5adf26df4" - integrity sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g== - dependencies: - fast-deep-equal "^3.1.1" - fast-json-stable-stringify "^2.0.0" - json-schema-traverse "^0.4.1" - uri-js "^4.2.2" - -ajv@^8.0.1: - version "8.6.3" - resolved "https://registry.yarnpkg.com/ajv/-/ajv-8.6.3.tgz#11a66527761dc3e9a3845ea775d2d3c0414e8764" - integrity sha512-SMJOdDP6LqTkD0Uq8qLi+gMwSt0imXLSV080qFVwJCpH9U6Mb+SUGHAXM0KNbcBPguytWyvFxcHgMLe2D2XSpw== - dependencies: - fast-deep-equal "^3.1.1" - json-schema-traverse "^1.0.0" - require-from-string "^2.0.2" - uri-js "^4.2.2" - -align-text@^0.1.1, align-text@^0.1.3: - version "0.1.4" - resolved "https://registry.yarnpkg.com/align-text/-/align-text-0.1.4.tgz#0cd90a561093f35d0a99256c22b7069433fad117" - integrity sha1-DNkKVhCT810KmSVsIrcGlDP60Rc= - dependencies: - kind-of "^3.0.2" - longest "^1.0.1" - repeat-string "^1.5.2" - -amdefine@>=0.0.4: - version "1.0.1" - resolved "https://registry.yarnpkg.com/amdefine/-/amdefine-1.0.1.tgz#4a5282ac164729e93619bcfd3ad151f817ce91f5" - integrity sha1-SlKCrBZHKek2Gbz9OtFR+BfOkfU= - -ansi-colors@4.1.1, ansi-colors@^4.1.1: - version "4.1.1" - resolved "https://registry.yarnpkg.com/ansi-colors/-/ansi-colors-4.1.1.tgz#cbb9ae256bf750af1eab344f229aa27fe94ba348" - integrity sha512-JoX0apGbHaUJBNl6yF+p6JAFYZ666/hhCGKN5t9QFjbJQKUU/g8MNbFDbvfrgKXvI1QpZplPOnwIo99lX/AAmA== - -ansi-colors@^1.0.1: - version "1.1.0" - resolved "https://registry.yarnpkg.com/ansi-colors/-/ansi-colors-1.1.0.tgz#6374b4dd5d4718ff3ce27a671a3b1cad077132a9" - integrity sha512-SFKX67auSNoVR38N3L+nvsPjOE0bybKTYbkf5tRvushrAPQ9V75huw0ZxBkKVeRU9kqH3d6HA4xTckbwZ4ixmA== - dependencies: - ansi-wrap "^0.1.0" - -ansi-colors@^2.0.5: - version "2.0.5" - resolved "https://registry.yarnpkg.com/ansi-colors/-/ansi-colors-2.0.5.tgz#5da37825fef3e75f3bda47f760d64bfd10e15e10" - integrity sha512-yAdfUZ+c2wetVNIFsNRn44THW+Lty6S5TwMpUfLA/UaGhiXbBv/F8E60/1hMLd0cnF/CDoWH8vzVaI5bAcHCjw== - -ansi-escapes@^3.0.0: - version "3.2.0" - resolved "https://registry.yarnpkg.com/ansi-escapes/-/ansi-escapes-3.2.0.tgz#8780b98ff9dbf5638152d1f1fe5c1d7b4442976b" - integrity sha512-cBhpre4ma+U0T1oM5fXg7Dy1Jw7zzwv7lt/GoCpr+hDQJoYnKVPLL4dCvSEFMmQurOQvSrwT7SL/DAlhBI97RQ== - -ansi-escapes@^4.2.1: - version "4.3.2" - resolved "https://registry.yarnpkg.com/ansi-escapes/-/ansi-escapes-4.3.2.tgz#6b2291d1db7d98b6521d5f1efa42d0f3a9feb65e" - integrity sha512-gKXj5ALrKWQLsYG9jlTRmR/xKluxHV+Z9QEwNIgCfM1/uwPMCuzVVnh5mwTd+OuBZcwSIMbqssNWRm1lE51QaQ== - dependencies: - type-fest "^0.21.3" - -ansi-gray@^0.1.1: - version "0.1.1" - resolved "https://registry.yarnpkg.com/ansi-gray/-/ansi-gray-0.1.1.tgz#2962cf54ec9792c48510a3deb524436861ef7251" - integrity sha1-KWLPVOyXksSFEKPetSRDaGHvclE= - dependencies: - ansi-wrap "0.1.0" - -ansi-html@^0.0.7: - version "0.0.7" - resolved "https://registry.yarnpkg.com/ansi-html/-/ansi-html-0.0.7.tgz#813584021962a9e9e6fd039f940d12f56ca7859e" - integrity sha1-gTWEAhliqenm/QOflA0S9WynhZ4= - -ansi-regex@^0.2.0, ansi-regex@^0.2.1: - version "0.2.1" - resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-0.2.1.tgz#0d8e946967a3d8143f93e24e298525fc1b2235f9" - integrity sha1-DY6UaWej2BQ/k+JOKYUl/BsiNfk= - -ansi-regex@^2.0.0: - version "2.1.1" - resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-2.1.1.tgz#c3b33ab5ee360d86e0e628f0468ae7ef27d654df" - integrity sha1-w7M6te42DYbg5ijwRorn7yfWVN8= - -ansi-regex@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-3.0.0.tgz#ed0317c322064f79466c02966bddb605ab37d998" - integrity sha1-7QMXwyIGT3lGbAKWa922Bas32Zg= - -ansi-regex@^5.0.1: - version "5.0.1" - resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-5.0.1.tgz#082cb2c89c9fe8659a311a53bd6a4dc5301db304" - integrity sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ== - -ansi-styles@^1.1.0: - version "1.1.0" - resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-1.1.0.tgz#eaecbf66cd706882760b2f4691582b8f55d7a7de" - integrity sha1-6uy/Zs1waIJ2Cy9GkVgrj1XXp94= - -ansi-styles@^2.2.1: - version "2.2.1" - resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-2.2.1.tgz#b432dd3358b634cf75e1e4664368240533c1ddbe" - integrity sha1-tDLdM1i2NM914eRmQ2gkBTPB3b4= - -ansi-styles@^3.2.1: - version "3.2.1" - resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-3.2.1.tgz#41fbb20243e50b12be0f04b8dedbf07520ce841d" - integrity sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA== - dependencies: - color-convert "^1.9.0" - -ansi-styles@^4.0.0, ansi-styles@^4.1.0: - version "4.3.0" - resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-4.3.0.tgz#edd803628ae71c04c85ae7a0906edad34b648937" - integrity sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg== - dependencies: - color-convert "^2.0.1" - -ansi-styles@^5.0.0: - version "5.2.0" - resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-5.2.0.tgz#07449690ad45777d1924ac2abb2fc8895dba836b" - integrity sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA== - -ansi-wrap@0.1.0, ansi-wrap@^0.1.0: - version "0.1.0" - resolved "https://registry.yarnpkg.com/ansi-wrap/-/ansi-wrap-0.1.0.tgz#a82250ddb0015e9a27ca82e82ea603bbfa45efaf" - integrity sha1-qCJQ3bABXponyoLoLqYDu/pF768= - -anymatch@^1.3.0: - version "1.3.2" - resolved "https://registry.yarnpkg.com/anymatch/-/anymatch-1.3.2.tgz#553dcb8f91e3c889845dfdba34c77721b90b9d7a" - integrity sha512-0XNayC8lTHQ2OI8aljNCN3sSx6hsr/1+rlcDAotXJR7C1oZZHCNsfpbKwMjRA3Uqb5tF1Rae2oloTr4xpq+WjA== - dependencies: - micromatch "^2.1.5" - normalize-path "^2.0.0" - -anymatch@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/anymatch/-/anymatch-2.0.0.tgz#bcb24b4f37934d9aa7ac17b4adaf89e7c76ef2eb" - integrity sha512-5teOsQWABXHHBFP9y3skS5P3d/WfWXpv3FUpy+LorMrNYaT9pI4oLMQX7jzQ2KklNpGpWHzdCXTDT2Y3XGlZBw== - dependencies: - micromatch "^3.1.4" - normalize-path "^2.1.1" - -anymatch@~3.1.2: - version "3.1.2" - resolved "https://registry.yarnpkg.com/anymatch/-/anymatch-3.1.2.tgz#c0557c096af32f106198f4f4e2a383537e378716" - integrity sha512-P43ePfOAIupkguHUycrc4qJ9kz8ZiuOUijaETwX7THt0Y/GNK7v0aa8rY816xWjZ7rJdA5XdMcpVFTKMq+RvWg== - dependencies: - normalize-path "^3.0.0" - picomatch "^2.0.4" - -append-buffer@^1.0.2: - version "1.0.2" - resolved "https://registry.yarnpkg.com/append-buffer/-/append-buffer-1.0.2.tgz#d8220cf466081525efea50614f3de6514dfa58f1" - integrity sha1-2CIM9GYIFSXv6lBhTz3mUU36WPE= - dependencies: - buffer-equal "^1.0.0" - -archiver-utils@^2.1.0: - version "2.1.0" - resolved "https://registry.yarnpkg.com/archiver-utils/-/archiver-utils-2.1.0.tgz#e8a460e94b693c3e3da182a098ca6285ba9249e2" - integrity sha512-bEL/yUb/fNNiNTuUz979Z0Yg5L+LzLxGJz8x79lYmR54fmTIb6ob/hNQgkQnIUDWIFjZVQwl9Xs356I6BAMHfw== - dependencies: - glob "^7.1.4" - graceful-fs "^4.2.0" - lazystream "^1.0.0" - lodash.defaults "^4.2.0" - lodash.difference "^4.5.0" - lodash.flatten "^4.4.0" - lodash.isplainobject "^4.0.6" - lodash.union "^4.6.0" - normalize-path "^3.0.0" - readable-stream "^2.0.0" - -archiver@^5.0.0: - version "5.3.0" - resolved "https://registry.yarnpkg.com/archiver/-/archiver-5.3.0.tgz#dd3e097624481741df626267564f7dd8640a45ba" - integrity sha512-iUw+oDwK0fgNpvveEsdQ0Ase6IIKztBJU2U0E9MzszMfmVVUyv1QJhS2ITW9ZCqx8dktAxVAjWWkKehuZE8OPg== - dependencies: - archiver-utils "^2.1.0" - async "^3.2.0" - buffer-crc32 "^0.2.1" - readable-stream "^3.6.0" - readdir-glob "^1.0.0" - tar-stream "^2.2.0" - zip-stream "^4.1.0" - -archy@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/archy/-/archy-1.0.0.tgz#f9c8c13757cc1dd7bc379ac77b2c62a5c2868c40" - integrity sha1-+cjBN1fMHde8N5rHeyxipcKGjEA= - -argparse@^1.0.7: - version "1.0.10" - resolved "https://registry.yarnpkg.com/argparse/-/argparse-1.0.10.tgz#bcd6791ea5ae09725e17e5ad988134cd40b3d911" - integrity sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg== - dependencies: - sprintf-js "~1.0.2" - -argparse@^2.0.1: - version "2.0.1" - resolved "https://registry.yarnpkg.com/argparse/-/argparse-2.0.1.tgz#246f50f3ca78a3240f6c997e8a9bd1eac49e4b38" - integrity sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q== - -aria-query@^4.2.2: - version "4.2.2" - resolved "https://registry.yarnpkg.com/aria-query/-/aria-query-4.2.2.tgz#0d2ca6c9aceb56b8977e9fed6aed7e15bbd2f83b" - integrity sha512-o/HelwhuKpTj/frsOsbNLNgnNGVIFsVP/SW2BSF14gVl7kAfMOJ6/8wUAUvG1R1NHKrfG+2sHZTu0yauT1qBrA== - dependencies: - "@babel/runtime" "^7.10.2" - "@babel/runtime-corejs3" "^7.10.2" - -arr-diff@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/arr-diff/-/arr-diff-2.0.0.tgz#8f3b827f955a8bd669697e4a4256ac3ceae356cf" - integrity sha1-jzuCf5Vai9ZpaX5KQlasPOrjVs8= - dependencies: - arr-flatten "^1.0.1" - -arr-diff@^4.0.0: - version "4.0.0" - resolved "https://registry.yarnpkg.com/arr-diff/-/arr-diff-4.0.0.tgz#d6461074febfec71e7e15235761a329a5dc7c520" - integrity sha1-1kYQdP6/7HHn4VI1dhoyml3HxSA= - -arr-filter@^1.1.1: - version "1.1.2" - resolved "https://registry.yarnpkg.com/arr-filter/-/arr-filter-1.1.2.tgz#43fdddd091e8ef11aa4c45d9cdc18e2dff1711ee" - integrity sha1-Q/3d0JHo7xGqTEXZzcGOLf8XEe4= - dependencies: - make-iterator "^1.0.0" - -arr-flatten@^1.0.1, arr-flatten@^1.1.0: - version "1.1.0" - resolved "https://registry.yarnpkg.com/arr-flatten/-/arr-flatten-1.1.0.tgz#36048bbff4e7b47e136644316c99669ea5ae91f1" - integrity sha512-L3hKV5R/p5o81R7O02IGnwpDmkp6E982XhtbuwSe3O4qOtMMMtodicASA1Cny2U+aCXcNpml+m4dPsvsJ3jatg== - -arr-map@^2.0.0, arr-map@^2.0.2: - version "2.0.2" - resolved "https://registry.yarnpkg.com/arr-map/-/arr-map-2.0.2.tgz#3a77345ffc1cf35e2a91825601f9e58f2e24cac4" - integrity sha1-Onc0X/wc814qkYJWAfnljy4kysQ= - dependencies: - make-iterator "^1.0.0" - -arr-union@^3.1.0: - version "3.1.0" - resolved "https://registry.yarnpkg.com/arr-union/-/arr-union-3.1.0.tgz#e39b09aea9def866a8f206e288af63919bae39c4" - integrity sha1-45sJrqne+Gao8gbiiK9jkZuuOcQ= - -array-differ@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/array-differ/-/array-differ-1.0.0.tgz#eff52e3758249d33be402b8bb8e564bb2b5d4031" - integrity sha1-7/UuN1gknTO+QCuLuOVkuytdQDE= - -array-each@^1.0.0, array-each@^1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/array-each/-/array-each-1.0.1.tgz#a794af0c05ab1752846ee753a1f211a05ba0c44f" - integrity sha1-p5SvDAWrF1KEbudTofIRoFugxE8= - -array-find-index@^1.0.1: - version "1.0.2" - resolved "https://registry.yarnpkg.com/array-find-index/-/array-find-index-1.0.2.tgz#df010aa1287e164bbda6f9723b0a96a1ec4187a1" - integrity sha1-3wEKoSh+Fku9pvlyOwqWoexBh6E= - -array-flatten@1.1.1: - version "1.1.1" - resolved "https://registry.yarnpkg.com/array-flatten/-/array-flatten-1.1.1.tgz#9a5f699051b1e7073328f2a008968b64ea2955d2" - integrity sha1-ml9pkFGx5wczKPKgCJaLZOopVdI= - -array-from@^2.1.1: - version "2.1.1" - resolved "https://registry.yarnpkg.com/array-from/-/array-from-2.1.1.tgz#cfe9d8c26628b9dc5aecc62a9f5d8f1f352c1195" - integrity sha1-z+nYwmYoudxa7MYqn12PHzUsEZU= - -array-ify@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/array-ify/-/array-ify-1.0.0.tgz#9e528762b4a9066ad163a6962a364418e9626ece" - integrity sha1-nlKHYrSpBmrRY6aWKjZEGOlibs4= - -array-includes@^3.1.3: - version "3.1.3" - resolved "https://registry.yarnpkg.com/array-includes/-/array-includes-3.1.3.tgz#c7f619b382ad2afaf5326cddfdc0afc61af7690a" - integrity sha512-gcem1KlBU7c9rB+Rq8/3PPKsK2kjqeEBa3bD5kkQo4nYlOHQCJqIJFqBXDEfwaRuYTT4E+FxA9xez7Gf/e3Q7A== - dependencies: - call-bind "^1.0.2" - define-properties "^1.1.3" - es-abstract "^1.18.0-next.2" - get-intrinsic "^1.1.1" - is-string "^1.0.5" - -array-initial@^1.0.0: - version "1.1.0" - resolved "https://registry.yarnpkg.com/array-initial/-/array-initial-1.1.0.tgz#2fa74b26739371c3947bd7a7adc73be334b3d795" - integrity sha1-L6dLJnOTccOUe9enrcc74zSz15U= - dependencies: - array-slice "^1.0.0" - is-number "^4.0.0" - -array-last@^1.1.1: - version "1.3.0" - resolved "https://registry.yarnpkg.com/array-last/-/array-last-1.3.0.tgz#7aa77073fec565ddab2493f5f88185f404a9d336" - integrity sha512-eOCut5rXlI6aCOS7Z7kCplKRKyiFQ6dHFBem4PwlwKeNFk2/XxTrhRh5T9PyaEWGy/NHTZWbY+nsZlNFJu9rYg== - dependencies: - is-number "^4.0.0" - -array-slice@^1.0.0: - version "1.1.0" - resolved "https://registry.yarnpkg.com/array-slice/-/array-slice-1.1.0.tgz#e368ea15f89bc7069f7ffb89aec3a6c7d4ac22d4" - integrity sha512-B1qMD3RBP7O8o0H2KbrXDyB0IccejMF15+87Lvlor12ONPRHP6gTjXMNkt/d3ZuOGbAe66hFmaCfECI24Ufp6w== - -array-sort@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/array-sort/-/array-sort-1.0.0.tgz#e4c05356453f56f53512a7d1d6123f2c54c0a88a" - integrity sha512-ihLeJkonmdiAsD7vpgN3CRcx2J2S0TiYW+IS/5zHBI7mKUq3ySvBdzzBfD236ubDBQFiiyG3SWCPc+msQ9KoYg== - dependencies: - default-compare "^1.0.0" - get-value "^2.0.6" - kind-of "^5.0.2" - -array-uniq@^1.0.2: - version "1.0.3" - resolved "https://registry.yarnpkg.com/array-uniq/-/array-uniq-1.0.3.tgz#af6ac877a25cc7f74e058894753858dfdb24fdb6" - integrity sha1-r2rId6Jcx/dOBYiUdThY39sk/bY= - -array-unique@^0.2.1: - version "0.2.1" - resolved "https://registry.yarnpkg.com/array-unique/-/array-unique-0.2.1.tgz#a1d97ccafcbc2625cc70fadceb36a50c58b01a53" - integrity sha1-odl8yvy8JiXMcPrc6zalDFiwGlM= - -array-unique@^0.3.2: - version "0.3.2" - resolved "https://registry.yarnpkg.com/array-unique/-/array-unique-0.3.2.tgz#a894b75d4bc4f6cd679ef3244a9fd8f46ae2d428" - integrity sha1-qJS3XUvE9s1nnvMkSp/Y9Gri1Cg= - -array.prototype.flat@^1.2.4: - version "1.2.5" - resolved "https://registry.yarnpkg.com/array.prototype.flat/-/array.prototype.flat-1.2.5.tgz#07e0975d84bbc7c48cd1879d609e682598d33e13" - integrity sha512-KaYU+S+ndVqyUnignHftkwc58o3uVU1jzczILJ1tN2YaIZpFIKBiP/x/j97E5MVPsaCloPbqWLB/8qCTVvT2qg== - dependencies: - call-bind "^1.0.2" - define-properties "^1.1.3" - es-abstract "^1.19.0" - -arrify@^1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/arrify/-/arrify-1.0.1.tgz#898508da2226f380df904728456849c1501a4b0d" - integrity sha1-iYUI2iIm84DfkEcoRWhJwVAaSw0= - -asn1.js@^5.2.0: - version "5.4.1" - resolved "https://registry.yarnpkg.com/asn1.js/-/asn1.js-5.4.1.tgz#11a980b84ebb91781ce35b0fdc2ee294e3783f07" - integrity sha512-+I//4cYPccV8LdmBLiX8CYvf9Sp3vQsrqu2QNXRcrbiWvcx/UdlFiqUJJzxRQxgsZmvhXhn4cSKeSmoFjVdupA== - dependencies: - bn.js "^4.0.0" - inherits "^2.0.1" - minimalistic-assert "^1.0.0" - safer-buffer "^2.1.0" - -asn1@~0.2.3: - version "0.2.4" - resolved "https://registry.yarnpkg.com/asn1/-/asn1-0.2.4.tgz#8d2475dfab553bb33e77b54e59e880bb8ce23136" - integrity sha512-jxwzQpLQjSmWXgwaCZE9Nz+glAG01yF1QnWgbhGwHI5A6FRIEY6IVqtHhIepHqI7/kyEyQEagBC5mBEFlIYvdg== - dependencies: - safer-buffer "~2.1.0" - -assert-plus@1.0.0, assert-plus@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/assert-plus/-/assert-plus-1.0.0.tgz#f12e0f3c5d77b0b1cdd9146942e4e96c1e4dd525" - integrity sha1-8S4PPF13sLHN2RRpQuTpbB5N1SU= - -assert@^1.1.1: - version "1.5.0" - resolved "https://registry.yarnpkg.com/assert/-/assert-1.5.0.tgz#55c109aaf6e0aefdb3dc4b71240c70bf574b18eb" - integrity sha512-EDsgawzwoun2CZkCgtxJbv392v4nbk9XDD06zI+kQYoBM/3RBWLlEyJARDOmhAAosBjWACEkKL6S+lIZtcAubA== - dependencies: - object-assign "^4.1.1" - util "0.10.3" - -assertion-error@^1.1.0: - version "1.1.0" - resolved "https://registry.yarnpkg.com/assertion-error/-/assertion-error-1.1.0.tgz#e60b6b0e8f301bd97e5375215bda406c85118c0b" - integrity sha512-jgsaNduz+ndvGyFt3uSuWqvy4lCnIJiovtouQN5JZHOKCS2QuhEdbcQHFhVksz2N2U9hXJo8odG7ETyWlEeuDw== - -assign-symbols@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/assign-symbols/-/assign-symbols-1.0.0.tgz#59667f41fadd4f20ccbc2bb96b8d4f7f78ec0367" - integrity sha1-WWZ/QfrdTyDMvCu5a41Pf3jsA2c= - -astral-regex@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/astral-regex/-/astral-regex-2.0.0.tgz#483143c567aeed4785759c0865786dc77d7d2e31" - integrity sha512-Z7tMw1ytTXt5jqMcOP+OQteU1VuNK9Y02uuJtKQ1Sv69jXQKKg5cibLwGJow8yzZP+eAc18EmLGPal0bp36rvQ== - -async-done@^1.2.0, async-done@^1.2.2: - version "1.3.2" - resolved "https://registry.yarnpkg.com/async-done/-/async-done-1.3.2.tgz#5e15aa729962a4b07414f528a88cdf18e0b290a2" - integrity sha512-uYkTP8dw2og1tu1nmza1n1CMW0qb8gWWlwqMmLb7MhBVs4BXrFziT6HXUd+/RlRA/i4H9AkofYloUbs1fwMqlw== - dependencies: - end-of-stream "^1.1.0" - once "^1.3.2" - process-nextick-args "^2.0.0" - stream-exhaust "^1.0.1" - -async-each@^1.0.0, async-each@^1.0.1: - version "1.0.3" - resolved "https://registry.yarnpkg.com/async-each/-/async-each-1.0.3.tgz#b727dbf87d7651602f06f4d4ac387f47d91b0cbf" - integrity sha512-z/WhQ5FPySLdvREByI2vZiTWwCnF0moMJ1hK9YQwDTHKh6I7/uSckMetoRGb5UBZPC1z0jlw+n/XCgjeH7y1AQ== - -async-exit-hook@^2.0.1: - version "2.0.1" - resolved "https://registry.yarnpkg.com/async-exit-hook/-/async-exit-hook-2.0.1.tgz#8bd8b024b0ec9b1c01cccb9af9db29bd717dfaf3" - integrity sha512-NW2cX8m1Q7KPA7a5M2ULQeZ2wR5qI5PAbw5L0UOMxdioVk9PMZ0h1TmyZEkPYrCvYjDlFICusOu1dlEKAAeXBw== - -async-limiter@~1.0.0: - version "1.0.1" - resolved "https://registry.yarnpkg.com/async-limiter/-/async-limiter-1.0.1.tgz#dd379e94f0db8310b08291f9d64c3209766617fd" - integrity sha512-csOlWGAcRFJaI6m+F2WKdnMKr4HhdhFVBk0H/QbJFMCr+uO2kwohwXQPxw/9OCxp05r5ghVBFSyioixx3gfkNQ== - -async-settle@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/async-settle/-/async-settle-1.0.0.tgz#1d0a914bb02575bec8a8f3a74e5080f72b2c0c6b" - integrity sha1-HQqRS7Aldb7IqPOnTlCA9yssDGs= - dependencies: - async-done "^1.2.2" - -async@0.9.x, async@^0.9.0: - version "0.9.2" - resolved "https://registry.yarnpkg.com/async/-/async-0.9.2.tgz#aea74d5e61c1f899613bf64bda66d4c78f2fd17d" - integrity sha1-rqdNXmHB+JlhO/ZL2mbUx48v0X0= - -async@1.x, async@^1.3.0: - version "1.5.2" - resolved "https://registry.yarnpkg.com/async/-/async-1.5.2.tgz#ec6a61ae56480c0c3cb241c95618e20892f9672a" - integrity sha1-7GphrlZIDAw8skHJVhjiCJL5Zyo= - -async@^2.0.0, async@^2.1.2: - version "2.6.3" - resolved "https://registry.yarnpkg.com/async/-/async-2.6.3.tgz#d72625e2344a3656e3a3ad4fa749fa83299d82ff" - integrity sha512-zflvls11DCy+dQWzTW2dzuilv8Z5X/pjfmZOWba6TNIVDm+2UDaJmXSOXlasHKfNBs8oo3M0aT50fDEWfKZjXg== - dependencies: - lodash "^4.17.14" - -async@^3.2.0: - version "3.2.1" - resolved "https://registry.yarnpkg.com/async/-/async-3.2.1.tgz#d3274ec66d107a47476a4c49136aacdb00665fc8" - integrity sha512-XdD5lRO/87udXCMC9meWdYiR+Nq6ZjUfXidViUZGu2F1MO4T3XwZ1et0hb2++BgLfhyJwy44BGB/yx80ABx8hg== - -async@~0.2.6: - version "0.2.10" - resolved "https://registry.yarnpkg.com/async/-/async-0.2.10.tgz#b6bbe0b0674b9d719708ca38de8c237cb526c3d1" - integrity sha1-trvgsGdLnXGXCMo43owjfLUmw9E= - -asynckit@^0.4.0: - version "0.4.0" - resolved "https://registry.yarnpkg.com/asynckit/-/asynckit-0.4.0.tgz#c79ed97f7f34cb8f2ba1bc9790bcc366474b4b79" - integrity sha1-x57Zf380y48robyXkLzDZkdLS3k= - -atob@^2.1.2: - version "2.1.2" - resolved "https://registry.yarnpkg.com/atob/-/atob-2.1.2.tgz#6d9517eb9e030d2436666651e86bd9f6f13533c9" - integrity sha512-Wm6ukoaOGJi/73p/cl2GvLjTI5JM1k/O14isD73YML8StrH/7/lRFgmg8nICZgD3bZZvjwCGxtMOD3wWNAu8cg== - -available-typed-arrays@^1.0.5: - version "1.0.5" - resolved "https://registry.yarnpkg.com/available-typed-arrays/-/available-typed-arrays-1.0.5.tgz#92f95616501069d07d10edb2fc37d3e1c65123b7" - integrity sha512-DMD0KiN46eipeziST1LPP/STfDU0sufISXmjSgvVsoU2tqxctQeASejWcfNtxYKqETM1UxQ8sp2OrSBWpHY6sw== - -aws-sign2@~0.7.0: - version "0.7.0" - resolved "https://registry.yarnpkg.com/aws-sign2/-/aws-sign2-0.7.0.tgz#b46e890934a9591f2d2f6f86d7e6a9f1b3fe76a8" - integrity sha1-tG6JCTSpWR8tL2+G1+ap8bP+dqg= - -aws4@^1.8.0: - version "1.11.0" - resolved "https://registry.yarnpkg.com/aws4/-/aws4-1.11.0.tgz#d61f46d83b2519250e2784daf5b09479a8b41c59" - integrity sha512-xh1Rl34h6Fi1DC2WWKfxUTVqRsNnr6LsKz2+hfwDxQJWmrx8+c7ylaqBMcHfl1U1r2dsifOvKX3LQuLNZ+XSvA== - -babel-code-frame@^6.22.0: - version "6.26.0" - resolved "https://registry.yarnpkg.com/babel-code-frame/-/babel-code-frame-6.26.0.tgz#63fd43f7dc1e3bb7ce35947db8fe369a3f58c74b" - integrity sha1-Y/1D99weO7fONZR9uP42mj9Yx0s= - dependencies: - chalk "^1.1.3" - esutils "^2.0.2" - js-tokens "^3.0.2" - -babel-loader@^8.0.5: - version "8.2.2" - resolved "https://registry.yarnpkg.com/babel-loader/-/babel-loader-8.2.2.tgz#9363ce84c10c9a40e6c753748e1441b60c8a0b81" - integrity sha512-JvTd0/D889PQBtUXJ2PXaKU/pjZDMtHA9V2ecm+eNRmmBCMR09a+fmpGTNwnJtFmFl5Ei7Vy47LjBb+L0wQ99g== - dependencies: - find-cache-dir "^3.3.1" - loader-utils "^1.4.0" - make-dir "^3.1.0" - schema-utils "^2.6.5" - -babel-plugin-dynamic-import-node@^2.3.3: - version "2.3.3" - resolved "https://registry.yarnpkg.com/babel-plugin-dynamic-import-node/-/babel-plugin-dynamic-import-node-2.3.3.tgz#84fda19c976ec5c6defef57f9427b3def66e17a3" - integrity sha512-jZVI+s9Zg3IqA/kdi0i6UDCybUI3aSBLnglhYbSSjKlV7yF1F/5LWv8MakQmvYpnbJDS6fcBL2KzHSxNCMtWSQ== - dependencies: - object.assign "^4.1.0" - -babel-plugin-polyfill-corejs2@^0.2.2: - version "0.2.2" - resolved "https://registry.yarnpkg.com/babel-plugin-polyfill-corejs2/-/babel-plugin-polyfill-corejs2-0.2.2.tgz#e9124785e6fd94f94b618a7954e5693053bf5327" - integrity sha512-kISrENsJ0z5dNPq5eRvcctITNHYXWOA4DUZRFYCz3jYCcvTb/A546LIddmoGNMVYg2U38OyFeNosQwI9ENTqIQ== - dependencies: - "@babel/compat-data" "^7.13.11" - "@babel/helper-define-polyfill-provider" "^0.2.2" - semver "^6.1.1" - -babel-plugin-polyfill-corejs3@^0.2.2: - version "0.2.5" - resolved "https://registry.yarnpkg.com/babel-plugin-polyfill-corejs3/-/babel-plugin-polyfill-corejs3-0.2.5.tgz#2779846a16a1652244ae268b1e906ada107faf92" - integrity sha512-ninF5MQNwAX9Z7c9ED+H2pGt1mXdP4TqzlHKyPIYmJIYz0N+++uwdM7RnJukklhzJ54Q84vA4ZJkgs7lu5vqcw== - dependencies: - "@babel/helper-define-polyfill-provider" "^0.2.2" - core-js-compat "^3.16.2" - -babel-plugin-polyfill-regenerator@^0.2.2: - version "0.2.2" - resolved "https://registry.yarnpkg.com/babel-plugin-polyfill-regenerator/-/babel-plugin-polyfill-regenerator-0.2.2.tgz#b310c8d642acada348c1fa3b3e6ce0e851bee077" - integrity sha512-Goy5ghsc21HgPDFtzRkSirpZVW35meGoTmTOb2bxqdl60ghub4xOidgNTHaZfQ2FaxQsKmwvXtOAkcIS4SMBWg== - dependencies: - "@babel/helper-define-polyfill-provider" "^0.2.2" - -babel-plugin-transform-object-assign@^6.22.0: - version "6.22.0" - resolved "https://registry.yarnpkg.com/babel-plugin-transform-object-assign/-/babel-plugin-transform-object-assign-6.22.0.tgz#f99d2f66f1a0b0d498e346c5359684740caa20ba" - integrity sha1-+Z0vZvGgsNSY40bFNZaEdAyqILo= - dependencies: - babel-runtime "^6.22.0" - -babel-runtime@^6.0.0, babel-runtime@^6.22.0: - version "6.26.0" - resolved "https://registry.yarnpkg.com/babel-runtime/-/babel-runtime-6.26.0.tgz#965c7058668e82b55d7bfe04ff2337bc8b5647fe" - integrity sha1-llxwWGaOgrVde/4E/yM3vItWR/4= - dependencies: - core-js "^2.4.0" - regenerator-runtime "^0.11.0" - -babelify@^10.0.0: - version "10.0.0" - resolved "https://registry.yarnpkg.com/babelify/-/babelify-10.0.0.tgz#fe73b1a22583f06680d8d072e25a1e0d1d1d7fb5" - integrity sha512-X40FaxyH7t3X+JFAKvb1H9wooWKLRCi8pg3m8poqtdZaIng+bjzp9RvKQCvRjF9isHiPkXspbbXT/zwXLtwgwg== - -bach@^1.0.0: - version "1.2.0" - resolved "https://registry.yarnpkg.com/bach/-/bach-1.2.0.tgz#4b3ce96bf27134f79a1b414a51c14e34c3bd9880" - integrity sha1-Szzpa/JxNPeaG0FKUcFONMO9mIA= - dependencies: - arr-filter "^1.1.1" - arr-flatten "^1.0.1" - arr-map "^2.0.0" - array-each "^1.0.0" - array-initial "^1.0.0" - array-last "^1.1.1" - async-done "^1.2.2" - async-settle "^1.0.0" - now-and-later "^2.0.0" - -bail@^1.0.0: - version "1.0.5" - resolved "https://registry.yarnpkg.com/bail/-/bail-1.0.5.tgz#b6fa133404a392cbc1f8c4bf63f5953351e7a776" - integrity sha512-xFbRxM1tahm08yHBP16MMjVUAvDaBMD38zsM9EMAUN61omwLmKlOpB/Zku5QkjZ8TZ4vn53pj+t518cH0S03RQ== - -balanced-match@^1.0.0: - version "1.0.2" - resolved "https://registry.yarnpkg.com/balanced-match/-/balanced-match-1.0.2.tgz#e83e3a7e3f300b34cb9d87f615fa0cbf357690ee" - integrity sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw== - -base64-arraybuffer@0.1.4: - version "0.1.4" - resolved "https://registry.yarnpkg.com/base64-arraybuffer/-/base64-arraybuffer-0.1.4.tgz#9818c79e059b1355f97e0428a017c838e90ba812" - integrity sha1-mBjHngWbE1X5fgQooBfIOOkLqBI= - -base64-js@^1.0.2, base64-js@^1.3.1: - version "1.5.1" - resolved "https://registry.yarnpkg.com/base64-js/-/base64-js-1.5.1.tgz#1b1b440160a5bf7ad40b650f095963481903930a" - integrity sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA== - -base64id@2.0.0, base64id@~2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/base64id/-/base64id-2.0.0.tgz#2770ac6bc47d312af97a8bf9a634342e0cd25cb6" - integrity sha512-lGe34o6EHj9y3Kts9R4ZYs/Gr+6N7MCaMlIFA3F1R2O5/m7K06AxfSeO5530PEERE6/WyEg3lsuyw4GHlPZHog== - -base@^0.11.1: - version "0.11.2" - resolved "https://registry.yarnpkg.com/base/-/base-0.11.2.tgz#7bde5ced145b6d551a90db87f83c558b4eb48a8f" - integrity sha512-5T6P4xPgpp0YDFvSWwEZ4NoE3aM4QBQXDzmVbraCkFj8zHM+mba8SyqB5DbZWyR7mYHo6Y7BdQo3MoA4m0TeQg== - dependencies: - cache-base "^1.0.1" - class-utils "^0.3.5" - component-emitter "^1.2.1" - define-property "^1.0.0" - isobject "^3.0.1" - mixin-deep "^1.2.0" - pascalcase "^0.1.1" - -basic-auth@~2.0.1: - version "2.0.1" - resolved "https://registry.yarnpkg.com/basic-auth/-/basic-auth-2.0.1.tgz#b998279bf47ce38344b4f3cf916d4679bbf51e3a" - integrity sha512-NF+epuEdnUYVlGuhaxbbq+dvJttwLnGY+YixlXlME5KpQ5W3CnXA5cVTneY3SPbPDRkcjMbifrwmFYcClgOZeg== - dependencies: - safe-buffer "5.1.2" - -batch@0.6.1: - version "0.6.1" - resolved "https://registry.yarnpkg.com/batch/-/batch-0.6.1.tgz#dc34314f4e679318093fc760272525f94bf25c16" - integrity sha1-3DQxT05nkxgJP8dgJyUl+UvyXBY= - -bcrypt-pbkdf@^1.0.0: - version "1.0.2" - resolved "https://registry.yarnpkg.com/bcrypt-pbkdf/-/bcrypt-pbkdf-1.0.2.tgz#a4301d389b6a43f9b67ff3ca11a3f6637e360e9e" - integrity sha1-pDAdOJtqQ/m2f/PKEaP2Y342Dp4= - dependencies: - tweetnacl "^0.14.3" - -beeper@^1.0.0: - version "1.1.1" - resolved "https://registry.yarnpkg.com/beeper/-/beeper-1.1.1.tgz#e6d5ea8c5dad001304a70b22638447f69cb2f809" - integrity sha1-5tXqjF2tABMEpwsiY4RH9pyy+Ak= - -bfj@^6.1.1: - version "6.1.2" - resolved "https://registry.yarnpkg.com/bfj/-/bfj-6.1.2.tgz#325c861a822bcb358a41c78a33b8e6e2086dde7f" - integrity sha512-BmBJa4Lip6BPRINSZ0BPEIfB1wUY/9rwbwvIHQA1KjX9om29B6id0wnWXq7m3bn5JrUVjeOTnVuhPT1FiHwPGw== - dependencies: - bluebird "^3.5.5" - check-types "^8.0.3" - hoopy "^0.1.4" - tryer "^1.0.1" - -big-integer@^1.6.17: - version "1.6.49" - resolved "https://registry.yarnpkg.com/big-integer/-/big-integer-1.6.49.tgz#f6817d3ea5d4f3fb19e24df9f4b1b4471a8328ce" - integrity sha512-KJ7VhqH+f/BOt9a3yMwJNmcZjG53ijWMTjSAGMveQWyLwqIiwkjNP5PFgDob3Snnx86SjDj6I89fIbv0dkQeNw== - -big.js@^3.1.3: - version "3.2.0" - resolved "https://registry.yarnpkg.com/big.js/-/big.js-3.2.0.tgz#a5fc298b81b9e0dca2e458824784b65c52ba588e" - integrity sha512-+hN/Zh2D08Mx65pZ/4g5bsmNiZUuChDiQfTUQ7qJr4/kuopCr88xZsAXv6mBoZEsUI4OuGHlX59qE94K2mMW8Q== - -big.js@^5.2.2: - version "5.2.2" - resolved "https://registry.yarnpkg.com/big.js/-/big.js-5.2.2.tgz#65f0af382f578bcdc742bd9c281e9cb2d7768328" - integrity sha512-vyL2OymJxmarO8gxMr0mhChsO9QGwhynfuu4+MHTAW6czfq9humCB7rKpUjDd9YUiDPU4mzpyupFSvOClAwbmQ== - -binary-extensions@^1.0.0: - version "1.13.1" - resolved "https://registry.yarnpkg.com/binary-extensions/-/binary-extensions-1.13.1.tgz#598afe54755b2868a5330d2aff9d4ebb53209b65" - integrity sha512-Un7MIEDdUC5gNpcGDV97op1Ywk748MpHcFTHoYs6qnj1Z3j7I53VG3nwZhKzoBZmbdRNnb6WRdFlwl7tSDuZGw== - -binary-extensions@^2.0.0: - version "2.2.0" - resolved "https://registry.yarnpkg.com/binary-extensions/-/binary-extensions-2.2.0.tgz#75f502eeaf9ffde42fc98829645be4ea76bd9e2d" - integrity sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA== - -binary@~0.3.0: - version "0.3.0" - resolved "https://registry.yarnpkg.com/binary/-/binary-0.3.0.tgz#9f60553bc5ce8c3386f3b553cff47462adecaa79" - integrity sha1-n2BVO8XOjDOG87VTz/R0Yq3sqnk= - dependencies: - buffers "~0.1.1" - chainsaw "~0.1.0" - -binaryextensions@^2.2.0: - version "2.3.0" - resolved "https://registry.yarnpkg.com/binaryextensions/-/binaryextensions-2.3.0.tgz#1d269cbf7e6243ea886aa41453c3651ccbe13c22" - integrity sha512-nAihlQsYGyc5Bwq6+EsubvANYGExeJKHDO3RjnvwU042fawQTQfM3Kxn7IHUXQOz4bzfwsGYYHGSvXyW4zOGLg== - -bindings@^1.5.0: - version "1.5.0" - resolved "https://registry.yarnpkg.com/bindings/-/bindings-1.5.0.tgz#10353c9e945334bc0511a6d90b38fbc7c9c504df" - integrity sha512-p2q/t/mhvuOj/UeLlV6566GD/guowlr0hHxClI0W9m7MWYkL1F0hLo+0Aexs9HSPCtR1SXQ0TD3MMKrXZajbiQ== - dependencies: - file-uri-to-path "1.0.0" - -bl@^4.0.3, bl@^4.1.0: - version "4.1.0" - resolved "https://registry.yarnpkg.com/bl/-/bl-4.1.0.tgz#451535264182bec2fbbc83a62ab98cf11d9f7b3a" - integrity sha512-1W07cM9gS6DcLperZfFSj+bWLtaPGSOHWhPiGzXmvVJbRLdG82sH/Kn8EtW1VqWVA54AKf2h5k5BbnIbwF3h6w== - dependencies: - buffer "^5.5.0" - inherits "^2.0.4" - readable-stream "^3.4.0" - -bluebird@^3.5.5: - version "3.7.2" - resolved "https://registry.yarnpkg.com/bluebird/-/bluebird-3.7.2.tgz#9f229c15be272454ffa973ace0dbee79a1b0c36f" - integrity sha512-XpNj6GDQzdfW+r2Wnn7xiSAd7TM3jzkxGXBGTtWKuSXv1xUV+azxAm8jdWZN06QTQk+2N2XB9jRDkvbmQmcRtg== - -bluebird@~3.4.1: - version "3.4.7" - resolved "https://registry.yarnpkg.com/bluebird/-/bluebird-3.4.7.tgz#f72d760be09b7f76d08ed8fae98b289a8d05fab3" - integrity sha1-9y12C+Cbf3bQjtj66Ysomo0F+rM= - -bn.js@^4.0.0, bn.js@^4.1.0, bn.js@^4.11.9: - version "4.12.0" - resolved "https://registry.yarnpkg.com/bn.js/-/bn.js-4.12.0.tgz#775b3f278efbb9718eec7361f483fb36fbbfea88" - integrity sha512-c98Bf3tPniI+scsdk237ku1Dc3ujXQTSgyiPUDEOe7tRkhrqridvh8klBv0HCEso1OLOYcHuCv/cS6DNxKH+ZA== - -bn.js@^5.0.0, bn.js@^5.1.1: - version "5.2.0" - resolved "https://registry.yarnpkg.com/bn.js/-/bn.js-5.2.0.tgz#358860674396c6997771a9d051fcc1b57d4ae002" - integrity sha512-D7iWRBvnZE8ecXiLj/9wbxH7Tk79fAh8IHaTNq1RWRixsS02W+5qS+iE9yq6RYl0asXx5tw0bLhmT5pIfbSquw== - -body-parser@1.19.0, body-parser@^1.19.0: - version "1.19.0" - resolved "https://registry.yarnpkg.com/body-parser/-/body-parser-1.19.0.tgz#96b2709e57c9c4e09a6fd66a8fd979844f69f08a" - integrity sha512-dhEPs72UPbDnAQJ9ZKMNTP6ptJaionhP5cBb541nXPlW60Jepo9RV/a4fX4XWW9CuFNK22krhrj1+rgzifNCsw== - dependencies: - bytes "3.1.0" - content-type "~1.0.4" - debug "2.6.9" - depd "~1.1.2" - http-errors "1.7.2" - iconv-lite "0.4.24" - on-finished "~2.3.0" - qs "6.7.0" - raw-body "2.4.0" - type-is "~1.6.17" - -body@^5.1.0: - version "5.1.0" - resolved "https://registry.yarnpkg.com/body/-/body-5.1.0.tgz#e4ba0ce410a46936323367609ecb4e6553125069" - integrity sha1-5LoM5BCkaTYyM2dgnstOZVMSUGk= - dependencies: - continuable-cache "^0.3.1" - error "^7.0.0" - raw-body "~1.1.0" - safe-json-parse "~1.0.1" - -brace-expansion@^1.1.7: - version "1.1.11" - resolved "https://registry.yarnpkg.com/brace-expansion/-/brace-expansion-1.1.11.tgz#3c7fcbf529d87226f3d2f52b966ff5271eb441dd" - integrity sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA== - dependencies: - balanced-match "^1.0.0" - concat-map "0.0.1" - -braces@^1.8.2: - version "1.8.5" - resolved "https://registry.yarnpkg.com/braces/-/braces-1.8.5.tgz#ba77962e12dff969d6b76711e914b737857bf6a7" - integrity sha1-uneWLhLf+WnWt2cR6RS3N4V79qc= - dependencies: - expand-range "^1.8.1" - preserve "^0.2.0" - repeat-element "^1.1.2" - -braces@^2.3.1, braces@^2.3.2: - version "2.3.2" - resolved "https://registry.yarnpkg.com/braces/-/braces-2.3.2.tgz#5979fd3f14cd531565e5fa2df1abfff1dfaee729" - integrity sha512-aNdbnj9P8PjdXU4ybaWLK2IF3jc/EoDYbC7AazW6to3TRsfXxscC9UXOB5iDiEQrkyIbWp2SLQda4+QAa7nc3w== - dependencies: - arr-flatten "^1.1.0" - array-unique "^0.3.2" - extend-shallow "^2.0.1" - fill-range "^4.0.0" - isobject "^3.0.1" - repeat-element "^1.1.2" - snapdragon "^0.8.1" - snapdragon-node "^2.0.1" - split-string "^3.0.2" - to-regex "^3.0.1" - -braces@^3.0.1, braces@^3.0.2, braces@~3.0.2: - version "3.0.2" - resolved "https://registry.yarnpkg.com/braces/-/braces-3.0.2.tgz#3454e1a462ee8d599e236df336cd9ea4f8afe107" - integrity sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A== - dependencies: - fill-range "^7.0.1" - -brorand@^1.0.1, brorand@^1.1.0: - version "1.1.0" - resolved "https://registry.yarnpkg.com/brorand/-/brorand-1.1.0.tgz#12c25efe40a45e3c323eb8675a0a0ce57b22371f" - integrity sha1-EsJe/kCkXjwyPrhnWgoM5XsiNx8= - -browser-resolve@^1.7.0: - version "1.11.3" - resolved "https://registry.yarnpkg.com/browser-resolve/-/browser-resolve-1.11.3.tgz#9b7cbb3d0f510e4cb86bdbd796124d28b5890af6" - integrity sha512-exDi1BYWB/6raKHmDTCicQfTkqwN5fioMFV4j8BsfMU4R2DK/QfZfK7kOVkmWCNANf0snkBzqGqAJBao9gZMdQ== - dependencies: - resolve "1.1.7" - -browser-stdout@1.3.1: - version "1.3.1" - resolved "https://registry.yarnpkg.com/browser-stdout/-/browser-stdout-1.3.1.tgz#baa559ee14ced73452229bad7326467c61fabd60" - integrity sha512-qhAVI1+Av2X7qelOfAIYwXONood6XlZE/fXaBSmW/T5SzLAmCgzi+eiWE7fUvbHaeNBQH13UftjpXxsfLkMpgw== - -browserify-aes@0.4.0: - version "0.4.0" - resolved "https://registry.yarnpkg.com/browserify-aes/-/browserify-aes-0.4.0.tgz#067149b668df31c4b58533e02d01e806d8608e2c" - integrity sha1-BnFJtmjfMcS1hTPgLQHoBthgjiw= - dependencies: - inherits "^2.0.1" - -browserify-aes@^1.0.0, browserify-aes@^1.0.4: - version "1.2.0" - resolved "https://registry.yarnpkg.com/browserify-aes/-/browserify-aes-1.2.0.tgz#326734642f403dabc3003209853bb70ad428ef48" - integrity sha512-+7CHXqGuspUn/Sl5aO7Ea0xWGAtETPXNSAjHo48JfLdPWcMng33Xe4znFvQweqc/uzk5zSOI3H52CYnjCfb5hA== - dependencies: - buffer-xor "^1.0.3" - cipher-base "^1.0.0" - create-hash "^1.1.0" - evp_bytestokey "^1.0.3" - inherits "^2.0.1" - safe-buffer "^5.0.1" - -browserify-cipher@^1.0.0: - version "1.0.1" - resolved "https://registry.yarnpkg.com/browserify-cipher/-/browserify-cipher-1.0.1.tgz#8d6474c1b870bfdabcd3bcfcc1934a10e94f15f0" - integrity sha512-sPhkz0ARKbf4rRQt2hTpAHqn47X3llLkUGn+xEJzLjwY8LRs2p0v7ljvI5EyoRO/mexrNunNECisZs+gw2zz1w== - dependencies: - browserify-aes "^1.0.4" - browserify-des "^1.0.0" - evp_bytestokey "^1.0.0" - -browserify-des@^1.0.0: - version "1.0.2" - resolved "https://registry.yarnpkg.com/browserify-des/-/browserify-des-1.0.2.tgz#3af4f1f59839403572f1c66204375f7a7f703e9c" - integrity sha512-BioO1xf3hFwz4kc6iBhI3ieDFompMhrMlnDFC4/0/vd5MokpuAc3R+LYbwTA9A5Yc9pq9UYPqffKpW2ObuwX5A== - dependencies: - cipher-base "^1.0.1" - des.js "^1.0.0" - inherits "^2.0.1" - safe-buffer "^5.1.2" - -browserify-rsa@^4.0.0, browserify-rsa@^4.0.1: - version "4.1.0" - resolved "https://registry.yarnpkg.com/browserify-rsa/-/browserify-rsa-4.1.0.tgz#b2fd06b5b75ae297f7ce2dc651f918f5be158c8d" - integrity sha512-AdEER0Hkspgno2aR97SAf6vi0y0k8NuOpGnVH3O99rcA5Q6sh8QxcngtHuJ6uXwnfAXNM4Gn1Gb7/MV1+Ymbog== - dependencies: - bn.js "^5.0.0" - randombytes "^2.0.1" - -browserify-sign@^4.0.0: - version "4.2.1" - resolved "https://registry.yarnpkg.com/browserify-sign/-/browserify-sign-4.2.1.tgz#eaf4add46dd54be3bb3b36c0cf15abbeba7956c3" - integrity sha512-/vrA5fguVAKKAVTNJjgSm1tRQDHUU6DbwO9IROu/0WAzC8PKhucDSh18J0RMvVeHAn5puMd+QHC2erPRNf8lmg== - dependencies: - bn.js "^5.1.1" - browserify-rsa "^4.0.1" - create-hash "^1.2.0" - create-hmac "^1.1.7" - elliptic "^6.5.3" - inherits "^2.0.4" - parse-asn1 "^5.1.5" - readable-stream "^3.6.0" - safe-buffer "^5.2.0" - -browserify-zlib@^0.1.4: - version "0.1.4" - resolved "https://registry.yarnpkg.com/browserify-zlib/-/browserify-zlib-0.1.4.tgz#bb35f8a519f600e0fa6b8485241c979d0141fb2d" - integrity sha1-uzX4pRn2AOD6a4SFJByXnQFB+y0= - dependencies: - pako "~0.2.0" - -browserify-zlib@^0.2.0: - version "0.2.0" - resolved "https://registry.yarnpkg.com/browserify-zlib/-/browserify-zlib-0.2.0.tgz#2869459d9aa3be245fe8fe2ca1f46e2e7f54d73f" - integrity sha512-Z942RysHXmJrhqk88FmKBVq/v5tqmSkDz7p54G/MGyjMnCFFnC79XWNbg+Vta8W6Wb2qtSZTSxIGkJrRpCFEiA== - dependencies: - pako "~1.0.5" - -browserslist@^4.16.6, browserslist@^4.17.1: - version "4.17.2" - resolved "https://registry.yarnpkg.com/browserslist/-/browserslist-4.17.2.tgz#aa15dbd2fab399a399fe4df601bb09363c5458a6" - integrity sha512-jSDZyqJmkKMEMi7SZAgX5UltFdR5NAO43vY0AwTpu4X3sGH7GLLQ83KiUomgrnvZRCeW0yPPnKqnxPqQOER9zQ== - dependencies: - caniuse-lite "^1.0.30001261" - electron-to-chromium "^1.3.854" - escalade "^3.1.1" - nanocolors "^0.2.12" - node-releases "^1.1.76" - -browserstack-local@^1.4.5: - version "1.4.8" - resolved "https://registry.yarnpkg.com/browserstack-local/-/browserstack-local-1.4.8.tgz#07f74a19b324cf2de69ffe65f9c2baa3a2dd9a0e" - integrity sha512-s+mc3gTOJwELdLWi4qFVKtGwMbb5JWsR+JxKlMaJkRJxoZ0gg3WREgPxAN0bm6iU5+S4Bi0sz0oxBRZT8BiNsQ== - dependencies: - https-proxy-agent "^4.0.0" - is-running "^2.1.0" - ps-tree "=1.2.0" - temp-fs "^0.9.9" - -browserstack@~1.5.1: - version "1.5.3" - resolved "https://registry.yarnpkg.com/browserstack/-/browserstack-1.5.3.tgz#93ab48799a12ef99dbd074dd595410ddb196a7ac" - integrity sha512-AO+mECXsW4QcqC9bxwM29O7qWa7bJT94uBFzeb5brylIQwawuEziwq20dPYbins95GlWzOawgyDNdjYAo32EKg== - dependencies: - https-proxy-agent "^2.2.1" - -browserstacktunnel-wrapper@~2.0.2: - version "2.0.4" - resolved "https://registry.yarnpkg.com/browserstacktunnel-wrapper/-/browserstacktunnel-wrapper-2.0.4.tgz#0ebffd3d6311b8526c30d8b430fdc651a535eebb" - integrity sha512-GCV599FUUxNOCFl3WgPnfc5dcqq9XTmMXoxWpqkvmk0R9TOIoqmjENNU6LY6DtgIL6WfBVbg/jmWtnM5K6UYSg== - dependencies: - https-proxy-agent "^2.2.1" - unzipper "^0.9.3" - -buffer-crc32@^0.2.1, buffer-crc32@^0.2.13, buffer-crc32@~0.2.3: - version "0.2.13" - resolved "https://registry.yarnpkg.com/buffer-crc32/-/buffer-crc32-0.2.13.tgz#0d333e3f00eac50aa1454abd30ef8c2a5d9a7242" - integrity sha1-DTM+PwDqxQqhRUq9MO+MKl2ackI= - -buffer-equal@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/buffer-equal/-/buffer-equal-1.0.0.tgz#59616b498304d556abd466966b22eeda3eca5fbe" - integrity sha1-WWFrSYME1Var1GaWayLu2j7KX74= - -buffer-from@^1.0.0: - version "1.1.2" - resolved "https://registry.yarnpkg.com/buffer-from/-/buffer-from-1.1.2.tgz#2b146a6fd72e80b4f55d255f35ed59a3a9a41bd5" - integrity sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ== - -buffer-indexof-polyfill@~1.0.0: - version "1.0.2" - resolved "https://registry.yarnpkg.com/buffer-indexof-polyfill/-/buffer-indexof-polyfill-1.0.2.tgz#d2732135c5999c64b277fcf9b1abe3498254729c" - integrity sha512-I7wzHwA3t1/lwXQh+A5PbNvJxgfo5r3xulgpYDB5zckTu/Z9oUK9biouBKQUjEqzaz3HnAT6TYoovmE+GqSf7A== - -buffer-shims@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/buffer-shims/-/buffer-shims-1.0.0.tgz#9978ce317388c649ad8793028c3477ef044a8b51" - integrity sha1-mXjOMXOIxkmth5MCjDR37wRKi1E= - -buffer-xor@^1.0.3: - version "1.0.3" - resolved "https://registry.yarnpkg.com/buffer-xor/-/buffer-xor-1.0.3.tgz#26e61ed1422fb70dd42e6e36729ed51d855fe8d9" - integrity sha1-JuYe0UIvtw3ULm42cp7VHYVf6Nk= - -buffer@^4.3.0, buffer@^4.9.0: - version "4.9.2" - resolved "https://registry.yarnpkg.com/buffer/-/buffer-4.9.2.tgz#230ead344002988644841ab0244af8c44bbe3ef8" - integrity sha512-xq+q3SRMOxGivLhBNaUdC64hDTQwejJ+H0T/NB1XMtTVEwNTrfFF3gAxiyW0Bu/xWEGhjVKgUcMhCrUy2+uCWg== - dependencies: - base64-js "^1.0.2" - ieee754 "^1.1.4" - isarray "^1.0.0" - -buffer@^5.2.1, buffer@^5.5.0: - version "5.7.1" - resolved "https://registry.yarnpkg.com/buffer/-/buffer-5.7.1.tgz#ba62e7c13133053582197160851a8f648e99eed0" - integrity sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ== - dependencies: - base64-js "^1.3.1" - ieee754 "^1.1.13" - -buffers@~0.1.1: - version "0.1.1" - resolved "https://registry.yarnpkg.com/buffers/-/buffers-0.1.1.tgz#b24579c3bed4d6d396aeee6d9a8ae7f5482ab7bb" - integrity sha1-skV5w77U1tOWru5tmorn9Ugqt7s= - -builtin-status-codes@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/builtin-status-codes/-/builtin-status-codes-3.0.0.tgz#85982878e21b98e1c66425e03d0174788f569ee8" - integrity sha1-hZgoeOIbmOHGZCXgPQF0eI9Wnug= - -bytes@1: - version "1.0.0" - resolved "https://registry.yarnpkg.com/bytes/-/bytes-1.0.0.tgz#3569ede8ba34315fab99c3e92cb04c7220de1fa8" - integrity sha1-NWnt6Lo0MV+rmcPpLLBMciDeH6g= - -bytes@3.1.0: - version "3.1.0" - resolved "https://registry.yarnpkg.com/bytes/-/bytes-3.1.0.tgz#f6cf7933a360e0588fa9fde85651cdc7f805d1f6" - integrity sha512-zauLjrfCG+xvoyaqLoV8bLVXXNGC4JqlxFCutSDWA6fJrTo2ZuvLYTqZ7aHBLZSMOopbzwv8f+wZcVzfVTI2Dg== - -cac@^3.0.3: - version "3.0.4" - resolved "https://registry.yarnpkg.com/cac/-/cac-3.0.4.tgz#6d24ceec372efe5c9b798808bc7f49b47242a4ef" - integrity sha1-bSTO7Dcu/lybeYgIvH9JtHJCpO8= - dependencies: - camelcase-keys "^3.0.0" - chalk "^1.1.3" - indent-string "^3.0.0" - minimist "^1.2.0" - read-pkg-up "^1.0.1" - suffix "^0.1.0" - text-table "^0.2.0" - -cache-base@^1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/cache-base/-/cache-base-1.0.1.tgz#0a7f46416831c8b662ee36fe4e7c59d76f666ab2" - integrity sha512-AKcdTnFSWATd5/GCPRxr2ChwIJ85CeyrEyjRHlKxQ56d4XJMGym0uAiKn0xbLOGOl3+yRpOTi484dVCEc5AUzQ== - dependencies: - collection-visit "^1.0.0" - component-emitter "^1.2.1" - get-value "^2.0.6" - has-value "^1.0.0" - isobject "^3.0.1" - set-value "^2.0.0" - to-object-path "^0.3.0" - union-value "^1.0.0" - unset-value "^1.0.0" - -cacheable-lookup@^5.0.3: - version "5.0.4" - resolved "https://registry.yarnpkg.com/cacheable-lookup/-/cacheable-lookup-5.0.4.tgz#5a6b865b2c44357be3d5ebc2a467b032719a7005" - integrity sha512-2/kNscPhpcxrOigMZzbiWF7dz8ilhb/nIHU3EyZiXWXpeq/au8qJ8VhdftMkty3n7Gj6HIGalQG8oiBNB3AJgA== - -cacheable-request@^7.0.1: - version "7.0.2" - resolved "https://registry.yarnpkg.com/cacheable-request/-/cacheable-request-7.0.2.tgz#ea0d0b889364a25854757301ca12b2da77f91d27" - integrity sha512-pouW8/FmiPQbuGpkXQ9BAPv/Mo5xDGANgSNXzTzJ8DrKGuXOssM4wIQRjfanNRh3Yu5cfYPvcorqbhg2KIJtew== - dependencies: - clone-response "^1.0.2" - get-stream "^5.1.0" - http-cache-semantics "^4.0.0" - keyv "^4.0.0" - lowercase-keys "^2.0.0" - normalize-url "^6.0.1" - responselike "^2.0.0" - -cached-path-relative@^1.0.0: - version "1.0.2" - resolved "https://registry.yarnpkg.com/cached-path-relative/-/cached-path-relative-1.0.2.tgz#a13df4196d26776220cc3356eb147a52dba2c6db" - integrity sha512-5r2GqsoEb4qMTTN9J+WzXfjov+hjxT+j3u5K+kIVNIwAd99DLCJE9pBIMP1qVeybV6JiijL385Oz0DcYxfbOIg== - -call-bind@^1.0.0, call-bind@^1.0.2: - version "1.0.2" - resolved "https://registry.yarnpkg.com/call-bind/-/call-bind-1.0.2.tgz#b1d4e89e688119c3c9a903ad30abb2f6a919be3c" - integrity sha512-7O+FbCihrB5WGbFYesctwmTKae6rOiIzmz1icreWJ+0aA7LJfuqhEso2T9ncpcFtzMQtzXf2QGGueWJGTYsqrA== - dependencies: - function-bind "^1.1.1" - get-intrinsic "^1.0.2" - -caller-path@^0.1.0: - version "0.1.0" - resolved "https://registry.yarnpkg.com/caller-path/-/caller-path-0.1.0.tgz#94085ef63581ecd3daa92444a8fe94e82577751f" - integrity sha1-lAhe9jWB7NPaqSREqP6U6CV3dR8= - dependencies: - callsites "^0.2.0" - -callsites@^0.2.0: - version "0.2.0" - resolved "https://registry.yarnpkg.com/callsites/-/callsites-0.2.0.tgz#afab96262910a7f33c19a5775825c69f34e350ca" - integrity sha1-r6uWJikQp/M8GaV3WCXGnzTjUMo= - -callsites@^3.0.0: - version "3.1.0" - resolved "https://registry.yarnpkg.com/callsites/-/callsites-3.1.0.tgz#b3630abd8943432f54b3f0519238e33cd7df2f73" - integrity sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ== - -camelcase-keys@^2.0.0: - version "2.1.0" - resolved "https://registry.yarnpkg.com/camelcase-keys/-/camelcase-keys-2.1.0.tgz#308beeaffdf28119051efa1d932213c91b8f92e7" - integrity sha1-MIvur/3ygRkFHvodkyITyRuPkuc= - dependencies: - camelcase "^2.0.0" - map-obj "^1.0.0" - -camelcase-keys@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/camelcase-keys/-/camelcase-keys-3.0.0.tgz#fc0c6c360363f7377e3793b9a16bccf1070c1ca4" - integrity sha1-/AxsNgNj9zd+N5O5oWvM8QcMHKQ= - dependencies: - camelcase "^3.0.0" - map-obj "^1.0.0" - -camelcase-keys@^6.2.2: - version "6.2.2" - resolved "https://registry.yarnpkg.com/camelcase-keys/-/camelcase-keys-6.2.2.tgz#5e755d6ba51aa223ec7d3d52f25778210f9dc3c0" - integrity sha512-YrwaA0vEKazPBkn0ipTiMpSajYDSe+KjQfrjhcBMxJt/znbvlHd8Pw/Vamaz5EB4Wfhs3SUR3Z9mwRu/P3s3Yg== - dependencies: - camelcase "^5.3.1" - map-obj "^4.0.0" - quick-lru "^4.0.1" - -camelcase@^1.0.2: - version "1.2.1" - resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-1.2.1.tgz#9bb5304d2e0b56698b2c758b08a3eaa9daa58a39" - integrity sha1-m7UwTS4LVmmLLHWLCKPqqdqlijk= - -camelcase@^2.0.0: - version "2.1.1" - resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-2.1.1.tgz#7c1d16d679a1bbe59ca02cacecfb011e201f5a1f" - integrity sha1-fB0W1nmhu+WcoCys7PsBHiAfWh8= - -camelcase@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-3.0.0.tgz#32fc4b9fcdaf845fcdf7e73bb97cac2261f0ab0a" - integrity sha1-MvxLn82vhF/N9+c7uXysImHwqwo= - -camelcase@^4.1.0: - version "4.1.0" - resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-4.1.0.tgz#d545635be1e33c542649c69173e5de6acfae34dd" - integrity sha1-1UVjW+HjPFQmScaRc+Xeas+uNN0= - -camelcase@^5.0.0, camelcase@^5.3.1: - version "5.3.1" - resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-5.3.1.tgz#e3c9b31569e106811df242f715725a1f4c494320" - integrity sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg== - -camelcase@^6.0.0: - version "6.2.0" - resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-6.2.0.tgz#924af881c9d525ac9d87f40d964e5cea982a1809" - integrity sha512-c7wVvbw3f37nuobQNtgsgG9POC9qMbNuMQmTCqZv23b6MIz0fcYpBiOlv9gEN/hdLdnZTDQhg6e9Dq5M1vKvfg== - -caniuse-lite@^1.0.30001261: - version "1.0.30001264" - resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001264.tgz#88f625a60efb6724c7c62ac698bc8dbd9757e55b" - integrity sha512-Ftfqqfcs/ePiUmyaySsQ4PUsdcYyXG2rfoBVsk3iY1ahHaJEw65vfb7Suzqm+cEkwwPIv/XWkg27iCpRavH4zA== - -caseless@~0.12.0: - version "0.12.0" - resolved "https://registry.yarnpkg.com/caseless/-/caseless-0.12.0.tgz#1b681c21ff84033c826543090689420d187151dc" - integrity sha1-G2gcIf+EAzyCZUMJBolCDRhxUdw= - -ccount@^1.0.0: - version "1.1.0" - resolved "https://registry.yarnpkg.com/ccount/-/ccount-1.1.0.tgz#246687debb6014735131be8abab2d93898f8d043" - integrity sha512-vlNK021QdI7PNeiUh/lKkC/mNHHfV0m/Ad5JoI0TYtlBnJAslM/JIkm/tGC88bkLIwO6OQ5uV6ztS6kVAtCDlg== - -center-align@^0.1.1: - version "0.1.3" - resolved "https://registry.yarnpkg.com/center-align/-/center-align-0.1.3.tgz#aa0d32629b6ee972200411cbd4461c907bc2b7ad" - integrity sha1-qg0yYptu6XIgBBHL1EYckHvCt60= - dependencies: - align-text "^0.1.3" - lazy-cache "^1.0.3" - -chai@^4.2.0: - version "4.3.4" - resolved "https://registry.yarnpkg.com/chai/-/chai-4.3.4.tgz#b55e655b31e1eac7099be4c08c21964fce2e6c49" - integrity sha512-yS5H68VYOCtN1cjfwumDSuzn/9c+yza4f3reKXlE5rUg7SFcCEy90gJvydNgOYtblyf4Zi6jIWRnXOgErta0KA== - dependencies: - assertion-error "^1.1.0" - check-error "^1.0.2" - deep-eql "^3.0.1" - get-func-name "^2.0.0" - pathval "^1.1.1" - type-detect "^4.0.5" - -chainsaw@~0.1.0: - version "0.1.0" - resolved "https://registry.yarnpkg.com/chainsaw/-/chainsaw-0.1.0.tgz#5eab50b28afe58074d0d58291388828b5e5fbc98" - integrity sha1-XqtQsor+WAdNDVgpE4iCi15fvJg= - dependencies: - traverse ">=0.3.0 <0.4" - -chalk@^0.5.0: - version "0.5.1" - resolved "https://registry.yarnpkg.com/chalk/-/chalk-0.5.1.tgz#663b3a648b68b55d04690d49167aa837858f2174" - integrity sha1-Zjs6ZItotV0EaQ1JFnqoN4WPIXQ= - dependencies: - ansi-styles "^1.1.0" - escape-string-regexp "^1.0.0" - has-ansi "^0.1.0" - strip-ansi "^0.3.0" - supports-color "^0.2.0" - -chalk@^1.0.0, chalk@^1.1.3: - version "1.1.3" - resolved "https://registry.yarnpkg.com/chalk/-/chalk-1.1.3.tgz#a8115c55e4a702fe4d150abd3872822a7e09fc98" - integrity sha1-qBFcVeSnAv5NFQq9OHKCKn4J/Jg= - dependencies: - ansi-styles "^2.2.1" - escape-string-regexp "^1.0.2" - has-ansi "^2.0.0" - strip-ansi "^3.0.0" - supports-color "^2.0.0" - -chalk@^2.0.0, chalk@^2.0.1, chalk@^2.1.0, chalk@^2.3.0, chalk@^2.4.1, chalk@^2.4.2: - version "2.4.2" - resolved "https://registry.yarnpkg.com/chalk/-/chalk-2.4.2.tgz#cd42541677a54333cf541a49108c1432b44c9424" - integrity sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ== - dependencies: - ansi-styles "^3.2.1" - escape-string-regexp "^1.0.5" - supports-color "^5.3.0" - -chalk@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/chalk/-/chalk-3.0.0.tgz#3f73c2bf526591f574cc492c51e2456349f844e4" - integrity sha512-4D3B6Wf41KOYRFdszmDqMCGq5VV/uMAB273JILmO+3jAlh8X4qDtdtgCR3fxtbLEMzSx22QdhnDcJvu2u1fVwg== - dependencies: - ansi-styles "^4.1.0" - supports-color "^7.1.0" - -chalk@^4.0.0, chalk@^4.1.0, chalk@^4.1.1: - version "4.1.2" - resolved "https://registry.yarnpkg.com/chalk/-/chalk-4.1.2.tgz#aac4e2b7734a740867aeb16bf02aad556a1e7a01" - integrity sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA== - dependencies: - ansi-styles "^4.1.0" - supports-color "^7.1.0" - -character-entities-html4@^1.0.0: - version "1.1.4" - resolved "https://registry.yarnpkg.com/character-entities-html4/-/character-entities-html4-1.1.4.tgz#0e64b0a3753ddbf1fdc044c5fd01d0199a02e125" - integrity sha512-HRcDxZuZqMx3/a+qrzxdBKBPUpxWEq9xw2OPZ3a/174ihfrQKVsFhqtthBInFy1zZ9GgZyFXOatNujm8M+El3g== - -character-entities-legacy@^1.0.0: - version "1.1.4" - resolved "https://registry.yarnpkg.com/character-entities-legacy/-/character-entities-legacy-1.1.4.tgz#94bc1845dce70a5bb9d2ecc748725661293d8fc1" - integrity sha512-3Xnr+7ZFS1uxeiUDvV02wQ+QDbc55o97tIV5zHScSPJpcLm/r0DFPcoY3tYRp+VZukxuMeKgXYmsXQHO05zQeA== - -character-entities@^1.0.0: - version "1.2.4" - resolved "https://registry.yarnpkg.com/character-entities/-/character-entities-1.2.4.tgz#e12c3939b7eaf4e5b15e7ad4c5e28e1d48c5b16b" - integrity sha512-iBMyeEHxfVnIakwOuDXpVkc54HijNgCyQB2w0VfGQThle6NXn50zU6V/u+LDhxHcDUPojn6Kpga3PTAD8W1bQw== - -character-reference-invalid@^1.0.0: - version "1.1.4" - resolved "https://registry.yarnpkg.com/character-reference-invalid/-/character-reference-invalid-1.1.4.tgz#083329cda0eae272ab3dbbf37e9a382c13af1560" - integrity sha512-mKKUkUbhPpQlCOfIuZkvSEgktjPFIsZKRRbC6KWVEMvlzblj3i3asQv5ODsrwt0N3pHAEvjP8KTQPHkp0+6jOg== - -chardet@^0.4.0: - version "0.4.2" - resolved "https://registry.yarnpkg.com/chardet/-/chardet-0.4.2.tgz#b5473b33dc97c424e5d98dc87d55d4d8a29c8bf2" - integrity sha1-tUc7M9yXxCTl2Y3IfVXU2KKci/I= - -chardet@^0.7.0: - version "0.7.0" - resolved "https://registry.yarnpkg.com/chardet/-/chardet-0.7.0.tgz#90094849f0937f2eedc2425d0d28a9e5f0cbad9e" - integrity sha512-mT8iDcrh03qDGRRmoA2hmBJnxpllMR+0/0qlzjqZES6NdiWDcZkCNAk4rPFZ9Q85r27unkiNNg8ZOiwZXBHwcA== - -check-error@^1.0.2: - version "1.0.2" - resolved "https://registry.yarnpkg.com/check-error/-/check-error-1.0.2.tgz#574d312edd88bb5dd8912e9286dd6c0aed4aac82" - integrity sha1-V00xLt2Iu13YkS6Sht1sCu1KrII= - -check-types@^8.0.3: - version "8.0.3" - resolved "https://registry.yarnpkg.com/check-types/-/check-types-8.0.3.tgz#3356cca19c889544f2d7a95ed49ce508a0ecf552" - integrity sha512-YpeKZngUmG65rLudJ4taU7VLkOCTMhNl/u4ctNC56LQS/zJTyNH0Lrtwm1tfTsbLlwvlfsA2d1c8vCf/Kh2KwQ== - -chokidar@3.5.2, chokidar@^3.0.0, chokidar@^3.4.0, chokidar@^3.4.1, chokidar@^3.5.1: - version "3.5.2" - resolved "https://registry.yarnpkg.com/chokidar/-/chokidar-3.5.2.tgz#dba3976fcadb016f66fd365021d91600d01c1e75" - integrity sha512-ekGhOnNVPgT77r4K/U3GDhu+FQ2S8TnK/s2KbIGXi0SZWuwkZ2QNyfWdZW+TVfn84DpEP7rLeCt2UI6bJ8GwbQ== - dependencies: - anymatch "~3.1.2" - braces "~3.0.2" - glob-parent "~5.1.2" - is-binary-path "~2.1.0" - is-glob "~4.0.1" - normalize-path "~3.0.0" - readdirp "~3.6.0" - optionalDependencies: - fsevents "~2.3.2" - -chokidar@^1.0.0: - version "1.7.0" - resolved "https://registry.yarnpkg.com/chokidar/-/chokidar-1.7.0.tgz#798e689778151c8076b4b360e5edd28cda2bb468" - integrity sha1-eY5ol3gVHIB2tLNg5e3SjNortGg= - dependencies: - anymatch "^1.3.0" - async-each "^1.0.0" - glob-parent "^2.0.0" - inherits "^2.0.1" - is-binary-path "^1.0.0" - is-glob "^2.0.0" - path-is-absolute "^1.0.0" - readdirp "^2.0.0" - optionalDependencies: - fsevents "^1.0.0" - -chokidar@^2.0.0, chokidar@^2.1.8: - version "2.1.8" - resolved "https://registry.yarnpkg.com/chokidar/-/chokidar-2.1.8.tgz#804b3a7b6a99358c3c5c61e71d8728f041cff917" - integrity sha512-ZmZUazfOzf0Nve7duiCKD23PFSCs4JPoYyccjUFF3aQkQadqBhfzhjkwBH2mNOG9cTBwhamM37EIsIkZw3nRgg== - dependencies: - anymatch "^2.0.0" - async-each "^1.0.1" - braces "^2.3.2" - glob-parent "^3.1.0" - inherits "^2.0.3" - is-binary-path "^1.0.0" - is-glob "^4.0.0" - normalize-path "^3.0.0" - path-is-absolute "^1.0.0" - readdirp "^2.2.1" - upath "^1.1.1" - optionalDependencies: - fsevents "^1.2.7" - -chownr@^1.1.1: - version "1.1.4" - resolved "https://registry.yarnpkg.com/chownr/-/chownr-1.1.4.tgz#6fc9d7b42d32a583596337666e7d08084da2cc6b" - integrity sha512-jJ0bqzaylmJtVnNgzTeSOs8DPavpbYgEr/b0YL8/2GO3xJEhInFmhKMUnEJQjZumK7KXGFhUy89PrsJWlakBVg== - -chrome-launcher@^0.14.0: - version "0.14.0" - resolved "https://registry.yarnpkg.com/chrome-launcher/-/chrome-launcher-0.14.0.tgz#de8d8a534ccaeea0f36ea8dc12dd99e3169f3320" - integrity sha512-W//HpflaW6qBGrmuskup7g+XJZN6w03ko9QSIe5CtcTal2u0up5SeReK3Ll1Why4Ey8dPkv8XSodZyHPnGbVHQ== - dependencies: - "@types/node" "*" - escape-string-regexp "^4.0.0" - is-wsl "^2.2.0" - lighthouse-logger "^1.0.0" - -cipher-base@^1.0.0, cipher-base@^1.0.1, cipher-base@^1.0.3: - version "1.0.4" - resolved "https://registry.yarnpkg.com/cipher-base/-/cipher-base-1.0.4.tgz#8760e4ecc272f4c363532f926d874aae2c1397de" - integrity sha512-Kkht5ye6ZGmwv40uUDZztayT2ThLQGfnj/T71N/XzeZeo3nf8foyW7zGTsPYkEya3m5f3cAypH+qe7YOrM1U2Q== - dependencies: - inherits "^2.0.1" - safe-buffer "^5.0.1" - -circular-json@^0.3.1: - version "0.3.3" - resolved "https://registry.yarnpkg.com/circular-json/-/circular-json-0.3.3.tgz#815c99ea84f6809529d2f45791bdf82711352d66" - integrity sha512-UZK3NBx2Mca+b5LsG7bY183pHWt5Y1xts4P3Pz7ENTwGVnJOUWbRb3ocjvX7hx9tq/yTAdclXm9sZ38gNuem4A== - -class-utils@^0.3.5: - version "0.3.6" - resolved "https://registry.yarnpkg.com/class-utils/-/class-utils-0.3.6.tgz#f93369ae8b9a7ce02fd41faad0ca83033190c463" - integrity sha512-qOhPa/Fj7s6TY8H8esGu5QNpMMQxz79h+urzrNYN6mn+9BnxlDGf5QZ+XeCDsxSjPqsSR56XOZOJmpeurnLMeg== - dependencies: - arr-union "^3.1.0" - define-property "^0.2.5" - isobject "^3.0.0" - static-extend "^0.1.1" - -cli-cursor@^2.1.0: - version "2.1.0" - resolved "https://registry.yarnpkg.com/cli-cursor/-/cli-cursor-2.1.0.tgz#b35dac376479facc3e94747d41d0d0f5238ffcb5" - integrity sha1-s12sN2R5+sw+lHR9QdDQ9SOP/LU= - dependencies: - restore-cursor "^2.0.0" - -cli-cursor@^3.1.0: - version "3.1.0" - resolved "https://registry.yarnpkg.com/cli-cursor/-/cli-cursor-3.1.0.tgz#264305a7ae490d1d03bf0c9ba7c925d1753af307" - integrity sha512-I/zHAwsKf9FqGoXM4WWRACob9+SNukZTd94DWF57E4toouRulbCxcUh6RKUEOQlYTHJnzkPMySvPNaaSLNfLZw== - dependencies: - restore-cursor "^3.1.0" - -cli-spinners@^2.1.0, cli-spinners@^2.5.0: - version "2.6.1" - resolved "https://registry.yarnpkg.com/cli-spinners/-/cli-spinners-2.6.1.tgz#adc954ebe281c37a6319bfa401e6dd2488ffb70d" - integrity sha512-x/5fWmGMnbKQAaNwN+UZlV79qBLM9JFnJuJ03gIi5whrob0xV0ofNVHy9DhwGdsMJQc2OKv0oGmLzvaqvAVv+g== - -cli-width@^2.0.0: - version "2.2.1" - resolved "https://registry.yarnpkg.com/cli-width/-/cli-width-2.2.1.tgz#b0433d0b4e9c847ef18868a4ef16fd5fc8271c48" - integrity sha512-GRMWDxpOB6Dgk2E5Uo+3eEBvtOOlimMmpbFiKuLFnQzYDavtLFY3K5ona41jgN/WdRZtG7utuVSVTL4HbZHGkw== - -cli-width@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/cli-width/-/cli-width-3.0.0.tgz#a2f48437a2caa9a22436e794bf071ec9e61cedf6" - integrity sha512-FxqpkPPwu1HjuN93Omfm4h8uIanXofW0RxVEW3k5RKx+mJJYSthzNhp32Kzxxy3YAEZ/Dc/EWN1vZRY0+kOhbw== - -cliui@^2.1.0: - version "2.1.0" - resolved "https://registry.yarnpkg.com/cliui/-/cliui-2.1.0.tgz#4b475760ff80264c762c3a1719032e91c7fea0d1" - integrity sha1-S0dXYP+AJkx2LDoXGQMukcf+oNE= - dependencies: - center-align "^0.1.1" - right-align "^0.1.1" - wordwrap "0.0.2" - -cliui@^3.2.0: - version "3.2.0" - resolved "https://registry.yarnpkg.com/cliui/-/cliui-3.2.0.tgz#120601537a916d29940f934da3b48d585a39213d" - integrity sha1-EgYBU3qRbSmUD5NNo7SNWFo5IT0= - dependencies: - string-width "^1.0.1" - strip-ansi "^3.0.1" - wrap-ansi "^2.0.0" - -cliui@^6.0.0: - version "6.0.0" - resolved "https://registry.yarnpkg.com/cliui/-/cliui-6.0.0.tgz#511d702c0c4e41ca156d7d0e96021f23e13225b1" - integrity sha512-t6wbgtoCXvAzst7QgXxJYqPt0usEfbgQdftEPbLL/cvv6HPE5VgvqCuAIDR0NgU52ds6rFwqrgakNLrHEjCbrQ== - dependencies: - string-width "^4.2.0" - strip-ansi "^6.0.0" - wrap-ansi "^6.2.0" - -cliui@^7.0.2: - version "7.0.4" - resolved "https://registry.yarnpkg.com/cliui/-/cliui-7.0.4.tgz#a0265ee655476fc807aea9df3df8df7783808b4f" - integrity sha512-OcRE68cOsVMXp1Yvonl/fzkQOyjLSu/8bhPDfQt0e0/Eb283TKP20Fs2MqoPsr9SwA595rRCA+QMzYc9nBP+JQ== - dependencies: - string-width "^4.2.0" - strip-ansi "^6.0.0" - wrap-ansi "^7.0.0" - -clone-buffer@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/clone-buffer/-/clone-buffer-1.0.0.tgz#e3e25b207ac4e701af721e2cb5a16792cac3dc58" - integrity sha1-4+JbIHrE5wGvch4staFnksrD3Fg= - -clone-response@^1.0.2: - version "1.0.2" - resolved "https://registry.yarnpkg.com/clone-response/-/clone-response-1.0.2.tgz#d1dc973920314df67fbeb94223b4ee350239e96b" - integrity sha1-0dyXOSAxTfZ/vrlCI7TuNQI56Ws= - dependencies: - mimic-response "^1.0.0" - -clone-stats@^0.0.1, clone-stats@~0.0.1: - version "0.0.1" - resolved "https://registry.yarnpkg.com/clone-stats/-/clone-stats-0.0.1.tgz#b88f94a82cf38b8791d58046ea4029ad88ca99d1" - integrity sha1-uI+UqCzzi4eR1YBG6kAprYjKmdE= - -clone-stats@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/clone-stats/-/clone-stats-1.0.0.tgz#b3782dff8bb5474e18b9b6bf0fdfe782f8777680" - integrity sha1-s3gt/4u1R04Yuba/D9/ngvh3doA= - -clone@^1.0.0, clone@^1.0.2: - version "1.0.4" - resolved "https://registry.yarnpkg.com/clone/-/clone-1.0.4.tgz#da309cc263df15994c688ca902179ca3c7cd7c7e" - integrity sha1-2jCcwmPfFZlMaIypAheco8fNfH4= - -clone@^2.1.1: - version "2.1.2" - resolved "https://registry.yarnpkg.com/clone/-/clone-2.1.2.tgz#1b7f4b9f591f1e8f83670401600345a02887435f" - integrity sha1-G39Ln1kfHo+DZwQBYANFoCiHQ18= - -cloneable-readable@^1.0.0: - version "1.1.3" - resolved "https://registry.yarnpkg.com/cloneable-readable/-/cloneable-readable-1.1.3.tgz#120a00cb053bfb63a222e709f9683ea2e11d8cec" - integrity sha512-2EF8zTQOxYq70Y4XKtorQupqF0m49MBz2/yf5Bj+MHjvpG3Hy7sImifnqD6UA+TKYxeSV+u6qqQPawN5UvnpKQ== - dependencies: - inherits "^2.0.1" - process-nextick-args "^2.0.0" - readable-stream "^2.3.5" - -co@^4.6.0: - version "4.6.0" - resolved "https://registry.yarnpkg.com/co/-/co-4.6.0.tgz#6ea6bdf3d853ae54ccb8e47bfa0bf3f9031fb184" - integrity sha1-bqa989hTrlTMuOR7+gvz+QMfsYQ= - -code-point-at@^1.0.0: - version "1.1.0" - resolved "https://registry.yarnpkg.com/code-point-at/-/code-point-at-1.1.0.tgz#0d070b4d043a5bea33a2f1a40e2edb3d9a4ccf77" - integrity sha1-DQcLTQQ6W+ozovGkDi7bPZpMz3c= - -collection-map@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/collection-map/-/collection-map-1.0.0.tgz#aea0f06f8d26c780c2b75494385544b2255af18c" - integrity sha1-rqDwb40mx4DCt1SUOFVEsiVa8Yw= - dependencies: - arr-map "^2.0.2" - for-own "^1.0.0" - make-iterator "^1.0.0" - -collection-visit@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/collection-visit/-/collection-visit-1.0.0.tgz#4bc0373c164bc3291b4d368c829cf1a80a59dca0" - integrity sha1-S8A3PBZLwykbTTaMgpzxqApZ3KA= - dependencies: - map-visit "^1.0.0" - object-visit "^1.0.0" - -color-convert@^1.9.0: - version "1.9.3" - resolved "https://registry.yarnpkg.com/color-convert/-/color-convert-1.9.3.tgz#bb71850690e1f136567de629d2d5471deda4c1e8" - integrity sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg== - dependencies: - color-name "1.1.3" - -color-convert@^2.0.1: - version "2.0.1" - resolved "https://registry.yarnpkg.com/color-convert/-/color-convert-2.0.1.tgz#72d3a68d598c9bdb3af2ad1e84f21d896abd4de3" - integrity sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ== - dependencies: - color-name "~1.1.4" - -color-name@1.1.3: - version "1.1.3" - resolved "https://registry.yarnpkg.com/color-name/-/color-name-1.1.3.tgz#a7d0558bd89c42f795dd42328f740831ca53bc25" - integrity sha1-p9BVi9icQveV3UIyj3QIMcpTvCU= - -color-name@~1.1.4: - version "1.1.4" - resolved "https://registry.yarnpkg.com/color-name/-/color-name-1.1.4.tgz#c2a09a87acbde69543de6f63fa3995c826c536a2" - integrity sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA== - -color-support@^1.1.3: - version "1.1.3" - resolved "https://registry.yarnpkg.com/color-support/-/color-support-1.1.3.tgz#93834379a1cc9a0c61f82f52f0d04322251bd5a2" - integrity sha512-qiBjkpbMLO/HL68y+lh4q0/O1MZFj2RX6X/KmMa3+gJD3z+WwI1ZzDHysvqHGS3mP6mznPckpXmw1nI9cJjyRg== - -colors@^1.1.2, colors@^1.4.0: - version "1.4.0" - resolved "https://registry.yarnpkg.com/colors/-/colors-1.4.0.tgz#c50491479d4c1bdaed2c9ced32cf7c7dc2360f78" - integrity sha512-a+UqTh4kgZg/SlGvfbzDHpgRu7AAQOmmqRHJnxhRZICKFUT91brVhNNt58CMWU9PsBbv3PDCZUHbVxuDiH2mtA== - -combined-stream@^1.0.6, combined-stream@~1.0.6: - version "1.0.8" - resolved "https://registry.yarnpkg.com/combined-stream/-/combined-stream-1.0.8.tgz#c3d45a8b34fd730631a110a8a2520682b31d5a7f" - integrity sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg== - dependencies: - delayed-stream "~1.0.0" - -comma-separated-tokens@^1.0.0: - version "1.0.8" - resolved "https://registry.yarnpkg.com/comma-separated-tokens/-/comma-separated-tokens-1.0.8.tgz#632b80b6117867a158f1080ad498b2fbe7e3f5ea" - integrity sha512-GHuDRO12Sypu2cV70d1dkA2EUmXHgntrzbpvOB+Qy+49ypNfGgFQIC2fhhXbnyrJRynDCAARsT7Ou0M6hirpfw== - -commander@2.15.1: - version "2.15.1" - resolved "https://registry.yarnpkg.com/commander/-/commander-2.15.1.tgz#df46e867d0fc2aec66a34662b406a9ccafff5b0f" - integrity sha512-VlfT9F3V0v+jr4yxPc5gg9s62/fIVWsd2Bk2iD435um1NlGMYdVCq+MjcXnhYq2icNOizHr1kK+5TI6H0Hy0ag== - -commander@^2.18.0, commander@^2.20.0: - version "2.20.3" - resolved "https://registry.yarnpkg.com/commander/-/commander-2.20.3.tgz#fd485e84c03eb4881c20722ba48035e8531aeb33" - integrity sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ== - -commondir@^1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/commondir/-/commondir-1.0.1.tgz#ddd800da0c66127393cca5950ea968a3aaf1253b" - integrity sha1-3dgA2gxmEnOTzKWVDqloo6rxJTs= - -compare-func@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/compare-func/-/compare-func-2.0.0.tgz#fb65e75edbddfd2e568554e8b5b05fff7a51fcb3" - integrity sha512-zHig5N+tPWARooBnb0Zx1MFcdfpyJrfTJ3Y5L+IFvUm8rM74hHz66z0gw0x4tijh5CorKkKUCnW82R2vmpeCRA== - dependencies: - array-ify "^1.0.0" - dot-prop "^5.1.0" - -component-emitter@^1.2.1, component-emitter@~1.3.0: - version "1.3.0" - resolved "https://registry.yarnpkg.com/component-emitter/-/component-emitter-1.3.0.tgz#16e4070fba8ae29b679f2215853ee181ab2eabc0" - integrity sha512-Rd3se6QB+sO1TwqZjscQrurpEPIfO0/yYnSin6Q/rD3mOutHvUrCAhJub3r90uNb+SESBuE0QYoB90YdfatsRg== - -compress-commons@^4.1.0: - version "4.1.1" - resolved "https://registry.yarnpkg.com/compress-commons/-/compress-commons-4.1.1.tgz#df2a09a7ed17447642bad10a85cc9a19e5c42a7d" - integrity sha512-QLdDLCKNV2dtoTorqgxngQCMA+gWXkM/Nwu7FpeBhk/RdkzimqC3jueb/FDmaZeXh+uby1jkBqE3xArsLBE5wQ== - dependencies: - buffer-crc32 "^0.2.13" - crc32-stream "^4.0.2" - normalize-path "^3.0.0" - readable-stream "^3.6.0" - -concat-map@0.0.1: - version "0.0.1" - resolved "https://registry.yarnpkg.com/concat-map/-/concat-map-0.0.1.tgz#d8a96bd77fd68df7793a73036a3ba0d5405d477b" - integrity sha1-2Klr13/Wjfd5OnMDajug1UBdR3s= - -concat-stream@^1.6.0: - version "1.6.2" - resolved "https://registry.yarnpkg.com/concat-stream/-/concat-stream-1.6.2.tgz#904bdf194cd3122fc675c77fc4ac3d4ff0fd1a34" - integrity sha512-27HBghJxjiZtIk3Ycvn/4kbJk/1uZuJFfuPEns6LaEvpvG1f0hTea8lilrouyo9mVc2GWdcEZ8OLoGmSADlrCw== - dependencies: - buffer-from "^1.0.0" - inherits "^2.0.3" - readable-stream "^2.2.2" - typedarray "^0.0.6" - -concat-stream@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/concat-stream/-/concat-stream-2.0.0.tgz#414cf5af790a48c60ab9be4527d56d5e41133cb1" - integrity sha512-MWufYdFw53ccGjCA+Ol7XJYpAlW6/prSMzuPOTRnJGcGzuhLn4Scrz7qf6o8bROZ514ltazcIFJZevcfbo0x7A== - dependencies: - buffer-from "^1.0.0" - inherits "^2.0.3" - readable-stream "^3.0.2" - typedarray "^0.0.6" - -concat-stream@~1.5.0: - version "1.5.2" - resolved "https://registry.yarnpkg.com/concat-stream/-/concat-stream-1.5.2.tgz#708978624d856af41a5a741defdd261da752c266" - integrity sha1-cIl4Yk2FavQaWnQd790mHadSwmY= - dependencies: - inherits "~2.0.1" - readable-stream "~2.0.0" - typedarray "~0.0.5" - -concat-with-sourcemaps@^1.0.0, concat-with-sourcemaps@^1.1.0: - version "1.1.0" - resolved "https://registry.yarnpkg.com/concat-with-sourcemaps/-/concat-with-sourcemaps-1.1.0.tgz#d4ea93f05ae25790951b99e7b3b09e3908a4082e" - integrity sha512-4gEjHJFT9e+2W/77h/DS5SGUgwDaOwprX8L/gl5+3ixnzkVJJsZWDSelmN3Oilw3LNDZjZV0yqH1hLG3k6nghg== - dependencies: - source-map "^0.6.1" - -connect-livereload@^0.6.0: - version "0.6.1" - resolved "https://registry.yarnpkg.com/connect-livereload/-/connect-livereload-0.6.1.tgz#1ac0c8bb9d9cfd5b28b629987a56a9239db9baaa" - integrity sha512-3R0kMOdL7CjJpU66fzAkCe6HNtd3AavCS4m+uW4KtJjrdGPT0SQEZieAYd+cm+lJoBznNQ4lqipYWkhBMgk00g== - -connect@^3.6.6, connect@^3.7.0: - version "3.7.0" - resolved "https://registry.yarnpkg.com/connect/-/connect-3.7.0.tgz#5d49348910caa5e07a01800b030d0c35f20484f8" - integrity sha512-ZqRXc+tZukToSNmh5C2iWMSoV3X1YUcPbqEM4DkEG5tNQXrQUZCNVGGv3IuicnkMtPfGf3Xtp8WCXs295iQ1pQ== - dependencies: - debug "2.6.9" - finalhandler "1.1.2" - parseurl "~1.3.3" - utils-merge "1.0.1" - -console-browserify@^1.1.0: - version "1.2.0" - resolved "https://registry.yarnpkg.com/console-browserify/-/console-browserify-1.2.0.tgz#67063cef57ceb6cf4993a2ab3a55840ae8c49336" - integrity sha512-ZMkYO/LkF17QvCPqM0gxw8yUzigAOZOSWSHg91FH6orS7vcEj5dVZTidN2fQ14yBSdg97RqhSNwLUXInd52OTA== - -constants-browserify@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/constants-browserify/-/constants-browserify-1.0.0.tgz#c20b96d8c617748aaf1c16021760cd27fcb8cb75" - integrity sha1-wguW2MYXdIqvHBYCF2DNJ/y4y3U= - -content-disposition@0.5.3: - version "0.5.3" - resolved "https://registry.yarnpkg.com/content-disposition/-/content-disposition-0.5.3.tgz#e130caf7e7279087c5616c2007d0485698984fbd" - integrity sha512-ExO0774ikEObIAEV9kDo50o+79VCUdEB6n6lzKgGwupcVeRlhrj3qGAfwq8G6uBJjkqLrhT0qEYFcWng8z1z0g== - dependencies: - safe-buffer "5.1.2" - -content-type@~1.0.4: - version "1.0.4" - resolved "https://registry.yarnpkg.com/content-type/-/content-type-1.0.4.tgz#e138cc75e040c727b1966fe5e5f8c9aee256fe3b" - integrity sha512-hIP3EEPs8tB9AT1L+NUqtwOAps4mk2Zob89MWXMHjHWg9milF/j4osnnQLXBCBFBk/tvIG/tUc9mOUJiPBhPXA== - -continuable-cache@^0.3.1: - version "0.3.1" - resolved "https://registry.yarnpkg.com/continuable-cache/-/continuable-cache-0.3.1.tgz#bd727a7faed77e71ff3985ac93351a912733ad0f" - integrity sha1-vXJ6f67XfnH/OYWskzUakSczrQ8= - -conventional-changelog-angular@^5.0.12: - version "5.0.13" - resolved "https://registry.yarnpkg.com/conventional-changelog-angular/-/conventional-changelog-angular-5.0.13.tgz#896885d63b914a70d4934b59d2fe7bde1832b28c" - integrity sha512-i/gipMxs7s8L/QeuavPF2hLnJgH6pEZAttySB6aiQLWcX3puWDL3ACVmvBhJGxnAy52Qc15ua26BufY6KpmrVA== - dependencies: - compare-func "^2.0.0" - q "^1.5.1" - -conventional-changelog-atom@^2.0.8: - version "2.0.8" - resolved "https://registry.yarnpkg.com/conventional-changelog-atom/-/conventional-changelog-atom-2.0.8.tgz#a759ec61c22d1c1196925fca88fe3ae89fd7d8de" - integrity sha512-xo6v46icsFTK3bb7dY/8m2qvc8sZemRgdqLb/bjpBsH2UyOS8rKNTgcb5025Hri6IpANPApbXMg15QLb1LJpBw== - dependencies: - q "^1.5.1" - -conventional-changelog-codemirror@^2.0.8: - version "2.0.8" - resolved "https://registry.yarnpkg.com/conventional-changelog-codemirror/-/conventional-changelog-codemirror-2.0.8.tgz#398e9530f08ce34ec4640af98eeaf3022eb1f7dc" - integrity sha512-z5DAsn3uj1Vfp7po3gpt2Boc+Bdwmw2++ZHa5Ak9k0UKsYAO5mH1UBTN0qSCuJZREIhX6WU4E1p3IW2oRCNzQw== - dependencies: - q "^1.5.1" - -conventional-changelog-config-spec@2.1.0: - version "2.1.0" - resolved "https://registry.yarnpkg.com/conventional-changelog-config-spec/-/conventional-changelog-config-spec-2.1.0.tgz#874a635287ef8b581fd8558532bf655d4fb59f2d" - integrity sha512-IpVePh16EbbB02V+UA+HQnnPIohgXvJRxHcS5+Uwk4AT5LjzCZJm5sp/yqs5C6KZJ1jMsV4paEV13BN1pvDuxQ== - -conventional-changelog-conventionalcommits@4.5.0: - version "4.5.0" - resolved "https://registry.yarnpkg.com/conventional-changelog-conventionalcommits/-/conventional-changelog-conventionalcommits-4.5.0.tgz#a02e0b06d11d342fdc0f00c91d78265ed0bc0a62" - integrity sha512-buge9xDvjjOxJlyxUnar/+6i/aVEVGA7EEh4OafBCXPlLUQPGbRUBhBUveWRxzvR8TEjhKEP4BdepnpG2FSZXw== - dependencies: - compare-func "^2.0.0" - lodash "^4.17.15" - q "^1.5.1" - -conventional-changelog-conventionalcommits@^4.5.0: - version "4.6.1" - resolved "https://registry.yarnpkg.com/conventional-changelog-conventionalcommits/-/conventional-changelog-conventionalcommits-4.6.1.tgz#f4c0921937050674e578dc7875f908351ccf4014" - integrity sha512-lzWJpPZhbM1R0PIzkwzGBCnAkH5RKJzJfFQZcl/D+2lsJxAwGnDKBqn/F4C1RD31GJNn8NuKWQzAZDAVXPp2Mw== - dependencies: - compare-func "^2.0.0" - lodash "^4.17.15" - q "^1.5.1" - -conventional-changelog-core@^4.2.1: - version "4.2.4" - resolved "https://registry.yarnpkg.com/conventional-changelog-core/-/conventional-changelog-core-4.2.4.tgz#e50d047e8ebacf63fac3dc67bf918177001e1e9f" - integrity sha512-gDVS+zVJHE2v4SLc6B0sLsPiloR0ygU7HaDW14aNJE1v4SlqJPILPl/aJC7YdtRE4CybBf8gDwObBvKha8Xlyg== - dependencies: - add-stream "^1.0.0" - conventional-changelog-writer "^5.0.0" - conventional-commits-parser "^3.2.0" - dateformat "^3.0.0" - get-pkg-repo "^4.0.0" - git-raw-commits "^2.0.8" - git-remote-origin-url "^2.0.0" - git-semver-tags "^4.1.1" - lodash "^4.17.15" - normalize-package-data "^3.0.0" - q "^1.5.1" - read-pkg "^3.0.0" - read-pkg-up "^3.0.0" - through2 "^4.0.0" - -conventional-changelog-ember@^2.0.9: - version "2.0.9" - resolved "https://registry.yarnpkg.com/conventional-changelog-ember/-/conventional-changelog-ember-2.0.9.tgz#619b37ec708be9e74a220f4dcf79212ae1c92962" - integrity sha512-ulzIReoZEvZCBDhcNYfDIsLTHzYHc7awh+eI44ZtV5cx6LVxLlVtEmcO+2/kGIHGtw+qVabJYjdI5cJOQgXh1A== - dependencies: - q "^1.5.1" - -conventional-changelog-eslint@^3.0.9: - version "3.0.9" - resolved "https://registry.yarnpkg.com/conventional-changelog-eslint/-/conventional-changelog-eslint-3.0.9.tgz#689bd0a470e02f7baafe21a495880deea18b7cdb" - integrity sha512-6NpUCMgU8qmWmyAMSZO5NrRd7rTgErjrm4VASam2u5jrZS0n38V7Y9CzTtLT2qwz5xEChDR4BduoWIr8TfwvXA== - dependencies: - q "^1.5.1" - -conventional-changelog-express@^2.0.6: - version "2.0.6" - resolved "https://registry.yarnpkg.com/conventional-changelog-express/-/conventional-changelog-express-2.0.6.tgz#420c9d92a347b72a91544750bffa9387665a6ee8" - integrity sha512-SDez2f3iVJw6V563O3pRtNwXtQaSmEfTCaTBPCqn0oG0mfkq0rX4hHBq5P7De2MncoRixrALj3u3oQsNK+Q0pQ== - dependencies: - q "^1.5.1" - -conventional-changelog-jquery@^3.0.11: - version "3.0.11" - resolved "https://registry.yarnpkg.com/conventional-changelog-jquery/-/conventional-changelog-jquery-3.0.11.tgz#d142207400f51c9e5bb588596598e24bba8994bf" - integrity sha512-x8AWz5/Td55F7+o/9LQ6cQIPwrCjfJQ5Zmfqi8thwUEKHstEn4kTIofXub7plf1xvFA2TqhZlq7fy5OmV6BOMw== - dependencies: - q "^1.5.1" - -conventional-changelog-jshint@^2.0.9: - version "2.0.9" - resolved "https://registry.yarnpkg.com/conventional-changelog-jshint/-/conventional-changelog-jshint-2.0.9.tgz#f2d7f23e6acd4927a238555d92c09b50fe3852ff" - integrity sha512-wMLdaIzq6TNnMHMy31hql02OEQ8nCQfExw1SE0hYL5KvU+JCTuPaDO+7JiogGT2gJAxiUGATdtYYfh+nT+6riA== - dependencies: - compare-func "^2.0.0" - q "^1.5.1" - -conventional-changelog-preset-loader@^2.3.4: - version "2.3.4" - resolved "https://registry.yarnpkg.com/conventional-changelog-preset-loader/-/conventional-changelog-preset-loader-2.3.4.tgz#14a855abbffd59027fd602581f1f34d9862ea44c" - integrity sha512-GEKRWkrSAZeTq5+YjUZOYxdHq+ci4dNwHvpaBC3+ENalzFWuCWa9EZXSuZBpkr72sMdKB+1fyDV4takK1Lf58g== - -conventional-changelog-writer@^5.0.0: - version "5.0.0" - resolved "https://registry.yarnpkg.com/conventional-changelog-writer/-/conventional-changelog-writer-5.0.0.tgz#c4042f3f1542f2f41d7d2e0d6cad23aba8df8eec" - integrity sha512-HnDh9QHLNWfL6E1uHz6krZEQOgm8hN7z/m7tT16xwd802fwgMN0Wqd7AQYVkhpsjDUx/99oo+nGgvKF657XP5g== - dependencies: - conventional-commits-filter "^2.0.7" - dateformat "^3.0.0" - handlebars "^4.7.6" - json-stringify-safe "^5.0.1" - lodash "^4.17.15" - meow "^8.0.0" - semver "^6.0.0" - split "^1.0.0" - through2 "^4.0.0" - -conventional-changelog@3.1.24: - version "3.1.24" - resolved "https://registry.yarnpkg.com/conventional-changelog/-/conventional-changelog-3.1.24.tgz#ebd180b0fd1b2e1f0095c4b04fd088698348a464" - integrity sha512-ed6k8PO00UVvhExYohroVPXcOJ/K1N0/drJHx/faTH37OIZthlecuLIRX/T6uOp682CAoVoFpu+sSEaeuH6Asg== - dependencies: - conventional-changelog-angular "^5.0.12" - conventional-changelog-atom "^2.0.8" - conventional-changelog-codemirror "^2.0.8" - conventional-changelog-conventionalcommits "^4.5.0" - conventional-changelog-core "^4.2.1" - conventional-changelog-ember "^2.0.9" - conventional-changelog-eslint "^3.0.9" - conventional-changelog-express "^2.0.6" - conventional-changelog-jquery "^3.0.11" - conventional-changelog-jshint "^2.0.9" - conventional-changelog-preset-loader "^2.3.4" - -conventional-commits-filter@^2.0.7: - version "2.0.7" - resolved "https://registry.yarnpkg.com/conventional-commits-filter/-/conventional-commits-filter-2.0.7.tgz#f8d9b4f182fce00c9af7139da49365b136c8a0b3" - integrity sha512-ASS9SamOP4TbCClsRHxIHXRfcGCnIoQqkvAzCSbZzTFLfcTqJVugB0agRgsEELsqaeWgsXv513eS116wnlSSPA== - dependencies: - lodash.ismatch "^4.4.0" - modify-values "^1.0.0" - -conventional-commits-parser@^3.2.0: - version "3.2.2" - resolved "https://registry.yarnpkg.com/conventional-commits-parser/-/conventional-commits-parser-3.2.2.tgz#190fb9900c6e02be0c0bca9b03d57e24982639fd" - integrity sha512-Jr9KAKgqAkwXMRHjxDwO/zOCDKod1XdAESHAGuJX38iZ7ZzVti/tvVoysO0suMsdAObp9NQ2rHSsSbnAqZ5f5g== - dependencies: - 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" - -conventional-recommended-bump@6.1.0: - version "6.1.0" - resolved "https://registry.yarnpkg.com/conventional-recommended-bump/-/conventional-recommended-bump-6.1.0.tgz#cfa623285d1de554012f2ffde70d9c8a22231f55" - integrity sha512-uiApbSiNGM/kkdL9GTOLAqC4hbptObFo4wW2QRyHsKciGAfQuLU1ShZ1BIVI/+K2BE/W1AWYQMCXAsv4dyKPaw== - dependencies: - concat-stream "^2.0.0" - conventional-changelog-preset-loader "^2.3.4" - conventional-commits-filter "^2.0.7" - conventional-commits-parser "^3.2.0" - git-raw-commits "^2.0.8" - git-semver-tags "^4.1.1" - meow "^8.0.0" - q "^1.5.1" - -convert-source-map@^1.0.0, convert-source-map@^1.5.0, convert-source-map@^1.7.0: - version "1.8.0" - resolved "https://registry.yarnpkg.com/convert-source-map/-/convert-source-map-1.8.0.tgz#f3373c32d21b4d780dd8004514684fb791ca4369" - integrity sha512-+OQdjP49zViI/6i7nIJpA8rAl4sV/JdPfU9nZs3VqOwGIgizICvuN2ru6fMd+4llL0tar18UYJXfZ/TWtmhUjA== - dependencies: - safe-buffer "~5.1.1" - -cookie-signature@1.0.6: - version "1.0.6" - resolved "https://registry.yarnpkg.com/cookie-signature/-/cookie-signature-1.0.6.tgz#e303a882b342cc3ee8ca513a79999734dab3ae2c" - integrity sha1-4wOogrNCzD7oylE6eZmXNNqzriw= - -cookie@0.4.0: - version "0.4.0" - resolved "https://registry.yarnpkg.com/cookie/-/cookie-0.4.0.tgz#beb437e7022b3b6d49019d088665303ebe9c14ba" - integrity sha512-+Hp8fLp57wnUSt0tY0tHEXh4voZRDnoIrZPqlo3DPiI4y9lwg/jqx+1Om94/W6ZaPDOUbnjOt/99w66zk+l1Xg== - -cookie@~0.4.1: - version "0.4.1" - resolved "https://registry.yarnpkg.com/cookie/-/cookie-0.4.1.tgz#afd713fe26ebd21ba95ceb61f9a8116e50a537d1" - integrity sha512-ZwrFkGJxUR3EIoXtO+yVE69Eb7KlixbaeAWfBQB9vVsNn/o+Yw69gBWSSDK825hQNdN+wF8zELf3dFNl/kxkUA== - -copy-descriptor@^0.1.0: - version "0.1.1" - resolved "https://registry.yarnpkg.com/copy-descriptor/-/copy-descriptor-0.1.1.tgz#676f6eb3c39997c2ee1ac3a924fd6124748f578d" - integrity sha1-Z29us8OZl8LuGsOpJP1hJHSPV40= - -copy-props@^2.0.1: - version "2.0.5" - resolved "https://registry.yarnpkg.com/copy-props/-/copy-props-2.0.5.tgz#03cf9ae328d4ebb36f8f1d804448a6af9ee3f2d2" - integrity sha512-XBlx8HSqrT0ObQwmSzM7WE5k8FxTV75h1DX1Z3n6NhQ/UYYAvInWYmG06vFt7hQZArE2fuO62aihiWIVQwh1sw== - dependencies: - each-props "^1.3.2" - is-plain-object "^5.0.0" - -core-js-compat@^3.16.0, core-js-compat@^3.16.2: - version "3.18.1" - resolved "https://registry.yarnpkg.com/core-js-compat/-/core-js-compat-3.18.1.tgz#01942a0877caf9c6e5007c027183cf0bdae6a191" - integrity sha512-XJMYx58zo4W0kLPmIingVZA10+7TuKrMLPt83+EzDmxFJQUMcTVVmQ+n5JP4r6Z14qSzhQBRi3NSWoeVyKKXUg== - dependencies: - browserslist "^4.17.1" - semver "7.0.0" - -core-js-pure@^3.13.0, core-js-pure@^3.16.0: - version "3.18.1" - resolved "https://registry.yarnpkg.com/core-js-pure/-/core-js-pure-3.18.1.tgz#097d34d24484be45cea700a448d1e74622646c80" - integrity sha512-kmW/k8MaSuqpvA1xm2l3TVlBuvW+XBkcaOroFUpO3D4lsTGQWBTb/tBDCf/PNkkPLrwgrkQRIYNPB0CeqGJWGQ== - -core-js@^2.4.0: - version "2.6.12" - resolved "https://registry.yarnpkg.com/core-js/-/core-js-2.6.12.tgz#d9333dfa7b065e347cc5682219d6f690859cc2ec" - integrity sha512-Kb2wC0fvsWfQrgk8HU5lW6U/Lcs8+9aaYcy4ZFc6DDlo4nZ7n70dEgE5rtR0oG6ufKDUnrwfWL1mXR5ljDatrQ== - -core-js@^3.13.0: - version "3.18.1" - resolved "https://registry.yarnpkg.com/core-js/-/core-js-3.18.1.tgz#289d4be2ce0085d40fc1244c0b1a54c00454622f" - integrity sha512-vJlUi/7YdlCZeL6fXvWNaLUPh/id12WXj3MbkMw5uOyF0PfWPBNOCNbs53YqgrvtujLNlt9JQpruyIKkUZ+PKA== - -core-util-is@1.0.2: - version "1.0.2" - resolved "https://registry.yarnpkg.com/core-util-is/-/core-util-is-1.0.2.tgz#b5fd54220aa2bc5ab57aab7140c940754503c1a7" - integrity sha1-tf1UIgqivFq1eqtxQMlAdUUDwac= - -core-util-is@~1.0.0: - version "1.0.3" - resolved "https://registry.yarnpkg.com/core-util-is/-/core-util-is-1.0.3.tgz#a6042d3634c2b27e9328f837b965fac83808db85" - integrity sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ== - -cors@~2.8.5: - version "2.8.5" - resolved "https://registry.yarnpkg.com/cors/-/cors-2.8.5.tgz#eac11da51592dd86b9f06f6e7ac293b3df875d29" - integrity sha512-KIHbLJqu73RGr/hnbrO9uBeixNGuvSQjul/jdFvS/KFSIH1hWVd1ng7zOHx+YrEfInLG7q4n6GHQ9cDtxv/P6g== - dependencies: - object-assign "^4" - vary "^1" - -coveralls@^3.1.0: - version "3.1.1" - resolved "https://registry.yarnpkg.com/coveralls/-/coveralls-3.1.1.tgz#f5d4431d8b5ae69c5079c8f8ca00d64ac77cf081" - integrity sha512-+dxnG2NHncSD1NrqbSM3dn/lE57O6Qf/koe9+I7c+wzkqRmEvcp0kgJdxKInzYzkICKkFMZsX3Vct3++tsF9ww== - dependencies: - js-yaml "^3.13.1" - lcov-parse "^1.0.0" - log-driver "^1.2.7" - minimist "^1.2.5" - request "^2.88.2" - -crc-32@^1.2.0: - version "1.2.0" - resolved "https://registry.yarnpkg.com/crc-32/-/crc-32-1.2.0.tgz#cb2db6e29b88508e32d9dd0ec1693e7b41a18208" - integrity sha512-1uBwHxF+Y/4yF5G48fwnKq6QsIXheor3ZLPT80yGBV1oEUwpPojlEhQbWKVw1VwcTQyMGHK1/XMmTjmlsmTTGA== - dependencies: - exit-on-epipe "~1.0.1" - printj "~1.1.0" - -crc32-stream@^4.0.2: - version "4.0.2" - resolved "https://registry.yarnpkg.com/crc32-stream/-/crc32-stream-4.0.2.tgz#c922ad22b38395abe9d3870f02fa8134ed709007" - integrity sha512-DxFZ/Hk473b/muq1VJ///PMNLj0ZMnzye9thBpmjpJKCc5eMgB95aK8zCGrGfQ90cWo561Te6HK9D+j4KPdM6w== - dependencies: - crc-32 "^1.2.0" - readable-stream "^3.4.0" - -create-ecdh@^4.0.0: - version "4.0.4" - resolved "https://registry.yarnpkg.com/create-ecdh/-/create-ecdh-4.0.4.tgz#d6e7f4bffa66736085a0762fd3a632684dabcc4e" - integrity sha512-mf+TCx8wWc9VpuxfP2ht0iSISLZnt0JgWlrOKZiNqyUZWnjIaCIVNQArMHnCZKfEYRg6IM7A+NeJoN8gf/Ws0A== - dependencies: - bn.js "^4.1.0" - elliptic "^6.5.3" - -create-hash@^1.1.0, create-hash@^1.1.2, create-hash@^1.2.0: - version "1.2.0" - resolved "https://registry.yarnpkg.com/create-hash/-/create-hash-1.2.0.tgz#889078af11a63756bcfb59bd221996be3a9ef196" - integrity sha512-z00bCGNHDG8mHAkP7CtT1qVu+bFQUPjYq/4Iv3C3kWjTFV10zIjfSoeqXo9Asws8gwSHDGj/hl2u4OGIjapeCg== - dependencies: - cipher-base "^1.0.1" - inherits "^2.0.1" - md5.js "^1.3.4" - ripemd160 "^2.0.1" - sha.js "^2.4.0" - -create-hmac@^1.1.0, create-hmac@^1.1.4, create-hmac@^1.1.7: - version "1.1.7" - resolved "https://registry.yarnpkg.com/create-hmac/-/create-hmac-1.1.7.tgz#69170c78b3ab957147b2b8b04572e47ead2243ff" - integrity sha512-MJG9liiZ+ogc4TzUwuvbER1JRdgvUFSB5+VR/g5h82fGaIRWMWddtKBHi7/sVhfjQZ6SehlyhvQYrcYkaUIpLg== - dependencies: - cipher-base "^1.0.3" - create-hash "^1.1.0" - inherits "^2.0.1" - ripemd160 "^2.0.0" - safe-buffer "^5.0.1" - sha.js "^2.4.8" - -criteo-direct-rsa-validate@^1.1.0: - version "1.1.0" - resolved "https://registry.yarnpkg.com/criteo-direct-rsa-validate/-/criteo-direct-rsa-validate-1.1.0.tgz#eb2fead0ff0bcbe4f6369213dd5eef455f0e53c4" - integrity sha512-7gQ3zX+d+hS/vOxzLrZ4aRAceB7qNJ0VzaGNpcWjDCmtOpASB50USJDupTik/H2nHgiSAA3VNZ3SFuONs8LR9Q== - -cross-spawn@^4.0.2: - version "4.0.2" - resolved "https://registry.yarnpkg.com/cross-spawn/-/cross-spawn-4.0.2.tgz#7b9247621c23adfdd3856004a823cbe397424d41" - integrity sha1-e5JHYhwjrf3ThWAEqCPL45dCTUE= - dependencies: - lru-cache "^4.0.1" - which "^1.2.9" - -cross-spawn@^5.0.1, cross-spawn@^5.1.0: - version "5.1.0" - resolved "https://registry.yarnpkg.com/cross-spawn/-/cross-spawn-5.1.0.tgz#e8bd0efee58fcff6f8f94510a0a554bbfa235449" - integrity sha1-6L0O/uWPz/b4+UUQoKVUu/ojVEk= - dependencies: - lru-cache "^4.0.1" - shebang-command "^1.2.0" - which "^1.2.9" - -cross-spawn@^6.0.0: - version "6.0.5" - resolved "https://registry.yarnpkg.com/cross-spawn/-/cross-spawn-6.0.5.tgz#4a5ec7c64dfae22c3a14124dbacdee846d80cbc4" - integrity sha512-eTVLrBSt7fjbDygz805pMnstIs2VTBNkRm0qxZd+M7A5XDdxVRWO5MxGBXZhjY4cqLYLdtrGqRf8mBPmzwSpWQ== - dependencies: - nice-try "^1.0.4" - path-key "^2.0.1" - semver "^5.5.0" - shebang-command "^1.2.0" - which "^1.2.9" - -cross-spawn@^7.0.2: - version "7.0.3" - resolved "https://registry.yarnpkg.com/cross-spawn/-/cross-spawn-7.0.3.tgz#f73a85b9d5d41d045551c177e2882d4ac85728a6" - integrity sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w== - dependencies: - path-key "^3.1.0" - shebang-command "^2.0.0" - which "^2.0.1" - -crypto-browserify@3.3.0: - version "3.3.0" - resolved "https://registry.yarnpkg.com/crypto-browserify/-/crypto-browserify-3.3.0.tgz#b9fc75bb4a0ed61dcf1cd5dae96eb30c9c3e506c" - integrity sha1-ufx1u0oO1h3PHNXa6W6zDJw+UGw= - dependencies: - browserify-aes "0.4.0" - pbkdf2-compat "2.0.1" - ripemd160 "0.2.0" - sha.js "2.2.6" - -crypto-browserify@^3.11.0: - version "3.12.0" - resolved "https://registry.yarnpkg.com/crypto-browserify/-/crypto-browserify-3.12.0.tgz#396cf9f3137f03e4b8e532c58f698254e00f80ec" - integrity sha512-fz4spIh+znjO2VjL+IdhEpRJ3YN6sMzITSBijk6FK2UvTqruSQW+/cCZTSNsMiZNvUeq0CqurF+dAbyiGOY6Wg== - dependencies: - browserify-cipher "^1.0.0" - browserify-sign "^4.0.0" - create-ecdh "^4.0.0" - create-hash "^1.1.0" - create-hmac "^1.1.0" - diffie-hellman "^5.0.0" - inherits "^2.0.1" - pbkdf2 "^3.0.3" - public-encrypt "^4.0.0" - randombytes "^2.0.0" - randomfill "^1.0.3" - -crypto-js@^3.3.0: - version "3.3.0" - resolved "https://registry.yarnpkg.com/crypto-js/-/crypto-js-3.3.0.tgz#846dd1cce2f68aacfa156c8578f926a609b7976b" - integrity sha512-DIT51nX0dCfKltpRiXV+/TVZq+Qq2NgF4644+K7Ttnla7zEzqc+kjJyiB96BHNyUTBxyjzRcZYpUdZa+QAqi6Q== - -css-shorthand-properties@^1.1.1: - version "1.1.1" - resolved "https://registry.yarnpkg.com/css-shorthand-properties/-/css-shorthand-properties-1.1.1.tgz#1c808e63553c283f289f2dd56fcee8f3337bd935" - integrity sha512-Md+Juc7M3uOdbAFwOYlTrccIZ7oCFuzrhKYQjdeUEW/sE1hv17Jp/Bws+ReOPpGVBTYCBoYo+G17V5Qo8QQ75A== - -css-value@^0.0.1: - version "0.0.1" - resolved "https://registry.yarnpkg.com/css-value/-/css-value-0.0.1.tgz#5efd6c2eea5ea1fd6b6ac57ec0427b18452424ea" - integrity sha1-Xv1sLupeof1rasV+wEJ7GEUkJOo= - -css@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/css/-/css-3.0.0.tgz#4447a4d58fdd03367c516ca9f64ae365cee4aa5d" - integrity sha512-DG9pFfwOrzc+hawpmqX/dHYHJG+Bsdb0klhyi1sDneOgGOXy9wQIC8hzyVp1e4NRYDBdxcylvywPkkXCHAzTyQ== - dependencies: - inherits "^2.0.4" - source-map "^0.6.1" - source-map-resolve "^0.6.0" - -currently-unhandled@^0.4.1: - version "0.4.1" - resolved "https://registry.yarnpkg.com/currently-unhandled/-/currently-unhandled-0.4.1.tgz#988df33feab191ef799a61369dd76c17adf957ea" - integrity sha1-mI3zP+qxke95mmE2nddsF635V+o= - dependencies: - array-find-index "^1.0.1" - -custom-event@~1.0.0: - version "1.0.1" - resolved "https://registry.yarnpkg.com/custom-event/-/custom-event-1.0.1.tgz#5d02a46850adf1b4a317946a3928fccb5bfd0425" - integrity sha1-XQKkaFCt8bSjF5RqOSj8y1v9BCU= - -d@1, d@^1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/d/-/d-1.0.1.tgz#8698095372d58dbee346ffd0c7093f99f8f9eb5a" - integrity sha512-m62ShEObQ39CfralilEQRjH6oAMtNCV1xJyEx5LpRYUVN+EviphDgUc/F3hnYbADmkiNs67Y+3ylmlG7Lnu+FA== - dependencies: - es5-ext "^0.10.50" - type "^1.0.1" - -dargs@^7.0.0: - version "7.0.0" - resolved "https://registry.yarnpkg.com/dargs/-/dargs-7.0.0.tgz#04015c41de0bcb69ec84050f3d9be0caf8d6d5cc" - integrity sha512-2iy1EkLdlBzQGvbweYRFxmFath8+K7+AKB0TlhHWkNuH+TmovaMH/Wp7V7R4u7f4SnX3OgLsU9t1NI9ioDnUpg== - -dashdash@^1.12.0: - version "1.14.1" - resolved "https://registry.yarnpkg.com/dashdash/-/dashdash-1.14.1.tgz#853cfa0f7cbe2fed5de20326b8dd581035f6e2f0" - integrity sha1-hTz6D3y+L+1d4gMmuN1YEDX24vA= - dependencies: - assert-plus "^1.0.0" - -date-format@^2.1.0: - version "2.1.0" - resolved "https://registry.yarnpkg.com/date-format/-/date-format-2.1.0.tgz#31d5b5ea211cf5fd764cd38baf9d033df7e125cf" - integrity sha512-bYQuGLeFxhkxNOF3rcMtiZxvCBAquGzZm6oWA1oZ0g2THUzivaRhv8uOhdr19LmoobSOLoIAxeUK2RdbM8IFTA== - -date-format@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/date-format/-/date-format-3.0.0.tgz#eb8780365c7d2b1511078fb491e6479780f3ad95" - integrity sha512-eyTcpKOcamdhWJXj56DpQMo1ylSQpcGtGKXcU0Tb97+K56/CF5amAqqqNj0+KvA0iw2ynxtHWFsPDSClCxe48w== - -dateformat@^1.0.7-1.2.3: - version "1.0.12" - resolved "https://registry.yarnpkg.com/dateformat/-/dateformat-1.0.12.tgz#9f124b67594c937ff706932e4a642cca8dbbfee9" - integrity sha1-nxJLZ1lMk3/3BpMuSmQsyo27/uk= - dependencies: - get-stdin "^4.0.1" - meow "^3.3.0" - -dateformat@^2.0.0: - version "2.2.0" - resolved "https://registry.yarnpkg.com/dateformat/-/dateformat-2.2.0.tgz#4065e2013cf9fb916ddfd82efb506ad4c6769062" - integrity sha1-QGXiATz5+5Ft39gu+1Bq1MZ2kGI= - -dateformat@^3.0.0: - version "3.0.3" - resolved "https://registry.yarnpkg.com/dateformat/-/dateformat-3.0.3.tgz#a6e37499a4d9a9cf85ef5872044d62901c9889ae" - integrity sha512-jyCETtSl3VMZMWeRo7iY1FL19ges1t55hMo5yaam4Jrsm5EPL89UQkoQRyiI+Yf4k8r2ZpdngkV8hr1lIdjb3Q== - -de-indent@^1.0.2: - version "1.0.2" - resolved "https://registry.yarnpkg.com/de-indent/-/de-indent-1.0.2.tgz#b2038e846dc33baa5796128d0804b455b8c1e21d" - integrity sha1-sgOOhG3DO6pXlhKNCAS0VbjB4h0= - -debug-fabulous@^1.0.0: - version "1.1.0" - resolved "https://registry.yarnpkg.com/debug-fabulous/-/debug-fabulous-1.1.0.tgz#af8a08632465224ef4174a9f06308c3c2a1ebc8e" - integrity sha512-GZqvGIgKNlUnHUPQhepnUZFIMoi3dgZKQBzKDeL2g7oJF9SNAji/AAu36dusFUas0O+pae74lNeoIPHqXWDkLg== - dependencies: - debug "3.X" - memoizee "0.4.X" - object-assign "4.X" - -debug@2.6.9, debug@^2.2.0, debug@^2.3.3, debug@^2.6.9: - version "2.6.9" - resolved "https://registry.yarnpkg.com/debug/-/debug-2.6.9.tgz#5d128515df134ff327e90a4c93f4e077a536341f" - integrity sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA== - dependencies: - ms "2.0.0" - -debug@3.1.0: - version "3.1.0" - resolved "https://registry.yarnpkg.com/debug/-/debug-3.1.0.tgz#5bb5a0672628b64149566ba16819e61518c67261" - integrity sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g== - dependencies: - ms "2.0.0" - -debug@3.X, debug@^3.1.0, debug@^3.2.7: - version "3.2.7" - resolved "https://registry.yarnpkg.com/debug/-/debug-3.2.7.tgz#72580b7e9145fb39b6676f9c5e5fb100b934179a" - integrity sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ== - dependencies: - ms "^2.1.1" - -debug@4, debug@4.3.2, debug@^4.0.0, debug@^4.0.1, debug@^4.1.0, debug@^4.1.1, debug@~4.3.1: - version "4.3.2" - resolved "https://registry.yarnpkg.com/debug/-/debug-4.3.2.tgz#f0a49c18ac8779e31d4a0c6029dfb76873c7428b" - integrity sha512-mOp8wKcvj7XxC78zLgw/ZA+6TSgkoE2C/ienthhRD298T7UNwAg9diBpLRxC0mOezLl4B0xV7M0cCO6P/O0Xhw== - dependencies: - ms "2.1.2" - -debug@4.3.1: - version "4.3.1" - resolved "https://registry.yarnpkg.com/debug/-/debug-4.3.1.tgz#f0d229c505e0c6d8c49ac553d1b13dc183f6b2ee" - integrity sha512-doEwdvm4PCeK4K3RQN2ZC2BYUBaxwLARCqZmMjtF8a51J2Rb0xpVloFRnCODwqjpwnAoao4pelN8l3RJdv3gRQ== - dependencies: - ms "2.1.2" - -decamelize-keys@^1.1.0: - version "1.1.0" - resolved "https://registry.yarnpkg.com/decamelize-keys/-/decamelize-keys-1.1.0.tgz#d171a87933252807eb3cb61dc1c1445d078df2d9" - integrity sha1-0XGoeTMlKAfrPLYdwcFEXQeN8tk= - dependencies: - decamelize "^1.1.0" - map-obj "^1.0.0" - -decamelize@^1.0.0, decamelize@^1.1.0, decamelize@^1.1.1, decamelize@^1.1.2, decamelize@^1.2.0: - version "1.2.0" - resolved "https://registry.yarnpkg.com/decamelize/-/decamelize-1.2.0.tgz#f6534d15148269b20352e7bee26f501f9a191290" - integrity sha1-9lNNFRSCabIDUue+4m9QH5oZEpA= - -decamelize@^4.0.0: - version "4.0.0" - resolved "https://registry.yarnpkg.com/decamelize/-/decamelize-4.0.0.tgz#aa472d7bf660eb15f3494efd531cab7f2a709837" - integrity sha512-9iE1PgSik9HeIIw2JO94IidnE3eBoQrFJ3w7sFuzSX4DpmZ3v5sZpUiV5Swcf6mQEF+Y0ru8Neo+p+nyh2J+hQ== - -decode-uri-component@^0.2.0: - version "0.2.0" - resolved "https://registry.yarnpkg.com/decode-uri-component/-/decode-uri-component-0.2.0.tgz#eb3913333458775cb84cd1a1fae062106bb87545" - integrity sha1-6zkTMzRYd1y4TNGh+uBiEGu4dUU= - -decompress-response@^6.0.0: - version "6.0.0" - resolved "https://registry.yarnpkg.com/decompress-response/-/decompress-response-6.0.0.tgz#ca387612ddb7e104bd16d85aab00d5ecf09c66fc" - integrity sha512-aW35yZM6Bb/4oJlZncMH2LCoZtJXTRxES17vE3hoRiowU2kWHaJKFkSBDnDR+cm9J+9QhXmREyIfv0pji9ejCQ== - dependencies: - mimic-response "^3.1.0" - -deep-eql@^3.0.1: - version "3.0.1" - resolved "https://registry.yarnpkg.com/deep-eql/-/deep-eql-3.0.1.tgz#dfc9404400ad1c8fe023e7da1df1c147c4b444df" - integrity sha512-+QeIQyN5ZuO+3Uk5DYh6/1eKO0m0YmJFGNmFHGACpf1ClL1nmlV/p4gNgbl2pJGxgXb4faqo6UE+M5ACEMyVcw== - dependencies: - type-detect "^4.0.0" - -deep-equal@^2.0.3: - version "2.0.5" - resolved "https://registry.yarnpkg.com/deep-equal/-/deep-equal-2.0.5.tgz#55cd2fe326d83f9cbf7261ef0e060b3f724c5cb9" - integrity sha512-nPiRgmbAtm1a3JsnLCf6/SLfXcjyN5v8L1TXzdCmHrXJ4hx+gW/w1YCcn7z8gJtSiDArZCgYtbao3QqLm/N1Sw== - dependencies: - call-bind "^1.0.0" - es-get-iterator "^1.1.1" - get-intrinsic "^1.0.1" - is-arguments "^1.0.4" - is-date-object "^1.0.2" - is-regex "^1.1.1" - isarray "^2.0.5" - object-is "^1.1.4" - object-keys "^1.1.1" - object.assign "^4.1.2" - regexp.prototype.flags "^1.3.0" - side-channel "^1.0.3" - which-boxed-primitive "^1.0.1" - which-collection "^1.0.1" - which-typed-array "^1.1.2" - -deep-is@^0.1.3, deep-is@~0.1.3: - version "0.1.4" - resolved "https://registry.yarnpkg.com/deep-is/-/deep-is-0.1.4.tgz#a6f2dce612fadd2ef1f519b73551f17e85199831" - integrity sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ== - -deepmerge@^4.0.0: - version "4.2.2" - resolved "https://registry.yarnpkg.com/deepmerge/-/deepmerge-4.2.2.tgz#44d2ea3679b8f4d4ffba33f03d865fc1e7bf4955" - integrity sha512-FJ3UgI4gIl+PHZm53knsuSFpE+nESMr7M4v9QcgB7S63Kj/6WqMiFQJpBBYz1Pt+66bZpP3Q7Lye0Oo9MPKEdg== - -default-compare@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/default-compare/-/default-compare-1.0.0.tgz#cb61131844ad84d84788fb68fd01681ca7781a2f" - integrity sha512-QWfXlM0EkAbqOCbD/6HjdwT19j7WCkMyiRhWilc4H9/5h/RzTF9gv5LYh1+CmDV5d1rki6KAWLtQale0xt20eQ== - dependencies: - kind-of "^5.0.2" - -default-resolution@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/default-resolution/-/default-resolution-2.0.0.tgz#bcb82baa72ad79b426a76732f1a81ad6df26d684" - integrity sha1-vLgrqnKtebQmp2cy8aga1t8m1oQ= - -defaults@^1.0.3: - version "1.0.3" - resolved "https://registry.yarnpkg.com/defaults/-/defaults-1.0.3.tgz#c656051e9817d9ff08ed881477f3fe4019f3ef7d" - integrity sha1-xlYFHpgX2f8I7YgUd/P+QBnz730= - dependencies: - clone "^1.0.2" - -defer-to-connect@^2.0.0: - version "2.0.1" - resolved "https://registry.yarnpkg.com/defer-to-connect/-/defer-to-connect-2.0.1.tgz#8016bdb4143e4632b77a3449c6236277de520587" - integrity sha512-4tvttepXG1VaYGrRibk5EwJd1t4udunSOVMdLSAL6mId1ix438oPwPZMALY41FCijukO1L0twNcGsdzS7dHgDg== - -define-properties@^1.1.3: - version "1.1.3" - resolved "https://registry.yarnpkg.com/define-properties/-/define-properties-1.1.3.tgz#cf88da6cbee26fe6db7094f61d870cbd84cee9f1" - integrity sha512-3MqfYKj2lLzdMSf8ZIZE/V+Zuy+BgD6f164e8K2w7dgnpKArBDerGYpM46IYYcjnkdPNMjPk9A6VFB8+3SKlXQ== - dependencies: - object-keys "^1.0.12" - -define-property@^0.2.5: - version "0.2.5" - resolved "https://registry.yarnpkg.com/define-property/-/define-property-0.2.5.tgz#c35b1ef918ec3c990f9a5bc57be04aacec5c8116" - integrity sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY= - dependencies: - is-descriptor "^0.1.0" - -define-property@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/define-property/-/define-property-1.0.0.tgz#769ebaaf3f4a63aad3af9e8d304c9bbe79bfb0e6" - integrity sha1-dp66rz9KY6rTr56NMEybvnm/sOY= - dependencies: - is-descriptor "^1.0.0" - -define-property@^2.0.2: - version "2.0.2" - resolved "https://registry.yarnpkg.com/define-property/-/define-property-2.0.2.tgz#d459689e8d654ba77e02a817f8710d702cb16e9d" - integrity sha512-jwK2UV4cnPpbcG7+VRARKTZPUWowwXA8bzH5NP6ud0oeAxyYPuGZUAC7hMugpCdz4BeSZl2Dl9k66CHJ/46ZYQ== - dependencies: - is-descriptor "^1.0.2" - isobject "^3.0.1" - -defined@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/defined/-/defined-1.0.0.tgz#c98d9bcef75674188e110969151199e39b1fa693" - integrity sha1-yY2bzvdWdBiOEQlpFRGZ45sfppM= - -delayed-stream@~1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/delayed-stream/-/delayed-stream-1.0.0.tgz#df3ae199acadfb7d440aaae0b29e2272b24ec619" - integrity sha1-3zrhmayt+31ECqrgsp4icrJOxhk= - -depd@~1.1.2: - version "1.1.2" - resolved "https://registry.yarnpkg.com/depd/-/depd-1.1.2.tgz#9bcd52e14c097763e749b274c4346ed2e560b5a9" - integrity sha1-m81S4UwJd2PnSbJ0xDRu0uVgtak= - -depd@~2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/depd/-/depd-2.0.0.tgz#b696163cc757560d09cf22cc8fad1571b79e76df" - integrity sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw== - -des.js@^1.0.0: - version "1.0.1" - resolved "https://registry.yarnpkg.com/des.js/-/des.js-1.0.1.tgz#5382142e1bdc53f85d86d53e5f4aa7deb91e0843" - integrity sha512-Q0I4pfFrv2VPd34/vfLrFOoRmlYj3OV50i7fskps1jZWK1kApMWWT9G6RRUeYedLcBDIhnSDaUvJMb3AhUlaEA== - dependencies: - inherits "^2.0.1" - minimalistic-assert "^1.0.0" - -destroy@~1.0.4: - version "1.0.4" - resolved "https://registry.yarnpkg.com/destroy/-/destroy-1.0.4.tgz#978857442c44749e4206613e37946205826abd80" - integrity sha1-l4hXRCxEdJ5CBmE+N5RiBYJqvYA= - -detect-file@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/detect-file/-/detect-file-1.0.0.tgz#f0d66d03672a825cb1b73bdb3fe62310c8e552b7" - integrity sha1-8NZtA2cqglyxtzvbP+YjEMjlUrc= - -detect-indent@^6.0.0: - version "6.1.0" - resolved "https://registry.yarnpkg.com/detect-indent/-/detect-indent-6.1.0.tgz#592485ebbbf6b3b1ab2be175c8393d04ca0d57e6" - integrity sha512-reYkTUJAZb9gUuZ2RvVCNhVHdg62RHnJ7WJl8ftMi4diZ6NWlciOzQN88pUhSELEwflJht4oQDv0F0BMlwaYtA== - -detect-libc@^1.0.3: - version "1.0.3" - resolved "https://registry.yarnpkg.com/detect-libc/-/detect-libc-1.0.3.tgz#fa137c4bd698edf55cd5cd02ac559f91a4c4ba9b" - integrity sha1-+hN8S9aY7fVc1c0CrFWfkaTEups= - -detect-newline@^2.0.0: - version "2.1.0" - resolved "https://registry.yarnpkg.com/detect-newline/-/detect-newline-2.1.0.tgz#f41f1c10be4b00e87b5f13da680759f2c5bfd3e2" - integrity sha1-9B8cEL5LAOh7XxPaaAdZ8sW/0+I= - -detect-newline@^3.1.0: - version "3.1.0" - resolved "https://registry.yarnpkg.com/detect-newline/-/detect-newline-3.1.0.tgz#576f5dfc63ae1a192ff192d8ad3af6308991b651" - integrity sha512-TLz+x/vEXm/Y7P7wn1EJFNLxYpUD4TgMosxY6fAVJUnJMbupHBOncxyWUG9OpTaH9EBD7uFI5LfEgmMOc54DsA== - -detective@^5.2.0: - version "5.2.0" - resolved "https://registry.yarnpkg.com/detective/-/detective-5.2.0.tgz#feb2a77e85b904ecdea459ad897cc90a99bd2a7b" - integrity sha512-6SsIx+nUUbuK0EthKjv0zrdnajCCXVYGmbYYiYjFVpzcjwEs/JMDZ8tPRG29J/HhN56t3GJp2cGSWDRjjot8Pg== - dependencies: - acorn-node "^1.6.1" - defined "^1.0.0" - minimist "^1.1.1" - -devtools-protocol@0.0.901419: - version "0.0.901419" - resolved "https://registry.yarnpkg.com/devtools-protocol/-/devtools-protocol-0.0.901419.tgz#79b5459c48fe7e1c5563c02bd72f8fec3e0cebcd" - integrity sha512-4INMPwNm9XRpBukhNbF7OB6fNTTCaI8pzy/fXg0xQzAy5h3zL1P8xT3QazgKqBrb/hAYwIBizqDBZ7GtJE74QQ== - -devtools-protocol@^0.0.925217: - version "0.0.925217" - resolved "https://registry.yarnpkg.com/devtools-protocol/-/devtools-protocol-0.0.925217.tgz#ac44689c0bc6e187da5cbd71bd10ebec3761b4d1" - integrity sha512-sI7aLeM9VcH1f+HYEGWaPv2RlWmfBCsnHt/rsPzJ4MCyejvx5R5fauW1dll7OIyE6frwXoEzqi7Y0925XdFIKA== - -devtools@7.13.2: - version "7.13.2" - resolved "https://registry.yarnpkg.com/devtools/-/devtools-7.13.2.tgz#00ed825f7402aa9c28b1c15241167205edd759b9" - integrity sha512-MwxanDu5o01uoWC6CzAoJASQSJRouJmz0rUgMu+5sZYnQ2f7J5QBB4djbPfGAr7titbQcVPkzASZXg54FP2PIg== - dependencies: - "@types/node" "^15.12.5" - "@wdio/config" "7.13.2" - "@wdio/logger" "7.7.0" - "@wdio/protocols" "7.13.2" - "@wdio/types" "7.13.2" - "@wdio/utils" "7.13.2" - chrome-launcher "^0.14.0" - edge-paths "^2.1.0" - puppeteer-core "^10.1.0" - query-selector-shadow-dom "^1.0.0" - ua-parser-js "^0.7.21" - uuid "^8.0.0" - -di@^0.0.1: - version "0.0.1" - resolved "https://registry.yarnpkg.com/di/-/di-0.0.1.tgz#806649326ceaa7caa3306d75d985ea2748ba913c" - integrity sha1-gGZJMmzqp8qjMG112YXqJ0i6kTw= - -diff-sequences@^27.0.6: - version "27.0.6" - resolved "https://registry.yarnpkg.com/diff-sequences/-/diff-sequences-27.0.6.tgz#3305cb2e55a033924054695cc66019fd7f8e5723" - integrity sha512-ag6wfpBFyNXZ0p8pcuIDS//D8H062ZQJ3fzYxjpmeKjnz8W4pekL3AI8VohmyZmsWW2PWaHgjsmqR6L13101VQ== - -diff@3.5.0, diff@^3.1.0: - version "3.5.0" - resolved "https://registry.yarnpkg.com/diff/-/diff-3.5.0.tgz#800c0dd1e0a8bfbc95835c202ad220fe317e5a12" - integrity sha512-A46qtFgd+g7pDZinpnwiRJtxbC1hpgf0uzP3iG89scHk0AUC7A1TGxf5OiiOUv/JMZR8GOt8hL900hV0bOy5xA== - -diff@5.0.0, diff@^5.0.0: - version "5.0.0" - resolved "https://registry.yarnpkg.com/diff/-/diff-5.0.0.tgz#7ed6ad76d859d030787ec35855f5b1daf31d852b" - integrity sha512-/VTCrvm5Z0JGty/BWHljh+BAiw3IK+2j87NGMu8Nwc/f48WoDAC395uomO9ZD117ZOBaHmkX1oyLvkVM/aIT3w== - -diff@^4.0.1: - version "4.0.2" - resolved "https://registry.yarnpkg.com/diff/-/diff-4.0.2.tgz#60f3aecb89d5fae520c11aa19efc2bb982aade7d" - integrity sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A== - -diffie-hellman@^5.0.0: - version "5.0.3" - resolved "https://registry.yarnpkg.com/diffie-hellman/-/diffie-hellman-5.0.3.tgz#40e8ee98f55a2149607146921c63e1ae5f3d2875" - integrity sha512-kqag/Nl+f3GwyK25fhUMYj81BUOrZ9IuJsjIcDE5icNM9FJHAVm3VcUDxdLPoQtTuUylWm6ZIknYJwwaPxsUzg== - dependencies: - bn.js "^4.1.0" - miller-rabin "^4.0.0" - randombytes "^2.0.0" - -dlv@1.1.3: - version "1.1.3" - resolved "https://registry.yarnpkg.com/dlv/-/dlv-1.1.3.tgz#5c198a8a11453596e751494d49874bc7732f2e79" - integrity sha512-+HlytyjlPKnIG8XuRG8WvmBP8xs8P71y+SKKS6ZXWoEgLuePxtDoUEiH7WkdePWrQ5JBpE6aoVqfZfJUQkjXwA== - -doctrine-temporary-fork@2.1.0: - version "2.1.0" - resolved "https://registry.yarnpkg.com/doctrine-temporary-fork/-/doctrine-temporary-fork-2.1.0.tgz#36f2154f556ee4f1e60311d391cd23de5187ed57" - integrity sha512-nliqOv5NkE4zMON4UA6AMJE6As35afs8aYXATpU4pTUdIKiARZwrJVEP1boA3Rx1ZXHVkwxkhcq4VkqvsuRLsA== - dependencies: - esutils "^2.0.2" - -doctrine@^2.1.0: - version "2.1.0" - resolved "https://registry.yarnpkg.com/doctrine/-/doctrine-2.1.0.tgz#5cd01fc101621b42c4cd7f5d1a66243716d3f39d" - integrity sha512-35mSku4ZXK0vfCuHEDAwt55dg2jNajHZ1odvF+8SSr82EsZY4QmXfuWso8oEd8zRhVObSN18aM0CjSdoBX7zIw== - dependencies: - esutils "^2.0.2" - -doctrine@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/doctrine/-/doctrine-3.0.0.tgz#addebead72a6574db783639dc87a121773973961" - integrity sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w== - dependencies: - esutils "^2.0.2" - -documentation@^13.2.5: - version "13.2.5" - resolved "https://registry.yarnpkg.com/documentation/-/documentation-13.2.5.tgz#2d4c8a94ce60a0342d6981d34488ad6184e463a0" - integrity sha512-d1TrfrHXYZR63xrOzkYwwe297vkSwBoEhyyMBOi20T+7Ohe1aX1dW4nqXncQmdmE5MxluSaxxa3BW1dCvbF5AQ== - dependencies: - "@babel/core" "7.12.3" - "@babel/generator" "7.12.1" - "@babel/parser" "7.12.3" - "@babel/traverse" "^7.12.1" - "@babel/types" "^7.12.1" - ansi-html "^0.0.7" - babelify "^10.0.0" - chalk "^2.3.0" - chokidar "^3.4.0" - concat-stream "^1.6.0" - diff "^4.0.1" - doctrine-temporary-fork "2.1.0" - get-port "^5.0.0" - git-url-parse "^11.1.2" - github-slugger "1.2.0" - glob "^7.1.2" - globals-docs "^2.4.0" - highlight.js "^10.7.2" - ini "^1.3.5" - js-yaml "^3.10.0" - lodash "^4.17.10" - mdast-util-find-and-replace "^1.1.1" - mdast-util-inject "^1.1.0" - micromatch "^3.1.5" - mime "^2.2.0" - module-deps-sortable "^5.0.3" - parse-filepath "^1.0.2" - pify "^5.0.0" - read-pkg-up "^4.0.0" - remark "^13.0.0" - remark-gfm "^1.0.0" - remark-html "^13.0.1" - remark-reference-links "^5.0.0" - remark-toc "^7.2.0" - resolve "^1.8.1" - stream-array "^1.1.2" - strip-json-comments "^2.0.1" - tiny-lr "^1.1.0" - unist-builder "^2.0.3" - unist-util-visit "^2.0.3" - vfile "^4.0.0" - vfile-reporter "^6.0.0" - vfile-sort "^2.1.0" - vinyl "^2.1.0" - vinyl-fs "^3.0.2" - yargs "^15.3.1" - optionalDependencies: - "@vue/compiler-sfc" "^3.0.11" - vue-template-compiler "^2.6.12" - -dom-serialize@^2.2.1: - version "2.2.1" - resolved "https://registry.yarnpkg.com/dom-serialize/-/dom-serialize-2.2.1.tgz#562ae8999f44be5ea3076f5419dcd59eb43ac95b" - integrity sha1-ViromZ9Evl6jB29UGdzVnrQ6yVs= - dependencies: - custom-event "~1.0.0" - ent "~2.2.0" - extend "^3.0.0" - void-elements "^2.0.0" - -domain-browser@^1.1.1: - version "1.2.0" - resolved "https://registry.yarnpkg.com/domain-browser/-/domain-browser-1.2.0.tgz#3d31f50191a6749dd1375a7f522e823d42e54eda" - integrity sha512-jnjyiM6eRyZl2H+W8Q/zLMA481hzi0eszAaBUzIVnmYVDBbnLxVNnfu1HgEBvCbL+71FrxMl3E6lpKH7Ge3OXA== - -dot-prop@^5.1.0: - version "5.3.0" - resolved "https://registry.yarnpkg.com/dot-prop/-/dot-prop-5.3.0.tgz#90ccce708cd9cd82cc4dc8c3ddd9abdd55b20e88" - integrity sha512-QM8q3zDe58hqUqjraQOmzZ1LIH9SWQJTlEKCH4kJ2oQvLZk7RbQXvtDM2XEq3fwkV9CCvvH4LA0AV+ogFsBM2Q== - dependencies: - is-obj "^2.0.0" - -dotgitignore@^2.1.0: - version "2.1.0" - resolved "https://registry.yarnpkg.com/dotgitignore/-/dotgitignore-2.1.0.tgz#a4b15a4e4ef3cf383598aaf1dfa4a04bcc089b7b" - integrity sha512-sCm11ak2oY6DglEPpCB8TixLjWAxd3kJTs6UIcSasNYxXdFPV+YKlye92c8H4kKFqV5qYMIh7d+cYecEg0dIkA== - dependencies: - find-up "^3.0.0" - minimatch "^3.0.4" - -dset@2.0.1: - version "2.0.1" - resolved "https://registry.yarnpkg.com/dset/-/dset-2.0.1.tgz#a15fff3d1e4d60ac0c95634625cbd5441a76deb1" - integrity sha512-nI29OZMRYq36hOcifB6HTjajNAAiBKSXsyWZrq+VniusseuP2OpNlTiYgsaNRSGvpyq5Wjbc2gQLyBdTyWqhnQ== - -duplexer2@0.0.2: - version "0.0.2" - resolved "https://registry.yarnpkg.com/duplexer2/-/duplexer2-0.0.2.tgz#c614dcf67e2fb14995a91711e5a617e8a60a31db" - integrity sha1-xhTc9n4vsUmVqRcR5aYX6KYKMds= - dependencies: - readable-stream "~1.1.9" - -duplexer2@^0.1.2, duplexer2@~0.1.0, duplexer2@~0.1.4: - version "0.1.4" - resolved "https://registry.yarnpkg.com/duplexer2/-/duplexer2-0.1.4.tgz#8b12dab878c0d69e3e7891051662a32fc6bddcc1" - integrity sha1-ixLauHjA1p4+eJEFFmKjL8a93ME= - dependencies: - readable-stream "^2.0.2" - -duplexer@^0.1.1, duplexer@~0.1.1: - version "0.1.2" - resolved "https://registry.yarnpkg.com/duplexer/-/duplexer-0.1.2.tgz#3abe43aef3835f8ae077d136ddce0f276b0400e6" - integrity sha512-jtD6YG370ZCIi/9GTaJKQxWTZD045+4R4hTk/x1UyoqadyJ9x9CgSi1RlVDQF8U2sxLLSnFkCaMihqljHIWgMg== - -duplexify@^3.6.0: - version "3.7.1" - resolved "https://registry.yarnpkg.com/duplexify/-/duplexify-3.7.1.tgz#2a4df5317f6ccfd91f86d6fd25d8d8a103b88309" - integrity sha512-07z8uv2wMyS51kKhD1KsdXJg5WQ6t93RneqRxUHnskXVtlYYkLqM0gqStQZ3pj073g687jPCHrqNfCzawLYh5g== - dependencies: - end-of-stream "^1.0.0" - inherits "^2.0.1" - readable-stream "^2.0.0" - stream-shift "^1.0.0" - -duplexify@^4.1.1: - version "4.1.2" - resolved "https://registry.yarnpkg.com/duplexify/-/duplexify-4.1.2.tgz#18b4f8d28289132fa0b9573c898d9f903f81c7b0" - integrity sha512-fz3OjcNCHmRP12MJoZMPglx8m4rrFP8rovnk4vT8Fs+aonZoCwGg10dSsQsfP/E62eZcPTMSMP6686fu9Qlqtw== - dependencies: - end-of-stream "^1.4.1" - inherits "^2.0.3" - readable-stream "^3.1.1" - stream-shift "^1.0.0" - -each-props@^1.3.2: - version "1.3.2" - resolved "https://registry.yarnpkg.com/each-props/-/each-props-1.3.2.tgz#ea45a414d16dd5cfa419b1a81720d5ca06892333" - integrity sha512-vV0Hem3zAGkJAyU7JSjixeU66rwdynTAa1vofCrSA5fEln+m67Az9CcnkVD776/fsN/UjIWmBDoNRS6t6G9RfA== - dependencies: - is-plain-object "^2.0.1" - object.defaults "^1.1.0" - -easy-table@^1.1.1: - version "1.1.1" - resolved "https://registry.yarnpkg.com/easy-table/-/easy-table-1.1.1.tgz#c1b9b9ad68a017091a1c235e4bcba277540e143f" - integrity sha512-C9Lvm0WFcn2RgxbMnTbXZenMIWcBtkzMr+dWqq/JsVoGFSVUVlPqeOa5LP5kM0I3zoOazFpckOEb2/0LDFfToQ== - dependencies: - ansi-regex "^3.0.0" - optionalDependencies: - wcwidth ">=1.0.1" - -ecc-jsbn@~0.1.1: - version "0.1.2" - resolved "https://registry.yarnpkg.com/ecc-jsbn/-/ecc-jsbn-0.1.2.tgz#3a83a904e54353287874c564b7549386849a98c9" - integrity sha1-OoOpBOVDUyh4dMVkt1SThoSamMk= - dependencies: - jsbn "~0.1.0" - safer-buffer "^2.1.0" - -edge-paths@^2.1.0: - version "2.2.1" - resolved "https://registry.yarnpkg.com/edge-paths/-/edge-paths-2.2.1.tgz#d2d91513225c06514aeac9843bfce546abbf4391" - integrity sha512-AI5fC7dfDmCdKo3m5y7PkYE8m6bMqR6pvVpgtrZkkhcJXFLelUgkjrhk3kXXx8Kbw2cRaTT4LkOR7hqf39KJdw== - dependencies: - "@types/which" "^1.3.2" - which "^2.0.2" - -ee-first@1.1.1: - version "1.1.1" - resolved "https://registry.yarnpkg.com/ee-first/-/ee-first-1.1.1.tgz#590c61156b0ae2f4f0255732a158b266bc56b21d" - integrity sha1-WQxhFWsK4vTwJVcyoViyZrxWsh0= - -ejs@^2.6.1: - version "2.7.4" - resolved "https://registry.yarnpkg.com/ejs/-/ejs-2.7.4.tgz#48661287573dcc53e366c7a1ae52c3a120eec9ba" - integrity sha512-7vmuyh5+kuUyJKePhQfRQBhXV5Ce+RnaeeQArKu1EAMpL3WbgMt5WG6uQZpEVvYSSsxMXRKOewtDk9RaTKXRlA== - -ejs@^3.0.1: - version "3.1.6" - resolved "https://registry.yarnpkg.com/ejs/-/ejs-3.1.6.tgz#5bfd0a0689743bb5268b3550cceeebbc1702822a" - integrity sha512-9lt9Zse4hPucPkoP7FHDF0LQAlGyF9JVpnClFLFH3aSSbxmyoqINRpp/9wePWJTUl4KOQwRL72Iw3InHPDkoGw== - dependencies: - jake "^10.6.1" - -electron-to-chromium@^1.3.854: - version "1.3.857" - resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.3.857.tgz#dcc239ff8a12b6e4b501e6a5ad20fd0d5a3210f9" - integrity sha512-a5kIr2lajm4bJ5E4D3fp8Y/BRB0Dx2VOcCRE5Gtb679mXIME/OFhWler8Gy2ksrf8gFX+EFCSIGA33FB3gqYpg== - -elliptic@^6.5.3: - version "6.5.4" - resolved "https://registry.yarnpkg.com/elliptic/-/elliptic-6.5.4.tgz#da37cebd31e79a1367e941b592ed1fbebd58abbb" - integrity sha512-iLhC6ULemrljPZb+QutR5TQGB+pdW6KGD5RSegS+8sorOZT+rdQFbsQFJgvN3eRqNALqJer4oQ16YvJHlU8hzQ== - dependencies: - bn.js "^4.11.9" - brorand "^1.1.0" - hash.js "^1.0.0" - hmac-drbg "^1.0.1" - inherits "^2.0.4" - minimalistic-assert "^1.0.1" - minimalistic-crypto-utils "^1.0.1" - -"emoji-regex@>=6.0.0 <=6.1.1": - version "6.1.1" - resolved "https://registry.yarnpkg.com/emoji-regex/-/emoji-regex-6.1.1.tgz#c6cd0ec1b0642e2a3c67a1137efc5e796da4f88e" - integrity sha1-xs0OwbBkLio8Z6ETfvxeeW2k+I4= - -emoji-regex@^8.0.0: - version "8.0.0" - resolved "https://registry.yarnpkg.com/emoji-regex/-/emoji-regex-8.0.0.tgz#e818fd69ce5ccfcb404594f842963bf53164cc37" - integrity sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A== - -emojis-list@^2.0.0: - version "2.1.0" - resolved "https://registry.yarnpkg.com/emojis-list/-/emojis-list-2.1.0.tgz#4daa4d9db00f9819880c79fa457ae5b09a1fd389" - integrity sha1-TapNnbAPmBmIDHn6RXrlsJof04k= - -emojis-list@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/emojis-list/-/emojis-list-3.0.0.tgz#5570662046ad29e2e916e71aae260abdff4f6a78" - integrity sha512-/kyM18EfinwXZbno9FyUGeFh87KC8HRQBQGildHZbEuRyWFOmv1U10o9BBp8XVZDVNNuQKyIGIu5ZYAAXJ0V2Q== - -encodeurl@~1.0.2: - version "1.0.2" - resolved "https://registry.yarnpkg.com/encodeurl/-/encodeurl-1.0.2.tgz#ad3ff4c86ec2d029322f5a02c3a9a606c95b3f59" - integrity sha1-rT/0yG7C0CkyL1oCw6mmBslbP1k= - -end-of-stream@^1.0.0, end-of-stream@^1.1.0, end-of-stream@^1.4.1: - version "1.4.4" - resolved "https://registry.yarnpkg.com/end-of-stream/-/end-of-stream-1.4.4.tgz#5ae64a5f45057baf3626ec14da0ca5e4b2431eb0" - integrity sha512-+uw1inIHVPQoaVuHzRyXd21icM+cnt4CzD5rW+NC1wjOUSTOs+Te7FOv7AhN7vS9x/oIyhLP5PR1H+phQAHu5Q== - dependencies: - once "^1.4.0" - -engine.io-parser@~4.0.0: - version "4.0.3" - resolved "https://registry.yarnpkg.com/engine.io-parser/-/engine.io-parser-4.0.3.tgz#83d3a17acfd4226f19e721bb22a1ee8f7662d2f6" - integrity sha512-xEAAY0msNnESNPc00e19y5heTPX4y/TJ36gr8t1voOaNmTojP9b3oK3BbJLFufW2XFPQaaijpFewm2g2Um3uqA== - dependencies: - base64-arraybuffer "0.1.4" - -engine.io@~4.1.0: - version "4.1.1" - resolved "https://registry.yarnpkg.com/engine.io/-/engine.io-4.1.1.tgz#9a8f8a5ac5a5ea316183c489bf7f5b6cf91ace5b" - integrity sha512-t2E9wLlssQjGw0nluF6aYyfX8LwYU8Jj0xct+pAhfWfv/YrBn6TSNtEYsgxHIfaMqfrLx07czcMg9bMN6di+3w== - dependencies: - accepts "~1.3.4" - base64id "2.0.0" - cookie "~0.4.1" - cors "~2.8.5" - debug "~4.3.1" - engine.io-parser "~4.0.0" - ws "~7.4.2" - -enhanced-resolve@^3.4.0: - version "3.4.1" - resolved "https://registry.yarnpkg.com/enhanced-resolve/-/enhanced-resolve-3.4.1.tgz#0421e339fd71419b3da13d129b3979040230476e" - integrity sha1-BCHjOf1xQZs9oT0Smzl5BAIwR24= - dependencies: - graceful-fs "^4.1.2" - memory-fs "^0.4.0" - object-assign "^4.0.1" - tapable "^0.2.7" - -enhanced-resolve@~0.9.0: - version "0.9.1" - resolved "https://registry.yarnpkg.com/enhanced-resolve/-/enhanced-resolve-0.9.1.tgz#4d6e689b3725f86090927ccc86cd9f1635b89e2e" - integrity sha1-TW5omzcl+GCQknzMhs2fFjW4ni4= - dependencies: - graceful-fs "^4.1.2" - memory-fs "^0.2.0" - tapable "^0.1.8" - -enquirer@^2.3.5: - version "2.3.6" - resolved "https://registry.yarnpkg.com/enquirer/-/enquirer-2.3.6.tgz#2a7fe5dd634a1e4125a975ec994ff5456dc3734d" - integrity sha512-yjNnPr315/FjS4zIsUxYguYUPP2e1NK4d7E7ZOLiyYCcbFBiTMyID+2wvm2w6+pZ/odMA7cRkjhsPbltwBOrLg== - dependencies: - ansi-colors "^4.1.1" - -ent@~2.2.0: - version "2.2.0" - resolved "https://registry.yarnpkg.com/ent/-/ent-2.2.0.tgz#e964219325a21d05f44466a2f686ed6ce5f5dd1d" - integrity sha1-6WQhkyWiHQX0RGai9obtbOX13R0= - -errno@^0.1.3: - version "0.1.8" - resolved "https://registry.yarnpkg.com/errno/-/errno-0.1.8.tgz#8bb3e9c7d463be4976ff888f76b4809ebc2e811f" - integrity sha512-dJ6oBr5SQ1VSd9qkk7ByRgb/1SH4JZjCHSW/mr63/QcXO9zLVxvJ6Oy13nio03rxpSnVDDjFor75SjVeZWPW/A== - dependencies: - prr "~1.0.1" - -error-ex@^1.2.0, error-ex@^1.3.1: - version "1.3.2" - resolved "https://registry.yarnpkg.com/error-ex/-/error-ex-1.3.2.tgz#b4ac40648107fdcdcfae242f428bea8a14d4f1bf" - integrity sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g== - dependencies: - is-arrayish "^0.2.1" - -error@^7.0.0: - version "7.2.1" - resolved "https://registry.yarnpkg.com/error/-/error-7.2.1.tgz#eab21a4689b5f684fc83da84a0e390de82d94894" - integrity sha512-fo9HBvWnx3NGUKMvMwB/CBCMMrfEJgbDTVDEkPygA3Bdd3lM1OyCd+rbQ8BwnpF6GdVeOLDNmyL4N5Bg80ZvdA== - dependencies: - string-template "~0.2.1" - -es-abstract@^1.18.0-next.2, es-abstract@^1.18.5, es-abstract@^1.19.0, es-abstract@^1.19.1: - version "1.19.1" - resolved "https://registry.yarnpkg.com/es-abstract/-/es-abstract-1.19.1.tgz#d4885796876916959de78edaa0df456627115ec3" - integrity sha512-2vJ6tjA/UfqLm2MPs7jxVybLoB8i1t1Jd9R3kISld20sIxPcTbLuggQOUxeWeAvIUkduv/CfMjuh4WmiXr2v9w== - dependencies: - call-bind "^1.0.2" - es-to-primitive "^1.2.1" - function-bind "^1.1.1" - get-intrinsic "^1.1.1" - get-symbol-description "^1.0.0" - has "^1.0.3" - has-symbols "^1.0.2" - internal-slot "^1.0.3" - is-callable "^1.2.4" - is-negative-zero "^2.0.1" - is-regex "^1.1.4" - is-shared-array-buffer "^1.0.1" - is-string "^1.0.7" - is-weakref "^1.0.1" - object-inspect "^1.11.0" - object-keys "^1.1.1" - object.assign "^4.1.2" - string.prototype.trimend "^1.0.4" - string.prototype.trimstart "^1.0.4" - unbox-primitive "^1.0.1" - -es-get-iterator@^1.1.1: - version "1.1.2" - resolved "https://registry.yarnpkg.com/es-get-iterator/-/es-get-iterator-1.1.2.tgz#9234c54aba713486d7ebde0220864af5e2b283f7" - integrity sha512-+DTO8GYwbMCwbywjimwZMHp8AuYXOS2JZFWoi2AlPOS3ebnII9w/NLpNZtA7A0YLaVDw+O7KFCeoIV7OPvM7hQ== - dependencies: - call-bind "^1.0.2" - get-intrinsic "^1.1.0" - has-symbols "^1.0.1" - is-arguments "^1.1.0" - is-map "^2.0.2" - is-set "^2.0.2" - is-string "^1.0.5" - isarray "^2.0.5" - -es-to-primitive@^1.2.1: - version "1.2.1" - resolved "https://registry.yarnpkg.com/es-to-primitive/-/es-to-primitive-1.2.1.tgz#e55cd4c9cdc188bcefb03b366c736323fc5c898a" - integrity sha512-QCOllgZJtaUo9miYBcLChTUaHNjJF3PYs1VidD7AwiEj1kYxKeQTctLAezAOH5ZKRH0g2IgPn6KwB4IT8iRpvA== - dependencies: - is-callable "^1.1.4" - is-date-object "^1.0.1" - is-symbol "^1.0.2" - -es5-ext@^0.10.35, es5-ext@^0.10.46, es5-ext@^0.10.50, es5-ext@^0.10.53, es5-ext@~0.10.14, es5-ext@~0.10.2, es5-ext@~0.10.46: - version "0.10.53" - resolved "https://registry.yarnpkg.com/es5-ext/-/es5-ext-0.10.53.tgz#93c5a3acfdbef275220ad72644ad02ee18368de1" - integrity sha512-Xs2Stw6NiNHWypzRTY1MtaG/uJlwCk8kH81920ma8mvN8Xq1gsfhZvpkImLQArw8AHnv8MT2I45J3c0R8slE+Q== - dependencies: - es6-iterator "~2.0.3" - es6-symbol "~3.1.3" - next-tick "~1.0.0" - -es5-shim@^4.0.5, es5-shim@^4.5.14: - version "4.6.2" - resolved "https://registry.yarnpkg.com/es5-shim/-/es5-shim-4.6.2.tgz#827cdd0c6fb5beb26fd368d65430e8b5eaeba942" - integrity sha512-n0XTVMGps+Deyr38jtqKPR5F5hb9owYeRQcKJW39eFvzUk/u/9Ww315werRzbiNMnHCUw/YHDPBphTlEnzdi+A== - -es6-iterator@^2.0.1, es6-iterator@^2.0.3, es6-iterator@~2.0.1, es6-iterator@~2.0.3: - version "2.0.3" - resolved "https://registry.yarnpkg.com/es6-iterator/-/es6-iterator-2.0.3.tgz#a7de889141a05a94b0854403b2d0a0fbfa98f3b7" - integrity sha1-p96IkUGgWpSwhUQDstCg+/qY87c= - dependencies: - d "1" - es5-ext "^0.10.35" - es6-symbol "^3.1.1" - -es6-map@^0.1.3: - version "0.1.5" - resolved "https://registry.yarnpkg.com/es6-map/-/es6-map-0.1.5.tgz#9136e0503dcc06a301690f0bb14ff4e364e949f0" - integrity sha1-kTbgUD3MBqMBaQ8LsU/042TpSfA= - dependencies: - d "1" - es5-ext "~0.10.14" - es6-iterator "~2.0.1" - es6-set "~0.1.5" - es6-symbol "~3.1.1" - event-emitter "~0.3.5" - -es6-promise@^4.0.3: - version "4.2.8" - resolved "https://registry.yarnpkg.com/es6-promise/-/es6-promise-4.2.8.tgz#4eb21594c972bc40553d276e510539143db53e0a" - integrity sha512-HJDGx5daxeIvxdBxvG2cb9g4tEvwIk3i8+nhX0yGrYmZUzbkdg8QbDevheDB8gd0//uPj4c1EQua8Q+MViT0/w== - -es6-promisify@^5.0.0: - version "5.0.0" - resolved "https://registry.yarnpkg.com/es6-promisify/-/es6-promisify-5.0.0.tgz#5109d62f3e56ea967c4b63505aef08291c8a5203" - integrity sha1-UQnWLz5W6pZ8S2NQWu8IKRyKUgM= - dependencies: - es6-promise "^4.0.3" - -es6-set@~0.1.5: - version "0.1.5" - resolved "https://registry.yarnpkg.com/es6-set/-/es6-set-0.1.5.tgz#d2b3ec5d4d800ced818db538d28974db0a73ccb1" - integrity sha1-0rPsXU2ADO2BjbU40ol02wpzzLE= - dependencies: - d "1" - es5-ext "~0.10.14" - es6-iterator "~2.0.1" - es6-symbol "3.1.1" - event-emitter "~0.3.5" - -es6-symbol@3.1.1: - version "3.1.1" - resolved "https://registry.yarnpkg.com/es6-symbol/-/es6-symbol-3.1.1.tgz#bf00ef4fdab6ba1b46ecb7b629b4c7ed5715cc77" - integrity sha1-vwDvT9q2uhtG7Le2KbTH7VcVzHc= - dependencies: - d "1" - es5-ext "~0.10.14" - -es6-symbol@^3.1.1, es6-symbol@~3.1.1, es6-symbol@~3.1.3: - version "3.1.3" - resolved "https://registry.yarnpkg.com/es6-symbol/-/es6-symbol-3.1.3.tgz#bad5d3c1bcdac28269f4cb331e431c78ac705d18" - integrity sha512-NJ6Yn3FuDinBaBRWl/q5X/s4koRHBrgKAu+yGI6JCBeiu3qrcbJhwT2GeR/EXVfylRk8dpQVJoLEFhK+Mu31NA== - dependencies: - d "^1.0.1" - ext "^1.1.2" - -es6-weak-map@^2.0.1, es6-weak-map@^2.0.3: - version "2.0.3" - resolved "https://registry.yarnpkg.com/es6-weak-map/-/es6-weak-map-2.0.3.tgz#b6da1f16cc2cc0d9be43e6bdbfc5e7dfcdf31d53" - integrity sha512-p5um32HOTO1kP+w7PRnB+5lQ43Z6muuMuIMffvDN8ZB4GcnjLBV6zGStpbASIMk4DCAvEaamhe2zhyCb/QXXsA== - dependencies: - d "1" - es5-ext "^0.10.46" - es6-iterator "^2.0.3" - es6-symbol "^3.1.1" - -escalade@^3.1.1: - version "3.1.1" - resolved "https://registry.yarnpkg.com/escalade/-/escalade-3.1.1.tgz#d8cfdc7000965c5a0174b4a82eaa5c0552742e40" - integrity sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw== - -escape-html@~1.0.3: - version "1.0.3" - resolved "https://registry.yarnpkg.com/escape-html/-/escape-html-1.0.3.tgz#0258eae4d3d0c0974de1c169188ef0051d1d1988" - integrity sha1-Aljq5NPQwJdN4cFpGI7wBR0dGYg= - -escape-string-regexp@1.0.5, escape-string-regexp@^1.0.0, escape-string-regexp@^1.0.2, escape-string-regexp@^1.0.3, escape-string-regexp@^1.0.5: - version "1.0.5" - resolved "https://registry.yarnpkg.com/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz#1b61c0562190a8dff6ae3bb2cf0200ca130b86d4" - integrity sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ= - -escape-string-regexp@4.0.0, escape-string-regexp@^4.0.0: - version "4.0.0" - resolved "https://registry.yarnpkg.com/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz#14ba83a5d373e3d311e5afca29cf5bfad965bf34" - integrity sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA== - -escape-string-regexp@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/escape-string-regexp/-/escape-string-regexp-2.0.0.tgz#a30304e99daa32e23b2fd20f51babd07cffca344" - integrity sha512-UpzcLCXolUWcNu5HtVMHYdXJjArjsF9C0aNnquZYY4uW/Vu0miy5YoWvbV345HauVvcAUnpRuhMMcqTcGOY2+w== - -escodegen@1.8.x: - version "1.8.1" - resolved "https://registry.yarnpkg.com/escodegen/-/escodegen-1.8.1.tgz#5a5b53af4693110bebb0867aa3430dd3b70a1018" - integrity sha1-WltTr0aTEQvrsIZ6o0MN07cKEBg= - dependencies: - esprima "^2.7.1" - estraverse "^1.9.1" - esutils "^2.0.2" - optionator "^0.8.1" - optionalDependencies: - source-map "~0.2.0" - -escope@^3.6.0: - version "3.6.0" - resolved "https://registry.yarnpkg.com/escope/-/escope-3.6.0.tgz#e01975e812781a163a6dadfdd80398dc64c889c3" - integrity sha1-4Bl16BJ4GhY6ba392AOY3GTIicM= - dependencies: - es6-map "^0.1.3" - es6-weak-map "^2.0.1" - esrecurse "^4.1.0" - estraverse "^4.1.1" - -eslint-config-standard@^10.2.1: - version "10.2.1" - resolved "https://registry.yarnpkg.com/eslint-config-standard/-/eslint-config-standard-10.2.1.tgz#c061e4d066f379dc17cd562c64e819b4dd454591" - integrity sha1-wGHk0GbzedwXzVYsZOgZtN1FRZE= - -eslint-import-resolver-node@^0.3.6: - version "0.3.6" - resolved "https://registry.yarnpkg.com/eslint-import-resolver-node/-/eslint-import-resolver-node-0.3.6.tgz#4048b958395da89668252001dbd9eca6b83bacbd" - integrity sha512-0En0w03NRVMn9Uiyn8YRPDKvWjxCWkslUEhGNTdGx15RvPJYQ+lbOlqrlNI2vEAs4pDYK4f/HN2TbDmk5TP0iw== - dependencies: - debug "^3.2.7" - resolve "^1.20.0" - -eslint-module-utils@^2.6.2: - version "2.6.2" - resolved "https://registry.yarnpkg.com/eslint-module-utils/-/eslint-module-utils-2.6.2.tgz#94e5540dd15fe1522e8ffa3ec8db3b7fa7e7a534" - integrity sha512-QG8pcgThYOuqxupd06oYTZoNOGaUdTY1PqK+oS6ElF6vs4pBdk/aYxFVQQXzcrAqp9m7cl7lb2ubazX+g16k2Q== - dependencies: - debug "^3.2.7" - pkg-dir "^2.0.0" - -eslint-plugin-es@^3.0.0: - version "3.0.1" - resolved "https://registry.yarnpkg.com/eslint-plugin-es/-/eslint-plugin-es-3.0.1.tgz#75a7cdfdccddc0589934aeeb384175f221c57893" - integrity sha512-GUmAsJaN4Fc7Gbtl8uOBlayo2DqhwWvEzykMHSCZHU3XdJ+NSzzZcVhXh3VxX5icqQ+oQdIEawXX8xkR3mIFmQ== - dependencies: - eslint-utils "^2.0.0" - regexpp "^3.0.0" - -eslint-plugin-import@^2.20.2: - version "2.24.2" - resolved "https://registry.yarnpkg.com/eslint-plugin-import/-/eslint-plugin-import-2.24.2.tgz#2c8cd2e341f3885918ee27d18479910ade7bb4da" - integrity sha512-hNVtyhiEtZmpsabL4neEj+6M5DCLgpYyG9nzJY8lZQeQXEn5UPW1DpUdsMHMXsq98dbNm7nt1w9ZMSVpfJdi8Q== - dependencies: - array-includes "^3.1.3" - array.prototype.flat "^1.2.4" - debug "^2.6.9" - doctrine "^2.1.0" - eslint-import-resolver-node "^0.3.6" - eslint-module-utils "^2.6.2" - find-up "^2.0.0" - has "^1.0.3" - is-core-module "^2.6.0" - minimatch "^3.0.4" - object.values "^1.1.4" - pkg-up "^2.0.0" - read-pkg-up "^3.0.0" - resolve "^1.20.0" - tsconfig-paths "^3.11.0" - -eslint-plugin-node@^11.1.0: - version "11.1.0" - resolved "https://registry.yarnpkg.com/eslint-plugin-node/-/eslint-plugin-node-11.1.0.tgz#c95544416ee4ada26740a30474eefc5402dc671d" - integrity sha512-oUwtPJ1W0SKD0Tr+wqu92c5xuCeQqB3hSCHasn/ZgjFdA9iDGNkNf2Zi9ztY7X+hNuMib23LNGRm6+uN+KLE3g== - dependencies: - eslint-plugin-es "^3.0.0" - eslint-utils "^2.0.0" - ignore "^5.1.1" - minimatch "^3.0.4" - resolve "^1.10.1" - semver "^6.1.0" - -"eslint-plugin-prebid@file:./plugins/eslint": - version "1.0.0" - -eslint-plugin-promise@^5.1.0: - version "5.1.0" - resolved "https://registry.yarnpkg.com/eslint-plugin-promise/-/eslint-plugin-promise-5.1.0.tgz#fb2188fb734e4557993733b41aa1a688f46c6f24" - integrity sha512-NGmI6BH5L12pl7ScQHbg7tvtk4wPxxj8yPHH47NvSmMtFneC077PSeY3huFj06ZWZvtbfxSPt3RuOQD5XcR4ng== - -eslint-plugin-standard@^3.0.1: - version "3.1.0" - resolved "https://registry.yarnpkg.com/eslint-plugin-standard/-/eslint-plugin-standard-3.1.0.tgz#2a9e21259ba4c47c02d53b2d0c9135d4b1022d47" - integrity sha512-fVcdyuKRr0EZ4fjWl3c+gp1BANFJD1+RaWa2UPYfMZ6jCtp5RG00kSaXnK/dE5sYzt4kaWJ9qdxqUfc0d9kX0w== - -eslint-scope@^3.7.1: - version "3.7.3" - resolved "https://registry.yarnpkg.com/eslint-scope/-/eslint-scope-3.7.3.tgz#bb507200d3d17f60247636160b4826284b108535" - integrity sha512-W+B0SvF4gamyCTmUc+uITPY0989iXVfKvhwtmJocTaYoc/3khEHmEmvfY/Gn9HA9VV75jrQECsHizkNw1b68FA== - dependencies: - esrecurse "^4.1.0" - estraverse "^4.1.1" - -eslint-scope@^5.1.1: - version "5.1.1" - resolved "https://registry.yarnpkg.com/eslint-scope/-/eslint-scope-5.1.1.tgz#e786e59a66cb92b3f6c1fb0d508aab174848f48c" - integrity sha512-2NxwbF/hZ0KpepYN0cNbo+FN6XoK7GaHlQhgx/hIZl6Va0bF45RQOOwhLIy8lQDbuCiadSLCBnH2CFYquit5bw== - dependencies: - esrecurse "^4.3.0" - estraverse "^4.1.1" - -eslint-utils@^2.0.0, eslint-utils@^2.1.0: - version "2.1.0" - resolved "https://registry.yarnpkg.com/eslint-utils/-/eslint-utils-2.1.0.tgz#d2de5e03424e707dc10c74068ddedae708741b27" - integrity sha512-w94dQYoauyvlDc43XnGB8lU3Zt713vNChgt4EWwhXAP2XkBvndfxF0AgIqKOOasjPIPzj9JqgwkwbCYD0/V3Zg== - dependencies: - eslint-visitor-keys "^1.1.0" - -eslint-visitor-keys@^1.0.0, eslint-visitor-keys@^1.1.0, eslint-visitor-keys@^1.3.0: - version "1.3.0" - resolved "https://registry.yarnpkg.com/eslint-visitor-keys/-/eslint-visitor-keys-1.3.0.tgz#30ebd1ef7c2fdff01c3a4f151044af25fab0523e" - integrity sha512-6J72N8UNa462wa/KFODt/PJ3IU60SDpC3QXC1Hjc1BXXpfL2C9R5+AU7jhe0F6GREqVMh4Juu+NY7xn+6dipUQ== - -eslint-visitor-keys@^2.0.0: - version "2.1.0" - resolved "https://registry.yarnpkg.com/eslint-visitor-keys/-/eslint-visitor-keys-2.1.0.tgz#f65328259305927392c938ed44eb0a5c9b2bd303" - integrity sha512-0rSmRBzXgDzIsD6mGdJgevzgezI534Cer5L/vyMX0kHzT/jiB43jRhd9YUlMGYLQy2zprNmoT8qasCGtY+QaKw== - -eslint@^4.0.0: - version "4.19.1" - resolved "https://registry.yarnpkg.com/eslint/-/eslint-4.19.1.tgz#32d1d653e1d90408854bfb296f076ec7e186a300" - integrity sha512-bT3/1x1EbZB7phzYu7vCr1v3ONuzDtX8WjuM9c0iYxe+cq+pwcKEoQjl7zd3RpC6YOLgnSy3cTN58M2jcoPDIQ== - dependencies: - ajv "^5.3.0" - babel-code-frame "^6.22.0" - chalk "^2.1.0" - concat-stream "^1.6.0" - cross-spawn "^5.1.0" - debug "^3.1.0" - doctrine "^2.1.0" - eslint-scope "^3.7.1" - eslint-visitor-keys "^1.0.0" - espree "^3.5.4" - esquery "^1.0.0" - esutils "^2.0.2" - file-entry-cache "^2.0.0" - functional-red-black-tree "^1.0.1" - glob "^7.1.2" - globals "^11.0.1" - ignore "^3.3.3" - imurmurhash "^0.1.4" - inquirer "^3.0.6" - is-resolvable "^1.0.0" - js-yaml "^3.9.1" - json-stable-stringify-without-jsonify "^1.0.1" - levn "^0.3.0" - lodash "^4.17.4" - minimatch "^3.0.2" - mkdirp "^0.5.1" - natural-compare "^1.4.0" - optionator "^0.8.2" - path-is-inside "^1.0.2" - pluralize "^7.0.0" - progress "^2.0.0" - regexpp "^1.0.1" - require-uncached "^1.0.3" - semver "^5.3.0" - strip-ansi "^4.0.0" - strip-json-comments "~2.0.1" - table "4.0.2" - text-table "~0.2.0" - -eslint@^7.27.0: - version "7.32.0" - resolved "https://registry.yarnpkg.com/eslint/-/eslint-7.32.0.tgz#c6d328a14be3fb08c8d1d21e12c02fdb7a2a812d" - integrity sha512-VHZ8gX+EDfz+97jGcgyGCyRia/dPOd6Xh9yPv8Bl1+SoaIwD+a/vlrOmGRUyOYu7MwUhc7CxqeaDZU13S4+EpA== - dependencies: - "@babel/code-frame" "7.12.11" - "@eslint/eslintrc" "^0.4.3" - "@humanwhocodes/config-array" "^0.5.0" - ajv "^6.10.0" - chalk "^4.0.0" - cross-spawn "^7.0.2" - debug "^4.0.1" - doctrine "^3.0.0" - enquirer "^2.3.5" - escape-string-regexp "^4.0.0" - eslint-scope "^5.1.1" - eslint-utils "^2.1.0" - eslint-visitor-keys "^2.0.0" - espree "^7.3.1" - esquery "^1.4.0" - esutils "^2.0.2" - fast-deep-equal "^3.1.3" - file-entry-cache "^6.0.1" - functional-red-black-tree "^1.0.1" - glob-parent "^5.1.2" - globals "^13.6.0" - ignore "^4.0.6" - import-fresh "^3.0.0" - imurmurhash "^0.1.4" - is-glob "^4.0.0" - js-yaml "^3.13.1" - json-stable-stringify-without-jsonify "^1.0.1" - levn "^0.4.1" - lodash.merge "^4.6.2" - minimatch "^3.0.4" - natural-compare "^1.4.0" - optionator "^0.9.1" - progress "^2.0.0" - regexpp "^3.1.0" - semver "^7.2.1" - strip-ansi "^6.0.0" - strip-json-comments "^3.1.0" - table "^6.0.9" - text-table "^0.2.0" - v8-compile-cache "^2.0.3" - -espree@^3.5.4: - version "3.5.4" - resolved "https://registry.yarnpkg.com/espree/-/espree-3.5.4.tgz#b0f447187c8a8bed944b815a660bddf5deb5d1a7" - integrity sha512-yAcIQxtmMiB/jL32dzEp2enBeidsB7xWPLNiw3IIkpVds1P+h7qF9YwJq1yUNzp2OKXgAprs4F61ih66UsoD1A== - dependencies: - acorn "^5.5.0" - acorn-jsx "^3.0.0" - -espree@^7.3.0, espree@^7.3.1: - version "7.3.1" - resolved "https://registry.yarnpkg.com/espree/-/espree-7.3.1.tgz#f2df330b752c6f55019f8bd89b7660039c1bbbb6" - integrity sha512-v3JCNCE64umkFpmkFGqzVKsOT0tN1Zr+ueqLZfpV1Ob8e+CEgPWa+OxCoGH3tnhimMKIaBm4m/vaRpJ/krRz2g== - dependencies: - acorn "^7.4.0" - acorn-jsx "^5.3.1" - eslint-visitor-keys "^1.3.0" - -esprima@2.7.x, esprima@^2.7.1: - version "2.7.3" - resolved "https://registry.yarnpkg.com/esprima/-/esprima-2.7.3.tgz#96e3b70d5779f6ad49cd032673d1c312767ba581" - integrity sha1-luO3DVd59q1JzQMmc9HDEnZ7pYE= - -esprima@^4.0.0: - version "4.0.1" - resolved "https://registry.yarnpkg.com/esprima/-/esprima-4.0.1.tgz#13b04cdb3e6c5d19df91ab6987a8695619b0aa71" - integrity sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A== - -esquery@^1.0.0, esquery@^1.4.0: - version "1.4.0" - resolved "https://registry.yarnpkg.com/esquery/-/esquery-1.4.0.tgz#2148ffc38b82e8c7057dfed48425b3e61f0f24a5" - integrity sha512-cCDispWt5vHHtwMY2YrAQ4ibFkAL8RbH5YGBnZBc90MolvvfkkQcJro/aZiAQUlQ3qgrYS6D6v8Gc5G5CQsc9w== - dependencies: - estraverse "^5.1.0" - -esrecurse@^4.1.0, esrecurse@^4.3.0: - version "4.3.0" - resolved "https://registry.yarnpkg.com/esrecurse/-/esrecurse-4.3.0.tgz#7ad7964d679abb28bee72cec63758b1c5d2c9921" - integrity sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag== - dependencies: - estraverse "^5.2.0" - -estraverse@^1.9.1: - version "1.9.3" - resolved "https://registry.yarnpkg.com/estraverse/-/estraverse-1.9.3.tgz#af67f2dc922582415950926091a4005d29c9bb44" - integrity sha1-r2fy3JIlgkFZUJJgkaQAXSnJu0Q= - -estraverse@^4.1.1: - version "4.3.0" - resolved "https://registry.yarnpkg.com/estraverse/-/estraverse-4.3.0.tgz#398ad3f3c5a24948be7725e83d11a7de28cdbd1d" - integrity sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw== - -estraverse@^5.1.0, estraverse@^5.2.0: - version "5.2.0" - resolved "https://registry.yarnpkg.com/estraverse/-/estraverse-5.2.0.tgz#307df42547e6cc7324d3cf03c155d5cdb8c53880" - integrity sha512-BxbNGGNm0RyRYvUdHpIwv9IWzeM9XClbOxwoATuFdOE7ZE6wHL+HQ5T8hoPM+zHvmKzzsEqhgy0GrQ5X13afiQ== - -estree-walker@^2.0.2: - version "2.0.2" - resolved "https://registry.yarnpkg.com/estree-walker/-/estree-walker-2.0.2.tgz#52f010178c2a4c117a7757cfe942adb7d2da4cac" - integrity sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w== - -esutils@^2.0.2: - version "2.0.3" - resolved "https://registry.yarnpkg.com/esutils/-/esutils-2.0.3.tgz#74d2eb4de0b8da1293711910d50775b9b710ef64" - integrity sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g== - -etag@~1.8.1: - version "1.8.1" - resolved "https://registry.yarnpkg.com/etag/-/etag-1.8.1.tgz#41ae2eeb65efa62268aebfea83ac7d79299b0887" - integrity sha1-Qa4u62XvpiJorr/qg6x9eSmbCIc= - -event-emitter@^0.3.5, event-emitter@~0.3.5: - version "0.3.5" - resolved "https://registry.yarnpkg.com/event-emitter/-/event-emitter-0.3.5.tgz#df8c69eef1647923c7157b9ce83840610b02cc39" - integrity sha1-34xp7vFkeSPHFXuc6DhAYQsCzDk= - dependencies: - d "1" - es5-ext "~0.10.14" - -event-stream@=3.3.4: - version "3.3.4" - resolved "https://registry.yarnpkg.com/event-stream/-/event-stream-3.3.4.tgz#4ab4c9a0f5a54db9338b4c34d86bfce8f4b35571" - integrity sha1-SrTJoPWlTbkzi0w02Gv86PSzVXE= - dependencies: - duplexer "~0.1.1" - from "~0" - map-stream "~0.1.0" - pause-stream "0.0.11" - split "0.3" - stream-combiner "~0.0.4" - through "~2.3.1" - -eventemitter3@^4.0.0: - version "4.0.7" - resolved "https://registry.yarnpkg.com/eventemitter3/-/eventemitter3-4.0.7.tgz#2de9b68f6528d5644ef5c59526a1b4a07306169f" - integrity sha512-8guHBZCwKnFhYdHr2ysuRWErTwhoN2X8XELRlrRwpmfeY2jjuUN4taQMsULKUVo1K4DvZl+0pgfyoysHxvmvEw== - -events@^1.0.0: - version "1.1.1" - resolved "https://registry.yarnpkg.com/events/-/events-1.1.1.tgz#9ebdb7635ad099c70dcc4c2a1f5004288e8bd924" - integrity sha1-nr23Y1rQmccNzEwqH1AEKI6L2SQ= - -events@^3.0.0: - version "3.3.0" - resolved "https://registry.yarnpkg.com/events/-/events-3.3.0.tgz#31a95ad0a924e2d2c419a813aeb2c4e878ea7400" - integrity sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q== - -evp_bytestokey@^1.0.0, evp_bytestokey@^1.0.3: - version "1.0.3" - resolved "https://registry.yarnpkg.com/evp_bytestokey/-/evp_bytestokey-1.0.3.tgz#7fcbdb198dc71959432efe13842684e0525acb02" - integrity sha512-/f2Go4TognH/KvCISP7OUsHn85hT9nUkxxA9BEWxFn+Oj9o8ZNLm/40hdlgSLyuOimsrTKLUMEorQexp/aPQeA== - dependencies: - md5.js "^1.3.4" - safe-buffer "^5.1.1" - -execa@^0.7.0: - version "0.7.0" - resolved "https://registry.yarnpkg.com/execa/-/execa-0.7.0.tgz#944becd34cc41ee32a63a9faf27ad5a65fc59777" - integrity sha1-lEvs00zEHuMqY6n68nrVpl/Fl3c= - dependencies: - cross-spawn "^5.0.1" - get-stream "^3.0.0" - is-stream "^1.1.0" - npm-run-path "^2.0.0" - p-finally "^1.0.0" - signal-exit "^3.0.0" - strip-eof "^1.0.0" - -execa@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/execa/-/execa-1.0.0.tgz#c6236a5bb4df6d6f15e88e7f017798216749ddd8" - integrity sha512-adbxcyWV46qiHyvSp50TKt05tB4tK3HcmF7/nxfAdhnox83seTDbwnaqKO4sXRy7roHAIFqJP/Rw/AuEbX61LA== - dependencies: - cross-spawn "^6.0.0" - get-stream "^4.0.0" - is-stream "^1.1.0" - npm-run-path "^2.0.0" - p-finally "^1.0.0" - signal-exit "^3.0.0" - strip-eof "^1.0.0" - -exit-on-epipe@~1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/exit-on-epipe/-/exit-on-epipe-1.0.1.tgz#0bdd92e87d5285d267daa8171d0eb06159689692" - integrity sha512-h2z5mrROTxce56S+pnvAV890uu7ls7f1kEvVGJbw1OlFH3/mlJ5bkXu0KRyW94v37zzHPiUd55iLn3DA7TjWpw== - -expand-brackets@^0.1.4: - version "0.1.5" - resolved "https://registry.yarnpkg.com/expand-brackets/-/expand-brackets-0.1.5.tgz#df07284e342a807cd733ac5af72411e581d1177b" - integrity sha1-3wcoTjQqgHzXM6xa9yQR5YHRF3s= - dependencies: - is-posix-bracket "^0.1.0" - -expand-brackets@^2.1.4: - version "2.1.4" - resolved "https://registry.yarnpkg.com/expand-brackets/-/expand-brackets-2.1.4.tgz#b77735e315ce30f6b6eff0f83b04151a22449622" - integrity sha1-t3c14xXOMPa27/D4OwQVGiJEliI= - dependencies: - debug "^2.3.3" - define-property "^0.2.5" - extend-shallow "^2.0.1" - posix-character-classes "^0.1.0" - regex-not "^1.0.0" - snapdragon "^0.8.1" - to-regex "^3.0.1" - -expand-range@^1.8.1: - version "1.8.2" - resolved "https://registry.yarnpkg.com/expand-range/-/expand-range-1.8.2.tgz#a299effd335fe2721ebae8e257ec79644fc85337" - integrity sha1-opnv/TNf4nIeuujiV+x5ZE/IUzc= - dependencies: - fill-range "^2.1.0" - -expand-tilde@^2.0.0, expand-tilde@^2.0.2: - version "2.0.2" - resolved "https://registry.yarnpkg.com/expand-tilde/-/expand-tilde-2.0.2.tgz#97e801aa052df02454de46b02bf621642cdc8502" - integrity sha1-l+gBqgUt8CRU3kawK/YhZCzchQI= - dependencies: - homedir-polyfill "^1.0.1" - -expect-webdriverio@^3.0.0: - version "3.1.3" - resolved "https://registry.yarnpkg.com/expect-webdriverio/-/expect-webdriverio-3.1.3.tgz#d9c52a1bf3300fd283bb98c09b484b6cfa53645a" - integrity sha512-p9h8ntvTGAN/nXP6RRPcqnPX4t0z/Axaf3WyfhgADHXOAz1WoJu4NBZlPItPvycXyRmMZ6MBsoUoF2soSpsOtA== - dependencies: - expect "^27.0.2" - jest-matcher-utils "^27.0.2" - -expect@^27.0.2: - version "27.2.4" - resolved "https://registry.yarnpkg.com/expect/-/expect-27.2.4.tgz#4debf546050bcdad8914a8c95fec7662e02bf67c" - integrity sha512-gOtuonQ8TCnbNNCSw2fhVzRf8EFYDII4nB5NmG4IEV0rbUnW1I5zXvoTntU4iicB/Uh0oZr20NGlOLdJiwsOZA== - dependencies: - "@jest/types" "^27.2.4" - ansi-styles "^5.0.0" - jest-get-type "^27.0.6" - jest-matcher-utils "^27.2.4" - jest-message-util "^27.2.4" - jest-regex-util "^27.0.6" - -express@^4.15.4, express@^4.16.3: - version "4.17.1" - resolved "https://registry.yarnpkg.com/express/-/express-4.17.1.tgz#4491fc38605cf51f8629d39c2b5d026f98a4c134" - integrity sha512-mHJ9O79RqluphRrcw2X/GTh3k9tVv8YcoyY4Kkh4WDMUYKRZUq0h1o0w2rrrxBqM7VoeUVqgb27xlEMXTnYt4g== - dependencies: - accepts "~1.3.7" - array-flatten "1.1.1" - body-parser "1.19.0" - content-disposition "0.5.3" - content-type "~1.0.4" - cookie "0.4.0" - cookie-signature "1.0.6" - debug "2.6.9" - depd "~1.1.2" - encodeurl "~1.0.2" - escape-html "~1.0.3" - etag "~1.8.1" - finalhandler "~1.1.2" - fresh "0.5.2" - merge-descriptors "1.0.1" - methods "~1.1.2" - on-finished "~2.3.0" - parseurl "~1.3.3" - path-to-regexp "0.1.7" - proxy-addr "~2.0.5" - qs "6.7.0" - range-parser "~1.2.1" - safe-buffer "5.1.2" - send "0.17.1" - serve-static "1.14.1" - setprototypeof "1.1.1" - statuses "~1.5.0" - type-is "~1.6.18" - utils-merge "1.0.1" - vary "~1.1.2" - -ext@^1.1.2: - version "1.6.0" - resolved "https://registry.yarnpkg.com/ext/-/ext-1.6.0.tgz#3871d50641e874cc172e2b53f919842d19db4c52" - integrity sha512-sdBImtzkq2HpkdRLtlLWDa6w4DX22ijZLKx8BMPUuKe1c5lbN6xwQDQCxSfxBQnHZ13ls/FH0MQZx/q/gr6FQg== - dependencies: - type "^2.5.0" - -extend-shallow@^2.0.1: - version "2.0.1" - resolved "https://registry.yarnpkg.com/extend-shallow/-/extend-shallow-2.0.1.tgz#51af7d614ad9a9f610ea1bafbb989d6b1c56890f" - integrity sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8= - dependencies: - is-extendable "^0.1.0" - -extend-shallow@^3.0.0, extend-shallow@^3.0.2: - version "3.0.2" - resolved "https://registry.yarnpkg.com/extend-shallow/-/extend-shallow-3.0.2.tgz#26a71aaf073b39fb2127172746131c2704028db8" - integrity sha1-Jqcarwc7OfshJxcnRhMcJwQCjbg= - dependencies: - assign-symbols "^1.0.0" - is-extendable "^1.0.1" - -extend@^3.0.0, extend@^3.0.2, extend@~3.0.2: - version "3.0.2" - resolved "https://registry.yarnpkg.com/extend/-/extend-3.0.2.tgz#f8b1136b4071fbd8eb140aff858b1019ec2915fa" - integrity sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g== - -external-editor@^2.0.4: - version "2.2.0" - resolved "https://registry.yarnpkg.com/external-editor/-/external-editor-2.2.0.tgz#045511cfd8d133f3846673d1047c154e214ad3d5" - integrity sha512-bSn6gvGxKt+b7+6TKEv1ZycHleA7aHhRHyAqJyp5pbUFuYYNIzpZnQDk7AsYckyWdEnTeAnay0aCy2aV6iTk9A== - dependencies: - chardet "^0.4.0" - iconv-lite "^0.4.17" - tmp "^0.0.33" - -external-editor@^3.0.3: - version "3.1.0" - resolved "https://registry.yarnpkg.com/external-editor/-/external-editor-3.1.0.tgz#cb03f740befae03ea4d283caed2741a83f335495" - integrity sha512-hMQ4CX1p1izmuLYyZqLMO/qGNw10wSv9QDCPfzXfyFrOaCSSoRfqE1Kf1s5an66J5JZC62NewG+mK49jOCtQew== - dependencies: - chardet "^0.7.0" - iconv-lite "^0.4.24" - tmp "^0.0.33" - -extglob@^0.3.1: - version "0.3.2" - resolved "https://registry.yarnpkg.com/extglob/-/extglob-0.3.2.tgz#2e18ff3d2f49ab2765cec9023f011daa8d8349a1" - integrity sha1-Lhj/PS9JqydlzskCPwEdqo2DSaE= - dependencies: - is-extglob "^1.0.0" - -extglob@^2.0.4: - version "2.0.4" - resolved "https://registry.yarnpkg.com/extglob/-/extglob-2.0.4.tgz#ad00fe4dc612a9232e8718711dc5cb5ab0285543" - integrity sha512-Nmb6QXkELsuBr24CJSkilo6UHHgbekK5UiZgfE6UHD3Eb27YC6oD+bhcT+tJ6cl8dmsgdQxnWlcry8ksBIBLpw== - dependencies: - array-unique "^0.3.2" - define-property "^1.0.0" - expand-brackets "^2.1.4" - extend-shallow "^2.0.1" - fragment-cache "^0.2.1" - regex-not "^1.0.0" - snapdragon "^0.8.1" - to-regex "^3.0.1" - -extract-zip@2.0.1: - version "2.0.1" - resolved "https://registry.yarnpkg.com/extract-zip/-/extract-zip-2.0.1.tgz#663dca56fe46df890d5f131ef4a06d22bb8ba13a" - integrity sha512-GDhU9ntwuKyGXdZBUgTIe+vXnWj0fppUEtMDL0+idd5Sta8TGpHssn/eusA9mrPr9qNDym6SxAYZjNvCn/9RBg== - dependencies: - debug "^4.1.1" - get-stream "^5.1.0" - yauzl "^2.10.0" - optionalDependencies: - "@types/yauzl" "^2.9.1" - -extsprintf@1.3.0: - version "1.3.0" - resolved "https://registry.yarnpkg.com/extsprintf/-/extsprintf-1.3.0.tgz#96918440e3041a7a414f8c52e3c574eb3c3e1e05" - integrity sha1-lpGEQOMEGnpBT4xS48V06zw+HgU= - -extsprintf@^1.2.0: - version "1.4.0" - resolved "https://registry.yarnpkg.com/extsprintf/-/extsprintf-1.4.0.tgz#e2689f8f356fad62cca65a3a91c5df5f9551692f" - integrity sha1-4mifjzVvrWLMplo6kcXfX5VRaS8= - -faker@^5.5.3: - version "5.5.3" - resolved "https://registry.yarnpkg.com/faker/-/faker-5.5.3.tgz#c57974ee484431b25205c2c8dc09fda861e51e0e" - integrity sha512-wLTv2a28wjUyWkbnX7u/ABZBkUkIF2fCd73V6P2oFqEGEktDfzWx4UxrSqtPRw0xPRAcjeAOIiJWqZm3pP4u3g== - -fancy-log@^1.1.0, fancy-log@^1.3.2, fancy-log@^1.3.3: - version "1.3.3" - resolved "https://registry.yarnpkg.com/fancy-log/-/fancy-log-1.3.3.tgz#dbc19154f558690150a23953a0adbd035be45fc7" - integrity sha512-k9oEhlyc0FrVh25qYuSELjr8oxsCoc4/LEZfg2iJJrfEk/tZL9bCoJE47gqAvI2m/AUjluCS4+3I0eTx8n3AEw== - dependencies: - ansi-gray "^0.1.1" - color-support "^1.1.3" - parse-node-version "^1.0.0" - time-stamp "^1.0.0" - -fast-deep-equal@^1.0.0: - version "1.1.0" - resolved "https://registry.yarnpkg.com/fast-deep-equal/-/fast-deep-equal-1.1.0.tgz#c053477817c86b51daa853c81e059b733d023614" - integrity sha1-wFNHeBfIa1HaqFPIHgWbcz0CNhQ= - -fast-deep-equal@^2.0.1: - version "2.0.1" - resolved "https://registry.yarnpkg.com/fast-deep-equal/-/fast-deep-equal-2.0.1.tgz#7b05218ddf9667bf7f370bf7fdb2cb15fdd0aa49" - integrity sha1-ewUhjd+WZ79/Nwv3/bLLFf3Qqkk= - -fast-deep-equal@^3.1.1, fast-deep-equal@^3.1.3: - version "3.1.3" - resolved "https://registry.yarnpkg.com/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz#3a7d56b559d6cbc3eb512325244e619a65c6c525" - integrity sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q== - -fast-json-stable-stringify@^2.0.0: - version "2.1.0" - resolved "https://registry.yarnpkg.com/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz#874bf69c6f404c2b5d99c481341399fd55892633" - integrity sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw== - -fast-levenshtein@^1.0.0: - version "1.1.4" - resolved "https://registry.yarnpkg.com/fast-levenshtein/-/fast-levenshtein-1.1.4.tgz#e6a754cc8f15e58987aa9cbd27af66fd6f4e5af9" - integrity sha1-5qdUzI8V5YmHqpy9J69m/W9OWvk= - -fast-levenshtein@^2.0.6, fast-levenshtein@~2.0.6: - version "2.0.6" - resolved "https://registry.yarnpkg.com/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz#3d8a5c66883a16a30ca8643e851f19baa7797917" - integrity sha1-PYpcZog6FqMMqGQ+hR8Zuqd5eRc= - -faye-websocket@~0.10.0: - version "0.10.0" - resolved "https://registry.yarnpkg.com/faye-websocket/-/faye-websocket-0.10.0.tgz#4e492f8d04dfb6f89003507f6edbf2d501e7c6f4" - integrity sha1-TkkvjQTftviQA1B/btvy1QHnxvQ= - dependencies: - websocket-driver ">=0.5.1" - -fd-slicer@~1.1.0: - version "1.1.0" - resolved "https://registry.yarnpkg.com/fd-slicer/-/fd-slicer-1.1.0.tgz#25c7c89cb1f9077f8891bbe61d8f390eae256f1e" - integrity sha1-JcfInLH5B3+IkbvmHY85Dq4lbx4= - dependencies: - pend "~1.2.0" - -fibers@^5.0.0: - version "5.0.0" - resolved "https://registry.yarnpkg.com/fibers/-/fibers-5.0.0.tgz#3a60e0695b3ee5f6db94e62726716fa7a59acc41" - integrity sha512-UpGv/YAZp7mhKHxDvC1tColrroGRX90sSvh8RMZV9leo+e5+EkRVgCEZPlmXeo3BUNQTZxUaVdLskq1Q2FyCPg== - dependencies: - detect-libc "^1.0.3" - -figures@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/figures/-/figures-2.0.0.tgz#3ab1a2d2a62c8bfb431a0c94cb797a2fce27c962" - integrity sha1-OrGi0qYsi/tDGgyUy3l6L84nyWI= - dependencies: - escape-string-regexp "^1.0.5" - -figures@^3.0.0, figures@^3.1.0: - version "3.2.0" - resolved "https://registry.yarnpkg.com/figures/-/figures-3.2.0.tgz#625c18bd293c604dc4a8ddb2febf0c88341746af" - integrity sha512-yaduQFRKLXYOGgEn6AZau90j3ggSOyiqXU0F9JZfeXYhNa+Jk4X+s45A2zg5jns87GAFa34BBm2kXw4XpNcbdg== - dependencies: - escape-string-regexp "^1.0.5" - -file-entry-cache@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/file-entry-cache/-/file-entry-cache-2.0.0.tgz#c392990c3e684783d838b8c84a45d8a048458361" - integrity sha1-w5KZDD5oR4PYOLjISkXYoEhFg2E= - dependencies: - flat-cache "^1.2.1" - object-assign "^4.0.1" - -file-entry-cache@^6.0.1: - version "6.0.1" - resolved "https://registry.yarnpkg.com/file-entry-cache/-/file-entry-cache-6.0.1.tgz#211b2dd9659cb0394b073e7323ac3c933d522027" - integrity sha512-7Gps/XWymbLk2QLYK4NzpMOrYjMhdIxXuIvy2QBsLE6ljuodKvdkWs/cpyJJ3CVIVpH0Oi1Hvg1ovbMzLdFBBg== - dependencies: - flat-cache "^3.0.4" - -file-uri-to-path@1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/file-uri-to-path/-/file-uri-to-path-1.0.0.tgz#553a7b8446ff6f684359c445f1e37a05dacc33dd" - integrity sha512-0Zt+s3L7Vf1biwWZ29aARiVYLx7iMGnEUl9x33fbB/j3jR81u/O2LbqK+Bm1CDSNDKVtJ/YjwY7TUd5SkeLQLw== - -filelist@^1.0.1: - version "1.0.2" - resolved "https://registry.yarnpkg.com/filelist/-/filelist-1.0.2.tgz#80202f21462d4d1c2e214119b1807c1bc0380e5b" - integrity sha512-z7O0IS8Plc39rTCq6i6iHxk43duYOn8uFJiWSewIq0Bww1RNybVHSCjahmcC87ZqAm4OTvFzlzeGu3XAzG1ctQ== - dependencies: - minimatch "^3.0.4" - -filename-regex@^2.0.0: - version "2.0.1" - resolved "https://registry.yarnpkg.com/filename-regex/-/filename-regex-2.0.1.tgz#c1c4b9bee3e09725ddb106b75c1e301fe2f18b26" - integrity sha1-wcS5vuPglyXdsQa3XB4wH+LxiyY= - -filesize@^3.6.1: - version "3.6.1" - resolved "https://registry.yarnpkg.com/filesize/-/filesize-3.6.1.tgz#090bb3ee01b6f801a8a8be99d31710b3422bb317" - integrity sha512-7KjR1vv6qnicaPMi1iiTcI85CyYwRO/PSFCu6SvqL8jN2Wjt/NIYQTFtFs7fSDCYOstUkEWIQGFUg5YZQfjlcg== - -fill-range@^2.1.0: - version "2.2.4" - resolved "https://registry.yarnpkg.com/fill-range/-/fill-range-2.2.4.tgz#eb1e773abb056dcd8df2bfdf6af59b8b3a936565" - integrity sha512-cnrcCbj01+j2gTG921VZPnHbjmdAf8oQV/iGeV2kZxGSyfYjjTyY79ErsK1WJWMpw6DaApEX72binqJE+/d+5Q== - dependencies: - is-number "^2.1.0" - isobject "^2.0.0" - randomatic "^3.0.0" - repeat-element "^1.1.2" - repeat-string "^1.5.2" - -fill-range@^4.0.0: - version "4.0.0" - resolved "https://registry.yarnpkg.com/fill-range/-/fill-range-4.0.0.tgz#d544811d428f98eb06a63dc402d2403c328c38f7" - integrity sha1-1USBHUKPmOsGpj3EAtJAPDKMOPc= - dependencies: - extend-shallow "^2.0.1" - is-number "^3.0.0" - repeat-string "^1.6.1" - to-regex-range "^2.1.0" - -fill-range@^7.0.1: - version "7.0.1" - resolved "https://registry.yarnpkg.com/fill-range/-/fill-range-7.0.1.tgz#1919a6a7c75fe38b2c7c77e5198535da9acdda40" - integrity sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ== - dependencies: - to-regex-range "^5.0.1" - -filter-obj@^1.1.0: - version "1.1.0" - resolved "https://registry.yarnpkg.com/filter-obj/-/filter-obj-1.1.0.tgz#9b311112bc6c6127a16e016c6c5d7f19e0805c5b" - integrity sha1-mzERErxsYSehbgFsbF1/GeCAXFs= - -finalhandler@1.1.2, finalhandler@~1.1.2: - version "1.1.2" - resolved "https://registry.yarnpkg.com/finalhandler/-/finalhandler-1.1.2.tgz#b7e7d000ffd11938d0fdb053506f6ebabe9f587d" - integrity sha512-aAWcW57uxVNrQZqFXjITpW3sIUQmHGG3qSb9mUah9MgMC4NeWhNOlNjXEYq3HjRAvL6arUviZGGJsBg6z0zsWA== - dependencies: - debug "2.6.9" - encodeurl "~1.0.2" - escape-html "~1.0.3" - on-finished "~2.3.0" - parseurl "~1.3.3" - statuses "~1.5.0" - unpipe "~1.0.0" - -find-cache-dir@^3.3.1: - version "3.3.2" - resolved "https://registry.yarnpkg.com/find-cache-dir/-/find-cache-dir-3.3.2.tgz#b30c5b6eff0730731aea9bbd9dbecbd80256d64b" - integrity sha512-wXZV5emFEjrridIgED11OoUKLxiYjAcqot/NJdAkOhlJ+vGzwhOAfcG5OX1jP+S0PcjEn8bdMJv+g2jwQ3Onig== - dependencies: - commondir "^1.0.1" - make-dir "^3.0.2" - pkg-dir "^4.1.0" - -find-up@5.0.0, find-up@^5.0.0: - version "5.0.0" - resolved "https://registry.yarnpkg.com/find-up/-/find-up-5.0.0.tgz#4c92819ecb7083561e4f4a240a86be5198f536fc" - integrity sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng== - dependencies: - locate-path "^6.0.0" - path-exists "^4.0.0" - -find-up@^1.0.0: - version "1.1.2" - resolved "https://registry.yarnpkg.com/find-up/-/find-up-1.1.2.tgz#6b2e9822b1a2ce0a60ab64d610eccad53cb24d0f" - integrity sha1-ay6YIrGizgpgq2TWEOzK1TyyTQ8= - dependencies: - path-exists "^2.0.0" - pinkie-promise "^2.0.0" - -find-up@^2.0.0, find-up@^2.1.0: - version "2.1.0" - resolved "https://registry.yarnpkg.com/find-up/-/find-up-2.1.0.tgz#45d1b7e506c717ddd482775a2b77920a3c0c57a7" - integrity sha1-RdG35QbHF93UgndaK3eSCjwMV6c= - dependencies: - locate-path "^2.0.0" - -find-up@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/find-up/-/find-up-3.0.0.tgz#49169f1d7993430646da61ecc5ae355c21c97b73" - integrity sha512-1yD6RmLI1XBfxugvORwlck6f75tYL+iR0jqwsOrOxMZyGYqUuDhJ0l4AXdO1iX/FTs9cBAMEk1gWSEx1kSbylg== - dependencies: - locate-path "^3.0.0" - -find-up@^4.0.0, find-up@^4.1.0: - version "4.1.0" - resolved "https://registry.yarnpkg.com/find-up/-/find-up-4.1.0.tgz#97afe7d6cdc0bc5928584b7c8d7b16e8a9aa5d19" - integrity sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw== - dependencies: - locate-path "^5.0.0" - path-exists "^4.0.0" - -findup-sync@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/findup-sync/-/findup-sync-2.0.0.tgz#9326b1488c22d1a6088650a86901b2d9a90a2cbc" - integrity sha1-kyaxSIwi0aYIhlCoaQGy2akKLLw= - dependencies: - detect-file "^1.0.0" - is-glob "^3.1.0" - micromatch "^3.0.4" - resolve-dir "^1.0.1" - -findup-sync@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/findup-sync/-/findup-sync-3.0.0.tgz#17b108f9ee512dfb7a5c7f3c8b27ea9e1a9c08d1" - integrity sha512-YbffarhcicEhOrm4CtrwdKBdCuz576RLdhJDsIfvNtxUuhdRet1qZcsMjqbePtAseKdAnDyM/IyXbu7PRPRLYg== - dependencies: - detect-file "^1.0.0" - is-glob "^4.0.0" - micromatch "^3.0.4" - resolve-dir "^1.0.1" - -fined@^1.0.1: - version "1.2.0" - resolved "https://registry.yarnpkg.com/fined/-/fined-1.2.0.tgz#d00beccf1aa2b475d16d423b0238b713a2c4a37b" - integrity sha512-ZYDqPLGxDkDhDZBjZBb+oD1+j0rA4E0pXY50eplAAOPg2N/gUBSSk5IM1/QhPfyVo19lJ+CvXpqfvk+b2p/8Ng== - dependencies: - expand-tilde "^2.0.2" - is-plain-object "^2.0.3" - object.defaults "^1.1.0" - object.pick "^1.2.0" - parse-filepath "^1.0.1" - -flagged-respawn@^1.0.0: - version "1.0.1" - resolved "https://registry.yarnpkg.com/flagged-respawn/-/flagged-respawn-1.0.1.tgz#e7de6f1279ddd9ca9aac8a5971d618606b3aab41" - integrity sha512-lNaHNVymajmk0OJMBn8fVUAU1BtDeKIqKoVhk4xAALB57aALg6b4W0MfJ/cUE0g9YBXy5XhSlPIpYIJ7HaY/3Q== - -flat-cache@^1.2.1: - version "1.3.4" - resolved "https://registry.yarnpkg.com/flat-cache/-/flat-cache-1.3.4.tgz#2c2ef77525cc2929007dfffa1dd314aa9c9dee6f" - integrity sha512-VwyB3Lkgacfik2vhqR4uv2rvebqmDvFu4jlN/C1RzWoJEo8I7z4Q404oiqYCkq41mni8EzQnm95emU9seckwtg== - dependencies: - circular-json "^0.3.1" - graceful-fs "^4.1.2" - rimraf "~2.6.2" - write "^0.2.1" - -flat-cache@^3.0.4: - version "3.0.4" - resolved "https://registry.yarnpkg.com/flat-cache/-/flat-cache-3.0.4.tgz#61b0338302b2fe9f957dcc32fc2a87f1c3048b11" - integrity sha512-dm9s5Pw7Jc0GvMYbshN6zchCA9RgQlzzEZX3vylR9IqFfS8XciblUXOKfW6SiuJ0e13eDYZoZV5wdrev7P3Nwg== - dependencies: - flatted "^3.1.0" - rimraf "^3.0.2" - -flat@^5.0.2: - version "5.0.2" - resolved "https://registry.yarnpkg.com/flat/-/flat-5.0.2.tgz#8ca6fe332069ffa9d324c327198c598259ceb241" - integrity sha512-b6suED+5/3rTpUBdG1gupIl8MPFCAMA0QXwmljLhvCUKcUvdE4gWky9zpuGCcXHOsz4J9wPGNWq6OKpmIzz3hQ== - -flatted@^2.0.1: - version "2.0.2" - resolved "https://registry.yarnpkg.com/flatted/-/flatted-2.0.2.tgz#4575b21e2bcee7434aa9be662f4b7b5f9c2b5138" - integrity sha512-r5wGx7YeOwNWNlCA0wQ86zKyDLMQr+/RB8xy74M4hTphfmjlijTSSXGuH8rnvKZnfT9i+75zmd8jcKdMR4O6jA== - -flatted@^3.1.0: - version "3.2.2" - resolved "https://registry.yarnpkg.com/flatted/-/flatted-3.2.2.tgz#64bfed5cb68fe3ca78b3eb214ad97b63bedce561" - integrity sha512-JaTY/wtrcSyvXJl4IMFHPKyFur1sE9AUqc0QnhOaJ0CxHtAoIV8pYDzeEfAaNEtGkOfq4gr3LBFmdXW5mOQFnA== - -flush-write-stream@^1.0.2: - version "1.1.1" - resolved "https://registry.yarnpkg.com/flush-write-stream/-/flush-write-stream-1.1.1.tgz#8dd7d873a1babc207d94ead0c2e0e44276ebf2e8" - integrity sha512-3Z4XhFZ3992uIq0XOqb9AreonueSYphE6oYbpt5+3u06JWklbsPkNv3ZKkP9Bz/r+1MWCaMoSQ28P85+1Yc77w== - dependencies: - inherits "^2.0.3" - readable-stream "^2.3.6" - -follow-redirects@^1.0.0: - version "1.14.4" - resolved "https://registry.yarnpkg.com/follow-redirects/-/follow-redirects-1.14.4.tgz#838fdf48a8bbdd79e52ee51fb1c94e3ed98b9379" - integrity sha512-zwGkiSXC1MUJG/qmeIFH2HBJx9u0V46QGUe3YR1fXG8bXQxq7fLj0RjLZQ5nubr9qNJUZrH+xUcwXEoXNpfS+g== - -for-in@^1.0.1, for-in@^1.0.2: - version "1.0.2" - resolved "https://registry.yarnpkg.com/for-in/-/for-in-1.0.2.tgz#81068d295a8142ec0ac726c6e2200c30fb6d5e80" - integrity sha1-gQaNKVqBQuwKxybG4iAMMPttXoA= - -for-own@^0.1.4: - version "0.1.5" - resolved "https://registry.yarnpkg.com/for-own/-/for-own-0.1.5.tgz#5265c681a4f294dabbf17c9509b6763aa84510ce" - integrity sha1-UmXGgaTylNq78XyVCbZ2OqhFEM4= - dependencies: - for-in "^1.0.1" - -for-own@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/for-own/-/for-own-1.0.0.tgz#c63332f415cedc4b04dbfe70cf836494c53cb44b" - integrity sha1-xjMy9BXO3EsE2/5wz4NklMU8tEs= - dependencies: - for-in "^1.0.1" - -foreach@^2.0.5: - version "2.0.5" - resolved "https://registry.yarnpkg.com/foreach/-/foreach-2.0.5.tgz#0bee005018aeb260d0a3af3ae658dd0136ec1b99" - integrity sha1-C+4AUBiusmDQo6865ljdATbsG5k= - -foreachasync@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/foreachasync/-/foreachasync-3.0.0.tgz#5502987dc8714be3392097f32e0071c9dee07cf6" - integrity sha1-VQKYfchxS+M5IJfzLgBxyd7gfPY= - -forever-agent@~0.6.1: - version "0.6.1" - resolved "https://registry.yarnpkg.com/forever-agent/-/forever-agent-0.6.1.tgz#fbc71f0c41adeb37f96c577ad1ed42d8fdacca91" - integrity sha1-+8cfDEGt6zf5bFd60e1C2P2sypE= - -fork-stream@^0.0.4: - version "0.0.4" - resolved "https://registry.yarnpkg.com/fork-stream/-/fork-stream-0.0.4.tgz#db849fce77f6708a5f8f386ae533a0907b54ae70" - integrity sha1-24Sfznf2cIpfjzhq5TOgkHtUrnA= - -form-data@~2.3.2: - version "2.3.3" - resolved "https://registry.yarnpkg.com/form-data/-/form-data-2.3.3.tgz#dcce52c05f644f298c6a7ab936bd724ceffbf3a6" - integrity sha512-1lLKB2Mu3aGP1Q/2eCOx0fNbRMe7XdwktwOruhfqqd0rIJWwN4Dh+E3hrPSlDCXnSR7UtZ1N38rVXm+6+MEhJQ== - dependencies: - asynckit "^0.4.0" - combined-stream "^1.0.6" - mime-types "^2.1.12" - -forwarded@0.2.0: - version "0.2.0" - resolved "https://registry.yarnpkg.com/forwarded/-/forwarded-0.2.0.tgz#2269936428aad4c15c7ebe9779a84bf0b2a81811" - integrity sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow== - -fragment-cache@^0.2.1: - version "0.2.1" - resolved "https://registry.yarnpkg.com/fragment-cache/-/fragment-cache-0.2.1.tgz#4290fad27f13e89be7f33799c6bc5a0abfff0d19" - integrity sha1-QpD60n8T6Jvn8zeZxrxaCr//DRk= - dependencies: - map-cache "^0.2.2" - -fresh@0.5.2: - version "0.5.2" - resolved "https://registry.yarnpkg.com/fresh/-/fresh-0.5.2.tgz#3d8cadd90d976569fa835ab1f8e4b23a105605a7" - integrity sha1-PYyt2Q2XZWn6g1qx+OSyOhBWBac= - -from@~0: - version "0.1.7" - resolved "https://registry.yarnpkg.com/from/-/from-0.1.7.tgz#83c60afc58b9c56997007ed1a768b3ab303a44fe" - integrity sha1-g8YK/Fi5xWmXAH7Rp2izqzA6RP4= - -fs-access@^1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/fs-access/-/fs-access-1.0.1.tgz#d6a87f262271cefebec30c553407fb995da8777a" - integrity sha1-1qh/JiJxzv6+wwxVNAf7mV2od3o= - dependencies: - null-check "^1.0.0" - -fs-constants@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/fs-constants/-/fs-constants-1.0.0.tgz#6be0de9be998ce16af8afc24497b9ee9b7ccd9ad" - integrity sha512-y6OAwoSIf7FyjMIv94u+b5rdheZEjzR63GTyZJm5qh4Bi+2YgwLCcI/fPFZkL5PSixOt6ZNKm+w+Hfp/Bciwow== - -fs-extra@^10.0.0: - version "10.0.0" - resolved "https://registry.yarnpkg.com/fs-extra/-/fs-extra-10.0.0.tgz#9ff61b655dde53fb34a82df84bb214ce802e17c1" - integrity sha512-C5owb14u9eJwizKGdchcDUQeFtlSHHthBk8pbX9Vc1PFZrLombudjDnNns88aYslCyF6IY5SUw3Roz6xShcEIQ== - dependencies: - graceful-fs "^4.2.0" - jsonfile "^6.0.1" - universalify "^2.0.0" - -fs-extra@^8.1.0: - version "8.1.0" - resolved "https://registry.yarnpkg.com/fs-extra/-/fs-extra-8.1.0.tgz#49d43c45a88cd9677668cb7be1b46efdb8d2e1c0" - integrity sha512-yhlQgA6mnOJUKOsRUFsgJdQCvkKhcz8tlZG5HBQfReYZy46OwLcY+Zia0mtdHsOo9y/hP+CxMN0TU9QxoOtG4g== - dependencies: - graceful-fs "^4.2.0" - jsonfile "^4.0.0" - universalify "^0.1.0" - -fs-extra@~0.6.1: - version "0.6.4" - resolved "https://registry.yarnpkg.com/fs-extra/-/fs-extra-0.6.4.tgz#f46f0c75b7841f8d200b3348cd4d691d5a099d15" - integrity sha1-9G8MdbeEH40gCzNIzU1pHVoJnRU= - dependencies: - jsonfile "~1.0.1" - mkdirp "0.3.x" - ncp "~0.4.2" - rimraf "~2.2.0" - -fs-mkdirp-stream@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/fs-mkdirp-stream/-/fs-mkdirp-stream-1.0.0.tgz#0b7815fc3201c6a69e14db98ce098c16935259eb" - integrity sha1-C3gV/DIBxqaeFNuYzgmMFpNSWes= - dependencies: - graceful-fs "^4.1.11" - through2 "^2.0.3" - -fs.extra@^1.3.2: - version "1.3.2" - resolved "https://registry.yarnpkg.com/fs.extra/-/fs.extra-1.3.2.tgz#dd023f93013bee24531f1b33514c37b20fd93349" - integrity sha1-3QI/kwE77iRTHxszUUw3sg/ZM0k= - dependencies: - fs-extra "~0.6.1" - mkdirp "~0.3.5" - walk "^2.3.9" - -fs.realpath@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/fs.realpath/-/fs.realpath-1.0.0.tgz#1504ad2523158caa40db4a2787cb01411994ea4f" - integrity sha1-FQStJSMVjKpA20onh8sBQRmU6k8= - -fsevents@^1.0.0, fsevents@^1.2.7: - version "1.2.13" - resolved "https://registry.yarnpkg.com/fsevents/-/fsevents-1.2.13.tgz#f325cb0455592428bcf11b383370ef70e3bfcc38" - integrity sha512-oWb1Z6mkHIskLzEJ/XWX0srkpkTQ7vaopMQkyaEIoq0fmtFVxOthb8cCxeT+p3ynTdkk/RZwbgG4brR5BeWECw== - dependencies: - bindings "^1.5.0" - nan "^2.12.1" - -fsevents@~2.3.2: - version "2.3.2" - resolved "https://registry.yarnpkg.com/fsevents/-/fsevents-2.3.2.tgz#8a526f78b8fdf4623b709e0b975c52c24c02fd1a" - integrity sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA== - -fstream@^1.0.12: - version "1.0.12" - resolved "https://registry.yarnpkg.com/fstream/-/fstream-1.0.12.tgz#4e8ba8ee2d48be4f7d0de505455548eae5932045" - integrity sha512-WvJ193OHa0GHPEL+AycEJgxvBEwyfRkN1vhjca23OaPVMCaLCXTd5qAu82AjTcgP1UJmytkOKb63Ypde7raDIg== - dependencies: - graceful-fs "^4.1.2" - inherits "~2.0.0" - mkdirp ">=0.5 0" - rimraf "2" - -fun-hooks@^0.9.9: - version "0.9.10" - resolved "https://registry.yarnpkg.com/fun-hooks/-/fun-hooks-0.9.10.tgz#3f123f990899f1933ca24492b97bd4989b18ee76" - integrity sha512-7xBjdT+oMYOPWgwFxNiNzF4ubeUvim4zs1DnQqSSGyxu8UD7AW/6Z0iFsVRwuVSIZKUks2en2VHHotmNfj3ipw== - dependencies: - typescript-tuple "^2.2.1" - -function-bind@^1.1.1: - version "1.1.1" - resolved "https://registry.yarnpkg.com/function-bind/-/function-bind-1.1.1.tgz#a56899d3ea3c9bab874bb9773b7c5ede92f4895d" - integrity sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A== - -functional-red-black-tree@^1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/functional-red-black-tree/-/functional-red-black-tree-1.0.1.tgz#1b0ab3bd553b2a0d6399d29c0e3ea0b252078327" - integrity sha1-GwqzvVU7Kg1jmdKcDj6gslIHgyc= - -gaze@^1.1.2: - version "1.1.3" - resolved "https://registry.yarnpkg.com/gaze/-/gaze-1.1.3.tgz#c441733e13b927ac8c0ff0b4c3b033f28812924a" - integrity sha512-BRdNm8hbWzFzWHERTrejLqwHDfS4GibPoq5wjTPIoJHoBtKGPg3xAFfxmM+9ztbXelxcf2hwQcaz1PtmFeue8g== - dependencies: - globule "^1.0.0" - -gensync@^1.0.0-beta.1, gensync@^1.0.0-beta.2: - version "1.0.0-beta.2" - resolved "https://registry.yarnpkg.com/gensync/-/gensync-1.0.0-beta.2.tgz#32a6ee76c3d7f52d46b2b1ae5d93fea8580a25e0" - integrity sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg== - -get-caller-file@^1.0.1: - version "1.0.3" - resolved "https://registry.yarnpkg.com/get-caller-file/-/get-caller-file-1.0.3.tgz#f978fa4c90d1dfe7ff2d6beda2a515e713bdcf4a" - integrity sha512-3t6rVToeoZfYSGd8YoLFR2DJkiQrIiUrGcjvFX2mDw3bn6k2OtwHN0TNCLbBO+w8qTvimhDkv+LSscbJY1vE6w== - -get-caller-file@^2.0.1, get-caller-file@^2.0.5: - version "2.0.5" - resolved "https://registry.yarnpkg.com/get-caller-file/-/get-caller-file-2.0.5.tgz#4f94412a82db32f36e3b0b9741f8a97feb031f7e" - integrity sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg== - -get-func-name@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/get-func-name/-/get-func-name-2.0.0.tgz#ead774abee72e20409433a066366023dd6887a41" - integrity sha1-6td0q+5y4gQJQzoGY2YCPdaIekE= - -get-intrinsic@^1.0.1, get-intrinsic@^1.0.2, get-intrinsic@^1.1.0, get-intrinsic@^1.1.1: - version "1.1.1" - resolved "https://registry.yarnpkg.com/get-intrinsic/-/get-intrinsic-1.1.1.tgz#15f59f376f855c446963948f0d24cd3637b4abc6" - integrity sha512-kWZrnVM42QCiEA2Ig1bG8zjoIMOgxWwYCEeNdwY6Tv/cOSeGpcoX4pXHfKUxNKVoArnrEr2e9srnAxxGIraS9Q== - dependencies: - function-bind "^1.1.1" - has "^1.0.3" - has-symbols "^1.0.1" - -get-pkg-repo@^4.0.0: - version "4.2.1" - resolved "https://registry.yarnpkg.com/get-pkg-repo/-/get-pkg-repo-4.2.1.tgz#75973e1c8050c73f48190c52047c4cee3acbf385" - integrity sha512-2+QbHjFRfGB74v/pYWjd5OhU3TDIC2Gv/YKUTk/tCvAz0pkn/Mz6P3uByuBimLOcPvN2jYdScl3xGFSrx0jEcA== - dependencies: - "@hutson/parse-repository-url" "^3.0.0" - hosted-git-info "^4.0.0" - through2 "^2.0.0" - yargs "^16.2.0" - -get-port@^5.0.0, get-port@^5.1.1: - version "5.1.1" - resolved "https://registry.yarnpkg.com/get-port/-/get-port-5.1.1.tgz#0469ed07563479de6efb986baf053dcd7d4e3193" - integrity sha512-g/Q1aTSDOxFpchXC4i8ZWvxA1lnPqx/JHqcpIw0/LX9T8x/GBbi6YnlN5nhaKIFkT8oFsscUKgDJYxfwfS6QsQ== - -get-stdin@^4.0.1: - version "4.0.1" - resolved "https://registry.yarnpkg.com/get-stdin/-/get-stdin-4.0.1.tgz#b968c6b0a04384324902e8bf1a5df32579a450fe" - integrity sha1-uWjGsKBDhDJJAui/Gl3zJXmkUP4= - -get-stream@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/get-stream/-/get-stream-3.0.0.tgz#8e943d1358dc37555054ecbe2edb05aa174ede14" - integrity sha1-jpQ9E1jcN1VQVOy+LtsFqhdO3hQ= - -get-stream@^4.0.0: - version "4.1.0" - resolved "https://registry.yarnpkg.com/get-stream/-/get-stream-4.1.0.tgz#c1b255575f3dc21d59bfc79cd3d2b46b1c3a54b5" - integrity sha512-GMat4EJ5161kIy2HevLlr4luNjBgvmj413KaQA7jt4V8B4RDsfpHk7WQ9GVqfYyyx8OS/L66Kox+rJRNklLK7w== - dependencies: - pump "^3.0.0" - -get-stream@^5.1.0: - version "5.2.0" - resolved "https://registry.yarnpkg.com/get-stream/-/get-stream-5.2.0.tgz#4966a1795ee5ace65e706c4b7beb71257d6e22d3" - integrity sha512-nBF+F1rAZVCu/p7rjzgA+Yb4lfYXrpl7a6VmJrU8wF9I1CKvP/QwPNZHnOlwbTkY6dvtFIzFMSyQXbLoTQPRpA== - dependencies: - pump "^3.0.0" - -get-symbol-description@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/get-symbol-description/-/get-symbol-description-1.0.0.tgz#7fdb81c900101fbd564dd5f1a30af5aadc1e58d6" - integrity sha512-2EmdH1YvIQiZpltCNgkuiUnyukzxM/R6NDJX31Ke3BG1Nq5b0S2PhX59UKi9vZpPDQVdqn+1IcaAwnzTT5vCjw== - dependencies: - call-bind "^1.0.2" - get-intrinsic "^1.1.1" - -get-value@^2.0.3, get-value@^2.0.6: - version "2.0.6" - resolved "https://registry.yarnpkg.com/get-value/-/get-value-2.0.6.tgz#dc15ca1c672387ca76bd37ac0a395ba2042a2c28" - integrity sha1-3BXKHGcjh8p2vTesCjlbogQqLCg= - -getpass@^0.1.1: - version "0.1.7" - resolved "https://registry.yarnpkg.com/getpass/-/getpass-0.1.7.tgz#5eff8e3e684d569ae4cb2b1282604e8ba62149fa" - integrity sha1-Xv+OPmhNVprkyysSgmBOi6YhSfo= - dependencies: - assert-plus "^1.0.0" - -git-raw-commits@^2.0.8: - version "2.0.10" - resolved "https://registry.yarnpkg.com/git-raw-commits/-/git-raw-commits-2.0.10.tgz#e2255ed9563b1c9c3ea6bd05806410290297bbc1" - integrity sha512-sHhX5lsbG9SOO6yXdlwgEMQ/ljIn7qMpAbJZCGfXX2fq5T8M5SrDnpYk9/4HswTildcIqatsWa91vty6VhWSaQ== - dependencies: - dargs "^7.0.0" - lodash "^4.17.15" - meow "^8.0.0" - split2 "^3.0.0" - through2 "^4.0.0" - -git-remote-origin-url@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/git-remote-origin-url/-/git-remote-origin-url-2.0.0.tgz#5282659dae2107145a11126112ad3216ec5fa65f" - integrity sha1-UoJlna4hBxRaERJhEq0yFuxfpl8= - dependencies: - gitconfiglocal "^1.0.0" - pify "^2.3.0" - -git-semver-tags@^4.0.0, git-semver-tags@^4.1.1: - version "4.1.1" - resolved "https://registry.yarnpkg.com/git-semver-tags/-/git-semver-tags-4.1.1.tgz#63191bcd809b0ec3e151ba4751c16c444e5b5780" - integrity sha512-OWyMt5zBe7xFs8vglMmhM9lRQzCWL3WjHtxNNfJTMngGym7pC1kh8sP6jevfydJ6LP3ZvGxfb6ABYgPUM0mtsA== - dependencies: - meow "^8.0.0" - semver "^6.0.0" - -git-up@^4.0.0: - version "4.0.5" - resolved "https://registry.yarnpkg.com/git-up/-/git-up-4.0.5.tgz#e7bb70981a37ea2fb8fe049669800a1f9a01d759" - integrity sha512-YUvVDg/vX3d0syBsk/CKUTib0srcQME0JyHkL5BaYdwLsiCslPWmDSi8PUMo9pXYjrryMcmsCoCgsTpSCJEQaA== - dependencies: - is-ssh "^1.3.0" - parse-url "^6.0.0" - -git-url-parse@^11.1.2: - version "11.6.0" - resolved "https://registry.yarnpkg.com/git-url-parse/-/git-url-parse-11.6.0.tgz#c634b8de7faa66498a2b88932df31702c67df605" - integrity sha512-WWUxvJs5HsyHL6L08wOusa/IXYtMuCAhrMmnTjQPpBU0TTHyDhnOATNH3xNQz7YOQUsqIIPTGr4xiVti1Hsk5g== - dependencies: - git-up "^4.0.0" - -gitconfiglocal@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/gitconfiglocal/-/gitconfiglocal-1.0.0.tgz#41d045f3851a5ea88f03f24ca1c6178114464b9b" - integrity sha1-QdBF84UaXqiPA/JMocYXgRRGS5s= - dependencies: - ini "^1.3.2" - -github-slugger@1.2.0: - version "1.2.0" - resolved "https://registry.yarnpkg.com/github-slugger/-/github-slugger-1.2.0.tgz#8ada3286fd046d8951c3c952a8d7854cfd90fd9a" - integrity sha512-wIaa75k1vZhyPm9yWrD08A5Xnx/V+RmzGrpjQuLemGKSb77Qukiaei58Bogrl/LZSADDfPzKJX8jhLs4CRTl7Q== - dependencies: - emoji-regex ">=6.0.0 <=6.1.1" - -github-slugger@^1.2.1: - version "1.4.0" - resolved "https://registry.yarnpkg.com/github-slugger/-/github-slugger-1.4.0.tgz#206eb96cdb22ee56fdc53a28d5a302338463444e" - integrity sha512-w0dzqw/nt51xMVmlaV1+JRzN+oCa1KfcgGEWhxUG16wbdA+Xnt/yoFO8Z8x/V82ZcZ0wy6ln9QDup5avbhiDhQ== - -glob-base@^0.3.0: - version "0.3.0" - resolved "https://registry.yarnpkg.com/glob-base/-/glob-base-0.3.0.tgz#dbb164f6221b1c0b1ccf82aea328b497df0ea3c4" - integrity sha1-27Fk9iIbHAscz4Kuoyi0l98Oo8Q= - dependencies: - glob-parent "^2.0.0" - is-glob "^2.0.0" - -glob-parent@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/glob-parent/-/glob-parent-2.0.0.tgz#81383d72db054fcccf5336daa902f182f6edbb28" - integrity sha1-gTg9ctsFT8zPUzbaqQLxgvbtuyg= - dependencies: - is-glob "^2.0.0" - -glob-parent@^3.1.0: - version "3.1.0" - resolved "https://registry.yarnpkg.com/glob-parent/-/glob-parent-3.1.0.tgz#9e6af6299d8d3bd2bd40430832bd113df906c5ae" - integrity sha1-nmr2KZ2NO9K9QEMIMr0RPfkGxa4= - dependencies: - is-glob "^3.1.0" - path-dirname "^1.0.0" - -glob-parent@^5.1.2, glob-parent@~5.1.2: - version "5.1.2" - resolved "https://registry.yarnpkg.com/glob-parent/-/glob-parent-5.1.2.tgz#869832c58034fe68a4093c17dc15e8340d8401c4" - integrity sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow== - dependencies: - is-glob "^4.0.1" - -glob-stream@^6.1.0: - version "6.1.0" - resolved "https://registry.yarnpkg.com/glob-stream/-/glob-stream-6.1.0.tgz#7045c99413b3eb94888d83ab46d0b404cc7bdde4" - integrity sha1-cEXJlBOz65SIjYOrRtC0BMx73eQ= - dependencies: - extend "^3.0.0" - glob "^7.1.1" - glob-parent "^3.1.0" - is-negated-glob "^1.0.0" - ordered-read-streams "^1.0.0" - pumpify "^1.3.5" - readable-stream "^2.1.5" - remove-trailing-separator "^1.0.1" - to-absolute-glob "^2.0.0" - unique-stream "^2.0.2" - -glob-watcher@^5.0.3: - version "5.0.5" - resolved "https://registry.yarnpkg.com/glob-watcher/-/glob-watcher-5.0.5.tgz#aa6bce648332924d9a8489be41e3e5c52d4186dc" - integrity sha512-zOZgGGEHPklZNjZQaZ9f41i7F2YwE+tS5ZHrDhbBCk3stwahn5vQxnFmBJZHoYdusR6R1bLSXeGUy/BhctwKzw== - dependencies: - anymatch "^2.0.0" - async-done "^1.2.0" - chokidar "^2.0.0" - is-negated-glob "^1.0.0" - just-debounce "^1.0.0" - normalize-path "^3.0.0" - object.defaults "^1.1.0" - -glob@7.1.2: - version "7.1.2" - resolved "https://registry.yarnpkg.com/glob/-/glob-7.1.2.tgz#c19c9df9a028702d678612384a6552404c636d15" - integrity sha512-MJTUg1kjuLeQCJ+ccE4Vpa6kKVXkPYJ2mOCQyUuKLcLQsdrMCpBPUi8qVE6+YuaJkozeA9NusTAw3hLr8Xe5EQ== - dependencies: - fs.realpath "^1.0.0" - inflight "^1.0.4" - inherits "2" - minimatch "^3.0.4" - once "^1.3.0" - path-is-absolute "^1.0.0" - -glob@7.1.7, glob@~7.1.1: - version "7.1.7" - resolved "https://registry.yarnpkg.com/glob/-/glob-7.1.7.tgz#3b193e9233f01d42d0b3f78294bbeeb418f94a90" - integrity sha512-OvD9ENzPLbegENnYP5UUfJIirTg4+XwMWGaQfQTY0JenxNvvIKP3U3/tAQSPIu/lHxXYSZmpXlUHeqAIdKzBLQ== - dependencies: - fs.realpath "^1.0.0" - inflight "^1.0.4" - inherits "2" - minimatch "^3.0.4" - once "^1.3.0" - path-is-absolute "^1.0.0" - -glob@^5.0.15: - version "5.0.15" - resolved "https://registry.yarnpkg.com/glob/-/glob-5.0.15.tgz#1bc936b9e02f4a603fcc222ecf7633d30b8b93b1" - integrity sha1-G8k2ueAvSmA/zCIuz3Yz0wuLk7E= - dependencies: - inflight "^1.0.4" - inherits "2" - minimatch "2 || 3" - once "^1.3.0" - path-is-absolute "^1.0.0" - -glob@^7.0.5, glob@^7.1.1, glob@^7.1.2, glob@^7.1.3, glob@^7.1.4, glob@^7.1.7: - version "7.2.0" - resolved "https://registry.yarnpkg.com/glob/-/glob-7.2.0.tgz#d15535af7732e02e948f4c41628bd910293f6023" - integrity sha512-lmLf6gtyrPq8tTjSmrO94wBeQbFR3HbLHbuyD69wuyQkImp2hWqMGB47OX65FBkPffO641IP9jWa1z4ivqG26Q== - dependencies: - fs.realpath "^1.0.0" - inflight "^1.0.4" - inherits "2" - minimatch "^3.0.4" - once "^1.3.0" - path-is-absolute "^1.0.0" - -global-modules@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/global-modules/-/global-modules-1.0.0.tgz#6d770f0eb523ac78164d72b5e71a8877265cc3ea" - integrity sha512-sKzpEkf11GpOFuw0Zzjzmt4B4UZwjOcG757PPvrfhxcLFbq0wpsgpOqxpxtxFiCG4DtG93M6XRVbF2oGdev7bg== - dependencies: - global-prefix "^1.0.1" - is-windows "^1.0.1" - resolve-dir "^1.0.0" - -global-prefix@^1.0.1: - version "1.0.2" - resolved "https://registry.yarnpkg.com/global-prefix/-/global-prefix-1.0.2.tgz#dbf743c6c14992593c655568cb66ed32c0122ebe" - integrity sha1-2/dDxsFJklk8ZVVoy2btMsASLr4= - dependencies: - expand-tilde "^2.0.2" - homedir-polyfill "^1.0.1" - ini "^1.3.4" - is-windows "^1.0.1" - which "^1.2.14" - -globals-docs@^2.4.0: - version "2.4.1" - resolved "https://registry.yarnpkg.com/globals-docs/-/globals-docs-2.4.1.tgz#d16887709f4a15eb22d97e96343591f87a2ee3db" - integrity sha512-qpPnUKkWnz8NESjrCvnlGklsgiQzlq+rcCxoG5uNQ+dNA7cFMCmn231slLAwS2N/PlkzZ3COL8CcS10jXmLHqg== - -globals@^11.0.1, globals@^11.1.0: - version "11.12.0" - resolved "https://registry.yarnpkg.com/globals/-/globals-11.12.0.tgz#ab8795338868a0babd8525758018c2a7eb95c42e" - integrity sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA== - -globals@^13.6.0, globals@^13.9.0: - version "13.11.0" - resolved "https://registry.yarnpkg.com/globals/-/globals-13.11.0.tgz#40ef678da117fe7bd2e28f1fab24951bd0255be7" - integrity sha512-08/xrJ7wQjK9kkkRoI3OFUBbLx4f+6x3SGwcPvQ0QH6goFDrOU2oyAWrmh3dJezu65buo+HBMzAMQy6rovVC3g== - dependencies: - type-fest "^0.20.2" - -globule@^1.0.0: - version "1.3.3" - resolved "https://registry.yarnpkg.com/globule/-/globule-1.3.3.tgz#811919eeac1ab7344e905f2e3be80a13447973c2" - integrity sha512-mb1aYtDbIjTu4ShMB85m3UzjX9BVKe9WCzsnfMSZk+K5GpIbBOexgg4PPCt5eHDEG5/ZQAUX2Kct02zfiPLsKg== - dependencies: - glob "~7.1.1" - lodash "~4.17.10" - minimatch "~3.0.2" - -glogg@^1.0.0: - version "1.0.2" - resolved "https://registry.yarnpkg.com/glogg/-/glogg-1.0.2.tgz#2d7dd702beda22eb3bffadf880696da6d846313f" - integrity sha512-5mwUoSuBk44Y4EshyiqcH95ZntbDdTQqA3QYSrxmzj28Ai0vXBGMH1ApSANH14j2sIRtqCEyg6PfsuP7ElOEDA== - dependencies: - sparkles "^1.0.0" - -got@^11.0.2, got@^11.8.1: - version "11.8.2" - resolved "https://registry.yarnpkg.com/got/-/got-11.8.2.tgz#7abb3959ea28c31f3576f1576c1effce23f33599" - integrity sha512-D0QywKgIe30ODs+fm8wMZiAcZjypcCodPNuMz5H9Mny7RJ+IjJ10BdmGW7OM7fHXP+O7r6ZwapQ/YQmMSvB0UQ== - dependencies: - "@sindresorhus/is" "^4.0.0" - "@szmarczak/http-timer" "^4.0.5" - "@types/cacheable-request" "^6.0.1" - "@types/responselike" "^1.0.0" - cacheable-lookup "^5.0.3" - cacheable-request "^7.0.1" - decompress-response "^6.0.0" - http2-wrapper "^1.0.0-beta.5.2" - lowercase-keys "^2.0.0" - p-cancelable "^2.0.0" - responselike "^2.0.0" - -graceful-fs@^4.0.0, graceful-fs@^4.1.11, graceful-fs@^4.1.2, graceful-fs@^4.1.6, graceful-fs@^4.2.0, graceful-fs@^4.2.4, graceful-fs@^4.2.6: - version "4.2.8" - resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.2.8.tgz#e412b8d33f5e006593cbd3cee6df9f2cebbe802a" - integrity sha512-qkIilPUYcNhJpd33n0GBXTB1MMPp14TxEsEs0pTrsSVucApsYzW5V+Q8Qxhik6KU3evy+qkAAowTByymK0avdg== - -grapheme-splitter@^1.0.2: - version "1.0.4" - resolved "https://registry.yarnpkg.com/grapheme-splitter/-/grapheme-splitter-1.0.4.tgz#9cf3a665c6247479896834af35cf1dbb4400767e" - integrity sha512-bzh50DW9kTPM00T8y4o8vQg89Di9oLJVLW/KaOGIXJWP/iqCN6WKYkbNOF04vFLJhwcpYUh9ydh/+5vpOqV4YQ== - -growl@1.10.5: - version "1.10.5" - resolved "https://registry.yarnpkg.com/growl/-/growl-1.10.5.tgz#f2735dc2283674fa67478b10181059355c369e5e" - integrity sha512-qBr4OuELkhPenW6goKVXiv47US3clb3/IbuWF9KNKEijAy9oeHxU9IgzjvJhHkUzhaj7rOUD7+YGWqUjLp5oSA== - -gulp-clean@^0.3.2: - version "0.3.2" - resolved "https://registry.yarnpkg.com/gulp-clean/-/gulp-clean-0.3.2.tgz#a347d473acea40182f935587a451941671928102" - integrity sha1-o0fUc6zqQBgvk1WHpFGUFnGSgQI= - dependencies: - gulp-util "^2.2.14" - rimraf "^2.2.8" - through2 "^0.4.2" - -gulp-cli@^2.2.0: - version "2.3.0" - resolved "https://registry.yarnpkg.com/gulp-cli/-/gulp-cli-2.3.0.tgz#ec0d380e29e52aa45e47977f0d32e18fd161122f" - integrity sha512-zzGBl5fHo0EKSXsHzjspp3y5CONegCm8ErO5Qh0UzFzk2y4tMvzLWhoDokADbarfZRL2pGpRp7yt6gfJX4ph7A== - dependencies: - ansi-colors "^1.0.1" - archy "^1.0.0" - array-sort "^1.0.0" - color-support "^1.1.3" - concat-stream "^1.6.0" - copy-props "^2.0.1" - fancy-log "^1.3.2" - gulplog "^1.0.0" - interpret "^1.4.0" - isobject "^3.0.1" - liftoff "^3.1.0" - matchdep "^2.0.0" - mute-stdout "^1.0.0" - pretty-hrtime "^1.0.0" - replace-homedir "^1.0.0" - semver-greatest-satisfied-range "^1.1.0" - v8flags "^3.2.0" - yargs "^7.1.0" - -gulp-concat@^2.6.0: - version "2.6.1" - resolved "https://registry.yarnpkg.com/gulp-concat/-/gulp-concat-2.6.1.tgz#633d16c95d88504628ad02665663cee5a4793353" - integrity sha1-Yz0WyV2IUEYorQJmVmPO5aR5M1M= - dependencies: - concat-with-sourcemaps "^1.0.0" - through2 "^2.0.0" - vinyl "^2.0.0" - -gulp-connect@^5.7.0: - version "5.7.0" - resolved "https://registry.yarnpkg.com/gulp-connect/-/gulp-connect-5.7.0.tgz#7e925f5e4c34ebfedf9f318576966e8fe8840d5a" - integrity sha512-8tRcC6wgXMLakpPw9M7GRJIhxkYdgZsXwn7n56BA2bQYGLR9NOPhMzx7js+qYDy6vhNkbApGKURjAw1FjY4pNA== - dependencies: - ansi-colors "^2.0.5" - connect "^3.6.6" - connect-livereload "^0.6.0" - fancy-log "^1.3.2" - map-stream "^0.0.7" - send "^0.16.2" - serve-index "^1.9.1" - serve-static "^1.13.2" - tiny-lr "^1.1.1" - -gulp-eslint@^4.0.0: - version "4.0.2" - resolved "https://registry.yarnpkg.com/gulp-eslint/-/gulp-eslint-4.0.2.tgz#18a2a6768e4404cbf3e203239cb57474168fa606" - integrity sha512-fcFUQzFsN6dJ6KZlG+qPOEkqfcevRUXgztkYCvhNvJeSvOicC8ucutN4qR/ID8LmNZx9YPIkBzazTNnVvbh8wg== - dependencies: - eslint "^4.0.0" - fancy-log "^1.3.2" - plugin-error "^1.0.0" - -gulp-footer@^2.0.2: - version "2.0.2" - resolved "https://registry.yarnpkg.com/gulp-footer/-/gulp-footer-2.0.2.tgz#2cf58e1cc046b8a3a479f1a41bcd1b7baae076cd" - integrity sha512-HsG5VOgKHFRqZXnHGI6oGhPDg70p9pobM+dYOnjBZVLMQUHzLG6bfaPNRJ7XG707E+vWO3TfN0CND9UrYhk94g== - dependencies: - lodash._reescape "^3.0.0" - lodash._reevaluate "^3.0.0" - lodash._reinterpolate "^3.0.0" - lodash.template "^3.6.2" - map-stream "0.0.7" - -gulp-header@^2.0.9: - version "2.0.9" - resolved "https://registry.yarnpkg.com/gulp-header/-/gulp-header-2.0.9.tgz#8b432c4d4379dee6788845b16785b09c7675af84" - integrity sha512-LMGiBx+qH8giwrOuuZXSGvswcIUh0OiioNkUpLhNyvaC6/Ga8X6cfAeme2L5PqsbXMhL8o8b/OmVqIQdxprhcQ== - dependencies: - concat-with-sourcemaps "^1.1.0" - lodash.template "^4.5.0" - map-stream "0.0.7" - through2 "^2.0.0" - -gulp-if@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/gulp-if/-/gulp-if-3.0.0.tgz#6c3e7edc8bafadc34f2ebecb314bf43324ba1e40" - integrity sha512-fCUEngzNiEZEK2YuPm+sdMpO6ukb8+/qzbGfJBXyNOXz85bCG7yBI+pPSl+N90d7gnLvMsarthsAImx0qy7BAw== - dependencies: - gulp-match "^1.1.0" - ternary-stream "^3.0.0" - through2 "^3.0.1" - -gulp-js-escape@^1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/gulp-js-escape/-/gulp-js-escape-1.0.1.tgz#1cd445fbd009e0da76959a03a7f49b3566aff868" - integrity sha1-HNRF+9AJ4Np2lZoDp/SbNWav+Gg= - dependencies: - through2 "^0.6.3" - -gulp-match@^1.1.0: - version "1.1.0" - resolved "https://registry.yarnpkg.com/gulp-match/-/gulp-match-1.1.0.tgz#552b7080fc006ee752c90563f9fec9d61aafdf4f" - integrity sha512-DlyVxa1Gj24DitY2OjEsS+X6tDpretuxD6wTfhXE/Rw2hweqc1f6D/XtsJmoiCwLWfXgR87W9ozEityPCVzGtQ== - dependencies: - minimatch "^3.0.3" - -gulp-replace@^1.0.0: - version "1.1.3" - resolved "https://registry.yarnpkg.com/gulp-replace/-/gulp-replace-1.1.3.tgz#8641cdca78e683e8573ca4a012e7e4ebb7e4db60" - integrity sha512-HcPHpWY4XdF8zxYkDODHnG2+7a3nD/Y8Mfu3aBgMiCFDW3X2GiOKXllsAmILcxe3KZT2BXoN18WrpEFm48KfLQ== - dependencies: - "@types/node" "^14.14.41" - "@types/vinyl" "^2.0.4" - istextorbinary "^3.0.0" - replacestream "^4.0.3" - yargs-parser ">=5.0.0-security.0" - -gulp-shell@^0.8.0: - version "0.8.0" - resolved "https://registry.yarnpkg.com/gulp-shell/-/gulp-shell-0.8.0.tgz#0ed4980de1d0c67e5f6cce971d7201fd0be50555" - integrity sha512-wHNCgmqbWkk1c6Gc2dOL5SprcoeujQdeepICwfQRo91DIylTE7a794VEE+leq3cE2YDoiS5ulvRfKVIEMazcTQ== - dependencies: - chalk "^3.0.0" - fancy-log "^1.3.3" - lodash.template "^4.5.0" - plugin-error "^1.0.1" - through2 "^3.0.1" - tslib "^1.10.0" - -gulp-sourcemaps@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/gulp-sourcemaps/-/gulp-sourcemaps-3.0.0.tgz#2e154e1a2efed033c0e48013969e6f30337b2743" - integrity sha512-RqvUckJkuYqy4VaIH60RMal4ZtG0IbQ6PXMNkNsshEGJ9cldUPRb/YCgboYae+CLAs1HQNb4ADTKCx65HInquQ== - dependencies: - "@gulp-sourcemaps/identity-map" "^2.0.1" - "@gulp-sourcemaps/map-sources" "^1.0.0" - acorn "^6.4.1" - convert-source-map "^1.0.0" - css "^3.0.0" - debug-fabulous "^1.0.0" - detect-newline "^2.0.0" - graceful-fs "^4.0.0" - source-map "^0.6.0" - strip-bom-string "^1.0.0" - through2 "^2.0.0" - -gulp-terser@^2.0.1: - version "2.1.0" - resolved "https://registry.yarnpkg.com/gulp-terser/-/gulp-terser-2.1.0.tgz#149b693a1adbde922807b60b844bb7351dafbde1" - integrity sha512-lQ3+JUdHDVISAlUIUSZ/G9Dz/rBQHxOiYDQ70IVWFQeh4b33TC1MCIU+K18w07PS3rq/CVc34aQO4SUbdaNMPQ== - dependencies: - plugin-error "^1.0.1" - terser "^5.9.0" - through2 "^4.0.2" - vinyl-sourcemaps-apply "^0.2.1" - -gulp-util@^2.2.14: - version "2.2.20" - resolved "https://registry.yarnpkg.com/gulp-util/-/gulp-util-2.2.20.tgz#d7146e5728910bd8f047a6b0b1e549bc22dbd64c" - integrity sha1-1xRuVyiRC9jwR6awseVJvCLb1kw= - dependencies: - chalk "^0.5.0" - dateformat "^1.0.7-1.2.3" - lodash._reinterpolate "^2.4.1" - lodash.template "^2.4.1" - minimist "^0.2.0" - multipipe "^0.1.0" - through2 "^0.5.0" - vinyl "^0.2.1" - -gulp-util@^3.0.0, gulp-util@^3.0.7: - version "3.0.8" - resolved "https://registry.yarnpkg.com/gulp-util/-/gulp-util-3.0.8.tgz#0054e1e744502e27c04c187c3ecc505dd54bbb4f" - integrity sha1-AFTh50RQLifATBh8PsxQXdVLu08= - dependencies: - array-differ "^1.0.0" - array-uniq "^1.0.2" - beeper "^1.0.0" - chalk "^1.0.0" - dateformat "^2.0.0" - fancy-log "^1.1.0" - gulplog "^1.0.0" - has-gulplog "^0.1.0" - lodash._reescape "^3.0.0" - lodash._reevaluate "^3.0.0" - lodash._reinterpolate "^3.0.0" - lodash.template "^3.0.0" - minimist "^1.1.0" - multipipe "^0.1.2" - object-assign "^3.0.0" - replace-ext "0.0.1" - through2 "^2.0.0" - vinyl "^0.5.0" - -gulp@^4.0.0: - version "4.0.2" - resolved "https://registry.yarnpkg.com/gulp/-/gulp-4.0.2.tgz#543651070fd0f6ab0a0650c6a3e6ff5a7cb09caa" - integrity sha512-dvEs27SCZt2ibF29xYgmnwwCYZxdxhQ/+LFWlbAW8y7jt68L/65402Lz3+CKy0Ov4rOs+NERmDq7YlZaDqUIfA== - dependencies: - glob-watcher "^5.0.3" - gulp-cli "^2.2.0" - undertaker "^1.2.1" - vinyl-fs "^3.0.0" - -gulplog@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/gulplog/-/gulplog-1.0.0.tgz#e28c4d45d05ecbbed818363ce8f9c5926229ffe5" - integrity sha1-4oxNRdBey77YGDY86PnFkmIp/+U= - dependencies: - glogg "^1.0.0" - -gzip-size@^5.0.0: - version "5.1.1" - resolved "https://registry.yarnpkg.com/gzip-size/-/gzip-size-5.1.1.tgz#cb9bee692f87c0612b232840a873904e4c135274" - integrity sha512-FNHi6mmoHvs1mxZAds4PpdCS6QG8B4C1krxJsMutgxl5t3+GlRTzzI3NEkifXx2pVsOvJdOGSmIgDhQ55FwdPA== - dependencies: - duplexer "^0.1.1" - pify "^4.0.1" - -handlebars@^4.0.1, handlebars@^4.7.6: - version "4.7.7" - resolved "https://registry.yarnpkg.com/handlebars/-/handlebars-4.7.7.tgz#9ce33416aad02dbd6c8fafa8240d5d98004945a1" - integrity sha512-aAcXm5OAfE/8IXkcZvCepKU3VzW1/39Fb5ZuqMtgI/hT8X2YgoMvBY5dLhq/cpOvw7Lk1nK/UF71aLG/ZnVYRA== - dependencies: - minimist "^1.2.5" - neo-async "^2.6.0" - source-map "^0.6.1" - wordwrap "^1.0.0" - optionalDependencies: - uglify-js "^3.1.4" - -har-schema@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/har-schema/-/har-schema-2.0.0.tgz#a94c2224ebcac04782a0d9035521f24735b7ec92" - integrity sha1-qUwiJOvKwEeCoNkDVSHyRzW37JI= - -har-validator@~5.1.3: - version "5.1.5" - resolved "https://registry.yarnpkg.com/har-validator/-/har-validator-5.1.5.tgz#1f0803b9f8cb20c0fa13822df1ecddb36bde1efd" - integrity sha512-nmT2T0lljbxdQZfspsno9hgrG3Uir6Ks5afism62poxqBM6sDnMEuPmzTq8XN0OEwqKLLdh1jQI3qyE66Nzb3w== - dependencies: - ajv "^6.12.3" - har-schema "^2.0.0" - -hard-rejection@^2.1.0: - version "2.1.0" - resolved "https://registry.yarnpkg.com/hard-rejection/-/hard-rejection-2.1.0.tgz#1c6eda5c1685c63942766d79bb40ae773cecd883" - integrity sha512-VIZB+ibDhx7ObhAe7OVtoEbuP4h/MuOTHJ+J8h/eBXotJYl0fBgR72xDFCKgIh22OJZIOVNxBMWuhAr10r8HdA== - -has-ansi@^0.1.0: - version "0.1.0" - resolved "https://registry.yarnpkg.com/has-ansi/-/has-ansi-0.1.0.tgz#84f265aae8c0e6a88a12d7022894b7568894c62e" - integrity sha1-hPJlqujA5qiKEtcCKJS3VoiUxi4= - dependencies: - ansi-regex "^0.2.0" - -has-ansi@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/has-ansi/-/has-ansi-2.0.0.tgz#34f5049ce1ecdf2b0649af3ef24e45ed35416d91" - integrity sha1-NPUEnOHs3ysGSa8+8k5F7TVBbZE= - dependencies: - ansi-regex "^2.0.0" - -has-bigints@^1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/has-bigints/-/has-bigints-1.0.1.tgz#64fe6acb020673e3b78db035a5af69aa9d07b113" - integrity sha512-LSBS2LjbNBTf6287JEbEzvJgftkF5qFkmCo9hDRpAzKhUOlJ+hx8dd4USs00SgsUNwc4617J9ki5YtEClM2ffA== - -has-flag@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/has-flag/-/has-flag-1.0.0.tgz#9d9e793165ce017a00f00418c43f942a7b1d11fa" - integrity sha1-nZ55MWXOAXoA8AQYxD+UKnsdEfo= - -has-flag@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/has-flag/-/has-flag-2.0.0.tgz#e8207af1cc7b30d446cc70b734b5e8be18f88d51" - integrity sha1-6CB68cx7MNRGzHC3NLXovhj4jVE= - -has-flag@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/has-flag/-/has-flag-3.0.0.tgz#b5d454dc2199ae225699f3467e5a07f3b955bafd" - integrity sha1-tdRU3CGZriJWmfNGfloH87lVuv0= - -has-flag@^4.0.0: - version "4.0.0" - resolved "https://registry.yarnpkg.com/has-flag/-/has-flag-4.0.0.tgz#944771fd9c81c81265c4d6941860da06bb59479b" - integrity sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ== - -has-gulplog@^0.1.0: - version "0.1.0" - resolved "https://registry.yarnpkg.com/has-gulplog/-/has-gulplog-0.1.0.tgz#6414c82913697da51590397dafb12f22967811ce" - integrity sha1-ZBTIKRNpfaUVkDl9r7EvIpZ4Ec4= - dependencies: - sparkles "^1.0.0" - -has-symbols@^1.0.1, has-symbols@^1.0.2: - version "1.0.2" - resolved "https://registry.yarnpkg.com/has-symbols/-/has-symbols-1.0.2.tgz#165d3070c00309752a1236a479331e3ac56f1423" - integrity sha512-chXa79rL/UC2KlX17jo3vRGz0azaWEx5tGqZg5pO3NUyEJVB17dMruQlzCCOfUvElghKcm5194+BCRvi2Rv/Gw== - -has-tostringtag@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/has-tostringtag/-/has-tostringtag-1.0.0.tgz#7e133818a7d394734f941e73c3d3f9291e658b25" - integrity sha512-kFjcSNhnlGV1kyoGk7OXKSawH5JOb/LzUc5w9B02hOTO0dfFRjbHQKvg1d6cf3HbeUmtU9VbbV3qzZ2Teh97WQ== - dependencies: - has-symbols "^1.0.2" - -has-value@^0.3.1: - version "0.3.1" - resolved "https://registry.yarnpkg.com/has-value/-/has-value-0.3.1.tgz#7b1f58bada62ca827ec0a2078025654845995e1f" - integrity sha1-ex9YutpiyoJ+wKIHgCVlSEWZXh8= - dependencies: - get-value "^2.0.3" - has-values "^0.1.4" - isobject "^2.0.0" - -has-value@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/has-value/-/has-value-1.0.0.tgz#18b281da585b1c5c51def24c930ed29a0be6b177" - integrity sha1-GLKB2lhbHFxR3vJMkw7SmgvmsXc= - dependencies: - get-value "^2.0.6" - has-values "^1.0.0" - isobject "^3.0.0" - -has-values@^0.1.4: - version "0.1.4" - resolved "https://registry.yarnpkg.com/has-values/-/has-values-0.1.4.tgz#6d61de95d91dfca9b9a02089ad384bff8f62b771" - integrity sha1-bWHeldkd/Km5oCCJrThL/49it3E= - -has-values@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/has-values/-/has-values-1.0.0.tgz#95b0b63fec2146619a6fe57fe75628d5a39efe4f" - integrity sha1-lbC2P+whRmGab+V/51Yo1aOe/k8= - dependencies: - is-number "^3.0.0" - kind-of "^4.0.0" - -has@^1.0.3: - version "1.0.3" - resolved "https://registry.yarnpkg.com/has/-/has-1.0.3.tgz#722d7cbfc1f6aa8241f16dd814e011e1f41e8796" - integrity sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw== - dependencies: - function-bind "^1.1.1" - -hash-base@^3.0.0: - version "3.1.0" - resolved "https://registry.yarnpkg.com/hash-base/-/hash-base-3.1.0.tgz#55c381d9e06e1d2997a883b4a3fddfe7f0d3af33" - integrity sha512-1nmYp/rhMDiE7AYkDw+lLwlAzz0AntGIe51F3RfFfEqyQ3feY2eI/NcwC6umIQVOASPMsWJLJScWKSSvzL9IVA== - dependencies: - inherits "^2.0.4" - readable-stream "^3.6.0" - safe-buffer "^5.2.0" - -hash.js@^1.0.0, hash.js@^1.0.3: - version "1.1.7" - resolved "https://registry.yarnpkg.com/hash.js/-/hash.js-1.1.7.tgz#0babca538e8d4ee4a0f8988d68866537a003cf42" - integrity sha512-taOaskGt4z4SOANNseOviYDvjEJinIkRgmp7LbKP2YTTmVxWBl87s/uzK9r+44BclBSp2X7K1hqeNfz9JbBeXA== - dependencies: - inherits "^2.0.3" - minimalistic-assert "^1.0.1" - -hast-util-is-element@^1.0.0: - version "1.1.0" - resolved "https://registry.yarnpkg.com/hast-util-is-element/-/hast-util-is-element-1.1.0.tgz#3b3ed5159a2707c6137b48637fbfe068e175a425" - integrity sha512-oUmNua0bFbdrD/ELDSSEadRVtWZOf3iF6Lbv81naqsIV99RnSCieTbWuWCY8BAeEfKJTKl0gRdokv+dELutHGQ== - -hast-util-sanitize@^3.0.0: - version "3.0.2" - resolved "https://registry.yarnpkg.com/hast-util-sanitize/-/hast-util-sanitize-3.0.2.tgz#b0b783220af528ba8fe6999f092d138908678520" - integrity sha512-+2I0x2ZCAyiZOO/sb4yNLFmdwPBnyJ4PBkVTUMKMqBwYNA+lXSgOmoRXlJFazoyid9QPogRRKgKhVEodv181sA== - dependencies: - xtend "^4.0.0" - -hast-util-to-html@^7.0.0: - version "7.1.3" - resolved "https://registry.yarnpkg.com/hast-util-to-html/-/hast-util-to-html-7.1.3.tgz#9f339ca9bea71246e565fc79ff7dbfe98bb50f5e" - integrity sha512-yk2+1p3EJTEE9ZEUkgHsUSVhIpCsL/bvT8E5GzmWc+N1Po5gBw+0F8bo7dpxXR0nu0bQVxVZGX2lBGF21CmeDw== - dependencies: - ccount "^1.0.0" - comma-separated-tokens "^1.0.0" - hast-util-is-element "^1.0.0" - hast-util-whitespace "^1.0.0" - html-void-elements "^1.0.0" - property-information "^5.0.0" - space-separated-tokens "^1.0.0" - stringify-entities "^3.0.1" - unist-util-is "^4.0.0" - xtend "^4.0.0" - -hast-util-whitespace@^1.0.0: - version "1.0.4" - resolved "https://registry.yarnpkg.com/hast-util-whitespace/-/hast-util-whitespace-1.0.4.tgz#e4fe77c4a9ae1cb2e6c25e02df0043d0164f6e41" - integrity sha512-I5GTdSfhYfAPNztx2xJRQpG8cuDSNt599/7YUn7Gx/WxNMsG+a835k97TDkFgk123cwjfwINaZknkKkphx/f2A== - -he@1.1.1: - version "1.1.1" - resolved "https://registry.yarnpkg.com/he/-/he-1.1.1.tgz#93410fd21b009735151f8868c2f271f3427e23fd" - integrity sha1-k0EP0hsAlzUVH4howvJx80J+I/0= - -he@1.2.0, he@^1.1.0: - version "1.2.0" - resolved "https://registry.yarnpkg.com/he/-/he-1.2.0.tgz#84ae65fa7eafb165fddb61566ae14baf05664f0f" - integrity sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw== - -highlight.js@^10.7.2: - version "10.7.3" - resolved "https://registry.yarnpkg.com/highlight.js/-/highlight.js-10.7.3.tgz#697272e3991356e40c3cac566a74eef681756531" - integrity sha512-tzcUFauisWKNHaRkN4Wjl/ZA07gENAjFl3J/c480dprkGTg5EQstgaNFqBfUqCq54kZRIEcreTsAgF/m2quD7A== - -hmac-drbg@^1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/hmac-drbg/-/hmac-drbg-1.0.1.tgz#d2745701025a6c775a6c545793ed502fc0c649a1" - integrity sha1-0nRXAQJabHdabFRXk+1QL8DGSaE= - dependencies: - hash.js "^1.0.3" - minimalistic-assert "^1.0.0" - minimalistic-crypto-utils "^1.0.1" - -homedir-polyfill@^1.0.1: - version "1.0.3" - resolved "https://registry.yarnpkg.com/homedir-polyfill/-/homedir-polyfill-1.0.3.tgz#743298cef4e5af3e194161fbadcc2151d3a058e8" - integrity sha512-eSmmWE5bZTK2Nou4g0AI3zZ9rswp7GRKoKXS1BLUkvPviOqs4YTN1djQIqrXy9k5gEtdLPy86JjRwsNM9tnDcA== - dependencies: - parse-passwd "^1.0.0" - -hoopy@^0.1.4: - version "0.1.4" - resolved "https://registry.yarnpkg.com/hoopy/-/hoopy-0.1.4.tgz#609207d661100033a9a9402ad3dea677381c1b1d" - integrity sha512-HRcs+2mr52W0K+x8RzcLzuPPmVIKMSv97RGHy0Ea9y/mpcaK+xTrjICA04KAHi4GRzxliNqNJEFYWHghy3rSfQ== - -hosted-git-info@^2.1.4: - version "2.8.9" - resolved "https://registry.yarnpkg.com/hosted-git-info/-/hosted-git-info-2.8.9.tgz#dffc0bf9a21c02209090f2aa69429e1414daf3f9" - integrity sha512-mxIDAb9Lsm6DoOJ7xH+5+X4y1LU/4Hi50L9C5sIswK3JzULS4bwk1FvjdBgvYR4bzT4tuUQiC15FE2f5HbLvYw== - -hosted-git-info@^4.0.0, hosted-git-info@^4.0.1: - version "4.0.2" - resolved "https://registry.yarnpkg.com/hosted-git-info/-/hosted-git-info-4.0.2.tgz#5e425507eede4fea846b7262f0838456c4209961" - integrity sha512-c9OGXbZ3guC/xOlCg1Ci/VgWlwsqDv1yMQL1CWqXDL0hDjXuNcq0zuR4xqPSuasI3kqFDhqSyTjREz5gzq0fXg== - dependencies: - lru-cache "^6.0.0" - -html-escaper@^2.0.0: - version "2.0.2" - resolved "https://registry.yarnpkg.com/html-escaper/-/html-escaper-2.0.2.tgz#dfd60027da36a36dfcbe236262c00a5822681453" - integrity sha512-H2iMtd0I4Mt5eYiapRdIDjp+XzelXQ0tFE4JS7YFwFevXXMmOp9myNrUvCg0D6ws8iqkRPBfKHgbwig1SmlLfg== - -html-void-elements@^1.0.0: - version "1.0.5" - resolved "https://registry.yarnpkg.com/html-void-elements/-/html-void-elements-1.0.5.tgz#ce9159494e86d95e45795b166c2021c2cfca4483" - integrity sha512-uE/TxKuyNIcx44cIWnjr/rfIATDH7ZaOMmstu0CwhFG1Dunhlp4OC6/NMbhiwoq5BpW0ubi303qnEk/PZj614w== - -http-cache-semantics@^4.0.0: - version "4.1.0" - resolved "https://registry.yarnpkg.com/http-cache-semantics/-/http-cache-semantics-4.1.0.tgz#49e91c5cbf36c9b94bcfcd71c23d5249ec74e390" - integrity sha512-carPklcUh7ROWRK7Cv27RPtdhYhUsela/ue5/jKzjegVvXDqM2ILE9Q2BGn9JZJh1g87cp56su/FgQSzcWS8cQ== - -http-errors@1.7.2: - version "1.7.2" - resolved "https://registry.yarnpkg.com/http-errors/-/http-errors-1.7.2.tgz#4f5029cf13239f31036e5b2e55292bcfbcc85c8f" - integrity sha512-uUQBt3H/cSIVfch6i1EuPNy/YsRSOUBXTVfZ+yR7Zjez3qjBz6i9+i4zjNaoqcoFVI4lQJ5plg63TvGfRSDCRg== - dependencies: - depd "~1.1.2" - inherits "2.0.3" - setprototypeof "1.1.1" - statuses ">= 1.5.0 < 2" - toidentifier "1.0.0" - -http-errors@~1.6.2: - version "1.6.3" - resolved "https://registry.yarnpkg.com/http-errors/-/http-errors-1.6.3.tgz#8b55680bb4be283a0b5bf4ea2e38580be1d9320d" - integrity sha1-i1VoC7S+KDoLW/TqLjhYC+HZMg0= - dependencies: - depd "~1.1.2" - inherits "2.0.3" - setprototypeof "1.1.0" - statuses ">= 1.4.0 < 2" - -http-errors@~1.7.2: - version "1.7.3" - resolved "https://registry.yarnpkg.com/http-errors/-/http-errors-1.7.3.tgz#6c619e4f9c60308c38519498c14fbb10aacebb06" - integrity sha512-ZTTX0MWrsQ2ZAhA1cejAwDLycFsd7I7nVtnkT3Ol0aqodaKW+0CTZDQ1uBv5whptCnc8e8HeRRJxRs0kmm/Qfw== - dependencies: - depd "~1.1.2" - inherits "2.0.4" - setprototypeof "1.1.1" - statuses ">= 1.5.0 < 2" - toidentifier "1.0.0" - -http-parser-js@>=0.5.1: - version "0.5.3" - resolved "https://registry.yarnpkg.com/http-parser-js/-/http-parser-js-0.5.3.tgz#01d2709c79d41698bb01d4decc5e9da4e4a033d9" - integrity sha512-t7hjvef/5HEK7RWTdUzVUhl8zkEu+LlaE0IYzdMuvbSDipxBRpOn4Uhw8ZyECEa808iVT8XCjzo6xmYt4CiLZg== - -http-proxy@^1.18.1: - version "1.18.1" - resolved "https://registry.yarnpkg.com/http-proxy/-/http-proxy-1.18.1.tgz#401541f0534884bbf95260334e72f88ee3976549" - integrity sha512-7mz/721AbnJwIVbnaSv1Cz3Am0ZLT/UBwkC92VlxhXv/k/BBQfM2fXElQNC27BVGr0uwUpplYPQM9LnaBMR5NQ== - dependencies: - eventemitter3 "^4.0.0" - follow-redirects "^1.0.0" - requires-port "^1.0.0" - -http-signature@~1.2.0: - version "1.2.0" - resolved "https://registry.yarnpkg.com/http-signature/-/http-signature-1.2.0.tgz#9aecd925114772f3d95b65a60abb8f7c18fbace1" - integrity sha1-muzZJRFHcvPZW2WmCruPfBj7rOE= - dependencies: - assert-plus "^1.0.0" - jsprim "^1.2.2" - sshpk "^1.7.0" - -http2-wrapper@^1.0.0-beta.5.2: - version "1.0.3" - resolved "https://registry.yarnpkg.com/http2-wrapper/-/http2-wrapper-1.0.3.tgz#b8f55e0c1f25d4ebd08b3b0c2c079f9590800b3d" - integrity sha512-V+23sDMr12Wnz7iTcDeJr3O6AIxlnvT/bmaAAAP/Xda35C90p9599p0F1eHR/N1KILWSoWVAiOMFjBBXaXSMxg== - dependencies: - quick-lru "^5.1.1" - resolve-alpn "^1.0.0" - -https-browserify@0.0.1: - version "0.0.1" - resolved "https://registry.yarnpkg.com/https-browserify/-/https-browserify-0.0.1.tgz#3f91365cabe60b77ed0ebba24b454e3e09d95a82" - integrity sha1-P5E2XKvmC3ftDruiS0VOPgnZWoI= - -https-browserify@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/https-browserify/-/https-browserify-1.0.0.tgz#ec06c10e0a34c0f2faf199f7fd7fc78fffd03c73" - integrity sha1-7AbBDgo0wPL68Zn3/X/Hj//QPHM= - -https-proxy-agent@5.0.0: - version "5.0.0" - resolved "https://registry.yarnpkg.com/https-proxy-agent/-/https-proxy-agent-5.0.0.tgz#e2a90542abb68a762e0a0850f6c9edadfd8506b2" - integrity sha512-EkYm5BcKUGiduxzSt3Eppko+PiNWNEpa4ySk9vTC6wDsQJW9rHSa+UhGNJoRYp7bz6Ht1eaRIa6QaJqO5rCFbA== - dependencies: - agent-base "6" - debug "4" - -https-proxy-agent@^2.2.1: - version "2.2.4" - resolved "https://registry.yarnpkg.com/https-proxy-agent/-/https-proxy-agent-2.2.4.tgz#4ee7a737abd92678a293d9b34a1af4d0d08c787b" - integrity sha512-OmvfoQ53WLjtA9HeYP9RNrWMJzzAz1JGaSFr1nijg0PVR1JaD/xbJq1mdEIIlxGpXp9eSe/O2LgU9DJmTPd0Eg== - dependencies: - agent-base "^4.3.0" - debug "^3.1.0" - -https-proxy-agent@^4.0.0: - version "4.0.0" - resolved "https://registry.yarnpkg.com/https-proxy-agent/-/https-proxy-agent-4.0.0.tgz#702b71fb5520a132a66de1f67541d9e62154d82b" - integrity sha512-zoDhWrkR3of1l9QAL8/scJZyLu8j/gBkcwcaQOZh7Gyh/+uJQzGVETdgT30akuwkpL8HTRfssqI3BZuV18teDg== - dependencies: - agent-base "5" - debug "4" - -iconv-lite@0.4.24, iconv-lite@^0.4.17, iconv-lite@^0.4.24: - version "0.4.24" - resolved "https://registry.yarnpkg.com/iconv-lite/-/iconv-lite-0.4.24.tgz#2022b4b25fbddc21d2f524974a474aafe733908b" - integrity sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA== - dependencies: - safer-buffer ">= 2.1.2 < 3" - -ieee754@^1.1.13, ieee754@^1.1.4: - version "1.2.1" - resolved "https://registry.yarnpkg.com/ieee754/-/ieee754-1.2.1.tgz#8eb7a10a63fff25d15a57b001586d177d1b0d352" - integrity sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA== - -ignore@^3.3.3: - version "3.3.10" - resolved "https://registry.yarnpkg.com/ignore/-/ignore-3.3.10.tgz#0a97fb876986e8081c631160f8f9f389157f0043" - integrity sha512-Pgs951kaMm5GXP7MOvxERINe3gsaVjUWFm+UZPSq9xYriQAksyhg0csnS0KXSNRD5NmNdapXEpjxG49+AKh/ug== - -ignore@^4.0.6: - version "4.0.6" - resolved "https://registry.yarnpkg.com/ignore/-/ignore-4.0.6.tgz#750e3db5862087b4737ebac8207ffd1ef27b25fc" - integrity sha512-cyFDKrqc/YdcWFniJhzI42+AzS+gNwmUzOSFcRCQYwySuBBBy/KjuxWLZ/FHEH6Moq1NizMOBWyTcv8O4OZIMg== - -ignore@^5.1.1: - version "5.1.8" - resolved "https://registry.yarnpkg.com/ignore/-/ignore-5.1.8.tgz#f150a8b50a34289b33e22f5889abd4d8016f0e57" - integrity sha512-BMpfD7PpiETpBl/A6S498BaIJ6Y/ABT93ETbby2fP00v4EbvPBXWEoaR1UBPKs3iR53pJY7EtZk5KACI57i1Uw== - -import-fresh@^3.0.0, import-fresh@^3.2.1: - version "3.3.0" - resolved "https://registry.yarnpkg.com/import-fresh/-/import-fresh-3.3.0.tgz#37162c25fcb9ebaa2e6e53d5b4d88ce17d9e0c2b" - integrity sha512-veYYhQa+D1QBKznvhUHxb8faxlrwUnxseDAbAp457E0wLNio2bOSKnjYDhMj+YiAq61xrMGhQk9iXVk5FzgQMw== - dependencies: - parent-module "^1.0.0" - resolve-from "^4.0.0" - -imurmurhash@^0.1.4: - version "0.1.4" - resolved "https://registry.yarnpkg.com/imurmurhash/-/imurmurhash-0.1.4.tgz#9218b9b2b928a238b13dc4fb6b6d576f231453ea" - integrity sha1-khi5srkoojixPcT7a21XbyMUU+o= - -indent-string@^2.1.0: - version "2.1.0" - resolved "https://registry.yarnpkg.com/indent-string/-/indent-string-2.1.0.tgz#8e2d48348742121b4a8218b7a137e9a52049dc80" - integrity sha1-ji1INIdCEhtKghi3oTfppSBJ3IA= - dependencies: - repeating "^2.0.0" - -indent-string@^3.0.0: - version "3.2.0" - resolved "https://registry.yarnpkg.com/indent-string/-/indent-string-3.2.0.tgz#4a5fd6d27cc332f37e5419a504dbb837105c9289" - integrity sha1-Sl/W0nzDMvN+VBmlBNu4NxBckok= - -indent-string@^4.0.0: - version "4.0.0" - resolved "https://registry.yarnpkg.com/indent-string/-/indent-string-4.0.0.tgz#624f8f4497d619b2d9768531d58f4122854d7251" - integrity sha512-EdDDZu4A2OyIK7Lr/2zG+w5jmbuk1DVBnEwREQvBzspBJkCEbRa8GxU1lghYcaGJCnRWibjDXlq779X1/y5xwg== - -indexof@0.0.1: - version "0.0.1" - resolved "https://registry.yarnpkg.com/indexof/-/indexof-0.0.1.tgz#82dc336d232b9062179d05ab3293a66059fd435d" - integrity sha1-gtwzbSMrkGIXnQWrMpOmYFn9Q10= - -inflight@^1.0.4: - version "1.0.6" - resolved "https://registry.yarnpkg.com/inflight/-/inflight-1.0.6.tgz#49bd6331d7d02d0c09bc910a1075ba8165b56df9" - integrity sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk= - dependencies: - once "^1.3.0" - wrappy "1" - -inherits@2, inherits@2.0.4, inherits@^2.0.1, inherits@^2.0.3, inherits@^2.0.4, inherits@~2.0.0, inherits@~2.0.1, inherits@~2.0.3: - version "2.0.4" - resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.4.tgz#0fa2c64f932917c3433a0ded55363aae37416b7c" - integrity sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ== - -inherits@2.0.1: - version "2.0.1" - resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.1.tgz#b17d08d326b4423e568eff719f91b0b1cbdf69f1" - integrity sha1-sX0I0ya0Qj5Wjv9xn5GwscvfafE= - -inherits@2.0.3: - version "2.0.3" - resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.3.tgz#633c2c83e3da42a502f52466022480f4208261de" - integrity sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4= - -ini@^1.3.2, ini@^1.3.4, ini@^1.3.5: - version "1.3.8" - resolved "https://registry.yarnpkg.com/ini/-/ini-1.3.8.tgz#a29da425b48806f34767a4efce397269af28432c" - integrity sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew== - -inquirer@8.1.5: - version "8.1.5" - resolved "https://registry.yarnpkg.com/inquirer/-/inquirer-8.1.5.tgz#2dc5159203c826d654915b5fe6990fd17f54a150" - integrity sha512-G6/9xUqmt/r+UvufSyrPpt84NYwhKZ9jLsgMbQzlx804XErNupor8WQdBnBRrXmBfTPpuwf1sV+ss2ovjgdXIg== - dependencies: - ansi-escapes "^4.2.1" - chalk "^4.1.1" - cli-cursor "^3.1.0" - cli-width "^3.0.0" - external-editor "^3.0.3" - figures "^3.0.0" - lodash "^4.17.21" - mute-stream "0.0.8" - ora "^5.4.1" - run-async "^2.4.0" - rxjs "^7.2.0" - string-width "^4.1.0" - strip-ansi "^6.0.0" - through "^2.3.6" - -inquirer@^3.0.6: - version "3.3.0" - resolved "https://registry.yarnpkg.com/inquirer/-/inquirer-3.3.0.tgz#9dd2f2ad765dcab1ff0443b491442a20ba227dc9" - integrity sha512-h+xtnyk4EwKvFWHrUYsWErEVR+igKtLdchu+o0Z1RL7VU/jVMFbYir2bp6bAj8efFNxWqHX0dIss6fJQ+/+qeQ== - dependencies: - ansi-escapes "^3.0.0" - chalk "^2.0.0" - cli-cursor "^2.1.0" - cli-width "^2.0.0" - external-editor "^2.0.4" - figures "^2.0.0" - lodash "^4.3.0" - mute-stream "0.0.7" - run-async "^2.2.0" - rx-lite "^4.0.8" - rx-lite-aggregates "^4.0.8" - string-width "^2.1.0" - strip-ansi "^4.0.0" - through "^2.3.6" - -internal-slot@^1.0.3: - version "1.0.3" - resolved "https://registry.yarnpkg.com/internal-slot/-/internal-slot-1.0.3.tgz#7347e307deeea2faac2ac6205d4bc7d34967f59c" - integrity sha512-O0DB1JC/sPyZl7cIo78n5dR7eUSwwpYPiXRhTzNxZVAMUuB8vlnRFyLxdrVToks6XPLVnFfbzaVd5WLjhgg+vA== - dependencies: - get-intrinsic "^1.1.0" - has "^1.0.3" - side-channel "^1.0.4" - -interpret@^0.6.4: - version "0.6.6" - resolved "https://registry.yarnpkg.com/interpret/-/interpret-0.6.6.tgz#fecd7a18e7ce5ca6abfb953e1f86213a49f1625b" - integrity sha1-/s16GOfOXKar+5U+H4YhOknxYls= - -interpret@^1.0.0, interpret@^1.4.0: - version "1.4.0" - resolved "https://registry.yarnpkg.com/interpret/-/interpret-1.4.0.tgz#665ab8bc4da27a774a40584e812e3e0fa45b1a1e" - integrity sha512-agE4QfB2Lkp9uICn7BAqoscw4SZP9kTE2hxiFI3jBPmXJfdqiahTbUuKGsMoN2GtqL9AxhYioAcVvgsb1HvRbA== - -invert-kv@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/invert-kv/-/invert-kv-1.0.0.tgz#104a8e4aaca6d3d8cd157a8ef8bfab2d7a3ffdb6" - integrity sha1-EEqOSqym09jNFXqO+L+rLXo//bY= - -ipaddr.js@1.9.1: - version "1.9.1" - resolved "https://registry.yarnpkg.com/ipaddr.js/-/ipaddr.js-1.9.1.tgz#bff38543eeb8984825079ff3a2a8e6cbd46781b3" - integrity sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g== - -is-absolute@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/is-absolute/-/is-absolute-1.0.0.tgz#395e1ae84b11f26ad1795e73c17378e48a301576" - integrity sha512-dOWoqflvcydARa360Gvv18DZ/gRuHKi2NU/wU5X1ZFzdYfH29nkiNZsF3mp4OJ3H4yo9Mx8A/uAGNzpzPN3yBA== - dependencies: - is-relative "^1.0.0" - is-windows "^1.0.1" - -is-accessor-descriptor@^0.1.6: - version "0.1.6" - resolved "https://registry.yarnpkg.com/is-accessor-descriptor/-/is-accessor-descriptor-0.1.6.tgz#a9e12cb3ae8d876727eeef3843f8a0897b5c98d6" - integrity sha1-qeEss66Nh2cn7u84Q/igiXtcmNY= - dependencies: - kind-of "^3.0.2" - -is-accessor-descriptor@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/is-accessor-descriptor/-/is-accessor-descriptor-1.0.0.tgz#169c2f6d3df1f992618072365c9b0ea1f6878656" - integrity sha512-m5hnHTkcVsPfqx3AKlyttIPb7J+XykHvJP2B9bZDjlhLIoEq4XoK64Vg7boZlVWYK6LUY94dYPEE7Lh0ZkZKcQ== - dependencies: - kind-of "^6.0.0" - -is-alphabetical@^1.0.0: - version "1.0.4" - resolved "https://registry.yarnpkg.com/is-alphabetical/-/is-alphabetical-1.0.4.tgz#9e7d6b94916be22153745d184c298cbf986a686d" - integrity sha512-DwzsA04LQ10FHTZuL0/grVDk4rFoVH1pjAToYwBrHSxcrBIGQuXrQMtD5U1b0U2XVgKZCTLLP8u2Qxqhy3l2Vg== - -is-alphanumerical@^1.0.0: - version "1.0.4" - resolved "https://registry.yarnpkg.com/is-alphanumerical/-/is-alphanumerical-1.0.4.tgz#7eb9a2431f855f6b1ef1a78e326df515696c4dbf" - integrity sha512-UzoZUr+XfVz3t3v4KyGEniVL9BDRoQtY7tOyrRybkVNjDFWyo1yhXNGrrBTQxp3ib9BLAWs7k2YKBQsFRkZG9A== - dependencies: - is-alphabetical "^1.0.0" - is-decimal "^1.0.0" - -is-arguments@^1.0.4, is-arguments@^1.1.0: - version "1.1.1" - resolved "https://registry.yarnpkg.com/is-arguments/-/is-arguments-1.1.1.tgz#15b3f88fda01f2a97fec84ca761a560f123efa9b" - integrity sha512-8Q7EARjzEnKpt/PCD7e1cgUS0a6X8u5tdSiMqXhojOdoV9TsMsiO+9VLC5vAmO8N7/GmXn7yjR8qnA6bVAEzfA== - dependencies: - call-bind "^1.0.2" - has-tostringtag "^1.0.0" - -is-arrayish@^0.2.1: - version "0.2.1" - resolved "https://registry.yarnpkg.com/is-arrayish/-/is-arrayish-0.2.1.tgz#77c99840527aa8ecb1a8ba697b80645a7a926a9d" - integrity sha1-d8mYQFJ6qOyxqLppe4BkWnqSap0= - -is-bigint@^1.0.1: - version "1.0.4" - resolved "https://registry.yarnpkg.com/is-bigint/-/is-bigint-1.0.4.tgz#08147a1875bc2b32005d41ccd8291dffc6691df3" - integrity sha512-zB9CruMamjym81i2JZ3UMn54PKGsQzsJeo6xvN3HJJ4CAsQNB6iRutp2To77OfCNuoxspsIhzaPoO1zyCEhFOg== - dependencies: - has-bigints "^1.0.1" - -is-binary-path@^1.0.0: - version "1.0.1" - resolved "https://registry.yarnpkg.com/is-binary-path/-/is-binary-path-1.0.1.tgz#75f16642b480f187a711c814161fd3a4a7655898" - integrity sha1-dfFmQrSA8YenEcgUFh/TpKdlWJg= - dependencies: - binary-extensions "^1.0.0" - -is-binary-path@~2.1.0: - version "2.1.0" - resolved "https://registry.yarnpkg.com/is-binary-path/-/is-binary-path-2.1.0.tgz#ea1f7f3b80f064236e83470f86c09c254fb45b09" - integrity sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw== - dependencies: - binary-extensions "^2.0.0" - -is-boolean-object@^1.1.0: - version "1.1.2" - resolved "https://registry.yarnpkg.com/is-boolean-object/-/is-boolean-object-1.1.2.tgz#5c6dc200246dd9321ae4b885a114bb1f75f63719" - integrity sha512-gDYaKHJmnj4aWxyj6YHyXVpdQawtVLHU5cb+eztPGczf6cjuTdwve5ZIEfgXqH4e57An1D1AKf8CZ3kYrQRqYA== - dependencies: - call-bind "^1.0.2" - has-tostringtag "^1.0.0" - -is-buffer@^1.1.5: - version "1.1.6" - resolved "https://registry.yarnpkg.com/is-buffer/-/is-buffer-1.1.6.tgz#efaa2ea9daa0d7ab2ea13a97b2b8ad51fefbe8be" - integrity sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w== - -is-buffer@^2.0.0: - version "2.0.5" - resolved "https://registry.yarnpkg.com/is-buffer/-/is-buffer-2.0.5.tgz#ebc252e400d22ff8d77fa09888821a24a658c191" - integrity sha512-i2R6zNFDwgEHJyQUtJEk0XFi1i0dPFn/oqjK3/vPCcDeJvW5NQ83V8QbicfF1SupOaB0h8ntgBC2YiE7dfyctQ== - -is-callable@^1.1.4, is-callable@^1.2.4: - version "1.2.4" - resolved "https://registry.yarnpkg.com/is-callable/-/is-callable-1.2.4.tgz#47301d58dd0259407865547853df6d61fe471945" - integrity sha512-nsuwtxZfMX67Oryl9LCQ+upnC0Z0BgpwntpS89m1H/TLF0zNfzfLMV/9Wa/6MZsj0acpEjAO0KF1xT6ZdLl95w== - -is-core-module@^2.2.0, is-core-module@^2.5.0, is-core-module@^2.6.0: - version "2.7.0" - resolved "https://registry.yarnpkg.com/is-core-module/-/is-core-module-2.7.0.tgz#3c0ef7d31b4acfc574f80c58409d568a836848e3" - integrity sha512-ByY+tjCciCr+9nLryBYcSD50EOGWt95c7tIsKTG1J2ixKKXPvF7Ej3AVd+UfDydAJom3biBGDBALaO79ktwgEQ== - dependencies: - has "^1.0.3" - -is-data-descriptor@^0.1.4: - version "0.1.4" - resolved "https://registry.yarnpkg.com/is-data-descriptor/-/is-data-descriptor-0.1.4.tgz#0b5ee648388e2c860282e793f1856fec3f301b56" - integrity sha1-C17mSDiOLIYCgueT8YVv7D8wG1Y= - dependencies: - kind-of "^3.0.2" - -is-data-descriptor@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/is-data-descriptor/-/is-data-descriptor-1.0.0.tgz#d84876321d0e7add03990406abbbbd36ba9268c7" - integrity sha512-jbRXy1FmtAoCjQkVmIVYwuuqDFUbaOeDjmed1tOGPrsMhtJA4rD9tkgA0F1qJ3gRFRXcHYVkdeaP50Q5rE/jLQ== - dependencies: - kind-of "^6.0.0" - -is-date-object@^1.0.1, is-date-object@^1.0.2: - version "1.0.5" - resolved "https://registry.yarnpkg.com/is-date-object/-/is-date-object-1.0.5.tgz#0841d5536e724c25597bf6ea62e1bd38298df31f" - integrity sha512-9YQaSxsAiSwcvS33MBk3wTCVnWK+HhF8VZR2jRxehM16QcVOdHqPn4VPHmRK4lSr38n9JriurInLcP90xsYNfQ== - dependencies: - has-tostringtag "^1.0.0" - -is-decimal@^1.0.0: - version "1.0.4" - resolved "https://registry.yarnpkg.com/is-decimal/-/is-decimal-1.0.4.tgz#65a3a5958a1c5b63a706e1b333d7cd9f630d3fa5" - integrity sha512-RGdriMmQQvZ2aqaQq3awNA6dCGtKpiDFcOzrTWrDAT2MiWrKQVPmxLGHl7Y2nNu6led0kEyoX0enY0qXYsv9zw== - -is-descriptor@^0.1.0: - version "0.1.6" - resolved "https://registry.yarnpkg.com/is-descriptor/-/is-descriptor-0.1.6.tgz#366d8240dde487ca51823b1ab9f07a10a78251ca" - integrity sha512-avDYr0SB3DwO9zsMov0gKCESFYqCnE4hq/4z3TdUlukEy5t9C0YRq7HLrsN52NAcqXKaepeCD0n+B0arnVG3Hg== - dependencies: - is-accessor-descriptor "^0.1.6" - is-data-descriptor "^0.1.4" - kind-of "^5.0.0" - -is-descriptor@^1.0.0, is-descriptor@^1.0.2: - version "1.0.2" - resolved "https://registry.yarnpkg.com/is-descriptor/-/is-descriptor-1.0.2.tgz#3b159746a66604b04f8c81524ba365c5f14d86ec" - integrity sha512-2eis5WqQGV7peooDyLmNEPUrps9+SXX5c9pL3xEB+4e9HnGuDa7mB7kHxHw4CbqS9k1T2hOH3miL8n8WtiYVtg== - dependencies: - is-accessor-descriptor "^1.0.0" - is-data-descriptor "^1.0.0" - kind-of "^6.0.2" - -is-docker@^2.0.0, is-docker@^2.2.1: - version "2.2.1" - resolved "https://registry.yarnpkg.com/is-docker/-/is-docker-2.2.1.tgz#33eeabe23cfe86f14bde4408a02c0cfb853acdaa" - integrity sha512-F+i2BKsFrH66iaUFc0woD8sLy8getkwTwtOBjvs56Cx4CgJDeKQeqfz8wAYiSb8JOprWhHH5p77PbmYCvvUuXQ== - -is-dotfile@^1.0.0: - version "1.0.3" - resolved "https://registry.yarnpkg.com/is-dotfile/-/is-dotfile-1.0.3.tgz#a6a2f32ffd2dfb04f5ca25ecd0f6b83cf798a1e1" - integrity sha1-pqLzL/0t+wT1yiXs0Pa4PPeYoeE= - -is-equal-shallow@^0.1.3: - version "0.1.3" - resolved "https://registry.yarnpkg.com/is-equal-shallow/-/is-equal-shallow-0.1.3.tgz#2238098fc221de0bcfa5d9eac4c45d638aa1c534" - integrity sha1-IjgJj8Ih3gvPpdnqxMRdY4qhxTQ= - dependencies: - is-primitive "^2.0.0" - -is-extendable@^0.1.0, is-extendable@^0.1.1: - version "0.1.1" - resolved "https://registry.yarnpkg.com/is-extendable/-/is-extendable-0.1.1.tgz#62b110e289a471418e3ec36a617d472e301dfc89" - integrity sha1-YrEQ4omkcUGOPsNqYX1HLjAd/Ik= - -is-extendable@^1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/is-extendable/-/is-extendable-1.0.1.tgz#a7470f9e426733d81bd81e1155264e3a3507cab4" - integrity sha512-arnXMxT1hhoKo9k1LZdmlNyJdDDfy2v0fXjFlmok4+i8ul/6WlbVge9bhM74OpNPQPMGUToDtz+KXa1PneJxOA== - dependencies: - is-plain-object "^2.0.4" - -is-extglob@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/is-extglob/-/is-extglob-1.0.0.tgz#ac468177c4943405a092fc8f29760c6ffc6206c0" - integrity sha1-rEaBd8SUNAWgkvyPKXYMb/xiBsA= - -is-extglob@^2.1.0, is-extglob@^2.1.1: - version "2.1.1" - resolved "https://registry.yarnpkg.com/is-extglob/-/is-extglob-2.1.1.tgz#a88c02535791f02ed37c76a1b9ea9773c833f8c2" - integrity sha1-qIwCU1eR8C7TfHahueqXc8gz+MI= - -is-finite@^1.0.0: - version "1.1.0" - resolved "https://registry.yarnpkg.com/is-finite/-/is-finite-1.1.0.tgz#904135c77fb42c0641d6aa1bcdbc4daa8da082f3" - integrity sha512-cdyMtqX/BOqqNBBiKlIVkytNHm49MtMlYyn1zxzvJKWmFMlGzm+ry5BBfYyeY9YmNKbRSo/o7OX9w9ale0wg3w== - -is-fullwidth-code-point@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/is-fullwidth-code-point/-/is-fullwidth-code-point-1.0.0.tgz#ef9e31386f031a7f0d643af82fde50c457ef00cb" - integrity sha1-754xOG8DGn8NZDr4L95QxFfvAMs= - dependencies: - number-is-nan "^1.0.0" - -is-fullwidth-code-point@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz#a3b30a5c4f199183167aaab93beefae3ddfb654f" - integrity sha1-o7MKXE8ZkYMWeqq5O+764937ZU8= - -is-fullwidth-code-point@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz#f116f8064fe90b3f7844a38997c0b75051269f1d" - integrity sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg== - -is-glob@^2.0.0, is-glob@^2.0.1: - version "2.0.1" - resolved "https://registry.yarnpkg.com/is-glob/-/is-glob-2.0.1.tgz#d096f926a3ded5600f3fdfd91198cb0888c2d863" - integrity sha1-0Jb5JqPe1WAPP9/ZEZjLCIjC2GM= - dependencies: - is-extglob "^1.0.0" - -is-glob@^3.1.0: - version "3.1.0" - resolved "https://registry.yarnpkg.com/is-glob/-/is-glob-3.1.0.tgz#7ba5ae24217804ac70707b96922567486cc3e84a" - integrity sha1-e6WuJCF4BKxwcHuWkiVnSGzD6Eo= - dependencies: - is-extglob "^2.1.0" - -is-glob@^4.0.0, is-glob@^4.0.1, is-glob@~4.0.1: - version "4.0.3" - resolved "https://registry.yarnpkg.com/is-glob/-/is-glob-4.0.3.tgz#64f61e42cbbb2eec2071a9dac0b28ba1e65d5084" - integrity sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg== - dependencies: - is-extglob "^2.1.1" - -is-hexadecimal@^1.0.0: - version "1.0.4" - resolved "https://registry.yarnpkg.com/is-hexadecimal/-/is-hexadecimal-1.0.4.tgz#cc35c97588da4bd49a8eedd6bc4082d44dcb23a7" - integrity sha512-gyPJuv83bHMpocVYoqof5VDiZveEoGoFL8m3BXNb2VW8Xs+rz9kqO8LOQ5DH6EsuvilT1ApazU0pyl+ytbPtlw== - -is-interactive@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/is-interactive/-/is-interactive-1.0.0.tgz#cea6e6ae5c870a7b0a0004070b7b587e0252912e" - integrity sha512-2HvIEKRoqS62guEC+qBjpvRubdX910WCMuJTZ+I9yvqKU2/12eSL549HMwtabb4oupdj2sMP50k+XJfB/8JE6w== - -is-map@^2.0.1, is-map@^2.0.2: - version "2.0.2" - resolved "https://registry.yarnpkg.com/is-map/-/is-map-2.0.2.tgz#00922db8c9bf73e81b7a335827bc2a43f2b91127" - integrity sha512-cOZFQQozTha1f4MxLFzlgKYPTyj26picdZTx82hbc/Xf4K/tZOOXSCkMvU4pKioRXGDLJRn0GM7Upe7kR721yg== - -is-negated-glob@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/is-negated-glob/-/is-negated-glob-1.0.0.tgz#6910bca5da8c95e784b5751b976cf5a10fee36d2" - integrity sha1-aRC8pdqMleeEtXUbl2z1oQ/uNtI= - -is-negative-zero@^2.0.1: - version "2.0.1" - resolved "https://registry.yarnpkg.com/is-negative-zero/-/is-negative-zero-2.0.1.tgz#3de746c18dda2319241a53675908d8f766f11c24" - integrity sha512-2z6JzQvZRa9A2Y7xC6dQQm4FSTSTNWjKIYYTt4246eMTJmIo0Q+ZyOsU66X8lxK1AbB92dFeglPLrhwpeRKO6w== - -is-number-object@^1.0.4: - version "1.0.6" - resolved "https://registry.yarnpkg.com/is-number-object/-/is-number-object-1.0.6.tgz#6a7aaf838c7f0686a50b4553f7e54a96494e89f0" - integrity sha512-bEVOqiRcvo3zO1+G2lVMy+gkkEm9Yh7cDMRusKKu5ZJKPUYSJwICTKZrNKHA2EbSP0Tu0+6B/emsYNHZyn6K8g== - dependencies: - has-tostringtag "^1.0.0" - -is-number@^2.1.0: - version "2.1.0" - resolved "https://registry.yarnpkg.com/is-number/-/is-number-2.1.0.tgz#01fcbbb393463a548f2f466cce16dece49db908f" - integrity sha1-Afy7s5NGOlSPL0ZszhbezknbkI8= - dependencies: - kind-of "^3.0.2" - -is-number@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/is-number/-/is-number-3.0.0.tgz#24fd6201a4782cf50561c810276afc7d12d71195" - integrity sha1-JP1iAaR4LPUFYcgQJ2r8fRLXEZU= - dependencies: - kind-of "^3.0.2" - -is-number@^4.0.0: - version "4.0.0" - resolved "https://registry.yarnpkg.com/is-number/-/is-number-4.0.0.tgz#0026e37f5454d73e356dfe6564699867c6a7f0ff" - integrity sha512-rSklcAIlf1OmFdyAqbnWTLVelsQ58uvZ66S/ZyawjWqIviTWCjg2PzVGw8WUA+nNuPTqb4wgA+NszrJ+08LlgQ== - -is-number@^7.0.0: - version "7.0.0" - resolved "https://registry.yarnpkg.com/is-number/-/is-number-7.0.0.tgz#7535345b896734d5f80c4d06c50955527a14f12b" - integrity sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng== - -is-obj@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/is-obj/-/is-obj-2.0.0.tgz#473fb05d973705e3fd9620545018ca8e22ef4982" - integrity sha512-drqDG3cbczxxEJRoOXcOjtdp1J/lyp1mNn0xaznRs8+muBhgQcrnbspox5X5fOw0HnMnbfDzvnEMEtqDEJEo8w== - -is-plain-obj@^1.1.0: - version "1.1.0" - resolved "https://registry.yarnpkg.com/is-plain-obj/-/is-plain-obj-1.1.0.tgz#71a50c8429dfca773c92a390a4a03b39fcd51d3e" - integrity sha1-caUMhCnfync8kqOQpKA7OfzVHT4= - -is-plain-obj@^2.0.0, is-plain-obj@^2.1.0: - version "2.1.0" - resolved "https://registry.yarnpkg.com/is-plain-obj/-/is-plain-obj-2.1.0.tgz#45e42e37fccf1f40da8e5f76ee21515840c09287" - integrity sha512-YWnfyRwxL/+SsrWYfOpUtz5b3YD+nyfkHvjbcanzk8zgyO4ASD67uVMRt8k5bM4lLMDnXfriRhOpemw+NfT1eA== - -is-plain-object@^2.0.1, is-plain-object@^2.0.3, is-plain-object@^2.0.4: - version "2.0.4" - resolved "https://registry.yarnpkg.com/is-plain-object/-/is-plain-object-2.0.4.tgz#2c163b3fafb1b606d9d17928f05c2a1c38e07677" - integrity sha512-h5PpgXkWitc38BBMYawTYMWJHFZJVnBquFE57xFpjB8pJFiF6gZ+bU+WyI/yqXiFR5mdLsgYNaPe8uao6Uv9Og== - dependencies: - isobject "^3.0.1" - -is-plain-object@^5.0.0: - version "5.0.0" - resolved "https://registry.yarnpkg.com/is-plain-object/-/is-plain-object-5.0.0.tgz#4427f50ab3429e9025ea7d52e9043a9ef4159344" - integrity sha512-VRSzKkbMm5jMDoKLbltAkFQ5Qr7VDiTFGXxYFXXowVj387GeGNOCsOH6Msy00SGZ3Fp84b1Naa1psqgcCIEP5Q== - -is-posix-bracket@^0.1.0: - version "0.1.1" - resolved "https://registry.yarnpkg.com/is-posix-bracket/-/is-posix-bracket-0.1.1.tgz#3334dc79774368e92f016e6fbc0a88f5cd6e6bc4" - integrity sha1-MzTceXdDaOkvAW5vvAqI9c1ua8Q= - -is-primitive@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/is-primitive/-/is-primitive-2.0.0.tgz#207bab91638499c07b2adf240a41a87210034575" - integrity sha1-IHurkWOEmcB7Kt8kCkGochADRXU= - -is-promise@^2.2.2: - version "2.2.2" - resolved "https://registry.yarnpkg.com/is-promise/-/is-promise-2.2.2.tgz#39ab959ccbf9a774cf079f7b40c7a26f763135f1" - integrity sha512-+lP4/6lKUBfQjZ2pdxThZvLUAafmZb8OAxFb8XXtiQmS35INgr85hdOGoEs124ez1FCnZJt6jau/T+alh58QFQ== - -is-regex@^1.1.1, is-regex@^1.1.4: - version "1.1.4" - resolved "https://registry.yarnpkg.com/is-regex/-/is-regex-1.1.4.tgz#eef5663cd59fa4c0ae339505323df6854bb15958" - integrity sha512-kvRdxDsxZjhzUX07ZnLydzS1TU/TJlTUHHY4YLL87e37oUA49DfkLqgy+VjFocowy29cKvcSiu+kIv728jTTVg== - dependencies: - call-bind "^1.0.2" - has-tostringtag "^1.0.0" - -is-relative@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/is-relative/-/is-relative-1.0.0.tgz#a1bb6935ce8c5dba1e8b9754b9b2dcc020e2260d" - integrity sha512-Kw/ReK0iqwKeu0MITLFuj0jbPAmEiOsIwyIXvvbfa6QfmN9pkD1M+8pdk7Rl/dTKbH34/XBFMbgD4iMJhLQbGA== - dependencies: - is-unc-path "^1.0.0" - -is-resolvable@^1.0.0: - version "1.1.0" - resolved "https://registry.yarnpkg.com/is-resolvable/-/is-resolvable-1.1.0.tgz#fb18f87ce1feb925169c9a407c19318a3206ed88" - integrity sha512-qgDYXFSR5WvEfuS5dMj6oTMEbrrSaM0CrFk2Yiq/gXnBvD9pMa2jGXxyhGLfvhZpuMZe18CJpFxAt3CRs42NMg== - -is-running@^2.1.0: - version "2.1.0" - resolved "https://registry.yarnpkg.com/is-running/-/is-running-2.1.0.tgz#30a73ff5cc3854e4fc25490809e9f5abf8de09e0" - integrity sha1-MKc/9cw4VOT8JUkICen1q/jeCeA= - -is-set@^2.0.1, is-set@^2.0.2: - version "2.0.2" - resolved "https://registry.yarnpkg.com/is-set/-/is-set-2.0.2.tgz#90755fa4c2562dc1c5d4024760d6119b94ca18ec" - integrity sha512-+2cnTEZeY5z/iXGbLhPrOAaK/Mau5k5eXq9j14CpRTftq0pAJu2MwVRSZhyZWBzx3o6X795Lz6Bpb6R0GKf37g== - -is-shared-array-buffer@^1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/is-shared-array-buffer/-/is-shared-array-buffer-1.0.1.tgz#97b0c85fbdacb59c9c446fe653b82cf2b5b7cfe6" - integrity sha512-IU0NmyknYZN0rChcKhRO1X8LYz5Isj/Fsqh8NJOSf+N/hCOTwy29F32Ik7a+QszE63IdvmwdTPDd6cZ5pg4cwA== - -is-ssh@^1.3.0: - version "1.3.3" - resolved "https://registry.yarnpkg.com/is-ssh/-/is-ssh-1.3.3.tgz#7f133285ccd7f2c2c7fc897b771b53d95a2b2c7e" - integrity sha512-NKzJmQzJfEEma3w5cJNcUMxoXfDjz0Zj0eyCalHn2E6VOwlzjZo0yuO2fcBSf8zhFuVCL/82/r5gRcoi6aEPVQ== - dependencies: - protocols "^1.1.0" - -is-stream@^1.1.0: - version "1.1.0" - resolved "https://registry.yarnpkg.com/is-stream/-/is-stream-1.1.0.tgz#12d4a3dd4e68e0b79ceb8dbc84173ae80d91ca44" - integrity sha1-EtSj3U5o4Lec6428hBc66A2RykQ= - -is-string@^1.0.5, is-string@^1.0.7: - version "1.0.7" - resolved "https://registry.yarnpkg.com/is-string/-/is-string-1.0.7.tgz#0dd12bf2006f255bb58f695110eff7491eebc0fd" - integrity sha512-tE2UXzivje6ofPW7l23cjDOMa09gb7xlAqG6jG5ej6uPV32TlWP3NKPigtaGeHNu9fohccRYvIiZMfOOnOYUtg== - dependencies: - has-tostringtag "^1.0.0" - -is-symbol@^1.0.2, is-symbol@^1.0.3: - version "1.0.4" - resolved "https://registry.yarnpkg.com/is-symbol/-/is-symbol-1.0.4.tgz#a6dac93b635b063ca6872236de88910a57af139c" - integrity sha512-C/CPBqKWnvdcxqIARxyOh4v1UUEOCHpgDa0WYgpKDFMszcrPcffg5uhwSgPCLD2WWxmq6isisz87tzT01tuGhg== - dependencies: - has-symbols "^1.0.2" - -is-text-path@^1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/is-text-path/-/is-text-path-1.0.1.tgz#4e1aa0fb51bfbcb3e92688001397202c1775b66e" - integrity sha1-Thqg+1G/vLPpJogAE5cgLBd1tm4= - dependencies: - text-extensions "^1.0.0" - -is-typed-array@^1.1.7: - version "1.1.8" - resolved "https://registry.yarnpkg.com/is-typed-array/-/is-typed-array-1.1.8.tgz#cbaa6585dc7db43318bc5b89523ea384a6f65e79" - integrity sha512-HqH41TNZq2fgtGT8WHVFVJhBVGuY3AnP3Q36K8JKXUxSxRgk/d+7NjmwG2vo2mYmXK8UYZKu0qH8bVP5gEisjA== - dependencies: - available-typed-arrays "^1.0.5" - call-bind "^1.0.2" - es-abstract "^1.18.5" - foreach "^2.0.5" - has-tostringtag "^1.0.0" - -is-typedarray@~1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/is-typedarray/-/is-typedarray-1.0.0.tgz#e479c80858df0c1b11ddda6940f96011fcda4a9a" - integrity sha1-5HnICFjfDBsR3dppQPlgEfzaSpo= - -is-unc-path@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/is-unc-path/-/is-unc-path-1.0.0.tgz#d731e8898ed090a12c352ad2eaed5095ad322c9d" - integrity sha512-mrGpVd0fs7WWLfVsStvgF6iEJnbjDFZh9/emhRDcGWTduTfNHd9CHeUwH3gYIjdbwo4On6hunkztwOaAw0yllQ== - dependencies: - unc-path-regex "^0.1.2" - -is-unicode-supported@^0.1.0: - version "0.1.0" - resolved "https://registry.yarnpkg.com/is-unicode-supported/-/is-unicode-supported-0.1.0.tgz#3f26c76a809593b52bfa2ecb5710ed2779b522a7" - integrity sha512-knxG2q4UC3u8stRGyAVJCOdxFmv5DZiRcdlIaAQXAbSfJya+OhopNotLQrstBhququ4ZpuKbDc/8S6mgXgPFPw== - -is-utf8@^0.2.0, is-utf8@^0.2.1: - version "0.2.1" - resolved "https://registry.yarnpkg.com/is-utf8/-/is-utf8-0.2.1.tgz#4b0da1442104d1b336340e80797e865cf39f7d72" - integrity sha1-Sw2hRCEE0bM2NA6AeX6GXPOffXI= - -is-valid-glob@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/is-valid-glob/-/is-valid-glob-1.0.0.tgz#29bf3eff701be2d4d315dbacc39bc39fe8f601aa" - integrity sha1-Kb8+/3Ab4tTTFdusw5vDn+j2Aao= - -is-weakmap@^2.0.1: - version "2.0.1" - resolved "https://registry.yarnpkg.com/is-weakmap/-/is-weakmap-2.0.1.tgz#5008b59bdc43b698201d18f62b37b2ca243e8cf2" - integrity sha512-NSBR4kH5oVj1Uwvv970ruUkCV7O1mzgVFO4/rev2cLRda9Tm9HrL70ZPut4rOHgY0FNrUu9BCbXA2sdQ+x0chA== - -is-weakref@^1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/is-weakref/-/is-weakref-1.0.1.tgz#842dba4ec17fa9ac9850df2d6efbc1737274f2a2" - integrity sha512-b2jKc2pQZjaeFYWEf7ScFj+Be1I+PXmlu572Q8coTXZ+LD/QQZ7ShPMst8h16riVgyXTQwUsFEl74mDvc/3MHQ== - dependencies: - call-bind "^1.0.0" - -is-weakset@^2.0.1: - version "2.0.1" - resolved "https://registry.yarnpkg.com/is-weakset/-/is-weakset-2.0.1.tgz#e9a0af88dbd751589f5e50d80f4c98b780884f83" - integrity sha512-pi4vhbhVHGLxohUw7PhGsueT4vRGFoXhP7+RGN0jKIv9+8PWYCQTqtADngrxOm2g46hoH0+g8uZZBzMrvVGDmw== - -is-windows@^1.0.1, is-windows@^1.0.2: - version "1.0.2" - resolved "https://registry.yarnpkg.com/is-windows/-/is-windows-1.0.2.tgz#d1850eb9791ecd18e6182ce12a30f396634bb19d" - integrity sha512-eXK1UInq2bPmjyX6e3VHIzMLobc4J94i4AWn+Hpq3OU5KkrRC96OAcR3PRJ/pGu6m8TRnBHP9dkXQVsT/COVIA== - -is-wsl@^1.1.0: - version "1.1.0" - resolved "https://registry.yarnpkg.com/is-wsl/-/is-wsl-1.1.0.tgz#1f16e4aa22b04d1336b66188a66af3c600c3a66d" - integrity sha1-HxbkqiKwTRM2tmGIpmrzxgDDpm0= - -is-wsl@^2.2.0: - version "2.2.0" - resolved "https://registry.yarnpkg.com/is-wsl/-/is-wsl-2.2.0.tgz#74a4c76e77ca9fd3f932f290c17ea326cd157271" - integrity sha512-fKzAra0rGJUUBwGBgNkHZuToZcn+TtXHpeCgmkMJMMYx1sQDYaCSyjJBSCa2nH1DGm7s3n1oBnohoVTBaN7Lww== - dependencies: - is-docker "^2.0.0" - -isarray@0.0.1: - version "0.0.1" - resolved "https://registry.yarnpkg.com/isarray/-/isarray-0.0.1.tgz#8a18acfca9a8f4177e09abfc6038939b05d1eedf" - integrity sha1-ihis/Kmo9Bd+Cav8YDiTmwXR7t8= - -isarray@1.0.0, isarray@^1.0.0, isarray@~1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/isarray/-/isarray-1.0.0.tgz#bb935d48582cba168c06834957a54a3e07124f11" - integrity sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE= - -isarray@^2.0.5: - version "2.0.5" - resolved "https://registry.yarnpkg.com/isarray/-/isarray-2.0.5.tgz#8af1e4c1221244cc62459faf38940d4e644a5723" - integrity sha512-xHjhDr3cNBK0BzdUJSPXZntQUx/mwMS5Rw4A7lPJ90XGAO6ISP/ePDNuo0vhqOZU+UD5JoodwCAAoZQd3FeAKw== - -isbinaryfile@^4.0.8: - version "4.0.8" - resolved "https://registry.yarnpkg.com/isbinaryfile/-/isbinaryfile-4.0.8.tgz#5d34b94865bd4946633ecc78a026fc76c5b11fcf" - integrity sha512-53h6XFniq77YdW+spoRrebh0mnmTxRPTlcuIArO57lmMdq4uBKFKaeTjnb92oYWrSn/LVL+LT+Hap2tFQj8V+w== - -isexe@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/isexe/-/isexe-2.0.0.tgz#e8fbf374dc556ff8947a10dcb0572d633f2cfa10" - integrity sha1-6PvzdNxVb/iUehDcsFctYz8s+hA= - -isobject@^2.0.0: - version "2.1.0" - resolved "https://registry.yarnpkg.com/isobject/-/isobject-2.1.0.tgz#f065561096a3f1da2ef46272f815c840d87e0c89" - integrity sha1-8GVWEJaj8dou9GJy+BXIQNh+DIk= - dependencies: - isarray "1.0.0" - -isobject@^3.0.0, isobject@^3.0.1: - version "3.0.1" - resolved "https://registry.yarnpkg.com/isobject/-/isobject-3.0.1.tgz#4e431e92b11a9731636aa1f9c8d1ccbcfdab78df" - integrity sha1-TkMekrEalzFjaqH5yNHMvP2reN8= - -isstream@~0.1.2: - version "0.1.2" - resolved "https://registry.yarnpkg.com/isstream/-/isstream-0.1.2.tgz#47e63f7af55afa6f92e1500e690eb8b8529c099a" - integrity sha1-R+Y/evVa+m+S4VAOaQ64uFKcCZo= - -istanbul-lib-coverage@^2.0.5: - version "2.0.5" - resolved "https://registry.yarnpkg.com/istanbul-lib-coverage/-/istanbul-lib-coverage-2.0.5.tgz#675f0ab69503fad4b1d849f736baaca803344f49" - integrity sha512-8aXznuEPCJvGnMSRft4udDRDtb1V3pkQkMMI5LI+6HuQz5oQ4J2UFn1H82raA3qJtyOLkkwVqICBQkjnGtn5mA== - -istanbul-lib-coverage@^3.0.0: - version "3.0.1" - resolved "https://registry.yarnpkg.com/istanbul-lib-coverage/-/istanbul-lib-coverage-3.0.1.tgz#e8900b3ed6069759229cf30f7067388d148aeb5e" - integrity sha512-GvCYYTxaCPqwMjobtVcVKvSHtAGe48MNhGjpK8LtVF8K0ISX7hCKl85LgtuaSneWVyQmaGcW3iXVV3GaZSLpmQ== - -istanbul-lib-instrument@^4.0.1, istanbul-lib-instrument@^4.0.3: - version "4.0.3" - resolved "https://registry.yarnpkg.com/istanbul-lib-instrument/-/istanbul-lib-instrument-4.0.3.tgz#873c6fff897450118222774696a3f28902d77c1d" - integrity sha512-BXgQl9kf4WTCPCCpmFGoJkz/+uhvm7h7PFKUYxh7qarQd3ER33vHG//qaE8eN25l07YqZPpHXU9I09l/RD5aGQ== - dependencies: - "@babel/core" "^7.7.5" - "@istanbuljs/schema" "^0.1.2" - istanbul-lib-coverage "^3.0.0" - semver "^6.3.0" - -istanbul-lib-report@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/istanbul-lib-report/-/istanbul-lib-report-3.0.0.tgz#7518fe52ea44de372f460a76b5ecda9ffb73d8a6" - integrity sha512-wcdi+uAKzfiGT2abPpKZ0hSU1rGQjUQnLvtY5MpQ7QCTahD3VODhcu4wcfY1YtkGaDD5yuydOLINXsfbus9ROw== - dependencies: - istanbul-lib-coverage "^3.0.0" - make-dir "^3.0.0" - supports-color "^7.1.0" - -istanbul-lib-source-maps@^3.0.6: - version "3.0.6" - resolved "https://registry.yarnpkg.com/istanbul-lib-source-maps/-/istanbul-lib-source-maps-3.0.6.tgz#284997c48211752ec486253da97e3879defba8c8" - integrity sha512-R47KzMtDJH6X4/YW9XTx+jrLnZnscW4VpNN+1PViSYTejLVPWv7oov+Duf8YQSPyVRUvueQqz1TcsC6mooZTXw== - dependencies: - debug "^4.1.1" - istanbul-lib-coverage "^2.0.5" - make-dir "^2.1.0" - rimraf "^2.6.3" - source-map "^0.6.1" - -istanbul-lib-source-maps@^4.0.0: - version "4.0.0" - resolved "https://registry.yarnpkg.com/istanbul-lib-source-maps/-/istanbul-lib-source-maps-4.0.0.tgz#75743ce6d96bb86dc7ee4352cf6366a23f0b1ad9" - integrity sha512-c16LpFRkR8vQXyHZ5nLpY35JZtzj1PQY1iZmesUbf1FZHbIupcWfjgOXBY9YHkLEQ6puz1u4Dgj6qmU/DisrZg== - dependencies: - debug "^4.1.1" - istanbul-lib-coverage "^3.0.0" - source-map "^0.6.1" - -istanbul-reports@^3.0.0, istanbul-reports@^3.0.2: - version "3.0.2" - resolved "https://registry.yarnpkg.com/istanbul-reports/-/istanbul-reports-3.0.2.tgz#d593210e5000683750cb09fc0644e4b6e27fd53b" - integrity sha512-9tZvz7AiR3PEDNGiV9vIouQ/EAcqMXFmkcA1CDFTwOB98OZVDL0PH9glHotf5Ugp6GCOTypfzGWI/OqjWNCRUw== - dependencies: - html-escaper "^2.0.0" - istanbul-lib-report "^3.0.0" - -istanbul@^0.4.5: - version "0.4.5" - resolved "https://registry.yarnpkg.com/istanbul/-/istanbul-0.4.5.tgz#65c7d73d4c4da84d4f3ac310b918fb0b8033733b" - integrity sha1-ZcfXPUxNqE1POsMQuRj7C4Azczs= - dependencies: - abbrev "1.0.x" - async "1.x" - escodegen "1.8.x" - esprima "2.7.x" - glob "^5.0.15" - handlebars "^4.0.1" - js-yaml "3.x" - mkdirp "0.5.x" - nopt "3.x" - once "1.x" - resolve "1.1.x" - supports-color "^3.1.0" - which "^1.1.1" - wordwrap "^1.0.0" - -istextorbinary@^3.0.0: - version "3.3.0" - resolved "https://registry.yarnpkg.com/istextorbinary/-/istextorbinary-3.3.0.tgz#06b1c57d948da11461bd237c00ce09e9902964f2" - integrity sha512-Tvq1W6NAcZeJ8op+Hq7tdZ434rqnMx4CCZ7H0ff83uEloDvVbqAwaMTZcafKGJT0VHkYzuXUiCY4hlXQg6WfoQ== - dependencies: - binaryextensions "^2.2.0" - textextensions "^3.2.0" - -jake@^10.6.1: - version "10.8.2" - resolved "https://registry.yarnpkg.com/jake/-/jake-10.8.2.tgz#ebc9de8558160a66d82d0eadc6a2e58fbc500a7b" - integrity sha512-eLpKyrfG3mzvGE2Du8VoPbeSkRry093+tyNjdYaBbJS9v17knImYGNXQCUV0gLxQtF82m3E8iRb/wdSQZLoq7A== - dependencies: - async "0.9.x" - chalk "^2.4.2" - filelist "^1.0.1" - minimatch "^3.0.4" - -jest-diff@^27.2.4: - version "27.2.4" - resolved "https://registry.yarnpkg.com/jest-diff/-/jest-diff-27.2.4.tgz#171c51d3d2c105c457100fee6e7bf7cee51c8d8c" - integrity sha512-bLAVlDSCR3gqUPGv+4nzVpEXGsHh98HjUL7Vb2hVyyuBDoQmja8eJb0imUABsuxBeUVmf47taJSAd9nDrwWKEg== - dependencies: - chalk "^4.0.0" - diff-sequences "^27.0.6" - jest-get-type "^27.0.6" - pretty-format "^27.2.4" - -jest-get-type@^27.0.6: - version "27.0.6" - resolved "https://registry.yarnpkg.com/jest-get-type/-/jest-get-type-27.0.6.tgz#0eb5c7f755854279ce9b68a9f1a4122f69047cfe" - integrity sha512-XTkK5exIeUbbveehcSR8w0bhH+c0yloW/Wpl+9vZrjzztCPWrxhHwkIFpZzCt71oRBsgxmuUfxEqOYoZI2macg== - -jest-matcher-utils@^27.0.2, jest-matcher-utils@^27.2.4: - version "27.2.4" - resolved "https://registry.yarnpkg.com/jest-matcher-utils/-/jest-matcher-utils-27.2.4.tgz#008fff018151415ad1b6cfc083fd70fe1e012525" - integrity sha512-nQeLfFAIPPkyhkDfifAPfP/U5wm1x0fLtAzqXZSSKckXDNuk2aaOfQiDYv1Mgf5GY6yOsxfUnvNm3dDjXM+BXw== - dependencies: - chalk "^4.0.0" - jest-diff "^27.2.4" - jest-get-type "^27.0.6" - pretty-format "^27.2.4" - -jest-message-util@^27.2.4: - version "27.2.4" - resolved "https://registry.yarnpkg.com/jest-message-util/-/jest-message-util-27.2.4.tgz#667e8c0f2b973156d1bac7398a7f677705cafaca" - integrity sha512-wbKT/BNGnBVB9nzi+IoaLkXt6fbSvqUxx+IYY66YFh96J3goY33BAaNG3uPqaw/Sh/FR9YpXGVDfd5DJdbh4nA== - dependencies: - "@babel/code-frame" "^7.12.13" - "@jest/types" "^27.2.4" - "@types/stack-utils" "^2.0.0" - chalk "^4.0.0" - graceful-fs "^4.2.4" - micromatch "^4.0.4" - pretty-format "^27.2.4" - slash "^3.0.0" - stack-utils "^2.0.3" - -jest-regex-util@^27.0.6: - version "27.0.6" - resolved "https://registry.yarnpkg.com/jest-regex-util/-/jest-regex-util-27.0.6.tgz#02e112082935ae949ce5d13b2675db3d8c87d9c5" - integrity sha512-SUhPzBsGa1IKm8hx2F4NfTGGp+r7BXJ4CulsZ1k2kI+mGLG+lxGrs76veN2LF/aUdGosJBzKgXmNCw+BzFqBDQ== - -js-tokens@^3.0.2: - version "3.0.2" - resolved "https://registry.yarnpkg.com/js-tokens/-/js-tokens-3.0.2.tgz#9866df395102130e38f7f996bceb65443209c25b" - integrity sha1-mGbfOVECEw449/mWvOtlRDIJwls= - -js-tokens@^4.0.0: - version "4.0.0" - resolved "https://registry.yarnpkg.com/js-tokens/-/js-tokens-4.0.0.tgz#19203fb59991df98e3a287050d4647cdeaf32499" - integrity sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ== - -js-yaml@3.x, js-yaml@^3.10.0, js-yaml@^3.13.1, js-yaml@^3.9.1: - version "3.14.1" - resolved "https://registry.yarnpkg.com/js-yaml/-/js-yaml-3.14.1.tgz#dae812fdb3825fa306609a8717383c50c36a0537" - integrity sha512-okMH7OXXJ7YrN9Ok3/SXrnu4iX9yOk+25nqX4imS2npuvTYDmo/QEZoqwZkYaIDk3jVvBOTOIEgEhaLOynBS9g== - dependencies: - argparse "^1.0.7" - esprima "^4.0.0" - -js-yaml@4.1.0: - version "4.1.0" - resolved "https://registry.yarnpkg.com/js-yaml/-/js-yaml-4.1.0.tgz#c1fb65f8f5017901cdd2c951864ba18458a10602" - integrity sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA== - dependencies: - argparse "^2.0.1" - -jsbn@~0.1.0: - version "0.1.1" - resolved "https://registry.yarnpkg.com/jsbn/-/jsbn-0.1.1.tgz#a5e654c2e5a2deb5f201d96cefbca80c0ef2f513" - integrity sha1-peZUwuWi3rXyAdls77yoDA7y9RM= - -jsesc@^2.5.1: - version "2.5.2" - resolved "https://registry.yarnpkg.com/jsesc/-/jsesc-2.5.2.tgz#80564d2e483dacf6e8ef209650a67df3f0c283a4" - integrity sha512-OYu7XEzjkCQ3C5Ps3QIZsQfNpqoJyZZA99wd9aWd05NCtC5pWOkShK2mkL6HXQR6/Cy2lbNdPlZBpuQHXE63gA== - -jsesc@~0.5.0: - version "0.5.0" - resolved "https://registry.yarnpkg.com/jsesc/-/jsesc-0.5.0.tgz#e7dee66e35d6fc16f710fe91d5cf69f70f08911d" - integrity sha1-597mbjXW/Bb3EP6R1c9p9w8IkR0= - -json-buffer@3.0.1: - version "3.0.1" - resolved "https://registry.yarnpkg.com/json-buffer/-/json-buffer-3.0.1.tgz#9338802a30d3b6605fbe0613e094008ca8c05a13" - integrity sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ== - -json-loader@^0.5.4: - version "0.5.7" - resolved "https://registry.yarnpkg.com/json-loader/-/json-loader-0.5.7.tgz#dca14a70235ff82f0ac9a3abeb60d337a365185d" - integrity sha512-QLPs8Dj7lnf3e3QYS1zkCo+4ZwqOiF9d/nZnYozTISxXWCfNs9yuky5rJw4/W34s7POaNlbZmQGaB5NiXCbP4w== - -json-parse-better-errors@^1.0.1: - version "1.0.2" - resolved "https://registry.yarnpkg.com/json-parse-better-errors/-/json-parse-better-errors-1.0.2.tgz#bb867cfb3450e69107c131d1c514bab3dc8bcaa9" - integrity sha512-mrqyZKfX5EhL7hvqcV6WG1yYjnjeuYDzDhhcAAUrq8Po85NBQBJP+ZDUT75qZQ98IkUoBqdkExkukOU7Ts2wrw== - -json-parse-even-better-errors@^2.3.0: - version "2.3.1" - resolved "https://registry.yarnpkg.com/json-parse-even-better-errors/-/json-parse-even-better-errors-2.3.1.tgz#7c47805a94319928e05777405dc12e1f7a4ee02d" - integrity sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w== - -json-schema-traverse@^0.3.0: - version "0.3.1" - resolved "https://registry.yarnpkg.com/json-schema-traverse/-/json-schema-traverse-0.3.1.tgz#349a6d44c53a51de89b40805c5d5e59b417d3340" - integrity sha1-NJptRMU6Ud6JtAgFxdXlm0F9M0A= - -json-schema-traverse@^0.4.1: - version "0.4.1" - resolved "https://registry.yarnpkg.com/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz#69f6a87d9513ab8bb8fe63bdb0979c448e684660" - integrity sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg== - -json-schema-traverse@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz#ae7bcb3656ab77a73ba5c49bf654f38e6b6860e2" - integrity sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug== - -json-schema@0.2.3: - version "0.2.3" - resolved "https://registry.yarnpkg.com/json-schema/-/json-schema-0.2.3.tgz#b480c892e59a2f05954ce727bd3f2a4e882f9e13" - integrity sha1-tIDIkuWaLwWVTOcnvT8qTogvnhM= - -json-stable-stringify-without-jsonify@^1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz#9db7b59496ad3f3cfef30a75142d2d930ad72651" - integrity sha1-nbe1lJatPzz+8wp1FC0tkwrXJlE= - -json-stringify-safe@^5.0.1, json-stringify-safe@~5.0.1: - version "5.0.1" - resolved "https://registry.yarnpkg.com/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz#1296a2d58fd45f19a0f6ce01d65701e2c735b6eb" - integrity sha1-Epai1Y/UXxmg9s4B1lcB4sc1tus= - -json5@^0.5.0, json5@^0.5.1: - version "0.5.1" - resolved "https://registry.yarnpkg.com/json5/-/json5-0.5.1.tgz#1eade7acc012034ad84e2396767ead9fa5495821" - integrity sha1-Hq3nrMASA0rYTiOWdn6tn6VJWCE= - -json5@^1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/json5/-/json5-1.0.1.tgz#779fb0018604fa854eacbf6252180d83543e3dbe" - integrity sha512-aKS4WQjPenRxiQsC93MNfjx+nbF4PAdYzmd/1JIj8HYzqfbu86beTuNgXDzPknWk0n0uARlyewZo4s++ES36Ow== - dependencies: - minimist "^1.2.0" - -json5@^2.1.2: - version "2.2.0" - resolved "https://registry.yarnpkg.com/json5/-/json5-2.2.0.tgz#2dfefe720c6ba525d9ebd909950f0515316c89a3" - integrity sha512-f+8cldu7X/y7RAJurMEJmdoKXGB/X550w2Nr3tTbezL6RwEE/iMcm+tZnXeoZtKuOq6ft8+CqzEkrIgx1fPoQA== - dependencies: - minimist "^1.2.5" - -jsonfile@^4.0.0: - version "4.0.0" - resolved "https://registry.yarnpkg.com/jsonfile/-/jsonfile-4.0.0.tgz#8771aae0799b64076b76640fca058f9c10e33ecb" - integrity sha1-h3Gq4HmbZAdrdmQPygWPnBDjPss= - optionalDependencies: - graceful-fs "^4.1.6" - -jsonfile@^6.0.1: - version "6.1.0" - resolved "https://registry.yarnpkg.com/jsonfile/-/jsonfile-6.1.0.tgz#bc55b2634793c679ec6403094eb13698a6ec0aae" - integrity sha512-5dgndWOriYSm5cnYaJNhalLNDKOqFwyDB/rr1E9ZsGciGvKPs8R2xYGCacuf3z6K1YKDz182fd+fY3cn3pMqXQ== - dependencies: - universalify "^2.0.0" - optionalDependencies: - graceful-fs "^4.1.6" - -jsonfile@~1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/jsonfile/-/jsonfile-1.0.1.tgz#ea5efe40b83690b98667614a7392fc60e842c0dd" - integrity sha1-6l7+QLg2kLmGZ2FKc5L8YOhCwN0= - -jsonparse@^1.2.0: - version "1.3.1" - resolved "https://registry.yarnpkg.com/jsonparse/-/jsonparse-1.3.1.tgz#3f4dae4a91fac315f71062f8521cc239f1366280" - integrity sha1-P02uSpH6wxX3EGL4UhzCOfE2YoA= - -jsprim@^1.2.2: - version "1.4.1" - resolved "https://registry.yarnpkg.com/jsprim/-/jsprim-1.4.1.tgz#313e66bc1e5cc06e438bc1b7499c2e5c56acb6a2" - integrity sha1-MT5mvB5cwG5Di8G3SZwuXFastqI= - dependencies: - assert-plus "1.0.0" - extsprintf "1.3.0" - json-schema "0.2.3" - verror "1.10.0" - -just-clone@^1.0.2: - version "1.0.2" - resolved "https://registry.yarnpkg.com/just-clone/-/just-clone-1.0.2.tgz#bfb3faef65aa12a316058712945c326fd8f01434" - integrity sha1-v7P672WqEqMWBYcSlFwyb9jwFDQ= - -just-debounce@^1.0.0: - version "1.1.0" - resolved "https://registry.yarnpkg.com/just-debounce/-/just-debounce-1.1.0.tgz#2f81a3ad4121a76bc7cb45dbf704c0d76a8e5ddf" - integrity sha512-qpcRocdkUmf+UTNBYx5w6dexX5J31AKK1OmPwH630a83DdVVUIngk55RSAiIGpQyoH0dlr872VHfPjnQnK1qDQ== - -just-extend@^4.0.2: - version "4.2.1" - resolved "https://registry.yarnpkg.com/just-extend/-/just-extend-4.2.1.tgz#ef5e589afb61e5d66b24eca749409a8939a8c744" - integrity sha512-g3UB796vUFIY90VIv/WX3L2c8CS2MdWUww3CNrYmqza1Fg0DURc2K/O4YrnklBdQarSJ/y8JnJYDGc+1iumQjg== - -karma-babel-preprocessor@^8.0.1: - version "8.0.1" - resolved "https://registry.yarnpkg.com/karma-babel-preprocessor/-/karma-babel-preprocessor-8.0.1.tgz#63b33cc7b5b3b5c7815ec453f7df58fb88345af1" - integrity sha512-5upyawNi3c7Gg6tPH1FWRVTmUijGf3v1GV4ScLM/2jKdDP18SlaKlUpu8eJrRI3STO8qK1bkqFcdgAA364nLYQ== - -karma-browserstack-launcher@1.4.0: - version "1.4.0" - resolved "https://registry.yarnpkg.com/karma-browserstack-launcher/-/karma-browserstack-launcher-1.4.0.tgz#22f92e969d2db6cfc00e578708bda39378d5f2ab" - integrity sha512-bUQK84U+euDfOUfEjcF4IareySMOBNRLrrl9q6cttIe8f011Ir6olLITTYMOJDcGY58wiFIdhPHSPd9Pi6+NfQ== - dependencies: - browserstack "~1.5.1" - browserstacktunnel-wrapper "~2.0.2" - q "~1.5.0" - -karma-chai@^0.1.0: - version "0.1.0" - resolved "https://registry.yarnpkg.com/karma-chai/-/karma-chai-0.1.0.tgz#bee5ad40400517811ae34bb945f762909108b79a" - integrity sha1-vuWtQEAFF4Ea40u5RfdikJEIt5o= - -karma-chrome-launcher@^3.1.0: - version "3.1.0" - resolved "https://registry.yarnpkg.com/karma-chrome-launcher/-/karma-chrome-launcher-3.1.0.tgz#805a586799a4d05f4e54f72a204979f3f3066738" - integrity sha512-3dPs/n7vgz1rxxtynpzZTvb9y/GIaW8xjAwcIGttLbycqoFtI7yo1NGnQi6oFTherRE+GIhCAHZC4vEqWGhNvg== - dependencies: - which "^1.2.1" - -karma-coverage-istanbul-reporter@^3.0.3: - version "3.0.3" - resolved "https://registry.yarnpkg.com/karma-coverage-istanbul-reporter/-/karma-coverage-istanbul-reporter-3.0.3.tgz#f3b5303553aadc8e681d40d360dfdc19bc7e9fe9" - integrity sha512-wE4VFhG/QZv2Y4CdAYWDbMmcAHeS926ZIji4z+FkB2aF/EposRb6DP6G5ncT/wXhqUfAb/d7kZrNKPonbvsATw== - dependencies: - istanbul-lib-coverage "^3.0.0" - istanbul-lib-report "^3.0.0" - istanbul-lib-source-maps "^3.0.6" - istanbul-reports "^3.0.2" - minimatch "^3.0.4" - -karma-coverage@^2.0.1: - version "2.0.3" - resolved "https://registry.yarnpkg.com/karma-coverage/-/karma-coverage-2.0.3.tgz#c10f4711f4cf5caaaa668b1d6f642e7da122d973" - integrity sha512-atDvLQqvPcLxhED0cmXYdsPMCQuh6Asa9FMZW1bhNqlVEhJoB9qyZ2BY1gu7D/rr5GLGb5QzYO4siQskxaWP/g== - dependencies: - istanbul-lib-coverage "^3.0.0" - istanbul-lib-instrument "^4.0.1" - istanbul-lib-report "^3.0.0" - istanbul-lib-source-maps "^4.0.0" - istanbul-reports "^3.0.0" - minimatch "^3.0.4" - -karma-es5-shim@^0.0.4: - version "0.0.4" - resolved "https://registry.yarnpkg.com/karma-es5-shim/-/karma-es5-shim-0.0.4.tgz#cdd00333cce77c2e4ce03e3ac93f2f8ecd1fb952" - integrity sha1-zdADM8znfC5M4D46yT8vjs0fuVI= - dependencies: - es5-shim "^4.0.5" - -karma-firefox-launcher@^2.1.0: - version "2.1.1" - resolved "https://registry.yarnpkg.com/karma-firefox-launcher/-/karma-firefox-launcher-2.1.1.tgz#6457226f8e4f091b664cef79bb5d39bf1e008765" - integrity sha512-VzDMgPseXak9DtfyE1O5bB2BwsMy1zzO1kUxVW1rP0yhC4tDNJ0p3JoFdzvrK4QqVzdqUMa9Rx9YzkdFp8hz3Q== - dependencies: - is-wsl "^2.2.0" - which "^2.0.1" - -karma-ie-launcher@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/karma-ie-launcher/-/karma-ie-launcher-1.0.0.tgz#497986842c490190346cd89f5494ca9830c6d59c" - integrity sha1-SXmGhCxJAZA0bNifVJTKmDDG1Zw= - dependencies: - lodash "^4.6.1" - -karma-mocha-reporter@^2.2.5: - version "2.2.5" - resolved "https://registry.yarnpkg.com/karma-mocha-reporter/-/karma-mocha-reporter-2.2.5.tgz#15120095e8ed819186e47a0b012f3cd741895560" - integrity sha1-FRIAlejtgZGG5HoLAS8810GJVWA= - dependencies: - chalk "^2.1.0" - log-symbols "^2.1.0" - strip-ansi "^4.0.0" - -karma-mocha@^2.0.1: - version "2.0.1" - resolved "https://registry.yarnpkg.com/karma-mocha/-/karma-mocha-2.0.1.tgz#4b0254a18dfee71bdbe6188d9a6861bf86b0cd7d" - integrity sha512-Tzd5HBjm8his2OA4bouAsATYEpZrp9vC7z5E5j4C5Of5Rrs1jY67RAwXNcVmd/Bnk1wgvQRou0zGVLey44G4tQ== - dependencies: - minimist "^1.2.3" - -karma-opera-launcher@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/karma-opera-launcher/-/karma-opera-launcher-1.0.0.tgz#fa51628531a1d0be84b2d8dc0d7ee209fc8ff91a" - integrity sha1-+lFihTGh0L6EstjcDX7iCfyP+Ro= - -karma-safari-launcher@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/karma-safari-launcher/-/karma-safari-launcher-1.0.0.tgz#96982a2cc47d066aae71c553babb28319115a2ce" - integrity sha1-lpgqLMR9BmquccVTursoMZEVos4= - -karma-script-launcher@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/karma-script-launcher/-/karma-script-launcher-1.0.0.tgz#cd017c4de5ef09e5a9da793276176108dd4b542d" - integrity sha1-zQF8TeXvCeWp2nkydhdhCN1LVC0= - -karma-sinon@^1.0.5: - version "1.0.5" - resolved "https://registry.yarnpkg.com/karma-sinon/-/karma-sinon-1.0.5.tgz#4e3443f2830fdecff624d3747163f1217daa2a9a" - integrity sha1-TjRD8oMP3s/2JNN0cWPxIX2qKpo= - -karma-sourcemap-loader@^0.3.7: - version "0.3.8" - resolved "https://registry.yarnpkg.com/karma-sourcemap-loader/-/karma-sourcemap-loader-0.3.8.tgz#d4bae72fb7a8397328a62b75013d2df937bdcf9c" - integrity sha512-zorxyAakYZuBcHRJE+vbrK2o2JXLFWK8VVjiT/6P+ltLBUGUvqTEkUiQ119MGdOrK7mrmxXHZF1/pfT6GgIZ6g== - dependencies: - graceful-fs "^4.1.2" - -karma-spec-reporter@^0.0.32: - version "0.0.32" - resolved "https://registry.yarnpkg.com/karma-spec-reporter/-/karma-spec-reporter-0.0.32.tgz#2e9c7207ea726771260259f82becb543209e440a" - integrity sha1-LpxyB+pyZ3EmAln4K+y1QyCeRAo= - dependencies: - colors "^1.1.2" - -karma-webpack@^3.0.5: - version "3.0.5" - resolved "https://registry.yarnpkg.com/karma-webpack/-/karma-webpack-3.0.5.tgz#1ff1e3a690fb73ae95ee95f9ab58f341cfc7b40f" - integrity sha512-nRudGJWstvVuA6Tbju9tyGUfXTtI1UXMXoRHVmM2/78D0q6s/Ye2IC157PKNDC15PWFGR0mVIRtWLAdcfsRJoA== - dependencies: - async "^2.0.0" - babel-runtime "^6.0.0" - loader-utils "^1.0.0" - lodash "^4.0.0" - source-map "^0.5.6" - webpack-dev-middleware "^2.0.6" - -karma@^6.3.2: - version "6.3.4" - resolved "https://registry.yarnpkg.com/karma/-/karma-6.3.4.tgz#359899d3aab3d6b918ea0f57046fd2a6b68565e6" - integrity sha512-hbhRogUYIulfkBTZT7xoPrCYhRBnBoqbbL4fszWD0ReFGUxU+LYBr3dwKdAluaDQ/ynT9/7C+Lf7pPNW4gSx4Q== - dependencies: - body-parser "^1.19.0" - braces "^3.0.2" - chokidar "^3.5.1" - colors "^1.4.0" - connect "^3.7.0" - di "^0.0.1" - dom-serialize "^2.2.1" - glob "^7.1.7" - graceful-fs "^4.2.6" - http-proxy "^1.18.1" - isbinaryfile "^4.0.8" - lodash "^4.17.21" - log4js "^6.3.0" - mime "^2.5.2" - minimatch "^3.0.4" - qjobs "^1.2.0" - range-parser "^1.2.1" - rimraf "^3.0.2" - socket.io "^3.1.0" - source-map "^0.6.1" - tmp "^0.2.1" - ua-parser-js "^0.7.28" - yargs "^16.1.1" - -keyv@^4.0.0: - version "4.0.3" - resolved "https://registry.yarnpkg.com/keyv/-/keyv-4.0.3.tgz#4f3aa98de254803cafcd2896734108daa35e4254" - integrity sha512-zdGa2TOpSZPq5mU6iowDARnMBZgtCqJ11dJROFi6tg6kTn4nuUdU09lFyLFSaHrWqpIJ+EBq4E8/Dc0Vx5vLdA== - dependencies: - json-buffer "3.0.1" - -kind-of@^3.0.2, kind-of@^3.0.3, kind-of@^3.2.0: - version "3.2.2" - resolved "https://registry.yarnpkg.com/kind-of/-/kind-of-3.2.2.tgz#31ea21a734bab9bbb0f32466d893aea51e4a3c64" - integrity sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ= - dependencies: - is-buffer "^1.1.5" - -kind-of@^4.0.0: - version "4.0.0" - resolved "https://registry.yarnpkg.com/kind-of/-/kind-of-4.0.0.tgz#20813df3d712928b207378691a45066fae72dd57" - integrity sha1-IIE989cSkosgc3hpGkUGb65y3Vc= - dependencies: - is-buffer "^1.1.5" - -kind-of@^5.0.0, kind-of@^5.0.2: - version "5.1.0" - resolved "https://registry.yarnpkg.com/kind-of/-/kind-of-5.1.0.tgz#729c91e2d857b7a419a1f9aa65685c4c33f5845d" - integrity sha512-NGEErnH6F2vUuXDh+OlbcKW7/wOcfdRHaZ7VWtqCztfHri/++YKmP51OdWeGPuqCOba6kk2OTe5d02VmTB80Pw== - -kind-of@^6.0.0, kind-of@^6.0.2, kind-of@^6.0.3: - version "6.0.3" - resolved "https://registry.yarnpkg.com/kind-of/-/kind-of-6.0.3.tgz#07c05034a6c349fa06e24fa35aa76db4580ce4dd" - integrity sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw== - -konan@^2.1.1: - version "2.1.1" - resolved "https://registry.yarnpkg.com/konan/-/konan-2.1.1.tgz#eea88f05c56249b78903b952b953393900346dd1" - integrity sha512-7ZhYV84UzJ0PR/RJnnsMZcAbn+kLasJhVNWsu8ZyVEJYRpGA5XESQ9d/7zOa08U0Ou4cmB++hMNY/3OSV9KIbg== - dependencies: - "@babel/parser" "^7.10.5" - "@babel/traverse" "^7.10.5" - -ky@^0.28.5: - version "0.28.5" - resolved "https://registry.yarnpkg.com/ky/-/ky-0.28.5.tgz#4b7ada24fb0440c3898406f3a4986abe60ba213e" - integrity sha512-O5gg9kF4MeyfSw+YkgPAafOPwEUU6xcdGEJKUJmKpIPbLzk3oxUtY4OdBNekG7mawofzkyZ/ZHuR9ev5uZZdAA== - -last-run@^1.1.0: - version "1.1.1" - resolved "https://registry.yarnpkg.com/last-run/-/last-run-1.1.1.tgz#45b96942c17b1c79c772198259ba943bebf8ca5b" - integrity sha1-RblpQsF7HHnHchmCWbqUO+v4yls= - dependencies: - default-resolution "^2.0.0" - es6-weak-map "^2.0.1" - -lazy-cache@^1.0.3: - version "1.0.4" - resolved "https://registry.yarnpkg.com/lazy-cache/-/lazy-cache-1.0.4.tgz#a1d78fc3a50474cb80845d3b3b6e1da49a446e8e" - integrity sha1-odePw6UEdMuAhF07O24dpJpEbo4= - -lazystream@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/lazystream/-/lazystream-1.0.0.tgz#f6995fe0f820392f61396be89462407bb77168e4" - integrity sha1-9plf4PggOS9hOWvolGJAe7dxaOQ= - dependencies: - readable-stream "^2.0.5" - -lcid@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/lcid/-/lcid-1.0.0.tgz#308accafa0bc483a3867b4b6f2b9506251d1b835" - integrity sha1-MIrMr6C8SDo4Z7S28rlQYlHRuDU= - dependencies: - invert-kv "^1.0.0" - -lcov-parse@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/lcov-parse/-/lcov-parse-1.0.0.tgz#eb0d46b54111ebc561acb4c408ef9363bdc8f7e0" - integrity sha1-6w1GtUER68VhrLTECO+TY73I9+A= - -lead@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/lead/-/lead-1.0.0.tgz#6f14f99a37be3a9dd784f5495690e5903466ee42" - integrity sha1-bxT5mje+Op3XhPVJVpDlkDRm7kI= - dependencies: - flush-write-stream "^1.0.2" - -levn@^0.3.0, levn@~0.3.0: - version "0.3.0" - resolved "https://registry.yarnpkg.com/levn/-/levn-0.3.0.tgz#3b09924edf9f083c0490fdd4c0bc4421e04764ee" - integrity sha1-OwmSTt+fCDwEkP3UwLxEIeBHZO4= - dependencies: - prelude-ls "~1.1.2" - type-check "~0.3.2" - -levn@^0.4.1: - version "0.4.1" - resolved "https://registry.yarnpkg.com/levn/-/levn-0.4.1.tgz#ae4562c007473b932a6200d403268dd2fffc6ade" - integrity sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ== - dependencies: - prelude-ls "^1.2.1" - type-check "~0.4.0" - -liftoff@^3.1.0: - version "3.1.0" - resolved "https://registry.yarnpkg.com/liftoff/-/liftoff-3.1.0.tgz#c9ba6081f908670607ee79062d700df062c52ed3" - integrity sha512-DlIPlJUkCV0Ips2zf2pJP0unEoT1kwYhiiPUGF3s/jtxTCjziNLoiVVh+jqWOWeFi6mmwQ5fNxvAUyPad4Dfog== - dependencies: - extend "^3.0.0" - findup-sync "^3.0.0" - fined "^1.0.1" - flagged-respawn "^1.0.0" - is-plain-object "^2.0.4" - object.map "^1.0.0" - rechoir "^0.6.2" - resolve "^1.1.7" - -lighthouse-logger@^1.0.0: - version "1.3.0" - resolved "https://registry.yarnpkg.com/lighthouse-logger/-/lighthouse-logger-1.3.0.tgz#ba6303e739307c4eee18f08249524e7dafd510db" - integrity sha512-BbqAKApLb9ywUli+0a+PcV04SyJ/N1q/8qgCNe6U97KbPCS1BTksEuHFLYdvc8DltuhfxIUBqDZsC0bBGtl3lA== - dependencies: - debug "^2.6.9" - marky "^1.2.2" - -lines-and-columns@^1.1.6: - version "1.1.6" - resolved "https://registry.yarnpkg.com/lines-and-columns/-/lines-and-columns-1.1.6.tgz#1c00c743b433cd0a4e80758f7b64a57440d9ff00" - integrity sha1-HADHQ7QzzQpOgHWPe2SldEDZ/wA= - -listenercount@~1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/listenercount/-/listenercount-1.0.1.tgz#84c8a72ab59c4725321480c975e6508342e70937" - integrity sha1-hMinKrWcRyUyFIDJdeZQg0LnCTc= - -live-connect-js@2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/live-connect-js/-/live-connect-js-2.0.0.tgz#1ad26e04ddece44e7cf7d902ab8003f6bc0b10f4" - integrity sha512-Xhrj1JU5LoLjJuujjTlvDfc/n3Shzk2hPlYmLdCx/lsltFFVuCFa9uM8u5mcHlmOUKP5pu9I54bAITxZBMHoXg== - dependencies: - tiny-hashes "1.0.1" - -livereload-js@^2.3.0: - version "2.4.0" - resolved "https://registry.yarnpkg.com/livereload-js/-/livereload-js-2.4.0.tgz#447c31cf1ea9ab52fc20db615c5ddf678f78009c" - integrity sha512-XPQH8Z2GDP/Hwz2PCDrh2mth4yFejwA1OZ/81Ti3LgKyhDcEjsSsqFWZojHG0va/duGd+WyosY7eXLDoOyqcPw== - -load-json-file@^1.0.0: - version "1.1.0" - resolved "https://registry.yarnpkg.com/load-json-file/-/load-json-file-1.1.0.tgz#956905708d58b4bab4c2261b04f59f31c99374c0" - integrity sha1-lWkFcI1YtLq0wiYbBPWfMcmTdMA= - dependencies: - graceful-fs "^4.1.2" - parse-json "^2.2.0" - pify "^2.0.0" - pinkie-promise "^2.0.0" - strip-bom "^2.0.0" - -load-json-file@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/load-json-file/-/load-json-file-2.0.0.tgz#7947e42149af80d696cbf797bcaabcfe1fe29ca8" - integrity sha1-eUfkIUmvgNaWy/eXvKq8/h/inKg= - dependencies: - graceful-fs "^4.1.2" - parse-json "^2.2.0" - pify "^2.0.0" - strip-bom "^3.0.0" - -load-json-file@^4.0.0: - version "4.0.0" - resolved "https://registry.yarnpkg.com/load-json-file/-/load-json-file-4.0.0.tgz#2f5f45ab91e33216234fd53adab668eb4ec0993b" - integrity sha1-L19Fq5HjMhYjT9U62rZo607AmTs= - dependencies: - graceful-fs "^4.1.2" - parse-json "^4.0.0" - pify "^3.0.0" - strip-bom "^3.0.0" - -loader-runner@^2.3.0: - version "2.4.0" - resolved "https://registry.yarnpkg.com/loader-runner/-/loader-runner-2.4.0.tgz#ed47066bfe534d7e84c4c7b9998c2a75607d9357" - integrity sha512-Jsmr89RcXGIwivFY21FcRrisYZfvLMTWx5kOLc+JTxtpBOG6xML0vzbc6SEQG2FO9/4Fc3wW4LVcB5DmGflaRw== - -loader-utils@^0.2.11: - version "0.2.17" - resolved "https://registry.yarnpkg.com/loader-utils/-/loader-utils-0.2.17.tgz#f86e6374d43205a6e6c60e9196f17c0299bfb348" - integrity sha1-+G5jdNQyBabmxg6RlvF8Apm/s0g= - dependencies: - big.js "^3.1.3" - emojis-list "^2.0.0" - json5 "^0.5.0" - object-assign "^4.0.1" - -loader-utils@^1.0.0, loader-utils@^1.1.0, loader-utils@^1.4.0: - version "1.4.0" - resolved "https://registry.yarnpkg.com/loader-utils/-/loader-utils-1.4.0.tgz#c579b5e34cb34b1a74edc6c1fb36bfa371d5a613" - integrity sha512-qH0WSMBtn/oHuwjy/NucEgbx5dbxxnxup9s4PVXJUDHZBQY+s0NWA9rJf53RBnQZxfch7euUui7hpoAPvALZdA== - dependencies: - big.js "^5.2.2" - emojis-list "^3.0.0" - json5 "^1.0.1" - -loader-utils@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/loader-utils/-/loader-utils-2.0.0.tgz#e4cace5b816d425a166b5f097e10cd12b36064b0" - integrity sha512-rP4F0h2RaWSvPEkD7BLDFQnvSf+nK+wr3ESUjNTyAGobqrijmW92zc+SO6d4p4B1wh7+B/Jg1mkQe5NYUEHtHQ== - dependencies: - big.js "^5.2.2" - emojis-list "^3.0.0" - json5 "^2.1.2" - -locate-path@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/locate-path/-/locate-path-2.0.0.tgz#2b568b265eec944c6d9c0de9c3dbbbca0354cd8e" - integrity sha1-K1aLJl7slExtnA3pw9u7ygNUzY4= - dependencies: - p-locate "^2.0.0" - path-exists "^3.0.0" - -locate-path@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/locate-path/-/locate-path-3.0.0.tgz#dbec3b3ab759758071b58fe59fc41871af21400e" - integrity sha512-7AO748wWnIhNqAuaty2ZWHkQHRSNfPVIsPIfwEOWO22AmaoVrWavlOcMR5nzTLNYvp36X220/maaRsrec1G65A== - dependencies: - p-locate "^3.0.0" - path-exists "^3.0.0" - -locate-path@^5.0.0: - version "5.0.0" - resolved "https://registry.yarnpkg.com/locate-path/-/locate-path-5.0.0.tgz#1afba396afd676a6d42504d0a67a3a7eb9f62aa0" - integrity sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g== - dependencies: - p-locate "^4.1.0" - -locate-path@^6.0.0: - version "6.0.0" - resolved "https://registry.yarnpkg.com/locate-path/-/locate-path-6.0.0.tgz#55321eb309febbc59c4801d931a72452a681d286" - integrity sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw== - dependencies: - p-locate "^5.0.0" - -lodash._basecopy@^3.0.0: - version "3.0.1" - resolved "https://registry.yarnpkg.com/lodash._basecopy/-/lodash._basecopy-3.0.1.tgz#8da0e6a876cf344c0ad8a54882111dd3c5c7ca36" - integrity sha1-jaDmqHbPNEwK2KVIghEd08XHyjY= - -lodash._basetostring@^3.0.0: - version "3.0.1" - resolved "https://registry.yarnpkg.com/lodash._basetostring/-/lodash._basetostring-3.0.1.tgz#d1861d877f824a52f669832dcaf3ee15566a07d5" - integrity sha1-0YYdh3+CSlL2aYMtyvPuFVZqB9U= - -lodash._basevalues@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/lodash._basevalues/-/lodash._basevalues-3.0.0.tgz#5b775762802bde3d3297503e26300820fdf661b7" - integrity sha1-W3dXYoAr3j0yl1A+JjAIIP32Ybc= - -lodash._escapehtmlchar@~2.4.1: - version "2.4.1" - resolved "https://registry.yarnpkg.com/lodash._escapehtmlchar/-/lodash._escapehtmlchar-2.4.1.tgz#df67c3bb6b7e8e1e831ab48bfa0795b92afe899d" - integrity sha1-32fDu2t+jh6DGrSL+geVuSr+iZ0= - dependencies: - lodash._htmlescapes "~2.4.1" - -lodash._escapestringchar@~2.4.1: - version "2.4.1" - resolved "https://registry.yarnpkg.com/lodash._escapestringchar/-/lodash._escapestringchar-2.4.1.tgz#ecfe22618a2ade50bfeea43937e51df66f0edb72" - integrity sha1-7P4iYYoq3lC/7qQ5N+Ud9m8O23I= - -lodash._getnative@^3.0.0: - version "3.9.1" - resolved "https://registry.yarnpkg.com/lodash._getnative/-/lodash._getnative-3.9.1.tgz#570bc7dede46d61cdcde687d65d3eecbaa3aaff5" - integrity sha1-VwvH3t5G1hzc3mh9ZdPuy6o6r/U= - -lodash._htmlescapes@~2.4.1: - version "2.4.1" - resolved "https://registry.yarnpkg.com/lodash._htmlescapes/-/lodash._htmlescapes-2.4.1.tgz#32d14bf0844b6de6f8b62a051b4f67c228b624cb" - integrity sha1-MtFL8IRLbeb4tioFG09nwii2JMs= - -lodash._isiterateecall@^3.0.0: - version "3.0.9" - resolved "https://registry.yarnpkg.com/lodash._isiterateecall/-/lodash._isiterateecall-3.0.9.tgz#5203ad7ba425fae842460e696db9cf3e6aac057c" - integrity sha1-UgOte6Ql+uhCRg5pbbnPPmqsBXw= - -lodash._isnative@~2.4.1: - version "2.4.1" - resolved "https://registry.yarnpkg.com/lodash._isnative/-/lodash._isnative-2.4.1.tgz#3ea6404b784a7be836c7b57580e1cdf79b14832c" - integrity sha1-PqZAS3hKe+g2x7V1gOHN95sUgyw= - -lodash._objecttypes@~2.4.1: - version "2.4.1" - resolved "https://registry.yarnpkg.com/lodash._objecttypes/-/lodash._objecttypes-2.4.1.tgz#7c0b7f69d98a1f76529f890b0cdb1b4dfec11c11" - integrity sha1-fAt/admKH3ZSn4kLDNsbTf7BHBE= - -lodash._reescape@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/lodash._reescape/-/lodash._reescape-3.0.0.tgz#2b1d6f5dfe07c8a355753e5f27fac7f1cde1616a" - integrity sha1-Kx1vXf4HyKNVdT5fJ/rH8c3hYWo= - -lodash._reevaluate@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/lodash._reevaluate/-/lodash._reevaluate-3.0.0.tgz#58bc74c40664953ae0b124d806996daca431e2ed" - integrity sha1-WLx0xAZklTrgsSTYBpltrKQx4u0= - -lodash._reinterpolate@^2.4.1, lodash._reinterpolate@~2.4.1: - version "2.4.1" - resolved "https://registry.yarnpkg.com/lodash._reinterpolate/-/lodash._reinterpolate-2.4.1.tgz#4f1227aa5a8711fc632f5b07a1f4607aab8b3222" - integrity sha1-TxInqlqHEfxjL1sHofRgequLMiI= - -lodash._reinterpolate@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/lodash._reinterpolate/-/lodash._reinterpolate-3.0.0.tgz#0ccf2d89166af03b3663c796538b75ac6e114d9d" - integrity sha1-DM8tiRZq8Ds2Y8eWU4t1rG4RTZ0= - -lodash._reunescapedhtml@~2.4.1: - version "2.4.1" - resolved "https://registry.yarnpkg.com/lodash._reunescapedhtml/-/lodash._reunescapedhtml-2.4.1.tgz#747c4fc40103eb3bb8a0976e571f7a2659e93ba7" - integrity sha1-dHxPxAED6zu4oJduVx96JlnpO6c= - dependencies: - lodash._htmlescapes "~2.4.1" - lodash.keys "~2.4.1" - -lodash._root@^3.0.0: - version "3.0.1" - resolved "https://registry.yarnpkg.com/lodash._root/-/lodash._root-3.0.1.tgz#fba1c4524c19ee9a5f8136b4609f017cf4ded692" - integrity sha1-+6HEUkwZ7ppfgTa0YJ8BfPTe1pI= - -lodash._shimkeys@~2.4.1: - version "2.4.1" - resolved "https://registry.yarnpkg.com/lodash._shimkeys/-/lodash._shimkeys-2.4.1.tgz#6e9cc9666ff081f0b5a6c978b83e242e6949d203" - integrity sha1-bpzJZm/wgfC1psl4uD4kLmlJ0gM= - dependencies: - lodash._objecttypes "~2.4.1" - -lodash.clone@^4.3.2: - version "4.5.0" - resolved "https://registry.yarnpkg.com/lodash.clone/-/lodash.clone-4.5.0.tgz#195870450f5a13192478df4bc3d23d2dea1907b6" - integrity sha1-GVhwRQ9aExkkeN9Lw9I9LeoZB7Y= - -lodash.clonedeep@^4.5.0: - version "4.5.0" - resolved "https://registry.yarnpkg.com/lodash.clonedeep/-/lodash.clonedeep-4.5.0.tgz#e23f3f9c4f8fbdde872529c1071857a086e5ccef" - integrity sha1-4j8/nE+Pvd6HJSnBBxhXoIblzO8= - -lodash.debounce@^4.0.8: - version "4.0.8" - resolved "https://registry.yarnpkg.com/lodash.debounce/-/lodash.debounce-4.0.8.tgz#82d79bff30a67c4005ffd5e2515300ad9ca4d7af" - integrity sha1-gteb/zCmfEAF/9XiUVMArZyk168= - -lodash.defaults@^4.2.0: - version "4.2.0" - resolved "https://registry.yarnpkg.com/lodash.defaults/-/lodash.defaults-4.2.0.tgz#d09178716ffea4dde9e5fb7b37f6f0802274580c" - integrity sha1-0JF4cW/+pN3p5ft7N/bwgCJ0WAw= - -lodash.defaults@~2.4.1: - version "2.4.1" - resolved "https://registry.yarnpkg.com/lodash.defaults/-/lodash.defaults-2.4.1.tgz#a7e8885f05e68851144b6e12a8f3678026bc4c54" - integrity sha1-p+iIXwXmiFEUS24SqPNngCa8TFQ= - dependencies: - lodash._objecttypes "~2.4.1" - lodash.keys "~2.4.1" - -lodash.difference@^4.5.0: - version "4.5.0" - resolved "https://registry.yarnpkg.com/lodash.difference/-/lodash.difference-4.5.0.tgz#9ccb4e505d486b91651345772885a2df27fd017c" - integrity sha1-nMtOUF1Ia5FlE0V3KIWi3yf9AXw= - -lodash.escape@^3.0.0: - version "3.2.0" - resolved "https://registry.yarnpkg.com/lodash.escape/-/lodash.escape-3.2.0.tgz#995ee0dc18c1b48cc92effae71a10aab5b487698" - integrity sha1-mV7g3BjBtIzJLv+ucaEKq1tIdpg= - dependencies: - lodash._root "^3.0.0" - -lodash.escape@~2.4.1: - version "2.4.1" - resolved "https://registry.yarnpkg.com/lodash.escape/-/lodash.escape-2.4.1.tgz#2ce12c5e084db0a57dda5e5d1eeeb9f5d175a3b4" - integrity sha1-LOEsXghNsKV92l5dHu659dF1o7Q= - dependencies: - lodash._escapehtmlchar "~2.4.1" - lodash._reunescapedhtml "~2.4.1" - lodash.keys "~2.4.1" - -lodash.flatten@^4.4.0: - version "4.4.0" - resolved "https://registry.yarnpkg.com/lodash.flatten/-/lodash.flatten-4.4.0.tgz#f31c22225a9632d2bbf8e4addbef240aa765a61f" - integrity sha1-8xwiIlqWMtK7+OSt2+8kCqdlph8= - -lodash.flattendeep@^4.4.0: - version "4.4.0" - resolved "https://registry.yarnpkg.com/lodash.flattendeep/-/lodash.flattendeep-4.4.0.tgz#fb030917f86a3134e5bc9bec0d69e0013ddfedb2" - integrity sha1-+wMJF/hqMTTlvJvsDWngAT3f7bI= - -lodash.get@^4.4.2: - version "4.4.2" - resolved "https://registry.yarnpkg.com/lodash.get/-/lodash.get-4.4.2.tgz#2d177f652fa31e939b4438d5341499dfa3825e99" - integrity sha1-LRd/ZS+jHpObRDjVNBSZ36OCXpk= - -lodash.isarguments@^3.0.0: - version "3.1.0" - resolved "https://registry.yarnpkg.com/lodash.isarguments/-/lodash.isarguments-3.1.0.tgz#2f573d85c6a24289ff00663b491c1d338ff3458a" - integrity sha1-L1c9hcaiQon/AGY7SRwdM4/zRYo= - -lodash.isarray@^3.0.0: - version "3.0.4" - resolved "https://registry.yarnpkg.com/lodash.isarray/-/lodash.isarray-3.0.4.tgz#79e4eb88c36a8122af86f844aa9bcd851b5fbb55" - integrity sha1-eeTriMNqgSKvhvhEqpvNhRtfu1U= - -lodash.ismatch@^4.4.0: - version "4.4.0" - resolved "https://registry.yarnpkg.com/lodash.ismatch/-/lodash.ismatch-4.4.0.tgz#756cb5150ca3ba6f11085a78849645f188f85f37" - integrity sha1-dWy1FQyjum8RCFp4hJZF8Yj4Xzc= - -lodash.isobject@^3.0.2: - version "3.0.2" - resolved "https://registry.yarnpkg.com/lodash.isobject/-/lodash.isobject-3.0.2.tgz#3c8fb8d5b5bf4bf90ae06e14f2a530a4ed935e1d" - integrity sha1-PI+41bW/S/kK4G4U8qUwpO2TXh0= - -lodash.isobject@~2.4.1: - version "2.4.1" - resolved "https://registry.yarnpkg.com/lodash.isobject/-/lodash.isobject-2.4.1.tgz#5a2e47fe69953f1ee631a7eba1fe64d2d06558f5" - integrity sha1-Wi5H/mmVPx7mMafrof5k0tBlWPU= - dependencies: - lodash._objecttypes "~2.4.1" - -lodash.isplainobject@^4.0.6: - version "4.0.6" - resolved "https://registry.yarnpkg.com/lodash.isplainobject/-/lodash.isplainobject-4.0.6.tgz#7c526a52d89b45c45cc690b88163be0497f550cb" - integrity sha1-fFJqUtibRcRcxpC4gWO+BJf1UMs= - -lodash.keys@^3.0.0: - version "3.1.2" - resolved "https://registry.yarnpkg.com/lodash.keys/-/lodash.keys-3.1.2.tgz#4dbc0472b156be50a0b286855d1bd0b0c656098a" - integrity sha1-TbwEcrFWvlCgsoaFXRvQsMZWCYo= - dependencies: - lodash._getnative "^3.0.0" - lodash.isarguments "^3.0.0" - lodash.isarray "^3.0.0" - -lodash.keys@~2.4.1: - version "2.4.1" - resolved "https://registry.yarnpkg.com/lodash.keys/-/lodash.keys-2.4.1.tgz#48dea46df8ff7632b10d706b8acb26591e2b3727" - integrity sha1-SN6kbfj/djKxDXBrissmWR4rNyc= - dependencies: - lodash._isnative "~2.4.1" - lodash._shimkeys "~2.4.1" - lodash.isobject "~2.4.1" - -lodash.merge@^4.6.1, lodash.merge@^4.6.2: - version "4.6.2" - resolved "https://registry.yarnpkg.com/lodash.merge/-/lodash.merge-4.6.2.tgz#558aa53b43b661e1925a0afdfa36a9a1085fe57a" - integrity sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ== - -lodash.pickby@^4.6.0: - version "4.6.0" - resolved "https://registry.yarnpkg.com/lodash.pickby/-/lodash.pickby-4.6.0.tgz#7dea21d8c18d7703a27c704c15d3b84a67e33aff" - integrity sha1-feoh2MGNdwOifHBMFdO4SmfjOv8= - -lodash.restparam@^3.0.0: - version "3.6.1" - resolved "https://registry.yarnpkg.com/lodash.restparam/-/lodash.restparam-3.6.1.tgz#936a4e309ef330a7645ed4145986c85ae5b20805" - integrity sha1-k2pOMJ7zMKdkXtQUWYbIWuWyCAU= - -lodash.some@^4.2.2: - version "4.6.0" - resolved "https://registry.yarnpkg.com/lodash.some/-/lodash.some-4.6.0.tgz#1bb9f314ef6b8baded13b549169b2a945eb68e4d" - integrity sha1-G7nzFO9ri63tE7VJFpsqlF62jk0= - -lodash.template@^2.4.1: - version "2.4.1" - resolved "https://registry.yarnpkg.com/lodash.template/-/lodash.template-2.4.1.tgz#9e611007edf629129a974ab3c48b817b3e1cf20d" - integrity sha1-nmEQB+32KRKal0qzxIuBez4c8g0= - dependencies: - lodash._escapestringchar "~2.4.1" - lodash._reinterpolate "~2.4.1" - lodash.defaults "~2.4.1" - lodash.escape "~2.4.1" - lodash.keys "~2.4.1" - lodash.templatesettings "~2.4.1" - lodash.values "~2.4.1" - -lodash.template@^3.0.0, lodash.template@^3.6.2: - version "3.6.2" - resolved "https://registry.yarnpkg.com/lodash.template/-/lodash.template-3.6.2.tgz#f8cdecc6169a255be9098ae8b0c53d378931d14f" - integrity sha1-+M3sxhaaJVvpCYrosMU9N4kx0U8= - dependencies: - lodash._basecopy "^3.0.0" - lodash._basetostring "^3.0.0" - lodash._basevalues "^3.0.0" - lodash._isiterateecall "^3.0.0" - lodash._reinterpolate "^3.0.0" - lodash.escape "^3.0.0" - lodash.keys "^3.0.0" - lodash.restparam "^3.0.0" - lodash.templatesettings "^3.0.0" - -lodash.template@^4.5.0: - version "4.5.0" - resolved "https://registry.yarnpkg.com/lodash.template/-/lodash.template-4.5.0.tgz#f976195cf3f347d0d5f52483569fe8031ccce8ab" - integrity sha512-84vYFxIkmidUiFxidA/KjjH9pAycqW+h980j7Fuz5qxRtO9pgB7MDFTdys1N7A5mcucRiDyEq4fusljItR1T/A== - dependencies: - lodash._reinterpolate "^3.0.0" - lodash.templatesettings "^4.0.0" - -lodash.templatesettings@^3.0.0: - version "3.1.1" - resolved "https://registry.yarnpkg.com/lodash.templatesettings/-/lodash.templatesettings-3.1.1.tgz#fb307844753b66b9f1afa54e262c745307dba8e5" - integrity sha1-+zB4RHU7Zrnxr6VOJix0UwfbqOU= - dependencies: - lodash._reinterpolate "^3.0.0" - lodash.escape "^3.0.0" - -lodash.templatesettings@^4.0.0: - version "4.2.0" - resolved "https://registry.yarnpkg.com/lodash.templatesettings/-/lodash.templatesettings-4.2.0.tgz#e481310f049d3cf6d47e912ad09313b154f0fb33" - integrity sha512-stgLz+i3Aa9mZgnjr/O+v9ruKZsPsndy7qPZOchbqk2cnTU1ZaldKK+v7m54WoKIyxiuMZTKT2H81F8BeAc3ZQ== - dependencies: - lodash._reinterpolate "^3.0.0" - -lodash.templatesettings@~2.4.1: - version "2.4.1" - resolved "https://registry.yarnpkg.com/lodash.templatesettings/-/lodash.templatesettings-2.4.1.tgz#ea76c75d11eb86d4dbe89a83893bb861929ac699" - integrity sha1-6nbHXRHrhtTb6JqDiTu4YZKaxpk= - dependencies: - lodash._reinterpolate "~2.4.1" - lodash.escape "~2.4.1" - -lodash.truncate@^4.4.2: - version "4.4.2" - resolved "https://registry.yarnpkg.com/lodash.truncate/-/lodash.truncate-4.4.2.tgz#5a350da0b1113b837ecfffd5812cbe58d6eae193" - integrity sha1-WjUNoLERO4N+z//VgSy+WNbq4ZM= - -lodash.union@^4.6.0: - version "4.6.0" - resolved "https://registry.yarnpkg.com/lodash.union/-/lodash.union-4.6.0.tgz#48bb5088409f16f1821666641c44dd1aaae3cd88" - integrity sha1-SLtQiECfFvGCFmZkHETdGqrjzYg= - -lodash.values@~2.4.1: - version "2.4.1" - resolved "https://registry.yarnpkg.com/lodash.values/-/lodash.values-2.4.1.tgz#abf514436b3cb705001627978cbcf30b1280eea4" - integrity sha1-q/UUQ2s8twUAFieXjLzzCxKA7qQ= - dependencies: - lodash.keys "~2.4.1" - -lodash.zip@^4.2.0: - version "4.2.0" - resolved "https://registry.yarnpkg.com/lodash.zip/-/lodash.zip-4.2.0.tgz#ec6662e4896408ed4ab6c542a3990b72cc080020" - integrity sha1-7GZi5IlkCO1KtsVCo5kLcswIACA= - -lodash@^4.0.0, lodash@^4.17.10, lodash@^4.17.14, lodash@^4.17.15, lodash@^4.17.19, lodash@^4.17.21, lodash@^4.17.4, lodash@^4.3.0, lodash@^4.6.1, lodash@~4.17.10: - version "4.17.21" - resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.21.tgz#679591c564c3bffaae8454cf0b3df370c3d6911c" - integrity sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg== - -log-driver@^1.2.7: - version "1.2.7" - resolved "https://registry.yarnpkg.com/log-driver/-/log-driver-1.2.7.tgz#63b95021f0702fedfa2c9bb0a24e7797d71871d8" - integrity sha512-U7KCmLdqsGHBLeWqYlFA0V0Sl6P08EE1ZrmA9cxjUE0WVqT9qnyVDPz1kzpFEP0jdJuFnasWIfSd7fsaNXkpbg== - -log-symbols@4.1.0, log-symbols@^4.1.0: - version "4.1.0" - resolved "https://registry.yarnpkg.com/log-symbols/-/log-symbols-4.1.0.tgz#3fbdbb95b4683ac9fc785111e792e558d4abd503" - integrity sha512-8XPvpAA8uyhfteu8pIvQxpJZ7SYYdpUivZpGy6sFsBuKRY/7rQGavedeB8aK+Zkyq6upMFVL/9AW6vOYzfRyLg== - dependencies: - chalk "^4.1.0" - is-unicode-supported "^0.1.0" - -log-symbols@^2.1.0: - version "2.2.0" - resolved "https://registry.yarnpkg.com/log-symbols/-/log-symbols-2.2.0.tgz#5740e1c5d6f0dfda4ad9323b5332107ef6b4c40a" - integrity sha512-VeIAFslyIerEJLXHziedo2basKbMKtTw3vfn5IzG0XTjhAVEJyNHnL2p7vc+wBDSdQuUpNw3M2u6xb9QsAY5Eg== - dependencies: - chalk "^2.0.1" - -log4js@^6.3.0: - version "6.3.0" - resolved "https://registry.yarnpkg.com/log4js/-/log4js-6.3.0.tgz#10dfafbb434351a3e30277a00b9879446f715bcb" - integrity sha512-Mc8jNuSFImQUIateBFwdOQcmC6Q5maU0VVvdC2R6XMb66/VnT+7WS4D/0EeNMZu1YODmJe5NIn2XftCzEocUgw== - dependencies: - date-format "^3.0.0" - debug "^4.1.1" - flatted "^2.0.1" - rfdc "^1.1.4" - streamroller "^2.2.4" - -loglevel-plugin-prefix@^0.8.4: - version "0.8.4" - resolved "https://registry.yarnpkg.com/loglevel-plugin-prefix/-/loglevel-plugin-prefix-0.8.4.tgz#2fe0e05f1a820317d98d8c123e634c1bd84ff644" - integrity sha512-WpG9CcFAOjz/FtNht+QJeGpvVl/cdR6P0z6OcXSkr8wFJOsV2GRj2j10JLfjuA4aYkcKCNIEqRGCyTife9R8/g== - -loglevel@^1.6.0: - version "1.7.1" - resolved "https://registry.yarnpkg.com/loglevel/-/loglevel-1.7.1.tgz#005fde2f5e6e47068f935ff28573e125ef72f197" - integrity sha512-Hesni4s5UkWkwCGJMQGAh71PaLUmKFM60dHvq0zi/vDhhrzuk+4GgNbTXJ12YYQJn6ZKBDNIjYcuQGKudvqrIw== - -loglevelnext@^1.0.1: - version "1.0.5" - resolved "https://registry.yarnpkg.com/loglevelnext/-/loglevelnext-1.0.5.tgz#36fc4f5996d6640f539ff203ba819641680d75a2" - integrity sha512-V/73qkPuJmx4BcBF19xPBr+0ZRVBhc4POxvZTZdMeXpJ4NItXSJ/MSwuFT0kQJlCbXvdlZoQQ/418bS1y9Jh6A== - dependencies: - es6-symbol "^3.1.1" - object.assign "^4.1.0" - -lolex@^2.2.0: - version "2.7.5" - resolved "https://registry.yarnpkg.com/lolex/-/lolex-2.7.5.tgz#113001d56bfc7e02d56e36291cc5c413d1aa0733" - integrity sha512-l9x0+1offnKKIzYVjyXU2SiwhXDLekRzKyhnbyldPHvC7BvLPVpdNUNR2KeMAiCN2D/kLNttZgQD5WjSxuBx3Q== - -lolex@^5.0.1: - version "5.1.2" - resolved "https://registry.yarnpkg.com/lolex/-/lolex-5.1.2.tgz#953694d098ce7c07bc5ed6d0e42bc6c0c6d5a367" - integrity sha512-h4hmjAvHTmd+25JSwrtTIuwbKdwg5NzZVRMLn9saij4SZaepCrTCxPr35H/3bjwfMJtN+t3CX8672UIkglz28A== - dependencies: - "@sinonjs/commons" "^1.7.0" - -longest-streak@^2.0.0: - version "2.0.4" - resolved "https://registry.yarnpkg.com/longest-streak/-/longest-streak-2.0.4.tgz#b8599957da5b5dab64dee3fe316fa774597d90e4" - integrity sha512-vM6rUVCVUJJt33bnmHiZEvr7wPT78ztX7rojL+LW51bHtLh6HTjx84LA5W4+oa6aKEJA7jJu5LR6vQRBpA5DVg== - -longest@^1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/longest/-/longest-1.0.1.tgz#30a0b2da38f73770e8294a0d22e6625ed77d0097" - integrity sha1-MKCy2jj3N3DoKUoNIuZiXtd9AJc= - -loud-rejection@^1.0.0, loud-rejection@^1.6.0: - version "1.6.0" - resolved "https://registry.yarnpkg.com/loud-rejection/-/loud-rejection-1.6.0.tgz#5b46f80147edee578870f086d04821cf998e551f" - integrity sha1-W0b4AUft7leIcPCG0Eghz5mOVR8= - dependencies: - currently-unhandled "^0.4.1" - signal-exit "^3.0.0" - -lowercase-keys@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/lowercase-keys/-/lowercase-keys-2.0.0.tgz#2603e78b7b4b0006cbca2fbcc8a3202558ac9479" - integrity sha512-tqNXrS78oMOE73NMxK4EMLQsQowWf8jKooH9g7xPavRT706R6bkQJ6DY2Te7QukaZsulxa30wQ7bk0pm4XiHmA== - -lru-cache@^4.0.1: - version "4.1.5" - resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-4.1.5.tgz#8bbe50ea85bed59bc9e33dcab8235ee9bcf443cd" - integrity sha512-sWZlbEP2OsHNkXrMl5GYk/jKk70MBng6UU4YI/qGDYbgf6YbP4EvmqISbXCoJiRKs+1bSpFHVgQxvJ17F2li5g== - dependencies: - pseudomap "^1.0.2" - yallist "^2.1.2" - -lru-cache@^6.0.0: - version "6.0.0" - resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-6.0.0.tgz#6d6fe6570ebd96aaf90fcad1dafa3b2566db3a94" - integrity sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA== - dependencies: - yallist "^4.0.0" - -lru-queue@^0.1.0: - version "0.1.0" - resolved "https://registry.yarnpkg.com/lru-queue/-/lru-queue-0.1.0.tgz#2738bd9f0d3cf4f84490c5736c48699ac632cda3" - integrity sha1-Jzi9nw089PhEkMVzbEhpmsYyzaM= - dependencies: - es5-ext "~0.10.2" - -magic-string@^0.25.7: - version "0.25.7" - resolved "https://registry.yarnpkg.com/magic-string/-/magic-string-0.25.7.tgz#3f497d6fd34c669c6798dcb821f2ef31f5445051" - integrity sha512-4CrMT5DOHTDk4HYDlzmwu4FVCcIYI8gauveasrdCu2IKIFOJ3f0v/8MDGJCDL9oD2ppz/Av1b0Nj345H9M+XIA== - dependencies: - sourcemap-codec "^1.4.4" - -make-dir@^2.1.0: - version "2.1.0" - resolved "https://registry.yarnpkg.com/make-dir/-/make-dir-2.1.0.tgz#5f0310e18b8be898cc07009295a30ae41e91e6f5" - integrity sha512-LS9X+dc8KLxXCb8dni79fLIIUA5VyZoyjSMCwTluaXA0o27cCK0bhXkpgw+sTXVpPy/lSO57ilRixqk0vDmtRA== - dependencies: - pify "^4.0.1" - semver "^5.6.0" - -make-dir@^3.0.0, make-dir@^3.0.2, make-dir@^3.1.0: - version "3.1.0" - resolved "https://registry.yarnpkg.com/make-dir/-/make-dir-3.1.0.tgz#415e967046b3a7f1d185277d84aa58203726a13f" - integrity sha512-g3FeP20LNwhALb/6Cz6Dd4F2ngze0jz7tbzrD2wAV+o9FeNHe4rL+yK2md0J/fiSf1sa1ADhXqi5+oVwOM/eGw== - dependencies: - semver "^6.0.0" - -make-iterator@^1.0.0: - version "1.0.1" - resolved "https://registry.yarnpkg.com/make-iterator/-/make-iterator-1.0.1.tgz#29b33f312aa8f547c4a5e490f56afcec99133ad6" - integrity sha512-pxiuXh0iVEq7VM7KMIhs5gxsfxCux2URptUQaXo4iZZJxBAzTPOLE2BumO5dbfVYq/hBJFBR/a1mFDmOx5AGmw== - dependencies: - kind-of "^6.0.2" - -map-cache@^0.2.0, map-cache@^0.2.2: - version "0.2.2" - resolved "https://registry.yarnpkg.com/map-cache/-/map-cache-0.2.2.tgz#c32abd0bd6525d9b051645bb4f26ac5dc98a0dbf" - integrity sha1-wyq9C9ZSXZsFFkW7TyasXcmKDb8= - -map-obj@^1.0.0, map-obj@^1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/map-obj/-/map-obj-1.0.1.tgz#d933ceb9205d82bdcf4886f6742bdc2b4dea146d" - integrity sha1-2TPOuSBdgr3PSIb2dCvcK03qFG0= - -map-obj@^4.0.0: - version "4.3.0" - resolved "https://registry.yarnpkg.com/map-obj/-/map-obj-4.3.0.tgz#9304f906e93faae70880da102a9f1df0ea8bb05a" - integrity sha512-hdN1wVrZbb29eBGiGjJbeP8JbKjq1urkHJ/LIP/NY48MZ1QVXUsQBV1G1zvYFHn1XE06cwjBsOI2K3Ulnj1YXQ== - -map-stream@0.0.7, map-stream@^0.0.7: - version "0.0.7" - resolved "https://registry.yarnpkg.com/map-stream/-/map-stream-0.0.7.tgz#8a1f07896d82b10926bd3744a2420009f88974a8" - integrity sha1-ih8HiW2CsQkmvTdEokIACfiJdKg= - -map-stream@~0.1.0: - version "0.1.0" - resolved "https://registry.yarnpkg.com/map-stream/-/map-stream-0.1.0.tgz#e56aa94c4c8055a16404a0674b78f215f7c8e194" - integrity sha1-5WqpTEyAVaFkBKBnS3jyFffI4ZQ= - -map-visit@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/map-visit/-/map-visit-1.0.0.tgz#ecdca8f13144e660f1b5bd41f12f3479d98dfb8f" - integrity sha1-7Nyo8TFE5mDxtb1B8S80edmN+48= - dependencies: - object-visit "^1.0.0" - -markdown-table@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/markdown-table/-/markdown-table-2.0.0.tgz#194a90ced26d31fe753d8b9434430214c011865b" - integrity sha512-Ezda85ToJUBhM6WGaG6veasyym+Tbs3cMAw/ZhOPqXiYsr0jgocBV3j3nx+4lk47plLlIqjwuTm/ywVI+zjJ/A== - dependencies: - repeat-string "^1.0.0" - -marky@^1.2.2: - version "1.2.2" - resolved "https://registry.yarnpkg.com/marky/-/marky-1.2.2.tgz#4456765b4de307a13d263a69b0c79bf226e68323" - integrity sha512-k1dB2HNeaNyORco8ulVEhctyEGkKHb2YWAhDsxeFlW2nROIirsctBYzKwwS3Vza+sKTS1zO4Z+n9/+9WbGLIxQ== - -matchdep@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/matchdep/-/matchdep-2.0.0.tgz#c6f34834a0d8dbc3b37c27ee8bbcb27c7775582e" - integrity sha1-xvNINKDY28OzfCfui7yyfHd1WC4= - dependencies: - findup-sync "^2.0.0" - micromatch "^3.0.4" - resolve "^1.4.0" - stack-trace "0.0.10" - -math-random@^1.0.1: - version "1.0.4" - resolved "https://registry.yarnpkg.com/math-random/-/math-random-1.0.4.tgz#5dd6943c938548267016d4e34f057583080c514c" - integrity sha512-rUxjysqif/BZQH2yhd5Aaq7vXMSx9NdEsQcyA07uEzIvxgI7zIr33gGsh+RU0/XjmQpCW7RsVof1vlkvQVCK5A== - -md5.js@^1.3.4: - version "1.3.5" - resolved "https://registry.yarnpkg.com/md5.js/-/md5.js-1.3.5.tgz#b5d07b8e3216e3e27cd728d72f70d1e6a342005f" - integrity sha512-xitP+WxNPcTTOgnTJcrhM0xvdPepipPSf3I8EIpGKeFLjt3PlJLIDG3u8EX53ZIubkb+5U2+3rELYpEhHhzdkg== - dependencies: - hash-base "^3.0.0" - inherits "^2.0.1" - safe-buffer "^5.1.2" - -mdast-util-definitions@^4.0.0: - version "4.0.0" - resolved "https://registry.yarnpkg.com/mdast-util-definitions/-/mdast-util-definitions-4.0.0.tgz#c5c1a84db799173b4dcf7643cda999e440c24db2" - integrity sha512-k8AJ6aNnUkB7IE+5azR9h81O5EQ/cTDXtWdMq9Kk5KcEW/8ritU5CeLg/9HhOC++nALHBlaogJ5jz0Ybk3kPMQ== - dependencies: - unist-util-visit "^2.0.0" - -mdast-util-find-and-replace@^1.1.0, mdast-util-find-and-replace@^1.1.1: - version "1.1.1" - resolved "https://registry.yarnpkg.com/mdast-util-find-and-replace/-/mdast-util-find-and-replace-1.1.1.tgz#b7db1e873f96f66588c321f1363069abf607d1b5" - integrity sha512-9cKl33Y21lyckGzpSmEQnIDjEfeeWelN5s1kUW1LwdB0Fkuq2u+4GdqcGEygYxJE8GVqCl0741bYXHgamfWAZA== - dependencies: - escape-string-regexp "^4.0.0" - unist-util-is "^4.0.0" - unist-util-visit-parents "^3.0.0" - -mdast-util-from-markdown@^0.8.0: - version "0.8.5" - resolved "https://registry.yarnpkg.com/mdast-util-from-markdown/-/mdast-util-from-markdown-0.8.5.tgz#d1ef2ca42bc377ecb0463a987910dae89bd9a28c" - integrity sha512-2hkTXtYYnr+NubD/g6KGBS/0mFmBcifAsI0yIWRiRo0PjVs6SSOSOdtzbp6kSGnShDN6G5aWZpKQ2lWRy27mWQ== - dependencies: - "@types/mdast" "^3.0.0" - mdast-util-to-string "^2.0.0" - micromark "~2.11.0" - parse-entities "^2.0.0" - unist-util-stringify-position "^2.0.0" - -mdast-util-gfm-autolink-literal@^0.1.0: - version "0.1.3" - resolved "https://registry.yarnpkg.com/mdast-util-gfm-autolink-literal/-/mdast-util-gfm-autolink-literal-0.1.3.tgz#9c4ff399c5ddd2ece40bd3b13e5447d84e385fb7" - integrity sha512-GjmLjWrXg1wqMIO9+ZsRik/s7PLwTaeCHVB7vRxUwLntZc8mzmTsLVr6HW1yLokcnhfURsn5zmSVdi3/xWWu1A== - dependencies: - ccount "^1.0.0" - mdast-util-find-and-replace "^1.1.0" - micromark "^2.11.3" - -mdast-util-gfm-strikethrough@^0.2.0: - version "0.2.3" - resolved "https://registry.yarnpkg.com/mdast-util-gfm-strikethrough/-/mdast-util-gfm-strikethrough-0.2.3.tgz#45eea337b7fff0755a291844fbea79996c322890" - integrity sha512-5OQLXpt6qdbttcDG/UxYY7Yjj3e8P7X16LzvpX8pIQPYJ/C2Z1qFGMmcw+1PZMUM3Z8wt8NRfYTvCni93mgsgA== - dependencies: - mdast-util-to-markdown "^0.6.0" - -mdast-util-gfm-table@^0.1.0: - version "0.1.6" - resolved "https://registry.yarnpkg.com/mdast-util-gfm-table/-/mdast-util-gfm-table-0.1.6.tgz#af05aeadc8e5ee004eeddfb324b2ad8c029b6ecf" - integrity sha512-j4yDxQ66AJSBwGkbpFEp9uG/LS1tZV3P33fN1gkyRB2LoRL+RR3f76m0HPHaby6F4Z5xr9Fv1URmATlRRUIpRQ== - dependencies: - markdown-table "^2.0.0" - mdast-util-to-markdown "~0.6.0" - -mdast-util-gfm-task-list-item@^0.1.0: - version "0.1.6" - resolved "https://registry.yarnpkg.com/mdast-util-gfm-task-list-item/-/mdast-util-gfm-task-list-item-0.1.6.tgz#70c885e6b9f543ddd7e6b41f9703ee55b084af10" - integrity sha512-/d51FFIfPsSmCIRNp7E6pozM9z1GYPIkSy1urQ8s/o4TC22BZ7DqfHFWiqBD23bc7J3vV1Fc9O4QIHBlfuit8A== - dependencies: - mdast-util-to-markdown "~0.6.0" - -mdast-util-gfm@^0.1.0: - version "0.1.2" - resolved "https://registry.yarnpkg.com/mdast-util-gfm/-/mdast-util-gfm-0.1.2.tgz#8ecddafe57d266540f6881f5c57ff19725bd351c" - integrity sha512-NNkhDx/qYcuOWB7xHUGWZYVXvjPFFd6afg6/e2g+SV4r9q5XUcCbV4Wfa3DLYIiD+xAEZc6K4MGaE/m0KDcPwQ== - dependencies: - mdast-util-gfm-autolink-literal "^0.1.0" - mdast-util-gfm-strikethrough "^0.2.0" - mdast-util-gfm-table "^0.1.0" - mdast-util-gfm-task-list-item "^0.1.0" - mdast-util-to-markdown "^0.6.1" - -mdast-util-inject@^1.1.0: - version "1.1.0" - resolved "https://registry.yarnpkg.com/mdast-util-inject/-/mdast-util-inject-1.1.0.tgz#db06b8b585be959a2dcd2f87f472ba9b756f3675" - integrity sha1-2wa4tYW+lZotzS+H9HK6m3VvNnU= - dependencies: - mdast-util-to-string "^1.0.0" - -mdast-util-to-hast@^10.0.0: - version "10.2.0" - resolved "https://registry.yarnpkg.com/mdast-util-to-hast/-/mdast-util-to-hast-10.2.0.tgz#61875526a017d8857b71abc9333942700b2d3604" - integrity sha512-JoPBfJ3gBnHZ18icCwHR50orC9kNH81tiR1gs01D8Q5YpV6adHNO9nKNuFBCJQ941/32PT1a63UF/DitmS3amQ== - dependencies: - "@types/mdast" "^3.0.0" - "@types/unist" "^2.0.0" - mdast-util-definitions "^4.0.0" - mdurl "^1.0.0" - unist-builder "^2.0.0" - unist-util-generated "^1.0.0" - unist-util-position "^3.0.0" - unist-util-visit "^2.0.0" - -mdast-util-to-markdown@^0.6.0, mdast-util-to-markdown@^0.6.1, mdast-util-to-markdown@~0.6.0: - version "0.6.5" - resolved "https://registry.yarnpkg.com/mdast-util-to-markdown/-/mdast-util-to-markdown-0.6.5.tgz#b33f67ca820d69e6cc527a93d4039249b504bebe" - integrity sha512-XeV9sDE7ZlOQvs45C9UKMtfTcctcaj/pGwH8YLbMHoMOXNNCn2LsqVQOqrF1+/NU8lKDAqozme9SCXWyo9oAcQ== - dependencies: - "@types/unist" "^2.0.0" - longest-streak "^2.0.0" - mdast-util-to-string "^2.0.0" - parse-entities "^2.0.0" - repeat-string "^1.0.0" - zwitch "^1.0.0" - -mdast-util-to-string@^1.0.0: - version "1.1.0" - resolved "https://registry.yarnpkg.com/mdast-util-to-string/-/mdast-util-to-string-1.1.0.tgz#27055500103f51637bd07d01da01eb1967a43527" - integrity sha512-jVU0Nr2B9X3MU4tSK7JP1CMkSvOj7X5l/GboG1tKRw52lLF1x2Ju92Ms9tNetCcbfX3hzlM73zYo2NKkWSfF/A== - -mdast-util-to-string@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/mdast-util-to-string/-/mdast-util-to-string-2.0.0.tgz#b8cfe6a713e1091cb5b728fc48885a4767f8b97b" - integrity sha512-AW4DRS3QbBayY/jJmD8437V1Gombjf8RSOUCMFBuo5iHi58AGEgVCKQ+ezHkZZDpAQS75hcBMpLqjpJTjtUL7w== - -mdast-util-toc@^5.0.0: - version "5.1.0" - resolved "https://registry.yarnpkg.com/mdast-util-toc/-/mdast-util-toc-5.1.0.tgz#3af0f9c9a764b993538af03f1f79f4e3cec22736" - integrity sha512-csimbRIVkiqc+PpFeKDGQ/Ck2N4f9FYH3zzBMMJzcxoKL8m+cM0n94xXm0I9eaxHnKdY9n145SGTdyJC7i273g== - dependencies: - "@types/mdast" "^3.0.3" - "@types/unist" "^2.0.3" - extend "^3.0.2" - github-slugger "^1.2.1" - mdast-util-to-string "^2.0.0" - unist-util-is "^4.0.0" - unist-util-visit "^2.0.0" - -mdurl@^1.0.0: - version "1.0.1" - resolved "https://registry.yarnpkg.com/mdurl/-/mdurl-1.0.1.tgz#fe85b2ec75a59037f2adfec100fd6c601761152e" - integrity sha1-/oWy7HWlkDfyrf7BAP1sYBdhFS4= - -media-typer@0.3.0: - version "0.3.0" - resolved "https://registry.yarnpkg.com/media-typer/-/media-typer-0.3.0.tgz#8710d7af0aa626f8fffa1ce00168545263255748" - integrity sha1-hxDXrwqmJvj/+hzgAWhUUmMlV0g= - -mem@^1.1.0: - version "1.1.0" - resolved "https://registry.yarnpkg.com/mem/-/mem-1.1.0.tgz#5edd52b485ca1d900fe64895505399a0dfa45f76" - integrity sha1-Xt1StIXKHZAP5kiVUFOZoN+kX3Y= - dependencies: - mimic-fn "^1.0.0" - -memoizee@0.4.X: - version "0.4.15" - resolved "https://registry.yarnpkg.com/memoizee/-/memoizee-0.4.15.tgz#e6f3d2da863f318d02225391829a6c5956555b72" - integrity sha512-UBWmJpLZd5STPm7PMUlOw/TSy972M+z8gcyQ5veOnSDRREz/0bmpyTfKt3/51DhEBqCZQn1udM/5flcSPYhkdQ== - dependencies: - d "^1.0.1" - es5-ext "^0.10.53" - es6-weak-map "^2.0.3" - event-emitter "^0.3.5" - is-promise "^2.2.2" - lru-queue "^0.1.0" - next-tick "^1.1.0" - timers-ext "^0.1.7" - -memory-fs@^0.2.0: - version "0.2.0" - resolved "https://registry.yarnpkg.com/memory-fs/-/memory-fs-0.2.0.tgz#f2bb25368bc121e391c2520de92969caee0a0290" - integrity sha1-8rslNovBIeORwlIN6Slpyu4KApA= - -memory-fs@^0.3.0, memory-fs@~0.3.0: - version "0.3.0" - resolved "https://registry.yarnpkg.com/memory-fs/-/memory-fs-0.3.0.tgz#7bcc6b629e3a43e871d7e29aca6ae8a7f15cbb20" - integrity sha1-e8xrYp46Q+hx1+Kaymrop/FcuyA= - dependencies: - errno "^0.1.3" - readable-stream "^2.0.1" - -memory-fs@^0.4.0, memory-fs@~0.4.1: - version "0.4.1" - resolved "https://registry.yarnpkg.com/memory-fs/-/memory-fs-0.4.1.tgz#3a9a20b8462523e447cfbc7e8bb80ed667bfc552" - integrity sha1-OpoguEYlI+RHz7x+i7gO1me/xVI= - dependencies: - errno "^0.1.3" - readable-stream "^2.0.1" - -meow@^3.3.0: - version "3.7.0" - resolved "https://registry.yarnpkg.com/meow/-/meow-3.7.0.tgz#72cb668b425228290abbfa856892587308a801fb" - integrity sha1-cstmi0JSKCkKu/qFaJJYcwioAfs= - dependencies: - camelcase-keys "^2.0.0" - decamelize "^1.1.2" - loud-rejection "^1.0.0" - map-obj "^1.0.1" - minimist "^1.1.3" - normalize-package-data "^2.3.4" - object-assign "^4.0.1" - read-pkg-up "^1.0.1" - redent "^1.0.0" - trim-newlines "^1.0.0" - -meow@^8.0.0: - version "8.1.2" - resolved "https://registry.yarnpkg.com/meow/-/meow-8.1.2.tgz#bcbe45bda0ee1729d350c03cffc8395a36c4e897" - integrity sha512-r85E3NdZ+mpYk1C6RjPFEMSE+s1iZMuHtsHAqY0DT3jZczl0diWUZ8g6oU7h0M9cD2EL+PzaYghhCLzR0ZNn5Q== - dependencies: - "@types/minimist" "^1.2.0" - camelcase-keys "^6.2.2" - decamelize-keys "^1.1.0" - hard-rejection "^2.1.0" - minimist-options "4.1.0" - normalize-package-data "^3.0.0" - read-pkg-up "^7.0.1" - redent "^3.0.0" - trim-newlines "^3.0.0" - type-fest "^0.18.0" - yargs-parser "^20.2.3" - -merge-descriptors@1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/merge-descriptors/-/merge-descriptors-1.0.1.tgz#b00aaa556dd8b44568150ec9d1b953f3f90cbb61" - integrity sha1-sAqqVW3YtEVoFQ7J0blT8/kMu2E= - -merge-source-map@^1.1.0: - version "1.1.0" - resolved "https://registry.yarnpkg.com/merge-source-map/-/merge-source-map-1.1.0.tgz#2fdde7e6020939f70906a68f2d7ae685e4c8c646" - integrity sha512-Qkcp7P2ygktpMPh2mCQZaf3jhN6D3Z/qVZHSdWvQ+2Ef5HgRAPBO57A77+ENm0CPx2+1Ce/MYKi3ymqdfuqibw== - dependencies: - source-map "^0.6.1" - -merge-stream@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/merge-stream/-/merge-stream-2.0.0.tgz#52823629a14dd00c9770fb6ad47dc6310f2c1f60" - integrity sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w== - -methods@~1.1.2: - version "1.1.2" - resolved "https://registry.yarnpkg.com/methods/-/methods-1.1.2.tgz#5529a4d67654134edcc5266656835b0f851afcee" - integrity sha1-VSmk1nZUE07cxSZmVoNbD4Ua/O4= - -micromark-extension-gfm-autolink-literal@~0.5.0: - version "0.5.7" - resolved "https://registry.yarnpkg.com/micromark-extension-gfm-autolink-literal/-/micromark-extension-gfm-autolink-literal-0.5.7.tgz#53866c1f0c7ef940ae7ca1f72c6faef8fed9f204" - integrity sha512-ePiDGH0/lhcngCe8FtH4ARFoxKTUelMp4L7Gg2pujYD5CSMb9PbblnyL+AAMud/SNMyusbS2XDSiPIRcQoNFAw== - dependencies: - micromark "~2.11.3" - -micromark-extension-gfm-strikethrough@~0.6.5: - version "0.6.5" - resolved "https://registry.yarnpkg.com/micromark-extension-gfm-strikethrough/-/micromark-extension-gfm-strikethrough-0.6.5.tgz#96cb83356ff87bf31670eefb7ad7bba73e6514d1" - integrity sha512-PpOKlgokpQRwUesRwWEp+fHjGGkZEejj83k9gU5iXCbDG+XBA92BqnRKYJdfqfkrRcZRgGuPuXb7DaK/DmxOhw== - dependencies: - micromark "~2.11.0" - -micromark-extension-gfm-table@~0.4.0: - version "0.4.3" - resolved "https://registry.yarnpkg.com/micromark-extension-gfm-table/-/micromark-extension-gfm-table-0.4.3.tgz#4d49f1ce0ca84996c853880b9446698947f1802b" - integrity sha512-hVGvESPq0fk6ALWtomcwmgLvH8ZSVpcPjzi0AjPclB9FsVRgMtGZkUcpE0zgjOCFAznKepF4z3hX8z6e3HODdA== - dependencies: - micromark "~2.11.0" - -micromark-extension-gfm-tagfilter@~0.3.0: - version "0.3.0" - resolved "https://registry.yarnpkg.com/micromark-extension-gfm-tagfilter/-/micromark-extension-gfm-tagfilter-0.3.0.tgz#d9f26a65adee984c9ccdd7e182220493562841ad" - integrity sha512-9GU0xBatryXifL//FJH+tAZ6i240xQuFrSL7mYi8f4oZSbc+NvXjkrHemeYP0+L4ZUT+Ptz3b95zhUZnMtoi/Q== - -micromark-extension-gfm-task-list-item@~0.3.0: - version "0.3.3" - resolved "https://registry.yarnpkg.com/micromark-extension-gfm-task-list-item/-/micromark-extension-gfm-task-list-item-0.3.3.tgz#d90c755f2533ed55a718129cee11257f136283b8" - integrity sha512-0zvM5iSLKrc/NQl84pZSjGo66aTGd57C1idmlWmE87lkMcXrTxg1uXa/nXomxJytoje9trP0NDLvw4bZ/Z/XCQ== - dependencies: - micromark "~2.11.0" - -micromark-extension-gfm@^0.3.0: - version "0.3.3" - resolved "https://registry.yarnpkg.com/micromark-extension-gfm/-/micromark-extension-gfm-0.3.3.tgz#36d1a4c089ca8bdfd978c9bd2bf1a0cb24e2acfe" - integrity sha512-oVN4zv5/tAIA+l3GbMi7lWeYpJ14oQyJ3uEim20ktYFAcfX1x3LNlFGGlmrZHt7u9YlKExmyJdDGaTt6cMSR/A== - dependencies: - micromark "~2.11.0" - micromark-extension-gfm-autolink-literal "~0.5.0" - micromark-extension-gfm-strikethrough "~0.6.5" - micromark-extension-gfm-table "~0.4.0" - micromark-extension-gfm-tagfilter "~0.3.0" - micromark-extension-gfm-task-list-item "~0.3.0" - -micromark@^2.11.3, micromark@~2.11.0, micromark@~2.11.3: - version "2.11.4" - resolved "https://registry.yarnpkg.com/micromark/-/micromark-2.11.4.tgz#d13436138eea826383e822449c9a5c50ee44665a" - integrity sha512-+WoovN/ppKolQOFIAajxi7Lu9kInbPxFuTBVEavFcL8eAfVstoc5MocPmqBeAdBOJV00uaVjegzH4+MA0DN/uA== - dependencies: - debug "^4.0.0" - parse-entities "^2.0.0" - -micromatch@^2.1.5: - version "2.3.11" - resolved "https://registry.yarnpkg.com/micromatch/-/micromatch-2.3.11.tgz#86677c97d1720b363431d04d0d15293bd38c1565" - integrity sha1-hmd8l9FyCzY0MdBNDRUpO9OMFWU= - dependencies: - arr-diff "^2.0.0" - array-unique "^0.2.1" - braces "^1.8.2" - expand-brackets "^0.1.4" - extglob "^0.3.1" - filename-regex "^2.0.0" - is-extglob "^1.0.0" - is-glob "^2.0.1" - kind-of "^3.0.2" - normalize-path "^2.0.1" - object.omit "^2.0.0" - parse-glob "^3.0.4" - regex-cache "^0.4.2" - -micromatch@^3.0.4, micromatch@^3.1.10, micromatch@^3.1.4, micromatch@^3.1.5: - version "3.1.10" - resolved "https://registry.yarnpkg.com/micromatch/-/micromatch-3.1.10.tgz#70859bc95c9840952f359a068a3fc49f9ecfac23" - integrity sha512-MWikgl9n9M3w+bpsY3He8L+w9eF9338xRl8IAO5viDizwSzziFEyUzo2xrrloB64ADbTf8uA8vRqqttDTOmccg== - dependencies: - arr-diff "^4.0.0" - array-unique "^0.3.2" - braces "^2.3.1" - define-property "^2.0.2" - extend-shallow "^3.0.2" - extglob "^2.0.4" - fragment-cache "^0.2.1" - kind-of "^6.0.2" - nanomatch "^1.2.9" - object.pick "^1.3.0" - regex-not "^1.0.0" - snapdragon "^0.8.1" - to-regex "^3.0.2" - -micromatch@^4.0.4: - version "4.0.4" - resolved "https://registry.yarnpkg.com/micromatch/-/micromatch-4.0.4.tgz#896d519dfe9db25fce94ceb7a500919bf881ebf9" - integrity sha512-pRmzw/XUcwXGpD9aI9q/0XOwLNygjETJ8y0ao0wdqprrzDa4YnxLcz7fQRZr8voh8V10kGhABbNcHVk5wHgWwg== - dependencies: - braces "^3.0.1" - picomatch "^2.2.3" - -miller-rabin@^4.0.0: - version "4.0.1" - resolved "https://registry.yarnpkg.com/miller-rabin/-/miller-rabin-4.0.1.tgz#f080351c865b0dc562a8462966daa53543c78a4d" - integrity sha512-115fLhvZVqWwHPbClyntxEVfVDfl9DLLTuJvq3g2O/Oxi8AiNouAHvDSzHS0viUJc+V5vm3eq91Xwqn9dp4jRA== - dependencies: - bn.js "^4.0.0" - brorand "^1.0.1" - -mime-db@1.50.0: - version "1.50.0" - resolved "https://registry.yarnpkg.com/mime-db/-/mime-db-1.50.0.tgz#abd4ac94e98d3c0e185016c67ab45d5fde40c11f" - integrity sha512-9tMZCDlYHqeERXEHO9f/hKfNXhre5dK2eE/krIvUjZbS2KPcqGDfNShIWS1uW9XOTKQKqK6qbeOci18rbfW77A== - -mime-types@^2.1.12, mime-types@~2.1.17, mime-types@~2.1.19, mime-types@~2.1.24: - version "2.1.33" - resolved "https://registry.yarnpkg.com/mime-types/-/mime-types-2.1.33.tgz#1fa12a904472fafd068e48d9e8401f74d3f70edb" - integrity sha512-plLElXp7pRDd0bNZHw+nMd52vRYjLwQjygaNg7ddJ2uJtTlmnTCjWuPKxVu6//AdaRuME84SvLW91sIkBqGT0g== - dependencies: - mime-db "1.50.0" - -mime@1.4.1: - version "1.4.1" - resolved "https://registry.yarnpkg.com/mime/-/mime-1.4.1.tgz#121f9ebc49e3766f311a76e1fa1c8003c4b03aa6" - integrity sha512-KI1+qOZu5DcW6wayYHSzR/tXKCDC5Om4s1z2QJjDULzLcmf3DvzS7oluY4HCTrc+9FiKmWUgeNLg7W3uIQvxtQ== - -mime@1.6.0: - version "1.6.0" - resolved "https://registry.yarnpkg.com/mime/-/mime-1.6.0.tgz#32cd9e5c64553bd58d19a568af452acff04981b1" - integrity sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg== - -mime@^2.1.0, mime@^2.2.0, mime@^2.5.2: - version "2.5.2" - resolved "https://registry.yarnpkg.com/mime/-/mime-2.5.2.tgz#6e3dc6cc2b9510643830e5f19d5cb753da5eeabe" - integrity sha512-tqkh47FzKeCPD2PUiPB6pkbMzsCasjxAfC62/Wap5qrUWcb+sFasXUC5I3gYM5iBM8v/Qpn4UK0x+j0iHyFPDg== - -mimic-fn@^1.0.0: - version "1.2.0" - resolved "https://registry.yarnpkg.com/mimic-fn/-/mimic-fn-1.2.0.tgz#820c86a39334640e99516928bd03fca88057d022" - integrity sha512-jf84uxzwiuiIVKiOLpfYk7N46TSy8ubTonmneY9vrpHNAnp0QBt2BxWV9dO3/j+BoVAb+a5G6YDPW3M5HOdMWQ== - -mimic-fn@^2.1.0: - version "2.1.0" - resolved "https://registry.yarnpkg.com/mimic-fn/-/mimic-fn-2.1.0.tgz#7ed2c2ccccaf84d3ffcb7a69b57711fc2083401b" - integrity sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg== - -mimic-response@^1.0.0: - version "1.0.1" - resolved "https://registry.yarnpkg.com/mimic-response/-/mimic-response-1.0.1.tgz#4923538878eef42063cb8a3e3b0798781487ab1b" - integrity sha512-j5EctnkH7amfV/q5Hgmoal1g2QHFJRraOtmx0JpIqkxhBhI/lJSl1nMpQ45hVarwNETOoWEimndZ4QK0RHxuxQ== - -mimic-response@^3.1.0: - version "3.1.0" - resolved "https://registry.yarnpkg.com/mimic-response/-/mimic-response-3.1.0.tgz#2d1d59af9c1b129815accc2c46a022a5ce1fa3c9" - integrity sha512-z0yWI+4FDrrweS8Zmt4Ej5HdJmky15+L2e6Wgn3+iK5fWzb6T3fhNFq2+MeTRb064c6Wr4N/wv0DzQTjNzHNGQ== - -min-indent@^1.0.0: - version "1.0.1" - resolved "https://registry.yarnpkg.com/min-indent/-/min-indent-1.0.1.tgz#a63f681673b30571fbe8bc25686ae746eefa9869" - integrity sha512-I9jwMn07Sy/IwOj3zVkVik2JTvgpaykDZEigL6Rx6N9LbMywwUSMtxET+7lVoDLLd3O3IXwJwvuuns8UB/HeAg== - -minimalistic-assert@^1.0.0, minimalistic-assert@^1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/minimalistic-assert/-/minimalistic-assert-1.0.1.tgz#2e194de044626d4a10e7f7fbc00ce73e83e4d5c7" - integrity sha512-UtJcAD4yEaGtjPezWuO9wC4nwUnVH/8/Im3yEHQP4b67cXlD/Qr9hdITCU1xDbSEXg2XKNaP8jsReV7vQd00/A== - -minimalistic-crypto-utils@^1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/minimalistic-crypto-utils/-/minimalistic-crypto-utils-1.0.1.tgz#f6c00c1c0b082246e5c4d99dfb8c7c083b2b582a" - integrity sha1-9sAMHAsIIkblxNmd+4x8CDsrWCo= - -"minimatch@2 || 3", minimatch@3.0.4, minimatch@^3.0.2, minimatch@^3.0.3, minimatch@^3.0.4, minimatch@~3.0.2: - version "3.0.4" - resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-3.0.4.tgz#5166e286457f03306064be5497e8dbb0c3d32083" - integrity sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA== - dependencies: - brace-expansion "^1.1.7" - -minimist-options@4.1.0: - version "4.1.0" - resolved "https://registry.yarnpkg.com/minimist-options/-/minimist-options-4.1.0.tgz#c0655713c53a8a2ebd77ffa247d342c40f010619" - integrity sha512-Q4r8ghd80yhO/0j1O3B2BjweX3fiHg9cdOwjJd2J76Q135c+NDxGCqdYKQ1SKBuFfgWbAUzBfvYjPUEeNgqN1A== - dependencies: - arrify "^1.0.1" - is-plain-obj "^1.1.0" - kind-of "^6.0.3" - -minimist@0.0.8: - version "0.0.8" - resolved "https://registry.yarnpkg.com/minimist/-/minimist-0.0.8.tgz#857fcabfc3397d2625b8228262e86aa7a011b05d" - integrity sha1-hX/Kv8M5fSYluCKCYuhqp6ARsF0= - -minimist@^0.2.0: - version "0.2.1" - resolved "https://registry.yarnpkg.com/minimist/-/minimist-0.2.1.tgz#827ba4e7593464e7c221e8c5bed930904ee2c455" - integrity sha512-GY8fANSrTMfBVfInqJAY41QkOM+upUTytK1jZ0c8+3HdHrJxBJ3rF5i9moClXTE8uUSnUo8cAsCoxDXvSY4DHg== - -minimist@^1.1.0, minimist@^1.1.1, minimist@^1.1.3, minimist@^1.2.0, minimist@^1.2.3, minimist@^1.2.5: - version "1.2.5" - resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.5.tgz#67d66014b66a6a8aaa0c083c5fd58df4e4e97602" - integrity sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw== - -minimist@~0.0.1: - version "0.0.10" - resolved "https://registry.yarnpkg.com/minimist/-/minimist-0.0.10.tgz#de3f98543dbf96082be48ad1a0c7cda836301dcf" - integrity sha1-3j+YVD2/lggr5IrRoMfNqDYwHc8= - -mixin-deep@^1.2.0: - version "1.3.2" - resolved "https://registry.yarnpkg.com/mixin-deep/-/mixin-deep-1.3.2.tgz#1120b43dc359a785dce65b55b82e257ccf479566" - integrity sha512-WRoDn//mXBiJ1H40rqa3vH0toePwSsGb45iInWlTySa+Uu4k3tYUSxa2v1KqAiLtvlrSzaExqS1gtk96A9zvEA== - dependencies: - for-in "^1.0.2" - is-extendable "^1.0.1" - -mkdirp@0.3.x, mkdirp@~0.3.5: - version "0.3.5" - resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-0.3.5.tgz#de3e5f8961c88c787ee1368df849ac4413eca8d7" - integrity sha1-3j5fiWHIjHh+4TaN+EmsRBPsqNc= - -mkdirp@0.5.1: - version "0.5.1" - resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-0.5.1.tgz#30057438eac6cf7f8c4767f38648d6697d75c903" - integrity sha1-MAV0OOrGz3+MR2fzhkjWaX11yQM= - dependencies: - minimist "0.0.8" - -mkdirp@0.5.x, "mkdirp@>=0.5 0", mkdirp@^0.5.1, mkdirp@~0.5.0: - version "0.5.5" - resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-0.5.5.tgz#d91cefd62d1436ca0f41620e251288d420099def" - integrity sha512-NKmAlESf6jMGym1++R0Ra7wvhV+wFW63FaSOFPwRahvea0gMUcGUhVeAg/0BC0wiv9ih5NYPB1Wn1UEI1/L+xQ== - dependencies: - minimist "^1.2.5" - -mkdirp@^1.0.4: - version "1.0.4" - resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-1.0.4.tgz#3eb5ed62622756d79a5f0e2a221dfebad75c2f7e" - integrity sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw== - -mocha@^5.0.0: - version "5.2.0" - resolved "https://registry.yarnpkg.com/mocha/-/mocha-5.2.0.tgz#6d8ae508f59167f940f2b5b3c4a612ae50c90ae6" - integrity sha512-2IUgKDhc3J7Uug+FxMXuqIyYzH7gJjXECKe/w43IGgQHTSj3InJi+yAA7T24L9bQMRKiUEHxEX37G5JpVUGLcQ== - dependencies: - browser-stdout "1.3.1" - commander "2.15.1" - debug "3.1.0" - diff "3.5.0" - escape-string-regexp "1.0.5" - glob "7.1.2" - growl "1.10.5" - he "1.1.1" - minimatch "3.0.4" - mkdirp "0.5.1" - supports-color "5.4.0" - -mocha@^9.0.0: - version "9.1.2" - resolved "https://registry.yarnpkg.com/mocha/-/mocha-9.1.2.tgz#93f53175b0f0dc4014bd2d612218fccfcf3534d3" - integrity sha512-ta3LtJ+63RIBP03VBjMGtSqbe6cWXRejF9SyM9Zyli1CKZJZ+vfCTj3oW24V7wAphMJdpOFLoMI3hjJ1LWbs0w== - dependencies: - "@ungap/promise-all-settled" "1.1.2" - ansi-colors "4.1.1" - browser-stdout "1.3.1" - chokidar "3.5.2" - debug "4.3.2" - 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.25" - serialize-javascript "6.0.0" - strip-json-comments "3.1.1" - supports-color "8.1.1" - which "2.0.2" - workerpool "6.1.5" - yargs "16.2.0" - yargs-parser "20.2.4" - yargs-unparser "2.0.0" - -modify-values@^1.0.0: - version "1.0.1" - resolved "https://registry.yarnpkg.com/modify-values/-/modify-values-1.0.1.tgz#b3939fa605546474e3e3e3c63d64bd43b4ee6022" - integrity sha512-xV2bxeN6F7oYjZWTe/YPAy6MN2M+sL4u/Rlm2AHCIVGfo2p1yGmBHQ6vHehl4bRTZBdHu3TSkWdYgkwpYzAGSw== - -module-deps-sortable@^5.0.3: - version "5.0.3" - resolved "https://registry.yarnpkg.com/module-deps-sortable/-/module-deps-sortable-5.0.3.tgz#e640e7450e0869f4ae8e03437665ca2a8a28f843" - integrity sha512-eiyIZj/A0dj1o4ywXWqicazUL3l0HP3TydUR6xF0X3xh3LGBMLqW8a9aFe6MuNH4mxNMk53QKBHM6LOPR8kSgw== - dependencies: - JSONStream "^1.0.3" - browser-resolve "^1.7.0" - cached-path-relative "^1.0.0" - concat-stream "~1.5.0" - defined "^1.0.0" - detective "^5.2.0" - duplexer2 "^0.1.2" - inherits "^2.0.1" - konan "^2.1.1" - readable-stream "^2.0.2" - resolve "^1.1.3" - standard-version "^9.0.0" - stream-combiner2 "^1.1.1" - subarg "^1.0.0" - through2 "^2.0.0" - xtend "^4.0.0" - -morgan@^1.10.0: - version "1.10.0" - resolved "https://registry.yarnpkg.com/morgan/-/morgan-1.10.0.tgz#091778abc1fc47cd3509824653dae1faab6b17d7" - integrity sha512-AbegBVI4sh6El+1gNwvD5YIck7nSA36weD7xvIxG4in80j/UoK8AEGaWnnz8v1GxonMCltmlNs5ZKbGvl9b1XQ== - dependencies: - basic-auth "~2.0.1" - debug "2.6.9" - depd "~2.0.0" - on-finished "~2.3.0" - on-headers "~1.0.2" - -ms@2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/ms/-/ms-2.0.0.tgz#5608aeadfc00be6c2901df5f9861788de0d597c8" - integrity sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g= - -ms@2.1.1: - version "2.1.1" - resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.1.tgz#30a5864eb3ebb0a66f2ebe6d727af06a09d86e0a" - integrity sha512-tgp+dl5cGk28utYktBsrFqA7HKgrhgPsg6Z/EfhWI4gl1Hwq8B/GmY/0oXZ6nF8hDVesS/FpnYaD/kOWhYQvyg== - -ms@2.1.2: - version "2.1.2" - resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.2.tgz#d09d1f357b443f493382a8eb3ccd183872ae6009" - integrity sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w== - -ms@2.1.3, ms@^2.1.1: - version "2.1.3" - resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.3.tgz#574c8138ce1d2b5861f0b44579dbadd60c6615b2" - integrity sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA== - -multipipe@^0.1.0, multipipe@^0.1.2: - version "0.1.2" - resolved "https://registry.yarnpkg.com/multipipe/-/multipipe-0.1.2.tgz#2a8f2ddf70eed564dff2d57f1e1a137d9f05078b" - integrity sha1-Ko8t33Du1WTf8tV/HhoTfZ8FB4s= - dependencies: - duplexer2 "0.0.2" - -mute-stdout@^1.0.0: - version "1.0.1" - resolved "https://registry.yarnpkg.com/mute-stdout/-/mute-stdout-1.0.1.tgz#acb0300eb4de23a7ddeec014e3e96044b3472331" - integrity sha512-kDcwXR4PS7caBpuRYYBUz9iVixUk3anO3f5OYFiIPwK/20vCzKCHyKoulbiDY1S53zD2bxUpxN/IJ+TnXjfvxg== - -mute-stream@0.0.7: - version "0.0.7" - resolved "https://registry.yarnpkg.com/mute-stream/-/mute-stream-0.0.7.tgz#3075ce93bc21b8fab43e1bc4da7e8115ed1e7bab" - integrity sha1-MHXOk7whuPq0PhvE2n6BFe0ee6s= - -mute-stream@0.0.8: - version "0.0.8" - resolved "https://registry.yarnpkg.com/mute-stream/-/mute-stream-0.0.8.tgz#1630c42b2251ff81e2a283de96a5497ea92e5e0d" - integrity sha512-nnbWWOkoWyUsTjKrhgD0dcz22mdkSnpYqbEjIm2nhwhuxlSkpywJmBo8h0ZqJdkp73mb90SssHkN4rsRaBAfAA== - -nan@^2.12.1: - version "2.15.0" - resolved "https://registry.yarnpkg.com/nan/-/nan-2.15.0.tgz#3f34a473ff18e15c1b5626b62903b5ad6e665fee" - integrity sha512-8ZtvEnA2c5aYCZYd1cvgdnU6cqwixRoYg70xPLWUws5ORTa/lnw+u4amixRS/Ac5U5mQVgp9pnlSUnbNWFaWZQ== - -nanocolors@^0.2.12, nanocolors@^0.2.2: - version "0.2.12" - resolved "https://registry.yarnpkg.com/nanocolors/-/nanocolors-0.2.12.tgz#4d05932e70116078673ea4cc6699a1c56cc77777" - integrity sha512-SFNdALvzW+rVlzqexid6epYdt8H9Zol7xDoQarioEFcFN0JHo4CYNztAxmtfgGTVRCmFlEOqqhBpoFGKqSAMug== - -nanoid@3.1.25: - version "3.1.25" - resolved "https://registry.yarnpkg.com/nanoid/-/nanoid-3.1.25.tgz#09ca32747c0e543f0e1814b7d3793477f9c8e152" - integrity sha512-rdwtIXaXCLFAQbnfqDRnI6jaRHp9fTcYBjtFKE8eezcZ7LuLjhUaQGNeMXf1HmRoCH32CLz6XwX0TtxEOS/A3Q== - -nanoid@^3.1.25: - version "3.1.28" - resolved "https://registry.yarnpkg.com/nanoid/-/nanoid-3.1.28.tgz#3c01bac14cb6c5680569014cc65a2f26424c6bd4" - integrity sha512-gSu9VZ2HtmoKYe/lmyPFES5nknFrHa+/DT9muUFWFMi6Jh9E1I7bkvlQ8xxf1Kos9pi9o8lBnIOkatMhKX/YUw== - -nanomatch@^1.2.9: - version "1.2.13" - resolved "https://registry.yarnpkg.com/nanomatch/-/nanomatch-1.2.13.tgz#b87a8aa4fc0de8fe6be88895b38983ff265bd119" - integrity sha512-fpoe2T0RbHwBTBUOftAfBPaDEi06ufaUai0mE6Yn1kacc3SnTErfb/h+X94VXzI64rKFHYImXSvdwGGCmwOqCA== - dependencies: - arr-diff "^4.0.0" - array-unique "^0.3.2" - define-property "^2.0.2" - extend-shallow "^3.0.2" - fragment-cache "^0.2.1" - is-windows "^1.0.2" - kind-of "^6.0.2" - object.pick "^1.3.0" - regex-not "^1.0.0" - snapdragon "^0.8.1" - to-regex "^3.0.1" - -natural-compare@^1.4.0: - version "1.4.0" - resolved "https://registry.yarnpkg.com/natural-compare/-/natural-compare-1.4.0.tgz#4abebfeed7541f2c27acfb29bdbbd15c8d5ba4f7" - integrity sha1-Sr6/7tdUHywnrPspvbvRXI1bpPc= - -ncp@~0.4.2: - version "0.4.2" - resolved "https://registry.yarnpkg.com/ncp/-/ncp-0.4.2.tgz#abcc6cbd3ec2ed2a729ff6e7c1fa8f01784a8574" - integrity sha1-q8xsvT7C7Spyn/bnwfqPAXhKhXQ= - -negotiator@0.6.2: - version "0.6.2" - resolved "https://registry.yarnpkg.com/negotiator/-/negotiator-0.6.2.tgz#feacf7ccf525a77ae9634436a64883ffeca346fb" - integrity sha512-hZXc7K2e+PgeI1eDBe/10Ard4ekbfrrqG8Ep+8Jmf4JID2bNg7NvCPOZN+kfF574pFQI7mum2AUqDidoKqcTOw== - -neo-async@^2.5.0, neo-async@^2.6.0: - version "2.6.2" - resolved "https://registry.yarnpkg.com/neo-async/-/neo-async-2.6.2.tgz#b4aafb93e3aeb2d8174ca53cf163ab7d7308305f" - integrity sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw== - -next-tick@1, next-tick@^1.1.0: - version "1.1.0" - resolved "https://registry.yarnpkg.com/next-tick/-/next-tick-1.1.0.tgz#1836ee30ad56d67ef281b22bd199f709449b35eb" - integrity sha512-CXdUiJembsNjuToQvxayPZF9Vqht7hewsvy2sOWafLvi2awflj9mOC6bHIg50orX8IJvWKY9wYQ/zB2kogPslQ== - -next-tick@~1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/next-tick/-/next-tick-1.0.0.tgz#ca86d1fe8828169b0120208e3dc8424b9db8342c" - integrity sha1-yobR/ogoFpsBICCOPchCS524NCw= - -nice-try@^1.0.4: - version "1.0.5" - resolved "https://registry.yarnpkg.com/nice-try/-/nice-try-1.0.5.tgz#a3378a7696ce7d223e88fc9b764bd7ef1089e366" - integrity sha512-1nh45deeb5olNY7eX82BkPO7SSxR5SSYJiPTrTdFUVYwAl8CKMA5N9PjTYkHiRjisVcxcQ1HXdLhx2qxxJzLNQ== - -nise@^1.2.0: - version "1.5.3" - resolved "https://registry.yarnpkg.com/nise/-/nise-1.5.3.tgz#9d2cfe37d44f57317766c6e9408a359c5d3ac1f7" - integrity sha512-Ymbac/94xeIrMf59REBPOv0thr+CJVFMhrlAkW/gjCIE58BGQdCj0x7KRCb3yz+Ga2Rz3E9XXSvUyyxqqhjQAQ== - dependencies: - "@sinonjs/formatio" "^3.2.1" - "@sinonjs/text-encoding" "^0.7.1" - just-extend "^4.0.2" - lolex "^5.0.1" - path-to-regexp "^1.7.0" - -node-fetch@2.6.1: - version "2.6.1" - resolved "https://registry.yarnpkg.com/node-fetch/-/node-fetch-2.6.1.tgz#045bd323631f76ed2e2b55573394416b639a0052" - integrity sha512-V4aYg89jEoVRxRb2fJdAg8FHvI7cEyYdVAh94HH0UIK8oJxUfkjlDQN9RbMx+bEjP7+ggMiFRprSti032Oipxw== - -node-libs-browser@^0.7.0: - version "0.7.0" - resolved "https://registry.yarnpkg.com/node-libs-browser/-/node-libs-browser-0.7.0.tgz#3e272c0819e308935e26674408d7af0e1491b83b" - integrity sha1-PicsCBnjCJNeJmdECNevDhSRuDs= - dependencies: - assert "^1.1.1" - browserify-zlib "^0.1.4" - buffer "^4.9.0" - console-browserify "^1.1.0" - constants-browserify "^1.0.0" - crypto-browserify "3.3.0" - domain-browser "^1.1.1" - events "^1.0.0" - https-browserify "0.0.1" - os-browserify "^0.2.0" - path-browserify "0.0.0" - process "^0.11.0" - punycode "^1.2.4" - querystring-es3 "^0.2.0" - readable-stream "^2.0.5" - stream-browserify "^2.0.1" - stream-http "^2.3.1" - string_decoder "^0.10.25" - timers-browserify "^2.0.2" - tty-browserify "0.0.0" - url "^0.11.0" - util "^0.10.3" - vm-browserify "0.0.4" - -node-libs-browser@^2.0.0: - version "2.2.1" - resolved "https://registry.yarnpkg.com/node-libs-browser/-/node-libs-browser-2.2.1.tgz#b64f513d18338625f90346d27b0d235e631f6425" - integrity sha512-h/zcD8H9kaDZ9ALUWwlBUDo6TKF8a7qBSCSEGfjTVIYeqsioSKaAX+BN7NgiMGp6iSIXZ3PxgCu8KS3b71YK5Q== - dependencies: - assert "^1.1.1" - browserify-zlib "^0.2.0" - buffer "^4.3.0" - console-browserify "^1.1.0" - constants-browserify "^1.0.0" - crypto-browserify "^3.11.0" - domain-browser "^1.1.1" - events "^3.0.0" - https-browserify "^1.0.0" - os-browserify "^0.3.0" - path-browserify "0.0.1" - process "^0.11.10" - punycode "^1.2.4" - querystring-es3 "^0.2.0" - readable-stream "^2.3.3" - stream-browserify "^2.0.1" - stream-http "^2.7.2" - string_decoder "^1.0.0" - timers-browserify "^2.0.4" - tty-browserify "0.0.0" - url "^0.11.0" - util "^0.11.0" - vm-browserify "^1.0.1" - -node-releases@^1.1.76: - version "1.1.77" - resolved "https://registry.yarnpkg.com/node-releases/-/node-releases-1.1.77.tgz#50b0cfede855dd374e7585bf228ff34e57c1c32e" - integrity sha512-rB1DUFUNAN4Gn9keO2K1efO35IDK7yKHCdCaIMvFO7yUYmmZYeDjnGKle26G4rwj+LKRQpjyUUvMkPglwGCYNQ== - -nopt@3.x: - version "3.0.6" - resolved "https://registry.yarnpkg.com/nopt/-/nopt-3.0.6.tgz#c6465dbf08abcd4db359317f79ac68a646b28ff9" - integrity sha1-xkZdvwirzU2zWTF/eaxopkayj/k= - dependencies: - abbrev "1" - -normalize-package-data@^2.3.2, normalize-package-data@^2.3.4, normalize-package-data@^2.5.0: - version "2.5.0" - resolved "https://registry.yarnpkg.com/normalize-package-data/-/normalize-package-data-2.5.0.tgz#e66db1838b200c1dfc233225d12cb36520e234a8" - integrity sha512-/5CMN3T0R4XTj4DcGaexo+roZSdSFW/0AOOTROrjxzCG1wrWXEsGbRKevjlIL+ZDE4sZlJr5ED4YW0yqmkK+eA== - dependencies: - hosted-git-info "^2.1.4" - resolve "^1.10.0" - semver "2 || 3 || 4 || 5" - validate-npm-package-license "^3.0.1" - -normalize-package-data@^3.0.0: - version "3.0.3" - resolved "https://registry.yarnpkg.com/normalize-package-data/-/normalize-package-data-3.0.3.tgz#dbcc3e2da59509a0983422884cd172eefdfa525e" - integrity sha512-p2W1sgqij3zMMyRC067Dg16bfzVH+w7hyegmpIvZ4JNjqtGOVAIvLmjBx3yP7YTe9vKJgkoNOPjwQGogDoMXFA== - dependencies: - hosted-git-info "^4.0.1" - is-core-module "^2.5.0" - semver "^7.3.4" - validate-npm-package-license "^3.0.1" - -normalize-path@^2.0.0, normalize-path@^2.0.1, normalize-path@^2.1.1: - version "2.1.1" - resolved "https://registry.yarnpkg.com/normalize-path/-/normalize-path-2.1.1.tgz#1ab28b556e198363a8c1a6f7e6fa20137fe6aed9" - integrity sha1-GrKLVW4Zg2Oowab35vogE3/mrtk= - dependencies: - remove-trailing-separator "^1.0.1" - -normalize-path@^3.0.0, normalize-path@~3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/normalize-path/-/normalize-path-3.0.0.tgz#0dcd69ff23a1c9b11fd0978316644a0388216a65" - integrity sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA== - -normalize-url@^6.0.1, normalize-url@^6.1.0: - version "6.1.0" - resolved "https://registry.yarnpkg.com/normalize-url/-/normalize-url-6.1.0.tgz#40d0885b535deffe3f3147bec877d05fe4c5668a" - integrity sha512-DlL+XwOy3NxAQ8xuC0okPgK46iuVNAK01YN7RueYBqqFeGsBjV9XmCAzAdgt+667bCl5kPh9EqKKDwnaPG1I7A== - -now-and-later@^2.0.0: - version "2.0.1" - resolved "https://registry.yarnpkg.com/now-and-later/-/now-and-later-2.0.1.tgz#8e579c8685764a7cc02cb680380e94f43ccb1f7c" - integrity sha512-KGvQ0cB70AQfg107Xvs/Fbu+dGmZoTRJp2TaPwcwQm3/7PteUyN2BCgk8KBMPGBUXZdVwyWS8fDCGFygBm19UQ== - dependencies: - once "^1.3.2" - -npm-run-path@^2.0.0: - version "2.0.2" - resolved "https://registry.yarnpkg.com/npm-run-path/-/npm-run-path-2.0.2.tgz#35a9232dfa35d7067b4cb2ddf2357b1871536c5f" - integrity sha1-NakjLfo11wZ7TLLd8jV7GHFTbF8= - dependencies: - path-key "^2.0.0" - -null-check@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/null-check/-/null-check-1.0.0.tgz#977dffd7176012b9ec30d2a39db5cf72a0439edd" - integrity sha1-l33/1xdgErnsMNKjnbXPcqBDnt0= - -number-is-nan@^1.0.0: - version "1.0.1" - resolved "https://registry.yarnpkg.com/number-is-nan/-/number-is-nan-1.0.1.tgz#097b602b53422a522c1afb8790318336941a011d" - integrity sha1-CXtgK1NCKlIsGvuHkDGDNpQaAR0= - -oauth-sign@~0.9.0: - version "0.9.0" - resolved "https://registry.yarnpkg.com/oauth-sign/-/oauth-sign-0.9.0.tgz#47a7b016baa68b5fa0ecf3dee08a85c679ac6455" - integrity sha512-fexhUFFPTGV8ybAtSIGbV6gOkSv8UtRbDBnAyLQw4QPKkgNlsH2ByPGtMUqdWkos6YCRmAqViwgZrJc/mRDzZQ== - -object-assign@4.X, object-assign@^4, object-assign@^4.0.1, object-assign@^4.1.0, object-assign@^4.1.1: - version "4.1.1" - resolved "https://registry.yarnpkg.com/object-assign/-/object-assign-4.1.1.tgz#2109adc7965887cfc05cbbd442cac8bfbb360863" - integrity sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM= - -object-assign@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/object-assign/-/object-assign-3.0.0.tgz#9bedd5ca0897949bca47e7ff408062d549f587f2" - integrity sha1-m+3VygiXlJvKR+f/QIBi1Un1h/I= - -object-copy@^0.1.0: - version "0.1.0" - resolved "https://registry.yarnpkg.com/object-copy/-/object-copy-0.1.0.tgz#7e7d858b781bd7c991a41ba975ed3812754e998c" - integrity sha1-fn2Fi3gb18mRpBupde04EnVOmYw= - dependencies: - copy-descriptor "^0.1.0" - define-property "^0.2.5" - kind-of "^3.0.3" - -object-inspect@^1.10.3, object-inspect@^1.11.0, object-inspect@^1.9.0: - version "1.11.0" - resolved "https://registry.yarnpkg.com/object-inspect/-/object-inspect-1.11.0.tgz#9dceb146cedd4148a0d9e51ab88d34cf509922b1" - integrity sha512-jp7ikS6Sd3GxQfZJPyH3cjcbJF6GZPClgdV+EFygjFLQ5FmW/dRUnTd9PQ9k0JhoNDabWFbpF1yCdSWCC6gexg== - -object-is@^1.1.4: - version "1.1.5" - resolved "https://registry.yarnpkg.com/object-is/-/object-is-1.1.5.tgz#b9deeaa5fc7f1846a0faecdceec138e5778f53ac" - integrity sha512-3cyDsyHgtmi7I7DfSSI2LDp6SK2lwvtbg0p0R1e0RvTqF5ceGx+K2dfSjm1bKDMVCFEDAQvy+o8c6a7VujOddw== - dependencies: - call-bind "^1.0.2" - define-properties "^1.1.3" - -object-keys@^1.0.12, object-keys@^1.1.1: - version "1.1.1" - resolved "https://registry.yarnpkg.com/object-keys/-/object-keys-1.1.1.tgz#1c47f272df277f3b1daf061677d9c82e2322c60e" - integrity sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA== - -object-keys@~0.4.0: - version "0.4.0" - resolved "https://registry.yarnpkg.com/object-keys/-/object-keys-0.4.0.tgz#28a6aae7428dd2c3a92f3d95f21335dd204e0336" - integrity sha1-KKaq50KN0sOpLz2V8hM13SBOAzY= - -object-visit@^1.0.0: - version "1.0.1" - resolved "https://registry.yarnpkg.com/object-visit/-/object-visit-1.0.1.tgz#f79c4493af0c5377b59fe39d395e41042dd045bb" - integrity sha1-95xEk68MU3e1n+OdOV5BBC3QRbs= - dependencies: - isobject "^3.0.0" - -object.assign@^4.0.4, object.assign@^4.1.0, object.assign@^4.1.2: - version "4.1.2" - resolved "https://registry.yarnpkg.com/object.assign/-/object.assign-4.1.2.tgz#0ed54a342eceb37b38ff76eb831a0e788cb63940" - integrity sha512-ixT2L5THXsApyiUPYKmW+2EHpXXe5Ii3M+f4e+aJFAHao5amFRW6J0OO6c/LU8Be47utCx2GL89hxGB6XSmKuQ== - dependencies: - call-bind "^1.0.0" - define-properties "^1.1.3" - has-symbols "^1.0.1" - object-keys "^1.1.1" - -object.defaults@^1.0.0, object.defaults@^1.1.0: - version "1.1.0" - resolved "https://registry.yarnpkg.com/object.defaults/-/object.defaults-1.1.0.tgz#3a7f868334b407dea06da16d88d5cd29e435fecf" - integrity sha1-On+GgzS0B96gbaFtiNXNKeQ1/s8= - dependencies: - array-each "^1.0.1" - array-slice "^1.0.0" - for-own "^1.0.0" - isobject "^3.0.0" - -object.map@^1.0.0: - version "1.0.1" - resolved "https://registry.yarnpkg.com/object.map/-/object.map-1.0.1.tgz#cf83e59dc8fcc0ad5f4250e1f78b3b81bd801d37" - integrity sha1-z4Plncj8wK1fQlDh94s7gb2AHTc= - dependencies: - for-own "^1.0.0" - make-iterator "^1.0.0" - -object.omit@^2.0.0: - version "2.0.1" - resolved "https://registry.yarnpkg.com/object.omit/-/object.omit-2.0.1.tgz#1a9c744829f39dbb858c76ca3579ae2a54ebd1fa" - integrity sha1-Gpx0SCnznbuFjHbKNXmuKlTr0fo= - dependencies: - for-own "^0.1.4" - is-extendable "^0.1.1" - -object.pick@^1.2.0, object.pick@^1.3.0: - version "1.3.0" - resolved "https://registry.yarnpkg.com/object.pick/-/object.pick-1.3.0.tgz#87a10ac4c1694bd2e1cbf53591a66141fb5dd747" - integrity sha1-h6EKxMFpS9Lhy/U1kaZhQftd10c= - dependencies: - isobject "^3.0.1" - -object.reduce@^1.0.0: - version "1.0.1" - resolved "https://registry.yarnpkg.com/object.reduce/-/object.reduce-1.0.1.tgz#6fe348f2ac7fa0f95ca621226599096825bb03ad" - integrity sha1-b+NI8qx/oPlcpiEiZZkJaCW7A60= - dependencies: - for-own "^1.0.0" - make-iterator "^1.0.0" - -object.values@^1.1.4: - version "1.1.5" - resolved "https://registry.yarnpkg.com/object.values/-/object.values-1.1.5.tgz#959f63e3ce9ef108720333082131e4a459b716ac" - integrity sha512-QUZRW0ilQ3PnPpbNtgdNV1PDbEqLIiSFB3l+EnGtBQ/8SUTLj1PZwtQHABZtLgwpJZTSZhuGLOGk57Drx2IvYg== - dependencies: - call-bind "^1.0.2" - define-properties "^1.1.3" - es-abstract "^1.19.1" - -on-finished@~2.3.0: - version "2.3.0" - resolved "https://registry.yarnpkg.com/on-finished/-/on-finished-2.3.0.tgz#20f1336481b083cd75337992a16971aa2d906947" - integrity sha1-IPEzZIGwg811M3mSoWlxqi2QaUc= - dependencies: - ee-first "1.1.1" - -on-headers@~1.0.2: - version "1.0.2" - resolved "https://registry.yarnpkg.com/on-headers/-/on-headers-1.0.2.tgz#772b0ae6aaa525c399e489adfad90c403eb3c28f" - integrity sha512-pZAE+FJLoyITytdqK0U5s+FIpjN0JP3OzFi/u8Rx+EV5/W+JTWGXG8xFzevE7AjBfDqHv/8vL8qQsIhHnqRkrA== - -once@1.x, once@^1.3.0, once@^1.3.1, once@^1.3.2, once@^1.4.0: - version "1.4.0" - resolved "https://registry.yarnpkg.com/once/-/once-1.4.0.tgz#583b1aa775961d4b113ac17d9c50baef9dd76bd1" - integrity sha1-WDsap3WWHUsROsF9nFC6753Xa9E= - dependencies: - wrappy "1" - -onetime@^2.0.0: - version "2.0.1" - resolved "https://registry.yarnpkg.com/onetime/-/onetime-2.0.1.tgz#067428230fd67443b2794b22bba528b6867962d4" - integrity sha1-BnQoIw/WdEOyeUsiu6UotoZ5YtQ= - dependencies: - mimic-fn "^1.0.0" - -onetime@^5.1.0: - version "5.1.2" - resolved "https://registry.yarnpkg.com/onetime/-/onetime-5.1.2.tgz#d0e96ebb56b07476df1dd9c4806e5237985ca45e" - integrity sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg== - dependencies: - mimic-fn "^2.1.0" - -opener@^1.5.1: - version "1.5.2" - resolved "https://registry.yarnpkg.com/opener/-/opener-1.5.2.tgz#5d37e1f35077b9dcac4301372271afdeb2a13598" - integrity sha512-ur5UIdyw5Y7yEj9wLzhqXiy6GZ3Mwx0yGI+5sMn2r0N0v3cKJvUmFH5yPP+WXh9e0xfyzyJX95D8l088DNFj7A== - -opn@^5.4.0: - version "5.5.0" - resolved "https://registry.yarnpkg.com/opn/-/opn-5.5.0.tgz#fc7164fab56d235904c51c3b27da6758ca3b9bfc" - integrity sha512-PqHpggC9bLV0VeWcdKhkpxY+3JTzetLSqTCWL/z/tFIbI6G8JCjondXklT1JinczLz2Xib62sSp0T/gKT4KksA== - dependencies: - is-wsl "^1.1.0" - -optimist@~0.6.0: - version "0.6.1" - resolved "https://registry.yarnpkg.com/optimist/-/optimist-0.6.1.tgz#da3ea74686fa21a19a111c326e90eb15a0196686" - integrity sha1-2j6nRob6IaGaERwybpDrFaAZZoY= - dependencies: - minimist "~0.0.1" - wordwrap "~0.0.2" - -optionator@^0.8.1, optionator@^0.8.2: - version "0.8.3" - resolved "https://registry.yarnpkg.com/optionator/-/optionator-0.8.3.tgz#84fa1d036fe9d3c7e21d99884b601167ec8fb495" - integrity sha512-+IW9pACdk3XWmmTXG8m3upGUJst5XRGzxMRjXzAuJ1XnIFNvfhjjIuYkDvysnPQ7qzqVzLt78BCruntqRhWQbA== - dependencies: - deep-is "~0.1.3" - fast-levenshtein "~2.0.6" - levn "~0.3.0" - prelude-ls "~1.1.2" - type-check "~0.3.2" - word-wrap "~1.2.3" - -optionator@^0.9.1: - version "0.9.1" - resolved "https://registry.yarnpkg.com/optionator/-/optionator-0.9.1.tgz#4f236a6373dae0566a6d43e1326674f50c291499" - integrity sha512-74RlY5FCnhq4jRxVUPKDaRwrVNXMqsGsiW6AJw4XK8hmtm10wC0ypZBLw5IIp85NZMr91+qd1RvvENwg7jjRFw== - dependencies: - deep-is "^0.1.3" - fast-levenshtein "^2.0.6" - levn "^0.4.1" - prelude-ls "^1.2.1" - type-check "^0.4.0" - word-wrap "^1.2.3" - -ora@^5.4.1: - version "5.4.1" - resolved "https://registry.yarnpkg.com/ora/-/ora-5.4.1.tgz#1b2678426af4ac4a509008e5e4ac9e9959db9e18" - integrity sha512-5b6Y85tPxZZ7QytO+BQzysW31HJku27cRIlkbAXaNx+BdcVi+LlRFmVXzeF6a7JCwJpyw5c4b+YSVImQIrBpuQ== - dependencies: - bl "^4.1.0" - chalk "^4.1.0" - cli-cursor "^3.1.0" - cli-spinners "^2.5.0" - is-interactive "^1.0.0" - is-unicode-supported "^0.1.0" - log-symbols "^4.1.0" - strip-ansi "^6.0.0" - wcwidth "^1.0.1" - -ordered-read-streams@^1.0.0: - version "1.0.1" - resolved "https://registry.yarnpkg.com/ordered-read-streams/-/ordered-read-streams-1.0.1.tgz#77c0cb37c41525d64166d990ffad7ec6a0e1363e" - integrity sha1-d8DLN8QVJdZBZtmQ/61+xqDhNj4= - dependencies: - readable-stream "^2.0.1" - -os-browserify@^0.2.0: - version "0.2.1" - resolved "https://registry.yarnpkg.com/os-browserify/-/os-browserify-0.2.1.tgz#63fc4ccee5d2d7763d26bbf8601078e6c2e0044f" - integrity sha1-Y/xMzuXS13Y9Jrv4YBB45sLgBE8= - -os-browserify@^0.3.0: - version "0.3.0" - resolved "https://registry.yarnpkg.com/os-browserify/-/os-browserify-0.3.0.tgz#854373c7f5c2315914fc9bfc6bd8238fdda1ec27" - integrity sha1-hUNzx/XCMVkU/Jv8a9gjj92h7Cc= - -os-locale@^1.4.0: - version "1.4.0" - resolved "https://registry.yarnpkg.com/os-locale/-/os-locale-1.4.0.tgz#20f9f17ae29ed345e8bde583b13d2009803c14d9" - integrity sha1-IPnxeuKe00XoveWDsT0gCYA8FNk= - dependencies: - lcid "^1.0.0" - -os-locale@^2.0.0: - version "2.1.0" - resolved "https://registry.yarnpkg.com/os-locale/-/os-locale-2.1.0.tgz#42bc2900a6b5b8bd17376c8e882b65afccf24bf2" - integrity sha512-3sslG3zJbEYcaC4YVAvDorjGxc7tv6KVATnLPZONiljsUncvihe9BQoVCEs0RZ1kmf4Hk9OBqlZfJZWI4GanKA== - dependencies: - execa "^0.7.0" - lcid "^1.0.0" - mem "^1.1.0" - -os-tmpdir@~1.0.2: - version "1.0.2" - resolved "https://registry.yarnpkg.com/os-tmpdir/-/os-tmpdir-1.0.2.tgz#bbe67406c79aa85c5cfec766fe5734555dfa1274" - integrity sha1-u+Z0BseaqFxc/sdm/lc0VV36EnQ= - -p-cancelable@^2.0.0: - version "2.1.1" - resolved "https://registry.yarnpkg.com/p-cancelable/-/p-cancelable-2.1.1.tgz#aab7fbd416582fa32a3db49859c122487c5ed2cf" - integrity sha512-BZOr3nRQHOntUjTrH8+Lh54smKHoHyur8We1V8DSMVrl5A2malOOwuJRnKRDjSnkoeBh4at6BwEnb5I7Jl31wg== - -p-finally@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/p-finally/-/p-finally-1.0.0.tgz#3fbcfb15b899a44123b34b6dcc18b724336a2cae" - integrity sha1-P7z7FbiZpEEjs0ttzBi3JDNqLK4= - -p-iteration@^1.1.8: - version "1.1.8" - resolved "https://registry.yarnpkg.com/p-iteration/-/p-iteration-1.1.8.tgz#14df726d55af368beba81bcc92a26bb1b48e714a" - integrity sha512-IMFBSDIYcPNnW7uWYGrBqmvTiq7W0uB0fJn6shQZs7dlF3OvrHOre+JT9ikSZ7gZS3vWqclVgoQSvToJrns7uQ== - -p-limit@^1.1.0: - version "1.3.0" - resolved "https://registry.yarnpkg.com/p-limit/-/p-limit-1.3.0.tgz#b86bd5f0c25690911c7590fcbfc2010d54b3ccb8" - integrity sha512-vvcXsLAJ9Dr5rQOPk7toZQZJApBl2K4J6dANSsEuh6QI41JYcsS/qhTGa9ErIUUgK3WNQoJYvylxvjqmiqEA9Q== - dependencies: - p-try "^1.0.0" - -p-limit@^2.0.0, p-limit@^2.2.0: - version "2.3.0" - resolved "https://registry.yarnpkg.com/p-limit/-/p-limit-2.3.0.tgz#3dd33c647a214fdfffd835933eb086da0dc21db1" - integrity sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w== - dependencies: - p-try "^2.0.0" - -p-limit@^3.0.2: - version "3.1.0" - resolved "https://registry.yarnpkg.com/p-limit/-/p-limit-3.1.0.tgz#e1daccbe78d0d1388ca18c64fea38e3e57e3706b" - integrity sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ== - dependencies: - yocto-queue "^0.1.0" - -p-locate@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/p-locate/-/p-locate-2.0.0.tgz#20a0103b222a70c8fd39cc2e580680f3dde5ec43" - integrity sha1-IKAQOyIqcMj9OcwuWAaA893l7EM= - dependencies: - p-limit "^1.1.0" - -p-locate@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/p-locate/-/p-locate-3.0.0.tgz#322d69a05c0264b25997d9f40cd8a891ab0064a4" - integrity sha512-x+12w/To+4GFfgJhBEpiDcLozRJGegY+Ei7/z0tSLkMmxGZNybVMSfWj9aJn8Z5Fc7dBUNJOOVgPv2H7IwulSQ== - dependencies: - p-limit "^2.0.0" - -p-locate@^4.1.0: - version "4.1.0" - resolved "https://registry.yarnpkg.com/p-locate/-/p-locate-4.1.0.tgz#a3428bb7088b3a60292f66919278b7c297ad4f07" - integrity sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A== - dependencies: - p-limit "^2.2.0" - -p-locate@^5.0.0: - version "5.0.0" - resolved "https://registry.yarnpkg.com/p-locate/-/p-locate-5.0.0.tgz#83c8315c6785005e3bd021839411c9e110e6d834" - integrity sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw== - dependencies: - p-limit "^3.0.2" - -p-try@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/p-try/-/p-try-1.0.0.tgz#cbc79cdbaf8fd4228e13f621f2b1a237c1b207b3" - integrity sha1-y8ec26+P1CKOE/Yh8rGiN8GyB7M= - -p-try@^2.0.0: - version "2.2.0" - resolved "https://registry.yarnpkg.com/p-try/-/p-try-2.2.0.tgz#cb2868540e313d61de58fafbe35ce9004d5540e6" - integrity sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ== - -pako@~0.2.0: - version "0.2.9" - resolved "https://registry.yarnpkg.com/pako/-/pako-0.2.9.tgz#f3f7522f4ef782348da8161bad9ecfd51bf83a75" - integrity sha1-8/dSL073gjSNqBYbrZ7P1Rv4OnU= - -pako@~1.0.5: - version "1.0.11" - resolved "https://registry.yarnpkg.com/pako/-/pako-1.0.11.tgz#6c9599d340d54dfd3946380252a35705a6b992bf" - integrity sha512-4hLB8Py4zZce5s4yd9XzopqwVv/yGNhV1Bl8NTmCq1763HeK2+EwVTv+leGeL13Dnh2wfbqowVPXCIO0z4taYw== - -parent-module@^1.0.0: - version "1.0.1" - resolved "https://registry.yarnpkg.com/parent-module/-/parent-module-1.0.1.tgz#691d2709e78c79fae3a156622452d00762caaaa2" - integrity sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g== - dependencies: - callsites "^3.0.0" - -parse-asn1@^5.0.0, parse-asn1@^5.1.5: - version "5.1.6" - resolved "https://registry.yarnpkg.com/parse-asn1/-/parse-asn1-5.1.6.tgz#385080a3ec13cb62a62d39409cb3e88844cdaed4" - integrity sha512-RnZRo1EPU6JBnra2vGHj0yhp6ebyjBZpmUCLHWiFhxlzvBCCpAuZ7elsBp1PVAbQN0/04VD/19rfzlBSwLstMw== - dependencies: - asn1.js "^5.2.0" - browserify-aes "^1.0.0" - evp_bytestokey "^1.0.0" - pbkdf2 "^3.0.3" - safe-buffer "^5.1.1" - -parse-entities@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/parse-entities/-/parse-entities-2.0.0.tgz#53c6eb5b9314a1f4ec99fa0fdf7ce01ecda0cbe8" - integrity sha512-kkywGpCcRYhqQIchaWqZ875wzpS/bMKhz5HnN3p7wveJTkTtyAB/AlnS0f8DFSqYW1T82t6yEAkEcB+A1I3MbQ== - dependencies: - character-entities "^1.0.0" - character-entities-legacy "^1.0.0" - character-reference-invalid "^1.0.0" - is-alphanumerical "^1.0.0" - is-decimal "^1.0.0" - is-hexadecimal "^1.0.0" - -parse-filepath@^1.0.1, parse-filepath@^1.0.2: - version "1.0.2" - resolved "https://registry.yarnpkg.com/parse-filepath/-/parse-filepath-1.0.2.tgz#a632127f53aaf3d15876f5872f3ffac763d6c891" - integrity sha1-pjISf1Oq89FYdvWHLz/6x2PWyJE= - dependencies: - is-absolute "^1.0.0" - map-cache "^0.2.0" - path-root "^0.1.1" - -parse-glob@^3.0.4: - version "3.0.4" - resolved "https://registry.yarnpkg.com/parse-glob/-/parse-glob-3.0.4.tgz#b2c376cfb11f35513badd173ef0bb6e3a388391c" - integrity sha1-ssN2z7EfNVE7rdFz7wu246OIORw= - dependencies: - glob-base "^0.3.0" - is-dotfile "^1.0.0" - is-extglob "^1.0.0" - is-glob "^2.0.0" - -parse-json@^2.2.0: - version "2.2.0" - resolved "https://registry.yarnpkg.com/parse-json/-/parse-json-2.2.0.tgz#f480f40434ef80741f8469099f8dea18f55a4dc9" - integrity sha1-9ID0BDTvgHQfhGkJn43qGPVaTck= - dependencies: - error-ex "^1.2.0" - -parse-json@^4.0.0: - version "4.0.0" - resolved "https://registry.yarnpkg.com/parse-json/-/parse-json-4.0.0.tgz#be35f5425be1f7f6c747184f98a788cb99477ee0" - integrity sha1-vjX1Qlvh9/bHRxhPmKeIy5lHfuA= - dependencies: - error-ex "^1.3.1" - json-parse-better-errors "^1.0.1" - -parse-json@^5.0.0: - version "5.2.0" - resolved "https://registry.yarnpkg.com/parse-json/-/parse-json-5.2.0.tgz#c76fc66dee54231c962b22bcc8a72cf2f99753cd" - integrity sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg== - dependencies: - "@babel/code-frame" "^7.0.0" - error-ex "^1.3.1" - json-parse-even-better-errors "^2.3.0" - lines-and-columns "^1.1.6" - -parse-ms@^2.1.0: - version "2.1.0" - resolved "https://registry.yarnpkg.com/parse-ms/-/parse-ms-2.1.0.tgz#348565a753d4391fa524029956b172cb7753097d" - integrity sha512-kHt7kzLoS9VBZfUsiKjv43mr91ea+U05EyKkEtqp7vNbHxmaVuEqN7XxeEVnGrMtYOAxGrDElSi96K7EgO1zCA== - -parse-node-version@^1.0.0: - version "1.0.1" - resolved "https://registry.yarnpkg.com/parse-node-version/-/parse-node-version-1.0.1.tgz#e2b5dbede00e7fa9bc363607f53327e8b073189b" - integrity sha512-3YHlOa/JgH6Mnpr05jP9eDG254US9ek25LyIxZlDItp2iJtwyaXQb57lBYLdT3MowkUFYEV2XXNAYIPlESvJlA== - -parse-passwd@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/parse-passwd/-/parse-passwd-1.0.0.tgz#6d5b934a456993b23d37f40a382d6f1666a8e5c6" - integrity sha1-bVuTSkVpk7I9N/QKOC1vFmao5cY= - -parse-path@^4.0.0: - version "4.0.3" - resolved "https://registry.yarnpkg.com/parse-path/-/parse-path-4.0.3.tgz#82d81ec3e071dcc4ab49aa9f2c9c0b8966bb22bf" - integrity sha512-9Cepbp2asKnWTJ9x2kpw6Fe8y9JDbqwahGCTvklzd/cEq5C5JC59x2Xb0Kx+x0QZ8bvNquGO8/BWP0cwBHzSAA== - dependencies: - is-ssh "^1.3.0" - protocols "^1.4.0" - qs "^6.9.4" - query-string "^6.13.8" - -parse-url@^6.0.0: - version "6.0.0" - resolved "https://registry.yarnpkg.com/parse-url/-/parse-url-6.0.0.tgz#f5dd262a7de9ec00914939220410b66cff09107d" - integrity sha512-cYyojeX7yIIwuJzledIHeLUBVJ6COVLeT4eF+2P6aKVzwvgKQPndCBv3+yQ7pcWjqToYwaligxzSYNNmGoMAvw== - dependencies: - is-ssh "^1.3.0" - normalize-url "^6.1.0" - parse-path "^4.0.0" - protocols "^1.4.0" - -parseurl@~1.3.2, parseurl@~1.3.3: - version "1.3.3" - resolved "https://registry.yarnpkg.com/parseurl/-/parseurl-1.3.3.tgz#9da19e7bee8d12dff0513ed5b76957793bc2e8d4" - integrity sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ== - -pascalcase@^0.1.1: - version "0.1.1" - resolved "https://registry.yarnpkg.com/pascalcase/-/pascalcase-0.1.1.tgz#b363e55e8006ca6fe21784d2db22bd15d7917f14" - integrity sha1-s2PlXoAGym/iF4TS2yK9FdeRfxQ= - -path-browserify@0.0.0: - version "0.0.0" - resolved "https://registry.yarnpkg.com/path-browserify/-/path-browserify-0.0.0.tgz#a0b870729aae214005b7d5032ec2cbbb0fb4451a" - integrity sha1-oLhwcpquIUAFt9UDLsLLuw+0RRo= - -path-browserify@0.0.1: - version "0.0.1" - resolved "https://registry.yarnpkg.com/path-browserify/-/path-browserify-0.0.1.tgz#e6c4ddd7ed3aa27c68a20cc4e50e1a4ee83bbc4a" - integrity sha512-BapA40NHICOS+USX9SN4tyhq+A2RrN/Ws5F0Z5aMHDp98Fl86lX8Oti8B7uN93L4Ifv4fHOEA+pQw87gmMO/lQ== - -path-dirname@^1.0.0: - version "1.0.2" - resolved "https://registry.yarnpkg.com/path-dirname/-/path-dirname-1.0.2.tgz#cc33d24d525e099a5388c0336c6e32b9160609e0" - integrity sha1-zDPSTVJeCZpTiMAzbG4yuRYGCeA= - -path-exists@^2.0.0: - version "2.1.0" - resolved "https://registry.yarnpkg.com/path-exists/-/path-exists-2.1.0.tgz#0feb6c64f0fc518d9a754dd5efb62c7022761f4b" - integrity sha1-D+tsZPD8UY2adU3V77YscCJ2H0s= - dependencies: - pinkie-promise "^2.0.0" - -path-exists@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/path-exists/-/path-exists-3.0.0.tgz#ce0ebeaa5f78cb18925ea7d810d7b59b010fd515" - integrity sha1-zg6+ql94yxiSXqfYENe1mwEP1RU= - -path-exists@^4.0.0: - version "4.0.0" - resolved "https://registry.yarnpkg.com/path-exists/-/path-exists-4.0.0.tgz#513bdbe2d3b95d7762e8c1137efa195c6c61b5b3" - integrity sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w== - -path-is-absolute@^1.0.0: - version "1.0.1" - resolved "https://registry.yarnpkg.com/path-is-absolute/-/path-is-absolute-1.0.1.tgz#174b9268735534ffbc7ace6bf53a5a9e1b5c5f5f" - integrity sha1-F0uSaHNVNP+8es5r9TpanhtcX18= - -path-is-inside@^1.0.2: - version "1.0.2" - resolved "https://registry.yarnpkg.com/path-is-inside/-/path-is-inside-1.0.2.tgz#365417dede44430d1c11af61027facf074bdfc53" - integrity sha1-NlQX3t5EQw0cEa9hAn+s8HS9/FM= - -path-key@^2.0.0, path-key@^2.0.1: - version "2.0.1" - resolved "https://registry.yarnpkg.com/path-key/-/path-key-2.0.1.tgz#411cadb574c5a140d3a4b1910d40d80cc9f40b40" - integrity sha1-QRyttXTFoUDTpLGRDUDYDMn0C0A= - -path-key@^3.1.0: - version "3.1.1" - resolved "https://registry.yarnpkg.com/path-key/-/path-key-3.1.1.tgz#581f6ade658cbba65a0d3380de7753295054f375" - integrity sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q== - -path-parse@^1.0.6: - version "1.0.7" - resolved "https://registry.yarnpkg.com/path-parse/-/path-parse-1.0.7.tgz#fbc114b60ca42b30d9daf5858e4bd68bbedb6735" - integrity sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw== - -path-root-regex@^0.1.0: - version "0.1.2" - resolved "https://registry.yarnpkg.com/path-root-regex/-/path-root-regex-0.1.2.tgz#bfccdc8df5b12dc52c8b43ec38d18d72c04ba96d" - integrity sha1-v8zcjfWxLcUsi0PsONGNcsBLqW0= - -path-root@^0.1.1: - version "0.1.1" - resolved "https://registry.yarnpkg.com/path-root/-/path-root-0.1.1.tgz#9a4a6814cac1c0cd73360a95f32083c8ea4745b7" - integrity sha1-mkpoFMrBwM1zNgqV8yCDyOpHRbc= - dependencies: - path-root-regex "^0.1.0" - -path-to-regexp@0.1.7: - version "0.1.7" - resolved "https://registry.yarnpkg.com/path-to-regexp/-/path-to-regexp-0.1.7.tgz#df604178005f522f15eb4490e7247a1bfaa67f8c" - integrity sha1-32BBeABfUi8V60SQ5yR6G/qmf4w= - -path-to-regexp@^1.7.0: - version "1.8.0" - resolved "https://registry.yarnpkg.com/path-to-regexp/-/path-to-regexp-1.8.0.tgz#887b3ba9d84393e87a0a0b9f4cb756198b53548a" - integrity sha512-n43JRhlUKUAlibEJhPeir1ncUID16QnEjNpwzNdO3Lm4ywrBpBZ5oLD0I6br9evr1Y9JTqwRtAh7JLoOzAQdVA== - dependencies: - isarray "0.0.1" - -path-type@^1.0.0: - version "1.1.0" - resolved "https://registry.yarnpkg.com/path-type/-/path-type-1.1.0.tgz#59c44f7ee491da704da415da5a4070ba4f8fe441" - integrity sha1-WcRPfuSR2nBNpBXaWkBwuk+P5EE= - dependencies: - graceful-fs "^4.1.2" - pify "^2.0.0" - pinkie-promise "^2.0.0" - -path-type@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/path-type/-/path-type-2.0.0.tgz#f012ccb8415b7096fc2daa1054c3d72389594c73" - integrity sha1-8BLMuEFbcJb8LaoQVMPXI4lZTHM= - dependencies: - pify "^2.0.0" - -path-type@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/path-type/-/path-type-3.0.0.tgz#cef31dc8e0a1a3bb0d105c0cd97cf3bf47f4e36f" - integrity sha512-T2ZUsdZFHgA3u4e5PfPbjd7HDDpxPnQb5jN0SrDsjNSuVXHJqtwTnWqG0B1jZrgmJ/7lj1EmVIByWt1gxGkWvg== - dependencies: - pify "^3.0.0" - -pathval@^1.1.1: - version "1.1.1" - resolved "https://registry.yarnpkg.com/pathval/-/pathval-1.1.1.tgz#8534e77a77ce7ac5a2512ea21e0fdb8fcf6c3d8d" - integrity sha512-Dp6zGqpTdETdR63lehJYPeIOqpiNBNtc7BpWSLrOje7UaIsE5aY92r/AunQA7rsXvet3lrJ3JnZX29UPTKXyKQ== - -pause-stream@0.0.11: - version "0.0.11" - resolved "https://registry.yarnpkg.com/pause-stream/-/pause-stream-0.0.11.tgz#fe5a34b0cbce12b5aa6a2b403ee2e73b602f1445" - integrity sha1-/lo0sMvOErWqaitAPuLnO2AvFEU= - dependencies: - through "~2.3" - -pbkdf2-compat@2.0.1: - version "2.0.1" - resolved "https://registry.yarnpkg.com/pbkdf2-compat/-/pbkdf2-compat-2.0.1.tgz#b6e0c8fa99494d94e0511575802a59a5c142f288" - integrity sha1-tuDI+plJTZTgURV1gCpZpcFC8og= - -pbkdf2@^3.0.3: - version "3.1.2" - resolved "https://registry.yarnpkg.com/pbkdf2/-/pbkdf2-3.1.2.tgz#dd822aa0887580e52f1a039dc3eda108efae3075" - integrity sha512-iuh7L6jA7JEGu2WxDwtQP1ddOpaJNC4KlDEFfdQajSGgGPNi4OyDc2R7QnbY2bR9QjBVGwgvTdNJZoE7RaxUMA== - dependencies: - create-hash "^1.1.2" - create-hmac "^1.1.4" - ripemd160 "^2.0.1" - safe-buffer "^5.0.1" - sha.js "^2.4.8" - -pend@~1.2.0: - version "1.2.0" - resolved "https://registry.yarnpkg.com/pend/-/pend-1.2.0.tgz#7a57eb550a6783f9115331fcf4663d5c8e007a50" - integrity sha1-elfrVQpng/kRUzH89GY9XI4AelA= - -performance-now@^2.1.0: - version "2.1.0" - resolved "https://registry.yarnpkg.com/performance-now/-/performance-now-2.1.0.tgz#6309f4e0e5fa913ec1c69307ae364b4b377c9e7b" - integrity sha1-Ywn04OX6kT7BxpMHrjZLSzd8nns= - -picomatch@^2.0.4, picomatch@^2.2.1, picomatch@^2.2.3: - version "2.3.0" - resolved "https://registry.yarnpkg.com/picomatch/-/picomatch-2.3.0.tgz#f1f061de8f6a4bf022892e2d128234fb98302972" - integrity sha512-lY1Q/PiJGC2zOv/z391WOTD+Z02bCgsFfvxoXXf6h7kv9o+WmsmzYqrAwY63sNgOxE4xEdq0WyUnXfKeBrSvYw== - -pify@^2.0.0, pify@^2.3.0: - version "2.3.0" - resolved "https://registry.yarnpkg.com/pify/-/pify-2.3.0.tgz#ed141a6ac043a849ea588498e7dca8b15330e90c" - integrity sha1-7RQaasBDqEnqWISY59yosVMw6Qw= - -pify@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/pify/-/pify-3.0.0.tgz#e5a4acd2c101fdf3d9a4d07f0dbc4db49dd28176" - integrity sha1-5aSs0sEB/fPZpNB/DbxNtJ3SgXY= - -pify@^4.0.1: - version "4.0.1" - resolved "https://registry.yarnpkg.com/pify/-/pify-4.0.1.tgz#4b2cd25c50d598735c50292224fd8c6df41e3231" - integrity sha512-uB80kBFb/tfd68bVleG9T5GGsGPjJrLAUpR5PZIrhBnIaRTQRjqdJSsIKkOP6OAIFbj7GOrcudc5pNjZ+geV2g== - -pify@^5.0.0: - version "5.0.0" - resolved "https://registry.yarnpkg.com/pify/-/pify-5.0.0.tgz#1f5eca3f5e87ebec28cc6d54a0e4aaf00acc127f" - integrity sha512-eW/gHNMlxdSP6dmG6uJip6FXN0EQBwm2clYYd8Wul42Cwu/DK8HEftzsapcNdYe2MfLiIwZqsDk2RDEsTE79hA== - -pinkie-promise@^2.0.0: - version "2.0.1" - resolved "https://registry.yarnpkg.com/pinkie-promise/-/pinkie-promise-2.0.1.tgz#2135d6dfa7a358c069ac9b178776288228450ffa" - integrity sha1-ITXW36ejWMBprJsXh3YogihFD/o= - dependencies: - pinkie "^2.0.0" - -pinkie@^2.0.0: - version "2.0.4" - resolved "https://registry.yarnpkg.com/pinkie/-/pinkie-2.0.4.tgz#72556b80cfa0d48a974e80e77248e80ed4f7f870" - integrity sha1-clVrgM+g1IqXToDnckjoDtT3+HA= - -pkg-dir@4.2.0, pkg-dir@^4.1.0: - version "4.2.0" - resolved "https://registry.yarnpkg.com/pkg-dir/-/pkg-dir-4.2.0.tgz#f099133df7ede422e81d1d8448270eeb3e4261f3" - integrity sha512-HRDzbaKjC+AOWVXxAU/x54COGeIv9eb+6CkDSQoNTt4XyWoIJvuPsXizxu/Fr23EiekbtZwmh1IcIG/l/a10GQ== - dependencies: - find-up "^4.0.0" - -pkg-dir@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/pkg-dir/-/pkg-dir-2.0.0.tgz#f6d5d1109e19d63edf428e0bd57e12777615334b" - integrity sha1-9tXREJ4Z1j7fQo4L1X4Sd3YVM0s= - dependencies: - find-up "^2.1.0" - -pkg-up@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/pkg-up/-/pkg-up-2.0.0.tgz#c819ac728059a461cab1c3889a2be3c49a004d7f" - integrity sha1-yBmscoBZpGHKscOImivjxJoATX8= - dependencies: - find-up "^2.1.0" - -plugin-error@^1.0.0, plugin-error@^1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/plugin-error/-/plugin-error-1.0.1.tgz#77016bd8919d0ac377fdcdd0322328953ca5781c" - integrity sha512-L1zP0dk7vGweZME2i+EeakvUNqSrdiI3F91TwEoYiGrAfUXmVv6fJIq4g82PAXxNsWOp0J7ZqQy/3Szz0ajTxA== - dependencies: - ansi-colors "^1.0.1" - arr-diff "^4.0.0" - arr-union "^3.1.0" - extend-shallow "^3.0.2" - -pluralize@^7.0.0: - version "7.0.0" - resolved "https://registry.yarnpkg.com/pluralize/-/pluralize-7.0.0.tgz#298b89df8b93b0221dbf421ad2b1b1ea23fc6777" - integrity sha512-ARhBOdzS3e41FbkW/XWrTEtukqqLoK5+Z/4UeDaLuSW+39JPeFgs4gCGqsrJHVZX0fUrx//4OF0K1CUGwlIFow== - -posix-character-classes@^0.1.0: - version "0.1.1" - resolved "https://registry.yarnpkg.com/posix-character-classes/-/posix-character-classes-0.1.1.tgz#01eac0fe3b5af71a2a6c02feabb8c1fef7e00eab" - integrity sha1-AerA/jta9xoqbAL+q7jB/vfgDqs= - -postcss@^7.0.16: - version "7.0.38" - resolved "https://registry.yarnpkg.com/postcss/-/postcss-7.0.38.tgz#5365a9c5126643d977046ad239f60eadda2491d6" - integrity sha512-wNrSHWjHDQJR/IZL5IKGxRtFgrYNaAA/UrkW2WqbtZO6uxSLMxMN+s2iqUMwnAWm3fMROlDYZB41dr0Mt7vBwQ== - dependencies: - nanocolors "^0.2.2" - source-map "^0.6.1" - -postcss@^8.1.10: - version "8.3.8" - resolved "https://registry.yarnpkg.com/postcss/-/postcss-8.3.8.tgz#9ebe2a127396b4b4570ae9f7770e7fb83db2bac1" - integrity sha512-GT5bTjjZnwDifajzczOC+r3FI3Cu+PgPvrsjhQdRqa2kTJ4968/X9CUce9xttIB0xOs5c6xf0TCWZo/y9lF6bA== - dependencies: - nanocolors "^0.2.2" - nanoid "^3.1.25" - source-map-js "^0.6.2" - -prelude-ls@^1.2.1: - version "1.2.1" - resolved "https://registry.yarnpkg.com/prelude-ls/-/prelude-ls-1.2.1.tgz#debc6489d7a6e6b0e7611888cec880337d316396" - integrity sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g== - -prelude-ls@~1.1.2: - version "1.1.2" - resolved "https://registry.yarnpkg.com/prelude-ls/-/prelude-ls-1.1.2.tgz#21932a549f5e52ffd9a827f570e04be62a97da54" - integrity sha1-IZMqVJ9eUv/ZqCf1cOBL5iqX2lQ= - -preserve@^0.2.0: - version "0.2.0" - resolved "https://registry.yarnpkg.com/preserve/-/preserve-0.2.0.tgz#815ed1f6ebc65926f865b310c0713bcb3315ce4b" - integrity sha1-gV7R9uvGWSb4ZbMQwHE7yzMVzks= - -pretty-format@^27.2.4: - version "27.2.4" - resolved "https://registry.yarnpkg.com/pretty-format/-/pretty-format-27.2.4.tgz#08ea39c5eab41b082852d7093059a091f6ddc748" - integrity sha512-NUjw22WJHldzxyps2YjLZkUj6q1HvjqFezkB9Y2cklN8NtVZN/kZEXGZdFw4uny3oENzV5EEMESrkI0YDUH8vg== - dependencies: - "@jest/types" "^27.2.4" - ansi-regex "^5.0.1" - ansi-styles "^5.0.0" - react-is "^17.0.1" - -pretty-hrtime@^1.0.0: - version "1.0.3" - resolved "https://registry.yarnpkg.com/pretty-hrtime/-/pretty-hrtime-1.0.3.tgz#b7e3ea42435a4c9b2759d99e0f201eb195802ee1" - integrity sha1-t+PqQkNaTJsnWdmeDyAesZWALuE= - -pretty-ms@^7.0.0: - version "7.0.1" - resolved "https://registry.yarnpkg.com/pretty-ms/-/pretty-ms-7.0.1.tgz#7d903eaab281f7d8e03c66f867e239dc32fb73e8" - integrity sha512-973driJZvxiGOQ5ONsFhOF/DtzPMOMtgC11kCpUrPGMTgqp2q/1gwzCquocrN33is0VZ5GFHXZYMM9l6h67v2Q== - dependencies: - parse-ms "^2.1.0" - -printj@~1.1.0: - version "1.1.2" - resolved "https://registry.yarnpkg.com/printj/-/printj-1.1.2.tgz#d90deb2975a8b9f600fb3a1c94e3f4c53c78a222" - integrity sha512-zA2SmoLaxZyArQTOPj5LXecR+RagfPSU5Kw1qP+jkWeNlrq+eJZyY2oS68SU1Z/7/myXM4lo9716laOFAVStCQ== - -process-nextick-args@^2.0.0, process-nextick-args@~2.0.0: - version "2.0.1" - resolved "https://registry.yarnpkg.com/process-nextick-args/-/process-nextick-args-2.0.1.tgz#7820d9b16120cc55ca9ae7792680ae7dba6d7fe2" - integrity sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag== - -process-nextick-args@~1.0.6: - version "1.0.7" - resolved "https://registry.yarnpkg.com/process-nextick-args/-/process-nextick-args-1.0.7.tgz#150e20b756590ad3f91093f25a4f2ad8bff30ba3" - integrity sha1-FQ4gt1ZZCtP5EJPyWk8q2L/zC6M= - -process@^0.11.0, process@^0.11.10: - version "0.11.10" - resolved "https://registry.yarnpkg.com/process/-/process-0.11.10.tgz#7332300e840161bda3e69a1d1d91a7d4bc16f182" - integrity sha1-czIwDoQBYb2j5podHZGn1LwW8YI= - -progress@2.0.1: - version "2.0.1" - resolved "https://registry.yarnpkg.com/progress/-/progress-2.0.1.tgz#c9242169342b1c29d275889c95734621b1952e31" - integrity sha512-OE+a6vzqazc+K6LxJrX5UPyKFvGnL5CYmq2jFGNIBWHpc4QyE49/YOumcrpQFJpfejmvRtbJzgO1zPmMCqlbBg== - -progress@^2.0.0: - version "2.0.3" - resolved "https://registry.yarnpkg.com/progress/-/progress-2.0.3.tgz#7e8cf8d8f5b8f239c1bc68beb4eb78567d572ef8" - integrity sha512-7PiHtLll5LdnKIMw100I+8xJXR5gW2QwWYkT6iJva0bXitZKa/XMrSbdmg3r2Xnaidz9Qumd0VPaMrZlF9V9sA== - -property-information@^5.0.0: - version "5.6.0" - resolved "https://registry.yarnpkg.com/property-information/-/property-information-5.6.0.tgz#61675545fb23002f245c6540ec46077d4da3ed69" - integrity sha512-YUHSPk+A30YPv+0Qf8i9Mbfe/C0hdPXk1s1jPVToV8pk8BQtpw10ct89Eo7OWkutrwqvT0eicAxlOg3dOAu8JA== - dependencies: - xtend "^4.0.0" - -protocols@^1.1.0, protocols@^1.4.0: - version "1.4.8" - resolved "https://registry.yarnpkg.com/protocols/-/protocols-1.4.8.tgz#48eea2d8f58d9644a4a32caae5d5db290a075ce8" - integrity sha512-IgjKyaUSjsROSO8/D49Ab7hP8mJgTYcqApOqdPhLoPxAplXmkp+zRvsrSQjFn5by0rhm4VH0GAUELIPpx7B1yg== - -proxy-addr@~2.0.5: - version "2.0.7" - resolved "https://registry.yarnpkg.com/proxy-addr/-/proxy-addr-2.0.7.tgz#f19fe69ceab311eeb94b42e70e8c2070f9ba1025" - integrity sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg== - dependencies: - forwarded "0.2.0" - ipaddr.js "1.9.1" - -proxy-from-env@1.1.0: - version "1.1.0" - resolved "https://registry.yarnpkg.com/proxy-from-env/-/proxy-from-env-1.1.0.tgz#e102f16ca355424865755d2c9e8ea4f24d58c3e2" - integrity sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg== - -prr@~1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/prr/-/prr-1.0.1.tgz#d3fc114ba06995a45ec6893f484ceb1d78f5f476" - integrity sha1-0/wRS6BplaRexok/SEzrHXj19HY= - -ps-tree@=1.2.0: - version "1.2.0" - resolved "https://registry.yarnpkg.com/ps-tree/-/ps-tree-1.2.0.tgz#5e7425b89508736cdd4f2224d028f7bb3f722ebd" - integrity sha512-0VnamPPYHl4uaU/nSFeZZpR21QAWRz+sRv4iW9+v/GS/J5U5iZB5BNN6J0RMoOvdx2gWM2+ZFMIm58q24e4UYA== - dependencies: - event-stream "=3.3.4" - -pseudomap@^1.0.2: - version "1.0.2" - resolved "https://registry.yarnpkg.com/pseudomap/-/pseudomap-1.0.2.tgz#f052a28da70e618917ef0a8ac34c1ae5a68286b3" - integrity sha1-8FKijacOYYkX7wqKw0wa5aaChrM= - -psl@^1.1.28: - version "1.8.0" - resolved "https://registry.yarnpkg.com/psl/-/psl-1.8.0.tgz#9326f8bcfb013adcc005fdff056acce020e51c24" - integrity sha512-RIdOzyoavK+hA18OGGWDqUTsCLhtA7IcZ/6NCs4fFJaHBDab+pDDmDIByWFRQJq2Cd7r1OoQxBGKOaztq+hjIQ== - -public-encrypt@^4.0.0: - version "4.0.3" - resolved "https://registry.yarnpkg.com/public-encrypt/-/public-encrypt-4.0.3.tgz#4fcc9d77a07e48ba7527e7cbe0de33d0701331e0" - integrity sha512-zVpa8oKZSz5bTMTFClc1fQOnyyEzpl5ozpi1B5YcvBrdohMjH2rfsBtyXcuNuwjsDIXmBYlF2N5FlJYhR29t8Q== - dependencies: - bn.js "^4.1.0" - browserify-rsa "^4.0.0" - create-hash "^1.1.0" - parse-asn1 "^5.0.0" - randombytes "^2.0.1" - safe-buffer "^5.1.2" - -pump@^2.0.0: - version "2.0.1" - resolved "https://registry.yarnpkg.com/pump/-/pump-2.0.1.tgz#12399add6e4cf7526d973cbc8b5ce2e2908b3909" - integrity sha512-ruPMNRkN3MHP1cWJc9OWr+T/xDP0jhXYCLfJcBuX54hhfIBnaQmAUMfDcG4DM5UMWByBbJY69QSphm3jtDKIkA== - dependencies: - end-of-stream "^1.1.0" - once "^1.3.1" - -pump@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/pump/-/pump-3.0.0.tgz#b4a2116815bde2f4e1ea602354e8c75565107a64" - integrity sha512-LwZy+p3SFs1Pytd/jYct4wpv49HiYCqd9Rlc5ZVdk0V+8Yzv6jR5Blk3TRmPL1ft69TxP0IMZGJ+WPFU2BFhww== - dependencies: - end-of-stream "^1.1.0" - once "^1.3.1" - -pumpify@^1.3.5: - version "1.5.1" - resolved "https://registry.yarnpkg.com/pumpify/-/pumpify-1.5.1.tgz#36513be246ab27570b1a374a5ce278bfd74370ce" - integrity sha512-oClZI37HvuUJJxSKKrC17bZ9Cu0ZYhEAGPsPUy9KlMUmv9dKX2o77RUmq7f3XjIxbwyGwYzbzQ1L2Ks8sIradQ== - dependencies: - duplexify "^3.6.0" - inherits "^2.0.3" - pump "^2.0.0" - -punycode@1.3.2: - version "1.3.2" - resolved "https://registry.yarnpkg.com/punycode/-/punycode-1.3.2.tgz#9653a036fb7c1ee42342f2325cceefea3926c48d" - integrity sha1-llOgNvt8HuQjQvIyXM7v6jkmxI0= - -punycode@^1.2.4: - version "1.4.1" - resolved "https://registry.yarnpkg.com/punycode/-/punycode-1.4.1.tgz#c0d5a63b2718800ad8e1eb0fa5269c84dd41845e" - integrity sha1-wNWmOycYgArY4esPpSachN1BhF4= - -punycode@^2.1.0, punycode@^2.1.1: - version "2.1.1" - resolved "https://registry.yarnpkg.com/punycode/-/punycode-2.1.1.tgz#b58b010ac40c22c5657616c8d2c2c02c7bf479ec" - integrity sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A== - -puppeteer-core@^10.1.0: - version "10.4.0" - resolved "https://registry.yarnpkg.com/puppeteer-core/-/puppeteer-core-10.4.0.tgz#4e2da57c52339b8c07cd9362922020eb3c3c95bf" - integrity sha512-KU8zyb7AIOqNjLCN3wkrFXxh+EVaG+zrs2P03ATNjc3iwSxHsu5/EvZiREpQ/IJiT9xfQbDVgKcsvRuzLCxglQ== - dependencies: - debug "4.3.1" - devtools-protocol "0.0.901419" - extract-zip "2.0.1" - https-proxy-agent "5.0.0" - node-fetch "2.6.1" - pkg-dir "4.2.0" - progress "2.0.1" - proxy-from-env "1.1.0" - rimraf "3.0.2" - tar-fs "2.0.0" - unbzip2-stream "1.3.3" - ws "7.4.6" - -q@^1.5.1, q@~1.5.0: - version "1.5.1" - resolved "https://registry.yarnpkg.com/q/-/q-1.5.1.tgz#7e32f75b41381291d04611f1bf14109ac00651d7" - integrity sha1-fjL3W0E4EpHQRhHxvxQQmsAGUdc= - -qjobs@^1.2.0: - version "1.2.0" - resolved "https://registry.yarnpkg.com/qjobs/-/qjobs-1.2.0.tgz#c45e9c61800bd087ef88d7e256423bdd49e5d071" - integrity sha512-8YOJEHtxpySA3fFDyCRxA+UUV+fA+rTWnuWvylOK/NCjhY+b4ocCtmu8TtsWb+mYeU+GCHf/S66KZF/AsteKHg== - -qs@6.7.0: - version "6.7.0" - resolved "https://registry.yarnpkg.com/qs/-/qs-6.7.0.tgz#41dc1a015e3d581f1621776be31afb2876a9b1bc" - integrity sha512-VCdBRNFTX1fyE7Nb6FYoURo/SPe62QCaAyzJvUjwRaIsc+NePBEniHlvxFmmX56+HZphIGtV0XeCirBtpDrTyQ== - -qs@^6.4.0, qs@^6.9.4: - version "6.10.1" - resolved "https://registry.yarnpkg.com/qs/-/qs-6.10.1.tgz#4931482fa8d647a5aab799c5271d2133b981fb6a" - integrity sha512-M528Hph6wsSVOBiYUnGf+K/7w0hNshs/duGsNXPUCLH5XAqjEtiPGwNONLV0tBH8NoGb0mvD5JubnUTrujKDTg== - dependencies: - side-channel "^1.0.4" - -qs@~6.5.2: - version "6.5.2" - resolved "https://registry.yarnpkg.com/qs/-/qs-6.5.2.tgz#cb3ae806e8740444584ef154ce8ee98d403f3e36" - integrity sha512-N5ZAX4/LxJmF+7wN74pUD6qAh9/wnvdQcjq9TZjevvXzSUo7bfmw91saqMjzGS2xq91/odN2dW/WOl7qQHNDGA== - -query-selector-shadow-dom@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/query-selector-shadow-dom/-/query-selector-shadow-dom-1.0.0.tgz#8fa7459a4620f094457640e74e953a9dbe61a38e" - integrity sha512-bK0/0cCI+R8ZmOF1QjT7HupDUYCxbf/9TJgAmSXQxZpftXmTAeil9DRoCnTDkWbvOyZzhcMBwKpptWcdkGFIMg== - -query-string@^6.13.8: - version "6.14.1" - resolved "https://registry.yarnpkg.com/query-string/-/query-string-6.14.1.tgz#7ac2dca46da7f309449ba0f86b1fd28255b0c86a" - integrity sha512-XDxAeVmpfu1/6IjyT/gXHOl+S0vQ9owggJ30hhWKdHAsNPOcasn5o9BW0eejZqL2e4vMjhAxoW3jVHcD6mbcYw== - dependencies: - decode-uri-component "^0.2.0" - filter-obj "^1.1.0" - split-on-first "^1.0.0" - strict-uri-encode "^2.0.0" - -querystring-es3@^0.2.0: - version "0.2.1" - resolved "https://registry.yarnpkg.com/querystring-es3/-/querystring-es3-0.2.1.tgz#9ec61f79049875707d69414596fd907a4d711e73" - integrity sha1-nsYfeQSYdXB9aUFFlv2Qek1xHnM= - -querystring@0.2.0: - version "0.2.0" - resolved "https://registry.yarnpkg.com/querystring/-/querystring-0.2.0.tgz#b209849203bb25df820da756e747005878521620" - integrity sha1-sgmEkgO7Jd+CDadW50cAWHhSFiA= - -querystringify@^2.1.1: - version "2.2.0" - resolved "https://registry.yarnpkg.com/querystringify/-/querystringify-2.2.0.tgz#3345941b4153cb9d082d8eee4cda2016a9aef7f6" - integrity sha512-FIqgj2EUvTa7R50u0rGsyTftzjYmv/a3hO345bZNrqabNqjtgiDMgmo4mkUjd+nzU5oF3dClKqFIPUKybUyqoQ== - -quick-lru@^4.0.1: - version "4.0.1" - resolved "https://registry.yarnpkg.com/quick-lru/-/quick-lru-4.0.1.tgz#5b8878f113a58217848c6482026c73e1ba57727f" - integrity sha512-ARhCpm70fzdcvNQfPoy49IaanKkTlRWF2JMzqhcJbhSFRZv7nPTvZJdcY7301IPmvW+/p0RgIWnQDLJxifsQ7g== - -quick-lru@^5.1.1: - version "5.1.1" - resolved "https://registry.yarnpkg.com/quick-lru/-/quick-lru-5.1.1.tgz#366493e6b3e42a3a6885e2e99d18f80fb7a8c932" - integrity sha512-WuyALRjWPDGtt/wzJiadO5AXY+8hZ80hVpe6MyivgraREW751X3SbhRvG3eLKOYN+8VEvqLcf3wdnt44Z4S4SA== - -randomatic@^3.0.0: - version "3.1.1" - resolved "https://registry.yarnpkg.com/randomatic/-/randomatic-3.1.1.tgz#b776efc59375984e36c537b2f51a1f0aff0da1ed" - integrity sha512-TuDE5KxZ0J461RVjrJZCJc+J+zCkTb1MbH9AQUq68sMhOMcy9jLcb3BrZKgp9q9Ncltdg4QVqWrH02W2EFFVYw== - dependencies: - is-number "^4.0.0" - kind-of "^6.0.0" - math-random "^1.0.1" - -randombytes@^2.0.0, randombytes@^2.0.1, randombytes@^2.0.5, randombytes@^2.1.0: - version "2.1.0" - resolved "https://registry.yarnpkg.com/randombytes/-/randombytes-2.1.0.tgz#df6f84372f0270dc65cdf6291349ab7a473d4f2a" - integrity sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ== - dependencies: - safe-buffer "^5.1.0" - -randomfill@^1.0.3: - version "1.0.4" - resolved "https://registry.yarnpkg.com/randomfill/-/randomfill-1.0.4.tgz#c92196fc86ab42be983f1bf31778224931d61458" - integrity sha512-87lcbR8+MhcWcUiQ+9e+Rwx8MyR2P7qnt15ynUlbm3TU/fjbgz4GsvfSUDTemtCCtVCqb4ZcEFlyPNTh9bBTLw== - dependencies: - randombytes "^2.0.5" - safe-buffer "^5.1.0" - -range-parser@^1.0.3, range-parser@^1.2.1, range-parser@~1.2.0, range-parser@~1.2.1: - version "1.2.1" - resolved "https://registry.yarnpkg.com/range-parser/-/range-parser-1.2.1.tgz#3cf37023d199e1c24d1a55b84800c2f3e6468031" - integrity sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg== - -raw-body@2.4.0: - version "2.4.0" - resolved "https://registry.yarnpkg.com/raw-body/-/raw-body-2.4.0.tgz#a1ce6fb9c9bc356ca52e89256ab59059e13d0332" - integrity sha512-4Oz8DUIwdvoa5qMJelxipzi/iJIi40O5cGV1wNYp5hvZP8ZN0T+jiNkL0QepXs+EsQ9XJ8ipEDoiH70ySUJP3Q== - dependencies: - bytes "3.1.0" - http-errors "1.7.2" - iconv-lite "0.4.24" - unpipe "1.0.0" - -raw-body@~1.1.0: - version "1.1.7" - resolved "https://registry.yarnpkg.com/raw-body/-/raw-body-1.1.7.tgz#1d027c2bfa116acc6623bca8f00016572a87d425" - integrity sha1-HQJ8K/oRasxmI7yo8AAWVyqH1CU= - dependencies: - bytes "1" - string_decoder "0.10" - -react-is@^17.0.1: - version "17.0.2" - resolved "https://registry.yarnpkg.com/react-is/-/react-is-17.0.2.tgz#e691d4a8e9c789365655539ab372762b0efb54f0" - integrity sha512-w2GsyukL62IJnlaff/nRegPQR94C/XXamvMWmSHRJ4y7Ts/4ocGRmTHvOs8PSE6pB3dWOrD/nueuU5sduBsQ4w== - -read-pkg-up@^1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/read-pkg-up/-/read-pkg-up-1.0.1.tgz#9d63c13276c065918d57f002a57f40a1b643fb02" - integrity sha1-nWPBMnbAZZGNV/ACpX9AobZD+wI= - dependencies: - find-up "^1.0.0" - read-pkg "^1.0.0" - -read-pkg-up@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/read-pkg-up/-/read-pkg-up-2.0.0.tgz#6b72a8048984e0c41e79510fd5e9fa99b3b549be" - integrity sha1-a3KoBImE4MQeeVEP1en6mbO1Sb4= - dependencies: - find-up "^2.0.0" - read-pkg "^2.0.0" - -read-pkg-up@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/read-pkg-up/-/read-pkg-up-3.0.0.tgz#3ed496685dba0f8fe118d0691dc51f4a1ff96f07" - integrity sha1-PtSWaF26D4/hGNBpHcUfSh/5bwc= - dependencies: - find-up "^2.0.0" - read-pkg "^3.0.0" - -read-pkg-up@^4.0.0: - version "4.0.0" - resolved "https://registry.yarnpkg.com/read-pkg-up/-/read-pkg-up-4.0.0.tgz#1b221c6088ba7799601c808f91161c66e58f8978" - integrity sha512-6etQSH7nJGsK0RbG/2TeDzZFa8shjQ1um+SwQQ5cwKy0dhSXdOncEhb1CPpvQG4h7FyOV6EB6YlV0yJvZQNAkA== - dependencies: - find-up "^3.0.0" - read-pkg "^3.0.0" - -read-pkg-up@^7.0.1: - version "7.0.1" - resolved "https://registry.yarnpkg.com/read-pkg-up/-/read-pkg-up-7.0.1.tgz#f3a6135758459733ae2b95638056e1854e7ef507" - integrity sha512-zK0TB7Xd6JpCLmlLmufqykGE+/TlOePD6qKClNW7hHDKFh/J7/7gCWGR7joEQEW1bKq3a3yUZSObOoWLFQ4ohg== - dependencies: - find-up "^4.1.0" - read-pkg "^5.2.0" - type-fest "^0.8.1" - -read-pkg@^1.0.0: - version "1.1.0" - resolved "https://registry.yarnpkg.com/read-pkg/-/read-pkg-1.1.0.tgz#f5ffaa5ecd29cb31c0474bca7d756b6bb29e3f28" - integrity sha1-9f+qXs0pyzHAR0vKfXVra7KePyg= - dependencies: - load-json-file "^1.0.0" - normalize-package-data "^2.3.2" - path-type "^1.0.0" - -read-pkg@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/read-pkg/-/read-pkg-2.0.0.tgz#8ef1c0623c6a6db0dc6713c4bfac46332b2368f8" - integrity sha1-jvHAYjxqbbDcZxPEv6xGMysjaPg= - dependencies: - load-json-file "^2.0.0" - normalize-package-data "^2.3.2" - path-type "^2.0.0" - -read-pkg@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/read-pkg/-/read-pkg-3.0.0.tgz#9cbc686978fee65d16c00e2b19c237fcf6e38389" - integrity sha1-nLxoaXj+5l0WwA4rGcI3/Pbjg4k= - dependencies: - load-json-file "^4.0.0" - normalize-package-data "^2.3.2" - path-type "^3.0.0" - -read-pkg@^5.2.0: - version "5.2.0" - resolved "https://registry.yarnpkg.com/read-pkg/-/read-pkg-5.2.0.tgz#7bf295438ca5a33e56cd30e053b34ee7250c93cc" - integrity sha512-Ug69mNOpfvKDAc2Q8DRpMjjzdtrnv9HcSMX+4VsZxD1aZ6ZzrIE7rlzXBtWTyhULSMKg076AW6WR5iZpD0JiOg== - dependencies: - "@types/normalize-package-data" "^2.4.0" - normalize-package-data "^2.5.0" - parse-json "^5.0.0" - type-fest "^0.6.0" - -"readable-stream@2 || 3", readable-stream@3, readable-stream@^3.0.0, readable-stream@^3.0.2, readable-stream@^3.1.1, readable-stream@^3.4.0, readable-stream@^3.6.0: - version "3.6.0" - resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-3.6.0.tgz#337bbda3adc0706bd3e024426a286d4b4b2c9198" - integrity sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA== - dependencies: - inherits "^2.0.3" - string_decoder "^1.1.1" - util-deprecate "^1.0.1" - -"readable-stream@>=1.0.33-1 <1.1.0-0", readable-stream@~1.0.17: - version "1.0.34" - resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-1.0.34.tgz#125820e34bc842d2f2aaafafe4c2916ee32c157c" - integrity sha1-Elgg40vIQtLyqq+v5MKRbuMsFXw= - dependencies: - core-util-is "~1.0.0" - inherits "~2.0.1" - isarray "0.0.1" - string_decoder "~0.10.x" - -readable-stream@^2.0.0, readable-stream@^2.0.1, readable-stream@^2.0.2, readable-stream@^2.0.5, readable-stream@^2.1.5, readable-stream@^2.2.2, readable-stream@^2.3.3, readable-stream@^2.3.5, readable-stream@^2.3.6, readable-stream@~2.3.6: - version "2.3.7" - resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-2.3.7.tgz#1eca1cf711aef814c04f62252a36a62f6cb23b57" - integrity sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw== - dependencies: - core-util-is "~1.0.0" - inherits "~2.0.3" - isarray "~1.0.0" - process-nextick-args "~2.0.0" - safe-buffer "~5.1.1" - string_decoder "~1.1.1" - util-deprecate "~1.0.1" - -readable-stream@~1.1.9: - version "1.1.14" - resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-1.1.14.tgz#7cf4c54ef648e3813084c636dd2079e166c081d9" - integrity sha1-fPTFTvZI44EwhMY23SB54WbAgdk= - dependencies: - core-util-is "~1.0.0" - inherits "~2.0.1" - isarray "0.0.1" - string_decoder "~0.10.x" - -readable-stream@~2.0.0: - version "2.0.6" - resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-2.0.6.tgz#8f90341e68a53ccc928788dacfcd11b36eb9b78e" - integrity sha1-j5A0HmilPMySh4jaz80Rs265t44= - dependencies: - core-util-is "~1.0.0" - inherits "~2.0.1" - isarray "~1.0.0" - process-nextick-args "~1.0.6" - string_decoder "~0.10.x" - util-deprecate "~1.0.1" - -readable-stream@~2.1.0: - version "2.1.5" - resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-2.1.5.tgz#66fa8b720e1438b364681f2ad1a63c618448c9d0" - integrity sha1-ZvqLcg4UOLNkaB8q0aY8YYRIydA= - dependencies: - buffer-shims "^1.0.0" - core-util-is "~1.0.0" - inherits "~2.0.1" - isarray "~1.0.0" - process-nextick-args "~1.0.6" - string_decoder "~0.10.x" - util-deprecate "~1.0.1" - -readdir-glob@^1.0.0: - version "1.1.1" - resolved "https://registry.yarnpkg.com/readdir-glob/-/readdir-glob-1.1.1.tgz#f0e10bb7bf7bfa7e0add8baffdc54c3f7dbee6c4" - integrity sha512-91/k1EzZwDx6HbERR+zucygRFfiPl2zkIYZtv3Jjr6Mn7SkKcVct8aVO+sSRiGMc6fLf72du3d92/uY63YPdEA== - dependencies: - minimatch "^3.0.4" - -readdirp@^2.0.0, readdirp@^2.2.1: - version "2.2.1" - resolved "https://registry.yarnpkg.com/readdirp/-/readdirp-2.2.1.tgz#0e87622a3325aa33e892285caf8b4e846529a525" - integrity sha512-1JU/8q+VgFZyxwrJ+SVIOsh+KywWGpds3NTqikiKpDMZWScmAYyKIgqkO+ARvNWJfXeXR1zxz7aHF4u4CyH6vQ== - dependencies: - graceful-fs "^4.1.11" - micromatch "^3.1.10" - readable-stream "^2.0.2" - -readdirp@~3.6.0: - version "3.6.0" - resolved "https://registry.yarnpkg.com/readdirp/-/readdirp-3.6.0.tgz#74a370bd857116e245b29cc97340cd431a02a6c7" - integrity sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA== - dependencies: - picomatch "^2.2.1" - -rechoir@^0.6.2: - version "0.6.2" - resolved "https://registry.yarnpkg.com/rechoir/-/rechoir-0.6.2.tgz#85204b54dba82d5742e28c96756ef43af50e3384" - integrity sha1-hSBLVNuoLVdC4oyWdW70OvUOM4Q= - dependencies: - resolve "^1.1.6" - -recursive-readdir@^2.2.2: - version "2.2.2" - resolved "https://registry.yarnpkg.com/recursive-readdir/-/recursive-readdir-2.2.2.tgz#9946fb3274e1628de6e36b2f6714953b4845094f" - integrity sha512-nRCcW9Sj7NuZwa2XvH9co8NPeXUBhZP7CRKJtU+cS6PW9FpCIFoI5ib0NT1ZrbNuPoRy0ylyCaUL8Gih4LSyFg== - dependencies: - minimatch "3.0.4" - -redent@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/redent/-/redent-1.0.0.tgz#cf916ab1fd5f1f16dfb20822dd6ec7f730c2afde" - integrity sha1-z5Fqsf1fHxbfsggi3W7H9zDCr94= - dependencies: - indent-string "^2.1.0" - strip-indent "^1.0.1" - -redent@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/redent/-/redent-3.0.0.tgz#e557b7998316bb53c9f1f56fa626352c6963059f" - integrity sha512-6tDA8g98We0zd0GvVeMT9arEOnTw9qM03L9cJXaCjrip1OO764RDBLBfrB4cwzNGDj5OA5ioymC9GkizgWJDUg== - dependencies: - indent-string "^4.0.0" - strip-indent "^3.0.0" - -regenerate-unicode-properties@^9.0.0: - version "9.0.0" - resolved "https://registry.yarnpkg.com/regenerate-unicode-properties/-/regenerate-unicode-properties-9.0.0.tgz#54d09c7115e1f53dc2314a974b32c1c344efe326" - integrity sha512-3E12UeNSPfjrgwjkR81m5J7Aw/T55Tu7nUyZVQYCKEOs+2dkxEY+DpPtZzO4YruuiPb7NkYLVcyJC4+zCbk5pA== - dependencies: - regenerate "^1.4.2" - -regenerate@^1.4.2: - version "1.4.2" - resolved "https://registry.yarnpkg.com/regenerate/-/regenerate-1.4.2.tgz#b9346d8827e8f5a32f7ba29637d398b69014848a" - integrity sha512-zrceR/XhGYU/d/opr2EKO7aRHUeiBI8qjtfHqADTwZd6Szfy16la6kqD0MIUs5z5hx6AaKa+PixpPrR289+I0A== - -regenerator-runtime@^0.11.0: - version "0.11.1" - resolved "https://registry.yarnpkg.com/regenerator-runtime/-/regenerator-runtime-0.11.1.tgz#be05ad7f9bf7d22e056f9726cee5017fbf19e2e9" - integrity sha512-MguG95oij0fC3QV3URf4V2SDYGJhJnJGqvIIgdECeODCT98wSWDAJ94SSuVpYQUoTcGUIL6L4yNB7j1DFFHSBg== - -regenerator-runtime@^0.13.4: - version "0.13.9" - resolved "https://registry.yarnpkg.com/regenerator-runtime/-/regenerator-runtime-0.13.9.tgz#8925742a98ffd90814988d7566ad30ca3b263b52" - integrity sha512-p3VT+cOEgxFsRRA9X4lkI1E+k2/CtnKtU4gcxyaCUreilL/vqI6CdZ3wxVUx3UOUg+gnUOQQcRI7BmSI656MYA== - -regenerator-transform@^0.14.2: - version "0.14.5" - resolved "https://registry.yarnpkg.com/regenerator-transform/-/regenerator-transform-0.14.5.tgz#c98da154683671c9c4dcb16ece736517e1b7feb4" - integrity sha512-eOf6vka5IO151Jfsw2NO9WpGX58W6wWmefK3I1zEGr0lOD0u8rwPaNqQL1aRxUaxLeKO3ArNh3VYg1KbaD+FFw== - dependencies: - "@babel/runtime" "^7.8.4" - -regex-cache@^0.4.2: - version "0.4.4" - resolved "https://registry.yarnpkg.com/regex-cache/-/regex-cache-0.4.4.tgz#75bdc58a2a1496cec48a12835bc54c8d562336dd" - integrity sha512-nVIZwtCjkC9YgvWkpM55B5rBhBYRZhAaJbgcFYXXsHnbZ9UZI9nnVWYZpBlCqv9ho2eZryPnWrZGsOdPwVWXWQ== - dependencies: - is-equal-shallow "^0.1.3" - -regex-not@^1.0.0, regex-not@^1.0.2: - version "1.0.2" - resolved "https://registry.yarnpkg.com/regex-not/-/regex-not-1.0.2.tgz#1f4ece27e00b0b65e0247a6810e6a85d83a5752c" - integrity sha512-J6SDjUgDxQj5NusnOtdFxDwN/+HWykR8GELwctJ7mdqhcyy1xEc4SRFHUXvxTp661YaVKAjfRLZ9cCqS6tn32A== - dependencies: - extend-shallow "^3.0.2" - safe-regex "^1.1.0" - -regexp.prototype.flags@^1.3.0: - version "1.3.1" - resolved "https://registry.yarnpkg.com/regexp.prototype.flags/-/regexp.prototype.flags-1.3.1.tgz#7ef352ae8d159e758c0eadca6f8fcb4eef07be26" - integrity sha512-JiBdRBq91WlY7uRJ0ds7R+dU02i6LKi8r3BuQhNXn+kmeLN+EfHhfjqMRis1zJxnlu88hq/4dx0P2OP3APRTOA== - dependencies: - call-bind "^1.0.2" - define-properties "^1.1.3" - -regexpp@^1.0.1: - version "1.1.0" - resolved "https://registry.yarnpkg.com/regexpp/-/regexpp-1.1.0.tgz#0e3516dd0b7904f413d2d4193dce4618c3a689ab" - integrity sha512-LOPw8FpgdQF9etWMaAfG/WRthIdXJGYp4mJ2Jgn/2lpkbod9jPn0t9UqN7AxBOKNfzRbYyVfgc7Vk4t/MpnXgw== - -regexpp@^3.0.0, regexpp@^3.1.0: - version "3.2.0" - resolved "https://registry.yarnpkg.com/regexpp/-/regexpp-3.2.0.tgz#0425a2768d8f23bad70ca4b90461fa2f1213e1b2" - integrity sha512-pq2bWo9mVD43nbts2wGv17XLiNLya+GklZ8kaDLV2Z08gDCsGpnKn9BFMepvWuHCbyVvY7J5o5+BVvoQbmlJLg== - -regexpu-core@^4.7.1: - version "4.8.0" - resolved "https://registry.yarnpkg.com/regexpu-core/-/regexpu-core-4.8.0.tgz#e5605ba361b67b1718478501327502f4479a98f0" - integrity sha512-1F6bYsoYiz6is+oz70NWur2Vlh9KWtswuRuzJOfeYUrfPX2o8n74AnUVaOGDbUqVGO9fNHu48/pjJO4sNVwsOg== - dependencies: - regenerate "^1.4.2" - regenerate-unicode-properties "^9.0.0" - regjsgen "^0.5.2" - regjsparser "^0.7.0" - unicode-match-property-ecmascript "^2.0.0" - unicode-match-property-value-ecmascript "^2.0.0" - -regjsgen@^0.5.2: - version "0.5.2" - resolved "https://registry.yarnpkg.com/regjsgen/-/regjsgen-0.5.2.tgz#92ff295fb1deecbf6ecdab2543d207e91aa33733" - integrity sha512-OFFT3MfrH90xIW8OOSyUrk6QHD5E9JOTeGodiJeBS3J6IwlgzJMNE/1bZklWz5oTg+9dCMyEetclvCVXOPoN3A== - -regjsparser@^0.7.0: - version "0.7.0" - resolved "https://registry.yarnpkg.com/regjsparser/-/regjsparser-0.7.0.tgz#a6b667b54c885e18b52554cb4960ef71187e9968" - integrity sha512-A4pcaORqmNMDVwUjWoTzuhwMGpP+NykpfqAsEgI1FSH/EzC7lrN5TMd+kN8YCovX+jMpu8eaqXgXPCa0g8FQNQ== - dependencies: - jsesc "~0.5.0" - -remark-gfm@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/remark-gfm/-/remark-gfm-1.0.0.tgz#9213643001be3f277da6256464d56fd28c3b3c0d" - integrity sha512-KfexHJCiqvrdBZVbQ6RopMZGwaXz6wFJEfByIuEwGf0arvITHjiKKZ1dpXujjH9KZdm1//XJQwgfnJ3lmXaDPA== - dependencies: - mdast-util-gfm "^0.1.0" - micromark-extension-gfm "^0.3.0" - -remark-html@^13.0.1: - version "13.0.2" - resolved "https://registry.yarnpkg.com/remark-html/-/remark-html-13.0.2.tgz#de5f052749ff61fc904c9708c155c88a2e2655dc" - integrity sha512-LhSRQ+3RKdBqB/RGesFWkNNfkGqprDUCwjq54SylfFeNyZby5kqOG8Dn/vYsRoM8htab6EWxFXCY6XIZvMoRiQ== - dependencies: - hast-util-sanitize "^3.0.0" - hast-util-to-html "^7.0.0" - mdast-util-to-hast "^10.0.0" - -remark-parse@^9.0.0: - version "9.0.0" - resolved "https://registry.yarnpkg.com/remark-parse/-/remark-parse-9.0.0.tgz#4d20a299665880e4f4af5d90b7c7b8a935853640" - integrity sha512-geKatMwSzEXKHuzBNU1z676sGcDcFoChMK38TgdHJNAYfFtsfHDQG7MoJAjs6sgYMqyLduCYWDIWZIxiPeafEw== - dependencies: - mdast-util-from-markdown "^0.8.0" - -remark-reference-links@^5.0.0: - version "5.0.0" - resolved "https://registry.yarnpkg.com/remark-reference-links/-/remark-reference-links-5.0.0.tgz#2c75b60a99c53251f25193566953b0c71e096b8d" - integrity sha512-oSIo6lfDyG/1yYl2jPZNXmD9dgyPxp07mSd7snJagVMsDU6NRlD8i54MwHWUgMoOHTs8lIKPkwaUok/tbr5syQ== - dependencies: - unist-util-visit "^2.0.0" - -remark-stringify@^9.0.0: - version "9.0.1" - resolved "https://registry.yarnpkg.com/remark-stringify/-/remark-stringify-9.0.1.tgz#576d06e910548b0a7191a71f27b33f1218862894" - integrity sha512-mWmNg3ZtESvZS8fv5PTvaPckdL4iNlCHTt8/e/8oN08nArHRHjNZMKzA/YW3+p7/lYqIw4nx1XsjCBo/AxNChg== - dependencies: - mdast-util-to-markdown "^0.6.0" - -remark-toc@^7.2.0: - version "7.2.0" - resolved "https://registry.yarnpkg.com/remark-toc/-/remark-toc-7.2.0.tgz#1c5159e9091826150db14c97ac00c2ad5a7f1523" - integrity sha512-ppHepvpbg7j5kPFmU5rzDC4k2GTcPDvWcxXyr/7BZzO1cBSPk0stKtEJdsgAyw2WHKPGxadcHIZRjb2/sHxjkg== - dependencies: - "@types/unist" "^2.0.3" - mdast-util-toc "^5.0.0" - -remark@^13.0.0: - version "13.0.0" - resolved "https://registry.yarnpkg.com/remark/-/remark-13.0.0.tgz#d15d9bf71a402f40287ebe36067b66d54868e425" - integrity sha512-HDz1+IKGtOyWN+QgBiAT0kn+2s6ovOxHyPAFGKVE81VSzJ+mq7RwHFledEvB5F1p4iJvOah/LOKdFuzvRnNLCA== - dependencies: - remark-parse "^9.0.0" - remark-stringify "^9.0.0" - unified "^9.1.0" - -remove-bom-buffer@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/remove-bom-buffer/-/remove-bom-buffer-3.0.0.tgz#c2bf1e377520d324f623892e33c10cac2c252b53" - integrity sha512-8v2rWhaakv18qcvNeli2mZ/TMTL2nEyAKRvzo1WtnZBl15SHyEhrCu2/xKlJyUFKHiHgfXIyuY6g2dObJJycXQ== - dependencies: - is-buffer "^1.1.5" - is-utf8 "^0.2.1" - -remove-bom-stream@^1.2.0: - version "1.2.0" - resolved "https://registry.yarnpkg.com/remove-bom-stream/-/remove-bom-stream-1.2.0.tgz#05f1a593f16e42e1fb90ebf59de8e569525f9523" - integrity sha1-BfGlk/FuQuH7kOv1nejlaVJflSM= - dependencies: - remove-bom-buffer "^3.0.0" - safe-buffer "^5.1.0" - through2 "^2.0.3" - -remove-trailing-separator@^1.0.1, remove-trailing-separator@^1.1.0: - version "1.1.0" - resolved "https://registry.yarnpkg.com/remove-trailing-separator/-/remove-trailing-separator-1.1.0.tgz#c24bce2a283adad5bc3f58e0d48249b92379d8ef" - integrity sha1-wkvOKig62tW8P1jg1IJJuSN52O8= - -repeat-element@^1.1.2: - version "1.1.4" - resolved "https://registry.yarnpkg.com/repeat-element/-/repeat-element-1.1.4.tgz#be681520847ab58c7568ac75fbfad28ed42d39e9" - integrity sha512-LFiNfRcSu7KK3evMyYOuCzv3L10TW7yC1G2/+StMjK8Y6Vqd2MG7r/Qjw4ghtuCOjFvlnms/iMmLqpvW/ES/WQ== - -repeat-string@^1.0.0, repeat-string@^1.5.0, repeat-string@^1.5.2, repeat-string@^1.6.1: - version "1.6.1" - resolved "https://registry.yarnpkg.com/repeat-string/-/repeat-string-1.6.1.tgz#8dcae470e1c88abc2d600fff4a776286da75e637" - integrity sha1-jcrkcOHIirwtYA//Sndihtp15jc= - -repeating@^2.0.0: - version "2.0.1" - resolved "https://registry.yarnpkg.com/repeating/-/repeating-2.0.1.tgz#5214c53a926d3552707527fbab415dbc08d06dda" - integrity sha1-UhTFOpJtNVJwdSf7q0FdvAjQbdo= - dependencies: - is-finite "^1.0.0" - -replace-ext@0.0.1: - version "0.0.1" - resolved "https://registry.yarnpkg.com/replace-ext/-/replace-ext-0.0.1.tgz#29bbd92078a739f0bcce2b4ee41e837953522924" - integrity sha1-KbvZIHinOfC8zitO5B6DeVNSKSQ= - -replace-ext@^1.0.0: - version "1.0.1" - resolved "https://registry.yarnpkg.com/replace-ext/-/replace-ext-1.0.1.tgz#2d6d996d04a15855d967443631dd5f77825b016a" - integrity sha512-yD5BHCe7quCgBph4rMQ+0KkIRKwWCrHDOX1p1Gp6HwjPM5kVoCdKGNhN7ydqqsX6lJEnQDKZ/tFMiEdQ1dvPEw== - -replace-homedir@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/replace-homedir/-/replace-homedir-1.0.0.tgz#e87f6d513b928dde808260c12be7fec6ff6e798c" - integrity sha1-6H9tUTuSjd6AgmDBK+f+xv9ueYw= - dependencies: - homedir-polyfill "^1.0.1" - is-absolute "^1.0.0" - remove-trailing-separator "^1.1.0" - -replacestream@^4.0.3: - version "4.0.3" - resolved "https://registry.yarnpkg.com/replacestream/-/replacestream-4.0.3.tgz#3ee5798092be364b1cdb1484308492cb3dff2f36" - integrity sha512-AC0FiLS352pBBiZhd4VXB1Ab/lh0lEgpP+GGvZqbQh8a5cmXVoTe5EX/YeTFArnp4SRGTHh1qCHu9lGs1qG8sA== - dependencies: - escape-string-regexp "^1.0.3" - object-assign "^4.0.1" - readable-stream "^2.0.2" - -request@^2.88.2: - version "2.88.2" - resolved "https://registry.yarnpkg.com/request/-/request-2.88.2.tgz#d73c918731cb5a87da047e207234146f664d12b3" - integrity sha512-MsvtOrfG9ZcrOwAW+Qi+F6HbD0CWXEh9ou77uOb7FM2WPhwT7smM833PzanhJLsgXjN89Ir6V2PczXNnMpwKhw== - dependencies: - aws-sign2 "~0.7.0" - aws4 "^1.8.0" - caseless "~0.12.0" - combined-stream "~1.0.6" - extend "~3.0.2" - forever-agent "~0.6.1" - form-data "~2.3.2" - har-validator "~5.1.3" - http-signature "~1.2.0" - is-typedarray "~1.0.0" - isstream "~0.1.2" - json-stringify-safe "~5.0.1" - mime-types "~2.1.19" - oauth-sign "~0.9.0" - performance-now "^2.1.0" - qs "~6.5.2" - safe-buffer "^5.1.2" - tough-cookie "~2.5.0" - tunnel-agent "^0.6.0" - uuid "^3.3.2" - -require-directory@^2.1.1: - version "2.1.1" - resolved "https://registry.yarnpkg.com/require-directory/-/require-directory-2.1.1.tgz#8c64ad5fd30dab1c976e2344ffe7f792a6a6df42" - integrity sha1-jGStX9MNqxyXbiNE/+f3kqam30I= - -require-from-string@^2.0.2: - version "2.0.2" - resolved "https://registry.yarnpkg.com/require-from-string/-/require-from-string-2.0.2.tgz#89a7fdd938261267318eafe14f9c32e598c36909" - integrity sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw== - -require-main-filename@^1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/require-main-filename/-/require-main-filename-1.0.1.tgz#97f717b69d48784f5f526a6c5aa8ffdda055a4d1" - integrity sha1-l/cXtp1IeE9fUmpsWqj/3aBVpNE= - -require-main-filename@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/require-main-filename/-/require-main-filename-2.0.0.tgz#d0b329ecc7cc0f61649f62215be69af54aa8989b" - integrity sha512-NKN5kMDylKuldxYLSUfrbo5Tuzh4hd+2E8NPPX02mZtn1VuREQToYe/ZdlJy+J3uCpfaiGF05e7B8W0iXbQHmg== - -require-uncached@^1.0.3: - version "1.0.3" - resolved "https://registry.yarnpkg.com/require-uncached/-/require-uncached-1.0.3.tgz#4e0d56d6c9662fd31e43011c4b95aa49955421d3" - integrity sha1-Tg1W1slmL9MeQwEcS5WqSZVUIdM= - dependencies: - caller-path "^0.1.0" - resolve-from "^1.0.0" - -requires-port@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/requires-port/-/requires-port-1.0.0.tgz#925d2601d39ac485e091cf0da5c6e694dc3dcaff" - integrity sha1-kl0mAdOaxIXgkc8NpcbmlNw9yv8= - -resolve-alpn@^1.0.0: - version "1.2.1" - resolved "https://registry.yarnpkg.com/resolve-alpn/-/resolve-alpn-1.2.1.tgz#b7adbdac3546aaaec20b45e7d8265927072726f9" - integrity sha512-0a1F4l73/ZFZOakJnQ3FvkJ2+gSTQWz/r2KE5OdDY0TxPm5h4GkqkWWfM47T7HsbnOtcJVEF4epCVy6u7Q3K+g== - -resolve-dir@^1.0.0, resolve-dir@^1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/resolve-dir/-/resolve-dir-1.0.1.tgz#79a40644c362be82f26effe739c9bb5382046f43" - integrity sha1-eaQGRMNivoLybv/nOcm7U4IEb0M= - dependencies: - expand-tilde "^2.0.0" - global-modules "^1.0.0" - -resolve-from@^1.0.0: - version "1.0.1" - resolved "https://registry.yarnpkg.com/resolve-from/-/resolve-from-1.0.1.tgz#26cbfe935d1aeeeabb29bc3fe5aeb01e93d44226" - integrity sha1-Jsv+k10a7uq7Kbw/5a6wHpPUQiY= - -resolve-from@^4.0.0: - version "4.0.0" - resolved "https://registry.yarnpkg.com/resolve-from/-/resolve-from-4.0.0.tgz#4abcd852ad32dd7baabfe9b40e00a36db5f392e6" - integrity sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g== - -resolve-from@^5.0.0: - version "5.0.0" - resolved "https://registry.yarnpkg.com/resolve-from/-/resolve-from-5.0.0.tgz#c35225843df8f776df21c57557bc087e9dfdfc69" - integrity sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw== - -resolve-options@^1.1.0: - version "1.1.0" - resolved "https://registry.yarnpkg.com/resolve-options/-/resolve-options-1.1.0.tgz#32bb9e39c06d67338dc9378c0d6d6074566ad131" - integrity sha1-MrueOcBtZzONyTeMDW1gdFZq0TE= - dependencies: - value-or-function "^3.0.0" - -resolve-url@^0.2.1: - version "0.2.1" - resolved "https://registry.yarnpkg.com/resolve-url/-/resolve-url-0.2.1.tgz#2c637fe77c893afd2a663fe21aa9080068e2052a" - integrity sha1-LGN/53yJOv0qZj/iGqkIAGjiBSo= - -resolve@1.1.7, resolve@1.1.x: - version "1.1.7" - resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.1.7.tgz#203114d82ad2c5ed9e8e0411b3932875e889e97b" - integrity sha1-IDEU2CrSxe2ejgQRs5ModeiJ6Xs= - -resolve@^1.1.3, resolve@^1.1.6, resolve@^1.1.7, resolve@^1.10.0, resolve@^1.10.1, resolve@^1.14.2, resolve@^1.20.0, resolve@^1.3.2, resolve@^1.4.0, resolve@^1.8.1: - version "1.20.0" - resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.20.0.tgz#629a013fb3f70755d6f0b7935cc1c2c5378b1975" - integrity sha512-wENBPt4ySzg4ybFQW2TT1zMQucPK95HSh/nq2CFTZVOGut2+pQvSsgtda4d26YrYcr067wjbmzOG8byDPBX63A== - dependencies: - is-core-module "^2.2.0" - path-parse "^1.0.6" - -responselike@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/responselike/-/responselike-2.0.0.tgz#26391bcc3174f750f9a79eacc40a12a5c42d7723" - integrity sha512-xH48u3FTB9VsZw7R+vvgaKeLKzT6jOogbQhEe/jewwnZgzPcnyWui2Av6JpoYZF/91uueC+lqhWqeURw5/qhCw== - dependencies: - lowercase-keys "^2.0.0" - -resq@^1.9.1: - version "1.10.1" - resolved "https://registry.yarnpkg.com/resq/-/resq-1.10.1.tgz#c05d1b3808016cceec4d485ceb375acb49565f53" - integrity sha512-zhp1iyUH02MLciv3bIM2bNtTFx/fqRsK4Jk73jcPqp00d/sMTTjOtjdTMAcgjrQKGx5DvQ/HSpeqaMW0atGRJA== - dependencies: - fast-deep-equal "^2.0.1" - -restore-cursor@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/restore-cursor/-/restore-cursor-2.0.0.tgz#9f7ee287f82fd326d4fd162923d62129eee0dfaf" - integrity sha1-n37ih/gv0ybU/RYpI9YhKe7g368= - dependencies: - onetime "^2.0.0" - signal-exit "^3.0.2" - -restore-cursor@^3.1.0: - version "3.1.0" - resolved "https://registry.yarnpkg.com/restore-cursor/-/restore-cursor-3.1.0.tgz#39f67c54b3a7a58cea5236d95cf0034239631f7e" - integrity sha512-l+sSefzHpj5qimhFSE5a8nufZYAM3sBSVMAPtYkmC+4EH2anSGaEMXSD0izRQbu9nfyQ9y5JrVmp7E8oZrUjvA== - dependencies: - onetime "^5.1.0" - signal-exit "^3.0.2" - -ret@~0.1.10: - version "0.1.15" - resolved "https://registry.yarnpkg.com/ret/-/ret-0.1.15.tgz#b8a4825d5bdb1fc3f6f53c2bc33f81388681c7bc" - integrity sha512-TTlYpa+OL+vMMNG24xSlQGEJ3B/RzEfUlLct7b5G/ytav+wPrplCpVMFuwzXbkecJrb6IYo1iFb0S9v37754mg== - -rfdc@^1.1.4: - version "1.3.0" - resolved "https://registry.yarnpkg.com/rfdc/-/rfdc-1.3.0.tgz#d0b7c441ab2720d05dc4cf26e01c89631d9da08b" - integrity sha512-V2hovdzFbOi77/WajaSMXk2OLm+xNIeQdMMuB7icj7bk6zi2F8GGAxigcnDFpJHbNyNcgyJDiP+8nOrY5cZGrA== - -rgb2hex@0.2.5: - version "0.2.5" - resolved "https://registry.yarnpkg.com/rgb2hex/-/rgb2hex-0.2.5.tgz#f82230cd3ab1364fa73c99be3a691ed688f8dbdc" - integrity sha512-22MOP1Rh7sAo1BZpDG6R5RFYzR2lYEgwq7HEmyW2qcsOqR2lQKmn+O//xV3YG/0rrhMC6KVX2hU+ZXuaw9a5bw== - -right-align@^0.1.1: - version "0.1.3" - resolved "https://registry.yarnpkg.com/right-align/-/right-align-0.1.3.tgz#61339b722fe6a3515689210d24e14c96148613ef" - integrity sha1-YTObci/mo1FWiSENJOFMlhSGE+8= - dependencies: - align-text "^0.1.1" - -rimraf@2, rimraf@^2.2.8, rimraf@^2.6.3: - version "2.7.1" - resolved "https://registry.yarnpkg.com/rimraf/-/rimraf-2.7.1.tgz#35797f13a7fdadc566142c29d4f07ccad483e3ec" - integrity sha512-uWjbaKIK3T1OSVptzX7Nl6PvQ3qAGtKEtVRjRuazjfL3Bx5eI409VZSqgND+4UNnmzLVdPj9FqFJNPqBZFve4w== - dependencies: - glob "^7.1.3" - -rimraf@3.0.2, rimraf@^3.0.0, rimraf@^3.0.2: - version "3.0.2" - resolved "https://registry.yarnpkg.com/rimraf/-/rimraf-3.0.2.tgz#f1a5402ba6220ad52cc1282bac1ae3aa49fd061a" - integrity sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA== - dependencies: - glob "^7.1.3" - -rimraf@~2.2.0: - version "2.2.8" - resolved "https://registry.yarnpkg.com/rimraf/-/rimraf-2.2.8.tgz#e439be2aaee327321952730f99a8929e4fc50582" - integrity sha1-5Dm+Kq7jJzIZUnMPmaiSnk/FBYI= - -rimraf@~2.5.2: - version "2.5.4" - resolved "https://registry.yarnpkg.com/rimraf/-/rimraf-2.5.4.tgz#96800093cbf1a0c86bd95b4625467535c29dfa04" - integrity sha1-loAAk8vxoMhr2VtGJUZ1NcKd+gQ= - dependencies: - glob "^7.0.5" - -rimraf@~2.6.2: - version "2.6.3" - resolved "https://registry.yarnpkg.com/rimraf/-/rimraf-2.6.3.tgz#b2d104fe0d8fb27cf9e0a1cda8262dd3833c6cab" - integrity sha512-mwqeW5XsA2qAejG46gYdENaxXjx9onRNCfn7L0duuP4hCuTIi/QO7PDK07KJfp1d+izWPrzEJDcSqBa0OZQriA== - dependencies: - glob "^7.1.3" - -ripemd160@0.2.0: - version "0.2.0" - resolved "https://registry.yarnpkg.com/ripemd160/-/ripemd160-0.2.0.tgz#2bf198bde167cacfa51c0a928e84b68bbe171fce" - integrity sha1-K/GYveFnys+lHAqSjoS2i74XH84= - -ripemd160@^2.0.0, ripemd160@^2.0.1: - version "2.0.2" - resolved "https://registry.yarnpkg.com/ripemd160/-/ripemd160-2.0.2.tgz#a1c1a6f624751577ba5d07914cbc92850585890c" - integrity sha512-ii4iagi25WusVoiC4B4lq7pbXfAp3D9v5CwfkY33vffw2+pkDjY1D8GaN7spsxvCSx8dkPqOZCEZyfxcmJG2IA== - dependencies: - hash-base "^3.0.0" - inherits "^2.0.1" - -run-async@^2.2.0, run-async@^2.4.0: - version "2.4.1" - resolved "https://registry.yarnpkg.com/run-async/-/run-async-2.4.1.tgz#8440eccf99ea3e70bd409d49aab88e10c189a455" - integrity sha512-tvVnVv01b8c1RrA6Ep7JkStj85Guv/YrMcwqYQnwjsAS2cTmmPGBBjAjpCW7RrSodNSoE2/qg9O4bceNvUuDgQ== - -rx-lite-aggregates@^4.0.8: - version "4.0.8" - resolved "https://registry.yarnpkg.com/rx-lite-aggregates/-/rx-lite-aggregates-4.0.8.tgz#753b87a89a11c95467c4ac1626c4efc4e05c67be" - integrity sha1-dTuHqJoRyVRnxKwWJsTvxOBcZ74= - dependencies: - rx-lite "*" - -rx-lite@*, rx-lite@^4.0.8: - version "4.0.8" - resolved "https://registry.yarnpkg.com/rx-lite/-/rx-lite-4.0.8.tgz#0b1e11af8bc44836f04a6407e92da42467b79444" - integrity sha1-Cx4Rr4vESDbwSmQH6S2kJGe3lEQ= - -rxjs@^7.2.0: - version "7.3.1" - resolved "https://registry.yarnpkg.com/rxjs/-/rxjs-7.3.1.tgz#cc375521f9e238b474fe552b0b9fd1be33d08099" - integrity sha512-vNenx7gqjPyeKpRnM6S5Ksm/oFTRijWWzYlRON04KaehZ3YjDwEmVjGUGo0TKWVjeNXOujVRlh0K1drUbcdPkw== - dependencies: - tslib "~2.1.0" - -safe-buffer@5.1.2, safe-buffer@~5.1.0, safe-buffer@~5.1.1: - version "5.1.2" - resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.1.2.tgz#991ec69d296e0313747d59bdfd2b745c35f8828d" - integrity sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g== - -safe-buffer@>=5.1.0, safe-buffer@^5.0.1, safe-buffer@^5.1.0, safe-buffer@^5.1.1, safe-buffer@^5.1.2, safe-buffer@^5.2.0, safe-buffer@~5.2.0: - version "5.2.1" - resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.2.1.tgz#1eaf9fa9bdb1fdd4ec75f58f9cdb4e6b7827eec6" - integrity sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ== - -safe-json-parse@~1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/safe-json-parse/-/safe-json-parse-1.0.1.tgz#3e76723e38dfdda13c9b1d29a1e07ffee4b30b57" - integrity sha1-PnZyPjjf3aE8mx0poeB//uSzC1c= - -safe-regex@^1.1.0: - version "1.1.0" - resolved "https://registry.yarnpkg.com/safe-regex/-/safe-regex-1.1.0.tgz#40a3669f3b077d1e943d44629e157dd48023bf2e" - integrity sha1-QKNmnzsHfR6UPURinhV91IAjvy4= - dependencies: - ret "~0.1.10" - -"safer-buffer@>= 2.1.2 < 3", safer-buffer@^2.0.2, safer-buffer@^2.1.0, safer-buffer@~2.1.0: - version "2.1.2" - resolved "https://registry.yarnpkg.com/safer-buffer/-/safer-buffer-2.1.2.tgz#44fa161b0187b9549dd84bb91802f9bd8385cd6a" - integrity sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg== - -samsam@1.3.0: - version "1.3.0" - resolved "https://registry.yarnpkg.com/samsam/-/samsam-1.3.0.tgz#8d1d9350e25622da30de3e44ba692b5221ab7c50" - integrity sha512-1HwIYD/8UlOtFS3QO3w7ey+SdSDFE4HRNLZoZRYVQefrOY3l17epswImeB1ijgJFQJodIaHcwkp3r/myBjFVbg== - -schema-utils@^2.6.5, schema-utils@^2.7.0: - version "2.7.1" - resolved "https://registry.yarnpkg.com/schema-utils/-/schema-utils-2.7.1.tgz#1ca4f32d1b24c590c203b8e7a50bf0ea4cd394d7" - integrity sha512-SHiNtMOUGWBQJwzISiVYKu82GiV4QYGePp3odlY1tuKO7gPtphAT5R/py0fA6xtbgLL/RvtJZnU9b8s0F1q0Xg== - dependencies: - "@types/json-schema" "^7.0.5" - ajv "^6.12.4" - ajv-keywords "^3.5.2" - -semver-greatest-satisfied-range@^1.1.0: - version "1.1.0" - resolved "https://registry.yarnpkg.com/semver-greatest-satisfied-range/-/semver-greatest-satisfied-range-1.1.0.tgz#13e8c2658ab9691cb0cd71093240280d36f77a5b" - integrity sha1-E+jCZYq5aRywzXEJMkAoDTb3els= - dependencies: - sver-compat "^1.5.0" - -"semver@2 || 3 || 4 || 5", semver@^5.3.0, semver@^5.4.1, semver@^5.5.0, semver@^5.6.0: - version "5.7.1" - resolved "https://registry.yarnpkg.com/semver/-/semver-5.7.1.tgz#a954f931aeba508d307bbf069eff0c01c96116f7" - integrity sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ== - -semver@7.0.0: - version "7.0.0" - resolved "https://registry.yarnpkg.com/semver/-/semver-7.0.0.tgz#5f3ca35761e47e05b206c6daff2cf814f0316b8e" - integrity sha512-+GB6zVA9LWh6zovYQLALHwv5rb2PHGlJi3lfiqIHxR0uuwCgefcOJc59v9fv1w8GbStwxuuqqAjI9NMAOOgq1A== - -semver@^6.0.0, semver@^6.1.0, semver@^6.1.1, semver@^6.1.2, semver@^6.3.0: - version "6.3.0" - resolved "https://registry.yarnpkg.com/semver/-/semver-6.3.0.tgz#ee0a64c8af5e8ceea67687b133761e1becbd1d3d" - integrity sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw== - -semver@^7.1.1, semver@^7.2.1, semver@^7.3.4: - version "7.3.5" - resolved "https://registry.yarnpkg.com/semver/-/semver-7.3.5.tgz#0b621c879348d8998e4b0e4be94b3f12e6018ef7" - integrity sha512-PoeGJYh8HK4BTO/a9Tf6ZG3veo/A7ZVsYrSA6J8ny9nb3B1VrpkuN+z9OE5wfE5p6H4LchYZsegiQgbJD94ZFQ== - dependencies: - lru-cache "^6.0.0" - -send@0.17.1: - version "0.17.1" - resolved "https://registry.yarnpkg.com/send/-/send-0.17.1.tgz#c1d8b059f7900f7466dd4938bdc44e11ddb376c8" - integrity sha512-BsVKsiGcQMFwT8UxypobUKyv7irCNRHk1T0G680vk88yf6LBByGcZJOTJCrTP2xVN6yI+XjPJcNuE3V4fT9sAg== - dependencies: - debug "2.6.9" - depd "~1.1.2" - destroy "~1.0.4" - encodeurl "~1.0.2" - escape-html "~1.0.3" - etag "~1.8.1" - fresh "0.5.2" - http-errors "~1.7.2" - mime "1.6.0" - ms "2.1.1" - on-finished "~2.3.0" - range-parser "~1.2.1" - statuses "~1.5.0" - -send@^0.16.2: - version "0.16.2" - resolved "https://registry.yarnpkg.com/send/-/send-0.16.2.tgz#6ecca1e0f8c156d141597559848df64730a6bbc1" - integrity sha512-E64YFPUssFHEFBvpbbjr44NCLtI1AohxQ8ZSiJjQLskAdKuriYEP6VyGEsRDH8ScozGpkaX1BGvhanqCwkcEZw== - dependencies: - debug "2.6.9" - depd "~1.1.2" - destroy "~1.0.4" - encodeurl "~1.0.2" - escape-html "~1.0.3" - etag "~1.8.1" - fresh "0.5.2" - http-errors "~1.6.2" - mime "1.4.1" - ms "2.0.0" - on-finished "~2.3.0" - range-parser "~1.2.0" - statuses "~1.4.0" - -serialize-error@^8.0.0: - version "8.1.0" - resolved "https://registry.yarnpkg.com/serialize-error/-/serialize-error-8.1.0.tgz#3a069970c712f78634942ddd50fbbc0eaebe2f67" - integrity sha512-3NnuWfM6vBYoy5gZFvHiYsVbafvI9vZv/+jlIigFn4oP4zjNPK3LhcY0xSCgeb1a5L8jO71Mit9LlNoi2UfDDQ== - dependencies: - type-fest "^0.20.2" - -serialize-javascript@6.0.0: - version "6.0.0" - resolved "https://registry.yarnpkg.com/serialize-javascript/-/serialize-javascript-6.0.0.tgz#efae5d88f45d7924141da8b5c3a7a7e663fefeb8" - integrity sha512-Qr3TosvguFt8ePWqsvRfrKyQXIiW+nGbYpy8XK24NQHE83caxWt+mIymTT19DGFbNWNLfEwsrkSmN64lVWB9ag== - dependencies: - randombytes "^2.1.0" - -serve-index@^1.9.1: - version "1.9.1" - resolved "https://registry.yarnpkg.com/serve-index/-/serve-index-1.9.1.tgz#d3768d69b1e7d82e5ce050fff5b453bea12a9239" - integrity sha1-03aNabHn2C5c4FD/9bRTvqEqkjk= - dependencies: - accepts "~1.3.4" - batch "0.6.1" - debug "2.6.9" - escape-html "~1.0.3" - http-errors "~1.6.2" - mime-types "~2.1.17" - parseurl "~1.3.2" - -serve-static@1.14.1, serve-static@^1.13.2: - version "1.14.1" - resolved "https://registry.yarnpkg.com/serve-static/-/serve-static-1.14.1.tgz#666e636dc4f010f7ef29970a88a674320898b2f9" - integrity sha512-JMrvUwE54emCYWlTI+hGrGv5I8dEwmco/00EvkzIIsR7MqrHonbD9pO2MOfFnpFntl7ecpZs+3mW+XbQZu9QCg== - dependencies: - encodeurl "~1.0.2" - escape-html "~1.0.3" - parseurl "~1.3.3" - send "0.17.1" - -set-blocking@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/set-blocking/-/set-blocking-2.0.0.tgz#045f9782d011ae9a6803ddd382b24392b3d890f7" - integrity sha1-BF+XgtARrppoA93TgrJDkrPYkPc= - -set-value@^2.0.0, set-value@^2.0.1: - version "2.0.1" - resolved "https://registry.yarnpkg.com/set-value/-/set-value-2.0.1.tgz#a18d40530e6f07de4228c7defe4227af8cad005b" - integrity sha512-JxHc1weCN68wRY0fhCoXpyK55m/XPHafOmK4UWD7m2CI14GMcFypt4w/0+NV5f/ZMby2F6S2wwA7fgynh9gWSw== - dependencies: - extend-shallow "^2.0.1" - is-extendable "^0.1.1" - is-plain-object "^2.0.3" - split-string "^3.0.1" - -setimmediate@^1.0.4, setimmediate@~1.0.4: - version "1.0.5" - resolved "https://registry.yarnpkg.com/setimmediate/-/setimmediate-1.0.5.tgz#290cbb232e306942d7d7ea9b83732ab7856f8285" - integrity sha1-KQy7Iy4waULX1+qbg3Mqt4VvgoU= - -setprototypeof@1.1.0: - version "1.1.0" - resolved "https://registry.yarnpkg.com/setprototypeof/-/setprototypeof-1.1.0.tgz#d0bd85536887b6fe7c0d818cb962d9d91c54e656" - integrity sha512-BvE/TwpZX4FXExxOxZyRGQQv651MSwmWKZGqvmPcRIjDqWub67kTKuIMx43cZZrS/cBBzwBcNDWoFxt2XEFIpQ== - -setprototypeof@1.1.1: - version "1.1.1" - resolved "https://registry.yarnpkg.com/setprototypeof/-/setprototypeof-1.1.1.tgz#7e95acb24aa92f5885e0abef5ba131330d4ae683" - integrity sha512-JvdAWfbXeIGaZ9cILp38HntZSFSo3mWg6xGcJJsd+d4aRMOqauag1C63dJfDw7OaMYwEbHMOxEZ1lqVRYP2OAw== - -sha.js@2.2.6: - version "2.2.6" - resolved "https://registry.yarnpkg.com/sha.js/-/sha.js-2.2.6.tgz#17ddeddc5f722fb66501658895461977867315ba" - integrity sha1-F93t3F9yL7ZlAWWIlUYZd4ZzFbo= - -sha.js@^2.4.0, sha.js@^2.4.8: - version "2.4.11" - resolved "https://registry.yarnpkg.com/sha.js/-/sha.js-2.4.11.tgz#37a5cf0b81ecbc6943de109ba2960d1b26584ae7" - integrity sha512-QMEp5B7cftE7APOjk5Y6xgrbWu+WkLVQwk8JNjZ8nKRciZaByEW6MubieAiToS7+dwvrjGhH8jRXz3MVd0AYqQ== - dependencies: - inherits "^2.0.1" - safe-buffer "^5.0.1" - -shebang-command@^1.2.0: - version "1.2.0" - resolved "https://registry.yarnpkg.com/shebang-command/-/shebang-command-1.2.0.tgz#44aac65b695b03398968c39f363fee5deafdf1ea" - integrity sha1-RKrGW2lbAzmJaMOfNj/uXer98eo= - dependencies: - shebang-regex "^1.0.0" - -shebang-command@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/shebang-command/-/shebang-command-2.0.0.tgz#ccd0af4f8835fbdc265b82461aaf0c36663f34ea" - integrity sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA== - dependencies: - shebang-regex "^3.0.0" - -shebang-regex@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/shebang-regex/-/shebang-regex-1.0.0.tgz#da42f49740c0b42db2ca9728571cb190c98efea3" - integrity sha1-2kL0l0DAtC2yypcoVxyxkMmO/qM= - -shebang-regex@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/shebang-regex/-/shebang-regex-3.0.0.tgz#ae16f1644d873ecad843b0307b143362d4c42172" - integrity sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A== - -side-channel@^1.0.3, side-channel@^1.0.4: - version "1.0.4" - resolved "https://registry.yarnpkg.com/side-channel/-/side-channel-1.0.4.tgz#efce5c8fdc104ee751b25c58d4290011fa5ea2cf" - integrity sha512-q5XPytqFEIKHkGdiMIrY10mvLRvnQh42/+GoBlFW3b2LXLE2xxJpZFdm94we0BaoV3RwJyGqg5wS7epxTv0Zvw== - dependencies: - call-bind "^1.0.0" - get-intrinsic "^1.0.2" - object-inspect "^1.9.0" - -signal-exit@^3.0.0, signal-exit@^3.0.2: - version "3.0.5" - resolved "https://registry.yarnpkg.com/signal-exit/-/signal-exit-3.0.5.tgz#9e3e8cc0c75a99472b44321033a7702e7738252f" - integrity sha512-KWcOiKeQj6ZyXx7zq4YxSMgHRlod4czeBQZrPb8OKcohcqAXShm7E20kEMle9WBt26hFcAf0qLOcp5zmY7kOqQ== - -sinon@^4.1.3: - version "4.5.0" - resolved "https://registry.yarnpkg.com/sinon/-/sinon-4.5.0.tgz#427ae312a337d3c516804ce2754e8c0d5028cb04" - integrity sha512-trdx+mB0VBBgoYucy6a9L7/jfQOmvGeaKZT4OOJ+lPAtI8623xyGr8wLiE4eojzBS8G9yXbhx42GHUOVLr4X2w== - dependencies: - "@sinonjs/formatio" "^2.0.0" - diff "^3.1.0" - lodash.get "^4.4.2" - lolex "^2.2.0" - nise "^1.2.0" - supports-color "^5.1.0" - type-detect "^4.0.5" - -slash@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/slash/-/slash-3.0.0.tgz#6539be870c165adbd5240220dbe361f1bc4d4634" - integrity sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q== - -slice-ansi@1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/slice-ansi/-/slice-ansi-1.0.0.tgz#044f1a49d8842ff307aad6b505ed178bd950134d" - integrity sha512-POqxBK6Lb3q6s047D/XsDVNPnF9Dl8JSaqe9h9lURl0OdNqy/ujDrOiIHtsqXMGbWWTIomRzAMaTyawAU//Reg== - dependencies: - is-fullwidth-code-point "^2.0.0" - -slice-ansi@^4.0.0: - version "4.0.0" - resolved "https://registry.yarnpkg.com/slice-ansi/-/slice-ansi-4.0.0.tgz#500e8dd0fd55b05815086255b3195adf2a45fe6b" - integrity sha512-qMCMfhY040cVHT43K9BFygqYbUPFZKHOg7K73mtTWJRb8pyP3fzf4Ixd5SzdEJQ6MRUg/WBnOLxghZtKKurENQ== - dependencies: - ansi-styles "^4.0.0" - astral-regex "^2.0.0" - is-fullwidth-code-point "^3.0.0" - -snapdragon-node@^2.0.1: - version "2.1.1" - resolved "https://registry.yarnpkg.com/snapdragon-node/-/snapdragon-node-2.1.1.tgz#6c175f86ff14bdb0724563e8f3c1b021a286853b" - integrity sha512-O27l4xaMYt/RSQ5TR3vpWCAB5Kb/czIcqUFOM/C4fYcLnbZUc1PkjTAMjof2pBWaSTwOUd6qUHcFGVGj7aIwnw== - dependencies: - define-property "^1.0.0" - isobject "^3.0.0" - snapdragon-util "^3.0.1" - -snapdragon-util@^3.0.1: - version "3.0.1" - resolved "https://registry.yarnpkg.com/snapdragon-util/-/snapdragon-util-3.0.1.tgz#f956479486f2acd79700693f6f7b805e45ab56e2" - integrity sha512-mbKkMdQKsjX4BAL4bRYTj21edOf8cN7XHdYUJEe+Zn99hVEYcMvKPct1IqNe7+AZPirn8BCDOQBHQZknqmKlZQ== - dependencies: - kind-of "^3.2.0" - -snapdragon@^0.8.1: - version "0.8.2" - resolved "https://registry.yarnpkg.com/snapdragon/-/snapdragon-0.8.2.tgz#64922e7c565b0e14204ba1aa7d6964278d25182d" - integrity sha512-FtyOnWN/wCHTVXOMwvSv26d+ko5vWlIDD6zoUJ7LW8vh+ZBC8QdljveRP+crNrtBwioEUWy/4dMtbBjA4ioNlg== - dependencies: - base "^0.11.1" - debug "^2.2.0" - define-property "^0.2.5" - extend-shallow "^2.0.1" - map-cache "^0.2.2" - source-map "^0.5.6" - source-map-resolve "^0.5.0" - use "^3.1.0" - -socket.io-adapter@~2.1.0: - version "2.1.0" - resolved "https://registry.yarnpkg.com/socket.io-adapter/-/socket.io-adapter-2.1.0.tgz#edc5dc36602f2985918d631c1399215e97a1b527" - integrity sha512-+vDov/aTsLjViYTwS9fPy5pEtTkrbEKsw2M+oVSoFGw6OD1IpvlV1VPhUzNbofCQ8oyMbdYJqDtGdmHQK6TdPg== - -socket.io-parser@~4.0.3: - version "4.0.4" - resolved "https://registry.yarnpkg.com/socket.io-parser/-/socket.io-parser-4.0.4.tgz#9ea21b0d61508d18196ef04a2c6b9ab630f4c2b0" - integrity sha512-t+b0SS+IxG7Rxzda2EVvyBZbvFPBCjJoyHuE0P//7OAsN23GItzDRdWa6ALxZI/8R5ygK7jAR6t028/z+7295g== - dependencies: - "@types/component-emitter" "^1.2.10" - component-emitter "~1.3.0" - debug "~4.3.1" - -socket.io@^3.1.0: - version "3.1.2" - resolved "https://registry.yarnpkg.com/socket.io/-/socket.io-3.1.2.tgz#06e27caa1c4fc9617547acfbb5da9bc1747da39a" - integrity sha512-JubKZnTQ4Z8G4IZWtaAZSiRP3I/inpy8c/Bsx2jrwGrTbKeVU5xd6qkKMHpChYeM3dWZSO0QACiGK+obhBNwYw== - dependencies: - "@types/cookie" "^0.4.0" - "@types/cors" "^2.8.8" - "@types/node" ">=10.0.0" - accepts "~1.3.4" - base64id "~2.0.0" - debug "~4.3.1" - engine.io "~4.1.0" - socket.io-adapter "~2.1.0" - socket.io-parser "~4.0.3" - -source-list-map@^2.0.0: - version "2.0.1" - resolved "https://registry.yarnpkg.com/source-list-map/-/source-list-map-2.0.1.tgz#3993bd873bfc48479cca9ea3a547835c7c154b34" - integrity sha512-qnQ7gVMxGNxsiL4lEuJwe/To8UnK7fAnmbGEEH8RpLouuKbeEm0lhbQVFIrNSuB+G7tVrAlVsZgETT5nljf+Iw== - -source-list-map@~0.1.7: - version "0.1.8" - resolved "https://registry.yarnpkg.com/source-list-map/-/source-list-map-0.1.8.tgz#c550b2ab5427f6b3f21f5afead88c4f5587b2106" - integrity sha1-xVCyq1Qn9rPyH1r+rYjE9Vh7IQY= - -source-map-js@^0.6.2: - version "0.6.2" - resolved "https://registry.yarnpkg.com/source-map-js/-/source-map-js-0.6.2.tgz#0bb5de631b41cfbda6cfba8bd05a80efdfd2385e" - integrity sha512-/3GptzWzu0+0MBQFrDKzw/DvvMTUORvgY6k6jd/VS6iCR4RDTKWH6v6WPwQoUO8667uQEf9Oe38DxAYWY5F/Ug== - -source-map-resolve@^0.5.0: - version "0.5.3" - resolved "https://registry.yarnpkg.com/source-map-resolve/-/source-map-resolve-0.5.3.tgz#190866bece7553e1f8f267a2ee82c606b5509a1a" - integrity sha512-Htz+RnsXWk5+P2slx5Jh3Q66vhQj1Cllm0zvnaY98+NFx+Dv2CF/f5O/t8x+KaNdrdIAsruNzoh/KpialbqAnw== - dependencies: - atob "^2.1.2" - decode-uri-component "^0.2.0" - resolve-url "^0.2.1" - source-map-url "^0.4.0" - urix "^0.1.0" - -source-map-resolve@^0.6.0: - version "0.6.0" - resolved "https://registry.yarnpkg.com/source-map-resolve/-/source-map-resolve-0.6.0.tgz#3d9df87e236b53f16d01e58150fc7711138e5ed2" - integrity sha512-KXBr9d/fO/bWo97NXsPIAW1bFSBOuCnjbNTBMO7N59hsv5i9yzRDfcYwwt0l04+VqnKC+EwzvJZIP/qkuMgR/w== - dependencies: - atob "^2.1.2" - decode-uri-component "^0.2.0" - -source-map-support@~0.5.20: - version "0.5.20" - resolved "https://registry.yarnpkg.com/source-map-support/-/source-map-support-0.5.20.tgz#12166089f8f5e5e8c56926b377633392dd2cb6c9" - integrity sha512-n1lZZ8Ve4ksRqizaBQgxXDgKwttHDhyfQjA6YZZn8+AroHbsIz+JjwxQDxbp+7y5OYCI8t1Yk7etjD9CRd2hIw== - dependencies: - buffer-from "^1.0.0" - source-map "^0.6.0" - -source-map-url@^0.4.0: - version "0.4.1" - resolved "https://registry.yarnpkg.com/source-map-url/-/source-map-url-0.4.1.tgz#0af66605a745a5a2f91cf1bbf8a7afbc283dec56" - integrity sha512-cPiFOTLUKvJFIg4SKVScy4ilPPW6rFgMgfuZJPNoDuMs3nC1HbMUycBoJw77xFIp6z1UJQJOfx6C9GMH80DiTw== - -source-map@^0.5.0, source-map@^0.5.1, source-map@^0.5.3, source-map@^0.5.6, source-map@~0.5.1: - version "0.5.7" - resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.5.7.tgz#8a039d2d1021d22d1ea14c80d8ea468ba2ef3fcc" - integrity sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w= - -source-map@^0.6.0, source-map@^0.6.1, source-map@~0.6.1: - version "0.6.1" - resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.6.1.tgz#74722af32e9614e9c287a8d0bbde48b5e2f1a263" - integrity sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g== - -source-map@~0.2.0: - version "0.2.0" - resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.2.0.tgz#dab73fbcfc2ba819b4de03bd6f6eaa48164b3f9d" - integrity sha1-2rc/vPwrqBm03gO9b26qSBZLP50= - dependencies: - amdefine ">=0.0.4" - -source-map@~0.4.1: - version "0.4.4" - resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.4.4.tgz#eba4f5da9c0dc999de68032d8b4f76173652036b" - integrity sha1-66T12pwNyZneaAMti092FzZSA2s= - dependencies: - amdefine ">=0.0.4" - -source-map@~0.7.2: - version "0.7.3" - resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.7.3.tgz#5302f8169031735226544092e64981f751750383" - integrity sha512-CkCj6giN3S+n9qrYiBTX5gystlENnRW5jZeNLHpe6aue+SrHcG5VYwujhW9s4dY31mEGsxBDrHR6oI69fTXsaQ== - -sourcemap-codec@^1.4.4: - version "1.4.8" - resolved "https://registry.yarnpkg.com/sourcemap-codec/-/sourcemap-codec-1.4.8.tgz#ea804bd94857402e6992d05a38ef1ae35a9ab4c4" - integrity sha512-9NykojV5Uih4lgo5So5dtw+f0JgJX30KCNI8gwhz2J9A15wD0Ml6tjHKwf6fTSa6fAdVBdZeNOs9eJ71qCk8vA== - -space-separated-tokens@^1.0.0: - version "1.1.5" - resolved "https://registry.yarnpkg.com/space-separated-tokens/-/space-separated-tokens-1.1.5.tgz#85f32c3d10d9682007e917414ddc5c26d1aa6899" - integrity sha512-q/JSVd1Lptzhf5bkYm4ob4iWPjx0KiRe3sRFBNrVqbJkFaBm5vbbowy1mymoPNLRa52+oadOhJ+K49wsSeSjTA== - -sparkles@^1.0.0: - version "1.0.1" - resolved "https://registry.yarnpkg.com/sparkles/-/sparkles-1.0.1.tgz#008db65edce6c50eec0c5e228e1945061dd0437c" - integrity sha512-dSO0DDYUahUt/0/pD/Is3VIm5TGJjludZ0HVymmhYF6eNA53PVLhnUk0znSYbH8IYBuJdCE+1luR22jNLMaQdw== - -spdx-correct@^3.0.0: - version "3.1.1" - resolved "https://registry.yarnpkg.com/spdx-correct/-/spdx-correct-3.1.1.tgz#dece81ac9c1e6713e5f7d1b6f17d468fa53d89a9" - integrity sha512-cOYcUWwhCuHCXi49RhFRCyJEK3iPj1Ziz9DpViV3tbZOwXD49QzIN3MpOLJNxh2qwq2lJJZaKMVw9qNi4jTC0w== - dependencies: - spdx-expression-parse "^3.0.0" - spdx-license-ids "^3.0.0" - -spdx-exceptions@^2.1.0: - version "2.3.0" - resolved "https://registry.yarnpkg.com/spdx-exceptions/-/spdx-exceptions-2.3.0.tgz#3f28ce1a77a00372683eade4a433183527a2163d" - integrity sha512-/tTrYOC7PPI1nUAgx34hUpqXuyJG+DTHJTnIULG4rDygi4xu/tfgmq1e1cIRwRzwZgo4NLySi+ricLkZkw4i5A== - -spdx-expression-parse@^3.0.0: - version "3.0.1" - resolved "https://registry.yarnpkg.com/spdx-expression-parse/-/spdx-expression-parse-3.0.1.tgz#cf70f50482eefdc98e3ce0a6833e4a53ceeba679" - integrity sha512-cbqHunsQWnJNE6KhVSMsMeH5H/L9EpymbzqTQ3uLwNCLZ1Q481oWaofqH7nO6V07xlXwY6PhQdQ2IedWx/ZK4Q== - dependencies: - spdx-exceptions "^2.1.0" - spdx-license-ids "^3.0.0" - -spdx-license-ids@^3.0.0: - version "3.0.10" - resolved "https://registry.yarnpkg.com/spdx-license-ids/-/spdx-license-ids-3.0.10.tgz#0d9becccde7003d6c658d487dd48a32f0bf3014b" - integrity sha512-oie3/+gKf7QtpitB0LYLETe+k8SifzsX4KixvpOsbI6S0kRiRQ5MKOio8eMSAKQ17N06+wdEOXRiId+zOxo0hA== - -split-on-first@^1.0.0: - version "1.1.0" - resolved "https://registry.yarnpkg.com/split-on-first/-/split-on-first-1.1.0.tgz#f610afeee3b12bce1d0c30425e76398b78249a5f" - integrity sha512-43ZssAJaMusuKWL8sKUBQXHWOpq8d6CfN/u1p4gUzfJkM05C8rxTmYrkIPTXapZpORA6LkkzcUulJ8FqA7Uudw== - -split-string@^3.0.1, split-string@^3.0.2: - version "3.1.0" - resolved "https://registry.yarnpkg.com/split-string/-/split-string-3.1.0.tgz#7cb09dda3a86585705c64b39a6466038682e8fe2" - integrity sha512-NzNVhJDYpwceVVii8/Hu6DKfD2G+NrQHlS/V/qgv763EYudVwEcMQNxd2lh+0VrUByXN/oJkl5grOhYWvQUYiw== - dependencies: - extend-shallow "^3.0.0" - -split2@^3.0.0, split2@^3.2.2: - version "3.2.2" - resolved "https://registry.yarnpkg.com/split2/-/split2-3.2.2.tgz#bf2cf2a37d838312c249c89206fd7a17dd12365f" - integrity sha512-9NThjpgZnifTkJpzTZ7Eue85S49QwpNhZTq6GRJwObb6jnLFNGB7Qm73V5HewTROPyxD0C29xqmaI68bQtV+hg== - dependencies: - readable-stream "^3.0.0" - -split@0.3: - version "0.3.3" - resolved "https://registry.yarnpkg.com/split/-/split-0.3.3.tgz#cd0eea5e63a211dfff7eb0f091c4133e2d0dd28f" - integrity sha1-zQ7qXmOiEd//frDwkcQTPi0N0o8= - dependencies: - through "2" - -split@^1.0.0: - version "1.0.1" - resolved "https://registry.yarnpkg.com/split/-/split-1.0.1.tgz#605bd9be303aa59fb35f9229fbea0ddec9ea07d9" - integrity sha512-mTyOoPbrivtXnwnIxZRFYRrPNtEFKlpB2fvjSnCQUiAA6qAZzqwna5envK4uk6OIeP17CsdF3rSBGYVBsU0Tkg== - dependencies: - through "2" - -sprintf-js@~1.0.2: - version "1.0.3" - resolved "https://registry.yarnpkg.com/sprintf-js/-/sprintf-js-1.0.3.tgz#04e6926f662895354f3dd015203633b857297e2c" - integrity sha1-BOaSb2YolTVPPdAVIDYzuFcpfiw= - -sshpk@^1.7.0: - version "1.16.1" - resolved "https://registry.yarnpkg.com/sshpk/-/sshpk-1.16.1.tgz#fb661c0bef29b39db40769ee39fa70093d6f6877" - integrity sha512-HXXqVUq7+pcKeLqqZj6mHFUMvXtOJt1uoUx09pFW6011inTMxqI8BA8PM95myrIyyKwdnzjdFjLiE6KBPVtJIg== - dependencies: - asn1 "~0.2.3" - assert-plus "^1.0.0" - bcrypt-pbkdf "^1.0.0" - dashdash "^1.12.0" - ecc-jsbn "~0.1.1" - getpass "^0.1.1" - jsbn "~0.1.0" - safer-buffer "^2.0.2" - tweetnacl "~0.14.0" - -stack-trace@0.0.10: - version "0.0.10" - resolved "https://registry.yarnpkg.com/stack-trace/-/stack-trace-0.0.10.tgz#547c70b347e8d32b4e108ea1a2a159e5fdde19c0" - integrity sha1-VHxws0fo0ytOEI6hoqFZ5f3eGcA= - -stack-utils@^2.0.3: - version "2.0.5" - resolved "https://registry.yarnpkg.com/stack-utils/-/stack-utils-2.0.5.tgz#d25265fca995154659dbbfba3b49254778d2fdd5" - integrity sha512-xrQcmYhOsn/1kX+Vraq+7j4oE2j/6BFscZ0etmYg81xuM8Gq0022Pxb8+IqgOFUIaxHs0KaSb7T1+OegiNrNFA== - dependencies: - escape-string-regexp "^2.0.0" - -standard-version@^9.0.0: - version "9.3.1" - resolved "https://registry.yarnpkg.com/standard-version/-/standard-version-9.3.1.tgz#786c16c318847f58a31a2434f97e8db33a635853" - integrity sha512-5qMxXw/FxLouC5nANyx/5RY1kiorJx9BppUso8gN07MG64q2uLRmrPb4KfXp3Ql4s/gxjZwZ89e0FwxeLubGww== - dependencies: - chalk "^2.4.2" - conventional-changelog "3.1.24" - conventional-changelog-config-spec "2.1.0" - conventional-changelog-conventionalcommits "4.5.0" - conventional-recommended-bump "6.1.0" - detect-indent "^6.0.0" - detect-newline "^3.1.0" - dotgitignore "^2.1.0" - figures "^3.1.0" - find-up "^5.0.0" - fs-access "^1.0.1" - git-semver-tags "^4.0.0" - semver "^7.1.1" - stringify-package "^1.0.1" - yargs "^16.0.0" - -static-extend@^0.1.1: - version "0.1.2" - resolved "https://registry.yarnpkg.com/static-extend/-/static-extend-0.1.2.tgz#60809c39cbff55337226fd5e0b520f341f1fb5c6" - integrity sha1-YICcOcv/VTNyJv1eC1IPNB8ftcY= - dependencies: - define-property "^0.2.5" - object-copy "^0.1.0" - -"statuses@>= 1.4.0 < 2", "statuses@>= 1.5.0 < 2", statuses@~1.5.0: - version "1.5.0" - resolved "https://registry.yarnpkg.com/statuses/-/statuses-1.5.0.tgz#161c7dac177659fd9811f43771fa99381478628c" - integrity sha1-Fhx9rBd2Wf2YEfQ3cfqZOBR4Yow= - -statuses@~1.4.0: - version "1.4.0" - resolved "https://registry.yarnpkg.com/statuses/-/statuses-1.4.0.tgz#bb73d446da2796106efcc1b601a253d6c46bd087" - integrity sha512-zhSCtt8v2NDrRlPQpCNtw/heZLtfUDqxBM1udqikb/Hbk52LK4nQSwr10u77iopCW5LsyHpuXS0GnEc48mLeew== - -stream-array@^1.1.2: - version "1.1.2" - resolved "https://registry.yarnpkg.com/stream-array/-/stream-array-1.1.2.tgz#9e5f7345f2137c30ee3b498b9114e80b52bb7eb5" - integrity sha1-nl9zRfITfDDuO0mLkRToC1K7frU= - dependencies: - readable-stream "~2.1.0" - -stream-browserify@^2.0.1: - version "2.0.2" - resolved "https://registry.yarnpkg.com/stream-browserify/-/stream-browserify-2.0.2.tgz#87521d38a44aa7ee91ce1cd2a47df0cb49dd660b" - integrity sha512-nX6hmklHs/gr2FuxYDltq8fJA1GDlxKQCz8O/IM4atRqBH8OORmBNgfvW5gG10GT/qQ9u0CzIvr2X5Pkt6ntqg== - dependencies: - inherits "~2.0.1" - readable-stream "^2.0.2" - -stream-buffers@^3.0.2: - version "3.0.2" - resolved "https://registry.yarnpkg.com/stream-buffers/-/stream-buffers-3.0.2.tgz#5249005a8d5c2d00b3a32e6e0a6ea209dc4f3521" - integrity sha512-DQi1h8VEBA/lURbSwFtEHnSTb9s2/pwLEaFuNhXwy1Dx3Sa0lOuYT2yNUr4/j2fs8oCAMANtrZ5OrPZtyVs3MQ== - -stream-combiner2@^1.1.1: - version "1.1.1" - resolved "https://registry.yarnpkg.com/stream-combiner2/-/stream-combiner2-1.1.1.tgz#fb4d8a1420ea362764e21ad4780397bebcb41cbe" - integrity sha1-+02KFCDqNidk4hrUeAOXvry0HL4= - dependencies: - duplexer2 "~0.1.0" - readable-stream "^2.0.2" - -stream-combiner@~0.0.4: - version "0.0.4" - resolved "https://registry.yarnpkg.com/stream-combiner/-/stream-combiner-0.0.4.tgz#4d5e433c185261dde623ca3f44c586bcf5c4ad14" - integrity sha1-TV5DPBhSYd3mI8o/RMWGvPXErRQ= - dependencies: - duplexer "~0.1.1" - -stream-exhaust@^1.0.1: - version "1.0.2" - resolved "https://registry.yarnpkg.com/stream-exhaust/-/stream-exhaust-1.0.2.tgz#acdac8da59ef2bc1e17a2c0ccf6c320d120e555d" - integrity sha512-b/qaq/GlBK5xaq1yrK9/zFcyRSTNxmcZwFLGSTG0mXgZl/4Z6GgiyYOXOvY7N3eEvFRAG1bkDRz5EPGSvPYQlw== - -stream-http@^2.3.1, stream-http@^2.7.2: - version "2.8.3" - resolved "https://registry.yarnpkg.com/stream-http/-/stream-http-2.8.3.tgz#b2d242469288a5a27ec4fe8933acf623de6514fc" - integrity sha512-+TSkfINHDo4J+ZobQLWiMouQYB+UVYFttRA94FpEzzJ7ZdqcL4uUUQ7WkdkI4DSozGmgBUE/a47L+38PenXhUw== - dependencies: - builtin-status-codes "^3.0.0" - inherits "^2.0.1" - readable-stream "^2.3.6" - to-arraybuffer "^1.0.0" - xtend "^4.0.0" - -stream-shift@^1.0.0: - version "1.0.1" - resolved "https://registry.yarnpkg.com/stream-shift/-/stream-shift-1.0.1.tgz#d7088281559ab2778424279b0877da3c392d5a3d" - integrity sha512-AiisoFqQ0vbGcZgQPY1cdP2I76glaVA/RauYR4G4thNFgkTqr90yXTo4LYX60Jl+sIlPNHHdGSwo01AvbKUSVQ== - -streamroller@^2.2.4: - version "2.2.4" - resolved "https://registry.yarnpkg.com/streamroller/-/streamroller-2.2.4.tgz#c198ced42db94086a6193608187ce80a5f2b0e53" - integrity sha512-OG79qm3AujAM9ImoqgWEY1xG4HX+Lw+yY6qZj9R1K2mhF5bEmQ849wvrb+4vt4jLMLzwXttJlQbOdPOQVRv7DQ== - dependencies: - date-format "^2.1.0" - debug "^4.1.1" - fs-extra "^8.1.0" - -strict-uri-encode@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/strict-uri-encode/-/strict-uri-encode-2.0.0.tgz#b9c7330c7042862f6b142dc274bbcc5866ce3546" - integrity sha1-ucczDHBChi9rFC3CdLvMWGbONUY= - -string-template@~0.2.1: - version "0.2.1" - resolved "https://registry.yarnpkg.com/string-template/-/string-template-0.2.1.tgz#42932e598a352d01fc22ec3367d9d84eec6c9add" - integrity sha1-QpMuWYo1LQH8IuwzZ9nYTuxsmt0= - -string-width@^1.0.1, string-width@^1.0.2: - version "1.0.2" - resolved "https://registry.yarnpkg.com/string-width/-/string-width-1.0.2.tgz#118bdf5b8cdc51a2a7e70d211e07e2b0b9b107d3" - integrity sha1-EYvfW4zcUaKn5w0hHgfisLmxB9M= - dependencies: - code-point-at "^1.0.0" - is-fullwidth-code-point "^1.0.0" - strip-ansi "^3.0.0" - -string-width@^2.0.0, string-width@^2.1.0, string-width@^2.1.1: - version "2.1.1" - resolved "https://registry.yarnpkg.com/string-width/-/string-width-2.1.1.tgz#ab93f27a8dc13d28cac815c462143a6d9012ae9e" - integrity sha512-nOqH59deCq9SRHlxq1Aw85Jnt4w6KvLKqWVik6oA9ZklXLNIOlqg4F2yrT1MVaTjAqvVwdfeZ7w7aCvJD7ugkw== - dependencies: - is-fullwidth-code-point "^2.0.0" - strip-ansi "^4.0.0" - -string-width@^4.0.0, string-width@^4.1.0, string-width@^4.2.0, string-width@^4.2.3: - version "4.2.3" - resolved "https://registry.yarnpkg.com/string-width/-/string-width-4.2.3.tgz#269c7117d27b05ad2e536830a8ec895ef9c6d010" - integrity sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g== - dependencies: - emoji-regex "^8.0.0" - is-fullwidth-code-point "^3.0.0" - strip-ansi "^6.0.1" - -string.prototype.trimend@^1.0.4: - version "1.0.4" - resolved "https://registry.yarnpkg.com/string.prototype.trimend/-/string.prototype.trimend-1.0.4.tgz#e75ae90c2942c63504686c18b287b4a0b1a45f80" - integrity sha512-y9xCjw1P23Awk8EvTpcyL2NIr1j7wJ39f+k6lvRnSMz+mz9CGz9NYPelDk42kOz6+ql8xjfK8oYzy3jAP5QU5A== - dependencies: - call-bind "^1.0.2" - define-properties "^1.1.3" - -string.prototype.trimstart@^1.0.4: - version "1.0.4" - resolved "https://registry.yarnpkg.com/string.prototype.trimstart/-/string.prototype.trimstart-1.0.4.tgz#b36399af4ab2999b4c9c648bd7a3fb2bb26feeed" - integrity sha512-jh6e984OBfvxS50tdY2nRZnoC5/mLFKOREQfw8t5yytkoUsJRNxvI/E39qu1sD0OtWI3OC0XgKSmcWwziwYuZw== - dependencies: - call-bind "^1.0.2" - define-properties "^1.1.3" - -string_decoder@0.10, string_decoder@^0.10.25, string_decoder@~0.10.x: - version "0.10.31" - resolved "https://registry.yarnpkg.com/string_decoder/-/string_decoder-0.10.31.tgz#62e203bc41766c6c28c9fc84301dab1c5310fa94" - integrity sha1-YuIDvEF2bGwoyfyEMB2rHFMQ+pQ= - -string_decoder@^1.0.0, string_decoder@^1.1.1: - version "1.3.0" - resolved "https://registry.yarnpkg.com/string_decoder/-/string_decoder-1.3.0.tgz#42f114594a46cf1a8e30b0a84f56c78c3edac21e" - integrity sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA== - dependencies: - safe-buffer "~5.2.0" - -string_decoder@~1.1.1: - version "1.1.1" - resolved "https://registry.yarnpkg.com/string_decoder/-/string_decoder-1.1.1.tgz#9cf1611ba62685d7030ae9e4ba34149c3af03fc8" - integrity sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg== - dependencies: - safe-buffer "~5.1.0" - -stringify-entities@^3.0.1: - version "3.1.0" - resolved "https://registry.yarnpkg.com/stringify-entities/-/stringify-entities-3.1.0.tgz#b8d3feac256d9ffcc9fa1fefdcf3ca70576ee903" - integrity sha512-3FP+jGMmMV/ffZs86MoghGqAoqXAdxLrJP4GUdrDN1aIScYih5tuIO3eF4To5AJZ79KDZ8Fpdy7QJnK8SsL1Vg== - dependencies: - character-entities-html4 "^1.0.0" - character-entities-legacy "^1.0.0" - xtend "^4.0.0" - -stringify-package@^1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/stringify-package/-/stringify-package-1.0.1.tgz#e5aa3643e7f74d0f28628b72f3dad5cecfc3ba85" - integrity sha512-sa4DUQsYciMP1xhKWGuFM04fB0LG/9DlluZoSVywUMRNvzid6XucHK0/90xGxRoHrAaROrcHK1aPKaijCtSrhg== - -strip-ansi@^0.3.0: - version "0.3.0" - resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-0.3.0.tgz#25f48ea22ca79187f3174a4db8759347bb126220" - integrity sha1-JfSOoiynkYfzF0pNuHWTR7sSYiA= - dependencies: - ansi-regex "^0.2.1" - -strip-ansi@^3.0.0, strip-ansi@^3.0.1: - version "3.0.1" - resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-3.0.1.tgz#6a385fb8853d952d5ff05d0e8aaf94278dc63dcf" - integrity sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8= - dependencies: - ansi-regex "^2.0.0" - -strip-ansi@^4.0.0: - version "4.0.0" - resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-4.0.0.tgz#a8479022eb1ac368a871389b635262c505ee368f" - integrity sha1-qEeQIusaw2iocTibY1JixQXuNo8= - dependencies: - ansi-regex "^3.0.0" - -strip-ansi@^6.0.0, strip-ansi@^6.0.1: - version "6.0.1" - resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-6.0.1.tgz#9e26c63d30f53443e9489495b2105d37b67a85d9" - integrity sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A== - dependencies: - ansi-regex "^5.0.1" - -strip-bom-string@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/strip-bom-string/-/strip-bom-string-1.0.0.tgz#e5211e9224369fbb81d633a2f00044dc8cedad92" - integrity sha1-5SEekiQ2n7uB1jOi8ABE3IztrZI= - -strip-bom@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/strip-bom/-/strip-bom-2.0.0.tgz#6219a85616520491f35788bdbf1447a99c7e6b0e" - integrity sha1-YhmoVhZSBJHzV4i9vxRHqZx+aw4= - dependencies: - is-utf8 "^0.2.0" - -strip-bom@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/strip-bom/-/strip-bom-3.0.0.tgz#2334c18e9c759f7bdd56fdef7e9ae3d588e68ed3" - integrity sha1-IzTBjpx1n3vdVv3vfprj1YjmjtM= - -strip-eof@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/strip-eof/-/strip-eof-1.0.0.tgz#bb43ff5598a6eb05d89b59fcd129c983313606bf" - integrity sha1-u0P/VZim6wXYm1n80SnJgzE2Br8= - -strip-indent@^1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/strip-indent/-/strip-indent-1.0.1.tgz#0c7962a6adefa7bbd4ac366460a638552ae1a0a2" - integrity sha1-DHlipq3vp7vUrDZkYKY4VSrhoKI= - dependencies: - get-stdin "^4.0.1" - -strip-indent@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/strip-indent/-/strip-indent-3.0.0.tgz#c32e1cee940b6b3432c771bc2c54bcce73cd3001" - integrity sha512-laJTa3Jb+VQpaC6DseHhF7dXVqHTfJPCRDaEbid/drOhgitgYku/letMUqOXFoWV0zIIUbjpdH2t+tYj4bQMRQ== - dependencies: - min-indent "^1.0.0" - -strip-json-comments@3.1.1, strip-json-comments@^3.1.0, strip-json-comments@^3.1.1: - version "3.1.1" - resolved "https://registry.yarnpkg.com/strip-json-comments/-/strip-json-comments-3.1.1.tgz#31f1281b3832630434831c310c01cccda8cbe006" - integrity sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig== - -strip-json-comments@^2.0.1, strip-json-comments@~2.0.1: - version "2.0.1" - resolved "https://registry.yarnpkg.com/strip-json-comments/-/strip-json-comments-2.0.1.tgz#3c531942e908c2697c0ec344858c286c7ca0a60a" - integrity sha1-PFMZQukIwml8DsNEhYwobHygpgo= - -subarg@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/subarg/-/subarg-1.0.0.tgz#f62cf17581e996b48fc965699f54c06ae268b8d2" - integrity sha1-9izxdYHplrSPyWVpn1TAauJouNI= - dependencies: - minimist "^1.1.0" - -suffix@^0.1.0: - version "0.1.1" - resolved "https://registry.yarnpkg.com/suffix/-/suffix-0.1.1.tgz#cc58231646a0ef1102f79478ef3a9248fd9c842f" - integrity sha1-zFgjFkag7xEC95R47zqSSP2chC8= - -supports-color@5.4.0: - version "5.4.0" - resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-5.4.0.tgz#1c6b337402c2137605efe19f10fec390f6faab54" - integrity sha512-zjaXglF5nnWpsq470jSv6P9DwPvgLkuapYmfDm3JWOm0vkNTVF2tI4UrN2r6jH1qM/uc/WtxYY1hYoA2dOKj5w== - dependencies: - has-flag "^3.0.0" - -supports-color@8.1.1: - version "8.1.1" - resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-8.1.1.tgz#cd6fc17e28500cff56c1b86c0a7fd4a54a73005c" - integrity sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q== - dependencies: - has-flag "^4.0.0" - -supports-color@^0.2.0: - version "0.2.0" - resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-0.2.0.tgz#d92de2694eb3f67323973d7ae3d8b55b4c22190a" - integrity sha1-2S3iaU6z9nMjlz1649i1W0wiGQo= - -supports-color@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-2.0.0.tgz#535d045ce6b6363fa40117084629995e9df324c7" - integrity sha1-U10EXOa2Nj+kARcIRimZXp3zJMc= - -supports-color@^3.1.0: - version "3.2.3" - resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-3.2.3.tgz#65ac0504b3954171d8a64946b2ae3cbb8a5f54f6" - integrity sha1-ZawFBLOVQXHYpklGsq48u4pfVPY= - dependencies: - has-flag "^1.0.0" - -supports-color@^4.2.1: - version "4.5.0" - resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-4.5.0.tgz#be7a0de484dec5c5cddf8b3d59125044912f635b" - integrity sha1-vnoN5ITexcXN34s9WRJQRJEvY1s= - dependencies: - has-flag "^2.0.0" - -supports-color@^5.1.0, supports-color@^5.3.0: - version "5.5.0" - resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-5.5.0.tgz#e2e69a44ac8772f78a1ec0b35b689df6530efc8f" - integrity sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow== - dependencies: - has-flag "^3.0.0" - -supports-color@^6.0.0: - version "6.1.0" - resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-6.1.0.tgz#0764abc69c63d5ac842dd4867e8d025e880df8f3" - integrity sha512-qe1jfm1Mg7Nq/NSh6XE24gPXROEVsWHxC1LIx//XNlD9iw7YZQGjZNjYN7xGaEG6iKdA8EtNFW6R0gjnVXp+wQ== - dependencies: - has-flag "^3.0.0" - -supports-color@^7.1.0: - version "7.2.0" - resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-7.2.0.tgz#1b7dcdcb32b8138801b3e478ba6a51caa89648da" - integrity sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw== - dependencies: - has-flag "^4.0.0" - -sver-compat@^1.5.0: - version "1.5.0" - resolved "https://registry.yarnpkg.com/sver-compat/-/sver-compat-1.5.0.tgz#3cf87dfeb4d07b4a3f14827bc186b3fd0c645cd8" - integrity sha1-PPh9/rTQe0o/FIJ7wYaz/QxkXNg= - dependencies: - es6-iterator "^2.0.1" - es6-symbol "^3.1.1" - -table@4.0.2: - version "4.0.2" - resolved "https://registry.yarnpkg.com/table/-/table-4.0.2.tgz#a33447375391e766ad34d3486e6e2aedc84d2e36" - integrity sha512-UUkEAPdSGxtRpiV9ozJ5cMTtYiqz7Ni1OGqLXRCynrvzdtR1p+cfOWe2RJLwvUG8hNanaSRjecIqwOjqeatDsA== - dependencies: - ajv "^5.2.3" - ajv-keywords "^2.1.0" - chalk "^2.1.0" - lodash "^4.17.4" - slice-ansi "1.0.0" - string-width "^2.1.1" - -table@^6.0.9: - version "6.7.2" - resolved "https://registry.yarnpkg.com/table/-/table-6.7.2.tgz#a8d39b9f5966693ca8b0feba270a78722cbaf3b0" - integrity sha512-UFZK67uvyNivLeQbVtkiUs8Uuuxv24aSL4/Vil2PJVtMgU8Lx0CYkP12uCGa3kjyQzOSgV1+z9Wkb82fCGsO0g== - dependencies: - ajv "^8.0.1" - lodash.clonedeep "^4.5.0" - lodash.truncate "^4.4.2" - slice-ansi "^4.0.0" - string-width "^4.2.3" - strip-ansi "^6.0.1" - -tapable@^0.1.8, tapable@~0.1.8: - version "0.1.10" - resolved "https://registry.yarnpkg.com/tapable/-/tapable-0.1.10.tgz#29c35707c2b70e50d07482b5d202e8ed446dafd4" - integrity sha1-KcNXB8K3DlDQdIK10gLo7URtr9Q= - -tapable@^0.2.7: - version "0.2.9" - resolved "https://registry.yarnpkg.com/tapable/-/tapable-0.2.9.tgz#af2d8bbc9b04f74ee17af2b4d9048f807acd18a8" - integrity sha512-2wsvQ+4GwBvLPLWsNfLCDYGsW6xb7aeC6utq2Qh0PFwgEy7K7dsma9Jsmb2zSQj7GvYAyUGSntLtsv++GmgL1A== - -tar-fs@2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/tar-fs/-/tar-fs-2.0.0.tgz#677700fc0c8b337a78bee3623fdc235f21d7afad" - integrity sha512-vaY0obB6Om/fso8a8vakQBzwholQ7v5+uy+tF3Ozvxv1KNezmVQAiWtcNmMHFSFPqL3dJA8ha6gdtFbfX9mcxA== - dependencies: - chownr "^1.1.1" - mkdirp "^0.5.1" - pump "^3.0.0" - tar-stream "^2.0.0" - -tar-stream@^2.0.0, tar-stream@^2.2.0: - version "2.2.0" - resolved "https://registry.yarnpkg.com/tar-stream/-/tar-stream-2.2.0.tgz#acad84c284136b060dc3faa64474aa9aebd77287" - integrity sha512-ujeqbceABgwMZxEJnk2HDY2DlnUZ+9oEcb1KzTVfYHio0UE6dG71n60d8D2I4qNvleWrrXpmjpt7vZeF1LnMZQ== - dependencies: - bl "^4.0.3" - end-of-stream "^1.4.1" - fs-constants "^1.0.0" - inherits "^2.0.3" - readable-stream "^3.1.1" - -temp-fs@^0.9.9: - version "0.9.9" - resolved "https://registry.yarnpkg.com/temp-fs/-/temp-fs-0.9.9.tgz#8071730437870720e9431532fe2814364f8803d7" - integrity sha1-gHFzBDeHByDpQxUy/igUNk+IA9c= - dependencies: - rimraf "~2.5.2" - -ternary-stream@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/ternary-stream/-/ternary-stream-3.0.0.tgz#7951930ea9e823924d956f03d516151a2d516253" - integrity sha512-oIzdi+UL/JdktkT+7KU5tSIQjj8pbShj3OASuvDEhm0NT5lppsm7aXWAmAq4/QMaBIyfuEcNLbAQA+HpaISobQ== - dependencies: - duplexify "^4.1.1" - fork-stream "^0.0.4" - merge-stream "^2.0.0" - through2 "^3.0.1" - -terser@^5.9.0: - version "5.9.0" - resolved "https://registry.yarnpkg.com/terser/-/terser-5.9.0.tgz#47d6e629a522963240f2b55fcaa3c99083d2c351" - integrity sha512-h5hxa23sCdpzcye/7b8YqbE5OwKca/ni0RQz1uRX3tGh8haaGHqcuSqbGRybuAKNdntZ0mDgFNXPJ48xQ2RXKQ== - dependencies: - commander "^2.20.0" - source-map "~0.7.2" - source-map-support "~0.5.20" - -text-extensions@^1.0.0: - version "1.9.0" - resolved "https://registry.yarnpkg.com/text-extensions/-/text-extensions-1.9.0.tgz#1853e45fee39c945ce6f6c36b2d659b5aabc2a26" - integrity sha512-wiBrwC1EhBelW12Zy26JeOUkQ5mRu+5o8rpsJk5+2t+Y5vE7e842qtZDQ2g1NpX/29HdyFeJ4nSIhI47ENSxlQ== - -text-table@^0.2.0, text-table@~0.2.0: - version "0.2.0" - resolved "https://registry.yarnpkg.com/text-table/-/text-table-0.2.0.tgz#7f5ee823ae805207c00af2df4a84ec3fcfa570b4" - integrity sha1-f17oI66AUgfACvLfSoTsP8+lcLQ= - -textextensions@^3.2.0: - version "3.3.0" - resolved "https://registry.yarnpkg.com/textextensions/-/textextensions-3.3.0.tgz#03530d5287b86773c08b77458589148870cc71d3" - integrity sha512-mk82dS8eRABNbeVJrEiN5/UMSCliINAuz8mkUwH4SwslkNP//gbEzlWNS5au0z5Dpx40SQxzqZevZkn+WYJ9Dw== - -through2-filter@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/through2-filter/-/through2-filter-3.0.0.tgz#700e786df2367c2c88cd8aa5be4cf9c1e7831254" - integrity sha512-jaRjI2WxN3W1V8/FMZ9HKIBXixtiqs3SQSX4/YGIiP3gL6djW48VoZq9tDqeCWs3MT8YY5wb/zli8VW8snY1CA== - dependencies: - through2 "~2.0.0" - xtend "~4.0.0" - -through2@^0.4.2: - version "0.4.2" - resolved "https://registry.yarnpkg.com/through2/-/through2-0.4.2.tgz#dbf5866031151ec8352bb6c4db64a2292a840b9b" - integrity sha1-2/WGYDEVHsg1K7bE22SiKSqEC5s= - dependencies: - readable-stream "~1.0.17" - xtend "~2.1.1" - -through2@^0.5.0: - version "0.5.1" - resolved "https://registry.yarnpkg.com/through2/-/through2-0.5.1.tgz#dfdd012eb9c700e2323fd334f38ac622ab372da7" - integrity sha1-390BLrnHAOIyP9M084rGIqs3Lac= - dependencies: - readable-stream "~1.0.17" - xtend "~3.0.0" - -through2@^0.6.3: - version "0.6.5" - resolved "https://registry.yarnpkg.com/through2/-/through2-0.6.5.tgz#41ab9c67b29d57209071410e1d7a7a968cd3ad48" - integrity sha1-QaucZ7KdVyCQcUEOHXp6lozTrUg= - dependencies: - readable-stream ">=1.0.33-1 <1.1.0-0" - xtend ">=4.0.0 <4.1.0-0" - -through2@^2.0.0, through2@^2.0.3, through2@~2.0.0: - version "2.0.5" - resolved "https://registry.yarnpkg.com/through2/-/through2-2.0.5.tgz#01c1e39eb31d07cb7d03a96a70823260b23132cd" - integrity sha512-/mrRod8xqpA+IHSLyGCQ2s8SPHiCDEeQJSep1jqLYeEUClOFG2Qsh+4FU6G9VeqpZnGW/Su8LQGc4YKni5rYSQ== - dependencies: - readable-stream "~2.3.6" - xtend "~4.0.1" - -through2@^3.0.1: - version "3.0.2" - resolved "https://registry.yarnpkg.com/through2/-/through2-3.0.2.tgz#99f88931cfc761ec7678b41d5d7336b5b6a07bf4" - integrity sha512-enaDQ4MUyP2W6ZyT6EsMzqBPZaM/avg8iuo+l2d3QCs0J+6RaqkHV/2/lOwDTueBHeJ/2LG9lrLW3d5rWPucuQ== - dependencies: - inherits "^2.0.4" - readable-stream "2 || 3" - -through2@^4.0.0, through2@^4.0.2: - version "4.0.2" - resolved "https://registry.yarnpkg.com/through2/-/through2-4.0.2.tgz#a7ce3ac2a7a8b0b966c80e7c49f0484c3b239764" - integrity sha512-iOqSav00cVxEEICeD7TjLB1sueEL+81Wpzp2bY17uZjZN0pWZPuo4suZ/61VujxmqSGFfgOcNuTZ85QJwNZQpw== - dependencies: - readable-stream "3" - -through@2, "through@>=2.2.7 <3", through@^2.3.6, through@^2.3.8, through@~2.3, through@~2.3.1: - version "2.3.8" - resolved "https://registry.yarnpkg.com/through/-/through-2.3.8.tgz#0dd4c9ffaabc357960b1b724115d7e0e86a2e1f5" - integrity sha1-DdTJ/6q8NXlgsbckEV1+Doai4fU= - -time-stamp@^1.0.0: - version "1.1.0" - resolved "https://registry.yarnpkg.com/time-stamp/-/time-stamp-1.1.0.tgz#764a5a11af50561921b133f3b44e618687e0f5c3" - integrity sha1-dkpaEa9QVhkhsTPztE5hhofg9cM= - -timers-browserify@^2.0.2, timers-browserify@^2.0.4: - version "2.0.12" - resolved "https://registry.yarnpkg.com/timers-browserify/-/timers-browserify-2.0.12.tgz#44a45c11fbf407f34f97bccd1577c652361b00ee" - integrity sha512-9phl76Cqm6FhSX9Xe1ZUAMLtm1BLkKj2Qd5ApyWkXzsMRaA7dgr81kf4wJmQf/hAvg8EEyJxDo3du/0KlhPiKQ== - dependencies: - setimmediate "^1.0.4" - -timers-ext@^0.1.7: - version "0.1.7" - resolved "https://registry.yarnpkg.com/timers-ext/-/timers-ext-0.1.7.tgz#6f57ad8578e07a3fb9f91d9387d65647555e25c6" - integrity sha512-b85NUNzTSdodShTIbky6ZF02e8STtVVfD+fu4aXXShEELpozH+bCpJLYMPZbsABN2wDH7fJpqIoXxJpzbf0NqQ== - dependencies: - es5-ext "~0.10.46" - next-tick "1" - -tiny-hashes@1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/tiny-hashes/-/tiny-hashes-1.0.1.tgz#ddbe9060312ddb4efe0a174bb3a27e1331c425a1" - integrity sha512-knIN5zj4fl7kW4EBU5sLP20DWUvi/rVouvJezV0UAym2DkQaqm365Nyc8F3QEiOvunNDMxR8UhcXd1d5g+Wg1g== - -tiny-lr@^1.1.0, tiny-lr@^1.1.1: - version "1.1.1" - resolved "https://registry.yarnpkg.com/tiny-lr/-/tiny-lr-1.1.1.tgz#9fa547412f238fedb068ee295af8b682c98b2aab" - integrity sha512-44yhA3tsaRoMOjQQ+5v5mVdqef+kH6Qze9jTpqtVufgYjYt08zyZAwNwwVBj3i1rJMnR52IxOW0LK0vBzgAkuA== - dependencies: - body "^5.1.0" - debug "^3.1.0" - faye-websocket "~0.10.0" - livereload-js "^2.3.0" - object-assign "^4.1.0" - qs "^6.4.0" - -tmp@^0.0.33: - version "0.0.33" - resolved "https://registry.yarnpkg.com/tmp/-/tmp-0.0.33.tgz#6d34335889768d21b2bcda0aa277ced3b1bfadf9" - integrity sha512-jRCJlojKnZ3addtTOjdIqoRuPEKBvNXcGYqzO6zWZX8KfKEpnGY5jfggJQ3EjKuu8D4bJRr0y+cYJFmYbImXGw== - dependencies: - os-tmpdir "~1.0.2" - -tmp@^0.2.1: - version "0.2.1" - resolved "https://registry.yarnpkg.com/tmp/-/tmp-0.2.1.tgz#8457fc3037dcf4719c251367a1af6500ee1ccf14" - integrity sha512-76SUhtfqR2Ijn+xllcI5P1oyannHNHByD80W1q447gU3mp9G9PSpGdWmjUOHRDPiHYacIk66W7ubDTuPF3BEtQ== - dependencies: - rimraf "^3.0.0" - -to-absolute-glob@^2.0.0: - version "2.0.2" - resolved "https://registry.yarnpkg.com/to-absolute-glob/-/to-absolute-glob-2.0.2.tgz#1865f43d9e74b0822db9f145b78cff7d0f7c849b" - integrity sha1-GGX0PZ50sIItufFFt4z/fQ98hJs= - dependencies: - is-absolute "^1.0.0" - is-negated-glob "^1.0.0" - -to-arraybuffer@^1.0.0: - version "1.0.1" - resolved "https://registry.yarnpkg.com/to-arraybuffer/-/to-arraybuffer-1.0.1.tgz#7d229b1fcc637e466ca081180836a7aabff83f43" - integrity sha1-fSKbH8xjfkZsoIEYCDanqr/4P0M= - -to-fast-properties@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/to-fast-properties/-/to-fast-properties-2.0.0.tgz#dc5e698cbd079265bc73e0377681a4e4e83f616e" - integrity sha1-3F5pjL0HkmW8c+A3doGk5Og/YW4= - -to-object-path@^0.3.0: - version "0.3.0" - resolved "https://registry.yarnpkg.com/to-object-path/-/to-object-path-0.3.0.tgz#297588b7b0e7e0ac08e04e672f85c1f4999e17af" - integrity sha1-KXWIt7Dn4KwI4E5nL4XB9JmeF68= - dependencies: - kind-of "^3.0.2" - -to-regex-range@^2.1.0: - version "2.1.1" - resolved "https://registry.yarnpkg.com/to-regex-range/-/to-regex-range-2.1.1.tgz#7c80c17b9dfebe599e27367e0d4dd5590141db38" - integrity sha1-fIDBe53+vlmeJzZ+DU3VWQFB2zg= - dependencies: - is-number "^3.0.0" - repeat-string "^1.6.1" - -to-regex-range@^5.0.1: - version "5.0.1" - resolved "https://registry.yarnpkg.com/to-regex-range/-/to-regex-range-5.0.1.tgz#1648c44aae7c8d988a326018ed72f5b4dd0392e4" - integrity sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ== - dependencies: - is-number "^7.0.0" - -to-regex@^3.0.1, to-regex@^3.0.2: - version "3.0.2" - resolved "https://registry.yarnpkg.com/to-regex/-/to-regex-3.0.2.tgz#13cfdd9b336552f30b51f33a8ae1b42a7a7599ce" - integrity sha512-FWtleNAtZ/Ki2qtqej2CXTOayOH9bHDQF+Q48VpWyDXjbYxA4Yz8iDB31zXOBUlOHHKidDbqGVrTUvQMPmBGBw== - dependencies: - define-property "^2.0.2" - extend-shallow "^3.0.2" - regex-not "^1.0.2" - safe-regex "^1.1.0" - -to-through@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/to-through/-/to-through-2.0.0.tgz#fc92adaba072647bc0b67d6b03664aa195093af6" - integrity sha1-/JKtq6ByZHvAtn1rA2ZKoZUJOvY= - dependencies: - through2 "^2.0.3" - -toidentifier@1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/toidentifier/-/toidentifier-1.0.0.tgz#7e1be3470f1e77948bc43d94a3c8f4d7752ba553" - integrity sha512-yaOH/Pk/VEhBWWTlhI+qXxDFXlejDGcQipMlyxda9nthulaxLZUNcUqFxokp0vcYnvteJln5FNQDRrxj3YcbVw== - -tough-cookie@~2.5.0: - version "2.5.0" - resolved "https://registry.yarnpkg.com/tough-cookie/-/tough-cookie-2.5.0.tgz#cd9fb2a0aa1d5a12b473bd9fb96fa3dcff65ade2" - integrity sha512-nlLsUzgm1kfLXSXfRZMc1KLAugd4hqJHDTvc2hDIwS3mZAfMEuMbc03SujMF+GEcpaX/qboeycw6iO8JwVv2+g== - dependencies: - psl "^1.1.28" - punycode "^2.1.1" - -"traverse@>=0.3.0 <0.4": - version "0.3.9" - resolved "https://registry.yarnpkg.com/traverse/-/traverse-0.3.9.tgz#717b8f220cc0bb7b44e40514c22b2e8bbc70d8b9" - integrity sha1-cXuPIgzAu3tE5AUUwisui7xw2Lk= - -trim-newlines@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/trim-newlines/-/trim-newlines-1.0.0.tgz#5887966bb582a4503a41eb524f7d35011815a613" - integrity sha1-WIeWa7WCpFA6QetST301ARgVphM= - -trim-newlines@^3.0.0: - version "3.0.1" - resolved "https://registry.yarnpkg.com/trim-newlines/-/trim-newlines-3.0.1.tgz#260a5d962d8b752425b32f3a7db0dcacd176c144" - integrity sha512-c1PTsA3tYrIsLGkJkzHF+w9F2EyxfXGo4UyJc4pFL++FMjnq0HJS69T3M7d//gKrFKwy429bouPescbjecU+Zw== - -trough@^1.0.0: - version "1.0.5" - resolved "https://registry.yarnpkg.com/trough/-/trough-1.0.5.tgz#b8b639cefad7d0bb2abd37d433ff8293efa5f406" - integrity sha512-rvuRbTarPXmMb79SmzEp8aqXNKcK+y0XaB298IXueQ8I2PsrATcPBCSPyK/dDNa2iWOhKlfNnOjdAOTBU/nkFA== - -tryer@^1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/tryer/-/tryer-1.0.1.tgz#f2c85406800b9b0f74c9f7465b81eaad241252f8" - integrity sha512-c3zayb8/kWWpycWYg87P71E1S1ZL6b6IJxfb5fvsUgsf0S2MVGaDhDXXjDMpdCpfWXqptc+4mXwmiy1ypXqRAA== - -tsconfig-paths@^3.11.0: - version "3.11.0" - resolved "https://registry.yarnpkg.com/tsconfig-paths/-/tsconfig-paths-3.11.0.tgz#954c1fe973da6339c78e06b03ce2e48810b65f36" - integrity sha512-7ecdYDnIdmv639mmDwslG6KQg1Z9STTz1j7Gcz0xa+nshh/gKDAHcPxRbWOsA3SPp0tXP2leTcY9Kw+NAkfZzA== - dependencies: - "@types/json5" "^0.0.29" - json5 "^1.0.1" - minimist "^1.2.0" - strip-bom "^3.0.0" - -tslib@^1.10.0: - version "1.14.1" - resolved "https://registry.yarnpkg.com/tslib/-/tslib-1.14.1.tgz#cf2d38bdc34a134bcaf1091c41f6619e2f672d00" - integrity sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg== - -tslib@~2.1.0: - version "2.1.0" - resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.1.0.tgz#da60860f1c2ecaa5703ab7d39bc05b6bf988b97a" - integrity sha512-hcVC3wYEziELGGmEEXue7D75zbwIIVUMWAVbHItGPx0ziyXxrOMQx4rQEVEV45Ut/1IotuEvwqPopzIOkDMf0A== - -tty-browserify@0.0.0: - version "0.0.0" - resolved "https://registry.yarnpkg.com/tty-browserify/-/tty-browserify-0.0.0.tgz#a157ba402da24e9bf957f9aa69d524eed42901a6" - integrity sha1-oVe6QC2iTpv5V/mqadUk7tQpAaY= - -tunnel-agent@^0.6.0: - version "0.6.0" - resolved "https://registry.yarnpkg.com/tunnel-agent/-/tunnel-agent-0.6.0.tgz#27a5dea06b36b04a0a9966774b290868f0fc40fd" - integrity sha1-J6XeoGs2sEoKmWZ3SykIaPD8QP0= - dependencies: - safe-buffer "^5.0.1" - -tweetnacl@^0.14.3, tweetnacl@~0.14.0: - version "0.14.5" - resolved "https://registry.yarnpkg.com/tweetnacl/-/tweetnacl-0.14.5.tgz#5ae68177f192d4456269d108afa93ff8743f4f64" - integrity sha1-WuaBd/GS1EViadEIr6k/+HQ/T2Q= - -type-check@^0.4.0, type-check@~0.4.0: - version "0.4.0" - resolved "https://registry.yarnpkg.com/type-check/-/type-check-0.4.0.tgz#07b8203bfa7056c0657050e3ccd2c37730bab8f1" - integrity sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew== - dependencies: - prelude-ls "^1.2.1" - -type-check@~0.3.2: - version "0.3.2" - resolved "https://registry.yarnpkg.com/type-check/-/type-check-0.3.2.tgz#5884cab512cf1d355e3fb784f30804b2b520db72" - integrity sha1-WITKtRLPHTVeP7eE8wgEsrUg23I= - dependencies: - prelude-ls "~1.1.2" - -type-detect@4.0.8, type-detect@^4.0.0, type-detect@^4.0.5: - version "4.0.8" - resolved "https://registry.yarnpkg.com/type-detect/-/type-detect-4.0.8.tgz#7646fb5f18871cfbb7749e69bd39a6388eb7450c" - integrity sha512-0fr/mIH1dlO+x7TlcMy+bIDqKPsw/70tVyeHW787goQjhmqaZe10uwLujubK9q9Lg6Fiho1KUKDYz0Z7k7g5/g== - -type-fest@^0.18.0: - version "0.18.1" - resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-0.18.1.tgz#db4bc151a4a2cf4eebf9add5db75508db6cc841f" - integrity sha512-OIAYXk8+ISY+qTOwkHtKqzAuxchoMiD9Udx+FSGQDuiRR+PJKJHc2NJAXlbhkGwTt/4/nKZxELY1w3ReWOL8mw== - -type-fest@^0.20.2: - version "0.20.2" - resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-0.20.2.tgz#1bf207f4b28f91583666cb5fbd327887301cd5f4" - integrity sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ== - -type-fest@^0.21.3: - version "0.21.3" - resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-0.21.3.tgz#d260a24b0198436e133fa26a524a6d65fa3b2e37" - integrity sha512-t0rzBq87m3fVcduHDUFhKmyyX+9eo6WQjZvf51Ea/M0Q7+T374Jp1aUiyUl0GKxp8M/OETVHSDvmkyPgvX+X2w== - -type-fest@^0.6.0: - version "0.6.0" - resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-0.6.0.tgz#8d2a2370d3df886eb5c90ada1c5bf6188acf838b" - integrity sha512-q+MB8nYR1KDLrgr4G5yemftpMC7/QLqVndBmEEdqzmNj5dcFOO4Oo8qlwZE3ULT3+Zim1F8Kq4cBnikNhlCMlg== - -type-fest@^0.8.1: - version "0.8.1" - resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-0.8.1.tgz#09e249ebde851d3b1e48d27c105444667f17b83d" - integrity sha512-4dbzIzqvjtgiM5rw1k5rEHtBANKmdudhGyBEajN01fEyhaAIhsoKNy6y7+IN93IfpFtwY9iqi7kD+xwKhQsNJA== - -type-is@~1.6.17, type-is@~1.6.18: - version "1.6.18" - resolved "https://registry.yarnpkg.com/type-is/-/type-is-1.6.18.tgz#4e552cd05df09467dcbc4ef739de89f2cf37c131" - integrity sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g== - dependencies: - media-typer "0.3.0" - mime-types "~2.1.24" - -type@^1.0.1: - version "1.2.0" - resolved "https://registry.yarnpkg.com/type/-/type-1.2.0.tgz#848dd7698dafa3e54a6c479e759c4bc3f18847a0" - integrity sha512-+5nt5AAniqsCnu2cEQQdpzCAh33kVx8n0VoFidKpB1dVVLAN/F+bgVOqOJqOnEnrhp222clB5p3vUlD+1QAnfg== - -type@^2.5.0: - version "2.5.0" - resolved "https://registry.yarnpkg.com/type/-/type-2.5.0.tgz#0a2e78c2e77907b252abe5f298c1b01c63f0db3d" - integrity sha512-180WMDQaIMm3+7hGXWf12GtdniDEy7nYcyFMKJn/eZz/6tSLXrUN9V0wKSbMjej0I1WHWbpREDEKHtqPQa9NNw== - -typedarray@^0.0.6, typedarray@~0.0.5: - version "0.0.6" - resolved "https://registry.yarnpkg.com/typedarray/-/typedarray-0.0.6.tgz#867ac74e3864187b1d3d47d996a78ec5c8830777" - integrity sha1-hnrHTjhkGHsdPUfZlqeOxciDB3c= - -typescript-compare@^0.0.2: - version "0.0.2" - resolved "https://registry.yarnpkg.com/typescript-compare/-/typescript-compare-0.0.2.tgz#7ee40a400a406c2ea0a7e551efd3309021d5f425" - integrity sha512-8ja4j7pMHkfLJQO2/8tut7ub+J3Lw2S3061eJLFQcvs3tsmJKp8KG5NtpLn7KcY2w08edF74BSVN7qJS0U6oHA== - dependencies: - typescript-logic "^0.0.0" - -typescript-logic@^0.0.0: - version "0.0.0" - resolved "https://registry.yarnpkg.com/typescript-logic/-/typescript-logic-0.0.0.tgz#66ebd82a2548f2b444a43667bec120b496890196" - integrity sha512-zXFars5LUkI3zP492ls0VskH3TtdeHCqu0i7/duGt60i5IGPIpAHE/DWo5FqJ6EjQ15YKXrt+AETjv60Dat34Q== - -typescript-tuple@^2.2.1: - version "2.2.1" - resolved "https://registry.yarnpkg.com/typescript-tuple/-/typescript-tuple-2.2.1.tgz#7d9813fb4b355f69ac55032e0363e8bb0f04dad2" - integrity sha512-Zcr0lbt8z5ZdEzERHAMAniTiIKerFCMgd7yjq1fPnDJ43et/k9twIFQMUYff9k5oXcsQ0WpvFcgzK2ZKASoW6Q== - dependencies: - typescript-compare "^0.0.2" - -ua-parser-js@^0.7.21, ua-parser-js@^0.7.28: - version "0.7.28" - resolved "https://registry.yarnpkg.com/ua-parser-js/-/ua-parser-js-0.7.28.tgz#8ba04e653f35ce210239c64661685bf9121dec31" - integrity sha512-6Gurc1n//gjp9eQNXjD9O3M/sMwVtN5S8Lv9bvOYBfKfDNiIIhqiyi01vMBO45u4zkDE420w/e0se7Vs+sIg+g== - -uglify-js@^2.8.29: - version "2.8.29" - resolved "https://registry.yarnpkg.com/uglify-js/-/uglify-js-2.8.29.tgz#29c5733148057bb4e1f75df35b7a9cb72e6a59dd" - integrity sha1-KcVzMUgFe7Th913zW3qcty5qWd0= - dependencies: - source-map "~0.5.1" - yargs "~3.10.0" - optionalDependencies: - uglify-to-browserify "~1.0.0" - -uglify-js@^3.1.4: - version "3.14.2" - resolved "https://registry.yarnpkg.com/uglify-js/-/uglify-js-3.14.2.tgz#d7dd6a46ca57214f54a2d0a43cad0f35db82ac99" - integrity sha512-rtPMlmcO4agTUfz10CbgJ1k6UAoXM2gWb3GoMPPZB/+/Ackf8lNWk11K4rYi2D0apgoFRLtQOZhb+/iGNJq26A== - -uglify-js@~2.7.3: - version "2.7.5" - resolved "https://registry.yarnpkg.com/uglify-js/-/uglify-js-2.7.5.tgz#4612c0c7baaee2ba7c487de4904ae122079f2ca8" - integrity sha1-RhLAx7qu4rp8SH3kkErhIgefLKg= - dependencies: - async "~0.2.6" - source-map "~0.5.1" - uglify-to-browserify "~1.0.0" - yargs "~3.10.0" - -uglify-to-browserify@~1.0.0: - version "1.0.2" - resolved "https://registry.yarnpkg.com/uglify-to-browserify/-/uglify-to-browserify-1.0.2.tgz#6e0924d6bda6b5afe349e39a6d632850a0f882b7" - integrity sha1-bgkk1r2mta/jSeOabWMoUKD4grc= - -uglifyjs-webpack-plugin@^0.4.6: - version "0.4.6" - resolved "https://registry.yarnpkg.com/uglifyjs-webpack-plugin/-/uglifyjs-webpack-plugin-0.4.6.tgz#b951f4abb6bd617e66f63eb891498e391763e309" - integrity sha1-uVH0q7a9YX5m9j64kUmOORdj4wk= - dependencies: - source-map "^0.5.6" - uglify-js "^2.8.29" - webpack-sources "^1.0.1" - -unbox-primitive@^1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/unbox-primitive/-/unbox-primitive-1.0.1.tgz#085e215625ec3162574dc8859abee78a59b14471" - integrity sha512-tZU/3NqK3dA5gpE1KtyiJUrEB0lxnGkMFHptJ7q6ewdZ8s12QrODwNbhIJStmJkd1QDXa1NRA8aF2A1zk/Ypyw== - dependencies: - function-bind "^1.1.1" - has-bigints "^1.0.1" - has-symbols "^1.0.2" - which-boxed-primitive "^1.0.2" - -unbzip2-stream@1.3.3: - version "1.3.3" - resolved "https://registry.yarnpkg.com/unbzip2-stream/-/unbzip2-stream-1.3.3.tgz#d156d205e670d8d8c393e1c02ebd506422873f6a" - integrity sha512-fUlAF7U9Ah1Q6EieQ4x4zLNejrRvDWUYmxXUpN3uziFYCHapjWFaCAnreY9bGgxzaMCFAPPpYNng57CypwJVhg== - dependencies: - buffer "^5.2.1" - through "^2.3.8" - -unc-path-regex@^0.1.2: - version "0.1.2" - resolved "https://registry.yarnpkg.com/unc-path-regex/-/unc-path-regex-0.1.2.tgz#e73dd3d7b0d7c5ed86fbac6b0ae7d8c6a69d50fa" - integrity sha1-5z3T17DXxe2G+6xrCufYxqadUPo= - -undertaker-registry@^1.0.0: - version "1.0.1" - resolved "https://registry.yarnpkg.com/undertaker-registry/-/undertaker-registry-1.0.1.tgz#5e4bda308e4a8a2ae584f9b9a4359a499825cc50" - integrity sha1-XkvaMI5KiirlhPm5pDWaSZglzFA= - -undertaker@^1.2.1: - version "1.3.0" - resolved "https://registry.yarnpkg.com/undertaker/-/undertaker-1.3.0.tgz#363a6e541f27954d5791d6fa3c1d321666f86d18" - integrity sha512-/RXwi5m/Mu3H6IHQGww3GNt1PNXlbeCuclF2QYR14L/2CHPz3DFZkvB5hZ0N/QUkiXWCACML2jXViIQEQc2MLg== - dependencies: - arr-flatten "^1.0.1" - arr-map "^2.0.0" - bach "^1.0.0" - collection-map "^1.0.0" - es6-weak-map "^2.0.1" - fast-levenshtein "^1.0.0" - last-run "^1.1.0" - object.defaults "^1.0.0" - object.reduce "^1.0.0" - undertaker-registry "^1.0.0" - -unicode-canonical-property-names-ecmascript@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/unicode-canonical-property-names-ecmascript/-/unicode-canonical-property-names-ecmascript-2.0.0.tgz#301acdc525631670d39f6146e0e77ff6bbdebddc" - integrity sha512-yY5PpDlfVIU5+y/BSCxAJRBIS1Zc2dDG3Ujq+sR0U+JjUevW2JhocOF+soROYDSaAezOzOKuyyixhD6mBknSmQ== - -unicode-match-property-ecmascript@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/unicode-match-property-ecmascript/-/unicode-match-property-ecmascript-2.0.0.tgz#54fd16e0ecb167cf04cf1f756bdcc92eba7976c3" - integrity sha512-5kaZCrbp5mmbz5ulBkDkbY0SsPOjKqVS35VpL9ulMPfSl0J0Xsm+9Evphv9CoIZFwre7aJoa94AY6seMKGVN5Q== - dependencies: - unicode-canonical-property-names-ecmascript "^2.0.0" - unicode-property-aliases-ecmascript "^2.0.0" - -unicode-match-property-value-ecmascript@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/unicode-match-property-value-ecmascript/-/unicode-match-property-value-ecmascript-2.0.0.tgz#1a01aa57247c14c568b89775a54938788189a714" - integrity sha512-7Yhkc0Ye+t4PNYzOGKedDhXbYIBe1XEQYQxOPyhcXNMJ0WCABqqj6ckydd6pWRZTHV4GuCPKdBAUiMc60tsKVw== - -unicode-property-aliases-ecmascript@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/unicode-property-aliases-ecmascript/-/unicode-property-aliases-ecmascript-2.0.0.tgz#0a36cb9a585c4f6abd51ad1deddb285c165297c8" - integrity sha512-5Zfuy9q/DFr4tfO7ZPeVXb1aPoeQSdeFMLpYuFebehDAhbuevLs5yxSZmIFN1tP5F9Wl4IpJrYojg85/zgyZHQ== - -unified@^9.1.0: - version "9.2.2" - resolved "https://registry.yarnpkg.com/unified/-/unified-9.2.2.tgz#67649a1abfc3ab85d2969502902775eb03146975" - integrity sha512-Sg7j110mtefBD+qunSLO1lqOEKdrwBFBrR6Qd8f4uwkhWNlbkaqwHse6e7QvD3AP/MNoJdEDLaf8OxYyoWgorQ== - dependencies: - bail "^1.0.0" - extend "^3.0.0" - is-buffer "^2.0.0" - is-plain-obj "^2.0.0" - trough "^1.0.0" - vfile "^4.0.0" - -union-value@^1.0.0: - version "1.0.1" - resolved "https://registry.yarnpkg.com/union-value/-/union-value-1.0.1.tgz#0b6fe7b835aecda61c6ea4d4f02c14221e109847" - integrity sha512-tJfXmxMeWYnczCVs7XAEvIV7ieppALdyepWMkHkwciRpZraG/xwT+s2JN8+pr1+8jCRf80FFzvr+MpQeeoF4Xg== - dependencies: - arr-union "^3.1.0" - get-value "^2.0.6" - is-extendable "^0.1.1" - set-value "^2.0.1" - -unique-stream@^2.0.2: - version "2.3.1" - resolved "https://registry.yarnpkg.com/unique-stream/-/unique-stream-2.3.1.tgz#c65d110e9a4adf9a6c5948b28053d9a8d04cbeac" - integrity sha512-2nY4TnBE70yoxHkDli7DMazpWiP7xMdCYqU2nBRO0UB+ZpEkGsSija7MvmvnZFUeC+mrgiUfcHSr3LmRFIg4+A== - dependencies: - json-stable-stringify-without-jsonify "^1.0.1" - through2-filter "^3.0.0" - -unist-builder@^2.0.0, unist-builder@^2.0.3: - version "2.0.3" - resolved "https://registry.yarnpkg.com/unist-builder/-/unist-builder-2.0.3.tgz#77648711b5d86af0942f334397a33c5e91516436" - integrity sha512-f98yt5pnlMWlzP539tPc4grGMsFaQQlP/vM396b00jngsiINumNmsY8rkXjfoi1c6QaM8nQ3vaGDuoKWbe/1Uw== - -unist-util-generated@^1.0.0: - version "1.1.6" - resolved "https://registry.yarnpkg.com/unist-util-generated/-/unist-util-generated-1.1.6.tgz#5ab51f689e2992a472beb1b35f2ce7ff2f324d4b" - integrity sha512-cln2Mm1/CZzN5ttGK7vkoGw+RZ8VcUH6BtGbq98DDtRGquAAOXig1mrBQYelOwMXYS8rK+vZDyyojSjp7JX+Lg== - -unist-util-is@^4.0.0: - version "4.1.0" - resolved "https://registry.yarnpkg.com/unist-util-is/-/unist-util-is-4.1.0.tgz#976e5f462a7a5de73d94b706bac1b90671b57797" - integrity sha512-ZOQSsnce92GrxSqlnEEseX0gi7GH9zTJZ0p9dtu87WRb/37mMPO2Ilx1s/t9vBHrFhbgweUwb+t7cIn5dxPhZg== - -unist-util-position@^3.0.0: - version "3.1.0" - resolved "https://registry.yarnpkg.com/unist-util-position/-/unist-util-position-3.1.0.tgz#1c42ee6301f8d52f47d14f62bbdb796571fa2d47" - integrity sha512-w+PkwCbYSFw8vpgWD0v7zRCl1FpY3fjDSQ3/N/wNd9Ffa4gPi8+4keqt99N3XW6F99t/mUzp2xAhNmfKWp95QA== - -unist-util-stringify-position@^2.0.0: - version "2.0.3" - resolved "https://registry.yarnpkg.com/unist-util-stringify-position/-/unist-util-stringify-position-2.0.3.tgz#cce3bfa1cdf85ba7375d1d5b17bdc4cada9bd9da" - integrity sha512-3faScn5I+hy9VleOq/qNbAd6pAx7iH5jYBMS9I1HgQVijz/4mv5Bvw5iw1sC/90CODiKo81G/ps8AJrISn687g== - dependencies: - "@types/unist" "^2.0.2" - -unist-util-visit-parents@^3.0.0: - version "3.1.1" - resolved "https://registry.yarnpkg.com/unist-util-visit-parents/-/unist-util-visit-parents-3.1.1.tgz#65a6ce698f78a6b0f56aa0e88f13801886cdaef6" - integrity sha512-1KROIZWo6bcMrZEwiH2UrXDyalAa0uqzWCxCJj6lPOvTve2WkfgCytoDTPaMnodXh1WrXOq0haVYHj99ynJlsg== - dependencies: - "@types/unist" "^2.0.0" - unist-util-is "^4.0.0" - -unist-util-visit@^2.0.0, unist-util-visit@^2.0.3: - version "2.0.3" - resolved "https://registry.yarnpkg.com/unist-util-visit/-/unist-util-visit-2.0.3.tgz#c3703893146df47203bb8a9795af47d7b971208c" - integrity sha512-iJ4/RczbJMkD0712mGktuGpm/U4By4FfDonL7N/9tATGIF4imikjOuagyMY53tnZq3NP6BcmlrHhEKAfGWjh7Q== - dependencies: - "@types/unist" "^2.0.0" - unist-util-is "^4.0.0" - unist-util-visit-parents "^3.0.0" - -universalify@^0.1.0: - version "0.1.2" - resolved "https://registry.yarnpkg.com/universalify/-/universalify-0.1.2.tgz#b646f69be3942dabcecc9d6639c80dc105efaa66" - integrity sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg== - -universalify@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/universalify/-/universalify-2.0.0.tgz#75a4984efedc4b08975c5aeb73f530d02df25717" - integrity sha512-hAZsKq7Yy11Zu1DE0OzWjw7nnLZmJZYTDZZyEFHZdUhV8FkH5MCfoU1XMaxXovpyW5nq5scPqq0ZDP9Zyl04oQ== - -unpipe@1.0.0, unpipe@~1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/unpipe/-/unpipe-1.0.0.tgz#b2bf4ee8514aae6165b4817829d21b2ef49904ec" - integrity sha1-sr9O6FFKrmFltIF4KdIbLvSZBOw= - -unset-value@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/unset-value/-/unset-value-1.0.0.tgz#8376873f7d2335179ffb1e6fc3a8ed0dfc8ab559" - integrity sha1-g3aHP30jNRef+x5vw6jtDfyKtVk= - dependencies: - has-value "^0.3.1" - isobject "^3.0.0" - -unzipper@^0.9.3: - version "0.9.15" - resolved "https://registry.yarnpkg.com/unzipper/-/unzipper-0.9.15.tgz#97d99203dad17698ee39882483c14e4845c7549c" - integrity sha512-2aaUvO4RAeHDvOCuEtth7jrHFaCKTSXPqUkXwADaLBzGbgZGzUDccoEdJ5lW+3RmfpOZYNx0Rw6F6PUzM6caIA== - dependencies: - big-integer "^1.6.17" - binary "~0.3.0" - bluebird "~3.4.1" - buffer-indexof-polyfill "~1.0.0" - duplexer2 "~0.1.4" - fstream "^1.0.12" - listenercount "~1.0.1" - readable-stream "~2.3.6" - setimmediate "~1.0.4" - -upath@^1.1.1: - version "1.2.0" - resolved "https://registry.yarnpkg.com/upath/-/upath-1.2.0.tgz#8f66dbcd55a883acdae4408af8b035a5044c1894" - integrity sha512-aZwGpamFO61g3OlfT7OQCHqhGnW43ieH9WZeP7QxN/G/jS4jfqUkZxoryvJgVPEcrl5NL/ggHsSmLMHuH64Lhg== - -uri-js@^4.2.2: - version "4.4.1" - resolved "https://registry.yarnpkg.com/uri-js/-/uri-js-4.4.1.tgz#9b1a52595225859e55f669d928f88c6c57f2a77e" - integrity sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg== - dependencies: - punycode "^2.1.0" - -urix@^0.1.0: - version "0.1.0" - resolved "https://registry.yarnpkg.com/urix/-/urix-0.1.0.tgz#da937f7a62e21fec1fd18d49b35c2935067a6c72" - integrity sha1-2pN/emLiH+wf0Y1Js1wpNQZ6bHI= - -url-join@^2.0.2: - version "2.0.5" - resolved "https://registry.yarnpkg.com/url-join/-/url-join-2.0.5.tgz#5af22f18c052a000a48d7b82c5e9c2e2feeda728" - integrity sha1-WvIvGMBSoACkjXuCxenC4v7tpyg= - -url-parse@^1.0.5: - version "1.5.3" - resolved "https://registry.yarnpkg.com/url-parse/-/url-parse-1.5.3.tgz#71c1303d38fb6639ade183c2992c8cc0686df862" - integrity sha512-IIORyIQD9rvj0A4CLWsHkBBJuNqWpFQe224b6j9t/ABmquIS0qDU2pY6kl6AuOrL5OkCXHMCFNe1jBcuAggjvQ== - dependencies: - querystringify "^2.1.1" - requires-port "^1.0.0" - -url@^0.11.0: - version "0.11.0" - resolved "https://registry.yarnpkg.com/url/-/url-0.11.0.tgz#3838e97cfc60521eb73c525a8e55bfdd9e2e28f1" - integrity sha1-ODjpfPxgUh63PFJajlW/3Z4uKPE= - dependencies: - punycode "1.3.2" - querystring "0.2.0" - -use@^3.1.0: - version "3.1.1" - resolved "https://registry.yarnpkg.com/use/-/use-3.1.1.tgz#d50c8cac79a19fbc20f2911f56eb973f4e10070f" - integrity sha512-cwESVXlO3url9YWlFW/TA9cshCEhtu7IKJ/p5soJ/gGpj7vbvFrAY/eIioQ6Dw23KjZhYgiIo8HOs1nQ2vr/oQ== - -util-deprecate@^1.0.1, util-deprecate@~1.0.1: - version "1.0.2" - resolved "https://registry.yarnpkg.com/util-deprecate/-/util-deprecate-1.0.2.tgz#450d4dc9fa70de732762fbd2d4a28981419a0ccf" - integrity sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8= - -util@0.10.3: - version "0.10.3" - resolved "https://registry.yarnpkg.com/util/-/util-0.10.3.tgz#7afb1afe50805246489e3db7fe0ed379336ac0f9" - integrity sha1-evsa/lCAUkZInj23/g7TeTNqwPk= - dependencies: - inherits "2.0.1" - -util@^0.10.3: - version "0.10.4" - resolved "https://registry.yarnpkg.com/util/-/util-0.10.4.tgz#3aa0125bfe668a4672de58857d3ace27ecb76901" - integrity sha512-0Pm9hTQ3se5ll1XihRic3FDIku70C+iHUdT/W926rSgHV5QgXsYbKZN8MSC3tJtSkhuROzvsQjAaFENRXr+19A== - dependencies: - inherits "2.0.3" - -util@^0.11.0: - version "0.11.1" - resolved "https://registry.yarnpkg.com/util/-/util-0.11.1.tgz#3236733720ec64bb27f6e26f421aaa2e1b588d61" - integrity sha512-HShAsny+zS2TZfaXxD9tYj4HQGlBezXZMZuM/S5PKLLoZkShZiGk9o5CzukI1LVHZvjdvZ2Sj1aW/Ndn2NB/HQ== - dependencies: - inherits "2.0.3" - -utils-merge@1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/utils-merge/-/utils-merge-1.0.1.tgz#9f95710f50a267947b2ccc124741c1028427e713" - integrity sha1-n5VxD1CiZ5R7LMwSR0HBAoQn5xM= - -uuid@^3.1.0, uuid@^3.3.2: - version "3.4.0" - resolved "https://registry.yarnpkg.com/uuid/-/uuid-3.4.0.tgz#b23e4358afa8a202fe7a100af1f5f883f02007ee" - integrity sha512-HjSDRw6gZE5JMggctHBcjVak08+KEVhSIiDzFnT9S9aegmp85S/bReBVTb4QTFaRNptJ9kuYaNhnbNEOkbKb/A== - -uuid@^8.0.0: - version "8.3.2" - resolved "https://registry.yarnpkg.com/uuid/-/uuid-8.3.2.tgz#80d5b5ced271bb9af6c445f21a1a04c606cefbe2" - integrity sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg== - -v8-compile-cache@^2.0.3: - version "2.3.0" - resolved "https://registry.yarnpkg.com/v8-compile-cache/-/v8-compile-cache-2.3.0.tgz#2de19618c66dc247dcfb6f99338035d8245a2cee" - integrity sha512-l8lCEmLcLYZh4nbunNZvQCJc5pv7+RCwa8q/LdUx8u7lsWvPDKmpodJAJNwkAhJC//dFY48KuIEmjtd4RViDrA== - -v8flags@^3.2.0: - version "3.2.0" - resolved "https://registry.yarnpkg.com/v8flags/-/v8flags-3.2.0.tgz#b243e3b4dfd731fa774e7492128109a0fe66d656" - integrity sha512-mH8etigqMfiGWdeXpaaqGfs6BndypxusHHcv2qSHyZkGEznCd/qAXCWWRzeowtL54147cktFOC4P5y+kl8d8Jg== - dependencies: - homedir-polyfill "^1.0.1" - -validate-npm-package-license@^3.0.1: - version "3.0.4" - resolved "https://registry.yarnpkg.com/validate-npm-package-license/-/validate-npm-package-license-3.0.4.tgz#fc91f6b9c7ba15c857f4cb2c5defeec39d4f410a" - integrity sha512-DpKm2Ui/xN7/HQKCtpZxoRWBhZ9Z0kqtygG8XCgNQ8ZlDnxuQmWhj566j8fN4Cu3/JmbhsDo7fcAJq4s9h27Ew== - dependencies: - spdx-correct "^3.0.0" - spdx-expression-parse "^3.0.0" - -value-or-function@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/value-or-function/-/value-or-function-3.0.0.tgz#1c243a50b595c1be54a754bfece8563b9ff8d813" - integrity sha1-HCQ6ULWVwb5Up1S/7OhWO5/42BM= - -vary@^1, vary@~1.1.2: - version "1.1.2" - resolved "https://registry.yarnpkg.com/vary/-/vary-1.1.2.tgz#2299f02c6ded30d4a5961b0b9f74524a18f634fc" - integrity sha1-IpnwLG3tMNSllhsLn3RSShj2NPw= - -verror@1.10.0: - version "1.10.0" - resolved "https://registry.yarnpkg.com/verror/-/verror-1.10.0.tgz#3a105ca17053af55d6e270c1f8288682e18da400" - integrity sha1-OhBcoXBTr1XW4nDB+CiGguGNpAA= - dependencies: - assert-plus "^1.0.0" - core-util-is "1.0.2" - extsprintf "^1.2.0" - -vfile-message@^2.0.0: - version "2.0.4" - resolved "https://registry.yarnpkg.com/vfile-message/-/vfile-message-2.0.4.tgz#5b43b88171d409eae58477d13f23dd41d52c371a" - integrity sha512-DjssxRGkMvifUOJre00juHoP9DPWuzjxKuMDrhNbk2TdaYYBNMStsNhEOt3idrtI12VQYM/1+iM0KOzXi4pxwQ== - dependencies: - "@types/unist" "^2.0.0" - unist-util-stringify-position "^2.0.0" - -vfile-reporter@^6.0.0: - version "6.0.2" - resolved "https://registry.yarnpkg.com/vfile-reporter/-/vfile-reporter-6.0.2.tgz#cbddaea2eec560f27574ce7b7b269822c191a676" - integrity sha512-GN2bH2gs4eLnw/4jPSgfBjo+XCuvnX9elHICJZjVD4+NM0nsUrMTvdjGY5Sc/XG69XVTgLwj7hknQVc6M9FukA== - dependencies: - repeat-string "^1.5.0" - string-width "^4.0.0" - supports-color "^6.0.0" - unist-util-stringify-position "^2.0.0" - vfile-sort "^2.1.2" - vfile-statistics "^1.1.0" - -vfile-sort@^2.1.0, vfile-sort@^2.1.2: - version "2.2.2" - resolved "https://registry.yarnpkg.com/vfile-sort/-/vfile-sort-2.2.2.tgz#720fe067ce156aba0b411a01bb0dc65596aa1190" - integrity sha512-tAyUqD2R1l/7Rn7ixdGkhXLD3zsg+XLAeUDUhXearjfIcpL1Hcsj5hHpCoy/gvfK/Ws61+e972fm0F7up7hfYA== - -vfile-statistics@^1.1.0: - version "1.1.4" - resolved "https://registry.yarnpkg.com/vfile-statistics/-/vfile-statistics-1.1.4.tgz#b99fd15ecf0f44ba088cc973425d666cb7a9f245" - integrity sha512-lXhElVO0Rq3frgPvFBwahmed3X03vjPF8OcjKMy8+F1xU/3Q3QU3tKEDp743SFtb74PdF0UWpxPvtOP0GCLheA== - -vfile@^4.0.0: - version "4.2.1" - resolved "https://registry.yarnpkg.com/vfile/-/vfile-4.2.1.tgz#03f1dce28fc625c625bc6514350fbdb00fa9e624" - integrity sha512-O6AE4OskCG5S1emQ/4gl8zK586RqA3srz3nfK/Viy0UPToBc5Trp9BVFb1u0CjsKrAWwnpr4ifM/KBXPWwJbCA== - dependencies: - "@types/unist" "^2.0.0" - is-buffer "^2.0.0" - unist-util-stringify-position "^2.0.0" - vfile-message "^2.0.0" - -vinyl-fs@^3.0.0, vinyl-fs@^3.0.2: - version "3.0.3" - resolved "https://registry.yarnpkg.com/vinyl-fs/-/vinyl-fs-3.0.3.tgz#c85849405f67428feabbbd5c5dbdd64f47d31bc7" - integrity sha512-vIu34EkyNyJxmP0jscNzWBSygh7VWhqun6RmqVfXePrOwi9lhvRs//dOaGOTRUQr4tx7/zd26Tk5WeSVZitgng== - dependencies: - fs-mkdirp-stream "^1.0.0" - glob-stream "^6.1.0" - graceful-fs "^4.0.0" - is-valid-glob "^1.0.0" - lazystream "^1.0.0" - lead "^1.0.0" - object.assign "^4.0.4" - pumpify "^1.3.5" - readable-stream "^2.3.3" - remove-bom-buffer "^3.0.0" - remove-bom-stream "^1.2.0" - resolve-options "^1.1.0" - through2 "^2.0.0" - to-through "^2.0.0" - value-or-function "^3.0.0" - vinyl "^2.0.0" - vinyl-sourcemap "^1.1.0" - -vinyl-sourcemap@^1.1.0: - version "1.1.0" - resolved "https://registry.yarnpkg.com/vinyl-sourcemap/-/vinyl-sourcemap-1.1.0.tgz#92a800593a38703a8cdb11d8b300ad4be63b3e16" - integrity sha1-kqgAWTo4cDqM2xHYswCtS+Y7PhY= - dependencies: - append-buffer "^1.0.2" - convert-source-map "^1.5.0" - graceful-fs "^4.1.6" - normalize-path "^2.1.1" - now-and-later "^2.0.0" - remove-bom-buffer "^3.0.0" - vinyl "^2.0.0" - -vinyl-sourcemaps-apply@^0.2.1: - version "0.2.1" - resolved "https://registry.yarnpkg.com/vinyl-sourcemaps-apply/-/vinyl-sourcemaps-apply-0.2.1.tgz#ab6549d61d172c2b1b87be5c508d239c8ef87705" - integrity sha1-q2VJ1h0XLCsbh75cUI0jnI74dwU= - dependencies: - source-map "^0.5.1" - -vinyl@^0.2.1: - version "0.2.3" - resolved "https://registry.yarnpkg.com/vinyl/-/vinyl-0.2.3.tgz#bca938209582ec5a49ad538a00fa1f125e513252" - integrity sha1-vKk4IJWC7FpJrVOKAPofEl5RMlI= - dependencies: - clone-stats "~0.0.1" - -vinyl@^0.5.0: - version "0.5.3" - resolved "https://registry.yarnpkg.com/vinyl/-/vinyl-0.5.3.tgz#b0455b38fc5e0cf30d4325132e461970c2091cde" - integrity sha1-sEVbOPxeDPMNQyUTLkYZcMIJHN4= - dependencies: - clone "^1.0.0" - clone-stats "^0.0.1" - replace-ext "0.0.1" - -vinyl@^1.1.0: - version "1.2.0" - resolved "https://registry.yarnpkg.com/vinyl/-/vinyl-1.2.0.tgz#5c88036cf565e5df05558bfc911f8656df218884" - integrity sha1-XIgDbPVl5d8FVYv8kR+GVt8hiIQ= - dependencies: - clone "^1.0.0" - clone-stats "^0.0.1" - replace-ext "0.0.1" - -vinyl@^2.0.0, vinyl@^2.1.0: - version "2.2.1" - resolved "https://registry.yarnpkg.com/vinyl/-/vinyl-2.2.1.tgz#23cfb8bbab5ece3803aa2c0a1eb28af7cbba1974" - integrity sha512-LII3bXRFBZLlezoG5FfZVcXflZgWP/4dCwKtxd5ky9+LOtM4CS3bIRQsmR1KMnMW07jpE8fqR2lcxPZ+8sJIcw== - dependencies: - clone "^2.1.1" - clone-buffer "^1.0.0" - clone-stats "^1.0.0" - cloneable-readable "^1.0.0" - remove-trailing-separator "^1.0.1" - replace-ext "^1.0.0" - -vm-browserify@0.0.4: - version "0.0.4" - resolved "https://registry.yarnpkg.com/vm-browserify/-/vm-browserify-0.0.4.tgz#5d7ea45bbef9e4a6ff65f95438e0a87c357d5a73" - integrity sha1-XX6kW7755Kb/ZflUOOCofDV9WnM= - dependencies: - indexof "0.0.1" - -vm-browserify@^1.0.1: - version "1.1.2" - resolved "https://registry.yarnpkg.com/vm-browserify/-/vm-browserify-1.1.2.tgz#78641c488b8e6ca91a75f511e7a3b32a86e5dda0" - integrity sha512-2ham8XPWTONajOR0ohOKOHXkm3+gaBmGut3SRuu75xLd/RRaY6vqgh8NBYYk7+RW3u5AtzPQZG8F10LHkl0lAQ== - -void-elements@^2.0.0: - version "2.0.1" - resolved "https://registry.yarnpkg.com/void-elements/-/void-elements-2.0.1.tgz#c066afb582bb1cb4128d60ea92392e94d5e9dbec" - integrity sha1-wGavtYK7HLQSjWDqkjkulNXp2+w= - -vue-template-compiler@^2.6.12: - version "2.6.14" - resolved "https://registry.yarnpkg.com/vue-template-compiler/-/vue-template-compiler-2.6.14.tgz#a2f0e7d985670d42c9c9ee0d044fed7690f4f763" - integrity sha512-ODQS1SyMbjKoO1JBJZojSw6FE4qnh9rIpUZn2EUT86FKizx9uH5z6uXiIrm4/Nb/gwxTi/o17ZDEGWAXHvtC7g== - dependencies: - de-indent "^1.0.2" - he "^1.1.0" - -walk@^2.3.9: - version "2.3.15" - resolved "https://registry.yarnpkg.com/walk/-/walk-2.3.15.tgz#1b4611e959d656426bc521e2da5db3acecae2424" - integrity sha512-4eRTBZljBfIISK1Vnt69Gvr2w/wc3U6Vtrw7qiN5iqYJPH7LElcYh/iU4XWhdCy2dZqv1ToMyYlybDylfG/5Vg== - dependencies: - foreachasync "^3.0.0" - -watchpack-chokidar2@^2.0.1: - version "2.0.1" - resolved "https://registry.yarnpkg.com/watchpack-chokidar2/-/watchpack-chokidar2-2.0.1.tgz#38500072ee6ece66f3769936950ea1771be1c957" - integrity sha512-nCFfBIPKr5Sh61s4LPpy1Wtfi0HE8isJ3d2Yb5/Ppw2P2B/3eVSEBjKfN0fmHJSK14+31KwMKmcrzs2GM4P0Ww== - dependencies: - chokidar "^2.1.8" - -watchpack@^0.2.1: - version "0.2.9" - resolved "https://registry.yarnpkg.com/watchpack/-/watchpack-0.2.9.tgz#62eaa4ab5e5ba35fdfc018275626e3c0f5e3fb0b" - integrity sha1-Yuqkq15bo1/fwBgnVibjwPXj+ws= - dependencies: - async "^0.9.0" - chokidar "^1.0.0" - graceful-fs "^4.1.2" - -watchpack@^1.4.0: - version "1.7.5" - resolved "https://registry.yarnpkg.com/watchpack/-/watchpack-1.7.5.tgz#1267e6c55e0b9b5be44c2023aed5437a2c26c453" - integrity sha512-9P3MWk6SrKjHsGkLT2KHXdQ/9SNkyoJbabxnKOoJepsvJjJG8uYTR3yTPxPQvNDI3w4Nz1xnE0TLHK4RIVe/MQ== - dependencies: - graceful-fs "^4.1.2" - neo-async "^2.5.0" - optionalDependencies: - chokidar "^3.4.1" - watchpack-chokidar2 "^2.0.1" - -wcwidth@>=1.0.1, wcwidth@^1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/wcwidth/-/wcwidth-1.0.1.tgz#f0b0dcf915bc5ff1528afadb2c0e17b532da2fe8" - integrity sha1-8LDc+RW8X/FSivrbLA4XtTLaL+g= - dependencies: - defaults "^1.0.3" - -webdriver@7.13.2: - version "7.13.2" - resolved "https://registry.yarnpkg.com/webdriver/-/webdriver-7.13.2.tgz#47fc49d5f30c7537707875143e08a83231b04eb8" - integrity sha512-NhAs5icJOMltKZHhk3dz3mKArUu4tBe+P6o8kNE5qJYhgXs6lkoBy03zBMh8x9tnbWykVM4Ccw38kA0k2BJ41Q== - dependencies: - "@types/node" "^15.12.5" - "@wdio/config" "7.13.2" - "@wdio/logger" "7.7.0" - "@wdio/protocols" "7.13.2" - "@wdio/types" "7.13.2" - "@wdio/utils" "7.13.2" - got "^11.0.2" - ky "^0.28.5" - lodash.merge "^4.6.1" - -webdriverio@7.13.2, webdriverio@^7.6.1: - version "7.13.2" - resolved "https://registry.yarnpkg.com/webdriverio/-/webdriverio-7.13.2.tgz#a97849c351d8f9fe96f83fe34f2778f211c7f737" - integrity sha512-T6zSlTEqIBLWHaUKv/vOg55OMjjczZ881MrE47p9mfJ3Po+pdTLWPRcx3WwAdnYrJUi30EXdv3QNX7gC+WmgSQ== - dependencies: - "@types/aria-query" "^4.2.1" - "@types/node" "^15.12.5" - "@wdio/config" "7.13.2" - "@wdio/logger" "7.7.0" - "@wdio/protocols" "7.13.2" - "@wdio/repl" "7.13.2" - "@wdio/types" "7.13.2" - "@wdio/utils" "7.13.2" - archiver "^5.0.0" - aria-query "^4.2.2" - atob "^2.1.2" - css-shorthand-properties "^1.1.1" - css-value "^0.0.1" - devtools "7.13.2" - devtools-protocol "^0.0.925217" - fs-extra "^10.0.0" - get-port "^5.1.1" - grapheme-splitter "^1.0.2" - lodash.clonedeep "^4.5.0" - lodash.isobject "^3.0.2" - lodash.isplainobject "^4.0.6" - lodash.zip "^4.2.0" - minimatch "^3.0.4" - puppeteer-core "^10.1.0" - query-selector-shadow-dom "^1.0.0" - resq "^1.9.1" - rgb2hex "0.2.5" - serialize-error "^8.0.0" - webdriver "7.13.2" - -webpack-bundle-analyzer@^3.8.0: - version "3.9.0" - resolved "https://registry.yarnpkg.com/webpack-bundle-analyzer/-/webpack-bundle-analyzer-3.9.0.tgz#f6f94db108fb574e415ad313de41a2707d33ef3c" - integrity sha512-Ob8amZfCm3rMB1ScjQVlbYYUEJyEjdEtQ92jqiFUYt5VkEeO2v5UMbv49P/gnmCZm3A6yaFQzCBvpZqN4MUsdA== - dependencies: - acorn "^7.1.1" - acorn-walk "^7.1.1" - bfj "^6.1.1" - chalk "^2.4.1" - commander "^2.18.0" - ejs "^2.6.1" - express "^4.16.3" - filesize "^3.6.1" - gzip-size "^5.0.0" - lodash "^4.17.19" - mkdirp "^0.5.1" - opener "^1.5.1" - ws "^6.0.0" - -webpack-core@~0.6.9: - version "0.6.9" - resolved "https://registry.yarnpkg.com/webpack-core/-/webpack-core-0.6.9.tgz#fc571588c8558da77be9efb6debdc5a3b172bdc2" - integrity sha1-/FcViMhVjad76e+23r3Fo7FyvcI= - dependencies: - source-list-map "~0.1.7" - source-map "~0.4.1" - -webpack-dev-middleware@^2.0.6: - version "2.0.6" - resolved "https://registry.yarnpkg.com/webpack-dev-middleware/-/webpack-dev-middleware-2.0.6.tgz#a51692801e8310844ef3e3790e1eacfe52326fd4" - integrity sha512-tj5LLD9r4tDuRIDa5Mu9lnY2qBBehAITv6A9irqXhw/HQquZgTx3BCd57zYbU2gMDnncA49ufK2qVQSbaKJwOw== - dependencies: - loud-rejection "^1.6.0" - memory-fs "~0.4.1" - mime "^2.1.0" - path-is-absolute "^1.0.0" - range-parser "^1.0.3" - url-join "^2.0.2" - webpack-log "^1.0.1" - -webpack-log@^1.0.1: - version "1.2.0" - resolved "https://registry.yarnpkg.com/webpack-log/-/webpack-log-1.2.0.tgz#a4b34cda6b22b518dbb0ab32e567962d5c72a43d" - integrity sha512-U9AnICnu50HXtiqiDxuli5gLB5PGBo7VvcHx36jRZHwK4vzOYLbImqT4lwWwoMHdQWwEKw736fCHEekokTEKHA== - dependencies: - chalk "^2.1.0" - log-symbols "^2.1.0" - loglevelnext "^1.0.1" - uuid "^3.1.0" - -webpack-sources@^1.0.1: - version "1.4.3" - resolved "https://registry.yarnpkg.com/webpack-sources/-/webpack-sources-1.4.3.tgz#eedd8ec0b928fbf1cbfe994e22d2d890f330a933" - integrity sha512-lgTS3Xhv1lCOKo7SA5TjKXMjpSM4sBjNV5+q2bqesbSPs5FjGmU6jjtBSkX9b4qW87vDIsCIlUPOEhbZrMdjeQ== - dependencies: - source-list-map "^2.0.0" - source-map "~0.6.1" - -webpack-stream@^3.2.0: - version "3.2.0" - resolved "https://registry.yarnpkg.com/webpack-stream/-/webpack-stream-3.2.0.tgz#3a1d160fb11d41727b7ce6f32f722464f98b2186" - integrity sha1-Oh0WD7EdQXJ7fObzL3IkZPmLIYY= - dependencies: - gulp-util "^3.0.7" - lodash.clone "^4.3.2" - lodash.some "^4.2.2" - memory-fs "^0.3.0" - through "^2.3.8" - vinyl "^1.1.0" - webpack "^1.12.9" - -webpack@^1.12.9: - version "1.15.0" - resolved "https://registry.yarnpkg.com/webpack/-/webpack-1.15.0.tgz#4ff31f53db03339e55164a9d468ee0324968fe98" - integrity sha1-T/MfU9sDM55VFkqdRo7gMklo/pg= - dependencies: - acorn "^3.0.0" - async "^1.3.0" - clone "^1.0.2" - enhanced-resolve "~0.9.0" - interpret "^0.6.4" - loader-utils "^0.2.11" - memory-fs "~0.3.0" - mkdirp "~0.5.0" - node-libs-browser "^0.7.0" - optimist "~0.6.0" - supports-color "^3.1.0" - tapable "~0.1.8" - uglify-js "~2.7.3" - watchpack "^0.2.1" - webpack-core "~0.6.9" - -webpack@^3.0.0: - version "3.12.0" - resolved "https://registry.yarnpkg.com/webpack/-/webpack-3.12.0.tgz#3f9e34360370602fcf639e97939db486f4ec0d74" - integrity sha512-Sw7MdIIOv/nkzPzee4o0EdvCuPmxT98+vVpIvwtcwcF1Q4SDSNp92vwcKc4REe7NItH9f1S4ra9FuQ7yuYZ8bQ== - dependencies: - acorn "^5.0.0" - acorn-dynamic-import "^2.0.0" - ajv "^6.1.0" - ajv-keywords "^3.1.0" - async "^2.1.2" - enhanced-resolve "^3.4.0" - escope "^3.6.0" - interpret "^1.0.0" - json-loader "^0.5.4" - json5 "^0.5.1" - loader-runner "^2.3.0" - loader-utils "^1.1.0" - memory-fs "~0.4.1" - mkdirp "~0.5.0" - node-libs-browser "^2.0.0" - source-map "^0.5.3" - supports-color "^4.2.1" - tapable "^0.2.7" - uglifyjs-webpack-plugin "^0.4.6" - watchpack "^1.4.0" - webpack-sources "^1.0.1" - yargs "^8.0.2" - -websocket-driver@>=0.5.1: - version "0.7.4" - resolved "https://registry.yarnpkg.com/websocket-driver/-/websocket-driver-0.7.4.tgz#89ad5295bbf64b480abcba31e4953aca706f5760" - integrity sha512-b17KeDIQVjvb0ssuSDF2cYXSg2iztliJ4B9WdsuB6J952qCPKmnVq4DyW5motImXHDC1cBT/1UezrJVsKw5zjg== - dependencies: - http-parser-js ">=0.5.1" - safe-buffer ">=5.1.0" - websocket-extensions ">=0.1.1" - -websocket-extensions@>=0.1.1: - version "0.1.4" - resolved "https://registry.yarnpkg.com/websocket-extensions/-/websocket-extensions-0.1.4.tgz#7f8473bc839dfd87608adb95d7eb075211578a42" - integrity sha512-OqedPIGOfsDlo31UNwYbCFMSaO9m9G/0faIHj5/dZFDMFqPTcx6UwqyOy3COEaEOg/9VsGIpdqn62W5KhoKSpg== - -which-boxed-primitive@^1.0.1, which-boxed-primitive@^1.0.2: - version "1.0.2" - resolved "https://registry.yarnpkg.com/which-boxed-primitive/-/which-boxed-primitive-1.0.2.tgz#13757bc89b209b049fe5d86430e21cf40a89a8e6" - integrity sha512-bwZdv0AKLpplFY2KZRX6TvyuN7ojjr7lwkg6ml0roIy9YeuSr7JS372qlNW18UQYzgYK9ziGcerWqZOmEn9VNg== - dependencies: - is-bigint "^1.0.1" - is-boolean-object "^1.1.0" - is-number-object "^1.0.4" - is-string "^1.0.5" - is-symbol "^1.0.3" - -which-collection@^1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/which-collection/-/which-collection-1.0.1.tgz#70eab71ebbbd2aefaf32f917082fc62cdcb70906" - integrity sha512-W8xeTUwaln8i3K/cY1nGXzdnVZlidBcagyNFtBdD5kxnb4TvGKR7FfSIS3mYpwWS1QUCutfKz8IY8RjftB0+1A== - dependencies: - is-map "^2.0.1" - is-set "^2.0.1" - is-weakmap "^2.0.1" - is-weakset "^2.0.1" - -which-module@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/which-module/-/which-module-1.0.0.tgz#bba63ca861948994ff307736089e3b96026c2a4f" - integrity sha1-u6Y8qGGUiZT/MHc2CJ47lgJsKk8= - -which-module@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/which-module/-/which-module-2.0.0.tgz#d9ef07dce77b9902b8a3a8fa4b31c3e3f7e6e87a" - integrity sha1-2e8H3Od7mQK4o6j6SzHD4/fm6Ho= - -which-typed-array@^1.1.2: - version "1.1.7" - resolved "https://registry.yarnpkg.com/which-typed-array/-/which-typed-array-1.1.7.tgz#2761799b9a22d4b8660b3c1b40abaa7739691793" - integrity sha512-vjxaB4nfDqwKI0ws7wZpxIlde1XrLX5uB0ZjpfshgmapJMD7jJWhZI+yToJTqaFByF0eNBcYxbjmCzoRP7CfEw== - dependencies: - available-typed-arrays "^1.0.5" - call-bind "^1.0.2" - es-abstract "^1.18.5" - foreach "^2.0.5" - has-tostringtag "^1.0.0" - is-typed-array "^1.1.7" - -which@2.0.2, which@^2.0.1, which@^2.0.2: - version "2.0.2" - resolved "https://registry.yarnpkg.com/which/-/which-2.0.2.tgz#7c6a8dd0a636a0327e10b59c9286eee93f3f51b1" - integrity sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA== - dependencies: - isexe "^2.0.0" - -which@^1.1.1, which@^1.2.1, which@^1.2.14, which@^1.2.9: - version "1.3.1" - resolved "https://registry.yarnpkg.com/which/-/which-1.3.1.tgz#a45043d54f5805316da8d62f9f50918d3da70b0a" - integrity sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ== - dependencies: - isexe "^2.0.0" - -window-size@0.1.0: - version "0.1.0" - resolved "https://registry.yarnpkg.com/window-size/-/window-size-0.1.0.tgz#5438cd2ea93b202efa3a19fe8887aee7c94f9c9d" - integrity sha1-VDjNLqk7IC76Ohn+iIeu58lPnJ0= - -word-wrap@^1.2.3, word-wrap@~1.2.3: - version "1.2.3" - resolved "https://registry.yarnpkg.com/word-wrap/-/word-wrap-1.2.3.tgz#610636f6b1f703891bd34771ccb17fb93b47079c" - integrity sha512-Hz/mrNwitNRh/HUAtM/VT/5VH+ygD6DV7mYKZAtHOrbs8U7lvPS6xf7EJKMF0uW1KJCl0H701g3ZGus+muE5vQ== - -wordwrap@0.0.2: - version "0.0.2" - resolved "https://registry.yarnpkg.com/wordwrap/-/wordwrap-0.0.2.tgz#b79669bb42ecb409f83d583cad52ca17eaa1643f" - integrity sha1-t5Zpu0LstAn4PVg8rVLKF+qhZD8= - -wordwrap@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/wordwrap/-/wordwrap-1.0.0.tgz#27584810891456a4171c8d0226441ade90cbcaeb" - integrity sha1-J1hIEIkUVqQXHI0CJkQa3pDLyus= - -wordwrap@~0.0.2: - version "0.0.3" - resolved "https://registry.yarnpkg.com/wordwrap/-/wordwrap-0.0.3.tgz#a3d5da6cd5c0bc0008d37234bbaf1bed63059107" - integrity sha1-o9XabNXAvAAI03I0u68b7WMFkQc= - -workerpool@6.1.5: - version "6.1.5" - resolved "https://registry.yarnpkg.com/workerpool/-/workerpool-6.1.5.tgz#0f7cf076b6215fd7e1da903ff6f22ddd1886b581" - integrity sha512-XdKkCK0Zqc6w3iTxLckiuJ81tiD/o5rBE/m+nXpRCB+/Sq4DqkfXZ/x0jW02DG1tGsfUGXbTJyZDP+eu67haSw== - -wrap-ansi@^2.0.0: - version "2.1.0" - resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-2.1.0.tgz#d8fc3d284dd05794fe84973caecdd1cf824fdd85" - integrity sha1-2Pw9KE3QV5T+hJc8rs3Rz4JP3YU= - dependencies: - string-width "^1.0.1" - strip-ansi "^3.0.1" - -wrap-ansi@^6.2.0: - version "6.2.0" - resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-6.2.0.tgz#e9393ba07102e6c91a3b221478f0257cd2856e53" - integrity sha512-r6lPcBGxZXlIcymEu7InxDMhdW0KDxpLgoFLcguasxCaJ/SOIZwINatK9KY/tf+ZrlywOKU0UDj3ATXUBfxJXA== - dependencies: - ansi-styles "^4.0.0" - string-width "^4.1.0" - strip-ansi "^6.0.0" - -wrap-ansi@^7.0.0: - version "7.0.0" - resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-7.0.0.tgz#67e145cff510a6a6984bdf1152911d69d2eb9e43" - integrity sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q== - dependencies: - ansi-styles "^4.0.0" - string-width "^4.1.0" - strip-ansi "^6.0.0" - -wrappy@1: - version "1.0.2" - resolved "https://registry.yarnpkg.com/wrappy/-/wrappy-1.0.2.tgz#b5243d8f3ec1aa35f1364605bc0d1036e30ab69f" - integrity sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8= - -write@^0.2.1: - version "0.2.1" - resolved "https://registry.yarnpkg.com/write/-/write-0.2.1.tgz#5fc03828e264cea3fe91455476f7a3c566cb0757" - integrity sha1-X8A4KOJkzqP+kUVUdvejxWbLB1c= - dependencies: - mkdirp "^0.5.1" - -ws@7.4.6, ws@~7.4.2: - version "7.4.6" - resolved "https://registry.yarnpkg.com/ws/-/ws-7.4.6.tgz#5654ca8ecdeee47c33a9a4bf6d28e2be2980377c" - integrity sha512-YmhHDO4MzaDLB+M9ym/mDA5z0naX8j7SIlT8f8z+I0VtzsRbekxEutHSme7NPS2qE8StCYQNUnfWdXta/Yu85A== - -ws@^6.0.0: - version "6.2.2" - resolved "https://registry.yarnpkg.com/ws/-/ws-6.2.2.tgz#dd5cdbd57a9979916097652d78f1cc5faea0c32e" - integrity sha512-zmhltoSR8u1cnDsD43TX59mzoMZsLKqUweyYBAIvTngR3shc0W6aOZylZmq/7hqyVxPdi+5Ud2QInblgyE72fw== - dependencies: - async-limiter "~1.0.0" - -"xtend@>=4.0.0 <4.1.0-0", xtend@^4.0.0, xtend@^4.0.2, xtend@~4.0.0, xtend@~4.0.1: - version "4.0.2" - resolved "https://registry.yarnpkg.com/xtend/-/xtend-4.0.2.tgz#bb72779f5fa465186b1f438f674fa347fdb5db54" - integrity sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ== - -xtend@~2.1.1: - version "2.1.2" - resolved "https://registry.yarnpkg.com/xtend/-/xtend-2.1.2.tgz#6efecc2a4dad8e6962c4901b337ce7ba87b5d28b" - integrity sha1-bv7MKk2tjmlixJAbM3znuoe10os= - dependencies: - object-keys "~0.4.0" - -xtend@~3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/xtend/-/xtend-3.0.0.tgz#5cce7407baf642cba7becda568111c493f59665a" - integrity sha1-XM50B7r2Qsunvs2laBEcST9ZZlo= - -y18n@^3.2.1: - version "3.2.2" - resolved "https://registry.yarnpkg.com/y18n/-/y18n-3.2.2.tgz#85c901bd6470ce71fc4bb723ad209b70f7f28696" - integrity sha512-uGZHXkHnhF0XeeAPgnKfPv1bgKAYyVvmNL1xlKsPYZPaIHxGti2hHqvOCQv71XMsLxu1QjergkqogUnms5D3YQ== - -y18n@^4.0.0: - version "4.0.3" - resolved "https://registry.yarnpkg.com/y18n/-/y18n-4.0.3.tgz#b5f259c82cd6e336921efd7bfd8bf560de9eeedf" - integrity sha512-JKhqTOwSrqNA1NY5lSztJ1GrBiUodLMmIZuLiDaMRJ+itFd+ABVE8XBjOvIWL+rSqNDC74LCSFmlb/U4UZ4hJQ== - -y18n@^5.0.5: - version "5.0.8" - resolved "https://registry.yarnpkg.com/y18n/-/y18n-5.0.8.tgz#7f4934d0f7ca8c56f95314939ddcd2dd91ce1d55" - integrity sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA== - -yallist@^2.1.2: - version "2.1.2" - resolved "https://registry.yarnpkg.com/yallist/-/yallist-2.1.2.tgz#1c11f9218f076089a47dd512f93c6699a6a81d52" - integrity sha1-HBH5IY8HYImkfdUS+TxmmaaoHVI= - -yallist@^4.0.0: - version "4.0.0" - resolved "https://registry.yarnpkg.com/yallist/-/yallist-4.0.0.tgz#9bb92790d9c0effec63be73519e11a35019a3a72" - integrity sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A== - -yargs-parser@20.2.4: - version "20.2.4" - resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-20.2.4.tgz#b42890f14566796f85ae8e3a25290d205f154a54" - integrity sha512-WOkpgNhPTlE73h4VFAFsOnomJVaovO8VqLDzy5saChRBFQFBoMYirowyW+Q9HB4HFF4Z7VZTiG3iSzJJA29yRA== - -yargs-parser@>=5.0.0-security.0, yargs-parser@^20.2.2, yargs-parser@^20.2.3: - version "20.2.9" - resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-20.2.9.tgz#2eb7dc3b0289718fc295f362753845c41a0c94ee" - integrity sha512-y11nGElTIV+CT3Zv9t7VKl+Q3hTQoT9a1Qzezhhl6Rp21gJ/IVTW7Z3y9EWXhuUBC2Shnf+DX0antecpAwSP8w== - -yargs-parser@^18.1.2: - version "18.1.3" - resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-18.1.3.tgz#be68c4975c6b2abf469236b0c870362fab09a7b0" - integrity sha512-o50j0JeToy/4K6OZcaQmW6lyXXKhq7csREXcDwk2omFPJEwUNOVtJKvmDr9EI1fAJZUyZcRF7kxGBWmRXudrCQ== - dependencies: - camelcase "^5.0.0" - decamelize "^1.2.0" - -yargs-parser@^5.0.1: - version "5.0.1" - resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-5.0.1.tgz#7ede329c1d8cdbbe209bd25cdb990e9b1ebbb394" - integrity sha512-wpav5XYiddjXxirPoCTUPbqM0PXvJ9hiBMvuJgInvo4/lAOTZzUprArw17q2O1P2+GHhbBr18/iQwjL5Z9BqfA== - dependencies: - camelcase "^3.0.0" - object.assign "^4.1.0" - -yargs-parser@^7.0.0: - version "7.0.0" - resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-7.0.0.tgz#8d0ac42f16ea55debd332caf4c4038b3e3f5dfd9" - integrity sha1-jQrELxbqVd69MyyvTEA4s+P139k= - dependencies: - camelcase "^4.1.0" - -yargs-unparser@2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/yargs-unparser/-/yargs-unparser-2.0.0.tgz#f131f9226911ae5d9ad38c432fe809366c2325eb" - integrity sha512-7pRTIA9Qc1caZ0bZ6RYRGbHJthJWuakf+WmHK0rVeLkNrrGhfoabBNdue6kdINI6r4if7ocq9aD/n7xwKOdzOA== - dependencies: - camelcase "^6.0.0" - decamelize "^4.0.0" - flat "^5.0.2" - is-plain-obj "^2.1.0" - -yargs@16.2.0, yargs@^16.0.0, yargs@^16.1.1, yargs@^16.2.0: - version "16.2.0" - resolved "https://registry.yarnpkg.com/yargs/-/yargs-16.2.0.tgz#1c82bf0f6b6a66eafce7ef30e376f49a12477f66" - integrity sha512-D1mvvtDG0L5ft/jGWkLpG1+m0eQxOfaBvTNELraWj22wSVUMWxZUvYgJYcKh6jGGIkJFhH4IZPQhR4TKpc8mBw== - dependencies: - cliui "^7.0.2" - escalade "^3.1.1" - get-caller-file "^2.0.5" - require-directory "^2.1.1" - string-width "^4.2.0" - y18n "^5.0.5" - yargs-parser "^20.2.2" - -yargs@^1.3.1: - version "1.3.3" - resolved "https://registry.yarnpkg.com/yargs/-/yargs-1.3.3.tgz#054de8b61f22eefdb7207059eaef9d6b83fb931a" - integrity sha1-BU3oth8i7v23IHBZ6u+da4P7kxo= - -yargs@^15.3.1: - version "15.4.1" - resolved "https://registry.yarnpkg.com/yargs/-/yargs-15.4.1.tgz#0d87a16de01aee9d8bec2bfbf74f67851730f4f8" - integrity sha512-aePbxDmcYW++PaqBsJ+HYUFwCdv4LVvdnhBy78E57PIor8/OVvhMrADFFEDh8DHDFRv/O9i3lPhsENjO7QX0+A== - dependencies: - cliui "^6.0.0" - decamelize "^1.2.0" - find-up "^4.1.0" - get-caller-file "^2.0.1" - require-directory "^2.1.1" - require-main-filename "^2.0.0" - set-blocking "^2.0.0" - string-width "^4.2.0" - which-module "^2.0.0" - y18n "^4.0.0" - yargs-parser "^18.1.2" - -yargs@^17.0.0: - version "17.2.1" - resolved "https://registry.yarnpkg.com/yargs/-/yargs-17.2.1.tgz#e2c95b9796a0e1f7f3bf4427863b42e0418191ea" - integrity sha512-XfR8du6ua4K6uLGm5S6fA+FIJom/MdJcFNVY8geLlp2v8GYbOXD4EB1tPNZsRn4vBzKGMgb5DRZMeWuFc2GO8Q== - dependencies: - cliui "^7.0.2" - escalade "^3.1.1" - get-caller-file "^2.0.5" - require-directory "^2.1.1" - string-width "^4.2.0" - y18n "^5.0.5" - yargs-parser "^20.2.2" - -yargs@^7.1.0: - version "7.1.2" - resolved "https://registry.yarnpkg.com/yargs/-/yargs-7.1.2.tgz#63a0a5d42143879fdbb30370741374e0641d55db" - integrity sha512-ZEjj/dQYQy0Zx0lgLMLR8QuaqTihnxirir7EwUHp1Axq4e3+k8jXU5K0VLbNvedv1f4EWtBonDIZm0NUr+jCcA== - dependencies: - camelcase "^3.0.0" - cliui "^3.2.0" - decamelize "^1.1.1" - get-caller-file "^1.0.1" - os-locale "^1.4.0" - read-pkg-up "^1.0.1" - require-directory "^2.1.1" - require-main-filename "^1.0.1" - set-blocking "^2.0.0" - string-width "^1.0.2" - which-module "^1.0.0" - y18n "^3.2.1" - yargs-parser "^5.0.1" - -yargs@^8.0.2: - version "8.0.2" - resolved "https://registry.yarnpkg.com/yargs/-/yargs-8.0.2.tgz#6299a9055b1cefc969ff7e79c1d918dceb22c360" - integrity sha1-YpmpBVsc78lp/355wdkY3Osiw2A= - dependencies: - camelcase "^4.1.0" - cliui "^3.2.0" - decamelize "^1.1.1" - get-caller-file "^1.0.1" - os-locale "^2.0.0" - read-pkg-up "^2.0.0" - require-directory "^2.1.1" - require-main-filename "^1.0.1" - set-blocking "^2.0.0" - string-width "^2.0.0" - which-module "^2.0.0" - y18n "^3.2.1" - yargs-parser "^7.0.0" - -yargs@~3.10.0: - version "3.10.0" - resolved "https://registry.yarnpkg.com/yargs/-/yargs-3.10.0.tgz#f7ee7bd857dd7c1d2d38c0e74efbd681d1431fd1" - integrity sha1-9+572FfdfB0tOMDnTvvWgdFDH9E= - dependencies: - camelcase "^1.0.2" - cliui "^2.1.0" - decamelize "^1.0.0" - window-size "0.1.0" - -yarn-install@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/yarn-install/-/yarn-install-1.0.0.tgz#57f45050b82efd57182b3973c54aa05cb5d25230" - integrity sha1-V/RQULgu/VcYKzlzxUqgXLXSUjA= - dependencies: - cac "^3.0.3" - chalk "^1.1.3" - cross-spawn "^4.0.2" - -yauzl@^2.10.0: - version "2.10.0" - resolved "https://registry.yarnpkg.com/yauzl/-/yauzl-2.10.0.tgz#c7eb17c93e112cb1086fa6d8e51fb0667b79a5f9" - integrity sha1-x+sXyT4RLLEIb6bY5R+wZnt5pfk= - dependencies: - buffer-crc32 "~0.2.3" - fd-slicer "~1.1.0" - -yocto-queue@^0.1.0: - version "0.1.0" - resolved "https://registry.yarnpkg.com/yocto-queue/-/yocto-queue-0.1.0.tgz#0294eb3dee05028d31ee1a5fa2c556a6aaf10a1b" - integrity sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q== - -zip-stream@^4.1.0: - version "4.1.0" - resolved "https://registry.yarnpkg.com/zip-stream/-/zip-stream-4.1.0.tgz#51dd326571544e36aa3f756430b313576dc8fc79" - integrity sha512-zshzwQW7gG7hjpBlgeQP9RuyPGNxvJdzR8SUM3QhxCnLjWN2E7j3dOvpeDcQoETfHx0urRS7EtmVToql7YpU4A== - dependencies: - archiver-utils "^2.1.0" - compress-commons "^4.1.0" - readable-stream "^3.6.0" - -zwitch@^1.0.0: - version "1.0.5" - resolved "https://registry.yarnpkg.com/zwitch/-/zwitch-1.0.5.tgz#d11d7381ffed16b742f6af7b3f223d5cd9fe9920" - integrity sha512-V50KMwwzqJV0NpZIZFwfOD5/lyny3WlSzRiXgA0G7VUnRlqttta1L6UQIHzd6EuBY/cHGfwTIck7w1yH6Q5zUw==