Repository navigation
Tercept Analytics Adapter: single request per auction, concurrent auction isolation & payload optimisation - #1
Closed
mdusmanalvi wants to merge 2 commits into
Closed
mdusmanalvi wants to merge 2 commits into
mdusmanalvi wants to merge 2 commits into
Conversation
- Replace shared events object with per-auctionId Map to fix data loss when multiple auctions run concurrently - Accumulate BID_WON, AD_RENDER_SUCCEEDED, AD_RENDER_FAILED and BIDDER_ERROR into the bids array; flush once 1.5s after AUCTION_END - Use navigator.sendBeacon on visibilitychange so data survives page exit - Add disableAnalytics to clear pending timers and maps on teardown - Add try/catch around track() to prevent adapter errors disrupting page - Remove ad, native and adUrl fields from bid payloads to reduce size - Limit auctionInit.bidderRequests to first entry (device/site data is identical across all bidder requests) - Add is_pl flag (true on first bid of first auction per page load)
Replace tests that assumed multiple requests per auction and separate top-level keys (bidWon, adRenderSucceeded, etc.) with tests matching the new single-request-per-auction design. Covers: - Request timing: no send before 1.5s timer, one request per auction - Payload structure: top-level shape, bidderRequests trimmed to first, host/path/search attached at send time, ad/native/adUrl absent - All event handlers: BID_REQUESTED, BID_RESPONSE, BID_TIMEOUT, NO_BID, BID_WON, AD_RENDER_SUCCEEDED, AD_RENDER_FAILED, BIDDER_ERROR - is_pl flag: true on first bid of first auction only, reset on disable - Concurrent auction isolation: separate bids and auctionInit per id - visibilitychange flush via sendBeacon - disableAnalytics: cancels timers, clears maps, resets firstSent - Error resilience: unknown auctionIds and undefined args do not throw
|
Tread carefully! This PR adds 81 linter errors (possibly disabled through directives):
|
mdusmanalvi
pushed a commit
that referenced
this pull request
Sep 15, 2026
…15379) * New RTD Module: Unicorn Viewability (slot position + viewability signals) (#1) * New RTD Module: Unicorn Viewability (slot position + viewability signals) - Add unicornViewabilityRtdProvider: measure each ad slot's on-screen position and visibility ratio on the client, inject into ortb2Imp.ext.data.unicorn and the standard ortb2Imp.banner.pos. - unicornBidAdapter: forward the measured signal to the wire as imp.ext.unicorn (flat vendor key), and pass through imp.banner.pos. - Add integration example page (integrationExamples/gpt). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * Unicorn Viewability RTD Module: add docs and unit tests - modules/unicornViewabilityRtdProvider.md: module overview, integration, configuration, slot element resolution, injected-field table. - test/spec/modules/unicornViewabilityRtdProvider_spec.js: cover init, getBidRequestData injection (ver 1 / banner.pos), missing-slot and empty-adUnits cases. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * Unicorn RTD Module: rename to unicornRtdProvider, address review feedback - Rename unicornViewabilityRtdProvider -> unicornRtdProvider (module, md, spec); submodule name 'unicornViewability' -> 'unicorn'; export unicornViewabilitySubmodule -> unicornSubmodule. - unicornBidAdapter: rename local var unicornViewability -> unicornSignal. - Drop the integration example page from the PR. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * Unicorn RTD Module: name the signal namespace imp.ext.adslot Rename the injected/wire key from the vendor name `unicorn` to `adslot`, reflecting that it carries the ad slot's position/geometry (x/y/w/h/fixed) plus visibility ratio. Internal ortb2Imp.ext.data.adslot, wire imp.ext.adslot. Also rename the adapter local var unicornSignal -> adslotSignal, and update the module doc and unit tests. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * Unicorn RTD Module: map fixed/sticky slots to banner.pos=2 (AdCOM Locked) Per AdCOM 1.0 Placement Positions (used by prebid/openrtb v20, which alicorn follows), position 2 = Locked (fixed position). Emit banner.pos=2 for fixed/sticky slots instead of only 1/3; keep the adslot.fixed flag as the raw signal. Update the module doc and add a unit test. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com> * Unicorn RTD Provider: address PR review feedback - Source the visibility ratio from Prebid's shared `percentInView` helper and drop the module's own IntersectionObserver. This removes the DOM-id-keyed map and observer lifecycle flagged in review (prototype pollution / observer leak) by removal rather than patching. - Read getBoundingClientRect directly for geometry so the shared cached helper can't return a prior-auction rectangle here. - Register unicornRtdProvider under rtdModule in modules/.submodules.json so `--modules=unicornRtdProvider` pulls in rtdModule. - Docs: consistent "RTD" capitalization; note the visibility-ratio source. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * Drop unicornRtdProvider, measure adslot signals inline in the bid adapter Per review feedback (patmmccann, dgirardi), moves slot position/geometry/ viewability measurement out of the RTD module and into unicornBidAdapter's buildRequests. This resolves the structural issues that came from writing to shared FPD (ortb2Imp), which affected every bidder on the page, not just Unicorn: - ext.data.adslot collided with an established key (a GAM slot path string read by beopBidAdapter/rubiconBidAdapter) — gone, since imp.ext.unicorn is now built only inside this adapter's own OpenRTB payload. - banner.pos was written unconditionally regardless of mediaType, corrupting ortb2Imp for video/native-only ad units — gone, same reason. - The RTD timeout/hidden-tab/stale-ad-unit-mutation issues (RTD ignored the timeout argument, requestAnimationFrame stalls in background tabs, and ad unit objects were mutated in place) no longer apply: measurement is a single synchronous read per bid request, the same pattern other adapters use with percentInView (33across, marsmedia, oms, ...). Also fixes, in the new inline implementation: - banner.pos now compares document-relative y (rect + scroll) against viewport height, instead of viewport-relative rect.top, so it no longer flips after the user scrolls and a refresh auction runs. - Drops the AdCOM "Locked" pos=2 value — OpenRTB 2.5 marks 2 DEPRECATED, and it would have reached bidders that relay OpenRTB. Fixed/sticky is now only carried in ext.unicorn's own fixed/sticky flags (kept as two flags, since `position: sticky` alone doesn't mean "currently stuck"). - Detects fixed/sticky by walking ancestors, not just the slot element, since anchor/sticky ad units are usually a fixed/sticky wrapper around a statically positioned ad div. - percentInView/getViewability are now called with the ad unit's size, so an unrendered (0x0) GPT slot still gets a real ratio/w/h via the size override, instead of ratio:0, h:0. - getViewportOffset compensates for measuring from inside a friendly iframe, consistent with how percentInView itself handles that case. - A publisher-declared ortb2Imp.banner.pos, if present, is used instead of the measured value. Removes modules/unicornRtdProvider.{js,md}, its spec, and the modules/.submodules.json entry. Documents the new imp.banner.pos/imp.ext.unicorn behavior in modules/unicornBidAdapter.md, with adapter spec coverage for the fold-vs-scroll fix, the size-override fix, fixed vs sticky detection, GPT slot mapping, the ortb2Imp.banner.pos override, and the no-matching-element case. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com> Co-authored-by: Patrick McCann <patmmccann@gmail.com> Co-authored-by: Demetrio Girardi <dgirardi@prebid.org>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Type of change
Description of change
Refactored the Tercept Analytics Adapter to fix a data-loss bug with concurrent auctions, reduce request volume from ~4 per auction to 1, and trim payload size.
Concurrent auction isolation (bug fix):
eventsobject with aMapkeyed byauctionIdAUCTION_INITwould reset shared state, silently dropping data from any in-flight auction; each auction is now fully isolatedSingle request per auction:
BID_WON,AD_RENDER_SUCCEEDED,AD_RENDER_FAILED,BIDDER_ERROR) are now accumulated into the bids array and flushed in one POST ~1.5s afterAUCTION_ENDPage-exit reliability via
sendBeacon:visibilitychangehandler flushes pending auctions usingnavigator.sendBeaconinstead ofajax, guaranteeing delivery when the user navigates away before the 1.5s timer firesPayload size reduction:
ad(full creative markup, 10–50 KB per bid),nativepayload, andadUrlfrom bid objectsauctionInit.bidderRequeststrimmed to first entry only — device and site data is identical across all bidder requestsPage-load detection:
is_plboolean field:trueon the first bid of the first auction per page load,falseon all othersResilience and cleanup:
try/catcharoundtrack()so any unexpected error does not disrupt the publisher pagedisableAnalyticsto cancel pending timers and clear maps on teardown (prevents state leaking across SPA navigations and test runs)Other information