From 9ee83ddae99645eca052c0d905926d684436ab16 Mon Sep 17 00:00:00 2001 From: floxis-admin Date: Wed, 1 Jul 2026 12:47:22 +0300 Subject: [PATCH] Floxis Bid Adapter: add first-party fallback id at user.ext.floxisId Mints a first-party UUID in the publisher's page context (localStorage + cookie, ~30d) as an identity-of-last-resort for cookieless browsers where the third-party __fxId cookie is blocked. Additive via mergeDeep; never overwrites an existing user.ext.floxisId; degrades to a safe no-op when storage access is disallowed. Co-Authored-By: Claude Opus 4.8 (1M context) Claude-Session: https://claude.ai/code/session_01LroYxzKYQwfC21pPcUNhjn --- modules/floxisBidAdapter.js | 49 +++++++- modules/floxisBidAdapter.md | 19 ++- test/spec/modules/floxisBidAdapter_spec.js | 140 ++++++++++++++++++++- 3 files changed, 205 insertions(+), 3 deletions(-) diff --git a/modules/floxisBidAdapter.js b/modules/floxisBidAdapter.js index ef681f09654..cb8be3a2464 100644 --- a/modules/floxisBidAdapter.js +++ b/modules/floxisBidAdapter.js @@ -1,7 +1,8 @@ import { registerBidder } from '../src/adapters/bidderFactory.js'; import { BANNER, NATIVE, VIDEO } from '../src/mediaTypes.js'; import { ortbConverter } from '../libraries/ortbConverter/converter.js'; -import { triggerPixel, politeTriggerPixel, mergeDeep, replaceAuctionPrice } from '../src/utils.js'; +import { triggerPixel, politeTriggerPixel, mergeDeep, replaceAuctionPrice, generateUUID } from '../src/utils.js'; +import { getStorageManager } from '../src/storageManager.js'; const BIDDER_CODE = 'floxis'; const GVLID = 1609; @@ -11,6 +12,12 @@ const DEFAULT_NET_REVENUE = true; const DEFAULT_REGION = 'us-e'; const DEFAULT_PARTNER = BIDDER_CODE; const SYNC_PATH = '/sync'; +const FLOXIS_ID_KEY = 'flx_uid'; +const FLOXIS_ID_COOKIE_EXP = 2592000000; // 30 days +const UUID_LENGTH = 36; + +export const storage = getStorageManager({ bidderCode: BIDDER_CODE }); + // Server-echo user-sync: the /pbjs response carries seat + region in this header (on bid and no-bid // alike), so getUserSyncs derives sync targets from serverResponses statelessly — no module state that // could leak across concurrent auctions. Absent header (older backend) => no sync, a safe no-op. @@ -88,6 +95,40 @@ function normalizeBidParams(params = {}) { }; } +function isValidFloxisId(id) { + return typeof id === 'string' && id.length === UUID_LENGTH; +} + +// Neither store available => null, not a freshly minted id we can't persist (would defeat stability). +function getOrCreateFloxisId() { + if (typeof window === 'undefined') return null; + try { + const localOk = storage.localStorageIsEnabled(); + const cookieOk = storage.cookiesAreEnabled(); + if (!localOk && !cookieOk) return null; + + let id = localOk ? storage.getDataFromLocalStorage(FLOXIS_ID_KEY) : null; + if (!isValidFloxisId(id) && cookieOk) { + id = storage.getCookie(FLOXIS_ID_KEY); + } + if (!isValidFloxisId(id)) { + id = generateUUID(); + } + + if (localOk) { + storage.setDataInLocalStorage(FLOXIS_ID_KEY, id); + } + if (cookieOk) { + const expires = new Date(Date.now() + FLOXIS_ID_COOKIE_EXP).toUTCString(); + storage.setCookie(FLOXIS_ID_KEY, id, expires); + } + + return id; + } catch (e) { + return null; + } +} + // Parse the server-echoed sync header (`seat=®ion=