@bidkernel/analytics 0.24.0 → 0.27.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
@@ -304,6 +304,14 @@ var TraceEventType = {
304
304
  CLICK: 12,
305
305
  /** AD_RENDER_FAILED - Prebid failed to render the winning ad */
306
306
  AD_RENDER_FAILED: 13,
307
+ /**
308
+ * WARN - A wrapper condition worth reporting that is not a failure: a slot with no
309
+ * fitting sizes, a video load the watchdog cut short. Carries its own
310
+ * event_name, like ERROR. Publishers gate these with warnings_enabled;
311
+ * ERROR is never gated. Before this type these events reached the adapter
312
+ * and were dropped for want of one, so warnings_enabled controlled nothing.
313
+ */
314
+ WARN: 17,
307
315
  UNRECOGNIZED: -1
308
316
  };
309
317
  var BidderOutcome = {
@@ -1010,6 +1018,14 @@ var EVENT_NAME_TO_TYPE = {
1010
1018
  timeInView: TraceEventType.TIME_IN_VIEW,
1011
1019
  viewable: TraceEventType.VIEWABLE
1012
1020
  };
1021
+ var LEVEL_TO_TYPE = {
1022
+ WARN: TraceEventType.WARN,
1023
+ ERROR: TraceEventType.ERROR
1024
+ };
1025
+ var SELF_NAMING_TYPES = /* @__PURE__ */ new Set([
1026
+ TraceEventType.ERROR,
1027
+ TraceEventType.WARN
1028
+ ]);
1013
1029
  var REVENUE_FLUSH_DELAY_MS = 3e3;
1014
1030
  var REVENUE_FLUSH_TYPES = /* @__PURE__ */ new Set([
1015
1031
  TraceEventType.IMPRESSION,
@@ -1277,11 +1293,26 @@ function parseBidDimensions(bid) {
1277
1293
  height: Number.isFinite(bid?.height) ? bid.height : 0
1278
1294
  };
1279
1295
  }
1296
+ var defaultLogger = createLogger({
1297
+ prefix: "[analytics]",
1298
+ logLevel: "INFO"
1299
+ });
1280
1300
  var BidkernelPrebidAnalytics = class _BidkernelPrebidAnalytics {
1281
1301
  static activeInstances = /* @__PURE__ */ new Set();
1302
+ static allInstances = /* @__PURE__ */ new Set();
1282
1303
  static getActiveInstances() {
1283
1304
  return _BidkernelPrebidAnalytics.activeInstances;
1284
1305
  }
1306
+ static setLogLevel(level) {
1307
+ const norm = normalizeLogLevel(level);
1308
+ defaultLogger.setLevel(norm);
1309
+ for (const inst of _BidkernelPrebidAnalytics.allInstances) {
1310
+ inst.setLogLevel(norm);
1311
+ }
1312
+ }
1313
+ static getLogLevel() {
1314
+ return defaultLogger.getLevel();
1315
+ }
1285
1316
  /**
1286
1317
  * Allows or forbids localStorage writes for every instance on the page.
1287
1318
  * Collection and delivery are unaffected: only the persisted session id,
@@ -1320,6 +1351,7 @@ var BidkernelPrebidAnalytics = class _BidkernelPrebidAnalytics {
1320
1351
  slotViewabilityRecords = /* @__PURE__ */ new Map();
1321
1352
  elementToSlotId = /* @__PURE__ */ new Map();
1322
1353
  slotRefreshIndices = /* @__PURE__ */ new Map();
1354
+ slotAdSessionCounters = /* @__PURE__ */ new Map();
1323
1355
  slotLastAuctionIds = /* @__PURE__ */ new Map();
1324
1356
  pendingThresholdListeners = /* @__PURE__ */ new Map();
1325
1357
  pendingViewableListeners = /* @__PURE__ */ new Map();
@@ -1363,11 +1395,12 @@ var BidkernelPrebidAnalytics = class _BidkernelPrebidAnalytics {
1363
1395
  errorsEnabled: config.errorsEnabled ?? true,
1364
1396
  viewabilityEnabled: config.viewabilityEnabled ?? true,
1365
1397
  storageAllowed: config.storageAllowed ?? true,
1366
- logLevel: config.logLevel || "INFO",
1398
+ logLevel: config.logLevel ? normalizeLogLevel(config.logLevel) : defaultLogger.getLevel(),
1367
1399
  pbjsGlobalName: config.pbjsGlobalName || "pbjs",
1368
1400
  attachPbjsListeners: config.attachPbjsListeners ?? true,
1369
1401
  revenueFlushDelayMs: Number.isFinite(config.revenueFlushDelayMs) && config.revenueFlushDelayMs >= 0 ? config.revenueFlushDelayMs : REVENUE_FLUSH_DELAY_MS
1370
1402
  };
1403
+ _BidkernelPrebidAnalytics.allInstances.add(this);
1371
1404
  this.logger = createLogger({
1372
1405
  prefix: "[analytics]",
1373
1406
  logLevel: this.config.logLevel
@@ -1387,6 +1420,15 @@ var BidkernelPrebidAnalytics = class _BidkernelPrebidAnalytics {
1387
1420
  };
1388
1421
  this.boundVisibilityChange = () => this.handleVisibilityChange();
1389
1422
  }
1423
+ setLogLevel(level) {
1424
+ const norm = normalizeLogLevel(level);
1425
+ this.config.logLevel = norm;
1426
+ this.logger.setLevel(norm);
1427
+ defaultLogger.setLevel(norm);
1428
+ }
1429
+ getLogLevel() {
1430
+ return this.logger.getLevel();
1431
+ }
1390
1432
  enable() {
1391
1433
  if (this.isEnabled) return;
1392
1434
  this.isEnabled = true;
@@ -2023,6 +2065,7 @@ var BidkernelPrebidAnalytics = class _BidkernelPrebidAnalytics {
2023
2065
  this.unobserveSlot(slotId);
2024
2066
  this.slotViewabilityRecords.delete(slotId);
2025
2067
  this.slotRefreshIndices.delete(slotId);
2068
+ this.slotAdSessionCounters.delete(slotId);
2026
2069
  this.slotLastAuctionIds.delete(slotId);
2027
2070
  this.pendingThresholdListeners.delete(slotId);
2028
2071
  this.pendingViewableListeners.delete(slotId);
@@ -2100,9 +2143,37 @@ var BidkernelPrebidAnalytics = class _BidkernelPrebidAnalytics {
2100
2143
  }
2101
2144
  return auctionMap;
2102
2145
  }
2103
- hasImpressionEmitted(slotId, refreshIndex, auctionId, creativeId) {
2146
+ nextSlotAdSession(slotId) {
2147
+ const next = (this.slotAdSessionCounters.get(slotId) ?? 0) + 1;
2148
+ this.slotAdSessionCounters.set(slotId, next);
2149
+ return `ad-session-${next}`;
2150
+ }
2151
+ hasImpressionEmitted(slotId, refreshIndex, auctionId, creativeId, adId) {
2104
2152
  const rIndex = refreshIndex ?? this.slotRefreshIndices.get(slotId) ?? 0;
2105
2153
  const baseSlotKey = `${escapeKeyPart(slotId)}:${rIndex}`;
2154
+ if (adId) {
2155
+ if (auctionId) {
2156
+ if (this.slotEmittedImpressionKeys.has(
2157
+ `${baseSlotKey}:auc:${escapeKeyPart(auctionId)}:ad:${escapeKeyPart(adId)}`
2158
+ ) || this.slotEmittedImpressionKeys.has(`${baseSlotKey}:ad:${escapeKeyPart(adId)}`)) {
2159
+ return true;
2160
+ }
2161
+ } else if (this.slotEmittedImpressionKeys.has(`${baseSlotKey}:ad:${escapeKeyPart(adId)}`)) {
2162
+ return true;
2163
+ }
2164
+ if (creativeId && creativeId !== adId) {
2165
+ if (auctionId) {
2166
+ if (this.slotEmittedImpressionKeys.has(
2167
+ `${baseSlotKey}:auc:${escapeKeyPart(auctionId)}:${escapeKeyPart(creativeId)}`
2168
+ ) || this.slotEmittedImpressionKeys.has(`${baseSlotKey}:${escapeKeyPart(creativeId)}`)) {
2169
+ return true;
2170
+ }
2171
+ } else if (this.slotEmittedImpressionKeys.has(`${baseSlotKey}:${escapeKeyPart(creativeId)}`)) {
2172
+ return true;
2173
+ }
2174
+ }
2175
+ return false;
2176
+ }
2106
2177
  if (creativeId) {
2107
2178
  if (auctionId) {
2108
2179
  return this.slotEmittedImpressionKeys.has(
@@ -2116,18 +2187,33 @@ var BidkernelPrebidAnalytics = class _BidkernelPrebidAnalytics {
2116
2187
  }
2117
2188
  return this.slotEmittedImpressionKeys.has(baseSlotKey);
2118
2189
  }
2119
- markImpressionEmitted(slotId, refreshIndex, auctionId, creativeId) {
2190
+ markImpressionEmitted(slotId, refreshIndex, auctionId, creativeId, adId) {
2120
2191
  const baseSlotKey = `${escapeKeyPart(slotId)}:${refreshIndex}`;
2121
- this.addImpressionKey(baseSlotKey);
2122
- if (creativeId) {
2123
- this.addImpressionKey(`${baseSlotKey}:${escapeKeyPart(creativeId)}`);
2124
- }
2125
- if (auctionId) {
2126
- this.addImpressionKey(`${baseSlotKey}:auc:${escapeKeyPart(auctionId)}`);
2127
- if (creativeId) {
2128
- this.addImpressionKey(
2129
- `${baseSlotKey}:auc:${escapeKeyPart(auctionId)}:${escapeKeyPart(creativeId)}`
2130
- );
2192
+ const hasIdentity = Boolean(adId || creativeId);
2193
+ if (hasIdentity) {
2194
+ if (adId) {
2195
+ this.addImpressionKey(`${baseSlotKey}:ad:${escapeKeyPart(adId)}`);
2196
+ if (auctionId) {
2197
+ this.addImpressionKey(
2198
+ `${baseSlotKey}:auc:${escapeKeyPart(auctionId)}:ad:${escapeKeyPart(adId)}`
2199
+ );
2200
+ }
2201
+ }
2202
+ if (creativeId && creativeId !== adId) {
2203
+ this.addImpressionKey(`${baseSlotKey}:${escapeKeyPart(creativeId)}`);
2204
+ if (auctionId) {
2205
+ this.addImpressionKey(
2206
+ `${baseSlotKey}:auc:${escapeKeyPart(auctionId)}:${escapeKeyPart(creativeId)}`
2207
+ );
2208
+ }
2209
+ }
2210
+ if (auctionId) {
2211
+ this.addImpressionKey(`${baseSlotKey}:auc:${escapeKeyPart(auctionId)}`);
2212
+ }
2213
+ } else {
2214
+ this.addImpressionKey(baseSlotKey);
2215
+ if (auctionId) {
2216
+ this.addImpressionKey(`${baseSlotKey}:auc:${escapeKeyPart(auctionId)}`);
2131
2217
  }
2132
2218
  }
2133
2219
  }
@@ -2140,6 +2226,7 @@ var BidkernelPrebidAnalytics = class _BidkernelPrebidAnalytics {
2140
2226
  const auctionId = options?.auctionId || rawBid.auctionId || cached?.auctionId || "";
2141
2227
  const transactionId = options?.transactionId || rawBid.transactionId || cached?.transactionId || "";
2142
2228
  const creativeId = rawBid.creativeId || cached?.bidTrace?.creativeId || options?.bid?.creativeId || "";
2229
+ const adId = options?.adId || rawBid.adId || options?.bid?.adId || "";
2143
2230
  const mediaType = options?.mediaType || rawBid.mediaType || cached?.bidTrace?.mediaType || "banner";
2144
2231
  const targetSlot = resolvedSlotId || adUnitCode;
2145
2232
  const rec = (resolvedSlotId ? this.slotViewabilityRecords.get(resolvedSlotId) : void 0) || (adUnitCode ? this.slotViewabilityRecords.get(adUnitCode) : void 0);
@@ -2180,7 +2267,13 @@ var BidkernelPrebidAnalytics = class _BidkernelPrebidAnalytics {
2180
2267
  }
2181
2268
  }
2182
2269
  const currentRefreshIndex = options?.refreshIndex ?? (resolvedSlotId ? this.slotRefreshIndices.get(resolvedSlotId) ?? (adUnitCode ? this.slotRefreshIndices.get(adUnitCode) : void 0) ?? (rec ? rec.refreshIndex : 0) : 0);
2183
- const alreadyEmitted = resolvedSlotId && this.hasImpressionEmitted(resolvedSlotId, currentRefreshIndex, auctionId, creativeId) || adUnitCode && adUnitCode !== resolvedSlotId && this.hasImpressionEmitted(adUnitCode, currentRefreshIndex, auctionId, creativeId);
2270
+ const alreadyEmitted = resolvedSlotId && this.hasImpressionEmitted(
2271
+ resolvedSlotId,
2272
+ currentRefreshIndex,
2273
+ auctionId,
2274
+ creativeId,
2275
+ adId
2276
+ ) || adUnitCode && adUnitCode !== resolvedSlotId && this.hasImpressionEmitted(adUnitCode, currentRefreshIndex, auctionId, creativeId, adId);
2184
2277
  if (alreadyEmitted) {
2185
2278
  this.log(
2186
2279
  "DEBUG",
@@ -2189,9 +2282,9 @@ var BidkernelPrebidAnalytics = class _BidkernelPrebidAnalytics {
2189
2282
  return false;
2190
2283
  }
2191
2284
  if (resolvedSlotId)
2192
- this.markImpressionEmitted(resolvedSlotId, currentRefreshIndex, auctionId, creativeId);
2285
+ this.markImpressionEmitted(resolvedSlotId, currentRefreshIndex, auctionId, creativeId, adId);
2193
2286
  if (adUnitCode && adUnitCode !== resolvedSlotId)
2194
- this.markImpressionEmitted(adUnitCode, currentRefreshIndex, auctionId, creativeId);
2287
+ this.markImpressionEmitted(adUnitCode, currentRefreshIndex, auctionId, creativeId, adId);
2195
2288
  this.enqueue(TraceEventType.IMPRESSION, "impression", {
2196
2289
  auctionId,
2197
2290
  transactionId,
@@ -2289,9 +2382,9 @@ var BidkernelPrebidAnalytics = class _BidkernelPrebidAnalytics {
2289
2382
  }
2290
2383
  }
2291
2384
  trackRawEvent(level, eventName, data) {
2292
- const eventType = EVENT_NAME_TO_TYPE[eventName] ?? (level === "ERROR" ? TraceEventType.ERROR : void 0);
2385
+ const eventType = LEVEL_TO_TYPE[level] ?? EVENT_NAME_TO_TYPE[eventName];
2293
2386
  if (eventType === void 0) return;
2294
- this.enqueue(eventType, eventName, data, level);
2387
+ this.enqueue(eventType, eventName, data);
2295
2388
  }
2296
2389
  /**
2297
2390
  * Record the wrapper's Live Floors decision for an auction and ad unit,
@@ -2330,6 +2423,7 @@ var BidkernelPrebidAnalytics = class _BidkernelPrebidAnalytics {
2330
2423
  navigate(pageviewId, pageUrl) {
2331
2424
  this.disable();
2332
2425
  this.slotRefreshIndices.clear();
2426
+ this.slotAdSessionCounters.clear();
2333
2427
  this.floorDecisions.clear();
2334
2428
  this.pendingThresholdListeners.clear();
2335
2429
  this.config.pageviewId = pageviewId || generateUUID();
@@ -2680,9 +2774,9 @@ var BidkernelPrebidAnalytics = class _BidkernelPrebidAnalytics {
2680
2774
  isDuplicate(eventName, data) {
2681
2775
  return this.deduper.isDuplicate(eventName, data);
2682
2776
  }
2683
- enqueue(type, eventName, data, level) {
2777
+ enqueue(type, eventName, data) {
2684
2778
  if (!this.isEnabled) return;
2685
- if (!this.shouldSample(type, level)) return;
2779
+ if (!this.shouldSample(type)) return;
2686
2780
  if (this.managesSessionId && sessionNeedsSync()) {
2687
2781
  this.config.sessionId = getOrCreateSessionId();
2688
2782
  }
@@ -2691,8 +2785,8 @@ var BidkernelPrebidAnalytics = class _BidkernelPrebidAnalytics {
2691
2785
  timestampMs: Date.now(),
2692
2786
  type,
2693
2787
  // Typed events have a fixed name that ingest derives from `type`; only
2694
- // ERROR events carry theirs, which names the failure.
2695
- eventName: type === TraceEventType.ERROR ? eventName : "",
2788
+ // ERROR and WARN carry theirs, which names the failure or condition.
2789
+ eventName: SELF_NAMING_TYPES.has(type) ? eventName : "",
2696
2790
  auctionId: idToBytes(String(data.auctionId || "")),
2697
2791
  transactionId: idToBytes(String(data.transactionId || "")),
2698
2792
  adUnitCode: data.adUnitCode || data.elementId || "",
@@ -2772,13 +2866,12 @@ var BidkernelPrebidAnalytics = class _BidkernelPrebidAnalytics {
2772
2866
  }, this.config.revenueFlushDelayMs);
2773
2867
  }
2774
2868
  }
2775
- shouldSample(type, level) {
2869
+ shouldSample(type) {
2776
2870
  if (type === TraceEventType.ERROR) {
2777
2871
  this.errorCount++;
2778
- if (this.errorCount > MAX_ERRORS_PER_SESSION) return false;
2779
- return this.config.errorsEnabled;
2872
+ return this.errorCount <= MAX_ERRORS_PER_SESSION;
2780
2873
  }
2781
- if (level === "WARN") {
2874
+ if (type === TraceEventType.WARN) {
2782
2875
  return this.config.warningsEnabled;
2783
2876
  }
2784
2877
  return this.config.auctionEnabled;
@@ -3033,10 +3126,6 @@ var BidkernelPrebidAnalytics = class _BidkernelPrebidAnalytics {
3033
3126
  this.logger.log(level, msg, ...args);
3034
3127
  }
3035
3128
  };
3036
- var defaultLogger = createLogger({
3037
- prefix: "[analytics]",
3038
- logLevel: "INFO"
3039
- });
3040
3129
  function extractAnalyticsOptions(config, allowProviderless = true) {
3041
3130
  if (!config) return null;
3042
3131
  if (Array.isArray(config)) {
@@ -3559,9 +3648,29 @@ function attachImaAdsManager(analytics, adsManager, options) {
3559
3648
  return cached;
3560
3649
  };
3561
3650
  let adStarted = false;
3651
+ let activeAdSession = null;
3652
+ const mintAdSession = (initialAd) => {
3653
+ const nextSession = typeof self.nextSlotAdSession === "function" ? self.nextSlotAdSession(slotId) : `ad-session-${(self.slotAdSessionCounters?.get(slotId) ?? 0) + 1}`;
3654
+ if (self.slotAdSessionCounters && typeof self.nextSlotAdSession !== "function") {
3655
+ const c = (self.slotAdSessionCounters.get(slotId) ?? 0) + 1;
3656
+ self.slotAdSessionCounters.set(slotId, c);
3657
+ }
3658
+ const adId = (typeof initialAd?.getAdId === "function" ? initialAd.getAdId() : initialAd?.adId ?? initialAd?.id) || "";
3659
+ const creativeId = (typeof initialAd?.getCreativeId === "function" ? initialAd.getCreativeId() : initialAd?.creativeId) || "";
3660
+ activeAdSession = {
3661
+ sessionId: nextSession,
3662
+ adId: adId || void 0,
3663
+ creativeId: creativeId || void 0
3664
+ };
3665
+ return activeAdSession;
3666
+ };
3667
+ const onAdLoaded = (event) => {
3668
+ const ad = typeof event?.getAd === "function" ? event.getAd() : event?.ad;
3669
+ mintAdSession(ad);
3670
+ self.log("DEBUG", "IMA AdEvent.LOADED received", { slotId, activeAdSession });
3671
+ };
3562
3672
  const onAdStartedOrImpression = (event) => {
3563
3673
  adStarted = true;
3564
- self.log("DEBUG", "IMA AdEvent.STARTED / IMPRESSION received", event);
3565
3674
  const ad = typeof event?.getAd === "function" ? event.getAd() : event?.ad;
3566
3675
  const adData = {};
3567
3676
  if (ad) {
@@ -3571,15 +3680,28 @@ function attachImaAdsManager(analytics, adsManager, options) {
3571
3680
  adData.duration = typeof ad.getDuration === "function" ? ad.getDuration() : ad.duration;
3572
3681
  adData.advertiserName = typeof ad.getAdvertiserName === "function" ? ad.getAdvertiserName() : "";
3573
3682
  }
3683
+ if (!activeAdSession || adData.adId && activeAdSession.adId && activeAdSession.adId !== adData.adId) {
3684
+ mintAdSession(ad);
3685
+ } else {
3686
+ if (!activeAdSession.adId && adData.adId) {
3687
+ activeAdSession.adId = adData.adId;
3688
+ }
3689
+ if (!activeAdSession.creativeId && adData.creativeId) {
3690
+ activeAdSession.creativeId = adData.creativeId;
3691
+ }
3692
+ }
3693
+ const session = activeAdSession || mintAdSession(ad);
3574
3694
  const cached = getWinningBid(adData);
3575
3695
  const resolvedSlotId = options?.slotId && options.slotId !== "video" ? options.slotId : options?.adUnitCode && options.adUnitCode !== "video" ? options.adUnitCode : cached?.adUnitCode || options?.slotId || options?.adUnitCode || "video";
3576
3696
  const resolvedAdUnitCode = options?.adUnitCode && options.adUnitCode !== "video" ? options.adUnitCode : cached?.adUnitCode || resolvedSlotId;
3577
3697
  const resolvedAuctionId = auctionId || cached?.auctionId || "";
3578
3698
  const resolvedTransactionId = transactionId || cached?.transactionId || "";
3699
+ const creativeId = session.creativeId || adData.creativeId || cached?.bidTrace?.creativeId || options?.bid?.creativeId || "";
3700
+ const stableAdIdentity = session.adId || adData.adId || creativeId || session.sessionId;
3579
3701
  const mergedBid = {
3580
3702
  ...cached?.rawBid || options?.bid,
3581
3703
  mediaType: "video",
3582
- creativeId: adData.creativeId || cached?.bidTrace?.creativeId || options?.bid?.creativeId || ""
3704
+ creativeId
3583
3705
  };
3584
3706
  const emitted = self.recordImpression(resolvedSlotId, {
3585
3707
  adUnitCode: resolvedAdUnitCode,
@@ -3587,6 +3709,7 @@ function attachImaAdsManager(analytics, adsManager, options) {
3587
3709
  transactionId: resolvedTransactionId,
3588
3710
  mediaType: "video",
3589
3711
  bid: mergedBid,
3712
+ adId: stableAdIdentity,
3590
3713
  metadata: {
3591
3714
  ...options?.metadata,
3592
3715
  media_type: "video",
@@ -3594,6 +3717,16 @@ function attachImaAdsManager(analytics, adsManager, options) {
3594
3717
  ...Number.isFinite(adData.duration) ? { ad_duration_s: String(adData.duration) } : {}
3595
3718
  }
3596
3719
  });
3720
+ let rawType = typeof event?.type === "string" ? event.type : typeof event?.getType === "function" ? event.getType() : typeof event === "string" ? event : "";
3721
+ if (rawType.toLowerCase().startsWith("adevent.")) {
3722
+ rawType = rawType.slice(8);
3723
+ }
3724
+ const eventName = rawType ? rawType.toUpperCase() : "STARTED/IMPRESSION";
3725
+ self.log(
3726
+ "DEBUG",
3727
+ `IMA AdEvent.${eventName} received${emitted ? "" : " (second event for this ad, already counted)"}`,
3728
+ event
3729
+ );
3597
3730
  if (emitted && options?.onImpression) {
3598
3731
  try {
3599
3732
  options.onImpression(resolvedSlotId, { ad, bid: mergedBid });
@@ -3628,6 +3761,7 @@ function attachImaAdsManager(analytics, adsManager, options) {
3628
3761
  });
3629
3762
  };
3630
3763
  const onAdError = (event) => {
3764
+ activeAdSession = null;
3631
3765
  const cached = getWinningBid();
3632
3766
  const resolvedAdUnitCode = options?.adUnitCode && options.adUnitCode !== "video" ? options.adUnitCode : cached?.adUnitCode || slotId;
3633
3767
  const err = recordImaAdError(analytics, event, {
@@ -3650,6 +3784,7 @@ function attachImaAdsManager(analytics, adsManager, options) {
3650
3784
  const adEventType = googleIma?.AdEvent?.Type || {};
3651
3785
  const adErrorEventType = googleIma?.AdErrorEvent?.Type || {};
3652
3786
  const rawListeners = [
3787
+ { type: adEventType.LOADED || "loaded", handler: onAdLoaded },
3653
3788
  { type: adEventType.STARTED || "started", handler: onAdStartedOrImpression },
3654
3789
  { type: adEventType.IMPRESSION || "impression", handler: onAdStartedOrImpression },
3655
3790
  {
@@ -3661,7 +3796,19 @@ function attachImaAdsManager(analytics, adsManager, options) {
3661
3796
  type: adEventType.THIRD_QUARTILE || "thirdQuartile",
3662
3797
  handler: () => onMilestone("thirdQuartile")
3663
3798
  },
3664
- { type: adEventType.COMPLETE || "complete", handler: () => onMilestone("complete") },
3799
+ {
3800
+ type: adEventType.COMPLETE || "complete",
3801
+ handler: () => {
3802
+ activeAdSession = null;
3803
+ onMilestone("complete");
3804
+ }
3805
+ },
3806
+ {
3807
+ type: adEventType.ALL_ADS_COMPLETED || "allAdsCompleted",
3808
+ handler: () => {
3809
+ activeAdSession = null;
3810
+ }
3811
+ },
3665
3812
  { type: adEventType.CLICK || "click", handler: onAdClick },
3666
3813
  { type: adErrorEventType.AD_ERROR || "adError", handler: onAdError }
3667
3814
  ];
@@ -3680,6 +3827,7 @@ function attachImaAdsManager(analytics, adsManager, options) {
3680
3827
  }
3681
3828
  }
3682
3829
  const cleanup = () => {
3830
+ activeAdSession = null;
3683
3831
  attached.delete(analytics);
3684
3832
  for (const { type, handler } of listeners) {
3685
3833
  try {
@@ -3946,6 +4094,8 @@ function normalizeImaEventName(name) {
3946
4094
  return "volumeChanged";
3947
4095
  case "loaded":
3948
4096
  return "loaded";
4097
+ case "alladscompleted":
4098
+ return "allAdsCompleted";
3949
4099
  case "aderror":
3950
4100
  case "error":
3951
4101
  return "adError";
@@ -4060,6 +4210,7 @@ export {
4060
4210
  BidkernelPrebidAnalytics,
4061
4211
  CACHED_BID_TTL_MS,
4062
4212
  ImaAdsManagerStub,
4213
+ LogLevel,
4063
4214
  PrebidEventDeduper,
4064
4215
  attachImaAdsManager,
4065
4216
  attachVideoPlayer,
@@ -4071,6 +4222,7 @@ export {
4071
4222
  isSessionActive,
4072
4223
  isTrustedEndpoint,
4073
4224
  normalizeImaEventName,
4225
+ normalizeLogLevel,
4074
4226
  onSessionResume,
4075
4227
  registerPrebidAnalytics
4076
4228
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@bidkernel/analytics",
3
- "version": "0.24.0",
3
+ "version": "0.27.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",