@bidkernel/analytics 0.6.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 +17 -17
- package/dist/analytics.global.js +1 -1
- package/dist/index.d.mts +137 -1
- package/dist/index.d.ts +137 -1
- package/dist/index.js +1096 -195
- package/dist/index.mjs +1095 -195
- 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 };
|
|
@@ -889,10 +1026,21 @@ var BidkernelPrebidAnalytics = class _BidkernelPrebidAnalytics {
|
|
|
889
1026
|
slotViewabilityRecords = /* @__PURE__ */ new Map();
|
|
890
1027
|
elementToSlotId = /* @__PURE__ */ new Map();
|
|
891
1028
|
slotRefreshIndices = /* @__PURE__ */ new Map();
|
|
1029
|
+
slotLastAuctionIds = /* @__PURE__ */ new Map();
|
|
892
1030
|
pendingThresholdListeners = /* @__PURE__ */ new Map();
|
|
1031
|
+
// Impression deduplication per slot and refresh cycle (strictly 1 impression per cycle)
|
|
1032
|
+
slotEmittedImpressionKeys = /* @__PURE__ */ new Set();
|
|
1033
|
+
// Winning bid cache from bidWon to marry with subsequent render triggers (adRenderSucceeded, video, etc.)
|
|
1034
|
+
cachedWinningBids = /* @__PURE__ */ new Map();
|
|
1035
|
+
// Active video player attachment cleanup routines
|
|
1036
|
+
videoDetachCleanups = /* @__PURE__ */ new Set();
|
|
1037
|
+
// Compacted bidder participation map per auction: auctionId -> Map<`${bidder}:${adUnitCode}`, BidderOutcomeEntry>
|
|
1038
|
+
auctionBidderOutcomes = /* @__PURE__ */ new Map();
|
|
893
1039
|
constructor(config) {
|
|
1040
|
+
const requestedEndpoint = config.endpoint || "";
|
|
1041
|
+
const endpoint = !requestedEndpoint || isTrustedEndpoint(requestedEndpoint) ? requestedEndpoint : "";
|
|
894
1042
|
this.config = {
|
|
895
|
-
endpoint
|
|
1043
|
+
endpoint,
|
|
896
1044
|
propertyId: config.propertyId || "",
|
|
897
1045
|
pageviewId: config.pageviewId || generateUUID(),
|
|
898
1046
|
sessionId: config.sessionId || getOrCreateSessionId(),
|
|
@@ -908,6 +1056,12 @@ var BidkernelPrebidAnalytics = class _BidkernelPrebidAnalytics {
|
|
|
908
1056
|
pbjsGlobalName: config.pbjsGlobalName || "pbjs",
|
|
909
1057
|
attachPbjsListeners: config.attachPbjsListeners ?? true
|
|
910
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
|
+
}
|
|
911
1065
|
this.boundFlushBeacon = () => {
|
|
912
1066
|
this.flushAllSlotsTimeInView();
|
|
913
1067
|
this.flushBeacon();
|
|
@@ -1035,6 +1189,17 @@ var BidkernelPrebidAnalytics = class _BidkernelPrebidAnalytics {
|
|
|
1035
1189
|
}
|
|
1036
1190
|
this.slotViewabilityRecords.clear();
|
|
1037
1191
|
this.elementToSlotId.clear();
|
|
1192
|
+
for (const cleanup of this.videoDetachCleanups) {
|
|
1193
|
+
try {
|
|
1194
|
+
cleanup();
|
|
1195
|
+
} catch {
|
|
1196
|
+
}
|
|
1197
|
+
}
|
|
1198
|
+
this.videoDetachCleanups.clear();
|
|
1199
|
+
this.cachedWinningBids.clear();
|
|
1200
|
+
this.slotEmittedImpressionKeys.clear();
|
|
1201
|
+
this.slotLastAuctionIds.clear();
|
|
1202
|
+
this.auctionBidderOutcomes.clear();
|
|
1038
1203
|
if (this.flushTimer) {
|
|
1039
1204
|
clearInterval(this.flushTimer);
|
|
1040
1205
|
this.flushTimer = null;
|
|
@@ -1357,6 +1522,9 @@ var BidkernelPrebidAnalytics = class _BidkernelPrebidAnalytics {
|
|
|
1357
1522
|
if (options?.bid !== void 0) existing.bidPayload = options.bid;
|
|
1358
1523
|
if (options?.metadata !== void 0) existing.metadata = options.metadata;
|
|
1359
1524
|
}
|
|
1525
|
+
if (options?.auctionId) {
|
|
1526
|
+
this.slotLastAuctionIds.set(resolvedSlotId, options.auctionId);
|
|
1527
|
+
}
|
|
1360
1528
|
if (el) {
|
|
1361
1529
|
if (existing.element && existing.element !== el && this.intersectionObserver) {
|
|
1362
1530
|
this.intersectionObserver.unobserve(existing.element);
|
|
@@ -1369,6 +1537,9 @@ var BidkernelPrebidAnalytics = class _BidkernelPrebidAnalytics {
|
|
|
1369
1537
|
} else {
|
|
1370
1538
|
const initialRefreshIndex = options?.refreshIndex ?? this.slotRefreshIndices.get(resolvedSlotId) ?? 0;
|
|
1371
1539
|
this.slotRefreshIndices.set(resolvedSlotId, initialRefreshIndex);
|
|
1540
|
+
if (options?.auctionId) {
|
|
1541
|
+
this.slotLastAuctionIds.set(resolvedSlotId, options.auctionId);
|
|
1542
|
+
}
|
|
1372
1543
|
const listeners = this.pendingThresholdListeners.get(resolvedSlotId) || /* @__PURE__ */ new Set();
|
|
1373
1544
|
this.pendingThresholdListeners.delete(resolvedSlotId);
|
|
1374
1545
|
const record = {
|
|
@@ -1424,7 +1595,551 @@ var BidkernelPrebidAnalytics = class _BidkernelPrebidAnalytics {
|
|
|
1424
1595
|
this.unobserveSlot(slotId);
|
|
1425
1596
|
this.slotViewabilityRecords.delete(slotId);
|
|
1426
1597
|
this.slotRefreshIndices.delete(slotId);
|
|
1598
|
+
this.slotLastAuctionIds.delete(slotId);
|
|
1427
1599
|
this.pendingThresholdListeners.delete(slotId);
|
|
1600
|
+
const prefix = `${escapeKeyPart(slotId)}:`;
|
|
1601
|
+
for (const key of Array.from(this.slotEmittedImpressionKeys)) {
|
|
1602
|
+
if (key === slotId || key.startsWith(prefix)) {
|
|
1603
|
+
this.slotEmittedImpressionKeys.delete(key);
|
|
1604
|
+
}
|
|
1605
|
+
}
|
|
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;
|
|
1673
|
+
}
|
|
1674
|
+
hasImpressionEmitted(slotId, refreshIndex, auctionId, creativeId) {
|
|
1675
|
+
const rIndex = refreshIndex ?? this.slotRefreshIndices.get(slotId) ?? 0;
|
|
1676
|
+
const baseSlotKey = `${escapeKeyPart(slotId)}:${rIndex}`;
|
|
1677
|
+
if (creativeId) {
|
|
1678
|
+
if (auctionId) {
|
|
1679
|
+
return this.slotEmittedImpressionKeys.has(
|
|
1680
|
+
`${baseSlotKey}:auc:${escapeKeyPart(auctionId)}:${escapeKeyPart(creativeId)}`
|
|
1681
|
+
) || this.slotEmittedImpressionKeys.has(`${baseSlotKey}:${escapeKeyPart(creativeId)}`);
|
|
1682
|
+
}
|
|
1683
|
+
return this.slotEmittedImpressionKeys.has(`${baseSlotKey}:${escapeKeyPart(creativeId)}`);
|
|
1684
|
+
}
|
|
1685
|
+
if (auctionId) {
|
|
1686
|
+
return this.slotEmittedImpressionKeys.has(`${baseSlotKey}:auc:${escapeKeyPart(auctionId)}`);
|
|
1687
|
+
}
|
|
1688
|
+
return this.slotEmittedImpressionKeys.has(baseSlotKey);
|
|
1689
|
+
}
|
|
1690
|
+
markImpressionEmitted(slotId, refreshIndex, auctionId, creativeId) {
|
|
1691
|
+
const baseSlotKey = `${escapeKeyPart(slotId)}:${refreshIndex}`;
|
|
1692
|
+
this.addImpressionKey(baseSlotKey);
|
|
1693
|
+
if (creativeId) {
|
|
1694
|
+
this.addImpressionKey(`${baseSlotKey}:${escapeKeyPart(creativeId)}`);
|
|
1695
|
+
}
|
|
1696
|
+
if (auctionId) {
|
|
1697
|
+
this.addImpressionKey(`${baseSlotKey}:auc:${escapeKeyPart(auctionId)}`);
|
|
1698
|
+
if (creativeId) {
|
|
1699
|
+
this.addImpressionKey(
|
|
1700
|
+
`${baseSlotKey}:auc:${escapeKeyPart(auctionId)}:${escapeKeyPart(creativeId)}`
|
|
1701
|
+
);
|
|
1702
|
+
}
|
|
1703
|
+
}
|
|
1704
|
+
}
|
|
1705
|
+
recordImpression(slotId, options) {
|
|
1706
|
+
if (!this.isEnabled) return false;
|
|
1707
|
+
const resolvedSlotId = slotId || options?.adUnitCode || "";
|
|
1708
|
+
const adUnitCode = options?.adUnitCode || resolvedSlotId;
|
|
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);
|
|
1710
|
+
const rawBid = options?.bid || cached?.rawBid || {};
|
|
1711
|
+
const auctionId = options?.auctionId || rawBid.auctionId || cached?.auctionId || "";
|
|
1712
|
+
const transactionId = options?.transactionId || rawBid.transactionId || cached?.transactionId || "";
|
|
1713
|
+
const creativeId = rawBid.creativeId || cached?.bidTrace?.creativeId || options?.bid?.creativeId || "";
|
|
1714
|
+
const mediaType = options?.mediaType || rawBid.mediaType || cached?.bidTrace?.mediaType || "banner";
|
|
1715
|
+
const targetSlot = resolvedSlotId || adUnitCode;
|
|
1716
|
+
const rec = (resolvedSlotId ? this.slotViewabilityRecords.get(resolvedSlotId) : void 0) || (adUnitCode ? this.slotViewabilityRecords.get(adUnitCode) : void 0);
|
|
1717
|
+
const lastAuctionId = targetSlot ? this.slotLastAuctionIds.get(targetSlot) || (rec ? rec.auctionId : void 0) : void 0;
|
|
1718
|
+
const isDifferentAuction = Boolean(
|
|
1719
|
+
targetSlot && auctionId && lastAuctionId && lastAuctionId !== auctionId && options?.refreshIndex === void 0
|
|
1720
|
+
);
|
|
1721
|
+
const bidTrace = {
|
|
1722
|
+
bidder: rawBid.bidderCode || rawBid.bidder || cached?.bidTrace?.bidder || "",
|
|
1723
|
+
cpm: Number.isFinite(rawBid.originalCpm) ? rawBid.originalCpm : Number.isFinite(rawBid.cpm) ? rawBid.cpm : cached?.bidTrace?.cpm ?? 0,
|
|
1724
|
+
currency: rawBid.originalCurrency ?? rawBid.currency ?? cached?.bidTrace?.currency ?? "USD",
|
|
1725
|
+
...parseBidDimensions(rawBid).width ? parseBidDimensions(rawBid) : cached?.bidTrace ? { width: cached.bidTrace.width, height: cached.bidTrace.height } : parseBidDimensions(rawBid),
|
|
1726
|
+
dealId: rawBid.dealId || cached?.bidTrace?.dealId || "",
|
|
1727
|
+
mediaType,
|
|
1728
|
+
latencyMs: Number.isFinite(rawBid.timeToRespond) ? rawBid.timeToRespond : cached?.bidTrace?.latencyMs ?? 0,
|
|
1729
|
+
advertiserDomain: rawBid.meta?.advertiserDomains?.[0] || cached?.bidTrace?.advertiserDomain || "",
|
|
1730
|
+
creativeId: rawBid.creativeId || cached?.bidTrace?.creativeId || ""
|
|
1731
|
+
};
|
|
1732
|
+
if (isDifferentAuction && targetSlot) {
|
|
1733
|
+
this.flushSlotTimeInView(targetSlot);
|
|
1734
|
+
const nextRefreshIndex = (this.slotRefreshIndices.get(targetSlot) ?? (rec ? rec.refreshIndex : 0)) + 1;
|
|
1735
|
+
this.slotRefreshIndices.set(targetSlot, nextRefreshIndex);
|
|
1736
|
+
this.slotLastAuctionIds.set(targetSlot, auctionId);
|
|
1737
|
+
if (rec) {
|
|
1738
|
+
rec.refreshIndex = nextRefreshIndex;
|
|
1739
|
+
rec.auctionId = auctionId;
|
|
1740
|
+
rec.transactionId = transactionId;
|
|
1741
|
+
rec.bidPayload = bidTrace;
|
|
1742
|
+
rec.viewableFired = false;
|
|
1743
|
+
rec.accumulatedTimeInViewMs = 0;
|
|
1744
|
+
}
|
|
1745
|
+
this.enqueue(TraceEventType.REFRESH, "refresh", {
|
|
1746
|
+
auctionId,
|
|
1747
|
+
transactionId,
|
|
1748
|
+
adUnitCode: targetSlot,
|
|
1749
|
+
bid: bidTrace,
|
|
1750
|
+
metadata: {
|
|
1751
|
+
...options?.metadata,
|
|
1752
|
+
refresh_index: String(nextRefreshIndex)
|
|
1753
|
+
}
|
|
1754
|
+
});
|
|
1755
|
+
} else if (targetSlot) {
|
|
1756
|
+
if (auctionId) {
|
|
1757
|
+
this.slotLastAuctionIds.set(targetSlot, auctionId);
|
|
1758
|
+
}
|
|
1759
|
+
if (!this.slotRefreshIndices.has(targetSlot)) {
|
|
1760
|
+
this.slotRefreshIndices.set(targetSlot, 0);
|
|
1761
|
+
}
|
|
1762
|
+
}
|
|
1763
|
+
const currentRefreshIndex = options?.refreshIndex ?? (resolvedSlotId ? this.slotRefreshIndices.get(resolvedSlotId) ?? (adUnitCode ? this.slotRefreshIndices.get(adUnitCode) : void 0) ?? (rec ? rec.refreshIndex : 0) : 0);
|
|
1764
|
+
const alreadyEmitted = resolvedSlotId && this.hasImpressionEmitted(resolvedSlotId, currentRefreshIndex, auctionId, creativeId) || adUnitCode && adUnitCode !== resolvedSlotId && this.hasImpressionEmitted(adUnitCode, currentRefreshIndex, auctionId, creativeId);
|
|
1765
|
+
if (alreadyEmitted) {
|
|
1766
|
+
this.log(
|
|
1767
|
+
"DEBUG",
|
|
1768
|
+
`Impression already emitted for slot ${resolvedSlotId || adUnitCode} in cycle ${currentRefreshIndex}`
|
|
1769
|
+
);
|
|
1770
|
+
return false;
|
|
1771
|
+
}
|
|
1772
|
+
if (resolvedSlotId)
|
|
1773
|
+
this.markImpressionEmitted(resolvedSlotId, currentRefreshIndex, auctionId, creativeId);
|
|
1774
|
+
if (adUnitCode && adUnitCode !== resolvedSlotId)
|
|
1775
|
+
this.markImpressionEmitted(adUnitCode, currentRefreshIndex, auctionId, creativeId);
|
|
1776
|
+
this.enqueue(TraceEventType.IMPRESSION, "impression", {
|
|
1777
|
+
auctionId,
|
|
1778
|
+
transactionId,
|
|
1779
|
+
adUnitCode,
|
|
1780
|
+
bid: bidTrace,
|
|
1781
|
+
metadata: {
|
|
1782
|
+
...options?.metadata,
|
|
1783
|
+
refresh_index: String(currentRefreshIndex)
|
|
1784
|
+
}
|
|
1785
|
+
});
|
|
1786
|
+
if (rec) {
|
|
1787
|
+
if (!rec.bidPayload && bidTrace) rec.bidPayload = bidTrace;
|
|
1788
|
+
if (!rec.auctionId && auctionId) rec.auctionId = auctionId;
|
|
1789
|
+
if (!rec.transactionId && transactionId) rec.transactionId = transactionId;
|
|
1790
|
+
}
|
|
1791
|
+
return true;
|
|
1792
|
+
}
|
|
1793
|
+
/**
|
|
1794
|
+
* Bridges Google IMA SDK AdsManager events to Bidkernel analytics.
|
|
1795
|
+
* Defers IMPRESSION emission until AdEvent.STARTED (or IMPRESSION),
|
|
1796
|
+
* tracks milestones (FIRST_QUARTILE, MIDPOINT, THIRD_QUARTILE, COMPLETE),
|
|
1797
|
+
* and handles AD_ERROR.
|
|
1798
|
+
*/
|
|
1799
|
+
attachImaAdsManager(adsManager, options) {
|
|
1800
|
+
if (!adsManager || typeof adsManager.addEventListener !== "function") {
|
|
1801
|
+
this.log("WARN", "Invalid AdsManager passed to attachImaAdsManager");
|
|
1802
|
+
return () => {
|
|
1803
|
+
};
|
|
1804
|
+
}
|
|
1805
|
+
const slotId = options?.slotId || options?.adUnitCode || "video";
|
|
1806
|
+
const adUnitCode = options?.adUnitCode || slotId;
|
|
1807
|
+
const auctionId = options?.auctionId || "";
|
|
1808
|
+
const transactionId = options?.transactionId || "";
|
|
1809
|
+
const getWinningBid = () => {
|
|
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);
|
|
1811
|
+
};
|
|
1812
|
+
const onAdStartedOrImpression = (event) => {
|
|
1813
|
+
this.log("DEBUG", "IMA AdEvent.STARTED / IMPRESSION received", event);
|
|
1814
|
+
const ad = typeof event?.getAd === "function" ? event.getAd() : event?.ad;
|
|
1815
|
+
const adData = {};
|
|
1816
|
+
if (ad) {
|
|
1817
|
+
adData.creativeId = (typeof ad.getCreativeId === "function" ? ad.getCreativeId() : ad.creativeId) || "";
|
|
1818
|
+
adData.adId = (typeof ad.getAdId === "function" ? ad.getAdId() : ad.id) || "";
|
|
1819
|
+
adData.title = (typeof ad.getTitle === "function" ? ad.getTitle() : ad.title) || "";
|
|
1820
|
+
adData.duration = typeof ad.getDuration === "function" ? ad.getDuration() : ad.duration;
|
|
1821
|
+
adData.advertiserName = typeof ad.getAdvertiserName === "function" ? ad.getAdvertiserName() : "";
|
|
1822
|
+
}
|
|
1823
|
+
const cached = getWinningBid();
|
|
1824
|
+
const mergedBid = {
|
|
1825
|
+
...cached?.rawBid || options?.bid,
|
|
1826
|
+
mediaType: "video",
|
|
1827
|
+
creativeId: adData.creativeId || cached?.bidTrace?.creativeId || options?.bid?.creativeId || ""
|
|
1828
|
+
};
|
|
1829
|
+
const emitted = this.recordImpression(slotId, {
|
|
1830
|
+
adUnitCode,
|
|
1831
|
+
auctionId: auctionId || cached?.auctionId,
|
|
1832
|
+
transactionId: transactionId || cached?.transactionId,
|
|
1833
|
+
mediaType: "video",
|
|
1834
|
+
bid: mergedBid,
|
|
1835
|
+
metadata: {
|
|
1836
|
+
...options?.metadata,
|
|
1837
|
+
media_type: "video",
|
|
1838
|
+
...adData.title ? { ad_title: adData.title } : {},
|
|
1839
|
+
...Number.isFinite(adData.duration) ? { ad_duration_s: String(adData.duration) } : {}
|
|
1840
|
+
}
|
|
1841
|
+
});
|
|
1842
|
+
if (emitted && options?.onImpression) {
|
|
1843
|
+
try {
|
|
1844
|
+
options.onImpression(slotId, { ad, bid: mergedBid });
|
|
1845
|
+
} catch (e) {
|
|
1846
|
+
this.log("ERROR", "Error in onImpression callback", e);
|
|
1847
|
+
}
|
|
1848
|
+
}
|
|
1849
|
+
};
|
|
1850
|
+
const onMilestone = (milestone) => {
|
|
1851
|
+
this.log("DEBUG", `IMA AdEvent milestone: ${milestone}`);
|
|
1852
|
+
if (options?.onMilestone) {
|
|
1853
|
+
try {
|
|
1854
|
+
options.onMilestone(milestone, slotId);
|
|
1855
|
+
} catch (e) {
|
|
1856
|
+
this.log("ERROR", "Error in onMilestone callback", e);
|
|
1857
|
+
}
|
|
1858
|
+
}
|
|
1859
|
+
};
|
|
1860
|
+
const onAdClick = (event) => {
|
|
1861
|
+
this.log("DEBUG", "IMA AdEvent.CLICK received", event);
|
|
1862
|
+
const cached = getWinningBid();
|
|
1863
|
+
this.enqueue(TraceEventType.CLICK, "click", {
|
|
1864
|
+
auctionId: auctionId || cached?.auctionId,
|
|
1865
|
+
transactionId: transactionId || cached?.transactionId,
|
|
1866
|
+
adUnitCode,
|
|
1867
|
+
bid: cached?.bidTrace || options?.bid,
|
|
1868
|
+
metadata: {
|
|
1869
|
+
...options?.metadata,
|
|
1870
|
+
media_type: "video"
|
|
1871
|
+
}
|
|
1872
|
+
});
|
|
1873
|
+
};
|
|
1874
|
+
const onAdError = (event) => {
|
|
1875
|
+
this.log("WARN", "IMA AdErrorEvent received", event);
|
|
1876
|
+
const err = typeof event?.getError === "function" ? event.getError() : event?.error || event;
|
|
1877
|
+
const msg = (err && typeof err.getMessage === "function" ? err.getMessage() : err?.message) || String(err || "IMA ad error");
|
|
1878
|
+
const code = (err && typeof err.getErrorCode === "function" ? err.getErrorCode() : err?.code) || "";
|
|
1879
|
+
const cached = getWinningBid();
|
|
1880
|
+
this.enqueue(TraceEventType.AD_RENDER_FAILED, "adRenderFailed", {
|
|
1881
|
+
auctionId: auctionId || cached?.auctionId,
|
|
1882
|
+
transactionId: transactionId || cached?.transactionId,
|
|
1883
|
+
adUnitCode,
|
|
1884
|
+
bid: cached?.bidTrace || options?.bid,
|
|
1885
|
+
metadata: {
|
|
1886
|
+
...options?.metadata,
|
|
1887
|
+
reason: "ima_ad_error",
|
|
1888
|
+
message: msg,
|
|
1889
|
+
...code ? { ima_error_code: String(code) } : {}
|
|
1890
|
+
},
|
|
1891
|
+
error: typeof err === "object" ? err : new Error(msg)
|
|
1892
|
+
});
|
|
1893
|
+
if (options?.onError) {
|
|
1894
|
+
try {
|
|
1895
|
+
options.onError(err);
|
|
1896
|
+
} catch (e) {
|
|
1897
|
+
this.log("ERROR", "Error in onError callback", e);
|
|
1898
|
+
}
|
|
1899
|
+
}
|
|
1900
|
+
};
|
|
1901
|
+
const googleIma = typeof window !== "undefined" ? window.google?.ima : void 0;
|
|
1902
|
+
const adEventType = googleIma?.AdEvent?.Type || {};
|
|
1903
|
+
const adErrorEventType = googleIma?.AdErrorEvent?.Type || {};
|
|
1904
|
+
const listeners = [
|
|
1905
|
+
{ type: adEventType.STARTED || "started", handler: onAdStartedOrImpression },
|
|
1906
|
+
{ type: adEventType.IMPRESSION || "impression", handler: onAdStartedOrImpression },
|
|
1907
|
+
{
|
|
1908
|
+
type: adEventType.FIRST_QUARTILE || "firstQuartile",
|
|
1909
|
+
handler: () => onMilestone("firstQuartile")
|
|
1910
|
+
},
|
|
1911
|
+
{ type: adEventType.MIDPOINT || "midpoint", handler: () => onMilestone("midpoint") },
|
|
1912
|
+
{
|
|
1913
|
+
type: adEventType.THIRD_QUARTILE || "thirdQuartile",
|
|
1914
|
+
handler: () => onMilestone("thirdQuartile")
|
|
1915
|
+
},
|
|
1916
|
+
{ type: adEventType.COMPLETE || "complete", handler: () => onMilestone("complete") },
|
|
1917
|
+
{ type: adEventType.CLICK || "click", handler: onAdClick },
|
|
1918
|
+
{ type: adErrorEventType.AD_ERROR || "adError", handler: onAdError }
|
|
1919
|
+
];
|
|
1920
|
+
for (const { type, handler } of listeners) {
|
|
1921
|
+
try {
|
|
1922
|
+
adsManager.addEventListener(type, handler);
|
|
1923
|
+
} catch {
|
|
1924
|
+
}
|
|
1925
|
+
}
|
|
1926
|
+
const cleanup = () => {
|
|
1927
|
+
for (const { type, handler } of listeners) {
|
|
1928
|
+
try {
|
|
1929
|
+
adsManager.removeEventListener(type, handler);
|
|
1930
|
+
} catch {
|
|
1931
|
+
}
|
|
1932
|
+
}
|
|
1933
|
+
this.videoDetachCleanups.delete(cleanup);
|
|
1934
|
+
};
|
|
1935
|
+
this.videoDetachCleanups.add(cleanup);
|
|
1936
|
+
return cleanup;
|
|
1937
|
+
}
|
|
1938
|
+
/**
|
|
1939
|
+
* Attaches render hooks and viewability tracking to a video player.
|
|
1940
|
+
* Supports HTML5 <video> elements, container elements, or Google IMA AdsManager.
|
|
1941
|
+
*/
|
|
1942
|
+
attachVideoPlayer(target, options) {
|
|
1943
|
+
if (!target) {
|
|
1944
|
+
this.log("WARN", "attachVideoPlayer called with null/undefined target");
|
|
1945
|
+
return () => {
|
|
1946
|
+
};
|
|
1947
|
+
}
|
|
1948
|
+
let el = null;
|
|
1949
|
+
if (typeof target === "string") {
|
|
1950
|
+
if (typeof document !== "undefined") {
|
|
1951
|
+
try {
|
|
1952
|
+
el = document.querySelector(target) || document.getElementById(target);
|
|
1953
|
+
} catch {
|
|
1954
|
+
el = document.getElementById(target);
|
|
1955
|
+
}
|
|
1956
|
+
}
|
|
1957
|
+
} else if (typeof HTMLElement !== "undefined" && target instanceof HTMLElement) {
|
|
1958
|
+
el = target;
|
|
1959
|
+
} else if (target && typeof target === "object" && target.nodeType === 1) {
|
|
1960
|
+
el = target;
|
|
1961
|
+
} else if (
|
|
1962
|
+
// Check if target is an IMA AdsManager
|
|
1963
|
+
typeof target.addEventListener === "function" && (typeof target.init === "function" || typeof target.start === "function" || typeof target.getCuePoints === "function" || typeof target.getVolume === "function" || target.__imaAdsManager)
|
|
1964
|
+
) {
|
|
1965
|
+
return this.attachImaAdsManager(target, options);
|
|
1966
|
+
} else if (target && typeof target.addEventListener === "function") {
|
|
1967
|
+
el = target;
|
|
1968
|
+
}
|
|
1969
|
+
const slotId = options?.slotId || options?.adUnitCode || (el ? el.id : "") || "video";
|
|
1970
|
+
const adUnitCode = options?.adUnitCode || slotId;
|
|
1971
|
+
const auctionId = options?.auctionId || "";
|
|
1972
|
+
const transactionId = options?.transactionId || "";
|
|
1973
|
+
const getWinningBid = () => {
|
|
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);
|
|
1975
|
+
};
|
|
1976
|
+
let videoEl = null;
|
|
1977
|
+
if (el) {
|
|
1978
|
+
if (el.tagName && el.tagName.toLowerCase() === "video") {
|
|
1979
|
+
videoEl = el;
|
|
1980
|
+
} else {
|
|
1981
|
+
videoEl = el.querySelector("video");
|
|
1982
|
+
}
|
|
1983
|
+
}
|
|
1984
|
+
const quartilesFired = {
|
|
1985
|
+
q1: false,
|
|
1986
|
+
q2: false,
|
|
1987
|
+
q3: false,
|
|
1988
|
+
q4: false
|
|
1989
|
+
};
|
|
1990
|
+
const triggerPlaybackImpression = (activeVideo) => {
|
|
1991
|
+
const vEl = activeVideo || videoEl || (el?.querySelector ? el.querySelector("video") : null);
|
|
1992
|
+
const cached = getWinningBid();
|
|
1993
|
+
const mergedBid = {
|
|
1994
|
+
...cached?.rawBid || options?.bid,
|
|
1995
|
+
mediaType: "video"
|
|
1996
|
+
};
|
|
1997
|
+
const emitted = this.recordImpression(slotId, {
|
|
1998
|
+
adUnitCode,
|
|
1999
|
+
auctionId: auctionId || cached?.auctionId,
|
|
2000
|
+
transactionId: transactionId || cached?.transactionId,
|
|
2001
|
+
mediaType: "video",
|
|
2002
|
+
bid: mergedBid,
|
|
2003
|
+
metadata: {
|
|
2004
|
+
...options?.metadata,
|
|
2005
|
+
media_type: "video",
|
|
2006
|
+
...vEl && Number.isFinite(vEl.duration) ? { video_duration_s: String(Math.round(vEl.duration)) } : {}
|
|
2007
|
+
}
|
|
2008
|
+
});
|
|
2009
|
+
if (emitted && options?.onImpression) {
|
|
2010
|
+
try {
|
|
2011
|
+
options.onImpression(slotId, { element: vEl || el, bid: mergedBid });
|
|
2012
|
+
} catch (e) {
|
|
2013
|
+
this.log("ERROR", "Error in onImpression callback", e);
|
|
2014
|
+
}
|
|
2015
|
+
}
|
|
2016
|
+
};
|
|
2017
|
+
const handlePlaying = (e) => {
|
|
2018
|
+
this.log("DEBUG", "HTML5 video playing event received");
|
|
2019
|
+
const targetVideo = e?.target instanceof HTMLVideoElement ? e.target : videoEl;
|
|
2020
|
+
triggerPlaybackImpression(targetVideo);
|
|
2021
|
+
};
|
|
2022
|
+
const handleTimeUpdate = (e) => {
|
|
2023
|
+
const targetVideo = e?.target instanceof HTMLVideoElement ? e.target : videoEl;
|
|
2024
|
+
if (targetVideo && targetVideo.currentTime > 0) {
|
|
2025
|
+
triggerPlaybackImpression(targetVideo);
|
|
2026
|
+
if (Number.isFinite(targetVideo.duration) && targetVideo.duration > 0) {
|
|
2027
|
+
const progress = targetVideo.currentTime / targetVideo.duration;
|
|
2028
|
+
if (progress >= 0.25 && !quartilesFired.q1) {
|
|
2029
|
+
quartilesFired.q1 = true;
|
|
2030
|
+
options?.onMilestone?.("firstQuartile", slotId);
|
|
2031
|
+
}
|
|
2032
|
+
if (progress >= 0.5 && !quartilesFired.q2) {
|
|
2033
|
+
quartilesFired.q2 = true;
|
|
2034
|
+
options?.onMilestone?.("midpoint", slotId);
|
|
2035
|
+
}
|
|
2036
|
+
if (progress >= 0.75 && !quartilesFired.q3) {
|
|
2037
|
+
quartilesFired.q3 = true;
|
|
2038
|
+
options?.onMilestone?.("thirdQuartile", slotId);
|
|
2039
|
+
}
|
|
2040
|
+
}
|
|
2041
|
+
}
|
|
2042
|
+
};
|
|
2043
|
+
const handleEnded = () => {
|
|
2044
|
+
if (!quartilesFired.q4) {
|
|
2045
|
+
quartilesFired.q4 = true;
|
|
2046
|
+
options?.onMilestone?.("complete", slotId);
|
|
2047
|
+
}
|
|
2048
|
+
};
|
|
2049
|
+
const handleError = (e) => {
|
|
2050
|
+
const targetVideo = e?.target instanceof HTMLVideoElement ? e.target : videoEl;
|
|
2051
|
+
const err = targetVideo?.error || videoEl?.error;
|
|
2052
|
+
const cached = getWinningBid();
|
|
2053
|
+
const msg = err ? `HTML5 video error code ${err.code}: ${err.message}` : "Video playback error";
|
|
2054
|
+
this.log("WARN", msg);
|
|
2055
|
+
this.enqueue(TraceEventType.AD_RENDER_FAILED, "adRenderFailed", {
|
|
2056
|
+
auctionId: auctionId || cached?.auctionId,
|
|
2057
|
+
transactionId: transactionId || cached?.transactionId,
|
|
2058
|
+
adUnitCode,
|
|
2059
|
+
bid: cached?.bidTrace || options?.bid,
|
|
2060
|
+
metadata: {
|
|
2061
|
+
...options?.metadata,
|
|
2062
|
+
reason: "html5_video_error",
|
|
2063
|
+
message: msg,
|
|
2064
|
+
...err?.code ? { video_error_code: String(err.code) } : {}
|
|
2065
|
+
},
|
|
2066
|
+
error: err ? new Error(msg) : void 0
|
|
2067
|
+
});
|
|
2068
|
+
if (options?.onError) {
|
|
2069
|
+
try {
|
|
2070
|
+
options.onError(err);
|
|
2071
|
+
} catch (e2) {
|
|
2072
|
+
this.log("ERROR", "Error in onError callback", e2);
|
|
2073
|
+
}
|
|
2074
|
+
}
|
|
2075
|
+
};
|
|
2076
|
+
const attachListenersToVideo = (targetVideo) => {
|
|
2077
|
+
targetVideo.addEventListener("playing", handlePlaying);
|
|
2078
|
+
targetVideo.addEventListener("play", handlePlaying);
|
|
2079
|
+
targetVideo.addEventListener("timeupdate", handleTimeUpdate);
|
|
2080
|
+
targetVideo.addEventListener("ended", handleEnded);
|
|
2081
|
+
targetVideo.addEventListener("error", handleError);
|
|
2082
|
+
};
|
|
2083
|
+
const removeListenersFromVideo = (targetVideo) => {
|
|
2084
|
+
targetVideo.removeEventListener("playing", handlePlaying);
|
|
2085
|
+
targetVideo.removeEventListener("play", handlePlaying);
|
|
2086
|
+
targetVideo.removeEventListener("timeupdate", handleTimeUpdate);
|
|
2087
|
+
targetVideo.removeEventListener("ended", handleEnded);
|
|
2088
|
+
targetVideo.removeEventListener("error", handleError);
|
|
2089
|
+
};
|
|
2090
|
+
let mutationObserver = null;
|
|
2091
|
+
if (el && el !== videoEl) {
|
|
2092
|
+
el.addEventListener("playing", handlePlaying, true);
|
|
2093
|
+
el.addEventListener("play", handlePlaying, true);
|
|
2094
|
+
el.addEventListener("timeupdate", handleTimeUpdate, true);
|
|
2095
|
+
el.addEventListener("ended", handleEnded, true);
|
|
2096
|
+
el.addEventListener("error", handleError, true);
|
|
2097
|
+
}
|
|
2098
|
+
if (videoEl) {
|
|
2099
|
+
attachListenersToVideo(videoEl);
|
|
2100
|
+
}
|
|
2101
|
+
if (el && el.tagName?.toLowerCase() !== "video" && typeof MutationObserver !== "undefined") {
|
|
2102
|
+
mutationObserver = new MutationObserver(() => {
|
|
2103
|
+
const found = el.querySelector("video");
|
|
2104
|
+
if (found && found !== videoEl) {
|
|
2105
|
+
if (videoEl) removeListenersFromVideo(videoEl);
|
|
2106
|
+
videoEl = found;
|
|
2107
|
+
if (videoEl) attachListenersToVideo(videoEl);
|
|
2108
|
+
}
|
|
2109
|
+
});
|
|
2110
|
+
mutationObserver.observe(el, { childList: true, subtree: true });
|
|
2111
|
+
}
|
|
2112
|
+
const cachedInitial = getWinningBid();
|
|
2113
|
+
if (options?.trackViewability !== false && el) {
|
|
2114
|
+
this.observeSlot(el, slotId, {
|
|
2115
|
+
adUnitCode,
|
|
2116
|
+
auctionId: auctionId || cachedInitial?.auctionId,
|
|
2117
|
+
transactionId: transactionId || cachedInitial?.transactionId,
|
|
2118
|
+
mediaType: "video",
|
|
2119
|
+
bid: cachedInitial?.bidTrace || options?.bid,
|
|
2120
|
+
metadata: options?.metadata,
|
|
2121
|
+
emitRefreshEvent: false
|
|
2122
|
+
});
|
|
2123
|
+
}
|
|
2124
|
+
const cleanup = () => {
|
|
2125
|
+
if (mutationObserver) {
|
|
2126
|
+
mutationObserver.disconnect();
|
|
2127
|
+
mutationObserver = null;
|
|
2128
|
+
}
|
|
2129
|
+
if (el && el !== videoEl) {
|
|
2130
|
+
el.removeEventListener("playing", handlePlaying, true);
|
|
2131
|
+
el.removeEventListener("play", handlePlaying, true);
|
|
2132
|
+
el.removeEventListener("timeupdate", handleTimeUpdate, true);
|
|
2133
|
+
el.removeEventListener("ended", handleEnded, true);
|
|
2134
|
+
el.removeEventListener("error", handleError, true);
|
|
2135
|
+
}
|
|
2136
|
+
if (videoEl) {
|
|
2137
|
+
removeListenersFromVideo(videoEl);
|
|
2138
|
+
}
|
|
2139
|
+
this.videoDetachCleanups.delete(cleanup);
|
|
2140
|
+
};
|
|
2141
|
+
this.videoDetachCleanups.add(cleanup);
|
|
2142
|
+
return cleanup;
|
|
1428
2143
|
}
|
|
1429
2144
|
onTimeInViewThreshold(slotId, thresholdMs, callback) {
|
|
1430
2145
|
const listener = {
|
|
@@ -1492,6 +2207,10 @@ var BidkernelPrebidAnalytics = class _BidkernelPrebidAnalytics {
|
|
|
1492
2207
|
refreshIndex: record.refreshIndex
|
|
1493
2208
|
};
|
|
1494
2209
|
}
|
|
2210
|
+
/** The validated ingest endpoint in use, or "" when none passed validation. */
|
|
2211
|
+
getEndpoint() {
|
|
2212
|
+
return this.config.endpoint;
|
|
2213
|
+
}
|
|
1495
2214
|
getRefreshIndex(slotId) {
|
|
1496
2215
|
return this.slotRefreshIndices.get(slotId) ?? 0;
|
|
1497
2216
|
}
|
|
@@ -1532,17 +2251,29 @@ var BidkernelPrebidAnalytics = class _BidkernelPrebidAnalytics {
|
|
|
1532
2251
|
handleAuctionInit(data) {
|
|
1533
2252
|
if (this.isDuplicate("auctionInit", data)) return;
|
|
1534
2253
|
this.log("DEBUG", "auctionInit", data);
|
|
2254
|
+
const auctionId = data.auctionId || "";
|
|
2255
|
+
if (auctionId) {
|
|
2256
|
+
this.auctionBidderOutcomes.delete(auctionId);
|
|
2257
|
+
this.getOrCreateAuctionOutcomes(auctionId);
|
|
2258
|
+
}
|
|
1535
2259
|
this.enqueue(TraceEventType.AUCTION_START, "auctionStart", {
|
|
1536
|
-
auctionId
|
|
2260
|
+
auctionId,
|
|
1537
2261
|
transactionId: data.transactionId || ""
|
|
1538
2262
|
});
|
|
1539
2263
|
}
|
|
1540
2264
|
handleAuctionEnd(data) {
|
|
1541
2265
|
if (this.isDuplicate("auctionEnd", data)) return;
|
|
1542
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
|
+
}
|
|
1543
2273
|
this.enqueue(TraceEventType.AUCTION_END, "auctionEnd", {
|
|
1544
|
-
auctionId
|
|
1545
|
-
transactionId: data.transactionId || ""
|
|
2274
|
+
auctionId,
|
|
2275
|
+
transactionId: data.transactionId || "",
|
|
2276
|
+
bidderOutcomes
|
|
1546
2277
|
});
|
|
1547
2278
|
}
|
|
1548
2279
|
handleBidRequested(data) {
|
|
@@ -1550,121 +2281,170 @@ var BidkernelPrebidAnalytics = class _BidkernelPrebidAnalytics {
|
|
|
1550
2281
|
this.log("DEBUG", "bidRequested", data);
|
|
1551
2282
|
const auctionId = data.auctionId || "";
|
|
1552
2283
|
const bidder = data.bidderCode || data.bidder || "";
|
|
2284
|
+
if (!auctionId || !bidder) return;
|
|
2285
|
+
const auctionMap = this.getOrCreateAuctionOutcomes(auctionId);
|
|
1553
2286
|
if (Array.isArray(data.bids)) {
|
|
1554
2287
|
data.bids.forEach((bid) => {
|
|
2288
|
+
const adUnitCode = bid.adUnitCode || "";
|
|
1555
2289
|
const mediaTypes = Object.keys(bid.mediaTypes || {});
|
|
1556
|
-
const
|
|
1557
|
-
const
|
|
1558
|
-
if (
|
|
1559
|
-
|
|
1560
|
-
|
|
1561
|
-
|
|
1562
|
-
|
|
1563
|
-
|
|
1564
|
-
|
|
1565
|
-
gpid,
|
|
1566
|
-
bid: {
|
|
1567
|
-
bidder,
|
|
1568
|
-
mediaType,
|
|
1569
|
-
width,
|
|
1570
|
-
height
|
|
1571
|
-
}
|
|
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
|
|
1572
2299
|
});
|
|
1573
|
-
return;
|
|
1574
2300
|
}
|
|
1575
|
-
mediaTypes.forEach((mediaType) => {
|
|
1576
|
-
let rawSize = null;
|
|
1577
|
-
if (mediaType === "banner") {
|
|
1578
|
-
rawSize = bid.mediaTypes?.banner?.sizes || bid.sizes;
|
|
1579
|
-
} else if (mediaType === "video") {
|
|
1580
|
-
rawSize = bid.mediaTypes?.video?.playerSize || bid.mediaTypes?.video?.sizes || bid.playerSize;
|
|
1581
|
-
} else {
|
|
1582
|
-
rawSize = bid.mediaTypes?.[mediaType]?.sizes;
|
|
1583
|
-
}
|
|
1584
|
-
const { width, height } = parseSize(rawSize);
|
|
1585
|
-
this.enqueue(TraceEventType.BID_REQUEST, "bidRequest", {
|
|
1586
|
-
auctionId,
|
|
1587
|
-
transactionId,
|
|
1588
|
-
adUnitCode: bid.adUnitCode || "",
|
|
1589
|
-
gpid,
|
|
1590
|
-
bid: {
|
|
1591
|
-
bidder,
|
|
1592
|
-
mediaType,
|
|
1593
|
-
width,
|
|
1594
|
-
height
|
|
1595
|
-
}
|
|
1596
|
-
});
|
|
1597
|
-
});
|
|
1598
2301
|
});
|
|
1599
2302
|
}
|
|
1600
2303
|
}
|
|
1601
2304
|
handleBidResponse(data) {
|
|
1602
2305
|
if (this.isDuplicate("bidResponse", data)) return;
|
|
1603
2306
|
this.log("DEBUG", "bidResponse", data);
|
|
1604
|
-
|
|
1605
|
-
|
|
1606
|
-
|
|
1607
|
-
|
|
1608
|
-
|
|
1609
|
-
|
|
1610
|
-
|
|
1611
|
-
|
|
1612
|
-
|
|
1613
|
-
|
|
1614
|
-
|
|
1615
|
-
|
|
1616
|
-
|
|
1617
|
-
|
|
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
|
+
});
|
|
1618
2330
|
}
|
|
1619
|
-
}
|
|
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
|
+
}
|
|
1620
2350
|
}
|
|
1621
2351
|
handleBidTimeout(data) {
|
|
1622
2352
|
if (this.isDuplicate("bidTimeout", data)) return;
|
|
1623
2353
|
this.log("DEBUG", "bidTimeout", data);
|
|
1624
2354
|
const items = Array.isArray(data) ? data : data ? [data] : [];
|
|
1625
2355
|
for (const t of items) {
|
|
1626
|
-
|
|
1627
|
-
|
|
1628
|
-
|
|
1629
|
-
|
|
1630
|
-
|
|
1631
|
-
|
|
1632
|
-
|
|
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
|
+
});
|
|
1633
2375
|
}
|
|
1634
|
-
}
|
|
2376
|
+
}
|
|
1635
2377
|
}
|
|
1636
2378
|
}
|
|
1637
2379
|
handleBidWon(data) {
|
|
1638
2380
|
if (this.isDuplicate("bidWon", data)) return;
|
|
1639
2381
|
this.log("DEBUG", "bidWon", data);
|
|
1640
|
-
|
|
1641
|
-
|
|
1642
|
-
|
|
1643
|
-
|
|
1644
|
-
|
|
1645
|
-
|
|
1646
|
-
|
|
1647
|
-
|
|
1648
|
-
|
|
1649
|
-
|
|
1650
|
-
|
|
1651
|
-
|
|
1652
|
-
|
|
1653
|
-
|
|
2382
|
+
const auctionId = data.auctionId || "";
|
|
2383
|
+
const transactionId = data.transactionId || "";
|
|
2384
|
+
const adUnitCode = data.adUnitCode || data.adId || "";
|
|
2385
|
+
const bidTrace = {
|
|
2386
|
+
bidder: data.bidderCode || data.bidder || "",
|
|
2387
|
+
cpm: Number.isFinite(data.originalCpm) ? data.originalCpm : Number.isFinite(data.cpm) ? data.cpm : 0,
|
|
2388
|
+
currency: data.originalCurrency ?? data.currency ?? "USD",
|
|
2389
|
+
...parseBidDimensions(data),
|
|
2390
|
+
dealId: data.dealId || "",
|
|
2391
|
+
mediaType: data.mediaType || "banner",
|
|
2392
|
+
latencyMs: Number.isFinite(data.timeToRespond) ? data.timeToRespond : 0,
|
|
2393
|
+
advertiserDomain: data.meta?.advertiserDomains?.[0] || "",
|
|
2394
|
+
creativeId: data.creativeId || ""
|
|
2395
|
+
};
|
|
2396
|
+
if (adUnitCode) {
|
|
2397
|
+
const cachedEntry = {
|
|
2398
|
+
bidTrace,
|
|
2399
|
+
auctionId,
|
|
2400
|
+
transactionId,
|
|
2401
|
+
adUnitCode,
|
|
2402
|
+
rawBid: data,
|
|
2403
|
+
timestamp: Date.now()
|
|
2404
|
+
};
|
|
2405
|
+
this.cachedWinningBids.set(adUnitCode, cachedEntry);
|
|
2406
|
+
if (data.adId && data.adId !== adUnitCode) {
|
|
2407
|
+
this.cachedWinningBids.set(data.adId, cachedEntry);
|
|
1654
2408
|
}
|
|
2409
|
+
if (auctionId) {
|
|
2410
|
+
this.cachedWinningBids.set(`${auctionId}:${adUnitCode}`, cachedEntry);
|
|
2411
|
+
if (data.adId && data.adId !== adUnitCode) {
|
|
2412
|
+
this.cachedWinningBids.set(`${auctionId}:${data.adId}`, cachedEntry);
|
|
2413
|
+
}
|
|
2414
|
+
}
|
|
2415
|
+
this.pruneCachedWinningBids();
|
|
2416
|
+
}
|
|
2417
|
+
this.enqueue(TraceEventType.BID_WIN, "bidWon", {
|
|
2418
|
+
auctionId,
|
|
2419
|
+
transactionId,
|
|
2420
|
+
adUnitCode,
|
|
2421
|
+
bid: bidTrace
|
|
1655
2422
|
});
|
|
1656
2423
|
}
|
|
1657
2424
|
handleNoBid(data) {
|
|
1658
2425
|
if (this.isDuplicate("noBid", data)) return;
|
|
1659
2426
|
this.log("DEBUG", "noBid", data);
|
|
1660
|
-
|
|
1661
|
-
|
|
1662
|
-
|
|
1663
|
-
|
|
1664
|
-
|
|
1665
|
-
|
|
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
|
+
});
|
|
1666
2446
|
}
|
|
1667
|
-
}
|
|
2447
|
+
}
|
|
1668
2448
|
}
|
|
1669
2449
|
handleAdRenderFailed(data) {
|
|
1670
2450
|
if (this.isDuplicate("adRenderFailed", data)) return;
|
|
@@ -1690,44 +2470,81 @@ var BidkernelPrebidAnalytics = class _BidkernelPrebidAnalytics {
|
|
|
1690
2470
|
if (this.isDuplicate("adRenderSucceeded", data)) return;
|
|
1691
2471
|
this.log("DEBUG", "adRenderSucceeded", data);
|
|
1692
2472
|
const bid = data.bid || data || {};
|
|
1693
|
-
const adUnitCode = data.adUnitCode || bid.adUnitCode || "";
|
|
2473
|
+
const adUnitCode = data.adUnitCode || bid.adUnitCode || data.adId || bid.adId || "";
|
|
1694
2474
|
const auctionId = bid.auctionId || data.auctionId || "";
|
|
1695
2475
|
const transactionId = bid.transactionId || data.transactionId || "";
|
|
1696
2476
|
const mediaType = bid.mediaType || "banner";
|
|
2477
|
+
const slotKey = data.adUnitCode || bid.adUnitCode || "";
|
|
2478
|
+
const altKey = data.adId || bid.adId || "";
|
|
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);
|
|
1697
2480
|
const bidTrace = {
|
|
1698
|
-
bidder: bid.bidderCode || bid.bidder || "",
|
|
1699
|
-
cpm: Number.isFinite(bid.originalCpm) ? bid.originalCpm : Number.isFinite(bid.cpm) ? bid.cpm : 0,
|
|
1700
|
-
currency: bid.originalCurrency ?? bid.currency ?? "USD",
|
|
1701
|
-
...parseBidDimensions(bid),
|
|
1702
|
-
dealId: bid.dealId || "",
|
|
1703
|
-
mediaType,
|
|
1704
|
-
latencyMs: Number.isFinite(bid.timeToRespond) ? bid.timeToRespond : 0,
|
|
1705
|
-
advertiserDomain: bid.meta?.advertiserDomains?.[0] || "",
|
|
1706
|
-
creativeId: bid.creativeId || ""
|
|
2481
|
+
bidder: bid.bidderCode || bid.bidder || cached?.bidTrace?.bidder || "",
|
|
2482
|
+
cpm: Number.isFinite(bid.originalCpm) ? bid.originalCpm : Number.isFinite(bid.cpm) ? bid.cpm : cached?.bidTrace?.cpm ?? 0,
|
|
2483
|
+
currency: bid.originalCurrency ?? bid.currency ?? cached?.bidTrace?.currency ?? "USD",
|
|
2484
|
+
...parseBidDimensions(bid).width ? parseBidDimensions(bid) : cached?.bidTrace ? { width: cached.bidTrace.width, height: cached.bidTrace.height } : parseBidDimensions(bid),
|
|
2485
|
+
dealId: bid.dealId || cached?.bidTrace?.dealId || "",
|
|
2486
|
+
mediaType: mediaType || cached?.bidTrace?.mediaType || "banner",
|
|
2487
|
+
latencyMs: Number.isFinite(bid.timeToRespond) ? bid.timeToRespond : cached?.bidTrace?.latencyMs ?? 0,
|
|
2488
|
+
advertiserDomain: bid.meta?.advertiserDomains?.[0] || cached?.bidTrace?.advertiserDomain || "",
|
|
2489
|
+
creativeId: bid.creativeId || cached?.bidTrace?.creativeId || ""
|
|
1707
2490
|
};
|
|
1708
|
-
const
|
|
2491
|
+
const resolvedAuctionId = auctionId || cached?.auctionId || "";
|
|
2492
|
+
const resolvedTransactionId = transactionId || cached?.transactionId || "";
|
|
2493
|
+
const record = adUnitCode ? this.slotViewabilityRecords.get(adUnitCode) : void 0;
|
|
2494
|
+
const isDifferentAuction = record && resolvedAuctionId && record.auctionId && record.auctionId !== resolvedAuctionId;
|
|
2495
|
+
const isRefresh = Boolean(adUnitCode && record && isDifferentAuction);
|
|
1709
2496
|
if (isRefresh) {
|
|
1710
2497
|
this.flushSlotTimeInView(adUnitCode);
|
|
1711
|
-
const nextRefreshIndex = (this.slotRefreshIndices.get(adUnitCode) ?? 0) + 1;
|
|
2498
|
+
const nextRefreshIndex = (this.slotRefreshIndices.get(adUnitCode) ?? (record ? record.refreshIndex : 0)) + 1;
|
|
1712
2499
|
this.slotRefreshIndices.set(adUnitCode, nextRefreshIndex);
|
|
2500
|
+
this.slotLastAuctionIds.set(adUnitCode, resolvedAuctionId);
|
|
2501
|
+
if (record) {
|
|
2502
|
+
record.refreshIndex = nextRefreshIndex;
|
|
2503
|
+
record.auctionId = resolvedAuctionId;
|
|
2504
|
+
record.transactionId = resolvedTransactionId;
|
|
2505
|
+
record.bidPayload = bidTrace;
|
|
2506
|
+
record.viewableFired = false;
|
|
2507
|
+
record.accumulatedTimeInViewMs = 0;
|
|
2508
|
+
}
|
|
1713
2509
|
this.enqueue(TraceEventType.REFRESH, "refresh", {
|
|
1714
|
-
auctionId,
|
|
1715
|
-
transactionId,
|
|
2510
|
+
auctionId: resolvedAuctionId,
|
|
2511
|
+
transactionId: resolvedTransactionId,
|
|
1716
2512
|
adUnitCode,
|
|
1717
2513
|
bid: bidTrace,
|
|
1718
2514
|
metadata: { refresh_index: String(nextRefreshIndex) }
|
|
1719
2515
|
});
|
|
1720
|
-
} else if (adUnitCode
|
|
1721
|
-
|
|
2516
|
+
} else if (adUnitCode) {
|
|
2517
|
+
if (resolvedAuctionId) {
|
|
2518
|
+
this.slotLastAuctionIds.set(adUnitCode, resolvedAuctionId);
|
|
2519
|
+
}
|
|
2520
|
+
if (!this.slotRefreshIndices.has(adUnitCode)) {
|
|
2521
|
+
this.slotRefreshIndices.set(adUnitCode, 0);
|
|
2522
|
+
}
|
|
1722
2523
|
}
|
|
1723
|
-
const currentRefreshIndex = adUnitCode ? this.slotRefreshIndices.get(adUnitCode) ?? 0 : 0;
|
|
1724
|
-
this.
|
|
1725
|
-
auctionId,
|
|
1726
|
-
transactionId,
|
|
2524
|
+
const currentRefreshIndex = adUnitCode ? this.slotRefreshIndices.get(adUnitCode) ?? (record ? record.refreshIndex : 0) : 0;
|
|
2525
|
+
const alreadyEmitted = adUnitCode ? this.hasImpressionEmitted(
|
|
1727
2526
|
adUnitCode,
|
|
1728
|
-
|
|
1729
|
-
|
|
1730
|
-
|
|
2527
|
+
currentRefreshIndex,
|
|
2528
|
+
resolvedAuctionId,
|
|
2529
|
+
bidTrace.creativeId
|
|
2530
|
+
) : false;
|
|
2531
|
+
if (!alreadyEmitted) {
|
|
2532
|
+
if (adUnitCode) {
|
|
2533
|
+
this.markImpressionEmitted(
|
|
2534
|
+
adUnitCode,
|
|
2535
|
+
currentRefreshIndex,
|
|
2536
|
+
resolvedAuctionId,
|
|
2537
|
+
bidTrace.creativeId
|
|
2538
|
+
);
|
|
2539
|
+
}
|
|
2540
|
+
this.enqueue(TraceEventType.IMPRESSION, "impression", {
|
|
2541
|
+
auctionId: resolvedAuctionId,
|
|
2542
|
+
transactionId: resolvedTransactionId,
|
|
2543
|
+
adUnitCode,
|
|
2544
|
+
bid: bidTrace,
|
|
2545
|
+
metadata: { refresh_index: String(currentRefreshIndex) }
|
|
2546
|
+
});
|
|
2547
|
+
}
|
|
1731
2548
|
if (this.config.viewabilityEnabled && typeof document !== "undefined") {
|
|
1732
2549
|
let el = null;
|
|
1733
2550
|
const targetId = adUnitCode || data.adId || bid.adId;
|
|
@@ -1748,15 +2565,20 @@ var BidkernelPrebidAnalytics = class _BidkernelPrebidAnalytics {
|
|
|
1748
2565
|
}
|
|
1749
2566
|
if (!el && targetId) {
|
|
1750
2567
|
try {
|
|
1751
|
-
|
|
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
|
+
}
|
|
1752
2574
|
} catch {
|
|
1753
2575
|
}
|
|
1754
2576
|
}
|
|
1755
2577
|
if (el) {
|
|
1756
2578
|
this.observeSlot(el, adUnitCode || targetId, {
|
|
1757
2579
|
adUnitCode,
|
|
1758
|
-
auctionId,
|
|
1759
|
-
transactionId,
|
|
2580
|
+
auctionId: resolvedAuctionId,
|
|
2581
|
+
transactionId: resolvedTransactionId,
|
|
1760
2582
|
mediaType,
|
|
1761
2583
|
bid: bidTrace,
|
|
1762
2584
|
refreshIndex: currentRefreshIndex,
|
|
@@ -1802,6 +2624,9 @@ var BidkernelPrebidAnalytics = class _BidkernelPrebidAnalytics {
|
|
|
1802
2624
|
if (data.viewableDurationMs !== void 0) {
|
|
1803
2625
|
protoEvent.viewableDurationMs = Number.isFinite(data.viewableDurationMs) ? data.viewableDurationMs : 0;
|
|
1804
2626
|
}
|
|
2627
|
+
if (data.bidderOutcomes && Array.isArray(data.bidderOutcomes)) {
|
|
2628
|
+
protoEvent.bidderOutcomes = data.bidderOutcomes;
|
|
2629
|
+
}
|
|
1805
2630
|
const metadata = { ...data.metadata };
|
|
1806
2631
|
if (data.error !== void 0 && data.error !== null) {
|
|
1807
2632
|
metadata.error = typeof data.error === "object" && data.error.message ? String(data.error.message) : String(data.error);
|
|
@@ -1812,8 +2637,9 @@ var BidkernelPrebidAnalytics = class _BidkernelPrebidAnalytics {
|
|
|
1812
2637
|
if (data.refreshIndex !== void 0 && metadata.refresh_index === void 0) {
|
|
1813
2638
|
metadata.refresh_index = String(data.refreshIndex);
|
|
1814
2639
|
}
|
|
1815
|
-
|
|
1816
|
-
|
|
2640
|
+
const boundedMetadata = boundMetadata(metadata);
|
|
2641
|
+
if (Object.keys(boundedMetadata).length > 0) {
|
|
2642
|
+
protoEvent.metadata = boundedMetadata;
|
|
1817
2643
|
}
|
|
1818
2644
|
this.queue.push(protoEvent);
|
|
1819
2645
|
if (this.queue.length > MAX_QUEUE_SIZE) {
|
|
@@ -1851,7 +2677,6 @@ var BidkernelPrebidAnalytics = class _BidkernelPrebidAnalytics {
|
|
|
1851
2677
|
// Shared by flush() and flushBeacon().
|
|
1852
2678
|
drainBatch() {
|
|
1853
2679
|
if (this.queue.length === 0 || !this.config.endpoint) return null;
|
|
1854
|
-
let events = this.queue;
|
|
1855
2680
|
const domain = typeof window !== "undefined" ? window.location.hostname : "";
|
|
1856
2681
|
const buildBatch = (evts) => ({
|
|
1857
2682
|
propertyId: this.config.propertyId,
|
|
@@ -1865,25 +2690,37 @@ var BidkernelPrebidAnalytics = class _BidkernelPrebidAnalytics {
|
|
|
1865
2690
|
domain,
|
|
1866
2691
|
events: evts
|
|
1867
2692
|
});
|
|
1868
|
-
|
|
1869
|
-
|
|
1870
|
-
|
|
1871
|
-
|
|
1872
|
-
events = events.slice(0, Math.floor(events.length / 2));
|
|
1873
|
-
encoded = TraceEventBatch.encode(
|
|
2693
|
+
for (; ; ) {
|
|
2694
|
+
if (this.queue.length === 0) return null;
|
|
2695
|
+
let events = this.queue;
|
|
2696
|
+
let encoded = TraceEventBatch.encode(
|
|
1874
2697
|
buildBatch(events)
|
|
1875
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
|
+
};
|
|
1876
2721
|
}
|
|
1877
|
-
this.queue = this.queue.slice(events.length);
|
|
1878
|
-
return {
|
|
1879
|
-
url: `${this.config.endpoint}/${this.config.propertyId}`,
|
|
1880
|
-
// protobufjs types finish() as Uint8Array<ArrayBufferLike>; the buffer is
|
|
1881
|
-
// always a plain ArrayBuffer, so narrow for fetch/Blob compatibility.
|
|
1882
|
-
encoded,
|
|
1883
|
-
events
|
|
1884
|
-
};
|
|
1885
2722
|
}
|
|
1886
|
-
sendFetch(payload, useKeepalive = false, onDelivered) {
|
|
2723
|
+
sendFetch(payload, useKeepalive = false, onDelivered, isReplay = false) {
|
|
1887
2724
|
const fetchOpts = {
|
|
1888
2725
|
method: "POST",
|
|
1889
2726
|
// Safelisted content type: no CORS preflight (see SAFE_CONTENT_TYPE).
|
|
@@ -1900,15 +2737,17 @@ var BidkernelPrebidAnalytics = class _BidkernelPrebidAnalytics {
|
|
|
1900
2737
|
this.log("WARN", `Dropping batch due to non-retryable client error HTTP ${res.status}`);
|
|
1901
2738
|
return;
|
|
1902
2739
|
}
|
|
1903
|
-
this.handleSendFailure(payload.events);
|
|
2740
|
+
if (!isReplay) this.handleSendFailure(payload.events);
|
|
1904
2741
|
} else {
|
|
1905
|
-
|
|
1906
|
-
|
|
2742
|
+
if (!isReplay) {
|
|
2743
|
+
this.consecutiveSendFailures = 0;
|
|
2744
|
+
this.nextSendAllowedAt = 0;
|
|
2745
|
+
}
|
|
1907
2746
|
if (onDelivered) onDelivered();
|
|
1908
2747
|
}
|
|
1909
2748
|
}).catch((err) => {
|
|
1910
2749
|
this.log("ERROR", "Failed to send batch", err);
|
|
1911
|
-
this.handleSendFailure(payload.events);
|
|
2750
|
+
if (!isReplay) this.handleSendFailure(payload.events);
|
|
1912
2751
|
});
|
|
1913
2752
|
const proc = typeof globalThis !== "undefined" ? globalThis.process : void 0;
|
|
1914
2753
|
if (proc && typeof proc._tickCallback === "function") {
|
|
@@ -1964,16 +2803,23 @@ var BidkernelPrebidAnalytics = class _BidkernelPrebidAnalytics {
|
|
|
1964
2803
|
this.schedulePersistedCleanup();
|
|
1965
2804
|
}
|
|
1966
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
|
+
}
|
|
1967
2812
|
persistBatch(encoded) {
|
|
1968
2813
|
try {
|
|
1969
2814
|
if (typeof localStorage === "undefined") return null;
|
|
2815
|
+
if (!this.config.propertyId) return null;
|
|
1970
2816
|
let pendingCount = 0;
|
|
1971
2817
|
for (let i = 0; i < localStorage.length; i++) {
|
|
1972
2818
|
const k = localStorage.key(i);
|
|
1973
2819
|
if (k && k.startsWith(PENDING_BATCH_KEY_PREFIX)) pendingCount++;
|
|
1974
2820
|
}
|
|
1975
2821
|
if (pendingCount >= MAX_PENDING_BATCHES) return null;
|
|
1976
|
-
const key = `${
|
|
2822
|
+
const key = `${this.pendingKeyPrefix()}${Date.now()}_${Math.floor(Math.random() * 1e6)}`;
|
|
1977
2823
|
localStorage.setItem(key, bytesToBase64(encoded));
|
|
1978
2824
|
this.persistedBatchKeys.push(key);
|
|
1979
2825
|
return key;
|
|
@@ -1998,22 +2844,39 @@ var BidkernelPrebidAnalytics = class _BidkernelPrebidAnalytics {
|
|
|
1998
2844
|
if (this.persistedCleanupTimer) clearTimeout(this.persistedCleanupTimer);
|
|
1999
2845
|
this.persistedCleanupTimer = setTimeout(() => {
|
|
2000
2846
|
this.persistedCleanupTimer = null;
|
|
2001
|
-
for (const key of
|
|
2847
|
+
for (const key of Array.from(this.persistedBatchKeys)) {
|
|
2002
2848
|
this.removePersistedBatch(key);
|
|
2003
2849
|
}
|
|
2004
2850
|
}, PENDING_BATCH_CLEANUP_DELAY_MS);
|
|
2005
2851
|
}
|
|
2006
2852
|
// Resend batches a previous pageview persisted at exit but could not
|
|
2007
|
-
// confirm.
|
|
2008
|
-
//
|
|
2009
|
-
//
|
|
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.
|
|
2010
2859
|
resendPersistedBatches() {
|
|
2011
2860
|
try {
|
|
2012
2861
|
if (typeof localStorage === "undefined" || !this.config.endpoint) return;
|
|
2862
|
+
if (!this.config.propertyId) return;
|
|
2863
|
+
const ownPrefix = this.pendingKeyPrefix();
|
|
2013
2864
|
const keys = [];
|
|
2865
|
+
const legacyKeys = [];
|
|
2014
2866
|
for (let i = 0; i < localStorage.length; i++) {
|
|
2015
2867
|
const k = localStorage.key(i);
|
|
2016
|
-
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
|
+
}
|
|
2017
2880
|
}
|
|
2018
2881
|
for (const key of keys) {
|
|
2019
2882
|
let value = null;
|
|
@@ -2024,7 +2887,7 @@ var BidkernelPrebidAnalytics = class _BidkernelPrebidAnalytics {
|
|
|
2024
2887
|
continue;
|
|
2025
2888
|
}
|
|
2026
2889
|
if (!value) continue;
|
|
2027
|
-
const ts = parseInt(key.slice(
|
|
2890
|
+
const ts = parseInt(key.slice(ownPrefix.length), 10);
|
|
2028
2891
|
if (!Number.isFinite(ts) || Date.now() - ts > PENDING_BATCH_MAX_AGE_MS) continue;
|
|
2029
2892
|
let encoded;
|
|
2030
2893
|
try {
|
|
@@ -2034,11 +2897,16 @@ var BidkernelPrebidAnalytics = class _BidkernelPrebidAnalytics {
|
|
|
2034
2897
|
}
|
|
2035
2898
|
if (encoded.byteLength === 0) continue;
|
|
2036
2899
|
this.log("DEBUG", `Resending persisted exit batch (${encoded.byteLength} bytes)`);
|
|
2037
|
-
this.sendFetch(
|
|
2038
|
-
|
|
2039
|
-
|
|
2040
|
-
|
|
2041
|
-
|
|
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
|
+
);
|
|
2042
2910
|
}
|
|
2043
2911
|
} catch {
|
|
2044
2912
|
}
|
|
@@ -2063,11 +2931,13 @@ var BidkernelPrebidAnalytics = class _BidkernelPrebidAnalytics {
|
|
|
2063
2931
|
}
|
|
2064
2932
|
}
|
|
2065
2933
|
};
|
|
2066
|
-
function extractAnalyticsOptions(config) {
|
|
2934
|
+
function extractAnalyticsOptions(config, allowProviderless = true) {
|
|
2067
2935
|
if (!config) return null;
|
|
2068
2936
|
if (Array.isArray(config)) {
|
|
2069
|
-
const matched = config.find((item) => item?.provider === "bidkernel") ?? config.find(
|
|
2070
|
-
|
|
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;
|
|
2071
2941
|
}
|
|
2072
2942
|
if (typeof config === "object") {
|
|
2073
2943
|
if (config.options && typeof config.options === "object") {
|
|
@@ -2109,18 +2979,27 @@ function registerPrebidAnalytics(pbjsGlobalName = "pbjs", defaults) {
|
|
|
2109
2979
|
win[pbjsGlobalName] = win[pbjsGlobalName] || {};
|
|
2110
2980
|
const pbjs = win[pbjsGlobalName];
|
|
2111
2981
|
const enableInstance = (options) => {
|
|
2112
|
-
|
|
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) {
|
|
2113
2996
|
try {
|
|
2114
|
-
|
|
2997
|
+
previous.disable();
|
|
2115
2998
|
} catch (e) {
|
|
2116
2999
|
console.warn("[bidkernel] Failed to disable previous analytics instance:", e);
|
|
2117
3000
|
}
|
|
2118
3001
|
}
|
|
2119
|
-
const analytics = new BidkernelPrebidAnalytics(
|
|
2120
|
-
pbjsGlobalName,
|
|
2121
|
-
...defaults,
|
|
2122
|
-
...options
|
|
2123
|
-
});
|
|
3002
|
+
const analytics = new BidkernelPrebidAnalytics(merged);
|
|
2124
3003
|
analytics.enable();
|
|
2125
3004
|
win._bidkernelPrebidAnalytics = analytics;
|
|
2126
3005
|
return analytics;
|
|
@@ -2131,7 +3010,7 @@ function registerPrebidAnalytics(pbjsGlobalName = "pbjs", defaults) {
|
|
|
2131
3010
|
pbjs.adapterManager.registerAnalyticsAdapter({
|
|
2132
3011
|
adapter: {
|
|
2133
3012
|
enableAnalytics: (config) => {
|
|
2134
|
-
const options = extractAnalyticsOptions(config);
|
|
3013
|
+
const options = extractAnalyticsOptions(config, !Array.isArray(config));
|
|
2135
3014
|
if (options) {
|
|
2136
3015
|
enableInstance(options);
|
|
2137
3016
|
}
|
|
@@ -2168,7 +3047,28 @@ function getbidkernel(alias = "bidkernel") {
|
|
|
2168
3047
|
}
|
|
2169
3048
|
const win = window;
|
|
2170
3049
|
win[alias] = win[alias] || { q: [] };
|
|
2171
|
-
|
|
3050
|
+
const sdk = win[alias];
|
|
3051
|
+
if (!sdk.attachVideoPlayer) {
|
|
3052
|
+
sdk.attachVideoPlayer = (target, options) => {
|
|
3053
|
+
const inst = win._bidkernelPrebidAnalytics;
|
|
3054
|
+
return inst?.attachVideoPlayer ? inst.attachVideoPlayer(target, options) : () => {
|
|
3055
|
+
};
|
|
3056
|
+
};
|
|
3057
|
+
}
|
|
3058
|
+
if (!sdk.attachImaAdsManager) {
|
|
3059
|
+
sdk.attachImaAdsManager = (adsManager, options) => {
|
|
3060
|
+
const inst = win._bidkernelPrebidAnalytics;
|
|
3061
|
+
return inst?.attachImaAdsManager ? inst.attachImaAdsManager(adsManager, options) : () => {
|
|
3062
|
+
};
|
|
3063
|
+
};
|
|
3064
|
+
}
|
|
3065
|
+
if (!sdk.recordImpression) {
|
|
3066
|
+
sdk.recordImpression = (slotId, options) => {
|
|
3067
|
+
const inst = win._bidkernelPrebidAnalytics;
|
|
3068
|
+
return inst?.recordImpression ? inst.recordImpression(slotId, options) : false;
|
|
3069
|
+
};
|
|
3070
|
+
}
|
|
3071
|
+
return sdk;
|
|
2172
3072
|
}
|
|
2173
3073
|
// Annotate the CommonJS export names for ESM import in node:
|
|
2174
3074
|
0 && (module.exports = {
|
|
@@ -2176,5 +3076,6 @@ function getbidkernel(alias = "bidkernel") {
|
|
|
2176
3076
|
PrebidEventDeduper,
|
|
2177
3077
|
getPrebidEventKey,
|
|
2178
3078
|
getbidkernel,
|
|
3079
|
+
isTrustedEndpoint,
|
|
2179
3080
|
registerPrebidAnalytics
|
|
2180
3081
|
});
|