Skip to content

Commit 077b2b1

Browse files
committed
Default debugger init version from build metadata
Use build-plugin injected Live Debugger metadata as the default runtime `init().version`, and warn when an explicit init version disagrees. Document the fallback behavior and cover the new version resolution path in unit tests.
1 parent 1eeda79 commit 077b2b1

3 files changed

Lines changed: 144 additions & 23 deletions

File tree

packages/debugger/README.md

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,10 +16,14 @@ datadogDebugger.init({
1616
site: '<DATADOG_SITE>',
1717
service: 'my-web-application',
1818
// env: 'production',
19-
// version: '1.0.0',
19+
// version: 'my-deployed-build-version',
2020
})
2121
```
2222

23+
When you also use the Datadog Live Debugger build plugin, `init().version` defaults to the build-time `liveDebugger.version` metadata injected into the bundle. If you pass both values explicitly and they differ, the SDK keeps the `init()` value and logs a warning.
24+
25+
If provided, `version` should be set to the immutable deployed browser build identifier used for source map upload and browser build resolution. If omitted, debugger delivery and snapshots still work, but browser build lookup and source-aware resolution may be unavailable.
26+
2327
## Troubleshooting
2428

2529
Need help? Contact [Datadog Support][3].
Lines changed: 87 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,98 @@
1+
import { display } from '@datadog/browser-core'
2+
import { registerCleanupTask, replaceMockableWithSpy } from '@datadog/browser-core/test'
3+
import { initDebuggerTransport } from '../domain/api'
4+
import { startDeliveryApiPolling } from '../domain/deliveryApi'
5+
import { startDebuggerBatch } from '../transport/startDebuggerBatch'
6+
import type { BrowserWindow } from './main'
17
import { datadogDebugger } from './main'
28

39
describe('datadogDebugger', () => {
10+
const browserWindow: BrowserWindow = window
11+
12+
beforeEach(() => {
13+
delete browserWindow.__DD_LIVE_DEBUGGER_BUILD__
14+
delete browserWindow.$dd_entry
15+
delete browserWindow.$dd_return
16+
delete browserWindow.$dd_throw
17+
delete browserWindow.$dd_probes
18+
19+
registerCleanupTask(() => {
20+
delete browserWindow.__DD_LIVE_DEBUGGER_BUILD__
21+
delete browserWindow.$dd_entry
22+
delete browserWindow.$dd_return
23+
delete browserWindow.$dd_throw
24+
delete browserWindow.$dd_probes
25+
})
26+
})
27+
428
it('should only expose init, version, and onReady', () => {
529
expect(datadogDebugger).toEqual({
630
init: jasmine.any(Function),
731
version: jasmine.any(String),
832
onReady: jasmine.any(Function),
933
})
1034
})
35+
36+
it('should default the init version from build-plugin metadata', async () => {
37+
browserWindow.__DD_LIVE_DEBUGGER_BUILD__ = { version: 'build-version' }
38+
replaceMockableWithSpy(startDebuggerBatch).and.callFake(() => ({
39+
flushController: undefined as any,
40+
add: () => undefined,
41+
flush: () => undefined,
42+
stop: () => undefined,
43+
upsert: () => undefined,
44+
}))
45+
const initTransportSpy = replaceMockableWithSpy(initDebuggerTransport)
46+
const startDeliveryApiPollingSpy = replaceMockableWithSpy(startDeliveryApiPolling)
47+
48+
datadogDebugger.init({
49+
clientToken: 'client-token',
50+
service: 'service-name',
51+
env: 'staging',
52+
})
53+
54+
await flushPromises()
55+
56+
expect(initTransportSpy).toHaveBeenCalledWith(
57+
jasmine.objectContaining({ version: 'build-version' }),
58+
jasmine.anything()
59+
)
60+
expect(startDeliveryApiPollingSpy).toHaveBeenCalledWith(jasmine.objectContaining({ version: 'build-version' }))
61+
expect(browserWindow.$dd_entry).toBeDefined()
62+
expect(browserWindow.$dd_return).toBeDefined()
63+
expect(browserWindow.$dd_throw).toBeDefined()
64+
expect(browserWindow.$dd_probes).toBeDefined()
65+
})
66+
67+
it('should warn when the explicit init version mismatches build-plugin metadata', async () => {
68+
browserWindow.__DD_LIVE_DEBUGGER_BUILD__ = { version: 'build-version' }
69+
replaceMockableWithSpy(startDebuggerBatch).and.callFake(() => ({
70+
flushController: undefined as any,
71+
add: () => undefined,
72+
flush: () => undefined,
73+
stop: () => undefined,
74+
upsert: () => undefined,
75+
}))
76+
replaceMockableWithSpy(initDebuggerTransport)
77+
const startDeliveryApiPollingSpy = replaceMockableWithSpy(startDeliveryApiPolling)
78+
const warnSpy = spyOn(display, 'warn')
79+
80+
datadogDebugger.init({
81+
clientToken: 'client-token',
82+
service: 'service-name',
83+
env: 'staging',
84+
version: 'runtime-version',
85+
})
86+
87+
await flushPromises()
88+
89+
expect(warnSpy).toHaveBeenCalledWith(jasmine.stringMatching(/does not match the build-plugin version/))
90+
expect(startDeliveryApiPollingSpy).toHaveBeenCalledWith(jasmine.objectContaining({ version: 'runtime-version' }))
91+
})
1192
})
93+
94+
async function flushPromises() {
95+
for (let i = 0; i < 10; i++) {
96+
await Promise.resolve()
97+
}
98+
}

packages/debugger/src/entries/main.ts

Lines changed: 52 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -6,13 +6,17 @@
66
* @see [Live Debugger Documentation](https://docs.datadoghq.com/tracing/live_debugger/)
77
*/
88

9-
import { defineGlobal, getGlobalObject, makePublicApi } from '@datadog/browser-core'
9+
import { defineGlobal, display, getGlobalObject, makePublicApi, mockable } from '@datadog/browser-core'
1010
import type { PublicApi, Site } from '@datadog/browser-core'
11-
import { onEntry, onReturn, onThrow, initDebuggerTransport } from '../domain/api'
11+
import { initDebuggerTransport, onEntry, onReturn, onThrow } from '../domain/api'
1212
import { startDeliveryApiPolling } from '../domain/deliveryApi'
1313
import { getProbes } from '../domain/probes'
1414
import { startDebuggerBatch } from '../transport/startDebuggerBatch'
1515

16+
export interface DebuggerBuildMetadata {
17+
version?: string
18+
}
19+
1620
/**
1721
* Configuration options for initializing the Live Debugger SDK
1822
*/
@@ -89,38 +93,68 @@ export interface DatadogDebugger extends PublicApi {
8993
* service: 'my-app',
9094
* site: 'datadoghq.com',
9195
* env: 'production'
96+
* version: 'my-deployed-build-version',
9297
* })
9398
* ```
9499
*/
95100
init: (initConfiguration: DebuggerInitConfiguration) => void
96101
}
97102

103+
export interface BrowserWindow extends Window {
104+
DD_DEBUGGER?: DatadogDebugger
105+
__DD_LIVE_DEBUGGER_BUILD__?: DebuggerBuildMetadata
106+
$dd_entry?: typeof onEntry
107+
$dd_return?: typeof onReturn
108+
$dd_throw?: typeof onThrow
109+
$dd_probes?: typeof getProbes
110+
}
111+
112+
function resolveDebuggerVersion(initConfiguration: DebuggerInitConfiguration): string | undefined {
113+
const buildVersion = getGlobalObject<BrowserWindow>().__DD_LIVE_DEBUGGER_BUILD__?.version
114+
115+
if (
116+
initConfiguration.version !== undefined &&
117+
buildVersion !== undefined &&
118+
initConfiguration.version !== buildVersion
119+
) {
120+
display.warn(
121+
`Debugger: init version "${initConfiguration.version}" does not match the build-plugin version "${buildVersion}". Using the init version.`
122+
)
123+
}
124+
125+
return initConfiguration.version ?? buildVersion
126+
}
127+
98128
/**
99129
* Create the public API for the Live Debugger
100130
*/
101131
function makeDebuggerPublicApi(): DatadogDebugger {
102132
return makePublicApi<DatadogDebugger>({
103133
init: (initConfiguration: DebuggerInitConfiguration) => {
134+
const resolvedConfiguration = {
135+
...initConfiguration,
136+
version: resolveDebuggerVersion(initConfiguration),
137+
}
138+
104139
// Initialize debugger's own transport
105-
const batch = startDebuggerBatch(initConfiguration)
106-
initDebuggerTransport(initConfiguration, batch)
140+
const batch = mockable(startDebuggerBatch)(resolvedConfiguration)
141+
mockable(initDebuggerTransport)(resolvedConfiguration, batch)
107142

108143
// Expose internal hooks on globalThis for instrumented code
109-
if (typeof globalThis !== 'undefined') {
110-
;(globalThis as any).$dd_entry = onEntry
111-
;(globalThis as any).$dd_return = onReturn
112-
;(globalThis as any).$dd_throw = onThrow
113-
;(globalThis as any).$dd_probes = getProbes
114-
}
144+
const debuggerGlobal = getGlobalObject<BrowserWindow>()
145+
debuggerGlobal.$dd_entry = onEntry
146+
debuggerGlobal.$dd_return = onReturn
147+
debuggerGlobal.$dd_throw = onThrow
148+
debuggerGlobal.$dd_probes = getProbes
115149

116-
startDeliveryApiPolling({
117-
service: initConfiguration.service,
118-
clientToken: initConfiguration.clientToken,
119-
site: initConfiguration.site,
120-
proxy: initConfiguration.proxy,
121-
env: initConfiguration.env,
122-
version: initConfiguration.version,
123-
pollInterval: initConfiguration.pollInterval,
150+
mockable(startDeliveryApiPolling)({
151+
service: resolvedConfiguration.service,
152+
clientToken: resolvedConfiguration.clientToken,
153+
site: resolvedConfiguration.site,
154+
proxy: resolvedConfiguration.proxy,
155+
env: resolvedConfiguration.env,
156+
version: resolvedConfiguration.version,
157+
pollInterval: resolvedConfiguration.pollInterval,
124158
})
125159
},
126160
})
@@ -135,8 +169,4 @@ function makeDebuggerPublicApi(): DatadogDebugger {
135169
*/
136170
export const datadogDebugger = makeDebuggerPublicApi()
137171

138-
export interface BrowserWindow extends Window {
139-
DD_DEBUGGER?: DatadogDebugger
140-
}
141-
142172
defineGlobal(getGlobalObject<BrowserWindow>(), 'DD_DEBUGGER', datadogDebugger)

0 commit comments

Comments
 (0)