diff --git a/eslint-local-rules/disallowSideEffects.ts b/eslint-local-rules/disallowSideEffects.ts index 3a8783b2b1..8ef566f70c 100644 --- a/eslint-local-rules/disallowSideEffects.ts +++ b/eslint-local-rules/disallowSideEffects.ts @@ -40,6 +40,7 @@ const pathsWithSideEffect = new Set([ `${packagesRoot}/browser-logs/src/entries/main.ts`, `${packagesRoot}/browser-rum/src/entries/main.ts`, `${packagesRoot}/browser-rum-slim/src/entries/main.ts`, + `${packagesRoot}/browser-rum-slim/src/entries/salesforce.ts`, `${packagesRoot}/browser-debugger/src/entries/main.ts`, ]) diff --git a/packages/browser-core/src/browser/addEventListener.ts b/packages/browser-core/src/browser/addEventListener.ts index 89d135f0ff..90e813c175 100644 --- a/packages/browser-core/src/browser/addEventListener.ts +++ b/packages/browser-core/src/browser/addEventListener.ts @@ -39,6 +39,7 @@ export const enum DOM_EVENT { SECURITY_POLICY_VIOLATION = 'securitypolicyviolation', SELECTION_CHANGE = 'selectionchange', STORAGE = 'storage', + UNHANDLED_REJECTION = 'unhandledrejection', } interface AddEventListenerOptions { @@ -146,3 +147,16 @@ export function addEventListeners & string>( + eventTarget: Target, + eventName: EventName, + listener: (event: EventMapFor[EventName] & { type: EventName }) => void +) { + try { + addEventListener({}, eventTarget, eventName, listener).stop() + return true + } catch { + return false + } +} diff --git a/packages/browser-core/src/browser/cookieAccess.ts b/packages/browser-core/src/browser/cookieAccess.ts index bfc9953727..03fc4fc671 100644 --- a/packages/browser-core/src/browser/cookieAccess.ts +++ b/packages/browser-core/src/browser/cookieAccess.ts @@ -4,10 +4,11 @@ import { Observable } from '../tools/observable' import { mockable } from '../tools/mockable' import { display } from '../tools/display' import { generateUUID } from '../tools/utils/stringUtils' +import { noop } from '../tools/utils/functionUtils' import type { Configuration } from '../domain/configuration' import { addTelemetryDebug } from '../domain/telemetry' import { globalObject } from '../tools/globalObject' -import { addEventListener, DOM_EVENT } from './addEventListener' +import { addEventListener, DOM_EVENT, isEventSupported } from './addEventListener' import { getCookies, setCookie } from './cookie' import type { CookieOptions } from './cookie' @@ -162,3 +163,8 @@ export function createDocumentCookieAccess( observable, } } + +// Salesforce LWS does not support the change event of CookieStore objects. https://developer.salesforce.com/tools/lws-distortion-viewer +export function isCookieStoreSupported(): boolean { + return Boolean(globalObject.cookieStore && isEventSupported(globalObject.cookieStore, DOM_EVENT.CHANGE, noop)) +} diff --git a/packages/browser-core/src/domain/error/trackRuntimeError.ts b/packages/browser-core/src/domain/error/trackRuntimeError.ts index f9b42b37b8..c3dbe2020c 100644 --- a/packages/browser-core/src/domain/error/trackRuntimeError.ts +++ b/packages/browser-core/src/domain/error/trackRuntimeError.ts @@ -4,6 +4,8 @@ import { clocksNow } from '../../tools/utils/timeUtils' import type { StackTrace } from '../../tools/stackTrace/computeStackTrace' import { computeStackTraceFromOnErrorMessage } from '../../tools/stackTrace/computeStackTrace' import { globalObject } from '../../tools/globalObject' +import { DOM_EVENT, isEventSupported } from '../../browser/addEventListener' +import { noop } from '../../tools/utils/functionUtils' import { computeRawError, isError } from './error' import type { RawError } from './error.types' import { ErrorHandling, ErrorSource, NonErrorPrefix } from './error.types' @@ -44,6 +46,11 @@ export function instrumentOnError(callback: UnhandledErrorCallback) { } export function instrumentUnhandledRejection(callback: UnhandledErrorCallback) { + // Salesforce LWS does not support the unhandledrejection event. https://developer.salesforce.com/tools/lws-distortion-viewer + if (!isEventSupported(globalObject.window, DOM_EVENT.UNHANDLED_REJECTION, noop)) { + return { stop: noop } + } + return instrumentMethod(globalObject, 'onunhandledrejection', ({ parameters: [e] }) => { callback(e.reason || 'Empty reason') }) diff --git a/packages/browser-core/src/domain/report/reportObservable.ts b/packages/browser-core/src/domain/report/reportObservable.ts index 282c5fc935..834f318901 100644 --- a/packages/browser-core/src/domain/report/reportObservable.ts +++ b/packages/browser-core/src/domain/report/reportObservable.ts @@ -1,12 +1,13 @@ import { toStackTraceString } from '../../tools/stackTrace/handlingStack' import { monitor } from '../../tools/monitor' import { mergeObservables, Observable } from '../../tools/observable' -import { addEventListener, DOM_EVENT } from '../../browser/addEventListener' +import { addEventListener, DOM_EVENT, isEventSupported } from '../../browser/addEventListener' import { safeTruncate } from '../../tools/utils/stringUtils' import type { Configuration } from '../configuration' import type { RawError } from '../error/error.types' import { ErrorHandling, ErrorSource } from '../error/error.types' import { clocksNow } from '../../tools/utils/timeUtils' +import { noop } from '../../tools/utils/functionUtils' import type { ReportType, InterventionReport, DeprecationReport } from './browser.types' export const RawReportType = { @@ -60,6 +61,11 @@ function createReportObservable(reportTypes: ReportType[]) { function createCspViolationReportObservable(configuration: Configuration) { return new Observable((observable) => { + // Salesforce does not allow to add a securitypolicyviolation event listener. https://developer.salesforce.com/tools/lws-distortion-viewer + if (!isEventSupported(document, DOM_EVENT.SECURITY_POLICY_VIOLATION, noop)) { + return + } + const { stop } = addEventListener(configuration, document, DOM_EVENT.SECURITY_POLICY_VIOLATION, (event) => { observable.notify(buildRawReportErrorFromCspViolation(event)) }) diff --git a/packages/browser-core/src/domain/session/storeStrategies/sessionInCookie.ts b/packages/browser-core/src/domain/session/storeStrategies/sessionInCookie.ts index 75945e6928..594c4063e5 100644 --- a/packages/browser-core/src/domain/session/storeStrategies/sessionInCookie.ts +++ b/packages/browser-core/src/domain/session/storeStrategies/sessionInCookie.ts @@ -13,8 +13,8 @@ import { areCookiesAuthorized, createCookieStoreAccess, createDocumentCookieAccess, + isCookieStoreSupported, } from '../../../browser/cookieAccess' -import { globalObject } from '../../../tools/globalObject' import { CookieApi, LEGACY_SESSION_STORE_KEY, SESSION_STORE_KEY } from './sessionStoreStrategy' import type { SessionStoreStrategy, @@ -33,10 +33,7 @@ export async function selectCookieStrategy( return undefined } - if ( - mockable(globalObject.cookieStore) && - (await areCookiesAuthorized(createCookieStoreAccess, cookieOptions, configuration)) - ) { + if (isCookieStoreSupported() && (await areCookiesAuthorized(createCookieStoreAccess, cookieOptions, configuration))) { return { type: SessionPersistence.COOKIE, cookieOptions, cookieApi: CookieApi.COOKIE_STORE } } diff --git a/packages/browser-core/src/index.ts b/packages/browser-core/src/index.ts index bbf50c05f5..c35a824d0d 100644 --- a/packages/browser-core/src/index.ts +++ b/packages/browser-core/src/index.ts @@ -103,6 +103,7 @@ export { export { NonErrorPrefix } from './domain/error/error.types' export type { Context, ContextArray, ContextValue } from './tools/serialisation/context' export { getCookie, getInitCookie, setCookie, deleteCookie, resetInitCookies } from './browser/cookie' +export { isCookieStoreSupported } from './browser/cookieAccess' export type { CookieStore, WeakRef, diff --git a/packages/browser-rum-core/src/browser/cookieObservable.ts b/packages/browser-rum-core/src/browser/cookieObservable.ts index 652f6ad703..4e0a72329f 100644 --- a/packages/browser-rum-core/src/browser/cookieObservable.ts +++ b/packages/browser-rum-core/src/browser/cookieObservable.ts @@ -8,12 +8,13 @@ import { DOM_EVENT, getCookie, globalObject, + isCookieStoreSupported, } from '@datadog/browser-core' export type CookieObservable = ReturnType export function createCookieObservable(configuration: Configuration, cookieName: string) { - const detectCookieChangeStrategy = globalObject.cookieStore + const detectCookieChangeStrategy = isCookieStoreSupported() ? listenToCookieStoreChange(configuration) : watchCookieFallback diff --git a/packages/browser-rum-core/src/browser/locationChangeObservable.ts b/packages/browser-rum-core/src/browser/locationChangeObservable.ts index fefa7d6ee0..9ec1b56953 100644 --- a/packages/browser-rum-core/src/browser/locationChangeObservable.ts +++ b/packages/browser-rum-core/src/browser/locationChangeObservable.ts @@ -1,4 +1,11 @@ -import { addEventListener, DOM_EVENT, instrumentMethod, Observable, shallowClone } from '@datadog/browser-core' +import { + addEventListener, + DOM_EVENT, + globalObject, + instrumentMethod, + Observable, + shallowClone, +} from '@datadog/browser-core' import type { RumConfiguration } from '../domain/configuration' export interface LocationChange { @@ -7,7 +14,7 @@ export interface LocationChange { } export function createLocationChangeObservable(configuration: RumConfiguration) { - let currentLocation = shallowClone(location) + let currentLocation = shallowClone(globalObject.location) return new Observable((observable) => { const { stop: stopHistoryTracking } = trackHistory(configuration, onLocationChange) diff --git a/packages/browser-rum-core/src/domain/contexts/urlContexts.ts b/packages/browser-rum-core/src/domain/contexts/urlContexts.ts index 530172a84c..3505b3a2f1 100644 --- a/packages/browser-rum-core/src/domain/contexts/urlContexts.ts +++ b/packages/browser-rum-core/src/domain/contexts/urlContexts.ts @@ -5,8 +5,9 @@ import { createValueHistory, HookNames, DISCARDED, - mockable, buildUrl, + mockable, + globalObject, } from '@datadog/browser-core' import type { LocationChange } from '../../browser/locationChangeObservable' import type { LifeCycle } from '../lifeCycle' @@ -41,8 +42,9 @@ export function startUrlContexts( let previousViewUrl: string | undefined lifeCycle.subscribe(LifeCycleEventType.BEFORE_VIEW_CREATED, ({ startClocks, url }) => { - const locationHref = mockable(location).href + const locationHref = mockable(globalObject.location).href const viewUrl = url !== undefined ? buildUrl(url, locationHref).href : locationHref + urlContextHistory.add( buildUrlContext({ url: viewUrl, diff --git a/packages/browser-rum-slim/package.json b/packages/browser-rum-slim/package.json index 5d6eb682f7..67b24fab8d 100644 --- a/packages/browser-rum-slim/package.json +++ b/packages/browser-rum-slim/package.json @@ -16,6 +16,7 @@ "scripts": { "build": "node ../../scripts/build/build-package.ts --modules --bundle datadog-rum-slim.js", "build:bundle": "node ../../scripts/build/build-package.ts --bundle datadog-rum-slim.js", + "build:salesforce": "node ../../scripts/build/build-package.ts --bundle rum-salesforce-lightning.js --entry ./src/entries/salesforce.ts", "prepack": "yarn build" }, "dependencies": { diff --git a/packages/browser-rum-slim/src/domain/salesforce/README.md b/packages/browser-rum-slim/src/domain/salesforce/README.md new file mode 100644 index 0000000000..21bd8f8dd0 --- /dev/null +++ b/packages/browser-rum-slim/src/domain/salesforce/README.md @@ -0,0 +1,99 @@ +# Salesforce Lightning RUM + +This entrypoint is intended for Salesforce Lightning and LWC applications using the `rum-salesforce-lightning.js` +bundle. The SDK forces the Salesforce-specific RUM settings required for this environment: + +- `trackViewsManually: true` +- `profilingSampleRate: 0` +- `sessionReplaySampleRate: 0` + +## Recommended LWC Setup + +```js +import { LightningElement, api, wire } from 'lwc' +import { NavigationMixin, CurrentPageReference } from 'lightning/navigation' +import rumSalesforceLightning from '@salesforce/resourceUrl/rum_salesforce_lightning' +import { loadScript } from 'lightning/platformResourceLoader' + +export default class DatadogInit extends NavigationMixin(LightningElement) { + @api applicationId + @api clientToken + @api site + @api service + @api env + + connectedCallback() { + this.loadDatadogRum() + } + + @wire(CurrentPageReference) + handleCurrentPageReference(pageReference) { + if (!pageReference) { + return + } + + this.loadDatadogRum().then(() => { + this.startPageReference(pageReference) + }) + } + + startPageReference(pageReference) { + window.DD_RUM?.startSalesforceView?.({ + pageReference, + baseUrl: window.location.origin || window.location.href, + generateUrl: (pageReferenceToGenerate) => this[NavigationMixin.GenerateUrl](pageReferenceToGenerate), + }) + } + + loadDatadogRum() { + return loadScript(this, rumSalesforceLightning).then(() => { + window.DD_RUM.initSalesforce({ + applicationId: this.applicationId, + clientToken: this.clientToken, + site: this.site, + service: this.service, + env: this.env, + }) + }) + } +} +``` + +## Salesforce Feature Support + +| Feature | Supported | +| ---------------------- | ------------------------------ | +| **View Events** | | +| Initial View | ✅ | +| Manual Tracking | ✅ | +| Navigation Timings | ✅ | +| Web Vitals | ✅ | +| Automatic Tracking | ✅⚠️ Supported with workaround | +| Loading Time | ✅⚠️(1) | +| **Resource Events** | | +| Fetch Resources | ✅⚠️(2) | +| XHR Resources | ✅⚠️(2) | +| Other Resources | ✅ | +| APM Correlation | ✅⚠️(2) | +| **Action Events** | | +| Custom Actions | ✅ | +| Click Actions | ✅ | +| Frustration Signals | ✅ | +| Selectors | ✅⚠️(3) | +| Action Name | ✅⚠️(2) | +| Loading Time | ✅⚠️(1) | +| **Error Events** | | +| Console Error | ✅ | +| Custom Errors | ✅ | +| Runtime Errors | ✅⚠️(4) | +| `onUnhandledRejection` | ❌ | +| CSP Violation | ❌ | +| **Other** | | +| Vital Events | ✅ | +| Long Task Events | ✅ | +| Session Replay | ❌ | + +1. Loading time depends on page activity detection, which may not work fully in Salesforce Lightning due to shadow DOM restrictions on page lifecycle signals. +2. Cross-origin restrictions in the Lightning shell may hide full request URLs; APM correlation headers and action names derived from URLs may be incomplete. +3. Due to shadow boundaries, the SDK might receive the component host as `event.target` instead of the actual clicked element. +4. Direct synchronous errors can reach the SDK through `window.onerror`, but in the Lightning shell they may be redacted as "Script error." with no original error object, URL, line, or stack available to the SDK. diff --git a/packages/browser-rum-slim/src/domain/salesforce/salesforceViewTracker.spec.ts b/packages/browser-rum-slim/src/domain/salesforce/salesforceViewTracker.spec.ts new file mode 100644 index 0000000000..0f24128421 --- /dev/null +++ b/packages/browser-rum-slim/src/domain/salesforce/salesforceViewTracker.spec.ts @@ -0,0 +1,182 @@ +import type { RumPublicApi } from '@datadog/browser-rum-core' +import { makeSalesforceRumPublicApi } from './salesforceViewTracker' + +describe('salesforce', () => { + let rumPublicApi: RumPublicApi + let initSpy: jasmine.Spy + let startViewSpy: jasmine.Spy + + beforeEach(() => { + initSpy = jasmine.createSpy() + startViewSpy = jasmine.createSpy() + rumPublicApi = { + init: initSpy, + startView: startViewSpy, + } as unknown as RumPublicApi + }) + + it('waits for the first Salesforce view before initializing RUM', () => { + const salesforceApi = makeSalesforceRumPublicApi(rumPublicApi) + + salesforceApi.initSalesforce({ + applicationId: 'application-id', + clientToken: 'client-token', + }) + + expect(initSpy).not.toHaveBeenCalled() + expect(startViewSpy).not.toHaveBeenCalled() + }) + + it('forces Salesforce-only init settings', async () => { + const salesforceApi = makeSalesforceRumPublicApi(rumPublicApi) + + salesforceApi.initSalesforce({ + applicationId: 'application-id', + clientToken: 'client-token', + profilingSampleRate: 100, + sessionReplaySampleRate: 100, + trackViewsManually: false, + } as any) + await salesforceApi.startSalesforceView({ + pageReference: {}, + baseUrl: 'https://example.com', + generateUrl: () => Promise.resolve('/generated'), + }) + + expect(initSpy.calls.mostRecent().args[0]).toEqual( + jasmine.objectContaining({ + profilingSampleRate: 0, + sessionReplaySampleRate: 0, + trackViewsManually: true, + }) + ) + }) + + it('passes customer init settings through', async () => { + const salesforceApi = makeSalesforceRumPublicApi(rumPublicApi) + + salesforceApi.initSalesforce({ + applicationId: 'application-id', + clientToken: 'client-token', + site: 'datadoghq.eu', + service: 'custom-service', + env: 'prod', + }) + await salesforceApi.startSalesforceView({ + pageReference: {}, + baseUrl: 'https://example.com', + generateUrl: () => Promise.resolve('/generated'), + }) + + expect(initSpy.calls.mostRecent().args[0]).toEqual( + jasmine.objectContaining({ + site: 'datadoghq.eu', + service: 'custom-service', + env: 'prod', + }) + ) + }) + + it('starts Salesforce views from page references', async () => { + const salesforceApi = makeSalesforceRumPublicApi(rumPublicApi) + const pageReference = {} + + salesforceApi.initSalesforce({ + applicationId: 'application-id', + clientToken: 'client-token', + }) + await salesforceApi.startSalesforceView({ + pageReference, + baseUrl: 'https://example.com', + generateUrl: (value) => { + expect(value).toBe(pageReference) + return Promise.resolve('/generated') + }, + }) + + expect(startViewSpy.calls.mostRecent().args[0]).toEqual({ + name: '/generated', + url: 'https://example.com/generated', + }) + }) + + it('uses the LWC-provided base URL to build absolute view URLs', async () => { + const salesforceApi = makeSalesforceRumPublicApi(rumPublicApi) + + salesforceApi.initSalesforce({ + applicationId: 'application-id', + clientToken: 'client-token', + }) + await salesforceApi.startSalesforceView({ + pageReference: {}, + baseUrl: 'https://example.com', + generateUrl: () => Promise.resolve('/lightning/page/home'), + }) + + expect(startViewSpy.calls.mostRecent().args[0]).toEqual({ + name: '/lightning/page/home', + url: 'https://example.com/lightning/page/home', + }) + }) + + it('deduplicates repeated Salesforce view URLs', async () => { + const salesforceApi = makeSalesforceRumPublicApi(rumPublicApi) + + salesforceApi.initSalesforce({ + applicationId: 'application-id', + clientToken: 'client-token', + }) + await salesforceApi.startSalesforceView({ + pageReference: {}, + baseUrl: 'https://example.com', + generateUrl: () => Promise.resolve('/generated'), + }) + await salesforceApi.startSalesforceView({ + pageReference: {}, + baseUrl: 'https://example.com', + generateUrl: () => Promise.resolve('/generated'), + }) + + expect(startViewSpy).toHaveBeenCalledTimes(1) + expect(initSpy).toHaveBeenCalledTimes(1) + }) + + it('starts route-change views after RUM is initialized', async () => { + const salesforceApi = makeSalesforceRumPublicApi(rumPublicApi) + + salesforceApi.initSalesforce({ + applicationId: 'application-id', + clientToken: 'client-token', + }) + await salesforceApi.startSalesforceView({ + pageReference: {}, + baseUrl: 'https://example.com', + generateUrl: () => Promise.resolve('/first'), + }) + await salesforceApi.startSalesforceView({ + pageReference: {}, + baseUrl: 'https://example.com', + generateUrl: () => Promise.resolve('/second'), + }) + + expect(startViewSpy).toHaveBeenCalledTimes(2) + expect(initSpy).toHaveBeenCalledTimes(1) + }) + + it('does not start a new view when Salesforce URL generation fails', async () => { + const salesforceApi = makeSalesforceRumPublicApi(rumPublicApi) + + salesforceApi.initSalesforce({ + applicationId: 'application-id', + clientToken: 'client-token', + }) + await salesforceApi.startSalesforceView({ + pageReference: {}, + baseUrl: 'https://example.com', + generateUrl: () => Promise.reject(new Error('failed')), + }) + + expect(startViewSpy).not.toHaveBeenCalled() + expect(initSpy).not.toHaveBeenCalled() + }) +}) diff --git a/packages/browser-rum-slim/src/domain/salesforce/salesforceViewTracker.ts b/packages/browser-rum-slim/src/domain/salesforce/salesforceViewTracker.ts new file mode 100644 index 0000000000..3a53502929 --- /dev/null +++ b/packages/browser-rum-slim/src/domain/salesforce/salesforceViewTracker.ts @@ -0,0 +1,71 @@ +import { addTelemetryError, buildUrl } from '@datadog/browser-core' +import type { RumInitConfiguration, RumPublicApi, ViewOptions } from '@datadog/browser-rum-core' + +export type SalesforceGenerateUrl = (pageReference: unknown) => Promise + +export type SalesforceRumInitConfiguration = Omit< + RumInitConfiguration, + 'profilingSampleRate' | 'sessionReplaySampleRate' | 'trackViewsManually' +> + +export interface SalesforceViewOptions { + pageReference: unknown + generateUrl: SalesforceGenerateUrl + baseUrl: string +} + +export interface SalesforceRumPublicApi extends RumPublicApi { + initSalesforce: (configuration: SalesforceRumInitConfiguration) => void + startSalesforceView: (options: SalesforceViewOptions) => Promise +} + +export function makeSalesforceRumPublicApi( + rumPublicApi: RumPublicApi +): Pick { + let initializationConfiguration: RumInitConfiguration | undefined + let initialized = false + let lastStartedUrl: string | undefined + + function initSalesforce(configuration: SalesforceRumInitConfiguration) { + if (initializationConfiguration) { + return + } + + initializationConfiguration = { + ...configuration, + profilingSampleRate: 0, + sessionReplaySampleRate: 0, + trackViewsManually: true, + } + startRum() + } + + async function startSalesforceView({ pageReference, generateUrl, baseUrl }: SalesforceViewOptions) { + try { + const url = await generateUrl(pageReference) + const absoluteUrl = buildUrl(url, baseUrl).href + startView(url, absoluteUrl) + startRum() + } catch (error) { + addTelemetryError(error) + } + } + + function startView(name: string, url: ViewOptions['url']) { + if (name === lastStartedUrl) { + return + } + lastStartedUrl = name + rumPublicApi.startView({ name, url }) + } + + function startRum() { + if (initialized || !initializationConfiguration || lastStartedUrl === undefined) { + return + } + initialized = true + rumPublicApi.init(initializationConfiguration) + } + + return { initSalesforce, startSalesforceView } +} diff --git a/packages/browser-rum-slim/src/entries/salesforce.ts b/packages/browser-rum-slim/src/entries/salesforce.ts new file mode 100644 index 0000000000..b1c206cc17 --- /dev/null +++ b/packages/browser-rum-slim/src/entries/salesforce.ts @@ -0,0 +1,81 @@ +import { defineGlobal, globalObject } from '@datadog/browser-core' +import type { RumPublicApi } from '@datadog/browser-rum-core' +import { makeRumPublicApi } from '@datadog/browser-rum-core' +import { makeRecorderApiStub } from '../boot/stubRecorderApi' +import { makeProfilerApiStub } from '../boot/stubProfilerApi' +import { makeSalesforceRumPublicApi } from '../domain/salesforce/salesforceViewTracker' + +export type { + User, + Account, + TraceContextInjection, + SessionPersistence, + TrackingConsent, + MatchOption, + ProxyFn, + Site, + Context, + ContextValue, + ContextArray, + RumInternalContext, +} from '@datadog/browser-core' + +/** + * @deprecated Use {@link DatadogRum} instead + */ +export type RumGlobal = RumPublicApi + +export type { + RumPublicApi as DatadogRum, + RumInitConfiguration, + RumBeforeSend, + ViewOptions, + StartRecordingOptions, + AddDurationVitalOptions, + DurationVitalOptions, + FeatureOperationOptions, + FailureReason, + ActionOptions, + ResourceOptions, + ResourceStopOptions, + TracingOption, + RumPlugin, + OnRumStartOptions, + PropagatorType, + FeatureFlagsForEvents, + MatchHeader, + CommonProperties, + RumEvent, + RumActionEvent, + RumErrorEvent, + RumLongTaskEvent, + RumResourceEvent, + RumViewEvent, + RumVitalEvent, + RumEventDomainContext, + RumViewEventDomainContext, + RumErrorEventDomainContext, + RumActionEventDomainContext, + RumVitalEventDomainContext, + RumResourceEventDomainContext, + RumLongTaskEventDomainContext, +} from '@datadog/browser-rum-core' +export { DEFAULT_TRACKED_RESOURCE_HEADERS } from '@datadog/browser-rum-core' +export { DefaultPrivacyLevel } from '@datadog/browser-core' +export type { + SalesforceGenerateUrl, + SalesforceRumInitConfiguration, + SalesforceRumPublicApi, + SalesforceViewOptions, +} from '../domain/salesforce/salesforceViewTracker' + +const rumPublicApi = makeRumPublicApi(makeRecorderApiStub(), makeProfilerApiStub(), { + sdkName: 'rum-slim', +}) + +export const datadogRum = Object.assign(rumPublicApi, makeSalesforceRumPublicApi(rumPublicApi)) + +interface BrowserWindow { + DD_RUM?: RumPublicApi & ReturnType +} +defineGlobal(globalObject as BrowserWindow, 'DD_RUM', datadogRum) diff --git a/scripts/build/build-package.ts b/scripts/build/build-package.ts index a60b5ff78e..3fe681e68d 100644 --- a/scripts/build/build-package.ts +++ b/scripts/build/build-package.ts @@ -19,6 +19,9 @@ runMain(async () => { bundle: { type: 'string', }, + entry: { + type: 'string', + }, verbose: { type: 'boolean', default: false, @@ -43,6 +46,7 @@ runMain(async () => { if (values.bundle) { printLog('Building bundle...') await buildBundle({ + entry: values.entry ?? './src/entries/main.ts', filename: values.bundle, verbose: values.verbose, }) @@ -51,13 +55,13 @@ runMain(async () => { printLog('Done.') }) -async function buildBundle({ filename, verbose }: { filename: string; verbose: boolean }) { +async function buildBundle({ entry, filename, verbose }: { entry: string; filename: string; verbose: boolean }) { await fs.rm('./bundle', { recursive: true, force: true }) return new Promise((resolve, reject) => { webpack( webpackBase({ mode: 'production', - entry: './src/entries/main.ts', + entry, filename, }), (error, stats) => {