From 4cba0db91ef14f9118b52e7d6713b1161e142e22 Mon Sep 17 00:00:00 2001 From: Lukas Stracke Date: Wed, 8 Apr 2026 15:04:53 +0200 Subject: [PATCH 1/5] fix(core): Fix `withStreamedSpan` typing error and export from SDKs --- .../beforeSendSpan-streamed/init.js | 28 ++++++++++++ .../beforeSendSpan-streamed/test.ts | 35 +++++++++++++++ .../beforeSendSpan-streamed/scenario.ts | 43 +++++++++++++++++++ .../beforeSendSpan-streamed/test.ts | 32 ++++++++++++++ packages/angular/src/sdk.ts | 8 ++++ packages/astro/src/index.server.ts | 1 + packages/astro/src/index.types.ts | 1 + packages/aws-serverless/src/index.ts | 1 + packages/browser/src/exports.ts | 1 + .../browser/src/integrations/spanstreaming.ts | 2 +- packages/bun/src/index.ts | 1 + packages/cloudflare/src/index.ts | 1 + .../core/src/tracing/spans/beforeSendSpan.ts | 15 ++++--- packages/core/src/types-hoist/options.ts | 4 +- packages/deno/src/index.ts | 1 + packages/effect/src/index.types.ts | 1 + packages/elysia/src/index.ts | 2 + packages/google-cloud-serverless/src/index.ts | 1 + packages/nextjs/src/index.types.ts | 1 + packages/node-core/src/common-exports.ts | 1 + packages/node/src/index.ts | 1 + packages/nuxt/src/index.types.ts | 1 + packages/opentelemetry/src/index.ts | 2 + packages/react-router/src/index.types.ts | 1 + packages/remix/src/cloudflare/index.ts | 1 + packages/remix/src/index.types.ts | 1 + packages/remix/src/server/index.ts | 1 + packages/solidstart/src/index.types.ts | 1 + packages/solidstart/src/server/index.ts | 1 + packages/sveltekit/src/index.types.ts | 1 + packages/sveltekit/src/server/index.ts | 1 + packages/sveltekit/src/worker/index.ts | 1 + .../tanstackstart-react/src/index.types.ts | 1 + packages/vercel-edge/src/index.ts | 1 + 34 files changed, 186 insertions(+), 9 deletions(-) create mode 100644 dev-packages/browser-integration-tests/suites/public-api/beforeSendSpan-streamed/init.js create mode 100644 dev-packages/browser-integration-tests/suites/public-api/beforeSendSpan-streamed/test.ts create mode 100644 dev-packages/node-core-integration-tests/suites/public-api/beforeSendSpan-streamed/scenario.ts create mode 100644 dev-packages/node-core-integration-tests/suites/public-api/beforeSendSpan-streamed/test.ts 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 new file mode 100644 index 000000000000..cd4c7480558e --- /dev/null +++ b/dev-packages/browser-integration-tests/suites/public-api/beforeSendSpan-streamed/init.js @@ -0,0 +1,28 @@ +import * as Sentry from '@sentry/browser'; + +window.Sentry = Sentry; + +Sentry.init({ + dsn: 'https://public@dsn.ingest.sentry.io/1337', + integrations: [Sentry.browserTracingIntegration(), Sentry.spanStreamingIntegration()], + tracesSampleRate: 1, + beforeSendSpan: Sentry.withStreamedSpan(span => { + if (span.attributes['sentry.op'] === 'pageload') { + span.name = 'customPageloadSpanName'; + span.links = [ + { + context: { + traceId: '123', + spanId: '456', + }, + attributes: { + 'sentry.link.type': 'custom_link', + }, + }, + ]; + span.attributes['sentry.custom_attribute'] = 'customAttributeValue'; + span.status = 'something' + } + return span; + }), +}); diff --git a/dev-packages/browser-integration-tests/suites/public-api/beforeSendSpan-streamed/test.ts b/dev-packages/browser-integration-tests/suites/public-api/beforeSendSpan-streamed/test.ts new file mode 100644 index 000000000000..ce07297c0a04 --- /dev/null +++ b/dev-packages/browser-integration-tests/suites/public-api/beforeSendSpan-streamed/test.ts @@ -0,0 +1,35 @@ +import { expect } from '@playwright/test'; +import { sentryTest } from '../../../utils/fixtures'; +import { shouldSkipTracingTest, testingCdnBundle } from '../../../utils/helpers'; +import { getSpanOp, waitForStreamedSpan } from '../../../utils/spanUtils'; + +sentryTest('beforeSendSpan applies changes to streamed span', async ({ getLocalTestUrl, page }) => { + sentryTest.skip(shouldSkipTracingTest() || testingCdnBundle()); + + const url = await getLocalTestUrl({ testDir: __dirname }); + + const pageloadSpanPromise = waitForStreamedSpan(page, span => getSpanOp(span) === 'pageload'); + + await page.goto(url); + + const pageloadSpan = await pageloadSpanPromise; + + expect(pageloadSpan.name).toBe('customPageloadSpanName'); + expect(pageloadSpan.links).toEqual([ + { + context: { + traceId: '123', + spanId: '456', + }, + attributes: { + 'sentry.link.type': { type: 'string', value: 'custom_link' }, + }, + }, + ]); + expect(pageloadSpan.attributes?.['sentry.custom_attribute']).toEqual({ + type: 'string', + value: 'customAttributeValue', + }); + // we allow overriding any kinds of fields on the span, so we have to expect invalid values + expect(pageloadSpan.status).toBe('something'); +}); diff --git a/dev-packages/node-core-integration-tests/suites/public-api/beforeSendSpan-streamed/scenario.ts b/dev-packages/node-core-integration-tests/suites/public-api/beforeSendSpan-streamed/scenario.ts new file mode 100644 index 000000000000..74cb01d6eb10 --- /dev/null +++ b/dev-packages/node-core-integration-tests/suites/public-api/beforeSendSpan-streamed/scenario.ts @@ -0,0 +1,43 @@ +import * as Sentry from '@sentry/node-core'; +import { loggingTransport } from '@sentry-internal/node-integration-tests'; +import { setupOtel } from '../../../utils/setupOtel'; + +const client = 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 => { + if (span.name === 'test-child-span') { + span.name = 'customChildSpanName'; + if (!span.attributes) { + span.attributes = {}; + } + span.attributes['sentry.custom_attribute'] = 'customAttributeValue'; + // oxlint-disable-next-line typescript/ban-ts-comment + // @ts-expect-error - technically this is something we have to expect, despite types saying it's invalid + span.status = 'something'; + span.links = [ + { + trace_id: '123', + span_id: '456', + attributes: { + 'sentry.link.type': 'custom_link', + }, + }, + ]; + } + return span; + }), +}); + +setupOtel(client); + +Sentry.startSpan({ name: 'test-span', op: 'test' }, () => { + Sentry.startSpan({ name: 'test-child-span', op: 'test-child' }, () => { + // noop + }); +}); + +void Sentry.flush(); diff --git a/dev-packages/node-core-integration-tests/suites/public-api/beforeSendSpan-streamed/test.ts b/dev-packages/node-core-integration-tests/suites/public-api/beforeSendSpan-streamed/test.ts new file mode 100644 index 000000000000..0de0f732f552 --- /dev/null +++ b/dev-packages/node-core-integration-tests/suites/public-api/beforeSendSpan-streamed/test.ts @@ -0,0 +1,32 @@ +import { expect, test } from 'vitest'; +import { createRunner } from '../../../utils/runner'; + +test('beforeSendSpan applies changes to streamed span', async () => { + await createRunner(__dirname, 'scenario.ts') + .expect({ + span: container => { + const spans = container.items; + expect(spans.length).toBe(2); + + const customChildSpan = spans.find(s => s.name === 'customChildSpanName'); + + expect(customChildSpan).toBeDefined(); + expect(customChildSpan!.attributes?.['sentry.custom_attribute']).toEqual({ + type: 'string', + value: 'customAttributeValue', + }); + expect(customChildSpan!.status).toBe('something'); + expect(customChildSpan!.links).toEqual([ + { + trace_id: '123', + span_id: '456', + attributes: { + 'sentry.link.type': { type: 'string', value: 'custom_link' }, + }, + }, + ]); + }, + }) + .start() + .completed(); +}); diff --git a/packages/angular/src/sdk.ts b/packages/angular/src/sdk.ts index 1fff722f964e..71e5e113b2c5 100755 --- a/packages/angular/src/sdk.ts +++ b/packages/angular/src/sdk.ts @@ -82,3 +82,11 @@ function checkAndSetAngularVersion(): void { setContext('angular', { version: angularVersion }); } } + +init({ + dsn: '__DSN__', + traceLifecycle: 'stream', + beforeSendSpan: span => { + return span; + }, +}); diff --git a/packages/astro/src/index.server.ts b/packages/astro/src/index.server.ts index fb03d5fc57af..b4fc6fccbee3 100644 --- a/packages/astro/src/index.server.ts +++ b/packages/astro/src/index.server.ts @@ -174,6 +174,7 @@ export { unleashIntegration, growthbookIntegration, spanStreamingIntegration, + withStreamedSpan, metrics, } from '@sentry/node'; diff --git a/packages/astro/src/index.types.ts b/packages/astro/src/index.types.ts index 8dfffdcbd852..0c8a5cca7bb6 100644 --- a/packages/astro/src/index.types.ts +++ b/packages/astro/src/index.types.ts @@ -21,6 +21,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 withStreamedSpan: typeof clientSdk.withStreamedSpan; export declare const getDefaultIntegrations: (options: Options) => Integration[]; export declare const defaultStackParser: StackParser; diff --git a/packages/aws-serverless/src/index.ts b/packages/aws-serverless/src/index.ts index a5044bc6b29e..0cbc893b4601 100644 --- a/packages/aws-serverless/src/index.ts +++ b/packages/aws-serverless/src/index.ts @@ -161,6 +161,7 @@ export { growthbookIntegration, metrics, spanStreamingIntegration, + withStreamedSpan, } from '@sentry/node'; export { diff --git a/packages/browser/src/exports.ts b/packages/browser/src/exports.ts index e77465a87b39..322cc0b3c8db 100644 --- a/packages/browser/src/exports.ts +++ b/packages/browser/src/exports.ts @@ -70,6 +70,7 @@ export { spanToTraceHeader, spanToBaggageHeader, updateSpanName, + withStreamedSpan, metrics, } from '@sentry/core'; diff --git a/packages/browser/src/integrations/spanstreaming.ts b/packages/browser/src/integrations/spanstreaming.ts index 9d1a296b5e57..4225b9342fca 100644 --- a/packages/browser/src/integrations/spanstreaming.ts +++ b/packages/browser/src/integrations/spanstreaming.ts @@ -19,7 +19,7 @@ export const spanStreamingIntegration = defineIntegration(() => { // This avoids the classic double-opt-in problem we'd otherwise have in the browser SDK. const clientOptions = client.getOptions(); if (!clientOptions.traceLifecycle) { - DEBUG_BUILD && debug.warn('[SpanStreaming] set `traceLifecycle` to "stream"'); + DEBUG_BUILD && debug.log('[SpanStreaming] set `traceLifecycle` to "stream"'); clientOptions.traceLifecycle = 'stream'; } }, diff --git a/packages/bun/src/index.ts b/packages/bun/src/index.ts index e80613bcc03a..c44742a0e2d3 100644 --- a/packages/bun/src/index.ts +++ b/packages/bun/src/index.ts @@ -179,6 +179,7 @@ export { unleashIntegration, metrics, spanStreamingIntegration, + withStreamedSpan, } from '@sentry/node'; export { diff --git a/packages/cloudflare/src/index.ts b/packages/cloudflare/src/index.ts index 7ce43c231c31..f69978064277 100644 --- a/packages/cloudflare/src/index.ts +++ b/packages/cloudflare/src/index.ts @@ -107,6 +107,7 @@ export { growthbookIntegration, logger, metrics, + withStreamedSpan, instrumentLangGraph, } from '@sentry/core'; diff --git a/packages/core/src/tracing/spans/beforeSendSpan.ts b/packages/core/src/tracing/spans/beforeSendSpan.ts index 84ec6a8a8b52..21caf10f1b0d 100644 --- a/packages/core/src/tracing/spans/beforeSendSpan.ts +++ b/packages/core/src/tracing/spans/beforeSendSpan.ts @@ -1,7 +1,10 @@ -import type { BeforeSendStramedSpanCallback, ClientOptions } from '../../types-hoist/options'; +import type { CoreOptions } from '../../types-hoist/options'; +import type { BeforeSendStreamedSpanCallback, ClientOptions } from '../../types-hoist/options'; import type { StreamedSpanJSON } from '../../types-hoist/span'; import { addNonEnumerableProperty } from '../../utils/object'; +type StaticBeforeSendSpanCallback = CoreOptions['beforeSendSpan']; + /** * A wrapper to use the new span format in your `beforeSendSpan` callback. * @@ -23,9 +26,9 @@ import { addNonEnumerableProperty } from '../../utils/object'; */ export function withStreamedSpan( callback: (span: StreamedSpanJSON) => StreamedSpanJSON, -): BeforeSendStramedSpanCallback { +): StaticBeforeSendSpanCallback & { _streamed: true } { addNonEnumerableProperty(callback, '_streamed', true); - return callback; + return callback as unknown as StaticBeforeSendSpanCallback & { _streamed: true }; } /** @@ -35,7 +38,7 @@ export function withStreamedSpan( * @returns `true` if the callback was wrapped with {@link withStreamedSpan}. */ export function isStreamedBeforeSendSpanCallback( - callback: ClientOptions['beforeSendSpan'], -): callback is BeforeSendStramedSpanCallback { - return !!callback && '_streamed' in callback && !!callback._streamed; + callback: (ClientOptions['beforeSendSpan'] & { _streamed?: true }) | unknown, +): callback is BeforeSendStreamedSpanCallback { + return !!callback && typeof callback === 'function' && '_streamed' in callback && !!callback._streamed; } diff --git a/packages/core/src/types-hoist/options.ts b/packages/core/src/types-hoist/options.ts index 63310a66c3d2..4f3df1f6365a 100644 --- a/packages/core/src/types-hoist/options.ts +++ b/packages/core/src/types-hoist/options.ts @@ -594,7 +594,7 @@ export interface ClientOptions SpanJSON) | BeforeSendStramedSpanCallback; + beforeSendSpan?: ((span: SpanJSON) => SpanJSON) & { _streamed?: true }; /** * An event-processing callback for transaction events, guaranteed to be invoked after all other event @@ -631,7 +631,7 @@ export interface ClientOptions StreamedSpanJSON) & { +export type BeforeSendStreamedSpanCallback = ((span: StreamedSpanJSON) => 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`. diff --git a/packages/deno/src/index.ts b/packages/deno/src/index.ts index 9d04cdb6d663..18bb26f06a4f 100644 --- a/packages/deno/src/index.ts +++ b/packages/deno/src/index.ts @@ -94,6 +94,7 @@ export { wrapMcpServerWithSentry, featureFlagsIntegration, metrics, + withStreamedSpan, logger, consoleLoggingIntegration, } from '@sentry/core'; diff --git a/packages/effect/src/index.types.ts b/packages/effect/src/index.types.ts index e811a9bc7c81..153f7eba465e 100644 --- a/packages/effect/src/index.types.ts +++ b/packages/effect/src/index.types.ts @@ -22,6 +22,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 withStreamedSpan: typeof clientSdk.withStreamedSpan; export declare const getDefaultIntegrations: (options: Options) => Integration[]; export declare const defaultStackParser: StackParser; export declare const logger: typeof clientSdk.logger | typeof serverSdk.logger; diff --git a/packages/elysia/src/index.ts b/packages/elysia/src/index.ts index cd42a0ff0f31..55542acfc55f 100644 --- a/packages/elysia/src/index.ts +++ b/packages/elysia/src/index.ts @@ -155,6 +155,8 @@ export { statsigIntegration, unleashIntegration, metrics, + spanStreamingIntegration, + withStreamedSpan, bunServerIntegration, makeFetchTransport, } from '@sentry/bun'; diff --git a/packages/google-cloud-serverless/src/index.ts b/packages/google-cloud-serverless/src/index.ts index da2a32b06e75..2662ef4720a0 100644 --- a/packages/google-cloud-serverless/src/index.ts +++ b/packages/google-cloud-serverless/src/index.ts @@ -161,6 +161,7 @@ export { unleashIntegration, metrics, spanStreamingIntegration, + withStreamedSpan, } from '@sentry/node'; export { diff --git a/packages/nextjs/src/index.types.ts b/packages/nextjs/src/index.types.ts index 1ac235711ca5..2ae03ae3f204 100644 --- a/packages/nextjs/src/index.types.ts +++ b/packages/nextjs/src/index.types.ts @@ -24,6 +24,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; +export declare const withStreamedSpan: typeof clientSdk.withStreamedSpan; // 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 3d9dbfea1f79..1c724d2c29f6 100644 --- a/packages/node-core/src/common-exports.ts +++ b/packages/node-core/src/common-exports.ts @@ -121,6 +121,7 @@ export { wrapMcpServerWithSentry, featureFlagsIntegration, spanStreamingIntegration, + withStreamedSpan, metrics, envToBool, } from '@sentry/core'; diff --git a/packages/node/src/index.ts b/packages/node/src/index.ts index 55fab2ed39bf..ce9458079980 100644 --- a/packages/node/src/index.ts +++ b/packages/node/src/index.ts @@ -205,5 +205,6 @@ export { cron, NODE_VERSION, validateOpenTelemetrySetup, + withStreamedSpan, _INTERNAL_normalizeCollectionInterval, } from '@sentry/node-core'; diff --git a/packages/nuxt/src/index.types.ts b/packages/nuxt/src/index.types.ts index 9a0283cfcee8..e058e0bfd099 100644 --- a/packages/nuxt/src/index.types.ts +++ b/packages/nuxt/src/index.types.ts @@ -17,6 +17,7 @@ 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 withStreamedSpan: typeof clientSdk.withStreamedSpan; export declare const getDefaultIntegrations: (options: Options) => Integration[]; export declare const defaultStackParser: StackParser; diff --git a/packages/opentelemetry/src/index.ts b/packages/opentelemetry/src/index.ts index e0112812dc69..f5260dc852c5 100644 --- a/packages/opentelemetry/src/index.ts +++ b/packages/opentelemetry/src/index.ts @@ -48,5 +48,7 @@ export { SentrySampler, wrapSamplingDecision } from './sampler'; export { openTelemetrySetupCheck } from './utils/setupCheck'; +export { withStreamedSpan } from '@sentry/core'; + // Legacy export { getClient } from '@sentry/core'; diff --git a/packages/react-router/src/index.types.ts b/packages/react-router/src/index.types.ts index 4d0abcb6b0a8..e7fb4382d0f2 100644 --- a/packages/react-router/src/index.types.ts +++ b/packages/react-router/src/index.types.ts @@ -17,6 +17,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 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 ede127a694ce..97f2609bf10d 100644 --- a/packages/remix/src/cloudflare/index.ts +++ b/packages/remix/src/cloudflare/index.ts @@ -114,5 +114,6 @@ export { spanToTraceHeader, spanToBaggageHeader, updateSpanName, + withStreamedSpan, featureFlagsIntegration, } from '@sentry/core'; diff --git a/packages/remix/src/index.types.ts b/packages/remix/src/index.types.ts index 1cac41c1aacf..1a54068ae019 100644 --- a/packages/remix/src/index.types.ts +++ b/packages/remix/src/index.types.ts @@ -19,6 +19,7 @@ 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 withStreamedSpan: typeof clientSdk.withStreamedSpan; export declare const getDefaultIntegrations: (options: Options) => Integration[]; export declare const defaultStackParser: StackParser; diff --git a/packages/remix/src/server/index.ts b/packages/remix/src/server/index.ts index 86be74079ce3..cc5f92bc2948 100644 --- a/packages/remix/src/server/index.ts +++ b/packages/remix/src/server/index.ts @@ -132,6 +132,7 @@ export { createConsolaReporter, createSentryWinstonTransport, spanStreamingIntegration, + withStreamedSpan, } from '@sentry/node'; // Keeping the `*` exports for backwards compatibility and types diff --git a/packages/solidstart/src/index.types.ts b/packages/solidstart/src/index.types.ts index 58e642ecba22..d984e1384a93 100644 --- a/packages/solidstart/src/index.types.ts +++ b/packages/solidstart/src/index.types.ts @@ -19,6 +19,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 withStreamedSpan: typeof clientSdk.withStreamedSpan; export declare const getDefaultIntegrations: (options: Options) => Integration[]; export declare const defaultStackParser: StackParser; diff --git a/packages/solidstart/src/server/index.ts b/packages/solidstart/src/server/index.ts index ffc73956a530..06c95b7bbc8b 100644 --- a/packages/solidstart/src/server/index.ts +++ b/packages/solidstart/src/server/index.ts @@ -131,6 +131,7 @@ export { createConsolaReporter, createSentryWinstonTransport, spanStreamingIntegration, + withStreamedSpan, } from '@sentry/node'; // We can still leave this for the carrier init and type exports diff --git a/packages/sveltekit/src/index.types.ts b/packages/sveltekit/src/index.types.ts index 78cb27e2e47d..1f4bbae0b4a9 100644 --- a/packages/sveltekit/src/index.types.ts +++ b/packages/sveltekit/src/index.types.ts @@ -48,6 +48,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; +export declare const withStreamedSpan: typeof clientSdk.withStreamedSpan; // Different implementation in server and worker export declare const vercelAIIntegration: typeof serverSdk.vercelAIIntegration; diff --git a/packages/sveltekit/src/server/index.ts b/packages/sveltekit/src/server/index.ts index 8db039bb4369..4bd05d6f4657 100644 --- a/packages/sveltekit/src/server/index.ts +++ b/packages/sveltekit/src/server/index.ts @@ -136,6 +136,7 @@ export { vercelAIIntegration, metrics, spanStreamingIntegration, + withStreamedSpan, } from '@sentry/node'; // We can still leave this for the carrier init and type exports diff --git a/packages/sveltekit/src/worker/index.ts b/packages/sveltekit/src/worker/index.ts index 8e4645741456..89beca6d718f 100644 --- a/packages/sveltekit/src/worker/index.ts +++ b/packages/sveltekit/src/worker/index.ts @@ -80,6 +80,7 @@ export { withIsolationScope, withMonitor, withScope, + withStreamedSpan, supabaseIntegration, instrumentSupabaseClient, zodErrorsIntegration, diff --git a/packages/tanstackstart-react/src/index.types.ts b/packages/tanstackstart-react/src/index.types.ts index 0545fbf5c87b..76e4ced73c92 100644 --- a/packages/tanstackstart-react/src/index.types.ts +++ b/packages/tanstackstart-react/src/index.types.ts @@ -19,6 +19,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 withStreamedSpan: typeof clientSdk.withStreamedSpan; export declare const getDefaultIntegrations: (options: Options) => Integration[]; export declare const defaultStackParser: StackParser; diff --git a/packages/vercel-edge/src/index.ts b/packages/vercel-edge/src/index.ts index 23f211d41f2a..c10ca12ebecb 100644 --- a/packages/vercel-edge/src/index.ts +++ b/packages/vercel-edge/src/index.ts @@ -102,6 +102,7 @@ export { featureFlagsIntegration, logger, metrics, + withStreamedSpan, } from '@sentry/core'; export { VercelEdgeClient } from './client'; From 44ea53197752a7c132eff152774395ce487df1bb Mon Sep 17 00:00:00 2001 From: Lukas Stracke Date: Wed, 8 Apr 2026 15:14:41 +0200 Subject: [PATCH 2/5] format --- .../suites/public-api/beforeSendSpan-streamed/init.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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 cd4c7480558e..f099c1f61c3e 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 @@ -21,7 +21,7 @@ Sentry.init({ }, ]; span.attributes['sentry.custom_attribute'] = 'customAttributeValue'; - span.status = 'something' + span.status = 'something'; } return span; }), From 53f5d9cbd690c1f71e86aee1bec88178e7ca2340 Mon Sep 17 00:00:00 2001 From: Lukas Stracke Date: Wed, 8 Apr 2026 15:22:51 +0200 Subject: [PATCH 3/5] fix leftover debug init and reduce to unknown in type check --- packages/angular/src/sdk.ts | 8 -------- packages/core/src/tracing/spans/beforeSendSpan.ts | 4 +--- 2 files changed, 1 insertion(+), 11 deletions(-) diff --git a/packages/angular/src/sdk.ts b/packages/angular/src/sdk.ts index 71e5e113b2c5..1fff722f964e 100755 --- a/packages/angular/src/sdk.ts +++ b/packages/angular/src/sdk.ts @@ -82,11 +82,3 @@ function checkAndSetAngularVersion(): void { setContext('angular', { version: angularVersion }); } } - -init({ - dsn: '__DSN__', - traceLifecycle: 'stream', - beforeSendSpan: span => { - return span; - }, -}); diff --git a/packages/core/src/tracing/spans/beforeSendSpan.ts b/packages/core/src/tracing/spans/beforeSendSpan.ts index 21caf10f1b0d..a0206d5f250e 100644 --- a/packages/core/src/tracing/spans/beforeSendSpan.ts +++ b/packages/core/src/tracing/spans/beforeSendSpan.ts @@ -37,8 +37,6 @@ export function withStreamedSpan( * @param callback - The `beforeSendSpan` callback to check. * @returns `true` if the callback was wrapped with {@link withStreamedSpan}. */ -export function isStreamedBeforeSendSpanCallback( - callback: (ClientOptions['beforeSendSpan'] & { _streamed?: true }) | unknown, -): callback is BeforeSendStreamedSpanCallback { +export function isStreamedBeforeSendSpanCallback(callback: unknown): callback is BeforeSendStreamedSpanCallback { return !!callback && typeof callback === 'function' && '_streamed' in callback && !!callback._streamed; } From b6e5ea9d00d30e15dd243e0d5fb1c732a9ae4860 Mon Sep 17 00:00:00 2001 From: Lukas Stracke Date: Thu, 9 Apr 2026 10:38:05 +0200 Subject: [PATCH 4/5] fix integration test (not our fault but express instrumentation changed) --- .../tracing/ignoreSpans-streamed/children/instrument.mjs | 2 +- .../suites/tracing/ignoreSpans-streamed/children/server.mjs | 6 ++++++ .../suites/tracing/ignoreSpans-streamed/segments/server.mjs | 5 +++++ 3 files changed, 12 insertions(+), 1 deletion(-) diff --git a/dev-packages/node-integration-tests/suites/tracing/ignoreSpans-streamed/children/instrument.mjs b/dev-packages/node-integration-tests/suites/tracing/ignoreSpans-streamed/children/instrument.mjs index 04f3908530c0..266ffdc4f52c 100644 --- a/dev-packages/node-integration-tests/suites/tracing/ignoreSpans-streamed/children/instrument.mjs +++ b/dev-packages/node-integration-tests/suites/tracing/ignoreSpans-streamed/children/instrument.mjs @@ -7,6 +7,6 @@ Sentry.init({ tracesSampleRate: 1.0, transport: loggingTransport, traceLifecycle: 'stream', - ignoreSpans: ['middleware - expressInit', /custom-to-drop/, { op: 'ignored-op' }], + ignoreSpans: ['expressInit', /custom-to-drop/, { op: 'ignored-op' }], clientReportFlushInterval: 1_000, }); diff --git a/dev-packages/node-integration-tests/suites/tracing/ignoreSpans-streamed/children/server.mjs b/dev-packages/node-integration-tests/suites/tracing/ignoreSpans-streamed/children/server.mjs index a79ef373ec11..e8af8b8c92c6 100644 --- a/dev-packages/node-integration-tests/suites/tracing/ignoreSpans-streamed/children/server.mjs +++ b/dev-packages/node-integration-tests/suites/tracing/ignoreSpans-streamed/children/server.mjs @@ -38,6 +38,12 @@ app.get('/test/express', (_req, res) => { () => {}, ); res.send({ response: 'response 1' }); + + setTimeout(() => { + // flush to avoid waiting for the span buffer timeout to send spans + // but defer it to the next tick to let the SDK finish the http.server span first. + Sentry.flush(); + }); }); Sentry.setupExpressErrorHandler(app); diff --git a/dev-packages/node-integration-tests/suites/tracing/ignoreSpans-streamed/segments/server.mjs b/dev-packages/node-integration-tests/suites/tracing/ignoreSpans-streamed/segments/server.mjs index f9c7f136aef2..b9ff3398b158 100644 --- a/dev-packages/node-integration-tests/suites/tracing/ignoreSpans-streamed/segments/server.mjs +++ b/dev-packages/node-integration-tests/suites/tracing/ignoreSpans-streamed/segments/server.mjs @@ -13,6 +13,11 @@ app.get('/health', (_req, res) => { app.get('/ok', (_req, res) => { res.send({ status: 'ok' }); + setTimeout(() => { + // flush to avoid waiting for the span buffer timeout to send spans + // but defer it to the next tick to let the SDK finish the http.server span first. + Sentry.flush(); + }); }); Sentry.setupExpressErrorHandler(app); From ab520d6c5e3e3354d4116d0a2dfa03ddde8069cf Mon Sep 17 00:00:00 2001 From: Lukas Stracke Date: Thu, 9 Apr 2026 11:00:40 +0200 Subject: [PATCH 5/5] maybe fix ts 3.8 test --- .../suites/public-api/beforeSendSpan-streamed/scenario.ts | 3 +-- packages/core/src/tracing/spans/beforeSendSpan.ts | 2 +- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/dev-packages/node-core-integration-tests/suites/public-api/beforeSendSpan-streamed/scenario.ts b/dev-packages/node-core-integration-tests/suites/public-api/beforeSendSpan-streamed/scenario.ts index 74cb01d6eb10..bd85d6b9776d 100644 --- a/dev-packages/node-core-integration-tests/suites/public-api/beforeSendSpan-streamed/scenario.ts +++ b/dev-packages/node-core-integration-tests/suites/public-api/beforeSendSpan-streamed/scenario.ts @@ -15,8 +15,7 @@ const client = Sentry.init({ span.attributes = {}; } span.attributes['sentry.custom_attribute'] = 'customAttributeValue'; - // oxlint-disable-next-line typescript/ban-ts-comment - // @ts-expect-error - technically this is something we have to expect, despite types saying it's invalid + // @ts-ignore - technically this is something we have to expect, despite types saying it's invalid span.status = 'something'; span.links = [ { diff --git a/packages/core/src/tracing/spans/beforeSendSpan.ts b/packages/core/src/tracing/spans/beforeSendSpan.ts index a0206d5f250e..28b1849c7779 100644 --- a/packages/core/src/tracing/spans/beforeSendSpan.ts +++ b/packages/core/src/tracing/spans/beforeSendSpan.ts @@ -1,5 +1,5 @@ import type { CoreOptions } from '../../types-hoist/options'; -import type { BeforeSendStreamedSpanCallback, ClientOptions } from '../../types-hoist/options'; +import type { BeforeSendStreamedSpanCallback } from '../../types-hoist/options'; import type { StreamedSpanJSON } from '../../types-hoist/span'; import { addNonEnumerableProperty } from '../../utils/object';