From 50c36300391508debd339941e35028a47557ea6d Mon Sep 17 00:00:00 2001 From: John Rosendahl Date: Tue, 10 Nov 2026 11:29:03 -0700 Subject: [PATCH 1/2] fix(resolvers): ignored priorities no longer discard all EIDs in multi-node targeting In priority mode, rules with no priority (or priority <= 0) were stored under key 0. Key 0 sorted first and then failed the `if (priority)` check, so any ignored rule that returned EIDs caused the resolver to return no EIDs at all, even when priority 1+ rules resolved. Exclude priorities below 1 from selection and check for undefined explicitly. Co-Authored-By: Claude Opus 5.5 --- .../resolvers/resolveMultiTargeting.test.ts | 55 +++++++++++++++++++ lib/core/resolvers/resolveMultiTargeting.ts | 5 +- 2 files changed, 58 insertions(+), 2 deletions(-) diff --git a/lib/core/resolvers/resolveMultiTargeting.test.ts b/lib/core/resolvers/resolveMultiTargeting.test.ts index 6994832a..cba2391c 100644 --- a/lib/core/resolvers/resolveMultiTargeting.test.ts +++ b/lib/core/resolvers/resolveMultiTargeting.test.ts @@ -281,6 +281,61 @@ describe("resolveMultiNodeTargeting", () => { expect(eidSources).toEqual(new Set([])); }); + test("ignored priorities do not discard eids from valid priorities", async () => { + const mockTargetingFnOne = new Promise((resolve) => { + resolve( + createTargetingResponse({ + eids: [{ inserter: "optable.co", source: "one.com", uids: [{ id: "uid1" }] }], + resolved_ids: ["id_one"], + }) + ); + }); + + const mockTargetingFnTwo = new Promise((resolve) => { + resolve( + createTargetingResponse({ eids: [{ inserter: "optable.co", source: "two.com", uids: [{ id: "uid2" }] }] }) + ); + }); + + const mockTargetingFnUnset = new Promise((resolve) => { + resolve( + createTargetingResponse({ eids: [{ inserter: "optable.co", source: "unset.com", uids: [{ id: "uid3" }] }] }) + ); + }); + + const mockTargetingFnNegative = new Promise((resolve) => { + resolve( + createTargetingResponse({ eids: [{ inserter: "optable.co", source: "negative.com", uids: [{ id: "uid4" }] }] }) + ); + }); + + const configs = [ + { targetingFn: () => mockTargetingFnOne, matcher: "matcher_one", mm: IDMatchMethod.AUTHENTICATED, priority: 1 }, + { targetingFn: () => mockTargetingFnTwo, matcher: "matcher_two", mm: IDMatchMethod.INFERENCE, priority: 2 }, + { targetingFn: () => mockTargetingFnUnset, matcher: "matcher_unset", mm: IDMatchMethod.INFERENCE }, + { + targetingFn: () => mockTargetingFnNegative, + matcher: "matcher_negative", + mm: IDMatchMethod.INFERENCE, + priority: -1, + }, + ]; + + const { ortb2, eidSources, resolvedIds } = await resolveMultiNodeTargeting(configs); + + expect(ortb2.user!.eids).toEqual([ + { + inserter: "optable.co", + matcher: "matcher_one", + mm: IDMatchMethod.AUTHENTICATED, + source: "one.com", + uids: [{ id: "uid1" }], + }, + ]); + expect(eidSources).toEqual(new Set(["matcher_one"])); + expect(resolvedIds).toEqual(new Set(["id_one"])); + }); + it("Should use matcher from response if provided on aggregate", async () => { const mockTargetingFnOne = new Promise((resolve) => { resolve( diff --git a/lib/core/resolvers/resolveMultiTargeting.ts b/lib/core/resolvers/resolveMultiTargeting.ts index efbda55f..6bc97bc5 100644 --- a/lib/core/resolvers/resolveMultiTargeting.ts +++ b/lib/core/resolvers/resolveMultiTargeting.ts @@ -122,12 +122,13 @@ async function resolvePriorityTargeting(rules: NodeTargetingRule[]): Promise x >= 1 && eidsByPriority.get(x)?.length) .sort((a, b) => a - b) - .filter((x) => eidsByPriority.get(x)?.length) .shift(); - if (priority) { + if (priority !== undefined) { const sources = sourcesByPriority.get(priority) || []; const resolvedIdsArray = resolvedIdsByPriority.get(priority) || []; ortb2.user!.eids!.push(...(eidsByPriority.get(priority) || [])); From c95f9052fce63715a771cbe9bfe85a2954da161f Mon Sep 17 00:00:00 2001 From: John Rosendahl Date: Wed, 11 Nov 2026 10:13:37 -0700 Subject: [PATCH 2/2] docs(resolvers): clarify that priorities below 1 are ignored only in priority mode Mode selection uses a truthiness check, so all-0/unset rules are aggregated while a negative priority switches to priority mode. Reword both comments to say so, and fix the "threated" typo. Co-Authored-By: Claude Opus 5.5 --- lib/core/resolvers/resolveMultiTargeting.ts | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/lib/core/resolvers/resolveMultiTargeting.ts b/lib/core/resolvers/resolveMultiTargeting.ts index 6bc97bc5..73aba894 100644 --- a/lib/core/resolvers/resolveMultiTargeting.ts +++ b/lib/core/resolvers/resolveMultiTargeting.ts @@ -16,7 +16,8 @@ type NodeTargetingRule = { // 0 = unknown, 1 = no_match, 2 = cookie_sync, 3 = authenticated, 4 = observed, 5 = inference. mm?: IDMatchMethod; // (Optional) If provided we will only pick one resolved Ortb2Response from the most prioritize matcher. - // Any values below 1 will be threated as ignore + // Any non-zero priority on any rule switches to priority mode, where rules with no priority + // or a priority below 1 never supply eids. 0 alone counts as no priority. priority?: number; }; @@ -122,7 +123,9 @@ async function resolvePriorityTargeting(rules: NodeTargetingRule[]): Promise x >= 1 && eidsByPriority.get(x)?.length) .sort((a, b) => a - b)