@bidkernel/analytics 0.12.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/index.mjs CHANGED
@@ -1052,11 +1052,13 @@ var CACHED_BID_TTL_MS = 30 * 60 * 1e3;
1052
1052
  var MAX_CACHED_BID_KEYS = 200;
1053
1053
  var MAX_IMPRESSION_KEYS = 1e3;
1054
1054
  var MAX_TRACKED_AUCTIONS = 50;
1055
+ var CLICK_DEDUP_MS = 1e3;
1055
1056
  var SESSION_KEY = "_bidkernel_session";
1056
1057
  var SESSION_TS_KEY = "_bidkernel_session_ts";
1057
1058
  var THIRTY_MINUTES_MS = 30 * 60 * 1e3;
1058
1059
  var inMemorySessionId = null;
1059
1060
  var inMemorySessionTs = 0;
1061
+ var storageAllowed = true;
1060
1062
  function generateUUID() {
1061
1063
  if (typeof crypto !== "undefined" && typeof crypto.randomUUID === "function") {
1062
1064
  return crypto.randomUUID();
@@ -1126,6 +1128,7 @@ function sameEndpointOrigin(a, b) {
1126
1128
  function extendSession() {
1127
1129
  const now = Date.now();
1128
1130
  inMemorySessionTs = now;
1131
+ if (!storageAllowed) return;
1129
1132
  try {
1130
1133
  if (typeof localStorage !== "undefined") {
1131
1134
  localStorage.setItem(SESSION_TS_KEY, now.toString());
@@ -1135,18 +1138,23 @@ function extendSession() {
1135
1138
  }
1136
1139
  function getOrCreateSessionId() {
1137
1140
  const now = Date.now();
1141
+ const memoryFresh = !!inMemorySessionId && !!inMemorySessionTs && now - inMemorySessionTs <= THIRTY_MINUTES_MS;
1138
1142
  try {
1139
- if (typeof localStorage !== "undefined") {
1143
+ if (storageAllowed && typeof localStorage !== "undefined") {
1140
1144
  const storedId = localStorage.getItem(SESSION_KEY);
1141
1145
  const tsStr = localStorage.getItem(SESSION_TS_KEY);
1142
1146
  const storedTs = tsStr ? parseInt(tsStr, 10) || 0 : 0;
1143
1147
  if (storedId && storedTs && now - storedTs <= THIRTY_MINUTES_MS) {
1144
1148
  localStorage.setItem(SESSION_TS_KEY, now.toString());
1149
+ inMemorySessionId = storedId;
1150
+ inMemorySessionTs = now;
1145
1151
  return storedId;
1146
1152
  }
1147
- const newId = generateUUID();
1153
+ const newId = memoryFresh ? inMemorySessionId : generateUUID();
1148
1154
  localStorage.setItem(SESSION_KEY, newId);
1149
1155
  localStorage.setItem(SESSION_TS_KEY, now.toString());
1156
+ inMemorySessionId = newId;
1157
+ inMemorySessionTs = now;
1150
1158
  return newId;
1151
1159
  }
1152
1160
  } catch {
@@ -1512,6 +1520,17 @@ var BidkernelPrebidAnalytics = class _BidkernelPrebidAnalytics {
1512
1520
  static initImaInterception() {
1513
1521
  initImaInterception();
1514
1522
  }
1523
+ /**
1524
+ * Allows or forbids localStorage writes for every instance on the page.
1525
+ * Collection and delivery are unaffected: only the persisted session id,
1526
+ * its timestamp and exit batches are gated.
1527
+ */
1528
+ static setStorageAllowed(allowed) {
1529
+ storageAllowed = allowed;
1530
+ }
1531
+ static isStorageAllowed() {
1532
+ return storageAllowed;
1533
+ }
1515
1534
  config;
1516
1535
  queue = [];
1517
1536
  errorCount = 0;
@@ -1540,6 +1559,12 @@ var BidkernelPrebidAnalytics = class _BidkernelPrebidAnalytics {
1540
1559
  slotRefreshIndices = /* @__PURE__ */ new Map();
1541
1560
  slotLastAuctionIds = /* @__PURE__ */ new Map();
1542
1561
  pendingThresholdListeners = /* @__PURE__ */ new Map();
1562
+ pendingViewableListeners = /* @__PURE__ */ new Map();
1563
+ // Element a subscriber named for a slot (a sticky bar or interstitial that
1564
+ // renders outside the slot element); wins over document.getElementById.
1565
+ slotElements = /* @__PURE__ */ new Map();
1566
+ clickDetachCleanups = /* @__PURE__ */ new Set();
1567
+ lastClickAt = /* @__PURE__ */ new Map();
1543
1568
  // Impression deduplication per slot and refresh cycle (strictly 1 impression per cycle)
1544
1569
  slotEmittedImpressionKeys = /* @__PURE__ */ new Set();
1545
1570
  // Winning bid cache from bidWon to marry with subsequent render triggers (adRenderSucceeded, video, etc.)
@@ -1550,6 +1575,9 @@ var BidkernelPrebidAnalytics = class _BidkernelPrebidAnalytics {
1550
1575
  auctionBidderOutcomes = /* @__PURE__ */ new Map();
1551
1576
  logger;
1552
1577
  constructor(config) {
1578
+ if (config.storageAllowed !== void 0) {
1579
+ _BidkernelPrebidAnalytics.setStorageAllowed(config.storageAllowed);
1580
+ }
1553
1581
  const requestedEndpoint = config.endpoint || "";
1554
1582
  const endpoint = !requestedEndpoint || isTrustedEndpoint(requestedEndpoint) ? requestedEndpoint : "";
1555
1583
  this.config = {
@@ -1565,6 +1593,7 @@ var BidkernelPrebidAnalytics = class _BidkernelPrebidAnalytics {
1565
1593
  warningsEnabled: config.warningsEnabled ?? true,
1566
1594
  errorsEnabled: config.errorsEnabled ?? true,
1567
1595
  viewabilityEnabled: config.viewabilityEnabled ?? true,
1596
+ storageAllowed: config.storageAllowed ?? true,
1568
1597
  logLevel: config.logLevel || "INFO",
1569
1598
  pbjsGlobalName: config.pbjsGlobalName || "pbjs",
1570
1599
  attachPbjsListeners: config.attachPbjsListeners ?? true,
@@ -1607,65 +1636,7 @@ var BidkernelPrebidAnalytics = class _BidkernelPrebidAnalytics {
1607
1636
  }
1608
1637
  }
1609
1638
  if (this.config.attachPbjsListeners) {
1610
- const win = typeof window !== "undefined" ? window : {};
1611
- const pbjs = win[this.config.pbjsGlobalName] || {};
1612
- if (typeof pbjs.onEvent === "function") {
1613
- this.log("DEBUG", "Attaching event listeners to pbjs");
1614
- const events = [
1615
- ["auctionInit", this.handleAuctionInit.bind(this)],
1616
- ["auctionEnd", this.handleAuctionEnd.bind(this)],
1617
- ["bidRequested", this.handleBidRequested.bind(this)],
1618
- ["bidResponse", this.handleBidResponse.bind(this)],
1619
- ["bidTimeout", this.handleBidTimeout.bind(this)],
1620
- ["bidWon", this.handleBidWon.bind(this)],
1621
- ["noBid", this.handleNoBid.bind(this)],
1622
- ["adRenderFailed", this.handleAdRenderFailed.bind(this)],
1623
- ["adRenderSucceeded", this.handleAdRenderSucceeded.bind(this)]
1624
- ];
1625
- for (const [name, fn] of events) {
1626
- pbjs.onEvent(name, fn);
1627
- this.boundPbjsHandlers.push({ event: name, handler: fn });
1628
- }
1629
- } else {
1630
- this.log("WARN", "pbjs.onEvent is not defined. Prebid analytics will not function.");
1631
- }
1632
- if (typeof pbjs.getEvents === "function") {
1633
- try {
1634
- const pastEvents = pbjs.getEvents();
1635
- if (Array.isArray(pastEvents)) {
1636
- const newPastEvents = pastEvents.slice(this.replayedEventCount);
1637
- this.replayedEventCount = pastEvents.length;
1638
- if (newPastEvents.length > 0) {
1639
- this.log(
1640
- "DEBUG",
1641
- `Replaying ${newPastEvents.length} historical events from pbjs.getEvents()`
1642
- );
1643
- const handlerMap = {
1644
- auctionInit: this.handleAuctionInit.bind(this),
1645
- auctionEnd: this.handleAuctionEnd.bind(this),
1646
- bidRequested: this.handleBidRequested.bind(this),
1647
- bidResponse: this.handleBidResponse.bind(this),
1648
- bidTimeout: this.handleBidTimeout.bind(this),
1649
- bidWon: this.handleBidWon.bind(this),
1650
- noBid: this.handleNoBid.bind(this),
1651
- adRenderFailed: this.handleAdRenderFailed.bind(this),
1652
- adRenderSucceeded: this.handleAdRenderSucceeded.bind(this)
1653
- };
1654
- for (const ev of newPastEvents) {
1655
- if (!ev) continue;
1656
- const eventType = ev.eventType || ev.event || ev.name;
1657
- const args = ev.args !== void 0 ? ev.args : ev.data !== void 0 ? ev.data : ev;
1658
- const handler = handlerMap[eventType];
1659
- if (handler) {
1660
- handler(args);
1661
- }
1662
- }
1663
- }
1664
- }
1665
- } catch (e) {
1666
- this.log("WARN", "Failed to replay historical events from pbjs.getEvents()", e);
1667
- }
1668
- }
1639
+ this.attachPbjs();
1669
1640
  }
1670
1641
  if (typeof window !== "undefined") {
1671
1642
  if (!this.pageUrl) {
@@ -1688,6 +1659,68 @@ var BidkernelPrebidAnalytics = class _BidkernelPrebidAnalytics {
1688
1659
  }
1689
1660
  }
1690
1661
  }
1662
+ /**
1663
+ * Binds the Prebid event handlers on the configured global and replays
1664
+ * pbjs.getEvents() history. enable() calls this when attachPbjsListeners is
1665
+ * true. An integration that loads Prebid lazily constructs the instance with
1666
+ * attachPbjsListeners=false and calls this once pbjs exists; disable() (and
1667
+ * therefore navigate()) drops the handlers, so call it again after either.
1668
+ * Idempotent. Returns true when handlers are bound after the call, false
1669
+ * when the instance is disabled or pbjs.onEvent is not available yet.
1670
+ */
1671
+ attachPbjs() {
1672
+ if (!this.isEnabled) return false;
1673
+ if (this.boundPbjsHandlers.length > 0) return true;
1674
+ const win = typeof window !== "undefined" ? window : {};
1675
+ const pbjs = win[this.config.pbjsGlobalName] || {};
1676
+ if (typeof pbjs.onEvent !== "function") {
1677
+ this.log("WARN", "pbjs.onEvent is not defined. Prebid analytics will not function.");
1678
+ return false;
1679
+ }
1680
+ this.log("DEBUG", "Attaching event listeners to pbjs");
1681
+ const handlerMap = {
1682
+ auctionInit: this.handleAuctionInit.bind(this),
1683
+ auctionEnd: this.handleAuctionEnd.bind(this),
1684
+ bidRequested: this.handleBidRequested.bind(this),
1685
+ bidResponse: this.handleBidResponse.bind(this),
1686
+ bidTimeout: this.handleBidTimeout.bind(this),
1687
+ bidWon: this.handleBidWon.bind(this),
1688
+ noBid: this.handleNoBid.bind(this),
1689
+ adRenderFailed: this.handleAdRenderFailed.bind(this),
1690
+ adRenderSucceeded: this.handleAdRenderSucceeded.bind(this)
1691
+ };
1692
+ for (const [name, fn] of Object.entries(handlerMap)) {
1693
+ pbjs.onEvent(name, fn);
1694
+ this.boundPbjsHandlers.push({ event: name, handler: fn });
1695
+ }
1696
+ if (typeof pbjs.getEvents === "function") {
1697
+ try {
1698
+ const pastEvents = pbjs.getEvents();
1699
+ if (Array.isArray(pastEvents)) {
1700
+ const newPastEvents = pastEvents.slice(this.replayedEventCount);
1701
+ this.replayedEventCount = pastEvents.length;
1702
+ if (newPastEvents.length > 0) {
1703
+ this.log(
1704
+ "DEBUG",
1705
+ `Replaying ${newPastEvents.length} historical events from pbjs.getEvents()`
1706
+ );
1707
+ for (const ev of newPastEvents) {
1708
+ if (!ev) continue;
1709
+ const eventType = ev.eventType || ev.event || ev.name;
1710
+ const args = ev.args !== void 0 ? ev.args : ev.data !== void 0 ? ev.data : ev;
1711
+ const handler = handlerMap[eventType];
1712
+ if (handler) {
1713
+ handler(args);
1714
+ }
1715
+ }
1716
+ }
1717
+ }
1718
+ } catch (e) {
1719
+ this.log("WARN", "Failed to replay historical events from pbjs.getEvents()", e);
1720
+ }
1721
+ }
1722
+ return true;
1723
+ }
1691
1724
  disable() {
1692
1725
  if (!this.isEnabled) return;
1693
1726
  this.flushAllSlotsTimeInView();
@@ -1719,6 +1752,14 @@ var BidkernelPrebidAnalytics = class _BidkernelPrebidAnalytics {
1719
1752
  }
1720
1753
  }
1721
1754
  this.videoDetachCleanups.clear();
1755
+ for (const cleanup of Array.from(this.clickDetachCleanups)) {
1756
+ try {
1757
+ cleanup();
1758
+ } catch {
1759
+ }
1760
+ }
1761
+ this.clickDetachCleanups.clear();
1762
+ this.slotElements.clear();
1722
1763
  this.cachedWinningBids.clear();
1723
1764
  this.slotEmittedImpressionKeys.clear();
1724
1765
  this.slotLastAuctionIds.clear();
@@ -1880,6 +1921,105 @@ var BidkernelPrebidAnalytics = class _BidkernelPrebidAnalytics {
1880
1921
  }
1881
1922
  });
1882
1923
  }
1924
+ this.notifyViewable(record);
1925
+ }
1926
+ notifyViewable(record) {
1927
+ if (!record.viewableFired) return;
1928
+ for (const listener of record.viewableListeners) {
1929
+ if (listener.firedForIndex === record.refreshIndex) continue;
1930
+ listener.firedForIndex = record.refreshIndex;
1931
+ try {
1932
+ listener.callback(record.slotId);
1933
+ } catch (e) {
1934
+ this.log("ERROR", "Error in viewable listener callback", e);
1935
+ }
1936
+ }
1937
+ }
1938
+ /**
1939
+ * Calls back once per render cycle when the slot has met the IAB viewable
1940
+ * standard this adapter measures (immediately if it already has). Works
1941
+ * before the slot is observed: the listener waits for observeSlot. Returns
1942
+ * an unsubscribe function.
1943
+ */
1944
+ /**
1945
+ * Names the element to measure for a slot when the creative renders
1946
+ * somewhere other than the slot element (a sticky bar, an interstitial).
1947
+ * Applies to the current cycle at once and to every later render.
1948
+ */
1949
+ setSlotElement(slotId, element) {
1950
+ this.slotElements.set(slotId, element);
1951
+ const record = this.slotViewabilityRecords.get(slotId);
1952
+ if (record && record.element !== element) {
1953
+ this.observeSlot(element, slotId, {
1954
+ refreshIndex: record.refreshIndex,
1955
+ emitRefreshEvent: false
1956
+ });
1957
+ }
1958
+ }
1959
+ /**
1960
+ * Records a click on the slot's current creative, attributed to the cached
1961
+ * winning bid. Repeats within one second are one click. Returns whether a
1962
+ * CLICK was recorded.
1963
+ */
1964
+ recordClick(slotId, options) {
1965
+ if (!this.isEnabled || !slotId) return false;
1966
+ const now = Date.now();
1967
+ if (now - (this.lastClickAt.get(slotId) || 0) < CLICK_DEDUP_MS) return false;
1968
+ this.lastClickAt.set(slotId, now);
1969
+ const cached = this.getCachedBid(slotId);
1970
+ this.enqueue(TraceEventType.CLICK, "click", {
1971
+ auctionId: cached?.auctionId || "",
1972
+ transactionId: cached?.transactionId || "",
1973
+ adUnitCode: slotId,
1974
+ bid: cached?.bidTrace,
1975
+ metadata: {
1976
+ ...options?.metadata,
1977
+ refresh_index: String(this.getRefreshIndex(slotId))
1978
+ }
1979
+ });
1980
+ return true;
1981
+ }
1982
+ /**
1983
+ * Detects clicks into a cross-origin creative frame: a click inside an
1984
+ * iframe moves focus into it and blurs the window. Returns a detach
1985
+ * function; every tracker is detached on disable().
1986
+ */
1987
+ attachClickTracker(element, slotId) {
1988
+ if (typeof window === "undefined") return () => {
1989
+ };
1990
+ const onBlur = () => {
1991
+ setTimeout(() => {
1992
+ const active = document.activeElement;
1993
+ if (active && (active === element || element.contains(active))) {
1994
+ this.recordClick(slotId, { metadata: { source: "blur" } });
1995
+ window.focus();
1996
+ }
1997
+ }, 0);
1998
+ };
1999
+ window.addEventListener("blur", onBlur);
2000
+ const cleanup = () => {
2001
+ window.removeEventListener("blur", onBlur);
2002
+ this.clickDetachCleanups.delete(cleanup);
2003
+ };
2004
+ this.clickDetachCleanups.add(cleanup);
2005
+ return cleanup;
2006
+ }
2007
+ onViewable(slotId, callback) {
2008
+ const listener = { callback, firedForIndex: -1 };
2009
+ const record = this.slotViewabilityRecords.get(slotId);
2010
+ if (record) {
2011
+ record.viewableListeners.add(listener);
2012
+ this.notifyViewable(record);
2013
+ } else {
2014
+ if (!this.pendingViewableListeners.has(slotId)) {
2015
+ this.pendingViewableListeners.set(slotId, /* @__PURE__ */ new Set());
2016
+ }
2017
+ this.pendingViewableListeners.get(slotId).add(listener);
2018
+ }
2019
+ return () => {
2020
+ this.slotViewabilityRecords.get(slotId)?.viewableListeners.delete(listener);
2021
+ this.pendingViewableListeners.get(slotId)?.delete(listener);
2022
+ };
1883
2023
  }
1884
2024
  scheduleThresholdTimers(record) {
1885
2025
  if (!record.inView || typeof document !== "undefined" && document.visibilityState === "hidden") {
@@ -2069,6 +2209,8 @@ var BidkernelPrebidAnalytics = class _BidkernelPrebidAnalytics {
2069
2209
  }
2070
2210
  const listeners = this.pendingThresholdListeners.get(resolvedSlotId) || /* @__PURE__ */ new Set();
2071
2211
  this.pendingThresholdListeners.delete(resolvedSlotId);
2212
+ const viewableListeners = this.pendingViewableListeners.get(resolvedSlotId) || /* @__PURE__ */ new Set();
2213
+ this.pendingViewableListeners.delete(resolvedSlotId);
2072
2214
  const record = {
2073
2215
  slotId: resolvedSlotId,
2074
2216
  element: el,
@@ -2085,7 +2227,8 @@ var BidkernelPrebidAnalytics = class _BidkernelPrebidAnalytics {
2085
2227
  viewableFired: false,
2086
2228
  dwellTimer: null,
2087
2229
  dwellStartedAt: null,
2088
- thresholdListeners: listeners
2230
+ thresholdListeners: listeners,
2231
+ viewableListeners
2089
2232
  };
2090
2233
  this.slotViewabilityRecords.set(resolvedSlotId, record);
2091
2234
  if (el) {
@@ -2110,6 +2253,7 @@ var BidkernelPrebidAnalytics = class _BidkernelPrebidAnalytics {
2110
2253
  }
2111
2254
  }
2112
2255
  record.thresholdListeners = /* @__PURE__ */ new Set();
2256
+ record.viewableListeners = /* @__PURE__ */ new Set();
2113
2257
  if (record.element && this.intersectionObserver) {
2114
2258
  this.intersectionObserver.unobserve(record.element);
2115
2259
  this.elementToSlotId.delete(record.element);
@@ -2124,6 +2268,7 @@ var BidkernelPrebidAnalytics = class _BidkernelPrebidAnalytics {
2124
2268
  this.slotRefreshIndices.delete(slotId);
2125
2269
  this.slotLastAuctionIds.delete(slotId);
2126
2270
  this.pendingThresholdListeners.delete(slotId);
2271
+ this.pendingViewableListeners.delete(slotId);
2127
2272
  const prefix = `${escapeKeyPart(slotId)}:`;
2128
2273
  for (const key of Array.from(this.slotEmittedImpressionKeys)) {
2129
2274
  if (key === slotId || key.startsWith(prefix)) {
@@ -2398,19 +2543,21 @@ var BidkernelPrebidAnalytics = class _BidkernelPrebidAnalytics {
2398
2543
  if (!adsManager.__bidkernelAttachedInstances) {
2399
2544
  try {
2400
2545
  Object.defineProperty(adsManager, "__bidkernelAttachedInstances", {
2401
- value: /* @__PURE__ */ new Set(),
2546
+ value: /* @__PURE__ */ new Map(),
2402
2547
  configurable: true,
2403
2548
  writable: true
2404
2549
  });
2405
2550
  } catch {
2406
- adsManager.__bidkernelAttachedInstances = /* @__PURE__ */ new Set();
2551
+ adsManager.__bidkernelAttachedInstances = /* @__PURE__ */ new Map();
2407
2552
  }
2408
2553
  }
2409
- if (adsManager.__bidkernelAttachedInstances.has(this)) {
2410
- return () => {
2554
+ const attached = adsManager.__bidkernelAttachedInstances;
2555
+ const previous = attached.get(this);
2556
+ if (previous) {
2557
+ if (!options) return () => {
2411
2558
  };
2559
+ previous();
2412
2560
  }
2413
- adsManager.__bidkernelAttachedInstances.add(this);
2414
2561
  const slotId = options?.slotId || options?.adUnitCode || "video";
2415
2562
  const adUnitCode = options?.adUnitCode || slotId;
2416
2563
  const auctionId = options?.auctionId || "";
@@ -2558,7 +2705,7 @@ var BidkernelPrebidAnalytics = class _BidkernelPrebidAnalytics {
2558
2705
  }
2559
2706
  }
2560
2707
  const cleanup = () => {
2561
- adsManager.__bidkernelAttachedInstances?.delete(this);
2708
+ attached.delete(this);
2562
2709
  for (const { type, handler } of listeners) {
2563
2710
  try {
2564
2711
  adsManager.removeEventListener(type, handler);
@@ -2567,6 +2714,7 @@ var BidkernelPrebidAnalytics = class _BidkernelPrebidAnalytics {
2567
2714
  }
2568
2715
  this.videoDetachCleanups.delete(cleanup);
2569
2716
  };
2717
+ attached.set(this, cleanup);
2570
2718
  this.videoDetachCleanups.add(cleanup);
2571
2719
  return cleanup;
2572
2720
  }
@@ -3194,9 +3342,9 @@ var BidkernelPrebidAnalytics = class _BidkernelPrebidAnalytics {
3194
3342
  );
3195
3343
  }
3196
3344
  if (this.config.viewabilityEnabled && typeof document !== "undefined") {
3197
- let el = null;
3345
+ let el = adUnitCode && this.slotElements.get(adUnitCode) || null;
3198
3346
  const targetId = adUnitCode || data.adId || bid.adId;
3199
- if (targetId) {
3347
+ if (!el && targetId) {
3200
3348
  el = document.getElementById(targetId);
3201
3349
  }
3202
3350
  if (!el && typeof window !== "undefined" && window.googletag?.pubads) {
@@ -3457,6 +3605,7 @@ var BidkernelPrebidAnalytics = class _BidkernelPrebidAnalytics {
3457
3605
  return `${PENDING_BATCH_KEY_PREFIX}${encodeURIComponent(this.config.propertyId)}_`;
3458
3606
  }
3459
3607
  persistBatch(encoded) {
3608
+ if (!storageAllowed) return null;
3460
3609
  try {
3461
3610
  if (typeof localStorage === "undefined") return null;
3462
3611
  if (!this.config.propertyId) return null;
@@ -3708,6 +3857,7 @@ function getbidkernel(alias = "bidkernel") {
3708
3857
  export {
3709
3858
  BidkernelPrebidAnalytics,
3710
3859
  PrebidEventDeduper,
3860
+ getOrCreateSessionId,
3711
3861
  getPrebidEventKey,
3712
3862
  getbidkernel,
3713
3863
  hookImaPrototype,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@bidkernel/analytics",
3
- "version": "0.12.0",
3
+ "version": "0.16.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",