Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
32 changes: 30 additions & 2 deletions crates/js/lib/src/integrations/prebid/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,35 @@ import 'prebid.js/modules/consentManagementTcf.js';
import 'prebid.js/modules/consentManagementGpp.js';
import 'prebid.js/modules/consentManagementUsp.js';

// Prebid User ID Module — core + submodules. The core module exposes
// `pbjs.getUserIdsAsEids`; submodules self-register at import time and
// activate when the publisher's origin-side `pbjs.setConfig({ userSync:
// { userIds: [...] } })` call runs during `processQueue()`.
import 'prebid.js/modules/userId.js';

// Zero-config / auto-populating submodules (resolve without publisher params).
import 'prebid.js/modules/sharedIdSystem.js';
import 'prebid.js/modules/criteoIdSystem.js';
import 'prebid.js/modules/33acrossIdSystem.js';
import 'prebid.js/modules/pubProvidedIdSystem.js';
import 'prebid.js/modules/quantcastIdSystem.js';

// Param-based submodules — inert until publisher setConfig supplies params.
import 'prebid.js/modules/id5IdSystem.js';
import 'prebid.js/modules/identityLinkIdSystem.js';
// NOTE: `liveIntentIdSystem.js` is intentionally not imported. Its upstream
// module uses a dynamic `require()` inside a build-flag-guarded branch that
// Prebid's own gulp pipeline dead-codes via constant folding; esbuild leaves
// the `require()` call in the bundle, which throws at browser runtime. Re-
// enabling it requires an esbuild resolver plugin (or switching to Prebid's
// own build pipeline). Tracked as a follow-up in the design spec.
import 'prebid.js/modules/uid2IdSystem.js';
import 'prebid.js/modules/euidIdSystem.js';
import 'prebid.js/modules/intentIqIdSystem.js';
import 'prebid.js/modules/lotamePanoramaIdSystem.js';
import 'prebid.js/modules/connectIdSystem.js';
import 'prebid.js/modules/merkleIdSystem.js';

// Client-side bid adapters — self-register with prebid.js on import.
// The set of adapters is controlled by the TSJS_PREBID_ADAPTERS env var at
// build time. See _adapters.generated.ts (written by build-all.mjs).
Expand Down Expand Up @@ -410,8 +439,7 @@ function syncPrebidEidsCookie(): void {
return; // Single EID too large — skip.
}

document.cookie =
`${EID_COOKIE_NAME}=${encoded}; Path=/; Secure; SameSite=Lax; Max-Age=${EID_COOKIE_MAX_AGE}`;
document.cookie = `${EID_COOKIE_NAME}=${encoded}; Path=/; Secure; SameSite=Lax; Max-Age=${EID_COOKIE_MAX_AGE}`;

log.debug(`[tsjs-prebid] synced ${payload.length} EIDs to cookie`);
} catch (err) {
Expand Down
77 changes: 77 additions & 0 deletions crates/js/lib/src/integrations/sourcepoint/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
import { log } from '../../core/log';

const SP_CONSENT_PREFIX = '_sp_user_consent_';

interface SourcepointGppData {
gppString: string;
applicableSections: number[];
}

interface SourcepointConsentPayload {
gppData?: SourcepointGppData;
}

function findSourcepointConsent(): SourcepointConsentPayload | null {
for (let i = 0; i < localStorage.length; i++) {
const key = localStorage.key(i);
if (!key?.startsWith(SP_CONSENT_PREFIX)) continue;

const raw = localStorage.getItem(key);
if (!raw) continue;

try {
return JSON.parse(raw) as SourcepointConsentPayload;
} catch {
log.debug('sourcepoint: failed to parse localStorage value', { key });
return null;
}
}
return null;
}

function writeCookie(name: string, value: string): void {
document.cookie = `${name}=${value}; path=/; SameSite=Lax`;
}

/**
* Reads Sourcepoint consent from localStorage and mirrors it into
* `__gpp` and `__gpp_sid` cookies for Trusted Server to read.
*
* Returns `true` if cookies were written, `false` otherwise.
*/
export function mirrorSourcepointConsent(): boolean {
if (typeof localStorage === 'undefined' || typeof document === 'undefined') {
return false;
}

const payload = findSourcepointConsent();
if (!payload?.gppData) {
log.debug('sourcepoint: no GPP data found in localStorage');
return false;
}

const { gppString, applicableSections } = payload.gppData;
if (!gppString) {
log.debug('sourcepoint: gppString is empty');
return false;
}

writeCookie('__gpp', gppString);

if (Array.isArray(applicableSections) && applicableSections.length > 0) {
writeCookie('__gpp_sid', applicableSections.join(','));
}

log.info('sourcepoint: mirrored GPP consent to cookies', {
gppLength: gppString.length,
sections: applicableSections,
});

return true;
}

if (typeof window !== 'undefined') {
mirrorSourcepointConsent();
}

export default mirrorSourcepointConsent;
Loading
Loading