Club integrations

The third-party services only this site uses, each wrapped in one package and off until its environment variable is set.

2026-11-07

The integration seams

Six packages are this site's own (the template has none of them), each wrapping one third party so its SDK or script is loaded in exactly one place. What each integration gives fans, and its status, is in Integrations; this page is how they are built.

The shared shape

  • One importer. The package is the only place the vendor's SDK, script or library is imported; a vendor change touches that package and nothing else.
  • A server-safe root and a ./client entry. The root holds settings, rules and links that server code can read; the client entry holds the provider, hooks or mount.
  • Off until configured. Each is declared in apps/web/club.config.ts only when its variable is set (the presence-gated blocks in Env markers), so CI, pnpm verify and local development build without any of them, and an unconfigured integration draws nothing and downloads nothing.

The six packages

Package Vendor Switched on by Where it mounts
@v4/wsc WSC Sports stories and clips (Blaze web SDK, pinned exactly) NEXT_PUBLIC_WSC_API_KEY WscProvider in the locale layout; WscWidget in the WSC content block (wscWidget, club/0009) and the team and player pages' rails
@v4/eltv EuroLeague TV (Deltatre Vesper SDK, pinned exactly) NEXT_PUBLIC_VESPER_API_KEY; the player also needs NEXT_PUBLIC_ELTV_ACCESS_URL Not mounted yet — the Game Centre links out to ELTV (see The ELTV livestream)
@v4/ecal ECAL "Add to calendar" widget NEXT_PUBLIC_ECAL_API_KEY EcalProvider in the locale layout; the Games page's schedule picker (EcalSchedulePicker), per-competition displays in features.calendar.displays
@v4/analytics Google Tag Manager and OneTrust NEXT_PUBLIC_GTM_CONTAINER_ID, NEXT_PUBLIC_ONETRUST_DOMAIN_SCRIPT_ID The head scripts and AnalyticsProvider in the locale layout; PageAnalytics on every route
@v4/cortex-sso Cortex (EuroLeague ID) over NextAuth CORTEX_SSO_BASE_URL (its companions then required) app/api/auth/[...nextauth], auth/login/return, auth/logout; the header's session hook
@v4/code-of-conduct — (email to ELB) Always mounted; sending needs an email provider /en/code-of-conduct and app/api/code-of-conduct/{games,reporter,report}

No GA4 or gtag.js is loaded: every tag lives in ELB's GTM container, and @v4/analytics is the only place the site pushes to the dataLayer. OneTrust's groups map onto the tracking plan's four consent categories, Consent Mode v2 is set before GTM loads, and without OneTrust nothing is ever sent — every event waits for consent. Each route renders PageAnalytics so its page_viewed says what the page is.

Sign-in

features.auth is an adapter seam, so Cortex's settings are read by readCortexSsoSettings(process.env) rather than through env markers, and a missing companion variable fails the build by name. It backs the header's sign-in and the Code of Conduct report; no video entitlement uses it yet.

The Code of Conduct report

The report checks the EuroLeague ID session, lists games from the Competition Engine (locally the sample season when the engine cannot be reached), reads the reporter's name and email from the session's ID token, and emails the report to ELB. No email provider is chosen: a local site logs the email, and elsewhere Send answers 503.

WSC

WSC writes its key into the page, so the key is a public marker by nature; the SDK is downloaded only when a widget mounts.

Each package has its own reference page: @v4/wsc, @v4/eltv, @v4/ecal, @v4/analytics, @v4/cortex-sso, @v4/code-of-conduct. Every variable is listed in Environment variables.