@optable/web-sdk 0.44.6 → 0.48.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.
@@ -0,0 +1,106 @@
1
+ import type OptableSDK from "../../sdk";
2
+ declare global {
3
+ interface Window {
4
+ pbjs?: any;
5
+ }
6
+ }
7
+ interface OptablePrebidAnalyticsConfig {
8
+ debug?: boolean;
9
+ analytics?: boolean;
10
+ tenant?: string;
11
+ bidWinTimeout?: number;
12
+ samplingVolume?: "session" | "event";
13
+ samplingSeed?: string;
14
+ samplingRate?: number;
15
+ samplingRateFn?: () => boolean;
16
+ }
17
+ declare class OptablePrebidAnalytics {
18
+ private readonly optableInstance;
19
+ private config;
20
+ readonly isInitialized: boolean;
21
+ private readonly labelStyle;
22
+ private readonly maxAuctionDataSize;
23
+ private auctions;
24
+ private prebidInstance;
25
+ /**
26
+ * Create a new OptablePrebidAnalytics instance.
27
+ * @param optableInstance - An initialized Optable SDK instance that exposes a `witness()` method.
28
+ * @param config - Optional configuration for sampling, debug and analytics behavior.
29
+ */
30
+ constructor(optableInstance: OptableSDK, config?: OptablePrebidAnalyticsConfig);
31
+ /**
32
+ * Log messages to the console when debugging is enabled.
33
+ * @param args - Values to log.
34
+ * @returns void
35
+ */
36
+ log(...args: unknown[]): void;
37
+ /**
38
+ * Determine whether the current event/session should be sampled according to
39
+ * the configured sampling rate, seed or function.
40
+ * @returns true if the event should be sampled and analytics calls may proceed.
41
+ */
42
+ shouldSample(): boolean;
43
+ /**
44
+ * Send an event to the Witness API when analytics are enabled and sampling passes.
45
+ * @param eventName - The name of the event to send (e.g. "optable.prebid.auction").
46
+ * @param properties - An object of event properties to include in the payload.
47
+ * @returns A small result object indicating whether the call was disabled or sent.
48
+ */
49
+ sendToWitnessAPI(eventName: string, properties?: Record<string, any>): Promise<{
50
+ disabled: boolean;
51
+ eventName: string;
52
+ properties: Record<string, any>;
53
+ }>;
54
+ /**
55
+ * Attach listeners to a Prebid.js instance and process any missed events.
56
+ * This will replay past `auctionEnd` and `bidWon` events and then register live handlers.
57
+ * @param pbjs - The Prebid.js global instance (or equivalent) to hook into.
58
+ * @returns void
59
+ */
60
+ setHooks(pbjs: any): void;
61
+ /**
62
+ * Hook into Prebid.js by attaching event hooks either immediately or by
63
+ * queueing callbacks when `pbjs.onEvent` is not available yet.
64
+ * @param prebidInstance - Optional Prebid.js instance to use (defaults to `window.pbjs`).
65
+ * @returns true when a hook has been registered, false when Prebid is not present.
66
+ */
67
+ hookIntoPrebid(prebidInstance?: any): boolean;
68
+ /**
69
+ * Process a Prebid `auctionEnd` event: build an internal representation of
70
+ * requests, merge in received bids and schedule a delayed Witness API call
71
+ * (to allow `bidWon` to be received) or mark as missed.
72
+ * @param event - The raw Prebid auctionEnd event object.
73
+ * @param missed - True when the event was previously emitted (missed replay).
74
+ * @returns void
75
+ */
76
+ trackAuctionEnd(event: any, missed?: boolean): Promise<void>;
77
+ /**
78
+ * Handle a Prebid `bidWon` event by finalizing the matching auction, clearing
79
+ * the pending timeout and sending the combined payload to Witness.
80
+ * @param event - The raw Prebid bidWon event object.
81
+ * @param missed - True when the event was previously emitted (missed replay).
82
+ * @returns void
83
+ */
84
+ trackBidWon(event: any, missed?: boolean): Promise<void>;
85
+ /**
86
+ * Clean up old auctions to prevent memory leaks.
87
+ * Removes the oldest auction when the internal store grows past the configured size.
88
+ * @returns void
89
+ */
90
+ cleanupOldAuctions(): void;
91
+ /**
92
+ * Clear all stored analytics data (useful for tests).
93
+ * @returns void
94
+ */
95
+ clearData(): void;
96
+ /**
97
+ * Convert internal auction state and optional bidWon event into a Witness payload.
98
+ * This collects matcher/source metadata, bid counts and optional custom analytics.
99
+ * @param auctionEndEvent - The `auctionEnd` event object from Prebid.js.
100
+ * @param bidWonEvent - Optional `bidWon` event when a winning bid exists.
101
+ * @param missed - True when the original events were already emitted (replayed).
102
+ * @returns A payload object compatible with the Witness API.
103
+ */
104
+ toWitness(auctionEndEvent: any, bidWonEvent: any | null, missed?: boolean): Promise<Record<string, any>>;
105
+ }
106
+ export default OptablePrebidAnalytics;
@@ -0,0 +1,474 @@
1
+ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
2
+ function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
3
+ return new (P || (P = Promise))(function (resolve, reject) {
4
+ function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
5
+ function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
6
+ function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
7
+ step((generator = generator.apply(thisArg, _arguments || [])).next());
8
+ });
9
+ };
10
+ import * as Bowser from "bowser";
11
+ const STATUS = {
12
+ REQUESTED: "REQUESTED",
13
+ RECEIVED: "RECEIVED",
14
+ NO_BID: "NO_BID",
15
+ TIMEOUT: "TIMEOUT",
16
+ };
17
+ const SESSION_SAMPLE_KEY = "optable:prebid:analytics:sample-number";
18
+ class OptablePrebidAnalytics {
19
+ /**
20
+ * Create a new OptablePrebidAnalytics instance.
21
+ * @param optableInstance - An initialized Optable SDK instance that exposes a `witness()` method.
22
+ * @param config - Optional configuration for sampling, debug and analytics behavior.
23
+ */
24
+ constructor(optableInstance, config = { samplingRate: 1, samplingVolume: "event", bidWinTimeout: 10000 }) {
25
+ var _a, _b, _c, _d;
26
+ this.optableInstance = optableInstance;
27
+ this.config = config;
28
+ this.isInitialized = false;
29
+ this.labelStyle = "color: white; background-color: #9198dc; padding: 2px 4px; border-radius: 2px;";
30
+ this.maxAuctionDataSize = 50;
31
+ this.auctions = new Map();
32
+ if (!optableInstance || typeof optableInstance.witness !== "function") {
33
+ throw new Error("OptablePrebidAnalytics requires a valid optable instance with witness() method");
34
+ }
35
+ this.config.debug = (_a = config.debug) !== null && _a !== void 0 ? _a : false;
36
+ this.config.bidWinTimeout = (_b = config.bidWinTimeout) !== null && _b !== void 0 ? _b : 10000;
37
+ this.config.samplingRate = (_c = config.samplingRate) !== null && _c !== void 0 ? _c : 1;
38
+ this.config.samplingVolume = (_d = config.samplingVolume) !== null && _d !== void 0 ? _d : "event";
39
+ if (this.config.samplingVolume === "session") {
40
+ sessionStorage.setItem(SESSION_SAMPLE_KEY, Math.random().toFixed(2));
41
+ }
42
+ else {
43
+ sessionStorage.removeItem(SESSION_SAMPLE_KEY);
44
+ }
45
+ sessionStorage.optableSessionDepth = (Number(sessionStorage === null || sessionStorage === void 0 ? void 0 : sessionStorage.optableSessionDepth) || 0) + 1;
46
+ this.isInitialized = true;
47
+ // Store auction data
48
+ this.maxAuctionDataSize = 50;
49
+ this.log("OptablePrebidAnalytics initialized");
50
+ }
51
+ /**
52
+ * Log messages to the console when debugging is enabled.
53
+ * @param args - Values to log.
54
+ * @returns void
55
+ */
56
+ log(...args) {
57
+ if (this.config.debug) {
58
+ console.log("%cOptable%c [OptablePrebidAnalytics]", this.labelStyle, "color: inherit;", ...args);
59
+ }
60
+ }
61
+ /**
62
+ * Determine whether the current event/session should be sampled according to
63
+ * the configured sampling rate, seed or function.
64
+ * @returns true if the event should be sampled and analytics calls may proceed.
65
+ */
66
+ shouldSample() {
67
+ if (this.config.samplingRate <= 0)
68
+ return false;
69
+ if (this.config.samplingRate >= 1)
70
+ return true;
71
+ if (this.config.samplingRateFn) {
72
+ return this.config.samplingRateFn();
73
+ }
74
+ if (this.config.samplingVolume === "session") {
75
+ const samplingNumber = Number(sessionStorage.getItem(SESSION_SAMPLE_KEY) || "1");
76
+ return samplingNumber < this.config.samplingRate;
77
+ }
78
+ // Optional: deterministic sampling by seed (e.g., user ID)
79
+ if (this.config.samplingSeed) {
80
+ const hash = [...this.config.samplingSeed].reduce((acc, c) => acc + c.charCodeAt(0), 0);
81
+ const normalized = (hash % 10000) / 10000;
82
+ return normalized < this.config.samplingRate;
83
+ }
84
+ // Random sampling
85
+ return Math.random() < this.config.samplingRate;
86
+ }
87
+ /**
88
+ * Send an event to the Witness API when analytics are enabled and sampling passes.
89
+ * @param eventName - The name of the event to send (e.g. "optable.prebid.auction").
90
+ * @param properties - An object of event properties to include in the payload.
91
+ * @returns A small result object indicating whether the call was disabled or sent.
92
+ */
93
+ sendToWitnessAPI(eventName_1) {
94
+ return __awaiter(this, arguments, void 0, function* (eventName, properties = {}) {
95
+ if (!this.config.analytics) {
96
+ this.log("Witness API calls disabled - would send:", eventName, properties);
97
+ return { disabled: true, eventName, properties };
98
+ }
99
+ if (!this.shouldSample()) {
100
+ this.log("Event not sampled - skipping Witness API call for:", eventName, properties);
101
+ return { disabled: true, eventName, properties };
102
+ }
103
+ try {
104
+ yield this.optableInstance.witness(eventName, properties);
105
+ this.log("Sending to Witness API:", eventName, properties);
106
+ }
107
+ catch (error) {
108
+ this.log("Error sending to Witness API:", eventName, properties, error);
109
+ throw error;
110
+ }
111
+ return { disabled: false, eventName, properties };
112
+ });
113
+ }
114
+ /**
115
+ * Attach listeners to a Prebid.js instance and process any missed events.
116
+ * This will replay past `auctionEnd` and `bidWon` events and then register live handlers.
117
+ * @param pbjs - The Prebid.js global instance (or equivalent) to hook into.
118
+ * @returns void
119
+ */
120
+ setHooks(pbjs) {
121
+ this.log("Processing missed auctionEnd");
122
+ pbjs.getEvents().forEach((event) => {
123
+ if (event.eventType === "auctionEnd") {
124
+ this.log("auction missed");
125
+ this.trackAuctionEnd(event.args, true);
126
+ }
127
+ if (event.eventType === "bidWon") {
128
+ this.log("bid won missed");
129
+ this.trackBidWon(event.args, true);
130
+ }
131
+ });
132
+ this.log("Hooking into Prebid.js events");
133
+ pbjs.onEvent("auctionEnd", (event) => {
134
+ this.log("auctionEnd event received");
135
+ this.trackAuctionEnd(event);
136
+ });
137
+ pbjs.onEvent("bidWon", (event) => {
138
+ this.log("bidWon event received");
139
+ this.trackBidWon(event);
140
+ });
141
+ }
142
+ /**
143
+ * Hook into Prebid.js by attaching event hooks either immediately or by
144
+ * queueing callbacks when `pbjs.onEvent` is not available yet.
145
+ * @param prebidInstance - Optional Prebid.js instance to use (defaults to `window.pbjs`).
146
+ * @returns true when a hook has been registered, false when Prebid is not present.
147
+ */
148
+ hookIntoPrebid(prebidInstance = window.pbjs) {
149
+ const pbjs = prebidInstance;
150
+ this.prebidInstance = pbjs;
151
+ if (typeof pbjs === "undefined") {
152
+ this.log("Prebid.js not found");
153
+ return false;
154
+ }
155
+ if (typeof pbjs.onEvent !== "function") {
156
+ pbjs.que = pbjs.que || [];
157
+ pbjs.que.push(() => this.setHooks(pbjs));
158
+ }
159
+ else {
160
+ this.setHooks(pbjs);
161
+ }
162
+ return true;
163
+ }
164
+ /**
165
+ * Process a Prebid `auctionEnd` event: build an internal representation of
166
+ * requests, merge in received bids and schedule a delayed Witness API call
167
+ * (to allow `bidWon` to be received) or mark as missed.
168
+ * @param event - The raw Prebid auctionEnd event object.
169
+ * @param missed - True when the event was previously emitted (missed replay).
170
+ * @returns void
171
+ */
172
+ trackAuctionEnd(event_1) {
173
+ return __awaiter(this, arguments, void 0, function* (event, missed = false) {
174
+ const { auctionId, timeout, bidderRequests = [], bidsReceived = [], noBids = [], timeoutBids = [] } = event;
175
+ this.log(`Processing auction ${auctionId} with ${bidderRequests.length} bidder requests`);
176
+ window.optable = window.optable || {};
177
+ window.optable.pageAuctionsCount = (Number(window.optable.pageAuctionsCount) || 0) + 1;
178
+ // Build auction object with bidder requests and EID flags
179
+ const auction = {
180
+ auctionId,
181
+ timeout,
182
+ bidderRequests: bidderRequests.map((br) => {
183
+ var _a, _b, _c, _d;
184
+ const { bidderCode, bidderRequestId, bids = [] } = br;
185
+ const domain = (_b = (_a = br.ortb2.site) === null || _a === void 0 ? void 0 : _a.domain) !== null && _b !== void 0 ? _b : "unknown";
186
+ const eids = (_d = (_c = br.ortb2.user) === null || _c === void 0 ? void 0 : _c.eids) !== null && _d !== void 0 ? _d : [];
187
+ // Optable EIDs
188
+ const optableEIDS = eids.filter((e) => e.inserter === "optable.co");
189
+ const optableMatchers = [...new Set(optableEIDS.map((e) => e.matcher).filter(Boolean))];
190
+ const optableSources = [...new Set(optableEIDS.map((e) => e.source).filter(Boolean))];
191
+ return {
192
+ bidderCode,
193
+ bidderRequestId,
194
+ domain,
195
+ hasOEids: optableEIDS.length > 0,
196
+ optableMatchers,
197
+ optableSources,
198
+ status: STATUS.REQUESTED,
199
+ bids: bids.map((b) => {
200
+ var _a, _b, _c, _d;
201
+ return ({
202
+ bidId: b.bidId,
203
+ bidderRequestId,
204
+ adUnitCode: b.adUnitCode,
205
+ adUnitId: b.adUnitId,
206
+ transactionId: b.transactionId,
207
+ src: b.src,
208
+ floorMin: (_a = b.floorData) === null || _a === void 0 ? void 0 : _a.floorMin,
209
+ splitTestAssignment: (_d = (_c = (_b = b.ortb2Imp) === null || _b === void 0 ? void 0 : _b.ext) === null || _c === void 0 ? void 0 : _c.optable) === null || _d === void 0 ? void 0 : _d.splitTestAssignment,
210
+ status: STATUS.REQUESTED,
211
+ });
212
+ }),
213
+ };
214
+ }),
215
+ };
216
+ // Build lookup tables for 1:many relationship
217
+ const requestIndex = {};
218
+ const bidIndex = {};
219
+ const bidToRequest = {};
220
+ auction.bidderRequests.forEach((br) => {
221
+ requestIndex[br.bidderRequestId] = br;
222
+ br.bids.forEach((bid) => {
223
+ bidIndex[bid.bidId] = bid;
224
+ bidToRequest[bid.bidId] = br;
225
+ });
226
+ });
227
+ // Merge in bidsReceived → update individual bids as RECEIVED
228
+ bidsReceived.forEach((b) => {
229
+ var _a, _b, _c, _d, _e, _f;
230
+ const bidId = b.requestId;
231
+ const br = bidToRequest[bidId];
232
+ if (!br) {
233
+ this.log(`No bidderRequest found for bidId=${bidId}`);
234
+ return;
235
+ }
236
+ // Find the specific bid to update
237
+ let bidObj = bidIndex[bidId];
238
+ if (bidObj) {
239
+ // Update existing bid
240
+ Object.assign(bidObj, {
241
+ status: STATUS.RECEIVED,
242
+ cpm: b.cpm,
243
+ size: `${b.width}x${b.height}`,
244
+ currency: b.currency,
245
+ splitTestAssignment: (_c = (_b = (_a = b.ortb2Imp) === null || _a === void 0 ? void 0 : _a.ext) === null || _b === void 0 ? void 0 : _b.optable) === null || _c === void 0 ? void 0 : _c.splitTestAssignment,
246
+ });
247
+ }
248
+ else {
249
+ // Create new bid object for this response
250
+ bidObj = {
251
+ bidId,
252
+ bidderRequestId: br.bidderRequestId,
253
+ adUnitCode: b.adUnitCode,
254
+ adUnitId: b.adUnitId,
255
+ transactionId: b.transactionId,
256
+ src: b.src,
257
+ cpm: b.cpm,
258
+ size: `${b.width}x${b.height}`,
259
+ currency: b.currency,
260
+ status: STATUS.RECEIVED,
261
+ splitTestAssignment: (_f = (_e = (_d = b.ortb2Imp) === null || _d === void 0 ? void 0 : _d.ext) === null || _e === void 0 ? void 0 : _e.optable) === null || _f === void 0 ? void 0 : _f.splitTestAssignment,
262
+ };
263
+ br.bids.push(bidObj);
264
+ bidIndex[bidId] = bidObj;
265
+ bidToRequest[bidId] = br;
266
+ }
267
+ // Update bidder request status to RECEIVED if any bid was received
268
+ if (br.status === STATUS.REQUESTED) {
269
+ br.status = STATUS.RECEIVED;
270
+ }
271
+ });
272
+ // Handle noBids → mark the entire request as NO_BID
273
+ noBids.forEach((nb) => {
274
+ const br = requestIndex[nb.bidderRequestId];
275
+ if (!br)
276
+ return;
277
+ br.status = STATUS.NO_BID;
278
+ // Mark all bids in this request as NO_BID
279
+ br.bids.forEach((bid) => {
280
+ bid.status = STATUS.NO_BID;
281
+ });
282
+ });
283
+ // Handle timeoutBids → mark the entire request as TIMEOUT
284
+ timeoutBids.forEach((tb) => {
285
+ const br = requestIndex[tb.bidderRequestId];
286
+ if (!br)
287
+ return;
288
+ br.status = STATUS.TIMEOUT;
289
+ // Mark all bids in this request as TIMEOUT
290
+ br.bids.forEach((bid) => {
291
+ bid.status = STATUS.TIMEOUT;
292
+ });
293
+ });
294
+ const createdAt = new Date();
295
+ const auctionEndTimeoutId = setTimeout(() => __awaiter(this, void 0, void 0, function* () {
296
+ const payload = yield this.toWitness(event, null, missed);
297
+ payload["auctionEndAt"] = createdAt.toISOString();
298
+ payload["bidWonAt"] = null;
299
+ payload["optableLoaded"] = !missed;
300
+ this.sendToWitnessAPI("optable.prebid.auction", payload);
301
+ }), this.config.bidWinTimeout);
302
+ // Store the auction data
303
+ this.auctions.set(auctionId, { auctionEnd: event, createdAt, missed, auctionEndTimeoutId });
304
+ // Clean up old auctions
305
+ this.cleanupOldAuctions();
306
+ });
307
+ }
308
+ /**
309
+ * Handle a Prebid `bidWon` event by finalizing the matching auction, clearing
310
+ * the pending timeout and sending the combined payload to Witness.
311
+ * @param event - The raw Prebid bidWon event object.
312
+ * @param missed - True when the event was previously emitted (missed replay).
313
+ * @returns void
314
+ */
315
+ trackBidWon(event_1) {
316
+ return __awaiter(this, arguments, void 0, function* (event, missed = false) {
317
+ const filteredEvent = {
318
+ auctionId: event.auctionId,
319
+ bidderCode: event.bidderCode,
320
+ bidId: event.requestId,
321
+ tenant: this.config.tenant,
322
+ missed,
323
+ };
324
+ this.log("bidWon filtered event", filteredEvent);
325
+ const auction = this.auctions.get(event.auctionId);
326
+ if (!auction) {
327
+ this.log("Missing 'auctionEnd' event. Skipping.");
328
+ return;
329
+ }
330
+ if (auction.auctionEndTimeoutId) {
331
+ clearTimeout(auction.auctionEndTimeoutId);
332
+ }
333
+ const payload = yield this.toWitness(auction.auctionEnd, event, missed);
334
+ payload["auctionEndAt"] = auction.createdAt.toISOString();
335
+ payload["bidWonAt"] = new Date().toISOString();
336
+ payload["optableLoaded"] = !missed;
337
+ this.sendToWitnessAPI("optable.prebid.auction", payload);
338
+ this.auctions.delete(event.auctionId);
339
+ });
340
+ }
341
+ /**
342
+ * Clean up old auctions to prevent memory leaks.
343
+ * Removes the oldest auction when the internal store grows past the configured size.
344
+ * @returns void
345
+ */
346
+ cleanupOldAuctions() {
347
+ const auctionIds = [...this.auctions.keys()];
348
+ if (auctionIds.length > this.maxAuctionDataSize) {
349
+ const oldestAuctionId = auctionIds[0];
350
+ this.auctions.delete(oldestAuctionId);
351
+ this.log(`Cleaned up old auction: ${oldestAuctionId}`);
352
+ }
353
+ }
354
+ /**
355
+ * Clear all stored analytics data (useful for tests).
356
+ * @returns void
357
+ */
358
+ clearData() {
359
+ this.auctions.clear();
360
+ this.log("All analytics data cleared");
361
+ }
362
+ /**
363
+ * Convert internal auction state and optional bidWon event into a Witness payload.
364
+ * This collects matcher/source metadata, bid counts and optional custom analytics.
365
+ * @param auctionEndEvent - The `auctionEnd` event object from Prebid.js.
366
+ * @param bidWonEvent - Optional `bidWon` event when a winning bid exists.
367
+ * @param missed - True when the original events were already emitted (replayed).
368
+ * @returns A payload object compatible with the Witness API.
369
+ */
370
+ toWitness(auctionEndEvent_1, bidWonEvent_1) {
371
+ return __awaiter(this, arguments, void 0, function* (auctionEndEvent, bidWonEvent, missed = false) {
372
+ var _a, _b, _c;
373
+ const { auctionId, bidderRequests = [], bidsReceived = [], noBids = [], timeoutBids = [] } = auctionEndEvent;
374
+ const oMatchersSet = new Set();
375
+ const oSourcesSet = new Set();
376
+ let adUnitCode = "unknown";
377
+ let totalBids = 0;
378
+ let device = null;
379
+ // Process bidder requests
380
+ const requests = bidderRequests.map((br) => {
381
+ var _a, _b, _c, _d;
382
+ const { bidderCode, bidderRequestId, bids = [] } = br;
383
+ const domain = (_b = (_a = br.ortb2.site) === null || _a === void 0 ? void 0 : _a.domain) !== null && _b !== void 0 ? _b : "unknown";
384
+ const eids = (_d = (_c = br.ortb2.user) === null || _c === void 0 ? void 0 : _c.eids) !== null && _d !== void 0 ? _d : [];
385
+ // Optable EIDs
386
+ const optableEIDS = eids.filter((e) => e.inserter === "optable.co");
387
+ const optableMatchers = [...new Set(optableEIDS.map((e) => e.matcher).filter(Boolean))];
388
+ const optableSources = [...new Set(optableEIDS.map((e) => e.source).filter(Boolean))];
389
+ device = br.ortb2.device;
390
+ return {
391
+ bidderCode,
392
+ bidderRequestId,
393
+ domain,
394
+ optableTargetingDone: optableEIDS.length > 0,
395
+ optableMatchers,
396
+ optableSources,
397
+ status: STATUS.REQUESTED,
398
+ bids: bids.map((b) => {
399
+ var _a, _b, _c, _d;
400
+ return ({
401
+ bidId: b.bidId,
402
+ bidderRequestId,
403
+ adUnitCode: b.adUnitCode,
404
+ adUnitId: b.adUnitId,
405
+ transactionId: b.transactionId,
406
+ src: b.src,
407
+ floorMin: (_a = b.floorData) === null || _a === void 0 ? void 0 : _a.floorMin,
408
+ splitTestAssignment: (_d = (_c = (_b = b.ortb2Imp) === null || _b === void 0 ? void 0 : _b.ext) === null || _c === void 0 ? void 0 : _c.optable) === null || _d === void 0 ? void 0 : _d.splitTestAssignment,
409
+ status: STATUS.REQUESTED,
410
+ });
411
+ }),
412
+ };
413
+ });
414
+ // Merge splitTestAssignment from bidsReceived into the requests
415
+ const bidsReceivedMap = new Map(bidsReceived.map((b) => [b.requestId, b]));
416
+ requests.forEach((request) => {
417
+ request.bids.forEach((bid) => {
418
+ var _a, _b, _c;
419
+ const bidReceived = bidsReceivedMap.get(bid.bidId);
420
+ if ((_c = (_b = (_a = bidReceived === null || bidReceived === void 0 ? void 0 : bidReceived.ortb2Imp) === null || _a === void 0 ? void 0 : _a.ext) === null || _b === void 0 ? void 0 : _b.optable) === null || _c === void 0 ? void 0 : _c.splitTestAssignment) {
421
+ bid.splitTestAssignment = bidReceived.ortb2Imp.ext.optable.splitTestAssignment;
422
+ }
423
+ });
424
+ });
425
+ const witnessData = {
426
+ bidderRequests: requests.map((br) => {
427
+ br.optableMatchers.forEach((m) => oMatchersSet.add(m));
428
+ br.optableSources.forEach((s) => oSourcesSet.add(s));
429
+ return br;
430
+ }),
431
+ auctionId,
432
+ adUnitCode,
433
+ totalRequests: bidderRequests.length,
434
+ optableSampling: this.config.samplingRate || 1,
435
+ optableTargetingDone: oMatchersSet.size || oSourcesSet.size,
436
+ optableMatchers: Array.from(oMatchersSet),
437
+ optableSources: Array.from(oSourcesSet),
438
+ bidWon: bidWonEvent
439
+ ? {
440
+ message: bidWonEvent.bidderCode +
441
+ " won the ad server auction for ad unit " +
442
+ bidWonEvent.adUnitCode +
443
+ " at " +
444
+ bidWonEvent.cpm +
445
+ " CPM",
446
+ bidderCode: bidWonEvent.bidderCode,
447
+ adUnitCode: bidWonEvent.adUnitCode,
448
+ cpm: bidWonEvent.cpm,
449
+ }
450
+ : null,
451
+ missed,
452
+ url: `${window.location.hostname}${window.location.pathname}`,
453
+ tenant: this.config.tenant,
454
+ // eslint-disable-next-line no-undef
455
+ optableWrapperVersion: SDK_WRAPPER_VERSION || "unknown",
456
+ userAgent: Bowser.parse(window.navigator.userAgent),
457
+ device,
458
+ prebidjsVersion: ((_a = this.prebidInstance) === null || _a === void 0 ? void 0 : _a.version) || "unknown",
459
+ sessionDepth: (sessionStorage === null || sessionStorage === void 0 ? void 0 : sessionStorage.optableSessionDepth) || 1,
460
+ pageAuctionsCount: ((_b = window.optable) === null || _b === void 0 ? void 0 : _b.pageAuctionsCount) || 1,
461
+ };
462
+ // Log summary with bid counts
463
+ this.log(`Auction ${auctionId} processed: ${bidderRequests.length} requests, ${totalBids} total bids, ${bidsReceived.length} received, ${noBids.length} no-bids, ${timeoutBids.length} timeouts`);
464
+ if ((_c = window.optable) === null || _c === void 0 ? void 0 : _c.customAnalytics) {
465
+ yield window.optable.customAnalytics().then((response) => {
466
+ this.log(`Adding custom data to payload ${JSON.stringify(response)}`);
467
+ Object.assign(witnessData, response);
468
+ });
469
+ }
470
+ return witnessData;
471
+ });
472
+ }
473
+ }
474
+ export default OptablePrebidAnalytics;
@@ -25,6 +25,7 @@ declare class OptablePrebidAnalytics {
25
25
  * Hook into Prebid.js events
26
26
  */
27
27
  hookIntoPrebid(prebidInstance?: any): boolean;
28
+ prebidInstance: any;
28
29
  trackAuctionEnd(event: any, missed: any): Promise<void>;
29
30
  trackBidWon(event: any, missed: any): void;
30
31
  /**