• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

prebid / Prebid.js / 21444465511

28 Jan 2026 03:32PM UTC coverage: 96.22% (+0.003%) from 96.217%
21444465511

push

github

web-flow
Taboola support extra signals (#14299)

* add deferredBilling support using onBidBillable

* update burl setting

* support nurl firing logic

* add extra signals to taboola request

* add extra ad signals

* fix missing semicolon

* use Prebid's built-in counters

* updated detectBot logic

41716 of 51289 branches covered (81.34%)

216 of 221 new or added lines in 2 files covered. (97.74%)

130 existing lines in 9 files now uncovered.

209165 of 217382 relevant lines covered (96.22%)

71.73 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

98.88
/modules/topLevelPaapi.js
1
import {submodule} from '../src/hook.js';
1✔
2
import {config} from '../src/config.js';
3
import {logError, logInfo, logWarn, mergeDeep} from '../src/utils.js';
4
import {auctionStore} from '../libraries/weakStore/weakStore.js';
5
import {getGlobal} from '../src/prebidGlobal.js';
6
import {emit} from '../src/events.js';
7
import {BID_STATUS, EVENTS} from '../src/constants.js';
8
import {PbPromise} from '../src/utils/promise.js';
9
import {getBidToRender, getRenderingData, markWinningBid} from '../src/adRendering.js';
10

11
let getPAAPIConfig, expandFilters, moduleConfig;
12

13
const paapiBids = auctionStore();
1✔
14
const MODULE_NAME = 'topLevelPaapi';
1✔
15

16
config.getConfig('paapi', (cfg) => {
1✔
17
  moduleConfig = cfg.paapi?.topLevelSeller;
145✔
18
  if (moduleConfig) {
145✔
19
    getBidToRender.before(renderPaapiHook);
41✔
20
    getRenderingData.before(getRenderingDataHook);
41✔
21
    markWinningBid.before(markWinningBidHook);
41✔
22
  } else {
23
    getBidToRender.getHooks({hook: renderPaapiHook}).remove();
104✔
24
    getRenderingData.getHooks({hook: getRenderingDataHook}).remove();
104✔
25
    markWinningBid.getHooks({hook: markWinningBidHook}).remove();
104✔
26
  }
27
});
28

29
function isPaapiBid(bid) {
30
  return bid?.source === 'paapi';
24✔
31
}
32

33
function bidIfRenderable(bid) {
34
  if (bid && !bid.urn) {
18✔
35
    logWarn(MODULE_NAME, 'rendering in fenced frames is not supported. Consider using resolveToConfig: false', bid);
7✔
36
    return;
7✔
37
  }
38
  return bid;
11✔
39
}
40

41
function renderPaapiHook(next, adId, forRender = true, cb) {
21!
42
  PbPromise
21✔
43
    .resolve()
44
    .then(() => {
45
      const ids = parsePaapiAdId(adId);
21✔
46
      if (ids) {
21✔
47
        const [auctionId, adUnitCode] = ids;
5✔
48
        return paapiBids(auctionId)?.[adUnitCode]?.then(bid => {
5✔
49
          if (!bid) {
5!
UNCOV
50
            logWarn(MODULE_NAME, `No PAAPI bid found for auctionId: "${auctionId}", adUnit: "${adUnitCode}"`);
×
51
          }
52
          return bidIfRenderable(bid);
5✔
53
        });
54
      }
55
    })
56
    .then((bid) => {
57
      if (bid != null) return bid;
21✔
58
      return new Promise(resolve => next(adId, forRender, resolve))
19✔
59
    })
60
    .then((bid) => {
61
      if (bid == null || isPaapiBid(bid) || bid?.status === BID_STATUS.RENDERED) return bid;
21✔
62
      return getPAAPIBids({adUnitCode: bid.adUnitCode}).then(res => {
13✔
63
        const paapiBid = bidIfRenderable(res[bid.adUnitCode]);
13✔
64
        if (paapiBid) {
13✔
65
          if (!forRender) return paapiBid;
5✔
66
          if (forRender && paapiBid.status !== BID_STATUS.RENDERED) {
4✔
67
            paapiBid.overriddenAdId = bid.adId;
2✔
68
            logInfo(MODULE_NAME, 'overriding contextual bid with PAAPI bid', bid, paapiBid)
2✔
69
            return paapiBid;
2✔
70
          }
71
        }
72
        return bid;
10✔
73
      });
74
    })
75
    .then(cb);
76
}
77

78
export function getRenderingDataHook(next, bid, options) {
79
  if (isPaapiBid(bid)) {
2✔
80
    next.bail({
1✔
81
      width: bid.width,
82
      height: bid.height,
83
      adUrl: bid.urn
84
    });
85
  } else {
86
    next(bid, options);
1✔
87
  }
88
}
89

90
export function markWinningBidHook(next, bid) {
91
  if (isPaapiBid(bid)) {
2✔
92
    emit(EVENTS.BID_WON, bid);
1✔
93
    next.bail();
1✔
94
  } else {
95
    next(bid);
1✔
96
  }
97
}
98

99
function getBaseAuctionConfig() {
100
  if (moduleConfig?.auctionConfig) {
36✔
101
    return Object.assign({
34✔
102
      resolveToConfig: false
103
    }, moduleConfig.auctionConfig);
104
  }
105
}
106

107
function onAuctionConfig(auctionId, auctionConfigs) {
108
  const base = getBaseAuctionConfig();
36✔
109
  if (base) {
36✔
110
    Object.entries(auctionConfigs).forEach(([adUnitCode, auctionConfig]) => {
38✔
111
      mergeDeep(auctionConfig, base);
38✔
112
      if (moduleConfig.autorun ?? true) {
38!
113
        getPAAPIBids({adUnitCode, auctionId});
1✔
114
      }
115
    });
116
  }
117
}
118

119
export function parsePaapiSize(size) {
120
  /* From https://github.com/WICG/turtledove/blob/main/FLEDGE.md#12-interest-group-attributes:
121
   *  Each size has the format {width: widthVal, height: heightVal},
122
   *  where the values can have either pixel units (e.g. 100 or '100px') or screen dimension coordinates (e.g. 100sw or 100sh).
123
   */
124
  if (typeof size === 'number') return size;
53✔
125
  if (typeof size === 'string') {
6✔
126
    const px = /^(\d+)(px)?$/.exec(size)?.[1];
4✔
127
    if (px) {
4✔
128
      return parseInt(px, 10);
2✔
129
    }
130
  }
131
  return null;
4✔
132
}
133

134
export function getPaapiAdId(auctionId, adUnitCode) {
135
  return `paapi:/${auctionId.replace(/:/g, '::')}/:/${adUnitCode.replace(/:/g, '::')}`;
26✔
136
}
137

138
export function parsePaapiAdId(adId) {
139
  const match = /^paapi:\/(.*)\/:\/(.*)$/.exec(adId);
46✔
140
  if (match) {
46✔
141
    return [match[1], match[2]].map(s => s.replace(/::/g, ':'));
52✔
142
  }
143
}
144

145
/**
146
 * Returns the PAAPI runAdAuction result for the given filters, as a map from ad unit code to auction result
147
 * (an object with `width`, `height`, and one of `urn` or `frameConfig`).
148
 *
149
 * @param filters
150
 * @param raa
151
 * @return {Promise<{[p: string]: any}>}
152
 */
153
export function getPAAPIBids(filters, raa = (...args) => navigator.runAdAuction(...args)) {
51✔
154
  return Promise.all(
51✔
155
    Object.entries(expandFilters(filters))
156
      .map(([adUnitCode, auctionId]) => {
53✔
157
        const bids = paapiBids(auctionId);
53✔
158
        if (bids && !bids.hasOwnProperty(adUnitCode)) {
53✔
159
          const auctionConfig = getPAAPIConfig({adUnitCode, auctionId})[adUnitCode];
36✔
160
          if (auctionConfig) {
36✔
161
            emit(EVENTS.RUN_PAAPI_AUCTION, {
34✔
162
              auctionId,
163
              adUnitCode,
164
              auctionConfig
165
            });
166
            bids[adUnitCode] = new Promise((resolve, reject) => raa(auctionConfig).then(resolve, reject))
34✔
167
              .then(result => {
168
                if (result) {
27✔
169
                  const bid = {
23✔
170
                    source: 'paapi',
171
                    adId: getPaapiAdId(auctionId, adUnitCode),
172
                    width: parsePaapiSize(auctionConfig.requestedSize?.width),
173
                    height: parsePaapiSize(auctionConfig.requestedSize?.height),
174
                    adUnitCode,
175
                    auctionId,
176
                    [typeof result === 'string' ? 'urn' : 'frameConfig']: result,
23✔
177
                    auctionConfig,
178
                  };
179
                  emit(EVENTS.PAAPI_BID, bid);
23✔
180
                  return bid;
23✔
181
                } else {
182
                  emit(EVENTS.PAAPI_NO_BID, {auctionId, adUnitCode, auctionConfig});
4✔
183
                  return null;
4✔
184
                }
185
              }).catch(error => {
186
                logError(MODULE_NAME, `error (auction "${auctionId}", adUnit "${adUnitCode}"):`, error);
7✔
187
                emit(EVENTS.PAAPI_ERROR, {auctionId, adUnitCode, error, auctionConfig});
7✔
188
                return null;
7✔
189
              });
190
          }
191
        }
192
        return bids?.[adUnitCode]?.then(res => [adUnitCode, res]);
53✔
193
      }).filter(e => e)
53✔
194
  ).then(result => Object.fromEntries(result));
51✔
195
}
196

197
getGlobal().getPAAPIBids = (filters) => getPAAPIBids(filters);
1✔
198

199
export const topLevelPAAPI = {
1✔
200
  name: MODULE_NAME,
201
  init(params) {
202
    getPAAPIConfig = params.getPAAPIConfig;
53✔
203
    expandFilters = params.expandFilters;
53✔
204
  },
205
  onAuctionConfig
206
};
207
submodule('paapi', topLevelPAAPI);
1✔
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc