@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.mjs
CHANGED
|
@@ -4,26 +4,33 @@ var TraceEventType = {
|
|
|
4
4
|
TRACE_EVENT_TYPE_UNSPECIFIED: 0,
|
|
5
5
|
AUCTION_START: 1,
|
|
6
6
|
AUCTION_END: 2,
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
BID_WIN: 5,
|
|
10
|
-
BID_TIMEOUT: 6,
|
|
7
|
+
BID_RESPONSE: 3,
|
|
8
|
+
BID_WIN: 4,
|
|
11
9
|
/** IMPRESSION - ad rendered into DOM */
|
|
12
|
-
IMPRESSION:
|
|
10
|
+
IMPRESSION: 5,
|
|
13
11
|
/** VIEWABLE - IAB viewability threshold met (boolean, once per render cycle) */
|
|
14
|
-
VIEWABLE:
|
|
12
|
+
VIEWABLE: 6,
|
|
15
13
|
/** TIME_IN_VIEW - accumulated in-viewport duration (once per render cycle end) */
|
|
16
|
-
TIME_IN_VIEW:
|
|
17
|
-
REFRESH:
|
|
18
|
-
ERROR:
|
|
19
|
-
SLOT_DEFINED:
|
|
20
|
-
SLOT_DESTROYED:
|
|
14
|
+
TIME_IN_VIEW: 7,
|
|
15
|
+
REFRESH: 8,
|
|
16
|
+
ERROR: 9,
|
|
17
|
+
SLOT_DEFINED: 10,
|
|
18
|
+
SLOT_DESTROYED: 11,
|
|
21
19
|
/** CLICK - ad click-through */
|
|
22
|
-
CLICK:
|
|
23
|
-
/** NO_BID - bidder responded with no bid */
|
|
24
|
-
NO_BID: 15,
|
|
20
|
+
CLICK: 12,
|
|
25
21
|
/** AD_RENDER_FAILED - Prebid failed to render the winning ad */
|
|
26
|
-
AD_RENDER_FAILED:
|
|
22
|
+
AD_RENDER_FAILED: 13,
|
|
23
|
+
UNRECOGNIZED: -1
|
|
24
|
+
};
|
|
25
|
+
var BidderOutcome = {
|
|
26
|
+
BIDDER_OUTCOME_UNSPECIFIED: 0,
|
|
27
|
+
/** BIDDER_OUTCOME_BID - returned a bid (CPM > 0) */
|
|
28
|
+
BIDDER_OUTCOME_BID: 1,
|
|
29
|
+
BIDDER_OUTCOME_NO_BID: 2,
|
|
30
|
+
BIDDER_OUTCOME_TIMEOUT: 3,
|
|
31
|
+
BIDDER_OUTCOME_ERROR: 4,
|
|
32
|
+
/** BIDDER_OUTCOME_NO_RESPONSE - requested, nothing came back by auction end */
|
|
33
|
+
BIDDER_OUTCOME_NO_RESPONSE: 5,
|
|
27
34
|
UNRECOGNIZED: -1
|
|
28
35
|
};
|
|
29
36
|
function createBaseTraceEventBatch() {
|
|
@@ -254,7 +261,8 @@ function createBaseTraceEvent() {
|
|
|
254
261
|
namespace: "",
|
|
255
262
|
eventId: "",
|
|
256
263
|
metadata: {},
|
|
257
|
-
viewableDurationMs: void 0
|
|
264
|
+
viewableDurationMs: void 0,
|
|
265
|
+
bidderOutcomes: []
|
|
258
266
|
};
|
|
259
267
|
}
|
|
260
268
|
var TraceEvent = {
|
|
@@ -292,6 +300,11 @@ var TraceEvent = {
|
|
|
292
300
|
if (message.viewableDurationMs !== void 0) {
|
|
293
301
|
writer.uint32(89).double(message.viewableDurationMs);
|
|
294
302
|
}
|
|
303
|
+
if (message.bidderOutcomes !== void 0 && message.bidderOutcomes.length !== 0) {
|
|
304
|
+
for (const v of message.bidderOutcomes) {
|
|
305
|
+
BidderOutcomeEntry.encode(v, writer.uint32(98).fork()).join();
|
|
306
|
+
}
|
|
307
|
+
}
|
|
295
308
|
return writer;
|
|
296
309
|
},
|
|
297
310
|
decode(input, length) {
|
|
@@ -381,6 +394,16 @@ var TraceEvent = {
|
|
|
381
394
|
message.viewableDurationMs = reader.double();
|
|
382
395
|
continue;
|
|
383
396
|
}
|
|
397
|
+
case 12: {
|
|
398
|
+
if (tag !== 98) {
|
|
399
|
+
break;
|
|
400
|
+
}
|
|
401
|
+
const el = BidderOutcomeEntry.decode(reader, reader.uint32());
|
|
402
|
+
if (el !== void 0) {
|
|
403
|
+
message.bidderOutcomes.push(el);
|
|
404
|
+
}
|
|
405
|
+
continue;
|
|
406
|
+
}
|
|
384
407
|
}
|
|
385
408
|
if ((tag & 7) === 4 || tag === 0) {
|
|
386
409
|
break;
|
|
@@ -413,6 +436,7 @@ var TraceEvent = {
|
|
|
413
436
|
{}
|
|
414
437
|
);
|
|
415
438
|
message.viewableDurationMs = object.viewableDurationMs ?? void 0;
|
|
439
|
+
message.bidderOutcomes = object.bidderOutcomes?.map((e) => BidderOutcomeEntry.fromPartial(e)) || [];
|
|
416
440
|
return message;
|
|
417
441
|
}
|
|
418
442
|
};
|
|
@@ -468,6 +492,91 @@ var TraceEvent_MetadataEntry = {
|
|
|
468
492
|
return message;
|
|
469
493
|
}
|
|
470
494
|
};
|
|
495
|
+
function createBaseBidderOutcomeEntry() {
|
|
496
|
+
return { bidder: "", adUnitCode: "", mediaType: "", outcome: 0, latencyMs: 0 };
|
|
497
|
+
}
|
|
498
|
+
var BidderOutcomeEntry = {
|
|
499
|
+
encode(message, writer = new BinaryWriter()) {
|
|
500
|
+
if (message.bidder !== void 0 && message.bidder !== "") {
|
|
501
|
+
writer.uint32(10).string(message.bidder);
|
|
502
|
+
}
|
|
503
|
+
if (message.adUnitCode !== void 0 && message.adUnitCode !== "") {
|
|
504
|
+
writer.uint32(18).string(message.adUnitCode);
|
|
505
|
+
}
|
|
506
|
+
if (message.mediaType !== void 0 && message.mediaType !== "") {
|
|
507
|
+
writer.uint32(26).string(message.mediaType);
|
|
508
|
+
}
|
|
509
|
+
if (message.outcome !== void 0 && message.outcome !== 0) {
|
|
510
|
+
writer.uint32(32).int32(message.outcome);
|
|
511
|
+
}
|
|
512
|
+
if (message.latencyMs !== void 0 && message.latencyMs !== 0) {
|
|
513
|
+
writer.uint32(41).double(message.latencyMs);
|
|
514
|
+
}
|
|
515
|
+
return writer;
|
|
516
|
+
},
|
|
517
|
+
decode(input, length) {
|
|
518
|
+
const reader = input instanceof BinaryReader ? input : new BinaryReader(input);
|
|
519
|
+
const end = length === void 0 ? reader.len : reader.pos + length;
|
|
520
|
+
const message = createBaseBidderOutcomeEntry();
|
|
521
|
+
while (reader.pos < end) {
|
|
522
|
+
const tag = reader.uint32();
|
|
523
|
+
switch (tag >>> 3) {
|
|
524
|
+
case 1: {
|
|
525
|
+
if (tag !== 10) {
|
|
526
|
+
break;
|
|
527
|
+
}
|
|
528
|
+
message.bidder = reader.string();
|
|
529
|
+
continue;
|
|
530
|
+
}
|
|
531
|
+
case 2: {
|
|
532
|
+
if (tag !== 18) {
|
|
533
|
+
break;
|
|
534
|
+
}
|
|
535
|
+
message.adUnitCode = reader.string();
|
|
536
|
+
continue;
|
|
537
|
+
}
|
|
538
|
+
case 3: {
|
|
539
|
+
if (tag !== 26) {
|
|
540
|
+
break;
|
|
541
|
+
}
|
|
542
|
+
message.mediaType = reader.string();
|
|
543
|
+
continue;
|
|
544
|
+
}
|
|
545
|
+
case 4: {
|
|
546
|
+
if (tag !== 32) {
|
|
547
|
+
break;
|
|
548
|
+
}
|
|
549
|
+
message.outcome = reader.int32();
|
|
550
|
+
continue;
|
|
551
|
+
}
|
|
552
|
+
case 5: {
|
|
553
|
+
if (tag !== 41) {
|
|
554
|
+
break;
|
|
555
|
+
}
|
|
556
|
+
message.latencyMs = reader.double();
|
|
557
|
+
continue;
|
|
558
|
+
}
|
|
559
|
+
}
|
|
560
|
+
if ((tag & 7) === 4 || tag === 0) {
|
|
561
|
+
break;
|
|
562
|
+
}
|
|
563
|
+
reader.skip(tag & 7);
|
|
564
|
+
}
|
|
565
|
+
return message;
|
|
566
|
+
},
|
|
567
|
+
create(base) {
|
|
568
|
+
return BidderOutcomeEntry.fromPartial(base ?? {});
|
|
569
|
+
},
|
|
570
|
+
fromPartial(object) {
|
|
571
|
+
const message = createBaseBidderOutcomeEntry();
|
|
572
|
+
message.bidder = object.bidder ?? "";
|
|
573
|
+
message.adUnitCode = object.adUnitCode ?? "";
|
|
574
|
+
message.mediaType = object.mediaType ?? "";
|
|
575
|
+
message.outcome = object.outcome ?? 0;
|
|
576
|
+
message.latencyMs = object.latencyMs ?? 0;
|
|
577
|
+
return message;
|
|
578
|
+
}
|
|
579
|
+
};
|
|
471
580
|
function createBaseBidTrace() {
|
|
472
581
|
return {
|
|
473
582
|
bidder: "",
|
|
@@ -628,6 +737,8 @@ var MAX_QUEUE_SIZE = 200;
|
|
|
628
737
|
var MAX_SEND_BACKOFF_MS = 5 * 60 * 1e3;
|
|
629
738
|
var MAX_CONSECUTIVE_SEND_FAILURES = 10;
|
|
630
739
|
var MAX_PAYLOAD_BYTES = 32 * 1024;
|
|
740
|
+
var MAX_METADATA_VALUE_CHARS = 1024;
|
|
741
|
+
var MAX_METADATA_ENTRIES = 16;
|
|
631
742
|
var SAFE_CONTENT_TYPE = "text/plain";
|
|
632
743
|
var PENDING_BATCH_KEY_PREFIX = "_bidkernel_pending_";
|
|
633
744
|
var MAX_PENDING_BATCHES = 20;
|
|
@@ -636,11 +747,8 @@ var PENDING_BATCH_CLEANUP_DELAY_MS = 1e4;
|
|
|
636
747
|
var EVENT_NAME_TO_TYPE = {
|
|
637
748
|
auctionStart: TraceEventType.AUCTION_START,
|
|
638
749
|
auctionEnd: TraceEventType.AUCTION_END,
|
|
639
|
-
bidRequest: TraceEventType.BID_REQUEST,
|
|
640
750
|
bidResponse: TraceEventType.BID_RESPONSE,
|
|
641
|
-
bidTimeout: TraceEventType.BID_TIMEOUT,
|
|
642
751
|
bidWin: TraceEventType.BID_WIN,
|
|
643
|
-
noBid: TraceEventType.NO_BID,
|
|
644
752
|
adRenderFailed: TraceEventType.AD_RENDER_FAILED,
|
|
645
753
|
click: TraceEventType.CLICK,
|
|
646
754
|
impression: TraceEventType.IMPRESSION,
|
|
@@ -653,6 +761,10 @@ var IMMEDIATE_FLUSH_TYPES = /* @__PURE__ */ new Set([
|
|
|
653
761
|
TraceEventType.BID_WIN,
|
|
654
762
|
TraceEventType.CLICK
|
|
655
763
|
]);
|
|
764
|
+
var CACHED_BID_TTL_MS = 30 * 60 * 1e3;
|
|
765
|
+
var MAX_CACHED_BID_KEYS = 200;
|
|
766
|
+
var MAX_IMPRESSION_KEYS = 1e3;
|
|
767
|
+
var MAX_TRACKED_AUCTIONS = 50;
|
|
656
768
|
var SESSION_KEY = "_bidkernel_session";
|
|
657
769
|
var SESSION_TS_KEY = "_bidkernel_session_ts";
|
|
658
770
|
var THIRTY_MINUTES_MS = 30 * 60 * 1e3;
|
|
@@ -684,6 +796,46 @@ function base64ToBytes(b64) {
|
|
|
684
796
|
}
|
|
685
797
|
return bytes;
|
|
686
798
|
}
|
|
799
|
+
function isTrustedEndpoint(raw) {
|
|
800
|
+
if (!raw) return false;
|
|
801
|
+
let url;
|
|
802
|
+
try {
|
|
803
|
+
url = new URL(raw);
|
|
804
|
+
} catch {
|
|
805
|
+
return false;
|
|
806
|
+
}
|
|
807
|
+
if (url.username || url.password) return false;
|
|
808
|
+
if (url.protocol === "https:") return true;
|
|
809
|
+
if (url.protocol === "http:") {
|
|
810
|
+
return url.hostname === "localhost" || url.hostname === "127.0.0.1" || url.hostname === "[::1]";
|
|
811
|
+
}
|
|
812
|
+
return false;
|
|
813
|
+
}
|
|
814
|
+
function boundMetadata(metadata) {
|
|
815
|
+
const bounded = {};
|
|
816
|
+
let count = 0;
|
|
817
|
+
for (const key of Object.keys(metadata)) {
|
|
818
|
+
if (count >= MAX_METADATA_ENTRIES) break;
|
|
819
|
+
const value = metadata[key];
|
|
820
|
+
if (value === void 0 || value === null) continue;
|
|
821
|
+
const str = String(value);
|
|
822
|
+
bounded[key] = str.length > MAX_METADATA_VALUE_CHARS ? `${str.slice(0, MAX_METADATA_VALUE_CHARS)}...[truncated]` : str;
|
|
823
|
+
count++;
|
|
824
|
+
}
|
|
825
|
+
return bounded;
|
|
826
|
+
}
|
|
827
|
+
function isLegacyPendingKey(key) {
|
|
828
|
+
const parts = key.slice(PENDING_BATCH_KEY_PREFIX.length).split("_");
|
|
829
|
+
return parts.length === 2 && /^\d+$/.test(parts[0]);
|
|
830
|
+
}
|
|
831
|
+
function sameEndpointOrigin(a, b) {
|
|
832
|
+
if (!a || !b) return false;
|
|
833
|
+
try {
|
|
834
|
+
return new URL(a).origin === new URL(b).origin;
|
|
835
|
+
} catch {
|
|
836
|
+
return false;
|
|
837
|
+
}
|
|
838
|
+
}
|
|
687
839
|
function extendSession() {
|
|
688
840
|
const now = Date.now();
|
|
689
841
|
inMemorySessionTs = now;
|
|
@@ -800,22 +952,6 @@ var PrebidEventDeduper = class {
|
|
|
800
952
|
return false;
|
|
801
953
|
}
|
|
802
954
|
};
|
|
803
|
-
function parseSize(raw) {
|
|
804
|
-
if (!raw) return { width: 0, height: 0 };
|
|
805
|
-
let cur = raw;
|
|
806
|
-
while (Array.isArray(cur) && cur.length > 0 && Array.isArray(cur[0])) {
|
|
807
|
-
cur = cur[0];
|
|
808
|
-
}
|
|
809
|
-
if (Array.isArray(cur) && cur.length >= 2) {
|
|
810
|
-
const w = Number(cur[0]);
|
|
811
|
-
const h = Number(cur[1]);
|
|
812
|
-
return {
|
|
813
|
-
width: Number.isFinite(w) ? w : 0,
|
|
814
|
-
height: Number.isFinite(h) ? h : 0
|
|
815
|
-
};
|
|
816
|
-
}
|
|
817
|
-
return { width: 0, height: 0 };
|
|
818
|
-
}
|
|
819
955
|
function parseBidDimensions(bid) {
|
|
820
956
|
if (Number.isFinite(bid?.width) && Number.isFinite(bid?.height)) {
|
|
821
957
|
return { width: bid.width, height: bid.height };
|
|
@@ -867,9 +1003,13 @@ var BidkernelPrebidAnalytics = class _BidkernelPrebidAnalytics {
|
|
|
867
1003
|
cachedWinningBids = /* @__PURE__ */ new Map();
|
|
868
1004
|
// Active video player attachment cleanup routines
|
|
869
1005
|
videoDetachCleanups = /* @__PURE__ */ new Set();
|
|
1006
|
+
// Compacted bidder participation map per auction: auctionId -> Map<`${bidder}:${adUnitCode}`, BidderOutcomeEntry>
|
|
1007
|
+
auctionBidderOutcomes = /* @__PURE__ */ new Map();
|
|
870
1008
|
constructor(config) {
|
|
1009
|
+
const requestedEndpoint = config.endpoint || "";
|
|
1010
|
+
const endpoint = !requestedEndpoint || isTrustedEndpoint(requestedEndpoint) ? requestedEndpoint : "";
|
|
871
1011
|
this.config = {
|
|
872
|
-
endpoint
|
|
1012
|
+
endpoint,
|
|
873
1013
|
propertyId: config.propertyId || "",
|
|
874
1014
|
pageviewId: config.pageviewId || generateUUID(),
|
|
875
1015
|
sessionId: config.sessionId || getOrCreateSessionId(),
|
|
@@ -885,6 +1025,12 @@ var BidkernelPrebidAnalytics = class _BidkernelPrebidAnalytics {
|
|
|
885
1025
|
pbjsGlobalName: config.pbjsGlobalName || "pbjs",
|
|
886
1026
|
attachPbjsListeners: config.attachPbjsListeners ?? true
|
|
887
1027
|
};
|
|
1028
|
+
if (requestedEndpoint && !endpoint) {
|
|
1029
|
+
this.log(
|
|
1030
|
+
"ERROR",
|
|
1031
|
+
`Rejected untrusted analytics endpoint ${requestedEndpoint}: must be an absolute https:// URL without embedded credentials. Events will not be transmitted.`
|
|
1032
|
+
);
|
|
1033
|
+
}
|
|
888
1034
|
this.boundFlushBeacon = () => {
|
|
889
1035
|
this.flushAllSlotsTimeInView();
|
|
890
1036
|
this.flushBeacon();
|
|
@@ -1022,6 +1168,7 @@ var BidkernelPrebidAnalytics = class _BidkernelPrebidAnalytics {
|
|
|
1022
1168
|
this.cachedWinningBids.clear();
|
|
1023
1169
|
this.slotEmittedImpressionKeys.clear();
|
|
1024
1170
|
this.slotLastAuctionIds.clear();
|
|
1171
|
+
this.auctionBidderOutcomes.clear();
|
|
1025
1172
|
if (this.flushTimer) {
|
|
1026
1173
|
clearInterval(this.flushTimer);
|
|
1027
1174
|
this.flushTimer = null;
|
|
@@ -1425,7 +1572,73 @@ var BidkernelPrebidAnalytics = class _BidkernelPrebidAnalytics {
|
|
|
1425
1572
|
this.slotEmittedImpressionKeys.delete(key);
|
|
1426
1573
|
}
|
|
1427
1574
|
}
|
|
1428
|
-
this.cachedWinningBids.
|
|
1575
|
+
const cached = this.cachedWinningBids.get(slotId);
|
|
1576
|
+
if (cached) this.deleteCachedBidEntry(cached);
|
|
1577
|
+
}
|
|
1578
|
+
// --- Bounded caches -------------------------------------------------------
|
|
1579
|
+
/**
|
|
1580
|
+
* Reads a cached winning bid, treating an entry past CACHED_BID_TTL_MS as
|
|
1581
|
+
* absent and dropping it. Every read goes through here so a stale bid can
|
|
1582
|
+
* never be married to a much later render.
|
|
1583
|
+
*/
|
|
1584
|
+
getCachedBid(key) {
|
|
1585
|
+
const entry = this.cachedWinningBids.get(key);
|
|
1586
|
+
if (!entry) return void 0;
|
|
1587
|
+
if (Date.now() - entry.timestamp > CACHED_BID_TTL_MS) {
|
|
1588
|
+
this.deleteCachedBidEntry(entry);
|
|
1589
|
+
return void 0;
|
|
1590
|
+
}
|
|
1591
|
+
return entry;
|
|
1592
|
+
}
|
|
1593
|
+
/** Removes every alias pointing at one entry. Aliases are stored by identity. */
|
|
1594
|
+
deleteCachedBidEntry(entry) {
|
|
1595
|
+
for (const [key, value] of Array.from(this.cachedWinningBids)) {
|
|
1596
|
+
if (value === entry) this.cachedWinningBids.delete(key);
|
|
1597
|
+
}
|
|
1598
|
+
}
|
|
1599
|
+
/**
|
|
1600
|
+
* Drops expired entries, then evicts oldest-first until the key cap holds.
|
|
1601
|
+
* Map iterates in insertion order and one win's aliases are written
|
|
1602
|
+
* consecutively, so eviction takes a whole bid's aliases together.
|
|
1603
|
+
*/
|
|
1604
|
+
pruneCachedWinningBids() {
|
|
1605
|
+
const now = Date.now();
|
|
1606
|
+
for (const [key, entry] of Array.from(this.cachedWinningBids)) {
|
|
1607
|
+
if (now - entry.timestamp > CACHED_BID_TTL_MS) this.cachedWinningBids.delete(key);
|
|
1608
|
+
}
|
|
1609
|
+
while (this.cachedWinningBids.size > MAX_CACHED_BID_KEYS) {
|
|
1610
|
+
const oldest = this.cachedWinningBids.keys().next();
|
|
1611
|
+
if (oldest.done) break;
|
|
1612
|
+
this.cachedWinningBids.delete(oldest.value);
|
|
1613
|
+
}
|
|
1614
|
+
}
|
|
1615
|
+
/** Records a dedup key, evicting oldest-first at the cap. */
|
|
1616
|
+
addImpressionKey(key) {
|
|
1617
|
+
if (this.slotEmittedImpressionKeys.has(key)) return;
|
|
1618
|
+
while (this.slotEmittedImpressionKeys.size >= MAX_IMPRESSION_KEYS) {
|
|
1619
|
+
const oldest = this.slotEmittedImpressionKeys.values().next();
|
|
1620
|
+
if (oldest.done) break;
|
|
1621
|
+
this.slotEmittedImpressionKeys.delete(oldest.value);
|
|
1622
|
+
}
|
|
1623
|
+
this.slotEmittedImpressionKeys.add(key);
|
|
1624
|
+
}
|
|
1625
|
+
/**
|
|
1626
|
+
* The bidder-outcome map for an auction, creating it if absent. Evicts the
|
|
1627
|
+
* oldest auction at the cap: without this, any auction whose auctionEnd never
|
|
1628
|
+
* fires (abandoned, navigated away from mid-flight) leaks its map forever.
|
|
1629
|
+
*/
|
|
1630
|
+
getOrCreateAuctionOutcomes(auctionId) {
|
|
1631
|
+
let auctionMap = this.auctionBidderOutcomes.get(auctionId);
|
|
1632
|
+
if (!auctionMap) {
|
|
1633
|
+
auctionMap = /* @__PURE__ */ new Map();
|
|
1634
|
+
this.auctionBidderOutcomes.set(auctionId, auctionMap);
|
|
1635
|
+
}
|
|
1636
|
+
while (this.auctionBidderOutcomes.size > MAX_TRACKED_AUCTIONS) {
|
|
1637
|
+
const oldest = this.auctionBidderOutcomes.keys().next();
|
|
1638
|
+
if (oldest.done || oldest.value === auctionId) break;
|
|
1639
|
+
this.auctionBidderOutcomes.delete(oldest.value);
|
|
1640
|
+
}
|
|
1641
|
+
return auctionMap;
|
|
1429
1642
|
}
|
|
1430
1643
|
hasImpressionEmitted(slotId, refreshIndex, auctionId, creativeId) {
|
|
1431
1644
|
const rIndex = refreshIndex ?? this.slotRefreshIndices.get(slotId) ?? 0;
|
|
@@ -1445,14 +1658,14 @@ var BidkernelPrebidAnalytics = class _BidkernelPrebidAnalytics {
|
|
|
1445
1658
|
}
|
|
1446
1659
|
markImpressionEmitted(slotId, refreshIndex, auctionId, creativeId) {
|
|
1447
1660
|
const baseSlotKey = `${escapeKeyPart(slotId)}:${refreshIndex}`;
|
|
1448
|
-
this.
|
|
1661
|
+
this.addImpressionKey(baseSlotKey);
|
|
1449
1662
|
if (creativeId) {
|
|
1450
|
-
this.
|
|
1663
|
+
this.addImpressionKey(`${baseSlotKey}:${escapeKeyPart(creativeId)}`);
|
|
1451
1664
|
}
|
|
1452
1665
|
if (auctionId) {
|
|
1453
|
-
this.
|
|
1666
|
+
this.addImpressionKey(`${baseSlotKey}:auc:${escapeKeyPart(auctionId)}`);
|
|
1454
1667
|
if (creativeId) {
|
|
1455
|
-
this.
|
|
1668
|
+
this.addImpressionKey(
|
|
1456
1669
|
`${baseSlotKey}:auc:${escapeKeyPart(auctionId)}:${escapeKeyPart(creativeId)}`
|
|
1457
1670
|
);
|
|
1458
1671
|
}
|
|
@@ -1462,7 +1675,7 @@ var BidkernelPrebidAnalytics = class _BidkernelPrebidAnalytics {
|
|
|
1462
1675
|
if (!this.isEnabled) return false;
|
|
1463
1676
|
const resolvedSlotId = slotId || options?.adUnitCode || "";
|
|
1464
1677
|
const adUnitCode = options?.adUnitCode || resolvedSlotId;
|
|
1465
|
-
const cached = (options?.auctionId && resolvedSlotId ? this.
|
|
1678
|
+
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);
|
|
1466
1679
|
const rawBid = options?.bid || cached?.rawBid || {};
|
|
1467
1680
|
const auctionId = options?.auctionId || rawBid.auctionId || cached?.auctionId || "";
|
|
1468
1681
|
const transactionId = options?.transactionId || rawBid.transactionId || cached?.transactionId || "";
|
|
@@ -1563,7 +1776,7 @@ var BidkernelPrebidAnalytics = class _BidkernelPrebidAnalytics {
|
|
|
1563
1776
|
const auctionId = options?.auctionId || "";
|
|
1564
1777
|
const transactionId = options?.transactionId || "";
|
|
1565
1778
|
const getWinningBid = () => {
|
|
1566
|
-
return (auctionId ? this.
|
|
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);
|
|
1567
1780
|
};
|
|
1568
1781
|
const onAdStartedOrImpression = (event) => {
|
|
1569
1782
|
this.log("DEBUG", "IMA AdEvent.STARTED / IMPRESSION received", event);
|
|
@@ -1727,7 +1940,7 @@ var BidkernelPrebidAnalytics = class _BidkernelPrebidAnalytics {
|
|
|
1727
1940
|
const auctionId = options?.auctionId || "";
|
|
1728
1941
|
const transactionId = options?.transactionId || "";
|
|
1729
1942
|
const getWinningBid = () => {
|
|
1730
|
-
return (auctionId ? this.
|
|
1943
|
+
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);
|
|
1731
1944
|
};
|
|
1732
1945
|
let videoEl = null;
|
|
1733
1946
|
if (el) {
|
|
@@ -1963,6 +2176,10 @@ var BidkernelPrebidAnalytics = class _BidkernelPrebidAnalytics {
|
|
|
1963
2176
|
refreshIndex: record.refreshIndex
|
|
1964
2177
|
};
|
|
1965
2178
|
}
|
|
2179
|
+
/** The validated ingest endpoint in use, or "" when none passed validation. */
|
|
2180
|
+
getEndpoint() {
|
|
2181
|
+
return this.config.endpoint;
|
|
2182
|
+
}
|
|
1966
2183
|
getRefreshIndex(slotId) {
|
|
1967
2184
|
return this.slotRefreshIndices.get(slotId) ?? 0;
|
|
1968
2185
|
}
|
|
@@ -2003,17 +2220,29 @@ var BidkernelPrebidAnalytics = class _BidkernelPrebidAnalytics {
|
|
|
2003
2220
|
handleAuctionInit(data) {
|
|
2004
2221
|
if (this.isDuplicate("auctionInit", data)) return;
|
|
2005
2222
|
this.log("DEBUG", "auctionInit", data);
|
|
2223
|
+
const auctionId = data.auctionId || "";
|
|
2224
|
+
if (auctionId) {
|
|
2225
|
+
this.auctionBidderOutcomes.delete(auctionId);
|
|
2226
|
+
this.getOrCreateAuctionOutcomes(auctionId);
|
|
2227
|
+
}
|
|
2006
2228
|
this.enqueue(TraceEventType.AUCTION_START, "auctionStart", {
|
|
2007
|
-
auctionId
|
|
2229
|
+
auctionId,
|
|
2008
2230
|
transactionId: data.transactionId || ""
|
|
2009
2231
|
});
|
|
2010
2232
|
}
|
|
2011
2233
|
handleAuctionEnd(data) {
|
|
2012
2234
|
if (this.isDuplicate("auctionEnd", data)) return;
|
|
2013
2235
|
this.log("DEBUG", "auctionEnd", data);
|
|
2236
|
+
const auctionId = data.auctionId || "";
|
|
2237
|
+
const outcomesMap = auctionId ? this.auctionBidderOutcomes.get(auctionId) : void 0;
|
|
2238
|
+
const bidderOutcomes = outcomesMap ? Array.from(outcomesMap.values()) : [];
|
|
2239
|
+
if (auctionId) {
|
|
2240
|
+
this.auctionBidderOutcomes.delete(auctionId);
|
|
2241
|
+
}
|
|
2014
2242
|
this.enqueue(TraceEventType.AUCTION_END, "auctionEnd", {
|
|
2015
|
-
auctionId
|
|
2016
|
-
transactionId: data.transactionId || ""
|
|
2243
|
+
auctionId,
|
|
2244
|
+
transactionId: data.transactionId || "",
|
|
2245
|
+
bidderOutcomes
|
|
2017
2246
|
});
|
|
2018
2247
|
}
|
|
2019
2248
|
handleBidRequested(data) {
|
|
@@ -2021,88 +2250,99 @@ var BidkernelPrebidAnalytics = class _BidkernelPrebidAnalytics {
|
|
|
2021
2250
|
this.log("DEBUG", "bidRequested", data);
|
|
2022
2251
|
const auctionId = data.auctionId || "";
|
|
2023
2252
|
const bidder = data.bidderCode || data.bidder || "";
|
|
2253
|
+
if (!auctionId || !bidder) return;
|
|
2254
|
+
const auctionMap = this.getOrCreateAuctionOutcomes(auctionId);
|
|
2024
2255
|
if (Array.isArray(data.bids)) {
|
|
2025
2256
|
data.bids.forEach((bid) => {
|
|
2257
|
+
const adUnitCode = bid.adUnitCode || "";
|
|
2026
2258
|
const mediaTypes = Object.keys(bid.mediaTypes || {});
|
|
2027
|
-
const
|
|
2028
|
-
const
|
|
2029
|
-
if (
|
|
2030
|
-
|
|
2031
|
-
|
|
2032
|
-
|
|
2033
|
-
|
|
2034
|
-
|
|
2035
|
-
|
|
2036
|
-
gpid,
|
|
2037
|
-
bid: {
|
|
2038
|
-
bidder,
|
|
2039
|
-
mediaType,
|
|
2040
|
-
width,
|
|
2041
|
-
height
|
|
2042
|
-
}
|
|
2259
|
+
const mediaType = mediaTypes.length > 0 ? mediaTypes[0] : bid.mediaType || "banner";
|
|
2260
|
+
const key = `${bidder}:${adUnitCode}`;
|
|
2261
|
+
if (!auctionMap.has(key)) {
|
|
2262
|
+
auctionMap.set(key, {
|
|
2263
|
+
bidder,
|
|
2264
|
+
adUnitCode,
|
|
2265
|
+
mediaType,
|
|
2266
|
+
outcome: BidderOutcome.BIDDER_OUTCOME_NO_RESPONSE,
|
|
2267
|
+
latencyMs: 0
|
|
2043
2268
|
});
|
|
2044
|
-
return;
|
|
2045
2269
|
}
|
|
2046
|
-
mediaTypes.forEach((mediaType) => {
|
|
2047
|
-
let rawSize = null;
|
|
2048
|
-
if (mediaType === "banner") {
|
|
2049
|
-
rawSize = bid.mediaTypes?.banner?.sizes || bid.sizes;
|
|
2050
|
-
} else if (mediaType === "video") {
|
|
2051
|
-
rawSize = bid.mediaTypes?.video?.playerSize || bid.mediaTypes?.video?.sizes || bid.playerSize;
|
|
2052
|
-
} else {
|
|
2053
|
-
rawSize = bid.mediaTypes?.[mediaType]?.sizes;
|
|
2054
|
-
}
|
|
2055
|
-
const { width, height } = parseSize(rawSize);
|
|
2056
|
-
this.enqueue(TraceEventType.BID_REQUEST, "bidRequest", {
|
|
2057
|
-
auctionId,
|
|
2058
|
-
transactionId,
|
|
2059
|
-
adUnitCode: bid.adUnitCode || "",
|
|
2060
|
-
gpid,
|
|
2061
|
-
bid: {
|
|
2062
|
-
bidder,
|
|
2063
|
-
mediaType,
|
|
2064
|
-
width,
|
|
2065
|
-
height
|
|
2066
|
-
}
|
|
2067
|
-
});
|
|
2068
|
-
});
|
|
2069
2270
|
});
|
|
2070
2271
|
}
|
|
2071
2272
|
}
|
|
2072
2273
|
handleBidResponse(data) {
|
|
2073
2274
|
if (this.isDuplicate("bidResponse", data)) return;
|
|
2074
2275
|
this.log("DEBUG", "bidResponse", data);
|
|
2075
|
-
|
|
2076
|
-
|
|
2077
|
-
|
|
2078
|
-
|
|
2079
|
-
|
|
2080
|
-
|
|
2081
|
-
|
|
2082
|
-
|
|
2083
|
-
|
|
2084
|
-
|
|
2085
|
-
|
|
2086
|
-
|
|
2087
|
-
|
|
2088
|
-
|
|
2276
|
+
const auctionId = data.auctionId || "";
|
|
2277
|
+
const bidder = data.bidderCode || data.bidder || "";
|
|
2278
|
+
const adUnitCode = data.adUnitCode || "";
|
|
2279
|
+
const cpm = Number.isFinite(data.originalCpm) ? data.originalCpm : Number.isFinite(data.cpm) ? data.cpm : 0;
|
|
2280
|
+
const latencyMs = Number.isFinite(data.timeToRespond) ? data.timeToRespond : 0;
|
|
2281
|
+
const mediaType = data.mediaType || "banner";
|
|
2282
|
+
if (auctionId && bidder) {
|
|
2283
|
+
const auctionMap = this.getOrCreateAuctionOutcomes(auctionId);
|
|
2284
|
+
const key = `${bidder}:${adUnitCode}`;
|
|
2285
|
+
const entry = auctionMap.get(key);
|
|
2286
|
+
const outcome = cpm > 0 ? BidderOutcome.BIDDER_OUTCOME_BID : BidderOutcome.BIDDER_OUTCOME_NO_BID;
|
|
2287
|
+
if (entry) {
|
|
2288
|
+
entry.outcome = outcome;
|
|
2289
|
+
entry.latencyMs = latencyMs;
|
|
2290
|
+
if (mediaType) entry.mediaType = mediaType;
|
|
2291
|
+
} else {
|
|
2292
|
+
auctionMap.set(key, {
|
|
2293
|
+
bidder,
|
|
2294
|
+
adUnitCode,
|
|
2295
|
+
mediaType,
|
|
2296
|
+
outcome,
|
|
2297
|
+
latencyMs
|
|
2298
|
+
});
|
|
2089
2299
|
}
|
|
2090
|
-
}
|
|
2300
|
+
}
|
|
2301
|
+
if (cpm > 0) {
|
|
2302
|
+
this.enqueue(TraceEventType.BID_RESPONSE, "bidResponse", {
|
|
2303
|
+
auctionId,
|
|
2304
|
+
transactionId: data.transactionId || "",
|
|
2305
|
+
adUnitCode,
|
|
2306
|
+
bid: {
|
|
2307
|
+
bidder,
|
|
2308
|
+
cpm,
|
|
2309
|
+
currency: data.originalCurrency ?? data.currency ?? "USD",
|
|
2310
|
+
...parseBidDimensions(data),
|
|
2311
|
+
dealId: data.dealId || "",
|
|
2312
|
+
mediaType,
|
|
2313
|
+
latencyMs,
|
|
2314
|
+
advertiserDomain: data.meta?.advertiserDomains?.[0] || "",
|
|
2315
|
+
creativeId: data.creativeId || ""
|
|
2316
|
+
}
|
|
2317
|
+
});
|
|
2318
|
+
}
|
|
2091
2319
|
}
|
|
2092
2320
|
handleBidTimeout(data) {
|
|
2093
2321
|
if (this.isDuplicate("bidTimeout", data)) return;
|
|
2094
2322
|
this.log("DEBUG", "bidTimeout", data);
|
|
2095
2323
|
const items = Array.isArray(data) ? data : data ? [data] : [];
|
|
2096
2324
|
for (const t of items) {
|
|
2097
|
-
|
|
2098
|
-
|
|
2099
|
-
|
|
2100
|
-
|
|
2101
|
-
|
|
2102
|
-
|
|
2103
|
-
|
|
2325
|
+
const auctionId = t.auctionId || "";
|
|
2326
|
+
const bidder = t.bidderCode || t.bidder || "";
|
|
2327
|
+
const adUnitCode = t.adUnitCode || "";
|
|
2328
|
+
const latencyMs = Number.isFinite(t.timeout) ? t.timeout : 0;
|
|
2329
|
+
if (auctionId && bidder) {
|
|
2330
|
+
const auctionMap = this.getOrCreateAuctionOutcomes(auctionId);
|
|
2331
|
+
const key = `${bidder}:${adUnitCode}`;
|
|
2332
|
+
const entry = auctionMap.get(key);
|
|
2333
|
+
if (entry) {
|
|
2334
|
+
entry.outcome = BidderOutcome.BIDDER_OUTCOME_TIMEOUT;
|
|
2335
|
+
if (latencyMs > 0) entry.latencyMs = latencyMs;
|
|
2336
|
+
} else {
|
|
2337
|
+
auctionMap.set(key, {
|
|
2338
|
+
bidder,
|
|
2339
|
+
adUnitCode,
|
|
2340
|
+
mediaType: "banner",
|
|
2341
|
+
outcome: BidderOutcome.BIDDER_OUTCOME_TIMEOUT,
|
|
2342
|
+
latencyMs
|
|
2343
|
+
});
|
|
2104
2344
|
}
|
|
2105
|
-
}
|
|
2345
|
+
}
|
|
2106
2346
|
}
|
|
2107
2347
|
}
|
|
2108
2348
|
handleBidWon(data) {
|
|
@@ -2141,6 +2381,7 @@ var BidkernelPrebidAnalytics = class _BidkernelPrebidAnalytics {
|
|
|
2141
2381
|
this.cachedWinningBids.set(`${auctionId}:${data.adId}`, cachedEntry);
|
|
2142
2382
|
}
|
|
2143
2383
|
}
|
|
2384
|
+
this.pruneCachedWinningBids();
|
|
2144
2385
|
}
|
|
2145
2386
|
this.enqueue(TraceEventType.BID_WIN, "bidWon", {
|
|
2146
2387
|
auctionId,
|
|
@@ -2152,14 +2393,27 @@ var BidkernelPrebidAnalytics = class _BidkernelPrebidAnalytics {
|
|
|
2152
2393
|
handleNoBid(data) {
|
|
2153
2394
|
if (this.isDuplicate("noBid", data)) return;
|
|
2154
2395
|
this.log("DEBUG", "noBid", data);
|
|
2155
|
-
|
|
2156
|
-
|
|
2157
|
-
|
|
2158
|
-
|
|
2159
|
-
|
|
2160
|
-
|
|
2396
|
+
const auctionId = data.auctionId || "";
|
|
2397
|
+
const bidder = data.bidderCode || data.bidder || "";
|
|
2398
|
+
const adUnitCode = data.adUnitCode || "";
|
|
2399
|
+
if (auctionId && bidder) {
|
|
2400
|
+
const auctionMap = this.getOrCreateAuctionOutcomes(auctionId);
|
|
2401
|
+
const key = `${bidder}:${adUnitCode}`;
|
|
2402
|
+
const entry = auctionMap.get(key);
|
|
2403
|
+
if (entry) {
|
|
2404
|
+
if (entry.outcome === BidderOutcome.BIDDER_OUTCOME_NO_RESPONSE) {
|
|
2405
|
+
entry.outcome = BidderOutcome.BIDDER_OUTCOME_NO_BID;
|
|
2406
|
+
}
|
|
2407
|
+
} else {
|
|
2408
|
+
auctionMap.set(key, {
|
|
2409
|
+
bidder,
|
|
2410
|
+
adUnitCode,
|
|
2411
|
+
mediaType: "banner",
|
|
2412
|
+
outcome: BidderOutcome.BIDDER_OUTCOME_NO_BID,
|
|
2413
|
+
latencyMs: 0
|
|
2414
|
+
});
|
|
2161
2415
|
}
|
|
2162
|
-
}
|
|
2416
|
+
}
|
|
2163
2417
|
}
|
|
2164
2418
|
handleAdRenderFailed(data) {
|
|
2165
2419
|
if (this.isDuplicate("adRenderFailed", data)) return;
|
|
@@ -2191,7 +2445,7 @@ var BidkernelPrebidAnalytics = class _BidkernelPrebidAnalytics {
|
|
|
2191
2445
|
const mediaType = bid.mediaType || "banner";
|
|
2192
2446
|
const slotKey = data.adUnitCode || bid.adUnitCode || "";
|
|
2193
2447
|
const altKey = data.adId || bid.adId || "";
|
|
2194
|
-
const cached = (auctionId && slotKey ? this.
|
|
2448
|
+
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);
|
|
2195
2449
|
const bidTrace = {
|
|
2196
2450
|
bidder: bid.bidderCode || bid.bidder || cached?.bidTrace?.bidder || "",
|
|
2197
2451
|
cpm: Number.isFinite(bid.originalCpm) ? bid.originalCpm : Number.isFinite(bid.cpm) ? bid.cpm : cached?.bidTrace?.cpm ?? 0,
|
|
@@ -2280,7 +2534,12 @@ var BidkernelPrebidAnalytics = class _BidkernelPrebidAnalytics {
|
|
|
2280
2534
|
}
|
|
2281
2535
|
if (!el && targetId) {
|
|
2282
2536
|
try {
|
|
2283
|
-
|
|
2537
|
+
for (const candidate of Array.from(document.querySelectorAll("[data-ad-slot]"))) {
|
|
2538
|
+
if (candidate.getAttribute("data-ad-slot") === targetId) {
|
|
2539
|
+
el = candidate;
|
|
2540
|
+
break;
|
|
2541
|
+
}
|
|
2542
|
+
}
|
|
2284
2543
|
} catch {
|
|
2285
2544
|
}
|
|
2286
2545
|
}
|
|
@@ -2334,6 +2593,9 @@ var BidkernelPrebidAnalytics = class _BidkernelPrebidAnalytics {
|
|
|
2334
2593
|
if (data.viewableDurationMs !== void 0) {
|
|
2335
2594
|
protoEvent.viewableDurationMs = Number.isFinite(data.viewableDurationMs) ? data.viewableDurationMs : 0;
|
|
2336
2595
|
}
|
|
2596
|
+
if (data.bidderOutcomes && Array.isArray(data.bidderOutcomes)) {
|
|
2597
|
+
protoEvent.bidderOutcomes = data.bidderOutcomes;
|
|
2598
|
+
}
|
|
2337
2599
|
const metadata = { ...data.metadata };
|
|
2338
2600
|
if (data.error !== void 0 && data.error !== null) {
|
|
2339
2601
|
metadata.error = typeof data.error === "object" && data.error.message ? String(data.error.message) : String(data.error);
|
|
@@ -2344,8 +2606,9 @@ var BidkernelPrebidAnalytics = class _BidkernelPrebidAnalytics {
|
|
|
2344
2606
|
if (data.refreshIndex !== void 0 && metadata.refresh_index === void 0) {
|
|
2345
2607
|
metadata.refresh_index = String(data.refreshIndex);
|
|
2346
2608
|
}
|
|
2347
|
-
|
|
2348
|
-
|
|
2609
|
+
const boundedMetadata = boundMetadata(metadata);
|
|
2610
|
+
if (Object.keys(boundedMetadata).length > 0) {
|
|
2611
|
+
protoEvent.metadata = boundedMetadata;
|
|
2349
2612
|
}
|
|
2350
2613
|
this.queue.push(protoEvent);
|
|
2351
2614
|
if (this.queue.length > MAX_QUEUE_SIZE) {
|
|
@@ -2383,7 +2646,6 @@ var BidkernelPrebidAnalytics = class _BidkernelPrebidAnalytics {
|
|
|
2383
2646
|
// Shared by flush() and flushBeacon().
|
|
2384
2647
|
drainBatch() {
|
|
2385
2648
|
if (this.queue.length === 0 || !this.config.endpoint) return null;
|
|
2386
|
-
let events = this.queue;
|
|
2387
2649
|
const domain = typeof window !== "undefined" ? window.location.hostname : "";
|
|
2388
2650
|
const buildBatch = (evts) => ({
|
|
2389
2651
|
propertyId: this.config.propertyId,
|
|
@@ -2397,25 +2659,37 @@ var BidkernelPrebidAnalytics = class _BidkernelPrebidAnalytics {
|
|
|
2397
2659
|
domain,
|
|
2398
2660
|
events: evts
|
|
2399
2661
|
});
|
|
2400
|
-
|
|
2401
|
-
|
|
2402
|
-
|
|
2403
|
-
|
|
2404
|
-
events = events.slice(0, Math.floor(events.length / 2));
|
|
2405
|
-
encoded = TraceEventBatch.encode(
|
|
2662
|
+
for (; ; ) {
|
|
2663
|
+
if (this.queue.length === 0) return null;
|
|
2664
|
+
let events = this.queue;
|
|
2665
|
+
let encoded = TraceEventBatch.encode(
|
|
2406
2666
|
buildBatch(events)
|
|
2407
2667
|
).finish();
|
|
2668
|
+
while (events.length > 1 && encoded.byteLength > MAX_PAYLOAD_BYTES) {
|
|
2669
|
+
events = events.slice(0, Math.floor(events.length / 2));
|
|
2670
|
+
encoded = TraceEventBatch.encode(
|
|
2671
|
+
buildBatch(events)
|
|
2672
|
+
).finish();
|
|
2673
|
+
}
|
|
2674
|
+
if (encoded.byteLength > MAX_PAYLOAD_BYTES) {
|
|
2675
|
+
const [dropped] = this.queue.splice(0, 1);
|
|
2676
|
+
this.log(
|
|
2677
|
+
"WARN",
|
|
2678
|
+
`Dropping oversized event ${dropped?.eventName || dropped?.eventId} (${encoded.byteLength} bytes exceeds the ${MAX_PAYLOAD_BYTES}-byte cap on its own)`
|
|
2679
|
+
);
|
|
2680
|
+
continue;
|
|
2681
|
+
}
|
|
2682
|
+
this.queue = this.queue.slice(events.length);
|
|
2683
|
+
return {
|
|
2684
|
+
url: `${this.config.endpoint}/${this.config.propertyId}`,
|
|
2685
|
+
// protobufjs types finish() as Uint8Array<ArrayBufferLike>; the buffer is
|
|
2686
|
+
// always a plain ArrayBuffer, so narrow for fetch/Blob compatibility.
|
|
2687
|
+
encoded,
|
|
2688
|
+
events
|
|
2689
|
+
};
|
|
2408
2690
|
}
|
|
2409
|
-
this.queue = this.queue.slice(events.length);
|
|
2410
|
-
return {
|
|
2411
|
-
url: `${this.config.endpoint}/${this.config.propertyId}`,
|
|
2412
|
-
// protobufjs types finish() as Uint8Array<ArrayBufferLike>; the buffer is
|
|
2413
|
-
// always a plain ArrayBuffer, so narrow for fetch/Blob compatibility.
|
|
2414
|
-
encoded,
|
|
2415
|
-
events
|
|
2416
|
-
};
|
|
2417
2691
|
}
|
|
2418
|
-
sendFetch(payload, useKeepalive = false, onDelivered) {
|
|
2692
|
+
sendFetch(payload, useKeepalive = false, onDelivered, isReplay = false) {
|
|
2419
2693
|
const fetchOpts = {
|
|
2420
2694
|
method: "POST",
|
|
2421
2695
|
// Safelisted content type: no CORS preflight (see SAFE_CONTENT_TYPE).
|
|
@@ -2432,15 +2706,17 @@ var BidkernelPrebidAnalytics = class _BidkernelPrebidAnalytics {
|
|
|
2432
2706
|
this.log("WARN", `Dropping batch due to non-retryable client error HTTP ${res.status}`);
|
|
2433
2707
|
return;
|
|
2434
2708
|
}
|
|
2435
|
-
this.handleSendFailure(payload.events);
|
|
2709
|
+
if (!isReplay) this.handleSendFailure(payload.events);
|
|
2436
2710
|
} else {
|
|
2437
|
-
|
|
2438
|
-
|
|
2711
|
+
if (!isReplay) {
|
|
2712
|
+
this.consecutiveSendFailures = 0;
|
|
2713
|
+
this.nextSendAllowedAt = 0;
|
|
2714
|
+
}
|
|
2439
2715
|
if (onDelivered) onDelivered();
|
|
2440
2716
|
}
|
|
2441
2717
|
}).catch((err) => {
|
|
2442
2718
|
this.log("ERROR", "Failed to send batch", err);
|
|
2443
|
-
this.handleSendFailure(payload.events);
|
|
2719
|
+
if (!isReplay) this.handleSendFailure(payload.events);
|
|
2444
2720
|
});
|
|
2445
2721
|
const proc = typeof globalThis !== "undefined" ? globalThis.process : void 0;
|
|
2446
2722
|
if (proc && typeof proc._tickCallback === "function") {
|
|
@@ -2496,16 +2772,23 @@ var BidkernelPrebidAnalytics = class _BidkernelPrebidAnalytics {
|
|
|
2496
2772
|
this.schedulePersistedCleanup();
|
|
2497
2773
|
}
|
|
2498
2774
|
// --- Exit-batch persistence -----------------------------------------------
|
|
2775
|
+
// Key prefix for this instance's property. encodeURIComponent keeps the
|
|
2776
|
+
// property segment free of the "_" the timestamp suffix is parsed against,
|
|
2777
|
+
// so a property ID containing one cannot forge another property's prefix.
|
|
2778
|
+
pendingKeyPrefix() {
|
|
2779
|
+
return `${PENDING_BATCH_KEY_PREFIX}${encodeURIComponent(this.config.propertyId)}_`;
|
|
2780
|
+
}
|
|
2499
2781
|
persistBatch(encoded) {
|
|
2500
2782
|
try {
|
|
2501
2783
|
if (typeof localStorage === "undefined") return null;
|
|
2784
|
+
if (!this.config.propertyId) return null;
|
|
2502
2785
|
let pendingCount = 0;
|
|
2503
2786
|
for (let i = 0; i < localStorage.length; i++) {
|
|
2504
2787
|
const k = localStorage.key(i);
|
|
2505
2788
|
if (k && k.startsWith(PENDING_BATCH_KEY_PREFIX)) pendingCount++;
|
|
2506
2789
|
}
|
|
2507
2790
|
if (pendingCount >= MAX_PENDING_BATCHES) return null;
|
|
2508
|
-
const key = `${
|
|
2791
|
+
const key = `${this.pendingKeyPrefix()}${Date.now()}_${Math.floor(Math.random() * 1e6)}`;
|
|
2509
2792
|
localStorage.setItem(key, bytesToBase64(encoded));
|
|
2510
2793
|
this.persistedBatchKeys.push(key);
|
|
2511
2794
|
return key;
|
|
@@ -2536,16 +2819,33 @@ var BidkernelPrebidAnalytics = class _BidkernelPrebidAnalytics {
|
|
|
2536
2819
|
}, PENDING_BATCH_CLEANUP_DELAY_MS);
|
|
2537
2820
|
}
|
|
2538
2821
|
// Resend batches a previous pageview persisted at exit but could not
|
|
2539
|
-
// confirm.
|
|
2540
|
-
//
|
|
2541
|
-
//
|
|
2822
|
+
// confirm. Only this property's own keys are claimed: batches belonging to
|
|
2823
|
+
// another property sharing this origin are left for that property's next
|
|
2824
|
+
// pageview, since resending them here would file them under this property.
|
|
2825
|
+
// Keys are claimed (removed) before sending so concurrent tabs don't
|
|
2826
|
+
// double-send; the batch bytes carry their original pageview, session, and
|
|
2827
|
+
// event timestamps, so late rows attribute correctly.
|
|
2542
2828
|
resendPersistedBatches() {
|
|
2543
2829
|
try {
|
|
2544
2830
|
if (typeof localStorage === "undefined" || !this.config.endpoint) return;
|
|
2831
|
+
if (!this.config.propertyId) return;
|
|
2832
|
+
const ownPrefix = this.pendingKeyPrefix();
|
|
2545
2833
|
const keys = [];
|
|
2834
|
+
const legacyKeys = [];
|
|
2546
2835
|
for (let i = 0; i < localStorage.length; i++) {
|
|
2547
2836
|
const k = localStorage.key(i);
|
|
2548
|
-
if (k
|
|
2837
|
+
if (!k || !k.startsWith(PENDING_BATCH_KEY_PREFIX)) continue;
|
|
2838
|
+
if (k.startsWith(ownPrefix)) {
|
|
2839
|
+
keys.push(k);
|
|
2840
|
+
} else if (isLegacyPendingKey(k)) {
|
|
2841
|
+
legacyKeys.push(k);
|
|
2842
|
+
}
|
|
2843
|
+
}
|
|
2844
|
+
for (const key of legacyKeys) {
|
|
2845
|
+
try {
|
|
2846
|
+
localStorage.removeItem(key);
|
|
2847
|
+
} catch {
|
|
2848
|
+
}
|
|
2549
2849
|
}
|
|
2550
2850
|
for (const key of keys) {
|
|
2551
2851
|
let value = null;
|
|
@@ -2556,7 +2856,7 @@ var BidkernelPrebidAnalytics = class _BidkernelPrebidAnalytics {
|
|
|
2556
2856
|
continue;
|
|
2557
2857
|
}
|
|
2558
2858
|
if (!value) continue;
|
|
2559
|
-
const ts = parseInt(key.slice(
|
|
2859
|
+
const ts = parseInt(key.slice(ownPrefix.length), 10);
|
|
2560
2860
|
if (!Number.isFinite(ts) || Date.now() - ts > PENDING_BATCH_MAX_AGE_MS) continue;
|
|
2561
2861
|
let encoded;
|
|
2562
2862
|
try {
|
|
@@ -2566,11 +2866,16 @@ var BidkernelPrebidAnalytics = class _BidkernelPrebidAnalytics {
|
|
|
2566
2866
|
}
|
|
2567
2867
|
if (encoded.byteLength === 0) continue;
|
|
2568
2868
|
this.log("DEBUG", `Resending persisted exit batch (${encoded.byteLength} bytes)`);
|
|
2569
|
-
this.sendFetch(
|
|
2570
|
-
|
|
2571
|
-
|
|
2572
|
-
|
|
2573
|
-
|
|
2869
|
+
this.sendFetch(
|
|
2870
|
+
{
|
|
2871
|
+
url: `${this.config.endpoint}/${this.config.propertyId}`,
|
|
2872
|
+
encoded,
|
|
2873
|
+
events: []
|
|
2874
|
+
},
|
|
2875
|
+
false,
|
|
2876
|
+
void 0,
|
|
2877
|
+
true
|
|
2878
|
+
);
|
|
2574
2879
|
}
|
|
2575
2880
|
} catch {
|
|
2576
2881
|
}
|
|
@@ -2595,11 +2900,13 @@ var BidkernelPrebidAnalytics = class _BidkernelPrebidAnalytics {
|
|
|
2595
2900
|
}
|
|
2596
2901
|
}
|
|
2597
2902
|
};
|
|
2598
|
-
function extractAnalyticsOptions(config) {
|
|
2903
|
+
function extractAnalyticsOptions(config, allowProviderless = true) {
|
|
2599
2904
|
if (!config) return null;
|
|
2600
2905
|
if (Array.isArray(config)) {
|
|
2601
|
-
const matched = config.find((item) => item?.provider === "bidkernel") ?? config.find(
|
|
2602
|
-
|
|
2906
|
+
const matched = config.find((item) => item?.provider === "bidkernel") ?? (allowProviderless ? config.find(
|
|
2907
|
+
(item) => item && typeof item === "object" && item.provider === void 0
|
|
2908
|
+
) : void 0);
|
|
2909
|
+
return matched ? extractAnalyticsOptions(matched, allowProviderless) : null;
|
|
2603
2910
|
}
|
|
2604
2911
|
if (typeof config === "object") {
|
|
2605
2912
|
if (config.options && typeof config.options === "object") {
|
|
@@ -2641,18 +2948,27 @@ function registerPrebidAnalytics(pbjsGlobalName = "pbjs", defaults) {
|
|
|
2641
2948
|
win[pbjsGlobalName] = win[pbjsGlobalName] || {};
|
|
2642
2949
|
const pbjs = win[pbjsGlobalName];
|
|
2643
2950
|
const enableInstance = (options) => {
|
|
2644
|
-
|
|
2951
|
+
const previous = win._bidkernelPrebidAnalytics;
|
|
2952
|
+
const pinnedEndpoint = previous?.getEndpoint?.() || "";
|
|
2953
|
+
const merged = {
|
|
2954
|
+
pbjsGlobalName,
|
|
2955
|
+
...defaults,
|
|
2956
|
+
...options
|
|
2957
|
+
};
|
|
2958
|
+
if (pinnedEndpoint && !sameEndpointOrigin(merged.endpoint || "", pinnedEndpoint)) {
|
|
2959
|
+
console.warn(
|
|
2960
|
+
`[bidkernel] Ignoring analytics endpoint change to ${merged.endpoint || "(empty)"}: this page is pinned to ${pinnedEndpoint}. Reload to change the ingest endpoint.`
|
|
2961
|
+
);
|
|
2962
|
+
merged.endpoint = pinnedEndpoint;
|
|
2963
|
+
}
|
|
2964
|
+
if (previous) {
|
|
2645
2965
|
try {
|
|
2646
|
-
|
|
2966
|
+
previous.disable();
|
|
2647
2967
|
} catch (e) {
|
|
2648
2968
|
console.warn("[bidkernel] Failed to disable previous analytics instance:", e);
|
|
2649
2969
|
}
|
|
2650
2970
|
}
|
|
2651
|
-
const analytics = new BidkernelPrebidAnalytics(
|
|
2652
|
-
pbjsGlobalName,
|
|
2653
|
-
...defaults,
|
|
2654
|
-
...options
|
|
2655
|
-
});
|
|
2971
|
+
const analytics = new BidkernelPrebidAnalytics(merged);
|
|
2656
2972
|
analytics.enable();
|
|
2657
2973
|
win._bidkernelPrebidAnalytics = analytics;
|
|
2658
2974
|
return analytics;
|
|
@@ -2663,7 +2979,7 @@ function registerPrebidAnalytics(pbjsGlobalName = "pbjs", defaults) {
|
|
|
2663
2979
|
pbjs.adapterManager.registerAnalyticsAdapter({
|
|
2664
2980
|
adapter: {
|
|
2665
2981
|
enableAnalytics: (config) => {
|
|
2666
|
-
const options = extractAnalyticsOptions(config);
|
|
2982
|
+
const options = extractAnalyticsOptions(config, !Array.isArray(config));
|
|
2667
2983
|
if (options) {
|
|
2668
2984
|
enableInstance(options);
|
|
2669
2985
|
}
|
|
@@ -2728,5 +3044,6 @@ export {
|
|
|
2728
3044
|
PrebidEventDeduper,
|
|
2729
3045
|
getPrebidEventKey,
|
|
2730
3046
|
getbidkernel,
|
|
3047
|
+
isTrustedEndpoint,
|
|
2731
3048
|
registerPrebidAnalytics
|
|
2732
3049
|
};
|