diff --git a/modules/adpod.js b/modules/adpod.js
new file mode 100644
index 00000000000..87e5869e17b
--- /dev/null
+++ b/modules/adpod.js
@@ -0,0 +1,414 @@
+/**
+ * This module houses the functionality to evaluate and process adpod adunits/bids. Specifically there are several hooked functions,
+ * that either supplement the base function (ie to check something additional or unique to adpod objects) or to replace the base funtion
+ * entirely when appropriate.
+ *
+ * Brief outline of each hook:
+ * - `callPrebidCacheHook` - for any adpod bids, this function will temporarily hold them in a queue in order to send the bids to Prebid Cache in bulk
+ * - `checkAdUnitSetupHook` - evaluates the adUnits to ensure that required fields for adpod adUnits are present. Invalid adpod adUntis are removed from the array.
+ * - `checkVideoBidSetupHook` - evaluates the adpod bid returned from an adaptor/bidder to ensure required fields are populated; also initializes duration bucket field.
+ *
+ * To initialize the module, there is an `initAdpodHooks()` function that should be imported and executed by a corresponding `...AdServerVideo`
+ * module that designed to support adpod video type ads. This import process allows this module to effectively act as a sub-module.
+ */
+
+import * as utils from '../src/utils';
+import { addBidToAuction, doCallbacksIfTimedout, AUCTION_IN_PROGRESS, callPrebidCache } from '../src/auction';
+import { checkAdUnitSetup } from '../src/prebid';
+import { checkVideoBidSetup } from '../src/video';
+import { setupBeforeHookFnOnce } from '../src/hook';
+import { store } from '../src/videoCache';
+import { config } from '../src/config';
+import { ADPOD } from '../src/mediaTypes';
+import Set from 'core-js/library/fn/set';
+import find from 'core-js/library/fn/array/find';
+const from = require('core-js/library/fn/array/from');
+
+export const TARGETING_KEY_PB_CAT_DUR = 'hb_pb_cat_dur';
+export const TARGETING_KEY_CACHE_ID = 'hb_cache_id'
+
+let queueTimeDelay = 50;
+let queueSizeLimit = 5;
+let bidCacheRegistry = createBidCacheRegistry();
+
+/**
+ * Create a registry object that stores/manages bids while be held in queue for Prebid Cache.
+ * @returns registry object with defined accessor functions
+ */
+function createBidCacheRegistry() {
+ let registry = {};
+
+ function setupRegistrySlot(auctionId) {
+ registry[auctionId] = {};
+ registry[auctionId].bidStorage = new Set();
+ registry[auctionId].queueDispatcher = createDispatcher(queueTimeDelay);
+ registry[auctionId].initialCacheKey = utils.generateUUID();
+ }
+
+ return {
+ addBid: function (bid) {
+ // create parent level object based on auction ID (in case there are concurrent auctions running) to store objects for that auction
+ if (!registry[bid.auctionId]) {
+ setupRegistrySlot(bid.auctionId);
+ }
+ registry[bid.auctionId].bidStorage.add(bid);
+ },
+ removeBid: function (bid) {
+ registry[bid.auctionId].bidStorage.delete(bid);
+ },
+ getBids: function (bid) {
+ return registry[bid.auctionId] && registry[bid.auctionId].bidStorage.values();
+ },
+ getQueueDispatcher: function(bid) {
+ return registry[bid.auctionId] && registry[bid.auctionId].queueDispatcher;
+ },
+ setupInitialCacheKey: function(bid) {
+ if (!registry[bid.auctionId]) {
+ registry[bid.auctionId] = {};
+ registry[bid.auctionId].initialCacheKey = utils.generateUUID();
+ }
+ },
+ getInitialCacheKey: function(bid) {
+ return registry[bid.auctionId] && registry[bid.auctionId].initialCacheKey;
+ }
+ }
+}
+
+/**
+ * Creates a function that when called updates the bid queue and extends the running timer (when called subsequently).
+ * Once the time threshold for the queue (defined by queueSizeLimit) is reached, the queue will be flushed by calling the `firePrebidCacheCall` function.
+ * If there is a long enough time between calls (based on timeoutDration), the queue will automatically flush itself.
+ * @param {Number} timeoutDuration number of milliseconds to pass before timer expires and current bid queue is flushed
+ * @returns {Function}
+ */
+function createDispatcher(timeoutDuration) {
+ let timeout;
+ let counter = 1;
+
+ return function(auctionInstance, bidListArr, afterBidAdded, killQueue) {
+ const context = this;
+
+ var callbackFn = function() {
+ firePrebidCacheCall.call(context, auctionInstance, bidListArr, afterBidAdded);
+ };
+
+ clearTimeout(timeout);
+
+ if (!killQueue) {
+ // want to fire off the queue if either: size limit is reached or time has passed since last call to dispatcher
+ if (counter === queueSizeLimit) {
+ counter = 1;
+ callbackFn();
+ } else {
+ counter++;
+ timeout = setTimeout(callbackFn, timeoutDuration);
+ }
+ } else {
+ counter = 1;
+ }
+ };
+}
+
+/**
+ * This function reads certain fields from the bid to generate a specific key used for caching the bid in Prebid Cache
+ * @param {Object} bid bid object to update
+ * @param {Boolean} brandCategoryExclusion value read from setConfig; influences whether category is required or not
+ */
+function attachPriceIndustryDurationKeyToBid(bid, brandCategoryExclusion) {
+ let initialCacheKey = bidCacheRegistry.getInitialCacheKey(bid);
+ let duration = utils.deepAccess(bid, 'video.durationBucket');
+ let cpmFixed = bid.cpm.toFixed(2);
+ let pcd;
+
+ if (brandCategoryExclusion) {
+ let category = utils.deepAccess(bid, 'meta.adServerCatId');
+ pcd = `${cpmFixed}_${category}_${duration}s`;
+ } else {
+ pcd = `${cpmFixed}_${duration}s`;
+ }
+
+ if (!bid.adserverTargeting) {
+ bid.adserverTargeting = {};
+ }
+ bid.adserverTargeting[TARGETING_KEY_PB_CAT_DUR] = pcd;
+ bid.adserverTargeting[TARGETING_KEY_CACHE_ID] = initialCacheKey;
+ bid.customCacheKey = `${pcd}_${initialCacheKey}`;
+}
+
+/**
+ * Updates the running queue for the associated auction.
+ * Does a check to ensure the auction is still running; if it's not - the previously running queue is killed.
+ * @param {*} auctionInstance running context of the auction
+ * @param {Object} bidResponse bid object being added to queue
+ * @param {Function} afterBidAdded callback function used when Prebid Cache responds
+ */
+function updateBidQueue(auctionInstance, bidResponse, afterBidAdded) {
+ let bidListIter = bidCacheRegistry.getBids(bidResponse);
+
+ if (bidListIter) {
+ let bidListArr = from(bidListIter);
+ let callDispatcher = bidCacheRegistry.getQueueDispatcher(bidResponse);
+ let killQueue = !!(auctionInstance.getAuctionStatus() !== AUCTION_IN_PROGRESS);
+ callDispatcher(auctionInstance, bidListArr, afterBidAdded, killQueue);
+ } else {
+ utils.logWarn('Attempted to cache a bid from an unknown auction. Bid:', bidResponse);
+ }
+}
+
+/**
+ * Small helper function to remove bids from internal storage; normally b/c they're about to sent to Prebid Cache for processing.
+ * @param {Array[Object]} bidResponses list of bids to remove
+ */
+function removeBidsFromStorage(bidResponses) {
+ for (let i = 0; i < bidResponses.length; i++) {
+ bidCacheRegistry.removeBid(bidResponses[i]);
+ }
+}
+
+/**
+ * This function will send a list of bids to Prebid Cache. It also removes the same bids from the internal bidCacheRegistry
+ * to maintain which bids are in queue.
+ * If the bids are successfully cached, they will be added to the respective auction.
+ * @param {*} auctionInstance running context of the auction
+ * @param {Array[Object]} bidList list of bid objects that need to be sent to Prebid Cache
+ * @param {Function} afterBidAdded callback function used when Prebid Cache responds
+ */
+function firePrebidCacheCall(auctionInstance, bidList, afterBidAdded) {
+ // remove entries now so other incoming bids won't accidentally have a stale version of the list while PBC is processing the current submitted list
+ removeBidsFromStorage(bidList);
+
+ store(bidList, function (error, cacheIds) {
+ if (error) {
+ utils.logWarn(`Failed to save to the video cache: ${error}. Video bid(s) must be discarded.`);
+ for (let i = 0; i < bidList.length; i++) {
+ doCallbacksIfTimedout(auctionInstance, bidList[i]);
+ }
+ } else {
+ for (let i = 0; i < cacheIds.length; i++) {
+ // when uuid in response is empty string then the key already existed, so this bid wasn't cached
+ if (cacheIds[i].uuid !== '') {
+ addBidToAuction(auctionInstance, bidList[i]);
+ } else {
+ utils.logInfo(`Detected a bid was not cached because the custom key was already registered. Attempted to use key: ${bidList[i].customCacheKey}. Bid was: `, bidList[i]);
+ }
+ afterBidAdded();
+ }
+ }
+ });
+}
+
+/**
+ * This is the main hook function to handle adpod bids; maintains the logic to temporarily hold bids in a queue in order to send bulk requests to Prebid Cache.
+ * @param {Function} fn reference to original function (used by hook logic)
+ * @param {*} auctionInstance running context of the auction
+ * @param {Object} bidResponse incoming bid; if adpod, will be processed through hook function. If not adpod, returns to original function.
+ * @param {Function} afterBidAdded callback function used when Prebid Cache responds
+ * @param {Object} bidderRequest copy of bid's associated bidderRequest object
+ */
+export function callPrebidCacheHook(fn, auctionInstance, bidResponse, afterBidAdded, bidderRequest) {
+ let videoConfig = utils.deepAccess(bidderRequest, 'mediaTypes.video');
+ if (videoConfig && videoConfig.context === ADPOD) {
+ let brandCategoryExclusion = config.getConfig('adpod.brandCategoryExclusion');
+ let adServerCatId = utils.deepAccess(bidResponse, 'meta.adServerCatId');
+ if (!adServerCatId && brandCategoryExclusion) {
+ utils.logWarn('Detected a bid without meta.adServerCatId while setConfig({adpod.brandCategoryExclusion}) was enabled. This bid has been rejected:', bidResponse)
+ afterBidAdded();
+ }
+
+ if (config.getConfig('adpod.deferCaching') === false) {
+ bidCacheRegistry.addBid(bidResponse);
+ attachPriceIndustryDurationKeyToBid(bidResponse, brandCategoryExclusion);
+
+ updateBidQueue(auctionInstance, bidResponse, afterBidAdded);
+ } else {
+ // generate targeting keys for bid
+ bidCacheRegistry.setupInitialCacheKey(bidResponse);
+ attachPriceIndustryDurationKeyToBid(bidResponse, brandCategoryExclusion);
+
+ // add bid to auction
+ addBidToAuction(auctionInstance, bidResponse);
+ afterBidAdded();
+ }
+ } else {
+ fn.call(this, auctionInstance, bidResponse, afterBidAdded, bidderRequest);
+ }
+}
+
+/**
+ * This hook function will review the adUnit setup and verify certain required values are present in any adpod adUnits.
+ * If the fields are missing or incorrectly setup, the adUnit is removed from the list.
+ * @param {Function} fn reference to original function (used by hook logic)
+ * @param {Array[Object]} adUnits list of adUnits to be evaluated
+ * @returns {Array[Object]} list of adUnits that passed the check
+ */
+export function checkAdUnitSetupHook(fn, adUnits) {
+ let goodAdUnits = adUnits.filter(adUnit => {
+ let mediaTypes = utils.deepAccess(adUnit, 'mediaTypes');
+ let videoConfig = utils.deepAccess(mediaTypes, 'video');
+ if (videoConfig && videoConfig.context === ADPOD) {
+ // run check to see if other mediaTypes are defined (ie multi-format); reject adUnit if so
+ if (Object.keys(mediaTypes).length > 1) {
+ utils.logWarn(`Detected more than one mediaType in adUnitCode: ${adUnit.code} while attempting to define an 'adpod' video adUnit. 'adpod' adUnits cannot be mixed with other mediaTypes. This adUnit will be removed from the auction.`);
+ return false;
+ }
+
+ let errMsg = `Detected missing or incorrectly setup fields for an adpod adUnit. Please review the following fields of adUnitCode: ${adUnit.code}. This adUnit will be removed from the auction.`;
+
+ let playerSize = !!(videoConfig.playerSize && utils.isArrayOfNums(videoConfig.playerSize));
+ let adPodDurationSec = !!(videoConfig.adPodDurationSec && utils.isNumber(videoConfig.adPodDurationSec));
+ let durationRangeSec = !!(videoConfig.durationRangeSec && utils.isArrayOfNums(videoConfig.durationRangeSec));
+
+ if (!playerSize || !adPodDurationSec || !durationRangeSec) {
+ errMsg += (!playerSize) ? '\nmediaTypes.video.playerSize' : '';
+ errMsg += (!adPodDurationSec) ? '\nmediaTypes.video.adPodDurationSec' : '';
+ errMsg += (!durationRangeSec) ? '\nmediaTypes.video.durationRangeSec' : '';
+ utils.logWarn(errMsg);
+ return false;
+ }
+ }
+ return true;
+ });
+ adUnits = goodAdUnits;
+ fn.call(this, adUnits);
+}
+
+/**
+ * This check evaluates the incoming bid's `video.durationSeconds` field and tests it against specific logic depending on adUnit config. Summary of logic below:
+ * when adUnit.mediaTypes.video.requireExactDuration is true
+ * - only bids that exactly match those listed values are accepted (don't round at all).
+ * - populate the `bid.video.durationBucket` field with the matching duration value
+ * when adUnit.mediaTypes.video.requireExactDuration is false
+ * - round the duration to the next highest specified duration value based on adunit. If the duration is above a range within a set buffer, that bid falls down into that bucket.
+ * (eg if range was [5, 15, 30] -> 2s is rounded to 5s; 17s is rounded back to 15s; 18s is rounded up to 30s)
+ * - if the bid is above the range of the listed durations (and outside the buffer), reject the bid
+ * - set the rounded duration value in the `bid.video.durationBucket` field for accepted bids
+ * @param {Object} bidderRequest copy of the bidderRequest object associated to bidResponse
+ * @param {Object} bidResponse incoming bidResponse being evaluated by bidderFactory
+ * @returns {boolean} return false if bid duration is deemed invalid as per adUnit configuration; return true if fine
+*/
+function checkBidDuration(bidderRequest, bidResponse) {
+ const buffer = 2;
+ let bidDuration = utils.deepAccess(bidResponse, 'video.durationSeconds');
+ let videoConfig = utils.deepAccess(bidderRequest, 'mediaTypes.video');
+ let adUnitRanges = videoConfig.durationRangeSec;
+ adUnitRanges.sort((a, b) => a - b); // ensure the ranges are sorted in numeric order
+
+ if (!videoConfig.requireExactDuration) {
+ let max = Math.max(...adUnitRanges);
+ if (bidDuration <= (max + buffer)) {
+ let nextHighestRange = find(adUnitRanges, range => (range + buffer) >= bidDuration);
+ bidResponse.video.durationBucket = nextHighestRange;
+ } else {
+ utils.logWarn(`Detected a bid with a duration value outside the accepted ranges specified in adUnit.mediaTypes.video.durationRangeSec. Rejecting bid: `, bidResponse);
+ return false;
+ }
+ } else {
+ if (find(adUnitRanges, range => range === bidDuration)) {
+ bidResponse.video.durationBucket = bidDuration;
+ } else {
+ utils.logWarn(`Detected a bid with a duration value not part of the list of accepted ranges specified in adUnit.mediaTypes.video.durationRangeSec. Exact match durations must be used for this adUnit. Rejecting bid: `, bidResponse);
+ return false;
+ }
+ }
+ return true;
+}
+
+/**
+ * This hooked function evaluates an adpod bid and determines if the required fields are present.
+ * If it's found to not be an adpod bid, it will return to original function via hook logic
+ * @param {Function} fn reference to original function (used by hook logic)
+ * @param {Object} bid incoming bid object
+ * @param {Object} bidRequest bidRequest object of associated bid
+ * @param {Object} videoMediaType copy of the `bidRequest.mediaTypes.video` object; used in original function
+ * @param {String} context value of the `bidRequest.mediaTypes.video.context` field; used in original function
+ * @returns {boolean} this return is only used for adpod bids
+ */
+export function checkVideoBidSetupHook(fn, bid, bidRequest, videoMediaType, context) {
+ if (context === ADPOD) {
+ let result = true;
+ let brandCategoryExclusion = config.getConfig('adpod.brandCategoryExclusion');
+ if (brandCategoryExclusion && !utils.deepAccess(bid, 'meta.iabSubCatId')) {
+ result = false;
+ }
+
+ if (utils.deepAccess(bid, 'video')) {
+ if (!utils.deepAccess(bid, 'video.context') || bid.video.context !== ADPOD) {
+ result = false;
+ }
+
+ if (!utils.deepAccess(bid, 'video.durationSeconds') || bid.video.durationSeconds <= 0) {
+ result = false;
+ } else {
+ let isBidGood = checkBidDuration(bidRequest, bid);
+ if (!isBidGood) result = false;
+ }
+ }
+
+ if (!config.getConfig('cache.url') && bid.vastXml && !bid.vastUrl) {
+ utils.logError(`
+ This bid contains only vastXml and will not work when a prebid cache url is not specified.
+ Try enabling prebid cache with pbjs.setConfig({ cache: {url: "..."} });
+ `);
+ result = false;
+ };
+
+ fn.bail(result);
+ } else {
+ fn.call(this, bid, bidRequest, videoMediaType, context);
+ }
+}
+
+/**
+ * This function reads the (optional) settings for the adpod as set from the setConfig()
+ * @param {Object} config contains the config settings for adpod module
+ */
+export function adpodSetConfig(config) {
+ if (config.bidQueueTimeDelay !== undefined) {
+ if (typeof config.bidQueueTimeDelay === 'number' && config.bidQueueTimeDelay > 0) {
+ queueTimeDelay = config.bidQueueTimeDelay;
+ } else {
+ utils.logWarn(`Detected invalid value for adpod.bidQueueTimeDelay in setConfig; must be a positive number. Using default: ${queueTimeDelay}`)
+ }
+ }
+
+ if (config.bidQueueSizeLimit !== undefined) {
+ if (typeof config.bidQueueSizeLimit === 'number' && config.bidQueueSizeLimit > 0) {
+ queueSizeLimit = config.bidQueueSizeLimit;
+ } else {
+ utils.logWarn(`Detected invalid value for adpod.bidQueueSizeLimit in setConfig; must be a positive number. Using default: ${queueSizeLimit}`)
+ }
+ }
+}
+config.getConfig('adpod', config => adpodSetConfig(config.adpod));
+
+/**
+ * This function initializes the adpod module's hooks. This is called by the corresponding adserver video module.
+ */
+export function initAdpodHooks() {
+ setupBeforeHookFnOnce(callPrebidCache, callPrebidCacheHook);
+ setupBeforeHookFnOnce(checkAdUnitSetup, checkAdUnitSetupHook);
+ setupBeforeHookFnOnce(checkVideoBidSetup, checkVideoBidSetupHook);
+}
+
+/**
+ *
+ * @param {Array[Object]} bids list of 'winning' bids that need to be cached
+ * @param {Function} callback send the cached bids (or error) back to adserverVideoModule for further processing
+ }}
+ */
+export function callPrebidCacheAfterAuction(bids, callback) {
+ // will call PBC here and execute cb param to initialize player code
+ store(bids, function(error, cacheIds) {
+ if (error) {
+ callback(error, null);
+ } else {
+ let successfulCachedBids = [];
+ for (let i = 0; i < cacheIds.length; i++) {
+ if (cacheIds[i] !== '') {
+ successfulCachedBids.push(bids[i]);
+ }
+ }
+ callback(null, successfulCachedBids);
+ }
+ })
+}
diff --git a/modules/advangelistsBidAdapter.js b/modules/advangelistsBidAdapter.js
new file mode 100644
index 00000000000..926be211649
--- /dev/null
+++ b/modules/advangelistsBidAdapter.js
@@ -0,0 +1,379 @@
+import * as utils from '../src/utils';
+import { parse as parseUrl } from '../src/url';
+import { config } from '../src/config';
+import { registerBidder } from '../src/adapters/bidderFactory';
+import { VIDEO, BANNER } from '../src/mediaTypes';
+import find from 'core-js/library/fn/array/find';
+import includes from 'core-js/library/fn/array/includes';
+
+const ADAPTER_VERSION = '1.0';
+const BIDDER_CODE = 'avng';
+
+export const VIDEO_ENDPOINT = '//nep.advangelists.com/xp/get?pubid=';// 0cf8d6d643e13d86a5b6374148a4afac';
+export const BANNER_ENDPOINT = '//nep.advangelists.com/xp/get?pubid=';// 0cf8d6d643e13d86a5b6374148a4afac';
+export const OUTSTREAM_SRC = '//player-cdn.beachfrontmedia.com/playerapi/loader/outstream.js';
+export const VIDEO_TARGETING = ['mimes', 'playbackmethod', 'maxduration', 'skip'];
+export const DEFAULT_MIMES = ['video/mp4', 'application/javascript'];
+
+let pubid = '';
+
+export const spec = {
+ code: BIDDER_CODE,
+ supportedMediaTypes: [BANNER, VIDEO],
+
+ isBidRequestValid(bidRequest) {
+ if (typeof bidRequest != 'undefined') {
+ if (bidRequest.bidder !== BIDDER_CODE && typeof bidRequest.params === 'undefined') { return false; }
+ if (bidRequest === '' || bidRequest.params.placement === '' || bidRequest.params.pubid === '') { return false; }
+ return true;
+ } else { return false; }
+ },
+
+ buildRequests(bids, bidderRequest) {
+ let requests = [];
+ let videoBids = bids.filter(bid => isVideoBidValid(bid));
+ let bannerBids = bids.filter(bid => isBannerBidValid(bid));
+ videoBids.forEach(bid => {
+ pubid = getVideoBidParam(bid, 'pubid');
+ requests.push({
+ method: 'POST',
+ url: VIDEO_ENDPOINT + pubid,
+ data: createVideoRequestData(bid, bidderRequest),
+ bidRequest: bid
+ });
+ });
+
+ bannerBids.forEach(bid => {
+ pubid = getBannerBidParam(bid, 'pubid');
+ requests.push({
+ method: 'POST',
+ url: BANNER_ENDPOINT + pubid,
+ data: createBannerRequestData(bid, bidderRequest),
+ bidRequest: bid
+ });
+ });
+ return requests;
+ },
+
+ interpretResponse(serverResponse, {bidRequest}) {
+ let response = serverResponse.body;
+ if (response !== null && utils.isEmpty(response) == false) {
+ if (isVideoBid(bidRequest)) {
+ let bidResponse = {
+ requestId: response.id,
+ bidderCode: BIDDER_CODE,
+ cpm: response.seatbid[0].bid[0].price,
+ width: response.seatbid[0].bid[0].w,
+ height: response.seatbid[0].bid[0].h,
+ ttl: response.seatbid[0].bid[0].ttl || 60,
+ creativeId: response.seatbid[0].bid[0].crid,
+ currency: response.cur,
+ mediaType: VIDEO,
+ netRevenue: true
+ }
+
+ if (response.seatbid[0].bid[0].adm) {
+ bidResponse.vastXml = response.seatbid[0].bid[0].adm;
+ bidResponse.adResponse = {
+ content: response.seatbid[0].bid[0].adm
+ };
+ } else {
+ bidResponse.vastUrl = response.seatbid[0].bid[0].nurl;
+ }
+
+ return bidResponse;
+ } else {
+ return {
+ requestId: response.id,
+ bidderCode: BIDDER_CODE,
+ cpm: response.seatbid[0].bid[0].price,
+ width: response.seatbid[0].bid[0].w,
+ height: response.seatbid[0].bid[0].h,
+ ad: response.seatbid[0].bid[0].adm,
+ ttl: response.seatbid[0].bid[0].ttl || 60,
+ creativeId: response.seatbid[0].bid[0].crid,
+ currency: response.cur,
+ mediaType: BANNER,
+ netRevenue: true
+ }
+ }
+ }
+ }
+};
+
+function isBannerBid(bid) {
+ return utils.deepAccess(bid, 'mediaTypes.banner') || !isVideoBid(bid);
+}
+
+function isVideoBid(bid) {
+ return utils.deepAccess(bid, 'mediaTypes.video');
+}
+
+function isVideoBidValid(bid) {
+ return isVideoBid(bid) && getVideoBidParam(bid, 'pubid') && getVideoBidParam(bid, 'placement');
+}
+
+function isBannerBidValid(bid) {
+ return isBannerBid(bid) && getBannerBidParam(bid, 'pubid') && getBannerBidParam(bid, 'placement');
+}
+
+function getVideoBidParam(bid, key) {
+ return utils.deepAccess(bid, 'params.video.' + key) || utils.deepAccess(bid, 'params.' + key);
+}
+
+function getBannerBidParam(bid, key) {
+ return utils.deepAccess(bid, 'params.banner.' + key) || utils.deepAccess(bid, 'params.' + key);
+}
+
+function isMobile() {
+ return (/(ios|ipod|ipad|iphone|android)/i).test(navigator.userAgent);
+}
+
+function isConnectedTV() {
+ return (/(smart[-]?tv|hbbtv|appletv|googletv|hdmi|netcast\.tv|viera|nettv|roku|\bdtv\b|sonydtv|inettvbrowser|\btv\b)/i).test(navigator.userAgent);
+}
+
+function getDoNotTrack() {
+ return navigator.doNotTrack === '1' || window.doNotTrack === '1' || navigator.msDoNoTrack === '1' || navigator.doNotTrack === 'yes';
+}
+
+function findAndFillParam(o, key, value) {
+ try {
+ if (typeof value === 'function') {
+ o[key] = value();
+ } else {
+ o[key] = value;
+ }
+ } catch (ex) {}
+}
+
+function getOsVersion() {
+ let clientStrings = [
+ { s: 'Android', r: /Android/ },
+ { s: 'iOS', r: /(iPhone|iPad|iPod)/ },
+ { s: 'Mac OS X', r: /Mac OS X/ },
+ { s: 'Mac OS', r: /(MacPPC|MacIntel|Mac_PowerPC|Macintosh)/ },
+ { s: 'Linux', r: /(Linux|X11)/ },
+ { s: 'Windows 10', r: /(Windows 10.0|Windows NT 10.0)/ },
+ { s: 'Windows 8.1', r: /(Windows 8.1|Windows NT 6.3)/ },
+ { s: 'Windows 8', r: /(Windows 8|Windows NT 6.2)/ },
+ { s: 'Windows 7', r: /(Windows 7|Windows NT 6.1)/ },
+ { s: 'Windows Vista', r: /Windows NT 6.0/ },
+ { s: 'Windows Server 2003', r: /Windows NT 5.2/ },
+ { s: 'Windows XP', r: /(Windows NT 5.1|Windows XP)/ },
+ { s: 'UNIX', r: /UNIX/ },
+ { s: 'Search Bot', r: /(nuhk|Googlebot|Yammybot|Openbot|Slurp|MSNBot|Ask Jeeves\/Teoma|ia_archiver)/ }
+ ];
+ let cs = find(clientStrings, cs => cs.r.test(navigator.userAgent));
+ return cs ? cs.s : 'unknown';
+}
+
+function getFirstSize(sizes) {
+ return (sizes && sizes.length) ? sizes[0] : { w: undefined, h: undefined };
+}
+
+function parseSizes(sizes) {
+ return utils.parseSizesInput(sizes).map(size => {
+ let [ width, height ] = size.split('x');
+ return {
+ w: parseInt(width, 10) || undefined,
+ h: parseInt(height, 10) || undefined
+ };
+ });
+}
+
+function getVideoSizes(bid) {
+ return parseSizes(utils.deepAccess(bid, 'mediaTypes.video.playerSize') || bid.sizes);
+}
+
+function getBannerSizes(bid) {
+ return parseSizes(utils.deepAccess(bid, 'mediaTypes.banner.sizes') || bid.sizes);
+}
+
+function getTopWindowReferrer() {
+ try {
+ return window.top.document.referrer;
+ } catch (e) {
+ return '';
+ }
+}
+
+function getVideoTargetingParams(bid) {
+ return Object.keys(Object(bid.params.video))
+ .filter(param => includes(VIDEO_TARGETING, param))
+ .reduce((obj, param) => {
+ obj[ param ] = bid.params.video[ param ];
+ return obj;
+ }, {});
+}
+
+function createVideoRequestData(bid, bidderRequest) {
+ let topLocation = getTopWindowLocation(bidderRequest);
+ let topReferrer = getTopWindowReferrer();
+
+ let sizes = getVideoSizes(bid);
+ let firstSize = getFirstSize(sizes);
+
+ let video = getVideoTargetingParams(bid);
+ const o = {
+ 'device': {
+ 'langauge': (global.navigator.language).split('-')[0],
+ 'dnt': (global.navigator.doNotTrack === 1 ? 1 : 0),
+ 'devicetype': isMobile() ? 4 : isConnectedTV() ? 3 : 2,
+ 'js': 1,
+ 'os': getOsVersion()
+ },
+ 'at': 2,
+ 'site': {},
+ 'tmax': 3000,
+ 'cur': ['USD'],
+ 'id': bid.bidId,
+ 'imp': [],
+ 'regs': {
+ 'ext': {
+ }
+ },
+ 'user': {
+ 'ext': {
+ }
+ }
+ };
+
+ o.site['page'] = topLocation.href;
+ o.site['domain'] = topLocation.hostname;
+ o.site['search'] = topLocation.search;
+ o.site['domain'] = topLocation.hostname;
+ o.site['ref'] = topReferrer;
+ o.site['mobile'] = isMobile() ? 1 : 0;
+ const secure = topLocation.protocol.indexOf('https') === 0 ? 1 : 0;
+
+ o.device['dnt'] = getDoNotTrack() ? 1 : 0;
+
+ findAndFillParam(o.site, 'name', function() {
+ return global.top.document.title;
+ });
+
+ findAndFillParam(o.device, 'h', function() {
+ return global.screen.height;
+ });
+ findAndFillParam(o.device, 'w', function() {
+ return global.screen.width;
+ });
+
+ let placement = getVideoBidParam(bid, 'placement');
+
+ for (let j = 0; j < sizes.length; j++) {
+ o.imp.push({
+ 'id': '' + j,
+ 'displaymanager': '' + BIDDER_CODE,
+ 'displaymanagerver': '' + ADAPTER_VERSION,
+ 'tagId': placement,
+ 'bidfloor': 2.0,
+ 'bidfloorcur': 'USD',
+ 'secure': secure,
+ 'video': Object.assign({
+ 'id': utils.generateUUID(),
+ 'pos': 0,
+ 'w': firstSize.w,
+ 'h': firstSize.h,
+ 'mimes': DEFAULT_MIMES
+ }, video)
+
+ });
+ }
+
+ if (bidderRequest && bidderRequest.gdprConsent) {
+ let { gdprApplies, consentString } = bidderRequest.gdprConsent;
+ o.regs.ext = {'gdpr': gdprApplies ? 1 : 0};
+ o.user.ext = {'consent': consentString};
+ }
+
+ return o;
+}
+
+function getTopWindowLocation(bidderRequest) {
+ let url = bidderRequest && bidderRequest.refererInfo && bidderRequest.refererInfo.referer;
+ return parseUrl(config.getConfig('pageUrl') || url, { decodeSearchAsString: true });
+}
+
+function createBannerRequestData(bid, bidderRequest) {
+ let topLocation = getTopWindowLocation(bidderRequest);
+ let topReferrer = getTopWindowReferrer();
+
+ let sizes = getBannerSizes(bid);
+
+ const o = {
+ 'device': {
+ 'langauge': (global.navigator.language).split('-')[0],
+ 'dnt': (global.navigator.doNotTrack === 1 ? 1 : 0),
+ 'devicetype': isMobile() ? 4 : isConnectedTV() ? 3 : 2,
+ 'js': 1
+ },
+ 'at': 2,
+ 'site': {},
+ 'tmax': 3000,
+ 'cur': ['USD'],
+ 'id': bid.bidId,
+ 'imp': [],
+ 'regs': {
+ 'ext': {
+ }
+ },
+ 'user': {
+ 'ext': {
+ }
+ }
+ };
+
+ o.site['page'] = topLocation.href;
+ o.site['domain'] = topLocation.hostname;
+ o.site['search'] = topLocation.search;
+ o.site['domain'] = topLocation.hostname;
+ o.site['ref'] = topReferrer;
+ o.site['mobile'] = isMobile() ? 1 : 0;
+ const secure = topLocation.protocol.indexOf('https') === 0 ? 1 : 0;
+
+ o.device['dnt'] = getDoNotTrack() ? 1 : 0;
+
+ findAndFillParam(o.site, 'name', function() {
+ return global.top.document.title;
+ });
+
+ findAndFillParam(o.device, 'h', function() {
+ return global.screen.height;
+ });
+ findAndFillParam(o.device, 'w', function() {
+ return global.screen.width;
+ });
+
+ let placement = getBannerBidParam(bid, 'placement');
+ for (let j = 0; j < sizes.length; j++) {
+ let size = sizes[j];
+
+ o.imp.push({
+ 'id': '' + j,
+ 'displaymanager': '' + BIDDER_CODE,
+ 'displaymanagerver': '' + ADAPTER_VERSION,
+ 'tagId': placement,
+ 'bidfloor': 2.0,
+ 'bidfloorcur': 'USD',
+ 'secure': secure,
+ 'banner': {
+ 'id': utils.generateUUID(),
+ 'pos': 0,
+ 'w': size['w'],
+ 'h': size['h']
+ }
+ });
+ }
+
+ if (bidderRequest && bidderRequest.gdprConsent) {
+ let { gdprApplies, consentString } = bidderRequest.gdprConsent;
+ o.regs.ext = {'gdpr': gdprApplies ? 1 : 0};
+ o.user.ext = {'consent': consentString};
+ }
+
+ return o;
+}
+
+registerBidder(spec);
diff --git a/modules/advangelistsBidAdapter.md b/modules/advangelistsBidAdapter.md
new file mode 100644
index 00000000000..14e2befd48f
--- /dev/null
+++ b/modules/advangelistsBidAdapter.md
@@ -0,0 +1,65 @@
+# Overview
+
+```
+Module Name: Advangelists Bidder Adapter
+Module Type: Bidder Adapter
+Maintainer: lokesh@advangelists.com
+```
+
+# Description
+
+Connects to Advangelists exchange for bids.
+
+Advangelists bid adapter supports Banner and Video ads currently.
+
+For more informatio
+
+# Sample Display Ad Unit: For Publishers
+```javascript
+var displayAdUnit = [
+{
+ code: 'display',
+ sizes: [
+ [300, 250],
+ [320, 50]
+ ],
+ bids: [{
+ bidder: 'avng',
+ params: {
+ pubid: '0cf8d6d643e13d86a5b6374148a4afac',
+ placement: 1234
+ }
+ }]
+}];
+```
+
+# Sample Video Ad Unit: For Publishers
+```javascript
+
+var videoAdUnit = {
+ code: 'video',
+ sizes: [320,480],
+ mediaTypes: {
+ video: {
+ playerSize : [[320, 480]],
+ context: 'instream'
+ }
+ },
+ bids: [
+ {
+ bidder: 'avng',
+ params: {
+ pubid: '8537f00948fc37cc03c5f0f88e198a76',
+ placement: 1234,
+ video: {
+ id: 123,
+ skip: 1,
+ mimes : ['video/mp4', 'application/javascript'],
+ playbackmethod : [2,6],
+ maxduration: 30
+ }
+ }
+ }
+ ]
+ };
+```
\ No newline at end of file
diff --git a/modules/appnexusBidAdapter.js b/modules/appnexusBidAdapter.js
index d330c09aa10..0f295dadef1 100644
--- a/modules/appnexusBidAdapter.js
+++ b/modules/appnexusBidAdapter.js
@@ -1,7 +1,8 @@
import { Renderer } from '../src/Renderer';
import * as utils from '../src/utils';
-import { registerBidder } from '../src/adapters/bidderFactory';
-import { BANNER, NATIVE, VIDEO } from '../src/mediaTypes';
+import { config } from '../src/config';
+import { registerBidder, getIabSubCategory } from '../src/adapters/bidderFactory';
+import { BANNER, NATIVE, VIDEO, ADPOD } from '../src/mediaTypes';
import find from 'core-js/library/fn/array/find';
import includes from 'core-js/library/fn/array/includes';
@@ -32,6 +33,8 @@ const NATIVE_MAPPING = {
displayUrl: 'displayurl'
};
const SOURCE = 'pbjs';
+const MAX_IMPS_PER_REQUEST = 15;
+const mappingFileUrl = '//acdn.adnxs.com/prebid/appnexus-mapping/mappings.json';
export const spec = {
code: BIDDER_CODE,
@@ -119,6 +122,7 @@ export const spec = {
version: '$prebid.version$'
}
};
+
if (member > 0) {
payload.member_id = member;
}
@@ -130,6 +134,10 @@ export const spec = {
payload.app = appIdObj;
}
+ if (config.getConfig('adpod.brandCategoryExclusion')) {
+ payload.brand_category_uniqueness = true;
+ }
+
if (debugObjParams.enabled) {
payload.debug = debugObjParams;
utils.logInfo('AppNexus Debug Auction Settings:\n\n' + JSON.stringify(debugObjParams, null, 4));
@@ -153,13 +161,18 @@ export const spec = {
payload.referrer_detection = refererinfo;
}
- const payloadString = JSON.stringify(payload);
- return {
- method: 'POST',
- url: URL,
- data: payloadString,
- bidderRequest
- };
+ const hasAdPodBid = find(bidRequests, hasAdPod);
+ if (hasAdPodBid) {
+ bidRequests.filter(hasAdPod).forEach(adPodBid => {
+ const adPodTags = createAdPodRequest(tags, adPodBid);
+ // don't need the original adpod placement because it's in adPodTags
+ const nonPodTags = payload.tags.filter(tag => tag.uuid !== adPodBid.bidId);
+ payload.tags = [...nonPodTags, ...adPodTags];
+ });
+ }
+
+ const request = formatRequest(payload, bidderRequest);
+ return request;
},
/**
@@ -209,6 +222,24 @@ export const spec = {
return bids;
},
+ /**
+ * @typedef {Object} mappingFileInfo
+ * @property {string} url mapping file json url
+ * @property {number} refreshInDays prebid stores mapping data in localstorage so you can return in how many days you want to update value stored in localstorage.
+ * @property {string} localStorageKey unique key to store your mapping json in localstorage
+ */
+
+ /**
+ * Returns mapping file info. This info will be used by bidderFactory to preload mapping file and store data in local storage
+ * @returns {mappingFileInfo}
+ */
+ getMappingFileInfo: function() {
+ return {
+ url: mappingFileUrl,
+ refreshInDays: 7
+ }
+ },
+
getUserSyncs: function(syncOptions) {
if (syncOptions.iframeEnabled) {
return [{
@@ -257,6 +288,35 @@ function deleteValues(keyPairObj) {
}
}
+function formatRequest(payload, bidderRequest) {
+ let request = [];
+
+ if (payload.tags.length > MAX_IMPS_PER_REQUEST) {
+ const clonedPayload = utils.deepClone(payload);
+
+ utils.chunk(payload.tags, MAX_IMPS_PER_REQUEST).forEach(tags => {
+ clonedPayload.tags = tags;
+ const payloadString = JSON.stringify(clonedPayload);
+ request.push({
+ method: 'POST',
+ url: URL,
+ data: payloadString,
+ bidderRequest
+ });
+ });
+ } else {
+ const payloadString = JSON.stringify(payload);
+ request = {
+ method: 'POST',
+ url: URL,
+ data: payloadString,
+ bidderRequest
+ };
+ }
+
+ return request;
+}
+
function newRenderer(adUnitCode, rtbBid, rendererOptions = {}) {
const renderer = Renderer.install({
id: rtbBid.renderer_id,
@@ -316,6 +376,20 @@ function newBid(serverBid, rtbBid, bidderRequest) {
vastImpUrl: rtbBid.notify_url,
ttl: 3600
});
+
+ const videoContext = utils.deepAccess(bidRequest, 'mediaTypes.video.context');
+ if (videoContext === ADPOD) {
+ const iabSubCatId = getIabSubCategory(bidRequest.bidder, rtbBid.brand_category_id);
+ bid.meta = {
+ iabSubCatId
+ };
+
+ bid.video = {
+ context: ADPOD,
+ durationSeconds: Math.floor(rtbBid.rtb.video.duration_ms / 1000),
+ };
+ }
+
// This supports Outstream Video
if (rtbBid.renderer_url) {
const rendererOptions = utils.deepAccess(
@@ -434,6 +508,9 @@ function bidToTag(bid) {
if (bid.mediaType === NATIVE || utils.deepAccess(bid, `mediaTypes.${NATIVE}`)) {
tag.ad_types.push(NATIVE);
+ if (tag.sizes.length === 0) {
+ tag.sizes = transformSizes([1, 1]);
+ }
if (bid.nativeParams) {
const nativeRequest = buildNativeRequest(bid.nativeParams);
@@ -519,6 +596,62 @@ function hasDebug(bid) {
return !!bid.debug
}
+function hasAdPod(bid) {
+ return (
+ bid.mediaTypes &&
+ bid.mediaTypes.video &&
+ bid.mediaTypes.video.context === ADPOD
+ );
+}
+
+/**
+ * Expand an adpod placement into a set of request objects according to the
+ * total adpod duration and the range of duration seconds. Sets minduration/
+ * maxduration video property according to requireExactDuration configuration
+ */
+function createAdPodRequest(tags, adPodBid) {
+ const { durationRangeSec, requireExactDuration } = adPodBid.mediaTypes.video;
+
+ const numberOfPlacements = getAdPodPlacementNumber(adPodBid.mediaTypes.video);
+ const maxDuration = utils.getMaxValueFromArray(durationRangeSec);
+
+ const tagToDuplicate = tags.filter(tag => tag.uuid === adPodBid.bidId);
+ let request = utils.fill(...tagToDuplicate, numberOfPlacements);
+
+ if (requireExactDuration) {
+ const divider = Math.ceil(numberOfPlacements / durationRangeSec.length);
+ const chunked = utils.chunk(request, divider);
+
+ // each configured duration is set as min/maxduration for a subset of requests
+ durationRangeSec.forEach((duration, index) => {
+ chunked[index].map(tag => {
+ setVideoProperty(tag, 'minduration', duration);
+ setVideoProperty(tag, 'maxduration', duration);
+ });
+ });
+ } else {
+ // all maxdurations should be the same
+ request.map(tag => setVideoProperty(tag, 'maxduration', maxDuration));
+ }
+
+ return request;
+}
+
+function getAdPodPlacementNumber(videoParams) {
+ const { adPodDurationSec, durationRangeSec, requireExactDuration } = videoParams;
+ const minAllowedDuration = utils.getMinValueFromArray(durationRangeSec);
+ const numberOfPlacements = Math.floor(adPodDurationSec / minAllowedDuration);
+
+ return requireExactDuration
+ ? Math.max(numberOfPlacements, durationRangeSec.length)
+ : numberOfPlacements;
+}
+
+function setVideoProperty(tag, key, value) {
+ if (utils.isEmpty(tag.video)) { tag.video = {}; }
+ tag.video[key] = value;
+}
+
function getRtbBid(tag) {
return tag && tag.ads && tag.ads.length && find(tag.ads, ad => ad.rtb);
}
diff --git a/modules/categoryTranslation.js b/modules/categoryTranslation.js
new file mode 100644
index 00000000000..f4d0a281f57
--- /dev/null
+++ b/modules/categoryTranslation.js
@@ -0,0 +1,96 @@
+/**
+ * This module translates iab category to freewheel industry using translation mapping file
+ * Publisher can set translation file by using setConfig method
+ *
+ * Example:
+ * config.setConfig({
+ * 'brandCategoryTranslation': {
+ * 'translationFile': 'http://sample.com'
+ * }
+ * });
+ * If publisher has not defined translation file than prebid will use default prebid translation file provided here //cdn.jsdelivr.net/gh/prebid/category-mapping-file@1/freewheel-mapping.json
+ */
+
+import { config } from '../src/config';
+import { setupBeforeHookFnOnce, hook } from '../src/hook';
+import { ajax } from '../src/ajax';
+import { timestamp, logError, setDataInLocalStorage, getDataFromLocalStorage } from '../src/utils';
+import { addBidResponse } from '../src/auction';
+
+const DEFAULT_TRANSLATION_FILE_URL = 'https://cdn.jsdelivr.net/gh/prebid/category-mapping-file@1/freewheel-mapping.json';
+const DEFAULT_IAB_TO_FW_MAPPING_KEY = 'iabToFwMappingkey';
+const DEFAULT_IAB_TO_FW_MAPPING_KEY_PUB = 'iabToFwMappingkeyPub';
+const refreshInDays = 1;
+
+export const registerAdserver = hook('async', function(adServer) {
+ let url;
+ if (adServer === 'freewheel') {
+ url = DEFAULT_TRANSLATION_FILE_URL;
+ }
+ initTranslation(url, DEFAULT_IAB_TO_FW_MAPPING_KEY);
+}, 'registerAdserver');
+registerAdserver();
+
+export function getAdserverCategoryHook(fn, adUnitCode, bid) {
+ if (!bid) {
+ return fn.call(this, adUnitCode); // if no bid, call original and let it display warnings
+ }
+
+ if (!config.getConfig('adpod.brandCategoryExclusion')) {
+ return fn.call(this, adUnitCode, bid);
+ }
+
+ let localStorageKey = (config.getConfig('brandCategoryTranslation.translationFile')) ? DEFAULT_IAB_TO_FW_MAPPING_KEY_PUB : DEFAULT_IAB_TO_FW_MAPPING_KEY;
+
+ if (bid.meta && !bid.meta.adServerCatId) {
+ let mapping = getDataFromLocalStorage(localStorageKey);
+ if (mapping) {
+ try {
+ mapping = JSON.parse(mapping);
+ } catch (error) {
+ logError('Failed to parse translation mapping file');
+ }
+ if (bid.meta.iabSubCatId && mapping['mapping'] && mapping['mapping'][bid.meta.iabSubCatId]) {
+ bid.meta.adServerCatId = mapping['mapping'][bid.meta.iabSubCatId]['id'];
+ } else {
+ // This bid will be automatically ignored by adpod module as adServerCatId was not found
+ bid.meta.adServerCatId = undefined;
+ }
+ } else {
+ logError('Translation mapping data not found in local storage');
+ }
+ }
+ fn.call(this, adUnitCode, bid);
+}
+
+export function initTranslation(url, localStorageKey) {
+ setupBeforeHookFnOnce(addBidResponse, getAdserverCategoryHook, 50);
+ let mappingData = getDataFromLocalStorage(localStorageKey);
+ if (!mappingData || timestamp() < mappingData.lastUpdated + refreshInDays * 24 * 60 * 60 * 1000) {
+ ajax(url,
+ {
+ success: (response) => {
+ try {
+ response = JSON.parse(response);
+ response['lastUpdated'] = timestamp();
+ setDataInLocalStorage(localStorageKey, JSON.stringify(response));
+ } catch (error) {
+ logError('Failed to parse translation mapping file');
+ }
+ },
+ error: () => {
+ logError('Failed to load brand category translation file.')
+ }
+ },
+ );
+ }
+}
+
+function setConfig(config) {
+ if (config.translationFile) {
+ // if publisher has defined the translation file, preload that file here
+ initTranslation(config.translationFile, DEFAULT_IAB_TO_FW_MAPPING_KEY_PUB);
+ }
+}
+
+config.getConfig('brandCategoryTranslation', config => setConfig(config.brandCategoryTranslation));
diff --git a/modules/currency.js b/modules/currency.js
index 700157acdd3..17c38b17a98 100644
--- a/modules/currency.js
+++ b/modules/currency.js
@@ -3,7 +3,7 @@ import { STATUS } from '../src/constants';
import { ajax } from '../src/ajax';
import * as utils from '../src/utils';
import { config } from '../src/config';
-import { hooks } from '../src/hook.js';
+import { getHook } from '../src/hook.js';
const DEFAULT_CURRENCY_RATE_URL = 'https://cdn.jsdelivr.net/gh/prebid/currency-file@1/latest.json?date=$$TODAY$$';
const CURRENCY_RATE_PRECISION = 4;
@@ -122,7 +122,7 @@ function initCurrency(url) {
utils.logInfo('Installing addBidResponse decorator for currency module', arguments);
- hooks['addBidResponse'].before(addBidResponseHook, 100);
+ getHook('addBidResponse').before(addBidResponseHook, 100);
// call for the file if we haven't already
if (needToCallForCurrencyFile) {
@@ -148,7 +148,7 @@ function initCurrency(url) {
function resetCurrency() {
utils.logInfo('Uninstalling addBidResponse decorator for currency module', arguments);
- hooks['addBidResponse'].getHooks({hook: addBidResponseHook}).remove();
+ getHook('addBidResponse').getHooks({hook: addBidResponseHook}).remove();
adServerCurrency = 'USD';
conversionCache = {};
diff --git a/modules/dfpAdServerVideo.js b/modules/dfpAdServerVideo.js
index 1b5f8509559..17a8f0f1144 100644
--- a/modules/dfpAdServerVideo.js
+++ b/modules/dfpAdServerVideo.js
@@ -160,9 +160,6 @@ function getCustParams(bid, options) {
let customParams = Object.assign({},
allTargetingData,
adserverTargeting,
- { hb_uuid: bid && bid.videoCacheKey },
- // hb_uuid will be deprecated and replaced by hb_cache_id
- { hb_cache_id: bid && bid.videoCacheKey },
optCustParams,
);
return encodeURIComponent(formatQS(customParams));
diff --git a/modules/freeWheelAdserverVideo.js b/modules/freeWheelAdserverVideo.js
new file mode 100644
index 00000000000..01ed145b00b
--- /dev/null
+++ b/modules/freeWheelAdserverVideo.js
@@ -0,0 +1,166 @@
+/**
+ * This module adds Freewheel support for Video to Prebid.
+ */
+
+import { registerVideoSupport } from '../src/adServerManager';
+import { auctionManager } from '../src/auctionManager';
+import { groupBy, deepAccess, logError } from '../src/utils';
+import { config } from '../src/config';
+import { ADPOD } from '../src/mediaTypes';
+import { initAdpodHooks, TARGETING_KEY_PB_CAT_DUR, TARGETING_KEY_CACHE_ID, callPrebidCacheAfterAuction } from './adpod';
+import { getHook } from '../src/hook';
+
+export function notifyTranslationModule(fn) {
+ fn.call(this, 'freewheel');
+}
+
+getHook('registerAdserver').before(notifyTranslationModule);
+
+/**
+ * This function returns targeting keyvalue pairs for freewheel adserver module
+ * @param {Object} options
+ * @param {Array[string]} codes
+ * @param {function} callback
+ * @returns targeting kvs for adUnitCodes
+ */
+export function getTargeting({codes, callback} = {}) {
+ if (!callback) {
+ logError('No callback function was defined in the getTargeting call. Aborting getTargeting().');
+ return;
+ }
+ codes = codes || [];
+ const adPodAdUnits = getAdPodAdUnits(codes);
+ const bidsReceived = auctionManager.getBidsReceived();
+ const competiveExclusionEnabled = config.getConfig('adpod.brandCategoryExclusion');
+ const deferCachingSetting = config.getConfig('adpod.deferCaching');
+ const deferCachingEnabled = (typeof deferCachingSetting === 'boolean') ? deferCachingSetting : true;
+
+ let bids = getBidsForAdpod(bidsReceived, adPodAdUnits);
+ bids = (competiveExclusionEnabled || deferCachingEnabled) ? getExclusiveBids(bids) : bids;
+ bids.sort(compareOn('cpm'));
+
+ let targeting = {};
+ if (deferCachingEnabled === false) {
+ adPodAdUnits.forEach((adUnit) => {
+ let adPodTargeting = [];
+ let adPodDurationSeconds = deepAccess(adUnit, 'mediaTypes.video.adPodDurationSec');
+
+ bids
+ .filter((bid) => bid.adUnitCode === adUnit.code)
+ .forEach((bid, index, arr) => {
+ if (bid.video.durationBucket <= adPodDurationSeconds) {
+ adPodTargeting.push({
+ [TARGETING_KEY_PB_CAT_DUR]: bid.adserverTargeting[TARGETING_KEY_PB_CAT_DUR]
+ });
+ adPodDurationSeconds -= bid.video.durationBucket;
+ }
+ if (index === arr.length - 1 && adPodTargeting.length > 0) {
+ adPodTargeting.push({
+ [TARGETING_KEY_CACHE_ID]: bid.adserverTargeting[TARGETING_KEY_CACHE_ID]
+ });
+ }
+ });
+ targeting[adUnit.code] = adPodTargeting;
+ });
+
+ callback(null, targeting);
+ } else {
+ let bidsToCache = [];
+ adPodAdUnits.forEach((adUnit) => {
+ let adPodDurationSeconds = deepAccess(adUnit, 'mediaTypes.video.adPodDurationSec');
+
+ bids
+ .filter((bid) => bid.adUnitCode === adUnit.code)
+ .forEach((bid) => {
+ if (bid.video.durationBucket <= adPodDurationSeconds) {
+ bidsToCache.push(bid);
+ adPodDurationSeconds -= bid.video.durationBucket;
+ }
+ });
+ });
+
+ callPrebidCacheAfterAuction(bidsToCache, function(error, bidsSuccessfullyCached) {
+ if (error) {
+ callback(error, null);
+ } else {
+ let groupedBids = groupBy(bidsSuccessfullyCached, 'adUnitCode');
+ Object.keys(groupedBids).forEach((adUnitCode) => {
+ let adPodTargeting = [];
+
+ groupedBids[adUnitCode].forEach((bid, index, arr) => {
+ adPodTargeting.push({
+ [TARGETING_KEY_PB_CAT_DUR]: bid.adserverTargeting[TARGETING_KEY_PB_CAT_DUR]
+ });
+
+ if (index === arr.length - 1 && adPodTargeting.length > 0) {
+ adPodTargeting.push({
+ [TARGETING_KEY_CACHE_ID]: bid.adserverTargeting[TARGETING_KEY_CACHE_ID]
+ });
+ }
+ });
+ targeting[adUnitCode] = adPodTargeting;
+ });
+
+ callback(null, targeting);
+ }
+ });
+ }
+ return targeting;
+}
+
+/**
+ * This function returns the adunit of mediaType adpod
+ * @param {Array} codes adUnitCodes
+ * @returns {Array[Object]} adunits of mediaType adpod
+ */
+function getAdPodAdUnits(codes) {
+ return auctionManager.getAdUnits()
+ .filter((adUnit) => deepAccess(adUnit, 'mediaTypes.video.context') === ADPOD)
+ .filter((adUnit) => (codes.length > 0) ? codes.indexOf(adUnit.code) != -1 : true);
+}
+
+function compareOn(property) {
+ return function compare(a, b) {
+ if (a[property] < b[property]) {
+ return 1;
+ }
+ if (a[property] > b[property]) {
+ return -1;
+ }
+ return 0;
+ }
+}
+
+/**
+ * This function removes bids of same freewheel category. It will be used when competitive exclusion is enabled.
+ * @param {Array[Object]} bidsReceived
+ * @returns {Array[Object]} unique freewheel category bids
+ */
+function getExclusiveBids(bidsReceived) {
+ let bids = bidsReceived
+ .map((bid) => Object.assign({}, bid, {[TARGETING_KEY_PB_CAT_DUR]: bid.adserverTargeting[TARGETING_KEY_PB_CAT_DUR]}));
+ bids = groupBy(bids, TARGETING_KEY_PB_CAT_DUR);
+ let filteredBids = [];
+ Object.keys(bids).forEach((targetingKey) => {
+ bids[targetingKey].sort(compareOn('responseTimestamp'));
+ filteredBids.push(bids[targetingKey][0]);
+ });
+ return filteredBids;
+}
+
+/**
+ * This function returns bids for adpod adunits
+ * @param {Array[Object]} bidsReceived
+ * @param {Array[Object]} adPodAdUnits
+ * @returns {Array[Object]} bids of mediaType adpod
+ */
+function getBidsForAdpod(bidsReceived, adPodAdUnits) {
+ let adUnitCodes = adPodAdUnits.map((adUnit) => adUnit.code);
+ return bidsReceived
+ .filter((bid) => adUnitCodes.indexOf(bid.adUnitCode) != -1 && (bid.video && bid.video.context === ADPOD))
+}
+
+initAdpodHooks();
+registerVideoSupport('freewheel', {
+ getTargeting: getTargeting
+});
diff --git a/modules/loopmeBidAdapter.js b/modules/loopmeBidAdapter.js
new file mode 100644
index 00000000000..fb2f891d3b0
--- /dev/null
+++ b/modules/loopmeBidAdapter.js
@@ -0,0 +1,99 @@
+import * as utils from 'src/utils';
+import { registerBidder } from 'src/adapters/bidderFactory';
+import { BANNER } from 'src/mediaTypes';
+
+const LOOPME_ENDPOINT = 'https://loopme.me/api/hb';
+
+const entries = (obj) => {
+ let output = [];
+ for (let key in obj) {
+ if (obj.hasOwnProperty(key)) {
+ output.push([key, obj[key]])
+ }
+ }
+ return output;
+}
+
+export const spec = {
+ code: 'loopme',
+ supportedMediaTypes: [BANNER],
+ /**
+ * @param {object} bid
+ * @return boolean
+ */
+ isBidRequestValid: function(bid) {
+ if (typeof bid.params !== 'object') {
+ return false;
+ }
+
+ return !!bid.params.ak;
+ },
+ /**
+ * @param {BidRequest[]} bidRequests
+ * @param bidderRequest
+ * @return ServerRequest[]
+ */
+ buildRequests: function(bidRequests, bidderRequest) {
+ return bidRequests.map(bidRequest => {
+ bidRequest.startTime = new Date().getTime();
+ let payload = bidRequest.params;
+
+ if (bidderRequest && bidderRequest.gdprConsent) {
+ payload.user_consent = bidderRequest.gdprConsent.consentString;
+ }
+
+ let queryString = entries(payload)
+ .map(item => `${item[0]}=${encodeURI(item[1])}`)
+ .join('&');
+
+ const sizes =
+ '&sizes=' +
+ utils
+ .getAdUnitSizes(bidRequest)
+ .map(size => `${size[0]}x${size[1]}`)
+ .join('&sizes=');
+
+ queryString = `${queryString}${sizes}`;
+
+ return {
+ method: 'GET',
+ url: `${LOOPME_ENDPOINT}`,
+ options: { withCredentials: false },
+ bidId: bidRequest.bidId,
+ data: queryString
+ };
+ });
+ },
+ /**
+ * @param {*} responseObj
+ * @param {BidRequest} bidRequest
+ * @return {Bid[]} An array of bids which
+ */
+ interpretResponse: function(response = {}, bidRequest) {
+ const responseObj = response.body;
+
+ if (
+ responseObj == null ||
+ typeof responseObj !== 'object' ||
+ !responseObj.hasOwnProperty('ad')
+ ) {
+ return [];
+ }
+
+ return [
+ {
+ requestId: bidRequest.bidId,
+ cpm: responseObj.cpm,
+ width: responseObj.width,
+ height: responseObj.height,
+ ad: responseObj.ad,
+ ttl: responseObj.ttl,
+ currency: responseObj.currency,
+ creativeId: responseObj.creativeId,
+ dealId: responseObj.dealId,
+ netRevenue: responseObj.netRevenue
+ }
+ ];
+ }
+};
+registerBidder(spec);
diff --git a/modules/loopmeBidAdapter.md b/modules/loopmeBidAdapter.md
new file mode 100644
index 00000000000..be8c20cfade
--- /dev/null
+++ b/modules/loopmeBidAdapter.md
@@ -0,0 +1,29 @@
+# Overview
+
+```
+Module Name: LoopMe Bid Adapter
+Module Type: Bidder Adapter
+Maintainer: support@loopme.com
+```
+
+# Description
+
+Connect to LoopMe's exchange for bids.
+
+# Test Parameters
+```
+var adUnits = [{
+ code: 'test-div',
+ mediaTypes: {
+ banner: {
+ sizes: [[300, 250], [300,600]],
+ }
+ },
+ bids: [{
+ bidder: 'loopme',
+ params: {
+ ak: 'cc885e3acc'
+ }
+ }]
+}];
+```
diff --git a/modules/microadBidAdapter.js b/modules/microadBidAdapter.js
new file mode 100644
index 00000000000..d42e4053fda
--- /dev/null
+++ b/modules/microadBidAdapter.js
@@ -0,0 +1,151 @@
+import { registerBidder } from 'src/adapters/bidderFactory';
+import { BANNER } from 'src/mediaTypes';
+
+const BIDDER_CODE = 'microad';
+
+const ENDPOINT_URLS = {
+ 'production': '//s-rtb-pb.send.microad.jp/prebid',
+ 'test': 'https://rtbtest.send.microad.jp/prebid'
+};
+export let ENVIRONMENT = 'production';
+
+/* eslint-disable no-template-curly-in-string */
+const EXT_URL_STRING = '${COMPASS_EXT_URL}';
+const EXT_REF_STRING = '${COMPASS_EXT_REF}';
+const EXT_IFA_STRING = '${COMPASS_EXT_IFA}';
+const EXT_APPID_STRING = '${COMPASS_EXT_APPID}';
+const EXT_GEO_STRING = '${COMPASS_EXT_GEO}';
+/* eslint-enable no-template-curly-in-string */
+
+const BANNER_CODE = 1;
+const NATIVE_CODE = 2;
+const VIDEO_CODE = 4;
+
+function createCBT() {
+ const randomValue = Math.floor(Math.random() * Math.pow(10, 18)).toString(16);
+ const date = new Date().getTime().toString(16);
+ return randomValue + date;
+}
+
+function createBitSequenceFromMediaType(hi, code) {
+ return (hi ? -1 : 0) & code;
+}
+
+function convertMediaTypes(bid) {
+ return createBitSequenceFromMediaType(bid.mediaTypes.banner, BANNER_CODE) |
+ createBitSequenceFromMediaType(bid.mediaTypes.native, NATIVE_CODE) |
+ createBitSequenceFromMediaType(bid.mediaTypes.video, VIDEO_CODE);
+}
+
+export const spec = {
+ code: BIDDER_CODE,
+ supportedMediaTypes: [BANNER],
+ isBidRequestValid: function(bid) {
+ return !!(bid && bid.params && bid.params.spot && bid.mediaTypes && (bid.mediaTypes.banner || bid.mediaTypes.native || bid.mediaTypes.video));
+ },
+ buildRequests: function(validBidRequests, bidderRequest) {
+ const requests = [];
+
+ validBidRequests.forEach(bid => {
+ const bidParams = bid.params;
+ const params = {
+ spot: bidParams.spot,
+ url: bidderRequest.refererInfo.canonicalUrl || window.location.href,
+ referrer: bidderRequest.refererInfo.referer,
+ bid_id: bid.bidId,
+ transaction_id: bid.transactionId,
+ media_types: convertMediaTypes(bid),
+ cbt: createCBT()
+ };
+
+ if (bidParams.url) {
+ params['url_macro'] = bidParams.url.replace(EXT_URL_STRING, '');
+ }
+
+ if (bidParams.referrer) {
+ params['referrer_macro'] = bidParams.referrer.replace(EXT_REF_STRING, '');
+ }
+
+ if (bidParams.ifa) {
+ params['ifa'] = bidParams.ifa.replace(EXT_IFA_STRING, '');
+ }
+
+ if (bidParams.appid) {
+ params['appid'] = bidParams.appid.replace(EXT_APPID_STRING, '');
+ }
+
+ if (bidParams.geo) {
+ const geo = bidParams.geo.replace(EXT_GEO_STRING, '');
+ if (/^[0-9.\-]+,[0-9.\-]+$/.test(geo)) {
+ params['geo'] = geo;
+ }
+ }
+
+ requests.push({
+ method: 'GET',
+ url: ENDPOINT_URLS[ENVIRONMENT],
+ data: params,
+ options: { Accept: 'application/json' }
+ });
+ });
+ return requests;
+ },
+ interpretResponse: function(serverResponse) {
+ const body = serverResponse.body;
+ const bidResponses = [];
+
+ if (body.cpm && body.cpm > 0) {
+ const bidResponse = {
+ requestId: body.requestId,
+ cpm: body.cpm,
+ width: body.width,
+ height: body.height,
+ ad: body.ad,
+ ttl: body.ttl,
+ creativeId: body.creativeId,
+ netRevenue: body.netRevenue,
+ currency: body.currency,
+ };
+
+ if (body.dealId) {
+ bidResponse['dealId'] = body.dealId;
+ }
+
+ bidResponses.push(bidResponse);
+ }
+
+ return bidResponses;
+ },
+ getUserSyncs: function(syncOptions, serverResponses) {
+ const syncs = [];
+
+ if (!syncOptions.iframeEnabled && !syncOptions.pixelEnabled) {
+ return syncs;
+ }
+
+ serverResponses.forEach(resp => {
+ const syncIframeUrls = resp.body.syncUrls.iframe;
+ const syncImageUrls = resp.body.syncUrls.image;
+ if (syncOptions.iframeEnabled && syncIframeUrls) {
+ syncIframeUrls.forEach(syncIframeUrl => {
+ syncs.push({
+ type: 'iframe',
+ url: syncIframeUrl
+ });
+ });
+ }
+ if (syncOptions.pixelEnabled && syncImageUrls) {
+ syncImageUrls.forEach(syncImageUrl => {
+ syncs.push({
+ type: 'image',
+ url: syncImageUrl
+ });
+ });
+ }
+ });
+
+ return syncs;
+ }
+};
+
+registerBidder(spec);
diff --git a/modules/microadBidAdapter.md b/modules/microadBidAdapter.md
new file mode 100644
index 00000000000..c805e5cf6fb
--- /dev/null
+++ b/modules/microadBidAdapter.md
@@ -0,0 +1,28 @@
+# Overview
+
+Module Name: MicroAd SSP Bidder Adapter
+Module Type: Bidder Adapter
+Maintainer: prebid@microad.co.jp
+
+# Description
+
+Module that connects to MicroAd SSP demand sources.
+
+# Test Parameters
+
+```javascript
+ var adUnits = [
+ code: '209e56872ae8b0442a60477ae0c58be9',
+ mediaTypes: {
+ banner: {
+ sizes: [[200, 200]]
+ }
+ },
+ bids: [{
+ bidder: 'microad',
+ params: {
+ spot: '209e56872ae8b0442a60477ae0c58be9'
+ }
+ }]
+ ];
+```
diff --git a/modules/mytargetBidAdapter.js b/modules/mytargetBidAdapter.js
new file mode 100644
index 00000000000..e5b6cc735ef
--- /dev/null
+++ b/modules/mytargetBidAdapter.js
@@ -0,0 +1,108 @@
+import * as utils from '../src/utils';
+import * as url from '../src/url';
+import { config } from '../src/config';
+import { registerBidder } from '../src/adapters/bidderFactory';
+
+const BIDDER_CODE = 'mytarget';
+const BIDDER_URL = '//ad.mail.ru/hbid_prebid/';
+const DEFAULT_CURRENCY = 'RUB';
+const DEFAULT_TTL = 180;
+
+function buildPlacement(bidRequest) {
+ let { bidId, params } = bidRequest;
+ let { placementId, position, response, bidfloor } = params;
+ let placement = {
+ placementId,
+ id: bidId,
+ position: position || 0,
+ response: response || 0
+ };
+
+ if (typeof bidfloor !== 'undefined') {
+ placement.bidfloor = bidfloor;
+ }
+
+ return placement;
+}
+
+function getSiteName(referrer) {
+ let sitename = config.getConfig('mytarget.sitename');
+
+ if (!sitename) {
+ sitename = url.parse(referrer).hostname;
+ }
+
+ return sitename;
+}
+
+function generateRandomId() {
+ return Math.random().toString(16).substring(2);
+}
+
+export const spec = {
+ code: BIDDER_CODE,
+
+ isBidRequestValid: function(bid) {
+ return !!bid.params.placementId;
+ },
+
+ buildRequests: function(validBidRequests, bidderRequest) {
+ let referrer = '';
+
+ if (bidderRequest && bidderRequest.refererInfo) {
+ referrer = bidderRequest.refererInfo.referer;
+ }
+
+ const payload = {
+ places: utils._map(validBidRequests, buildPlacement),
+ site: {
+ sitename: getSiteName(referrer),
+ page: referrer
+ },
+ settings: {
+ currency: DEFAULT_CURRENCY,
+ windowSize: {
+ width: window.screen.width,
+ height: window.screen.height
+ }
+ }
+ };
+
+ return {
+ method: 'POST',
+ url: BIDDER_URL,
+ data: payload,
+ };
+ },
+
+ interpretResponse: function(serverResponse, bidRequest) {
+ let { body } = serverResponse;
+
+ if (body.bids) {
+ return utils._map(body.bids, (bid) => {
+ let bidResponse = {
+ requestId: bid.id,
+ cpm: bid.price,
+ width: bid.size.width,
+ height: bid.size.height,
+ ttl: bid.ttl || DEFAULT_TTL,
+ currency: bid.currency || DEFAULT_CURRENCY,
+ creativeId: bid.creativeId || generateRandomId(),
+ netRevenue: true
+ }
+
+ if (bid.adm) {
+ bidResponse.ad = bid.adm;
+ } else {
+ bidResponse.adUrl = bid.displayUrl;
+ }
+
+ return bidResponse;
+ });
+ }
+
+ return [];
+ }
+}
+
+registerBidder(spec);
diff --git a/modules/mytargetBidAdapter.md b/modules/mytargetBidAdapter.md
new file mode 100644
index 00000000000..3292ff561fa
--- /dev/null
+++ b/modules/mytargetBidAdapter.md
@@ -0,0 +1,40 @@
+# Overview
+
+```
+Module Name: myTarget Bidder Adapter
+Module Type: Bidder Adapter
+Maintainer: support_target@corp.my.com
+```
+
+# Description
+
+Module that connects to myTarget demand sources.
+
+# Test Parameters
+
+```
+ var adUnits = [{
+ code: 'placementCode',
+ mediaTypes: {
+ banner: {
+ sizes: [[240, 400]],
+ }
+ },
+ bids: [{
+ bidder: 'mytarget',
+ params: {
+ placementId: '379783',
+
+ // OPTIONAL: custom bid floor
+ bidfloor: 10000,
+
+ // OPTIONAL: if you know the ad position on the page, specify it here
+ // (this corresponds to "Ad Position" in OpenRTB 2.3, section 5.4)
+ position: 0,
+
+ // OPTIONAL: bid response type: 0 - ad url (default), 1 - ad markup
+ response: 0
+ }
+ }]
+ }];
+```
diff --git a/modules/prebidServerBidAdapter/index.js b/modules/prebidServerBidAdapter/index.js
index 5938c493871..fdcab82d247 100644
--- a/modules/prebidServerBidAdapter/index.js
+++ b/modules/prebidServerBidAdapter/index.js
@@ -71,6 +71,7 @@ config.setDefaults({
* @property {boolean} [cacheMarkup] whether to cache the adm result
* @property {string} [adapter] adapter code to use for S2S
* @property {string} [syncEndpoint] endpoint URL for syncing cookies
+ * @property {Object} [extPrebid] properties will be merged into request.ext.prebid
* @property {AdapterOptions} [adapterOptions] adds arguments to resulting OpenRTB payload to Prebid Server
*/
function setS2sConfig(options) {
@@ -169,7 +170,7 @@ function doAllSyncs(bidders) {
const thisSync = bidders.pop();
if (thisSync.no_cookie) {
- doBidderSync(thisSync.usersync.type, thisSync.usersync.url, thisSync.bidder, doAllSyncs.bind(null, bidders));
+ doBidderSync(thisSync.usersync.type, thisSync.usersync.url, thisSync.bidder, utils.bind.call(doAllSyncs, null, bidders));
} else {
doAllSyncs(bidders);
}
@@ -483,8 +484,23 @@ const OPEN_RTB_PROTOCOL = {
tmax: _s2sConfig.timeout,
imp: imps,
test: getConfig('debug') ? 1 : 0,
+ ext: {
+ prebid: {
+ targeting: {
+ // includewinners is always true for openrtb
+ includewinners: true,
+ // includebidderkeys always false for openrtb
+ includebidderkeys: false
+ }
+ }
+ }
};
+ // 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);
+ }
+
_appendSiteAppDevice(request);
const digiTrust = _getDigiTrustQueryParams();
@@ -493,7 +509,7 @@ const OPEN_RTB_PROTOCOL = {
}
if (!utils.isEmpty(aliases)) {
- request.ext = { prebid: { aliases } };
+ request.ext.prebid.aliases = aliases;
}
if (bidRequests && bidRequests[0].userId && typeof bidRequests[0].userId === 'object') {
@@ -571,10 +587,29 @@ const OPEN_RTB_PROTOCOL = {
bidRequest.serverResponseTimeMs = serverResponseTimeMs;
}
+ const extPrebidTargeting = utils.deepAccess(bid, 'ext.prebid.targeting');
+
+ // If ext.prebid.targeting exists, add it as a property value named 'adserverTargeting'
+ if (extPrebidTargeting && typeof extPrebidTargeting === 'object') {
+ bidObject.adserverTargeting = extPrebidTargeting;
+ }
+
if (utils.deepAccess(bid, 'ext.prebid.type') === VIDEO) {
bidObject.mediaType = VIDEO;
+
+ // try to get cache values from 'response.ext.prebid.cache'
+ // else try 'bid.ext.prebid.targeting' as fallback
+ if (bid.ext.prebid.cache && typeof bid.ext.prebid.cache.vastXml === 'object' && bid.ext.prebid.cache.vastXml.cacheId && bid.ext.prebid.cache.vastXml.url) {
+ bidObject.videoCacheKey = bid.ext.prebid.cache.vastXml.cacheId;
+ bidObject.vastUrl = bid.ext.prebid.cache.vastXml.url;
+ } else if (extPrebidTargeting && extPrebidTargeting.hb_uuid && extPrebidTargeting.hb_cache_host && extPrebidTargeting.hb_cache_path) {
+ bidObject.videoCacheKey = extPrebidTargeting.hb_uuid;
+ // build url using key and cache host
+ bidObject.vastUrl = `https://${extPrebidTargeting.hb_cache_host}${extPrebidTargeting.hb_cache_path}?uuid=${extPrebidTargeting.hb_uuid}`;
+ }
+
if (bid.adm) { bidObject.vastXml = bid.adm; }
- if (bid.nurl) { bidObject.vastUrl = bid.nurl; }
+ if (!bidObject.vastUrl && bid.nurl) { bidObject.vastUrl = bid.nurl; }
} else { // banner
if (bid.adm && bid.nurl) {
bidObject.ad = bid.adm;
diff --git a/modules/rubiconBidAdapter.js b/modules/rubiconBidAdapter.js
index b076df68474..c5fb4486d20 100644
--- a/modules/rubiconBidAdapter.js
+++ b/modules/rubiconBidAdapter.js
@@ -11,11 +11,9 @@ function isSecure() {
// use protocol relative urls for http or https
export const FASTLANE_ENDPOINT = '//fastlane.rubiconproject.com/a/api/fastlane.json';
-export const VIDEO_ENDPOINT = '//fastlane-adv.rubiconproject.com/v1/auction/video';
+export const VIDEO_ENDPOINT = '//prebid-server.rubiconproject.com/openrtb2/auction';
export const SYNC_ENDPOINT = 'https://eus.rubiconproject.com/usync.html';
-const TIMEOUT_BUFFER = 500;
-
var sizeMap = {
1: '468x60',
2: '728x90',
@@ -87,7 +85,6 @@ utils._each(sizeMap, (item, key) => sizeMap[item] = key);
export const spec = {
code: 'rubicon',
- aliases: ['rubiconLite'],
supportedMediaTypes: [BANNER, VIDEO],
/**
* @param {object} bid
@@ -97,15 +94,28 @@ export const spec = {
if (typeof bid.params !== 'object') {
return false;
}
- if (!/^\d+$/.test(bid.params.accountId)) {
+ // validate account, site, zone have numeric values
+ for (let i = 0, props = ['accountId', 'siteId', 'zoneId']; i < props.length; i++) {
+ bid.params[props[i]] = parseInt(bid.params[props[i]])
+ if (isNaN(bid.params[props[i]])) {
+ utils.logError('Rubicon bid adapter Error: wrong format of accountId or siteId or zoneId.')
+ return false
+ }
+ }
+ let bidFormat = bidType(bid, true);
+ // bidType is undefined? Return false
+ if (!bidFormat) {
return false;
+ } else if (bidFormat === 'video') { // bidType is video, make sure it has required params
+ return hasValidVideoParams(bid);
}
- return !!bidType(bid, true);
+ // bidType is banner? return true
+ return true;
},
/**
* @param {BidRequest[]} bidRequests
* @param bidderRequest
- * @return ServerRequest[]
+ * @return BidRequest[]
*/
buildRequests: function (bidRequests, bidderRequest) {
// separate video bids because the requests are structured differently
@@ -113,66 +123,82 @@ export const spec = {
const videoRequests = bidRequests.filter(bidRequest => bidType(bidRequest) === 'video').map(bidRequest => {
bidRequest.startTime = new Date().getTime();
- let params = bidRequest.params;
- let size = parseSizes(bidRequest, 'video');
-
- let data = {
- page_url: _getPageUrl(bidRequest, bidderRequest),
- resolution: _getScreenResolution(),
- account_id: params.accountId,
- integration: INTEGRATION,
- 'x_source.tid': bidRequest.transactionId,
- timeout: bidderRequest.timeout - (Date.now() - bidderRequest.auctionStart + TIMEOUT_BUFFER),
- stash_creatives: true,
- slots: []
- };
-
- // Define the slot object
- let slotData = {
- site_id: params.siteId,
- zone_id: params.zoneId,
- position: params.position === 'atf' || params.position === 'btf' ? params.position : 'unknown',
- floor: parseFloat(params.floor) > 0.01 ? params.floor : 0.01,
- element_id: bidRequest.adUnitCode,
- name: bidRequest.adUnitCode,
- width: size[0],
- height: size[1],
- size_id: determineRubiconVideoSizeId(bidRequest)
- };
-
- if (params.video) {
- data.ae_pass_through_parameters = params.video.aeParams;
- slotData.language = params.video.language;
- }
-
- // Add visitor and inventory FPD values
- // Frank expects the vales in each inventory and visitor fpd to be an array. so params.inventory.something === [] of some sort, otherwise it 400s
- ['inventory', 'visitor'].forEach(function(key) {
- if (params[key] && typeof params[key] === 'object') {
- slotData[key] = {};
- Object.keys(params[key]).forEach(function(fpdKey) {
- let value = params[key][fpdKey];
- if (Array.isArray(value)) {
- slotData[key][fpdKey] = value;
- } else if ((typeof value === 'string' && value !== '') || typeof value === 'number') {
- slotData[key][fpdKey] = [value];
+ const data = {
+ id: bidRequest.transactionId,
+ test: config.getConfig('debug') ? 1 : 0,
+ cur: ['USD'],
+ source: {
+ tid: bidRequest.transactionId
+ },
+ tmax: config.getConfig('TTL') || 1000,
+ imp: [{
+ exp: 300,
+ id: bidRequest.adUnitCode,
+ secure: isSecure() || bidRequest.params.secure ? 1 : 0,
+ ext: {
+ rubicon: bidRequest.params
+ },
+ video: utils.deepAccess(bidRequest, 'mediaTypes.video') || {}
+ }],
+ ext: {
+ prebid: {
+ cache: {
+ vastxml: {
+ returnCreative: false // don't return the VAST
+ }
+ },
+ targeting: {
+ includewinners: true,
+ // includebidderkeys always false for openrtb
+ includebidderkeys: false,
+ priceGranularity: getPriceGranularity(config)
}
- });
+ }
}
- });
-
- if (params.keywords && Array.isArray(params.keywords)) {
- slotData.keywords = params.keywords;
}
+ // if value is set, will overwrite with same value
+ data.imp[0].ext.rubicon.video.size_id = determineRubiconVideoSizeId(bidRequest)
- data.slots.push(slotData);
+ appendSiteAppDevice(data, bidRequest, bidderRequest);
+
+ addVideoParameters(data, bidRequest);
+
+ const digiTrust = getDigiTrustQueryParams();
+ if (digiTrust) {
+ data.user = {
+ ext: {
+ digitrust: digiTrust
+ }
+ };
+ }
if (bidderRequest.gdprConsent) {
- // add 'gdpr' only if 'gdprApplies' is defined
+ // note - gdprApplies & consentString may be undefined in certain use-cases for consentManagement module
+ let gdprApplies;
if (typeof bidderRequest.gdprConsent.gdprApplies === 'boolean') {
- data.gdpr = Number(bidderRequest.gdprConsent.gdprApplies);
+ gdprApplies = bidderRequest.gdprConsent.gdprApplies ? 1 : 0;
+ }
+
+ if (data.regs) {
+ if (data.regs.ext) {
+ data.regs.ext.gdpr = gdprApplies;
+ } else {
+ data.regs.ext = {gdpr: gdprApplies};
+ }
+ } else {
+ data.regs = {ext: {gdpr: gdprApplies}};
+ }
+
+ const consentString = bidderRequest.gdprConsent.consentString;
+ if (data.user) {
+ if (data.user.ext) {
+ data.user.ext.consent = consentString;
+ } else {
+ data.user.ext = {consent: consentString};
+ }
+ } else {
+ data.user = {ext: {consent: consentString}};
}
- data.gdpr_consent = bidderRequest.gdprConsent.consentString;
}
return {
@@ -390,6 +416,72 @@ export const spec = {
return [];
}
+ // video response from PBS Java openRTB
+ if (responseObj.seatbid) {
+ const responseErrors = utils.deepAccess(responseObj, 'ext.errors.rubicon');
+ if (Array.isArray(responseErrors) && responseErrors.length > 0) {
+ responseErrors.forEach(error => {
+ utils.logError('Got error from PBS Java openRTB: ' + error);
+ });
+ }
+ const bids = [];
+ responseObj.seatbid.forEach(seatbid => {
+ (seatbid.bid || []).forEach(bid => {
+ let bidObject = {
+ requestId: bidRequest.bidId,
+ currency: responseObj.cur || 'USD',
+ creativeId: bid.crid,
+ cpm: bid.price || 0,
+ bidderCode: seatbid.seat,
+ ttl: 300,
+ netRevenue: config.getConfig('rubicon.netRevenue') || false,
+ width: bid.w || utils.deepAccess(bidRequest, 'mediaTypes.video.w') || utils.deepAccess(bidRequest, 'params.video.playerWidth'),
+ height: bid.h || utils.deepAccess(bidRequest, 'mediaTypes.video.h') || utils.deepAccess(bidRequest, 'params.video.playerHeight'),
+ };
+
+ if (bid.dealid) {
+ bidObject.dealId = bid.dealid;
+ }
+
+ let serverResponseTimeMs = utils.deepAccess(responseObj, 'ext.responsetimemillis.rubicon');
+ if (bidRequest && serverResponseTimeMs) {
+ bidRequest.serverResponseTimeMs = serverResponseTimeMs;
+ }
+
+ if (utils.deepAccess(bid, 'ext.prebid.type') === VIDEO) {
+ bidObject.mediaType = VIDEO;
+ const extPrebidTargeting = utils.deepAccess(bid, 'ext.prebid.targeting');
+
+ // If ext.prebid.targeting exists, add it as a property value named 'adserverTargeting'
+ if (extPrebidTargeting && typeof extPrebidTargeting === 'object') {
+ bidObject.adserverTargeting = extPrebidTargeting;
+ }
+
+ // try to get cache values from 'response.ext.prebid.cache'
+ // else try 'bid.ext.prebid.targeting' as fallback
+ if (bid.ext.prebid.cache && typeof bid.ext.prebid.cache.vastXml === 'object' && bid.ext.prebid.cache.vastXml.cacheId && bid.ext.prebid.cache.vastXml.url) {
+ bidObject.videoCacheKey = bid.ext.prebid.cache.vastXml.cacheId;
+ bidObject.vastUrl = bid.ext.prebid.cache.vastXml.url;
+ } else if (extPrebidTargeting && extPrebidTargeting.hb_uuid && extPrebidTargeting.hb_cache_host && extPrebidTargeting.hb_cache_path) {
+ bidObject.videoCacheKey = extPrebidTargeting.hb_uuid;
+ // build url using key and cache host
+ bidObject.vastUrl = `https://${extPrebidTargeting.hb_cache_host}${extPrebidTargeting.hb_cache_path}?uuid=${extPrebidTargeting.hb_uuid}`;
+ }
+
+ if (bid.adm) { bidObject.vastXml = bid.adm; }
+ if (bid.nurl) { bidObject.vastUrl = bid.nurl; }
+ if (!bidObject.vastUrl && bid.nurl) { bidObject.vastUrl = bid.nurl; }
+ } else {
+ utils.logError('Prebid Server Java openRTB returns response with media type other than video for video request.');
+ }
+
+ bids.push(bidObject);
+ });
+ });
+
+ return bids;
+ }
+
let ads = responseObj.ads;
// video ads array is wrapped in an object
@@ -516,6 +608,7 @@ function _getDigiTrustQueryParams() {
/**
* @param {BidRequest} bidRequest
+ * @param bidderRequest
* @returns {string}
*/
function _getPageUrl(bidRequest, bidderRequest) {
@@ -572,6 +665,77 @@ function parseSizes(bid, mediaType) {
return masSizeOrdering(sizes);
}
+function getDigiTrustQueryParams() {
+ function getDigiTrustId() {
+ let digiTrustUser = window.DigiTrust && (config.getConfig('digiTrustId') || window.DigiTrust.getUser({member: 'T9QSFKPDN9'}));
+ return (digiTrustUser && digiTrustUser.success && digiTrustUser.identity) || null;
+ }
+
+ let digiTrustId = getDigiTrustId();
+ // Verify there is an ID and this user has not opted out
+ if (!digiTrustId || (digiTrustId.privacy && digiTrustId.privacy.optout)) {
+ return null;
+ }
+ return {
+ id: digiTrustId.id,
+ keyv: digiTrustId.keyv,
+ pref: 0
+ };
+}
+
+/**
+ * @param {Object} data
+ * @param bidRequest
+ * @param bidderRequest
+ */
+function appendSiteAppDevice(data, bidRequest, bidderRequest) {
+ if (!data) return;
+
+ // ORTB specifies app OR site
+ if (typeof config.getConfig('app') === 'object') {
+ data.app = config.getConfig('app');
+ } else {
+ data.site = {
+ page: _getPageUrl(bidRequest, bidderRequest)
+ }
+ }
+ if (typeof config.getConfig('device') === 'object') {
+ data.device = config.getConfig('device');
+ }
+ // Add language to site and device objects if there
+ if (bidRequest.params.video.language) {
+ ['site', 'device'].forEach(function(param) {
+ if (data[param]) {
+ data[param].content = Object.assign({language: bidRequest.params.video.language}, data[param].content)
+ }
+ });
+ }
+}
+
+/**
+ * @param {Object} data
+ * @param {BidRequest} bidRequest
+ */
+function addVideoParameters(data, bidRequest) {
+ if (typeof data.imp[0].video === 'object' && data.imp[0].video.skip === undefined) {
+ data.imp[0].video.skip = bidRequest.params.video.skip;
+ }
+ if (typeof data.imp[0].video === 'object' && data.imp[0].video.skipafter === undefined) {
+ data.imp[0].video.skipafter = bidRequest.params.video.skipdelay;
+ }
+ if (typeof data.imp[0].video === 'object' && data.imp[0].video.pos === undefined) {
+ data.imp[0].video.pos = bidRequest.params.position === 'atf' ? 1 : (bidRequest.params.position === 'btf' ? 3 : 0);
+ }
+
+ const size = parseSizes(bidRequest, 'video')
+ data.imp[0].video.w = size[0]
+ data.imp[0].video.h = size[1]
+}
+
+/**
+ * @param sizes
+ * @returns {*}
+ */
function mapSizes(sizes) {
return utils.parseSizesInput(sizes)
// map sizes while excluding non-matches
@@ -594,7 +758,7 @@ export function hasVideoMediaType(bidRequest) {
if (typeof utils.deepAccess(bidRequest, 'params.video') !== 'object') {
return false;
}
- return (bidRequest.mediaType === VIDEO || typeof utils.deepAccess(bidRequest, `mediaTypes.${VIDEO}`) !== 'undefined');
+ return (typeof utils.deepAccess(bidRequest, `mediaTypes.${VIDEO}`) !== 'undefined');
}
/**
@@ -606,27 +770,15 @@ export function hasVideoMediaType(bidRequest) {
function bidType(bid, log = false) {
// Is it considered video ad unit by rubicon
if (hasVideoMediaType(bid)) {
- // legacy mediaType or the new mediaTypes
- // this is the preffered "new" way to define mediaTypes
- if (typeof utils.deepAccess(bid, `mediaTypes.${VIDEO}`) !== 'undefined') {
- // We require either context as instream or outstream
- if (['outstream', 'instream'].indexOf(utils.deepAccess(bid, `mediaTypes.${VIDEO}.context`)) === -1) {
- if (log) {
- utils.logError('Rubicon bid adapter requires mediaTypes.video.context to be one of outstream or instream');
- }
- return;
- }
- } else { // Otherwise its the legacy way where mediaType == 'video'
+ // Removed legacy mediaType support. new way using mediaTypes.video object is now required
+ // We require either context as instream or outstream
+ if (['outstream', 'instream'].indexOf(utils.deepAccess(bid, `mediaTypes.${VIDEO}.context`)) === -1) {
if (log) {
- utils.logWarn('Rubicon video bid requested using legacy `adUnit.mediaType = `video``\nThis is deprecated\nPlease move towards the PBJS standard using mediaTypes object!');
- }
- if (isNaN(parseInt(utils.deepAccess(bid, 'params.video.size_id')))) {
- if (log) {
- utils.logError('Rubicon bid adapter needs params.video.size_id to be declared and an integer in order to process a legacy video request using mediaType == video');
- }
- return;
+ utils.logError('Rubicon bid adapter requires mediaTypes.video.context to be one of outstream or instream');
}
+ return;
}
+
// we require playerWidth and playerHeight to come from one of params.playerWidth/playerHeight or mediaTypes.video.playerSize or adUnit.sizes
if (parseSizes(bid, 'video').length < 2) {
if (log) {
@@ -691,6 +843,53 @@ export function determineRubiconVideoSizeId(bid) {
return utils.deepAccess(bid, `mediaTypes.${VIDEO}.context`) === 'outstream' ? 203 : 201;
}
+export function getPriceGranularity(config) {
+ const granularityMappings = {
+ low: [{max: 5.00, increment: 0.50}],
+ medium: [{max: 20.00, increment: 0.10}],
+ high: [{max: 20.00, increment: 0.01}],
+ auto: [
+ {max: 5.00, increment: 0.05},
+ {min: 5.00, max: 10.00, increment: 0.10},
+ {min: 10.00, max: 20.00, increment: 0.50}
+ ],
+ dense: [
+ {max: 3.00, increment: 0.01},
+ {min: 3.00, max: 8.00, increment: 0.05},
+ {min: 8.00, max: 20.00, increment: 0.50}
+ ]
+ }
+ if (config.getConfig('priceGranularity') === 'custom') {
+ return {ranges: config.getConfig('customPriceGranularity').buckets}
+ } else {
+ return {ranges: granularityMappings[config.getConfig('priceGranularity')]}
+ }
+}
+
+// Function to validate the required video params
+export function hasValidVideoParams(bid) {
+ let isValid = true;
+ // incase future javascript changes the string represenation of the array or number classes!
+ let arrayType = Object.prototype.toString.call([]);
+ let numberType = Object.prototype.toString.call(0);
+ // required params and their associated object type
+ var requiredParams = {
+ mimes: arrayType,
+ protocols: arrayType,
+ maxduration: numberType,
+ linearity: numberType,
+ api: arrayType
+ }
+ // loop through each param and verify it has the correct
+ Object.keys(requiredParams).forEach(function(param) {
+ if (Object.prototype.toString.call(utils.deepAccess(bid, 'mediaTypes.video.' + param)) !== requiredParams[param]) {
+ isValid = false;
+ utils.logError('Rubicon Bid Adapter: mediaTypes.video.' + param + ' is required and must be of type: ' + requiredParams[param]);
+ }
+ })
+ return isValid;
+}
+
var hasSynced = false;
export function resetUserSync() {
diff --git a/modules/sharethroughBidAdapter.js b/modules/sharethroughBidAdapter.js
index ee97ac739c4..ec97649df6d 100644
--- a/modules/sharethroughBidAdapter.js
+++ b/modules/sharethroughBidAdapter.js
@@ -12,12 +12,13 @@ export const sharethroughAdapterSpec = {
buildRequests: (bidRequests, bidderRequest) => {
return bidRequests.map(bid => {
let query = {
- bidId: bid.bidId,
placement_key: bid.params.pkey,
- hbVersion: '$prebid.version$',
- strVersion: VERSION,
+ bidId: bid.bidId,
+ consent_required: false,
+ instant_play_capable: canAutoPlayHTML5Video(),
hbSource: 'prebid',
- consent_required: false
+ hbVersion: '$prebid.version$',
+ strVersion: VERSION
};
if (bidderRequest && bidderRequest.gdprConsent && bidderRequest.gdprConsent.consentString) {
@@ -148,4 +149,25 @@ function b64EncodeUnicode(str) {
}));
}
+function canAutoPlayHTML5Video() {
+ const userAgent = navigator.userAgent;
+ if (!userAgent) return false;
+
+ const isAndroid = /Android/i.test(userAgent);
+ const isiOS = /iPhone|iPad|iPod/i.test(userAgent);
+ const chromeVersion = parseInt((/Chrome\/([0-9]+)/.exec(userAgent) || [0, 0])[1]);
+ const chromeiOSVersion = parseInt((/CriOS\/([0-9]+)/.exec(userAgent) || [0, 0])[1]);
+ const safariVersion = parseInt((/Version\/([0-9]+)/.exec(userAgent) || [0, 0])[1]);
+
+ if (
+ (isAndroid && chromeVersion >= 53) ||
+ (isiOS && (safariVersion >= 10 || chromeiOSVersion >= 53)) ||
+ !(isAndroid || isiOS)
+ ) {
+ return true;
+ } else {
+ return false;
+ }
+}
+
registerBidder(sharethroughAdapterSpec);
diff --git a/modules/unrulyBidAdapter.js b/modules/unrulyBidAdapter.js
index 8fb7e1913b3..5647d2cd6a3 100644
--- a/modules/unrulyBidAdapter.js
+++ b/modules/unrulyBidAdapter.js
@@ -29,7 +29,8 @@ const serverResponseToBid = (bid, rendererInstance) => ({
creativeId: bid.bidId,
ttl: 360,
currency: 'USD',
- renderer: rendererInstance
+ renderer: rendererInstance,
+ mediaType: VIDEO
});
const buildPrebidResponseAndInstallRenderer = bids =>
diff --git a/modules/yieldlabBidAdapter.js b/modules/yieldlabBidAdapter.js
index cb5535bb165..1bbb3f11a2e 100644
--- a/modules/yieldlabBidAdapter.js
+++ b/modules/yieldlabBidAdapter.js
@@ -77,6 +77,7 @@ export const spec = {
if (matchedBid) {
const primarysize = bidRequest.sizes.length === 2 && !utils.isArray(bidRequest.sizes[0]) ? bidRequest.sizes : bidRequest.sizes[0]
const customsize = bidRequest.params.adSize !== undefined ? parseSize(bidRequest.params.adSize) : primarysize
+ const extId = bidRequest.params.extId !== undefined ? '&id=' + bidRequest.params.extId : ''
const bidResponse = {
requestId: bidRequest.bidId,
cpm: matchedBid.price / 100,
@@ -88,11 +89,12 @@ export const spec = {
netRevenue: false,
ttl: BID_RESPONSE_TTL_SEC,
referrer: '',
- ad: ``
+ ad: ``
}
+
if (isVideo(bidRequest)) {
bidResponse.mediaType = VIDEO
- bidResponse.vastUrl = `${ENDPOINT}/d/${matchedBid.id}/${bidRequest.params.supplyId}/${customsize[0]}x${customsize[1]}?ts=${timestamp}`
+ bidResponse.vastUrl = `${ENDPOINT}/d/${matchedBid.id}/${bidRequest.params.supplyId}/${customsize[0]}x${customsize[1]}?ts=${timestamp}${extId}`
}
bidResponses.push(bidResponse)
diff --git a/modules/yieldlabBidAdapter.md b/modules/yieldlabBidAdapter.md
index 96b62f5cf8c..de93baf42ae 100644
--- a/modules/yieldlabBidAdapter.md
+++ b/modules/yieldlabBidAdapter.md
@@ -25,7 +25,8 @@ Module that connects to Yieldlab's demand sources
targeting: {
key1: "value1",
key2: "value2"
- }
+ },
+ extId: "abc"
}
}]
}, {
diff --git a/package-lock.json b/package-lock.json
index 64ec08e0ca3..54eddd62325 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -1,6 +1,6 @@
{
"name": "prebid.js",
- "version": "2.2.0",
+ "version": "2.4.0",
"lockfileVersion": 1,
"requires": true,
"dependencies": {
@@ -14,36 +14,36 @@
}
},
"@babel/core": {
- "version": "7.2.2",
- "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.2.2.tgz",
- "integrity": "sha512-59vB0RWt09cAct5EIe58+NzGP4TFSD3Bz//2/ELy3ZeTeKF6VTD1AXlH8BGGbCX0PuobZBsIzO7IAI9PH67eKw==",
+ "version": "7.3.4",
+ "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.3.4.tgz",
+ "integrity": "sha512-jRsuseXBo9pN197KnDwhhaaBzyZr2oIcLHHTt2oDdQrej5Qp57dCCJafWx5ivU8/alEYDpssYqv1MUqcxwQlrA==",
"dev": true,
"requires": {
"@babel/code-frame": "^7.0.0",
- "@babel/generator": "^7.2.2",
+ "@babel/generator": "^7.3.4",
"@babel/helpers": "^7.2.0",
- "@babel/parser": "^7.2.2",
+ "@babel/parser": "^7.3.4",
"@babel/template": "^7.2.2",
- "@babel/traverse": "^7.2.2",
- "@babel/types": "^7.2.2",
+ "@babel/traverse": "^7.3.4",
+ "@babel/types": "^7.3.4",
"convert-source-map": "^1.1.0",
"debug": "^4.1.0",
"json5": "^2.1.0",
- "lodash": "^4.17.10",
+ "lodash": "^4.17.11",
"resolve": "^1.3.2",
"semver": "^5.4.1",
"source-map": "^0.5.0"
}
},
"@babel/generator": {
- "version": "7.3.2",
- "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.3.2.tgz",
- "integrity": "sha512-f3QCuPppXxtZOEm5GWPra/uYUjmNQlu9pbAD8D/9jze4pTY83rTtB1igTBSwvkeNlC5gR24zFFkz+2WHLFQhqQ==",
+ "version": "7.3.4",
+ "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.3.4.tgz",
+ "integrity": "sha512-8EXhHRFqlVVWXPezBW5keTiQi/rJMQTg/Y9uVCEZ0CAF3PKtCCaVRnp64Ii1ujhkoDhhF1fVsImoN4yJ2uz4Wg==",
"dev": true,
"requires": {
- "@babel/types": "^7.3.2",
+ "@babel/types": "^7.3.4",
"jsesc": "^2.5.1",
- "lodash": "^4.17.10",
+ "lodash": "^4.17.11",
"source-map": "^0.5.0",
"trim-right": "^1.0.1"
}
@@ -198,15 +198,15 @@
}
},
"@babel/helper-replace-supers": {
- "version": "7.2.3",
- "resolved": "https://registry.npmjs.org/@babel/helper-replace-supers/-/helper-replace-supers-7.2.3.tgz",
- "integrity": "sha512-GyieIznGUfPXPWu0yLS6U55Mz67AZD9cUk0BfirOWlPrXlBcan9Gz+vHGz+cPfuoweZSnPzPIm67VtQM0OWZbA==",
+ "version": "7.3.4",
+ "resolved": "https://registry.npmjs.org/@babel/helper-replace-supers/-/helper-replace-supers-7.3.4.tgz",
+ "integrity": "sha512-pvObL9WVf2ADs+ePg0jrqlhHoxRXlOa+SHRHzAXIz2xkYuOHfGl+fKxPMaS4Fq+uje8JQPobnertBBvyrWnQ1A==",
"dev": true,
"requires": {
"@babel/helper-member-expression-to-functions": "^7.0.0",
"@babel/helper-optimise-call-expression": "^7.0.0",
- "@babel/traverse": "^7.2.3",
- "@babel/types": "^7.0.0"
+ "@babel/traverse": "^7.3.4",
+ "@babel/types": "^7.3.4"
}
},
"@babel/helper-simple-access": {
@@ -263,9 +263,9 @@
}
},
"@babel/parser": {
- "version": "7.3.2",
- "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.3.2.tgz",
- "integrity": "sha512-QzNUC2RO1gadg+fs21fi0Uu0OuGNzRKEmgCxoLNzbCdoprLwjfmZwzUrpUNfJPaVRwBpDY47A17yYEGWyRelnQ==",
+ "version": "7.3.4",
+ "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.3.4.tgz",
+ "integrity": "sha512-tXZCqWtlOOP4wgCp6RjRvLmfuhnqTLy9VHwRochJBCP2nDm27JnnuFEnXFASVyQNHk36jD1tAammsCEEqgscIQ==",
"dev": true
},
"@babel/plugin-proposal-async-generator-functions": {
@@ -290,9 +290,9 @@
}
},
"@babel/plugin-proposal-object-rest-spread": {
- "version": "7.3.2",
- "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-object-rest-spread/-/plugin-proposal-object-rest-spread-7.3.2.tgz",
- "integrity": "sha512-DjeMS+J2+lpANkYLLO+m6GjoTMygYglKmRe6cDTbFv3L9i6mmiE8fe6B8MtCSLZpVXscD5kn7s6SgtHrDoBWoA==",
+ "version": "7.3.4",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-object-rest-spread/-/plugin-proposal-object-rest-spread-7.3.4.tgz",
+ "integrity": "sha512-j7VQmbbkA+qrzNqbKHrBsW3ddFnOeva6wzSe/zB7T+xaxGc+RCpwo44wCmRixAIGRoIpmVgvzFzNJqQcO3/9RA==",
"dev": true,
"requires": {
"@babel/helper-plugin-utils": "^7.0.0",
@@ -366,9 +366,9 @@
}
},
"@babel/plugin-transform-async-to-generator": {
- "version": "7.2.0",
- "resolved": "https://registry.npmjs.org/@babel/plugin-transform-async-to-generator/-/plugin-transform-async-to-generator-7.2.0.tgz",
- "integrity": "sha512-CEHzg4g5UraReozI9D4fblBYABs7IM6UerAVG7EJVrTLC5keh00aEuLUT+O40+mJCEzaXkYfTCUKIyeDfMOFFQ==",
+ "version": "7.3.4",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-transform-async-to-generator/-/plugin-transform-async-to-generator-7.3.4.tgz",
+ "integrity": "sha512-Y7nCzv2fw/jEZ9f678MuKdMo99MFDJMT/PvD9LisrR5JDFcJH6vYeH6RnjVt3p5tceyGRvTtEN0VOlU+rgHZjA==",
"dev": true,
"requires": {
"@babel/helper-module-imports": "^7.0.0",
@@ -386,19 +386,19 @@
}
},
"@babel/plugin-transform-block-scoping": {
- "version": "7.2.0",
- "resolved": "https://registry.npmjs.org/@babel/plugin-transform-block-scoping/-/plugin-transform-block-scoping-7.2.0.tgz",
- "integrity": "sha512-vDTgf19ZEV6mx35yiPJe4fS02mPQUUcBNwWQSZFXSzTSbsJFQvHt7DqyS3LK8oOWALFOsJ+8bbqBgkirZteD5Q==",
+ "version": "7.3.4",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-transform-block-scoping/-/plugin-transform-block-scoping-7.3.4.tgz",
+ "integrity": "sha512-blRr2O8IOZLAOJklXLV4WhcEzpYafYQKSGT3+R26lWG41u/FODJuBggehtOwilVAcFu393v3OFj+HmaE6tVjhA==",
"dev": true,
"requires": {
"@babel/helper-plugin-utils": "^7.0.0",
- "lodash": "^4.17.10"
+ "lodash": "^4.17.11"
}
},
"@babel/plugin-transform-classes": {
- "version": "7.2.2",
- "resolved": "https://registry.npmjs.org/@babel/plugin-transform-classes/-/plugin-transform-classes-7.2.2.tgz",
- "integrity": "sha512-gEZvgTy1VtcDOaQty1l10T3jQmJKlNVxLDCs+3rCVPr6nMkODLELxViq5X9l+rfxbie3XrfrMCYYY6eX3aOcOQ==",
+ "version": "7.3.4",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-transform-classes/-/plugin-transform-classes-7.3.4.tgz",
+ "integrity": "sha512-J9fAvCFBkXEvBimgYxCjvaVDzL6thk0j0dBvCeZmIUDBwyt+nv6HfbImsSrWsYXfDNDivyANgJlFXDUWRTZBuA==",
"dev": true,
"requires": {
"@babel/helper-annotate-as-pure": "^7.0.0",
@@ -406,7 +406,7 @@
"@babel/helper-function-name": "^7.1.0",
"@babel/helper-optimise-call-expression": "^7.0.0",
"@babel/helper-plugin-utils": "^7.0.0",
- "@babel/helper-replace-supers": "^7.1.0",
+ "@babel/helper-replace-supers": "^7.3.4",
"@babel/helper-split-export-declaration": "^7.0.0",
"globals": "^11.1.0"
}
@@ -509,9 +509,9 @@
}
},
"@babel/plugin-transform-modules-systemjs": {
- "version": "7.2.0",
- "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-systemjs/-/plugin-transform-modules-systemjs-7.2.0.tgz",
- "integrity": "sha512-aYJwpAhoK9a+1+O625WIjvMY11wkB/ok0WClVwmeo3mCjcNRjt+/8gHWrB5i+00mUju0gWsBkQnPpdvQ7PImmQ==",
+ "version": "7.3.4",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-systemjs/-/plugin-transform-modules-systemjs-7.3.4.tgz",
+ "integrity": "sha512-VZ4+jlGOF36S7TjKs8g4ojp4MEI+ebCQZdswWb/T9I4X84j8OtFAyjXjt/M16iIm5RIZn0UMQgg/VgIwo/87vw==",
"dev": true,
"requires": {
"@babel/helper-hoist-variables": "^7.0.0",
@@ -557,9 +557,9 @@
}
},
"@babel/plugin-transform-parameters": {
- "version": "7.2.0",
- "resolved": "https://registry.npmjs.org/@babel/plugin-transform-parameters/-/plugin-transform-parameters-7.2.0.tgz",
- "integrity": "sha512-kB9+hhUidIgUoBQ0MsxMewhzr8i60nMa2KgeJKQWYrqQpqcBYtnpR+JgkadZVZoaEZ/eKu9mclFaVwhRpLNSzA==",
+ "version": "7.3.3",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-transform-parameters/-/plugin-transform-parameters-7.3.3.tgz",
+ "integrity": "sha512-IrIP25VvXWu/VlBWTpsjGptpomtIkYrN/3aDp4UKm7xK6UxZY88kcJ1UwETbzHAlwN21MnNfwlar0u8y3KpiXw==",
"dev": true,
"requires": {
"@babel/helper-call-delegate": "^7.1.0",
@@ -568,12 +568,12 @@
}
},
"@babel/plugin-transform-regenerator": {
- "version": "7.0.0",
- "resolved": "https://registry.npmjs.org/@babel/plugin-transform-regenerator/-/plugin-transform-regenerator-7.0.0.tgz",
- "integrity": "sha512-sj2qzsEx8KDVv1QuJc/dEfilkg3RRPvPYx/VnKLtItVQRWt1Wqf5eVCOLZm29CiGFfYYsA3VPjfizTCV0S0Dlw==",
+ "version": "7.3.4",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-transform-regenerator/-/plugin-transform-regenerator-7.3.4.tgz",
+ "integrity": "sha512-hvJg8EReQvXT6G9H2MvNPXkv9zK36Vxa1+csAVTpE1J3j0zlHplw76uudEbJxgvqZzAq9Yh45FLD4pk5mKRFQA==",
"dev": true,
"requires": {
- "regenerator-transform": "^0.13.3"
+ "regenerator-transform": "^0.13.4"
}
},
"@babel/plugin-transform-shorthand-properties": {
@@ -635,16 +635,16 @@
}
},
"@babel/preset-env": {
- "version": "7.3.1",
- "resolved": "https://registry.npmjs.org/@babel/preset-env/-/preset-env-7.3.1.tgz",
- "integrity": "sha512-FHKrD6Dxf30e8xgHQO0zJZpUPfVZg+Xwgz5/RdSWCbza9QLNk4Qbp40ctRoqDxml3O8RMzB1DU55SXeDG6PqHQ==",
+ "version": "7.3.4",
+ "resolved": "https://registry.npmjs.org/@babel/preset-env/-/preset-env-7.3.4.tgz",
+ "integrity": "sha512-2mwqfYMK8weA0g0uBKOt4FE3iEodiHy9/CW0b+nWXcbL+pGzLx8ESYc+j9IIxr6LTDHWKgPm71i9smo02bw+gA==",
"dev": true,
"requires": {
"@babel/helper-module-imports": "^7.0.0",
"@babel/helper-plugin-utils": "^7.0.0",
"@babel/plugin-proposal-async-generator-functions": "^7.2.0",
"@babel/plugin-proposal-json-strings": "^7.2.0",
- "@babel/plugin-proposal-object-rest-spread": "^7.3.1",
+ "@babel/plugin-proposal-object-rest-spread": "^7.3.4",
"@babel/plugin-proposal-optional-catch-binding": "^7.2.0",
"@babel/plugin-proposal-unicode-property-regex": "^7.2.0",
"@babel/plugin-syntax-async-generators": "^7.2.0",
@@ -652,10 +652,10 @@
"@babel/plugin-syntax-object-rest-spread": "^7.2.0",
"@babel/plugin-syntax-optional-catch-binding": "^7.2.0",
"@babel/plugin-transform-arrow-functions": "^7.2.0",
- "@babel/plugin-transform-async-to-generator": "^7.2.0",
+ "@babel/plugin-transform-async-to-generator": "^7.3.4",
"@babel/plugin-transform-block-scoped-functions": "^7.2.0",
- "@babel/plugin-transform-block-scoping": "^7.2.0",
- "@babel/plugin-transform-classes": "^7.2.0",
+ "@babel/plugin-transform-block-scoping": "^7.3.4",
+ "@babel/plugin-transform-classes": "^7.3.4",
"@babel/plugin-transform-computed-properties": "^7.2.0",
"@babel/plugin-transform-destructuring": "^7.2.0",
"@babel/plugin-transform-dotall-regex": "^7.2.0",
@@ -666,13 +666,13 @@
"@babel/plugin-transform-literals": "^7.2.0",
"@babel/plugin-transform-modules-amd": "^7.2.0",
"@babel/plugin-transform-modules-commonjs": "^7.2.0",
- "@babel/plugin-transform-modules-systemjs": "^7.2.0",
+ "@babel/plugin-transform-modules-systemjs": "^7.3.4",
"@babel/plugin-transform-modules-umd": "^7.2.0",
"@babel/plugin-transform-named-capturing-groups-regex": "^7.3.0",
"@babel/plugin-transform-new-target": "^7.0.0",
"@babel/plugin-transform-object-super": "^7.2.0",
"@babel/plugin-transform-parameters": "^7.2.0",
- "@babel/plugin-transform-regenerator": "^7.0.0",
+ "@babel/plugin-transform-regenerator": "^7.3.4",
"@babel/plugin-transform-shorthand-properties": "^7.2.0",
"@babel/plugin-transform-spread": "^7.2.0",
"@babel/plugin-transform-sticky-regex": "^7.2.0",
@@ -697,30 +697,30 @@
}
},
"@babel/traverse": {
- "version": "7.2.3",
- "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.2.3.tgz",
- "integrity": "sha512-Z31oUD/fJvEWVR0lNZtfgvVt512ForCTNKYcJBGbPb1QZfve4WGH8Wsy7+Mev33/45fhP/hwQtvgusNdcCMgSw==",
+ "version": "7.3.4",
+ "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.3.4.tgz",
+ "integrity": "sha512-TvTHKp6471OYEcE/91uWmhR6PrrYywQntCHSaZ8CM8Vmp+pjAusal4nGB2WCCQd0rvI7nOMKn9GnbcvTUz3/ZQ==",
"dev": true,
"requires": {
"@babel/code-frame": "^7.0.0",
- "@babel/generator": "^7.2.2",
+ "@babel/generator": "^7.3.4",
"@babel/helper-function-name": "^7.1.0",
"@babel/helper-split-export-declaration": "^7.0.0",
- "@babel/parser": "^7.2.3",
- "@babel/types": "^7.2.2",
+ "@babel/parser": "^7.3.4",
+ "@babel/types": "^7.3.4",
"debug": "^4.1.0",
"globals": "^11.1.0",
- "lodash": "^4.17.10"
+ "lodash": "^4.17.11"
}
},
"@babel/types": {
- "version": "7.3.2",
- "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.3.2.tgz",
- "integrity": "sha512-3Y6H8xlUlpbGR+XvawiH0UXehqydTmNmEpozWcXymqwcrwYAl5KMvKtQ+TF6f6E08V6Jur7v/ykdDSF+WDEIXQ==",
+ "version": "7.3.4",
+ "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.3.4.tgz",
+ "integrity": "sha512-WEkp8MsLftM7O/ty580wAmZzN1nDmCACc5+jFzUt+GUFNNIi3LdRlueYz0YIlmJhlZx1QYDMZL5vdWCL0fNjFQ==",
"dev": true,
"requires": {
"esutils": "^2.0.2",
- "lodash": "^4.17.10",
+ "lodash": "^4.17.11",
"to-fast-properties": "^2.0.0"
}
},
@@ -800,16 +800,22 @@
}
},
"@sinonjs/samsam": {
- "version": "3.1.0",
- "resolved": "https://registry.npmjs.org/@sinonjs/samsam/-/samsam-3.1.0.tgz",
- "integrity": "sha512-IXio+GWY+Q8XUjHUOgK7wx8fpvr7IFffgyXb1bnJFfX3001KmHt35Zq4tp7MXZyjJPCLPuadesDYNk41LYtVjw==",
+ "version": "3.2.0",
+ "resolved": "https://registry.npmjs.org/@sinonjs/samsam/-/samsam-3.2.0.tgz",
+ "integrity": "sha512-j5F1rScewLtx6pbTK0UAjA3jJj4RYiSKOix53YWv+Jzy/AZ69qHxUpU8fwVLjyKbEEud9QrLpv6Ggs7WqTimYw==",
"dev": true,
"requires": {
"@sinonjs/commons": "^1.0.2",
"array-from": "^2.1.1",
- "lodash.get": "^4.4.2"
+ "lodash": "^4.17.11"
}
},
+ "@sinonjs/text-encoding": {
+ "version": "0.7.1",
+ "resolved": "https://registry.npmjs.org/@sinonjs/text-encoding/-/text-encoding-0.7.1.tgz",
+ "integrity": "sha512-+iTbntw2IZPb/anVDbypzfQa+ay64MW0Zo8aJ8gZPWMMK6/OubMVb6lUPMagqjOPnmtauXnFCACVl3O7ogjeqQ==",
+ "dev": true
+ },
"@types/node": {
"version": "8.10.40",
"resolved": "https://registry.npmjs.org/@types/node/-/node-8.10.40.tgz",
@@ -974,9 +980,9 @@
"dev": true
},
"ansi-regex": {
- "version": "3.0.0",
- "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-3.0.0.tgz",
- "integrity": "sha1-7QMXwyIGT3lGbAKWa922Bas32Zg=",
+ "version": "2.1.1",
+ "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz",
+ "integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8=",
"dev": true
},
"ansi-styles": {
@@ -1338,12 +1344,6 @@
"js-tokens": "^3.0.2"
},
"dependencies": {
- "ansi-regex": {
- "version": "2.1.1",
- "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz",
- "integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8=",
- "dev": true
- },
"ansi-styles": {
"version": "2.2.1",
"resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-2.2.1.tgz",
@@ -1369,15 +1369,6 @@
"integrity": "sha1-mGbfOVECEw449/mWvOtlRDIJwls=",
"dev": true
},
- "strip-ansi": {
- "version": "3.0.1",
- "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz",
- "integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=",
- "dev": true,
- "requires": {
- "ansi-regex": "^2.0.0"
- }
- },
"supports-color": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/supports-color/-/supports-color-2.0.0.tgz",
@@ -2600,9 +2591,9 @@
}
},
"big-integer": {
- "version": "1.6.41",
- "resolved": "https://registry.npmjs.org/big-integer/-/big-integer-1.6.41.tgz",
- "integrity": "sha512-d5AT9lMTYJ/ZE/4gzxb+5ttPcRWljVsvv7lF1w9KzkPhVUhBtHrjDo1J8swfZKepfLsliDhYa31zRYwcD0Yg9w==",
+ "version": "1.6.42",
+ "resolved": "https://registry.npmjs.org/big-integer/-/big-integer-1.6.42.tgz",
+ "integrity": "sha512-3UQFKcRMx+5Z+IK5vYTMYK2jzLRJkt+XqyDdacgWgtMjjuifKpKTFneJLEgeBElOE2/lXZ1LcMcb5s8pwG2U8Q==",
"dev": true
},
"big.js": {
@@ -2865,14 +2856,14 @@
}
},
"browserslist": {
- "version": "4.4.1",
- "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.4.1.tgz",
- "integrity": "sha512-pEBxEXg7JwaakBXjATYw/D1YZh4QUSCX/Mnd/wnqSRPPSi1U39iDhDoKGoBUcraKdxDlrYqJxSI5nNvD+dWP2A==",
+ "version": "4.4.2",
+ "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.4.2.tgz",
+ "integrity": "sha512-ISS/AIAiHERJ3d45Fz0AVYKkgcy+F/eJHzKEvv1j0wwKGKD9T3BrwKr/5g45L+Y4XIK5PlTqefHciRFcfE1Jxg==",
"dev": true,
"requires": {
- "caniuse-lite": "^1.0.30000929",
- "electron-to-chromium": "^1.3.103",
- "node-releases": "^1.1.3"
+ "caniuse-lite": "^1.0.30000939",
+ "electron-to-chromium": "^1.3.113",
+ "node-releases": "^1.1.8"
}
},
"browserstack": {
@@ -3007,12 +2998,6 @@
"responselike": "1.0.2"
},
"dependencies": {
- "get-stream": {
- "version": "3.0.0",
- "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-3.0.0.tgz",
- "integrity": "sha1-jpQ9E1jcN1VQVOy+LtsFqhdO3hQ=",
- "dev": true
- },
"lowercase-keys": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/lowercase-keys/-/lowercase-keys-1.0.0.tgz",
@@ -3080,9 +3065,9 @@
"dev": true
},
"camelcase": {
- "version": "5.0.0",
- "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.0.0.tgz",
- "integrity": "sha512-faqwZqnWxbxn+F1d399ygeamQNy3lPp/H9H6rNrqYh4FSVCtcY+3cub1MxA8o9mDd55mM8Aghuu/kuyYA6VTsA==",
+ "version": "4.1.0",
+ "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-4.1.0.tgz",
+ "integrity": "sha1-1UVjW+HjPFQmScaRc+Xeas+uNN0=",
"dev": true
},
"camelcase-keys": {
@@ -3104,9 +3089,9 @@
}
},
"caniuse-lite": {
- "version": "1.0.30000936",
- "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30000936.tgz",
- "integrity": "sha512-orX4IdpbFhdNO7bTBhSbahp1EBpqzBc+qrvTRVUFfZgA4zta7TdM6PN5ZxkEUgDnz36m+PfWGcdX7AVfFWItJw==",
+ "version": "1.0.30000939",
+ "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30000939.tgz",
+ "integrity": "sha512-oXB23ImDJOgQpGjRv1tCtzAvJr4/OvrHi5SO2vUgB0g0xpdZZoA/BxfImiWfdwoYdUTtQrPsXsvYU/dmCSM8gg==",
"dev": true
},
"caseless": {
@@ -3235,9 +3220,9 @@
"dev": true
},
"chokidar": {
- "version": "2.1.1",
- "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-2.1.1.tgz",
- "integrity": "sha512-gfw3p2oQV2wEt+8VuMlNsPjCxDxvvgnm/kz+uATu805mWVF8IJN7uz9DN7iBz+RMJISmiVbCOBFs9qBGMjtPfQ==",
+ "version": "2.1.2",
+ "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-2.1.2.tgz",
+ "integrity": "sha512-IwXUx0FXc5ibYmPC2XeEj5mpXoV66sR+t3jqu2NS2GYwCktt3KF1/Qqjws/NkegajBA4RbZ5+DDwlOiJsxDHEg==",
"dev": true,
"requires": {
"anymatch": "^2.0.0",
@@ -3302,17 +3287,6 @@
"restore-cursor": "^2.0.0"
}
},
- "cli-table3": {
- "version": "0.5.1",
- "resolved": "https://registry.npmjs.org/cli-table3/-/cli-table3-0.5.1.tgz",
- "integrity": "sha512-7Qg2Jrep1S/+Q3EceiZtQcDPWxhAvBw+ERf1162v4sikJrvojMHFqXt8QIVha8UlH9rgU0BeWPytZ9/TzYqlUw==",
- "dev": true,
- "requires": {
- "colors": "^1.1.2",
- "object-assign": "^4.1.0",
- "string-width": "^2.1.1"
- }
- },
"cli-width": {
"version": "2.2.0",
"resolved": "https://registry.npmjs.org/cli-width/-/cli-width-2.2.0.tgz",
@@ -3320,13 +3294,13 @@
"dev": true
},
"cliui": {
- "version": "4.1.0",
- "resolved": "https://registry.npmjs.org/cliui/-/cliui-4.1.0.tgz",
- "integrity": "sha512-4FG+RSG9DL7uEwRUZXZn3SS34DiDPfzP0VOiEwtUWlE+AR2EIg+hSyvrIgUUfhdgR/UkAeW2QHgeP+hWrXs7jQ==",
+ "version": "3.2.0",
+ "resolved": "https://registry.npmjs.org/cliui/-/cliui-3.2.0.tgz",
+ "integrity": "sha1-EgYBU3qRbSmUD5NNo7SNWFo5IT0=",
"dev": true,
"requires": {
- "string-width": "^2.1.1",
- "strip-ansi": "^4.0.0",
+ "string-width": "^1.0.1",
+ "strip-ansi": "^3.0.1",
"wrap-ansi": "^2.0.0"
}
},
@@ -3626,9 +3600,9 @@
}
},
"core-js": {
- "version": "2.6.4",
- "resolved": "https://registry.npmjs.org/core-js/-/core-js-2.6.4.tgz",
- "integrity": "sha512-05qQ5hXShcqGkPZpXEFLIpxayZscVD2kuMBZewxiIPPEagukO4mqgPA9CWhUvFBJfy3ODdK2p9xyHh7FTU9/7A=="
+ "version": "2.6.5",
+ "resolved": "https://registry.npmjs.org/core-js/-/core-js-2.6.5.tgz",
+ "integrity": "sha512-klh/kDpwX8hryYL14M9w/xei6vrv6sE8gTHDG7/T/+SEovB/G4ejwcfE/CBzO6Edsu+OETZMZ3wcX/EjUkrl5A=="
},
"core-util-is": {
"version": "1.0.2",
@@ -3637,9 +3611,9 @@
"dev": true
},
"coveralls": {
- "version": "3.0.2",
- "resolved": "https://registry.npmjs.org/coveralls/-/coveralls-3.0.2.tgz",
- "integrity": "sha512-Tv0LKe/MkBOilH2v7WBiTBdudg2ChfGbdXafc/s330djpF3zKOmuehTeRwjXWc7pzfj9FrDUTA7tEx6Div8NFw==",
+ "version": "3.0.3",
+ "resolved": "https://registry.npmjs.org/coveralls/-/coveralls-3.0.3.tgz",
+ "integrity": "sha512-viNfeGlda2zJr8Gj1zqXpDMRjw9uM54p7wzZdvLRyOgnAfCe974Dq4veZkjJdxQXbmdppu6flEajFYseHYaUhg==",
"dev": true,
"requires": {
"growl": "~> 1.10.0",
@@ -3647,7 +3621,7 @@
"lcov-parse": "^0.0.10",
"log-driver": "^1.2.7",
"minimist": "^1.2.0",
- "request": "^2.85.0"
+ "request": "^2.86.0"
}
},
"create-ecdh": {
@@ -3688,14 +3662,12 @@
}
},
"cross-spawn": {
- "version": "6.0.5",
- "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-6.0.5.tgz",
- "integrity": "sha512-eTVLrBSt7fjbDygz805pMnstIs2VTBNkRm0qxZd+M7A5XDdxVRWO5MxGBXZhjY4cqLYLdtrGqRf8mBPmzwSpWQ==",
+ "version": "5.1.0",
+ "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-5.1.0.tgz",
+ "integrity": "sha1-6L0O/uWPz/b4+UUQoKVUu/ojVEk=",
"dev": true,
"requires": {
- "nice-try": "^1.0.4",
- "path-key": "^2.0.1",
- "semver": "^5.5.0",
+ "lru-cache": "^4.0.1",
"shebang-command": "^1.2.0",
"which": "^1.2.9"
}
@@ -4180,67 +4152,11 @@
},
"dependencies": {
"ansi-regex": {
- "version": "2.1.1",
- "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz",
- "integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8=",
- "dev": true
- },
- "camelcase": {
- "version": "4.1.0",
- "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-4.1.0.tgz",
- "integrity": "sha1-1UVjW+HjPFQmScaRc+Xeas+uNN0=",
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-3.0.0.tgz",
+ "integrity": "sha1-7QMXwyIGT3lGbAKWa922Bas32Zg=",
"dev": true
},
- "cliui": {
- "version": "3.2.0",
- "resolved": "https://registry.npmjs.org/cliui/-/cliui-3.2.0.tgz",
- "integrity": "sha1-EgYBU3qRbSmUD5NNo7SNWFo5IT0=",
- "dev": true,
- "requires": {
- "string-width": "^1.0.1",
- "strip-ansi": "^3.0.1",
- "wrap-ansi": "^2.0.0"
- },
- "dependencies": {
- "string-width": {
- "version": "1.0.2",
- "resolved": "https://registry.npmjs.org/string-width/-/string-width-1.0.2.tgz",
- "integrity": "sha1-EYvfW4zcUaKn5w0hHgfisLmxB9M=",
- "dev": true,
- "requires": {
- "code-point-at": "^1.0.0",
- "is-fullwidth-code-point": "^1.0.0",
- "strip-ansi": "^3.0.0"
- }
- }
- }
- },
- "cross-spawn": {
- "version": "5.1.0",
- "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-5.1.0.tgz",
- "integrity": "sha1-6L0O/uWPz/b4+UUQoKVUu/ojVEk=",
- "dev": true,
- "requires": {
- "lru-cache": "^4.0.1",
- "shebang-command": "^1.2.0",
- "which": "^1.2.9"
- }
- },
- "execa": {
- "version": "0.7.0",
- "resolved": "https://registry.npmjs.org/execa/-/execa-0.7.0.tgz",
- "integrity": "sha1-lEvs00zEHuMqY6n68nrVpl/Fl3c=",
- "dev": true,
- "requires": {
- "cross-spawn": "^5.0.1",
- "get-stream": "^3.0.0",
- "is-stream": "^1.1.0",
- "npm-run-path": "^2.0.0",
- "p-finally": "^1.0.0",
- "signal-exit": "^3.0.0",
- "strip-eof": "^1.0.0"
- }
- },
"find-up": {
"version": "2.1.0",
"resolved": "https://registry.npmjs.org/find-up/-/find-up-2.1.0.tgz",
@@ -4250,35 +4166,11 @@
"locate-path": "^2.0.0"
}
},
- "get-stream": {
- "version": "3.0.0",
- "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-3.0.0.tgz",
- "integrity": "sha1-jpQ9E1jcN1VQVOy+LtsFqhdO3hQ=",
- "dev": true
- },
- "invert-kv": {
- "version": "1.0.0",
- "resolved": "https://registry.npmjs.org/invert-kv/-/invert-kv-1.0.0.tgz",
- "integrity": "sha1-EEqOSqym09jNFXqO+L+rLXo//bY=",
- "dev": true
- },
"is-fullwidth-code-point": {
- "version": "1.0.0",
- "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-1.0.0.tgz",
- "integrity": "sha1-754xOG8DGn8NZDr4L95QxFfvAMs=",
- "dev": true,
- "requires": {
- "number-is-nan": "^1.0.0"
- }
- },
- "lcid": {
- "version": "1.0.0",
- "resolved": "https://registry.npmjs.org/lcid/-/lcid-1.0.0.tgz",
- "integrity": "sha1-MIrMr6C8SDo4Z7S28rlQYlHRuDU=",
- "dev": true,
- "requires": {
- "invert-kv": "^1.0.0"
- }
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz",
+ "integrity": "sha1-o7MKXE8ZkYMWeqq5O+764937ZU8=",
+ "dev": true
},
"load-json-file": {
"version": "2.0.0",
@@ -4310,26 +4202,6 @@
"path-exists": "^3.0.0"
}
},
- "mem": {
- "version": "1.1.0",
- "resolved": "https://registry.npmjs.org/mem/-/mem-1.1.0.tgz",
- "integrity": "sha1-Xt1StIXKHZAP5kiVUFOZoN+kX3Y=",
- "dev": true,
- "requires": {
- "mimic-fn": "^1.0.0"
- }
- },
- "os-locale": {
- "version": "2.1.0",
- "resolved": "https://registry.npmjs.org/os-locale/-/os-locale-2.1.0.tgz",
- "integrity": "sha512-3sslG3zJbEYcaC4YVAvDorjGxc7tv6KVATnLPZONiljsUncvihe9BQoVCEs0RZ1kmf4Hk9OBqlZfJZWI4GanKA==",
- "dev": true,
- "requires": {
- "execa": "^0.7.0",
- "lcid": "^1.0.0",
- "mem": "^1.1.0"
- }
- },
"p-limit": {
"version": "1.3.0",
"resolved": "https://registry.npmjs.org/p-limit/-/p-limit-1.3.0.tgz",
@@ -4391,20 +4263,24 @@
"path-type": "^2.0.0"
}
},
- "strip-ansi": {
- "version": "3.0.1",
- "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz",
- "integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=",
+ "string-width": {
+ "version": "2.1.1",
+ "resolved": "https://registry.npmjs.org/string-width/-/string-width-2.1.1.tgz",
+ "integrity": "sha512-nOqH59deCq9SRHlxq1Aw85Jnt4w6KvLKqWVik6oA9ZklXLNIOlqg4F2yrT1MVaTjAqvVwdfeZ7w7aCvJD7ugkw==",
"dev": true,
"requires": {
- "ansi-regex": "^2.0.0"
+ "is-fullwidth-code-point": "^2.0.0",
+ "strip-ansi": "^4.0.0"
}
},
- "y18n": {
- "version": "3.2.1",
- "resolved": "https://registry.npmjs.org/y18n/-/y18n-3.2.1.tgz",
- "integrity": "sha1-bRX7qITAhnnA136I53WegR4H+kE=",
- "dev": true
+ "strip-ansi": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-4.0.0.tgz",
+ "integrity": "sha1-qEeQIusaw2iocTibY1JixQXuNo8=",
+ "dev": true,
+ "requires": {
+ "ansi-regex": "^3.0.0"
+ }
},
"yargs": {
"version": "9.0.1",
@@ -4438,15 +4314,6 @@
}
}
}
- },
- "yargs-parser": {
- "version": "7.0.0",
- "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-7.0.0.tgz",
- "integrity": "sha1-jQrELxbqVd69MyyvTEA4s+P139k=",
- "dev": true,
- "requires": {
- "camelcase": "^4.1.0"
- }
}
}
},
@@ -4739,9 +4606,9 @@
}
},
"es5-ext": {
- "version": "0.10.47",
- "resolved": "https://registry.npmjs.org/es5-ext/-/es5-ext-0.10.47.tgz",
- "integrity": "sha512-/1TItLfj+TTfWoeRcDn/0FbGV6SNo4R+On2GGVucPU/j3BWnXE2Co8h8CTo4Tu34gFJtnmwS9xiScKs4EjZhdw==",
+ "version": "0.10.48",
+ "resolved": "https://registry.npmjs.org/es5-ext/-/es5-ext-0.10.48.tgz",
+ "integrity": "sha512-CdRvPlX/24Mj5L4NVxTs4804sxiS2CjVprgCmrgoDkdmjdY4D+ySHa7K3jJf8R40dFg0tIm3z/dk326LrnuSGw==",
"dev": true,
"requires": {
"es6-iterator": "~2.0.3",
@@ -4781,9 +4648,9 @@
}
},
"es6-promise": {
- "version": "4.2.5",
- "resolved": "https://registry.npmjs.org/es6-promise/-/es6-promise-4.2.5.tgz",
- "integrity": "sha512-n6wvpdE43VFtJq+lUDYDBFUwV8TZbuGXLV4D6wKafg13ldznKsyEvatubnmUe31zcvelSzOHF+XbaT+Bl9ObDg==",
+ "version": "4.2.6",
+ "resolved": "https://registry.npmjs.org/es6-promise/-/es6-promise-4.2.6.tgz",
+ "integrity": "sha512-aRVgGdnmW2OiySVPUC9e6m+plolMAJKjZnQlCwNSuK5yQ0JN61DZSO1X1Ufd1foqWRAlig0rhduTCHe7sVtK5Q==",
"dev": true
},
"es6-promisify": {
@@ -4937,16 +4804,11 @@
"text-table": "~0.2.0"
},
"dependencies": {
- "cross-spawn": {
- "version": "5.1.0",
- "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-5.1.0.tgz",
- "integrity": "sha1-6L0O/uWPz/b4+UUQoKVUu/ojVEk=",
- "dev": true,
- "requires": {
- "lru-cache": "^4.0.1",
- "shebang-command": "^1.2.0",
- "which": "^1.2.9"
- }
+ "ansi-regex": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-3.0.0.tgz",
+ "integrity": "sha1-7QMXwyIGT3lGbAKWa922Bas32Zg=",
+ "dev": true
},
"debug": {
"version": "3.2.6",
@@ -4965,6 +4827,15 @@
"requires": {
"esutils": "^2.0.2"
}
+ },
+ "strip-ansi": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-4.0.0.tgz",
+ "integrity": "sha1-qEeQIusaw2iocTibY1JixQXuNo8=",
+ "dev": true,
+ "requires": {
+ "ansi-regex": "^3.0.0"
+ }
}
}
},
@@ -5377,13 +5248,13 @@
}
},
"execa": {
- "version": "1.0.0",
- "resolved": "https://registry.npmjs.org/execa/-/execa-1.0.0.tgz",
- "integrity": "sha512-adbxcyWV46qiHyvSp50TKt05tB4tK3HcmF7/nxfAdhnox83seTDbwnaqKO4sXRy7roHAIFqJP/Rw/AuEbX61LA==",
+ "version": "0.7.0",
+ "resolved": "https://registry.npmjs.org/execa/-/execa-0.7.0.tgz",
+ "integrity": "sha1-lEvs00zEHuMqY6n68nrVpl/Fl3c=",
"dev": true,
"requires": {
- "cross-spawn": "^6.0.0",
- "get-stream": "^4.0.0",
+ "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",
@@ -5860,28 +5731,22 @@
}
},
"follow-redirects": {
- "version": "1.6.1",
- "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.6.1.tgz",
- "integrity": "sha512-t2JCjbzxQpWvbhts3l6SH1DKzSrx8a+SsaVf4h6bG4kOXUuPYS/kg2Lr4gQSb7eemaHqJkOThF1BGyjlUkO1GQ==",
+ "version": "1.7.0",
+ "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.7.0.tgz",
+ "integrity": "sha512-m/pZQy4Gj287eNy94nivy5wchN3Kp+Q5WgUPNy5lJSZ3sgkVKSYV/ZChMAQVIgx1SqfZ2zBZtPA2YlXIWxxJOQ==",
"dev": true,
"requires": {
- "debug": "=3.1.0"
+ "debug": "^3.2.6"
},
"dependencies": {
"debug": {
- "version": "3.1.0",
- "resolved": "https://registry.npmjs.org/debug/-/debug-3.1.0.tgz",
- "integrity": "sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g==",
+ "version": "3.2.6",
+ "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.6.tgz",
+ "integrity": "sha512-mel+jf7nrtEl5Pn1Qx46zARXKDpBbvzezse7p7LqINmdoIk8PYP5SySaxEmYv6TZ0JyEKA1hsCId6DIhgITtWQ==",
"dev": true,
"requires": {
- "ms": "2.0.0"
+ "ms": "^2.1.1"
}
- },
- "ms": {
- "version": "2.0.0",
- "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
- "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=",
- "dev": true
}
}
},
@@ -6602,9 +6467,9 @@
}
},
"fun-hooks": {
- "version": "0.6.6",
- "resolved": "https://registry.npmjs.org/fun-hooks/-/fun-hooks-0.6.6.tgz",
- "integrity": "sha512-Q+UdtGIpteY7Wd6z4T9MZ4GlqHtrRY1gCk+XuLyRxqgLsCaPKOOBY7EKJRFlXm1oQoNIrg2b7W55dEN55O8FBA=="
+ "version": "0.8.1",
+ "resolved": "https://registry.npmjs.org/fun-hooks/-/fun-hooks-0.8.1.tgz",
+ "integrity": "sha512-qhyQAO6vhmzzwOJ2SvqeCvL2dqBCw3NeuIpNOfMPv2bucFYXLur9UbXTiUAbm7EE2TrdLgIKJZkO0DfwEY+KVQ=="
},
"function-bind": {
"version": "1.1.1",
@@ -6643,13 +6508,10 @@
"dev": true
},
"get-stream": {
- "version": "4.1.0",
- "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-4.1.0.tgz",
- "integrity": "sha512-GMat4EJ5161kIy2HevLlr4luNjBgvmj413KaQA7jt4V8B4RDsfpHk7WQ9GVqfYyyx8OS/L66Kox+rJRNklLK7w==",
- "dev": true,
- "requires": {
- "pump": "^3.0.0"
- }
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-3.0.0.tgz",
+ "integrity": "sha1-jpQ9E1jcN1VQVOy+LtsFqhdO3hQ=",
+ "dev": true
},
"get-uri": {
"version": "2.0.3",
@@ -6893,14 +6755,6 @@
"timed-out": "^4.0.1",
"url-parse-lax": "^3.0.0",
"url-to-options": "^1.0.1"
- },
- "dependencies": {
- "get-stream": {
- "version": "3.0.0",
- "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-3.0.0.tgz",
- "integrity": "sha1-jpQ9E1jcN1VQVOy+LtsFqhdO3hQ=",
- "dev": true
- }
}
},
"graceful-fs": {
@@ -6927,29 +6781,12 @@
"vinyl-fs": "^3.0.0"
},
"dependencies": {
- "ansi-regex": {
- "version": "2.1.1",
- "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz",
- "integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8=",
- "dev": true
- },
"camelcase": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/camelcase/-/camelcase-3.0.0.tgz",
"integrity": "sha1-MvxLn82vhF/N9+c7uXysImHwqwo=",
"dev": true
},
- "cliui": {
- "version": "3.2.0",
- "resolved": "https://registry.npmjs.org/cliui/-/cliui-3.2.0.tgz",
- "integrity": "sha1-EgYBU3qRbSmUD5NNo7SNWFo5IT0=",
- "dev": true,
- "requires": {
- "string-width": "^1.0.1",
- "strip-ansi": "^3.0.1",
- "wrap-ansi": "^2.0.0"
- }
- },
"find-up": {
"version": "1.1.2",
"resolved": "https://registry.npmjs.org/find-up/-/find-up-1.1.2.tgz",
@@ -6986,30 +6823,6 @@
"yargs": "^7.1.0"
}
},
- "invert-kv": {
- "version": "1.0.0",
- "resolved": "https://registry.npmjs.org/invert-kv/-/invert-kv-1.0.0.tgz",
- "integrity": "sha1-EEqOSqym09jNFXqO+L+rLXo//bY=",
- "dev": true
- },
- "is-fullwidth-code-point": {
- "version": "1.0.0",
- "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-1.0.0.tgz",
- "integrity": "sha1-754xOG8DGn8NZDr4L95QxFfvAMs=",
- "dev": true,
- "requires": {
- "number-is-nan": "^1.0.0"
- }
- },
- "lcid": {
- "version": "1.0.0",
- "resolved": "https://registry.npmjs.org/lcid/-/lcid-1.0.0.tgz",
- "integrity": "sha1-MIrMr6C8SDo4Z7S28rlQYlHRuDU=",
- "dev": true,
- "requires": {
- "invert-kv": "^1.0.0"
- }
- },
"load-json-file": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/load-json-file/-/load-json-file-1.1.0.tgz",
@@ -7088,26 +6901,6 @@
"read-pkg": "^1.0.0"
}
},
- "string-width": {
- "version": "1.0.2",
- "resolved": "https://registry.npmjs.org/string-width/-/string-width-1.0.2.tgz",
- "integrity": "sha1-EYvfW4zcUaKn5w0hHgfisLmxB9M=",
- "dev": true,
- "requires": {
- "code-point-at": "^1.0.0",
- "is-fullwidth-code-point": "^1.0.0",
- "strip-ansi": "^3.0.0"
- }
- },
- "strip-ansi": {
- "version": "3.0.1",
- "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz",
- "integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=",
- "dev": true,
- "requires": {
- "ansi-regex": "^2.0.0"
- }
- },
"strip-bom": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-2.0.0.tgz",
@@ -7123,12 +6916,6 @@
"integrity": "sha1-u6Y8qGGUiZT/MHc2CJ47lgJsKk8=",
"dev": true
},
- "y18n": {
- "version": "3.2.1",
- "resolved": "https://registry.npmjs.org/y18n/-/y18n-3.2.1.tgz",
- "integrity": "sha1-bRX7qITAhnnA136I53WegR4H+kE=",
- "dev": true
- },
"yargs": {
"version": "7.1.0",
"resolved": "https://registry.npmjs.org/yargs/-/yargs-7.1.0.tgz",
@@ -7582,9 +7369,9 @@
}
},
"gulp-sourcemaps": {
- "version": "2.6.4",
- "resolved": "https://registry.npmjs.org/gulp-sourcemaps/-/gulp-sourcemaps-2.6.4.tgz",
- "integrity": "sha1-y7IAhFCxvM5s0jv5gze+dRv24wo=",
+ "version": "2.6.5",
+ "resolved": "https://registry.npmjs.org/gulp-sourcemaps/-/gulp-sourcemaps-2.6.5.tgz",
+ "integrity": "sha512-SYLBRzPTew8T5Suh2U8jCSDKY+4NARua4aqjj8HOysBh2tSgT9u4jc1FYirAdPx1akUxxDeK++fqw6Jg0LkQRg==",
"dev": true,
"requires": {
"@gulp-sourcemaps/identity-map": "1.X",
@@ -7650,12 +7437,6 @@
"vinyl": "^0.5.0"
},
"dependencies": {
- "ansi-regex": {
- "version": "2.1.1",
- "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz",
- "integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8=",
- "dev": true
- },
"ansi-styles": {
"version": "2.2.1",
"resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-2.2.1.tgz",
@@ -7758,15 +7539,6 @@
"integrity": "sha1-KbvZIHinOfC8zitO5B6DeVNSKSQ=",
"dev": true
},
- "strip-ansi": {
- "version": "3.0.1",
- "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz",
- "integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=",
- "dev": true,
- "requires": {
- "ansi-regex": "^2.0.0"
- }
- },
"supports-color": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/supports-color/-/supports-color-2.0.0.tgz",
@@ -7808,12 +7580,12 @@
},
"dependencies": {
"async": {
- "version": "2.6.1",
- "resolved": "https://registry.npmjs.org/async/-/async-2.6.1.tgz",
- "integrity": "sha512-fNEiL2+AZt6AlAw/29Cr0UDe4sRAHCpEHh54WMz+Bb7QfNcFw4h3loofyJpLeQs4Yx7yuqu/2dLgM5hKOs6HlQ==",
+ "version": "2.6.2",
+ "resolved": "https://registry.npmjs.org/async/-/async-2.6.2.tgz",
+ "integrity": "sha512-H1qVYh1MYhEEFLsP97cVKqCGo7KfCyTt6uEWqsTBr9SO84oK9Uwbyd/yCW+6rKJLHksBNUVWZDAjfS+Ccx0Bbg==",
"dev": true,
"requires": {
- "lodash": "^4.17.10"
+ "lodash": "^4.17.11"
}
},
"source-map": {
@@ -7841,9 +7613,9 @@
},
"dependencies": {
"ajv": {
- "version": "6.9.1",
- "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.9.1.tgz",
- "integrity": "sha512-XDN92U311aINL77ieWHmqCcNlwjoP5cHXDxIxbf2MaPYuCXOHS7gHH8jktxeK5omgd52XbSTX6a4Piwd1pQmzA==",
+ "version": "6.9.2",
+ "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.9.2.tgz",
+ "integrity": "sha512-4UFy0/LgDo7Oa/+wOAlj44tp9K78u38E5/359eSrqEp1Z5PdVfimCcs7SluXMP755RUQu6d2b4AvF0R1C9RZjg==",
"dev": true,
"requires": {
"fast-deep-equal": "^2.0.1",
@@ -7882,14 +7654,6 @@
"dev": true,
"requires": {
"ansi-regex": "^2.0.0"
- },
- "dependencies": {
- "ansi-regex": {
- "version": "2.1.1",
- "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz",
- "integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8=",
- "dev": true
- }
}
},
"has-binary2": {
@@ -8050,9 +7814,9 @@
"dev": true
},
"highlight.js": {
- "version": "9.14.2",
- "resolved": "https://registry.npmjs.org/highlight.js/-/highlight.js-9.14.2.tgz",
- "integrity": "sha512-Nc6YNECYpxyJABGYJAyw7dBAYbXEuIzwzkqoJnwbc1nIpCiN+3ioYf0XrBnLiyyG0JLuJhpPtt2iTSbXiKLoyA==",
+ "version": "9.15.6",
+ "resolved": "https://registry.npmjs.org/highlight.js/-/highlight.js-9.15.6.tgz",
+ "integrity": "sha512-zozTAWM1D6sozHo8kqhfYgsac+B+q0PmsjXeyDrYIHHcBN0zTVT66+s2GW1GZv7DbyaROdLXKdabwS/WqPyIdQ==",
"dev": true
},
"hmac-drbg": {
@@ -8077,9 +7841,9 @@
}
},
"homedir-polyfill": {
- "version": "1.0.1",
- "resolved": "https://registry.npmjs.org/homedir-polyfill/-/homedir-polyfill-1.0.1.tgz",
- "integrity": "sha1-TCu8inWJmP7r9e1oWA921GdotLw=",
+ "version": "1.0.3",
+ "resolved": "https://registry.npmjs.org/homedir-polyfill/-/homedir-polyfill-1.0.3.tgz",
+ "integrity": "sha512-eSmmWE5bZTK2Nou4g0AI3zZ9rswp7GRKoKXS1BLUkvPviOqs4YTN1djQIqrXy9k5gEtdLPy86JjRwsNM9tnDcA==",
"dev": true,
"requires": {
"parse-passwd": "^1.0.0"
@@ -8289,32 +8053,57 @@
"string-width": "^2.1.0",
"strip-ansi": "^4.0.0",
"through": "^2.3.6"
- }
- },
- "interpret": {
- "version": "1.2.0",
- "resolved": "https://registry.npmjs.org/interpret/-/interpret-1.2.0.tgz",
- "integrity": "sha512-mT34yGKMNceBQUoVn7iCDKDntA7SC6gycMAWzGx1z/CMCTV7b2AAtXlo3nRyHZ1FelRkQbQjprHSYGwzLtkVbw==",
- "dev": true
- },
- "into-stream": {
- "version": "3.1.0",
- "resolved": "https://registry.npmjs.org/into-stream/-/into-stream-3.1.0.tgz",
- "integrity": "sha1-lvsKk2wSur1v8XUqF9BWFqvQlMY=",
- "dev": true,
- "requires": {
- "from2": "^2.1.1",
- "p-is-promise": "^1.1.0"
},
"dependencies": {
- "p-is-promise": {
- "version": "1.1.0",
- "resolved": "https://registry.npmjs.org/p-is-promise/-/p-is-promise-1.1.0.tgz",
- "integrity": "sha1-nJRWmJ6fZYgBewQ01WCXZ1w9oF4=",
+ "ansi-regex": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-3.0.0.tgz",
+ "integrity": "sha1-7QMXwyIGT3lGbAKWa922Bas32Zg=",
"dev": true
+ },
+ "is-fullwidth-code-point": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz",
+ "integrity": "sha1-o7MKXE8ZkYMWeqq5O+764937ZU8=",
+ "dev": true
+ },
+ "string-width": {
+ "version": "2.1.1",
+ "resolved": "https://registry.npmjs.org/string-width/-/string-width-2.1.1.tgz",
+ "integrity": "sha512-nOqH59deCq9SRHlxq1Aw85Jnt4w6KvLKqWVik6oA9ZklXLNIOlqg4F2yrT1MVaTjAqvVwdfeZ7w7aCvJD7ugkw==",
+ "dev": true,
+ "requires": {
+ "is-fullwidth-code-point": "^2.0.0",
+ "strip-ansi": "^4.0.0"
+ }
+ },
+ "strip-ansi": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-4.0.0.tgz",
+ "integrity": "sha1-qEeQIusaw2iocTibY1JixQXuNo8=",
+ "dev": true,
+ "requires": {
+ "ansi-regex": "^3.0.0"
+ }
}
}
},
+ "interpret": {
+ "version": "1.2.0",
+ "resolved": "https://registry.npmjs.org/interpret/-/interpret-1.2.0.tgz",
+ "integrity": "sha512-mT34yGKMNceBQUoVn7iCDKDntA7SC6gycMAWzGx1z/CMCTV7b2AAtXlo3nRyHZ1FelRkQbQjprHSYGwzLtkVbw==",
+ "dev": true
+ },
+ "into-stream": {
+ "version": "3.1.0",
+ "resolved": "https://registry.npmjs.org/into-stream/-/into-stream-3.1.0.tgz",
+ "integrity": "sha1-lvsKk2wSur1v8XUqF9BWFqvQlMY=",
+ "dev": true,
+ "requires": {
+ "from2": "^2.1.1",
+ "p-is-promise": "^1.1.0"
+ }
+ },
"invariant": {
"version": "2.2.4",
"resolved": "https://registry.npmjs.org/invariant/-/invariant-2.2.4.tgz",
@@ -8325,9 +8114,9 @@
}
},
"invert-kv": {
- "version": "2.0.0",
- "resolved": "https://registry.npmjs.org/invert-kv/-/invert-kv-2.0.0.tgz",
- "integrity": "sha512-wPVv/y/QQ/Uiirj/vh3oP+1Ww+AWehmi1g5fFWGPF6IpCBCDVrhgHRMvrLfdYcwDh3QJbGXDW4JAuzxElLSqKA==",
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/invert-kv/-/invert-kv-1.0.0.tgz",
+ "integrity": "sha1-EEqOSqym09jNFXqO+L+rLXo//bY=",
"dev": true
},
"ip": {
@@ -8509,10 +8298,13 @@
}
},
"is-fullwidth-code-point": {
- "version": "2.0.0",
- "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz",
- "integrity": "sha1-o7MKXE8ZkYMWeqq5O+764937ZU8=",
- "dev": true
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-1.0.0.tgz",
+ "integrity": "sha1-754xOG8DGn8NZDr4L95QxFfvAMs=",
+ "dev": true,
+ "requires": {
+ "number-is-nan": "^1.0.0"
+ }
},
"is-glob": {
"version": "4.0.0",
@@ -8816,12 +8608,12 @@
},
"dependencies": {
"async": {
- "version": "2.6.1",
- "resolved": "https://registry.npmjs.org/async/-/async-2.6.1.tgz",
- "integrity": "sha512-fNEiL2+AZt6AlAw/29Cr0UDe4sRAHCpEHh54WMz+Bb7QfNcFw4h3loofyJpLeQs4Yx7yuqu/2dLgM5hKOs6HlQ==",
+ "version": "2.6.2",
+ "resolved": "https://registry.npmjs.org/async/-/async-2.6.2.tgz",
+ "integrity": "sha512-H1qVYh1MYhEEFLsP97cVKqCGo7KfCyTt6uEWqsTBr9SO84oK9Uwbyd/yCW+6rKJLHksBNUVWZDAjfS+Ccx0Bbg==",
"dev": true,
"requires": {
- "lodash": "^4.17.10"
+ "lodash": "^4.17.11"
}
}
}
@@ -8973,9 +8765,9 @@
"dev": true
},
"js-yaml": {
- "version": "3.12.1",
- "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.12.1.tgz",
- "integrity": "sha512-um46hB9wNOKlwkHgiuyEVAybXBjwFUV0Z/RaHJblRd9DXltue9FTYvzCr9ErQrK9Adz5MU4gHWVaNUfdmrC8qA==",
+ "version": "3.12.2",
+ "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.12.2.tgz",
+ "integrity": "sha512-QHn/Lh/7HhZ/Twc7vJYQTkjuCa0kaCcDcjK5Zlk2rvnUpy7DxMJ23+Jc2dcyvltwQVg1nygAVlB2oRDFHoRS5Q==",
"dev": true,
"requires": {
"argparse": "^1.0.7",
@@ -9238,6 +9030,23 @@
"chalk": "^2.1.0",
"log-symbols": "^2.1.0",
"strip-ansi": "^4.0.0"
+ },
+ "dependencies": {
+ "ansi-regex": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-3.0.0.tgz",
+ "integrity": "sha1-7QMXwyIGT3lGbAKWa922Bas32Zg=",
+ "dev": true
+ },
+ "strip-ansi": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-4.0.0.tgz",
+ "integrity": "sha1-qEeQIusaw2iocTibY1JixQXuNo8=",
+ "dev": true,
+ "requires": {
+ "ansi-regex": "^3.0.0"
+ }
+ }
}
},
"karma-opera-launcher": {
@@ -9297,12 +9106,12 @@
},
"dependencies": {
"async": {
- "version": "2.6.1",
- "resolved": "https://registry.npmjs.org/async/-/async-2.6.1.tgz",
- "integrity": "sha512-fNEiL2+AZt6AlAw/29Cr0UDe4sRAHCpEHh54WMz+Bb7QfNcFw4h3loofyJpLeQs4Yx7yuqu/2dLgM5hKOs6HlQ==",
+ "version": "2.6.2",
+ "resolved": "https://registry.npmjs.org/async/-/async-2.6.2.tgz",
+ "integrity": "sha512-H1qVYh1MYhEEFLsP97cVKqCGo7KfCyTt6uEWqsTBr9SO84oK9Uwbyd/yCW+6rKJLHksBNUVWZDAjfS+Ccx0Bbg==",
"dev": true,
"requires": {
- "lodash": "^4.17.10"
+ "lodash": "^4.17.11"
}
}
}
@@ -9354,12 +9163,12 @@
}
},
"lcid": {
- "version": "2.0.0",
- "resolved": "https://registry.npmjs.org/lcid/-/lcid-2.0.0.tgz",
- "integrity": "sha512-avPEb8P8EGnwXKClwsNUgryVjllcRqtMYa49NTsbQagYuT1DcXnl1915oxWjoyGrXR6zH/Y0Zc96xWsPcoDKeA==",
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/lcid/-/lcid-1.0.0.tgz",
+ "integrity": "sha1-MIrMr6C8SDo4Z7S28rlQYlHRuDU=",
"dev": true,
"requires": {
- "invert-kv": "^2.0.0"
+ "invert-kv": "^1.0.0"
}
},
"lcov-parse": {
@@ -9944,15 +9753,6 @@
"kind-of": "^6.0.2"
}
},
- "map-age-cleaner": {
- "version": "0.1.3",
- "resolved": "https://registry.npmjs.org/map-age-cleaner/-/map-age-cleaner-0.1.3.tgz",
- "integrity": "sha512-bJzx6nMoP6PDLPBFmg7+xRKeFZvFboMrGlxmNj9ClvX53KrmvM5bXFXEWjbz4cz1AFn+jWJ9z/DJSz7hrs0w3w==",
- "dev": true,
- "requires": {
- "p-defer": "^1.0.0"
- }
- },
"map-cache": {
"version": "0.2.2",
"resolved": "https://registry.npmjs.org/map-cache/-/map-cache-0.2.2.tgz",
@@ -10074,14 +9874,26 @@
"dev": true
},
"mdast-util-toc": {
- "version": "3.0.1",
- "resolved": "https://registry.npmjs.org/mdast-util-toc/-/mdast-util-toc-3.0.1.tgz",
- "integrity": "sha512-Z8lKq6sQr/vDNIcUkIWzPwKo5JQIzlDLouZuzIMVajOdUAyjnkA+s98RhjVpFt7SiuJzase9oh6Iw7n4zhVNDQ==",
+ "version": "3.1.0",
+ "resolved": "https://registry.npmjs.org/mdast-util-toc/-/mdast-util-toc-3.1.0.tgz",
+ "integrity": "sha512-Za0hqL1PqWrvxGtA/3NH9D5nhGAUS9grMM4obEAz5+zsk1RIw/vWUchkaoDLNdrwk05A0CSC5eEXng36/1qE5w==",
"dev": true,
"requires": {
- "github-slugger": "^1.1.1",
- "mdast-util-to-string": "^1.0.2",
+ "github-slugger": "^1.2.1",
+ "mdast-util-to-string": "^1.0.5",
+ "unist-util-is": "^2.1.2",
"unist-util-visit": "^1.1.0"
+ },
+ "dependencies": {
+ "github-slugger": {
+ "version": "1.2.1",
+ "resolved": "https://registry.npmjs.org/github-slugger/-/github-slugger-1.2.1.tgz",
+ "integrity": "sha512-SsZUjg/P03KPzQBt7OxJPasGw6NRO5uOgiZ5RGXVud5iSIZ0eNZeNp5rTwCxtavrRUa/A77j8mePVc5lEvk0KQ==",
+ "dev": true,
+ "requires": {
+ "emoji-regex": ">=6.0.0 <=6.1.1"
+ }
+ }
}
},
"mdurl": {
@@ -10097,14 +9909,12 @@
"dev": true
},
"mem": {
- "version": "4.1.0",
- "resolved": "https://registry.npmjs.org/mem/-/mem-4.1.0.tgz",
- "integrity": "sha512-I5u6Q1x7wxO0kdOpYBB28xueHADYps5uty/zg936CiG8NTe5sJL8EjrCuLneuDW3PlMdZBGDIn8BirEVdovZvg==",
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/mem/-/mem-1.1.0.tgz",
+ "integrity": "sha1-Xt1StIXKHZAP5kiVUFOZoN+kX3Y=",
"dev": true,
"requires": {
- "map-age-cleaner": "^0.1.1",
- "mimic-fn": "^1.0.0",
- "p-is-promise": "^2.0.0"
+ "mimic-fn": "^1.0.0"
}
},
"memoizee": {
@@ -10288,18 +10098,18 @@
"dev": true
},
"mime-db": {
- "version": "1.37.0",
- "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.37.0.tgz",
- "integrity": "sha512-R3C4db6bgQhlIhPU48fUtdVmKnflq+hRdad7IyKhtFj06VPNVdk2RhiYL3UjQIlso8L+YxAtFkobT0VK+S/ybg==",
+ "version": "1.38.0",
+ "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.38.0.tgz",
+ "integrity": "sha512-bqVioMFFzc2awcdJZIzR3HjZFX20QhilVS7hytkKrv7xFAn8bM1gzc/FOX2awLISvWe0PV8ptFKcon+wZ5qYkg==",
"dev": true
},
"mime-types": {
- "version": "2.1.21",
- "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.21.tgz",
- "integrity": "sha512-3iL6DbwpyLzjR3xHSFNFeb9Nz/M8WDkX33t1GFQnFOllWk8pOrh/LSrB5OXlnlW5P9LH73X6loW/eogc+F5lJg==",
+ "version": "2.1.22",
+ "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.22.tgz",
+ "integrity": "sha512-aGl6TZGnhm/li6F7yx82bJiBZwgiEa4Hf6CNr8YO+r5UHr53tSTYZb102zyU50DOWWKeOv0uQLRL0/9EiKWCog==",
"dev": true,
"requires": {
- "mime-db": "~1.37.0"
+ "mime-db": "~1.38.0"
}
},
"mimic-fn": {
@@ -10637,12 +10447,6 @@
"integrity": "sha1-yobR/ogoFpsBICCOPchCS524NCw=",
"dev": true
},
- "nice-try": {
- "version": "1.0.5",
- "resolved": "https://registry.npmjs.org/nice-try/-/nice-try-1.0.5.tgz",
- "integrity": "sha512-1nh45deeb5olNY7eX82BkPO7SSxR5SSYJiPTrTdFUVYwAl8CKMA5N9PjTYkHiRjisVcxcQ1HXdLhx2qxxJzLNQ==",
- "dev": true
- },
"nightwatch": {
"version": "1.0.19",
"resolved": "https://registry.npmjs.org/nightwatch/-/nightwatch-1.0.19.tgz",
@@ -10663,16 +10467,16 @@
}
},
"nise": {
- "version": "1.4.8",
- "resolved": "https://registry.npmjs.org/nise/-/nise-1.4.8.tgz",
- "integrity": "sha512-kGASVhuL4tlAV0tvA34yJYZIVihrUt/5bDwpp4tTluigxUr2bBlJeDXmivb6NuEdFkqvdv/Ybb9dm16PSKUhtw==",
+ "version": "1.4.10",
+ "resolved": "https://registry.npmjs.org/nise/-/nise-1.4.10.tgz",
+ "integrity": "sha512-sa0RRbj53dovjc7wombHmVli9ZihXbXCQ2uH3TNm03DyvOSIQbxg+pbqDKrk2oxMK1rtLGVlKxcB9rrc6X5YjA==",
"dev": true,
"requires": {
"@sinonjs/formatio": "^3.1.0",
+ "@sinonjs/text-encoding": "^0.7.1",
"just-extend": "^4.0.2",
"lolex": "^2.3.2",
- "path-to-regexp": "^1.7.0",
- "text-encoding": "^0.6.4"
+ "path-to-regexp": "^1.7.0"
},
"dependencies": {
"@sinonjs/formatio": {
@@ -10726,9 +10530,9 @@
}
},
"node-releases": {
- "version": "1.1.7",
- "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-1.1.7.tgz",
- "integrity": "sha512-bKdrwaqJUPHqlCzDD7so/R+Nk0jGv9a11ZhLrD9f6i947qGLrGAhU3OxRENa19QQmwzGy/g6zCDEuLGDO8HPvA==",
+ "version": "1.1.8",
+ "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-1.1.8.tgz",
+ "integrity": "sha512-gQm+K9mGCiT/NXHy+V/ZZS1N/LOaGGqRAAJJs3X9Ah1g+CIbRcBgNyoNYQ+SEtcyAtB9KqDruu+fF7nWjsqRaA==",
"dev": true,
"requires": {
"semver": "^5.3.0"
@@ -11030,29 +10834,12 @@
"integrity": "sha1-ReN/s56No/JbruP/U2niu18iAXo=",
"dev": true
},
- "ansi-regex": {
- "version": "2.1.1",
- "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz",
- "integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8=",
- "dev": true
- },
"camelcase": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/camelcase/-/camelcase-3.0.0.tgz",
"integrity": "sha1-MvxLn82vhF/N9+c7uXysImHwqwo=",
"dev": true
},
- "cliui": {
- "version": "3.2.0",
- "resolved": "https://registry.npmjs.org/cliui/-/cliui-3.2.0.tgz",
- "integrity": "sha1-EgYBU3qRbSmUD5NNo7SNWFo5IT0=",
- "dev": true,
- "requires": {
- "string-width": "^1.0.1",
- "strip-ansi": "^3.0.1",
- "wrap-ansi": "^2.0.0"
- }
- },
"find-up": {
"version": "1.1.2",
"resolved": "https://registry.npmjs.org/find-up/-/find-up-1.1.2.tgz",
@@ -11063,30 +10850,6 @@
"pinkie-promise": "^2.0.0"
}
},
- "invert-kv": {
- "version": "1.0.0",
- "resolved": "https://registry.npmjs.org/invert-kv/-/invert-kv-1.0.0.tgz",
- "integrity": "sha1-EEqOSqym09jNFXqO+L+rLXo//bY=",
- "dev": true
- },
- "is-fullwidth-code-point": {
- "version": "1.0.0",
- "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-1.0.0.tgz",
- "integrity": "sha1-754xOG8DGn8NZDr4L95QxFfvAMs=",
- "dev": true,
- "requires": {
- "number-is-nan": "^1.0.0"
- }
- },
- "lcid": {
- "version": "1.0.0",
- "resolved": "https://registry.npmjs.org/lcid/-/lcid-1.0.0.tgz",
- "integrity": "sha1-MIrMr6C8SDo4Z7S28rlQYlHRuDU=",
- "dev": true,
- "requires": {
- "invert-kv": "^1.0.0"
- }
- },
"load-json-file": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/load-json-file/-/load-json-file-1.1.0.tgz",
@@ -11165,26 +10928,6 @@
"read-pkg": "^1.0.0"
}
},
- "string-width": {
- "version": "1.0.2",
- "resolved": "https://registry.npmjs.org/string-width/-/string-width-1.0.2.tgz",
- "integrity": "sha1-EYvfW4zcUaKn5w0hHgfisLmxB9M=",
- "dev": true,
- "requires": {
- "code-point-at": "^1.0.0",
- "is-fullwidth-code-point": "^1.0.0",
- "strip-ansi": "^3.0.0"
- }
- },
- "strip-ansi": {
- "version": "3.0.1",
- "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz",
- "integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=",
- "dev": true,
- "requires": {
- "ansi-regex": "^2.0.0"
- }
- },
"strip-bom": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-2.0.0.tgz",
@@ -11200,12 +10943,6 @@
"integrity": "sha1-u6Y8qGGUiZT/MHc2CJ47lgJsKk8=",
"dev": true
},
- "y18n": {
- "version": "3.2.1",
- "resolved": "https://registry.npmjs.org/y18n/-/y18n-3.2.1.tgz",
- "integrity": "sha1-bRX7qITAhnnA136I53WegR4H+kE=",
- "dev": true
- },
"yargs": {
"version": "4.8.1",
"resolved": "https://registry.npmjs.org/yargs/-/yargs-4.8.1.tgz",
@@ -11276,14 +11013,14 @@
"dev": true
},
"os-locale": {
- "version": "3.1.0",
- "resolved": "https://registry.npmjs.org/os-locale/-/os-locale-3.1.0.tgz",
- "integrity": "sha512-Z8l3R4wYWM40/52Z+S265okfFj8Kt2cC2MKY+xNi3kFs+XGI7WXu/I309QQQYbRW4ijiZ+yxs9pqEhJh0DqW3Q==",
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/os-locale/-/os-locale-2.1.0.tgz",
+ "integrity": "sha512-3sslG3zJbEYcaC4YVAvDorjGxc7tv6KVATnLPZONiljsUncvihe9BQoVCEs0RZ1kmf4Hk9OBqlZfJZWI4GanKA==",
"dev": true,
"requires": {
- "execa": "^1.0.0",
- "lcid": "^2.0.0",
- "mem": "^4.0.0"
+ "execa": "^0.7.0",
+ "lcid": "^1.0.0",
+ "mem": "^1.1.0"
}
},
"os-tmpdir": {
@@ -11298,12 +11035,6 @@
"integrity": "sha512-HNa1A8LvB1kie7cERyy21VNeHb2CWJJYqyyC2o3klWFfMGlFmWv2Z7sFgZH8ZiaYL95ydToKTFVXgMV/Os0bBQ==",
"dev": true
},
- "p-defer": {
- "version": "1.0.0",
- "resolved": "https://registry.npmjs.org/p-defer/-/p-defer-1.0.0.tgz",
- "integrity": "sha1-n26xgvbJqozXQwBKfU+WsZaw+ww=",
- "dev": true
- },
"p-finally": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/p-finally/-/p-finally-1.0.0.tgz",
@@ -11311,9 +11042,9 @@
"dev": true
},
"p-is-promise": {
- "version": "2.0.0",
- "resolved": "https://registry.npmjs.org/p-is-promise/-/p-is-promise-2.0.0.tgz",
- "integrity": "sha512-pzQPhYMCAgLAKPWD2jC3Se9fEfrD9npNos0y150EeqZll7akhEgGhTW/slB6lHku8AvYGiJ+YJ5hfHKePPgFWg==",
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/p-is-promise/-/p-is-promise-1.1.0.tgz",
+ "integrity": "sha1-nJRWmJ6fZYgBewQ01WCXZ1w9oF4=",
"dev": true
},
"p-limit": {
@@ -11432,9 +11163,9 @@
}
},
"parse-asn1": {
- "version": "5.1.3",
- "resolved": "https://registry.npmjs.org/parse-asn1/-/parse-asn1-5.1.3.tgz",
- "integrity": "sha512-VrPoetlz7B/FqjBLD2f5wBVZvsZVLnRUrxVLfRYhGXCODa/NWE4p3Wp+6+aV3ZPL3KM7/OZmxDIwwijD7yuucg==",
+ "version": "5.1.4",
+ "resolved": "https://registry.npmjs.org/parse-asn1/-/parse-asn1-5.1.4.tgz",
+ "integrity": "sha512-Qs5duJcuvNExRfFZ99HDD3z4mAi3r9Wl/FOjEOijlxwCZs7E7mW2vjTpgQ4J8LpTF8x5v+1Vn5UQFejmWT11aw==",
"dev": true,
"requires": {
"asn1.js": "^4.0.0",
@@ -11458,9 +11189,9 @@
}
},
"parse-entities": {
- "version": "1.2.0",
- "resolved": "https://registry.npmjs.org/parse-entities/-/parse-entities-1.2.0.tgz",
- "integrity": "sha512-XXtDdOPLSB0sHecbEapQi6/58U/ODj/KWfIXmmMCJF/eRn8laX6LZbOyioMoETOOJoWRW8/qTSl5VQkUIfKM5g==",
+ "version": "1.2.1",
+ "resolved": "https://registry.npmjs.org/parse-entities/-/parse-entities-1.2.1.tgz",
+ "integrity": "sha512-NBWYLQm1KSoDKk7GAHyioLTvCZ5QjdH/ASBBQTD3iLiAWJXS5bg1jEWI8nIJ+vgVvsceBVBcDGRWSo0KVQBvvg==",
"dev": true,
"requires": {
"character-entities": "^1.0.0",
@@ -11903,9 +11634,9 @@
}
},
"pump": {
- "version": "3.0.0",
- "resolved": "https://registry.npmjs.org/pump/-/pump-3.0.0.tgz",
- "integrity": "sha512-LwZy+p3SFs1Pytd/jYct4wpv49HiYCqd9Rlc5ZVdk0V+8Yzv6jR5Blk3TRmPL1ft69TxP0IMZGJ+WPFU2BFhww==",
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/pump/-/pump-2.0.1.tgz",
+ "integrity": "sha512-ruPMNRkN3MHP1cWJc9OWr+T/xDP0jhXYCLfJcBuX54hhfIBnaQmAUMfDcG4DM5UMWByBbJY69QSphm3jtDKIkA==",
"dev": true,
"requires": {
"end-of-stream": "^1.1.0",
@@ -11921,18 +11652,6 @@
"duplexify": "^3.6.0",
"inherits": "^2.0.3",
"pump": "^2.0.0"
- },
- "dependencies": {
- "pump": {
- "version": "2.0.1",
- "resolved": "https://registry.npmjs.org/pump/-/pump-2.0.1.tgz",
- "integrity": "sha512-ruPMNRkN3MHP1cWJc9OWr+T/xDP0jhXYCLfJcBuX54hhfIBnaQmAUMfDcG4DM5UMWByBbJY69QSphm3jtDKIkA==",
- "dev": true,
- "requires": {
- "end-of-stream": "^1.1.0",
- "once": "^1.3.1"
- }
- }
}
},
"punycode": {
@@ -12007,9 +11726,9 @@
}
},
"randombytes": {
- "version": "2.0.6",
- "resolved": "https://registry.npmjs.org/randombytes/-/randombytes-2.0.6.tgz",
- "integrity": "sha512-CIQ5OFxf4Jou6uOKe9t1AOgqpeU5fd70A8NPdHSGeYXqXsPe6peOwI0cUl88RWZ6sP1vPMV3avd/R6cZ5/sP1A==",
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/randombytes/-/randombytes-2.1.0.tgz",
+ "integrity": "sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ==",
"dev": true,
"requires": {
"safe-buffer": "^5.1.0"
@@ -12181,9 +11900,9 @@
"integrity": "sha512-MguG95oij0fC3QV3URf4V2SDYGJhJnJGqvIIgdECeODCT98wSWDAJ94SSuVpYQUoTcGUIL6L4yNB7j1DFFHSBg=="
},
"regenerator-transform": {
- "version": "0.13.3",
- "resolved": "https://registry.npmjs.org/regenerator-transform/-/regenerator-transform-0.13.3.tgz",
- "integrity": "sha512-5ipTrZFSq5vU2YoGoww4uaRVAK4wyYC4TSICibbfEPOruUu8FFP7ErV0BjmbIOEpn3O/k9na9UEdYR/3m7N6uA==",
+ "version": "0.13.4",
+ "resolved": "https://registry.npmjs.org/regenerator-transform/-/regenerator-transform-0.13.4.tgz",
+ "integrity": "sha512-T0QMBjK3J0MtxjPmdIMXm72Wvj2Abb0Bd4HADdfijwMdoIsyQZ6fWC7kDFhk2YinBBEMZDL7Y7wh0J1sGx3S4A==",
"dev": true,
"requires": {
"private": "^0.1.6"
@@ -12209,37 +11928,10 @@
}
},
"regexp-tree": {
- "version": "0.1.1",
- "resolved": "https://registry.npmjs.org/regexp-tree/-/regexp-tree-0.1.1.tgz",
- "integrity": "sha512-HwRjOquc9QOwKTgbxvZTcddS5mlNlwePMQ3NFL8broajMLD5CXDAqas8Y5yxJH5QtZp5iRor3YCILd5pz71Cgw==",
- "dev": true,
- "requires": {
- "cli-table3": "^0.5.0",
- "colors": "^1.1.2",
- "yargs": "^12.0.5"
- },
- "dependencies": {
- "yargs": {
- "version": "12.0.5",
- "resolved": "https://registry.npmjs.org/yargs/-/yargs-12.0.5.tgz",
- "integrity": "sha512-Lhz8TLaYnxq/2ObqHDql8dX8CJi97oHxrjUcYtzKbbykPtVW9WB+poxI+NM2UIzsMgNCZTIf0AQwsjK5yMAqZw==",
- "dev": true,
- "requires": {
- "cliui": "^4.0.0",
- "decamelize": "^1.2.0",
- "find-up": "^3.0.0",
- "get-caller-file": "^1.0.1",
- "os-locale": "^3.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 || ^4.0.0",
- "yargs-parser": "^11.1.1"
- }
- }
- }
+ "version": "0.1.5",
+ "resolved": "https://registry.npmjs.org/regexp-tree/-/regexp-tree-0.1.5.tgz",
+ "integrity": "sha512-nUmxvfJyAODw+0B13hj8CFVAxhe7fDEAgJgaotBu3nnR+IgGgZq59YedJP5VYTlkEfqjuK6TuRpnymKdatLZfQ==",
+ "dev": true
},
"regexpp": {
"version": "1.1.0",
@@ -12913,6 +12605,14 @@
"dev": true,
"requires": {
"is-fullwidth-code-point": "^2.0.0"
+ },
+ "dependencies": {
+ "is-fullwidth-code-point": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz",
+ "integrity": "sha1-o7MKXE8ZkYMWeqq5O+764937ZU8=",
+ "dev": true
+ }
}
},
"smart-buffer": {
@@ -13477,13 +13177,14 @@
"dev": true
},
"string-width": {
- "version": "2.1.1",
- "resolved": "https://registry.npmjs.org/string-width/-/string-width-2.1.1.tgz",
- "integrity": "sha512-nOqH59deCq9SRHlxq1Aw85Jnt4w6KvLKqWVik6oA9ZklXLNIOlqg4F2yrT1MVaTjAqvVwdfeZ7w7aCvJD7ugkw==",
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/string-width/-/string-width-1.0.2.tgz",
+ "integrity": "sha1-EYvfW4zcUaKn5w0hHgfisLmxB9M=",
"dev": true,
"requires": {
- "is-fullwidth-code-point": "^2.0.0",
- "strip-ansi": "^4.0.0"
+ "code-point-at": "^1.0.0",
+ "is-fullwidth-code-point": "^1.0.0",
+ "strip-ansi": "^3.0.0"
}
},
"string_decoder": {
@@ -13508,12 +13209,12 @@
}
},
"strip-ansi": {
- "version": "4.0.0",
- "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-4.0.0.tgz",
- "integrity": "sha1-qEeQIusaw2iocTibY1JixQXuNo8=",
+ "version": "3.0.1",
+ "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz",
+ "integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=",
"dev": true,
"requires": {
- "ansi-regex": "^3.0.0"
+ "ansi-regex": "^2.0.0"
}
},
"strip-bom": {
@@ -13589,6 +13290,39 @@
"lodash": "^4.17.4",
"slice-ansi": "1.0.0",
"string-width": "^2.1.1"
+ },
+ "dependencies": {
+ "ansi-regex": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-3.0.0.tgz",
+ "integrity": "sha1-7QMXwyIGT3lGbAKWa922Bas32Zg=",
+ "dev": true
+ },
+ "is-fullwidth-code-point": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz",
+ "integrity": "sha1-o7MKXE8ZkYMWeqq5O+764937ZU8=",
+ "dev": true
+ },
+ "string-width": {
+ "version": "2.1.1",
+ "resolved": "https://registry.npmjs.org/string-width/-/string-width-2.1.1.tgz",
+ "integrity": "sha512-nOqH59deCq9SRHlxq1Aw85Jnt4w6KvLKqWVik6oA9ZklXLNIOlqg4F2yrT1MVaTjAqvVwdfeZ7w7aCvJD7ugkw==",
+ "dev": true,
+ "requires": {
+ "is-fullwidth-code-point": "^2.0.0",
+ "strip-ansi": "^4.0.0"
+ }
+ },
+ "strip-ansi": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-4.0.0.tgz",
+ "integrity": "sha1-qEeQIusaw2iocTibY1JixQXuNo8=",
+ "dev": true,
+ "requires": {
+ "ansi-regex": "^3.0.0"
+ }
+ }
}
},
"tapable": {
@@ -13609,12 +13343,6 @@
"through2": "^2.0.1"
}
},
- "text-encoding": {
- "version": "0.6.4",
- "resolved": "https://registry.npmjs.org/text-encoding/-/text-encoding-0.6.4.tgz",
- "integrity": "sha1-45mpgiV6J22uQou5KEXLcb3CbRk=",
- "dev": true
- },
"text-table": {
"version": "0.2.0",
"resolved": "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz",
@@ -14249,9 +13977,9 @@
}
},
"unzipper": {
- "version": "0.9.10",
- "resolved": "https://registry.npmjs.org/unzipper/-/unzipper-0.9.10.tgz",
- "integrity": "sha512-dhxTaR67KGyrmxseXTmsyzdlRWkuN0rMPo9j6lxosR/PkzbHNd3smzMobaApx6o/oYvqU1uv+fAPoWr1P4bd8Q==",
+ "version": "0.9.11",
+ "resolved": "https://registry.npmjs.org/unzipper/-/unzipper-0.9.11.tgz",
+ "integrity": "sha512-G0z5zv8LYv4/XwpOiXgTGTcN4jyxgyg3P1DfdIeCN2QGOd6ZBl49BSbOe9JsIEvKh3tG7/b0bdJvz+UmwA+BRg==",
"dev": true,
"requires": {
"big-integer": "^1.6.17",
@@ -14488,47 +14216,12 @@
"vfile-statistics": "^1.1.0"
},
"dependencies": {
- "ansi-regex": {
- "version": "2.1.1",
- "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz",
- "integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8=",
- "dev": true
- },
"has-flag": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/has-flag/-/has-flag-2.0.0.tgz",
"integrity": "sha1-6CB68cx7MNRGzHC3NLXovhj4jVE=",
"dev": true
},
- "is-fullwidth-code-point": {
- "version": "1.0.0",
- "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-1.0.0.tgz",
- "integrity": "sha1-754xOG8DGn8NZDr4L95QxFfvAMs=",
- "dev": true,
- "requires": {
- "number-is-nan": "^1.0.0"
- }
- },
- "string-width": {
- "version": "1.0.2",
- "resolved": "https://registry.npmjs.org/string-width/-/string-width-1.0.2.tgz",
- "integrity": "sha1-EYvfW4zcUaKn5w0hHgfisLmxB9M=",
- "dev": true,
- "requires": {
- "code-point-at": "^1.0.0",
- "is-fullwidth-code-point": "^1.0.0",
- "strip-ansi": "^3.0.0"
- }
- },
- "strip-ansi": {
- "version": "3.0.1",
- "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz",
- "integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=",
- "dev": true,
- "requires": {
- "ansi-regex": "^2.0.0"
- }
- },
"supports-color": {
"version": "4.5.0",
"resolved": "https://registry.npmjs.org/supports-color/-/supports-color-4.5.0.tgz",
@@ -14698,9 +14391,9 @@
},
"dependencies": {
"ajv": {
- "version": "6.9.1",
- "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.9.1.tgz",
- "integrity": "sha512-XDN92U311aINL77ieWHmqCcNlwjoP5cHXDxIxbf2MaPYuCXOHS7gHH8jktxeK5omgd52XbSTX6a4Piwd1pQmzA==",
+ "version": "6.9.2",
+ "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.9.2.tgz",
+ "integrity": "sha512-4UFy0/LgDo7Oa/+wOAlj44tp9K78u38E5/359eSrqEp1Z5PdVfimCcs7SluXMP755RUQu6d2b4AvF0R1C9RZjg==",
"dev": true,
"requires": {
"fast-deep-equal": "^2.0.1",
@@ -14716,74 +14409,18 @@
"dev": true
},
"ansi-regex": {
- "version": "2.1.1",
- "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz",
- "integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8=",
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-3.0.0.tgz",
+ "integrity": "sha1-7QMXwyIGT3lGbAKWa922Bas32Zg=",
"dev": true
},
"async": {
- "version": "2.6.1",
- "resolved": "https://registry.npmjs.org/async/-/async-2.6.1.tgz",
- "integrity": "sha512-fNEiL2+AZt6AlAw/29Cr0UDe4sRAHCpEHh54WMz+Bb7QfNcFw4h3loofyJpLeQs4Yx7yuqu/2dLgM5hKOs6HlQ==",
- "dev": true,
- "requires": {
- "lodash": "^4.17.10"
- }
- },
- "camelcase": {
- "version": "4.1.0",
- "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-4.1.0.tgz",
- "integrity": "sha1-1UVjW+HjPFQmScaRc+Xeas+uNN0=",
- "dev": true
- },
- "cliui": {
- "version": "3.2.0",
- "resolved": "https://registry.npmjs.org/cliui/-/cliui-3.2.0.tgz",
- "integrity": "sha1-EgYBU3qRbSmUD5NNo7SNWFo5IT0=",
+ "version": "2.6.2",
+ "resolved": "https://registry.npmjs.org/async/-/async-2.6.2.tgz",
+ "integrity": "sha512-H1qVYh1MYhEEFLsP97cVKqCGo7KfCyTt6uEWqsTBr9SO84oK9Uwbyd/yCW+6rKJLHksBNUVWZDAjfS+Ccx0Bbg==",
"dev": true,
"requires": {
- "string-width": "^1.0.1",
- "strip-ansi": "^3.0.1",
- "wrap-ansi": "^2.0.0"
- },
- "dependencies": {
- "string-width": {
- "version": "1.0.2",
- "resolved": "https://registry.npmjs.org/string-width/-/string-width-1.0.2.tgz",
- "integrity": "sha1-EYvfW4zcUaKn5w0hHgfisLmxB9M=",
- "dev": true,
- "requires": {
- "code-point-at": "^1.0.0",
- "is-fullwidth-code-point": "^1.0.0",
- "strip-ansi": "^3.0.0"
- }
- }
- }
- },
- "cross-spawn": {
- "version": "5.1.0",
- "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-5.1.0.tgz",
- "integrity": "sha1-6L0O/uWPz/b4+UUQoKVUu/ojVEk=",
- "dev": true,
- "requires": {
- "lru-cache": "^4.0.1",
- "shebang-command": "^1.2.0",
- "which": "^1.2.9"
- }
- },
- "execa": {
- "version": "0.7.0",
- "resolved": "https://registry.npmjs.org/execa/-/execa-0.7.0.tgz",
- "integrity": "sha1-lEvs00zEHuMqY6n68nrVpl/Fl3c=",
- "dev": true,
- "requires": {
- "cross-spawn": "^5.0.1",
- "get-stream": "^3.0.0",
- "is-stream": "^1.1.0",
- "npm-run-path": "^2.0.0",
- "p-finally": "^1.0.0",
- "signal-exit": "^3.0.0",
- "strip-eof": "^1.0.0"
+ "lodash": "^4.17.11"
}
},
"fast-deep-equal": {
@@ -14801,32 +14438,17 @@
"locate-path": "^2.0.0"
}
},
- "get-stream": {
- "version": "3.0.0",
- "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-3.0.0.tgz",
- "integrity": "sha1-jpQ9E1jcN1VQVOy+LtsFqhdO3hQ=",
- "dev": true
- },
"has-flag": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/has-flag/-/has-flag-2.0.0.tgz",
"integrity": "sha1-6CB68cx7MNRGzHC3NLXovhj4jVE=",
"dev": true
},
- "invert-kv": {
- "version": "1.0.0",
- "resolved": "https://registry.npmjs.org/invert-kv/-/invert-kv-1.0.0.tgz",
- "integrity": "sha1-EEqOSqym09jNFXqO+L+rLXo//bY=",
- "dev": true
- },
"is-fullwidth-code-point": {
- "version": "1.0.0",
- "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-1.0.0.tgz",
- "integrity": "sha1-754xOG8DGn8NZDr4L95QxFfvAMs=",
- "dev": true,
- "requires": {
- "number-is-nan": "^1.0.0"
- }
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz",
+ "integrity": "sha1-o7MKXE8ZkYMWeqq5O+764937ZU8=",
+ "dev": true
},
"json-schema-traverse": {
"version": "0.4.1",
@@ -14840,15 +14462,6 @@
"integrity": "sha1-Hq3nrMASA0rYTiOWdn6tn6VJWCE=",
"dev": true
},
- "lcid": {
- "version": "1.0.0",
- "resolved": "https://registry.npmjs.org/lcid/-/lcid-1.0.0.tgz",
- "integrity": "sha1-MIrMr6C8SDo4Z7S28rlQYlHRuDU=",
- "dev": true,
- "requires": {
- "invert-kv": "^1.0.0"
- }
- },
"load-json-file": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/load-json-file/-/load-json-file-2.0.0.tgz",
@@ -14871,26 +14484,6 @@
"path-exists": "^3.0.0"
}
},
- "mem": {
- "version": "1.1.0",
- "resolved": "https://registry.npmjs.org/mem/-/mem-1.1.0.tgz",
- "integrity": "sha1-Xt1StIXKHZAP5kiVUFOZoN+kX3Y=",
- "dev": true,
- "requires": {
- "mimic-fn": "^1.0.0"
- }
- },
- "os-locale": {
- "version": "2.1.0",
- "resolved": "https://registry.npmjs.org/os-locale/-/os-locale-2.1.0.tgz",
- "integrity": "sha512-3sslG3zJbEYcaC4YVAvDorjGxc7tv6KVATnLPZONiljsUncvihe9BQoVCEs0RZ1kmf4Hk9OBqlZfJZWI4GanKA==",
- "dev": true,
- "requires": {
- "execa": "^0.7.0",
- "lcid": "^1.0.0",
- "mem": "^1.1.0"
- }
- },
"p-limit": {
"version": "1.3.0",
"resolved": "https://registry.npmjs.org/p-limit/-/p-limit-1.3.0.tgz",
@@ -14960,13 +14553,23 @@
"read-pkg": "^2.0.0"
}
},
+ "string-width": {
+ "version": "2.1.1",
+ "resolved": "https://registry.npmjs.org/string-width/-/string-width-2.1.1.tgz",
+ "integrity": "sha512-nOqH59deCq9SRHlxq1Aw85Jnt4w6KvLKqWVik6oA9ZklXLNIOlqg4F2yrT1MVaTjAqvVwdfeZ7w7aCvJD7ugkw==",
+ "dev": true,
+ "requires": {
+ "is-fullwidth-code-point": "^2.0.0",
+ "strip-ansi": "^4.0.0"
+ }
+ },
"strip-ansi": {
- "version": "3.0.1",
- "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz",
- "integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=",
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-4.0.0.tgz",
+ "integrity": "sha1-qEeQIusaw2iocTibY1JixQXuNo8=",
"dev": true,
"requires": {
- "ansi-regex": "^2.0.0"
+ "ansi-regex": "^3.0.0"
}
},
"supports-color": {
@@ -14978,12 +14581,6 @@
"has-flag": "^2.0.0"
}
},
- "y18n": {
- "version": "3.2.1",
- "resolved": "https://registry.npmjs.org/y18n/-/y18n-3.2.1.tgz",
- "integrity": "sha1-bRX7qITAhnnA136I53WegR4H+kE=",
- "dev": true
- },
"yargs": {
"version": "8.0.2",
"resolved": "https://registry.npmjs.org/yargs/-/yargs-8.0.2.tgz",
@@ -15004,15 +14601,6 @@
"y18n": "^3.2.1",
"yargs-parser": "^7.0.0"
}
- },
- "yargs-parser": {
- "version": "7.0.0",
- "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-7.0.0.tgz",
- "integrity": "sha1-jQrELxbqVd69MyyvTEA4s+P139k=",
- "dev": true,
- "requires": {
- "camelcase": "^4.1.0"
- }
}
}
},
@@ -15680,43 +15268,6 @@
"requires": {
"string-width": "^1.0.1",
"strip-ansi": "^3.0.1"
- },
- "dependencies": {
- "ansi-regex": {
- "version": "2.1.1",
- "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz",
- "integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8=",
- "dev": true
- },
- "is-fullwidth-code-point": {
- "version": "1.0.0",
- "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-1.0.0.tgz",
- "integrity": "sha1-754xOG8DGn8NZDr4L95QxFfvAMs=",
- "dev": true,
- "requires": {
- "number-is-nan": "^1.0.0"
- }
- },
- "string-width": {
- "version": "1.0.2",
- "resolved": "https://registry.npmjs.org/string-width/-/string-width-1.0.2.tgz",
- "integrity": "sha1-EYvfW4zcUaKn5w0hHgfisLmxB9M=",
- "dev": true,
- "requires": {
- "code-point-at": "^1.0.0",
- "is-fullwidth-code-point": "^1.0.0",
- "strip-ansi": "^3.0.0"
- }
- },
- "strip-ansi": {
- "version": "3.0.1",
- "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz",
- "integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=",
- "dev": true,
- "requires": {
- "ansi-regex": "^2.0.0"
- }
- }
}
},
"wrappy": {
@@ -15770,9 +15321,9 @@
"dev": true
},
"y18n": {
- "version": "4.0.0",
- "resolved": "https://registry.npmjs.org/y18n/-/y18n-4.0.0.tgz",
- "integrity": "sha512-r9S/ZyXu/Xu9q1tYlpsLIsa3EeLXXk0VwlxqTcFRfg9EhMW+17kbt9G0NrgCmhGb5vT2hyhJZLfDGx+7+5Uj/w==",
+ "version": "3.2.1",
+ "resolved": "https://registry.npmjs.org/y18n/-/y18n-3.2.1.tgz",
+ "integrity": "sha1-bRX7qITAhnnA136I53WegR4H+kE=",
"dev": true
},
"yallist": {
@@ -15788,13 +15339,12 @@
"dev": true
},
"yargs-parser": {
- "version": "11.1.1",
- "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-11.1.1.tgz",
- "integrity": "sha512-C6kB/WJDiaxONLJQnF8ccx9SEeoTTLek8RVbaOIsrAUS8VrBEXfmeSnCZxygc+XC2sNMBIwOOnfcxiynjHsVSQ==",
+ "version": "7.0.0",
+ "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-7.0.0.tgz",
+ "integrity": "sha1-jQrELxbqVd69MyyvTEA4s+P139k=",
"dev": true,
"requires": {
- "camelcase": "^5.0.0",
- "decamelize": "^1.2.0"
+ "camelcase": "^4.1.0"
}
},
"yeast": {
diff --git a/package.json b/package.json
index 6d597aeca8c..cd78a3036b9 100755
--- a/package.json
+++ b/package.json
@@ -1,6 +1,6 @@
{
"name": "prebid.js",
- "version": "2.4.0-pre",
+ "version": "2.6.0-pre",
"description": "Header Bidding Management Library",
"main": "src/prebid.js",
"scripts": {
@@ -92,7 +92,7 @@
"babel-plugin-transform-object-assign": "^6.22.0",
"core-js": "^2.4.1",
"crypto-js": "^3.1.9-1",
- "fun-hooks": "^0.6.5",
+ "fun-hooks": "^0.8.1",
"jsencrypt": "^3.0.0-rc.1",
"just-clone": "^1.0.2"
}
diff --git a/src/adapters/bidderFactory.js b/src/adapters/bidderFactory.js
index 8d67fe098f9..a65c657cbb5 100644
--- a/src/adapters/bidderFactory.js
+++ b/src/adapters/bidderFactory.js
@@ -8,7 +8,10 @@ import { isValidVideoBid } from '../video';
import CONSTANTS from '../constants.json';
import events from '../events';
import includes from 'core-js/library/fn/array/includes';
-import { logWarn, logError, parseQueryStringParameters, delayExecution, parseSizesInput, getBidderRequest } from '../utils';
+import { ajax } from '../ajax';
+import { logWarn, logError, parseQueryStringParameters, delayExecution, parseSizesInput, getBidderRequest, flatten, uniques, timestamp, setDataInLocalStorage, getDataFromLocalStorage, deepAccess } from '../utils';
+import { ADPOD } from '../mediaTypes';
+import { getHook } from '../hook';
/**
* This file aims to support Adapters during the Prebid 0.x -> 1.x transition.
@@ -126,6 +129,8 @@ import { logWarn, logError, parseQueryStringParameters, delayExecution, parseSiz
// common params for all mediaTypes
const COMMON_BID_RESPONSE_KEYS = ['requestId', 'cpm', 'ttl', 'creativeId', 'netRevenue', 'currency'];
+const DEFAULT_REFRESHIN_DAYS = 1;
+
/**
* Register a bidder with prebid, using the given spec.
*
@@ -345,6 +350,73 @@ export function newBidder(spec) {
}
}
+export function preloadBidderMappingFile(fn, adUnits) {
+ if (!config.getConfig('adpod.brandCategoryExclusion')) {
+ return fn.call(this, adUnits);
+ }
+ let adPodBidders = adUnits
+ .filter((adUnit) => deepAccess(adUnit, 'mediaTypes.video.context') === ADPOD)
+ .map((adUnit) => adUnit.bids.map((bid) => bid.bidder))
+ .reduce(flatten, [])
+ .filter(uniques);
+
+ adPodBidders.forEach(bidder => {
+ let bidderSpec = adapterManager.getBidAdapter(bidder);
+ if (bidderSpec.getSpec().getMappingFileInfo) {
+ let info = bidderSpec.getSpec().getMappingFileInfo();
+ let refreshInDays = (info.refreshInDays) ? info.refreshInDays : DEFAULT_REFRESHIN_DAYS;
+ let key = (info.localStorageKey) ? info.localStorageKey : bidderSpec.getSpec().code;
+ let mappingData = getDataFromLocalStorage(key);
+ if (!mappingData || timestamp() < mappingData.lastUpdated + refreshInDays * 24 * 60 * 60 * 1000) {
+ ajax(info.url,
+ {
+ success: (response) => {
+ try {
+ response = JSON.parse(response);
+ let mapping = {
+ lastUpdated: timestamp(),
+ mapping: response.mapping
+ }
+ setDataInLocalStorage(key, JSON.stringify(mapping));
+ } catch (error) {
+ logError(`Failed to parse ${bidder} bidder translation mapping file`);
+ }
+ },
+ error: () => {
+ logError(`Failed to load ${bidder} bidder translation file`)
+ }
+ },
+ );
+ }
+ }
+ });
+ fn.call(this, adUnits);
+}
+
+getHook('checkAdUnitSetup').before(preloadBidderMappingFile);
+
+/**
+ * Reads the data stored in localstorage and returns iab subcategory
+ * @param {string} bidderCode bidderCode
+ * @param {string} category bidders category
+ */
+export function getIabSubCategory(bidderCode, category) {
+ let bidderSpec = adapterManager.getBidAdapter(bidderCode);
+ if (bidderSpec.getSpec().getMappingFileInfo) {
+ let info = bidderSpec.getSpec().getMappingFileInfo();
+ let key = (info.localStorageKey) ? info.localStorageKey : bidderSpec.getBidderCode();
+ let data = getDataFromLocalStorage(key);
+ if (data) {
+ try {
+ data = JSON.parse(data);
+ } catch (error) {
+ logError(`Failed to parse ${bidderCode} mapping data stored in local storage`);
+ }
+ return (data.mapping[category]) ? data.mapping[category] : null;
+ }
+ }
+}
+
// check that the bid has a width and height set
function validBidSize(adUnitCode, bid, bidRequests) {
if ((bid.width || bid.width === 0) && (bid.height || bid.height === 0)) {
diff --git a/src/auction.js b/src/auction.js
index fc8c42023f7..bf3f1bb1b71 100644
--- a/src/auction.js
+++ b/src/auction.js
@@ -157,7 +157,7 @@ export function newAuction({adUnits, adUnitCodes, callback, cbTimeout, labels})
const adUnitCodes = _adUnitCodes;
const bids = _bidsReceived
- .filter(adUnitsFilter.bind(this, adUnitCodes))
+ .filter(utils.bind.call(adUnitsFilter, this, adUnitCodes))
.reduce(groupByPlacement, {});
_callback.apply($$PREBID_GLOBAL$$, [bids, timedOut]);
} catch (e) {
@@ -386,6 +386,10 @@ export function doCallbacksIfTimedout(auctionInstance, bidResponse) {
// Add a bid to the auction.
export function addBidToAuction(auctionInstance, bidResponse) {
+ let bidderRequests = auctionInstance.getBidRequests();
+ let bidderRequest = find(bidderRequests, bidderRequest => bidderRequest.bidderCode === bidResponse.bidderCode);
+ setupBidTargeting(bidResponse, bidderRequest);
+
events.emit(CONSTANTS.EVENTS.BID_RESPONSE, bidResponse);
auctionInstance.addBidReceived(bidResponse);
@@ -416,7 +420,7 @@ function tryAddVideoBid(auctionInstance, bidResponse, bidRequests, afterBidAdded
}
}
-const callPrebidCache = hook('async', function(auctionInstance, bidResponse, afterBidAdded, bidderRequest) {
+export const callPrebidCache = hook('async', function(auctionInstance, bidResponse, afterBidAdded, bidderRequest) {
store([bidResponse], function (error, cacheIds) {
if (error) {
utils.logWarn(`Failed to save to the video cache: ${error}. Video bid must be discarded.`);
@@ -429,6 +433,7 @@ const callPrebidCache = hook('async', function(auctionInstance, bidResponse, aft
doCallbacksIfTimedout(auctionInstance, bidResponse);
} else {
bidResponse.videoCacheKey = cacheIds[0].uuid;
+
if (!bidResponse.vastUrl) {
bidResponse.vastUrl = getCacheUrl(bidResponse.videoCacheKey);
}
@@ -485,16 +490,18 @@ function getPreparedBidForAuction({adUnitCode, bid, bidderRequest, auctionId}) {
bidObject.pbDg = priceStringsObj.dense;
bidObject.pbCg = priceStringsObj.custom;
- // if there is any key value pairs to map do here
- var keyValues;
+ return bidObject;
+}
+
+function setupBidTargeting(bidObject, bidderRequest) {
+ let keyValues;
if (bidObject.bidderCode && (bidObject.cpm > 0 || bidObject.dealId)) {
- keyValues = getKeyValueTargetingPairs(bidObject.bidderCode, bidObject);
+ let bidReq = find(bidderRequest.bids, bid => bid.adUnitCode === bidObject.adUnitCode);
+ keyValues = getKeyValueTargetingPairs(bidObject.bidderCode, bidObject, bidReq);
}
// use any targeting provided as defaults, otherwise just set from getKeyValueTargetingPairs
bidObject.adserverTargeting = Object.assign(bidObject.adserverTargeting || {}, keyValues);
-
- return bidObject;
}
export function getStandardBidderSettings(mediaType) {
@@ -559,11 +566,22 @@ export function getStandardBidderSettings(mediaType) {
}
},
]
+
+ if (mediaType === 'video') {
+ [CONSTANTS.TARGETING_KEYS.UUID, CONSTANTS.TARGETING_KEYS.CACHE_ID].forEach(item => {
+ bidderSettings[CONSTANTS.JSON_MAPPING.BD_SETTING_STANDARD][CONSTANTS.JSON_MAPPING.ADSERVER_TARGETING].push({
+ key: item,
+ val: function val(bidResponse) {
+ return bidResponse.videoCacheKey;
+ }
+ })
+ });
+ }
}
return bidderSettings[CONSTANTS.JSON_MAPPING.BD_SETTING_STANDARD];
}
-export function getKeyValueTargetingPairs(bidderCode, custBidObj) {
+export function getKeyValueTargetingPairs(bidderCode, custBidObj, bidReq) {
if (!custBidObj) {
return {};
}
@@ -586,7 +604,7 @@ export function getKeyValueTargetingPairs(bidderCode, custBidObj) {
// set native key value targeting
if (custBidObj['native']) {
- keyValues = Object.assign({}, keyValues, getNativeTargeting(custBidObj));
+ keyValues = Object.assign({}, keyValues, getNativeTargeting(custBidObj, bidReq));
}
return keyValues;
diff --git a/src/constants.json b/src/constants.json
index 4e946d9c593..1a78e376150 100644
--- a/src/constants.json
+++ b/src/constants.json
@@ -62,7 +62,9 @@
"SIZE": "hb_size",
"DEAL": "hb_deal",
"SOURCE": "hb_source",
- "FORMAT": "hb_format"
+ "FORMAT": "hb_format",
+ "UUID": "hb_uuid",
+ "CACHE_ID": "hb_cache_id"
},
"NATIVE_KEYS": {
"title": "hb_native_title",
diff --git a/src/hook.js b/src/hook.js
index c4e450bf5f2..7727155e8aa 100644
--- a/src/hook.js
+++ b/src/hook.js
@@ -5,8 +5,11 @@ export let hook = funHooks({
ready: funHooks.SYNC | funHooks.ASYNC | funHooks.QUEUE
});
-/**
- * A map of global hook methods to allow easy extension of hooked functions that are intended to be extended globally
- * @type {{}}
- */
-export const hooks = hook.hooks;
+export const getHook = hook.get;
+
+export function setupBeforeHookFnOnce(baseFn, hookFn, priority = 15) {
+ let result = baseFn.getHooks({hook: hookFn});
+ if (result.length === 0) {
+ baseFn.before(hookFn, priority);
+ }
+}
diff --git a/src/native.js b/src/native.js
index a5609739832..c9a67ca7541 100644
--- a/src/native.js
+++ b/src/native.js
@@ -1,4 +1,4 @@
-import { deepAccess, getBidRequest, logError, triggerPixel, insertHtmlIntoIframe } from './utils';
+import { deepAccess, getBidRequest, getKeyByValue, insertHtmlIntoIframe, logError, triggerPixel } from './utils';
import includes from 'core-js/library/fn/array/includes';
const CONSTANTS = require('./constants.json');
@@ -144,6 +144,7 @@ export function fireNativeTrackers(message, adObject) {
}
(trackers || []).forEach(triggerPixel);
+ return message.action;
}
/**
@@ -151,16 +152,21 @@ export function fireNativeTrackers(message, adObject) {
* @param {Object} bid
* @return {Object} targeting
*/
-export function getNativeTargeting(bid) {
+export function getNativeTargeting(bid, bidReq) {
let keyValues = {};
Object.keys(bid['native']).forEach(asset => {
const key = CONSTANTS.NATIVE_KEYS[asset];
- let value = bid['native'][asset];
+ let value = getAssetValue(bid['native'][asset]);
- // native image-type assets can be a string or an object with a url prop
- if (typeof value === 'object' && value.url) {
- value = value.url;
+ const sendPlaceholder = deepAccess(
+ bidReq,
+ `mediaTypes.native.${asset}.sendId`
+ );
+
+ if (sendPlaceholder) {
+ const placeholder = `${key}:${bid.adId}`;
+ value = placeholder;
}
if (key && value) {
@@ -170,3 +176,36 @@ export function getNativeTargeting(bid) {
return keyValues;
}
+
+/**
+ * Constructs a message object containing asset values for each of the
+ * requested data keys.
+ */
+export function getAssetMessage(data, adObject) {
+ const message = {
+ message: 'assetResponse',
+ adId: data.adId,
+ assets: [],
+ };
+
+ data.assets.forEach(asset => {
+ const key = getKeyByValue(CONSTANTS.NATIVE_KEYS, asset);
+ const value = getAssetValue(adObject.native[key]);
+
+ message.assets.push({ key, value });
+ });
+
+ return message;
+}
+
+/**
+ * Native assets can be a string or an object with a url prop. Returns the value
+ * appropriate for sending in adserver targeting or placeholder replacement.
+ */
+function getAssetValue(value) {
+ if (typeof value === 'object' && value.url) {
+ return value.url;
+ }
+
+ return value;
+}
diff --git a/src/prebid.js b/src/prebid.js
index ff780d6deda..0374649c47c 100644
--- a/src/prebid.js
+++ b/src/prebid.js
@@ -69,7 +69,7 @@ function setRenderSize(doc, width, height) {
}
}
-const checkAdUnitSetup = hook('sync', function (adUnits) {
+export const checkAdUnitSetup = hook('sync', function (adUnits) {
adUnits.forEach((adUnit) => {
const mediaTypes = adUnit.mediaTypes;
const normalizedSize = utils.getAdUnitSizes(adUnit);
@@ -172,7 +172,7 @@ $$PREBID_GLOBAL$$.getAdserverTargeting = function (adUnitCode) {
function getBids(type) {
const responses = auctionManager[type]()
- .filter(adUnitsFilter.bind(this, auctionManager.getAdUnitCodes()));
+ .filter(utils.bind.call(adUnitsFilter, this, auctionManager.getAdUnitCodes()));
// find the last auction id to get responses for most recent auction only
const currentAuctionId = auctionManager.getLastAuctionId();
diff --git a/src/secureCreatives.js b/src/secureCreatives.js
index 32ad27a0496..8505923c493 100644
--- a/src/secureCreatives.js
+++ b/src/secureCreatives.js
@@ -4,7 +4,7 @@
*/
import events from './events';
-import { fireNativeTrackers } from './native';
+import { fireNativeTrackers, getAssetMessage } from './native';
import { EVENTS } from './constants';
import { isSlotMatchingAdUnitCode, logWarn, replaceAuctionPrice } from './utils';
import { auctionManager } from './auctionManager';
@@ -46,7 +46,15 @@ function receiveMessage(ev) {
// adId: '%%PATTERN:hb_adid%%'
// }), '*');
if (data.message === 'Prebid Native') {
- fireNativeTrackers(data, adObject);
+ if (data.action === 'assetRequest') {
+ const message = getAssetMessage(data, adObject);
+ ev.source.postMessage(JSON.stringify(message), ev.origin);
+ return;
+ }
+
+ const trackerType = fireNativeTrackers(data, adObject);
+ if (trackerType === 'click') { return; }
+
auctionManager.addWinningBid(adObject);
events.emit(BID_WON, adObject);
}
diff --git a/src/targeting.js b/src/targeting.js
index 1d0a1de2cd7..90897b8d956 100644
--- a/src/targeting.js
+++ b/src/targeting.js
@@ -1,8 +1,9 @@
-import { uniques, isGptPubadsDefined, getHighestCpm, getOldestHighestCpmBid, groupBy, isAdUnitCodeMatchingSlot, timestamp } from './utils';
+import { uniques, isGptPubadsDefined, getHighestCpm, getOldestHighestCpmBid, groupBy, isAdUnitCodeMatchingSlot, timestamp, deepAccess } from './utils';
import { config } from './config';
import { NATIVE_TARGETING_KEYS } from './native';
import { auctionManager } from './auctionManager';
import { sizeSupported } from './sizeMapping';
+import { ADPOD } from './mediaTypes';
import includes from 'core-js/library/fn/array/includes';
const utils = require('./utils.js');
@@ -220,6 +221,7 @@ export function newTargeting(auctionManager) {
}
bidsReceived = bidsReceived
+ .filter(bid => deepAccess(bid, 'video.context') !== ADPOD)
.filter(bid => bid.mediaType !== 'banner' || sizeSupported([bid.width, bid.height]))
.filter(filters.isUnusedBid)
.filter(filters.isBidNotExpired)
diff --git a/src/utils.js b/src/utils.js
index 2abad759e7a..5e70b31b0b2 100644
--- a/src/utils.js
+++ b/src/utils.js
@@ -40,6 +40,17 @@ export const internal = {
logInfo
};
+var uniqueRef = {};
+export let bind = function(a, b) { return b; }.bind(null, 1, uniqueRef)() === uniqueRef
+ ? Function.prototype.bind
+ : function(bind) {
+ var self = this;
+ var args = Array.prototype.slice.call(arguments, 1);
+ return function() {
+ return self.apply(bind, args.concat(Array.prototype.slice.call(arguments)));
+ };
+ };
+
/*
* Substitutes into a string from a given map using the token
* Usage
@@ -772,6 +783,19 @@ export function getValue(obj, key) {
return obj[key];
}
+/**
+ * Get the key of an object for a given value
+ */
+export function getKeyByValue(obj, value) {
+ for (let prop in obj) {
+ if (obj.hasOwnProperty(prop)) {
+ if (obj[prop] === value) {
+ return prop;
+ }
+ }
+ }
+}
+
export function getBidderCodes(adUnits = $$PREBID_GLOBAL$$.adUnits) {
// this could memoize adUnits
return adUnits.map(unit => unit.bids.map(bid => bid.bidder)
@@ -986,7 +1010,7 @@ export function getDefinedParams(object, params) {
*/
export function isValidMediaTypes(mediaTypes) {
const SUPPORTED_MEDIA_TYPES = ['banner', 'native', 'video'];
- const SUPPORTED_STREAM_TYPES = ['instream', 'outstream'];
+ const SUPPORTED_STREAM_TYPES = ['instream', 'outstream', 'adpod'];
const types = Object.keys(mediaTypes);
@@ -1187,6 +1211,68 @@ export function convertTypes(types, params) {
return params;
}
+export function setDataInLocalStorage(key, value) {
+ if (hasLocalStorage()) {
+ window.localStorage.setItem(key, value);
+ }
+}
+
+export function getDataFromLocalStorage(key) {
+ if (hasLocalStorage()) {
+ return window.localStorage.getItem(key);
+ }
+}
+
+export function hasLocalStorage() {
+ try {
+ return !!window.localStorage;
+ } catch (e) {
+ logError('Local storage api disabled');
+ }
+}
+
export function isArrayOfNums(val, size) {
return (isArray(val)) && ((size) ? val.length === size : true) && (val.every(v => isInteger(v)));
}
+
+/**
+ * Creates an array of n length and fills each item with the given value
+ */
+export function fill(value, length) {
+ let newArray = [];
+
+ for (let i = 0; i < length; i++) {
+ let valueToPush = isPlainObject(value) ? deepClone(value) : value;
+ newArray.push(valueToPush);
+ }
+
+ return newArray;
+}
+
+/**
+ * http://npm.im/chunk
+ * Returns an array with *size* chunks from given array
+ *
+ * Example:
+ * ['a', 'b', 'c', 'd', 'e'] chunked by 2 =>
+ * [['a', 'b'], ['c', 'd'], ['e']]
+ */
+export function chunk(array, size) {
+ let newArray = [];
+
+ for (let i = 0; i < Math.ceil(array.length / size); i++) {
+ let start = i * size;
+ let end = start + size;
+ newArray.push(array.slice(start, end));
+ }
+
+ return newArray;
+}
+
+export function getMinValueFromArray(array) {
+ return Math.min(...array);
+}
+
+export function getMaxValueFromArray(array) {
+ return Math.max(...array);
+}
diff --git a/src/video.js b/src/video.js
index d5fcadd1f38..9cf25016d46 100644
--- a/src/video.js
+++ b/src/video.js
@@ -42,7 +42,7 @@ export function isValidVideoBid(bid, bidRequests) {
return checkVideoBidSetup(bid, bidRequest, videoMediaType, context);
}
-const checkVideoBidSetup = hook('sync', function(bid, bidRequest, videoMediaType, context) {
+export const checkVideoBidSetup = hook('sync', function(bid, bidRequest, videoMediaType, context) {
if (!bidRequest || (videoMediaType && context !== OUTSTREAM)) {
// xml-only video bids require a prebid cache url
if (!config.getConfig('cache.url') && bid.vastXml && !bid.vastUrl) {
diff --git a/test/spec/modules/adpod_spec.js b/test/spec/modules/adpod_spec.js
new file mode 100644
index 00000000000..16e94fd569f
--- /dev/null
+++ b/test/spec/modules/adpod_spec.js
@@ -0,0 +1,982 @@
+import * as utils from 'src/utils';
+import { config } from 'src/config';
+import * as videoCache from 'src/videoCache';
+import * as auction from 'src/auction';
+import { ADPOD } from 'src/mediaTypes';
+
+import { callPrebidCacheHook, checkAdUnitSetupHook, checkVideoBidSetupHook, adpodSetConfig } from 'modules/adpod';
+
+let expect = require('chai').expect;
+
+describe('adpod.js', function () {
+ let logErrorStub;
+ let logWarnStub;
+ let logInfoStub;
+
+ describe('callPrebidCacheHook', function () {
+ let callbackResult;
+ let clock;
+ let addBidToAuctionStub;
+ let doCallbacksIfTimedoutStub;
+ let storeStub;
+ let afterBidAddedSpy;
+ let auctionBids = [];
+
+ let callbackFn = function() {
+ callbackResult = true;
+ };
+
+ let auctionInstance = {
+ getAuctionStatus: function() {
+ return auction.AUCTION_IN_PROGRESS;
+ }
+ }
+
+ const fakeStoreFn = function(bids, callback) {
+ let payload = [];
+ bids.forEach(bid => payload.push({uuid: bid.customCacheKey}));
+ callback(null, payload);
+ };
+
+ beforeEach(function() {
+ callbackResult = null;
+ afterBidAddedSpy = sinon.spy();
+ storeStub = sinon.stub(videoCache, 'store');
+ logWarnStub = sinon.stub(utils, 'logWarn');
+ logInfoStub = sinon.stub(utils, 'logInfo');
+ addBidToAuctionStub = sinon.stub(auction, 'addBidToAuction').callsFake(function (auctionInstance, bid) {
+ auctionBids.push(bid);
+ });
+ doCallbacksIfTimedoutStub = sinon.stub(auction, 'doCallbacksIfTimedout');
+ clock = sinon.useFakeTimers();
+ config.setConfig({
+ cache: {
+ url: 'https://prebid.adnxs.com/pbc/v1/cache'
+ }
+ });
+ });
+
+ afterEach(function() {
+ storeStub.restore();
+ logWarnStub.restore();
+ logInfoStub.restore();
+ addBidToAuctionStub.restore();
+ doCallbacksIfTimedoutStub.restore();
+ clock.restore();
+ config.resetConfig();
+ auctionBids = [];
+ })
+
+ it('should redirect back to the original function if bid is not an adpod video', function () {
+ let bid = {
+ adId: 'testAdId_123',
+ mediaType: 'video'
+ };
+
+ let bidderRequest = {
+ adUnitCode: 'adUnit_123',
+ mediaTypes: {
+ video: {
+ context: 'outstream'
+ }
+ }
+ }
+
+ callPrebidCacheHook(callbackFn, auctionInstance, bid, function () {}, bidderRequest);
+ expect(callbackResult).to.equal(true);
+ });
+
+ it('should immediately add the adpod bid to auction if adpod.deferCaching in config is true', function() {
+ config.setConfig({
+ adpod: {
+ deferCaching: true,
+ brandCategoryExclusion: true
+ }
+ });
+
+ let bidResponse1 = {
+ adId: 'adId01277',
+ auctionId: 'no_defer_123',
+ mediaType: 'video',
+ cpm: 5,
+ meta: {
+ adServerCatId: 'test'
+ },
+ video: {
+ context: ADPOD,
+ durationSeconds: 15,
+ durationBucket: 15
+ }
+ };
+
+ let bidResponse2 = {
+ adId: 'adId46547',
+ auctionId: 'no_defer_123',
+ mediaType: 'video',
+ cpm: 12,
+ meta: {
+ adServerCatId: 'value'
+ },
+ video: {
+ context: ADPOD,
+ durationSeconds: 15,
+ durationBucket: 15
+ }
+ };
+
+ let bidderRequest = {
+ adUnitCode: 'adpod_1',
+ auctionId: 'no_defer_123',
+ mediaTypes: {
+ video: {
+ context: ADPOD,
+ playerSize: [300, 300],
+ adPodDurationSec: 300,
+ durationRangeSec: [15, 30, 45],
+ requireExactDuration: false
+ }
+ },
+ };
+
+ callPrebidCacheHook(callbackFn, auctionInstance, bidResponse1, afterBidAddedSpy, bidderRequest);
+ callPrebidCacheHook(callbackFn, auctionInstance, bidResponse2, afterBidAddedSpy, bidderRequest);
+
+ // check if bid adsereverTargeting is setup
+ expect(callbackResult).to.be.null;
+ expect(storeStub.called).to.equal(false);
+ expect(afterBidAddedSpy.calledTwice).to.equal(true);
+ expect(auctionBids.length).to.equal(2);
+ expect(auctionBids[0].adId).to.equal(bidResponse1.adId);
+ expect(auctionBids[0].customCacheKey).to.exist.and.to.match(/^5\.00_test_15s_.*/);
+ expect(auctionBids[0].adserverTargeting.hb_pb_cat_dur).to.equal('5.00_test_15s');
+ expect(auctionBids[0].adserverTargeting.hb_cache_id).to.exist;
+ expect(auctionBids[1].adId).to.equal(bidResponse2.adId);
+ expect(auctionBids[1].customCacheKey).to.exist.and.to.match(/^12\.00_value_15s_.*/);
+ expect(auctionBids[1].adserverTargeting.hb_pb_cat_dur).to.equal('12.00_value_15s');
+ expect(auctionBids[1].adserverTargeting.hb_cache_id).to.exist;
+ expect(auctionBids[1].adserverTargeting.hb_cache_id).to.equal(auctionBids[0].adserverTargeting.hb_cache_id);
+ });
+
+ it('should send prebid cache call once bid queue is full', function () {
+ storeStub.callsFake(fakeStoreFn);
+
+ config.setConfig({
+ adpod: {
+ bidQueueSizeLimit: 2,
+ deferCaching: false,
+ brandCategoryExclusion: true
+ }
+ });
+
+ let bidResponse1 = {
+ adId: 'adId123',
+ auctionId: 'full_abc123',
+ mediaType: 'video',
+ cpm: 10,
+ meta: {
+ adServerCatId: 'airline'
+ },
+ video: {
+ context: ADPOD,
+ durationSeconds: 20,
+ durationBucket: 30
+ }
+ };
+ let bidResponse2 = {
+ adId: 'adId234',
+ auctionId: 'full_abc123',
+ mediaType: 'video',
+ cpm: 15,
+ meta: {
+ adServerCatId: 'airline'
+ },
+ video: {
+ context: ADPOD,
+ durationSeconds: 25,
+ durationBucket: 30
+ }
+ };
+ let bidderRequest = {
+ adUnitCode: 'adpod_1',
+ auctionId: 'full_abc123',
+ mediaTypes: {
+ video: {
+ context: ADPOD,
+ playerSize: [300, 300],
+ adPodDurationSec: 120,
+ durationRangeSec: [15, 30],
+ requireExactDuration: false
+ }
+ }
+ };
+
+ callPrebidCacheHook(callbackFn, auctionInstance, bidResponse1, afterBidAddedSpy, bidderRequest);
+ callPrebidCacheHook(callbackFn, auctionInstance, bidResponse2, afterBidAddedSpy, bidderRequest);
+
+ expect(callbackResult).to.be.null;
+ expect(afterBidAddedSpy.calledTwice).to.equal(true);
+ expect(auctionBids.length).to.equal(2);
+ expect(auctionBids[0].adId).to.equal('adId123');
+ expect(auctionBids[0].customCacheKey).to.exist.and.to.match(/^10\.00_airline_30s_.*/);
+ expect(auctionBids[0].adserverTargeting.hb_pb_cat_dur).to.equal('10.00_airline_30s');
+ expect(auctionBids[0].adserverTargeting.hb_cache_id).to.exist;
+ expect(auctionBids[1].adId).to.equal('adId234');
+ expect(auctionBids[1].customCacheKey).to.exist.and.to.match(/^15\.00_airline_30s_.*/);
+ expect(auctionBids[1].adserverTargeting.hb_pb_cat_dur).to.equal('15.00_airline_30s');
+ expect(auctionBids[1].adserverTargeting.hb_cache_id).to.exist;
+ });
+
+ it('should send prebid cache call after set period of time (even if queue is not full)', function () {
+ storeStub.callsFake(fakeStoreFn);
+
+ config.setConfig({
+ adpod: {
+ bidQueueSizeLimit: 2,
+ bidQueueTimeDelay: 30,
+ deferCaching: false,
+ brandCategoryExclusion: true
+ }
+ });
+
+ let bidResponse = {
+ adId: 'adId234',
+ auctionId: 'timer_abc234',
+ mediaType: 'video',
+ cpm: 15,
+ meta: {
+ adServerCatId: 'airline'
+ },
+ video: {
+ context: ADPOD,
+ durationSeconds: 30,
+ durationBucket: 30
+ }
+ };
+ let bidderRequest = {
+ adUnitCode: 'adpod_2',
+ auctionId: 'timer_abc234',
+ mediaTypes: {
+ video: {
+ context: ADPOD,
+ playerSize: [300, 300],
+ adPodDurationSec: 120,
+ durationRangeSec: [15, 30],
+ requireExactDuration: true
+ }
+ }
+ };
+
+ callPrebidCacheHook(callbackFn, auctionInstance, bidResponse, afterBidAddedSpy, bidderRequest);
+ clock.tick(31);
+
+ expect(callbackResult).to.be.null;
+ expect(afterBidAddedSpy.calledOnce).to.equal(true);
+ expect(auctionBids.length).to.equal(1);
+ expect(auctionBids[0].adId).to.equal('adId234');
+ expect(auctionBids[0].customCacheKey).to.exist.and.to.match(/^15\.00_airline_30s_.*/);
+ expect(auctionBids[0].adserverTargeting.hb_pb_cat_dur).to.equal('15.00_airline_30s');
+ expect(auctionBids[0].adserverTargeting.hb_cache_id).to.exist;
+ });
+
+ it('should execute multiple prebid cache calls when number of bids exceeds queue size', function () {
+ storeStub.callsFake(fakeStoreFn);
+
+ config.setConfig({
+ adpod: {
+ bidQueueSizeLimit: 2,
+ bidQueueTimeDelay: 30,
+ deferCaching: false,
+ brandCategoryExclusion: true
+ }
+ });
+
+ let bidResponse1 = {
+ adId: 'multi_ad1',
+ auctionId: 'multi_call_abc345',
+ mediaType: 'video',
+ cpm: 15,
+ meta: {
+ adServerCatId: 'airline'
+ },
+ video: {
+ context: ADPOD,
+ durationSeconds: 15,
+ durationBucket: 15
+ }
+ };
+ let bidResponse2 = {
+ adId: 'multi_ad2',
+ auctionId: 'multi_call_abc345',
+ mediaType: 'video',
+ cpm: 15,
+ meta: {
+ adServerCatId: 'news'
+ },
+ video: {
+ context: ADPOD,
+ durationSeconds: 15,
+ durationBucket: 15
+ }
+ };
+ let bidResponse3 = {
+ adId: 'multi_ad3',
+ auctionId: 'multi_call_abc345',
+ mediaType: 'video',
+ cpm: 10,
+ meta: {
+ adServerCatId: 'sports'
+ },
+ video: {
+ context: ADPOD,
+ durationSeconds: 15,
+ durationBucket: 15
+ }
+ };
+
+ let bidderRequest = {
+ adUnitCode: 'adpod_3',
+ auctionId: 'multi_call_abc345',
+ mediaTypes: {
+ video: {
+ context: ADPOD,
+ playerSize: [300, 300],
+ adPodDurationSec: 45,
+ durationRangeSec: [15, 30],
+ requireExactDuration: false
+ }
+ }
+ };
+
+ callPrebidCacheHook(callbackFn, auctionInstance, bidResponse1, afterBidAddedSpy, bidderRequest);
+ callPrebidCacheHook(callbackFn, auctionInstance, bidResponse2, afterBidAddedSpy, bidderRequest);
+ callPrebidCacheHook(callbackFn, auctionInstance, bidResponse3, afterBidAddedSpy, bidderRequest);
+ clock.next();
+
+ expect(callbackResult).to.be.null;
+ expect(afterBidAddedSpy.calledThrice).to.equal(true);
+ expect(storeStub.calledTwice).to.equal(true);
+ expect(auctionBids.length).to.equal(3);
+ expect(auctionBids[0].adId).to.equal('multi_ad1');
+ expect(auctionBids[0].customCacheKey).to.exist.and.to.match(/^15\.00_airline_15s_.*/);
+ expect(auctionBids[0].adserverTargeting.hb_pb_cat_dur).to.equal('15.00_airline_15s');
+ expect(auctionBids[0].adserverTargeting.hb_cache_id).to.exist;
+ expect(auctionBids[1].adId).to.equal('multi_ad2');
+ expect(auctionBids[1].customCacheKey).to.exist.and.to.match(/^15\.00_news_15s_.*/);
+ expect(auctionBids[1].adserverTargeting.hb_pb_cat_dur).to.equal('15.00_news_15s');
+ expect(auctionBids[1].adserverTargeting.hb_cache_id).to.exist.and.to.equal(auctionBids[0].adserverTargeting.hb_cache_id);
+ expect(auctionBids[2].adId).to.equal('multi_ad3');
+ expect(auctionBids[2].customCacheKey).to.exist.and.to.match(/^10\.00_sports_15s_.*/);
+ expect(auctionBids[2].adserverTargeting.hb_pb_cat_dur).to.equal('10.00_sports_15s');
+ expect(auctionBids[2].adserverTargeting.hb_cache_id).to.exist.and.to.equal(auctionBids[0].adserverTargeting.hb_cache_id);
+ });
+
+ it('should cache the bids with a shortened custom key when adpod.brandCategoryExclusion is false', function() {
+ storeStub.callsFake(fakeStoreFn);
+
+ config.setConfig({
+ adpod: {
+ bidQueueSizeLimit: 2,
+ bidQueueTimeDelay: 30,
+ deferCaching: false,
+ brandCategoryExclusion: false
+ }
+ });
+
+ let bidResponse1 = {
+ adId: 'nocat_ad1',
+ auctionId: 'no_category_abc345',
+ mediaType: 'video',
+ cpm: 10,
+ meta: {
+ adServerCatId: undefined
+ },
+ video: {
+ context: ADPOD,
+ durationSeconds: 15,
+ durationBucket: 15
+ }
+ };
+ let bidResponse2 = {
+ adId: 'nocat_ad2',
+ auctionId: 'no_category_abc345',
+ mediaType: 'video',
+ cpm: 15,
+ meta: {
+ adServerCatId: undefined
+ },
+ video: {
+ context: ADPOD,
+ durationSeconds: 15,
+ durationBucket: 15
+ }
+ };
+
+ let bidderRequest = {
+ adUnitCode: 'adpod_4',
+ auctionId: 'no_category_abc345',
+ mediaTypes: {
+ video: {
+ context: ADPOD,
+ playerSize: [300, 300],
+ adPodDurationSec: 45,
+ durationRangeSec: [15, 30],
+ requireExactDuration: false
+ }
+ }
+ };
+
+ callPrebidCacheHook(callbackFn, auctionInstance, bidResponse1, afterBidAddedSpy, bidderRequest);
+ callPrebidCacheHook(callbackFn, auctionInstance, bidResponse2, afterBidAddedSpy, bidderRequest);
+
+ expect(callbackResult).to.be.null;
+ expect(afterBidAddedSpy.calledTwice).to.equal(true);
+ expect(storeStub.calledOnce).to.equal(true);
+ expect(auctionBids.length).to.equal(2);
+ expect(auctionBids[0].adId).to.equal('nocat_ad1');
+ expect(auctionBids[0].customCacheKey).to.exist.and.to.match(/^10\.00_15s_.*/);
+ expect(auctionBids[0].adserverTargeting.hb_pb_cat_dur).to.equal('10.00_15s');
+ expect(auctionBids[0].adserverTargeting.hb_cache_id).to.exist;
+ expect(auctionBids[1].adId).to.equal('nocat_ad2');
+ expect(auctionBids[1].customCacheKey).to.exist.and.to.match(/^15\.00_15s_.*/);
+ expect(auctionBids[1].adserverTargeting.hb_pb_cat_dur).to.equal('15.00_15s');
+ expect(auctionBids[1].adserverTargeting.hb_cache_id).to.exist.and.to.equal(auctionBids[0].adserverTargeting.hb_cache_id);
+ });
+
+ it('should not add bid to auction when config adpod.brandCategoryExclusion is true but bid is missing adServerCatId', function() {
+ storeStub.callsFake(fakeStoreFn);
+
+ config.setConfig({
+ adpod: {
+ bidQueueSizeLimit: 2,
+ bidQueueTimeDelay: 30,
+ deferCaching: false,
+ brandCategoryExclusion: true
+ }
+ });
+
+ let bidResponse1 = {
+ adId: 'missingCat_ad1',
+ auctionId: 'missing_category_abc345',
+ mediaType: 'video',
+ cpm: 10,
+ meta: {
+ adServerCatId: undefined
+ },
+ video: {
+ context: ADPOD,
+ durationSeconds: 15,
+ durationBucket: 15
+ }
+ };
+
+ let bidderRequest = {
+ adUnitCode: 'adpod_5',
+ auctionId: 'missing_category_abc345',
+ mediaTypes: {
+ video: {
+ context: ADPOD,
+ playerSize: [300, 300],
+ adPodDurationSec: 45,
+ durationRangeSec: [15, 30],
+ requireExactDuration: false
+ }
+ }
+ };
+
+ callPrebidCacheHook(callbackFn, auctionInstance, bidResponse1, afterBidAddedSpy, bidderRequest);
+
+ expect(callbackResult).to.be.null;
+ expect(afterBidAddedSpy.calledOnce).to.equal(true);
+ expect(storeStub.called).to.equal(false);
+ expect(logWarnStub.calledOnce).to.equal(true);
+ expect(auctionBids.length).to.equal(0);
+ });
+
+ it('should not add bid to auction when Prebid Cache detects an existing key', function () {
+ storeStub.callsFake(function(bids, callback) {
+ let payload = [];
+ bids.forEach(bid => payload.push({uuid: bid.customCacheKey}));
+
+ // fake a duplicate bid response from PBC (sets an empty string for the uuid)
+ payload[1].uuid = '';
+ callback(null, payload);
+ });
+
+ config.setConfig({
+ adpod: {
+ bidQueueSizeLimit: 2,
+ deferCaching: false,
+ brandCategoryExclusion: true
+ }
+ });
+
+ let bidResponse1 = {
+ adId: 'dup_ad_1',
+ auctionId: 'duplicate_def123',
+ mediaType: 'video',
+ cpm: 5,
+ meta: {
+ adServerCatId: 'tech'
+ },
+ video: {
+ context: ADPOD,
+ durationSeconds: 45,
+ durationBucket: 45
+ }
+ };
+ let bidResponse2 = {
+ adId: 'dup_ad_2',
+ auctionId: 'duplicate_def123',
+ mediaType: 'video',
+ cpm: 5,
+ meta: {
+ adServerCatId: 'tech'
+ },
+ video: {
+ context: ADPOD,
+ durationSeconds: 45,
+ durationBucket: 45
+ }
+ };
+ let bidderRequest = {
+ adUnitCode: 'adpod_4',
+ auctionId: 'duplicate_def123',
+ mediaTypes: {
+ video: {
+ context: ADPOD,
+ playerSize: [300, 300],
+ adPodDurationSec: 120,
+ durationRangeSec: [15, 30, 45],
+ requireExactDuration: false
+ }
+ }
+ };
+
+ callPrebidCacheHook(callbackFn, auctionInstance, bidResponse1, afterBidAddedSpy, bidderRequest);
+ callPrebidCacheHook(callbackFn, auctionInstance, bidResponse2, afterBidAddedSpy, bidderRequest);
+
+ expect(callbackResult).to.be.null;
+ expect(afterBidAddedSpy.calledTwice).to.equal(true);
+ expect(storeStub.calledOnce).to.equal(true);
+ expect(logInfoStub.calledOnce).to.equal(true);
+ expect(auctionBids.length).to.equal(1);
+ expect(auctionBids[0].adId).to.equal('dup_ad_1');
+ expect(auctionBids[0].customCacheKey).to.exist.and.to.match(/^5\.00_tech_45s_.*/);
+ expect(auctionBids[0].adserverTargeting.hb_pb_cat_dur).to.equal('5.00_tech_45s');
+ expect(auctionBids[0].adserverTargeting.hb_cache_id).to.exist;
+ });
+
+ it('should not add bids to auction if PBC returns an error', function() {
+ storeStub.callsFake(function(bids, callback) {
+ let payload = [];
+ let errmsg = 'invalid json';
+
+ callback(errmsg, payload);
+ });
+
+ config.setConfig({
+ adpod: {
+ bidQueueSizeLimit: 2,
+ deferCaching: false,
+ brandCategoryExclusion: true
+ }
+ });
+
+ let bidResponse1 = {
+ adId: 'err_ad_1',
+ auctionId: 'error_xyz123',
+ mediaType: 'video',
+ cpm: 5,
+ meta: {
+ adServerCatId: 'tech'
+ },
+ video: {
+ context: ADPOD,
+ durationSeconds: 30,
+ durationBucket: 30
+ }
+ };
+ let bidResponse2 = {
+ adId: 'err_ad_2',
+ auctionId: 'error_xyz123',
+ mediaType: 'video',
+ cpm: 5,
+ meta: {
+ adServerCatId: 'tech'
+ },
+ video: {
+ context: ADPOD,
+ durationSeconds: 30,
+ durationBucket: 30
+ }
+ };
+ let bidderRequest = {
+ adUnitCode: 'adpod_5',
+ auctionId: 'error_xyz123',
+ mediaTypes: {
+ video: {
+ context: ADPOD,
+ playerSize: [300, 300],
+ adPodDurationSec: 120,
+ durationRangeSec: [15, 30, 45],
+ requireExactDuration: false
+ }
+ }
+ };
+
+ callPrebidCacheHook(callbackFn, auctionInstance, bidResponse1, afterBidAddedSpy, bidderRequest);
+ callPrebidCacheHook(callbackFn, auctionInstance, bidResponse2, afterBidAddedSpy, bidderRequest);
+
+ expect(doCallbacksIfTimedoutStub.calledTwice).to.equal(true);
+ expect(logWarnStub.calledOnce).to.equal(true);
+ expect(auctionBids.length).to.equal(0);
+ });
+ });
+
+ describe('checkAdUnitSetupHook', function () {
+ let results;
+ let callbackFn = function (adUnits) {
+ results = adUnits;
+ };
+
+ beforeEach(function () {
+ logWarnStub = sinon.stub(utils, 'logWarn');
+ results = null;
+ });
+
+ afterEach(function() {
+ utils.logWarn.restore();
+ });
+
+ it('removes an incorrectly setup adpod adunit - required fields are missing', function() {
+ let adUnits = [{
+ code: 'test1',
+ mediaTypes: {
+ video: {
+ context: ADPOD
+ }
+ }
+ }, {
+ code: 'test2',
+ mediaTypes: {
+ video: {
+ context: 'instream'
+ }
+ }
+ }];
+
+ checkAdUnitSetupHook(callbackFn, adUnits);
+
+ expect(results).to.deep.equal([{
+ code: 'test2',
+ mediaTypes: {
+ video: {
+ context: 'instream'
+ }
+ }
+ }]);
+ expect(logWarnStub.calledOnce).to.equal(true);
+ });
+
+ it('removes an incorrectly setup adpod adunit - attempting to use multi-format adUnit', function() {
+ let adUnits = [{
+ code: 'multi_test1',
+ mediaTypes: {
+ banner: {
+ sizes: [[300, 250], [300, 600]]
+ },
+ video: {
+ context: 'adpod',
+ playerSize: [300, 250],
+ durationRangeSec: [15, 30, 45],
+ adPodDurationSec: 300
+ }
+ }
+ }];
+
+ checkAdUnitSetupHook(callbackFn, adUnits);
+
+ expect(results).to.deep.equal([]);
+ expect(logWarnStub.calledOnce).to.equal(true);
+ });
+
+ it('accepts mixed set of adunits', function() {
+ let adUnits = [{
+ code: 'test3',
+ mediaTypes: {
+ video: {
+ context: ADPOD,
+ playerSize: [300, 300],
+ adPodDurationSec: 360,
+ durationRangeSec: [15, 30, 45],
+ requireExactDuration: true
+ }
+ }
+ }, {
+ code: 'test4',
+ mediaTypes: {
+ banner: {
+ sizes: [[300, 250]]
+ }
+ }
+ }];
+
+ checkAdUnitSetupHook(callbackFn, adUnits);
+
+ expect(results).to.deep.equal(adUnits);
+ expect(logWarnStub.called).to.equal(false);
+ });
+ });
+
+ describe('checkVideoBidSetupHook', function () {
+ let callbackResult;
+ let bailResult;
+ const callbackFn = {
+ call: function(context, bid) {
+ callbackResult = bid;
+ },
+ bail: function(result) {
+ bailResult = result;
+ }
+ }
+ const adpodTestBid = {
+ video: {
+ context: ADPOD,
+ durationSeconds: 15,
+ durationBucket: 15
+ },
+ meta: {
+ iabSubCatId: 'testCategory_123'
+ },
+ vastXml: '
Ad
', + 'size': + { + 'height': '250', + 'width': '300' + }, + 'id': '2', + 'price': 200 + } + ] + } + }; + + let bids = spec.interpretResponse(serverResponse); + + it('should return empty array for response with no bids', function() { + let emptyBids = spec.interpretResponse({ body: {} }); + + expect(emptyBids).to.have.lengthOf(0); + }); + + it('should parse all bids from response', function() { + expect(bids).to.have.lengthOf(2); + }); + + it('should parse bid with ad url', function() { + expect(bids[0].requestId).to.equal('1'); + expect(bids[0].cpm).to.equal(100); + expect(bids[0].width).to.equal('240'); + expect(bids[0].height).to.equal('400'); + expect(bids[0].ttl).to.equal(360); + expect(bids[0].currency).to.equal('RUB'); + expect(bids[0]).to.have.property('creativeId'); + expect(bids[0].creativeId).to.equal('123456'); + expect(bids[0].netRevenue).to.equal(true); + expect(bids[0].adUrl).to.equal('https://ad.mail.ru/hbid_imp/12345'); + expect(bids[0]).to.not.have.property('ad'); + }); + + it('should parse bid with ad markup', function() { + expect(bids[1].requestId).to.equal('2'); + expect(bids[1].cpm).to.equal(200); + expect(bids[1].width).to.equal('300'); + expect(bids[1].height).to.equal('250'); + expect(bids[1].ttl).to.equal(180); + expect(bids[1].currency).to.equal('RUB'); + expect(bids[1]).to.have.property('creativeId'); + expect(bids[1].creativeId).not.to.equal('123456'); + expect(bids[1].netRevenue).to.equal(true); + expect(bids[1].ad).to.equal('Ad
'); + expect(bids[1]).to.not.have.property('adUrl'); + }); + }); +}); diff --git a/test/spec/modules/prebidServerBidAdapter_spec.js b/test/spec/modules/prebidServerBidAdapter_spec.js index 457d768f7a4..f14c171ee6c 100644 --- a/test/spec/modules/prebidServerBidAdapter_spec.js +++ b/test/spec/modules/prebidServerBidAdapter_spec.js @@ -654,6 +654,10 @@ describe('S2S Adapter', function () { prebid: { aliases: { brealtime: 'appnexus' + }, + targeting: { + includebidderkeys: false, + includewinners: true } } }); @@ -684,6 +688,10 @@ describe('S2S Adapter', function () { prebid: { aliases: { [alias]: 'appnexus' + }, + targeting: { + includebidderkeys: false, + includewinners: true } } }); @@ -822,6 +830,146 @@ describe('S2S Adapter', function () { expect(requestBid.user.ext.tpid.foo).is.equal('abc123'); expect(requestBid.user.ext.tpid.unifiedid).is.equal('1234'); }) + + it('always add ext.prebid.targeting.includebidderkeys: false for ORTB', function () { + const s2sConfig = Object.assign({}, CONFIG, { + endpoint: 'https://prebid.adnxs.com/pbs/v1/openrtb2/auction', + adapterOptions: { + appnexus: { + key: 'value' + } + } + }); + const _config = { + s2sConfig: s2sConfig, + device: { ifa: '6D92078A-8246-4BA4-AE5B-76104861E7DC' }, + app: { bundle: 'com.test.app' }, + }; + + config.setConfig(_config); + adapter.callBids(REQUEST, BID_REQUESTS, addBidResponse, done, ajax); + const requestBid = JSON.parse(requests[0].requestBody); + + expect(requestBid.ext.prebid.targeting).to.haveOwnProperty('includebidderkeys'); + expect(requestBid.ext.prebid.targeting.includebidderkeys).to.equal(false); + }); + + it('always add ext.prebid.targeting.includewinners: true for ORTB', function () { + const s2sConfig = Object.assign({}, CONFIG, { + endpoint: 'https://prebid.adnxs.com/pbs/v1/openrtb2/auction', + adapterOptions: { + appnexus: { + key: 'value' + } + } + }); + const _config = { + s2sConfig: s2sConfig, + device: { ifa: '6D92078A-8246-4BA4-AE5B-76104861E7DC' }, + app: { bundle: 'com.test.app' }, + }; + + config.setConfig(_config); + adapter.callBids(REQUEST, BID_REQUESTS, addBidResponse, done, ajax); + const requestBid = JSON.parse(requests[0].requestBody); + + expect(requestBid.ext.prebid.targeting).to.haveOwnProperty('includewinners'); + expect(requestBid.ext.prebid.targeting.includewinners).to.equal(true); + }); + + it('adds s2sConfig video.ext.prebid to request for ORTB', function () { + const s2sConfig = Object.assign({}, CONFIG, { + endpoint: 'https://prebid.adnxs.com/pbs/v1/openrtb2/auction', + extPrebid: { + foo: 'bar' + } + }); + const _config = { + s2sConfig: s2sConfig, + device: { ifa: '6D92078A-8246-4BA4-AE5B-76104861E7DC' }, + app: { bundle: 'com.test.app' }, + }; + + config.setConfig(_config); + adapter.callBids(REQUEST, BID_REQUESTS, addBidResponse, done, ajax); + const requestBid = JSON.parse(requests[0].requestBody); + + expect(requestBid).to.haveOwnProperty('ext'); + expect(requestBid.ext).to.haveOwnProperty('prebid'); + expect(requestBid.ext.prebid).to.deep.equal({ + foo: 'bar', + targeting: { + includewinners: true, + includebidderkeys: false + } + }); + }); + + it('overrides request.ext.prebid properties using s2sConfig video.ext.prebid values for ORTB', function () { + const s2sConfig = Object.assign({}, CONFIG, { + endpoint: 'https://prebid.adnxs.com/pbs/v1/openrtb2/auction', + extPrebid: { + targeting: { + includewinners: false, + includebidderkeys: true + } + } + }); + const _config = { + s2sConfig: s2sConfig, + device: { ifa: '6D92078A-8246-4BA4-AE5B-76104861E7DC' }, + app: { bundle: 'com.test.app' }, + }; + + config.setConfig(_config); + adapter.callBids(REQUEST, BID_REQUESTS, addBidResponse, done, ajax); + const requestBid = JSON.parse(requests[0].requestBody); + + expect(requestBid).to.haveOwnProperty('ext'); + expect(requestBid.ext).to.haveOwnProperty('prebid'); + expect(requestBid.ext.prebid).to.deep.equal({ + targeting: { + includewinners: false, + includebidderkeys: true + } + }); + }); + + it('overrides request.ext.prebid properties using s2sConfig video.ext.prebid values for ORTB', function () { + const s2sConfig = Object.assign({}, CONFIG, { + endpoint: 'https://prebid.adnxs.com/pbs/v1/openrtb2/auction', + extPrebid: { + cache: { + vastxml: 'vastxml-set-though-extPrebid.cache.vastXml' + }, + targeting: { + includewinners: false, + includebidderkeys: false + } + } + }); + const _config = { + s2sConfig: s2sConfig, + device: { ifa: '6D92078A-8246-4BA4-AE5B-76104861E7DC' }, + app: { bundle: 'com.test.app' }, + }; + + config.setConfig(_config); + adapter.callBids(REQUEST, BID_REQUESTS, addBidResponse, done, ajax); + const requestBid = JSON.parse(requests[0].requestBody); + + expect(requestBid).to.haveOwnProperty('ext'); + expect(requestBid.ext).to.haveOwnProperty('prebid'); + expect(requestBid.ext.prebid).to.deep.equal({ + cache: { + vastxml: 'vastxml-set-though-extPrebid.cache.vastXml' + }, + targeting: { + includewinners: false, + includebidderkeys: false + } + }); + }); }); describe('response handler', function () { @@ -1058,6 +1206,85 @@ describe('S2S Adapter', function () { expect(response).to.have.property('cpm', 10); }); + it('handles response cache from ext.prebid.cache.vastXml', function () { + const s2sConfig = Object.assign({}, CONFIG, { + endpoint: 'https://prebidserverurl/openrtb2/auction?querystring=param' + }); + config.setConfig({s2sConfig}); + const cacheResponse = utils.deepClone(RESPONSE_OPENRTB_VIDEO); + cacheResponse.seatbid.forEach(item => { + item.bid[0].ext.prebid.cache = { + vastXml: { + cacheId: 'abcd1234', + url: 'https://prebid-cache.net/cache?uuid=abcd1234' + } + } + }); + server.respondWith(JSON.stringify(cacheResponse)); + adapter.callBids(VIDEO_REQUEST, BID_REQUESTS, addBidResponse, done, ajax); + server.respond(); + + sinon.assert.calledOnce(addBidResponse); + const response = addBidResponse.firstCall.args[1]; + + expect(response).to.have.property('statusMessage', 'Bid available'); + expect(response).to.have.property('videoCacheKey', 'abcd1234'); + expect(response).to.have.property('vastUrl', 'https://prebid-cache.net/cache?uuid=abcd1234'); + }); + + it('add adserverTargeting object to bids when ext.prebid.targeting is defined', function () { + const s2sConfig = Object.assign({}, CONFIG, { + endpoint: 'https://prebidserverurl/openrtb2/auction?querystring=param' + }); + config.setConfig({s2sConfig}); + const cacheResponse = utils.deepClone(RESPONSE_OPENRTB_VIDEO); + const targetingTestData = { + hb_cache_path: '/cache', + hb_cache_host: 'prebid-cache.testurl.com' + }; + + cacheResponse.seatbid.forEach(item => { + item.bid[0].ext.prebid.targeting = targetingTestData + }); + server.respondWith(JSON.stringify(cacheResponse)); + adapter.callBids(VIDEO_REQUEST, BID_REQUESTS, addBidResponse, done, ajax); + server.respond(); + + sinon.assert.calledOnce(addBidResponse); + const response = addBidResponse.firstCall.args[1]; + + expect(response).to.have.property('adserverTargeting'); + expect(response.adserverTargeting).to.deep.equal({ + 'hb_cache_path': '/cache', + 'hb_cache_host': 'prebid-cache.testurl.com' + }); + }); + + it('handles response cache from ext.prebid.targeting', function () { + const s2sConfig = Object.assign({}, CONFIG, { + endpoint: 'https://prebidserverurl/openrtb2/auction?querystring=param' + }); + config.setConfig({s2sConfig}); + const cacheResponse = utils.deepClone(RESPONSE_OPENRTB_VIDEO); + cacheResponse.seatbid.forEach(item => { + item.bid[0].ext.prebid.targeting = { + hb_uuid: 'a5ad3993', + hb_cache_host: 'prebid-cache.net', + hb_cache_path: '/cache' + } + }); + server.respondWith(JSON.stringify(cacheResponse)); + adapter.callBids(VIDEO_REQUEST, BID_REQUESTS, addBidResponse, done, ajax); + server.respond(); + + sinon.assert.calledOnce(addBidResponse); + const response = addBidResponse.firstCall.args[1]; + + expect(response).to.have.property('statusMessage', 'Bid available'); + expect(response).to.have.property('videoCacheKey', 'a5ad3993'); + expect(response).to.have.property('vastUrl', 'https://prebid-cache.net/cache?uuid=a5ad3993'); + }); + it('should log warning for unsupported bidder', function () { server.respondWith(JSON.stringify(RESPONSE_UNSUPPORTED_BIDDER)); diff --git a/test/spec/modules/rubiconBidAdapter_spec.js b/test/spec/modules/rubiconBidAdapter_spec.js index 839d34d5c57..81816d42407 100644 --- a/test/spec/modules/rubiconBidAdapter_spec.js +++ b/test/spec/modules/rubiconBidAdapter_spec.js @@ -149,40 +149,28 @@ describe('the rubicon adapter', function () { let bid = bidderRequest.bids[0]; bid.mediaTypes = { video: { - context: 'instream' + context: 'instream', + mimes: ['video/mp4', 'video/x-flv'], + api: [2], + minduration: 15, + playerSize: [640, 480], + maxduration: 30, + startdelay: 0, + playbackmethod: [2], + linearity: 1, + skip: 1, + skipafter: 15, + pos: 1, + protocols: [1, 2, 3, 4, 5, 6] } }; bid.params.video = { 'language': 'en', - 'p_aso.video.ext.skip': true, - 'p_aso.video.ext.skipdelay': 15, - 'playerHeight': 320, - 'playerWidth': 640, - 'size_id': 201, - 'aeParams': { - 'p_aso.video.ext.skip': '1', - 'p_aso.video.ext.skipdelay': '15' - } - }; - } - - function createLegacyVideoBidderRequest() { - createGdprBidderRequest(true); - - let bid = bidderRequest.bids[0]; - // Legacy property (Prebid <1.0) - bid.mediaType = 'video'; - bid.params.video = { - 'language': 'en', - 'p_aso.video.ext.skip': true, - 'p_aso.video.ext.skipdelay': 15, - 'playerHeight': 320, + 'skip': 1, + 'skipafter': 15, + 'playerHeight': 480, 'playerWidth': 640, 'size_id': 201, - 'aeParams': { - 'p_aso.video.ext.skip': '1', - 'p_aso.video.ext.skipdelay': '15' - } }; } @@ -196,64 +184,35 @@ describe('the rubicon adapter', function () { bid.params.video = ''; } - function createLegacyVideoBidderRequestNoVideo() { - let bid = bidderRequest.bids[0]; - bid.mediaType = 'video'; - bid.params.video = ''; - } - function createVideoBidderRequestOutstream() { let bid = bidderRequest.bids[0]; bid.mediaTypes = { video: { - context: 'outstream' + context: 'outstream', + mimes: ['video/mp4', 'video/x-flv'], + api: [2], + minduration: 15, + playerSize: [640, 480], + maxduration: 30, + startdelay: 0, + playbackmethod: [2], + linearity: 1, + skip: 1, + skipafter: 15, + pos: 1, + protocols: [1, 2, 3, 4, 5, 6] }, }; + bid.params.accountId = 14062; + bid.params.siteId = 70608; + bid.params.zoneId = 335918; bid.params.video = { 'language': 'en', - 'p_aso.video.ext.skip': true, - 'p_aso.video.ext.skipdelay': 15, + 'skip': 1, + 'skipafter': 15, 'playerHeight': 320, 'playerWidth': 640, - 'size_id': 203, - 'aeParams': { - 'p_aso.video.ext.skip': '1', - 'p_aso.video.ext.skipdelay': '15' - } - }; - } - - function createVideoBidderRequestNoPlayer() { - let bid = bidderRequest.bids[0]; - bid.mediaTypes = { - video: { - context: 'instream' - }, - }; - bid.params.video = { - 'language': 'en', - 'p_aso.video.ext.skip': true, - 'p_aso.video.ext.skipdelay': 15, - 'size_id': 201, - 'aeParams': { - 'p_aso.video.ext.skip': '1', - 'p_aso.video.ext.skipdelay': '15' - } - }; - } - - function createLegacyVideoBidderRequestNoPlayer() { - let bid = bidderRequest.bids[0]; - bid.mediaType = 'video'; - bid.params.video = { - 'language': 'en', - 'p_aso.video.ext.skip': true, - 'p_aso.video.ext.skipdelay': 15, - 'size_id': 201, - 'aeParams': { - 'p_aso.video.ext.skip': '1', - 'p_aso.video.ext.skipdelay': '15' - } + 'size_id': 203 }; } @@ -434,8 +393,8 @@ describe('the rubicon adapter', function () { sandbox.stub(Math, 'random').callsFake(() => 0.1); delete bidderRequest.bids[0].params.latLong; - [request] = spec.buildRequests(bidderRequest.bids, bidderRequest); - data = parseQuery(request.data); + let [request] = spec.buildRequests(bidderRequest.bids, bidderRequest); + let data = parseQuery(request.data); expect(request.url).to.equal('//fastlane.rubiconproject.com/a/api/fastlane.json'); @@ -450,8 +409,8 @@ describe('the rubicon adapter', function () { }); bidderRequest.bids[0].params.latLong = []; - let [request] = spec.buildRequests(bidderRequest.bids, bidderRequest); - let data = parseQuery(request.data); + [request] = spec.buildRequests(bidderRequest.bids, bidderRequest); + data = parseQuery(request.data); expect(request.url).to.equal('//fastlane.rubiconproject.com/a/api/fastlane.json'); @@ -480,7 +439,6 @@ describe('the rubicon adapter', function () { bidderRequest = Object.assign({refererInfo}, bidderRequest); delete bidderRequest.bids[0].params.referrer; let [request] = spec.buildRequests(bidderRequest.bids, bidderRequest); - let data = parseQuery(request.data); expect(parseQuery(request.data).rf).to.exist; expect(parseQuery(request.data).rf).to.equal('http://www.prebid.org'); @@ -655,7 +613,6 @@ describe('the rubicon adapter', function () { }); describe('digiTrustId config', function () { - var origGetConfig; beforeEach(function () { window.DigiTrust = { getUser: sandbox.spy() @@ -1074,7 +1031,17 @@ describe('the rubicon adapter', function () { bidderRequest.bids.push(bidCopy3); const bidCopy4 = clone(bidderRequest.bids[0]); - bidCopy4.mediaType = 'video'; + bidCopy4.mediaTypes = { + video: { + context: 'instream', + playerSize: [640, 480], + mimes: ['video/mp4', 'video/x-ms-wmv'], + protocols: [2, 5], + maxduration: 30, + linearity: 1, + api: [2] + } + }; bidCopy4.params.video = { 'language': 'en', 'p_aso.video.ext.skip': true, @@ -1096,70 +1063,6 @@ describe('the rubicon adapter', function () { }); describe('for video requests', function () { - it('should make a well-formed video request with legacy mediaType config', function () { - createLegacyVideoBidderRequest(); - - sandbox.stub(Date, 'now').callsFake(() => - bidderRequest.auctionStart + 100 - ); - - let [request] = spec.buildRequests(bidderRequest.bids, bidderRequest); - let post = request.data; - - let url = request.url; - - expect(url).to.equal('//fastlane-adv.rubiconproject.com/v1/auction/video'); - - expect(post).to.have.property('page_url').that.is.a('string'); - expect(post.resolution).to.match(/\d+x\d+/); - expect(post.account_id).to.equal('14062'); - expect(post.integration).to.equal(INTEGRATION); - expect(post['x_source.tid']).to.equal('d45dd707-a418-42ec-b8a7-b70a6c6fab0b'); - expect(post).to.have.property('timeout').that.is.a('number'); - expect(post.timeout < 5000).to.equal(true); - expect(post.stash_creatives).to.equal(true); - expect(post.gdpr_consent).to.equal('BOJ/P2HOJ/P2HABABMAAAAAZ+A=='); - expect(post.gdpr).to.equal(1); - - expect(post).to.have.property('ae_pass_through_parameters'); - expect(post.ae_pass_through_parameters) - .to.have.property('p_aso.video.ext.skip') - .that.equals('1'); - expect(post.ae_pass_through_parameters) - .to.have.property('p_aso.video.ext.skipdelay') - .that.equals('15'); - - expect(post).to.have.property('slots') - .with.a.lengthOf(1); - - let slot = post.slots[0]; - - expect(slot.site_id).to.equal('70608'); - expect(slot.zone_id).to.equal('335918'); - expect(slot.position).to.equal('atf'); - expect(slot.floor).to.equal(0.01); - expect(slot.element_id).to.equal(bidderRequest.bids[0].adUnitCode); - expect(slot.name).to.equal(bidderRequest.bids[0].adUnitCode); - expect(slot.language).to.equal('en'); - expect(slot.width).to.equal(640); - expect(slot.height).to.equal(320); - expect(slot.size_id).to.equal(201); - - expect(slot).to.have.property('inventory').that.is.an('object'); - expect(slot.inventory).to.have.property('rating').that.deep.equals(['5-star']); - expect(slot.inventory).to.have.property('prodtype').that.deep.equals(['tech', 'mobile']); - - expect(slot).to.have.property('keywords') - .that.is.an('array') - .of.length(3) - .that.deep.equals(['a', 'b', 'c']); - - expect(slot).to.have.property('visitor').that.is.an('object'); - expect(slot.visitor).to.have.property('ucat').that.deep.equals(['new']); - expect(slot.visitor).to.have.property('lastsearch').that.deep.equals(['iphone']); - expect(slot.visitor).to.have.property('likes').that.deep.equals(['sports', 'video games']); - }); - it('should make a well-formed video request', function () { createVideoBidderRequest(); @@ -1170,152 +1073,84 @@ describe('the rubicon adapter', function () { let [request] = spec.buildRequests(bidderRequest.bids, bidderRequest); let post = request.data; - let url = request.url; - - expect(url).to.equal('//fastlane-adv.rubiconproject.com/v1/auction/video'); - - expect(post).to.have.property('page_url').that.is.a('string'); - expect(post.resolution).to.match(/\d+x\d+/); - expect(post.account_id).to.equal('14062'); - expect(post.integration).to.equal(INTEGRATION); - expect(post['x_source.tid']).to.equal('d45dd707-a418-42ec-b8a7-b70a6c6fab0b'); - expect(post).to.have.property('timeout').that.is.a('number'); - expect(post.timeout < 5000).to.equal(true); - expect(post.stash_creatives).to.equal(true); - expect(post.gdpr_consent).to.equal('BOJ/P2HOJ/P2HABABMAAAAAZ+A=='); - expect(post.gdpr).to.equal(1); - - expect(post).to.have.property('ae_pass_through_parameters'); - expect(post.ae_pass_through_parameters) - .to.have.property('p_aso.video.ext.skip') - .that.equals('1'); - expect(post.ae_pass_through_parameters) - .to.have.property('p_aso.video.ext.skipdelay') - .that.equals('15'); - - expect(post).to.have.property('slots') - .with.a.lengthOf(1); - - let slot = post.slots[0]; - - expect(slot.site_id).to.equal('70608'); - expect(slot.zone_id).to.equal('335918'); - expect(slot.position).to.equal('atf'); - expect(slot.floor).to.equal(0.01); - expect(slot.element_id).to.equal(bidderRequest.bids[0].adUnitCode); - expect(slot.name).to.equal(bidderRequest.bids[0].adUnitCode); - expect(slot.language).to.equal('en'); - expect(slot.width).to.equal(640); - expect(slot.height).to.equal(320); - expect(slot.size_id).to.equal(201); - - expect(slot).to.have.property('inventory').that.is.an('object'); - expect(slot.inventory).to.have.property('rating').that.deep.equals(['5-star']); - expect(slot.inventory).to.have.property('prodtype').that.deep.equals(['tech', 'mobile']); - - expect(slot).to.have.property('keywords') - .that.is.an('array') - .of.length(3) - .that.deep.equals(['a', 'b', 'c']); - - expect(slot).to.have.property('visitor').that.is.an('object'); - expect(slot.visitor).to.have.property('ucat').that.deep.equals(['new']); - expect(slot.visitor).to.have.property('lastsearch').that.deep.equals(['iphone']); - expect(slot.visitor).to.have.property('likes').that.deep.equals(['sports', 'video games']); + expect(post).to.have.property('imp') + // .with.length.of(1); + let imp = post.imp[0]; + expect(imp.id).to.equal(bidderRequest.bids[0].adUnitCode); + expect(imp.exp).to.equal(300); + expect(imp.video.w).to.equal(640); + expect(imp.video.h).to.equal(480); + expect(imp.video.pos).to.equal(1); + expect(imp.video.context).to.equal('instream'); + expect(imp.video.minduration).to.equal(15); + expect(imp.video.maxduration).to.equal(30); + expect(imp.video.startdelay).to.equal(0); + expect(imp.video.skip).to.equal(1); + expect(imp.video.skipafter).to.equal(15); + expect(imp.ext.rubicon.video.playerWidth).to.equal(640); + expect(imp.ext.rubicon.video.playerHeight).to.equal(480); + expect(imp.ext.rubicon.video.size_id).to.equal(201); + expect(imp.ext.rubicon.video.language).to.equal('en'); + // Also want it to be in post.site.content.language + expect(post.site.content.language).to.equal('en'); + expect(imp.ext.rubicon.video.skip).to.equal(1); + expect(imp.ext.rubicon.video.skipafter).to.equal(15); + expect(post.user.ext.consent).to.equal('BOJ/P2HOJ/P2HABABMAAAAAZ+A=='); + expect(post.regs.ext.gdpr).to.equal(1); + expect(post).to.have.property('ext').that.is.an('object'); + expect(post.ext.prebid.targeting.includewinners).to.equal(true); + expect(post.ext.prebid).to.have.property('cache').that.is.an('object') + expect(post.ext.prebid.cache).to.have.property('vastxml').that.is.an('object') + expect(post.ext.prebid.cache.vastxml).to.have.property('returnCreative').that.is.an('boolean') + expect(post.ext.prebid.cache.vastxml.returnCreative).to.equal(false) }); it('should send request with proper ad position', function () { createVideoBidderRequest(); - var positionBidderRequest = clone(bidderRequest); - positionBidderRequest.bids[0].params.position = 'atf'; + let positionBidderRequest = clone(bidderRequest); + positionBidderRequest.bids[0].mediaTypes.video.pos = 1; let [request] = spec.buildRequests(positionBidderRequest.bids, positionBidderRequest); - let post = request.data; - let slot = post.slots[0]; - - expect(slot.position).to.equal('atf'); - - positionBidderRequest = clone(bidderRequest); - positionBidderRequest.bids[0].params.position = 'btf'; - [request] = spec.buildRequests(positionBidderRequest.bids, positionBidderRequest); - post = request.data; - slot = post.slots[0]; + expect(request.data.imp[0].video.pos).to.equal(1); + }); - expect(slot.position).to.equal('btf'); + it('should send request with proper ad position when mediaTypes.video.pos is not defined', function () { + createVideoBidderRequest(); + let positionBidderRequest = clone(bidderRequest); + positionBidderRequest.bids[0].params.position = undefined; + positionBidderRequest.bids[0].mediaTypes.video.pos = undefined; + let [request] = spec.buildRequests(positionBidderRequest.bids, positionBidderRequest); + expect(request.data.imp[0].video.pos).to.equal(0); positionBidderRequest = clone(bidderRequest); - positionBidderRequest.bids[0].params.position = 'unknown'; + positionBidderRequest.bids[0].params.position = 'atf' + positionBidderRequest.bids[0].mediaTypes.video.pos = undefined; [request] = spec.buildRequests(positionBidderRequest.bids, positionBidderRequest); - post = request.data; - slot = post.slots[0]; - - expect(slot.position).to.equal('unknown'); + expect(request.data.imp[0].video.pos).to.equal(1); positionBidderRequest = clone(bidderRequest); - positionBidderRequest.bids[0].params.position = '123'; + positionBidderRequest.bids[0].params.position = 'btf'; + positionBidderRequest.bids[0].mediaTypes.video.pos = undefined; [request] = spec.buildRequests(positionBidderRequest.bids, positionBidderRequest); - post = request.data; - slot = post.slots[0]; - - expect(slot.position).to.equal('unknown'); + expect(request.data.imp[0].video.pos).to.equal(3); positionBidderRequest = clone(bidderRequest); - delete positionBidderRequest.bids[0].params.position; - expect(positionBidderRequest.bids[0].params.position).to.equal(undefined); + positionBidderRequest.bids[0].params.position = 'foobar'; + positionBidderRequest.bids[0].mediaTypes.video.pos = undefined; [request] = spec.buildRequests(positionBidderRequest.bids, positionBidderRequest); - post = request.data; - slot = post.slots[0]; - - expect(slot.position).to.equal('unknown'); - }); - - it('should allow a floor price override', function () { - createVideoBidderRequest(); - - sandbox.stub(Date, 'now').callsFake(() => - bidderRequest.auctionStart + 100 - ); - - var floorBidderRequest = clone(bidderRequest); - - // enter an explicit floor price // - floorBidderRequest.bids[0].params.floor = 3.25; - - let [request] = spec.buildRequests(floorBidderRequest.bids, floorBidderRequest); - let post = request.data; - - let floor = post.slots[0].floor; - - expect(floor).to.equal(3.25); - }); - - it('should validate bid request with invalid video if a mediaTypes banner property is defined', function () { - const bidRequest = { - mediaTypes: { - video: { - context: 'instream' - }, - banner: { - sizes: [[300, 250]] - } - }, - params: { - accountId: 1001, - video: { - size_id: 201 - } - }, - sizes: [[300, 250]] - } - sandbox.stub(Date, 'now').callsFake(() => - bidderRequest.auctionStart + 100 - ); - expect(spec.isBidRequestValid(bidRequest)).to.equal(true); + expect(request.data.imp[0].video.pos).to.equal(0); }); - it('should not validate bid request when a params.video object is present but no context instream or outstream is passed in', function () { + it('should properly enforce video.context to be either instream or outstream', function () { let bid = bidderRequest.bids[0]; bid.mediaTypes = { - video: {} + video: { + context: 'instream', + mimes: ['video/mp4', 'video/x-ms-wmv'], + protocols: [2, 5], + maxduration: 30, + linearity: 1, + api: [2] + } } bid.params.video = {}; @@ -1324,48 +1159,83 @@ describe('the rubicon adapter', function () { ); const bidRequestCopy = clone(bidderRequest.bids[0]); - expect(spec.isBidRequestValid(bidRequestCopy)).to.equal(false); + expect(spec.isBidRequestValid(bidRequestCopy)).to.equal(true); - bidRequestCopy.params.video = {sizeId: 201}; - expect(spec.isBidRequestValid(bidRequestCopy)).to.equal(false); + // change context to outstream, still true + bidRequestCopy.mediaTypes.video.context = 'outstream'; + expect(spec.isBidRequestValid(bidRequestCopy)).to.equal(true); - bidRequestCopy.mediaTypes.video = {context: undefined}; + // change context to random, false now + bidRequestCopy.mediaTypes.video.context = 'random'; expect(spec.isBidRequestValid(bidRequestCopy)).to.equal(false); - bidRequestCopy.mediaTypes.video = {context: ''}; + // change context to undefined, still false + bidRequestCopy.mediaTypes.video.context = undefined; expect(spec.isBidRequestValid(bidRequestCopy)).to.equal(false); - bidRequestCopy.mediaTypes.video = {context: 'random'}; + // remove context, still false + delete bidRequestCopy.mediaTypes.video.context; expect(spec.isBidRequestValid(bidRequestCopy)).to.equal(false); - - bidRequestCopy.mediaTypes.video = {context: 'instream'}; - expect(spec.isBidRequestValid(bidRequestCopy)).to.equal(true); - - bidRequestCopy.mediaTypes.video = {context: 'outstream'}; - expect(spec.isBidRequestValid(bidRequestCopy)).to.equal(true); }); - it('should not validate bid request when an invalid video object is passed in with legacy config mediaType', function () { - createLegacyVideoBidderRequestNoVideo(); + it('should enforce the new required mediaTypes.video params', function () { + createVideoBidderRequest(); + sandbox.stub(Date, 'now').callsFake(() => bidderRequest.auctionStart + 100 ); - const bidderRequestCopy = clone(bidderRequest); - bidderRequestCopy.bids[0].params.video = {}; - expect(spec.isBidRequestValid(bidderRequestCopy.bids[0])).to.equal(false); + expect(spec.isBidRequestValid(bidderRequest.bids[0])).to.equal(true); + + // change mimes to a non array, no good + createVideoBidderRequest(); + bidderRequest.bids[0].mediaTypes.video.mimes = 'video/mp4'; + expect(spec.isBidRequestValid(bidderRequest.bids[0])).to.equal(false); + + // delete mimes, no good + createVideoBidderRequest(); + delete bidderRequest.bids[0].mediaTypes.video.mimes; + expect(spec.isBidRequestValid(bidderRequest.bids[0])).to.equal(false); + + // change protocols to an int not array of ints, no good + createVideoBidderRequest(); + bidderRequest.bids[0].mediaTypes.video.protocols = 1; + expect(spec.isBidRequestValid(bidderRequest.bids[0])).to.equal(false); + + // delete protocols, no good + createVideoBidderRequest(); + delete bidderRequest.bids[0].mediaTypes.video.protocols; + expect(spec.isBidRequestValid(bidderRequest.bids[0])).to.equal(false); + + // change maxduration to an string, no good + createVideoBidderRequest(); + bidderRequest.bids[0].mediaTypes.video.maxduration = 'string'; + expect(spec.isBidRequestValid(bidderRequest.bids[0])).to.equal(false); - bidderRequestCopy.bids[0].params.video = {size_id: undefined}; - expect(spec.isBidRequestValid(bidderRequestCopy.bids[0])).to.equal(false); + // delete maxduration, no good + createVideoBidderRequest(); + delete bidderRequest.bids[0].mediaTypes.video.maxduration; + expect(spec.isBidRequestValid(bidderRequest.bids[0])).to.equal(false); - bidderRequestCopy.bids[0].params.video = {size_id: 'size'}; - expect(spec.isBidRequestValid(bidderRequestCopy.bids[0])).to.equal(false); + // change linearity to an string, no good + createVideoBidderRequest(); + bidderRequest.bids[0].mediaTypes.video.linearity = 'string'; + expect(spec.isBidRequestValid(bidderRequest.bids[0])).to.equal(false); - bidderRequestCopy.bids[0].params.video = {size_id: '201'}; - expect(spec.isBidRequestValid(bidderRequestCopy.bids[0])).to.equal(true); + // delete linearity, no good + createVideoBidderRequest(); + delete bidderRequest.bids[0].mediaTypes.video.linearity; + expect(spec.isBidRequestValid(bidderRequest.bids[0])).to.equal(false); - bidderRequestCopy.bids[0].params.video = {size_id: 201}; - expect(spec.isBidRequestValid(bidderRequestCopy.bids[0])).to.equal(true); + // change api to an string, no good + createVideoBidderRequest(); + bidderRequest.bids[0].mediaTypes.video.api = 'string'; + expect(spec.isBidRequestValid(bidderRequest.bids[0])).to.equal(false); + + // delete api, no good + createVideoBidderRequest(); + delete bidderRequest.bids[0].mediaTypes.video.api; + expect(spec.isBidRequestValid(bidderRequest.bids[0])).to.equal(false); }); it('bid request is valid when video context is outstream', function () { @@ -1378,11 +1248,10 @@ describe('the rubicon adapter', function () { let [request] = spec.buildRequests(bidRequestCopy.bids, bidRequestCopy); expect(spec.isBidRequestValid(bidderRequest.bids[0])).to.equal(true); - expect(request.data.slots[0].size_id).to.equal(203); + expect(request.data.imp[0].ext.rubicon.video.size_id).to.equal(203); }); it('should send banner request when outstream or instream video included but no rubicon video obect is present', function () { - let bid = bidderRequest.bids[0]; // add banner and video mediaTypes bidderRequest.mediaTypes = { banner: { @@ -1429,34 +1298,6 @@ describe('the rubicon adapter', function () { expect(requests.length).to.equal(1); expect(requests[0].url).to.equal(FASTLANE_ENDPOINT); }); - - it('should get size from bid.sizes too', () => { - createVideoBidderRequestNoPlayer(); - sandbox.stub(Date, 'now').callsFake(() => - bidderRequest.auctionStart + 100 - ); - - const bidRequestCopy = clone(bidderRequest); - - let [request] = spec.buildRequests(bidRequestCopy.bids, bidRequestCopy); - - expect(request.data.slots[0].width).to.equal(300); - expect(request.data.slots[0].height).to.equal(250); - }); - - it('should get size from bid.sizes too with legacy config mediaType', function () { - createLegacyVideoBidderRequestNoPlayer(); - sandbox.stub(Date, 'now').callsFake(() => - bidderRequest.auctionStart + 100 - ); - - const bidRequestCopy = clone(bidderRequest); - - let [request] = spec.buildRequests(bidRequestCopy.bids, bidRequestCopy); - - expect(request.data.slots[0].width).to.equal(300); - expect(request.data.slots[0].height).to.equal(250); - }); }); describe('combineSlotUrlParams', function () { @@ -1531,14 +1372,12 @@ describe('the rubicon adapter', function () { expect(legacyVideoTypeBidRequest).is.equal(true); }); - it('should return false if mediaType is video and size_id is not defined', function () { - expect(spec.isBidRequestValid({ - bid: 99, - mediaType: 'video', - params: { - video: {} - } - })).is.equal(false); + it('should return false if trying to use legacy mediaType with video', function () { + createVideoBidderRequest(); + delete bidderRequest.bids[0].mediaTypes; + bidderRequest.bids[0].mediaType = 'video'; + const legacyVideoTypeBidRequest = hasVideoMediaType(bidderRequest.bids[0]); + expect(legacyVideoTypeBidRequest).is.equal(false); }); it('should return false if bidRequest.mediaType is not equal to video', function () { @@ -2069,30 +1908,31 @@ describe('the rubicon adapter', function () { it('should register a successful bid', function () { let response = { - 'status': 'ok', - 'ads': { - '/19968336/header-bid-tag-0': [ - { - 'status': 'ok', - 'cpm': 1, - 'tier': 'tier0200', - 'targeting': { - 'rpfl_8000': '201_tier0200', - 'rpfl_elemid': '/19968336/header-bid-tag-0' + cur: 'USD', + seatbid: [{ + bid: [{ + id: '0', + impid: 'instream_video1', + price: 2, + crid: '4259970', + ext: { + bidder: { + rp: { + mime: 'application/javascript', + size_id: 201 + } }, - 'impression_id': 'a40fe16e-d08d-46a9-869d-2e1573599e0c', - 'site_id': 88888, - 'zone_id': 54321, - 'creative_type': 'video', - 'creative_depot_url': 'https://fastlane-adv.rubiconproject.com/v1/creative/a40fe16e-d08d-46a9-869d-2e1573599e0c.xml', - 'ad_id': 999999, - 'creative_id': 'crid-999999', - 'size_id': 201, - 'advertiser': 12345 + prebid: { + targeting: { + hb_uuid: '0c498f63-5111-4bed-98e2-9be7cb932a64' + }, + type: 'video' + } } - ] - }, - 'account_id': 7780 + }], + group: 0, + seat: 'rubicon' + }], }; let bids = spec.interpretResponse({body: response}, { @@ -2101,16 +1941,16 @@ describe('the rubicon adapter', function () { expect(bids).to.be.lengthOf(1); - expect(bids[0].creativeId).to.equal('crid-999999'); - expect(bids[0].cpm).to.equal(1); + expect(bids[0].creativeId).to.equal('4259970'); + expect(bids[0].cpm).to.equal(2); expect(bids[0].ttl).to.equal(300); expect(bids[0].netRevenue).to.equal(false); - expect(bids[0].vastUrl).to.equal( - 'https://fastlane-adv.rubiconproject.com/v1/creative/a40fe16e-d08d-46a9-869d-2e1573599e0c.xml' - ); - expect(bids[0].impression_id).to.equal('a40fe16e-d08d-46a9-869d-2e1573599e0c'); + expect(bids[0].adserverTargeting).to.deep.equal({hb_uuid: '0c498f63-5111-4bed-98e2-9be7cb932a64'}); expect(bids[0].mediaType).to.equal('video'); - expect(bids[0].videoCacheKey).to.equal('a40fe16e-d08d-46a9-869d-2e1573599e0c'); + expect(bids[0].bidderCode).to.equal('rubicon'); + expect(bids[0].currency).to.equal('USD'); + expect(bids[0].width).to.equal(640); + expect(bids[0].height).to.equal(480); }); }); }); diff --git a/test/spec/modules/unrulyBidAdapter_spec.js b/test/spec/modules/unrulyBidAdapter_spec.js index b27fdc5c78b..e39f9a8e996 100644 --- a/test/spec/modules/unrulyBidAdapter_spec.js +++ b/test/spec/modules/unrulyBidAdapter_spec.js @@ -146,7 +146,8 @@ describe('UnrulyAdapter', function () { creativeId: 'mockBidId', ttl: 360, currency: 'USD', - renderer: fakeRenderer + renderer: fakeRenderer, + mediaType: 'video' } ]) }); diff --git a/test/spec/modules/yieldlabBidAdapter_spec.js b/test/spec/modules/yieldlabBidAdapter_spec.js index 497e9c7b894..c2e12408cdd 100644 --- a/test/spec/modules/yieldlabBidAdapter_spec.js +++ b/test/spec/modules/yieldlabBidAdapter_spec.js @@ -11,7 +11,8 @@ const REQUEST = { 'targeting': { 'key1': 'value1', 'key2': 'value2' - } + }, + 'extId': 'abc' }, 'bidderRequestId': '143346cf0f1731', 'auctionId': '2e41f65424c87c', @@ -104,6 +105,7 @@ describe('yieldlabBidAdapter', function () { expect(result[0].ttl).to.equal(300) expect(result[0].referrer).to.equal('') expect(result[0].ad).to.include('