Skip to content
Open
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
2 changes: 1 addition & 1 deletion .github/actions/setup-integration-test-env/action.yml
Original file line number Diff line number Diff line change
Expand Up @@ -80,7 +80,7 @@ runs:
env:
TRUSTED_SERVER__PUBLISHER__ORIGIN_URL: http://127.0.0.1:${{ inputs.origin-port }}
TRUSTED_SERVER__PUBLISHER__PROXY_SECRET: integration-test-proxy-secret
TRUSTED_SERVER__EC__PASSPHRASE: integration-test-ec-secret
TRUSTED_SERVER__EC__PASSPHRASE: integration-test-ec-secret-32-bytes
TRUSTED_SERVER__PROXY__CERTIFICATE_CHECK: "false"
run: cargo build --package trusted-server-adapter-fastly --release --target wasm32-wasip1

Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,7 @@ jobs:
env:
TRUSTED_SERVER__PUBLISHER__ORIGIN_URL: http://127.0.0.1:8080
TRUSTED_SERVER__PUBLISHER__PROXY_SECRET: integration-test-proxy-secret
TRUSTED_SERVER__EC__PASSPHRASE: integration-test-ec-secret
TRUSTED_SERVER__EC__PASSPHRASE: integration-test-ec-secret-32-bytes
TRUSTED_SERVER__PROXY__CERTIFICATE_CHECK: "false"
run: cargo build --package trusted-server-adapter-fastly --release --target wasm32-wasip1

Expand Down
121 changes: 93 additions & 28 deletions crates/js/lib/build-all.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,10 @@
* names to include in the bundle (e.g. "rubicon,appnexus,openx").
* Each name must have a corresponding {name}BidAdapter.js module in
* the prebid.js package. Default: "rubicon".
* TSJS_PREBID_USER_ID_MODULES — Comma-separated list of Prebid.js User ID
* modules to include in the bundle (e.g. "sharedId,pubProvided,uid2").
* Names may be module bases ("sharedId") or exact module names
* ("sharedIdSystem"). Default: empty.
*/

import fs from 'node:fs';
Expand All @@ -30,11 +34,10 @@ const integrationsDir = path.join(srcDir, 'integrations');
// ---------------------------------------------------------------------------

const DEFAULT_PREBID_ADAPTERS = 'rubicon';
const ADAPTERS_FILE = path.join(
integrationsDir,
'prebid',
'_adapters.generated.ts',
);
const DEFAULT_PREBID_USER_ID_MODULES = '';
const prebidIntegrationDir = path.join(integrationsDir, 'prebid');
const ADAPTERS_FILE = path.join(prebidIntegrationDir, '_adapters.generated.ts');
const USER_ID_MODULES_FILE = path.join(prebidIntegrationDir, '_user_id_modules.generated.ts');

/**
* Generate `_adapters.generated.ts` with import statements for each adapter
Expand All @@ -43,36 +46,34 @@ const ADAPTERS_FILE = path.join(
* Invalid adapter names (those without a matching module in prebid.js) are
* logged and skipped.
*/
function generatePrebidAdapters() {
const raw = process.env.TSJS_PREBID_ADAPTERS || DEFAULT_PREBID_ADAPTERS;
const names = raw
const prebidModulesDir = path.join(__dirname, 'node_modules', 'prebid.js', 'modules');

function parseCommaSeparatedEnv(name, fallback) {
return (process.env[name] || fallback)
.split(',')
.map((s) => s.trim())
.filter(Boolean);
}

function generatePrebidAdapters() {
const names = parseCommaSeparatedEnv('TSJS_PREBID_ADAPTERS', DEFAULT_PREBID_ADAPTERS);

if (names.length === 0) {
console.warn(
'[build-all] TSJS_PREBID_ADAPTERS is empty, falling back to default:',
DEFAULT_PREBID_ADAPTERS,
DEFAULT_PREBID_ADAPTERS
);
names.push(DEFAULT_PREBID_ADAPTERS);
}

const modulesDir = path.join(
__dirname,
'node_modules',
'prebid.js',
'modules',
);

// Validate each adapter and build import lines
const imports = [];
for (const name of names) {
const moduleFile = `${name}BidAdapter.js`;
const modulePath = path.join(modulesDir, moduleFile);
const modulePath = path.join(prebidModulesDir, moduleFile);
if (!fs.existsSync(modulePath)) {
console.error(
`[build-all] WARNING: Prebid adapter "${name}" not found (expected ${moduleFile}), skipping`,
`[build-all] WARNING: Prebid adapter "${name}" not found (expected ${moduleFile}), skipping`
);
continue;
}
Expand All @@ -81,7 +82,7 @@ function generatePrebidAdapters() {

if (imports.length === 0) {
console.error(
'[build-all] WARNING: No valid Prebid adapters found, bundle will have no client-side adapters',
'[build-all] WARNING: No valid Prebid adapters found, bundle will have no client-side adapters'
);
}

Expand All @@ -94,18 +95,77 @@ function generatePrebidAdapters() {
`// Default: "${DEFAULT_PREBID_ADAPTERS}"`,
'',
...imports,
'',
].join('\n');

fs.writeFileSync(ADAPTERS_FILE, content);
fs.writeFileSync(ADAPTERS_FILE, `${content}\n`);

const adapterNames = names.filter((name) =>
fs.existsSync(path.join(modulesDir, `${name}BidAdapter.js`)),
fs.existsSync(path.join(prebidModulesDir, `${name}BidAdapter.js`))
);
console.log('[build-all] Prebid adapters:', adapterNames);
}

function prebidModuleExists(moduleFile) {
const sourceFile = path.join(prebidModulesDir, moduleFile);
const sourceTsFile = path.join(prebidModulesDir, moduleFile.replace(/\.js$/, '.ts'));
const publicDistFile = path.join(
__dirname,
'node_modules',
'prebid.js',
'dist',
'src',
'public',
moduleFile
);

return fs.existsSync(sourceFile) || fs.existsSync(sourceTsFile) || fs.existsSync(publicDistFile);
}

function resolvePrebidUserIdModule(name) {
const candidates = [
`${name}.js`,
`${name}System.js`,
`${name}IdSystem.js`,
`${name}IdSubmodule.js`,
];

return candidates.find(prebidModuleExists);
}

function generatePrebidUserIdModules() {
const names = parseCommaSeparatedEnv(
'TSJS_PREBID_USER_ID_MODULES',
DEFAULT_PREBID_USER_ID_MODULES
);

const imports = [];
const moduleNames = [];
for (const name of names) {
const moduleFile = resolvePrebidUserIdModule(name);
if (!moduleFile) {
console.error(`[build-all] WARNING: Prebid User ID module "${name}" not found, skipping`);
continue;
}
imports.push(`import 'prebid.js/modules/${moduleFile}';`);
moduleNames.push(moduleFile.replace(/\.js$/, ''));
}

const content = [
'// Auto-generated by build-all.mjs — manual edits will be overwritten at build time.',
'//',
'// Controls which Prebid.js User ID modules are included in the bundle.',
'// Set the TSJS_PREBID_USER_ID_MODULES environment variable to a comma-separated list',
'// of module names (e.g. "sharedId,pubProvided,uid2") before building.',
'// Default: empty',
...(imports.length > 0 ? ['', ...imports] : []),
].join('\n');

fs.writeFileSync(USER_ID_MODULES_FILE, `${content}\n`);
console.log('[build-all] Prebid User ID modules:', moduleNames);
}

generatePrebidAdapters();
generatePrebidUserIdModules();

// ---------------------------------------------------------------------------

Expand All @@ -120,8 +180,7 @@ const integrationModules = fs.existsSync(integrationsDir)
.filter((name) => {
const fullPath = path.join(integrationsDir, name);
return (
fs.statSync(fullPath).isDirectory() &&
fs.existsSync(path.join(fullPath, 'index.ts'))
fs.statSync(fullPath).isDirectory() && fs.existsSync(path.join(fullPath, 'index.ts'))
);
})
.sort()
Expand All @@ -143,7 +202,15 @@ async function buildModule(name, entryPath) {
// "exports" map, but we need it for client-side bidder validation.
'prebid.js/src/adapterManager.js': path.resolve(
__dirname,
'node_modules/prebid.js/dist/src/src/adapterManager.js',
'node_modules/prebid.js/dist/src/src/adapterManager.js'
),
// The published liveIntentIdSystem module contains a build-time
// require() switch that is not replaced by our Vite build. Import the
// standard implementation directly so browser bundles do not contain
// CommonJS require calls.
'prebid.js/modules/liveIntentIdSystem.js': path.resolve(
__dirname,
'node_modules/prebid.js/dist/src/libraries/liveIntentId/idSystem.js'
),
},
},
Expand Down Expand Up @@ -176,9 +243,7 @@ async function buildModule(name, entryPath) {
await buildModule('core', path.join(srcDir, 'core', 'index.ts'));

await Promise.all(
integrationModules.map((name) =>
buildModule(name, path.join(integrationsDir, name, 'index.ts')),
),
integrationModules.map((name) => buildModule(name, path.join(integrationsDir, name, 'index.ts')))
);

// List all built files
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
// Auto-generated by build-all.mjs — manual edits will be overwritten at build time.
//
// Controls which Prebid.js User ID modules are included in the bundle.
// Set the TSJS_PREBID_USER_ID_MODULES environment variable to a comma-separated list
// of module names (e.g. "sharedId,pubProvided,uid2") before building.
// Default: empty
19 changes: 18 additions & 1 deletion crates/js/lib/src/integrations/prebid/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,11 @@ import 'prebid.js/modules/consentManagementGpp.js';
import 'prebid.js/modules/consentManagementUsp.js';
import 'prebid.js/modules/userId.js';

// User ID submodules — self-register with prebid.js on import.
// The set of submodules is controlled by the TSJS_PREBID_USER_ID_MODULES env var
// at build time. See _user_id_modules.generated.ts (written by build-all.mjs).
import './_user_id_modules.generated';

// 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 All @@ -42,6 +47,8 @@ export interface PrebidNpmConfig {
timeout?: number;
/** Enable Prebid.js debug logging. Defaults to false. */
debug?: boolean;
/** Prebid.js userSync configuration for User ID modules. */
userSync?: unknown;
}

/**
Expand All @@ -53,6 +60,8 @@ interface InjectedPrebidConfig {
timeout?: number;
debug?: boolean;
bidders?: string[];
/** Prebid.js userSync configuration for User ID modules. */
userSync?: unknown;
/** Bidders that run client-side via native Prebid.js adapters. */
clientSideBidders?: string[];
}
Expand Down Expand Up @@ -177,6 +186,10 @@ function isDefined<T>(value: T | undefined): value is T {
return value !== undefined;
}

function isRecord(value: unknown): value is Record<string, unknown> {
return !!value && typeof value === 'object' && !Array.isArray(value);
}

function collectAuctionEids(): AuctionEid[] | undefined {
if (typeof pbjs.getUserIdsAsEids !== 'function') {
return undefined;
Expand Down Expand Up @@ -218,6 +231,7 @@ export function installPrebidNpm(config?: Partial<PrebidNpmConfig>): typeof pbjs
endpoint: config?.endpoint,
timeout: config?.timeout ?? injected?.timeout,
debug: config?.debug ?? injected?.debug,
userSync: config?.userSync ?? injected?.userSync,
};

auctionEndpoint = merged.endpoint ?? '/auction';
Expand Down Expand Up @@ -366,12 +380,15 @@ export function installPrebidNpm(config?: Partial<PrebidNpmConfig>): typeof pbjs
};

// Apply initial configuration
const pbjsConfig: PbjsConfig & { bidderTimeout?: number } = {
const pbjsConfig: PbjsConfig & { bidderTimeout?: number; userSync?: unknown } = {
debug: merged.debug ?? false,
};
if (typeof merged.timeout === 'number') {
pbjsConfig.bidderTimeout = merged.timeout;
}
if (isRecord(merged.userSync)) {
pbjsConfig.userSync = merged.userSync;
}
pbjs.setConfig(pbjsConfig as PbjsConfig);

// processQueue() must be called after all modules are loaded when using
Expand Down
Loading
Loading