@bidkernel/analytics 0.11.0 → 0.16.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/analytics.global.js +1 -1
- package/dist/index.d.mts +73 -2
- package/dist/index.d.ts +73 -2
- package/dist/index.js +234 -78
- package/dist/index.mjs +233 -78
- package/package.json +1 -1
package/dist/index.d.ts
CHANGED
|
@@ -54,11 +54,24 @@ interface AnalyticsConfig {
|
|
|
54
54
|
errorsEnabled?: boolean;
|
|
55
55
|
/** Whether to enable client-side IAB viewability and time-in-view measurement. Defaults to true. */
|
|
56
56
|
viewabilityEnabled?: boolean;
|
|
57
|
+
/**
|
|
58
|
+
* Whether identifiers may be written to localStorage (session id and its
|
|
59
|
+
* timestamp, exit batches). Defaults to true. Set false until storage
|
|
60
|
+
* consent is known, then flip with setStorageAllowed(true); events are
|
|
61
|
+
* always collected and sent regardless.
|
|
62
|
+
*/
|
|
63
|
+
storageAllowed?: boolean;
|
|
57
64
|
logLevel?: "DEBUG" | "INFO" | "WARN" | "ERROR";
|
|
58
65
|
/** Window global holding the Prebid instance to attach to. Defaults to "pbjs". */
|
|
59
66
|
pbjsGlobalName?: string;
|
|
60
67
|
/** Whether to attach pbjs.onEvent listeners. Defaults to true. Set false for full SDK mode. */
|
|
61
68
|
attachPbjsListeners?: boolean;
|
|
69
|
+
/**
|
|
70
|
+
* Milliseconds a batch holding a revenue event (impression, win, click)
|
|
71
|
+
* waits before it is sent, so the events of one render cycle share a POST.
|
|
72
|
+
* 0 sends on the next tick. Defaults to 3000.
|
|
73
|
+
*/
|
|
74
|
+
revenueFlushDelayMs?: number;
|
|
62
75
|
}
|
|
63
76
|
interface SlotObserveOptions {
|
|
64
77
|
adUnitCode?: string;
|
|
@@ -117,6 +130,15 @@ interface SlotViewabilityState {
|
|
|
117
130
|
* registerPrebidAnalytics.
|
|
118
131
|
*/
|
|
119
132
|
declare function isTrustedEndpoint(raw: string): boolean;
|
|
133
|
+
/**
|
|
134
|
+
* The visitor's session id: a 30-minute rolling session persisted in
|
|
135
|
+
* localStorage when storage is allowed, kept in memory otherwise. A session
|
|
136
|
+
* begun in memory (before storage consent) is adopted and persisted the
|
|
137
|
+
* first time this runs with storage allowed, so consent never splits a
|
|
138
|
+
* session. Exported so an integration can report the same id the adapter
|
|
139
|
+
* stamps on its batches.
|
|
140
|
+
*/
|
|
141
|
+
declare function getOrCreateSessionId(): string;
|
|
120
142
|
/**
|
|
121
143
|
* Canonical duplicate-detection key for a Prebid event. Shared by the adapter
|
|
122
144
|
* and the diagnostic extension so the two can never drift apart. Covers the
|
|
@@ -143,6 +165,13 @@ declare class BidkernelPrebidAnalytics {
|
|
|
143
165
|
private static activeInstances;
|
|
144
166
|
static getActiveInstances(): Set<BidkernelPrebidAnalytics>;
|
|
145
167
|
static initImaInterception(): void;
|
|
168
|
+
/**
|
|
169
|
+
* Allows or forbids localStorage writes for every instance on the page.
|
|
170
|
+
* Collection and delivery are unaffected: only the persisted session id,
|
|
171
|
+
* its timestamp and exit batches are gated.
|
|
172
|
+
*/
|
|
173
|
+
static setStorageAllowed(allowed: boolean): void;
|
|
174
|
+
static isStorageAllowed(): boolean;
|
|
146
175
|
private config;
|
|
147
176
|
private queue;
|
|
148
177
|
private errorCount;
|
|
@@ -156,7 +185,7 @@ declare class BidkernelPrebidAnalytics {
|
|
|
156
185
|
private consecutiveSendFailures;
|
|
157
186
|
private nextSendAllowedAt;
|
|
158
187
|
private replayedEventCount;
|
|
159
|
-
private
|
|
188
|
+
private revenueFlushTimer;
|
|
160
189
|
private persistedBatchKeys;
|
|
161
190
|
private persistedCleanupTimer;
|
|
162
191
|
private intersectionObserver;
|
|
@@ -165,6 +194,10 @@ declare class BidkernelPrebidAnalytics {
|
|
|
165
194
|
private slotRefreshIndices;
|
|
166
195
|
private slotLastAuctionIds;
|
|
167
196
|
private pendingThresholdListeners;
|
|
197
|
+
private pendingViewableListeners;
|
|
198
|
+
private slotElements;
|
|
199
|
+
private clickDetachCleanups;
|
|
200
|
+
private lastClickAt;
|
|
168
201
|
private slotEmittedImpressionKeys;
|
|
169
202
|
private cachedWinningBids;
|
|
170
203
|
private videoDetachCleanups;
|
|
@@ -172,10 +205,48 @@ declare class BidkernelPrebidAnalytics {
|
|
|
172
205
|
private logger;
|
|
173
206
|
constructor(config: AnalyticsConfig);
|
|
174
207
|
enable(): void;
|
|
208
|
+
/**
|
|
209
|
+
* Binds the Prebid event handlers on the configured global and replays
|
|
210
|
+
* pbjs.getEvents() history. enable() calls this when attachPbjsListeners is
|
|
211
|
+
* true. An integration that loads Prebid lazily constructs the instance with
|
|
212
|
+
* attachPbjsListeners=false and calls this once pbjs exists; disable() (and
|
|
213
|
+
* therefore navigate()) drops the handlers, so call it again after either.
|
|
214
|
+
* Idempotent. Returns true when handlers are bound after the call, false
|
|
215
|
+
* when the instance is disabled or pbjs.onEvent is not available yet.
|
|
216
|
+
*/
|
|
217
|
+
attachPbjs(): boolean;
|
|
175
218
|
disable(): void;
|
|
176
219
|
private handleVisibilityChange;
|
|
177
220
|
private handleIntersection;
|
|
178
221
|
private handleDwellComplete;
|
|
222
|
+
private notifyViewable;
|
|
223
|
+
/**
|
|
224
|
+
* Calls back once per render cycle when the slot has met the IAB viewable
|
|
225
|
+
* standard this adapter measures (immediately if it already has). Works
|
|
226
|
+
* before the slot is observed: the listener waits for observeSlot. Returns
|
|
227
|
+
* an unsubscribe function.
|
|
228
|
+
*/
|
|
229
|
+
/**
|
|
230
|
+
* Names the element to measure for a slot when the creative renders
|
|
231
|
+
* somewhere other than the slot element (a sticky bar, an interstitial).
|
|
232
|
+
* Applies to the current cycle at once and to every later render.
|
|
233
|
+
*/
|
|
234
|
+
setSlotElement(slotId: string, element: HTMLElement): void;
|
|
235
|
+
/**
|
|
236
|
+
* Records a click on the slot's current creative, attributed to the cached
|
|
237
|
+
* winning bid. Repeats within one second are one click. Returns whether a
|
|
238
|
+
* CLICK was recorded.
|
|
239
|
+
*/
|
|
240
|
+
recordClick(slotId: string, options?: {
|
|
241
|
+
metadata?: Record<string, string>;
|
|
242
|
+
}): boolean;
|
|
243
|
+
/**
|
|
244
|
+
* Detects clicks into a cross-origin creative frame: a click inside an
|
|
245
|
+
* iframe moves focus into it and blurs the window. Returns a detach
|
|
246
|
+
* function; every tracker is detached on disable().
|
|
247
|
+
*/
|
|
248
|
+
attachClickTracker(element: HTMLElement, slotId: string): () => void;
|
|
249
|
+
onViewable(slotId: string, callback: (slotId: string) => void): () => void;
|
|
179
250
|
private scheduleThresholdTimers;
|
|
180
251
|
private checkThresholdListeners;
|
|
181
252
|
private getCurrentTimeInView;
|
|
@@ -331,4 +402,4 @@ interface AdsGlobal {
|
|
|
331
402
|
*/
|
|
332
403
|
declare function getbidkernel(alias?: string): AdsGlobal;
|
|
333
404
|
|
|
334
|
-
export { type AdsGlobal, type AnalyticsConfig, BidkernelPrebidAnalytics, type CachedWinningBid, PrebidEventDeduper, type SizeMapping, type SlotConfig, type SlotObserveOptions, type SlotSize, type SlotSizes, type SlotViewabilityState, type VideoPlayerOptions, getPrebidEventKey, getbidkernel, hookImaPrototype, initImaInterception, isTrustedEndpoint, registerPrebidAnalytics };
|
|
405
|
+
export { type AdsGlobal, type AnalyticsConfig, BidkernelPrebidAnalytics, type CachedWinningBid, PrebidEventDeduper, type SizeMapping, type SlotConfig, type SlotObserveOptions, type SlotSize, type SlotSizes, type SlotViewabilityState, type VideoPlayerOptions, getOrCreateSessionId, getPrebidEventKey, getbidkernel, hookImaPrototype, initImaInterception, isTrustedEndpoint, registerPrebidAnalytics };
|
package/dist/index.js
CHANGED
|
@@ -22,6 +22,7 @@ var src_exports = {};
|
|
|
22
22
|
__export(src_exports, {
|
|
23
23
|
BidkernelPrebidAnalytics: () => BidkernelPrebidAnalytics,
|
|
24
24
|
PrebidEventDeduper: () => PrebidEventDeduper,
|
|
25
|
+
getOrCreateSessionId: () => getOrCreateSessionId,
|
|
25
26
|
getPrebidEventKey: () => getPrebidEventKey,
|
|
26
27
|
getbidkernel: () => getbidkernel,
|
|
27
28
|
hookImaPrototype: () => hookImaPrototype,
|
|
@@ -1075,7 +1076,8 @@ var EVENT_NAME_TO_TYPE = {
|
|
|
1075
1076
|
timeInView: TraceEventType.TIME_IN_VIEW,
|
|
1076
1077
|
viewable: TraceEventType.VIEWABLE
|
|
1077
1078
|
};
|
|
1078
|
-
var
|
|
1079
|
+
var REVENUE_FLUSH_DELAY_MS = 3e3;
|
|
1080
|
+
var REVENUE_FLUSH_TYPES = /* @__PURE__ */ new Set([
|
|
1079
1081
|
TraceEventType.IMPRESSION,
|
|
1080
1082
|
TraceEventType.BID_WIN,
|
|
1081
1083
|
TraceEventType.CLICK
|
|
@@ -1084,11 +1086,13 @@ var CACHED_BID_TTL_MS = 30 * 60 * 1e3;
|
|
|
1084
1086
|
var MAX_CACHED_BID_KEYS = 200;
|
|
1085
1087
|
var MAX_IMPRESSION_KEYS = 1e3;
|
|
1086
1088
|
var MAX_TRACKED_AUCTIONS = 50;
|
|
1089
|
+
var CLICK_DEDUP_MS = 1e3;
|
|
1087
1090
|
var SESSION_KEY = "_bidkernel_session";
|
|
1088
1091
|
var SESSION_TS_KEY = "_bidkernel_session_ts";
|
|
1089
1092
|
var THIRTY_MINUTES_MS = 30 * 60 * 1e3;
|
|
1090
1093
|
var inMemorySessionId = null;
|
|
1091
1094
|
var inMemorySessionTs = 0;
|
|
1095
|
+
var storageAllowed = true;
|
|
1092
1096
|
function generateUUID() {
|
|
1093
1097
|
if (typeof crypto !== "undefined" && typeof crypto.randomUUID === "function") {
|
|
1094
1098
|
return crypto.randomUUID();
|
|
@@ -1158,6 +1162,7 @@ function sameEndpointOrigin(a, b) {
|
|
|
1158
1162
|
function extendSession() {
|
|
1159
1163
|
const now = Date.now();
|
|
1160
1164
|
inMemorySessionTs = now;
|
|
1165
|
+
if (!storageAllowed) return;
|
|
1161
1166
|
try {
|
|
1162
1167
|
if (typeof localStorage !== "undefined") {
|
|
1163
1168
|
localStorage.setItem(SESSION_TS_KEY, now.toString());
|
|
@@ -1167,18 +1172,23 @@ function extendSession() {
|
|
|
1167
1172
|
}
|
|
1168
1173
|
function getOrCreateSessionId() {
|
|
1169
1174
|
const now = Date.now();
|
|
1175
|
+
const memoryFresh = !!inMemorySessionId && !!inMemorySessionTs && now - inMemorySessionTs <= THIRTY_MINUTES_MS;
|
|
1170
1176
|
try {
|
|
1171
|
-
if (typeof localStorage !== "undefined") {
|
|
1177
|
+
if (storageAllowed && typeof localStorage !== "undefined") {
|
|
1172
1178
|
const storedId = localStorage.getItem(SESSION_KEY);
|
|
1173
1179
|
const tsStr = localStorage.getItem(SESSION_TS_KEY);
|
|
1174
1180
|
const storedTs = tsStr ? parseInt(tsStr, 10) || 0 : 0;
|
|
1175
1181
|
if (storedId && storedTs && now - storedTs <= THIRTY_MINUTES_MS) {
|
|
1176
1182
|
localStorage.setItem(SESSION_TS_KEY, now.toString());
|
|
1183
|
+
inMemorySessionId = storedId;
|
|
1184
|
+
inMemorySessionTs = now;
|
|
1177
1185
|
return storedId;
|
|
1178
1186
|
}
|
|
1179
|
-
const newId = generateUUID();
|
|
1187
|
+
const newId = memoryFresh ? inMemorySessionId : generateUUID();
|
|
1180
1188
|
localStorage.setItem(SESSION_KEY, newId);
|
|
1181
1189
|
localStorage.setItem(SESSION_TS_KEY, now.toString());
|
|
1190
|
+
inMemorySessionId = newId;
|
|
1191
|
+
inMemorySessionTs = now;
|
|
1182
1192
|
return newId;
|
|
1183
1193
|
}
|
|
1184
1194
|
} catch {
|
|
@@ -1544,6 +1554,17 @@ var BidkernelPrebidAnalytics = class _BidkernelPrebidAnalytics {
|
|
|
1544
1554
|
static initImaInterception() {
|
|
1545
1555
|
initImaInterception();
|
|
1546
1556
|
}
|
|
1557
|
+
/**
|
|
1558
|
+
* Allows or forbids localStorage writes for every instance on the page.
|
|
1559
|
+
* Collection and delivery are unaffected: only the persisted session id,
|
|
1560
|
+
* its timestamp and exit batches are gated.
|
|
1561
|
+
*/
|
|
1562
|
+
static setStorageAllowed(allowed) {
|
|
1563
|
+
storageAllowed = allowed;
|
|
1564
|
+
}
|
|
1565
|
+
static isStorageAllowed() {
|
|
1566
|
+
return storageAllowed;
|
|
1567
|
+
}
|
|
1547
1568
|
config;
|
|
1548
1569
|
queue = [];
|
|
1549
1570
|
errorCount = 0;
|
|
@@ -1559,7 +1580,7 @@ var BidkernelPrebidAnalytics = class _BidkernelPrebidAnalytics {
|
|
|
1559
1580
|
consecutiveSendFailures = 0;
|
|
1560
1581
|
nextSendAllowedAt = 0;
|
|
1561
1582
|
replayedEventCount = 0;
|
|
1562
|
-
|
|
1583
|
+
revenueFlushTimer = null;
|
|
1563
1584
|
// localStorage keys of exit batches this instance persisted, so a page that
|
|
1564
1585
|
// survives its own pagehide/hidden (bfcache restore, tab re-focus) can
|
|
1565
1586
|
// remove them instead of leaving them for a duplicate resend.
|
|
@@ -1572,6 +1593,12 @@ var BidkernelPrebidAnalytics = class _BidkernelPrebidAnalytics {
|
|
|
1572
1593
|
slotRefreshIndices = /* @__PURE__ */ new Map();
|
|
1573
1594
|
slotLastAuctionIds = /* @__PURE__ */ new Map();
|
|
1574
1595
|
pendingThresholdListeners = /* @__PURE__ */ new Map();
|
|
1596
|
+
pendingViewableListeners = /* @__PURE__ */ new Map();
|
|
1597
|
+
// Element a subscriber named for a slot (a sticky bar or interstitial that
|
|
1598
|
+
// renders outside the slot element); wins over document.getElementById.
|
|
1599
|
+
slotElements = /* @__PURE__ */ new Map();
|
|
1600
|
+
clickDetachCleanups = /* @__PURE__ */ new Set();
|
|
1601
|
+
lastClickAt = /* @__PURE__ */ new Map();
|
|
1575
1602
|
// Impression deduplication per slot and refresh cycle (strictly 1 impression per cycle)
|
|
1576
1603
|
slotEmittedImpressionKeys = /* @__PURE__ */ new Set();
|
|
1577
1604
|
// Winning bid cache from bidWon to marry with subsequent render triggers (adRenderSucceeded, video, etc.)
|
|
@@ -1582,6 +1609,9 @@ var BidkernelPrebidAnalytics = class _BidkernelPrebidAnalytics {
|
|
|
1582
1609
|
auctionBidderOutcomes = /* @__PURE__ */ new Map();
|
|
1583
1610
|
logger;
|
|
1584
1611
|
constructor(config) {
|
|
1612
|
+
if (config.storageAllowed !== void 0) {
|
|
1613
|
+
_BidkernelPrebidAnalytics.setStorageAllowed(config.storageAllowed);
|
|
1614
|
+
}
|
|
1585
1615
|
const requestedEndpoint = config.endpoint || "";
|
|
1586
1616
|
const endpoint = !requestedEndpoint || isTrustedEndpoint(requestedEndpoint) ? requestedEndpoint : "";
|
|
1587
1617
|
this.config = {
|
|
@@ -1597,9 +1627,11 @@ var BidkernelPrebidAnalytics = class _BidkernelPrebidAnalytics {
|
|
|
1597
1627
|
warningsEnabled: config.warningsEnabled ?? true,
|
|
1598
1628
|
errorsEnabled: config.errorsEnabled ?? true,
|
|
1599
1629
|
viewabilityEnabled: config.viewabilityEnabled ?? true,
|
|
1630
|
+
storageAllowed: config.storageAllowed ?? true,
|
|
1600
1631
|
logLevel: config.logLevel || "INFO",
|
|
1601
1632
|
pbjsGlobalName: config.pbjsGlobalName || "pbjs",
|
|
1602
|
-
attachPbjsListeners: config.attachPbjsListeners ?? true
|
|
1633
|
+
attachPbjsListeners: config.attachPbjsListeners ?? true,
|
|
1634
|
+
revenueFlushDelayMs: Number.isFinite(config.revenueFlushDelayMs) && config.revenueFlushDelayMs >= 0 ? config.revenueFlushDelayMs : REVENUE_FLUSH_DELAY_MS
|
|
1603
1635
|
};
|
|
1604
1636
|
this.logger = createLogger({
|
|
1605
1637
|
prefix: "[bidkernel][prebid-analytics]",
|
|
@@ -1638,65 +1670,7 @@ var BidkernelPrebidAnalytics = class _BidkernelPrebidAnalytics {
|
|
|
1638
1670
|
}
|
|
1639
1671
|
}
|
|
1640
1672
|
if (this.config.attachPbjsListeners) {
|
|
1641
|
-
|
|
1642
|
-
const pbjs = win[this.config.pbjsGlobalName] || {};
|
|
1643
|
-
if (typeof pbjs.onEvent === "function") {
|
|
1644
|
-
this.log("DEBUG", "Attaching event listeners to pbjs");
|
|
1645
|
-
const events = [
|
|
1646
|
-
["auctionInit", this.handleAuctionInit.bind(this)],
|
|
1647
|
-
["auctionEnd", this.handleAuctionEnd.bind(this)],
|
|
1648
|
-
["bidRequested", this.handleBidRequested.bind(this)],
|
|
1649
|
-
["bidResponse", this.handleBidResponse.bind(this)],
|
|
1650
|
-
["bidTimeout", this.handleBidTimeout.bind(this)],
|
|
1651
|
-
["bidWon", this.handleBidWon.bind(this)],
|
|
1652
|
-
["noBid", this.handleNoBid.bind(this)],
|
|
1653
|
-
["adRenderFailed", this.handleAdRenderFailed.bind(this)],
|
|
1654
|
-
["adRenderSucceeded", this.handleAdRenderSucceeded.bind(this)]
|
|
1655
|
-
];
|
|
1656
|
-
for (const [name, fn] of events) {
|
|
1657
|
-
pbjs.onEvent(name, fn);
|
|
1658
|
-
this.boundPbjsHandlers.push({ event: name, handler: fn });
|
|
1659
|
-
}
|
|
1660
|
-
} else {
|
|
1661
|
-
this.log("WARN", "pbjs.onEvent is not defined. Prebid analytics will not function.");
|
|
1662
|
-
}
|
|
1663
|
-
if (typeof pbjs.getEvents === "function") {
|
|
1664
|
-
try {
|
|
1665
|
-
const pastEvents = pbjs.getEvents();
|
|
1666
|
-
if (Array.isArray(pastEvents)) {
|
|
1667
|
-
const newPastEvents = pastEvents.slice(this.replayedEventCount);
|
|
1668
|
-
this.replayedEventCount = pastEvents.length;
|
|
1669
|
-
if (newPastEvents.length > 0) {
|
|
1670
|
-
this.log(
|
|
1671
|
-
"DEBUG",
|
|
1672
|
-
`Replaying ${newPastEvents.length} historical events from pbjs.getEvents()`
|
|
1673
|
-
);
|
|
1674
|
-
const handlerMap = {
|
|
1675
|
-
auctionInit: this.handleAuctionInit.bind(this),
|
|
1676
|
-
auctionEnd: this.handleAuctionEnd.bind(this),
|
|
1677
|
-
bidRequested: this.handleBidRequested.bind(this),
|
|
1678
|
-
bidResponse: this.handleBidResponse.bind(this),
|
|
1679
|
-
bidTimeout: this.handleBidTimeout.bind(this),
|
|
1680
|
-
bidWon: this.handleBidWon.bind(this),
|
|
1681
|
-
noBid: this.handleNoBid.bind(this),
|
|
1682
|
-
adRenderFailed: this.handleAdRenderFailed.bind(this),
|
|
1683
|
-
adRenderSucceeded: this.handleAdRenderSucceeded.bind(this)
|
|
1684
|
-
};
|
|
1685
|
-
for (const ev of newPastEvents) {
|
|
1686
|
-
if (!ev) continue;
|
|
1687
|
-
const eventType = ev.eventType || ev.event || ev.name;
|
|
1688
|
-
const args = ev.args !== void 0 ? ev.args : ev.data !== void 0 ? ev.data : ev;
|
|
1689
|
-
const handler = handlerMap[eventType];
|
|
1690
|
-
if (handler) {
|
|
1691
|
-
handler(args);
|
|
1692
|
-
}
|
|
1693
|
-
}
|
|
1694
|
-
}
|
|
1695
|
-
}
|
|
1696
|
-
} catch (e) {
|
|
1697
|
-
this.log("WARN", "Failed to replay historical events from pbjs.getEvents()", e);
|
|
1698
|
-
}
|
|
1699
|
-
}
|
|
1673
|
+
this.attachPbjs();
|
|
1700
1674
|
}
|
|
1701
1675
|
if (typeof window !== "undefined") {
|
|
1702
1676
|
if (!this.pageUrl) {
|
|
@@ -1719,6 +1693,68 @@ var BidkernelPrebidAnalytics = class _BidkernelPrebidAnalytics {
|
|
|
1719
1693
|
}
|
|
1720
1694
|
}
|
|
1721
1695
|
}
|
|
1696
|
+
/**
|
|
1697
|
+
* Binds the Prebid event handlers on the configured global and replays
|
|
1698
|
+
* pbjs.getEvents() history. enable() calls this when attachPbjsListeners is
|
|
1699
|
+
* true. An integration that loads Prebid lazily constructs the instance with
|
|
1700
|
+
* attachPbjsListeners=false and calls this once pbjs exists; disable() (and
|
|
1701
|
+
* therefore navigate()) drops the handlers, so call it again after either.
|
|
1702
|
+
* Idempotent. Returns true when handlers are bound after the call, false
|
|
1703
|
+
* when the instance is disabled or pbjs.onEvent is not available yet.
|
|
1704
|
+
*/
|
|
1705
|
+
attachPbjs() {
|
|
1706
|
+
if (!this.isEnabled) return false;
|
|
1707
|
+
if (this.boundPbjsHandlers.length > 0) return true;
|
|
1708
|
+
const win = typeof window !== "undefined" ? window : {};
|
|
1709
|
+
const pbjs = win[this.config.pbjsGlobalName] || {};
|
|
1710
|
+
if (typeof pbjs.onEvent !== "function") {
|
|
1711
|
+
this.log("WARN", "pbjs.onEvent is not defined. Prebid analytics will not function.");
|
|
1712
|
+
return false;
|
|
1713
|
+
}
|
|
1714
|
+
this.log("DEBUG", "Attaching event listeners to pbjs");
|
|
1715
|
+
const handlerMap = {
|
|
1716
|
+
auctionInit: this.handleAuctionInit.bind(this),
|
|
1717
|
+
auctionEnd: this.handleAuctionEnd.bind(this),
|
|
1718
|
+
bidRequested: this.handleBidRequested.bind(this),
|
|
1719
|
+
bidResponse: this.handleBidResponse.bind(this),
|
|
1720
|
+
bidTimeout: this.handleBidTimeout.bind(this),
|
|
1721
|
+
bidWon: this.handleBidWon.bind(this),
|
|
1722
|
+
noBid: this.handleNoBid.bind(this),
|
|
1723
|
+
adRenderFailed: this.handleAdRenderFailed.bind(this),
|
|
1724
|
+
adRenderSucceeded: this.handleAdRenderSucceeded.bind(this)
|
|
1725
|
+
};
|
|
1726
|
+
for (const [name, fn] of Object.entries(handlerMap)) {
|
|
1727
|
+
pbjs.onEvent(name, fn);
|
|
1728
|
+
this.boundPbjsHandlers.push({ event: name, handler: fn });
|
|
1729
|
+
}
|
|
1730
|
+
if (typeof pbjs.getEvents === "function") {
|
|
1731
|
+
try {
|
|
1732
|
+
const pastEvents = pbjs.getEvents();
|
|
1733
|
+
if (Array.isArray(pastEvents)) {
|
|
1734
|
+
const newPastEvents = pastEvents.slice(this.replayedEventCount);
|
|
1735
|
+
this.replayedEventCount = pastEvents.length;
|
|
1736
|
+
if (newPastEvents.length > 0) {
|
|
1737
|
+
this.log(
|
|
1738
|
+
"DEBUG",
|
|
1739
|
+
`Replaying ${newPastEvents.length} historical events from pbjs.getEvents()`
|
|
1740
|
+
);
|
|
1741
|
+
for (const ev of newPastEvents) {
|
|
1742
|
+
if (!ev) continue;
|
|
1743
|
+
const eventType = ev.eventType || ev.event || ev.name;
|
|
1744
|
+
const args = ev.args !== void 0 ? ev.args : ev.data !== void 0 ? ev.data : ev;
|
|
1745
|
+
const handler = handlerMap[eventType];
|
|
1746
|
+
if (handler) {
|
|
1747
|
+
handler(args);
|
|
1748
|
+
}
|
|
1749
|
+
}
|
|
1750
|
+
}
|
|
1751
|
+
}
|
|
1752
|
+
} catch (e) {
|
|
1753
|
+
this.log("WARN", "Failed to replay historical events from pbjs.getEvents()", e);
|
|
1754
|
+
}
|
|
1755
|
+
}
|
|
1756
|
+
return true;
|
|
1757
|
+
}
|
|
1722
1758
|
disable() {
|
|
1723
1759
|
if (!this.isEnabled) return;
|
|
1724
1760
|
this.flushAllSlotsTimeInView();
|
|
@@ -1750,6 +1786,14 @@ var BidkernelPrebidAnalytics = class _BidkernelPrebidAnalytics {
|
|
|
1750
1786
|
}
|
|
1751
1787
|
}
|
|
1752
1788
|
this.videoDetachCleanups.clear();
|
|
1789
|
+
for (const cleanup of Array.from(this.clickDetachCleanups)) {
|
|
1790
|
+
try {
|
|
1791
|
+
cleanup();
|
|
1792
|
+
} catch {
|
|
1793
|
+
}
|
|
1794
|
+
}
|
|
1795
|
+
this.clickDetachCleanups.clear();
|
|
1796
|
+
this.slotElements.clear();
|
|
1753
1797
|
this.cachedWinningBids.clear();
|
|
1754
1798
|
this.slotEmittedImpressionKeys.clear();
|
|
1755
1799
|
this.slotLastAuctionIds.clear();
|
|
@@ -1758,6 +1802,10 @@ var BidkernelPrebidAnalytics = class _BidkernelPrebidAnalytics {
|
|
|
1758
1802
|
clearInterval(this.flushTimer);
|
|
1759
1803
|
this.flushTimer = null;
|
|
1760
1804
|
}
|
|
1805
|
+
if (this.revenueFlushTimer) {
|
|
1806
|
+
clearTimeout(this.revenueFlushTimer);
|
|
1807
|
+
this.revenueFlushTimer = null;
|
|
1808
|
+
}
|
|
1761
1809
|
if (this.persistedCleanupTimer) {
|
|
1762
1810
|
clearTimeout(this.persistedCleanupTimer);
|
|
1763
1811
|
this.persistedCleanupTimer = null;
|
|
@@ -1907,6 +1955,105 @@ var BidkernelPrebidAnalytics = class _BidkernelPrebidAnalytics {
|
|
|
1907
1955
|
}
|
|
1908
1956
|
});
|
|
1909
1957
|
}
|
|
1958
|
+
this.notifyViewable(record);
|
|
1959
|
+
}
|
|
1960
|
+
notifyViewable(record) {
|
|
1961
|
+
if (!record.viewableFired) return;
|
|
1962
|
+
for (const listener of record.viewableListeners) {
|
|
1963
|
+
if (listener.firedForIndex === record.refreshIndex) continue;
|
|
1964
|
+
listener.firedForIndex = record.refreshIndex;
|
|
1965
|
+
try {
|
|
1966
|
+
listener.callback(record.slotId);
|
|
1967
|
+
} catch (e) {
|
|
1968
|
+
this.log("ERROR", "Error in viewable listener callback", e);
|
|
1969
|
+
}
|
|
1970
|
+
}
|
|
1971
|
+
}
|
|
1972
|
+
/**
|
|
1973
|
+
* Calls back once per render cycle when the slot has met the IAB viewable
|
|
1974
|
+
* standard this adapter measures (immediately if it already has). Works
|
|
1975
|
+
* before the slot is observed: the listener waits for observeSlot. Returns
|
|
1976
|
+
* an unsubscribe function.
|
|
1977
|
+
*/
|
|
1978
|
+
/**
|
|
1979
|
+
* Names the element to measure for a slot when the creative renders
|
|
1980
|
+
* somewhere other than the slot element (a sticky bar, an interstitial).
|
|
1981
|
+
* Applies to the current cycle at once and to every later render.
|
|
1982
|
+
*/
|
|
1983
|
+
setSlotElement(slotId, element) {
|
|
1984
|
+
this.slotElements.set(slotId, element);
|
|
1985
|
+
const record = this.slotViewabilityRecords.get(slotId);
|
|
1986
|
+
if (record && record.element !== element) {
|
|
1987
|
+
this.observeSlot(element, slotId, {
|
|
1988
|
+
refreshIndex: record.refreshIndex,
|
|
1989
|
+
emitRefreshEvent: false
|
|
1990
|
+
});
|
|
1991
|
+
}
|
|
1992
|
+
}
|
|
1993
|
+
/**
|
|
1994
|
+
* Records a click on the slot's current creative, attributed to the cached
|
|
1995
|
+
* winning bid. Repeats within one second are one click. Returns whether a
|
|
1996
|
+
* CLICK was recorded.
|
|
1997
|
+
*/
|
|
1998
|
+
recordClick(slotId, options) {
|
|
1999
|
+
if (!this.isEnabled || !slotId) return false;
|
|
2000
|
+
const now = Date.now();
|
|
2001
|
+
if (now - (this.lastClickAt.get(slotId) || 0) < CLICK_DEDUP_MS) return false;
|
|
2002
|
+
this.lastClickAt.set(slotId, now);
|
|
2003
|
+
const cached = this.getCachedBid(slotId);
|
|
2004
|
+
this.enqueue(TraceEventType.CLICK, "click", {
|
|
2005
|
+
auctionId: cached?.auctionId || "",
|
|
2006
|
+
transactionId: cached?.transactionId || "",
|
|
2007
|
+
adUnitCode: slotId,
|
|
2008
|
+
bid: cached?.bidTrace,
|
|
2009
|
+
metadata: {
|
|
2010
|
+
...options?.metadata,
|
|
2011
|
+
refresh_index: String(this.getRefreshIndex(slotId))
|
|
2012
|
+
}
|
|
2013
|
+
});
|
|
2014
|
+
return true;
|
|
2015
|
+
}
|
|
2016
|
+
/**
|
|
2017
|
+
* Detects clicks into a cross-origin creative frame: a click inside an
|
|
2018
|
+
* iframe moves focus into it and blurs the window. Returns a detach
|
|
2019
|
+
* function; every tracker is detached on disable().
|
|
2020
|
+
*/
|
|
2021
|
+
attachClickTracker(element, slotId) {
|
|
2022
|
+
if (typeof window === "undefined") return () => {
|
|
2023
|
+
};
|
|
2024
|
+
const onBlur = () => {
|
|
2025
|
+
setTimeout(() => {
|
|
2026
|
+
const active = document.activeElement;
|
|
2027
|
+
if (active && (active === element || element.contains(active))) {
|
|
2028
|
+
this.recordClick(slotId, { metadata: { source: "blur" } });
|
|
2029
|
+
window.focus();
|
|
2030
|
+
}
|
|
2031
|
+
}, 0);
|
|
2032
|
+
};
|
|
2033
|
+
window.addEventListener("blur", onBlur);
|
|
2034
|
+
const cleanup = () => {
|
|
2035
|
+
window.removeEventListener("blur", onBlur);
|
|
2036
|
+
this.clickDetachCleanups.delete(cleanup);
|
|
2037
|
+
};
|
|
2038
|
+
this.clickDetachCleanups.add(cleanup);
|
|
2039
|
+
return cleanup;
|
|
2040
|
+
}
|
|
2041
|
+
onViewable(slotId, callback) {
|
|
2042
|
+
const listener = { callback, firedForIndex: -1 };
|
|
2043
|
+
const record = this.slotViewabilityRecords.get(slotId);
|
|
2044
|
+
if (record) {
|
|
2045
|
+
record.viewableListeners.add(listener);
|
|
2046
|
+
this.notifyViewable(record);
|
|
2047
|
+
} else {
|
|
2048
|
+
if (!this.pendingViewableListeners.has(slotId)) {
|
|
2049
|
+
this.pendingViewableListeners.set(slotId, /* @__PURE__ */ new Set());
|
|
2050
|
+
}
|
|
2051
|
+
this.pendingViewableListeners.get(slotId).add(listener);
|
|
2052
|
+
}
|
|
2053
|
+
return () => {
|
|
2054
|
+
this.slotViewabilityRecords.get(slotId)?.viewableListeners.delete(listener);
|
|
2055
|
+
this.pendingViewableListeners.get(slotId)?.delete(listener);
|
|
2056
|
+
};
|
|
1910
2057
|
}
|
|
1911
2058
|
scheduleThresholdTimers(record) {
|
|
1912
2059
|
if (!record.inView || typeof document !== "undefined" && document.visibilityState === "hidden") {
|
|
@@ -2096,6 +2243,8 @@ var BidkernelPrebidAnalytics = class _BidkernelPrebidAnalytics {
|
|
|
2096
2243
|
}
|
|
2097
2244
|
const listeners = this.pendingThresholdListeners.get(resolvedSlotId) || /* @__PURE__ */ new Set();
|
|
2098
2245
|
this.pendingThresholdListeners.delete(resolvedSlotId);
|
|
2246
|
+
const viewableListeners = this.pendingViewableListeners.get(resolvedSlotId) || /* @__PURE__ */ new Set();
|
|
2247
|
+
this.pendingViewableListeners.delete(resolvedSlotId);
|
|
2099
2248
|
const record = {
|
|
2100
2249
|
slotId: resolvedSlotId,
|
|
2101
2250
|
element: el,
|
|
@@ -2112,7 +2261,8 @@ var BidkernelPrebidAnalytics = class _BidkernelPrebidAnalytics {
|
|
|
2112
2261
|
viewableFired: false,
|
|
2113
2262
|
dwellTimer: null,
|
|
2114
2263
|
dwellStartedAt: null,
|
|
2115
|
-
thresholdListeners: listeners
|
|
2264
|
+
thresholdListeners: listeners,
|
|
2265
|
+
viewableListeners
|
|
2116
2266
|
};
|
|
2117
2267
|
this.slotViewabilityRecords.set(resolvedSlotId, record);
|
|
2118
2268
|
if (el) {
|
|
@@ -2137,6 +2287,7 @@ var BidkernelPrebidAnalytics = class _BidkernelPrebidAnalytics {
|
|
|
2137
2287
|
}
|
|
2138
2288
|
}
|
|
2139
2289
|
record.thresholdListeners = /* @__PURE__ */ new Set();
|
|
2290
|
+
record.viewableListeners = /* @__PURE__ */ new Set();
|
|
2140
2291
|
if (record.element && this.intersectionObserver) {
|
|
2141
2292
|
this.intersectionObserver.unobserve(record.element);
|
|
2142
2293
|
this.elementToSlotId.delete(record.element);
|
|
@@ -2151,6 +2302,7 @@ var BidkernelPrebidAnalytics = class _BidkernelPrebidAnalytics {
|
|
|
2151
2302
|
this.slotRefreshIndices.delete(slotId);
|
|
2152
2303
|
this.slotLastAuctionIds.delete(slotId);
|
|
2153
2304
|
this.pendingThresholdListeners.delete(slotId);
|
|
2305
|
+
this.pendingViewableListeners.delete(slotId);
|
|
2154
2306
|
const prefix = `${escapeKeyPart(slotId)}:`;
|
|
2155
2307
|
for (const key of Array.from(this.slotEmittedImpressionKeys)) {
|
|
2156
2308
|
if (key === slotId || key.startsWith(prefix)) {
|
|
@@ -2425,19 +2577,21 @@ var BidkernelPrebidAnalytics = class _BidkernelPrebidAnalytics {
|
|
|
2425
2577
|
if (!adsManager.__bidkernelAttachedInstances) {
|
|
2426
2578
|
try {
|
|
2427
2579
|
Object.defineProperty(adsManager, "__bidkernelAttachedInstances", {
|
|
2428
|
-
value: /* @__PURE__ */ new
|
|
2580
|
+
value: /* @__PURE__ */ new Map(),
|
|
2429
2581
|
configurable: true,
|
|
2430
2582
|
writable: true
|
|
2431
2583
|
});
|
|
2432
2584
|
} catch {
|
|
2433
|
-
adsManager.__bidkernelAttachedInstances = /* @__PURE__ */ new
|
|
2585
|
+
adsManager.__bidkernelAttachedInstances = /* @__PURE__ */ new Map();
|
|
2434
2586
|
}
|
|
2435
2587
|
}
|
|
2436
|
-
|
|
2437
|
-
|
|
2588
|
+
const attached = adsManager.__bidkernelAttachedInstances;
|
|
2589
|
+
const previous = attached.get(this);
|
|
2590
|
+
if (previous) {
|
|
2591
|
+
if (!options) return () => {
|
|
2438
2592
|
};
|
|
2593
|
+
previous();
|
|
2439
2594
|
}
|
|
2440
|
-
adsManager.__bidkernelAttachedInstances.add(this);
|
|
2441
2595
|
const slotId = options?.slotId || options?.adUnitCode || "video";
|
|
2442
2596
|
const adUnitCode = options?.adUnitCode || slotId;
|
|
2443
2597
|
const auctionId = options?.auctionId || "";
|
|
@@ -2585,7 +2739,7 @@ var BidkernelPrebidAnalytics = class _BidkernelPrebidAnalytics {
|
|
|
2585
2739
|
}
|
|
2586
2740
|
}
|
|
2587
2741
|
const cleanup = () => {
|
|
2588
|
-
|
|
2742
|
+
attached.delete(this);
|
|
2589
2743
|
for (const { type, handler } of listeners) {
|
|
2590
2744
|
try {
|
|
2591
2745
|
adsManager.removeEventListener(type, handler);
|
|
@@ -2594,6 +2748,7 @@ var BidkernelPrebidAnalytics = class _BidkernelPrebidAnalytics {
|
|
|
2594
2748
|
}
|
|
2595
2749
|
this.videoDetachCleanups.delete(cleanup);
|
|
2596
2750
|
};
|
|
2751
|
+
attached.set(this, cleanup);
|
|
2597
2752
|
this.videoDetachCleanups.add(cleanup);
|
|
2598
2753
|
return cleanup;
|
|
2599
2754
|
}
|
|
@@ -3221,9 +3376,9 @@ var BidkernelPrebidAnalytics = class _BidkernelPrebidAnalytics {
|
|
|
3221
3376
|
);
|
|
3222
3377
|
}
|
|
3223
3378
|
if (this.config.viewabilityEnabled && typeof document !== "undefined") {
|
|
3224
|
-
let el = null;
|
|
3379
|
+
let el = adUnitCode && this.slotElements.get(adUnitCode) || null;
|
|
3225
3380
|
const targetId = adUnitCode || data.adId || bid.adId;
|
|
3226
|
-
if (targetId) {
|
|
3381
|
+
if (!el && targetId) {
|
|
3227
3382
|
el = document.getElementById(targetId);
|
|
3228
3383
|
}
|
|
3229
3384
|
if (!el && typeof window !== "undefined" && window.googletag?.pubads) {
|
|
@@ -3322,12 +3477,11 @@ var BidkernelPrebidAnalytics = class _BidkernelPrebidAnalytics {
|
|
|
3322
3477
|
}
|
|
3323
3478
|
if (this.queue.length >= BATCH_SIZE) {
|
|
3324
3479
|
this.flush();
|
|
3325
|
-
} else if (
|
|
3326
|
-
this.
|
|
3327
|
-
|
|
3328
|
-
this.immediateFlushScheduled = false;
|
|
3480
|
+
} else if (REVENUE_FLUSH_TYPES.has(type) && this.revenueFlushTimer === null) {
|
|
3481
|
+
this.revenueFlushTimer = setTimeout(() => {
|
|
3482
|
+
this.revenueFlushTimer = null;
|
|
3329
3483
|
this.flush();
|
|
3330
|
-
},
|
|
3484
|
+
}, this.config.revenueFlushDelayMs);
|
|
3331
3485
|
}
|
|
3332
3486
|
}
|
|
3333
3487
|
shouldSample(type, level) {
|
|
@@ -3485,6 +3639,7 @@ var BidkernelPrebidAnalytics = class _BidkernelPrebidAnalytics {
|
|
|
3485
3639
|
return `${PENDING_BATCH_KEY_PREFIX}${encodeURIComponent(this.config.propertyId)}_`;
|
|
3486
3640
|
}
|
|
3487
3641
|
persistBatch(encoded) {
|
|
3642
|
+
if (!storageAllowed) return null;
|
|
3488
3643
|
try {
|
|
3489
3644
|
if (typeof localStorage === "undefined") return null;
|
|
3490
3645
|
if (!this.config.propertyId) return null;
|
|
@@ -3737,6 +3892,7 @@ function getbidkernel(alias = "bidkernel") {
|
|
|
3737
3892
|
0 && (module.exports = {
|
|
3738
3893
|
BidkernelPrebidAnalytics,
|
|
3739
3894
|
PrebidEventDeduper,
|
|
3895
|
+
getOrCreateSessionId,
|
|
3740
3896
|
getPrebidEventKey,
|
|
3741
3897
|
getbidkernel,
|
|
3742
3898
|
hookImaPrototype,
|