Skip to content
5 changes: 5 additions & 0 deletions developer-extension/src/common/extension.types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,9 @@ export type SdkMessage =

export type EventCollectionStrategy = 'sdk' | 'requests'

export type InjectionVariant = 'local-dev' | 'cdn'

export type SdkInjectionType = 'RUM' | 'LOGS' | 'BOTH'
export interface Settings {
useDevBundles: DevBundlesOverride
useDevReplaySandbox: boolean
Expand All @@ -57,4 +60,6 @@ export interface Settings {
logsConfigurationOverride: object | null
debugMode: boolean
datadogMode: boolean
injectionVariant: InjectionVariant
sdkInjectionType: SdkInjectionType
Comment on lines +63 to +64
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🔨 warning: ‏The settings should be redesigned to make it more explicit and ideally avoid forbidden states. Things that should be clarified at the settings level:

  • useDevBundles: npm won't work if injectionVariant is not cdn.
  • How can I disable injection?
  • Why injection variant CDN only available in datadogMode?
  • What's the difference between injectionVariant and sdkInjectionType just by looking at property names?

My suggestion: keep things simple. Always inject both RUM and Logs CDN bundles with a default config. If dev bundle override is enabled, those bundles will be overridden (including rum-slim override). If config override is enabled, the config will be overridden. No need to do anything at the injection level.

So in the end you have a single option, inject: boolean. No need to enforce datadogMode usage, people can use it it's fine.

}
7 changes: 7 additions & 0 deletions developer-extension/src/common/packagesUrlConstants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,13 @@ export const DEV_LOGS_URL = `${DEV_SERVER_ORIGIN}/datadog-logs.js`
export const DEV_RUM_SLIM_URL = `${DEV_SERVER_ORIGIN}/datadog-rum-slim.js`
export const DEV_RUM_URL = `${DEV_SERVER_ORIGIN}/datadog-rum.js`

export const CDN_BASE_URL = 'https://www.datadoghq-browser-agent.com'
// This version corresponds to the major version of the Browser SDK and needs to be manually updated when bumping major versions
export const CDN_VERSION = 'v6'
export const CDN_RUM_URL = `${CDN_BASE_URL}/${CDN_VERSION}/datadog-rum.js`
export const CDN_RUM_SLIM_URL = `${CDN_BASE_URL}/${CDN_VERSION}/datadog-rum-slim.js`
export const CDN_LOGS_URL = `${CDN_BASE_URL}/${CDN_VERSION}/datadog-logs.js`
Comment on lines +9 to +11
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks like those constants aren't used


// To follow web-ui development, this version will need to be manually updated from time to time.
// When doing that, be sure to update types and implement any protocol changes.
export const PROD_REPLAY_SANDBOX_VERSION = '0.119.0'
Expand Down
119 changes: 114 additions & 5 deletions developer-extension/src/content-scripts/main.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
import type { Settings } from '../common/extension.types'
import { EventListeners } from '../common/eventListeners'
import { DEV_LOGS_URL, DEV_RUM_SLIM_URL, DEV_RUM_URL } from '../common/packagesUrlConstants'
import { CDN_BASE_URL, CDN_VERSION, DEV_LOGS_URL, DEV_RUM_SLIM_URL, DEV_RUM_URL } from '../common/packagesUrlConstants'
import { SESSION_STORAGE_SETTINGS_KEY } from '../common/sessionKeyConstant'
import { createLogger } from '../common/logger'

declare global {
interface Window extends EventTarget {
Expand All @@ -11,6 +12,8 @@ declare global {
}
}

const logger = createLogger('content-script-main')

interface SdkPublicApi {
[key: string]: (...args: any[]) => unknown
}
Expand All @@ -22,7 +25,6 @@ function main() {
}

sendEventsToExtension()

const settings = getSettings()

if (
Expand All @@ -47,9 +49,11 @@ function main() {
overrideInitConfiguration(ddLogsGlobal, settings.logsConfigurationOverride)
}

if (settings.useDevBundles === 'npm') {
if (settings.injectionVariant === 'local-dev' && settings.useDevBundles === 'npm') {
injectDevBundle(settings.useRumSlim ? DEV_RUM_SLIM_URL : DEV_RUM_URL, ddRumGlobal)
injectDevBundle(DEV_LOGS_URL, ddLogsGlobal)
} else if (settings.injectionVariant === 'cdn') {
injectCdnBundle(settings)
Comment on lines 52 to 56

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Gate CDN injection on Datadog mode state

The content script triggers CDN injection whenever settings.injectionVariant === 'cdn'. Because injectionVariant is persisted in storage, a user can enable Datadog mode, pick the CDN variant, and then disable Datadog mode. The UI hides the variant control, but the stored value remains cdn, so this branch still runs and injects scripts even though Datadog mode is off. This means turning off Datadog mode does not actually disable the CDN injection feature and there is no way to revert without re‑enabling Datadog mode. Consider resetting injectionVariant when Datadog mode is disabled or guarding the CDN path with settings.datadogMode.

Useful? React with 👍 / 👎.

}
}
}
Expand Down Expand Up @@ -126,8 +130,7 @@ function loadSdkScriptFromURL(url: string) {
xhr.open('GET', url, false) // `false` makes the request synchronous
xhr.send()
} catch (error) {
// eslint-disable-next-line no-console
console.error(`[DD Browser SDK extension] Error while loading ${url}:`, error)
logger.error(`Error while loading ${url}:`, error)
return
}
if (xhr.status === 200) {
Expand Down Expand Up @@ -184,3 +187,109 @@ function instrumentGlobal(global: 'DD_RUM' | 'DD_LOGS') {
function proxySdk(target: SdkPublicApi, root: SdkPublicApi) {
Object.assign(target, root)
}

function injectCdnBundle(settings: Settings) {
const injectWhenReady = () => {
if (settings.sdkInjectionType === 'RUM' || settings.sdkInjectionType === 'BOTH') {
const rumSite = (settings.rumConfigurationOverride as any)?.site as string | undefined
const rumUrl = getRumBundleUrl(settings.useRumSlim ? 'rum-slim' : 'rum', rumSite)
const rumConfig =
settings.rumConfigurationOverride ||
(settings.datadogMode
? {
applicationId: 'bd3472ea-efc2-45e1-8dff-be4cea9429b3',
clientToken: 'pub7216f8a2d1091e263c95c1205882474e',
site: 'datad0g.com',
allowedTrackingOrigins: [() => true],
sessionReplaySampleRate: 100,
}
: null)
injectAndInitializeSDK(rumUrl, 'DD_RUM', rumConfig as any)
}

if (settings.sdkInjectionType === 'LOGS' || settings.sdkInjectionType === 'BOTH') {
const logsSite = (settings.logsConfigurationOverride as any)?.site as string | undefined
const logsUrl = getLogsBundleUrl(logsSite)
const logsConfig =
settings.logsConfigurationOverride ||
(settings.datadogMode
? {
clientToken: 'pub7216f8a2d1091e263c95c1205882474e',
site: 'datad0g.com',
allowedTrackingOrigins: [() => true],
}
: null)
injectAndInitializeSDK(logsUrl, 'DD_LOGS', logsConfig as any)
}
}

if (document.readyState === 'loading') {
document.addEventListener('DOMContentLoaded', injectWhenReady, { once: true })
} else {
injectWhenReady()
}
}

function getRumBundleUrl(bundle: 'rum' | 'rum-slim', site?: string): string {
const region = getCdnRegion(site)
return `${CDN_BASE_URL}/${region}/${CDN_VERSION}/datadog-${bundle}.js`
}

function getLogsBundleUrl(site?: string) {
const region = getCdnRegion(site)
return `${CDN_BASE_URL}/${region}/${CDN_VERSION}/datadog-logs.js`
}
Comment on lines +233 to +241
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💬 suggestion: ‏Just include us1 prod rum and logs. No need to worry about regions, staging, etc.


function getCdnRegion(site?: string) {
if (!site || site === 'datadoghq.com') {
return 'us1'
}
if (site === 'datadoghq.eu') {
return 'eu1'
}
if (site?.startsWith('us3.')) {
return 'us3'
}
if (site?.startsWith('us5.')) {
return 'us5'
}
if (site?.endsWith('datad0g.com')) {
return 'us3'
}
Comment on lines +256 to +258
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🔨 warning: ‏this seems incorrect


return 'us1'
}
Copy link
Contributor Author

@BeltranBulbarellaDD BeltranBulbarellaDD Sep 26, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I can probably remove this, the thing is that I was getting 403 so this was the fallback.
Gonna look for a better way.
(Open to suggestions)


function injectAndInitializeSDK(url: string, globalName: 'DD_RUM' | 'DD_LOGS', config: object | null) {
// If the SDK is already loaded, don't try to load it again
if (window[globalName]) {
logger.log(`${globalName} already exists, skipping injection`)
return
}

if (url.includes('datadoghq-browser-agent.com')) {
const script = document.createElement('script')
script.src = url
script.async = true
script.onload = () => {
if (config && window[globalName] && 'init' in window[globalName]) {
try {
window[globalName].init(config)
} catch (e) {
logger.error(`Error initializing ${globalName}:`, e)
}
} else {
logger.log(`${globalName} loaded. No init called (no config provided).`)
}
}
script.onerror = (e) => {
logger.error(`Error loading ${globalName} script:`, e)
}
try {
document.head.appendChild(script)
} catch (appendErr) {
logger.error('failed to append script to head, retrying on documentElement', appendErr)
document.documentElement.appendChild(script)
}
}
}
92 changes: 80 additions & 12 deletions developer-extension/src/panel/components/tabs/settingsTab.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,12 @@ import { DevServerStatus, useDevServerStatus } from '../../hooks/useDevServerSta
import { useSettings } from '../../hooks/useSettings'
import { Columns } from '../columns'
import { TabBase } from '../tabBase'
import type { DevBundlesOverride, EventCollectionStrategy } from '../../../common/extension.types'
import type {
DevBundlesOverride,
EventCollectionStrategy,
InjectionVariant,
SdkInjectionType,
} from '../../../common/extension.types'

export function SettingsTab() {
const sdkDevServerStatus = useDevServerStatus(DEV_LOGS_URL)
Expand All @@ -21,10 +26,34 @@ export function SettingsTab() {
autoFlush,
debugMode: debug,
datadogMode,
injectionVariant,
sdkInjectionType,
},
setSetting,
] = useSettings()

const badgeStatus = () => {
const toBadge = (color: 'blue' | 'green' | 'yellow' | 'red', text: string) => <Badge color={color}>{text}</Badge>

const overridden = useDevBundles && (injectionVariant === 'cdn' || sdkDevServerStatus === DevServerStatus.AVAILABLE)
if (overridden) {
return toBadge('blue', 'Overridden')
}

if (injectionVariant === 'cdn') {
return toBadge('green', 'Available')
}

switch (sdkDevServerStatus) {
case DevServerStatus.AVAILABLE:
return toBadge('green', 'Available')
case DevServerStatus.CHECKING:
return toBadge('yellow', 'Checking...')
default:
return toBadge('red', 'Unavailable')
}
}

return (
<TabBase>
<div className="dd-privacy-allow">
Expand All @@ -35,17 +64,7 @@ export function SettingsTab() {
<Accordion.Control>
<Group>
<Text>Browser SDK</Text>
<Box style={{ marginLeft: 'auto' }}>
{sdkDevServerStatus === DevServerStatus.AVAILABLE && useDevBundles ? (
<Badge color="blue">Overridden</Badge>
) : sdkDevServerStatus === DevServerStatus.AVAILABLE ? (
<Badge color="green">Available</Badge>
) : sdkDevServerStatus === DevServerStatus.CHECKING ? (
<Badge color="yellow">Checking...</Badge>
) : (
<Badge color="red">Unavailable</Badge>
)}
</Box>
<Box style={{ marginLeft: 'auto' }}>{badgeStatus()}</Box>
</Group>
</Accordion.Control>
<Accordion.Panel>
Expand All @@ -56,6 +75,55 @@ export function SettingsTab() {

<Space h="md" />

{datadogMode && (
<>
<SettingItem
input={
<Group>
<Text>Injection variant:</Text>
<SegmentedControl
color="violet"
value={injectionVariant}
size="xs"
data={[
{ value: 'local-dev', label: 'Local Dev' },
{ value: 'cdn', label: 'CDN' },
]}
onChange={(value) => {
setSetting('injectionVariant', value as InjectionVariant)
}}
/>
</Group>
}
description={<></>}
/>

{injectionVariant === 'cdn' && (
<SettingItem
input={
<Group>
<Text>SDK injection type:</Text>
<SegmentedControl
color="violet"
value={sdkInjectionType}
size="xs"
data={[
{ value: 'RUM', label: 'RUM' },
{ value: 'LOGS', label: 'LOGS' },
{ value: 'BOTH', label: 'BOTH' },
]}
onChange={(value) => {
setSetting('sdkInjectionType', value as SdkInjectionType)
}}
/>
</Group>
}
description={<></>}
/>
)}
</>
)}

<SettingItem
input={
<Group>
Expand Down
2 changes: 2 additions & 0 deletions developer-extension/src/panel/hooks/useSettings.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,8 @@ const DEFAULT_SETTINGS: Readonly<Settings> = {
logsConfigurationOverride: null,
debugMode: false,
datadogMode: false,
injectionVariant: 'local-dev',
sdkInjectionType: 'BOTH',
}

let settings: Settings | undefined
Expand Down