diff --git a/dev-packages/browser-integration-tests/package.json b/dev-packages/browser-integration-tests/package.json index 59e83fa7cca1..2eb1ae9ebfb5 100644 --- a/dev-packages/browser-integration-tests/package.json +++ b/dev-packages/browser-integration-tests/package.json @@ -61,6 +61,8 @@ "@playwright/test": "~1.56.0", "@sentry-internal/rrweb": "2.34.0", "@sentry/browser": "10.50.0", + "@sentry-internal/replay": "10.50.0", + "@sentry/opentelemetry": "10.50.0", "@supabase/supabase-js": "2.49.3", "axios": "1.15.0", "babel-loader": "^10.1.1", diff --git a/dev-packages/browser-integration-tests/suites/opentelemetry/node-exports/init.js b/dev-packages/browser-integration-tests/suites/opentelemetry/node-exports/init.js new file mode 100644 index 000000000000..d8c94f36fdd0 --- /dev/null +++ b/dev-packages/browser-integration-tests/suites/opentelemetry/node-exports/init.js @@ -0,0 +1,7 @@ +import * as Sentry from '@sentry/browser'; + +window.Sentry = Sentry; + +Sentry.init({ + dsn: 'https://public@dsn.ingest.sentry.io/1337', +}); diff --git a/dev-packages/browser-integration-tests/suites/opentelemetry/node-exports/subject.js b/dev-packages/browser-integration-tests/suites/opentelemetry/node-exports/subject.js new file mode 100644 index 000000000000..8d51286de101 --- /dev/null +++ b/dev-packages/browser-integration-tests/suites/opentelemetry/node-exports/subject.js @@ -0,0 +1,13 @@ +import * as SentryOpenTelemetry from '@sentry/opentelemetry'; +import * as Sentry from '@sentry/browser'; + +// Verify that generally all imports can be resolved +// oxlint-disable-next-line no-console +for (const key in SentryOpenTelemetry) { + console.log(key, SentryOpenTelemetry[key]); +} + +// Verify that it console.errors if calling node-only thing +new SentryOpenTelemetry.SentryAsyncLocalStorageContextManager(); + +Sentry.captureException(new Error('test')); diff --git a/dev-packages/browser-integration-tests/suites/opentelemetry/node-exports/test.ts b/dev-packages/browser-integration-tests/suites/opentelemetry/node-exports/test.ts new file mode 100644 index 000000000000..79a45bd8a1ed --- /dev/null +++ b/dev-packages/browser-integration-tests/suites/opentelemetry/node-exports/test.ts @@ -0,0 +1,26 @@ +import { expect } from '@playwright/test'; +import { sentryTest } from '../../../utils/fixtures'; +import { envelopeRequestParser, waitForErrorRequestOnUrl } from '../../../utils/helpers'; + +sentryTest('Should allow importing from @sentry/opentelemetry package', async ({ getLocalTestUrl, page }) => { + const bundle = process.env.PW_BUNDLE; + + if (bundle && bundle.includes('bundle')) { + sentryTest.skip(); + return; + } + + const consoleMessages: string[] = []; + page.on('console', msg => { + consoleMessages.push(msg.text()); + }); + + const url = await getLocalTestUrl({ testDir: __dirname }); + const req = await waitForErrorRequestOnUrl(page, url); + const eventData = envelopeRequestParser(req); + + expect(eventData.exception?.values).toHaveLength(1); + expect(eventData.exception?.values?.[0].value).toBe('test'); + + expect(consoleMessages).toContainEqual('SentryAsyncLocalStorageContextManager is not supported in the browser'); +}); diff --git a/dev-packages/browser-integration-tests/utils/generatePage.ts b/dev-packages/browser-integration-tests/utils/generatePage.ts index 3bba565fd147..a74cd7dfe7bb 100644 --- a/dev-packages/browser-integration-tests/utils/generatePage.ts +++ b/dev-packages/browser-integration-tests/utils/generatePage.ts @@ -38,11 +38,13 @@ export async function generatePage( compiler.run(err => { if (err) { reject(err); + return; } compiler.close(err => { if (err) { reject(err); + return; } resolve(); diff --git a/dev-packages/browser-integration-tests/utils/generatePlugin.ts b/dev-packages/browser-integration-tests/utils/generatePlugin.ts index 72d93bdabf33..ff45418add73 100644 --- a/dev-packages/browser-integration-tests/utils/generatePlugin.ts +++ b/dev-packages/browser-integration-tests/utils/generatePlugin.ts @@ -147,6 +147,10 @@ export const LOADER_CONFIGS: Record; * so that the compiled versions aren't included */ function generateSentryAlias(): Record { + if (!useBundleOrLoader) { + return {}; + } + const rootPackageJson = JSON.parse(fs.readFileSync(ROOT_PACKAGE_JSON_PATH, 'utf8')) as { workspaces: string[] }; const packageNames = rootPackageJson.workspaces .filter(workspace => !workspace.startsWith('dev-packages/')) @@ -189,7 +193,10 @@ class SentryScenarioGenerationPlugin { } public apply(compiler: Compiler): void { - compiler.options.resolve.alias = generateSentryAlias(); + const sentryAlias = generateSentryAlias(); + if (Object.keys(sentryAlias).length > 0) { + compiler.options.resolve.alias = sentryAlias; + } compiler.options.externals = useBundleOrLoader ? { // To help Webpack resolve Sentry modules in `import` statements in cases where they're provided in bundles rather than in `node_modules` diff --git a/dev-packages/browser-integration-tests/webpack.config.ts b/dev-packages/browser-integration-tests/webpack.config.ts index ddf31bc897c4..6aa2f6cfb0cf 100644 --- a/dev-packages/browser-integration-tests/webpack.config.ts +++ b/dev-packages/browser-integration-tests/webpack.config.ts @@ -3,7 +3,11 @@ import type { Configuration } from 'webpack'; const config = function (userConfig: Record): Configuration { return { ...userConfig, + target: 'web', mode: 'none', + resolve: { + conditionNames: ['webpack', 'import', 'require', 'browser', 'default'], + }, module: { rules: [ { diff --git a/packages/opentelemetry/package.json b/packages/opentelemetry/package.json index 5195c8b0638a..efa8a0cb6aff 100644 --- a/packages/opentelemetry/package.json +++ b/packages/opentelemetry/package.json @@ -19,10 +19,26 @@ "./package.json": "./package.json", ".": { "import": { + "node": { + "types": "./build/types/index.d.ts", + "default": "./build/esm/index.js" + }, + "browser": { + "types": "./build/types/index.d.ts", + "default": "./build/esm/index.browser.js" + }, "types": "./build/types/index.d.ts", "default": "./build/esm/index.js" }, "require": { + "node": { + "types": "./build/types/index.d.ts", + "default": "./build/cjs/index.js" + }, + "browser": { + "types": "./build/types/index.d.ts", + "default": "./build/cjs/index.browser.js" + }, "types": "./build/types/index.d.ts", "default": "./build/cjs/index.js" } diff --git a/packages/opentelemetry/rollup.npm.config.mjs b/packages/opentelemetry/rollup.npm.config.mjs index e6f5ecdd4871..d0d33de1790f 100644 --- a/packages/opentelemetry/rollup.npm.config.mjs +++ b/packages/opentelemetry/rollup.npm.config.mjs @@ -4,7 +4,7 @@ export default makeNPMConfigVariants( makeBaseNPMConfig({ // `tracingChannel` is a Node.js-only subpath so `node:diagnostics_channel` // isn't pulled into the main bundle (breaks edge/browser builds). - entrypoints: ['src/index.ts', 'src/tracingChannel.ts'], + entrypoints: ['src/index.ts', 'src/tracingChannel.ts', 'src/index.browser.ts'], packageSpecificConfig: { output: { // set exports to 'named' or 'auto' so that rollup doesn't warn diff --git a/packages/opentelemetry/src/exports.ts b/packages/opentelemetry/src/exports.ts new file mode 100644 index 000000000000..bdda20fd94ce --- /dev/null +++ b/packages/opentelemetry/src/exports.ts @@ -0,0 +1,56 @@ +export { SEMANTIC_ATTRIBUTE_SENTRY_GRAPHQL_OPERATION } from './semanticAttributes'; + +export { getRequestSpanData } from './utils/getRequestSpanData'; + +export type { OpenTelemetryClient } from './types'; +export { wrapClientClass } from './custom/client'; + +export { getSpanKind } from './utils/getSpanKind'; + +export { getScopesFromContext } from './utils/contextData'; + +export { + spanHasAttributes, + spanHasEvents, + spanHasKind, + spanHasName, + spanHasParentId, + spanHasStatus, +} from './utils/spanTypes'; + +// Re-export this for backwards compatibility (this used to be a different implementation) +export { getDynamicSamplingContextFromSpan } from '@sentry/core'; + +export { isSentryRequestSpan } from './utils/isSentryRequest'; + +export { enhanceDscWithOpenTelemetryRootSpanName } from './utils/enhanceDscWithOpenTelemetryRootSpanName'; + +export { getActiveSpan } from './utils/getActiveSpan'; +export { + startSpan, + startSpanManual, + startInactiveSpan, + withActiveSpan, + continueTrace, + getTraceContextForScope, +} from './trace'; + +export { suppressTracing } from './utils/suppressTracing'; + +export { setupEventContextTrace } from './setupEventContextTrace'; + +export { setOpenTelemetryContextAsyncContextStrategy } from './asyncContextStrategy'; +export { wrapContextManagerClass } from './contextManager'; + +export { SentryPropagator, shouldPropagateTraceForUrl } from './propagator'; +export { SentrySpanProcessor } from './spanProcessor'; +export { SentrySampler, wrapSamplingDecision } from './sampler'; + +export { openTelemetrySetupCheck } from './utils/setupCheck'; + +export { getSentryResource } from './resource'; + +export { withStreamedSpan } from '@sentry/core'; + +// Legacy +export { getClient } from '@sentry/core'; diff --git a/packages/opentelemetry/src/index.browser.ts b/packages/opentelemetry/src/index.browser.ts new file mode 100644 index 000000000000..4667379fc749 --- /dev/null +++ b/packages/opentelemetry/src/index.browser.ts @@ -0,0 +1,18 @@ +import { consoleSandbox } from '@sentry/core'; + +export * from './exports'; + +// Stubs for node-specific exports +export class SentryAsyncLocalStorageContextManager { + public constructor() { + consoleSandbox(() => { + // oxlint-disable-next-line no-console + console.error('SentryAsyncLocalStorageContextManager is not supported in the browser'); + }); + } +} + +export type AsyncLocalStorageLookup = { + asyncLocalStorage: unknown; + contextSymbol: symbol; +}; diff --git a/packages/opentelemetry/src/index.ts b/packages/opentelemetry/src/index.ts index a49597f67fdf..66766f554327 100644 --- a/packages/opentelemetry/src/index.ts +++ b/packages/opentelemetry/src/index.ts @@ -1,57 +1,5 @@ -export { SEMANTIC_ATTRIBUTE_SENTRY_GRAPHQL_OPERATION } from './semanticAttributes'; +export * from './exports'; -export { getRequestSpanData } from './utils/getRequestSpanData'; - -export type { OpenTelemetryClient } from './types'; -export { wrapClientClass } from './custom/client'; - -export { getSpanKind } from './utils/getSpanKind'; - -export { getScopesFromContext } from './utils/contextData'; - -export { - spanHasAttributes, - spanHasEvents, - spanHasKind, - spanHasName, - spanHasParentId, - spanHasStatus, -} from './utils/spanTypes'; - -// Re-export this for backwards compatibility (this used to be a different implementation) -export { getDynamicSamplingContextFromSpan } from '@sentry/core'; - -export { isSentryRequestSpan } from './utils/isSentryRequest'; - -export { enhanceDscWithOpenTelemetryRootSpanName } from './utils/enhanceDscWithOpenTelemetryRootSpanName'; - -export { getActiveSpan } from './utils/getActiveSpan'; -export { - startSpan, - startSpanManual, - startInactiveSpan, - withActiveSpan, - continueTrace, - getTraceContextForScope, -} from './trace'; - -export { suppressTracing } from './utils/suppressTracing'; - -export { setupEventContextTrace } from './setupEventContextTrace'; - -export { setOpenTelemetryContextAsyncContextStrategy } from './asyncContextStrategy'; -export { wrapContextManagerClass } from './contextManager'; +// Node-specific exports export { SentryAsyncLocalStorageContextManager } from './asyncLocalStorageContextManager'; export type { AsyncLocalStorageLookup } from './contextManager'; -export { SentryPropagator, shouldPropagateTraceForUrl } from './propagator'; -export { SentrySpanProcessor } from './spanProcessor'; -export { SentrySampler, wrapSamplingDecision } from './sampler'; - -export { openTelemetrySetupCheck } from './utils/setupCheck'; - -export { getSentryResource } from './resource'; - -export { withStreamedSpan } from '@sentry/core'; - -// Legacy -export { getClient } from '@sentry/core';