@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.mjs CHANGED
@@ -967,8 +967,264 @@ function parseBidDimensions(bid) {
967
967
  height: Number.isFinite(bid?.height) ? bid.height : 0
968
968
  };
969
969
  }
970
+ var adsLoaderListenerMap = /* @__PURE__ */ new WeakMap();
971
+ function hookAdsManagerLoadedEvent(loadedEventCtor) {
972
+ if (!loadedEventCtor) return;
973
+ try {
974
+ const loadedProto = loadedEventCtor.prototype;
975
+ if (loadedProto && typeof loadedProto.getAdsManager === "function" && !loadedProto.getAdsManager.__bidkernelHooked) {
976
+ const originalGetAdsManager = loadedProto.getAdsManager;
977
+ const hookedGetAdsManager = function(...args) {
978
+ const adsManager = originalGetAdsManager.apply(this, args);
979
+ if (adsManager) {
980
+ for (const instance of BidkernelPrebidAnalytics.getActiveInstances()) {
981
+ try {
982
+ instance.attachImaAdsManager(adsManager);
983
+ } catch {
984
+ }
985
+ }
986
+ }
987
+ return adsManager;
988
+ };
989
+ hookedGetAdsManager.__bidkernelHooked = true;
990
+ loadedProto.getAdsManager = hookedGetAdsManager;
991
+ }
992
+ } catch {
993
+ }
994
+ }
995
+ function hookAdsManagerPrototype(adsManagerCtorOrProto) {
996
+ if (!adsManagerCtorOrProto) return;
997
+ try {
998
+ const proto = adsManagerCtorOrProto.prototype || adsManagerCtorOrProto;
999
+ if (proto && typeof proto.init === "function" && !proto.init.__bidkernelHooked) {
1000
+ const origInit = proto.init;
1001
+ const hookedInit = function(...args) {
1002
+ for (const instance of BidkernelPrebidAnalytics.getActiveInstances()) {
1003
+ try {
1004
+ instance.attachImaAdsManager(this);
1005
+ } catch {
1006
+ }
1007
+ }
1008
+ return origInit.apply(this, args);
1009
+ };
1010
+ hookedInit.__bidkernelHooked = true;
1011
+ proto.init = hookedInit;
1012
+ }
1013
+ if (proto && typeof proto.start === "function" && !proto.start.__bidkernelHooked) {
1014
+ const origStart = proto.start;
1015
+ const hookedStart = function(...args) {
1016
+ for (const instance of BidkernelPrebidAnalytics.getActiveInstances()) {
1017
+ try {
1018
+ instance.attachImaAdsManager(this);
1019
+ } catch {
1020
+ }
1021
+ }
1022
+ return origStart.apply(this, args);
1023
+ };
1024
+ hookedStart.__bidkernelHooked = true;
1025
+ proto.start = hookedStart;
1026
+ }
1027
+ } catch {
1028
+ }
1029
+ }
1030
+ function hookAdsLoader(adsLoaderCtor) {
1031
+ if (!adsLoaderCtor) return;
1032
+ try {
1033
+ const proto = adsLoaderCtor.prototype;
1034
+ if (!proto) return;
1035
+ if (typeof proto.addEventListener === "function" && !proto.addEventListener.__bidkernelHooked) {
1036
+ const origAddEventListener = proto.addEventListener;
1037
+ const hookedAddEventListener = function(type, listener, ...rest) {
1038
+ if ((type === "adsManagerLoaded" || type === (this?.AdsManagerLoadedEvent?.Type?.ADS_MANAGER_LOADED || "adsManagerLoaded")) && typeof listener === "function") {
1039
+ let wrappedListener = adsLoaderListenerMap.get(listener);
1040
+ if (!wrappedListener) {
1041
+ wrappedListener = function(event) {
1042
+ if (event && typeof event.getAdsManager === "function" && !event.getAdsManager.__bidkernelHooked) {
1043
+ const origGetAdsManager = event.getAdsManager;
1044
+ event.getAdsManager = function(...args) {
1045
+ const adsManager = origGetAdsManager.apply(this, args);
1046
+ if (adsManager) {
1047
+ for (const sdk of BidkernelPrebidAnalytics.getActiveInstances()) {
1048
+ try {
1049
+ sdk.attachImaAdsManager(adsManager);
1050
+ } catch {
1051
+ }
1052
+ }
1053
+ }
1054
+ return adsManager;
1055
+ };
1056
+ event.getAdsManager.__bidkernelHooked = true;
1057
+ }
1058
+ return listener.apply(this, arguments);
1059
+ };
1060
+ adsLoaderListenerMap.set(listener, wrappedListener);
1061
+ }
1062
+ return origAddEventListener.call(this, type, wrappedListener, ...rest);
1063
+ }
1064
+ return origAddEventListener.call(this, type, listener, ...rest);
1065
+ };
1066
+ hookedAddEventListener.__bidkernelHooked = true;
1067
+ proto.addEventListener = hookedAddEventListener;
1068
+ }
1069
+ if (typeof proto.removeEventListener === "function" && !proto.removeEventListener.__bidkernelHooked) {
1070
+ const origRemoveEventListener = proto.removeEventListener;
1071
+ const hookedRemoveEventListener = function(type, listener, ...rest) {
1072
+ const wrapped = typeof listener === "function" ? adsLoaderListenerMap.get(listener) : void 0;
1073
+ return origRemoveEventListener.call(this, type, wrapped || listener, ...rest);
1074
+ };
1075
+ hookedRemoveEventListener.__bidkernelHooked = true;
1076
+ proto.removeEventListener = hookedRemoveEventListener;
1077
+ }
1078
+ } catch {
1079
+ }
1080
+ }
1081
+ function hookImaPrototype(ima) {
1082
+ if (!ima || typeof ima !== "object") return;
1083
+ try {
1084
+ if (ima.AdsManagerLoadedEvent) {
1085
+ hookAdsManagerLoadedEvent(ima.AdsManagerLoadedEvent);
1086
+ }
1087
+ const loadedDesc = Object.getOwnPropertyDescriptor(ima, "AdsManagerLoadedEvent");
1088
+ if (!loadedDesc || loadedDesc.configurable) {
1089
+ let _loadedEvent = ima.AdsManagerLoadedEvent;
1090
+ try {
1091
+ Object.defineProperty(ima, "AdsManagerLoadedEvent", {
1092
+ configurable: true,
1093
+ enumerable: true,
1094
+ get() {
1095
+ return _loadedEvent;
1096
+ },
1097
+ set(val) {
1098
+ _loadedEvent = val;
1099
+ hookAdsManagerLoadedEvent(val);
1100
+ }
1101
+ });
1102
+ } catch {
1103
+ }
1104
+ }
1105
+ if (ima.AdsLoader) {
1106
+ hookAdsLoader(ima.AdsLoader);
1107
+ }
1108
+ const loaderDesc = Object.getOwnPropertyDescriptor(ima, "AdsLoader");
1109
+ if (!loaderDesc || loaderDesc.configurable) {
1110
+ let _loader = ima.AdsLoader;
1111
+ try {
1112
+ Object.defineProperty(ima, "AdsLoader", {
1113
+ configurable: true,
1114
+ enumerable: true,
1115
+ get() {
1116
+ return _loader;
1117
+ },
1118
+ set(val) {
1119
+ _loader = val;
1120
+ hookAdsLoader(val);
1121
+ }
1122
+ });
1123
+ } catch {
1124
+ }
1125
+ }
1126
+ if (ima.AdsManager) {
1127
+ hookAdsManagerPrototype(ima.AdsManager);
1128
+ }
1129
+ const mgrDesc = Object.getOwnPropertyDescriptor(ima, "AdsManager");
1130
+ if (!mgrDesc || mgrDesc.configurable) {
1131
+ let _mgr = ima.AdsManager;
1132
+ try {
1133
+ Object.defineProperty(ima, "AdsManager", {
1134
+ configurable: true,
1135
+ enumerable: true,
1136
+ get() {
1137
+ return _mgr;
1138
+ },
1139
+ set(val) {
1140
+ _mgr = val;
1141
+ hookAdsManagerPrototype(val);
1142
+ }
1143
+ });
1144
+ } catch {
1145
+ }
1146
+ }
1147
+ } catch {
1148
+ }
1149
+ }
1150
+ function initImaInterception() {
1151
+ if (typeof window === "undefined") return;
1152
+ const win = window;
1153
+ if (win.google?.ima) {
1154
+ hookImaPrototype(win.google.ima);
1155
+ }
1156
+ if (win.google && typeof win.google === "object") {
1157
+ const imaDesc = Object.getOwnPropertyDescriptor(win.google, "ima");
1158
+ if (!imaDesc || imaDesc.configurable) {
1159
+ let _ima = win.google.ima;
1160
+ try {
1161
+ Object.defineProperty(win.google, "ima", {
1162
+ configurable: true,
1163
+ enumerable: true,
1164
+ get() {
1165
+ return _ima;
1166
+ },
1167
+ set(val) {
1168
+ _ima = val;
1169
+ hookImaPrototype(val);
1170
+ }
1171
+ });
1172
+ } catch {
1173
+ }
1174
+ }
1175
+ }
1176
+ const googleDesc = Object.getOwnPropertyDescriptor(win, "google");
1177
+ if (!googleDesc || googleDesc.configurable) {
1178
+ let _google = win.google;
1179
+ try {
1180
+ Object.defineProperty(win, "google", {
1181
+ configurable: true,
1182
+ enumerable: true,
1183
+ get() {
1184
+ return _google;
1185
+ },
1186
+ set(val) {
1187
+ _google = val;
1188
+ if (_google && typeof _google === "object") {
1189
+ if (_google.ima) {
1190
+ hookImaPrototype(_google.ima);
1191
+ }
1192
+ const iDesc = Object.getOwnPropertyDescriptor(_google, "ima");
1193
+ if (!iDesc || iDesc.configurable) {
1194
+ let _i = _google.ima;
1195
+ try {
1196
+ Object.defineProperty(_google, "ima", {
1197
+ configurable: true,
1198
+ enumerable: true,
1199
+ get() {
1200
+ return _i;
1201
+ },
1202
+ set(iv) {
1203
+ _i = iv;
1204
+ hookImaPrototype(iv);
1205
+ }
1206
+ });
1207
+ } catch {
1208
+ }
1209
+ }
1210
+ }
1211
+ }
1212
+ });
1213
+ } catch {
1214
+ }
1215
+ }
1216
+ }
1217
+ if (typeof window !== "undefined") {
1218
+ initImaInterception();
1219
+ }
970
1220
  var BidkernelPrebidAnalytics = class _BidkernelPrebidAnalytics {
971
1221
  static activeInstances = /* @__PURE__ */ new Set();
1222
+ static getActiveInstances() {
1223
+ return _BidkernelPrebidAnalytics.activeInstances;
1224
+ }
1225
+ static initImaInterception() {
1226
+ initImaInterception();
1227
+ }
972
1228
  config;
973
1229
  queue = [];
974
1230
  errorCount = 0;
@@ -1132,6 +1388,10 @@ var BidkernelPrebidAnalytics = class _BidkernelPrebidAnalytics {
1132
1388
  }
1133
1389
  this.flushTimer = setInterval(() => this.flush(), FLUSH_INTERVAL_MS);
1134
1390
  this.resendPersistedBatches();
1391
+ initImaInterception();
1392
+ if (window.google?.ima) {
1393
+ hookImaPrototype(window.google.ima);
1394
+ }
1135
1395
  }
1136
1396
  }
1137
1397
  disable() {
@@ -1612,6 +1872,72 @@ var BidkernelPrebidAnalytics = class _BidkernelPrebidAnalytics {
1612
1872
  this.cachedWinningBids.delete(oldest.value);
1613
1873
  }
1614
1874
  }
1875
+ /**
1876
+ * Searches cached winning bids for an entry matching the given filters (creativeId,
1877
+ * adId, or mediaType), falling back to the most recent winning video bid.
1878
+ */
1879
+ findCachedWinningBid(filter) {
1880
+ const now = Date.now();
1881
+ const isConsumed = (entry) => {
1882
+ return this.hasImpressionEmitted(
1883
+ entry.adUnitCode,
1884
+ void 0,
1885
+ entry.auctionId,
1886
+ entry.bidTrace.creativeId
1887
+ );
1888
+ };
1889
+ if (filter?.creativeId || filter?.adId) {
1890
+ for (const entry of Array.from(this.cachedWinningBids.values())) {
1891
+ if (now - entry.timestamp > CACHED_BID_TTL_MS) continue;
1892
+ if (filter.creativeId && (entry.bidTrace.creativeId === filter.creativeId || entry.rawBid?.creativeId === filter.creativeId) || filter.adId && (entry.rawBid?.adId === filter.adId || entry.adUnitCode === filter.adId)) {
1893
+ return entry;
1894
+ }
1895
+ }
1896
+ }
1897
+ let bestUnconsumedMatch;
1898
+ if (filter?.mediaType) {
1899
+ for (const entry of Array.from(this.cachedWinningBids.values())) {
1900
+ if (now - entry.timestamp > CACHED_BID_TTL_MS) continue;
1901
+ const isEntryVideo = entry.bidTrace.mediaType === filter.mediaType || entry.rawBid?.mediaType === filter.mediaType || filter.mediaType === "video" && entry.rawBid?.mediaTypes?.video !== void 0;
1902
+ if (isEntryVideo) {
1903
+ if (!isConsumed(entry)) {
1904
+ if (!bestUnconsumedMatch || entry.timestamp < bestUnconsumedMatch.timestamp) {
1905
+ bestUnconsumedMatch = entry;
1906
+ }
1907
+ }
1908
+ }
1909
+ }
1910
+ if (bestUnconsumedMatch) return bestUnconsumedMatch;
1911
+ let bestFallbackMatch;
1912
+ for (const entry of Array.from(this.cachedWinningBids.values())) {
1913
+ if (now - entry.timestamp > CACHED_BID_TTL_MS) continue;
1914
+ const isEntryVideo = entry.bidTrace.mediaType === filter.mediaType || entry.rawBid?.mediaType === filter.mediaType || filter.mediaType === "video" && entry.rawBid?.mediaTypes?.video !== void 0;
1915
+ if (isEntryVideo) {
1916
+ if (!bestFallbackMatch || entry.timestamp > bestFallbackMatch.timestamp) {
1917
+ bestFallbackMatch = entry;
1918
+ }
1919
+ }
1920
+ }
1921
+ if (bestFallbackMatch) return bestFallbackMatch;
1922
+ }
1923
+ for (const entry of Array.from(this.cachedWinningBids.values())) {
1924
+ if (now - entry.timestamp > CACHED_BID_TTL_MS) continue;
1925
+ if (!isConsumed(entry)) {
1926
+ if (!bestUnconsumedMatch || entry.timestamp > bestUnconsumedMatch.timestamp) {
1927
+ bestUnconsumedMatch = entry;
1928
+ }
1929
+ }
1930
+ }
1931
+ if (bestUnconsumedMatch) return bestUnconsumedMatch;
1932
+ let bestRecent;
1933
+ for (const entry of Array.from(this.cachedWinningBids.values())) {
1934
+ if (now - entry.timestamp > CACHED_BID_TTL_MS) continue;
1935
+ if (!bestRecent || entry.timestamp > bestRecent.timestamp) {
1936
+ bestRecent = entry;
1937
+ }
1938
+ }
1939
+ return bestRecent;
1940
+ }
1615
1941
  /** Records a dedup key, evicting oldest-first at the cap. */
1616
1942
  addImpressionKey(key) {
1617
1943
  if (this.slotEmittedImpressionKeys.has(key)) return;
@@ -1771,14 +2097,40 @@ var BidkernelPrebidAnalytics = class _BidkernelPrebidAnalytics {
1771
2097
  return () => {
1772
2098
  };
1773
2099
  }
2100
+ if (!adsManager.__bidkernelAttachedInstances) {
2101
+ try {
2102
+ Object.defineProperty(adsManager, "__bidkernelAttachedInstances", {
2103
+ value: /* @__PURE__ */ new Set(),
2104
+ configurable: true,
2105
+ writable: true
2106
+ });
2107
+ } catch {
2108
+ adsManager.__bidkernelAttachedInstances = /* @__PURE__ */ new Set();
2109
+ }
2110
+ }
2111
+ if (adsManager.__bidkernelAttachedInstances.has(this)) {
2112
+ return () => {
2113
+ };
2114
+ }
2115
+ adsManager.__bidkernelAttachedInstances.add(this);
1774
2116
  const slotId = options?.slotId || options?.adUnitCode || "video";
1775
2117
  const adUnitCode = options?.adUnitCode || slotId;
1776
2118
  const auctionId = options?.auctionId || "";
1777
2119
  const transactionId = options?.transactionId || "";
1778
- const getWinningBid = () => {
1779
- 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);
2120
+ const getWinningBid = (adData) => {
2121
+ 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);
2122
+ if (!cached) {
2123
+ cached = this.findCachedWinningBid({
2124
+ creativeId: adData?.creativeId,
2125
+ adId: adData?.adId,
2126
+ mediaType: "video"
2127
+ });
2128
+ }
2129
+ return cached;
1780
2130
  };
2131
+ let adStarted = false;
1781
2132
  const onAdStartedOrImpression = (event) => {
2133
+ adStarted = true;
1782
2134
  this.log("DEBUG", "IMA AdEvent.STARTED / IMPRESSION received", event);
1783
2135
  const ad = typeof event?.getAd === "function" ? event.getAd() : event?.ad;
1784
2136
  const adData = {};
@@ -1789,16 +2141,20 @@ var BidkernelPrebidAnalytics = class _BidkernelPrebidAnalytics {
1789
2141
  adData.duration = typeof ad.getDuration === "function" ? ad.getDuration() : ad.duration;
1790
2142
  adData.advertiserName = typeof ad.getAdvertiserName === "function" ? ad.getAdvertiserName() : "";
1791
2143
  }
1792
- const cached = getWinningBid();
2144
+ const cached = getWinningBid(adData);
2145
+ const resolvedSlotId = options?.slotId && options.slotId !== "video" ? options.slotId : options?.adUnitCode && options.adUnitCode !== "video" ? options.adUnitCode : cached?.adUnitCode || options?.slotId || options?.adUnitCode || "video";
2146
+ const resolvedAdUnitCode = options?.adUnitCode && options.adUnitCode !== "video" ? options.adUnitCode : cached?.adUnitCode || resolvedSlotId;
2147
+ const resolvedAuctionId = auctionId || cached?.auctionId || "";
2148
+ const resolvedTransactionId = transactionId || cached?.transactionId || "";
1793
2149
  const mergedBid = {
1794
2150
  ...cached?.rawBid || options?.bid,
1795
2151
  mediaType: "video",
1796
2152
  creativeId: adData.creativeId || cached?.bidTrace?.creativeId || options?.bid?.creativeId || ""
1797
2153
  };
1798
- const emitted = this.recordImpression(slotId, {
1799
- adUnitCode,
1800
- auctionId: auctionId || cached?.auctionId,
1801
- transactionId: transactionId || cached?.transactionId,
2154
+ const emitted = this.recordImpression(resolvedSlotId, {
2155
+ adUnitCode: resolvedAdUnitCode,
2156
+ auctionId: resolvedAuctionId,
2157
+ transactionId: resolvedTransactionId,
1802
2158
  mediaType: "video",
1803
2159
  bid: mergedBid,
1804
2160
  metadata: {
@@ -1810,7 +2166,7 @@ var BidkernelPrebidAnalytics = class _BidkernelPrebidAnalytics {
1810
2166
  });
1811
2167
  if (emitted && options?.onImpression) {
1812
2168
  try {
1813
- options.onImpression(slotId, { ad, bid: mergedBid });
2169
+ options.onImpression(resolvedSlotId, { ad, bid: mergedBid });
1814
2170
  } catch (e) {
1815
2171
  this.log("ERROR", "Error in onImpression callback", e);
1816
2172
  }
@@ -1829,10 +2185,11 @@ var BidkernelPrebidAnalytics = class _BidkernelPrebidAnalytics {
1829
2185
  const onAdClick = (event) => {
1830
2186
  this.log("DEBUG", "IMA AdEvent.CLICK received", event);
1831
2187
  const cached = getWinningBid();
2188
+ const resolvedAdUnitCode = options?.adUnitCode && options.adUnitCode !== "video" ? options.adUnitCode : cached?.adUnitCode || slotId;
1832
2189
  this.enqueue(TraceEventType.CLICK, "click", {
1833
2190
  auctionId: auctionId || cached?.auctionId,
1834
2191
  transactionId: transactionId || cached?.transactionId,
1835
- adUnitCode,
2192
+ adUnitCode: resolvedAdUnitCode,
1836
2193
  bid: cached?.bidTrace || options?.bid,
1837
2194
  metadata: {
1838
2195
  ...options?.metadata,
@@ -1846,14 +2203,16 @@ var BidkernelPrebidAnalytics = class _BidkernelPrebidAnalytics {
1846
2203
  const msg = (err && typeof err.getMessage === "function" ? err.getMessage() : err?.message) || String(err || "IMA ad error");
1847
2204
  const code = (err && typeof err.getErrorCode === "function" ? err.getErrorCode() : err?.code) || "";
1848
2205
  const cached = getWinningBid();
2206
+ const resolvedAdUnitCode = options?.adUnitCode && options.adUnitCode !== "video" ? options.adUnitCode : cached?.adUnitCode || slotId;
1849
2207
  this.enqueue(TraceEventType.AD_RENDER_FAILED, "adRenderFailed", {
1850
2208
  auctionId: auctionId || cached?.auctionId,
1851
2209
  transactionId: transactionId || cached?.transactionId,
1852
- adUnitCode,
2210
+ adUnitCode: resolvedAdUnitCode,
1853
2211
  bid: cached?.bidTrace || options?.bid,
1854
2212
  metadata: {
1855
2213
  ...options?.metadata,
1856
- reason: "ima_ad_error",
2214
+ reason: adStarted ? "ima_playback_error" : "ima_ad_error",
2215
+ playback_phase: adStarted ? "post_start" : "pre_start",
1857
2216
  message: msg,
1858
2217
  ...code ? { ima_error_code: String(code) } : {}
1859
2218
  },
@@ -1870,7 +2229,7 @@ var BidkernelPrebidAnalytics = class _BidkernelPrebidAnalytics {
1870
2229
  const googleIma = typeof window !== "undefined" ? window.google?.ima : void 0;
1871
2230
  const adEventType = googleIma?.AdEvent?.Type || {};
1872
2231
  const adErrorEventType = googleIma?.AdErrorEvent?.Type || {};
1873
- const listeners = [
2232
+ const rawListeners = [
1874
2233
  { type: adEventType.STARTED || "started", handler: onAdStartedOrImpression },
1875
2234
  { type: adEventType.IMPRESSION || "impression", handler: onAdStartedOrImpression },
1876
2235
  {
@@ -1886,6 +2245,14 @@ var BidkernelPrebidAnalytics = class _BidkernelPrebidAnalytics {
1886
2245
  { type: adEventType.CLICK || "click", handler: onAdClick },
1887
2246
  { type: adErrorEventType.AD_ERROR || "adError", handler: onAdError }
1888
2247
  ];
2248
+ const seenListenerTypes = /* @__PURE__ */ new Set();
2249
+ const listeners = [];
2250
+ for (const l of rawListeners) {
2251
+ if (!seenListenerTypes.has(l.type)) {
2252
+ seenListenerTypes.add(l.type);
2253
+ listeners.push(l);
2254
+ }
2255
+ }
1889
2256
  for (const { type, handler } of listeners) {
1890
2257
  try {
1891
2258
  adsManager.addEventListener(type, handler);
@@ -1893,6 +2260,7 @@ var BidkernelPrebidAnalytics = class _BidkernelPrebidAnalytics {
1893
2260
  }
1894
2261
  }
1895
2262
  const cleanup = () => {
2263
+ adsManager.__bidkernelAttachedInstances?.delete(this);
1896
2264
  for (const { type, handler } of listeners) {
1897
2265
  try {
1898
2266
  adsManager.removeEventListener(type, handler);
@@ -2278,7 +2646,8 @@ var BidkernelPrebidAnalytics = class _BidkernelPrebidAnalytics {
2278
2646
  const adUnitCode = data.adUnitCode || "";
2279
2647
  const cpm = Number.isFinite(data.originalCpm) ? data.originalCpm : Number.isFinite(data.cpm) ? data.cpm : 0;
2280
2648
  const latencyMs = Number.isFinite(data.timeToRespond) ? data.timeToRespond : 0;
2281
- const mediaType = data.mediaType || "banner";
2649
+ const resMediaTypes = Object.keys(data.mediaTypes || {});
2650
+ const mediaType = data.mediaType || (data.mediaTypes?.video ? "video" : resMediaTypes.length > 0 ? resMediaTypes[0] : "banner");
2282
2651
  if (auctionId && bidder) {
2283
2652
  const auctionMap = this.getOrCreateAuctionOutcomes(auctionId);
2284
2653
  const key = `${bidder}:${adUnitCode}`;
@@ -2351,13 +2720,15 @@ var BidkernelPrebidAnalytics = class _BidkernelPrebidAnalytics {
2351
2720
  const auctionId = data.auctionId || "";
2352
2721
  const transactionId = data.transactionId || "";
2353
2722
  const adUnitCode = data.adUnitCode || data.adId || "";
2723
+ const wonMediaTypes = Object.keys(data.mediaTypes || {});
2724
+ const resolvedMediaType = data.mediaType || (data.mediaTypes?.video ? "video" : wonMediaTypes.length > 0 ? wonMediaTypes[0] : "banner");
2354
2725
  const bidTrace = {
2355
2726
  bidder: data.bidderCode || data.bidder || "",
2356
2727
  cpm: Number.isFinite(data.originalCpm) ? data.originalCpm : Number.isFinite(data.cpm) ? data.cpm : 0,
2357
2728
  currency: data.originalCurrency ?? data.currency ?? "USD",
2358
2729
  ...parseBidDimensions(data),
2359
2730
  dealId: data.dealId || "",
2360
- mediaType: data.mediaType || "banner",
2731
+ mediaType: resolvedMediaType,
2361
2732
  latencyMs: Number.isFinite(data.timeToRespond) ? data.timeToRespond : 0,
2362
2733
  advertiserDomain: data.meta?.advertiserDomains?.[0] || "",
2363
2734
  creativeId: data.creativeId || ""
@@ -2446,13 +2817,15 @@ var BidkernelPrebidAnalytics = class _BidkernelPrebidAnalytics {
2446
2817
  const slotKey = data.adUnitCode || bid.adUnitCode || "";
2447
2818
  const altKey = data.adId || bid.adId || "";
2448
2819
  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);
2820
+ 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";
2821
+ const resolvedMediaType = isVideo ? "video" : mediaType || cached?.bidTrace?.mediaType || "banner";
2449
2822
  const bidTrace = {
2450
2823
  bidder: bid.bidderCode || bid.bidder || cached?.bidTrace?.bidder || "",
2451
2824
  cpm: Number.isFinite(bid.originalCpm) ? bid.originalCpm : Number.isFinite(bid.cpm) ? bid.cpm : cached?.bidTrace?.cpm ?? 0,
2452
2825
  currency: bid.originalCurrency ?? bid.currency ?? cached?.bidTrace?.currency ?? "USD",
2453
2826
  ...parseBidDimensions(bid).width ? parseBidDimensions(bid) : cached?.bidTrace ? { width: cached.bidTrace.width, height: cached.bidTrace.height } : parseBidDimensions(bid),
2454
2827
  dealId: bid.dealId || cached?.bidTrace?.dealId || "",
2455
- mediaType: mediaType || cached?.bidTrace?.mediaType || "banner",
2828
+ mediaType: resolvedMediaType,
2456
2829
  latencyMs: Number.isFinite(bid.timeToRespond) ? bid.timeToRespond : cached?.bidTrace?.latencyMs ?? 0,
2457
2830
  advertiserDomain: bid.meta?.advertiserDomains?.[0] || cached?.bidTrace?.advertiserDomain || "",
2458
2831
  creativeId: bid.creativeId || cached?.bidTrace?.creativeId || ""
@@ -2491,28 +2864,35 @@ var BidkernelPrebidAnalytics = class _BidkernelPrebidAnalytics {
2491
2864
  }
2492
2865
  }
2493
2866
  const currentRefreshIndex = adUnitCode ? this.slotRefreshIndices.get(adUnitCode) ?? (record ? record.refreshIndex : 0) : 0;
2494
- const alreadyEmitted = adUnitCode ? this.hasImpressionEmitted(
2495
- adUnitCode,
2496
- currentRefreshIndex,
2497
- resolvedAuctionId,
2498
- bidTrace.creativeId
2499
- ) : false;
2500
- if (!alreadyEmitted) {
2501
- if (adUnitCode) {
2502
- this.markImpressionEmitted(
2867
+ if (!isVideo) {
2868
+ const alreadyEmitted = adUnitCode ? this.hasImpressionEmitted(
2869
+ adUnitCode,
2870
+ currentRefreshIndex,
2871
+ resolvedAuctionId,
2872
+ bidTrace.creativeId
2873
+ ) : false;
2874
+ if (!alreadyEmitted) {
2875
+ if (adUnitCode) {
2876
+ this.markImpressionEmitted(
2877
+ adUnitCode,
2878
+ currentRefreshIndex,
2879
+ resolvedAuctionId,
2880
+ bidTrace.creativeId
2881
+ );
2882
+ }
2883
+ this.enqueue(TraceEventType.IMPRESSION, "impression", {
2884
+ auctionId: resolvedAuctionId,
2885
+ transactionId: resolvedTransactionId,
2503
2886
  adUnitCode,
2504
- currentRefreshIndex,
2505
- resolvedAuctionId,
2506
- bidTrace.creativeId
2507
- );
2887
+ bid: bidTrace,
2888
+ metadata: { refresh_index: String(currentRefreshIndex) }
2889
+ });
2508
2890
  }
2509
- this.enqueue(TraceEventType.IMPRESSION, "impression", {
2510
- auctionId: resolvedAuctionId,
2511
- transactionId: resolvedTransactionId,
2512
- adUnitCode,
2513
- bid: bidTrace,
2514
- metadata: { refresh_index: String(currentRefreshIndex) }
2515
- });
2891
+ } else {
2892
+ this.log(
2893
+ "DEBUG",
2894
+ `adRenderSucceeded received for video ad unit ${adUnitCode || "unknown"}: suppressing IMPRESSION until verified video lifecycle event`
2895
+ );
2516
2896
  }
2517
2897
  if (this.config.viewabilityEnabled && typeof document !== "undefined") {
2518
2898
  let el = null;
@@ -2548,7 +2928,7 @@ var BidkernelPrebidAnalytics = class _BidkernelPrebidAnalytics {
2548
2928
  adUnitCode,
2549
2929
  auctionId: resolvedAuctionId,
2550
2930
  transactionId: resolvedTransactionId,
2551
- mediaType,
2931
+ mediaType: resolvedMediaType,
2552
2932
  bid: bidTrace,
2553
2933
  refreshIndex: currentRefreshIndex,
2554
2934
  emitRefreshEvent: false
@@ -3044,6 +3424,8 @@ export {
3044
3424
  PrebidEventDeduper,
3045
3425
  getPrebidEventKey,
3046
3426
  getbidkernel,
3427
+ hookImaPrototype,
3428
+ initImaInterception,
3047
3429
  isTrustedEndpoint,
3048
3430
  registerPrebidAnalytics
3049
3431
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@bidkernel/analytics",
3
- "version": "0.9.0",
3
+ "version": "0.10.0",
4
4
  "description": "Bidkernel auction analytics: a standalone Prebid.js analytics adapter, plus TypeScript bindings for the Bidkernel ad SDK.",
5
5
  "keywords": [
6
6
  "advertising",