Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
62 commits
Select commit Hold shift + click to select a range
13f348e
👷 build modules using a script
BenoitZugmeyer Oct 30, 2025
daf1130
👷 build bundles using a script
BenoitZugmeyer Oct 31, 2025
a299840
👷 adjust sandbox build
BenoitZugmeyer Oct 31, 2025
de520ca
👷 fix check-packages
BenoitZugmeyer Oct 31, 2025
2be9686
🔥 remove now unused npm-run-all dependency
BenoitZugmeyer Nov 4, 2025
3b108a1
👌 revamp build-package script
BenoitZugmeyer Nov 6, 2025
0092167
merge main
BenoitZugmeyer Nov 6, 2025
8722954
adjust build-package script to use the native globSync
BenoitZugmeyer Nov 6, 2025
02b3e38
👷 handle dev-extension to run in electron
thomas-lebeau Nov 11, 2025
540c639
Merge branch 'benoit/reduce-build-boilerplate' into thomas.lebeau/ele…
thomas-lebeau Nov 11, 2025
d81ce59
Merge branch 'benoit/reduce-build-boilerplate' into thomas.lebeau/ele…
thomas-lebeau Nov 11, 2025
bbcfe4e
🙈 [wip] Add Electron package with initial setup and integration for D…
thomas-lebeau Nov 11, 2025
a16f3a6
Extract sendRumEvent + Add ApplicationLaunch view
bcaudan Nov 12, 2025
ac4259c
add dummy exception tracking to link with view / session context
bcaudan Nov 13, 2025
e7fd56a
Track activity and update view time spent
bcaudan Nov 13, 2025
154ad4e
Use observable to send and assemble RUM events
bcaudan Nov 13, 2025
949f563
Generate RUM errors from error spans
bcaudan Nov 13, 2025
0ef6e03
♻️ rename bride.ts to renderer and move expoerted function in separat…
thomas-lebeau Nov 14, 2025
19dfc92
✨ add ipcMain and move tracer
thomas-lebeau Nov 14, 2025
86ff2f3
refactor: move setupMainBridge in it's own file
thomas-lebeau Nov 14, 2025
7134ff5
🚧 send trace
mormubis Nov 14, 2025
1678750
♻️ put back batching
mormubis Nov 14, 2025
a514452
refactor: better ipc span name
thomas-lebeau Nov 14, 2025
08d9503
attach RUM context to spans
bcaudan Nov 14, 2025
200a387
extract rum files
bcaudan Nov 14, 2025
8562721
extract trace directory
bcaudan Nov 14, 2025
dbaeeff
feat: add electron plugin for browser-rum
thomas-lebeau Nov 14, 2025
11e5c05
fix: conflicts
thomas-lebeau Nov 14, 2025
50a9e3d
fix: disable side-effect-free check for electron package
thomas-lebeau Nov 14, 2025
11ec945
fix(eslint): specify listener type in withDatadogCarrier function
thomas-lebeau Nov 14, 2025
4cc417e
fix(lint): no more deep imports
thomas-lebeau Nov 14, 2025
225dc46
Generate RUM resources from resources spans
bcaudan Nov 17, 2025
bdec71a
Increment error and resource counter
bcaudan Nov 17, 2025
9327732
fix: parse span and trace IDs as integers in ipcRenderer
thomas-lebeau Nov 17, 2025
c2605b8
chore: update TODOs in main.ts for IPC, telemetry, and testing improv…
thomas-lebeau Nov 17, 2025
f6dc907
chore: update TODOs in main.ts to include source and user agent in th…
thomas-lebeau Nov 18, 2025
3df655d
Merge branch 'main' into thomas.lebeau/electron
bcaudan Nov 18, 2025
fb5713a
fix licenses
bcaudan Nov 18, 2025
9b78148
fix peerDeps
bcaudan Nov 18, 2025
d401f7e
fix license
bcaudan Nov 18, 2025
098aeec
attach service/env/version
bcaudan Nov 19, 2025
b720a93
add required deps to package with dd-trace
bcaudan Nov 20, 2025
f32666f
attach same service,env,version to traces
bcaudan Nov 20, 2025
73d283e
allow to provide renderer variable with or without context isolation …
bcaudan Nov 20, 2025
1dbb4a0
Setup electron e2e test
bcaudan Nov 24, 2025
3ba3652
Make e2e pass on ci
bcaudan Nov 24, 2025
a70e509
Use ApplicationLaunch for node view url
bcaudan Nov 25, 2025
5ebdee8
Add support for renderer telemetry / logs
bcaudan Nov 25, 2025
6edf098
crash report first iteration
mormubis Nov 24, 2025
6ab2013
clean
mormubis Nov 25, 2025
ca0608f
add global context APIs
bcaudan Nov 25, 2025
5d554cc
setup node telemetry
bcaudan Nov 26, 2025
cc8cbff
Capture uncaught exception and unhandled rejection
bcaudan Nov 26, 2025
2b26db9
pass env info through userAgent
bcaudan Nov 26, 2025
6545880
rework entries
bcaudan Nov 26, 2025
5490759
✨ session manager & crash report
mormubis Nov 28, 2025
f147ce9
Switch file accesses to async, remove some logs
bcaudan Dec 1, 2025
ad456f6
look for dump in every directory (cross os compatibility)
bcaudan Dec 1, 2025
ede97fd
workaround for windows startup issue
bcaudan Dec 1, 2025
11bcc81
add comment on setTimeout
bcaudan Dec 2, 2025
9bb1705
fix build on windows
bcaudan Dec 2, 2025
6c83862
increase timeout and wait for first renderer event
bcaudan Dec 2, 2025
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
7 changes: 5 additions & 2 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,9 @@ package.tgz
docs/
*.tsbuildinfo

# Generated WASM base64 files
**/*.wasm.base64.ts

# https://yarnpkg.com/getting-started/qa#which-files-should-be-gitignored
.pnp.*
.yarn/*
Expand All @@ -20,8 +23,8 @@ docs/
!.yarn/releases
!.yarn/sdks
!.yarn/versions
/test-results/
/playwright-report/
**/test-results/
**/playwright-report/
/blob-report/
/playwright/.cache/
.vscode
2 changes: 1 addition & 1 deletion .gitlab-ci.yml
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
variables:
CURRENT_STAGING: staging-47
APP: 'browser-sdk'
CURRENT_CI_IMAGE: 93
CURRENT_CI_IMAGE: 94
BUILD_STABLE_REGISTRY: 'registry.ddbuild.io'
CI_IMAGE: '$BUILD_STABLE_REGISTRY/ci/$APP:$CURRENT_CI_IMAGE'
GIT_REPOSITORY: 'git@github.com:DataDog/browser-sdk.git'
Expand Down
2 changes: 2 additions & 0 deletions Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,8 @@ RUN apt-get -y install git

RUN apt-get -y install procps

RUN apt-get -y install xvfb

# Woke
RUN set -o pipefail \
&& curl -sSfL https://git.io/getwoke | bash -s -- -b /usr/local/bin v0.17.1
Expand Down
5 changes: 5 additions & 0 deletions LICENSE-3rdparty.csv
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,10 @@ prod,@tabler/icons-react,MIT,Copyright (c) 2020-2023 Paweł Kuna
prod,clsx,MIT,Copyright (c) Luke Edwards <luke.edwards05@gmail.com> (lukeed.com)
prod,react,MIT,Copyright (c) Facebook, Inc. and its affiliates.
prod,react-dom,MIT,Copyright (c) Facebook, Inc. and its affiliates.
prod,@msgpack/msgpack,Apache-2.0,Copyright (c) 2010 Peter Griess
prod,dd-trace,Apache-2.0,Copyright (c) 2018, Datadog Inc.
prod,graphql,MIT,Copyright (c) GraphQL Contributors
prod,@openfeature/core,Apache-2.0,Copyright OpenFeature Maintainers
dev,typedoc,Apache-2.0,TypeStrong
dev,@eslint/js,MIT,Copyright OpenJS Foundation and other contributors, <www.openjsf.org>
dev,@jsdevtools/coverage-istanbul-loader,MIT,Copyright (c) 2015 James Messinger
Expand Down Expand Up @@ -84,3 +88,4 @@ dev,react-refresh-typescript,MIT,Copyright (c) Piotr Monwid-Olechnowicz
dev,webpack-dev-server,MIT,Copyright JS Foundation and other contributors
dev,http-server,MIT,Copyright http-party contributors
dev,react-router,MIT,Copyright (c) React Training LLC 2015-2019 Copyright (c) Remix Software Inc. 2020-2021 Copyright (c) Shopify Inc. 2022-2023
dev,electron,MIT,Copyright (c) Electron contributors Copyright (c) 2013-2020 GitHub Inc.
4 changes: 2 additions & 2 deletions developer-extension/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,8 @@
"version": "6.24.0",
"private": true,
"scripts": {
"build": "rm -rf dist && webpack --disable-interpret --mode production",
"dev": "rm -rf dist && webpack --disable-interpret --mode development && webpack serve --disable-interpret --mode development --config-name=panel --hot"
"build": "webpack --disable-interpret --mode production",
"dev": "webpack --disable-interpret --mode development && webpack serve --disable-interpret --mode development --config-name=panel --hot"
},
"devDependencies": {
"@pmmmwh/react-refresh-webpack-plugin": "^0.6.0",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ export function useEvents({
}, [eventCollectionStrategy])

useEffect(() => {
if (!preserveEvents) {
if (!preserveEvents && 'webNavigation' in chrome) {
const clearCurrentEvents = (details: chrome.webNavigation.WebNavigationTransitionCallbackDetails) => {
if (details.transitionType === 'reload' && details.tabId === chrome.devtools.inspectedWindow.tabId) {
clearEvents()
Expand Down
1 change: 1 addition & 0 deletions developer-extension/webpack.config.mts
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ export default (_env: unknown, argv: { mode?: webpack.Configuration['mode'] }) =
entry: './src/background',
output: {
filename: 'background.js',
clean: true,
},
plugins: [
...(isDevelopment
Expand Down
1 change: 1 addition & 0 deletions eslint-local-rules/disallowSideEffects.js
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ const packagesWithoutSideEffect = new Set([
'@datadog/browser-rum-core',
'react',
'react-router-dom',
'electron',
])

/**
Expand Down
2 changes: 1 addition & 1 deletion eslint-local-rules/enforceProdDepsImports.js
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ export default {
return moduleVisitor((source) => {
const importTypeResult = importType(source.value, context)
// Use an allow list instead of a deny list to make the rule more future-proof.
if (importTypeResult === 'parent' || importTypeResult === 'sibling') {
if (importTypeResult === 'parent' || importTypeResult === 'sibling' || importTypeResult === 'builtin') {
return
}

Expand Down
12 changes: 12 additions & 0 deletions eslint.config.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -418,6 +418,18 @@ export default tseslint.config(
},
},

{
files: ['packages/electron/src/**/*.ts'],
ignores: [SPEC_FILES],
rules: {
// TODO: verify this is safe
'local-rules/disallow-side-effects': 'off',

// TODO: remove before merging this to main: quick dev only
'no-console': 'off',
},
},

{
// Files executed by nodejs
files: [
Expand Down
3 changes: 2 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
"scripts": {
"postinstall": "scripts/cli init_submodule",
"build": "yarn workspaces foreach --all --parallel --topological run build",
"build:electron": "lerna run build:modules --scope=@datadog/electron",
"build:bundle": "yarn workspaces foreach --all --parallel run build:bundle",
"build:apps": "node scripts/build/build-test-apps.ts",
"build:docs:json": "typedoc --logLevel Verbose --json ./docs.json",
Expand All @@ -32,7 +33,7 @@
"test:e2e:init": "yarn build && yarn build:apps && yarn playwright install chromium --with-deps",
"test:e2e": "playwright test --config test/e2e/playwright.local.config.ts --project chromium",
"test:e2e:bs": "node ./scripts/test/bs-wrapper.ts playwright test --config test/e2e/playwright.bs.config.ts",
"test:e2e:ci": "yarn test:e2e:init && yarn test:e2e",
"test:e2e:ci": "yarn test:e2e:init && xvfb-run -a yarn test:e2e",
"test:e2e:ci:bs": "yarn build && yarn build:apps && yarn test:e2e:bs",
"test:compat:tsc": "node scripts/check-typescript-compatibility.ts",
"test:compat:ssr": "scripts/cli check_server_side_rendering_compatibility",
Expand Down
1 change: 1 addition & 0 deletions packages/core/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
"types": "cjs/index.d.ts",
"sideEffects": false,
"scripts": {
"pack": "yarn pack",
"build": "node ../../scripts/build/build-package.ts --modules"
},
"repository": {
Expand Down
3 changes: 2 additions & 1 deletion packages/core/src/browser/cookie.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import {
generateUUID,
} from '../tools/utils/stringUtils'
import { buildUrl } from '../tools/utils/urlPolyfill'
import { getGlobalObject } from '../tools/globalObject'

export interface CookieOptions {
secure?: boolean
Expand Down Expand Up @@ -65,7 +66,7 @@ export function deleteCookie(name: string, options?: CookieOptions) {
}

export function areCookiesAuthorized(options: CookieOptions): boolean {
if (document.cookie === undefined || document.cookie === null) {
if (getGlobalObject<Window>().document?.cookie === undefined || getGlobalObject<Window>().document?.cookie === null) {
return false
}
try {
Expand Down
1 change: 1 addition & 0 deletions packages/core/src/domain/resourceUtils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ export const ResourceType = {
FONT: 'font',
MEDIA: 'media',
OTHER: 'other',
NATIVE: 'native',
} as const

export type ResourceType = (typeof ResourceType)[keyof typeof ResourceType]
Expand Down
1 change: 1 addition & 0 deletions packages/core/src/domain/telemetry/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ export {
addTelemetryError,
resetTelemetry,
startTelemetry,
startTelemetryCollection,
addTelemetryConfiguration,
addTelemetryUsage,
addTelemetryMetrics,
Expand Down
1 change: 1 addition & 0 deletions packages/core/src/domain/telemetry/telemetry.ts
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,7 @@ const ALLOWED_FRAME_URLS = [
export const enum TelemetryService {
LOGS = 'browser-logs-sdk',
RUM = 'browser-rum-sdk',
ELECTRON = 'electron-sdk',
}

export interface Telemetry {
Expand Down
18 changes: 16 additions & 2 deletions packages/core/src/index.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
export type { Configuration, InitConfiguration, EndpointBuilder, ProxyFn } from './domain/configuration'
export type { Configuration, InitConfiguration, EndpointBuilder, ProxyFn, TrackType } from './domain/configuration'
export {
validateAndBuildConfiguration,
DefaultPrivacyLevel,
Expand All @@ -7,6 +7,7 @@ export {
isSampleRate,
buildEndpointHost,
isIntakeUrl,
createEndpointBuilder,
} from './domain/configuration'
export * from './domain/intakeSites'
export type { TrackingConsentState } from './domain/trackingConsent'
Expand Down Expand Up @@ -40,6 +41,7 @@ export type {
} from './domain/telemetry'
export {
startTelemetry,
startTelemetryCollection,
addTelemetryDebug,
addTelemetryError,
resetTelemetry,
Expand All @@ -55,13 +57,25 @@ export { Observable, BufferedObservable } from './tools/observable'
export type { SessionManager } from './domain/session/sessionManager'
export { startSessionManager, stopSessionManager } from './domain/session/sessionManager'
export {
SESSION_EXPIRATION_DELAY,
SESSION_TIME_OUT_DELAY, // Exposed for tests
SESSION_NOT_TRACKED,
SessionPersistence,
} from './domain/session/sessionConstants'
export type { BandwidthStats, HttpRequest, HttpRequestEvent, Payload, FlushEvent, FlushReason } from './transport'
export type {
BandwidthStats,
HttpRequest,
HttpRequestEvent,
Payload,
FlushEvent,
FlushReason,
DatadogEventBridge,
BrowserWindowWithEventBridge,
Batch,
} from './transport'
export {
createHttpRequest,
RECOMMENDED_REQUEST_BYTES_LIMIT,
canUseEventBridge,
getEventBridge,
bridgeSupports,
Expand Down
6 changes: 4 additions & 2 deletions packages/core/src/tools/utils/byteUtils.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
import { globalObject } from '../globalObject'

export const ONE_KIBI_BYTE = 1024
export const ONE_MEBI_BYTE = 1024 * ONE_KIBI_BYTE

Expand All @@ -16,8 +18,8 @@ export function computeBytesCount(candidate: string): number {
return candidate.length
}

if (window.TextEncoder !== undefined) {
return new TextEncoder().encode(candidate).length
if ('TextEncoder' in globalObject) {
return new (globalObject as { TextEncoder: typeof TextEncoder }).TextEncoder().encode(candidate).length
}

return new Blob([candidate]).size
Expand Down
3 changes: 2 additions & 1 deletion packages/core/src/tools/utils/urlPolyfill.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
import { globalObject } from '../globalObject'

export function normalizeUrl(url: string) {
return buildUrl(url, location.href).href
const base = typeof location !== 'undefined' ? location.href : undefined
return buildUrl(url, base).href
}

export function isValidUrl(url: string) {
Expand Down
38 changes: 27 additions & 11 deletions packages/core/src/transport/httpRequest.ts
Original file line number Diff line number Diff line change
Expand Up @@ -73,7 +73,8 @@ export interface RetryInfo {
export function createHttpRequest<Body extends Payload = Payload>(
endpointBuilders: EndpointBuilder[],
reportError: (error: RawError) => void,
bytesLimit: number = RECOMMENDED_REQUEST_BYTES_LIMIT
bytesLimit: number = RECOMMENDED_REQUEST_BYTES_LIMIT,
userAgent?: string
): HttpRequest<Body> {
const observable = new Observable<HttpRequestEvent<Body>>()
const retryState = newRetryState<Body>()
Expand All @@ -87,9 +88,9 @@ export function createHttpRequest<Body extends Payload = Payload>(
retryState,
(payload, onResponse) => {
if (isExperimentalFeatureEnabled(ExperimentalFeature.AVOID_FETCH_KEEPALIVE)) {
fetchStrategy(endpointBuilder, payload, onResponse)
fetchStrategy(endpointBuilder, payload, onResponse, userAgent)
} else {
fetchKeepAliveStrategy(endpointBuilder, bytesLimit, payload, onResponse)
fetchKeepAliveStrategy(endpointBuilder, bytesLimit, payload, onResponse, userAgent)
}
},
endpointBuilder.trackType,
Expand All @@ -104,13 +105,18 @@ export function createHttpRequest<Body extends Payload = Payload>(
*/
sendOnExit: (payload: Body) => {
for (const endpointBuilder of endpointBuilders) {
sendBeaconStrategy(endpointBuilder, bytesLimit, payload)
sendBeaconStrategy(endpointBuilder, bytesLimit, payload, userAgent)
}
},
}
}

function sendBeaconStrategy(endpointBuilder: EndpointBuilder, bytesLimit: number, payload: Payload) {
function sendBeaconStrategy(
endpointBuilder: EndpointBuilder,
bytesLimit: number,
payload: Payload,
userAgent?: string
) {
const canUseBeacon = !!navigator.sendBeacon && payload.bytesCount < bytesLimit
if (canUseBeacon) {
try {
Expand All @@ -125,7 +131,7 @@ function sendBeaconStrategy(endpointBuilder: EndpointBuilder, bytesLimit: number
}
}

fetchStrategy(endpointBuilder, payload)
fetchStrategy(endpointBuilder, payload, undefined, userAgent)
}

let hasReportedBeaconError = false
Expand All @@ -141,29 +147,39 @@ export function fetchKeepAliveStrategy(
endpointBuilder: EndpointBuilder,
bytesLimit: number,
payload: Payload,
onResponse?: (r: HttpResponse) => void
onResponse?: (r: HttpResponse) => void,
userAgent?: string
) {
const canUseKeepAlive = isKeepAliveSupported() && payload.bytesCount < bytesLimit

if (canUseKeepAlive) {
const fetchUrl = endpointBuilder.build('fetch-keepalive', payload)

fetch(fetchUrl, { method: 'POST', body: payload.data, keepalive: true, mode: 'cors' })
const config: RequestInit = { method: 'POST', body: payload.data, keepalive: true, mode: 'cors' }
if (userAgent) {
config.headers = { 'User-Agent': userAgent }
}
fetch(fetchUrl, config)
.then(monitor((response: Response) => onResponse?.({ status: response.status, type: response.type })))
.catch(monitor(() => fetchStrategy(endpointBuilder, payload, onResponse)))
} else {
fetchStrategy(endpointBuilder, payload, onResponse)
fetchStrategy(endpointBuilder, payload, onResponse, userAgent)
}
}

export function fetchStrategy(
endpointBuilder: EndpointBuilder,
payload: Payload,
onResponse?: (r: HttpResponse) => void
onResponse?: (r: HttpResponse) => void,
userAgent?: string
) {
const fetchUrl = endpointBuilder.build('fetch', payload)

fetch(fetchUrl, { method: 'POST', body: payload.data, mode: 'cors' })
const config: RequestInit = { method: 'POST', body: payload.data, mode: 'cors' }
if (userAgent) {
config.headers = { 'User-Agent': userAgent }
}
fetch(fetchUrl, config)
.then(monitor((response: Response) => onResponse?.({ status: response.status, type: response.type })))
.catch(monitor(() => onResponse?.({ status: 0 })))
}
Expand Down
3 changes: 2 additions & 1 deletion packages/core/src/transport/index.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
export type { BandwidthStats, HttpRequest, HttpRequestEvent, Payload, RetryInfo } from './httpRequest'
export { createHttpRequest } from './httpRequest'
export { createHttpRequest, RECOMMENDED_REQUEST_BYTES_LIMIT } from './httpRequest'
export type { BrowserWindowWithEventBridge, DatadogEventBridge } from './eventBridge'
export { canUseEventBridge, bridgeSupports, getEventBridge, BridgeCapability } from './eventBridge'
export type { Batch } from './batch'
export { createBatch } from './batch'
export type { FlushController, FlushEvent, FlushReason } from './flushController'
export { createFlushController, FLUSH_DURATION_LIMIT } from './flushController'
9 changes: 9 additions & 0 deletions packages/electron/.npmignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
*
!/bundle/**/*.js
!/cjs/**/*
!/esm/**/*
!/src/**/*
/src/**/*.spec.ts
/src/**/*.specHelper.ts
!/internal/*
!/internal-synthetics/*
2 changes: 2 additions & 0 deletions packages/electron/.yarnrc
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
save-exact true

6 changes: 6 additions & 0 deletions packages/electron/main/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
{
"private": true,
"main": "../cjs/entries/main.js",
"module": "../esm/entries/main.js",
"types": "../cjs/entries/main.d.ts"
}
Loading