diff --git a/dev-packages/browser-integration-tests/suites/transport/multiplexed/init.js b/dev-packages/browser-integration-tests/suites/transport/multiplexed/init.js new file mode 100644 index 000000000000..9247e1d8bcc2 --- /dev/null +++ b/dev-packages/browser-integration-tests/suites/transport/multiplexed/init.js @@ -0,0 +1,20 @@ +import * as Sentry from '@sentry/browser'; + +import { makeMultiplexedTransport } from '@sentry/browser'; + +window.Sentry = Sentry; + +Sentry.init({ + dsn: 'https://public@dsn.ingest.sentry.io/1337', + transport: makeMultiplexedTransport(Sentry.makeFetchTransport, ({ getEvent }) => { + const event = getEvent('event'); + + if (event.tags.to === 'a') { + return ['https://public@dsn.ingest.sentry.io/1337']; + } else if (event.tags.to === 'b') { + return ['https://public@dsn.ingest.sentry.io/1337']; + } else { + throw new Error('Unknown destination'); + } + }), +}); diff --git a/dev-packages/browser-integration-tests/suites/transport/multiplexed/subject.js b/dev-packages/browser-integration-tests/suites/transport/multiplexed/subject.js new file mode 100644 index 000000000000..89bb4b22eca1 --- /dev/null +++ b/dev-packages/browser-integration-tests/suites/transport/multiplexed/subject.js @@ -0,0 +1,10 @@ +setTimeout(() => { + Sentry.withScope(scope => { + scope.setTag('to', 'a'); + Sentry.captureException(new Error('Error a')); + }); + Sentry.withScope(scope => { + scope.setTag('to', 'b'); + Sentry.captureException(new Error('Error b')); + }); +}, 0); diff --git a/dev-packages/browser-integration-tests/suites/transport/multiplexed/test.ts b/dev-packages/browser-integration-tests/suites/transport/multiplexed/test.ts new file mode 100644 index 000000000000..0bf274291df4 --- /dev/null +++ b/dev-packages/browser-integration-tests/suites/transport/multiplexed/test.ts @@ -0,0 +1,20 @@ +import { expect } from '@playwright/test'; +import type { Event } from '@sentry/core'; + +import { sentryTest } from '../../../utils/fixtures'; +import { getMultipleSentryEnvelopeRequests } from '../../../utils/helpers'; + +sentryTest('sends event to DSNs specified in makeMultiplexedTransport', async ({ getLocalTestUrl, page }) => { + const url = await getLocalTestUrl({ testDir: __dirname }); + const errorEvents = await getMultipleSentryEnvelopeRequests(page, 2, { envelopeType: 'event', url }); + + expect(errorEvents).toHaveLength(2); + + const [evt1, evt2] = errorEvents; + + const errorA = evt1?.tags?.to === 'a' ? evt1 : evt2; + const errorB = evt1?.tags?.to === 'b' ? evt1 : evt2; + + expect(errorA.tags?.to).toBe('a'); + expect(errorB.tags?.to).toBe('b'); +}); diff --git a/dev-packages/browser-integration-tests/utils/generatePlugin.ts b/dev-packages/browser-integration-tests/utils/generatePlugin.ts index b9b4dcb4c1f3..7ca1250296b9 100644 --- a/dev-packages/browser-integration-tests/utils/generatePlugin.ts +++ b/dev-packages/browser-integration-tests/utils/generatePlugin.ts @@ -1,6 +1,6 @@ import fs from 'fs'; import path from 'path'; -import type { Package } from '@sentry/core'; +import { type Package } from '@sentry/core'; import HtmlWebpackPlugin, { createHtmlTagObject } from 'html-webpack-plugin'; import type { Compiler } from 'webpack'; @@ -38,6 +38,8 @@ const IMPORTED_INTEGRATION_CDN_BUNDLE_PATHS: Record = { sessionTimingIntegration: 'sessiontiming', feedbackIntegration: 'feedback', moduleMetadataIntegration: 'modulemetadata', + // technically, this is not an integration, but let's add it anyway for simplicity + makeMultiplexedTransport: 'multiplexedtransport', }; const BUNDLE_PATHS: Record> = { diff --git a/packages/browser/rollup.bundle.config.mjs b/packages/browser/rollup.bundle.config.mjs index f65c27aad6e9..eaf1e1b54a8e 100644 --- a/packages/browser/rollup.bundle.config.mjs +++ b/packages/browser/rollup.bundle.config.mjs @@ -37,6 +37,19 @@ reexportedPluggableIntegrationFiles.forEach(integrationName => { builds.push(...makeBundleConfigVariants(integrationsBundleConfig)); }); +// Bundle config for additional exports we don't want to include in the main SDK bundle +// if we need more of these, we can generalize the config as for pluggable integrations +builds.push( + ...makeBundleConfigVariants( + makeBaseBundleConfig({ + bundleType: 'addon', + entrypoints: ['src/pluggable-exports-bundle/index.multiplexedtransport.ts'], + licenseTitle: '@sentry/browser - multiplexedtransport', + outputFileBase: () => 'bundles/multiplexedtransport', + }), + ), +); + const baseBundleConfig = makeBaseBundleConfig({ bundleType: 'standalone', entrypoints: ['src/index.bundle.ts'], diff --git a/packages/browser/src/pluggable-exports-bundle/index.multiplexedtransport.ts b/packages/browser/src/pluggable-exports-bundle/index.multiplexedtransport.ts new file mode 100644 index 000000000000..a7d637d9e62f --- /dev/null +++ b/packages/browser/src/pluggable-exports-bundle/index.multiplexedtransport.ts @@ -0,0 +1 @@ +export { makeMultiplexedTransport } from '@sentry/core';