diff --git a/LICENSE-3rdparty.csv b/LICENSE-3rdparty.csv index 9b93d133db..890275288a 100644 --- a/LICENSE-3rdparty.csv +++ b/LICENSE-3rdparty.csv @@ -66,7 +66,6 @@ dev,prettier,MIT,Copyright James Long and contributors dev,puppeteer,Apache-2.0,Copyright 2017 Google Inc. dev,replace-in-file,MIT,Copyright 2015-2019 Adam Reis dev,sinon,BSD-3-Clause,Copyright 2010-2017 Christian Johansen -dev,string-replace-loader,MIT,Copyright 2015 Valentyn Barmashyn dev,terser-webpack-plugin,MIT,Copyright JS Foundation and other contributors dev,ts-loader,MIT,Copyright 2015 TypeStrong dev,ts-node,MIT,Copyright 2014 Blake Embrey diff --git a/package.json b/package.json index 4d367f3499..6eb398cb13 100644 --- a/package.json +++ b/package.json @@ -80,7 +80,6 @@ "prettier": "2.5.1", "replace-in-file": "6.3.2", "sinon": "9.2.4", - "string-replace-loader": "3.1.0", "terser-webpack-plugin": "5.1.1", "ts-loader": "8.0.18", "ts-node": "9.1.1", diff --git a/packages/core/package.json b/packages/core/package.json index 4125ec2169..96c2113cfd 100644 --- a/packages/core/package.json +++ b/packages/core/package.json @@ -8,8 +8,9 @@ "sideEffects": false, "scripts": { "build": "run-p build:cjs build:esm", - "build:cjs": "rm -rf cjs && tsc -p tsconfig.cjs.json", - "build:esm": "rm -rf esm && tsc -p tsconfig.esm.json" + "build:cjs": "rm -rf cjs && tsc -p tsconfig.cjs.json && yarn replace-build-env cjs", + "build:esm": "rm -rf esm && tsc -p tsconfig.esm.json && yarn replace-build-env esm", + "replace-build-env": "node ../../scripts/replace-build-env.js" }, "dependencies": { "tslib": "^1.10.0" diff --git a/packages/core/src/boot/init.ts b/packages/core/src/boot/init.ts index e2c3a637a0..9345cc8dfd 100644 --- a/packages/core/src/boot/init.ts +++ b/packages/core/src/boot/init.ts @@ -1,14 +1,14 @@ import { setDebugMode } from '../domain/internalMonitoring' import { catchUserErrors } from '../tools/catchUserErrors' -export function makePublicApi( - buildEnv: BuildEnv, - stub: T -): T & { onReady(callback: () => void): void; version: string } { +// replaced at build time +declare const __BUILD_ENV__SDK_VERSION__: string + +export function makePublicApi(stub: T): T & { onReady(callback: () => void): void; version: string } { const publicApi = { ...stub, - version: buildEnv.sdkVersion, + version: __BUILD_ENV__SDK_VERSION__, // This API method is intentionally not monitored, since the only thing executed is the // user-provided 'callback'. All SDK usages executed in the callback should be monitored, and @@ -37,14 +37,3 @@ export function defineGlobal(global: Global, existingGlobalVariable.q.forEach((fn) => catchUserErrors(fn, 'onReady callback threw an error:')()) } } - -export enum BuildMode { - RELEASE = 'release', - CANARY = 'canary', - E2E_TEST = 'e2e-test', -} - -export interface BuildEnv { - buildMode: BuildMode - sdkVersion: string -} diff --git a/packages/core/src/domain/configuration/configuration.spec.ts b/packages/core/src/domain/configuration/configuration.spec.ts index fe156208a4..e9f57c201e 100644 --- a/packages/core/src/domain/configuration/configuration.spec.ts +++ b/packages/core/src/domain/configuration/configuration.spec.ts @@ -1,6 +1,4 @@ import type { RumEvent } from '../../../../rum-core/src' -import type { BuildEnv } from '../../boot/init' -import { BuildMode } from '../../boot/init' import { display } from '../../tools/display' import type { InitConfiguration } from './configuration' import { validateAndBuildConfiguration } from './configuration' @@ -8,17 +6,13 @@ import { isExperimentalFeatureEnabled, updateExperimentalFeatures } from './expe describe('validateAndBuildConfiguration', () => { const clientToken = 'some_client_token' - const buildEnv: BuildEnv = { - buildMode: BuildMode.RELEASE, - sdkVersion: 'some_version', - } beforeEach(() => { updateExperimentalFeatures([]) }) it('updates experimental feature flags', () => { - validateAndBuildConfiguration({ clientToken, enableExperimentalFeatures: ['foo'] }, buildEnv) + validateAndBuildConfiguration({ clientToken, enableExperimentalFeatures: ['foo'] }) expect(isExperimentalFeatureEnabled('foo')).toBeTrue() }) @@ -30,62 +24,59 @@ describe('validateAndBuildConfiguration', () => { }) it('requires the InitConfiguration to be defined', () => { - expect(validateAndBuildConfiguration(undefined as unknown as InitConfiguration, buildEnv)).toBeUndefined() + expect(validateAndBuildConfiguration(undefined as unknown as InitConfiguration)).toBeUndefined() expect(displaySpy).toHaveBeenCalledOnceWith('Client Token is not configured, we will not send any data.') }) it('requires clientToken to be defined', () => { - expect(validateAndBuildConfiguration({} as unknown as InitConfiguration, buildEnv)).toBeUndefined() + expect(validateAndBuildConfiguration({} as unknown as InitConfiguration)).toBeUndefined() expect(displaySpy).toHaveBeenCalledOnceWith('Client Token is not configured, we will not send any data.') }) it('requires sampleRate to be a percentage', () => { expect( - validateAndBuildConfiguration({ clientToken, sampleRate: 'foo' } as unknown as InitConfiguration, buildEnv) + validateAndBuildConfiguration({ clientToken, sampleRate: 'foo' } as unknown as InitConfiguration) ).toBeUndefined() expect(displaySpy).toHaveBeenCalledOnceWith('Sample Rate should be a number between 0 and 100') displaySpy.calls.reset() expect( - validateAndBuildConfiguration({ clientToken, sampleRate: 200 } as unknown as InitConfiguration, buildEnv) + validateAndBuildConfiguration({ clientToken, sampleRate: 200 } as unknown as InitConfiguration) ).toBeUndefined() expect(displaySpy).toHaveBeenCalledOnceWith('Sample Rate should be a number between 0 and 100') }) it("shouldn't display any error if the configuration is correct", () => { - validateAndBuildConfiguration({ clientToken: 'yes', sampleRate: 1 }, buildEnv) + validateAndBuildConfiguration({ clientToken: 'yes', sampleRate: 1 }) expect(displaySpy).not.toHaveBeenCalled() }) }) describe('cookie options', () => { it('should not be secure nor crossSite by default', () => { - const configuration = validateAndBuildConfiguration({ clientToken }, buildEnv)! + const configuration = validateAndBuildConfiguration({ clientToken })! expect(configuration.cookieOptions).toEqual({ secure: false, crossSite: false }) }) it('should be secure when `useSecureSessionCookie` is truthy', () => { - const configuration = validateAndBuildConfiguration({ clientToken, useSecureSessionCookie: true }, buildEnv)! + const configuration = validateAndBuildConfiguration({ clientToken, useSecureSessionCookie: true })! expect(configuration.cookieOptions).toEqual({ secure: true, crossSite: false }) }) it('should be secure and crossSite when `useCrossSiteSessionCookie` is truthy', () => { - const configuration = validateAndBuildConfiguration({ clientToken, useCrossSiteSessionCookie: true }, buildEnv)! + const configuration = validateAndBuildConfiguration({ clientToken, useCrossSiteSessionCookie: true })! expect(configuration.cookieOptions).toEqual({ secure: true, crossSite: true }) }) it('should have domain when `trackSessionAcrossSubdomains` is truthy', () => { - const configuration = validateAndBuildConfiguration( - { clientToken, trackSessionAcrossSubdomains: true }, - buildEnv - )! + const configuration = validateAndBuildConfiguration({ clientToken, trackSessionAcrossSubdomains: true })! expect(configuration.cookieOptions).toEqual({ secure: false, crossSite: false, domain: jasmine.any(String) }) }) }) describe('beforeSend', () => { it('should be undefined when beforeSend is missing on user configuration', () => { - const configuration = validateAndBuildConfiguration({ clientToken }, buildEnv)! + const configuration = validateAndBuildConfiguration({ clientToken })! expect(configuration.beforeSend).toBeUndefined() }) @@ -95,7 +86,7 @@ describe('validateAndBuildConfiguration', () => { return false } } - const configuration = validateAndBuildConfiguration({ clientToken, beforeSend }, buildEnv)! + const configuration = validateAndBuildConfiguration({ clientToken, beforeSend })! expect(configuration.beforeSend!({ view: { url: '/foo' } }, {})).toBeFalse() expect(configuration.beforeSend!({ view: { url: '/bar' } }, {})).toBeUndefined() }) @@ -105,7 +96,7 @@ describe('validateAndBuildConfiguration', () => { const beforeSend = () => { throw myError } - const configuration = validateAndBuildConfiguration({ clientToken, beforeSend }, buildEnv)! + const configuration = validateAndBuildConfiguration({ clientToken, beforeSend })! const displaySpy = spyOn(display, 'error') expect(configuration.beforeSend!(null, {})).toBeUndefined() expect(displaySpy).toHaveBeenCalledWith('beforeSend threw an error:', myError) diff --git a/packages/core/src/domain/configuration/configuration.ts b/packages/core/src/domain/configuration/configuration.ts index f9fdcd281a..365c1d5c38 100644 --- a/packages/core/src/domain/configuration/configuration.ts +++ b/packages/core/src/domain/configuration/configuration.ts @@ -1,4 +1,3 @@ -import type { BuildEnv } from '../../boot/init' import type { CookieOptions } from '../../browser/cookie' import { getCurrentSite } from '../../browser/cookie' import { catchUserErrors } from '../../tools/catchUserErrors' @@ -71,10 +70,7 @@ export interface Configuration extends TransportConfiguration { maxMessageSize: number } -export function validateAndBuildConfiguration( - initConfiguration: InitConfiguration, - buildEnv: BuildEnv -): Configuration | undefined { +export function validateAndBuildConfiguration(initConfiguration: InitConfiguration): Configuration | undefined { if (!initConfiguration || !initConfiguration.clientToken) { display.error('Client Token is not configured, we will not send any data.') return @@ -85,11 +81,11 @@ export function validateAndBuildConfiguration( return } - // Set the experimental feature flags as early as possible so we can use them in most places + // Set the experimental feature flags as early as possible, so we can use them in most places updateExperimentalFeatures(initConfiguration.enableExperimentalFeatures) return { - ...computeTransportConfiguration(initConfiguration, buildEnv), + ...computeTransportConfiguration(initConfiguration), beforeSend: initConfiguration.beforeSend && catchUserErrors(initConfiguration.beforeSend, 'beforeSend threw an error:'), diff --git a/packages/core/src/domain/configuration/endpointBuilder.ts b/packages/core/src/domain/configuration/endpointBuilder.ts index f768f99fef..c5ff332c44 100644 --- a/packages/core/src/domain/configuration/endpointBuilder.ts +++ b/packages/core/src/domain/configuration/endpointBuilder.ts @@ -1,9 +1,11 @@ -import type { BuildEnv } from '../../boot/init' import { timeStampNow } from '../../tools/timeUtils' import { normalizeUrl } from '../../tools/urlPolyfill' import { generateUUID } from '../../tools/utils' import type { InitConfiguration } from './configuration' +// replaced at build time +declare const __BUILD_ENV__SDK_VERSION__: string + export const ENDPOINTS = { logs: 'logs', rum: 'rum', @@ -24,7 +26,6 @@ export type EndpointBuilder = ReturnType export function createEndpointBuilder( initConfiguration: InitConfiguration, - buildEnv: BuildEnv, endpointType: EndpointType, tags: string[], source?: string @@ -41,9 +42,9 @@ export function createEndpointBuilder( build() { let parameters = `ddsource=${source || 'browser'}` + - `&ddtags=${encodeURIComponent([`sdk_version:${buildEnv.sdkVersion}`].concat(tags).join(','))}` + + `&ddtags=${encodeURIComponent([`sdk_version:${__BUILD_ENV__SDK_VERSION__}`].concat(tags).join(','))}` + `&dd-api-key=${clientToken}` + - `&dd-evp-origin-version=${encodeURIComponent(buildEnv.sdkVersion)}` + + `&dd-evp-origin-version=${encodeURIComponent(__BUILD_ENV__SDK_VERSION__)}` + `&dd-evp-origin=browser` + `&dd-request-id=${generateUUID()}` diff --git a/packages/core/src/domain/configuration/transportConfiguration.spec.ts b/packages/core/src/domain/configuration/transportConfiguration.spec.ts index c19079375f..8c43d9c0e9 100644 --- a/packages/core/src/domain/configuration/transportConfiguration.spec.ts +++ b/packages/core/src/domain/configuration/transportConfiguration.spec.ts @@ -1,35 +1,29 @@ -import type { BuildEnv } from '../../boot/init' -import { BuildMode } from '../../boot/init' +import type { BuildEnvWindow } from '../../../test/specHelper' import { computeTransportConfiguration } from './transportConfiguration' describe('transportConfiguration', () => { const clientToken = 'some_client_token' const otherClientToken = 'some_other_client_token' - const buildEnv: BuildEnv = { - buildMode: BuildMode.RELEASE, - sdkVersion: 'some_version', - } + + beforeEach(() => { + ;(window as unknown as BuildEnvWindow).__BUILD_ENV__SDK_VERSION__ = 'some_version' + }) describe('internal monitoring endpoint', () => { it('should only be defined when api key is provided', () => { - let configuration = computeTransportConfiguration({ clientToken }, buildEnv) + let configuration = computeTransportConfiguration({ clientToken }) expect(configuration.internalMonitoringEndpointBuilder).toBeUndefined() - configuration = computeTransportConfiguration( - { clientToken, internalMonitoringApiKey: otherClientToken }, - buildEnv - ) + configuration = computeTransportConfiguration({ clientToken, internalMonitoringApiKey: otherClientToken }) expect(configuration.internalMonitoringEndpointBuilder?.build()).toContain(otherClientToken) }) }) describe('endpoint overload', () => { it('should be available for e2e-test build mode', () => { - const e2eEnv = { - buildMode: BuildMode.E2E_TEST, - sdkVersion: 'some_version', - } - const configuration = computeTransportConfiguration({ clientToken }, e2eEnv) + ;(window as unknown as BuildEnvWindow).__BUILD_ENV__BUILD_MODE__ = 'e2e-test' + + const configuration = computeTransportConfiguration({ clientToken }) expect(configuration.rumEndpointBuilder.build()).toEqual('<<< E2E RUM ENDPOINT >>>') expect(configuration.logsEndpointBuilder.build()).toEqual('<<< E2E LOGS ENDPOINT >>>') expect(configuration.internalMonitoringEndpointBuilder?.build()).toEqual( @@ -46,31 +40,31 @@ describe('transportConfiguration', () => { describe('site', () => { it('should use US site by default', () => { - const configuration = computeTransportConfiguration({ clientToken }, buildEnv) + const configuration = computeTransportConfiguration({ clientToken }) expect(configuration.rumEndpointBuilder.build()).toContain('datadoghq.com') }) it('should use site value when set', () => { - const configuration = computeTransportConfiguration({ clientToken, site: 'foo.com' }, buildEnv) + const configuration = computeTransportConfiguration({ clientToken, site: 'foo.com' }) expect(configuration.rumEndpointBuilder.build()).toContain('foo.com') }) }) describe('query parameters', () => { it('should add intake query parameters', () => { - const configuration = computeTransportConfiguration({ clientToken }, buildEnv) + const configuration = computeTransportConfiguration({ clientToken }) expect(configuration.rumEndpointBuilder.build()).toMatch( `&dd-api-key=${clientToken}&dd-evp-origin-version=(.*)&dd-evp-origin=browser&dd-request-id=(.*)` ) }) it('should add batch_time for rum endpoint', () => { - const configuration = computeTransportConfiguration({ clientToken }, buildEnv) + const configuration = computeTransportConfiguration({ clientToken }) expect(configuration.rumEndpointBuilder.build()).toContain(`&batch_time=`) }) it('should not add batch_time for logs and replay endpoints', () => { - const configuration = computeTransportConfiguration({ clientToken }, buildEnv) + const configuration = computeTransportConfiguration({ clientToken }) expect(configuration.logsEndpointBuilder.build()).not.toContain(`&batch_time=`) expect(configuration.sessionReplayEndpointBuilder.build()).not.toContain(`&batch_time=`) }) @@ -78,7 +72,7 @@ describe('transportConfiguration', () => { describe('proxyUrl', () => { it('should replace the full intake endpoint by the proxyUrl and set it in the attribute ddforward', () => { - const configuration = computeTransportConfiguration({ clientToken, proxyUrl: 'https://proxy.io/path' }, buildEnv) + const configuration = computeTransportConfiguration({ clientToken, proxyUrl: 'https://proxy.io/path' }) expect(configuration.rumEndpointBuilder.build()).toMatch( `https://proxy.io/path\\?ddforward=${encodeURIComponent( `https://rum.browser-intake-datadoghq.com/api/v2/rum?ddsource=(.*)&ddtags=(.*)&dd-api-key=${clientToken}` + @@ -90,10 +84,8 @@ describe('transportConfiguration', () => { describe('sdk_version, env, version and service', () => { it('should not modify the logs and rum endpoints tags when not defined', () => { - const configuration = computeTransportConfiguration({ clientToken }, buildEnv) - expect(decodeURIComponent(configuration.rumEndpointBuilder.build())).toContain( - `&ddtags=sdk_version:${buildEnv.sdkVersion}` - ) + const configuration = computeTransportConfiguration({ clientToken }) + expect(decodeURIComponent(configuration.rumEndpointBuilder.build())).toContain('&ddtags=sdk_version:some_version') expect(decodeURIComponent(configuration.rumEndpointBuilder.build())).not.toContain(',env:') expect(decodeURIComponent(configuration.rumEndpointBuilder.build())).not.toContain(',service:') @@ -107,25 +99,23 @@ describe('transportConfiguration', () => { }) it('should be set as tags in the logs and rum endpoints', () => { - const configuration = computeTransportConfiguration( - { clientToken, env: 'foo', service: 'bar', version: 'baz' }, - buildEnv - ) + const configuration = computeTransportConfiguration({ clientToken, env: 'foo', service: 'bar', version: 'baz' }) expect(decodeURIComponent(configuration.rumEndpointBuilder.build())).toContain( - `&ddtags=sdk_version:${buildEnv.sdkVersion},env:foo,service:bar,version:baz` + `&ddtags=sdk_version:some_version,env:foo,service:bar,version:baz` ) expect(decodeURIComponent(configuration.logsEndpointBuilder.build())).toContain( - `&ddtags=sdk_version:${buildEnv.sdkVersion},env:foo,service:bar,version:baz` + `&ddtags=sdk_version:some_version,env:foo,service:bar,version:baz` ) }) }) describe('tags', () => { it('should be encoded', () => { - const configuration = computeTransportConfiguration( - { clientToken, service: 'bar:foo', datacenter: 'us1.prod.dog' }, - buildEnv - ) + const configuration = computeTransportConfiguration({ + clientToken, + service: 'bar:foo', + datacenter: 'us1.prod.dog', + }) expect(configuration.rumEndpointBuilder.build()).toContain( `ddtags=sdk_version%3Asome_version%2Cservice%3Abar%3Afoo%2Cdatacenter%3Aus1.prod.dog` ) @@ -141,7 +131,7 @@ describe('transportConfiguration', () => { { site: 'ddog-gov.com', intakeDomain: 'browser-intake-ddog-gov.com' }, ].forEach(({ site, intakeDomain }) => { it(`should detect intake request for ${site} site`, () => { - const configuration = computeTransportConfiguration({ clientToken, site }, buildEnv) + const configuration = computeTransportConfiguration({ clientToken, site }) expect(configuration.isIntakeUrl(`https://rum.${intakeDomain}/api/v2/rum?xxx`)).toBe(true) expect(configuration.isIntakeUrl(`https://logs.${intakeDomain}/api/v2/logs?xxx`)).toBe(true) expect(configuration.isIntakeUrl(`https://session-replay.${intakeDomain}/api/v2/replay?xxx`)).toBe(true) @@ -149,12 +139,12 @@ describe('transportConfiguration', () => { }) it('should not detect non intake request', () => { - const configuration = computeTransportConfiguration({ clientToken }, buildEnv) + const configuration = computeTransportConfiguration({ clientToken }) expect(configuration.isIntakeUrl('https://www.foo.com')).toBe(false) }) it('should handle sites with subdomains', () => { - const configuration = computeTransportConfiguration({ clientToken, site: 'foo.datadoghq.com' }, buildEnv) + const configuration = computeTransportConfiguration({ clientToken, site: 'foo.datadoghq.com' }) expect(configuration.isIntakeUrl(`https://rum.browser-intake-foo-datadoghq.com/api/v2/rum?xxx`)).toBe(true) expect(configuration.isIntakeUrl(`https://logs.browser-intake-foo-datadoghq.com/api/v2/logs?xxx`)).toBe(true) expect( @@ -163,18 +153,15 @@ describe('transportConfiguration', () => { }) it('should detect proxy intake request', () => { - let configuration = computeTransportConfiguration({ clientToken, proxyUrl: 'https://www.proxy.com' }, buildEnv) + let configuration = computeTransportConfiguration({ clientToken, proxyUrl: 'https://www.proxy.com' }) expect(configuration.isIntakeUrl(`https://www.proxy.com/?ddforward=xxx`)).toBe(true) - configuration = computeTransportConfiguration( - { clientToken, proxyUrl: 'https://www.proxy.com/custom/path' }, - buildEnv - ) + configuration = computeTransportConfiguration({ clientToken, proxyUrl: 'https://www.proxy.com/custom/path' }) expect(configuration.isIntakeUrl(`https://www.proxy.com/custom/path?ddforward=xxx`)).toBe(true) }) it('should not detect request done on the same host as the proxy', () => { - const configuration = computeTransportConfiguration({ clientToken, proxyUrl: 'https://www.proxy.com' }, buildEnv) + const configuration = computeTransportConfiguration({ clientToken, proxyUrl: 'https://www.proxy.com' }) expect(configuration.isIntakeUrl('https://www.proxy.com/foo')).toBe(false) }) ;[ @@ -183,14 +170,11 @@ describe('transportConfiguration', () => { { site: 'us5.datadoghq.com', intakeDomain: 'browser-intake-us5-datadoghq.com' }, ].forEach(({ site, intakeDomain }) => { it(`should detect replica intake request for site ${site}`, () => { - const configuration = computeTransportConfiguration( - { - clientToken, - site, - replica: { clientToken }, - }, - buildEnv - ) + const configuration = computeTransportConfiguration({ + clientToken, + site, + replica: { clientToken }, + }) expect(configuration.isIntakeUrl(`https://rum.${intakeDomain}/api/v2/rum?xxx`)).toBe(true) expect(configuration.isIntakeUrl(`https://logs.${intakeDomain}/api/v2/logs?xxx`)).toBe(true) diff --git a/packages/core/src/domain/configuration/transportConfiguration.ts b/packages/core/src/domain/configuration/transportConfiguration.ts index 0e69a4c2f5..1d17847207 100644 --- a/packages/core/src/domain/configuration/transportConfiguration.ts +++ b/packages/core/src/domain/configuration/transportConfiguration.ts @@ -1,11 +1,12 @@ -import type { BuildEnv } from '../../boot/init' -import { BuildMode } from '../../boot/init' import { objectValues } from '../../tools/utils' import type { InitConfiguration } from './configuration' import type { EndpointBuilder } from './endpointBuilder' import { createEndpointBuilder, INTAKE_SITE_US } from './endpointBuilder' import { buildTags } from './tags' +// replaced at build time +declare const __BUILD_ENV__BUILD_MODE__: string + export interface TransportConfiguration { logsEndpointBuilder: EndpointBuilder rumEndpointBuilder: EndpointBuilder @@ -22,16 +23,13 @@ export interface ReplicaConfiguration { internalMonitoringEndpointBuilder: EndpointBuilder } -export function computeTransportConfiguration( - initConfiguration: InitConfiguration, - buildEnv: BuildEnv -): TransportConfiguration { +export function computeTransportConfiguration(initConfiguration: InitConfiguration): TransportConfiguration { const tags = buildTags(initConfiguration) - const endpointBuilders = computeEndpointBuilders(initConfiguration, buildEnv, tags) + const endpointBuilders = computeEndpointBuilders(initConfiguration, tags) const intakeEndpoints = objectValues(endpointBuilders).map((builder) => builder.buildIntakeUrl()) - const replicaConfiguration = computeReplicaConfiguration(initConfiguration, buildEnv, intakeEndpoints, tags) + const replicaConfiguration = computeReplicaConfiguration(initConfiguration, intakeEndpoints, tags) return { isIntakeUrl: (url) => intakeEndpoints.some((intakeEndpoint) => url.indexOf(intakeEndpoint) === 0), @@ -40,8 +38,8 @@ export function computeTransportConfiguration( } } -function computeEndpointBuilders(initConfiguration: InitConfiguration, buildEnv: BuildEnv, tags: string[]) { - if (buildEnv.buildMode === BuildMode.E2E_TEST) { +function computeEndpointBuilders(initConfiguration: InitConfiguration, tags: string[]) { + if (__BUILD_ENV__BUILD_MODE__ === 'e2e-test') { const e2eEndpointBuilder = (placeholder: string) => ({ build: () => placeholder, buildIntakeUrl: () => placeholder, @@ -56,9 +54,9 @@ function computeEndpointBuilders(initConfiguration: InitConfiguration, buildEnv: } const endpointBuilders = { - logsEndpointBuilder: createEndpointBuilder(initConfiguration, buildEnv, 'logs', tags), - rumEndpointBuilder: createEndpointBuilder(initConfiguration, buildEnv, 'rum', tags), - sessionReplayEndpointBuilder: createEndpointBuilder(initConfiguration, buildEnv, 'sessionReplay', tags), + logsEndpointBuilder: createEndpointBuilder(initConfiguration, 'logs', tags), + rumEndpointBuilder: createEndpointBuilder(initConfiguration, 'rum', tags), + sessionReplayEndpointBuilder: createEndpointBuilder(initConfiguration, 'sessionReplay', tags), } if (initConfiguration.internalMonitoringApiKey) { @@ -66,7 +64,6 @@ function computeEndpointBuilders(initConfiguration: InitConfiguration, buildEnv: ...endpointBuilders, internalMonitoringEndpointBuilder: createEndpointBuilder( { ...initConfiguration, clientToken: initConfiguration.internalMonitoringApiKey }, - buildEnv, 'logs', tags, 'browser-agent-internal-monitoring' @@ -79,7 +76,6 @@ function computeEndpointBuilders(initConfiguration: InitConfiguration, buildEnv: function computeReplicaConfiguration( initConfiguration: InitConfiguration, - buildEnv: BuildEnv, intakeEndpoints: string[], tags: string[] ): ReplicaConfiguration | undefined { @@ -94,11 +90,10 @@ function computeReplicaConfiguration( } const replicaEndpointBuilders = { - logsEndpointBuilder: createEndpointBuilder(replicaConfiguration, buildEnv, 'logs', tags), - rumEndpointBuilder: createEndpointBuilder(replicaConfiguration, buildEnv, 'rum', tags), + logsEndpointBuilder: createEndpointBuilder(replicaConfiguration, 'logs', tags), + rumEndpointBuilder: createEndpointBuilder(replicaConfiguration, 'rum', tags), internalMonitoringEndpointBuilder: createEndpointBuilder( replicaConfiguration, - buildEnv, 'logs', tags, 'browser-agent-internal-monitoring' diff --git a/packages/core/src/index.ts b/packages/core/src/index.ts index bca2b978b3..62f2a71a69 100644 --- a/packages/core/src/index.ts +++ b/packages/core/src/index.ts @@ -11,7 +11,7 @@ export { } from './domain/configuration' export { trackRuntimeError } from './domain/error/trackRuntimeError' export { computeStackTrace, StackTrace } from './domain/tracekit' -export { BuildEnv, BuildMode, defineGlobal, makePublicApi } from './boot/init' +export { defineGlobal, makePublicApi } from './boot/init' export { startInternalMonitoring, InternalMonitoring, diff --git a/packages/core/src/transport/httpRequest.spec.ts b/packages/core/src/transport/httpRequest.spec.ts index c27f8ee9fb..d670ac0fb5 100644 --- a/packages/core/src/transport/httpRequest.spec.ts +++ b/packages/core/src/transport/httpRequest.spec.ts @@ -3,7 +3,6 @@ import sinon from 'sinon' import { stubEndpointBuilder } from '../../test/specHelper' import type { EndpointBuilder } from '../domain/configuration' import { createEndpointBuilder } from '../domain/configuration' -import type { BuildEnv } from '..' import { HttpRequest } from './httpRequest' describe('httpRequest', () => { @@ -72,7 +71,6 @@ describe('httpRequest', () => { describe('httpRequest intake parameters', () => { const clientToken = 'some_client_token' - const buildEnv = {} as BuildEnv const BATCH_BYTES_LIMIT = 100 let server: sinon.SinonFakeServer let endpointBuilder: EndpointBuilder @@ -80,7 +78,7 @@ describe('httpRequest intake parameters', () => { beforeEach(() => { server = sinon.fakeServer.create() - endpointBuilder = createEndpointBuilder({ clientToken }, buildEnv, 'logs', []) + endpointBuilder = createEndpointBuilder({ clientToken }, 'logs', []) request = new HttpRequest(endpointBuilder, BATCH_BYTES_LIMIT) }) diff --git a/packages/core/test/forEach.spec.ts b/packages/core/test/forEach.spec.ts index 1b239f5025..0942a02ba0 100644 --- a/packages/core/test/forEach.spec.ts +++ b/packages/core/test/forEach.spec.ts @@ -1,6 +1,9 @@ +import type { BuildEnvWindow } from './specHelper' import { clearAllCookies } from './specHelper' beforeEach(() => { + ;(window as unknown as BuildEnvWindow).__BUILD_ENV__BUILD_MODE__ = 'dev' + ;(window as unknown as BuildEnvWindow).__BUILD_ENV__SDK_VERSION__ = 'dev' ;(navigator.sendBeacon as any) = false // reset globals ;(window as any).DD_LOGS = {} diff --git a/packages/core/test/specHelper.ts b/packages/core/test/specHelper.ts index a27a658f06..9803c574f6 100644 --- a/packages/core/test/specHelper.ts +++ b/packages/core/test/specHelper.ts @@ -5,6 +5,12 @@ import { buildUrl } from '../src/tools/urlPolyfill' import { noop, objectEntries, assign } from '../src/tools/utils' import type { BrowserWindowWithEventBridge } from '../src/transport' +// to simulate different build env behavior +export interface BuildEnvWindow { + __BUILD_ENV__BUILD_MODE__: string + __BUILD_ENV__SDK_VERSION__: string +} + export function stubEndpointBuilder(url: string) { return { build: () => url } as EndpointBuilder } diff --git a/packages/logs/src/boot/buildEnv.ts b/packages/logs/src/boot/buildEnv.ts deleted file mode 100644 index 86ba9758f0..0000000000 --- a/packages/logs/src/boot/buildEnv.ts +++ /dev/null @@ -1,6 +0,0 @@ -import type { BuildEnv, BuildMode } from '@datadog/browser-core' - -export const buildEnv: BuildEnv = { - buildMode: '<<< BUILD_MODE >>>' as BuildMode, - sdkVersion: '<<< SDK_VERSION >>>', -} diff --git a/packages/logs/src/boot/logsPublicApi.ts b/packages/logs/src/boot/logsPublicApi.ts index c4e5a68b20..1454c53447 100644 --- a/packages/logs/src/boot/logsPublicApi.ts +++ b/packages/logs/src/boot/logsPublicApi.ts @@ -14,7 +14,6 @@ import { validateAndBuildLogsConfiguration } from '../domain/configuration' import type { HandlerType, LogsMessage, StatusType } from '../domain/logger' import { Logger } from '../domain/logger' import type { startLogs } from './startLogs' -import { buildEnv } from './buildEnv' export interface LoggerConfiguration { level?: StatusType @@ -39,7 +38,7 @@ export function makeLogsPublicApi(startLogsImpl: StartLogs) { let getInitConfigurationStrategy = (): InitConfiguration | undefined => undefined const logger = new Logger(sendLog) - return makePublicApi(buildEnv, { + return makePublicApi({ logger, init: monitor((initConfiguration: LogsInitConfiguration) => { diff --git a/packages/logs/src/boot/startLogs.spec.ts b/packages/logs/src/boot/startLogs.spec.ts index 7551003d92..3f60dd17e4 100644 --- a/packages/logs/src/boot/startLogs.spec.ts +++ b/packages/logs/src/boot/startLogs.spec.ts @@ -40,11 +40,6 @@ function getLoggedMessage(server: sinon.SinonFakeServer, index: number) { } const FAKE_DATE = 123456 const SESSION_ID = 'session-id' -const baseConfiguration: LogsConfiguration = { - ...validateAndBuildLogsConfiguration({ clientToken: 'xxx', service: 'service' })!, - logsEndpointBuilder: stubEndpointBuilder('https://localhost/v1/input/log'), - maxBatchSize: 1, -} const internalMonitoring = { setExternalContextProvider: () => undefined } interface Rum { @@ -59,6 +54,7 @@ declare global { const DEFAULT_MESSAGE = { status: StatusType.info, message: 'message' } describe('logs', () => { + let baseConfiguration: LogsConfiguration let sessionIsTracked: boolean let server: sinon.SinonFakeServer let rawErrorObservable: Observable @@ -82,6 +78,11 @@ describe('logs', () => { } beforeEach(() => { + baseConfiguration = { + ...validateAndBuildLogsConfiguration({ clientToken: 'xxx', service: 'service' })!, + logsEndpointBuilder: stubEndpointBuilder('https://localhost/v1/input/log'), + maxBatchSize: 1, + } sessionIsTracked = true rawErrorObservable = new Observable() consoleObservable = new Observable() diff --git a/packages/logs/src/domain/configuration.ts b/packages/logs/src/domain/configuration.ts index 5e3fec389d..420f826381 100644 --- a/packages/logs/src/domain/configuration.ts +++ b/packages/logs/src/domain/configuration.ts @@ -8,7 +8,6 @@ import { ConsoleApiName, objectHasValue, } from '@datadog/browser-core' -import { buildEnv } from '../boot/buildEnv' import type { LogsEvent } from '../logsEvent.types' import type { StatusType } from './logger' @@ -34,7 +33,7 @@ export const DEFAULT_REQUEST_ERROR_RESPONSE_LENGTH_LIMIT = 32 * ONE_KILO_BYTE export function validateAndBuildLogsConfiguration( initConfiguration: LogsInitConfiguration ): LogsConfiguration | undefined { - const baseConfiguration = validateAndBuildConfiguration(initConfiguration, buildEnv) + const baseConfiguration = validateAndBuildConfiguration(initConfiguration) if (!baseConfiguration) { return } diff --git a/packages/rum-core/src/boot/buildEnv.ts b/packages/rum-core/src/boot/buildEnv.ts deleted file mode 100644 index 86ba9758f0..0000000000 --- a/packages/rum-core/src/boot/buildEnv.ts +++ /dev/null @@ -1,6 +0,0 @@ -import type { BuildEnv, BuildMode } from '@datadog/browser-core' - -export const buildEnv: BuildEnv = { - buildMode: '<<< BUILD_MODE >>>' as BuildMode, - sdkVersion: '<<< SDK_VERSION >>>', -} diff --git a/packages/rum-core/src/boot/rumPublicApi.ts b/packages/rum-core/src/boot/rumPublicApi.ts index 3ee113aad2..b80fe7a73c 100644 --- a/packages/rum-core/src/boot/rumPublicApi.ts +++ b/packages/rum-core/src/boot/rumPublicApi.ts @@ -24,7 +24,6 @@ import { willSyntheticsInjectRum } from '../domain/syntheticsContext' import type { RumConfiguration, RumInitConfiguration } from '../domain/configuration' import { validateAndBuildRumConfiguration } from '../domain/configuration' import type { startRum } from './startRum' -import { buildEnv } from './buildEnv' export type RumPublicApi = ReturnType @@ -167,7 +166,7 @@ export function makeRumPublicApi( ) } - const rumPublicApi = makePublicApi(buildEnv, { + const rumPublicApi = makePublicApi({ init: monitor(initRum), addRumGlobalContext: monitor(globalContextManager.add), diff --git a/packages/rum-core/src/domain/assembly.ts b/packages/rum-core/src/domain/assembly.ts index 4bbafbdf59..50e5c0e0b0 100644 --- a/packages/rum-core/src/domain/assembly.ts +++ b/packages/rum-core/src/domain/assembly.ts @@ -21,7 +21,6 @@ import type { } from '../rawRumEvent.types' import { RumEventType } from '../rawRumEvent.types' import { RumEvent } from '../rumEvent.types' -import { buildEnv } from '../boot/buildEnv' import { getSyntheticsContext } from './syntheticsContext' import { getCiTestContext } from './ciTestContext' import type { LifeCycle } from './lifeCycle' @@ -32,6 +31,9 @@ import { RumSessionPlan } from './rumSessionManager' import type { UrlContexts } from './urlContexts' import type { RumConfiguration } from './configuration' +// replaced at build time +declare const __BUILD_ENV__SDK_VERSION__: string + enum SessionType { SYNTHETICS = 'synthetics', USER = 'user', @@ -104,7 +106,7 @@ export function startRumAssembly( session: { plan: session.hasReplayPlan ? RumSessionPlan.REPLAY : RumSessionPlan.LITE, }, - browser_sdk_version: canUseEventBridge() ? buildEnv.sdkVersion : undefined, + browser_sdk_version: canUseEventBridge() ? __BUILD_ENV__SDK_VERSION__ : undefined, }, application: { id: configuration.applicationId, diff --git a/packages/rum-core/src/domain/configuration.ts b/packages/rum-core/src/domain/configuration.ts index d49db06562..44c9096534 100644 --- a/packages/rum-core/src/domain/configuration.ts +++ b/packages/rum-core/src/domain/configuration.ts @@ -6,7 +6,6 @@ import { objectHasValue, validateAndBuildConfiguration, } from '@datadog/browser-core' -import { buildEnv } from '../boot/buildEnv' import type { RumEventDomainContext } from '../domainContext.types' import type { RumEvent } from '../rumEvent.types' @@ -67,7 +66,7 @@ export function validateAndBuildRumConfiguration( } } - const baseConfiguration = validateAndBuildConfiguration(initConfiguration, buildEnv) + const baseConfiguration = validateAndBuildConfiguration(initConfiguration) if (!baseConfiguration) { return } diff --git a/packages/rum-core/src/domain/requestCollection.spec.ts b/packages/rum-core/src/domain/requestCollection.spec.ts index 974987e72e..f9097cc9fe 100644 --- a/packages/rum-core/src/domain/requestCollection.spec.ts +++ b/packages/rum-core/src/domain/requestCollection.spec.ts @@ -9,13 +9,8 @@ import { trackFetch, trackXhr } from './requestCollection' import type { Tracer } from './tracing/tracer' import { clearTracingIfNeeded, TraceIdentifier } from './tracing/tracer' -const configuration: RumConfiguration = { - ...validateAndBuildRumConfiguration({ clientToken: 'xxx', applicationId: 'xxx' })!, - ...SPEC_ENDPOINTS, - maxBatchSize: 1, -} - describe('collect fetch', () => { + let configuration: RumConfiguration const FAKE_URL = 'http://fake-url/' let fetchStub: FetchStub let fetchStubManager: FetchStubManager @@ -27,6 +22,11 @@ describe('collect fetch', () => { if (isIE()) { pending('no fetch support') } + configuration = { + ...validateAndBuildRumConfiguration({ clientToken: 'xxx', applicationId: 'xxx' })!, + ...SPEC_ENDPOINTS, + maxBatchSize: 1, + } fetchStubManager = stubFetch() startSpy = jasmine.createSpy('requestStart') @@ -138,6 +138,7 @@ describe('collect fetch', () => { }) describe('collect xhr', () => { + let configuration: RumConfiguration let startSpy: jasmine.Spy<(requestStartEvent: RequestStartEvent) => void> let completeSpy: jasmine.Spy<(requestCompleteEvent: RequestCompleteEvent) => void> let stubXhrManager: { reset(): void } @@ -147,6 +148,11 @@ describe('collect xhr', () => { if (isIE()) { pending('no fetch support') } + configuration = { + ...validateAndBuildRumConfiguration({ clientToken: 'xxx', applicationId: 'xxx' })!, + ...SPEC_ENDPOINTS, + maxBatchSize: 1, + } stubXhrManager = stubXhr() startSpy = jasmine.createSpy('requestStart') completeSpy = jasmine.createSpy('requestComplete') diff --git a/packages/rum-core/src/domain/rumEventsCollection/resource/resourceUtils.spec.ts b/packages/rum-core/src/domain/rumEventsCollection/resource/resourceUtils.spec.ts index 32c9fa76dd..31fcfed8da 100644 --- a/packages/rum-core/src/domain/rumEventsCollection/resource/resourceUtils.spec.ts +++ b/packages/rum-core/src/domain/rumEventsCollection/resource/resourceUtils.spec.ts @@ -292,10 +292,14 @@ describe('computePerformanceResourceDuration', () => { }) describe('shouldTrackResource', () => { - const configuration: RumConfiguration = { - ...validateAndBuildRumConfiguration({ clientToken: 'xxx', applicationId: 'xxx' })!, - ...SPEC_ENDPOINTS, - } + let configuration: RumConfiguration + + beforeEach(() => { + configuration = { + ...validateAndBuildRumConfiguration({ clientToken: 'xxx', applicationId: 'xxx' })!, + ...SPEC_ENDPOINTS, + } + }) it('should exclude requests on intakes endpoints', () => { expect(isAllowedRequestUrl(configuration, 'https://rum-intake.com/v1/input/abcde?foo=bar')).toBe(false) diff --git a/packages/rum-core/src/domain/tracing/tracer.spec.ts b/packages/rum-core/src/domain/tracing/tracer.spec.ts index be94da4c0c..6e8b8053a5 100644 --- a/packages/rum-core/src/domain/tracing/tracer.spec.ts +++ b/packages/rum-core/src/domain/tracing/tracer.spec.ts @@ -4,16 +4,12 @@ import { setup } from '../../../test/specHelper' import type { RumSessionManagerMock } from '../../../test/mockRumSessionManager' import { createRumSessionManagerMock } from '../../../test/mockRumSessionManager' import type { RumFetchCompleteContext, RumFetchStartContext, RumXhrStartContext } from '../requestCollection' +import type { RumConfiguration } from '../configuration' import { validateAndBuildRumConfiguration } from '../configuration' import { startTracer, TraceIdentifier } from './tracer' describe('tracer', () => { - const configuration = validateAndBuildRumConfiguration({ - clientToken: 'xxx', - applicationId: 'xxx', - allowedTracingOrigins: [window.location.origin], - service: 'service', - })! + let configuration: RumConfiguration const ALLOWED_DOMAIN_CONTEXT: Partial = { url: window.location.origin, } @@ -24,6 +20,12 @@ describe('tracer', () => { let sessionManager: RumSessionManagerMock beforeEach(() => { + configuration = validateAndBuildRumConfiguration({ + clientToken: 'xxx', + applicationId: 'xxx', + allowedTracingOrigins: [window.location.origin], + service: 'service', + })! setupBuilder = setup() sessionManager = createRumSessionManagerMock() }) diff --git a/scripts/replace-build-env.js b/scripts/replace-build-env.js index dc6cbbe010..c335797ec4 100644 --- a/scripts/replace-build-env.js +++ b/scripts/replace-build-env.js @@ -15,8 +15,8 @@ async function main() { const results = await replace({ files: `${buildDirectory}/**/*.js`, - from: Object.keys(buildEnv).map((entry) => `<<< ${entry} >>>`), - to: Object.values(buildEnv), + from: Object.keys(buildEnv).map((entry) => `__BUILD_ENV__${entry}__`), + to: Object.values(buildEnv).map((value) => `"${value}"`), }) printLog( 'Changed files:', diff --git a/test/unit/karma.base.conf.js b/test/unit/karma.base.conf.js index ffa43c8b68..1bb5a7b60d 100644 --- a/test/unit/karma.base.conf.js +++ b/test/unit/karma.base.conf.js @@ -1,5 +1,10 @@ const webpack = require('webpack') -const webpackConfig = require('../../webpack.base')({ mode: 'development', types: ['jasmine'] }) +const webpackConfig = require('../../webpack.base')({ + mode: 'development', + types: ['jasmine'], + // do not replace build env variables in unit test in order to test different build behaviors + keepBuildEnvVariables: true, +}) const getTestReportDirectory = require('../getTestReportDirectory') const jasmineSeedReporterPlugin = require('./jasmineSeedReporterPlugin') diff --git a/webpack.base.js b/webpack.base.js index afcbd50d1c..12b0341e97 100644 --- a/webpack.base.js +++ b/webpack.base.js @@ -1,11 +1,12 @@ const path = require('path') +const webpack = require('webpack') const TsconfigPathsPlugin = require('tsconfig-paths-webpack-plugin') const TerserPlugin = require('terser-webpack-plugin') const buildEnv = require('./scripts/build-env') const tsconfigPath = path.join(__dirname, 'tsconfig.webpack.json') -module.exports = ({ entry, mode, filename, types }) => ({ +module.exports = ({ entry, mode, filename, types, keepBuildEnvVariables }) => ({ entry, mode, output: { @@ -16,17 +17,6 @@ module.exports = ({ entry, mode, filename, types }) => ({ devtool: mode === 'development' ? 'inline-source-map' : false, module: { rules: [ - { - test: /\.ts$/, - loader: 'string-replace-loader', - options: { - multiple: [ - { search: '<<< SDK_VERSION >>>', replace: buildEnv.SDK_VERSION }, - { search: '<<< BUILD_MODE >>>', replace: buildEnv.BUILD_MODE }, - ], - }, - }, - { test: /\.(ts|js)$/, loader: 'ts-loader', @@ -64,4 +54,13 @@ module.exports = ({ entry, mode, filename, types }) => ({ }), ], }, + + plugins: !keepBuildEnvVariables + ? [ + new webpack.DefinePlugin({ + __BUILD_ENV__BUILD_MODE__: JSON.stringify(buildEnv.BUILD_MODE), + __BUILD_ENV__SDK_VERSION__: JSON.stringify(buildEnv.SDK_VERSION), + }), + ] + : [], }) diff --git a/yarn.lock b/yarn.lock index 38927e6610..1cd040f401 100644 --- a/yarn.lock +++ b/yarn.lock @@ -8556,14 +8556,6 @@ streamsearch@0.1.2: resolved "https://registry.yarnpkg.com/streamsearch/-/streamsearch-0.1.2.tgz#808b9d0e56fc273d809ba57338e929919a1a9f1a" integrity sha1-gIudDlb8Jz2Am6VzOOkpkZoanxo= -string-replace-loader@3.1.0: - version "3.1.0" - resolved "https://registry.yarnpkg.com/string-replace-loader/-/string-replace-loader-3.1.0.tgz#11ac6ee76bab80316a86af358ab773193dd57a4f" - integrity sha512-5AOMUZeX5HE/ylKDnEa/KKBqvlnFmRZudSOjVJHxhoJg9QYTwl1rECx7SLR8BBH7tfxb4Rp7EM2XVfQFxIhsbQ== - dependencies: - loader-utils "^2.0.0" - schema-utils "^3.0.0" - string-width@^1.0.1: version "1.0.2" resolved "https://registry.yarnpkg.com/string-width/-/string-width-1.0.2.tgz#118bdf5b8cdc51a2a7e70d211e07e2b0b9b107d3"