From 344055e8fba65a3986d4c2a309b5f3f02a69daab Mon Sep 17 00:00:00 2001 From: Lukas Stracke Date: Fri, 24 Jul 2026 18:32:38 +0200 Subject: [PATCH 1/2] feat(core)!: Stream beforeSendSpan payloads by default Make streamed span JSON the default beforeSendSpan contract and provide withStaticSpan for callbacks that still process transaction span JSON. Deprecate withStreamedSpan for removal in version 12. BREAKING CHANGE: beforeSendSpan receives StreamedSpanJSON by default. Fixes #22349 Co-Authored-By: Cursor Co-authored-by: Cursor --- .../beforeSendSpan-streamed/init.js | 6 +-- .../beforeSendSpan-streamed/scenario.ts | 5 +- .../rollup-utils/plugins/bundlePlugins.mjs | 3 +- packages/astro/src/index.server.ts | 2 + packages/astro/src/index.types.ts | 2 + packages/aws-serverless/src/index.ts | 2 + packages/browser/src/exports.ts | 2 + .../browser/src/integrations/spanstreaming.ts | 10 ++-- .../test/integrations/spanstreaming.test.ts | 6 +-- packages/bun/src/index.ts | 2 + packages/cloudflare/src/index.ts | 2 + packages/core/src/client.ts | 4 +- .../core/src/integrations/spanStreaming.ts | 8 +-- packages/core/src/shared-exports.ts | 8 ++- .../core/src/tracing/spans/beforeSendSpan.ts | 53 +++++++++++++------ packages/core/src/types/options.ts | 21 ++++++-- .../test/integrations/spanStreaming.test.ts | 6 +-- packages/core/test/lib/client.test.ts | 33 +++++++----- .../core/test/lib/tracing/sentrySpan.test.ts | 11 ++-- .../lib/tracing/spans/beforeSendSpan.test.ts | 46 ++++++++++++---- .../lib/tracing/spans/captureSpan.test.ts | 13 +++-- packages/deno/src/index.ts | 2 + packages/effect/src/index.types.ts | 2 + packages/elysia/src/index.ts | 2 + packages/google-cloud-serverless/src/index.ts | 2 + packages/nextjs/src/index.types.ts | 2 + packages/node/src/index.ts | 3 +- packages/nuxt/src/index.types.ts | 2 + packages/react-router/src/index.types.ts | 2 + packages/remix/src/cloudflare/index.ts | 2 + packages/remix/src/index.types.ts | 2 + packages/remix/src/server/index.ts | 2 + packages/solidstart/src/index.types.ts | 2 + packages/solidstart/src/server/index.ts | 2 + packages/sveltekit/src/index.types.ts | 2 + packages/sveltekit/src/server/index.ts | 2 + packages/sveltekit/src/worker/index.ts | 2 + .../tanstackstart-react/src/index.types.ts | 2 + packages/vercel-edge/src/index.ts | 2 + 39 files changed, 199 insertions(+), 83 deletions(-) diff --git a/dev-packages/browser-integration-tests/suites/public-api/beforeSendSpan-streamed/init.js b/dev-packages/browser-integration-tests/suites/public-api/beforeSendSpan-streamed/init.js index f099c1f61c3e..a3fae7e92b48 100644 --- a/dev-packages/browser-integration-tests/suites/public-api/beforeSendSpan-streamed/init.js +++ b/dev-packages/browser-integration-tests/suites/public-api/beforeSendSpan-streamed/init.js @@ -4,9 +4,9 @@ window.Sentry = Sentry; Sentry.init({ dsn: 'https://public@dsn.ingest.sentry.io/1337', - integrations: [Sentry.browserTracingIntegration(), Sentry.spanStreamingIntegration()], + integrations: [Sentry.browserTracingIntegration()], tracesSampleRate: 1, - beforeSendSpan: Sentry.withStreamedSpan(span => { + beforeSendSpan: span => { if (span.attributes['sentry.op'] === 'pageload') { span.name = 'customPageloadSpanName'; span.links = [ @@ -24,5 +24,5 @@ Sentry.init({ span.status = 'something'; } return span; - }), + }, }); diff --git a/dev-packages/node-integration-tests/suites/public-api/beforeSendSpan-streamed/scenario.ts b/dev-packages/node-integration-tests/suites/public-api/beforeSendSpan-streamed/scenario.ts index 3c7c0ed81edf..3e885e4170f9 100644 --- a/dev-packages/node-integration-tests/suites/public-api/beforeSendSpan-streamed/scenario.ts +++ b/dev-packages/node-integration-tests/suites/public-api/beforeSendSpan-streamed/scenario.ts @@ -4,10 +4,9 @@ import { loggingTransport } from '@sentry-internal/node-integration-tests'; Sentry.init({ dsn: 'https://public@dsn.ingest.sentry.io/1337', tracesSampleRate: 1.0, - traceLifecycle: 'stream', transport: loggingTransport, release: '1.0.0', - beforeSendSpan: Sentry.withStreamedSpan(span => { + beforeSendSpan: span => { if (span.name === 'test-child-span') { span.name = 'customChildSpanName'; if (!span.attributes) { @@ -27,7 +26,7 @@ Sentry.init({ ]; } return span; - }), + }, }); Sentry.startSpan({ name: 'test-span', op: 'test' }, () => { diff --git a/dev-packages/rollup-utils/plugins/bundlePlugins.mjs b/dev-packages/rollup-utils/plugins/bundlePlugins.mjs index 56712e33a458..b1836e17127f 100644 --- a/dev-packages/rollup-utils/plugins/bundlePlugins.mjs +++ b/dev-packages/rollup-utils/plugins/bundlePlugins.mjs @@ -150,7 +150,8 @@ export function makeTerserPlugin() { '_resolveFilename', // Set on e.g. the shim feedbackIntegration to be able to detect it '_isShim', - // Marker set by `withStreamedSpan()` to tag streamed `beforeSendSpan` callbacks + // Markers used to distinguish static and explicitly streamed `beforeSendSpan` callbacks + '_static', '_streamed', // This is used in metadata integration '_sentryModuleMetadata', diff --git a/packages/astro/src/index.server.ts b/packages/astro/src/index.server.ts index cb157ec9007e..b12070bdf9ec 100644 --- a/packages/astro/src/index.server.ts +++ b/packages/astro/src/index.server.ts @@ -173,6 +173,8 @@ export { unleashIntegration, growthbookIntegration, spanStreamingIntegration, + withStaticSpan, + // eslint-disable-next-line typescript/no-deprecated withStreamedSpan, metrics, } from '@sentry/node'; diff --git a/packages/astro/src/index.types.ts b/packages/astro/src/index.types.ts index 0c8a5cca7bb6..b3f5037f8751 100644 --- a/packages/astro/src/index.types.ts +++ b/packages/astro/src/index.types.ts @@ -21,6 +21,8 @@ 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 withStaticSpan: typeof clientSdk.withStaticSpan; +// eslint-disable-next-line typescript/no-deprecated export declare const withStreamedSpan: typeof clientSdk.withStreamedSpan; export declare const getDefaultIntegrations: (options: Options) => Integration[]; diff --git a/packages/aws-serverless/src/index.ts b/packages/aws-serverless/src/index.ts index 30f597757b40..8870c7ca67d0 100644 --- a/packages/aws-serverless/src/index.ts +++ b/packages/aws-serverless/src/index.ts @@ -160,6 +160,8 @@ export { growthbookIntegration, metrics, spanStreamingIntegration, + withStaticSpan, + // eslint-disable-next-line typescript/no-deprecated withStreamedSpan, } from '@sentry/node'; diff --git a/packages/browser/src/exports.ts b/packages/browser/src/exports.ts index d1e9d7662db3..e3a7e5da2850 100644 --- a/packages/browser/src/exports.ts +++ b/packages/browser/src/exports.ts @@ -72,6 +72,8 @@ export { spanToTraceHeader, spanToBaggageHeader, updateSpanName, + withStaticSpan, + // eslint-disable-next-line typescript/no-deprecated withStreamedSpan, metrics, } from '@sentry/core/browser'; diff --git a/packages/browser/src/integrations/spanstreaming.ts b/packages/browser/src/integrations/spanstreaming.ts index e8ed1a0de537..617d1a700f5d 100644 --- a/packages/browser/src/integrations/spanstreaming.ts +++ b/packages/browser/src/integrations/spanstreaming.ts @@ -4,7 +4,7 @@ import { debug, defineIntegration, hasSpanStreamingEnabled, - isStreamedBeforeSendSpanCallback, + isStaticBeforeSendSpanCallback, SpanBuffer, spanIsSampled, } from '@sentry/core/browser'; @@ -26,12 +26,12 @@ export const spanStreamingIntegration = defineIntegration(() => { } 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)) { + if (isStaticBeforeSendSpanCallback(beforeSendSpan)) { clientOptions.traceLifecycle = 'static'; DEBUG_BUILD && - debug.warn(`${initialMessage} a beforeSendSpan callback using \`withStreamedSpan\`! ${fallbackMsg}`); + debug.warn( + `SpanStreaming integration is incompatible with a beforeSendSpan callback using \`withStaticSpan\`! ${fallbackMsg}`, + ); return; } diff --git a/packages/browser/test/integrations/spanstreaming.test.ts b/packages/browser/test/integrations/spanstreaming.test.ts index db4209a823e8..b1c9705a1871 100644 --- a/packages/browser/test/integrations/spanstreaming.test.ts +++ b/packages/browser/test/integrations/spanstreaming.test.ts @@ -64,21 +64,21 @@ describe('spanStreamingIntegration', () => { }, ); - it('falls back to static trace lifecycle if beforeSendSpan is not compatible with span streaming', () => { + it('falls back to static trace lifecycle if beforeSendSpan is marked as static', () => { const debugSpy = vi.spyOn(debug, 'warn').mockImplementation(() => {}); const client = new BrowserClient({ ...getDefaultBrowserClientOptions(), dsn: 'https://username@domain/123', integrations: [spanStreamingIntegration()], traceLifecycle: 'stream', - beforeSendSpan: (span: Span) => span, + beforeSendSpan: SentryCore.withStaticSpan(span => span), }); SentryCore.setCurrentClient(client); client.init(); expect(debugSpy).toHaveBeenCalledWith( - 'SpanStreaming integration requires a beforeSendSpan callback using `withStreamedSpan`! Falling back to static trace lifecycle.', + 'SpanStreaming integration is incompatible with a beforeSendSpan callback using `withStaticSpan`! Falling back to static trace lifecycle.', ); debugSpy.mockRestore(); diff --git a/packages/bun/src/index.ts b/packages/bun/src/index.ts index 4a6c25eed27d..beb8a365839c 100644 --- a/packages/bun/src/index.ts +++ b/packages/bun/src/index.ts @@ -178,6 +178,8 @@ export { unleashIntegration, metrics, spanStreamingIntegration, + withStaticSpan, + // eslint-disable-next-line typescript/no-deprecated withStreamedSpan, } from '@sentry/node'; diff --git a/packages/cloudflare/src/index.ts b/packages/cloudflare/src/index.ts index d1f787345503..a07fdb43322b 100644 --- a/packages/cloudflare/src/index.ts +++ b/packages/cloudflare/src/index.ts @@ -110,6 +110,8 @@ export { growthbookIntegration, logger, metrics, + withStaticSpan, + // eslint-disable-next-line typescript/no-deprecated withStreamedSpan, spanStreamingIntegration, instrumentStateGraph, diff --git a/packages/core/src/client.ts b/packages/core/src/client.ts index 6a11959e9983..4d8909032acf 100644 --- a/packages/core/src/client.ts +++ b/packages/core/src/client.ts @@ -11,7 +11,7 @@ import { _INTERNAL_flushMetricsBuffer } from './metrics/internal'; import type { Scope } from './scope'; import { updateSession } from './session'; import { getDynamicSamplingContextFromScope } from './tracing/dynamicSamplingContext'; -import { isStreamedBeforeSendSpanCallback } from './tracing/spans/beforeSendSpan'; +import { isStaticBeforeSendSpanCallback } from './tracing/spans/beforeSendSpan'; import { extractGenAiSpansFromEvent } from './tracing/spans/extractGenAiSpans'; import { DEFAULT_TRANSPORT_BUFFER_SIZE } from './transports/base'; import type { Breadcrumb, BreadcrumbHint, FetchBreadcrumbHint, XhrBreadcrumbHint } from './types/breadcrumb'; @@ -1654,7 +1654,7 @@ function processBeforeSend( hint: EventHint, ): PromiseLike | Event | null { const { beforeSend, beforeSendTransaction, ignoreSpans } = options; - const beforeSendSpan = !isStreamedBeforeSendSpanCallback(options.beforeSendSpan) && options.beforeSendSpan; + const beforeSendSpan = isStaticBeforeSendSpanCallback(options.beforeSendSpan) && options.beforeSendSpan; let processedEvent = event; diff --git a/packages/core/src/integrations/spanStreaming.ts b/packages/core/src/integrations/spanStreaming.ts index ffd5d0bb7694..36bc2ff55c2d 100644 --- a/packages/core/src/integrations/spanStreaming.ts +++ b/packages/core/src/integrations/spanStreaming.ts @@ -1,7 +1,7 @@ import type { IntegrationFn } from '../types/integration'; import { DEBUG_BUILD } from '../debug-build'; import { defineIntegration } from '../integration'; -import { isStreamedBeforeSendSpanCallback } from '../tracing/spans/beforeSendSpan'; +import { isStaticBeforeSendSpanCallback } from '../tracing/spans/beforeSendSpan'; import { captureSpan } from '../tracing/spans/captureSpan'; import { hasSpanStreamingEnabled } from '../tracing/spans/hasSpanStreamingEnabled'; import { SpanBuffer } from '../tracing/spans/spanBuffer'; @@ -24,10 +24,12 @@ export const spanStreamingIntegration = defineIntegration(() => { } const beforeSendSpan = clientOptions.beforeSendSpan; - if (beforeSendSpan && !isStreamedBeforeSendSpanCallback(beforeSendSpan)) { + if (isStaticBeforeSendSpanCallback(beforeSendSpan)) { clientOptions.traceLifecycle = 'static'; DEBUG_BUILD && - debug.warn(`${initialMessage} a beforeSendSpan callback using \`withStreamedSpan\`! ${fallbackMsg}`); + debug.warn( + `SpanStreaming integration is incompatible with a beforeSendSpan callback using \`withStaticSpan\`! ${fallbackMsg}`, + ); return; } diff --git a/packages/core/src/shared-exports.ts b/packages/core/src/shared-exports.ts index 700e65d2a974..a36e15105ec6 100644 --- a/packages/core/src/shared-exports.ts +++ b/packages/core/src/shared-exports.ts @@ -86,8 +86,12 @@ export { prepareEvent } from './utils/prepareEvent'; export type { ExclusiveEventHintOrCaptureContext } from './utils/prepareEvent'; export { createCheckInEnvelope } from './checkin'; export { hasSpansEnabled } from './utils/hasSpansEnabled'; -export { withStreamedSpan } from './tracing/spans/beforeSendSpan'; -export { isStreamedBeforeSendSpanCallback } from './tracing/spans/beforeSendSpan'; +export { + withStaticSpan, + // eslint-disable-next-line typescript/no-deprecated + withStreamedSpan, +} from './tracing/spans/beforeSendSpan'; +export { isStaticBeforeSendSpanCallback, isStreamedBeforeSendSpanCallback } from './tracing/spans/beforeSendSpan'; export { safeSetSpanJSONAttributes } from './tracing/spans/captureSpan'; export { isSentryRequestUrl } from './utils/isSentryRequestUrl'; export { handleCallbackErrors } from './utils/handleCallbackErrors'; diff --git a/packages/core/src/tracing/spans/beforeSendSpan.ts b/packages/core/src/tracing/spans/beforeSendSpan.ts index 57797e5daead..3306a594c94f 100644 --- a/packages/core/src/tracing/spans/beforeSendSpan.ts +++ b/packages/core/src/tracing/spans/beforeSendSpan.ts @@ -1,42 +1,63 @@ -import type { CoreOptions } from '../../types/options'; -import type { BeforeSendStreamedSpanCallback } from '../../types/options'; -import type { StreamedSpanJSON } from '../../types/span'; +import type { BeforeSendStaticSpanCallback, BeforeSendStreamedSpanCallback } from '../../types/options'; +import type { SpanJSON, StreamedSpanJSON } from '../../types/span'; import { addNonEnumerableProperty } from '../../utils/object'; -type StaticBeforeSendSpanCallback = CoreOptions['beforeSendSpan']; - /** - * A wrapper to use the new span format in your `beforeSendSpan` callback. + * A wrapper to use the legacy transaction span format in your `beforeSendSpan` callback. * - * When using `traceLifecycle: 'stream'`, wrap your callback with this function - * to receive and return {@link StreamedSpanJSON} instead of the standard {@link SpanJSON}. + * When using `traceLifecycle: 'static'`, wrap your callback with this function + * to receive and return {@link SpanJSON} instead of {@link StreamedSpanJSON}. * * @example * * Sentry.init({ - * traceLifecycle: 'stream', - * beforeSendSpan: withStreamedSpan((span) => { - * // span is of type StreamedSpanJSON + * traceLifecycle: 'static', + * beforeSendSpan: withStaticSpan((span) => { + * // span is of type SpanJSON * return span; * }), * }); * + * @param callback - The callback function that receives and returns a {@link SpanJSON}. + * @returns A callback that is compatible with the `beforeSendSpan` option when using `traceLifecycle: 'static'`. + */ +export function withStaticSpan(callback: (span: SpanJSON) => SpanJSON): BeforeSendStaticSpanCallback { + addNonEnumerableProperty(callback, '_static', true); + return callback as BeforeSendStaticSpanCallback; +} + +/** + * A wrapper to explicitly use the streamed span format in your `beforeSendSpan` callback. + * + * @deprecated `beforeSendSpan` callbacks receive {@link StreamedSpanJSON} by default. + * This function will be removed in SDK version 12. + * * @param callback - The callback function that receives and returns a {@link StreamedSpanJSON}. - * @returns A callback that is compatible with the `beforeSendSpan` option when using `traceLifecycle: 'stream'`. + * @returns The provided callback. */ export function withStreamedSpan( callback: (span: StreamedSpanJSON) => StreamedSpanJSON, -): StaticBeforeSendSpanCallback & { _streamed: true } { +): BeforeSendStreamedSpanCallback & { _streamed: true } { addNonEnumerableProperty(callback, '_streamed', true); - return callback as unknown as StaticBeforeSendSpanCallback & { _streamed: true }; + return callback as BeforeSendStreamedSpanCallback & { _streamed: true }; +} + +/** + * Typesafe check to identify if a `beforeSendSpan` callback expects the static span JSON format. + * + * @param callback - The `beforeSendSpan` callback to check. + * @returns `true` if the callback was wrapped with {@link withStaticSpan}. + */ +export function isStaticBeforeSendSpanCallback(callback: unknown): callback is BeforeSendStaticSpanCallback { + return !!callback && typeof callback === 'function' && '_static' in callback && !!callback._static; } /** * Typesafe check to identify if a `beforeSendSpan` callback expects the streamed span JSON format. * * @param callback - The `beforeSendSpan` callback to check. - * @returns `true` if the callback was wrapped with {@link withStreamedSpan}. + * @returns `true` unless the callback was wrapped with {@link withStaticSpan}. */ export function isStreamedBeforeSendSpanCallback(callback: unknown): callback is BeforeSendStreamedSpanCallback { - return !!callback && typeof callback === 'function' && '_streamed' in callback && !!callback._streamed; + return !!callback && typeof callback === 'function' && !isStaticBeforeSendSpanCallback(callback); } diff --git a/packages/core/src/types/options.ts b/packages/core/src/types/options.ts index 4aa93e6ac12e..a1b67accc107 100644 --- a/packages/core/src/types/options.ts +++ b/packages/core/src/types/options.ts @@ -652,14 +652,14 @@ export interface ClientOptions SpanJSON) & { _streamed?: true }; + beforeSendSpan?: BeforeSendStreamedSpanCallback | BeforeSendStaticSpanCallbackMarker; /** * An event-processing callback for transaction events, guaranteed to be invoked after all other event @@ -698,12 +698,23 @@ export interface ClientOptions StreamedSpanJSON) & { /** - * When true, indicates this callback is designed to handle the {@link StreamedSpanJSON} format - * used with `traceLifecycle: 'stream'`. Set this by wrapping your callback with `withStreamedSpan`. + * @deprecated `beforeSendSpan` callbacks use {@link StreamedSpanJSON} by default. */ _streamed?: true; }; +declare const beforeSendStaticSpanCallback: unique symbol; + +type BeforeSendStaticSpanCallbackMarker = { + readonly [beforeSendStaticSpanCallback]: true; +}; + +/** A callback for processing spans sent as part of transaction events. */ +export type BeforeSendStaticSpanCallback = ((span: SpanJSON) => SpanJSON) & + BeforeSendStaticSpanCallbackMarker & { + _static: true; + }; + /** Base configuration options for every SDK. */ export interface CoreOptions extends Omit< Partial>, diff --git a/packages/core/test/integrations/spanStreaming.test.ts b/packages/core/test/integrations/spanStreaming.test.ts index 8b2badf575ff..a2cf52f4deed 100644 --- a/packages/core/test/integrations/spanStreaming.test.ts +++ b/packages/core/test/integrations/spanStreaming.test.ts @@ -58,21 +58,21 @@ describe('spanStreamingIntegration (core)', () => { }, ); - it('falls back to static trace lifecycle if beforeSendSpan is not compatible with span streaming', () => { + it('falls back to static trace lifecycle if beforeSendSpan is marked as static', () => { 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, + beforeSendSpan: SentryCore.withStaticSpan(span => span), }); SentryCore.setCurrentClient(client); client.init(); expect(debugSpy).toHaveBeenCalledWith( - 'SpanStreaming integration requires a beforeSendSpan callback using `withStreamedSpan`! Falling back to static trace lifecycle.', + 'SpanStreaming integration is incompatible with a beforeSendSpan callback using `withStaticSpan`! Falling back to static trace lifecycle.', ); debugSpy.mockRestore(); diff --git a/packages/core/test/lib/client.test.ts b/packages/core/test/lib/client.test.ts index 99112f0e35ef..240502ae5842 100644 --- a/packages/core/test/lib/client.test.ts +++ b/packages/core/test/lib/client.test.ts @@ -11,6 +11,7 @@ import { setCurrentClient, SyncPromise, withMonitor, + withStaticSpan, } from '../../src'; import * as integrationModule from '../../src/integration'; import * as logsInternalModule from '../../src/logs/internal'; @@ -1065,7 +1066,7 @@ describe('Client', () => { test('calls `beforeSendSpan` and uses original spans without any changes', () => { expect.assertions(3); - const beforeSendSpan = vi.fn(span => span); + const beforeSendSpan = withStaticSpan(vi.fn(span => span)); const options = getDefaultTestClientOptions({ dsn: PUBLIC_DSN, beforeSendSpan }); const client = new TestClient(options); @@ -1284,14 +1285,16 @@ describe('Client', () => { }); test('does not modify existing contexts for root span in `beforeSendSpan`', () => { - const beforeSendSpan = vi.fn((span: SpanJSON) => { - return { - ...span, - data: { - modified: 'true', - }, - }; - }); + const beforeSendSpan = withStaticSpan( + vi.fn((span: SpanJSON) => { + return { + ...span, + data: { + modified: 'true', + }, + }; + }), + ); const options = getDefaultTestClientOptions({ dsn: PUBLIC_DSN, beforeSendSpan }); const client = new TestClient(options); @@ -1399,10 +1402,12 @@ describe('Client', () => { test('calls `beforeSendSpan` and uses the modified spans', () => { expect.assertions(4); - const beforeSendSpan = vi.fn(span => { - span.data = { version: 'bravo' }; - return span; - }); + const beforeSendSpan = withStaticSpan( + vi.fn(span => { + span.data = { version: 'bravo' }; + return span; + }), + ); const options = getDefaultTestClientOptions({ dsn: PUBLIC_DSN, beforeSendSpan }); const client = new TestClient(options); @@ -1481,7 +1486,7 @@ describe('Client', () => { test('does not discard span and warn when returning null from `beforeSendSpan', () => { const consoleWarnSpy = vi.spyOn(console, 'warn').mockImplementation(() => undefined); - const beforeSendSpan = vi.fn(() => null as unknown as SpanJSON); + const beforeSendSpan = withStaticSpan(vi.fn(() => null as unknown as SpanJSON)); const options = getDefaultTestClientOptions({ dsn: PUBLIC_DSN, beforeSendSpan }); const client = new TestClient(options); diff --git a/packages/core/test/lib/tracing/sentrySpan.test.ts b/packages/core/test/lib/tracing/sentrySpan.test.ts index 7522b7061234..33da904954b8 100644 --- a/packages/core/test/lib/tracing/sentrySpan.test.ts +++ b/packages/core/test/lib/tracing/sentrySpan.test.ts @@ -14,6 +14,7 @@ import { markSpanForOtelSourceInference, spanSourceWasExplicitlySet, } from '../../../src/tracing/utils'; +import { withStaticSpan } from '../../../src/tracing/spans/beforeSendSpan'; import type { Envelope } from '../../../src/types/envelope'; import type { SpanJSON } from '../../../src/types/span'; import { spanToJSON, TRACE_FLAG_NONE, TRACE_FLAG_SAMPLED } from '../../../src/utils/spanUtils'; @@ -272,7 +273,7 @@ describe('SentrySpan', () => { }); test('sends the span if `beforeSendSpan` does not modify the span', () => { - const beforeSendSpan = vi.fn(span => span); + const beforeSendSpan = withStaticSpan(vi.fn(span => span)); const client = new TestClient( getDefaultTestClientOptions({ dsn: 'https://username@domain/123', @@ -295,10 +296,10 @@ describe('SentrySpan', () => { expect(mockSend).toHaveBeenCalled(); }); - test('ignores a non-streamed `beforeSendSpan` for standalone spans', () => { - // Standalone spans are sent as v2 streamed spans, which only honor a `beforeSendSpan` wrapped - // with `withStreamedSpan`. A plain callback is ignored, so the span is sent unmodified. - const beforeSendSpan = vi.fn(() => null as unknown as SpanJSON); + test('ignores a static `beforeSendSpan` for standalone spans', () => { + // Standalone spans are sent as v2 streamed spans, which only honor an unwrapped (streamed) + // `beforeSendSpan`. A callback wrapped with `withStaticSpan` is ignored, so the span is sent unmodified. + const beforeSendSpan = withStaticSpan(vi.fn(() => null as unknown as SpanJSON)); const client = new TestClient( getDefaultTestClientOptions({ dsn: 'https://username@domain/123', diff --git a/packages/core/test/lib/tracing/spans/beforeSendSpan.test.ts b/packages/core/test/lib/tracing/spans/beforeSendSpan.test.ts index 79fd838a1b27..2616415d8bf2 100644 --- a/packages/core/test/lib/tracing/spans/beforeSendSpan.test.ts +++ b/packages/core/test/lib/tracing/spans/beforeSendSpan.test.ts @@ -1,26 +1,52 @@ import { describe, expect, it, vi } from 'vitest'; -import { withStreamedSpan } from '../../../../src'; -import { isStreamedBeforeSendSpanCallback } from '../../../../src/tracing/spans/beforeSendSpan'; +import { withStaticSpan, withStreamedSpan } from '../../../../src'; +import { + isStaticBeforeSendSpanCallback, + isStreamedBeforeSendSpanCallback, +} from '../../../../src/tracing/spans/beforeSendSpan'; + +describe('beforeSendSpan callback formats', () => { + describe('withStaticSpan', () => { + it('marks the callback as static without making the marker enumerable', () => { + const beforeSendSpan = vi.fn(); + const wrapped = withStaticSpan(beforeSendSpan); + + expect(wrapped._static).toBe(true); + expect(Object.keys(wrapped)).not.toContain('_static'); + }); + }); -describe('beforeSendSpan for span streaming', () => { describe('withStreamedSpan', () => { - it('should be able to modify the span', () => { + it('marks the callback as streamed', () => { const beforeSendSpan = vi.fn(); const wrapped = withStreamedSpan(beforeSendSpan); expect(wrapped._streamed).toBe(true); }); }); + describe('isStaticBeforeSendSpanCallback', () => { + it('returns true if the callback is wrapped with withStaticSpan', () => { + const wrapped = withStaticSpan(vi.fn()); + + expect(isStaticBeforeSendSpanCallback(wrapped)).toBe(true); + }); + + it('returns false for an unwrapped callback', () => { + expect(isStaticBeforeSendSpanCallback(vi.fn())).toBe(false); + }); + }); + describe('isStreamedBeforeSendSpanCallback', () => { + it('returns true for an unwrapped callback', () => { + expect(isStreamedBeforeSendSpanCallback(vi.fn())).toBe(true); + }); + it('returns true if the callback is wrapped with withStreamedSpan', () => { - const beforeSendSpan = vi.fn(); - const wrapped = withStreamedSpan(beforeSendSpan); - expect(isStreamedBeforeSendSpanCallback(wrapped)).toBe(true); + expect(isStreamedBeforeSendSpanCallback(withStreamedSpan(vi.fn()))).toBe(true); }); - it('returns false if the callback is not wrapped with withStreamedSpan', () => { - const beforeSendSpan = vi.fn(); - expect(isStreamedBeforeSendSpanCallback(beforeSendSpan)).toBe(false); + it('returns false if the callback is wrapped with withStaticSpan', () => { + expect(isStreamedBeforeSendSpanCallback(withStaticSpan(vi.fn()))).toBe(false); }); }); }); diff --git a/packages/core/test/lib/tracing/spans/captureSpan.test.ts b/packages/core/test/lib/tracing/spans/captureSpan.test.ts index 0560ebd2f702..34c35868d20b 100644 --- a/packages/core/test/lib/tracing/spans/captureSpan.test.ts +++ b/packages/core/test/lib/tracing/spans/captureSpan.test.ts @@ -15,8 +15,8 @@ import { SEMANTIC_ATTRIBUTE_USER_USERNAME, startInactiveSpan, startSpan, + withStaticSpan, withScope, - withStreamedSpan, } from '../../../../src'; import { safeSetSpanJSONAttributes } from '../../../../src/tracing/spans/captureSpan'; import { scopeContextsToSpanAttributes } from '../../../../src/tracing/spans/scopeContextAttributes'; @@ -475,8 +475,8 @@ describe('captureSpan', () => { }); describe('beforeSendSpan', () => { - it('applies beforeSendSpan if it is a span streaming compatible callback', () => { - const beforeSendSpan = withStreamedSpan(vi.fn(span => span)); + it('applies an unwrapped beforeSendSpan callback', () => { + const beforeSendSpan = vi.fn(span => span); const client = new TestClient( getDefaultTestClientOptions({ @@ -496,8 +496,8 @@ describe('captureSpan', () => { expect(beforeSendSpan).toHaveBeenCalledWith(expect.objectContaining({ span_id: span.spanContext().spanId })); }); - it("doesn't apply beforeSendSpan if it is not a span streaming compatible callback", () => { - const beforeSendSpan = vi.fn(span => span); + it("doesn't apply beforeSendSpan if it is marked as static", () => { + const beforeSendSpan = withStaticSpan(vi.fn(span => span)); const client = new TestClient( getDefaultTestClientOptions({ @@ -519,8 +519,7 @@ describe('captureSpan', () => { it('logs a warning if the beforeSendSpan callback returns null', () => { const consoleWarnSpy = vi.spyOn(console, 'warn').mockImplementation(() => undefined); - // @ts-expect-error - the types dissallow returning null but this is javascript, so we need to test it - const beforeSendSpan = withStreamedSpan(() => null); + const beforeSendSpan = vi.fn(() => null as unknown as StreamedSpanJSON); const client = new TestClient( getDefaultTestClientOptions({ diff --git a/packages/deno/src/index.ts b/packages/deno/src/index.ts index 52a04b18aeb7..505217df5ee7 100644 --- a/packages/deno/src/index.ts +++ b/packages/deno/src/index.ts @@ -97,6 +97,8 @@ export { wrapMcpServerWithSentry, featureFlagsIntegration, metrics, + withStaticSpan, + // eslint-disable-next-line typescript/no-deprecated withStreamedSpan, logger, consoleLoggingIntegration, diff --git a/packages/effect/src/index.types.ts b/packages/effect/src/index.types.ts index e1544da73485..940d59f49f7f 100644 --- a/packages/effect/src/index.types.ts +++ b/packages/effect/src/index.types.ts @@ -22,6 +22,8 @@ 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 withStaticSpan: typeof clientSdk.withStaticSpan; +// eslint-disable-next-line typescript/no-deprecated export declare const withStreamedSpan: typeof clientSdk.withStreamedSpan; export declare const getDefaultIntegrations: (options: Options) => Integration[]; export declare const defaultStackParser: StackParser; diff --git a/packages/elysia/src/index.ts b/packages/elysia/src/index.ts index 715c2484296e..d5939487ea8d 100644 --- a/packages/elysia/src/index.ts +++ b/packages/elysia/src/index.ts @@ -155,6 +155,8 @@ export { unleashIntegration, metrics, spanStreamingIntegration, + withStaticSpan, + // eslint-disable-next-line typescript/no-deprecated withStreamedSpan, bunServerIntegration, makeFetchTransport, diff --git a/packages/google-cloud-serverless/src/index.ts b/packages/google-cloud-serverless/src/index.ts index d07490563212..2a9978672951 100644 --- a/packages/google-cloud-serverless/src/index.ts +++ b/packages/google-cloud-serverless/src/index.ts @@ -160,6 +160,8 @@ export { unleashIntegration, metrics, spanStreamingIntegration, + withStaticSpan, + // eslint-disable-next-line typescript/no-deprecated withStreamedSpan, } from '@sentry/node'; diff --git a/packages/nextjs/src/index.types.ts b/packages/nextjs/src/index.types.ts index e0d918708bb1..151d338cf3ce 100644 --- a/packages/nextjs/src/index.types.ts +++ b/packages/nextjs/src/index.types.ts @@ -27,6 +27,8 @@ export declare const consoleIntegration: typeof serverSdk.consoleIntegration; // Node-only at runtime; the edge build exports an inert shim so named imports resolve in edge-compiled modules. export declare const pinoIntegration: typeof serverSdk.pinoIntegration; export declare const spanStreamingIntegration: typeof clientSdk.spanStreamingIntegration; +export declare const withStaticSpan: typeof clientSdk.withStaticSpan; +// eslint-disable-next-line typescript/no-deprecated export declare const withStreamedSpan: typeof clientSdk.withStreamedSpan; // Different implementation in server and worker diff --git a/packages/node/src/index.ts b/packages/node/src/index.ts index 69ef5b6067be..7b82407809db 100644 --- a/packages/node/src/index.ts +++ b/packages/node/src/index.ts @@ -177,7 +177,8 @@ export type { CaptureContext, } from '@sentry/core'; -export { metrics, withStreamedSpan } from '@sentry/core'; +// eslint-disable-next-line typescript/no-deprecated +export { metrics, withStaticSpan, withStreamedSpan } from '@sentry/core'; export * as logger from './logs/exports'; export { childProcessIntegration } from './integrations/childProcess'; diff --git a/packages/nuxt/src/index.types.ts b/packages/nuxt/src/index.types.ts index e058e0bfd099..e2e8ba3b66d0 100644 --- a/packages/nuxt/src/index.types.ts +++ b/packages/nuxt/src/index.types.ts @@ -17,6 +17,8 @@ export declare function init(options: Options | SentryNuxtClientOptions | Sentry export declare const linkedErrorsIntegration: typeof clientSdk.linkedErrorsIntegration; export declare const contextLinesIntegration: typeof clientSdk.contextLinesIntegration; export declare const spanStreamingIntegration: typeof clientSdk.spanStreamingIntegration; +export declare const withStaticSpan: typeof clientSdk.withStaticSpan; +// eslint-disable-next-line typescript/no-deprecated export declare const withStreamedSpan: typeof clientSdk.withStreamedSpan; export declare const getDefaultIntegrations: (options: Options) => Integration[]; export declare const defaultStackParser: StackParser; diff --git a/packages/react-router/src/index.types.ts b/packages/react-router/src/index.types.ts index e7fb4382d0f2..16f2eca441a3 100644 --- a/packages/react-router/src/index.types.ts +++ b/packages/react-router/src/index.types.ts @@ -17,6 +17,8 @@ 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 withStaticSpan: typeof clientSdk.withStaticSpan; +// eslint-disable-next-line typescript/no-deprecated export declare const withStreamedSpan: typeof clientSdk.withStreamedSpan; export declare const defaultStackParser: StackParser; export declare const getDefaultIntegrations: (options: Options) => Integration[]; diff --git a/packages/remix/src/cloudflare/index.ts b/packages/remix/src/cloudflare/index.ts index ef2f89cee2a5..a91869f0d976 100644 --- a/packages/remix/src/cloudflare/index.ts +++ b/packages/remix/src/cloudflare/index.ts @@ -117,6 +117,8 @@ export { spanToTraceHeader, spanToBaggageHeader, updateSpanName, + withStaticSpan, + // eslint-disable-next-line typescript/no-deprecated withStreamedSpan, featureFlagsIntegration, } from '@sentry/core'; diff --git a/packages/remix/src/index.types.ts b/packages/remix/src/index.types.ts index 1a54068ae019..867bc011dfc2 100644 --- a/packages/remix/src/index.types.ts +++ b/packages/remix/src/index.types.ts @@ -19,6 +19,8 @@ export declare const browserTracingIntegration: typeof clientSdk.browserTracingI export declare const linkedErrorsIntegration: typeof clientSdk.linkedErrorsIntegration; export declare const contextLinesIntegration: typeof clientSdk.contextLinesIntegration; export declare const spanStreamingIntegration: typeof clientSdk.spanStreamingIntegration; +export declare const withStaticSpan: typeof clientSdk.withStaticSpan; +// eslint-disable-next-line typescript/no-deprecated export declare const withStreamedSpan: typeof clientSdk.withStreamedSpan; export declare const getDefaultIntegrations: (options: Options) => Integration[]; diff --git a/packages/remix/src/server/index.ts b/packages/remix/src/server/index.ts index 4fd2331b34fa..e21dffa78807 100644 --- a/packages/remix/src/server/index.ts +++ b/packages/remix/src/server/index.ts @@ -133,6 +133,8 @@ export { createConsolaReporter, createSentryWinstonTransport, spanStreamingIntegration, + withStaticSpan, + // eslint-disable-next-line typescript/no-deprecated withStreamedSpan, } from '@sentry/node'; diff --git a/packages/solidstart/src/index.types.ts b/packages/solidstart/src/index.types.ts index d984e1384a93..1b458e7cf929 100644 --- a/packages/solidstart/src/index.types.ts +++ b/packages/solidstart/src/index.types.ts @@ -19,6 +19,8 @@ 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 withStaticSpan: typeof clientSdk.withStaticSpan; +// eslint-disable-next-line typescript/no-deprecated export declare const withStreamedSpan: typeof clientSdk.withStreamedSpan; export declare const getDefaultIntegrations: (options: Options) => Integration[]; diff --git a/packages/solidstart/src/server/index.ts b/packages/solidstart/src/server/index.ts index fd5fbb2e0e39..e46cd2744e49 100644 --- a/packages/solidstart/src/server/index.ts +++ b/packages/solidstart/src/server/index.ts @@ -132,6 +132,8 @@ export { createConsolaReporter, createSentryWinstonTransport, spanStreamingIntegration, + withStaticSpan, + // eslint-disable-next-line typescript/no-deprecated withStreamedSpan, } from '@sentry/node'; diff --git a/packages/sveltekit/src/index.types.ts b/packages/sveltekit/src/index.types.ts index 1f4bbae0b4a9..0a328b38ab1b 100644 --- a/packages/sveltekit/src/index.types.ts +++ b/packages/sveltekit/src/index.types.ts @@ -48,6 +48,8 @@ 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; +export declare const withStaticSpan: typeof clientSdk.withStaticSpan; +// eslint-disable-next-line typescript/no-deprecated export declare const withStreamedSpan: typeof clientSdk.withStreamedSpan; // Different implementation in server and worker diff --git a/packages/sveltekit/src/server/index.ts b/packages/sveltekit/src/server/index.ts index 5ebd934e9e88..a6defb62c39d 100644 --- a/packages/sveltekit/src/server/index.ts +++ b/packages/sveltekit/src/server/index.ts @@ -137,6 +137,8 @@ export { vercelAIIntegration, metrics, spanStreamingIntegration, + withStaticSpan, + // eslint-disable-next-line typescript/no-deprecated withStreamedSpan, } from '@sentry/node'; diff --git a/packages/sveltekit/src/worker/index.ts b/packages/sveltekit/src/worker/index.ts index de2146c9e259..e52ebc8aedc4 100644 --- a/packages/sveltekit/src/worker/index.ts +++ b/packages/sveltekit/src/worker/index.ts @@ -83,6 +83,8 @@ export { withIsolationScope, withMonitor, withScope, + withStaticSpan, + // eslint-disable-next-line typescript/no-deprecated withStreamedSpan, supabaseIntegration, instrumentSupabaseClient, diff --git a/packages/tanstackstart-react/src/index.types.ts b/packages/tanstackstart-react/src/index.types.ts index d6465a0e14d2..faedd2ab18b5 100644 --- a/packages/tanstackstart-react/src/index.types.ts +++ b/packages/tanstackstart-react/src/index.types.ts @@ -19,6 +19,8 @@ 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 withStaticSpan: typeof clientSdk.withStaticSpan; +// eslint-disable-next-line typescript/no-deprecated export declare const withStreamedSpan: typeof clientSdk.withStreamedSpan; export declare const getDefaultIntegrations: (options: Options) => Integration[]; diff --git a/packages/vercel-edge/src/index.ts b/packages/vercel-edge/src/index.ts index d8e277f9fea0..7fef86995fee 100644 --- a/packages/vercel-edge/src/index.ts +++ b/packages/vercel-edge/src/index.ts @@ -105,6 +105,8 @@ export { featureFlagsIntegration, logger, metrics, + withStaticSpan, + // eslint-disable-next-line typescript/no-deprecated withStreamedSpan, spanStreamingIntegration, } from '@sentry/core'; From 83056a57e6bae4059e7c9910036dbf0d1195b6d2 Mon Sep 17 00:00:00 2001 From: Lukas Stracke Date: Tue, 28 Jul 2026 14:05:56 +0200 Subject: [PATCH 2/2] ref(core): Centralize span lifecycle and `beforeSendSpan` validation `withStreamedSpan` no longer marks its callback. Nothing reads `_streamed` since `isStreamedBeforeSendSpanCallback` became "not wrapped with `withStaticSpan`", so the wrapper returns the callback unchanged instead of mutating a function it was handed. Resolve `traceLifecycle` once in the `Client` constructor, where any value other than `'static'` normalizes to the `'stream'` default. Neither span streaming integration writes back to the option anymore, which removes an ordering hazard: integrations that read `hasSpanStreamingEnabled` during their own `setup` could observe the value before or after the mutation depending on integration order. The registration gates now test `!== 'static'` so they agree with the normalized value; previously an unknown value failed the gate but still resolved to `'stream'`, leaving nothing to flush spans. Move the `beforeSendSpan` format check into the constructor as well. A callback is only invoked for the span format matching the trace lifecycle, so a mismatch means it is never called. The client can warn where the integration could not: with `traceLifecycle: 'static'` the streaming integration is never registered, so an unwrapped callback previously went unreported. A `withStaticSpan` callback under `traceLifecycle: 'stream'` no longer downgrades the lifecycle to `'static'`. Opting out of streaming requires setting `traceLifecycle: 'static'` explicitly. Refs #22349 Co-Authored-By: Claude Opus 5 (1M context) --- .../rollup-utils/plugins/bundlePlugins.mjs | 3 +- packages/astro/src/index.server.ts | 2 +- packages/astro/src/index.types.ts | 2 +- packages/aws-serverless/src/index.ts | 2 +- packages/browser/src/exports.ts | 2 +- .../browser/src/integrations/spanstreaming.ts | 23 +---- packages/browser/src/sdk.ts | 4 +- .../test/integrations/spanstreaming.test.ts | 64 +++++++------- packages/bun/src/index.ts | 2 +- packages/cloudflare/src/index.ts | 2 +- packages/core/src/client.ts | 23 ++++- .../core/src/integrations/spanStreaming.ts | 22 +---- packages/core/src/server-runtime-client.ts | 4 +- packages/core/src/shared-exports.ts | 2 +- .../core/src/tracing/spans/beforeSendSpan.ts | 7 +- packages/core/src/types/options.ts | 7 +- .../test/integrations/spanStreaming.test.ts | 63 +++++++------- packages/core/test/lib/client.test.ts | 83 +++++++++++++++++++ .../lib/tracing/spans/beforeSendSpan.test.ts | 7 +- packages/deno/src/index.ts | 2 +- packages/effect/src/index.types.ts | 2 +- packages/elysia/src/index.ts | 2 +- packages/google-cloud-serverless/src/index.ts | 2 +- packages/nextjs/src/index.types.ts | 2 +- packages/node/src/index.ts | 2 +- packages/nuxt/src/index.types.ts | 2 +- packages/react-router/src/index.types.ts | 2 +- packages/remix/src/cloudflare/index.ts | 2 +- packages/remix/src/index.types.ts | 2 +- packages/remix/src/server/index.ts | 2 +- packages/solidstart/src/index.types.ts | 2 +- packages/solidstart/src/server/index.ts | 2 +- packages/sveltekit/src/index.types.ts | 2 +- packages/sveltekit/src/server/index.ts | 2 +- packages/sveltekit/src/worker/index.ts | 2 +- .../tanstackstart-react/src/index.types.ts | 2 +- packages/vercel-edge/src/index.ts | 2 +- 37 files changed, 210 insertions(+), 150 deletions(-) diff --git a/dev-packages/rollup-utils/plugins/bundlePlugins.mjs b/dev-packages/rollup-utils/plugins/bundlePlugins.mjs index b1836e17127f..571bb2af7189 100644 --- a/dev-packages/rollup-utils/plugins/bundlePlugins.mjs +++ b/dev-packages/rollup-utils/plugins/bundlePlugins.mjs @@ -150,9 +150,8 @@ export function makeTerserPlugin() { '_resolveFilename', // Set on e.g. the shim feedbackIntegration to be able to detect it '_isShim', - // Markers used to distinguish static and explicitly streamed `beforeSendSpan` callbacks + // Marker used to detect `beforeSendSpan` callbacks expecting the static span format '_static', - '_streamed', // This is used in metadata integration '_sentryModuleMetadata', ], diff --git a/packages/astro/src/index.server.ts b/packages/astro/src/index.server.ts index b12070bdf9ec..f503915569af 100644 --- a/packages/astro/src/index.server.ts +++ b/packages/astro/src/index.server.ts @@ -174,7 +174,7 @@ export { growthbookIntegration, spanStreamingIntegration, withStaticSpan, - // eslint-disable-next-line typescript/no-deprecated + // oxlint-disable-next-line typescript/no-deprecated withStreamedSpan, metrics, } from '@sentry/node'; diff --git a/packages/astro/src/index.types.ts b/packages/astro/src/index.types.ts index b3f5037f8751..3472adfad925 100644 --- a/packages/astro/src/index.types.ts +++ b/packages/astro/src/index.types.ts @@ -22,7 +22,7 @@ export declare const linkedErrorsIntegration: typeof clientSdk.linkedErrorsInteg export declare const contextLinesIntegration: typeof clientSdk.contextLinesIntegration; export declare const spanStreamingIntegration: typeof clientSdk.spanStreamingIntegration; export declare const withStaticSpan: typeof clientSdk.withStaticSpan; -// eslint-disable-next-line typescript/no-deprecated +// oxlint-disable-next-line typescript/no-deprecated export declare const withStreamedSpan: typeof clientSdk.withStreamedSpan; export declare const getDefaultIntegrations: (options: Options) => Integration[]; diff --git a/packages/aws-serverless/src/index.ts b/packages/aws-serverless/src/index.ts index 8870c7ca67d0..991ef239696a 100644 --- a/packages/aws-serverless/src/index.ts +++ b/packages/aws-serverless/src/index.ts @@ -161,7 +161,7 @@ export { metrics, spanStreamingIntegration, withStaticSpan, - // eslint-disable-next-line typescript/no-deprecated + // oxlint-disable-next-line typescript/no-deprecated withStreamedSpan, } from '@sentry/node'; diff --git a/packages/browser/src/exports.ts b/packages/browser/src/exports.ts index e3a7e5da2850..86e038cbb850 100644 --- a/packages/browser/src/exports.ts +++ b/packages/browser/src/exports.ts @@ -73,7 +73,7 @@ export { spanToBaggageHeader, updateSpanName, withStaticSpan, - // eslint-disable-next-line typescript/no-deprecated + // oxlint-disable-next-line typescript/no-deprecated withStreamedSpan, metrics, } from '@sentry/core/browser'; diff --git a/packages/browser/src/integrations/spanstreaming.ts b/packages/browser/src/integrations/spanstreaming.ts index 617d1a700f5d..9b617ba4b014 100644 --- a/packages/browser/src/integrations/spanstreaming.ts +++ b/packages/browser/src/integrations/spanstreaming.ts @@ -4,34 +4,19 @@ import { debug, defineIntegration, hasSpanStreamingEnabled, - isStaticBeforeSendSpanCallback, SpanBuffer, spanIsSampled, } from '@sentry/core/browser'; import { DEBUG_BUILD } from '../debug-build'; +const INTEGRATION_NAME = 'SpanStreaming' as const; + export const spanStreamingIntegration = defineIntegration(() => { return { - name: 'SpanStreaming' as const, - + name: INTEGRATION_NAME, 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 = clientOptions.beforeSendSpan; - if (isStaticBeforeSendSpanCallback(beforeSendSpan)) { - clientOptions.traceLifecycle = 'static'; - DEBUG_BUILD && - debug.warn( - `SpanStreaming integration is incompatible with a beforeSendSpan callback using \`withStaticSpan\`! ${fallbackMsg}`, - ); + DEBUG_BUILD && debug.log(`[${INTEGRATION_NAME}] \`traceLifecycle\` is "static", skipping setup.`); return; } diff --git a/packages/browser/src/sdk.ts b/packages/browser/src/sdk.ts index d3bf3c960d6e..0309aed98248 100644 --- a/packages/browser/src/sdk.ts +++ b/packages/browser/src/sdk.ts @@ -116,9 +116,7 @@ export function init(options: BrowserOptions = {}): Client | undefined { defaultIntegrations, }); - options.traceLifecycle ??= 'stream'; - - if (options.traceLifecycle === 'stream' && !integrations.some(integration => integration.name === 'SpanStreaming')) { + if (options.traceLifecycle !== 'static' && !integrations.some(integration => integration.name === 'SpanStreaming')) { integrations.push(spanStreamingIntegration()); } diff --git a/packages/browser/test/integrations/spanstreaming.test.ts b/packages/browser/test/integrations/spanstreaming.test.ts index b1c9705a1871..9fd7c934b1be 100644 --- a/packages/browser/test/integrations/spanstreaming.test.ts +++ b/packages/browser/test/integrations/spanstreaming.test.ts @@ -40,62 +40,60 @@ describe('spanStreamingIntegration', () => { expect(integration.setup).toBeDefined(); }); - 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 marked as static', () => { - const debugSpy = vi.spyOn(debug, 'warn').mockImplementation(() => {}); + it('does not set up span streaming if traceLifecycle is "static"', () => { + const debugSpy = vi.spyOn(debug, 'log').mockImplementation(() => {}); const client = new BrowserClient({ ...getDefaultBrowserClientOptions(), dsn: 'https://username@domain/123', integrations: [spanStreamingIntegration()], - traceLifecycle: 'stream', - beforeSendSpan: SentryCore.withStaticSpan(span => span), + traceLifecycle: 'static', + tracesSampleRate: 1, }); SentryCore.setCurrentClient(client); client.init(); - expect(debugSpy).toHaveBeenCalledWith( - 'SpanStreaming integration is incompatible with a beforeSendSpan callback using `withStaticSpan`! Falling back to static trace lifecycle.', - ); + expect(debugSpy).toHaveBeenCalledWith('[SpanStreaming] `traceLifecycle` is "static", skipping setup.'); debugSpy.mockRestore(); - expect(client.getOptions().traceLifecycle).toBe('static'); + expect(MockSpanBuffer).not.toHaveBeenCalled(); + + // Without the hooks registered, ending a span must not enqueue anything + client.emit('afterSpanEnd', new SentryCore.SentrySpan({ name: 'test', sampled: true })); + expect(mockSpanBufferInstance.add).not.toHaveBeenCalled(); + }); + + it.each([ + ['explicitly set to "stream"', 'stream' as const], + ['left unset', undefined], + ])('sets up span streaming if traceLifecycle is %s', (_, traceLifecycle) => { + const client = new BrowserClient({ + ...getDefaultBrowserClientOptions(), + dsn: 'https://username@domain/123', + integrations: [spanStreamingIntegration()], + traceLifecycle, + }); + + SentryCore.setCurrentClient(client); + client.init(); + + expect(MockSpanBuffer).toHaveBeenCalledTimes(1); + expect(client.getOptions().traceLifecycle).toBe('stream'); }); - it('does nothing if traceLifecycle set to "stream"', () => { + it('still sets up span streaming if beforeSendSpan is wrapped with withStaticSpan', () => { const client = new BrowserClient({ ...getDefaultBrowserClientOptions(), dsn: 'https://username@domain/123', integrations: [spanStreamingIntegration()], traceLifecycle: 'stream', + beforeSendSpan: SentryCore.withStaticSpan(span => span), }); SentryCore.setCurrentClient(client); client.init(); + expect(MockSpanBuffer).toHaveBeenCalledTimes(1); expect(client.getOptions().traceLifecycle).toBe('stream'); }); diff --git a/packages/bun/src/index.ts b/packages/bun/src/index.ts index beb8a365839c..353d16545358 100644 --- a/packages/bun/src/index.ts +++ b/packages/bun/src/index.ts @@ -179,7 +179,7 @@ export { metrics, spanStreamingIntegration, withStaticSpan, - // eslint-disable-next-line typescript/no-deprecated + // oxlint-disable-next-line typescript/no-deprecated withStreamedSpan, } from '@sentry/node'; diff --git a/packages/cloudflare/src/index.ts b/packages/cloudflare/src/index.ts index a07fdb43322b..46839754ac23 100644 --- a/packages/cloudflare/src/index.ts +++ b/packages/cloudflare/src/index.ts @@ -111,7 +111,7 @@ export { logger, metrics, withStaticSpan, - // eslint-disable-next-line typescript/no-deprecated + // oxlint-disable-next-line typescript/no-deprecated withStreamedSpan, spanStreamingIntegration, instrumentStateGraph, diff --git a/packages/core/src/client.ts b/packages/core/src/client.ts index 4d8909032acf..54be7264ab7d 100644 --- a/packages/core/src/client.ts +++ b/packages/core/src/client.ts @@ -234,7 +234,13 @@ export abstract class Client { * @param options Options for the client. */ protected constructor(options: O) { - this._options = { attachStacktrace: true, traceLifecycle: 'stream', ...options }; + // Any value other than `'static'` normalizes to the `'stream'` default, so that `traceLifecycle` + // is always one of the two known values for the rest of the SDK. + this._options = { + attachStacktrace: true, + ...options, + traceLifecycle: options.traceLifecycle === 'static' ? 'static' : 'stream', + }; this._integrations = {}; this._numProcessing = 0; this._outcomes = {}; @@ -250,6 +256,21 @@ export abstract class Client { DEBUG_BUILD && debug.warn('No DSN provided, client will not send events.'); } + const { beforeSendSpan, traceLifecycle } = this._options; + // A `beforeSendSpan` callback is only invoked for the span format matching the trace lifecycle, + // so a mismatch means it is silently never called. + if ( + DEBUG_BUILD && + beforeSendSpan && + isStaticBeforeSendSpanCallback(beforeSendSpan) !== (traceLifecycle === 'static') + ) { + debug.warn( + `Ignoring \`beforeSendSpan\`: ${ + traceLifecycle === 'static' ? 'wrap it with' : 'remove' + } \`Sentry.withStaticSpan\` to use it with \`traceLifecycle: "${traceLifecycle}"\`.`, + ); + } + if (this._dsn) { const url = getEnvelopeEndpointWithUrlEncodedAuth( this._dsn, diff --git a/packages/core/src/integrations/spanStreaming.ts b/packages/core/src/integrations/spanStreaming.ts index 36bc2ff55c2d..662e8c83a093 100644 --- a/packages/core/src/integrations/spanStreaming.ts +++ b/packages/core/src/integrations/spanStreaming.ts @@ -1,35 +1,21 @@ import type { IntegrationFn } from '../types/integration'; import { DEBUG_BUILD } from '../debug-build'; import { defineIntegration } from '../integration'; -import { isStaticBeforeSendSpanCallback } 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'; +const INTEGRATION_NAME = 'SpanStreaming' as const; + export const spanStreamingIntegration = defineIntegration(() => { return { - name: 'SpanStreaming' as const, + name: INTEGRATION_NAME, 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 = clientOptions.beforeSendSpan; - if (isStaticBeforeSendSpanCallback(beforeSendSpan)) { - clientOptions.traceLifecycle = 'static'; - DEBUG_BUILD && - debug.warn( - `SpanStreaming integration is incompatible with a beforeSendSpan callback using \`withStaticSpan\`! ${fallbackMsg}`, - ); + DEBUG_BUILD && debug.log(`[${INTEGRATION_NAME}] \`traceLifecycle\` is "static", skipping setup.`); return; } diff --git a/packages/core/src/server-runtime-client.ts b/packages/core/src/server-runtime-client.ts index ec10c516ef9b..ae564663f6c7 100644 --- a/packages/core/src/server-runtime-client.ts +++ b/packages/core/src/server-runtime-client.ts @@ -40,12 +40,10 @@ export class ServerRuntimeClient< public constructor(options: O) { addUserAgentToTransportHeaders(options); - options.traceLifecycle ??= 'stream'; - // When span streaming is enabled (`traceLifecycle: 'stream'`), the `spanStreamingIntegration` // is required to flush spans. We add it here so the individual server SDKs don't have to. // A user-provided `spanStreamingIntegration` always takes precedence over the one we add. - if (options.traceLifecycle === 'stream' && !options.integrations.some(i => i.name === 'SpanStreaming')) { + if (options.traceLifecycle !== 'static' && !options.integrations.some(i => i.name === 'SpanStreaming')) { options.integrations.push(spanStreamingIntegration()); } diff --git a/packages/core/src/shared-exports.ts b/packages/core/src/shared-exports.ts index a36e15105ec6..d48c7f075e33 100644 --- a/packages/core/src/shared-exports.ts +++ b/packages/core/src/shared-exports.ts @@ -88,7 +88,7 @@ export { createCheckInEnvelope } from './checkin'; export { hasSpansEnabled } from './utils/hasSpansEnabled'; export { withStaticSpan, - // eslint-disable-next-line typescript/no-deprecated + // oxlint-disable-next-line typescript/no-deprecated withStreamedSpan, } from './tracing/spans/beforeSendSpan'; export { isStaticBeforeSendSpanCallback, isStreamedBeforeSendSpanCallback } from './tracing/spans/beforeSendSpan'; diff --git a/packages/core/src/tracing/spans/beforeSendSpan.ts b/packages/core/src/tracing/spans/beforeSendSpan.ts index 3306a594c94f..a6065218438a 100644 --- a/packages/core/src/tracing/spans/beforeSendSpan.ts +++ b/packages/core/src/tracing/spans/beforeSendSpan.ts @@ -30,16 +30,15 @@ export function withStaticSpan(callback: (span: SpanJSON) => SpanJSON): BeforeSe * A wrapper to explicitly use the streamed span format in your `beforeSendSpan` callback. * * @deprecated `beforeSendSpan` callbacks receive {@link StreamedSpanJSON} by default. - * This function will be removed in SDK version 12. + * This function returns the callback unchanged and will be removed in SDK version 12. * * @param callback - The callback function that receives and returns a {@link StreamedSpanJSON}. * @returns The provided callback. */ export function withStreamedSpan( callback: (span: StreamedSpanJSON) => StreamedSpanJSON, -): BeforeSendStreamedSpanCallback & { _streamed: true } { - addNonEnumerableProperty(callback, '_streamed', true); - return callback as BeforeSendStreamedSpanCallback & { _streamed: true }; +): BeforeSendStreamedSpanCallback { + return callback; } /** diff --git a/packages/core/src/types/options.ts b/packages/core/src/types/options.ts index a1b67accc107..c63991b50766 100644 --- a/packages/core/src/types/options.ts +++ b/packages/core/src/types/options.ts @@ -696,12 +696,7 @@ export interface ClientOptions StreamedSpanJSON) & { - /** - * @deprecated `beforeSendSpan` callbacks use {@link StreamedSpanJSON} by default. - */ - _streamed?: true; -}; +export type BeforeSendStreamedSpanCallback = (span: StreamedSpanJSON) => StreamedSpanJSON; declare const beforeSendStaticSpanCallback: unique symbol; diff --git a/packages/core/test/integrations/spanStreaming.test.ts b/packages/core/test/integrations/spanStreaming.test.ts index a2cf52f4deed..3219fb4e14d4 100644 --- a/packages/core/test/integrations/spanStreaming.test.ts +++ b/packages/core/test/integrations/spanStreaming.test.ts @@ -34,57 +34,54 @@ describe('spanStreamingIntegration (core)', () => { expect(integration.setup).toBeDefined(); }); - 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 marked as static', () => { - const debugSpy = vi.spyOn(debug, 'warn').mockImplementation(() => {}); + it('does not set up span streaming if traceLifecycle is "static"', () => { + const debugSpy = vi.spyOn(debug, 'log').mockImplementation(() => {}); const client = new TestClient({ ...getDefaultTestClientOptions(), dsn: 'https://username@domain/123', integrations: [spanStreamingIntegration()], - traceLifecycle: 'stream', - beforeSendSpan: SentryCore.withStaticSpan(span => span), + traceLifecycle: 'static', + tracesSampleRate: 1, }); SentryCore.setCurrentClient(client); client.init(); - expect(debugSpy).toHaveBeenCalledWith( - 'SpanStreaming integration is incompatible with a beforeSendSpan callback using `withStaticSpan`! Falling back to static trace lifecycle.', - ); + expect(debugSpy).toHaveBeenCalledWith('[SpanStreaming] `traceLifecycle` is "static", skipping setup.'); debugSpy.mockRestore(); - expect(client.getOptions().traceLifecycle).toBe('static'); + expect(MockSpanBuffer).not.toHaveBeenCalled(); + + // Without the hooks registered, ending a span must not enqueue anything + client.emit('afterSpanEnd', new SentryCore.SentrySpan({ name: 'test', sampled: true })); + expect(mockSpanBufferInstance.add).not.toHaveBeenCalled(); + }); + + it.each([ + ['explicitly set to "stream"', 'stream' as const], + ['left unset', undefined], + ])('sets up span streaming if traceLifecycle is %s', (_, traceLifecycle) => { + const client = new TestClient({ + ...getDefaultTestClientOptions(), + dsn: 'https://username@domain/123', + integrations: [spanStreamingIntegration()], + traceLifecycle, + }); + + SentryCore.setCurrentClient(client); + client.init(); + + expect(MockSpanBuffer).toHaveBeenCalledWith(client); + expect(client.getOptions().traceLifecycle).toBe('stream'); }); - it('sets up buffer when traceLifecycle is "stream"', () => { + it('still sets up span streaming if beforeSendSpan is wrapped with withStaticSpan', () => { const client = new TestClient({ ...getDefaultTestClientOptions(), dsn: 'https://username@domain/123', integrations: [spanStreamingIntegration()], traceLifecycle: 'stream', + beforeSendSpan: SentryCore.withStaticSpan(span => span), }); SentryCore.setCurrentClient(client); diff --git a/packages/core/test/lib/client.test.ts b/packages/core/test/lib/client.test.ts index 240502ae5842..a5d191f16e67 100644 --- a/packages/core/test/lib/client.test.ts +++ b/packages/core/test/lib/client.test.ts @@ -95,6 +95,80 @@ describe('Client', () => { expect(consoleWarnSpy).toHaveBeenCalledTimes(0); consoleWarnSpy.mockRestore(); }); + + test('warns that a streamed beforeSendSpan is ignored with traceLifecycle "static"', () => { + const warnSpy = vi.spyOn(debugLoggerModule.debug, 'warn').mockImplementation(() => undefined); + + new TestClient( + getDefaultTestClientOptions({ dsn: PUBLIC_DSN, traceLifecycle: 'static', beforeSendSpan: span => span }), + ); + + expect(warnSpy).toHaveBeenCalledWith( + 'Ignoring `beforeSendSpan`: wrap it with `Sentry.withStaticSpan` to use it with `traceLifecycle: "static"`.', + ); + warnSpy.mockRestore(); + }); + + test('warns that a static beforeSendSpan is ignored with traceLifecycle "stream"', () => { + const warnSpy = vi.spyOn(debugLoggerModule.debug, 'warn').mockImplementation(() => undefined); + + new TestClient( + getDefaultTestClientOptions({ + dsn: PUBLIC_DSN, + traceLifecycle: 'stream', + beforeSendSpan: withStaticSpan(span => span), + }), + ); + + expect(warnSpy).toHaveBeenCalledWith( + 'Ignoring `beforeSendSpan`: remove `Sentry.withStaticSpan` to use it with `traceLifecycle: "stream"`.', + ); + warnSpy.mockRestore(); + }); + + test('reports the normalized traceLifecycle when warning about an unknown value', () => { + const warnSpy = vi.spyOn(debugLoggerModule.debug, 'warn').mockImplementation(() => undefined); + + new TestClient( + getDefaultTestClientOptions({ + dsn: PUBLIC_DSN, + // @ts-expect-error - we want to test normalization of invalid traceLifecycle values + traceLifecycle: 'somethingElse', + beforeSendSpan: withStaticSpan(span => span), + }), + ); + + expect(warnSpy).toHaveBeenCalledWith( + 'Ignoring `beforeSendSpan`: remove `Sentry.withStaticSpan` to use it with `traceLifecycle: "stream"`.', + ); + warnSpy.mockRestore(); + }); + + test('does not warn for a streamed beforeSendSpan with traceLifecycle "stream"', () => { + const warnSpy = vi.spyOn(debugLoggerModule.debug, 'warn').mockImplementation(() => undefined); + + new TestClient( + getDefaultTestClientOptions({ dsn: PUBLIC_DSN, traceLifecycle: 'stream', beforeSendSpan: span => span }), + ); + + expect(warnSpy).not.toHaveBeenCalled(); + warnSpy.mockRestore(); + }); + + test('does not warn for a static beforeSendSpan with traceLifecycle "static"', () => { + const warnSpy = vi.spyOn(debugLoggerModule.debug, 'warn').mockImplementation(() => undefined); + + new TestClient( + getDefaultTestClientOptions({ + dsn: PUBLIC_DSN, + traceLifecycle: 'static', + beforeSendSpan: withStaticSpan(span => span), + }), + ); + + expect(warnSpy).not.toHaveBeenCalled(); + warnSpy.mockRestore(); + }); }); describe('getOptions()', () => { @@ -120,6 +194,15 @@ describe('Client', () => { expect(client.getOptions().traceLifecycle).toBe('static'); }); + + test('normalizes an unknown traceLifecycle to stream', () => { + const client = new TestClient( + // @ts-expect-error - we want to test normalization of invalid traceLifecycle values + getDefaultTestClientOptions({ traceLifecycle: 'somethingElse' }), + ); + + expect(client.getOptions().traceLifecycle).toBe('stream'); + }); }); describe('getTransport()', () => { diff --git a/packages/core/test/lib/tracing/spans/beforeSendSpan.test.ts b/packages/core/test/lib/tracing/spans/beforeSendSpan.test.ts index 2616415d8bf2..c4b3f8817b9c 100644 --- a/packages/core/test/lib/tracing/spans/beforeSendSpan.test.ts +++ b/packages/core/test/lib/tracing/spans/beforeSendSpan.test.ts @@ -17,10 +17,11 @@ describe('beforeSendSpan callback formats', () => { }); describe('withStreamedSpan', () => { - it('marks the callback as streamed', () => { + it('returns the callback unchanged', () => { const beforeSendSpan = vi.fn(); - const wrapped = withStreamedSpan(beforeSendSpan); - expect(wrapped._streamed).toBe(true); + + expect(withStreamedSpan(beforeSendSpan)).toBe(beforeSendSpan); + expect(Object.keys(beforeSendSpan)).not.toContain('_streamed'); }); }); diff --git a/packages/deno/src/index.ts b/packages/deno/src/index.ts index 505217df5ee7..4ff878591009 100644 --- a/packages/deno/src/index.ts +++ b/packages/deno/src/index.ts @@ -98,7 +98,7 @@ export { featureFlagsIntegration, metrics, withStaticSpan, - // eslint-disable-next-line typescript/no-deprecated + // oxlint-disable-next-line typescript/no-deprecated withStreamedSpan, logger, consoleLoggingIntegration, diff --git a/packages/effect/src/index.types.ts b/packages/effect/src/index.types.ts index 940d59f49f7f..bc77a68bd35d 100644 --- a/packages/effect/src/index.types.ts +++ b/packages/effect/src/index.types.ts @@ -23,7 +23,7 @@ export declare const linkedErrorsIntegration: typeof clientSdk.linkedErrorsInteg export declare const contextLinesIntegration: typeof clientSdk.contextLinesIntegration; export declare const spanStreamingIntegration: typeof clientSdk.spanStreamingIntegration; export declare const withStaticSpan: typeof clientSdk.withStaticSpan; -// eslint-disable-next-line typescript/no-deprecated +// oxlint-disable-next-line typescript/no-deprecated export declare const withStreamedSpan: typeof clientSdk.withStreamedSpan; export declare const getDefaultIntegrations: (options: Options) => Integration[]; export declare const defaultStackParser: StackParser; diff --git a/packages/elysia/src/index.ts b/packages/elysia/src/index.ts index d5939487ea8d..74257f226a90 100644 --- a/packages/elysia/src/index.ts +++ b/packages/elysia/src/index.ts @@ -156,7 +156,7 @@ export { metrics, spanStreamingIntegration, withStaticSpan, - // eslint-disable-next-line typescript/no-deprecated + // oxlint-disable-next-line typescript/no-deprecated withStreamedSpan, bunServerIntegration, makeFetchTransport, diff --git a/packages/google-cloud-serverless/src/index.ts b/packages/google-cloud-serverless/src/index.ts index 2a9978672951..191c79a6756b 100644 --- a/packages/google-cloud-serverless/src/index.ts +++ b/packages/google-cloud-serverless/src/index.ts @@ -161,7 +161,7 @@ export { metrics, spanStreamingIntegration, withStaticSpan, - // eslint-disable-next-line typescript/no-deprecated + // oxlint-disable-next-line typescript/no-deprecated withStreamedSpan, } from '@sentry/node'; diff --git a/packages/nextjs/src/index.types.ts b/packages/nextjs/src/index.types.ts index 151d338cf3ce..42ef9d6a9502 100644 --- a/packages/nextjs/src/index.types.ts +++ b/packages/nextjs/src/index.types.ts @@ -28,7 +28,7 @@ export declare const consoleIntegration: typeof serverSdk.consoleIntegration; export declare const pinoIntegration: typeof serverSdk.pinoIntegration; export declare const spanStreamingIntegration: typeof clientSdk.spanStreamingIntegration; export declare const withStaticSpan: typeof clientSdk.withStaticSpan; -// eslint-disable-next-line typescript/no-deprecated +// oxlint-disable-next-line typescript/no-deprecated export declare const withStreamedSpan: typeof clientSdk.withStreamedSpan; // Different implementation in server and worker diff --git a/packages/node/src/index.ts b/packages/node/src/index.ts index 7b82407809db..ce59b6083d99 100644 --- a/packages/node/src/index.ts +++ b/packages/node/src/index.ts @@ -177,7 +177,7 @@ export type { CaptureContext, } from '@sentry/core'; -// eslint-disable-next-line typescript/no-deprecated +// oxlint-disable-next-line typescript/no-deprecated export { metrics, withStaticSpan, withStreamedSpan } from '@sentry/core'; export * as logger from './logs/exports'; diff --git a/packages/nuxt/src/index.types.ts b/packages/nuxt/src/index.types.ts index e2e8ba3b66d0..4865aac868d3 100644 --- a/packages/nuxt/src/index.types.ts +++ b/packages/nuxt/src/index.types.ts @@ -18,7 +18,7 @@ export declare const linkedErrorsIntegration: typeof clientSdk.linkedErrorsInteg export declare const contextLinesIntegration: typeof clientSdk.contextLinesIntegration; export declare const spanStreamingIntegration: typeof clientSdk.spanStreamingIntegration; export declare const withStaticSpan: typeof clientSdk.withStaticSpan; -// eslint-disable-next-line typescript/no-deprecated +// oxlint-disable-next-line typescript/no-deprecated export declare const withStreamedSpan: typeof clientSdk.withStreamedSpan; export declare const getDefaultIntegrations: (options: Options) => Integration[]; export declare const defaultStackParser: StackParser; diff --git a/packages/react-router/src/index.types.ts b/packages/react-router/src/index.types.ts index 16f2eca441a3..af8c4da6d94a 100644 --- a/packages/react-router/src/index.types.ts +++ b/packages/react-router/src/index.types.ts @@ -18,7 +18,7 @@ export declare const contextLinesIntegration: typeof clientSdk.contextLinesInteg export declare const linkedErrorsIntegration: typeof clientSdk.linkedErrorsIntegration; export declare const spanStreamingIntegration: typeof clientSdk.spanStreamingIntegration; export declare const withStaticSpan: typeof clientSdk.withStaticSpan; -// eslint-disable-next-line typescript/no-deprecated +// oxlint-disable-next-line typescript/no-deprecated export declare const withStreamedSpan: typeof clientSdk.withStreamedSpan; export declare const defaultStackParser: StackParser; export declare const getDefaultIntegrations: (options: Options) => Integration[]; diff --git a/packages/remix/src/cloudflare/index.ts b/packages/remix/src/cloudflare/index.ts index a91869f0d976..99c642506693 100644 --- a/packages/remix/src/cloudflare/index.ts +++ b/packages/remix/src/cloudflare/index.ts @@ -118,7 +118,7 @@ export { spanToBaggageHeader, updateSpanName, withStaticSpan, - // eslint-disable-next-line typescript/no-deprecated + // oxlint-disable-next-line typescript/no-deprecated withStreamedSpan, featureFlagsIntegration, } from '@sentry/core'; diff --git a/packages/remix/src/index.types.ts b/packages/remix/src/index.types.ts index 867bc011dfc2..498b640d7e6e 100644 --- a/packages/remix/src/index.types.ts +++ b/packages/remix/src/index.types.ts @@ -20,7 +20,7 @@ export declare const linkedErrorsIntegration: typeof clientSdk.linkedErrorsInteg export declare const contextLinesIntegration: typeof clientSdk.contextLinesIntegration; export declare const spanStreamingIntegration: typeof clientSdk.spanStreamingIntegration; export declare const withStaticSpan: typeof clientSdk.withStaticSpan; -// eslint-disable-next-line typescript/no-deprecated +// oxlint-disable-next-line typescript/no-deprecated export declare const withStreamedSpan: typeof clientSdk.withStreamedSpan; export declare const getDefaultIntegrations: (options: Options) => Integration[]; diff --git a/packages/remix/src/server/index.ts b/packages/remix/src/server/index.ts index e21dffa78807..88350f5e593d 100644 --- a/packages/remix/src/server/index.ts +++ b/packages/remix/src/server/index.ts @@ -134,7 +134,7 @@ export { createSentryWinstonTransport, spanStreamingIntegration, withStaticSpan, - // eslint-disable-next-line typescript/no-deprecated + // oxlint-disable-next-line typescript/no-deprecated withStreamedSpan, } from '@sentry/node'; diff --git a/packages/solidstart/src/index.types.ts b/packages/solidstart/src/index.types.ts index 1b458e7cf929..71e45f1d2a2d 100644 --- a/packages/solidstart/src/index.types.ts +++ b/packages/solidstart/src/index.types.ts @@ -20,7 +20,7 @@ export declare const linkedErrorsIntegration: typeof clientSdk.linkedErrorsInteg export declare const contextLinesIntegration: typeof clientSdk.contextLinesIntegration; export declare const spanStreamingIntegration: typeof clientSdk.spanStreamingIntegration; export declare const withStaticSpan: typeof clientSdk.withStaticSpan; -// eslint-disable-next-line typescript/no-deprecated +// oxlint-disable-next-line typescript/no-deprecated export declare const withStreamedSpan: typeof clientSdk.withStreamedSpan; export declare const getDefaultIntegrations: (options: Options) => Integration[]; diff --git a/packages/solidstart/src/server/index.ts b/packages/solidstart/src/server/index.ts index e46cd2744e49..dc8640b965e8 100644 --- a/packages/solidstart/src/server/index.ts +++ b/packages/solidstart/src/server/index.ts @@ -133,7 +133,7 @@ export { createSentryWinstonTransport, spanStreamingIntegration, withStaticSpan, - // eslint-disable-next-line typescript/no-deprecated + // oxlint-disable-next-line typescript/no-deprecated withStreamedSpan, } from '@sentry/node'; diff --git a/packages/sveltekit/src/index.types.ts b/packages/sveltekit/src/index.types.ts index 0a328b38ab1b..5404d001b205 100644 --- a/packages/sveltekit/src/index.types.ts +++ b/packages/sveltekit/src/index.types.ts @@ -49,7 +49,7 @@ export declare const linkedErrorsIntegration: typeof clientSdk.linkedErrorsInteg export declare const contextLinesIntegration: typeof clientSdk.contextLinesIntegration; export declare const spanStreamingIntegration: typeof clientSdk.spanStreamingIntegration; export declare const withStaticSpan: typeof clientSdk.withStaticSpan; -// eslint-disable-next-line typescript/no-deprecated +// oxlint-disable-next-line typescript/no-deprecated export declare const withStreamedSpan: typeof clientSdk.withStreamedSpan; // Different implementation in server and worker diff --git a/packages/sveltekit/src/server/index.ts b/packages/sveltekit/src/server/index.ts index a6defb62c39d..07a8147cefe8 100644 --- a/packages/sveltekit/src/server/index.ts +++ b/packages/sveltekit/src/server/index.ts @@ -138,7 +138,7 @@ export { metrics, spanStreamingIntegration, withStaticSpan, - // eslint-disable-next-line typescript/no-deprecated + // oxlint-disable-next-line typescript/no-deprecated withStreamedSpan, } from '@sentry/node'; diff --git a/packages/sveltekit/src/worker/index.ts b/packages/sveltekit/src/worker/index.ts index e52ebc8aedc4..65fe98529a5c 100644 --- a/packages/sveltekit/src/worker/index.ts +++ b/packages/sveltekit/src/worker/index.ts @@ -84,7 +84,7 @@ export { withMonitor, withScope, withStaticSpan, - // eslint-disable-next-line typescript/no-deprecated + // oxlint-disable-next-line typescript/no-deprecated withStreamedSpan, supabaseIntegration, instrumentSupabaseClient, diff --git a/packages/tanstackstart-react/src/index.types.ts b/packages/tanstackstart-react/src/index.types.ts index faedd2ab18b5..d1464a88175e 100644 --- a/packages/tanstackstart-react/src/index.types.ts +++ b/packages/tanstackstart-react/src/index.types.ts @@ -20,7 +20,7 @@ export declare const linkedErrorsIntegration: typeof clientSdk.linkedErrorsInteg export declare const contextLinesIntegration: typeof clientSdk.contextLinesIntegration; export declare const spanStreamingIntegration: typeof clientSdk.spanStreamingIntegration; export declare const withStaticSpan: typeof clientSdk.withStaticSpan; -// eslint-disable-next-line typescript/no-deprecated +// oxlint-disable-next-line typescript/no-deprecated export declare const withStreamedSpan: typeof clientSdk.withStreamedSpan; export declare const getDefaultIntegrations: (options: Options) => Integration[]; diff --git a/packages/vercel-edge/src/index.ts b/packages/vercel-edge/src/index.ts index 7fef86995fee..29831c013835 100644 --- a/packages/vercel-edge/src/index.ts +++ b/packages/vercel-edge/src/index.ts @@ -106,7 +106,7 @@ export { logger, metrics, withStaticSpan, - // eslint-disable-next-line typescript/no-deprecated + // oxlint-disable-next-line typescript/no-deprecated withStreamedSpan, spanStreamingIntegration, } from '@sentry/core';