@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.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 };
|
|
@@ -859,10 +995,21 @@ var BidkernelPrebidAnalytics = class _BidkernelPrebidAnalytics {
|
|
|
859
995
|
slotViewabilityRecords = /* @__PURE__ */ new Map();
|
|
860
996
|
elementToSlotId = /* @__PURE__ */ new Map();
|
|
861
997
|
slotRefreshIndices = /* @__PURE__ */ new Map();
|
|
998
|
+
slotLastAuctionIds = /* @__PURE__ */ new Map();
|
|
862
999
|
pendingThresholdListeners = /* @__PURE__ */ new Map();
|
|
1000
|
+
// Impression deduplication per slot and refresh cycle (strictly 1 impression per cycle)
|
|
1001
|
+
slotEmittedImpressionKeys = /* @__PURE__ */ new Set();
|
|
1002
|
+
// Winning bid cache from bidWon to marry with subsequent render triggers (adRenderSucceeded, video, etc.)
|
|
1003
|
+
cachedWinningBids = /* @__PURE__ */ new Map();
|
|
1004
|
+
// Active video player attachment cleanup routines
|
|
1005
|
+
videoDetachCleanups = /* @__PURE__ */ new Set();
|
|
1006
|
+
// Compacted bidder participation map per auction: auctionId -> Map<`${bidder}:${adUnitCode}`, BidderOutcomeEntry>
|
|
1007
|
+
auctionBidderOutcomes = /* @__PURE__ */ new Map();
|
|
863
1008
|
constructor(config) {
|
|
1009
|
+
const requestedEndpoint = config.endpoint || "";
|
|
1010
|
+
const endpoint = !requestedEndpoint || isTrustedEndpoint(requestedEndpoint) ? requestedEndpoint : "";
|
|
864
1011
|
this.config = {
|
|
865
|
-
endpoint
|
|
1012
|
+
endpoint,
|
|
866
1013
|
propertyId: config.propertyId || "",
|
|
867
1014
|
pageviewId: config.pageviewId || generateUUID(),
|
|
868
1015
|
sessionId: config.sessionId || getOrCreateSessionId(),
|
|
@@ -878,6 +1025,12 @@ var BidkernelPrebidAnalytics = class _BidkernelPrebidAnalytics {
|
|
|
878
1025
|
pbjsGlobalName: config.pbjsGlobalName || "pbjs",
|
|
879
1026
|
attachPbjsListeners: config.attachPbjsListeners ?? true
|
|
880
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
|
+
}
|
|
881
1034
|
this.boundFlushBeacon = () => {
|
|
882
1035
|
this.flushAllSlotsTimeInView();
|
|
883
1036
|
this.flushBeacon();
|
|
@@ -1005,6 +1158,17 @@ var BidkernelPrebidAnalytics = class _BidkernelPrebidAnalytics {
|
|
|
1005
1158
|
}
|
|
1006
1159
|
this.slotViewabilityRecords.clear();
|
|
1007
1160
|
this.elementToSlotId.clear();
|
|
1161
|
+
for (const cleanup of this.videoDetachCleanups) {
|
|
1162
|
+
try {
|
|
1163
|
+
cleanup();
|
|
1164
|
+
} catch {
|
|
1165
|
+
}
|
|
1166
|
+
}
|
|
1167
|
+
this.videoDetachCleanups.clear();
|
|
1168
|
+
this.cachedWinningBids.clear();
|
|
1169
|
+
this.slotEmittedImpressionKeys.clear();
|
|
1170
|
+
this.slotLastAuctionIds.clear();
|
|
1171
|
+
this.auctionBidderOutcomes.clear();
|
|
1008
1172
|
if (this.flushTimer) {
|
|
1009
1173
|
clearInterval(this.flushTimer);
|
|
1010
1174
|
this.flushTimer = null;
|
|
@@ -1327,6 +1491,9 @@ var BidkernelPrebidAnalytics = class _BidkernelPrebidAnalytics {
|
|
|
1327
1491
|
if (options?.bid !== void 0) existing.bidPayload = options.bid;
|
|
1328
1492
|
if (options?.metadata !== void 0) existing.metadata = options.metadata;
|
|
1329
1493
|
}
|
|
1494
|
+
if (options?.auctionId) {
|
|
1495
|
+
this.slotLastAuctionIds.set(resolvedSlotId, options.auctionId);
|
|
1496
|
+
}
|
|
1330
1497
|
if (el) {
|
|
1331
1498
|
if (existing.element && existing.element !== el && this.intersectionObserver) {
|
|
1332
1499
|
this.intersectionObserver.unobserve(existing.element);
|
|
@@ -1339,6 +1506,9 @@ var BidkernelPrebidAnalytics = class _BidkernelPrebidAnalytics {
|
|
|
1339
1506
|
} else {
|
|
1340
1507
|
const initialRefreshIndex = options?.refreshIndex ?? this.slotRefreshIndices.get(resolvedSlotId) ?? 0;
|
|
1341
1508
|
this.slotRefreshIndices.set(resolvedSlotId, initialRefreshIndex);
|
|
1509
|
+
if (options?.auctionId) {
|
|
1510
|
+
this.slotLastAuctionIds.set(resolvedSlotId, options.auctionId);
|
|
1511
|
+
}
|
|
1342
1512
|
const listeners = this.pendingThresholdListeners.get(resolvedSlotId) || /* @__PURE__ */ new Set();
|
|
1343
1513
|
this.pendingThresholdListeners.delete(resolvedSlotId);
|
|
1344
1514
|
const record = {
|
|
@@ -1394,7 +1564,551 @@ var BidkernelPrebidAnalytics = class _BidkernelPrebidAnalytics {
|
|
|
1394
1564
|
this.unobserveSlot(slotId);
|
|
1395
1565
|
this.slotViewabilityRecords.delete(slotId);
|
|
1396
1566
|
this.slotRefreshIndices.delete(slotId);
|
|
1567
|
+
this.slotLastAuctionIds.delete(slotId);
|
|
1397
1568
|
this.pendingThresholdListeners.delete(slotId);
|
|
1569
|
+
const prefix = `${escapeKeyPart(slotId)}:`;
|
|
1570
|
+
for (const key of Array.from(this.slotEmittedImpressionKeys)) {
|
|
1571
|
+
if (key === slotId || key.startsWith(prefix)) {
|
|
1572
|
+
this.slotEmittedImpressionKeys.delete(key);
|
|
1573
|
+
}
|
|
1574
|
+
}
|
|
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;
|
|
1642
|
+
}
|
|
1643
|
+
hasImpressionEmitted(slotId, refreshIndex, auctionId, creativeId) {
|
|
1644
|
+
const rIndex = refreshIndex ?? this.slotRefreshIndices.get(slotId) ?? 0;
|
|
1645
|
+
const baseSlotKey = `${escapeKeyPart(slotId)}:${rIndex}`;
|
|
1646
|
+
if (creativeId) {
|
|
1647
|
+
if (auctionId) {
|
|
1648
|
+
return this.slotEmittedImpressionKeys.has(
|
|
1649
|
+
`${baseSlotKey}:auc:${escapeKeyPart(auctionId)}:${escapeKeyPart(creativeId)}`
|
|
1650
|
+
) || this.slotEmittedImpressionKeys.has(`${baseSlotKey}:${escapeKeyPart(creativeId)}`);
|
|
1651
|
+
}
|
|
1652
|
+
return this.slotEmittedImpressionKeys.has(`${baseSlotKey}:${escapeKeyPart(creativeId)}`);
|
|
1653
|
+
}
|
|
1654
|
+
if (auctionId) {
|
|
1655
|
+
return this.slotEmittedImpressionKeys.has(`${baseSlotKey}:auc:${escapeKeyPart(auctionId)}`);
|
|
1656
|
+
}
|
|
1657
|
+
return this.slotEmittedImpressionKeys.has(baseSlotKey);
|
|
1658
|
+
}
|
|
1659
|
+
markImpressionEmitted(slotId, refreshIndex, auctionId, creativeId) {
|
|
1660
|
+
const baseSlotKey = `${escapeKeyPart(slotId)}:${refreshIndex}`;
|
|
1661
|
+
this.addImpressionKey(baseSlotKey);
|
|
1662
|
+
if (creativeId) {
|
|
1663
|
+
this.addImpressionKey(`${baseSlotKey}:${escapeKeyPart(creativeId)}`);
|
|
1664
|
+
}
|
|
1665
|
+
if (auctionId) {
|
|
1666
|
+
this.addImpressionKey(`${baseSlotKey}:auc:${escapeKeyPart(auctionId)}`);
|
|
1667
|
+
if (creativeId) {
|
|
1668
|
+
this.addImpressionKey(
|
|
1669
|
+
`${baseSlotKey}:auc:${escapeKeyPart(auctionId)}:${escapeKeyPart(creativeId)}`
|
|
1670
|
+
);
|
|
1671
|
+
}
|
|
1672
|
+
}
|
|
1673
|
+
}
|
|
1674
|
+
recordImpression(slotId, options) {
|
|
1675
|
+
if (!this.isEnabled) return false;
|
|
1676
|
+
const resolvedSlotId = slotId || options?.adUnitCode || "";
|
|
1677
|
+
const adUnitCode = options?.adUnitCode || resolvedSlotId;
|
|
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);
|
|
1679
|
+
const rawBid = options?.bid || cached?.rawBid || {};
|
|
1680
|
+
const auctionId = options?.auctionId || rawBid.auctionId || cached?.auctionId || "";
|
|
1681
|
+
const transactionId = options?.transactionId || rawBid.transactionId || cached?.transactionId || "";
|
|
1682
|
+
const creativeId = rawBid.creativeId || cached?.bidTrace?.creativeId || options?.bid?.creativeId || "";
|
|
1683
|
+
const mediaType = options?.mediaType || rawBid.mediaType || cached?.bidTrace?.mediaType || "banner";
|
|
1684
|
+
const targetSlot = resolvedSlotId || adUnitCode;
|
|
1685
|
+
const rec = (resolvedSlotId ? this.slotViewabilityRecords.get(resolvedSlotId) : void 0) || (adUnitCode ? this.slotViewabilityRecords.get(adUnitCode) : void 0);
|
|
1686
|
+
const lastAuctionId = targetSlot ? this.slotLastAuctionIds.get(targetSlot) || (rec ? rec.auctionId : void 0) : void 0;
|
|
1687
|
+
const isDifferentAuction = Boolean(
|
|
1688
|
+
targetSlot && auctionId && lastAuctionId && lastAuctionId !== auctionId && options?.refreshIndex === void 0
|
|
1689
|
+
);
|
|
1690
|
+
const bidTrace = {
|
|
1691
|
+
bidder: rawBid.bidderCode || rawBid.bidder || cached?.bidTrace?.bidder || "",
|
|
1692
|
+
cpm: Number.isFinite(rawBid.originalCpm) ? rawBid.originalCpm : Number.isFinite(rawBid.cpm) ? rawBid.cpm : cached?.bidTrace?.cpm ?? 0,
|
|
1693
|
+
currency: rawBid.originalCurrency ?? rawBid.currency ?? cached?.bidTrace?.currency ?? "USD",
|
|
1694
|
+
...parseBidDimensions(rawBid).width ? parseBidDimensions(rawBid) : cached?.bidTrace ? { width: cached.bidTrace.width, height: cached.bidTrace.height } : parseBidDimensions(rawBid),
|
|
1695
|
+
dealId: rawBid.dealId || cached?.bidTrace?.dealId || "",
|
|
1696
|
+
mediaType,
|
|
1697
|
+
latencyMs: Number.isFinite(rawBid.timeToRespond) ? rawBid.timeToRespond : cached?.bidTrace?.latencyMs ?? 0,
|
|
1698
|
+
advertiserDomain: rawBid.meta?.advertiserDomains?.[0] || cached?.bidTrace?.advertiserDomain || "",
|
|
1699
|
+
creativeId: rawBid.creativeId || cached?.bidTrace?.creativeId || ""
|
|
1700
|
+
};
|
|
1701
|
+
if (isDifferentAuction && targetSlot) {
|
|
1702
|
+
this.flushSlotTimeInView(targetSlot);
|
|
1703
|
+
const nextRefreshIndex = (this.slotRefreshIndices.get(targetSlot) ?? (rec ? rec.refreshIndex : 0)) + 1;
|
|
1704
|
+
this.slotRefreshIndices.set(targetSlot, nextRefreshIndex);
|
|
1705
|
+
this.slotLastAuctionIds.set(targetSlot, auctionId);
|
|
1706
|
+
if (rec) {
|
|
1707
|
+
rec.refreshIndex = nextRefreshIndex;
|
|
1708
|
+
rec.auctionId = auctionId;
|
|
1709
|
+
rec.transactionId = transactionId;
|
|
1710
|
+
rec.bidPayload = bidTrace;
|
|
1711
|
+
rec.viewableFired = false;
|
|
1712
|
+
rec.accumulatedTimeInViewMs = 0;
|
|
1713
|
+
}
|
|
1714
|
+
this.enqueue(TraceEventType.REFRESH, "refresh", {
|
|
1715
|
+
auctionId,
|
|
1716
|
+
transactionId,
|
|
1717
|
+
adUnitCode: targetSlot,
|
|
1718
|
+
bid: bidTrace,
|
|
1719
|
+
metadata: {
|
|
1720
|
+
...options?.metadata,
|
|
1721
|
+
refresh_index: String(nextRefreshIndex)
|
|
1722
|
+
}
|
|
1723
|
+
});
|
|
1724
|
+
} else if (targetSlot) {
|
|
1725
|
+
if (auctionId) {
|
|
1726
|
+
this.slotLastAuctionIds.set(targetSlot, auctionId);
|
|
1727
|
+
}
|
|
1728
|
+
if (!this.slotRefreshIndices.has(targetSlot)) {
|
|
1729
|
+
this.slotRefreshIndices.set(targetSlot, 0);
|
|
1730
|
+
}
|
|
1731
|
+
}
|
|
1732
|
+
const currentRefreshIndex = options?.refreshIndex ?? (resolvedSlotId ? this.slotRefreshIndices.get(resolvedSlotId) ?? (adUnitCode ? this.slotRefreshIndices.get(adUnitCode) : void 0) ?? (rec ? rec.refreshIndex : 0) : 0);
|
|
1733
|
+
const alreadyEmitted = resolvedSlotId && this.hasImpressionEmitted(resolvedSlotId, currentRefreshIndex, auctionId, creativeId) || adUnitCode && adUnitCode !== resolvedSlotId && this.hasImpressionEmitted(adUnitCode, currentRefreshIndex, auctionId, creativeId);
|
|
1734
|
+
if (alreadyEmitted) {
|
|
1735
|
+
this.log(
|
|
1736
|
+
"DEBUG",
|
|
1737
|
+
`Impression already emitted for slot ${resolvedSlotId || adUnitCode} in cycle ${currentRefreshIndex}`
|
|
1738
|
+
);
|
|
1739
|
+
return false;
|
|
1740
|
+
}
|
|
1741
|
+
if (resolvedSlotId)
|
|
1742
|
+
this.markImpressionEmitted(resolvedSlotId, currentRefreshIndex, auctionId, creativeId);
|
|
1743
|
+
if (adUnitCode && adUnitCode !== resolvedSlotId)
|
|
1744
|
+
this.markImpressionEmitted(adUnitCode, currentRefreshIndex, auctionId, creativeId);
|
|
1745
|
+
this.enqueue(TraceEventType.IMPRESSION, "impression", {
|
|
1746
|
+
auctionId,
|
|
1747
|
+
transactionId,
|
|
1748
|
+
adUnitCode,
|
|
1749
|
+
bid: bidTrace,
|
|
1750
|
+
metadata: {
|
|
1751
|
+
...options?.metadata,
|
|
1752
|
+
refresh_index: String(currentRefreshIndex)
|
|
1753
|
+
}
|
|
1754
|
+
});
|
|
1755
|
+
if (rec) {
|
|
1756
|
+
if (!rec.bidPayload && bidTrace) rec.bidPayload = bidTrace;
|
|
1757
|
+
if (!rec.auctionId && auctionId) rec.auctionId = auctionId;
|
|
1758
|
+
if (!rec.transactionId && transactionId) rec.transactionId = transactionId;
|
|
1759
|
+
}
|
|
1760
|
+
return true;
|
|
1761
|
+
}
|
|
1762
|
+
/**
|
|
1763
|
+
* Bridges Google IMA SDK AdsManager events to Bidkernel analytics.
|
|
1764
|
+
* Defers IMPRESSION emission until AdEvent.STARTED (or IMPRESSION),
|
|
1765
|
+
* tracks milestones (FIRST_QUARTILE, MIDPOINT, THIRD_QUARTILE, COMPLETE),
|
|
1766
|
+
* and handles AD_ERROR.
|
|
1767
|
+
*/
|
|
1768
|
+
attachImaAdsManager(adsManager, options) {
|
|
1769
|
+
if (!adsManager || typeof adsManager.addEventListener !== "function") {
|
|
1770
|
+
this.log("WARN", "Invalid AdsManager passed to attachImaAdsManager");
|
|
1771
|
+
return () => {
|
|
1772
|
+
};
|
|
1773
|
+
}
|
|
1774
|
+
const slotId = options?.slotId || options?.adUnitCode || "video";
|
|
1775
|
+
const adUnitCode = options?.adUnitCode || slotId;
|
|
1776
|
+
const auctionId = options?.auctionId || "";
|
|
1777
|
+
const transactionId = options?.transactionId || "";
|
|
1778
|
+
const getWinningBid = () => {
|
|
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);
|
|
1780
|
+
};
|
|
1781
|
+
const onAdStartedOrImpression = (event) => {
|
|
1782
|
+
this.log("DEBUG", "IMA AdEvent.STARTED / IMPRESSION received", event);
|
|
1783
|
+
const ad = typeof event?.getAd === "function" ? event.getAd() : event?.ad;
|
|
1784
|
+
const adData = {};
|
|
1785
|
+
if (ad) {
|
|
1786
|
+
adData.creativeId = (typeof ad.getCreativeId === "function" ? ad.getCreativeId() : ad.creativeId) || "";
|
|
1787
|
+
adData.adId = (typeof ad.getAdId === "function" ? ad.getAdId() : ad.id) || "";
|
|
1788
|
+
adData.title = (typeof ad.getTitle === "function" ? ad.getTitle() : ad.title) || "";
|
|
1789
|
+
adData.duration = typeof ad.getDuration === "function" ? ad.getDuration() : ad.duration;
|
|
1790
|
+
adData.advertiserName = typeof ad.getAdvertiserName === "function" ? ad.getAdvertiserName() : "";
|
|
1791
|
+
}
|
|
1792
|
+
const cached = getWinningBid();
|
|
1793
|
+
const mergedBid = {
|
|
1794
|
+
...cached?.rawBid || options?.bid,
|
|
1795
|
+
mediaType: "video",
|
|
1796
|
+
creativeId: adData.creativeId || cached?.bidTrace?.creativeId || options?.bid?.creativeId || ""
|
|
1797
|
+
};
|
|
1798
|
+
const emitted = this.recordImpression(slotId, {
|
|
1799
|
+
adUnitCode,
|
|
1800
|
+
auctionId: auctionId || cached?.auctionId,
|
|
1801
|
+
transactionId: transactionId || cached?.transactionId,
|
|
1802
|
+
mediaType: "video",
|
|
1803
|
+
bid: mergedBid,
|
|
1804
|
+
metadata: {
|
|
1805
|
+
...options?.metadata,
|
|
1806
|
+
media_type: "video",
|
|
1807
|
+
...adData.title ? { ad_title: adData.title } : {},
|
|
1808
|
+
...Number.isFinite(adData.duration) ? { ad_duration_s: String(adData.duration) } : {}
|
|
1809
|
+
}
|
|
1810
|
+
});
|
|
1811
|
+
if (emitted && options?.onImpression) {
|
|
1812
|
+
try {
|
|
1813
|
+
options.onImpression(slotId, { ad, bid: mergedBid });
|
|
1814
|
+
} catch (e) {
|
|
1815
|
+
this.log("ERROR", "Error in onImpression callback", e);
|
|
1816
|
+
}
|
|
1817
|
+
}
|
|
1818
|
+
};
|
|
1819
|
+
const onMilestone = (milestone) => {
|
|
1820
|
+
this.log("DEBUG", `IMA AdEvent milestone: ${milestone}`);
|
|
1821
|
+
if (options?.onMilestone) {
|
|
1822
|
+
try {
|
|
1823
|
+
options.onMilestone(milestone, slotId);
|
|
1824
|
+
} catch (e) {
|
|
1825
|
+
this.log("ERROR", "Error in onMilestone callback", e);
|
|
1826
|
+
}
|
|
1827
|
+
}
|
|
1828
|
+
};
|
|
1829
|
+
const onAdClick = (event) => {
|
|
1830
|
+
this.log("DEBUG", "IMA AdEvent.CLICK received", event);
|
|
1831
|
+
const cached = getWinningBid();
|
|
1832
|
+
this.enqueue(TraceEventType.CLICK, "click", {
|
|
1833
|
+
auctionId: auctionId || cached?.auctionId,
|
|
1834
|
+
transactionId: transactionId || cached?.transactionId,
|
|
1835
|
+
adUnitCode,
|
|
1836
|
+
bid: cached?.bidTrace || options?.bid,
|
|
1837
|
+
metadata: {
|
|
1838
|
+
...options?.metadata,
|
|
1839
|
+
media_type: "video"
|
|
1840
|
+
}
|
|
1841
|
+
});
|
|
1842
|
+
};
|
|
1843
|
+
const onAdError = (event) => {
|
|
1844
|
+
this.log("WARN", "IMA AdErrorEvent received", event);
|
|
1845
|
+
const err = typeof event?.getError === "function" ? event.getError() : event?.error || event;
|
|
1846
|
+
const msg = (err && typeof err.getMessage === "function" ? err.getMessage() : err?.message) || String(err || "IMA ad error");
|
|
1847
|
+
const code = (err && typeof err.getErrorCode === "function" ? err.getErrorCode() : err?.code) || "";
|
|
1848
|
+
const cached = getWinningBid();
|
|
1849
|
+
this.enqueue(TraceEventType.AD_RENDER_FAILED, "adRenderFailed", {
|
|
1850
|
+
auctionId: auctionId || cached?.auctionId,
|
|
1851
|
+
transactionId: transactionId || cached?.transactionId,
|
|
1852
|
+
adUnitCode,
|
|
1853
|
+
bid: cached?.bidTrace || options?.bid,
|
|
1854
|
+
metadata: {
|
|
1855
|
+
...options?.metadata,
|
|
1856
|
+
reason: "ima_ad_error",
|
|
1857
|
+
message: msg,
|
|
1858
|
+
...code ? { ima_error_code: String(code) } : {}
|
|
1859
|
+
},
|
|
1860
|
+
error: typeof err === "object" ? err : new Error(msg)
|
|
1861
|
+
});
|
|
1862
|
+
if (options?.onError) {
|
|
1863
|
+
try {
|
|
1864
|
+
options.onError(err);
|
|
1865
|
+
} catch (e) {
|
|
1866
|
+
this.log("ERROR", "Error in onError callback", e);
|
|
1867
|
+
}
|
|
1868
|
+
}
|
|
1869
|
+
};
|
|
1870
|
+
const googleIma = typeof window !== "undefined" ? window.google?.ima : void 0;
|
|
1871
|
+
const adEventType = googleIma?.AdEvent?.Type || {};
|
|
1872
|
+
const adErrorEventType = googleIma?.AdErrorEvent?.Type || {};
|
|
1873
|
+
const listeners = [
|
|
1874
|
+
{ type: adEventType.STARTED || "started", handler: onAdStartedOrImpression },
|
|
1875
|
+
{ type: adEventType.IMPRESSION || "impression", handler: onAdStartedOrImpression },
|
|
1876
|
+
{
|
|
1877
|
+
type: adEventType.FIRST_QUARTILE || "firstQuartile",
|
|
1878
|
+
handler: () => onMilestone("firstQuartile")
|
|
1879
|
+
},
|
|
1880
|
+
{ type: adEventType.MIDPOINT || "midpoint", handler: () => onMilestone("midpoint") },
|
|
1881
|
+
{
|
|
1882
|
+
type: adEventType.THIRD_QUARTILE || "thirdQuartile",
|
|
1883
|
+
handler: () => onMilestone("thirdQuartile")
|
|
1884
|
+
},
|
|
1885
|
+
{ type: adEventType.COMPLETE || "complete", handler: () => onMilestone("complete") },
|
|
1886
|
+
{ type: adEventType.CLICK || "click", handler: onAdClick },
|
|
1887
|
+
{ type: adErrorEventType.AD_ERROR || "adError", handler: onAdError }
|
|
1888
|
+
];
|
|
1889
|
+
for (const { type, handler } of listeners) {
|
|
1890
|
+
try {
|
|
1891
|
+
adsManager.addEventListener(type, handler);
|
|
1892
|
+
} catch {
|
|
1893
|
+
}
|
|
1894
|
+
}
|
|
1895
|
+
const cleanup = () => {
|
|
1896
|
+
for (const { type, handler } of listeners) {
|
|
1897
|
+
try {
|
|
1898
|
+
adsManager.removeEventListener(type, handler);
|
|
1899
|
+
} catch {
|
|
1900
|
+
}
|
|
1901
|
+
}
|
|
1902
|
+
this.videoDetachCleanups.delete(cleanup);
|
|
1903
|
+
};
|
|
1904
|
+
this.videoDetachCleanups.add(cleanup);
|
|
1905
|
+
return cleanup;
|
|
1906
|
+
}
|
|
1907
|
+
/**
|
|
1908
|
+
* Attaches render hooks and viewability tracking to a video player.
|
|
1909
|
+
* Supports HTML5 <video> elements, container elements, or Google IMA AdsManager.
|
|
1910
|
+
*/
|
|
1911
|
+
attachVideoPlayer(target, options) {
|
|
1912
|
+
if (!target) {
|
|
1913
|
+
this.log("WARN", "attachVideoPlayer called with null/undefined target");
|
|
1914
|
+
return () => {
|
|
1915
|
+
};
|
|
1916
|
+
}
|
|
1917
|
+
let el = null;
|
|
1918
|
+
if (typeof target === "string") {
|
|
1919
|
+
if (typeof document !== "undefined") {
|
|
1920
|
+
try {
|
|
1921
|
+
el = document.querySelector(target) || document.getElementById(target);
|
|
1922
|
+
} catch {
|
|
1923
|
+
el = document.getElementById(target);
|
|
1924
|
+
}
|
|
1925
|
+
}
|
|
1926
|
+
} else if (typeof HTMLElement !== "undefined" && target instanceof HTMLElement) {
|
|
1927
|
+
el = target;
|
|
1928
|
+
} else if (target && typeof target === "object" && target.nodeType === 1) {
|
|
1929
|
+
el = target;
|
|
1930
|
+
} else if (
|
|
1931
|
+
// Check if target is an IMA AdsManager
|
|
1932
|
+
typeof target.addEventListener === "function" && (typeof target.init === "function" || typeof target.start === "function" || typeof target.getCuePoints === "function" || typeof target.getVolume === "function" || target.__imaAdsManager)
|
|
1933
|
+
) {
|
|
1934
|
+
return this.attachImaAdsManager(target, options);
|
|
1935
|
+
} else if (target && typeof target.addEventListener === "function") {
|
|
1936
|
+
el = target;
|
|
1937
|
+
}
|
|
1938
|
+
const slotId = options?.slotId || options?.adUnitCode || (el ? el.id : "") || "video";
|
|
1939
|
+
const adUnitCode = options?.adUnitCode || slotId;
|
|
1940
|
+
const auctionId = options?.auctionId || "";
|
|
1941
|
+
const transactionId = options?.transactionId || "";
|
|
1942
|
+
const getWinningBid = () => {
|
|
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);
|
|
1944
|
+
};
|
|
1945
|
+
let videoEl = null;
|
|
1946
|
+
if (el) {
|
|
1947
|
+
if (el.tagName && el.tagName.toLowerCase() === "video") {
|
|
1948
|
+
videoEl = el;
|
|
1949
|
+
} else {
|
|
1950
|
+
videoEl = el.querySelector("video");
|
|
1951
|
+
}
|
|
1952
|
+
}
|
|
1953
|
+
const quartilesFired = {
|
|
1954
|
+
q1: false,
|
|
1955
|
+
q2: false,
|
|
1956
|
+
q3: false,
|
|
1957
|
+
q4: false
|
|
1958
|
+
};
|
|
1959
|
+
const triggerPlaybackImpression = (activeVideo) => {
|
|
1960
|
+
const vEl = activeVideo || videoEl || (el?.querySelector ? el.querySelector("video") : null);
|
|
1961
|
+
const cached = getWinningBid();
|
|
1962
|
+
const mergedBid = {
|
|
1963
|
+
...cached?.rawBid || options?.bid,
|
|
1964
|
+
mediaType: "video"
|
|
1965
|
+
};
|
|
1966
|
+
const emitted = this.recordImpression(slotId, {
|
|
1967
|
+
adUnitCode,
|
|
1968
|
+
auctionId: auctionId || cached?.auctionId,
|
|
1969
|
+
transactionId: transactionId || cached?.transactionId,
|
|
1970
|
+
mediaType: "video",
|
|
1971
|
+
bid: mergedBid,
|
|
1972
|
+
metadata: {
|
|
1973
|
+
...options?.metadata,
|
|
1974
|
+
media_type: "video",
|
|
1975
|
+
...vEl && Number.isFinite(vEl.duration) ? { video_duration_s: String(Math.round(vEl.duration)) } : {}
|
|
1976
|
+
}
|
|
1977
|
+
});
|
|
1978
|
+
if (emitted && options?.onImpression) {
|
|
1979
|
+
try {
|
|
1980
|
+
options.onImpression(slotId, { element: vEl || el, bid: mergedBid });
|
|
1981
|
+
} catch (e) {
|
|
1982
|
+
this.log("ERROR", "Error in onImpression callback", e);
|
|
1983
|
+
}
|
|
1984
|
+
}
|
|
1985
|
+
};
|
|
1986
|
+
const handlePlaying = (e) => {
|
|
1987
|
+
this.log("DEBUG", "HTML5 video playing event received");
|
|
1988
|
+
const targetVideo = e?.target instanceof HTMLVideoElement ? e.target : videoEl;
|
|
1989
|
+
triggerPlaybackImpression(targetVideo);
|
|
1990
|
+
};
|
|
1991
|
+
const handleTimeUpdate = (e) => {
|
|
1992
|
+
const targetVideo = e?.target instanceof HTMLVideoElement ? e.target : videoEl;
|
|
1993
|
+
if (targetVideo && targetVideo.currentTime > 0) {
|
|
1994
|
+
triggerPlaybackImpression(targetVideo);
|
|
1995
|
+
if (Number.isFinite(targetVideo.duration) && targetVideo.duration > 0) {
|
|
1996
|
+
const progress = targetVideo.currentTime / targetVideo.duration;
|
|
1997
|
+
if (progress >= 0.25 && !quartilesFired.q1) {
|
|
1998
|
+
quartilesFired.q1 = true;
|
|
1999
|
+
options?.onMilestone?.("firstQuartile", slotId);
|
|
2000
|
+
}
|
|
2001
|
+
if (progress >= 0.5 && !quartilesFired.q2) {
|
|
2002
|
+
quartilesFired.q2 = true;
|
|
2003
|
+
options?.onMilestone?.("midpoint", slotId);
|
|
2004
|
+
}
|
|
2005
|
+
if (progress >= 0.75 && !quartilesFired.q3) {
|
|
2006
|
+
quartilesFired.q3 = true;
|
|
2007
|
+
options?.onMilestone?.("thirdQuartile", slotId);
|
|
2008
|
+
}
|
|
2009
|
+
}
|
|
2010
|
+
}
|
|
2011
|
+
};
|
|
2012
|
+
const handleEnded = () => {
|
|
2013
|
+
if (!quartilesFired.q4) {
|
|
2014
|
+
quartilesFired.q4 = true;
|
|
2015
|
+
options?.onMilestone?.("complete", slotId);
|
|
2016
|
+
}
|
|
2017
|
+
};
|
|
2018
|
+
const handleError = (e) => {
|
|
2019
|
+
const targetVideo = e?.target instanceof HTMLVideoElement ? e.target : videoEl;
|
|
2020
|
+
const err = targetVideo?.error || videoEl?.error;
|
|
2021
|
+
const cached = getWinningBid();
|
|
2022
|
+
const msg = err ? `HTML5 video error code ${err.code}: ${err.message}` : "Video playback error";
|
|
2023
|
+
this.log("WARN", msg);
|
|
2024
|
+
this.enqueue(TraceEventType.AD_RENDER_FAILED, "adRenderFailed", {
|
|
2025
|
+
auctionId: auctionId || cached?.auctionId,
|
|
2026
|
+
transactionId: transactionId || cached?.transactionId,
|
|
2027
|
+
adUnitCode,
|
|
2028
|
+
bid: cached?.bidTrace || options?.bid,
|
|
2029
|
+
metadata: {
|
|
2030
|
+
...options?.metadata,
|
|
2031
|
+
reason: "html5_video_error",
|
|
2032
|
+
message: msg,
|
|
2033
|
+
...err?.code ? { video_error_code: String(err.code) } : {}
|
|
2034
|
+
},
|
|
2035
|
+
error: err ? new Error(msg) : void 0
|
|
2036
|
+
});
|
|
2037
|
+
if (options?.onError) {
|
|
2038
|
+
try {
|
|
2039
|
+
options.onError(err);
|
|
2040
|
+
} catch (e2) {
|
|
2041
|
+
this.log("ERROR", "Error in onError callback", e2);
|
|
2042
|
+
}
|
|
2043
|
+
}
|
|
2044
|
+
};
|
|
2045
|
+
const attachListenersToVideo = (targetVideo) => {
|
|
2046
|
+
targetVideo.addEventListener("playing", handlePlaying);
|
|
2047
|
+
targetVideo.addEventListener("play", handlePlaying);
|
|
2048
|
+
targetVideo.addEventListener("timeupdate", handleTimeUpdate);
|
|
2049
|
+
targetVideo.addEventListener("ended", handleEnded);
|
|
2050
|
+
targetVideo.addEventListener("error", handleError);
|
|
2051
|
+
};
|
|
2052
|
+
const removeListenersFromVideo = (targetVideo) => {
|
|
2053
|
+
targetVideo.removeEventListener("playing", handlePlaying);
|
|
2054
|
+
targetVideo.removeEventListener("play", handlePlaying);
|
|
2055
|
+
targetVideo.removeEventListener("timeupdate", handleTimeUpdate);
|
|
2056
|
+
targetVideo.removeEventListener("ended", handleEnded);
|
|
2057
|
+
targetVideo.removeEventListener("error", handleError);
|
|
2058
|
+
};
|
|
2059
|
+
let mutationObserver = null;
|
|
2060
|
+
if (el && el !== videoEl) {
|
|
2061
|
+
el.addEventListener("playing", handlePlaying, true);
|
|
2062
|
+
el.addEventListener("play", handlePlaying, true);
|
|
2063
|
+
el.addEventListener("timeupdate", handleTimeUpdate, true);
|
|
2064
|
+
el.addEventListener("ended", handleEnded, true);
|
|
2065
|
+
el.addEventListener("error", handleError, true);
|
|
2066
|
+
}
|
|
2067
|
+
if (videoEl) {
|
|
2068
|
+
attachListenersToVideo(videoEl);
|
|
2069
|
+
}
|
|
2070
|
+
if (el && el.tagName?.toLowerCase() !== "video" && typeof MutationObserver !== "undefined") {
|
|
2071
|
+
mutationObserver = new MutationObserver(() => {
|
|
2072
|
+
const found = el.querySelector("video");
|
|
2073
|
+
if (found && found !== videoEl) {
|
|
2074
|
+
if (videoEl) removeListenersFromVideo(videoEl);
|
|
2075
|
+
videoEl = found;
|
|
2076
|
+
if (videoEl) attachListenersToVideo(videoEl);
|
|
2077
|
+
}
|
|
2078
|
+
});
|
|
2079
|
+
mutationObserver.observe(el, { childList: true, subtree: true });
|
|
2080
|
+
}
|
|
2081
|
+
const cachedInitial = getWinningBid();
|
|
2082
|
+
if (options?.trackViewability !== false && el) {
|
|
2083
|
+
this.observeSlot(el, slotId, {
|
|
2084
|
+
adUnitCode,
|
|
2085
|
+
auctionId: auctionId || cachedInitial?.auctionId,
|
|
2086
|
+
transactionId: transactionId || cachedInitial?.transactionId,
|
|
2087
|
+
mediaType: "video",
|
|
2088
|
+
bid: cachedInitial?.bidTrace || options?.bid,
|
|
2089
|
+
metadata: options?.metadata,
|
|
2090
|
+
emitRefreshEvent: false
|
|
2091
|
+
});
|
|
2092
|
+
}
|
|
2093
|
+
const cleanup = () => {
|
|
2094
|
+
if (mutationObserver) {
|
|
2095
|
+
mutationObserver.disconnect();
|
|
2096
|
+
mutationObserver = null;
|
|
2097
|
+
}
|
|
2098
|
+
if (el && el !== videoEl) {
|
|
2099
|
+
el.removeEventListener("playing", handlePlaying, true);
|
|
2100
|
+
el.removeEventListener("play", handlePlaying, true);
|
|
2101
|
+
el.removeEventListener("timeupdate", handleTimeUpdate, true);
|
|
2102
|
+
el.removeEventListener("ended", handleEnded, true);
|
|
2103
|
+
el.removeEventListener("error", handleError, true);
|
|
2104
|
+
}
|
|
2105
|
+
if (videoEl) {
|
|
2106
|
+
removeListenersFromVideo(videoEl);
|
|
2107
|
+
}
|
|
2108
|
+
this.videoDetachCleanups.delete(cleanup);
|
|
2109
|
+
};
|
|
2110
|
+
this.videoDetachCleanups.add(cleanup);
|
|
2111
|
+
return cleanup;
|
|
1398
2112
|
}
|
|
1399
2113
|
onTimeInViewThreshold(slotId, thresholdMs, callback) {
|
|
1400
2114
|
const listener = {
|
|
@@ -1462,6 +2176,10 @@ var BidkernelPrebidAnalytics = class _BidkernelPrebidAnalytics {
|
|
|
1462
2176
|
refreshIndex: record.refreshIndex
|
|
1463
2177
|
};
|
|
1464
2178
|
}
|
|
2179
|
+
/** The validated ingest endpoint in use, or "" when none passed validation. */
|
|
2180
|
+
getEndpoint() {
|
|
2181
|
+
return this.config.endpoint;
|
|
2182
|
+
}
|
|
1465
2183
|
getRefreshIndex(slotId) {
|
|
1466
2184
|
return this.slotRefreshIndices.get(slotId) ?? 0;
|
|
1467
2185
|
}
|
|
@@ -1502,17 +2220,29 @@ var BidkernelPrebidAnalytics = class _BidkernelPrebidAnalytics {
|
|
|
1502
2220
|
handleAuctionInit(data) {
|
|
1503
2221
|
if (this.isDuplicate("auctionInit", data)) return;
|
|
1504
2222
|
this.log("DEBUG", "auctionInit", data);
|
|
2223
|
+
const auctionId = data.auctionId || "";
|
|
2224
|
+
if (auctionId) {
|
|
2225
|
+
this.auctionBidderOutcomes.delete(auctionId);
|
|
2226
|
+
this.getOrCreateAuctionOutcomes(auctionId);
|
|
2227
|
+
}
|
|
1505
2228
|
this.enqueue(TraceEventType.AUCTION_START, "auctionStart", {
|
|
1506
|
-
auctionId
|
|
2229
|
+
auctionId,
|
|
1507
2230
|
transactionId: data.transactionId || ""
|
|
1508
2231
|
});
|
|
1509
2232
|
}
|
|
1510
2233
|
handleAuctionEnd(data) {
|
|
1511
2234
|
if (this.isDuplicate("auctionEnd", data)) return;
|
|
1512
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
|
+
}
|
|
1513
2242
|
this.enqueue(TraceEventType.AUCTION_END, "auctionEnd", {
|
|
1514
|
-
auctionId
|
|
1515
|
-
transactionId: data.transactionId || ""
|
|
2243
|
+
auctionId,
|
|
2244
|
+
transactionId: data.transactionId || "",
|
|
2245
|
+
bidderOutcomes
|
|
1516
2246
|
});
|
|
1517
2247
|
}
|
|
1518
2248
|
handleBidRequested(data) {
|
|
@@ -1520,121 +2250,170 @@ var BidkernelPrebidAnalytics = class _BidkernelPrebidAnalytics {
|
|
|
1520
2250
|
this.log("DEBUG", "bidRequested", data);
|
|
1521
2251
|
const auctionId = data.auctionId || "";
|
|
1522
2252
|
const bidder = data.bidderCode || data.bidder || "";
|
|
2253
|
+
if (!auctionId || !bidder) return;
|
|
2254
|
+
const auctionMap = this.getOrCreateAuctionOutcomes(auctionId);
|
|
1523
2255
|
if (Array.isArray(data.bids)) {
|
|
1524
2256
|
data.bids.forEach((bid) => {
|
|
2257
|
+
const adUnitCode = bid.adUnitCode || "";
|
|
1525
2258
|
const mediaTypes = Object.keys(bid.mediaTypes || {});
|
|
1526
|
-
const
|
|
1527
|
-
const
|
|
1528
|
-
if (
|
|
1529
|
-
|
|
1530
|
-
|
|
1531
|
-
|
|
1532
|
-
|
|
1533
|
-
|
|
1534
|
-
|
|
1535
|
-
gpid,
|
|
1536
|
-
bid: {
|
|
1537
|
-
bidder,
|
|
1538
|
-
mediaType,
|
|
1539
|
-
width,
|
|
1540
|
-
height
|
|
1541
|
-
}
|
|
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
|
|
1542
2268
|
});
|
|
1543
|
-
return;
|
|
1544
2269
|
}
|
|
1545
|
-
mediaTypes.forEach((mediaType) => {
|
|
1546
|
-
let rawSize = null;
|
|
1547
|
-
if (mediaType === "banner") {
|
|
1548
|
-
rawSize = bid.mediaTypes?.banner?.sizes || bid.sizes;
|
|
1549
|
-
} else if (mediaType === "video") {
|
|
1550
|
-
rawSize = bid.mediaTypes?.video?.playerSize || bid.mediaTypes?.video?.sizes || bid.playerSize;
|
|
1551
|
-
} else {
|
|
1552
|
-
rawSize = bid.mediaTypes?.[mediaType]?.sizes;
|
|
1553
|
-
}
|
|
1554
|
-
const { width, height } = parseSize(rawSize);
|
|
1555
|
-
this.enqueue(TraceEventType.BID_REQUEST, "bidRequest", {
|
|
1556
|
-
auctionId,
|
|
1557
|
-
transactionId,
|
|
1558
|
-
adUnitCode: bid.adUnitCode || "",
|
|
1559
|
-
gpid,
|
|
1560
|
-
bid: {
|
|
1561
|
-
bidder,
|
|
1562
|
-
mediaType,
|
|
1563
|
-
width,
|
|
1564
|
-
height
|
|
1565
|
-
}
|
|
1566
|
-
});
|
|
1567
|
-
});
|
|
1568
2270
|
});
|
|
1569
2271
|
}
|
|
1570
2272
|
}
|
|
1571
2273
|
handleBidResponse(data) {
|
|
1572
2274
|
if (this.isDuplicate("bidResponse", data)) return;
|
|
1573
2275
|
this.log("DEBUG", "bidResponse", data);
|
|
1574
|
-
|
|
1575
|
-
|
|
1576
|
-
|
|
1577
|
-
|
|
1578
|
-
|
|
1579
|
-
|
|
1580
|
-
|
|
1581
|
-
|
|
1582
|
-
|
|
1583
|
-
|
|
1584
|
-
|
|
1585
|
-
|
|
1586
|
-
|
|
1587
|
-
|
|
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
|
+
});
|
|
1588
2299
|
}
|
|
1589
|
-
}
|
|
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
|
+
}
|
|
1590
2319
|
}
|
|
1591
2320
|
handleBidTimeout(data) {
|
|
1592
2321
|
if (this.isDuplicate("bidTimeout", data)) return;
|
|
1593
2322
|
this.log("DEBUG", "bidTimeout", data);
|
|
1594
2323
|
const items = Array.isArray(data) ? data : data ? [data] : [];
|
|
1595
2324
|
for (const t of items) {
|
|
1596
|
-
|
|
1597
|
-
|
|
1598
|
-
|
|
1599
|
-
|
|
1600
|
-
|
|
1601
|
-
|
|
1602
|
-
|
|
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
|
+
});
|
|
1603
2344
|
}
|
|
1604
|
-
}
|
|
2345
|
+
}
|
|
1605
2346
|
}
|
|
1606
2347
|
}
|
|
1607
2348
|
handleBidWon(data) {
|
|
1608
2349
|
if (this.isDuplicate("bidWon", data)) return;
|
|
1609
2350
|
this.log("DEBUG", "bidWon", data);
|
|
1610
|
-
|
|
1611
|
-
|
|
1612
|
-
|
|
1613
|
-
|
|
1614
|
-
|
|
1615
|
-
|
|
1616
|
-
|
|
1617
|
-
|
|
1618
|
-
|
|
1619
|
-
|
|
1620
|
-
|
|
1621
|
-
|
|
1622
|
-
|
|
1623
|
-
|
|
2351
|
+
const auctionId = data.auctionId || "";
|
|
2352
|
+
const transactionId = data.transactionId || "";
|
|
2353
|
+
const adUnitCode = data.adUnitCode || data.adId || "";
|
|
2354
|
+
const bidTrace = {
|
|
2355
|
+
bidder: data.bidderCode || data.bidder || "",
|
|
2356
|
+
cpm: Number.isFinite(data.originalCpm) ? data.originalCpm : Number.isFinite(data.cpm) ? data.cpm : 0,
|
|
2357
|
+
currency: data.originalCurrency ?? data.currency ?? "USD",
|
|
2358
|
+
...parseBidDimensions(data),
|
|
2359
|
+
dealId: data.dealId || "",
|
|
2360
|
+
mediaType: data.mediaType || "banner",
|
|
2361
|
+
latencyMs: Number.isFinite(data.timeToRespond) ? data.timeToRespond : 0,
|
|
2362
|
+
advertiserDomain: data.meta?.advertiserDomains?.[0] || "",
|
|
2363
|
+
creativeId: data.creativeId || ""
|
|
2364
|
+
};
|
|
2365
|
+
if (adUnitCode) {
|
|
2366
|
+
const cachedEntry = {
|
|
2367
|
+
bidTrace,
|
|
2368
|
+
auctionId,
|
|
2369
|
+
transactionId,
|
|
2370
|
+
adUnitCode,
|
|
2371
|
+
rawBid: data,
|
|
2372
|
+
timestamp: Date.now()
|
|
2373
|
+
};
|
|
2374
|
+
this.cachedWinningBids.set(adUnitCode, cachedEntry);
|
|
2375
|
+
if (data.adId && data.adId !== adUnitCode) {
|
|
2376
|
+
this.cachedWinningBids.set(data.adId, cachedEntry);
|
|
1624
2377
|
}
|
|
2378
|
+
if (auctionId) {
|
|
2379
|
+
this.cachedWinningBids.set(`${auctionId}:${adUnitCode}`, cachedEntry);
|
|
2380
|
+
if (data.adId && data.adId !== adUnitCode) {
|
|
2381
|
+
this.cachedWinningBids.set(`${auctionId}:${data.adId}`, cachedEntry);
|
|
2382
|
+
}
|
|
2383
|
+
}
|
|
2384
|
+
this.pruneCachedWinningBids();
|
|
2385
|
+
}
|
|
2386
|
+
this.enqueue(TraceEventType.BID_WIN, "bidWon", {
|
|
2387
|
+
auctionId,
|
|
2388
|
+
transactionId,
|
|
2389
|
+
adUnitCode,
|
|
2390
|
+
bid: bidTrace
|
|
1625
2391
|
});
|
|
1626
2392
|
}
|
|
1627
2393
|
handleNoBid(data) {
|
|
1628
2394
|
if (this.isDuplicate("noBid", data)) return;
|
|
1629
2395
|
this.log("DEBUG", "noBid", data);
|
|
1630
|
-
|
|
1631
|
-
|
|
1632
|
-
|
|
1633
|
-
|
|
1634
|
-
|
|
1635
|
-
|
|
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
|
+
});
|
|
1636
2415
|
}
|
|
1637
|
-
}
|
|
2416
|
+
}
|
|
1638
2417
|
}
|
|
1639
2418
|
handleAdRenderFailed(data) {
|
|
1640
2419
|
if (this.isDuplicate("adRenderFailed", data)) return;
|
|
@@ -1660,44 +2439,81 @@ var BidkernelPrebidAnalytics = class _BidkernelPrebidAnalytics {
|
|
|
1660
2439
|
if (this.isDuplicate("adRenderSucceeded", data)) return;
|
|
1661
2440
|
this.log("DEBUG", "adRenderSucceeded", data);
|
|
1662
2441
|
const bid = data.bid || data || {};
|
|
1663
|
-
const adUnitCode = data.adUnitCode || bid.adUnitCode || "";
|
|
2442
|
+
const adUnitCode = data.adUnitCode || bid.adUnitCode || data.adId || bid.adId || "";
|
|
1664
2443
|
const auctionId = bid.auctionId || data.auctionId || "";
|
|
1665
2444
|
const transactionId = bid.transactionId || data.transactionId || "";
|
|
1666
2445
|
const mediaType = bid.mediaType || "banner";
|
|
2446
|
+
const slotKey = data.adUnitCode || bid.adUnitCode || "";
|
|
2447
|
+
const altKey = data.adId || bid.adId || "";
|
|
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);
|
|
1667
2449
|
const bidTrace = {
|
|
1668
|
-
bidder: bid.bidderCode || bid.bidder || "",
|
|
1669
|
-
cpm: Number.isFinite(bid.originalCpm) ? bid.originalCpm : Number.isFinite(bid.cpm) ? bid.cpm : 0,
|
|
1670
|
-
currency: bid.originalCurrency ?? bid.currency ?? "USD",
|
|
1671
|
-
...parseBidDimensions(bid),
|
|
1672
|
-
dealId: bid.dealId || "",
|
|
1673
|
-
mediaType,
|
|
1674
|
-
latencyMs: Number.isFinite(bid.timeToRespond) ? bid.timeToRespond : 0,
|
|
1675
|
-
advertiserDomain: bid.meta?.advertiserDomains?.[0] || "",
|
|
1676
|
-
creativeId: bid.creativeId || ""
|
|
2450
|
+
bidder: bid.bidderCode || bid.bidder || cached?.bidTrace?.bidder || "",
|
|
2451
|
+
cpm: Number.isFinite(bid.originalCpm) ? bid.originalCpm : Number.isFinite(bid.cpm) ? bid.cpm : cached?.bidTrace?.cpm ?? 0,
|
|
2452
|
+
currency: bid.originalCurrency ?? bid.currency ?? cached?.bidTrace?.currency ?? "USD",
|
|
2453
|
+
...parseBidDimensions(bid).width ? parseBidDimensions(bid) : cached?.bidTrace ? { width: cached.bidTrace.width, height: cached.bidTrace.height } : parseBidDimensions(bid),
|
|
2454
|
+
dealId: bid.dealId || cached?.bidTrace?.dealId || "",
|
|
2455
|
+
mediaType: mediaType || cached?.bidTrace?.mediaType || "banner",
|
|
2456
|
+
latencyMs: Number.isFinite(bid.timeToRespond) ? bid.timeToRespond : cached?.bidTrace?.latencyMs ?? 0,
|
|
2457
|
+
advertiserDomain: bid.meta?.advertiserDomains?.[0] || cached?.bidTrace?.advertiserDomain || "",
|
|
2458
|
+
creativeId: bid.creativeId || cached?.bidTrace?.creativeId || ""
|
|
1677
2459
|
};
|
|
1678
|
-
const
|
|
2460
|
+
const resolvedAuctionId = auctionId || cached?.auctionId || "";
|
|
2461
|
+
const resolvedTransactionId = transactionId || cached?.transactionId || "";
|
|
2462
|
+
const record = adUnitCode ? this.slotViewabilityRecords.get(adUnitCode) : void 0;
|
|
2463
|
+
const isDifferentAuction = record && resolvedAuctionId && record.auctionId && record.auctionId !== resolvedAuctionId;
|
|
2464
|
+
const isRefresh = Boolean(adUnitCode && record && isDifferentAuction);
|
|
1679
2465
|
if (isRefresh) {
|
|
1680
2466
|
this.flushSlotTimeInView(adUnitCode);
|
|
1681
|
-
const nextRefreshIndex = (this.slotRefreshIndices.get(adUnitCode) ?? 0) + 1;
|
|
2467
|
+
const nextRefreshIndex = (this.slotRefreshIndices.get(adUnitCode) ?? (record ? record.refreshIndex : 0)) + 1;
|
|
1682
2468
|
this.slotRefreshIndices.set(adUnitCode, nextRefreshIndex);
|
|
2469
|
+
this.slotLastAuctionIds.set(adUnitCode, resolvedAuctionId);
|
|
2470
|
+
if (record) {
|
|
2471
|
+
record.refreshIndex = nextRefreshIndex;
|
|
2472
|
+
record.auctionId = resolvedAuctionId;
|
|
2473
|
+
record.transactionId = resolvedTransactionId;
|
|
2474
|
+
record.bidPayload = bidTrace;
|
|
2475
|
+
record.viewableFired = false;
|
|
2476
|
+
record.accumulatedTimeInViewMs = 0;
|
|
2477
|
+
}
|
|
1683
2478
|
this.enqueue(TraceEventType.REFRESH, "refresh", {
|
|
1684
|
-
auctionId,
|
|
1685
|
-
transactionId,
|
|
2479
|
+
auctionId: resolvedAuctionId,
|
|
2480
|
+
transactionId: resolvedTransactionId,
|
|
1686
2481
|
adUnitCode,
|
|
1687
2482
|
bid: bidTrace,
|
|
1688
2483
|
metadata: { refresh_index: String(nextRefreshIndex) }
|
|
1689
2484
|
});
|
|
1690
|
-
} else if (adUnitCode
|
|
1691
|
-
|
|
2485
|
+
} else if (adUnitCode) {
|
|
2486
|
+
if (resolvedAuctionId) {
|
|
2487
|
+
this.slotLastAuctionIds.set(adUnitCode, resolvedAuctionId);
|
|
2488
|
+
}
|
|
2489
|
+
if (!this.slotRefreshIndices.has(adUnitCode)) {
|
|
2490
|
+
this.slotRefreshIndices.set(adUnitCode, 0);
|
|
2491
|
+
}
|
|
1692
2492
|
}
|
|
1693
|
-
const currentRefreshIndex = adUnitCode ? this.slotRefreshIndices.get(adUnitCode) ?? 0 : 0;
|
|
1694
|
-
this.
|
|
1695
|
-
auctionId,
|
|
1696
|
-
transactionId,
|
|
2493
|
+
const currentRefreshIndex = adUnitCode ? this.slotRefreshIndices.get(adUnitCode) ?? (record ? record.refreshIndex : 0) : 0;
|
|
2494
|
+
const alreadyEmitted = adUnitCode ? this.hasImpressionEmitted(
|
|
1697
2495
|
adUnitCode,
|
|
1698
|
-
|
|
1699
|
-
|
|
1700
|
-
|
|
2496
|
+
currentRefreshIndex,
|
|
2497
|
+
resolvedAuctionId,
|
|
2498
|
+
bidTrace.creativeId
|
|
2499
|
+
) : false;
|
|
2500
|
+
if (!alreadyEmitted) {
|
|
2501
|
+
if (adUnitCode) {
|
|
2502
|
+
this.markImpressionEmitted(
|
|
2503
|
+
adUnitCode,
|
|
2504
|
+
currentRefreshIndex,
|
|
2505
|
+
resolvedAuctionId,
|
|
2506
|
+
bidTrace.creativeId
|
|
2507
|
+
);
|
|
2508
|
+
}
|
|
2509
|
+
this.enqueue(TraceEventType.IMPRESSION, "impression", {
|
|
2510
|
+
auctionId: resolvedAuctionId,
|
|
2511
|
+
transactionId: resolvedTransactionId,
|
|
2512
|
+
adUnitCode,
|
|
2513
|
+
bid: bidTrace,
|
|
2514
|
+
metadata: { refresh_index: String(currentRefreshIndex) }
|
|
2515
|
+
});
|
|
2516
|
+
}
|
|
1701
2517
|
if (this.config.viewabilityEnabled && typeof document !== "undefined") {
|
|
1702
2518
|
let el = null;
|
|
1703
2519
|
const targetId = adUnitCode || data.adId || bid.adId;
|
|
@@ -1718,15 +2534,20 @@ var BidkernelPrebidAnalytics = class _BidkernelPrebidAnalytics {
|
|
|
1718
2534
|
}
|
|
1719
2535
|
if (!el && targetId) {
|
|
1720
2536
|
try {
|
|
1721
|
-
|
|
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
|
+
}
|
|
1722
2543
|
} catch {
|
|
1723
2544
|
}
|
|
1724
2545
|
}
|
|
1725
2546
|
if (el) {
|
|
1726
2547
|
this.observeSlot(el, adUnitCode || targetId, {
|
|
1727
2548
|
adUnitCode,
|
|
1728
|
-
auctionId,
|
|
1729
|
-
transactionId,
|
|
2549
|
+
auctionId: resolvedAuctionId,
|
|
2550
|
+
transactionId: resolvedTransactionId,
|
|
1730
2551
|
mediaType,
|
|
1731
2552
|
bid: bidTrace,
|
|
1732
2553
|
refreshIndex: currentRefreshIndex,
|
|
@@ -1772,6 +2593,9 @@ var BidkernelPrebidAnalytics = class _BidkernelPrebidAnalytics {
|
|
|
1772
2593
|
if (data.viewableDurationMs !== void 0) {
|
|
1773
2594
|
protoEvent.viewableDurationMs = Number.isFinite(data.viewableDurationMs) ? data.viewableDurationMs : 0;
|
|
1774
2595
|
}
|
|
2596
|
+
if (data.bidderOutcomes && Array.isArray(data.bidderOutcomes)) {
|
|
2597
|
+
protoEvent.bidderOutcomes = data.bidderOutcomes;
|
|
2598
|
+
}
|
|
1775
2599
|
const metadata = { ...data.metadata };
|
|
1776
2600
|
if (data.error !== void 0 && data.error !== null) {
|
|
1777
2601
|
metadata.error = typeof data.error === "object" && data.error.message ? String(data.error.message) : String(data.error);
|
|
@@ -1782,8 +2606,9 @@ var BidkernelPrebidAnalytics = class _BidkernelPrebidAnalytics {
|
|
|
1782
2606
|
if (data.refreshIndex !== void 0 && metadata.refresh_index === void 0) {
|
|
1783
2607
|
metadata.refresh_index = String(data.refreshIndex);
|
|
1784
2608
|
}
|
|
1785
|
-
|
|
1786
|
-
|
|
2609
|
+
const boundedMetadata = boundMetadata(metadata);
|
|
2610
|
+
if (Object.keys(boundedMetadata).length > 0) {
|
|
2611
|
+
protoEvent.metadata = boundedMetadata;
|
|
1787
2612
|
}
|
|
1788
2613
|
this.queue.push(protoEvent);
|
|
1789
2614
|
if (this.queue.length > MAX_QUEUE_SIZE) {
|
|
@@ -1821,7 +2646,6 @@ var BidkernelPrebidAnalytics = class _BidkernelPrebidAnalytics {
|
|
|
1821
2646
|
// Shared by flush() and flushBeacon().
|
|
1822
2647
|
drainBatch() {
|
|
1823
2648
|
if (this.queue.length === 0 || !this.config.endpoint) return null;
|
|
1824
|
-
let events = this.queue;
|
|
1825
2649
|
const domain = typeof window !== "undefined" ? window.location.hostname : "";
|
|
1826
2650
|
const buildBatch = (evts) => ({
|
|
1827
2651
|
propertyId: this.config.propertyId,
|
|
@@ -1835,25 +2659,37 @@ var BidkernelPrebidAnalytics = class _BidkernelPrebidAnalytics {
|
|
|
1835
2659
|
domain,
|
|
1836
2660
|
events: evts
|
|
1837
2661
|
});
|
|
1838
|
-
|
|
1839
|
-
|
|
1840
|
-
|
|
1841
|
-
|
|
1842
|
-
events = events.slice(0, Math.floor(events.length / 2));
|
|
1843
|
-
encoded = TraceEventBatch.encode(
|
|
2662
|
+
for (; ; ) {
|
|
2663
|
+
if (this.queue.length === 0) return null;
|
|
2664
|
+
let events = this.queue;
|
|
2665
|
+
let encoded = TraceEventBatch.encode(
|
|
1844
2666
|
buildBatch(events)
|
|
1845
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
|
+
};
|
|
1846
2690
|
}
|
|
1847
|
-
this.queue = this.queue.slice(events.length);
|
|
1848
|
-
return {
|
|
1849
|
-
url: `${this.config.endpoint}/${this.config.propertyId}`,
|
|
1850
|
-
// protobufjs types finish() as Uint8Array<ArrayBufferLike>; the buffer is
|
|
1851
|
-
// always a plain ArrayBuffer, so narrow for fetch/Blob compatibility.
|
|
1852
|
-
encoded,
|
|
1853
|
-
events
|
|
1854
|
-
};
|
|
1855
2691
|
}
|
|
1856
|
-
sendFetch(payload, useKeepalive = false, onDelivered) {
|
|
2692
|
+
sendFetch(payload, useKeepalive = false, onDelivered, isReplay = false) {
|
|
1857
2693
|
const fetchOpts = {
|
|
1858
2694
|
method: "POST",
|
|
1859
2695
|
// Safelisted content type: no CORS preflight (see SAFE_CONTENT_TYPE).
|
|
@@ -1870,15 +2706,17 @@ var BidkernelPrebidAnalytics = class _BidkernelPrebidAnalytics {
|
|
|
1870
2706
|
this.log("WARN", `Dropping batch due to non-retryable client error HTTP ${res.status}`);
|
|
1871
2707
|
return;
|
|
1872
2708
|
}
|
|
1873
|
-
this.handleSendFailure(payload.events);
|
|
2709
|
+
if (!isReplay) this.handleSendFailure(payload.events);
|
|
1874
2710
|
} else {
|
|
1875
|
-
|
|
1876
|
-
|
|
2711
|
+
if (!isReplay) {
|
|
2712
|
+
this.consecutiveSendFailures = 0;
|
|
2713
|
+
this.nextSendAllowedAt = 0;
|
|
2714
|
+
}
|
|
1877
2715
|
if (onDelivered) onDelivered();
|
|
1878
2716
|
}
|
|
1879
2717
|
}).catch((err) => {
|
|
1880
2718
|
this.log("ERROR", "Failed to send batch", err);
|
|
1881
|
-
this.handleSendFailure(payload.events);
|
|
2719
|
+
if (!isReplay) this.handleSendFailure(payload.events);
|
|
1882
2720
|
});
|
|
1883
2721
|
const proc = typeof globalThis !== "undefined" ? globalThis.process : void 0;
|
|
1884
2722
|
if (proc && typeof proc._tickCallback === "function") {
|
|
@@ -1934,16 +2772,23 @@ var BidkernelPrebidAnalytics = class _BidkernelPrebidAnalytics {
|
|
|
1934
2772
|
this.schedulePersistedCleanup();
|
|
1935
2773
|
}
|
|
1936
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
|
+
}
|
|
1937
2781
|
persistBatch(encoded) {
|
|
1938
2782
|
try {
|
|
1939
2783
|
if (typeof localStorage === "undefined") return null;
|
|
2784
|
+
if (!this.config.propertyId) return null;
|
|
1940
2785
|
let pendingCount = 0;
|
|
1941
2786
|
for (let i = 0; i < localStorage.length; i++) {
|
|
1942
2787
|
const k = localStorage.key(i);
|
|
1943
2788
|
if (k && k.startsWith(PENDING_BATCH_KEY_PREFIX)) pendingCount++;
|
|
1944
2789
|
}
|
|
1945
2790
|
if (pendingCount >= MAX_PENDING_BATCHES) return null;
|
|
1946
|
-
const key = `${
|
|
2791
|
+
const key = `${this.pendingKeyPrefix()}${Date.now()}_${Math.floor(Math.random() * 1e6)}`;
|
|
1947
2792
|
localStorage.setItem(key, bytesToBase64(encoded));
|
|
1948
2793
|
this.persistedBatchKeys.push(key);
|
|
1949
2794
|
return key;
|
|
@@ -1968,22 +2813,39 @@ var BidkernelPrebidAnalytics = class _BidkernelPrebidAnalytics {
|
|
|
1968
2813
|
if (this.persistedCleanupTimer) clearTimeout(this.persistedCleanupTimer);
|
|
1969
2814
|
this.persistedCleanupTimer = setTimeout(() => {
|
|
1970
2815
|
this.persistedCleanupTimer = null;
|
|
1971
|
-
for (const key of
|
|
2816
|
+
for (const key of Array.from(this.persistedBatchKeys)) {
|
|
1972
2817
|
this.removePersistedBatch(key);
|
|
1973
2818
|
}
|
|
1974
2819
|
}, PENDING_BATCH_CLEANUP_DELAY_MS);
|
|
1975
2820
|
}
|
|
1976
2821
|
// Resend batches a previous pageview persisted at exit but could not
|
|
1977
|
-
// confirm.
|
|
1978
|
-
//
|
|
1979
|
-
//
|
|
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.
|
|
1980
2828
|
resendPersistedBatches() {
|
|
1981
2829
|
try {
|
|
1982
2830
|
if (typeof localStorage === "undefined" || !this.config.endpoint) return;
|
|
2831
|
+
if (!this.config.propertyId) return;
|
|
2832
|
+
const ownPrefix = this.pendingKeyPrefix();
|
|
1983
2833
|
const keys = [];
|
|
2834
|
+
const legacyKeys = [];
|
|
1984
2835
|
for (let i = 0; i < localStorage.length; i++) {
|
|
1985
2836
|
const k = localStorage.key(i);
|
|
1986
|
-
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
|
+
}
|
|
1987
2849
|
}
|
|
1988
2850
|
for (const key of keys) {
|
|
1989
2851
|
let value = null;
|
|
@@ -1994,7 +2856,7 @@ var BidkernelPrebidAnalytics = class _BidkernelPrebidAnalytics {
|
|
|
1994
2856
|
continue;
|
|
1995
2857
|
}
|
|
1996
2858
|
if (!value) continue;
|
|
1997
|
-
const ts = parseInt(key.slice(
|
|
2859
|
+
const ts = parseInt(key.slice(ownPrefix.length), 10);
|
|
1998
2860
|
if (!Number.isFinite(ts) || Date.now() - ts > PENDING_BATCH_MAX_AGE_MS) continue;
|
|
1999
2861
|
let encoded;
|
|
2000
2862
|
try {
|
|
@@ -2004,11 +2866,16 @@ var BidkernelPrebidAnalytics = class _BidkernelPrebidAnalytics {
|
|
|
2004
2866
|
}
|
|
2005
2867
|
if (encoded.byteLength === 0) continue;
|
|
2006
2868
|
this.log("DEBUG", `Resending persisted exit batch (${encoded.byteLength} bytes)`);
|
|
2007
|
-
this.sendFetch(
|
|
2008
|
-
|
|
2009
|
-
|
|
2010
|
-
|
|
2011
|
-
|
|
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
|
+
);
|
|
2012
2879
|
}
|
|
2013
2880
|
} catch {
|
|
2014
2881
|
}
|
|
@@ -2033,11 +2900,13 @@ var BidkernelPrebidAnalytics = class _BidkernelPrebidAnalytics {
|
|
|
2033
2900
|
}
|
|
2034
2901
|
}
|
|
2035
2902
|
};
|
|
2036
|
-
function extractAnalyticsOptions(config) {
|
|
2903
|
+
function extractAnalyticsOptions(config, allowProviderless = true) {
|
|
2037
2904
|
if (!config) return null;
|
|
2038
2905
|
if (Array.isArray(config)) {
|
|
2039
|
-
const matched = config.find((item) => item?.provider === "bidkernel") ?? config.find(
|
|
2040
|
-
|
|
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;
|
|
2041
2910
|
}
|
|
2042
2911
|
if (typeof config === "object") {
|
|
2043
2912
|
if (config.options && typeof config.options === "object") {
|
|
@@ -2079,18 +2948,27 @@ function registerPrebidAnalytics(pbjsGlobalName = "pbjs", defaults) {
|
|
|
2079
2948
|
win[pbjsGlobalName] = win[pbjsGlobalName] || {};
|
|
2080
2949
|
const pbjs = win[pbjsGlobalName];
|
|
2081
2950
|
const enableInstance = (options) => {
|
|
2082
|
-
|
|
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) {
|
|
2083
2965
|
try {
|
|
2084
|
-
|
|
2966
|
+
previous.disable();
|
|
2085
2967
|
} catch (e) {
|
|
2086
2968
|
console.warn("[bidkernel] Failed to disable previous analytics instance:", e);
|
|
2087
2969
|
}
|
|
2088
2970
|
}
|
|
2089
|
-
const analytics = new BidkernelPrebidAnalytics(
|
|
2090
|
-
pbjsGlobalName,
|
|
2091
|
-
...defaults,
|
|
2092
|
-
...options
|
|
2093
|
-
});
|
|
2971
|
+
const analytics = new BidkernelPrebidAnalytics(merged);
|
|
2094
2972
|
analytics.enable();
|
|
2095
2973
|
win._bidkernelPrebidAnalytics = analytics;
|
|
2096
2974
|
return analytics;
|
|
@@ -2101,7 +2979,7 @@ function registerPrebidAnalytics(pbjsGlobalName = "pbjs", defaults) {
|
|
|
2101
2979
|
pbjs.adapterManager.registerAnalyticsAdapter({
|
|
2102
2980
|
adapter: {
|
|
2103
2981
|
enableAnalytics: (config) => {
|
|
2104
|
-
const options = extractAnalyticsOptions(config);
|
|
2982
|
+
const options = extractAnalyticsOptions(config, !Array.isArray(config));
|
|
2105
2983
|
if (options) {
|
|
2106
2984
|
enableInstance(options);
|
|
2107
2985
|
}
|
|
@@ -2138,12 +3016,34 @@ function getbidkernel(alias = "bidkernel") {
|
|
|
2138
3016
|
}
|
|
2139
3017
|
const win = window;
|
|
2140
3018
|
win[alias] = win[alias] || { q: [] };
|
|
2141
|
-
|
|
3019
|
+
const sdk = win[alias];
|
|
3020
|
+
if (!sdk.attachVideoPlayer) {
|
|
3021
|
+
sdk.attachVideoPlayer = (target, options) => {
|
|
3022
|
+
const inst = win._bidkernelPrebidAnalytics;
|
|
3023
|
+
return inst?.attachVideoPlayer ? inst.attachVideoPlayer(target, options) : () => {
|
|
3024
|
+
};
|
|
3025
|
+
};
|
|
3026
|
+
}
|
|
3027
|
+
if (!sdk.attachImaAdsManager) {
|
|
3028
|
+
sdk.attachImaAdsManager = (adsManager, options) => {
|
|
3029
|
+
const inst = win._bidkernelPrebidAnalytics;
|
|
3030
|
+
return inst?.attachImaAdsManager ? inst.attachImaAdsManager(adsManager, options) : () => {
|
|
3031
|
+
};
|
|
3032
|
+
};
|
|
3033
|
+
}
|
|
3034
|
+
if (!sdk.recordImpression) {
|
|
3035
|
+
sdk.recordImpression = (slotId, options) => {
|
|
3036
|
+
const inst = win._bidkernelPrebidAnalytics;
|
|
3037
|
+
return inst?.recordImpression ? inst.recordImpression(slotId, options) : false;
|
|
3038
|
+
};
|
|
3039
|
+
}
|
|
3040
|
+
return sdk;
|
|
2142
3041
|
}
|
|
2143
3042
|
export {
|
|
2144
3043
|
BidkernelPrebidAnalytics,
|
|
2145
3044
|
PrebidEventDeduper,
|
|
2146
3045
|
getPrebidEventKey,
|
|
2147
3046
|
getbidkernel,
|
|
3047
|
+
isTrustedEndpoint,
|
|
2148
3048
|
registerPrebidAnalytics
|
|
2149
3049
|
};
|