@bidkernel/analytics 0.9.0 → 0.10.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/index.js CHANGED
@@ -24,6 +24,8 @@ __export(src_exports, {
24
24
  PrebidEventDeduper: () => PrebidEventDeduper,
25
25
  getPrebidEventKey: () => getPrebidEventKey,
26
26
  getbidkernel: () => getbidkernel,
27
+ hookImaPrototype: () => hookImaPrototype,
28
+ initImaInterception: () => initImaInterception,
27
29
  isTrustedEndpoint: () => isTrustedEndpoint,
28
30
  registerPrebidAnalytics: () => registerPrebidAnalytics
29
31
  });
@@ -998,8 +1000,264 @@ function parseBidDimensions(bid) {
998
1000
  height: Number.isFinite(bid?.height) ? bid.height : 0
999
1001
  };
1000
1002
  }
1003
+ var adsLoaderListenerMap = /* @__PURE__ */ new WeakMap();
1004
+ function hookAdsManagerLoadedEvent(loadedEventCtor) {
1005
+ if (!loadedEventCtor) return;
1006
+ try {
1007
+ const loadedProto = loadedEventCtor.prototype;
1008
+ if (loadedProto && typeof loadedProto.getAdsManager === "function" && !loadedProto.getAdsManager.__bidkernelHooked) {
1009
+ const originalGetAdsManager = loadedProto.getAdsManager;
1010
+ const hookedGetAdsManager = function(...args) {
1011
+ const adsManager = originalGetAdsManager.apply(this, args);
1012
+ if (adsManager) {
1013
+ for (const instance of BidkernelPrebidAnalytics.getActiveInstances()) {
1014
+ try {
1015
+ instance.attachImaAdsManager(adsManager);
1016
+ } catch {
1017
+ }
1018
+ }
1019
+ }
1020
+ return adsManager;
1021
+ };
1022
+ hookedGetAdsManager.__bidkernelHooked = true;
1023
+ loadedProto.getAdsManager = hookedGetAdsManager;
1024
+ }
1025
+ } catch {
1026
+ }
1027
+ }
1028
+ function hookAdsManagerPrototype(adsManagerCtorOrProto) {
1029
+ if (!adsManagerCtorOrProto) return;
1030
+ try {
1031
+ const proto = adsManagerCtorOrProto.prototype || adsManagerCtorOrProto;
1032
+ if (proto && typeof proto.init === "function" && !proto.init.__bidkernelHooked) {
1033
+ const origInit = proto.init;
1034
+ const hookedInit = function(...args) {
1035
+ for (const instance of BidkernelPrebidAnalytics.getActiveInstances()) {
1036
+ try {
1037
+ instance.attachImaAdsManager(this);
1038
+ } catch {
1039
+ }
1040
+ }
1041
+ return origInit.apply(this, args);
1042
+ };
1043
+ hookedInit.__bidkernelHooked = true;
1044
+ proto.init = hookedInit;
1045
+ }
1046
+ if (proto && typeof proto.start === "function" && !proto.start.__bidkernelHooked) {
1047
+ const origStart = proto.start;
1048
+ const hookedStart = function(...args) {
1049
+ for (const instance of BidkernelPrebidAnalytics.getActiveInstances()) {
1050
+ try {
1051
+ instance.attachImaAdsManager(this);
1052
+ } catch {
1053
+ }
1054
+ }
1055
+ return origStart.apply(this, args);
1056
+ };
1057
+ hookedStart.__bidkernelHooked = true;
1058
+ proto.start = hookedStart;
1059
+ }
1060
+ } catch {
1061
+ }
1062
+ }
1063
+ function hookAdsLoader(adsLoaderCtor) {
1064
+ if (!adsLoaderCtor) return;
1065
+ try {
1066
+ const proto = adsLoaderCtor.prototype;
1067
+ if (!proto) return;
1068
+ if (typeof proto.addEventListener === "function" && !proto.addEventListener.__bidkernelHooked) {
1069
+ const origAddEventListener = proto.addEventListener;
1070
+ const hookedAddEventListener = function(type, listener, ...rest) {
1071
+ if ((type === "adsManagerLoaded" || type === (this?.AdsManagerLoadedEvent?.Type?.ADS_MANAGER_LOADED || "adsManagerLoaded")) && typeof listener === "function") {
1072
+ let wrappedListener = adsLoaderListenerMap.get(listener);
1073
+ if (!wrappedListener) {
1074
+ wrappedListener = function(event) {
1075
+ if (event && typeof event.getAdsManager === "function" && !event.getAdsManager.__bidkernelHooked) {
1076
+ const origGetAdsManager = event.getAdsManager;
1077
+ event.getAdsManager = function(...args) {
1078
+ const adsManager = origGetAdsManager.apply(this, args);
1079
+ if (adsManager) {
1080
+ for (const sdk of BidkernelPrebidAnalytics.getActiveInstances()) {
1081
+ try {
1082
+ sdk.attachImaAdsManager(adsManager);
1083
+ } catch {
1084
+ }
1085
+ }
1086
+ }
1087
+ return adsManager;
1088
+ };
1089
+ event.getAdsManager.__bidkernelHooked = true;
1090
+ }
1091
+ return listener.apply(this, arguments);
1092
+ };
1093
+ adsLoaderListenerMap.set(listener, wrappedListener);
1094
+ }
1095
+ return origAddEventListener.call(this, type, wrappedListener, ...rest);
1096
+ }
1097
+ return origAddEventListener.call(this, type, listener, ...rest);
1098
+ };
1099
+ hookedAddEventListener.__bidkernelHooked = true;
1100
+ proto.addEventListener = hookedAddEventListener;
1101
+ }
1102
+ if (typeof proto.removeEventListener === "function" && !proto.removeEventListener.__bidkernelHooked) {
1103
+ const origRemoveEventListener = proto.removeEventListener;
1104
+ const hookedRemoveEventListener = function(type, listener, ...rest) {
1105
+ const wrapped = typeof listener === "function" ? adsLoaderListenerMap.get(listener) : void 0;
1106
+ return origRemoveEventListener.call(this, type, wrapped || listener, ...rest);
1107
+ };
1108
+ hookedRemoveEventListener.__bidkernelHooked = true;
1109
+ proto.removeEventListener = hookedRemoveEventListener;
1110
+ }
1111
+ } catch {
1112
+ }
1113
+ }
1114
+ function hookImaPrototype(ima) {
1115
+ if (!ima || typeof ima !== "object") return;
1116
+ try {
1117
+ if (ima.AdsManagerLoadedEvent) {
1118
+ hookAdsManagerLoadedEvent(ima.AdsManagerLoadedEvent);
1119
+ }
1120
+ const loadedDesc = Object.getOwnPropertyDescriptor(ima, "AdsManagerLoadedEvent");
1121
+ if (!loadedDesc || loadedDesc.configurable) {
1122
+ let _loadedEvent = ima.AdsManagerLoadedEvent;
1123
+ try {
1124
+ Object.defineProperty(ima, "AdsManagerLoadedEvent", {
1125
+ configurable: true,
1126
+ enumerable: true,
1127
+ get() {
1128
+ return _loadedEvent;
1129
+ },
1130
+ set(val) {
1131
+ _loadedEvent = val;
1132
+ hookAdsManagerLoadedEvent(val);
1133
+ }
1134
+ });
1135
+ } catch {
1136
+ }
1137
+ }
1138
+ if (ima.AdsLoader) {
1139
+ hookAdsLoader(ima.AdsLoader);
1140
+ }
1141
+ const loaderDesc = Object.getOwnPropertyDescriptor(ima, "AdsLoader");
1142
+ if (!loaderDesc || loaderDesc.configurable) {
1143
+ let _loader = ima.AdsLoader;
1144
+ try {
1145
+ Object.defineProperty(ima, "AdsLoader", {
1146
+ configurable: true,
1147
+ enumerable: true,
1148
+ get() {
1149
+ return _loader;
1150
+ },
1151
+ set(val) {
1152
+ _loader = val;
1153
+ hookAdsLoader(val);
1154
+ }
1155
+ });
1156
+ } catch {
1157
+ }
1158
+ }
1159
+ if (ima.AdsManager) {
1160
+ hookAdsManagerPrototype(ima.AdsManager);
1161
+ }
1162
+ const mgrDesc = Object.getOwnPropertyDescriptor(ima, "AdsManager");
1163
+ if (!mgrDesc || mgrDesc.configurable) {
1164
+ let _mgr = ima.AdsManager;
1165
+ try {
1166
+ Object.defineProperty(ima, "AdsManager", {
1167
+ configurable: true,
1168
+ enumerable: true,
1169
+ get() {
1170
+ return _mgr;
1171
+ },
1172
+ set(val) {
1173
+ _mgr = val;
1174
+ hookAdsManagerPrototype(val);
1175
+ }
1176
+ });
1177
+ } catch {
1178
+ }
1179
+ }
1180
+ } catch {
1181
+ }
1182
+ }
1183
+ function initImaInterception() {
1184
+ if (typeof window === "undefined") return;
1185
+ const win = window;
1186
+ if (win.google?.ima) {
1187
+ hookImaPrototype(win.google.ima);
1188
+ }
1189
+ if (win.google && typeof win.google === "object") {
1190
+ const imaDesc = Object.getOwnPropertyDescriptor(win.google, "ima");
1191
+ if (!imaDesc || imaDesc.configurable) {
1192
+ let _ima = win.google.ima;
1193
+ try {
1194
+ Object.defineProperty(win.google, "ima", {
1195
+ configurable: true,
1196
+ enumerable: true,
1197
+ get() {
1198
+ return _ima;
1199
+ },
1200
+ set(val) {
1201
+ _ima = val;
1202
+ hookImaPrototype(val);
1203
+ }
1204
+ });
1205
+ } catch {
1206
+ }
1207
+ }
1208
+ }
1209
+ const googleDesc = Object.getOwnPropertyDescriptor(win, "google");
1210
+ if (!googleDesc || googleDesc.configurable) {
1211
+ let _google = win.google;
1212
+ try {
1213
+ Object.defineProperty(win, "google", {
1214
+ configurable: true,
1215
+ enumerable: true,
1216
+ get() {
1217
+ return _google;
1218
+ },
1219
+ set(val) {
1220
+ _google = val;
1221
+ if (_google && typeof _google === "object") {
1222
+ if (_google.ima) {
1223
+ hookImaPrototype(_google.ima);
1224
+ }
1225
+ const iDesc = Object.getOwnPropertyDescriptor(_google, "ima");
1226
+ if (!iDesc || iDesc.configurable) {
1227
+ let _i = _google.ima;
1228
+ try {
1229
+ Object.defineProperty(_google, "ima", {
1230
+ configurable: true,
1231
+ enumerable: true,
1232
+ get() {
1233
+ return _i;
1234
+ },
1235
+ set(iv) {
1236
+ _i = iv;
1237
+ hookImaPrototype(iv);
1238
+ }
1239
+ });
1240
+ } catch {
1241
+ }
1242
+ }
1243
+ }
1244
+ }
1245
+ });
1246
+ } catch {
1247
+ }
1248
+ }
1249
+ }
1250
+ if (typeof window !== "undefined") {
1251
+ initImaInterception();
1252
+ }
1001
1253
  var BidkernelPrebidAnalytics = class _BidkernelPrebidAnalytics {
1002
1254
  static activeInstances = /* @__PURE__ */ new Set();
1255
+ static getActiveInstances() {
1256
+ return _BidkernelPrebidAnalytics.activeInstances;
1257
+ }
1258
+ static initImaInterception() {
1259
+ initImaInterception();
1260
+ }
1003
1261
  config;
1004
1262
  queue = [];
1005
1263
  errorCount = 0;
@@ -1163,6 +1421,10 @@ var BidkernelPrebidAnalytics = class _BidkernelPrebidAnalytics {
1163
1421
  }
1164
1422
  this.flushTimer = setInterval(() => this.flush(), FLUSH_INTERVAL_MS);
1165
1423
  this.resendPersistedBatches();
1424
+ initImaInterception();
1425
+ if (window.google?.ima) {
1426
+ hookImaPrototype(window.google.ima);
1427
+ }
1166
1428
  }
1167
1429
  }
1168
1430
  disable() {
@@ -1643,6 +1905,72 @@ var BidkernelPrebidAnalytics = class _BidkernelPrebidAnalytics {
1643
1905
  this.cachedWinningBids.delete(oldest.value);
1644
1906
  }
1645
1907
  }
1908
+ /**
1909
+ * Searches cached winning bids for an entry matching the given filters (creativeId,
1910
+ * adId, or mediaType), falling back to the most recent winning video bid.
1911
+ */
1912
+ findCachedWinningBid(filter) {
1913
+ const now = Date.now();
1914
+ const isConsumed = (entry) => {
1915
+ return this.hasImpressionEmitted(
1916
+ entry.adUnitCode,
1917
+ void 0,
1918
+ entry.auctionId,
1919
+ entry.bidTrace.creativeId
1920
+ );
1921
+ };
1922
+ if (filter?.creativeId || filter?.adId) {
1923
+ for (const entry of Array.from(this.cachedWinningBids.values())) {
1924
+ if (now - entry.timestamp > CACHED_BID_TTL_MS) continue;
1925
+ if (filter.creativeId && (entry.bidTrace.creativeId === filter.creativeId || entry.rawBid?.creativeId === filter.creativeId) || filter.adId && (entry.rawBid?.adId === filter.adId || entry.adUnitCode === filter.adId)) {
1926
+ return entry;
1927
+ }
1928
+ }
1929
+ }
1930
+ let bestUnconsumedMatch;
1931
+ if (filter?.mediaType) {
1932
+ for (const entry of Array.from(this.cachedWinningBids.values())) {
1933
+ if (now - entry.timestamp > CACHED_BID_TTL_MS) continue;
1934
+ const isEntryVideo = entry.bidTrace.mediaType === filter.mediaType || entry.rawBid?.mediaType === filter.mediaType || filter.mediaType === "video" && entry.rawBid?.mediaTypes?.video !== void 0;
1935
+ if (isEntryVideo) {
1936
+ if (!isConsumed(entry)) {
1937
+ if (!bestUnconsumedMatch || entry.timestamp < bestUnconsumedMatch.timestamp) {
1938
+ bestUnconsumedMatch = entry;
1939
+ }
1940
+ }
1941
+ }
1942
+ }
1943
+ if (bestUnconsumedMatch) return bestUnconsumedMatch;
1944
+ let bestFallbackMatch;
1945
+ for (const entry of Array.from(this.cachedWinningBids.values())) {
1946
+ if (now - entry.timestamp > CACHED_BID_TTL_MS) continue;
1947
+ const isEntryVideo = entry.bidTrace.mediaType === filter.mediaType || entry.rawBid?.mediaType === filter.mediaType || filter.mediaType === "video" && entry.rawBid?.mediaTypes?.video !== void 0;
1948
+ if (isEntryVideo) {
1949
+ if (!bestFallbackMatch || entry.timestamp > bestFallbackMatch.timestamp) {
1950
+ bestFallbackMatch = entry;
1951
+ }
1952
+ }
1953
+ }
1954
+ if (bestFallbackMatch) return bestFallbackMatch;
1955
+ }
1956
+ for (const entry of Array.from(this.cachedWinningBids.values())) {
1957
+ if (now - entry.timestamp > CACHED_BID_TTL_MS) continue;
1958
+ if (!isConsumed(entry)) {
1959
+ if (!bestUnconsumedMatch || entry.timestamp > bestUnconsumedMatch.timestamp) {
1960
+ bestUnconsumedMatch = entry;
1961
+ }
1962
+ }
1963
+ }
1964
+ if (bestUnconsumedMatch) return bestUnconsumedMatch;
1965
+ let bestRecent;
1966
+ for (const entry of Array.from(this.cachedWinningBids.values())) {
1967
+ if (now - entry.timestamp > CACHED_BID_TTL_MS) continue;
1968
+ if (!bestRecent || entry.timestamp > bestRecent.timestamp) {
1969
+ bestRecent = entry;
1970
+ }
1971
+ }
1972
+ return bestRecent;
1973
+ }
1646
1974
  /** Records a dedup key, evicting oldest-first at the cap. */
1647
1975
  addImpressionKey(key) {
1648
1976
  if (this.slotEmittedImpressionKeys.has(key)) return;
@@ -1802,14 +2130,40 @@ var BidkernelPrebidAnalytics = class _BidkernelPrebidAnalytics {
1802
2130
  return () => {
1803
2131
  };
1804
2132
  }
2133
+ if (!adsManager.__bidkernelAttachedInstances) {
2134
+ try {
2135
+ Object.defineProperty(adsManager, "__bidkernelAttachedInstances", {
2136
+ value: /* @__PURE__ */ new Set(),
2137
+ configurable: true,
2138
+ writable: true
2139
+ });
2140
+ } catch {
2141
+ adsManager.__bidkernelAttachedInstances = /* @__PURE__ */ new Set();
2142
+ }
2143
+ }
2144
+ if (adsManager.__bidkernelAttachedInstances.has(this)) {
2145
+ return () => {
2146
+ };
2147
+ }
2148
+ adsManager.__bidkernelAttachedInstances.add(this);
1805
2149
  const slotId = options?.slotId || options?.adUnitCode || "video";
1806
2150
  const adUnitCode = options?.adUnitCode || slotId;
1807
2151
  const auctionId = options?.auctionId || "";
1808
2152
  const transactionId = options?.transactionId || "";
1809
- const getWinningBid = () => {
1810
- return (auctionId ? this.getCachedBid(`${auctionId}:${adUnitCode}`) : void 0) || (auctionId && slotId !== adUnitCode ? this.getCachedBid(`${auctionId}:${slotId}`) : void 0) || this.getCachedBid(adUnitCode) || (slotId !== adUnitCode ? this.getCachedBid(slotId) : void 0);
2153
+ const getWinningBid = (adData) => {
2154
+ let cached = (auctionId ? this.getCachedBid(`${auctionId}:${adUnitCode}`) : void 0) || (auctionId && slotId !== adUnitCode ? this.getCachedBid(`${auctionId}:${slotId}`) : void 0) || (adUnitCode !== "video" ? this.getCachedBid(adUnitCode) : void 0) || (slotId !== adUnitCode && slotId !== "video" ? this.getCachedBid(slotId) : void 0);
2155
+ if (!cached) {
2156
+ cached = this.findCachedWinningBid({
2157
+ creativeId: adData?.creativeId,
2158
+ adId: adData?.adId,
2159
+ mediaType: "video"
2160
+ });
2161
+ }
2162
+ return cached;
1811
2163
  };
2164
+ let adStarted = false;
1812
2165
  const onAdStartedOrImpression = (event) => {
2166
+ adStarted = true;
1813
2167
  this.log("DEBUG", "IMA AdEvent.STARTED / IMPRESSION received", event);
1814
2168
  const ad = typeof event?.getAd === "function" ? event.getAd() : event?.ad;
1815
2169
  const adData = {};
@@ -1820,16 +2174,20 @@ var BidkernelPrebidAnalytics = class _BidkernelPrebidAnalytics {
1820
2174
  adData.duration = typeof ad.getDuration === "function" ? ad.getDuration() : ad.duration;
1821
2175
  adData.advertiserName = typeof ad.getAdvertiserName === "function" ? ad.getAdvertiserName() : "";
1822
2176
  }
1823
- const cached = getWinningBid();
2177
+ const cached = getWinningBid(adData);
2178
+ const resolvedSlotId = options?.slotId && options.slotId !== "video" ? options.slotId : options?.adUnitCode && options.adUnitCode !== "video" ? options.adUnitCode : cached?.adUnitCode || options?.slotId || options?.adUnitCode || "video";
2179
+ const resolvedAdUnitCode = options?.adUnitCode && options.adUnitCode !== "video" ? options.adUnitCode : cached?.adUnitCode || resolvedSlotId;
2180
+ const resolvedAuctionId = auctionId || cached?.auctionId || "";
2181
+ const resolvedTransactionId = transactionId || cached?.transactionId || "";
1824
2182
  const mergedBid = {
1825
2183
  ...cached?.rawBid || options?.bid,
1826
2184
  mediaType: "video",
1827
2185
  creativeId: adData.creativeId || cached?.bidTrace?.creativeId || options?.bid?.creativeId || ""
1828
2186
  };
1829
- const emitted = this.recordImpression(slotId, {
1830
- adUnitCode,
1831
- auctionId: auctionId || cached?.auctionId,
1832
- transactionId: transactionId || cached?.transactionId,
2187
+ const emitted = this.recordImpression(resolvedSlotId, {
2188
+ adUnitCode: resolvedAdUnitCode,
2189
+ auctionId: resolvedAuctionId,
2190
+ transactionId: resolvedTransactionId,
1833
2191
  mediaType: "video",
1834
2192
  bid: mergedBid,
1835
2193
  metadata: {
@@ -1841,7 +2199,7 @@ var BidkernelPrebidAnalytics = class _BidkernelPrebidAnalytics {
1841
2199
  });
1842
2200
  if (emitted && options?.onImpression) {
1843
2201
  try {
1844
- options.onImpression(slotId, { ad, bid: mergedBid });
2202
+ options.onImpression(resolvedSlotId, { ad, bid: mergedBid });
1845
2203
  } catch (e) {
1846
2204
  this.log("ERROR", "Error in onImpression callback", e);
1847
2205
  }
@@ -1860,10 +2218,11 @@ var BidkernelPrebidAnalytics = class _BidkernelPrebidAnalytics {
1860
2218
  const onAdClick = (event) => {
1861
2219
  this.log("DEBUG", "IMA AdEvent.CLICK received", event);
1862
2220
  const cached = getWinningBid();
2221
+ const resolvedAdUnitCode = options?.adUnitCode && options.adUnitCode !== "video" ? options.adUnitCode : cached?.adUnitCode || slotId;
1863
2222
  this.enqueue(TraceEventType.CLICK, "click", {
1864
2223
  auctionId: auctionId || cached?.auctionId,
1865
2224
  transactionId: transactionId || cached?.transactionId,
1866
- adUnitCode,
2225
+ adUnitCode: resolvedAdUnitCode,
1867
2226
  bid: cached?.bidTrace || options?.bid,
1868
2227
  metadata: {
1869
2228
  ...options?.metadata,
@@ -1877,14 +2236,16 @@ var BidkernelPrebidAnalytics = class _BidkernelPrebidAnalytics {
1877
2236
  const msg = (err && typeof err.getMessage === "function" ? err.getMessage() : err?.message) || String(err || "IMA ad error");
1878
2237
  const code = (err && typeof err.getErrorCode === "function" ? err.getErrorCode() : err?.code) || "";
1879
2238
  const cached = getWinningBid();
2239
+ const resolvedAdUnitCode = options?.adUnitCode && options.adUnitCode !== "video" ? options.adUnitCode : cached?.adUnitCode || slotId;
1880
2240
  this.enqueue(TraceEventType.AD_RENDER_FAILED, "adRenderFailed", {
1881
2241
  auctionId: auctionId || cached?.auctionId,
1882
2242
  transactionId: transactionId || cached?.transactionId,
1883
- adUnitCode,
2243
+ adUnitCode: resolvedAdUnitCode,
1884
2244
  bid: cached?.bidTrace || options?.bid,
1885
2245
  metadata: {
1886
2246
  ...options?.metadata,
1887
- reason: "ima_ad_error",
2247
+ reason: adStarted ? "ima_playback_error" : "ima_ad_error",
2248
+ playback_phase: adStarted ? "post_start" : "pre_start",
1888
2249
  message: msg,
1889
2250
  ...code ? { ima_error_code: String(code) } : {}
1890
2251
  },
@@ -1901,7 +2262,7 @@ var BidkernelPrebidAnalytics = class _BidkernelPrebidAnalytics {
1901
2262
  const googleIma = typeof window !== "undefined" ? window.google?.ima : void 0;
1902
2263
  const adEventType = googleIma?.AdEvent?.Type || {};
1903
2264
  const adErrorEventType = googleIma?.AdErrorEvent?.Type || {};
1904
- const listeners = [
2265
+ const rawListeners = [
1905
2266
  { type: adEventType.STARTED || "started", handler: onAdStartedOrImpression },
1906
2267
  { type: adEventType.IMPRESSION || "impression", handler: onAdStartedOrImpression },
1907
2268
  {
@@ -1917,6 +2278,14 @@ var BidkernelPrebidAnalytics = class _BidkernelPrebidAnalytics {
1917
2278
  { type: adEventType.CLICK || "click", handler: onAdClick },
1918
2279
  { type: adErrorEventType.AD_ERROR || "adError", handler: onAdError }
1919
2280
  ];
2281
+ const seenListenerTypes = /* @__PURE__ */ new Set();
2282
+ const listeners = [];
2283
+ for (const l of rawListeners) {
2284
+ if (!seenListenerTypes.has(l.type)) {
2285
+ seenListenerTypes.add(l.type);
2286
+ listeners.push(l);
2287
+ }
2288
+ }
1920
2289
  for (const { type, handler } of listeners) {
1921
2290
  try {
1922
2291
  adsManager.addEventListener(type, handler);
@@ -1924,6 +2293,7 @@ var BidkernelPrebidAnalytics = class _BidkernelPrebidAnalytics {
1924
2293
  }
1925
2294
  }
1926
2295
  const cleanup = () => {
2296
+ adsManager.__bidkernelAttachedInstances?.delete(this);
1927
2297
  for (const { type, handler } of listeners) {
1928
2298
  try {
1929
2299
  adsManager.removeEventListener(type, handler);
@@ -2309,7 +2679,8 @@ var BidkernelPrebidAnalytics = class _BidkernelPrebidAnalytics {
2309
2679
  const adUnitCode = data.adUnitCode || "";
2310
2680
  const cpm = Number.isFinite(data.originalCpm) ? data.originalCpm : Number.isFinite(data.cpm) ? data.cpm : 0;
2311
2681
  const latencyMs = Number.isFinite(data.timeToRespond) ? data.timeToRespond : 0;
2312
- const mediaType = data.mediaType || "banner";
2682
+ const resMediaTypes = Object.keys(data.mediaTypes || {});
2683
+ const mediaType = data.mediaType || (data.mediaTypes?.video ? "video" : resMediaTypes.length > 0 ? resMediaTypes[0] : "banner");
2313
2684
  if (auctionId && bidder) {
2314
2685
  const auctionMap = this.getOrCreateAuctionOutcomes(auctionId);
2315
2686
  const key = `${bidder}:${adUnitCode}`;
@@ -2382,13 +2753,15 @@ var BidkernelPrebidAnalytics = class _BidkernelPrebidAnalytics {
2382
2753
  const auctionId = data.auctionId || "";
2383
2754
  const transactionId = data.transactionId || "";
2384
2755
  const adUnitCode = data.adUnitCode || data.adId || "";
2756
+ const wonMediaTypes = Object.keys(data.mediaTypes || {});
2757
+ const resolvedMediaType = data.mediaType || (data.mediaTypes?.video ? "video" : wonMediaTypes.length > 0 ? wonMediaTypes[0] : "banner");
2385
2758
  const bidTrace = {
2386
2759
  bidder: data.bidderCode || data.bidder || "",
2387
2760
  cpm: Number.isFinite(data.originalCpm) ? data.originalCpm : Number.isFinite(data.cpm) ? data.cpm : 0,
2388
2761
  currency: data.originalCurrency ?? data.currency ?? "USD",
2389
2762
  ...parseBidDimensions(data),
2390
2763
  dealId: data.dealId || "",
2391
- mediaType: data.mediaType || "banner",
2764
+ mediaType: resolvedMediaType,
2392
2765
  latencyMs: Number.isFinite(data.timeToRespond) ? data.timeToRespond : 0,
2393
2766
  advertiserDomain: data.meta?.advertiserDomains?.[0] || "",
2394
2767
  creativeId: data.creativeId || ""
@@ -2477,13 +2850,15 @@ var BidkernelPrebidAnalytics = class _BidkernelPrebidAnalytics {
2477
2850
  const slotKey = data.adUnitCode || bid.adUnitCode || "";
2478
2851
  const altKey = data.adId || bid.adId || "";
2479
2852
  const cached = (auctionId && slotKey ? this.getCachedBid(`${auctionId}:${slotKey}`) : void 0) || (auctionId && altKey ? this.getCachedBid(`${auctionId}:${altKey}`) : void 0) || (slotKey ? this.getCachedBid(slotKey) : void 0) || (altKey ? this.getCachedBid(altKey) : void 0);
2853
+ const isVideo = mediaType === "video" || bid.mediaType === "video" || bid.mediaTypes?.video !== void 0 || cached?.bidTrace?.mediaType === "video" || cached?.rawBid?.mediaType === "video" || adUnitCode && this.slotViewabilityRecords.get(adUnitCode)?.mediaType === "video";
2854
+ const resolvedMediaType = isVideo ? "video" : mediaType || cached?.bidTrace?.mediaType || "banner";
2480
2855
  const bidTrace = {
2481
2856
  bidder: bid.bidderCode || bid.bidder || cached?.bidTrace?.bidder || "",
2482
2857
  cpm: Number.isFinite(bid.originalCpm) ? bid.originalCpm : Number.isFinite(bid.cpm) ? bid.cpm : cached?.bidTrace?.cpm ?? 0,
2483
2858
  currency: bid.originalCurrency ?? bid.currency ?? cached?.bidTrace?.currency ?? "USD",
2484
2859
  ...parseBidDimensions(bid).width ? parseBidDimensions(bid) : cached?.bidTrace ? { width: cached.bidTrace.width, height: cached.bidTrace.height } : parseBidDimensions(bid),
2485
2860
  dealId: bid.dealId || cached?.bidTrace?.dealId || "",
2486
- mediaType: mediaType || cached?.bidTrace?.mediaType || "banner",
2861
+ mediaType: resolvedMediaType,
2487
2862
  latencyMs: Number.isFinite(bid.timeToRespond) ? bid.timeToRespond : cached?.bidTrace?.latencyMs ?? 0,
2488
2863
  advertiserDomain: bid.meta?.advertiserDomains?.[0] || cached?.bidTrace?.advertiserDomain || "",
2489
2864
  creativeId: bid.creativeId || cached?.bidTrace?.creativeId || ""
@@ -2522,28 +2897,35 @@ var BidkernelPrebidAnalytics = class _BidkernelPrebidAnalytics {
2522
2897
  }
2523
2898
  }
2524
2899
  const currentRefreshIndex = adUnitCode ? this.slotRefreshIndices.get(adUnitCode) ?? (record ? record.refreshIndex : 0) : 0;
2525
- const alreadyEmitted = adUnitCode ? this.hasImpressionEmitted(
2526
- adUnitCode,
2527
- currentRefreshIndex,
2528
- resolvedAuctionId,
2529
- bidTrace.creativeId
2530
- ) : false;
2531
- if (!alreadyEmitted) {
2532
- if (adUnitCode) {
2533
- this.markImpressionEmitted(
2900
+ if (!isVideo) {
2901
+ const alreadyEmitted = adUnitCode ? this.hasImpressionEmitted(
2902
+ adUnitCode,
2903
+ currentRefreshIndex,
2904
+ resolvedAuctionId,
2905
+ bidTrace.creativeId
2906
+ ) : false;
2907
+ if (!alreadyEmitted) {
2908
+ if (adUnitCode) {
2909
+ this.markImpressionEmitted(
2910
+ adUnitCode,
2911
+ currentRefreshIndex,
2912
+ resolvedAuctionId,
2913
+ bidTrace.creativeId
2914
+ );
2915
+ }
2916
+ this.enqueue(TraceEventType.IMPRESSION, "impression", {
2917
+ auctionId: resolvedAuctionId,
2918
+ transactionId: resolvedTransactionId,
2534
2919
  adUnitCode,
2535
- currentRefreshIndex,
2536
- resolvedAuctionId,
2537
- bidTrace.creativeId
2538
- );
2920
+ bid: bidTrace,
2921
+ metadata: { refresh_index: String(currentRefreshIndex) }
2922
+ });
2539
2923
  }
2540
- this.enqueue(TraceEventType.IMPRESSION, "impression", {
2541
- auctionId: resolvedAuctionId,
2542
- transactionId: resolvedTransactionId,
2543
- adUnitCode,
2544
- bid: bidTrace,
2545
- metadata: { refresh_index: String(currentRefreshIndex) }
2546
- });
2924
+ } else {
2925
+ this.log(
2926
+ "DEBUG",
2927
+ `adRenderSucceeded received for video ad unit ${adUnitCode || "unknown"}: suppressing IMPRESSION until verified video lifecycle event`
2928
+ );
2547
2929
  }
2548
2930
  if (this.config.viewabilityEnabled && typeof document !== "undefined") {
2549
2931
  let el = null;
@@ -2579,7 +2961,7 @@ var BidkernelPrebidAnalytics = class _BidkernelPrebidAnalytics {
2579
2961
  adUnitCode,
2580
2962
  auctionId: resolvedAuctionId,
2581
2963
  transactionId: resolvedTransactionId,
2582
- mediaType,
2964
+ mediaType: resolvedMediaType,
2583
2965
  bid: bidTrace,
2584
2966
  refreshIndex: currentRefreshIndex,
2585
2967
  emitRefreshEvent: false
@@ -3076,6 +3458,8 @@ function getbidkernel(alias = "bidkernel") {
3076
3458
  PrebidEventDeduper,
3077
3459
  getPrebidEventKey,
3078
3460
  getbidkernel,
3461
+ hookImaPrototype,
3462
+ initImaInterception,
3079
3463
  isTrustedEndpoint,
3080
3464
  registerPrebidAnalytics
3081
3465
  });