@bidkernel/analytics 0.7.0 → 0.9.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/README.md +13 -13
- package/dist/analytics.global.js +1 -1
- package/dist/index.d.mts +42 -1
- package/dist/index.d.ts +42 -1
- package/dist/index.js +481 -163
- package/dist/index.mjs +480 -163
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -24,6 +24,7 @@ __export(src_exports, {
|
|
|
24
24
|
PrebidEventDeduper: () => PrebidEventDeduper,
|
|
25
25
|
getPrebidEventKey: () => getPrebidEventKey,
|
|
26
26
|
getbidkernel: () => getbidkernel,
|
|
27
|
+
isTrustedEndpoint: () => isTrustedEndpoint,
|
|
27
28
|
registerPrebidAnalytics: () => registerPrebidAnalytics
|
|
28
29
|
});
|
|
29
30
|
module.exports = __toCommonJS(src_exports);
|
|
@@ -34,26 +35,33 @@ var TraceEventType = {
|
|
|
34
35
|
TRACE_EVENT_TYPE_UNSPECIFIED: 0,
|
|
35
36
|
AUCTION_START: 1,
|
|
36
37
|
AUCTION_END: 2,
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
BID_WIN: 5,
|
|
40
|
-
BID_TIMEOUT: 6,
|
|
38
|
+
BID_RESPONSE: 3,
|
|
39
|
+
BID_WIN: 4,
|
|
41
40
|
/** IMPRESSION - ad rendered into DOM */
|
|
42
|
-
IMPRESSION:
|
|
41
|
+
IMPRESSION: 5,
|
|
43
42
|
/** VIEWABLE - IAB viewability threshold met (boolean, once per render cycle) */
|
|
44
|
-
VIEWABLE:
|
|
43
|
+
VIEWABLE: 6,
|
|
45
44
|
/** TIME_IN_VIEW - accumulated in-viewport duration (once per render cycle end) */
|
|
46
|
-
TIME_IN_VIEW:
|
|
47
|
-
REFRESH:
|
|
48
|
-
ERROR:
|
|
49
|
-
SLOT_DEFINED:
|
|
50
|
-
SLOT_DESTROYED:
|
|
45
|
+
TIME_IN_VIEW: 7,
|
|
46
|
+
REFRESH: 8,
|
|
47
|
+
ERROR: 9,
|
|
48
|
+
SLOT_DEFINED: 10,
|
|
49
|
+
SLOT_DESTROYED: 11,
|
|
51
50
|
/** CLICK - ad click-through */
|
|
52
|
-
CLICK:
|
|
53
|
-
/** NO_BID - bidder responded with no bid */
|
|
54
|
-
NO_BID: 15,
|
|
51
|
+
CLICK: 12,
|
|
55
52
|
/** AD_RENDER_FAILED - Prebid failed to render the winning ad */
|
|
56
|
-
AD_RENDER_FAILED:
|
|
53
|
+
AD_RENDER_FAILED: 13,
|
|
54
|
+
UNRECOGNIZED: -1
|
|
55
|
+
};
|
|
56
|
+
var BidderOutcome = {
|
|
57
|
+
BIDDER_OUTCOME_UNSPECIFIED: 0,
|
|
58
|
+
/** BIDDER_OUTCOME_BID - returned a bid (CPM > 0) */
|
|
59
|
+
BIDDER_OUTCOME_BID: 1,
|
|
60
|
+
BIDDER_OUTCOME_NO_BID: 2,
|
|
61
|
+
BIDDER_OUTCOME_TIMEOUT: 3,
|
|
62
|
+
BIDDER_OUTCOME_ERROR: 4,
|
|
63
|
+
/** BIDDER_OUTCOME_NO_RESPONSE - requested, nothing came back by auction end */
|
|
64
|
+
BIDDER_OUTCOME_NO_RESPONSE: 5,
|
|
57
65
|
UNRECOGNIZED: -1
|
|
58
66
|
};
|
|
59
67
|
function createBaseTraceEventBatch() {
|
|
@@ -284,7 +292,8 @@ function createBaseTraceEvent() {
|
|
|
284
292
|
namespace: "",
|
|
285
293
|
eventId: "",
|
|
286
294
|
metadata: {},
|
|
287
|
-
viewableDurationMs: void 0
|
|
295
|
+
viewableDurationMs: void 0,
|
|
296
|
+
bidderOutcomes: []
|
|
288
297
|
};
|
|
289
298
|
}
|
|
290
299
|
var TraceEvent = {
|
|
@@ -322,6 +331,11 @@ var TraceEvent = {
|
|
|
322
331
|
if (message.viewableDurationMs !== void 0) {
|
|
323
332
|
writer.uint32(89).double(message.viewableDurationMs);
|
|
324
333
|
}
|
|
334
|
+
if (message.bidderOutcomes !== void 0 && message.bidderOutcomes.length !== 0) {
|
|
335
|
+
for (const v of message.bidderOutcomes) {
|
|
336
|
+
BidderOutcomeEntry.encode(v, writer.uint32(98).fork()).join();
|
|
337
|
+
}
|
|
338
|
+
}
|
|
325
339
|
return writer;
|
|
326
340
|
},
|
|
327
341
|
decode(input, length) {
|
|
@@ -411,6 +425,16 @@ var TraceEvent = {
|
|
|
411
425
|
message.viewableDurationMs = reader.double();
|
|
412
426
|
continue;
|
|
413
427
|
}
|
|
428
|
+
case 12: {
|
|
429
|
+
if (tag !== 98) {
|
|
430
|
+
break;
|
|
431
|
+
}
|
|
432
|
+
const el = BidderOutcomeEntry.decode(reader, reader.uint32());
|
|
433
|
+
if (el !== void 0) {
|
|
434
|
+
message.bidderOutcomes.push(el);
|
|
435
|
+
}
|
|
436
|
+
continue;
|
|
437
|
+
}
|
|
414
438
|
}
|
|
415
439
|
if ((tag & 7) === 4 || tag === 0) {
|
|
416
440
|
break;
|
|
@@ -443,6 +467,7 @@ var TraceEvent = {
|
|
|
443
467
|
{}
|
|
444
468
|
);
|
|
445
469
|
message.viewableDurationMs = object.viewableDurationMs ?? void 0;
|
|
470
|
+
message.bidderOutcomes = object.bidderOutcomes?.map((e) => BidderOutcomeEntry.fromPartial(e)) || [];
|
|
446
471
|
return message;
|
|
447
472
|
}
|
|
448
473
|
};
|
|
@@ -498,6 +523,91 @@ var TraceEvent_MetadataEntry = {
|
|
|
498
523
|
return message;
|
|
499
524
|
}
|
|
500
525
|
};
|
|
526
|
+
function createBaseBidderOutcomeEntry() {
|
|
527
|
+
return { bidder: "", adUnitCode: "", mediaType: "", outcome: 0, latencyMs: 0 };
|
|
528
|
+
}
|
|
529
|
+
var BidderOutcomeEntry = {
|
|
530
|
+
encode(message, writer = new import_wire.BinaryWriter()) {
|
|
531
|
+
if (message.bidder !== void 0 && message.bidder !== "") {
|
|
532
|
+
writer.uint32(10).string(message.bidder);
|
|
533
|
+
}
|
|
534
|
+
if (message.adUnitCode !== void 0 && message.adUnitCode !== "") {
|
|
535
|
+
writer.uint32(18).string(message.adUnitCode);
|
|
536
|
+
}
|
|
537
|
+
if (message.mediaType !== void 0 && message.mediaType !== "") {
|
|
538
|
+
writer.uint32(26).string(message.mediaType);
|
|
539
|
+
}
|
|
540
|
+
if (message.outcome !== void 0 && message.outcome !== 0) {
|
|
541
|
+
writer.uint32(32).int32(message.outcome);
|
|
542
|
+
}
|
|
543
|
+
if (message.latencyMs !== void 0 && message.latencyMs !== 0) {
|
|
544
|
+
writer.uint32(41).double(message.latencyMs);
|
|
545
|
+
}
|
|
546
|
+
return writer;
|
|
547
|
+
},
|
|
548
|
+
decode(input, length) {
|
|
549
|
+
const reader = input instanceof import_wire.BinaryReader ? input : new import_wire.BinaryReader(input);
|
|
550
|
+
const end = length === void 0 ? reader.len : reader.pos + length;
|
|
551
|
+
const message = createBaseBidderOutcomeEntry();
|
|
552
|
+
while (reader.pos < end) {
|
|
553
|
+
const tag = reader.uint32();
|
|
554
|
+
switch (tag >>> 3) {
|
|
555
|
+
case 1: {
|
|
556
|
+
if (tag !== 10) {
|
|
557
|
+
break;
|
|
558
|
+
}
|
|
559
|
+
message.bidder = reader.string();
|
|
560
|
+
continue;
|
|
561
|
+
}
|
|
562
|
+
case 2: {
|
|
563
|
+
if (tag !== 18) {
|
|
564
|
+
break;
|
|
565
|
+
}
|
|
566
|
+
message.adUnitCode = reader.string();
|
|
567
|
+
continue;
|
|
568
|
+
}
|
|
569
|
+
case 3: {
|
|
570
|
+
if (tag !== 26) {
|
|
571
|
+
break;
|
|
572
|
+
}
|
|
573
|
+
message.mediaType = reader.string();
|
|
574
|
+
continue;
|
|
575
|
+
}
|
|
576
|
+
case 4: {
|
|
577
|
+
if (tag !== 32) {
|
|
578
|
+
break;
|
|
579
|
+
}
|
|
580
|
+
message.outcome = reader.int32();
|
|
581
|
+
continue;
|
|
582
|
+
}
|
|
583
|
+
case 5: {
|
|
584
|
+
if (tag !== 41) {
|
|
585
|
+
break;
|
|
586
|
+
}
|
|
587
|
+
message.latencyMs = reader.double();
|
|
588
|
+
continue;
|
|
589
|
+
}
|
|
590
|
+
}
|
|
591
|
+
if ((tag & 7) === 4 || tag === 0) {
|
|
592
|
+
break;
|
|
593
|
+
}
|
|
594
|
+
reader.skip(tag & 7);
|
|
595
|
+
}
|
|
596
|
+
return message;
|
|
597
|
+
},
|
|
598
|
+
create(base) {
|
|
599
|
+
return BidderOutcomeEntry.fromPartial(base ?? {});
|
|
600
|
+
},
|
|
601
|
+
fromPartial(object) {
|
|
602
|
+
const message = createBaseBidderOutcomeEntry();
|
|
603
|
+
message.bidder = object.bidder ?? "";
|
|
604
|
+
message.adUnitCode = object.adUnitCode ?? "";
|
|
605
|
+
message.mediaType = object.mediaType ?? "";
|
|
606
|
+
message.outcome = object.outcome ?? 0;
|
|
607
|
+
message.latencyMs = object.latencyMs ?? 0;
|
|
608
|
+
return message;
|
|
609
|
+
}
|
|
610
|
+
};
|
|
501
611
|
function createBaseBidTrace() {
|
|
502
612
|
return {
|
|
503
613
|
bidder: "",
|
|
@@ -658,6 +768,8 @@ var MAX_QUEUE_SIZE = 200;
|
|
|
658
768
|
var MAX_SEND_BACKOFF_MS = 5 * 60 * 1e3;
|
|
659
769
|
var MAX_CONSECUTIVE_SEND_FAILURES = 10;
|
|
660
770
|
var MAX_PAYLOAD_BYTES = 32 * 1024;
|
|
771
|
+
var MAX_METADATA_VALUE_CHARS = 1024;
|
|
772
|
+
var MAX_METADATA_ENTRIES = 16;
|
|
661
773
|
var SAFE_CONTENT_TYPE = "text/plain";
|
|
662
774
|
var PENDING_BATCH_KEY_PREFIX = "_bidkernel_pending_";
|
|
663
775
|
var MAX_PENDING_BATCHES = 20;
|
|
@@ -666,11 +778,8 @@ var PENDING_BATCH_CLEANUP_DELAY_MS = 1e4;
|
|
|
666
778
|
var EVENT_NAME_TO_TYPE = {
|
|
667
779
|
auctionStart: TraceEventType.AUCTION_START,
|
|
668
780
|
auctionEnd: TraceEventType.AUCTION_END,
|
|
669
|
-
bidRequest: TraceEventType.BID_REQUEST,
|
|
670
781
|
bidResponse: TraceEventType.BID_RESPONSE,
|
|
671
|
-
bidTimeout: TraceEventType.BID_TIMEOUT,
|
|
672
782
|
bidWin: TraceEventType.BID_WIN,
|
|
673
|
-
noBid: TraceEventType.NO_BID,
|
|
674
783
|
adRenderFailed: TraceEventType.AD_RENDER_FAILED,
|
|
675
784
|
click: TraceEventType.CLICK,
|
|
676
785
|
impression: TraceEventType.IMPRESSION,
|
|
@@ -683,6 +792,10 @@ var IMMEDIATE_FLUSH_TYPES = /* @__PURE__ */ new Set([
|
|
|
683
792
|
TraceEventType.BID_WIN,
|
|
684
793
|
TraceEventType.CLICK
|
|
685
794
|
]);
|
|
795
|
+
var CACHED_BID_TTL_MS = 30 * 60 * 1e3;
|
|
796
|
+
var MAX_CACHED_BID_KEYS = 200;
|
|
797
|
+
var MAX_IMPRESSION_KEYS = 1e3;
|
|
798
|
+
var MAX_TRACKED_AUCTIONS = 50;
|
|
686
799
|
var SESSION_KEY = "_bidkernel_session";
|
|
687
800
|
var SESSION_TS_KEY = "_bidkernel_session_ts";
|
|
688
801
|
var THIRTY_MINUTES_MS = 30 * 60 * 1e3;
|
|
@@ -714,6 +827,46 @@ function base64ToBytes(b64) {
|
|
|
714
827
|
}
|
|
715
828
|
return bytes;
|
|
716
829
|
}
|
|
830
|
+
function isTrustedEndpoint(raw) {
|
|
831
|
+
if (!raw) return false;
|
|
832
|
+
let url;
|
|
833
|
+
try {
|
|
834
|
+
url = new URL(raw);
|
|
835
|
+
} catch {
|
|
836
|
+
return false;
|
|
837
|
+
}
|
|
838
|
+
if (url.username || url.password) return false;
|
|
839
|
+
if (url.protocol === "https:") return true;
|
|
840
|
+
if (url.protocol === "http:") {
|
|
841
|
+
return url.hostname === "localhost" || url.hostname === "127.0.0.1" || url.hostname === "[::1]";
|
|
842
|
+
}
|
|
843
|
+
return false;
|
|
844
|
+
}
|
|
845
|
+
function boundMetadata(metadata) {
|
|
846
|
+
const bounded = {};
|
|
847
|
+
let count = 0;
|
|
848
|
+
for (const key of Object.keys(metadata)) {
|
|
849
|
+
if (count >= MAX_METADATA_ENTRIES) break;
|
|
850
|
+
const value = metadata[key];
|
|
851
|
+
if (value === void 0 || value === null) continue;
|
|
852
|
+
const str = String(value);
|
|
853
|
+
bounded[key] = str.length > MAX_METADATA_VALUE_CHARS ? `${str.slice(0, MAX_METADATA_VALUE_CHARS)}...[truncated]` : str;
|
|
854
|
+
count++;
|
|
855
|
+
}
|
|
856
|
+
return bounded;
|
|
857
|
+
}
|
|
858
|
+
function isLegacyPendingKey(key) {
|
|
859
|
+
const parts = key.slice(PENDING_BATCH_KEY_PREFIX.length).split("_");
|
|
860
|
+
return parts.length === 2 && /^\d+$/.test(parts[0]);
|
|
861
|
+
}
|
|
862
|
+
function sameEndpointOrigin(a, b) {
|
|
863
|
+
if (!a || !b) return false;
|
|
864
|
+
try {
|
|
865
|
+
return new URL(a).origin === new URL(b).origin;
|
|
866
|
+
} catch {
|
|
867
|
+
return false;
|
|
868
|
+
}
|
|
869
|
+
}
|
|
717
870
|
function extendSession() {
|
|
718
871
|
const now = Date.now();
|
|
719
872
|
inMemorySessionTs = now;
|
|
@@ -830,22 +983,6 @@ var PrebidEventDeduper = class {
|
|
|
830
983
|
return false;
|
|
831
984
|
}
|
|
832
985
|
};
|
|
833
|
-
function parseSize(raw) {
|
|
834
|
-
if (!raw) return { width: 0, height: 0 };
|
|
835
|
-
let cur = raw;
|
|
836
|
-
while (Array.isArray(cur) && cur.length > 0 && Array.isArray(cur[0])) {
|
|
837
|
-
cur = cur[0];
|
|
838
|
-
}
|
|
839
|
-
if (Array.isArray(cur) && cur.length >= 2) {
|
|
840
|
-
const w = Number(cur[0]);
|
|
841
|
-
const h = Number(cur[1]);
|
|
842
|
-
return {
|
|
843
|
-
width: Number.isFinite(w) ? w : 0,
|
|
844
|
-
height: Number.isFinite(h) ? h : 0
|
|
845
|
-
};
|
|
846
|
-
}
|
|
847
|
-
return { width: 0, height: 0 };
|
|
848
|
-
}
|
|
849
986
|
function parseBidDimensions(bid) {
|
|
850
987
|
if (Number.isFinite(bid?.width) && Number.isFinite(bid?.height)) {
|
|
851
988
|
return { width: bid.width, height: bid.height };
|
|
@@ -897,9 +1034,13 @@ var BidkernelPrebidAnalytics = class _BidkernelPrebidAnalytics {
|
|
|
897
1034
|
cachedWinningBids = /* @__PURE__ */ new Map();
|
|
898
1035
|
// Active video player attachment cleanup routines
|
|
899
1036
|
videoDetachCleanups = /* @__PURE__ */ new Set();
|
|
1037
|
+
// Compacted bidder participation map per auction: auctionId -> Map<`${bidder}:${adUnitCode}`, BidderOutcomeEntry>
|
|
1038
|
+
auctionBidderOutcomes = /* @__PURE__ */ new Map();
|
|
900
1039
|
constructor(config) {
|
|
1040
|
+
const requestedEndpoint = config.endpoint || "";
|
|
1041
|
+
const endpoint = !requestedEndpoint || isTrustedEndpoint(requestedEndpoint) ? requestedEndpoint : "";
|
|
901
1042
|
this.config = {
|
|
902
|
-
endpoint
|
|
1043
|
+
endpoint,
|
|
903
1044
|
propertyId: config.propertyId || "",
|
|
904
1045
|
pageviewId: config.pageviewId || generateUUID(),
|
|
905
1046
|
sessionId: config.sessionId || getOrCreateSessionId(),
|
|
@@ -915,6 +1056,12 @@ var BidkernelPrebidAnalytics = class _BidkernelPrebidAnalytics {
|
|
|
915
1056
|
pbjsGlobalName: config.pbjsGlobalName || "pbjs",
|
|
916
1057
|
attachPbjsListeners: config.attachPbjsListeners ?? true
|
|
917
1058
|
};
|
|
1059
|
+
if (requestedEndpoint && !endpoint) {
|
|
1060
|
+
this.log(
|
|
1061
|
+
"ERROR",
|
|
1062
|
+
`Rejected untrusted analytics endpoint ${requestedEndpoint}: must be an absolute https:// URL without embedded credentials. Events will not be transmitted.`
|
|
1063
|
+
);
|
|
1064
|
+
}
|
|
918
1065
|
this.boundFlushBeacon = () => {
|
|
919
1066
|
this.flushAllSlotsTimeInView();
|
|
920
1067
|
this.flushBeacon();
|
|
@@ -1052,6 +1199,7 @@ var BidkernelPrebidAnalytics = class _BidkernelPrebidAnalytics {
|
|
|
1052
1199
|
this.cachedWinningBids.clear();
|
|
1053
1200
|
this.slotEmittedImpressionKeys.clear();
|
|
1054
1201
|
this.slotLastAuctionIds.clear();
|
|
1202
|
+
this.auctionBidderOutcomes.clear();
|
|
1055
1203
|
if (this.flushTimer) {
|
|
1056
1204
|
clearInterval(this.flushTimer);
|
|
1057
1205
|
this.flushTimer = null;
|
|
@@ -1455,7 +1603,73 @@ var BidkernelPrebidAnalytics = class _BidkernelPrebidAnalytics {
|
|
|
1455
1603
|
this.slotEmittedImpressionKeys.delete(key);
|
|
1456
1604
|
}
|
|
1457
1605
|
}
|
|
1458
|
-
this.cachedWinningBids.
|
|
1606
|
+
const cached = this.cachedWinningBids.get(slotId);
|
|
1607
|
+
if (cached) this.deleteCachedBidEntry(cached);
|
|
1608
|
+
}
|
|
1609
|
+
// --- Bounded caches -------------------------------------------------------
|
|
1610
|
+
/**
|
|
1611
|
+
* Reads a cached winning bid, treating an entry past CACHED_BID_TTL_MS as
|
|
1612
|
+
* absent and dropping it. Every read goes through here so a stale bid can
|
|
1613
|
+
* never be married to a much later render.
|
|
1614
|
+
*/
|
|
1615
|
+
getCachedBid(key) {
|
|
1616
|
+
const entry = this.cachedWinningBids.get(key);
|
|
1617
|
+
if (!entry) return void 0;
|
|
1618
|
+
if (Date.now() - entry.timestamp > CACHED_BID_TTL_MS) {
|
|
1619
|
+
this.deleteCachedBidEntry(entry);
|
|
1620
|
+
return void 0;
|
|
1621
|
+
}
|
|
1622
|
+
return entry;
|
|
1623
|
+
}
|
|
1624
|
+
/** Removes every alias pointing at one entry. Aliases are stored by identity. */
|
|
1625
|
+
deleteCachedBidEntry(entry) {
|
|
1626
|
+
for (const [key, value] of Array.from(this.cachedWinningBids)) {
|
|
1627
|
+
if (value === entry) this.cachedWinningBids.delete(key);
|
|
1628
|
+
}
|
|
1629
|
+
}
|
|
1630
|
+
/**
|
|
1631
|
+
* Drops expired entries, then evicts oldest-first until the key cap holds.
|
|
1632
|
+
* Map iterates in insertion order and one win's aliases are written
|
|
1633
|
+
* consecutively, so eviction takes a whole bid's aliases together.
|
|
1634
|
+
*/
|
|
1635
|
+
pruneCachedWinningBids() {
|
|
1636
|
+
const now = Date.now();
|
|
1637
|
+
for (const [key, entry] of Array.from(this.cachedWinningBids)) {
|
|
1638
|
+
if (now - entry.timestamp > CACHED_BID_TTL_MS) this.cachedWinningBids.delete(key);
|
|
1639
|
+
}
|
|
1640
|
+
while (this.cachedWinningBids.size > MAX_CACHED_BID_KEYS) {
|
|
1641
|
+
const oldest = this.cachedWinningBids.keys().next();
|
|
1642
|
+
if (oldest.done) break;
|
|
1643
|
+
this.cachedWinningBids.delete(oldest.value);
|
|
1644
|
+
}
|
|
1645
|
+
}
|
|
1646
|
+
/** Records a dedup key, evicting oldest-first at the cap. */
|
|
1647
|
+
addImpressionKey(key) {
|
|
1648
|
+
if (this.slotEmittedImpressionKeys.has(key)) return;
|
|
1649
|
+
while (this.slotEmittedImpressionKeys.size >= MAX_IMPRESSION_KEYS) {
|
|
1650
|
+
const oldest = this.slotEmittedImpressionKeys.values().next();
|
|
1651
|
+
if (oldest.done) break;
|
|
1652
|
+
this.slotEmittedImpressionKeys.delete(oldest.value);
|
|
1653
|
+
}
|
|
1654
|
+
this.slotEmittedImpressionKeys.add(key);
|
|
1655
|
+
}
|
|
1656
|
+
/**
|
|
1657
|
+
* The bidder-outcome map for an auction, creating it if absent. Evicts the
|
|
1658
|
+
* oldest auction at the cap: without this, any auction whose auctionEnd never
|
|
1659
|
+
* fires (abandoned, navigated away from mid-flight) leaks its map forever.
|
|
1660
|
+
*/
|
|
1661
|
+
getOrCreateAuctionOutcomes(auctionId) {
|
|
1662
|
+
let auctionMap = this.auctionBidderOutcomes.get(auctionId);
|
|
1663
|
+
if (!auctionMap) {
|
|
1664
|
+
auctionMap = /* @__PURE__ */ new Map();
|
|
1665
|
+
this.auctionBidderOutcomes.set(auctionId, auctionMap);
|
|
1666
|
+
}
|
|
1667
|
+
while (this.auctionBidderOutcomes.size > MAX_TRACKED_AUCTIONS) {
|
|
1668
|
+
const oldest = this.auctionBidderOutcomes.keys().next();
|
|
1669
|
+
if (oldest.done || oldest.value === auctionId) break;
|
|
1670
|
+
this.auctionBidderOutcomes.delete(oldest.value);
|
|
1671
|
+
}
|
|
1672
|
+
return auctionMap;
|
|
1459
1673
|
}
|
|
1460
1674
|
hasImpressionEmitted(slotId, refreshIndex, auctionId, creativeId) {
|
|
1461
1675
|
const rIndex = refreshIndex ?? this.slotRefreshIndices.get(slotId) ?? 0;
|
|
@@ -1475,14 +1689,14 @@ var BidkernelPrebidAnalytics = class _BidkernelPrebidAnalytics {
|
|
|
1475
1689
|
}
|
|
1476
1690
|
markImpressionEmitted(slotId, refreshIndex, auctionId, creativeId) {
|
|
1477
1691
|
const baseSlotKey = `${escapeKeyPart(slotId)}:${refreshIndex}`;
|
|
1478
|
-
this.
|
|
1692
|
+
this.addImpressionKey(baseSlotKey);
|
|
1479
1693
|
if (creativeId) {
|
|
1480
|
-
this.
|
|
1694
|
+
this.addImpressionKey(`${baseSlotKey}:${escapeKeyPart(creativeId)}`);
|
|
1481
1695
|
}
|
|
1482
1696
|
if (auctionId) {
|
|
1483
|
-
this.
|
|
1697
|
+
this.addImpressionKey(`${baseSlotKey}:auc:${escapeKeyPart(auctionId)}`);
|
|
1484
1698
|
if (creativeId) {
|
|
1485
|
-
this.
|
|
1699
|
+
this.addImpressionKey(
|
|
1486
1700
|
`${baseSlotKey}:auc:${escapeKeyPart(auctionId)}:${escapeKeyPart(creativeId)}`
|
|
1487
1701
|
);
|
|
1488
1702
|
}
|
|
@@ -1492,7 +1706,7 @@ var BidkernelPrebidAnalytics = class _BidkernelPrebidAnalytics {
|
|
|
1492
1706
|
if (!this.isEnabled) return false;
|
|
1493
1707
|
const resolvedSlotId = slotId || options?.adUnitCode || "";
|
|
1494
1708
|
const adUnitCode = options?.adUnitCode || resolvedSlotId;
|
|
1495
|
-
const cached = (options?.auctionId && resolvedSlotId ? this.
|
|
1709
|
+
const cached = (options?.auctionId && resolvedSlotId ? this.getCachedBid(`${options.auctionId}:${resolvedSlotId}`) : void 0) || (options?.auctionId && adUnitCode ? this.getCachedBid(`${options.auctionId}:${adUnitCode}`) : void 0) || (resolvedSlotId ? this.getCachedBid(resolvedSlotId) : void 0) || (adUnitCode ? this.getCachedBid(adUnitCode) : void 0);
|
|
1496
1710
|
const rawBid = options?.bid || cached?.rawBid || {};
|
|
1497
1711
|
const auctionId = options?.auctionId || rawBid.auctionId || cached?.auctionId || "";
|
|
1498
1712
|
const transactionId = options?.transactionId || rawBid.transactionId || cached?.transactionId || "";
|
|
@@ -1593,7 +1807,7 @@ var BidkernelPrebidAnalytics = class _BidkernelPrebidAnalytics {
|
|
|
1593
1807
|
const auctionId = options?.auctionId || "";
|
|
1594
1808
|
const transactionId = options?.transactionId || "";
|
|
1595
1809
|
const getWinningBid = () => {
|
|
1596
|
-
return (auctionId ? this.
|
|
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);
|
|
1597
1811
|
};
|
|
1598
1812
|
const onAdStartedOrImpression = (event) => {
|
|
1599
1813
|
this.log("DEBUG", "IMA AdEvent.STARTED / IMPRESSION received", event);
|
|
@@ -1757,7 +1971,7 @@ var BidkernelPrebidAnalytics = class _BidkernelPrebidAnalytics {
|
|
|
1757
1971
|
const auctionId = options?.auctionId || "";
|
|
1758
1972
|
const transactionId = options?.transactionId || "";
|
|
1759
1973
|
const getWinningBid = () => {
|
|
1760
|
-
return (auctionId ? this.
|
|
1974
|
+
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);
|
|
1761
1975
|
};
|
|
1762
1976
|
let videoEl = null;
|
|
1763
1977
|
if (el) {
|
|
@@ -1993,6 +2207,10 @@ var BidkernelPrebidAnalytics = class _BidkernelPrebidAnalytics {
|
|
|
1993
2207
|
refreshIndex: record.refreshIndex
|
|
1994
2208
|
};
|
|
1995
2209
|
}
|
|
2210
|
+
/** The validated ingest endpoint in use, or "" when none passed validation. */
|
|
2211
|
+
getEndpoint() {
|
|
2212
|
+
return this.config.endpoint;
|
|
2213
|
+
}
|
|
1996
2214
|
getRefreshIndex(slotId) {
|
|
1997
2215
|
return this.slotRefreshIndices.get(slotId) ?? 0;
|
|
1998
2216
|
}
|
|
@@ -2033,17 +2251,29 @@ var BidkernelPrebidAnalytics = class _BidkernelPrebidAnalytics {
|
|
|
2033
2251
|
handleAuctionInit(data) {
|
|
2034
2252
|
if (this.isDuplicate("auctionInit", data)) return;
|
|
2035
2253
|
this.log("DEBUG", "auctionInit", data);
|
|
2254
|
+
const auctionId = data.auctionId || "";
|
|
2255
|
+
if (auctionId) {
|
|
2256
|
+
this.auctionBidderOutcomes.delete(auctionId);
|
|
2257
|
+
this.getOrCreateAuctionOutcomes(auctionId);
|
|
2258
|
+
}
|
|
2036
2259
|
this.enqueue(TraceEventType.AUCTION_START, "auctionStart", {
|
|
2037
|
-
auctionId
|
|
2260
|
+
auctionId,
|
|
2038
2261
|
transactionId: data.transactionId || ""
|
|
2039
2262
|
});
|
|
2040
2263
|
}
|
|
2041
2264
|
handleAuctionEnd(data) {
|
|
2042
2265
|
if (this.isDuplicate("auctionEnd", data)) return;
|
|
2043
2266
|
this.log("DEBUG", "auctionEnd", data);
|
|
2267
|
+
const auctionId = data.auctionId || "";
|
|
2268
|
+
const outcomesMap = auctionId ? this.auctionBidderOutcomes.get(auctionId) : void 0;
|
|
2269
|
+
const bidderOutcomes = outcomesMap ? Array.from(outcomesMap.values()) : [];
|
|
2270
|
+
if (auctionId) {
|
|
2271
|
+
this.auctionBidderOutcomes.delete(auctionId);
|
|
2272
|
+
}
|
|
2044
2273
|
this.enqueue(TraceEventType.AUCTION_END, "auctionEnd", {
|
|
2045
|
-
auctionId
|
|
2046
|
-
transactionId: data.transactionId || ""
|
|
2274
|
+
auctionId,
|
|
2275
|
+
transactionId: data.transactionId || "",
|
|
2276
|
+
bidderOutcomes
|
|
2047
2277
|
});
|
|
2048
2278
|
}
|
|
2049
2279
|
handleBidRequested(data) {
|
|
@@ -2051,88 +2281,99 @@ var BidkernelPrebidAnalytics = class _BidkernelPrebidAnalytics {
|
|
|
2051
2281
|
this.log("DEBUG", "bidRequested", data);
|
|
2052
2282
|
const auctionId = data.auctionId || "";
|
|
2053
2283
|
const bidder = data.bidderCode || data.bidder || "";
|
|
2284
|
+
if (!auctionId || !bidder) return;
|
|
2285
|
+
const auctionMap = this.getOrCreateAuctionOutcomes(auctionId);
|
|
2054
2286
|
if (Array.isArray(data.bids)) {
|
|
2055
2287
|
data.bids.forEach((bid) => {
|
|
2288
|
+
const adUnitCode = bid.adUnitCode || "";
|
|
2056
2289
|
const mediaTypes = Object.keys(bid.mediaTypes || {});
|
|
2057
|
-
const
|
|
2058
|
-
const
|
|
2059
|
-
if (
|
|
2060
|
-
|
|
2061
|
-
|
|
2062
|
-
|
|
2063
|
-
|
|
2064
|
-
|
|
2065
|
-
|
|
2066
|
-
gpid,
|
|
2067
|
-
bid: {
|
|
2068
|
-
bidder,
|
|
2069
|
-
mediaType,
|
|
2070
|
-
width,
|
|
2071
|
-
height
|
|
2072
|
-
}
|
|
2290
|
+
const mediaType = mediaTypes.length > 0 ? mediaTypes[0] : bid.mediaType || "banner";
|
|
2291
|
+
const key = `${bidder}:${adUnitCode}`;
|
|
2292
|
+
if (!auctionMap.has(key)) {
|
|
2293
|
+
auctionMap.set(key, {
|
|
2294
|
+
bidder,
|
|
2295
|
+
adUnitCode,
|
|
2296
|
+
mediaType,
|
|
2297
|
+
outcome: BidderOutcome.BIDDER_OUTCOME_NO_RESPONSE,
|
|
2298
|
+
latencyMs: 0
|
|
2073
2299
|
});
|
|
2074
|
-
return;
|
|
2075
2300
|
}
|
|
2076
|
-
mediaTypes.forEach((mediaType) => {
|
|
2077
|
-
let rawSize = null;
|
|
2078
|
-
if (mediaType === "banner") {
|
|
2079
|
-
rawSize = bid.mediaTypes?.banner?.sizes || bid.sizes;
|
|
2080
|
-
} else if (mediaType === "video") {
|
|
2081
|
-
rawSize = bid.mediaTypes?.video?.playerSize || bid.mediaTypes?.video?.sizes || bid.playerSize;
|
|
2082
|
-
} else {
|
|
2083
|
-
rawSize = bid.mediaTypes?.[mediaType]?.sizes;
|
|
2084
|
-
}
|
|
2085
|
-
const { width, height } = parseSize(rawSize);
|
|
2086
|
-
this.enqueue(TraceEventType.BID_REQUEST, "bidRequest", {
|
|
2087
|
-
auctionId,
|
|
2088
|
-
transactionId,
|
|
2089
|
-
adUnitCode: bid.adUnitCode || "",
|
|
2090
|
-
gpid,
|
|
2091
|
-
bid: {
|
|
2092
|
-
bidder,
|
|
2093
|
-
mediaType,
|
|
2094
|
-
width,
|
|
2095
|
-
height
|
|
2096
|
-
}
|
|
2097
|
-
});
|
|
2098
|
-
});
|
|
2099
2301
|
});
|
|
2100
2302
|
}
|
|
2101
2303
|
}
|
|
2102
2304
|
handleBidResponse(data) {
|
|
2103
2305
|
if (this.isDuplicate("bidResponse", data)) return;
|
|
2104
2306
|
this.log("DEBUG", "bidResponse", data);
|
|
2105
|
-
|
|
2106
|
-
|
|
2107
|
-
|
|
2108
|
-
|
|
2109
|
-
|
|
2110
|
-
|
|
2111
|
-
|
|
2112
|
-
|
|
2113
|
-
|
|
2114
|
-
|
|
2115
|
-
|
|
2116
|
-
|
|
2117
|
-
|
|
2118
|
-
|
|
2307
|
+
const auctionId = data.auctionId || "";
|
|
2308
|
+
const bidder = data.bidderCode || data.bidder || "";
|
|
2309
|
+
const adUnitCode = data.adUnitCode || "";
|
|
2310
|
+
const cpm = Number.isFinite(data.originalCpm) ? data.originalCpm : Number.isFinite(data.cpm) ? data.cpm : 0;
|
|
2311
|
+
const latencyMs = Number.isFinite(data.timeToRespond) ? data.timeToRespond : 0;
|
|
2312
|
+
const mediaType = data.mediaType || "banner";
|
|
2313
|
+
if (auctionId && bidder) {
|
|
2314
|
+
const auctionMap = this.getOrCreateAuctionOutcomes(auctionId);
|
|
2315
|
+
const key = `${bidder}:${adUnitCode}`;
|
|
2316
|
+
const entry = auctionMap.get(key);
|
|
2317
|
+
const outcome = cpm > 0 ? BidderOutcome.BIDDER_OUTCOME_BID : BidderOutcome.BIDDER_OUTCOME_NO_BID;
|
|
2318
|
+
if (entry) {
|
|
2319
|
+
entry.outcome = outcome;
|
|
2320
|
+
entry.latencyMs = latencyMs;
|
|
2321
|
+
if (mediaType) entry.mediaType = mediaType;
|
|
2322
|
+
} else {
|
|
2323
|
+
auctionMap.set(key, {
|
|
2324
|
+
bidder,
|
|
2325
|
+
adUnitCode,
|
|
2326
|
+
mediaType,
|
|
2327
|
+
outcome,
|
|
2328
|
+
latencyMs
|
|
2329
|
+
});
|
|
2119
2330
|
}
|
|
2120
|
-
}
|
|
2331
|
+
}
|
|
2332
|
+
if (cpm > 0) {
|
|
2333
|
+
this.enqueue(TraceEventType.BID_RESPONSE, "bidResponse", {
|
|
2334
|
+
auctionId,
|
|
2335
|
+
transactionId: data.transactionId || "",
|
|
2336
|
+
adUnitCode,
|
|
2337
|
+
bid: {
|
|
2338
|
+
bidder,
|
|
2339
|
+
cpm,
|
|
2340
|
+
currency: data.originalCurrency ?? data.currency ?? "USD",
|
|
2341
|
+
...parseBidDimensions(data),
|
|
2342
|
+
dealId: data.dealId || "",
|
|
2343
|
+
mediaType,
|
|
2344
|
+
latencyMs,
|
|
2345
|
+
advertiserDomain: data.meta?.advertiserDomains?.[0] || "",
|
|
2346
|
+
creativeId: data.creativeId || ""
|
|
2347
|
+
}
|
|
2348
|
+
});
|
|
2349
|
+
}
|
|
2121
2350
|
}
|
|
2122
2351
|
handleBidTimeout(data) {
|
|
2123
2352
|
if (this.isDuplicate("bidTimeout", data)) return;
|
|
2124
2353
|
this.log("DEBUG", "bidTimeout", data);
|
|
2125
2354
|
const items = Array.isArray(data) ? data : data ? [data] : [];
|
|
2126
2355
|
for (const t of items) {
|
|
2127
|
-
|
|
2128
|
-
|
|
2129
|
-
|
|
2130
|
-
|
|
2131
|
-
|
|
2132
|
-
|
|
2133
|
-
|
|
2356
|
+
const auctionId = t.auctionId || "";
|
|
2357
|
+
const bidder = t.bidderCode || t.bidder || "";
|
|
2358
|
+
const adUnitCode = t.adUnitCode || "";
|
|
2359
|
+
const latencyMs = Number.isFinite(t.timeout) ? t.timeout : 0;
|
|
2360
|
+
if (auctionId && bidder) {
|
|
2361
|
+
const auctionMap = this.getOrCreateAuctionOutcomes(auctionId);
|
|
2362
|
+
const key = `${bidder}:${adUnitCode}`;
|
|
2363
|
+
const entry = auctionMap.get(key);
|
|
2364
|
+
if (entry) {
|
|
2365
|
+
entry.outcome = BidderOutcome.BIDDER_OUTCOME_TIMEOUT;
|
|
2366
|
+
if (latencyMs > 0) entry.latencyMs = latencyMs;
|
|
2367
|
+
} else {
|
|
2368
|
+
auctionMap.set(key, {
|
|
2369
|
+
bidder,
|
|
2370
|
+
adUnitCode,
|
|
2371
|
+
mediaType: "banner",
|
|
2372
|
+
outcome: BidderOutcome.BIDDER_OUTCOME_TIMEOUT,
|
|
2373
|
+
latencyMs
|
|
2374
|
+
});
|
|
2134
2375
|
}
|
|
2135
|
-
}
|
|
2376
|
+
}
|
|
2136
2377
|
}
|
|
2137
2378
|
}
|
|
2138
2379
|
handleBidWon(data) {
|
|
@@ -2171,6 +2412,7 @@ var BidkernelPrebidAnalytics = class _BidkernelPrebidAnalytics {
|
|
|
2171
2412
|
this.cachedWinningBids.set(`${auctionId}:${data.adId}`, cachedEntry);
|
|
2172
2413
|
}
|
|
2173
2414
|
}
|
|
2415
|
+
this.pruneCachedWinningBids();
|
|
2174
2416
|
}
|
|
2175
2417
|
this.enqueue(TraceEventType.BID_WIN, "bidWon", {
|
|
2176
2418
|
auctionId,
|
|
@@ -2182,14 +2424,27 @@ var BidkernelPrebidAnalytics = class _BidkernelPrebidAnalytics {
|
|
|
2182
2424
|
handleNoBid(data) {
|
|
2183
2425
|
if (this.isDuplicate("noBid", data)) return;
|
|
2184
2426
|
this.log("DEBUG", "noBid", data);
|
|
2185
|
-
|
|
2186
|
-
|
|
2187
|
-
|
|
2188
|
-
|
|
2189
|
-
|
|
2190
|
-
|
|
2427
|
+
const auctionId = data.auctionId || "";
|
|
2428
|
+
const bidder = data.bidderCode || data.bidder || "";
|
|
2429
|
+
const adUnitCode = data.adUnitCode || "";
|
|
2430
|
+
if (auctionId && bidder) {
|
|
2431
|
+
const auctionMap = this.getOrCreateAuctionOutcomes(auctionId);
|
|
2432
|
+
const key = `${bidder}:${adUnitCode}`;
|
|
2433
|
+
const entry = auctionMap.get(key);
|
|
2434
|
+
if (entry) {
|
|
2435
|
+
if (entry.outcome === BidderOutcome.BIDDER_OUTCOME_NO_RESPONSE) {
|
|
2436
|
+
entry.outcome = BidderOutcome.BIDDER_OUTCOME_NO_BID;
|
|
2437
|
+
}
|
|
2438
|
+
} else {
|
|
2439
|
+
auctionMap.set(key, {
|
|
2440
|
+
bidder,
|
|
2441
|
+
adUnitCode,
|
|
2442
|
+
mediaType: "banner",
|
|
2443
|
+
outcome: BidderOutcome.BIDDER_OUTCOME_NO_BID,
|
|
2444
|
+
latencyMs: 0
|
|
2445
|
+
});
|
|
2191
2446
|
}
|
|
2192
|
-
}
|
|
2447
|
+
}
|
|
2193
2448
|
}
|
|
2194
2449
|
handleAdRenderFailed(data) {
|
|
2195
2450
|
if (this.isDuplicate("adRenderFailed", data)) return;
|
|
@@ -2221,7 +2476,7 @@ var BidkernelPrebidAnalytics = class _BidkernelPrebidAnalytics {
|
|
|
2221
2476
|
const mediaType = bid.mediaType || "banner";
|
|
2222
2477
|
const slotKey = data.adUnitCode || bid.adUnitCode || "";
|
|
2223
2478
|
const altKey = data.adId || bid.adId || "";
|
|
2224
|
-
const cached = (auctionId && slotKey ? this.
|
|
2479
|
+
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);
|
|
2225
2480
|
const bidTrace = {
|
|
2226
2481
|
bidder: bid.bidderCode || bid.bidder || cached?.bidTrace?.bidder || "",
|
|
2227
2482
|
cpm: Number.isFinite(bid.originalCpm) ? bid.originalCpm : Number.isFinite(bid.cpm) ? bid.cpm : cached?.bidTrace?.cpm ?? 0,
|
|
@@ -2310,7 +2565,12 @@ var BidkernelPrebidAnalytics = class _BidkernelPrebidAnalytics {
|
|
|
2310
2565
|
}
|
|
2311
2566
|
if (!el && targetId) {
|
|
2312
2567
|
try {
|
|
2313
|
-
|
|
2568
|
+
for (const candidate of Array.from(document.querySelectorAll("[data-ad-slot]"))) {
|
|
2569
|
+
if (candidate.getAttribute("data-ad-slot") === targetId) {
|
|
2570
|
+
el = candidate;
|
|
2571
|
+
break;
|
|
2572
|
+
}
|
|
2573
|
+
}
|
|
2314
2574
|
} catch {
|
|
2315
2575
|
}
|
|
2316
2576
|
}
|
|
@@ -2364,6 +2624,9 @@ var BidkernelPrebidAnalytics = class _BidkernelPrebidAnalytics {
|
|
|
2364
2624
|
if (data.viewableDurationMs !== void 0) {
|
|
2365
2625
|
protoEvent.viewableDurationMs = Number.isFinite(data.viewableDurationMs) ? data.viewableDurationMs : 0;
|
|
2366
2626
|
}
|
|
2627
|
+
if (data.bidderOutcomes && Array.isArray(data.bidderOutcomes)) {
|
|
2628
|
+
protoEvent.bidderOutcomes = data.bidderOutcomes;
|
|
2629
|
+
}
|
|
2367
2630
|
const metadata = { ...data.metadata };
|
|
2368
2631
|
if (data.error !== void 0 && data.error !== null) {
|
|
2369
2632
|
metadata.error = typeof data.error === "object" && data.error.message ? String(data.error.message) : String(data.error);
|
|
@@ -2374,8 +2637,9 @@ var BidkernelPrebidAnalytics = class _BidkernelPrebidAnalytics {
|
|
|
2374
2637
|
if (data.refreshIndex !== void 0 && metadata.refresh_index === void 0) {
|
|
2375
2638
|
metadata.refresh_index = String(data.refreshIndex);
|
|
2376
2639
|
}
|
|
2377
|
-
|
|
2378
|
-
|
|
2640
|
+
const boundedMetadata = boundMetadata(metadata);
|
|
2641
|
+
if (Object.keys(boundedMetadata).length > 0) {
|
|
2642
|
+
protoEvent.metadata = boundedMetadata;
|
|
2379
2643
|
}
|
|
2380
2644
|
this.queue.push(protoEvent);
|
|
2381
2645
|
if (this.queue.length > MAX_QUEUE_SIZE) {
|
|
@@ -2413,7 +2677,6 @@ var BidkernelPrebidAnalytics = class _BidkernelPrebidAnalytics {
|
|
|
2413
2677
|
// Shared by flush() and flushBeacon().
|
|
2414
2678
|
drainBatch() {
|
|
2415
2679
|
if (this.queue.length === 0 || !this.config.endpoint) return null;
|
|
2416
|
-
let events = this.queue;
|
|
2417
2680
|
const domain = typeof window !== "undefined" ? window.location.hostname : "";
|
|
2418
2681
|
const buildBatch = (evts) => ({
|
|
2419
2682
|
propertyId: this.config.propertyId,
|
|
@@ -2427,25 +2690,37 @@ var BidkernelPrebidAnalytics = class _BidkernelPrebidAnalytics {
|
|
|
2427
2690
|
domain,
|
|
2428
2691
|
events: evts
|
|
2429
2692
|
});
|
|
2430
|
-
|
|
2431
|
-
|
|
2432
|
-
|
|
2433
|
-
|
|
2434
|
-
events = events.slice(0, Math.floor(events.length / 2));
|
|
2435
|
-
encoded = TraceEventBatch.encode(
|
|
2693
|
+
for (; ; ) {
|
|
2694
|
+
if (this.queue.length === 0) return null;
|
|
2695
|
+
let events = this.queue;
|
|
2696
|
+
let encoded = TraceEventBatch.encode(
|
|
2436
2697
|
buildBatch(events)
|
|
2437
2698
|
).finish();
|
|
2699
|
+
while (events.length > 1 && encoded.byteLength > MAX_PAYLOAD_BYTES) {
|
|
2700
|
+
events = events.slice(0, Math.floor(events.length / 2));
|
|
2701
|
+
encoded = TraceEventBatch.encode(
|
|
2702
|
+
buildBatch(events)
|
|
2703
|
+
).finish();
|
|
2704
|
+
}
|
|
2705
|
+
if (encoded.byteLength > MAX_PAYLOAD_BYTES) {
|
|
2706
|
+
const [dropped] = this.queue.splice(0, 1);
|
|
2707
|
+
this.log(
|
|
2708
|
+
"WARN",
|
|
2709
|
+
`Dropping oversized event ${dropped?.eventName || dropped?.eventId} (${encoded.byteLength} bytes exceeds the ${MAX_PAYLOAD_BYTES}-byte cap on its own)`
|
|
2710
|
+
);
|
|
2711
|
+
continue;
|
|
2712
|
+
}
|
|
2713
|
+
this.queue = this.queue.slice(events.length);
|
|
2714
|
+
return {
|
|
2715
|
+
url: `${this.config.endpoint}/${this.config.propertyId}`,
|
|
2716
|
+
// protobufjs types finish() as Uint8Array<ArrayBufferLike>; the buffer is
|
|
2717
|
+
// always a plain ArrayBuffer, so narrow for fetch/Blob compatibility.
|
|
2718
|
+
encoded,
|
|
2719
|
+
events
|
|
2720
|
+
};
|
|
2438
2721
|
}
|
|
2439
|
-
this.queue = this.queue.slice(events.length);
|
|
2440
|
-
return {
|
|
2441
|
-
url: `${this.config.endpoint}/${this.config.propertyId}`,
|
|
2442
|
-
// protobufjs types finish() as Uint8Array<ArrayBufferLike>; the buffer is
|
|
2443
|
-
// always a plain ArrayBuffer, so narrow for fetch/Blob compatibility.
|
|
2444
|
-
encoded,
|
|
2445
|
-
events
|
|
2446
|
-
};
|
|
2447
2722
|
}
|
|
2448
|
-
sendFetch(payload, useKeepalive = false, onDelivered) {
|
|
2723
|
+
sendFetch(payload, useKeepalive = false, onDelivered, isReplay = false) {
|
|
2449
2724
|
const fetchOpts = {
|
|
2450
2725
|
method: "POST",
|
|
2451
2726
|
// Safelisted content type: no CORS preflight (see SAFE_CONTENT_TYPE).
|
|
@@ -2462,15 +2737,17 @@ var BidkernelPrebidAnalytics = class _BidkernelPrebidAnalytics {
|
|
|
2462
2737
|
this.log("WARN", `Dropping batch due to non-retryable client error HTTP ${res.status}`);
|
|
2463
2738
|
return;
|
|
2464
2739
|
}
|
|
2465
|
-
this.handleSendFailure(payload.events);
|
|
2740
|
+
if (!isReplay) this.handleSendFailure(payload.events);
|
|
2466
2741
|
} else {
|
|
2467
|
-
|
|
2468
|
-
|
|
2742
|
+
if (!isReplay) {
|
|
2743
|
+
this.consecutiveSendFailures = 0;
|
|
2744
|
+
this.nextSendAllowedAt = 0;
|
|
2745
|
+
}
|
|
2469
2746
|
if (onDelivered) onDelivered();
|
|
2470
2747
|
}
|
|
2471
2748
|
}).catch((err) => {
|
|
2472
2749
|
this.log("ERROR", "Failed to send batch", err);
|
|
2473
|
-
this.handleSendFailure(payload.events);
|
|
2750
|
+
if (!isReplay) this.handleSendFailure(payload.events);
|
|
2474
2751
|
});
|
|
2475
2752
|
const proc = typeof globalThis !== "undefined" ? globalThis.process : void 0;
|
|
2476
2753
|
if (proc && typeof proc._tickCallback === "function") {
|
|
@@ -2526,16 +2803,23 @@ var BidkernelPrebidAnalytics = class _BidkernelPrebidAnalytics {
|
|
|
2526
2803
|
this.schedulePersistedCleanup();
|
|
2527
2804
|
}
|
|
2528
2805
|
// --- Exit-batch persistence -----------------------------------------------
|
|
2806
|
+
// Key prefix for this instance's property. encodeURIComponent keeps the
|
|
2807
|
+
// property segment free of the "_" the timestamp suffix is parsed against,
|
|
2808
|
+
// so a property ID containing one cannot forge another property's prefix.
|
|
2809
|
+
pendingKeyPrefix() {
|
|
2810
|
+
return `${PENDING_BATCH_KEY_PREFIX}${encodeURIComponent(this.config.propertyId)}_`;
|
|
2811
|
+
}
|
|
2529
2812
|
persistBatch(encoded) {
|
|
2530
2813
|
try {
|
|
2531
2814
|
if (typeof localStorage === "undefined") return null;
|
|
2815
|
+
if (!this.config.propertyId) return null;
|
|
2532
2816
|
let pendingCount = 0;
|
|
2533
2817
|
for (let i = 0; i < localStorage.length; i++) {
|
|
2534
2818
|
const k = localStorage.key(i);
|
|
2535
2819
|
if (k && k.startsWith(PENDING_BATCH_KEY_PREFIX)) pendingCount++;
|
|
2536
2820
|
}
|
|
2537
2821
|
if (pendingCount >= MAX_PENDING_BATCHES) return null;
|
|
2538
|
-
const key = `${
|
|
2822
|
+
const key = `${this.pendingKeyPrefix()}${Date.now()}_${Math.floor(Math.random() * 1e6)}`;
|
|
2539
2823
|
localStorage.setItem(key, bytesToBase64(encoded));
|
|
2540
2824
|
this.persistedBatchKeys.push(key);
|
|
2541
2825
|
return key;
|
|
@@ -2566,16 +2850,33 @@ var BidkernelPrebidAnalytics = class _BidkernelPrebidAnalytics {
|
|
|
2566
2850
|
}, PENDING_BATCH_CLEANUP_DELAY_MS);
|
|
2567
2851
|
}
|
|
2568
2852
|
// Resend batches a previous pageview persisted at exit but could not
|
|
2569
|
-
// confirm.
|
|
2570
|
-
//
|
|
2571
|
-
//
|
|
2853
|
+
// confirm. Only this property's own keys are claimed: batches belonging to
|
|
2854
|
+
// another property sharing this origin are left for that property's next
|
|
2855
|
+
// pageview, since resending them here would file them under this property.
|
|
2856
|
+
// Keys are claimed (removed) before sending so concurrent tabs don't
|
|
2857
|
+
// double-send; the batch bytes carry their original pageview, session, and
|
|
2858
|
+
// event timestamps, so late rows attribute correctly.
|
|
2572
2859
|
resendPersistedBatches() {
|
|
2573
2860
|
try {
|
|
2574
2861
|
if (typeof localStorage === "undefined" || !this.config.endpoint) return;
|
|
2862
|
+
if (!this.config.propertyId) return;
|
|
2863
|
+
const ownPrefix = this.pendingKeyPrefix();
|
|
2575
2864
|
const keys = [];
|
|
2865
|
+
const legacyKeys = [];
|
|
2576
2866
|
for (let i = 0; i < localStorage.length; i++) {
|
|
2577
2867
|
const k = localStorage.key(i);
|
|
2578
|
-
if (k
|
|
2868
|
+
if (!k || !k.startsWith(PENDING_BATCH_KEY_PREFIX)) continue;
|
|
2869
|
+
if (k.startsWith(ownPrefix)) {
|
|
2870
|
+
keys.push(k);
|
|
2871
|
+
} else if (isLegacyPendingKey(k)) {
|
|
2872
|
+
legacyKeys.push(k);
|
|
2873
|
+
}
|
|
2874
|
+
}
|
|
2875
|
+
for (const key of legacyKeys) {
|
|
2876
|
+
try {
|
|
2877
|
+
localStorage.removeItem(key);
|
|
2878
|
+
} catch {
|
|
2879
|
+
}
|
|
2579
2880
|
}
|
|
2580
2881
|
for (const key of keys) {
|
|
2581
2882
|
let value = null;
|
|
@@ -2586,7 +2887,7 @@ var BidkernelPrebidAnalytics = class _BidkernelPrebidAnalytics {
|
|
|
2586
2887
|
continue;
|
|
2587
2888
|
}
|
|
2588
2889
|
if (!value) continue;
|
|
2589
|
-
const ts = parseInt(key.slice(
|
|
2890
|
+
const ts = parseInt(key.slice(ownPrefix.length), 10);
|
|
2590
2891
|
if (!Number.isFinite(ts) || Date.now() - ts > PENDING_BATCH_MAX_AGE_MS) continue;
|
|
2591
2892
|
let encoded;
|
|
2592
2893
|
try {
|
|
@@ -2596,11 +2897,16 @@ var BidkernelPrebidAnalytics = class _BidkernelPrebidAnalytics {
|
|
|
2596
2897
|
}
|
|
2597
2898
|
if (encoded.byteLength === 0) continue;
|
|
2598
2899
|
this.log("DEBUG", `Resending persisted exit batch (${encoded.byteLength} bytes)`);
|
|
2599
|
-
this.sendFetch(
|
|
2600
|
-
|
|
2601
|
-
|
|
2602
|
-
|
|
2603
|
-
|
|
2900
|
+
this.sendFetch(
|
|
2901
|
+
{
|
|
2902
|
+
url: `${this.config.endpoint}/${this.config.propertyId}`,
|
|
2903
|
+
encoded,
|
|
2904
|
+
events: []
|
|
2905
|
+
},
|
|
2906
|
+
false,
|
|
2907
|
+
void 0,
|
|
2908
|
+
true
|
|
2909
|
+
);
|
|
2604
2910
|
}
|
|
2605
2911
|
} catch {
|
|
2606
2912
|
}
|
|
@@ -2625,11 +2931,13 @@ var BidkernelPrebidAnalytics = class _BidkernelPrebidAnalytics {
|
|
|
2625
2931
|
}
|
|
2626
2932
|
}
|
|
2627
2933
|
};
|
|
2628
|
-
function extractAnalyticsOptions(config) {
|
|
2934
|
+
function extractAnalyticsOptions(config, allowProviderless = true) {
|
|
2629
2935
|
if (!config) return null;
|
|
2630
2936
|
if (Array.isArray(config)) {
|
|
2631
|
-
const matched = config.find((item) => item?.provider === "bidkernel") ?? config.find(
|
|
2632
|
-
|
|
2937
|
+
const matched = config.find((item) => item?.provider === "bidkernel") ?? (allowProviderless ? config.find(
|
|
2938
|
+
(item) => item && typeof item === "object" && item.provider === void 0
|
|
2939
|
+
) : void 0);
|
|
2940
|
+
return matched ? extractAnalyticsOptions(matched, allowProviderless) : null;
|
|
2633
2941
|
}
|
|
2634
2942
|
if (typeof config === "object") {
|
|
2635
2943
|
if (config.options && typeof config.options === "object") {
|
|
@@ -2671,18 +2979,27 @@ function registerPrebidAnalytics(pbjsGlobalName = "pbjs", defaults) {
|
|
|
2671
2979
|
win[pbjsGlobalName] = win[pbjsGlobalName] || {};
|
|
2672
2980
|
const pbjs = win[pbjsGlobalName];
|
|
2673
2981
|
const enableInstance = (options) => {
|
|
2674
|
-
|
|
2982
|
+
const previous = win._bidkernelPrebidAnalytics;
|
|
2983
|
+
const pinnedEndpoint = previous?.getEndpoint?.() || "";
|
|
2984
|
+
const merged = {
|
|
2985
|
+
pbjsGlobalName,
|
|
2986
|
+
...defaults,
|
|
2987
|
+
...options
|
|
2988
|
+
};
|
|
2989
|
+
if (pinnedEndpoint && !sameEndpointOrigin(merged.endpoint || "", pinnedEndpoint)) {
|
|
2990
|
+
console.warn(
|
|
2991
|
+
`[bidkernel] Ignoring analytics endpoint change to ${merged.endpoint || "(empty)"}: this page is pinned to ${pinnedEndpoint}. Reload to change the ingest endpoint.`
|
|
2992
|
+
);
|
|
2993
|
+
merged.endpoint = pinnedEndpoint;
|
|
2994
|
+
}
|
|
2995
|
+
if (previous) {
|
|
2675
2996
|
try {
|
|
2676
|
-
|
|
2997
|
+
previous.disable();
|
|
2677
2998
|
} catch (e) {
|
|
2678
2999
|
console.warn("[bidkernel] Failed to disable previous analytics instance:", e);
|
|
2679
3000
|
}
|
|
2680
3001
|
}
|
|
2681
|
-
const analytics = new BidkernelPrebidAnalytics(
|
|
2682
|
-
pbjsGlobalName,
|
|
2683
|
-
...defaults,
|
|
2684
|
-
...options
|
|
2685
|
-
});
|
|
3002
|
+
const analytics = new BidkernelPrebidAnalytics(merged);
|
|
2686
3003
|
analytics.enable();
|
|
2687
3004
|
win._bidkernelPrebidAnalytics = analytics;
|
|
2688
3005
|
return analytics;
|
|
@@ -2693,7 +3010,7 @@ function registerPrebidAnalytics(pbjsGlobalName = "pbjs", defaults) {
|
|
|
2693
3010
|
pbjs.adapterManager.registerAnalyticsAdapter({
|
|
2694
3011
|
adapter: {
|
|
2695
3012
|
enableAnalytics: (config) => {
|
|
2696
|
-
const options = extractAnalyticsOptions(config);
|
|
3013
|
+
const options = extractAnalyticsOptions(config, !Array.isArray(config));
|
|
2697
3014
|
if (options) {
|
|
2698
3015
|
enableInstance(options);
|
|
2699
3016
|
}
|
|
@@ -2759,5 +3076,6 @@ function getbidkernel(alias = "bidkernel") {
|
|
|
2759
3076
|
PrebidEventDeduper,
|
|
2760
3077
|
getPrebidEventKey,
|
|
2761
3078
|
getbidkernel,
|
|
3079
|
+
isTrustedEndpoint,
|
|
2762
3080
|
registerPrebidAnalytics
|
|
2763
3081
|
});
|