From ee823b8c0d14117ba4afa74db250d7ab7dc8485a Mon Sep 17 00:00:00 2001 From: mosherBT Date: Tue, 27 Oct 2026 10:42:39 -0400 Subject: [PATCH 1/2] analytics: report every Optable matcher in V2 prebid analytics --- lib/addons/prebid/analytics.test.ts | 12 ++++-------- lib/addons/prebid/analytics.ts | 14 ++++++-------- 2 files changed, 10 insertions(+), 16 deletions(-) diff --git a/lib/addons/prebid/analytics.test.ts b/lib/addons/prebid/analytics.test.ts index 30a2031f..54f2087b 100644 --- a/lib/addons/prebid/analytics.test.ts +++ b/lib/addons/prebid/analytics.test.ts @@ -2400,10 +2400,9 @@ describe("OptablePrebidAnalytics", () => { const result = await analytics.toWitness(auctionEndEvent, []); - // Should only have one source since they have the same source value + // One source, but both matchers that resolved it expect(result.optableSources).toEqual(["optable.co"]); - // The last occurrence (from eids, not ext.eids) should win due to deduplication - expect(result.optableMatchers).toEqual(["matcher2"]); + expect(result.optableMatchers).toEqual(["matcher1", "matcher2"]); }); it("should preserve EIDs with different sources from ext.eids and eids", async () => { @@ -2560,9 +2559,8 @@ describe("OptablePrebidAnalytics", () => { const result = await analytics.toWitness(auctionEndEvent, []); - // Should only have one optable source and matcher due to deduplication expect(result.optableSources).toEqual(["optable.co"]); - expect(result.optableMatchers).toEqual(["matcher2"]); + expect(result.optableMatchers).toEqual(["matcher1", "matcher2"]); // Non-optable EIDs should be filtered out expect(result.optableMatchers).not.toContain("other1"); expect(result.optableMatchers).not.toContain("other2"); @@ -2599,10 +2597,8 @@ describe("OptablePrebidAnalytics", () => { expect(storedAuction).toBeDefined(); const payload = await analytics.toWitness(storedAuction.auctionEnd, []); - // Should only have one source after deduplication expect(payload.optableSources).toEqual(["optable.co"]); - // The last matcher should win - expect(payload.optableMatchers).toEqual(["matcher2"]); + expect(payload.optableMatchers).toEqual(["matcher1", "matcher2"]); }); }); diff --git a/lib/addons/prebid/analytics.ts b/lib/addons/prebid/analytics.ts index a7d6fa4f..c10667c2 100644 --- a/lib/addons/prebid/analytics.ts +++ b/lib/addons/prebid/analytics.ts @@ -429,20 +429,18 @@ class OptablePrebidAnalytics { const requests = bidderRequests.map((br: any) => { const { bidderCode, bidderRequestId, bids = [] } = br; const domain = br.ortb2.site?.domain ?? "unknown"; - const allEids = [...(br.ortb2.user?.ext?.eids ?? []), ...(br.ortb2.user?.eids ?? [])]; - // Deduplicate EIDs by source and inserter, so another inserter's EID can't hide Optable's - const eids = Array.from(new Map(allEids.map((eid: any) => [`${eid.source}|${eid.inserter}`, eid])).values()); + const eids = [...(br.ortb2.user?.ext?.eids ?? []), ...(br.ortb2.user?.eids ?? [])]; // Optable EIDs const optableEIDS = eids.filter((e: { inserter: string }) => e.inserter === "optable.co"); const optableMatchers = [...new Set(optableEIDS.map((e: any) => e.matcher).filter(Boolean))]; const optableSources = [...new Set(optableEIDS.map((e: any) => e.source).filter(Boolean))]; - // Competitor EIDs, matched before the dedupe so a same-source EID can't hide them - const liSources = sourcesOf(allEids.filter(COMPETITOR_EIDS.liveintent)); - const iiqSources = sourcesOf(allEids.filter(COMPETITOR_EIDS.intentiq)); - const gcSources = sourcesOf(allEids.filter(COMPETITOR_EIDS.growthcode)); - const tuSources = sourcesOf(allEids.filter(COMPETITOR_EIDS.transunion)); + // Competitor EIDs + const liSources = sourcesOf(eids.filter(COMPETITOR_EIDS.liveintent)); + const iiqSources = sourcesOf(eids.filter(COMPETITOR_EIDS.intentiq)); + const gcSources = sourcesOf(eids.filter(COMPETITOR_EIDS.growthcode)); + const tuSources = sourcesOf(eids.filter(COMPETITOR_EIDS.transunion)); device = br.ortb2.device; From 7f794b5729e87d1f2174b5eecdaa2cfba0338741 Mon Sep 17 00:00:00 2001 From: mosherBT Date: Wed, 28 Oct 2026 11:33:19 -0400 Subject: [PATCH 2/2] comments --- lib/addons/prebid/analytics.test.ts | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/lib/addons/prebid/analytics.test.ts b/lib/addons/prebid/analytics.test.ts index 54f2087b..a7438f34 100644 --- a/lib/addons/prebid/analytics.test.ts +++ b/lib/addons/prebid/analytics.test.ts @@ -466,7 +466,7 @@ describe("OptablePrebidAnalytics", () => { expect(result.liveintentEIDs).toEqual([]); }); - it("reports LiveIntent and Optable EIDs side by side, before the source dedupe", async () => { + it("reports LiveIntent and Optable EIDs side by side", async () => { const result = await analytics.toWitness( liveintentAuction([ { @@ -2369,12 +2369,12 @@ describe("OptablePrebidAnalytics", () => { }); }); - describe("EID deduplication", () => { + describe("EID sources and matchers across ext.eids and eids", () => { beforeEach(() => { analytics = new OptablePrebidAnalytics(mockOptableInstance); }); - it("should deduplicate EIDs by source when both ext.eids and eids contain same source", async () => { + it("dedupes sources but keeps every Optable matcher when ext.eids and eids share a source", async () => { const auctionEndEvent = { auctionId: "auction-dedup", bidderRequests: [ @@ -2527,7 +2527,7 @@ describe("OptablePrebidAnalytics", () => { expect(result.optableMatchers).toEqual(["matcher1"]); }); - it("should deduplicate multiple EIDs with same source across different locations", async () => { + it("dedupes sources but keeps every Optable matcher across several shared sources", async () => { const auctionEndEvent = { auctionId: "auction-multi-dedup", bidderRequests: [ @@ -2566,7 +2566,7 @@ describe("OptablePrebidAnalytics", () => { expect(result.optableMatchers).not.toContain("other2"); }); - it("should handle trackAuctionEnd with duplicate EIDs in ext.eids and eids", async () => { + it("keeps every Optable matcher through trackAuctionEnd when ext.eids and eids share a source", async () => { const event = { auctionId: "auction-track-dedup", timeout: 3000,