From 52c06cbe1b6385ad422b19a870f689e93ee31e8f Mon Sep 17 00:00:00 2001 From: Lukas Stracke Date: Tue, 10 Mar 2026 17:17:53 +0100 Subject: [PATCH 1/9] feat(core,node-core,node): Add server-side span streaming implementation --- packages/astro/src/index.types.ts | 1 + packages/core/src/index.ts | 1 + packages/nextjs/src/index.types.ts | 1 + packages/node-core/src/common-exports.ts | 1 + packages/node-core/src/light/sdk.ts | 5 ++ packages/node-core/src/sdk/client.ts | 1 + packages/node-core/src/sdk/index.ts | 5 ++ packages/node/src/index.ts | 1 + packages/nuxt/src/index.types.ts | 1 + packages/opentelemetry/src/spanProcessor.ts | 90 +++++++++---------- packages/react-router/src/index.types.ts | 1 + packages/remix/src/index.types.ts | 1 + packages/solidstart/src/index.types.ts | 1 + packages/sveltekit/src/index.types.ts | 1 + .../tanstackstart-react/src/index.types.ts | 1 + 15 files changed, 65 insertions(+), 47 deletions(-) diff --git a/packages/astro/src/index.types.ts b/packages/astro/src/index.types.ts index 2e51ab1b0f1e..8dfffdcbd852 100644 --- a/packages/astro/src/index.types.ts +++ b/packages/astro/src/index.types.ts @@ -20,6 +20,7 @@ export declare function init(options: Options | clientSdk.BrowserOptions | NodeO export declare const linkedErrorsIntegration: typeof clientSdk.linkedErrorsIntegration; export declare const contextLinesIntegration: typeof clientSdk.contextLinesIntegration; +export declare const spanStreamingIntegration: typeof clientSdk.spanStreamingIntegration; export declare const getDefaultIntegrations: (options: Options) => Integration[]; export declare const defaultStackParser: StackParser; diff --git a/packages/core/src/index.ts b/packages/core/src/index.ts index a2af055232a1..539405f09adc 100644 --- a/packages/core/src/index.ts +++ b/packages/core/src/index.ts @@ -184,6 +184,7 @@ export type { export { SpanBuffer } from './tracing/spans/spanBuffer'; export { hasSpanStreamingEnabled } from './tracing/spans/hasSpanStreamingEnabled'; +export { spanStreamingIntegration } from './integrations/spanStreaming'; export type { FeatureFlag } from './utils/featureFlags'; diff --git a/packages/nextjs/src/index.types.ts b/packages/nextjs/src/index.types.ts index 7c92fecd7834..1ac235711ca5 100644 --- a/packages/nextjs/src/index.types.ts +++ b/packages/nextjs/src/index.types.ts @@ -23,6 +23,7 @@ export declare function init( export declare const linkedErrorsIntegration: typeof clientSdk.linkedErrorsIntegration; export declare const contextLinesIntegration: typeof clientSdk.contextLinesIntegration; +export declare const spanStreamingIntegration: typeof clientSdk.spanStreamingIntegration; // Different implementation in server and worker export declare const vercelAIIntegration: typeof serverSdk.vercelAIIntegration; diff --git a/packages/node-core/src/common-exports.ts b/packages/node-core/src/common-exports.ts index 3fff4100b352..4877fc159e88 100644 --- a/packages/node-core/src/common-exports.ts +++ b/packages/node-core/src/common-exports.ts @@ -115,6 +115,7 @@ export { consoleIntegration, wrapMcpServerWithSentry, featureFlagsIntegration, + spanStreamingIntegration, metrics, envToBool, } from '@sentry/core'; diff --git a/packages/node-core/src/light/sdk.ts b/packages/node-core/src/light/sdk.ts index ef9229f28de6..1edacca2b07a 100644 --- a/packages/node-core/src/light/sdk.ts +++ b/packages/node-core/src/light/sdk.ts @@ -12,6 +12,7 @@ import { linkedErrorsIntegration, propagationContextFromHeaders, requestDataIntegration, + spanStreamingIntegration, stackParserFromStackParserOptions, } from '@sentry/core'; import { DEBUG_BUILD } from '../debug-build'; @@ -112,6 +113,10 @@ function _init( ); } + if (options.traceLifecycle === 'stream' && !options.integrations.some(({ name }) => name === 'SpanStreaming')) { + options.integrations.push(spanStreamingIntegration()); + } + applySdkMetadata(options, 'node-light', ['node-core']); const client = new LightNodeClient(options); diff --git a/packages/node-core/src/sdk/client.ts b/packages/node-core/src/sdk/client.ts index 80a233aa3954..211610d6491c 100644 --- a/packages/node-core/src/sdk/client.ts +++ b/packages/node-core/src/sdk/client.ts @@ -9,6 +9,7 @@ import { _INTERNAL_flushLogsBuffer, applySdkMetadata, debug, + hasSpanStreamingEnabled, SDK_VERSION, ServerRuntimeClient, } from '@sentry/core'; diff --git a/packages/node-core/src/sdk/index.ts b/packages/node-core/src/sdk/index.ts index de1569135cfd..5b495ad6bf8a 100644 --- a/packages/node-core/src/sdk/index.ts +++ b/packages/node-core/src/sdk/index.ts @@ -14,6 +14,7 @@ import { linkedErrorsIntegration, propagationContextFromHeaders, requestDataIntegration, + spanStreamingIntegration, stackParserFromStackParserOptions, } from '@sentry/core'; import { @@ -126,6 +127,10 @@ function _init( ); } + if (options.traceLifecycle === 'stream' && !options.integrations.some(({ name }) => name === 'SpanStreaming')) { + options.integrations.push(spanStreamingIntegration()); + } + applySdkMetadata(options, 'node-core'); const client = new NodeClient(options); diff --git a/packages/node/src/index.ts b/packages/node/src/index.ts index 8458dee5f6a7..9e1e892ed7ce 100644 --- a/packages/node/src/index.ts +++ b/packages/node/src/index.ts @@ -140,6 +140,7 @@ export { consoleIntegration, wrapMcpServerWithSentry, featureFlagsIntegration, + spanStreamingIntegration, createLangChainCallbackHandler, instrumentLangGraph, instrumentStateGraphCompile, diff --git a/packages/nuxt/src/index.types.ts b/packages/nuxt/src/index.types.ts index 7109e7ad9c78..9a0283cfcee8 100644 --- a/packages/nuxt/src/index.types.ts +++ b/packages/nuxt/src/index.types.ts @@ -16,6 +16,7 @@ export * from './index.server'; export declare function init(options: Options | SentryNuxtClientOptions | SentryNuxtServerOptions): Client | undefined; export declare const linkedErrorsIntegration: typeof clientSdk.linkedErrorsIntegration; export declare const contextLinesIntegration: typeof clientSdk.contextLinesIntegration; +export declare const spanStreamingIntegration: typeof clientSdk.spanStreamingIntegration; export declare const getDefaultIntegrations: (options: Options) => Integration[]; export declare const defaultStackParser: StackParser; diff --git a/packages/opentelemetry/src/spanProcessor.ts b/packages/opentelemetry/src/spanProcessor.ts index 3430456caaee..b4b913c5535e 100644 --- a/packages/opentelemetry/src/spanProcessor.ts +++ b/packages/opentelemetry/src/spanProcessor.ts @@ -6,6 +6,7 @@ import { getClient, getDefaultCurrentScope, getDefaultIsolationScope, + hasSpanStreamingEnabled, logSpanEnd, logSpanStart, setCapturedScopesOnSpan, @@ -14,50 +15,6 @@ import { SEMANTIC_ATTRIBUTE_SENTRY_PARENT_IS_REMOTE } from './semanticAttributes import { SentrySpanExporter } from './spanExporter'; import { getScopesFromContext } from './utils/contextData'; import { setIsSetup } from './utils/setupCheck'; - -function onSpanStart(span: Span, parentContext: Context): void { - // This is a reliable way to get the parent span - because this is exactly how the parent is identified in the OTEL SDK - const parentSpan = trace.getSpan(parentContext); - - let scopes = getScopesFromContext(parentContext); - - // We need access to the parent span in order to be able to move up the span tree for breadcrumbs - if (parentSpan && !parentSpan.spanContext().isRemote) { - addChildSpanToSpan(parentSpan, span); - } - - // We need this in the span exporter - if (parentSpan?.spanContext().isRemote) { - span.setAttribute(SEMANTIC_ATTRIBUTE_SENTRY_PARENT_IS_REMOTE, true); - } - - // The root context does not have scopes stored, so we check for this specifically - // As fallback we attach the global scopes - if (parentContext === ROOT_CONTEXT) { - scopes = { - scope: getDefaultCurrentScope(), - isolationScope: getDefaultIsolationScope(), - }; - } - - // We need the scope at time of span creation in order to apply it to the event when the span is finished - if (scopes) { - setCapturedScopesOnSpan(span, scopes.scope, scopes.isolationScope); - } - - logSpanStart(span); - - const client = getClient(); - client?.emit('spanStart', span); -} - -function onSpanEnd(span: Span): void { - logSpanEnd(span); - - const client = getClient(); - client?.emit('spanEnd', span); -} - /** * Converts OpenTelemetry Spans to Sentry Spans and sends them to Sentry via * the Sentry SDK. @@ -88,13 +45,52 @@ export class SentrySpanProcessor implements SpanProcessorInterface { * @inheritDoc */ public onStart(span: Span, parentContext: Context): void { - onSpanStart(span, parentContext); + // This is a reliable way to get the parent span - because this is exactly how the parent is identified in the OTEL SDK + const parentSpan = trace.getSpan(parentContext); + + let scopes = getScopesFromContext(parentContext); + + // We need access to the parent span in order to be able to move up the span tree for breadcrumbs + if (parentSpan && !parentSpan.spanContext().isRemote) { + addChildSpanToSpan(parentSpan, span); + } + + // We need this in the span exporter + if (parentSpan?.spanContext().isRemote) { + span.setAttribute(SEMANTIC_ATTRIBUTE_SENTRY_PARENT_IS_REMOTE, true); + } + + // The root context does not have scopes stored, so we check for this specifically + // As fallback we attach the global scopes + if (parentContext === ROOT_CONTEXT) { + scopes = { + scope: getDefaultCurrentScope(), + isolationScope: getDefaultIsolationScope(), + }; + } + + // We need the scope at time of span creation in order to apply it to the event when the span is finished + if (scopes) { + setCapturedScopesOnSpan(span, scopes.scope, scopes.isolationScope); + } + + logSpanStart(span); + + const client = getClient(); + client?.emit('spanStart', span); } /** @inheritDoc */ public onEnd(span: Span & ReadableSpan): void { - onSpanEnd(span); + logSpanEnd(span); + + const client = getClient(); + client?.emit('spanEnd', span); - this._exporter.export(span); + if (client && hasSpanStreamingEnabled(client)) { + client.emit('afterSpanEnd', span); + } else { + this._exporter.export(span); + } } } diff --git a/packages/react-router/src/index.types.ts b/packages/react-router/src/index.types.ts index c9c5cb371763..4d0abcb6b0a8 100644 --- a/packages/react-router/src/index.types.ts +++ b/packages/react-router/src/index.types.ts @@ -16,6 +16,7 @@ export declare function init(options: Options | clientSdk.BrowserOptions | serve export declare const contextLinesIntegration: typeof clientSdk.contextLinesIntegration; export declare const linkedErrorsIntegration: typeof clientSdk.linkedErrorsIntegration; +export declare const spanStreamingIntegration: typeof clientSdk.spanStreamingIntegration; export declare const defaultStackParser: StackParser; export declare const getDefaultIntegrations: (options: Options) => Integration[]; diff --git a/packages/remix/src/index.types.ts b/packages/remix/src/index.types.ts index 61d4f7e0b9bb..1cac41c1aacf 100644 --- a/packages/remix/src/index.types.ts +++ b/packages/remix/src/index.types.ts @@ -18,6 +18,7 @@ export declare function init(options: RemixOptions): Client | undefined; export declare const browserTracingIntegration: typeof clientSdk.browserTracingIntegration; export declare const linkedErrorsIntegration: typeof clientSdk.linkedErrorsIntegration; export declare const contextLinesIntegration: typeof clientSdk.contextLinesIntegration; +export declare const spanStreamingIntegration: typeof clientSdk.spanStreamingIntegration; export declare const getDefaultIntegrations: (options: Options) => Integration[]; export declare const defaultStackParser: StackParser; diff --git a/packages/solidstart/src/index.types.ts b/packages/solidstart/src/index.types.ts index 4c5ff491c740..58e642ecba22 100644 --- a/packages/solidstart/src/index.types.ts +++ b/packages/solidstart/src/index.types.ts @@ -18,6 +18,7 @@ export declare function init(options: Options | clientSdk.BrowserOptions | serve export declare const linkedErrorsIntegration: typeof clientSdk.linkedErrorsIntegration; export declare const contextLinesIntegration: typeof clientSdk.contextLinesIntegration; +export declare const spanStreamingIntegration: typeof clientSdk.spanStreamingIntegration; export declare const getDefaultIntegrations: (options: Options) => Integration[]; export declare const defaultStackParser: StackParser; diff --git a/packages/sveltekit/src/index.types.ts b/packages/sveltekit/src/index.types.ts index d46e88e720ed..78cb27e2e47d 100644 --- a/packages/sveltekit/src/index.types.ts +++ b/packages/sveltekit/src/index.types.ts @@ -47,6 +47,7 @@ export declare function wrapLoadWithSentry any>(orig export declare const linkedErrorsIntegration: typeof clientSdk.linkedErrorsIntegration; export declare const contextLinesIntegration: typeof clientSdk.contextLinesIntegration; +export declare const spanStreamingIntegration: typeof clientSdk.spanStreamingIntegration; // Different implementation in server and worker export declare const vercelAIIntegration: typeof serverSdk.vercelAIIntegration; diff --git a/packages/tanstackstart-react/src/index.types.ts b/packages/tanstackstart-react/src/index.types.ts index 7ab05bfc3831..0545fbf5c87b 100644 --- a/packages/tanstackstart-react/src/index.types.ts +++ b/packages/tanstackstart-react/src/index.types.ts @@ -18,6 +18,7 @@ export declare function init(options: Options | clientSdk.BrowserOptions | serve export declare const linkedErrorsIntegration: typeof clientSdk.linkedErrorsIntegration; export declare const contextLinesIntegration: typeof clientSdk.contextLinesIntegration; +export declare const spanStreamingIntegration: typeof clientSdk.spanStreamingIntegration; export declare const getDefaultIntegrations: (options: Options) => Integration[]; export declare const defaultStackParser: StackParser; From 0a989e3d725fbd309da62ff14510793be2e0199a Mon Sep 17 00:00:00 2001 From: Lukas Stracke Date: Tue, 10 Mar 2026 17:18:39 +0100 Subject: [PATCH 2/9] add new files --- .../core/src/integrations/spanStreaming.ts | 42 ++++++ .../test/integrations/spanStreaming.test.ts | 135 ++++++++++++++++++ .../opentelemetry/test/spanProcessor.test.ts | 57 ++++++++ 3 files changed, 234 insertions(+) create mode 100644 packages/core/src/integrations/spanStreaming.ts create mode 100644 packages/core/test/integrations/spanStreaming.test.ts create mode 100644 packages/opentelemetry/test/spanProcessor.test.ts diff --git a/packages/core/src/integrations/spanStreaming.ts b/packages/core/src/integrations/spanStreaming.ts new file mode 100644 index 000000000000..069382929b2f --- /dev/null +++ b/packages/core/src/integrations/spanStreaming.ts @@ -0,0 +1,42 @@ +import type { IntegrationFn } from '../types-hoist/integration'; +import { DEBUG_BUILD } from '../debug-build'; +import { defineIntegration } from '../integration'; +import { isStreamedBeforeSendSpanCallback } from '../tracing/spans/beforeSendSpan'; +import { captureSpan } from '../tracing/spans/captureSpan'; +import { hasSpanStreamingEnabled } from '../tracing/spans/hasSpanStreamingEnabled'; +import { SpanBuffer } from '../tracing/spans/spanBuffer'; +import { debug } from '../utils/debug-logger'; +import { spanIsSampled } from '../utils/spanUtils'; + +export const spanStreamingIntegration = defineIntegration(() => { + return { + name: 'SpanStreaming', + + setup(client) { + const initialMessage = 'SpanStreaming integration requires'; + const fallbackMsg = 'Falling back to static trace lifecycle.'; + + if (!hasSpanStreamingEnabled(client)) { + DEBUG_BUILD && debug.warn(`${initialMessage} \`traceLifecycle\` to be set to "stream"! ${fallbackMsg}`); + return; + } + + const beforeSendSpan = client.getOptions().beforeSendSpan; + if (beforeSendSpan && !isStreamedBeforeSendSpanCallback(beforeSendSpan)) { + client.getOptions().traceLifecycle = 'static'; + DEBUG_BUILD && + debug.warn(`${initialMessage} a beforeSendSpan callback using \`withStreamedSpan\`! ${fallbackMsg}`); + return; + } + + const buffer = new SpanBuffer(client); + + client.on('afterSpanEnd', span => { + if (!spanIsSampled(span)) { + return; + } + buffer.add(captureSpan(span, client)); + }); + }, + }; +}) satisfies IntegrationFn; diff --git a/packages/core/test/integrations/spanStreaming.test.ts b/packages/core/test/integrations/spanStreaming.test.ts new file mode 100644 index 000000000000..a7b45184145a --- /dev/null +++ b/packages/core/test/integrations/spanStreaming.test.ts @@ -0,0 +1,135 @@ +import * as SentryCore from '../../src'; +import { debug } from '../../src'; +import { beforeEach, describe, expect, it, vi } from 'vitest'; +import { spanStreamingIntegration } from '../../src/integrations/spanStreaming'; +import { TestClient, getDefaultTestClientOptions } from '../mocks/client'; + +const mockSpanBufferInstance = vi.hoisted(() => ({ + flush: vi.fn(), + add: vi.fn(), + drain: vi.fn(), +})); + +const MockSpanBuffer = vi.hoisted(() => { + return vi.fn(() => mockSpanBufferInstance); +}); + +vi.mock('../../src/tracing/spans/spanBuffer', async () => { + const original = await vi.importActual('../../src/tracing/spans/spanBuffer'); + return { + ...original, + SpanBuffer: MockSpanBuffer, + }; +}); + +describe('spanStreamingIntegration (core)', () => { + beforeEach(() => { + vi.clearAllMocks(); + }); + + it('has the correct name and setup hook', () => { + const integration = spanStreamingIntegration(); + expect(integration.name).toBe('SpanStreaming'); + // eslint-disable-next-line @typescript-eslint/unbound-method + expect(integration.setup).toBeDefined(); + }); + + it('logs a warning if traceLifecycle is not set to "stream"', () => { + const debugSpy = vi.spyOn(debug, 'warn').mockImplementation(() => {}); + const client = new TestClient({ + ...getDefaultTestClientOptions(), + dsn: 'https://username@domain/123', + integrations: [spanStreamingIntegration()], + traceLifecycle: 'static', + }); + + SentryCore.setCurrentClient(client); + client.init(); + + expect(debugSpy).toHaveBeenCalledWith( + 'SpanStreaming integration requires `traceLifecycle` to be set to "stream"! Falling back to static trace lifecycle.', + ); + debugSpy.mockRestore(); + + expect(client.getOptions().traceLifecycle).toBe('static'); + }); + + it('falls back to static trace lifecycle if beforeSendSpan is not compatible with span streaming', () => { + const debugSpy = vi.spyOn(debug, 'warn').mockImplementation(() => {}); + const client = new TestClient({ + ...getDefaultTestClientOptions(), + dsn: 'https://username@domain/123', + integrations: [spanStreamingIntegration()], + traceLifecycle: 'stream', + beforeSendSpan: (span: SentryCore.SpanJSON) => span, + }); + + SentryCore.setCurrentClient(client); + client.init(); + + expect(debugSpy).toHaveBeenCalledWith( + 'SpanStreaming integration requires a beforeSendSpan callback using `withStreamedSpan`! Falling back to static trace lifecycle.', + ); + debugSpy.mockRestore(); + + expect(client.getOptions().traceLifecycle).toBe('static'); + }); + + it('sets up buffer when traceLifecycle is "stream"', () => { + const client = new TestClient({ + ...getDefaultTestClientOptions(), + dsn: 'https://username@domain/123', + integrations: [spanStreamingIntegration()], + traceLifecycle: 'stream', + }); + + SentryCore.setCurrentClient(client); + client.init(); + + expect(MockSpanBuffer).toHaveBeenCalledWith(client); + expect(client.getOptions().traceLifecycle).toBe('stream'); + }); + + it('enqueues a span into the buffer when the span ends', () => { + const client = new TestClient({ + ...getDefaultTestClientOptions(), + dsn: 'https://username@domain/123', + integrations: [spanStreamingIntegration()], + traceLifecycle: 'stream', + tracesSampleRate: 1, + }); + + SentryCore.setCurrentClient(client); + client.init(); + + const span = new SentryCore.SentrySpan({ name: 'test', sampled: true }); + client.emit('afterSpanEnd', span); + + expect(mockSpanBufferInstance.add).toHaveBeenCalledWith( + expect.objectContaining({ + _segmentSpan: span, + trace_id: span.spanContext().traceId, + span_id: span.spanContext().spanId, + name: 'test', + }), + ); + }); + + it('does not enqueue a span into the buffer when the span is not sampled', () => { + const client = new TestClient({ + ...getDefaultTestClientOptions(), + dsn: 'https://username@domain/123', + integrations: [spanStreamingIntegration()], + traceLifecycle: 'stream', + tracesSampleRate: 1, + }); + + SentryCore.setCurrentClient(client); + client.init(); + + const span = new SentryCore.SentrySpan({ name: 'test', sampled: false }); + client.emit('afterSpanEnd', span); + + expect(mockSpanBufferInstance.add).not.toHaveBeenCalled(); + }); +}); diff --git a/packages/opentelemetry/test/spanProcessor.test.ts b/packages/opentelemetry/test/spanProcessor.test.ts new file mode 100644 index 000000000000..2e3b0b5b999e --- /dev/null +++ b/packages/opentelemetry/test/spanProcessor.test.ts @@ -0,0 +1,57 @@ +import { getClient, startInactiveSpan } from '@sentry/core'; +import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest'; +import { SentrySpanExporter } from '../src/spanExporter'; +import { cleanupOtel, mockSdkInit } from './helpers/mockSdkInit'; + +const exportSpy = vi.spyOn(SentrySpanExporter.prototype, 'export'); + +describe('SentrySpanProcessor', () => { + beforeEach(() => { + exportSpy.mockClear(); + }); + + describe('with traceLifecycle: static (default)', () => { + beforeEach(() => { + mockSdkInit({ tracesSampleRate: 1 }); + }); + + afterEach(async () => { + await cleanupOtel(); + }); + + it('exports spans via the exporter', () => { + const span = startInactiveSpan({ name: 'test' }); + span.end(); + + expect(exportSpy).toHaveBeenCalled(); + }); + }); + + describe('with traceLifecycle: stream', () => { + beforeEach(() => { + mockSdkInit({ tracesSampleRate: 1, traceLifecycle: 'stream' }); + }); + + afterEach(async () => { + await cleanupOtel(); + }); + + it('does not export spans via the exporter', () => { + const span = startInactiveSpan({ name: 'test' }); + span.end(); + + expect(exportSpy).not.toHaveBeenCalled(); + }); + + it('emits afterSpanEnd', () => { + const afterSpanEndCallback = vi.fn(); + const client = getClient()!; + client.on('afterSpanEnd', afterSpanEndCallback); + + const span = startInactiveSpan({ name: 'test' }); + span.end(); + + expect(afterSpanEndCallback).toHaveBeenCalledWith(span); + }); + }); +}); From e3e9a86a3148223ed5a860f935807ce72cb6d532 Mon Sep 17 00:00:00 2001 From: Lukas Stracke Date: Tue, 10 Mar 2026 18:29:10 +0100 Subject: [PATCH 3/9] use `getDefaultIntegrations` to add/not add span streaming integration --- packages/node-core/src/light/sdk.ts | 7 ++----- packages/node-core/src/sdk/index.ts | 7 ++----- packages/node-core/test/light/sdk.test.ts | 14 ++++++++++++++ packages/node/src/sdk/index.ts | 3 ++- packages/node/test/sdk/init.test.ts | 22 ++++++++++++++++++++++ 5 files changed, 42 insertions(+), 11 deletions(-) diff --git a/packages/node-core/src/light/sdk.ts b/packages/node-core/src/light/sdk.ts index 1edacca2b07a..1d46d99dd134 100644 --- a/packages/node-core/src/light/sdk.ts +++ b/packages/node-core/src/light/sdk.ts @@ -39,7 +39,7 @@ import { nativeNodeFetchIntegration } from './integrations/nativeNodeFetchIntegr /** * Get default integrations for the Light Node-Core SDK. */ -export function getDefaultIntegrations(): Integration[] { +export function getDefaultIntegrations(options?: Options): Integration[] { return [ // Common eventFiltersIntegration(), @@ -61,6 +61,7 @@ export function getDefaultIntegrations(): Integration[] { childProcessIntegration(), processSessionIntegration(), modulesIntegration(), + ...(options?.traceLifecycle === 'stream' ? [spanStreamingIntegration()] : []), ]; } @@ -113,10 +114,6 @@ function _init( ); } - if (options.traceLifecycle === 'stream' && !options.integrations.some(({ name }) => name === 'SpanStreaming')) { - options.integrations.push(spanStreamingIntegration()); - } - applySdkMetadata(options, 'node-light', ['node-core']); const client = new LightNodeClient(options); diff --git a/packages/node-core/src/sdk/index.ts b/packages/node-core/src/sdk/index.ts index 5b495ad6bf8a..c87d58c5edac 100644 --- a/packages/node-core/src/sdk/index.ts +++ b/packages/node-core/src/sdk/index.ts @@ -47,7 +47,7 @@ import { initializeEsmLoader } from './esmLoader'; /** * Get default integrations for the Node-Core SDK. */ -export function getDefaultIntegrations(): Integration[] { +export function getDefaultIntegrations(options?: Options): Integration[] { return [ // Common // TODO(v11): Replace with `eventFiltersIntegration` once we remove the deprecated `inboundFiltersIntegration` @@ -72,6 +72,7 @@ export function getDefaultIntegrations(): Integration[] { childProcessIntegration(), processSessionIntegration(), modulesIntegration(), + ...(options?.traceLifecycle === 'stream' ? [spanStreamingIntegration()] : []), ]; } @@ -127,10 +128,6 @@ function _init( ); } - if (options.traceLifecycle === 'stream' && !options.integrations.some(({ name }) => name === 'SpanStreaming')) { - options.integrations.push(spanStreamingIntegration()); - } - applySdkMetadata(options, 'node-core'); const client = new NodeClient(options); diff --git a/packages/node-core/test/light/sdk.test.ts b/packages/node-core/test/light/sdk.test.ts index 8b0ef03700d9..522ccd1f55e6 100644 --- a/packages/node-core/test/light/sdk.test.ts +++ b/packages/node-core/test/light/sdk.test.ts @@ -106,6 +106,20 @@ describe('Light Mode | SDK', () => { expect(integrationNames).toContain('NodeFetch'); }); + + it('includes spanStreaming integration when traceLifecycle is "stream"', () => { + const integrations = Sentry.getDefaultIntegrations({ traceLifecycle: 'stream' }); + const integrationNames = integrations.map(i => i.name); + + expect(integrationNames).toContain('SpanStreaming'); + }); + + it("doesn't include spanStreaming integration when traceLifecycle is not 'stream'", () => { + const integrations = Sentry.getDefaultIntegrations(); + const integrationNames = integrations.map(i => i.name); + + expect(integrationNames).not.toContain('SpanStreaming'); + }); }); describe('isInitialized', () => { diff --git a/packages/node/src/sdk/index.ts b/packages/node/src/sdk/index.ts index 6942c6500f84..0e74edf602eb 100644 --- a/packages/node/src/sdk/index.ts +++ b/packages/node/src/sdk/index.ts @@ -1,5 +1,5 @@ import type { Integration, Options } from '@sentry/core'; -import { applySdkMetadata, hasSpansEnabled } from '@sentry/core'; +import { applySdkMetadata, hasSpansEnabled, spanStreamingIntegration } from '@sentry/core'; import type { NodeClient } from '@sentry/node-core'; import { getDefaultIntegrations as getNodeCoreDefaultIntegrations, @@ -33,6 +33,7 @@ export function getDefaultIntegrations(options: Options): Integration[] { // This means that generally request isolation will work (because that is done by httpIntegration) // But `transactionName` will not be set automatically ...(hasSpansEnabled(options) ? getAutoPerformanceIntegrations() : []), + ...(options.traceLifecycle === 'stream' ? [spanStreamingIntegration()] : []), ]; } diff --git a/packages/node/test/sdk/init.test.ts b/packages/node/test/sdk/init.test.ts index a6a76a4439bd..101a82e3b6d1 100644 --- a/packages/node/test/sdk/init.test.ts +++ b/packages/node/test/sdk/init.test.ts @@ -143,6 +143,28 @@ describe('init()', () => { }), ); }); + + it('installs spanStreaming integration when traceLifecycle is "stream"', () => { + init({ dsn: PUBLIC_DSN, traceLifecycle: 'stream' }); + const client = getClient(); + + expect(client?.getOptions()).toEqual( + expect.objectContaining({ + integrations: expect.arrayContaining([expect.objectContaining({ name: 'SpanStreaming' })]), + }), + ); + }); + + it("doesn't install spanStreaming integration when traceLifecycle is not 'stream'", () => { + init({ dsn: PUBLIC_DSN }); + + const client = getClient(); + expect(client?.getOptions()).toEqual( + expect.objectContaining({ + integrations: expect.not.arrayContaining([expect.objectContaining({ name: 'SpanStreaming' })]), + }), + ); + }); }); describe('OpenTelemetry', () => { From 17df9599f5db9e8a4ed8573d44a1fe23460cdd01 Mon Sep 17 00:00:00 2001 From: Lukas Stracke Date: Tue, 10 Mar 2026 22:57:15 +0100 Subject: [PATCH 4/9] bump size limits --- .size-limit.js | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/.size-limit.js b/.size-limit.js index 3a4689d59faa..aef521dc83f6 100644 --- a/.size-limit.js +++ b/.size-limit.js @@ -117,7 +117,7 @@ module.exports = [ path: 'packages/browser/build/npm/esm/prod/index.js', import: createImport('init', 'metrics'), gzip: true, - limit: '27 KB', + limit: '28 KB', }, { name: '@sentry/browser (incl. Logs)', @@ -220,13 +220,13 @@ module.exports = [ name: 'CDN Bundle (incl. Tracing, Replay, Feedback)', path: createCDNPath('bundle.tracing.replay.feedback.min.js'), gzip: true, - limit: '86 KB', + limit: '87 KB', }, { name: 'CDN Bundle (incl. Tracing, Replay, Feedback, Logs, Metrics)', path: createCDNPath('bundle.tracing.replay.feedback.logs.metrics.min.js'), gzip: true, - limit: '87 KB', + limit: '88 KB', }, // browser CDN bundles (non-gzipped) { @@ -317,7 +317,7 @@ module.exports = [ import: createImport('init'), ignore: [...builtinModules, ...nodePrefixedBuiltinModules], gzip: true, - limit: '53 KB', + limit: '55 KB', }, // Node SDK (ESM) { @@ -326,14 +326,14 @@ module.exports = [ import: createImport('init'), ignore: [...builtinModules, ...nodePrefixedBuiltinModules], gzip: true, - limit: '175 KB', + limit: '177 KB', }, { name: '@sentry/node - without tracing', path: 'packages/node/build/esm/index.js', import: createImport('initWithoutDefaultIntegrations', 'getDefaultIntegrationsWithoutPerformance'), gzip: true, - limit: '98 KB', + limit: '100 KB', ignore: [...builtinModules, ...nodePrefixedBuiltinModules], modifyWebpackConfig: function (config) { const webpack = require('webpack'); @@ -356,7 +356,7 @@ module.exports = [ import: createImport('init'), ignore: [...builtinModules, ...nodePrefixedBuiltinModules], gzip: true, - limit: '114 KB', + limit: '116 KB', }, ]; From e8c9f048379c38bd9b00fdbb752c165483163c1b Mon Sep 17 00:00:00 2001 From: Lukas Stracke Date: Tue, 10 Mar 2026 23:05:08 +0100 Subject: [PATCH 5/9] fix missing exports --- packages/astro/src/index.server.ts | 1 + packages/aws-serverless/src/index.ts | 1 + packages/bun/src/index.ts | 1 + packages/google-cloud-serverless/src/index.ts | 1 + packages/remix/src/server/index.ts | 1 + packages/solidstart/src/server/index.ts | 1 + packages/sveltekit/src/server/index.ts | 1 + 7 files changed, 7 insertions(+) diff --git a/packages/astro/src/index.server.ts b/packages/astro/src/index.server.ts index ac01ff0647a7..cf4c6dc6e9bb 100644 --- a/packages/astro/src/index.server.ts +++ b/packages/astro/src/index.server.ts @@ -170,6 +170,7 @@ export { statsigIntegration, unleashIntegration, growthbookIntegration, + spanStreamingIntegration, metrics, } from '@sentry/node'; diff --git a/packages/aws-serverless/src/index.ts b/packages/aws-serverless/src/index.ts index 1c980e4cae2d..b49b214b65d7 100644 --- a/packages/aws-serverless/src/index.ts +++ b/packages/aws-serverless/src/index.ts @@ -157,6 +157,7 @@ export { unleashIntegration, growthbookIntegration, metrics, + spanStreamingIntegration, } from '@sentry/node'; export { diff --git a/packages/bun/src/index.ts b/packages/bun/src/index.ts index c2990e6262a7..6f52a92ab6da 100644 --- a/packages/bun/src/index.ts +++ b/packages/bun/src/index.ts @@ -177,6 +177,7 @@ export { statsigIntegration, unleashIntegration, metrics, + spanStreamingIntegration, } from '@sentry/node'; export { diff --git a/packages/google-cloud-serverless/src/index.ts b/packages/google-cloud-serverless/src/index.ts index 9478b98f5a58..a881f911e31c 100644 --- a/packages/google-cloud-serverless/src/index.ts +++ b/packages/google-cloud-serverless/src/index.ts @@ -157,6 +157,7 @@ export { statsigIntegration, unleashIntegration, metrics, + spanStreamingIntegration, } from '@sentry/node'; export { diff --git a/packages/remix/src/server/index.ts b/packages/remix/src/server/index.ts index 1533a1ca7221..eccafe08a637 100644 --- a/packages/remix/src/server/index.ts +++ b/packages/remix/src/server/index.ts @@ -131,6 +131,7 @@ export { consoleLoggingIntegration, createConsolaReporter, createSentryWinstonTransport, + spanStreamingIntegration, } from '@sentry/node'; // Keeping the `*` exports for backwards compatibility and types diff --git a/packages/solidstart/src/server/index.ts b/packages/solidstart/src/server/index.ts index 6e2bc1cb9f61..ffc73956a530 100644 --- a/packages/solidstart/src/server/index.ts +++ b/packages/solidstart/src/server/index.ts @@ -130,6 +130,7 @@ export { consoleLoggingIntegration, createConsolaReporter, createSentryWinstonTransport, + spanStreamingIntegration, } from '@sentry/node'; // We can still leave this for the carrier init and type exports diff --git a/packages/sveltekit/src/server/index.ts b/packages/sveltekit/src/server/index.ts index d42975ef7876..8db039bb4369 100644 --- a/packages/sveltekit/src/server/index.ts +++ b/packages/sveltekit/src/server/index.ts @@ -135,6 +135,7 @@ export { createSentryWinstonTransport, vercelAIIntegration, metrics, + spanStreamingIntegration, } from '@sentry/node'; // We can still leave this for the carrier init and type exports From 39783482e16801400d8d5ebc8268bb6d8f28c1df Mon Sep 17 00:00:00 2001 From: Lukas Stracke Date: Tue, 10 Mar 2026 23:43:25 +0100 Subject: [PATCH 6/9] fix unused import --- packages/node-core/src/sdk/client.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/packages/node-core/src/sdk/client.ts b/packages/node-core/src/sdk/client.ts index 211610d6491c..80a233aa3954 100644 --- a/packages/node-core/src/sdk/client.ts +++ b/packages/node-core/src/sdk/client.ts @@ -9,7 +9,6 @@ import { _INTERNAL_flushLogsBuffer, applySdkMetadata, debug, - hasSpanStreamingEnabled, SDK_VERSION, ServerRuntimeClient, } from '@sentry/core'; From f280ff3b4b3c36079c53244fa9b51f60fd1e1162 Mon Sep 17 00:00:00 2001 From: Lukas Stracke Date: Wed, 11 Mar 2026 15:11:12 +0100 Subject: [PATCH 7/9] fix defaultIntegrations --- packages/node/src/sdk/index.ts | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/packages/node/src/sdk/index.ts b/packages/node/src/sdk/index.ts index 0e74edf602eb..6773b4f98f52 100644 --- a/packages/node/src/sdk/index.ts +++ b/packages/node/src/sdk/index.ts @@ -1,5 +1,5 @@ import type { Integration, Options } from '@sentry/core'; -import { applySdkMetadata, hasSpansEnabled, spanStreamingIntegration } from '@sentry/core'; +import { applySdkMetadata, hasSpansEnabled } from '@sentry/core'; import type { NodeClient } from '@sentry/node-core'; import { getDefaultIntegrations as getNodeCoreDefaultIntegrations, @@ -15,8 +15,8 @@ import { initOpenTelemetry } from './initOtel'; /** * Get default integrations, excluding performance. */ -export function getDefaultIntegrationsWithoutPerformance(): Integration[] { - const nodeCoreIntegrations = getNodeCoreDefaultIntegrations(); +export function getDefaultIntegrationsWithoutPerformance(options: Options): Integration[] { + const nodeCoreIntegrations = getNodeCoreDefaultIntegrations(options); // Filter out the node-core HTTP and NodeFetch integrations and replace them with Node SDK's composite versions return nodeCoreIntegrations @@ -27,13 +27,12 @@ export function getDefaultIntegrationsWithoutPerformance(): Integration[] { /** Get the default integrations for the Node SDK. */ export function getDefaultIntegrations(options: Options): Integration[] { return [ - ...getDefaultIntegrationsWithoutPerformance(), + ...getDefaultIntegrationsWithoutPerformance(options), // We only add performance integrations if tracing is enabled // Note that this means that without tracing enabled, e.g. `expressIntegration()` will not be added // This means that generally request isolation will work (because that is done by httpIntegration) // But `transactionName` will not be set automatically ...(hasSpansEnabled(options) ? getAutoPerformanceIntegrations() : []), - ...(options.traceLifecycle === 'stream' ? [spanStreamingIntegration()] : []), ]; } From 85a792c870db4c08d7fb12c4b0a5b11118610a92 Mon Sep 17 00:00:00 2001 From: Lukas Stracke Date: Wed, 11 Mar 2026 18:10:04 +0100 Subject: [PATCH 8/9] fix aws and gcp integration --- packages/aws-serverless/src/init.ts | 4 ++-- packages/google-cloud-serverless/src/sdk.ts | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/packages/aws-serverless/src/init.ts b/packages/aws-serverless/src/init.ts index 25180a41f6e6..77aa47989fc2 100644 --- a/packages/aws-serverless/src/init.ts +++ b/packages/aws-serverless/src/init.ts @@ -49,8 +49,8 @@ function shouldDisableLayerExtensionForProxy(): boolean { */ // NOTE: in awslambda-auto.ts, we also call the original `getDefaultIntegrations` from `@sentry/node` to load performance integrations. // If at some point we need to filter a node integration out for good, we need to make sure to also filter it out there. -export function getDefaultIntegrations(_options: Options): Integration[] { - return [...getDefaultIntegrationsWithoutPerformance(), awsIntegration(), awsLambdaIntegration()]; +export function getDefaultIntegrations(options: Options): Integration[] { + return [...getDefaultIntegrationsWithoutPerformance(options), awsIntegration(), awsLambdaIntegration()]; } export interface AwsServerlessOptions extends NodeOptions { diff --git a/packages/google-cloud-serverless/src/sdk.ts b/packages/google-cloud-serverless/src/sdk.ts index 1161ab60300e..7e6a56812aaf 100644 --- a/packages/google-cloud-serverless/src/sdk.ts +++ b/packages/google-cloud-serverless/src/sdk.ts @@ -16,8 +16,8 @@ function getCjsOnlyIntegrations(): Integration[] { } /** Get the default integrations for the GCP SDK. */ -export function getDefaultIntegrations(_options: Options): Integration[] { - return [...getDefaultIntegrationsWithoutPerformance(), ...getCjsOnlyIntegrations()]; +export function getDefaultIntegrations(options: Options): Integration[] { + return [...getDefaultIntegrationsWithoutPerformance(options), ...getCjsOnlyIntegrations()]; } /** From 4d16cfed2bbac695d2ed3677f7b73e7add07dc1a Mon Sep 17 00:00:00 2001 From: Lukas Stracke Date: Wed, 11 Mar 2026 18:10:12 +0100 Subject: [PATCH 9/9] always set fallback traceLifecycle to 'static' --- .../browser/src/integrations/spanstreaming.ts | 6 ++- .../test/integrations/spanstreaming.test.ts | 42 ++++++++++--------- .../core/src/integrations/spanStreaming.ts | 6 ++- .../test/integrations/spanStreaming.test.ts | 42 ++++++++++--------- packages/node/src/sdk/index.ts | 2 +- 5 files changed, 55 insertions(+), 43 deletions(-) diff --git a/packages/browser/src/integrations/spanstreaming.ts b/packages/browser/src/integrations/spanstreaming.ts index ab07f75c2b7d..9d1a296b5e57 100644 --- a/packages/browser/src/integrations/spanstreaming.ts +++ b/packages/browser/src/integrations/spanstreaming.ts @@ -27,17 +27,19 @@ export const spanStreamingIntegration = defineIntegration(() => { setup(client) { const initialMessage = 'SpanStreaming integration requires'; const fallbackMsg = 'Falling back to static trace lifecycle.'; + const clientOptions = client.getOptions(); if (!hasSpanStreamingEnabled(client)) { + clientOptions.traceLifecycle = 'static'; DEBUG_BUILD && debug.warn(`${initialMessage} \`traceLifecycle\` to be set to "stream"! ${fallbackMsg}`); return; } - const beforeSendSpan = client.getOptions().beforeSendSpan; + const beforeSendSpan = clientOptions.beforeSendSpan; // If users misconfigure their SDK by opting into span streaming but // using an incompatible beforeSendSpan callback, we fall back to the static trace lifecycle. if (beforeSendSpan && !isStreamedBeforeSendSpanCallback(beforeSendSpan)) { - client.getOptions().traceLifecycle = 'static'; + clientOptions.traceLifecycle = 'static'; DEBUG_BUILD && debug.warn(`${initialMessage} a beforeSendSpan callback using \`withStreamedSpan\`! ${fallbackMsg}`); return; diff --git a/packages/browser/test/integrations/spanstreaming.test.ts b/packages/browser/test/integrations/spanstreaming.test.ts index 5b84c52f8d7e..1d5d587290a3 100644 --- a/packages/browser/test/integrations/spanstreaming.test.ts +++ b/packages/browser/test/integrations/spanstreaming.test.ts @@ -50,25 +50,29 @@ describe('spanStreamingIntegration', () => { expect(client.getOptions().traceLifecycle).toBe('stream'); }); - it('logs a warning if traceLifecycle is not set to "stream"', () => { - const debugSpy = vi.spyOn(debug, 'warn').mockImplementation(() => {}); - const client = new BrowserClient({ - ...getDefaultBrowserClientOptions(), - dsn: 'https://username@domain/123', - integrations: [spanStreamingIntegration()], - traceLifecycle: 'static', - }); - - SentryCore.setCurrentClient(client); - client.init(); - - expect(debugSpy).toHaveBeenCalledWith( - 'SpanStreaming integration requires `traceLifecycle` to be set to "stream"! Falling back to static trace lifecycle.', - ); - debugSpy.mockRestore(); - - expect(client.getOptions().traceLifecycle).toBe('static'); - }); + it.each(['static', 'somethingElse'])( + 'logs a warning if traceLifecycle is not set to "stream" but to %s', + traceLifecycle => { + const debugSpy = vi.spyOn(debug, 'warn').mockImplementation(() => {}); + const client = new BrowserClient({ + ...getDefaultBrowserClientOptions(), + dsn: 'https://username@domain/123', + integrations: [spanStreamingIntegration()], + // @ts-expect-error - we want to test the warning for invalid traceLifecycle values + traceLifecycle, + }); + + SentryCore.setCurrentClient(client); + client.init(); + + expect(debugSpy).toHaveBeenCalledWith( + 'SpanStreaming integration requires `traceLifecycle` to be set to "stream"! Falling back to static trace lifecycle.', + ); + debugSpy.mockRestore(); + + expect(client.getOptions().traceLifecycle).toBe('static'); + }, + ); it('falls back to static trace lifecycle if beforeSendSpan is not compatible with span streaming', () => { const debugSpy = vi.spyOn(debug, 'warn').mockImplementation(() => {}); diff --git a/packages/core/src/integrations/spanStreaming.ts b/packages/core/src/integrations/spanStreaming.ts index 069382929b2f..541be6b7f5e9 100644 --- a/packages/core/src/integrations/spanStreaming.ts +++ b/packages/core/src/integrations/spanStreaming.ts @@ -15,15 +15,17 @@ export const spanStreamingIntegration = defineIntegration(() => { setup(client) { const initialMessage = 'SpanStreaming integration requires'; const fallbackMsg = 'Falling back to static trace lifecycle.'; + const clientOptions = client.getOptions(); if (!hasSpanStreamingEnabled(client)) { + clientOptions.traceLifecycle = 'static'; DEBUG_BUILD && debug.warn(`${initialMessage} \`traceLifecycle\` to be set to "stream"! ${fallbackMsg}`); return; } - const beforeSendSpan = client.getOptions().beforeSendSpan; + const beforeSendSpan = clientOptions.beforeSendSpan; if (beforeSendSpan && !isStreamedBeforeSendSpanCallback(beforeSendSpan)) { - client.getOptions().traceLifecycle = 'static'; + clientOptions.traceLifecycle = 'static'; DEBUG_BUILD && debug.warn(`${initialMessage} a beforeSendSpan callback using \`withStreamedSpan\`! ${fallbackMsg}`); return; diff --git a/packages/core/test/integrations/spanStreaming.test.ts b/packages/core/test/integrations/spanStreaming.test.ts index a7b45184145a..8b2badf575ff 100644 --- a/packages/core/test/integrations/spanStreaming.test.ts +++ b/packages/core/test/integrations/spanStreaming.test.ts @@ -34,25 +34,29 @@ describe('spanStreamingIntegration (core)', () => { expect(integration.setup).toBeDefined(); }); - it('logs a warning if traceLifecycle is not set to "stream"', () => { - const debugSpy = vi.spyOn(debug, 'warn').mockImplementation(() => {}); - const client = new TestClient({ - ...getDefaultTestClientOptions(), - dsn: 'https://username@domain/123', - integrations: [spanStreamingIntegration()], - traceLifecycle: 'static', - }); - - SentryCore.setCurrentClient(client); - client.init(); - - expect(debugSpy).toHaveBeenCalledWith( - 'SpanStreaming integration requires `traceLifecycle` to be set to "stream"! Falling back to static trace lifecycle.', - ); - debugSpy.mockRestore(); - - expect(client.getOptions().traceLifecycle).toBe('static'); - }); + it.each(['static', 'somethingElse'])( + 'logs a warning if traceLifecycle is not set to "stream" but to %s', + traceLifecycle => { + const debugSpy = vi.spyOn(debug, 'warn').mockImplementation(() => {}); + const client = new TestClient({ + ...getDefaultTestClientOptions(), + dsn: 'https://username@domain/123', + integrations: [spanStreamingIntegration()], + // @ts-expect-error - we want to test the warning for invalid traceLifecycle values + traceLifecycle, + }); + + SentryCore.setCurrentClient(client); + client.init(); + + expect(debugSpy).toHaveBeenCalledWith( + 'SpanStreaming integration requires `traceLifecycle` to be set to "stream"! Falling back to static trace lifecycle.', + ); + debugSpy.mockRestore(); + + expect(client.getOptions().traceLifecycle).toBe('static'); + }, + ); it('falls back to static trace lifecycle if beforeSendSpan is not compatible with span streaming', () => { const debugSpy = vi.spyOn(debug, 'warn').mockImplementation(() => {}); diff --git a/packages/node/src/sdk/index.ts b/packages/node/src/sdk/index.ts index 6773b4f98f52..702606e530ec 100644 --- a/packages/node/src/sdk/index.ts +++ b/packages/node/src/sdk/index.ts @@ -15,7 +15,7 @@ import { initOpenTelemetry } from './initOtel'; /** * Get default integrations, excluding performance. */ -export function getDefaultIntegrationsWithoutPerformance(options: Options): Integration[] { +export function getDefaultIntegrationsWithoutPerformance(options?: Options | undefined): Integration[] { const nodeCoreIntegrations = getNodeCoreDefaultIntegrations(options); // Filter out the node-core HTTP and NodeFetch integrations and replace them with Node SDK's composite versions