@optable/web-sdk 0.45.0 → 0.49.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,478 @@
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, _e, _f, _g;
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 allEids = [...((_e = (_d = (_c = br.ortb2.user) === null || _c === void 0 ? void 0 : _c.ext) === null || _d === void 0 ? void 0 : _d.eids) !== null && _e !== void 0 ? _e : []), ...((_g = (_f = br.ortb2.user) === null || _f === void 0 ? void 0 : _f.eids) !== null && _g !== void 0 ? _g : [])];
187
+ // Deduplicate EIDs by source
188
+ const eids = Array.from(new Map(allEids.map((eid) => [eid.source, eid])).values());
189
+ // Optable EIDs
190
+ const optableEIDS = eids.filter((e) => e.inserter === "optable.co");
191
+ const optableMatchers = [...new Set(optableEIDS.map((e) => e.matcher).filter(Boolean))];
192
+ const optableSources = [...new Set(optableEIDS.map((e) => e.source).filter(Boolean))];
193
+ return {
194
+ bidderCode,
195
+ bidderRequestId,
196
+ domain,
197
+ hasOEids: optableEIDS.length > 0,
198
+ optableMatchers,
199
+ optableSources,
200
+ status: STATUS.REQUESTED,
201
+ bids: bids.map((b) => {
202
+ var _a, _b, _c, _d;
203
+ return ({
204
+ bidId: b.bidId,
205
+ bidderRequestId,
206
+ adUnitCode: b.adUnitCode,
207
+ adUnitId: b.adUnitId,
208
+ transactionId: b.transactionId,
209
+ src: b.src,
210
+ floorMin: (_a = b.floorData) === null || _a === void 0 ? void 0 : _a.floorMin,
211
+ 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,
212
+ status: STATUS.REQUESTED,
213
+ });
214
+ }),
215
+ };
216
+ }),
217
+ };
218
+ // Build lookup tables for 1:many relationship
219
+ const requestIndex = {};
220
+ const bidIndex = {};
221
+ const bidToRequest = {};
222
+ auction.bidderRequests.forEach((br) => {
223
+ requestIndex[br.bidderRequestId] = br;
224
+ br.bids.forEach((bid) => {
225
+ bidIndex[bid.bidId] = bid;
226
+ bidToRequest[bid.bidId] = br;
227
+ });
228
+ });
229
+ // Merge in bidsReceived → update individual bids as RECEIVED
230
+ bidsReceived.forEach((b) => {
231
+ var _a, _b, _c, _d, _e, _f;
232
+ const bidId = b.requestId;
233
+ const br = bidToRequest[bidId];
234
+ if (!br) {
235
+ this.log(`No bidderRequest found for bidId=${bidId}`);
236
+ return;
237
+ }
238
+ // Find the specific bid to update
239
+ let bidObj = bidIndex[bidId];
240
+ if (bidObj) {
241
+ // Update existing bid
242
+ Object.assign(bidObj, {
243
+ status: STATUS.RECEIVED,
244
+ cpm: b.cpm,
245
+ size: `${b.width}x${b.height}`,
246
+ currency: b.currency,
247
+ 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,
248
+ });
249
+ }
250
+ else {
251
+ // Create new bid object for this response
252
+ bidObj = {
253
+ bidId,
254
+ bidderRequestId: br.bidderRequestId,
255
+ adUnitCode: b.adUnitCode,
256
+ adUnitId: b.adUnitId,
257
+ transactionId: b.transactionId,
258
+ src: b.src,
259
+ cpm: b.cpm,
260
+ size: `${b.width}x${b.height}`,
261
+ currency: b.currency,
262
+ status: STATUS.RECEIVED,
263
+ 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,
264
+ };
265
+ br.bids.push(bidObj);
266
+ bidIndex[bidId] = bidObj;
267
+ bidToRequest[bidId] = br;
268
+ }
269
+ // Update bidder request status to RECEIVED if any bid was received
270
+ if (br.status === STATUS.REQUESTED) {
271
+ br.status = STATUS.RECEIVED;
272
+ }
273
+ });
274
+ // Handle noBids → mark the entire request as NO_BID
275
+ noBids.forEach((nb) => {
276
+ const br = requestIndex[nb.bidderRequestId];
277
+ if (!br)
278
+ return;
279
+ br.status = STATUS.NO_BID;
280
+ // Mark all bids in this request as NO_BID
281
+ br.bids.forEach((bid) => {
282
+ bid.status = STATUS.NO_BID;
283
+ });
284
+ });
285
+ // Handle timeoutBids → mark the entire request as TIMEOUT
286
+ timeoutBids.forEach((tb) => {
287
+ const br = requestIndex[tb.bidderRequestId];
288
+ if (!br)
289
+ return;
290
+ br.status = STATUS.TIMEOUT;
291
+ // Mark all bids in this request as TIMEOUT
292
+ br.bids.forEach((bid) => {
293
+ bid.status = STATUS.TIMEOUT;
294
+ });
295
+ });
296
+ const createdAt = new Date();
297
+ const auctionEndTimeoutId = setTimeout(() => __awaiter(this, void 0, void 0, function* () {
298
+ const payload = yield this.toWitness(event, null, missed);
299
+ payload["auctionEndAt"] = createdAt.toISOString();
300
+ payload["bidWonAt"] = null;
301
+ payload["optableLoaded"] = !missed;
302
+ this.sendToWitnessAPI("optable.prebid.auction", payload);
303
+ }), this.config.bidWinTimeout);
304
+ // Store the auction data
305
+ this.auctions.set(auctionId, { auctionEnd: event, createdAt, missed, auctionEndTimeoutId });
306
+ // Clean up old auctions
307
+ this.cleanupOldAuctions();
308
+ });
309
+ }
310
+ /**
311
+ * Handle a Prebid `bidWon` event by finalizing the matching auction, clearing
312
+ * the pending timeout and sending the combined payload to Witness.
313
+ * @param event - The raw Prebid bidWon event object.
314
+ * @param missed - True when the event was previously emitted (missed replay).
315
+ * @returns void
316
+ */
317
+ trackBidWon(event_1) {
318
+ return __awaiter(this, arguments, void 0, function* (event, missed = false) {
319
+ const filteredEvent = {
320
+ auctionId: event.auctionId,
321
+ bidderCode: event.bidderCode,
322
+ bidId: event.requestId,
323
+ tenant: this.config.tenant,
324
+ missed,
325
+ };
326
+ this.log("bidWon filtered event", filteredEvent);
327
+ const auction = this.auctions.get(event.auctionId);
328
+ if (!auction) {
329
+ this.log("Missing 'auctionEnd' event. Skipping.");
330
+ return;
331
+ }
332
+ if (auction.auctionEndTimeoutId) {
333
+ clearTimeout(auction.auctionEndTimeoutId);
334
+ }
335
+ const payload = yield this.toWitness(auction.auctionEnd, event, missed);
336
+ payload["auctionEndAt"] = auction.createdAt.toISOString();
337
+ payload["bidWonAt"] = new Date().toISOString();
338
+ payload["optableLoaded"] = !missed;
339
+ this.sendToWitnessAPI("optable.prebid.auction", payload);
340
+ this.auctions.delete(event.auctionId);
341
+ });
342
+ }
343
+ /**
344
+ * Clean up old auctions to prevent memory leaks.
345
+ * Removes the oldest auction when the internal store grows past the configured size.
346
+ * @returns void
347
+ */
348
+ cleanupOldAuctions() {
349
+ const auctionIds = [...this.auctions.keys()];
350
+ if (auctionIds.length > this.maxAuctionDataSize) {
351
+ const oldestAuctionId = auctionIds[0];
352
+ this.auctions.delete(oldestAuctionId);
353
+ this.log(`Cleaned up old auction: ${oldestAuctionId}`);
354
+ }
355
+ }
356
+ /**
357
+ * Clear all stored analytics data (useful for tests).
358
+ * @returns void
359
+ */
360
+ clearData() {
361
+ this.auctions.clear();
362
+ this.log("All analytics data cleared");
363
+ }
364
+ /**
365
+ * Convert internal auction state and optional bidWon event into a Witness payload.
366
+ * This collects matcher/source metadata, bid counts and optional custom analytics.
367
+ * @param auctionEndEvent - The `auctionEnd` event object from Prebid.js.
368
+ * @param bidWonEvent - Optional `bidWon` event when a winning bid exists.
369
+ * @param missed - True when the original events were already emitted (replayed).
370
+ * @returns A payload object compatible with the Witness API.
371
+ */
372
+ toWitness(auctionEndEvent_1, bidWonEvent_1) {
373
+ return __awaiter(this, arguments, void 0, function* (auctionEndEvent, bidWonEvent, missed = false) {
374
+ var _a, _b, _c;
375
+ const { auctionId, bidderRequests = [], bidsReceived = [], noBids = [], timeoutBids = [] } = auctionEndEvent;
376
+ const oMatchersSet = new Set();
377
+ const oSourcesSet = new Set();
378
+ let adUnitCode = "unknown";
379
+ let totalBids = 0;
380
+ let device = null;
381
+ // Process bidder requests
382
+ const requests = bidderRequests.map((br) => {
383
+ var _a, _b, _c, _d, _e, _f, _g;
384
+ const { bidderCode, bidderRequestId, bids = [] } = br;
385
+ const domain = (_b = (_a = br.ortb2.site) === null || _a === void 0 ? void 0 : _a.domain) !== null && _b !== void 0 ? _b : "unknown";
386
+ const allEids = [...((_e = (_d = (_c = br.ortb2.user) === null || _c === void 0 ? void 0 : _c.ext) === null || _d === void 0 ? void 0 : _d.eids) !== null && _e !== void 0 ? _e : []), ...((_g = (_f = br.ortb2.user) === null || _f === void 0 ? void 0 : _f.eids) !== null && _g !== void 0 ? _g : [])];
387
+ // Deduplicate EIDs by source
388
+ const eids = Array.from(new Map(allEids.map((eid) => [eid.source, eid])).values());
389
+ // Optable EIDs
390
+ const optableEIDS = eids.filter((e) => e.inserter === "optable.co");
391
+ const optableMatchers = [...new Set(optableEIDS.map((e) => e.matcher).filter(Boolean))];
392
+ const optableSources = [...new Set(optableEIDS.map((e) => e.source).filter(Boolean))];
393
+ device = br.ortb2.device;
394
+ return {
395
+ bidderCode,
396
+ bidderRequestId,
397
+ domain,
398
+ optableTargetingDone: optableEIDS.length > 0,
399
+ optableMatchers,
400
+ optableSources,
401
+ status: STATUS.REQUESTED,
402
+ bids: bids.map((b) => {
403
+ var _a, _b, _c, _d;
404
+ return ({
405
+ bidId: b.bidId,
406
+ bidderRequestId,
407
+ adUnitCode: b.adUnitCode,
408
+ adUnitId: b.adUnitId,
409
+ transactionId: b.transactionId,
410
+ src: b.src,
411
+ floorMin: (_a = b.floorData) === null || _a === void 0 ? void 0 : _a.floorMin,
412
+ 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,
413
+ status: STATUS.REQUESTED,
414
+ });
415
+ }),
416
+ };
417
+ });
418
+ // Merge splitTestAssignment from bidsReceived into the requests
419
+ const bidsReceivedMap = new Map(bidsReceived.map((b) => [b.requestId, b]));
420
+ requests.forEach((request) => {
421
+ request.bids.forEach((bid) => {
422
+ var _a, _b, _c;
423
+ const bidReceived = bidsReceivedMap.get(bid.bidId);
424
+ 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) {
425
+ bid.splitTestAssignment = bidReceived.ortb2Imp.ext.optable.splitTestAssignment;
426
+ }
427
+ });
428
+ });
429
+ const witnessData = {
430
+ bidderRequests: requests.map((br) => {
431
+ br.optableMatchers.forEach((m) => oMatchersSet.add(m));
432
+ br.optableSources.forEach((s) => oSourcesSet.add(s));
433
+ return br;
434
+ }),
435
+ auctionId,
436
+ adUnitCode,
437
+ totalRequests: bidderRequests.length,
438
+ optableSampling: this.config.samplingRate || 1,
439
+ optableTargetingDone: oMatchersSet.size || oSourcesSet.size,
440
+ optableMatchers: Array.from(oMatchersSet),
441
+ optableSources: Array.from(oSourcesSet),
442
+ bidWon: bidWonEvent
443
+ ? {
444
+ message: bidWonEvent.bidderCode +
445
+ " won the ad server auction for ad unit " +
446
+ bidWonEvent.adUnitCode +
447
+ " at " +
448
+ bidWonEvent.cpm +
449
+ " CPM",
450
+ bidderCode: bidWonEvent.bidderCode,
451
+ adUnitCode: bidWonEvent.adUnitCode,
452
+ cpm: bidWonEvent.cpm,
453
+ }
454
+ : null,
455
+ missed,
456
+ url: `${window.location.hostname}${window.location.pathname}`,
457
+ tenant: this.config.tenant,
458
+ // eslint-disable-next-line no-undef
459
+ optableWrapperVersion: SDK_WRAPPER_VERSION || "unknown",
460
+ userAgent: Bowser.parse(window.navigator.userAgent),
461
+ device,
462
+ prebidjsVersion: ((_a = this.prebidInstance) === null || _a === void 0 ? void 0 : _a.version) || "unknown",
463
+ sessionDepth: (sessionStorage === null || sessionStorage === void 0 ? void 0 : sessionStorage.optableSessionDepth) || 1,
464
+ pageAuctionsCount: ((_b = window.optable) === null || _b === void 0 ? void 0 : _b.pageAuctionsCount) || 1,
465
+ };
466
+ // Log summary with bid counts
467
+ this.log(`Auction ${auctionId} processed: ${bidderRequests.length} requests, ${totalBids} total bids, ${bidsReceived.length} received, ${noBids.length} no-bids, ${timeoutBids.length} timeouts`);
468
+ if ((_c = window.optable) === null || _c === void 0 ? void 0 : _c.customAnalytics) {
469
+ yield window.optable.customAnalytics().then((response) => {
470
+ this.log(`Adding custom data to payload ${JSON.stringify(response)}`);
471
+ Object.assign(witnessData, response);
472
+ });
473
+ }
474
+ return witnessData;
475
+ });
476
+ }
477
+ }
478
+ 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
  /**
@@ -8,7 +8,6 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge
8
8
  });
9
9
  };
10
10
  /* eslint-disable no-param-reassign */
11
- /* eslint-disable no-console */
12
11
  const STATUS = {
13
12
  REQUESTED: "REQUESTED",
14
13
  RECEIVED: "RECEIVED",
@@ -27,6 +26,7 @@ class OptablePrebidAnalytics {
27
26
  // Store auction data
28
27
  this.auctions = {};
29
28
  this.maxAuctionDataSize = 20;
29
+ sessionStorage.optableSessionDepth = (Number(sessionStorage === null || sessionStorage === void 0 ? void 0 : sessionStorage.optableSessionDepth) || 0) + 1;
30
30
  this.log("OptablePrebidAnalytics initialized");
31
31
  }
32
32
  /**
@@ -34,7 +34,7 @@ class OptablePrebidAnalytics {
34
34
  */
35
35
  log(...args) {
36
36
  if (this.config.debug) {
37
- console.log("[OptablePrebidAnalytics]", ...args);
37
+ console.log("[OptablePrebidAnalytics]", ...args); /* eslint-disable-line no-console */
38
38
  }
39
39
  }
40
40
  /**
@@ -84,6 +84,7 @@ class OptablePrebidAnalytics {
84
84
  */
85
85
  hookIntoPrebid(prebidInstance = window.pbjs) {
86
86
  const pbjs = prebidInstance;
87
+ this.prebidInstance = pbjs;
87
88
  if (typeof pbjs === "undefined") {
88
89
  this.log("Prebid.js not found");
89
90
  return false;
@@ -99,17 +100,19 @@ class OptablePrebidAnalytics {
99
100
  }
100
101
  trackAuctionEnd(event, missed) {
101
102
  return __awaiter(this, void 0, void 0, function* () {
103
+ var _a, _b;
102
104
  const { auctionId, timeout, bidderRequests = [], bidsReceived = [], noBids = [], timeoutBids = [] } = event;
105
+ window.optable.pageAuctionsCount = (Number(window.optable.pageAuctionsCount) || 0) + 1;
103
106
  this.log(`Processing auction ${auctionId} with ${bidderRequests.length} bidder requests`);
104
107
  // Build auction object with bidder requests and EID flags
105
108
  const auction = {
106
109
  auctionId,
107
110
  timeout,
108
111
  bidderRequests: bidderRequests.map((br) => {
109
- var _a, _b, _c;
112
+ var _a, _b, _c, _d;
110
113
  const { bidderCode, bidderRequestId, ortb2, bids = [] } = br;
111
114
  const domain = (_a = ortb2 === null || ortb2 === void 0 ? void 0 : ortb2.site) === null || _a === void 0 ? void 0 : _a.domain;
112
- const eids = (_c = (_b = ortb2 === null || ortb2 === void 0 ? void 0 : ortb2.user) === null || _b === void 0 ? void 0 : _b.ext) === null || _c === void 0 ? void 0 : _c.eids;
115
+ const eids = ((_c = (_b = ortb2 === null || ortb2 === void 0 ? void 0 : ortb2.user) === null || _b === void 0 ? void 0 : _b.ext) === null || _c === void 0 ? void 0 : _c.eids) || ((_d = ortb2.user) === null || _d === void 0 ? void 0 : _d.eids) || [];
113
116
  // Optable EIDs
114
117
  const optableEIDS = eids.filter((e) => e.inserter === "optable.co");
115
118
  const optableMatchers = [...new Set(optableEIDS.map((e) => e.matcher).filter(Boolean))];
@@ -130,7 +133,7 @@ class OptablePrebidAnalytics {
130
133
  liSources,
131
134
  status: STATUS.REQUESTED,
132
135
  bids: bids.map((b) => {
133
- var _a;
136
+ var _a, _b, _c, _d;
134
137
  return ({
135
138
  bidId: b.bidId,
136
139
  bidderRequestId,
@@ -139,6 +142,7 @@ class OptablePrebidAnalytics {
139
142
  transactionId: b.transactionId,
140
143
  src: b.src,
141
144
  floorMin: (_a = b.floorData) === null || _a === void 0 ? void 0 : _a.floorMin,
145
+ 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,
142
146
  status: STATUS.REQUESTED,
143
147
  });
144
148
  }),
@@ -158,6 +162,7 @@ class OptablePrebidAnalytics {
158
162
  });
159
163
  // Merge in bidsReceived → update individual bids as RECEIVED
160
164
  bidsReceived.forEach((b) => {
165
+ var _a, _b, _c, _d, _e, _f;
161
166
  const bidId = b.requestId;
162
167
  const br = bidToRequest[bidId];
163
168
  if (!br) {
@@ -173,6 +178,7 @@ class OptablePrebidAnalytics {
173
178
  cpm: b.cpm,
174
179
  size: `${b.width}x${b.height}`,
175
180
  currency: b.currency,
181
+ 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,
176
182
  });
177
183
  }
178
184
  else {
@@ -188,6 +194,7 @@ class OptablePrebidAnalytics {
188
194
  size: `${b.width}x${b.height}`,
189
195
  currency: b.currency,
190
196
  status: STATUS.RECEIVED,
197
+ 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,
191
198
  };
192
199
  br.bids.push(bidObj);
193
200
  bidIndex[bidId] = bidObj;
@@ -247,6 +254,7 @@ class OptablePrebidAnalytics {
247
254
  cpm: b.cpm,
248
255
  size: b.size,
249
256
  bidId: b.bidId,
257
+ splitTestAssignment: b.splitTestAssignment,
250
258
  };
251
259
  }),
252
260
  };
@@ -261,8 +269,10 @@ class OptablePrebidAnalytics {
261
269
  missed,
262
270
  url: `${window.location.hostname}${window.location.pathname}`,
263
271
  tenant: this.config.tenant,
264
- // eslint-disable-next-line no-undef
265
- optableWrapperVersion: SDK_WRAPPER_VERSION,
272
+ optableWrapperVersion: SDK_WRAPPER_VERSION, // eslint-disable-line no-undef
273
+ prebidjsVersion: ((_a = this.prebidInstance) === null || _a === void 0 ? void 0 : _a.version) || "unknown",
274
+ sessionDepth: (sessionStorage === null || sessionStorage === void 0 ? void 0 : sessionStorage.optableSessionDepth) || 1,
275
+ pageAuctionsCount: ((_b = window.optable) === null || _b === void 0 ? void 0 : _b.pageAuctionsCount) || 1,
266
276
  };
267
277
  // Log summary with bid counts
268
278
  this.log(`Auction ${auctionId} processed: ${bidderRequests.length} requests, ${totalBids} total bids, ${bidsReceived.length} received, ${noBids.length} no-bids, ${timeoutBids.length} timeouts`);
@@ -1,3 +1,3 @@
1
1
  {
2
- "version": "v0.45.0"
2
+ "version": "v0.49.0"
3
3
  }
@@ -1,4 +1,5 @@
1
1
  import type { CMPApiConfig, Consent } from "./core/regs/consent";
2
+ import type { PageContextConfig } from "./core/context";
2
3
  type Experiment = never;
3
4
  type MatcherOverride = {
4
5
  id: string;
@@ -9,6 +10,7 @@ type ABTestConfig = {
9
10
  trafficPercentage: number;
10
11
  matcher_override?: MatcherOverride[];
11
12
  skipMatchers?: string[];
13
+ skipResolvers?: string[];
12
14
  };
13
15
  type TargetingSignals = {
14
16
  ref?: boolean;
@@ -35,6 +37,7 @@ type InitConfig = {
35
37
  abTests?: ABTestConfig[];
36
38
  additionalTargetingSignals?: TargetingSignals;
37
39
  timeout?: string;
40
+ pageContext?: PageContextConfig | boolean;
38
41
  };
39
42
  type ResolvedConfig = {
40
43
  site: string;
@@ -70,5 +73,5 @@ declare const DCN_DEFAULTS: {
70
73
  };
71
74
  declare function getConfig(init: InitConfig): ResolvedConfig;
72
75
  declare function generateSessionID(): string;
73
- export type { InitConsent, CMPApiConfig, InitConfig, ResolvedConfig, ABTestConfig, MatcherOverride, Experiment };
76
+ export type { InitConsent, CMPApiConfig, InitConfig, ResolvedConfig, ABTestConfig, MatcherOverride, Experiment, PageContextConfig, };
74
77
  export { getConfig, DCN_DEFAULTS, generateSessionID };
@@ -0,0 +1,32 @@
1
+ type SemanticContent = {
2
+ title: string;
3
+ description?: string;
4
+ keywords?: string[];
5
+ canonicalUrl?: string;
6
+ ogTags?: Record<string, string>;
7
+ headings?: Array<{
8
+ level: number;
9
+ text: string;
10
+ }>;
11
+ content?: string;
12
+ jsonLd?: object[];
13
+ language?: string;
14
+ };
15
+ type ContextData = {
16
+ semantic: SemanticContent;
17
+ html?: string;
18
+ url: string;
19
+ referrer?: string;
20
+ extractedAt: number;
21
+ };
22
+ type PageContextConfig = {
23
+ includeHtml?: boolean;
24
+ contentSelector?: string;
25
+ maxContentLength?: number;
26
+ maxHtmlLength?: number;
27
+ };
28
+ declare function extractSemanticContent(config: PageContextConfig): SemanticContent;
29
+ declare function extractContext(config: PageContextConfig): ContextData;
30
+ declare function normalizeContextConfig(config: PageContextConfig | boolean | undefined): PageContextConfig | null;
31
+ export type { SemanticContent, ContextData, PageContextConfig };
32
+ export { extractContext, extractSemanticContent, normalizeContextConfig };