@optable/web-sdk 0.45.0 → 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.
- package/README.md +13 -10
- package/browser/dist/sdk.js +1 -1
- package/lib/dist/addons/gpt.js +3 -1
- package/lib/dist/addons/prebid/analytics.d.ts +106 -0
- package/lib/dist/addons/prebid/analytics.js +474 -0
- package/lib/dist/addons/prototypes/analytics.d.ts +1 -0
- package/lib/dist/addons/prototypes/analytics.js +17 -7
- package/lib/dist/build.json +1 -1
- package/lib/dist/config.d.ts +4 -1
- package/lib/dist/core/context.d.ts +32 -0
- package/lib/dist/core/context.js +146 -0
- package/lib/dist/edge/targeting.js +3 -0
- package/lib/dist/edge/witness.d.ts +5 -2
- package/lib/dist/edge/witness.js +4 -1
- package/lib/dist/sdk.d.ts +6 -1
- package/lib/dist/sdk.js +14 -2
- package/package.json +9 -6
|
@@ -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
|
/**
|
|
@@ -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-
|
|
265
|
-
|
|
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`);
|
package/lib/dist/build.json
CHANGED
package/lib/dist/config.d.ts
CHANGED
|
@@ -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 };
|