diff --git a/dev-packages/browser-integration-tests/suites/integrations/featureFlags/constants.ts b/dev-packages/browser-integration-tests/suites/integrations/featureFlags/constants.ts deleted file mode 100644 index ba3c35a08241..000000000000 --- a/dev-packages/browser-integration-tests/suites/integrations/featureFlags/constants.ts +++ /dev/null @@ -1,3 +0,0 @@ -// Corresponds to constants in featureFlags.ts, in browser utils. -export const FLAG_BUFFER_SIZE = 100; -export const MAX_FLAGS_PER_SPAN = 10; diff --git a/dev-packages/browser-integration-tests/suites/integrations/featureFlags/featureFlags/onError/basic/test.ts b/dev-packages/browser-integration-tests/suites/integrations/featureFlags/featureFlags/onError/basic/test.ts index 742cdd42109b..3233c9047649 100644 --- a/dev-packages/browser-integration-tests/suites/integrations/featureFlags/featureFlags/onError/basic/test.ts +++ b/dev-packages/browser-integration-tests/suites/integrations/featureFlags/featureFlags/onError/basic/test.ts @@ -1,11 +1,11 @@ import { expect } from '@playwright/test'; +import { _INTERNAL_FLAG_BUFFER_SIZE as FLAG_BUFFER_SIZE } from '@sentry/core'; import { sentryTest } from '../../../../../../utils/fixtures'; import { envelopeRequestParser, shouldSkipFeatureFlagsTest, waitForErrorRequest, } from '../../../../../../utils/helpers'; -import { FLAG_BUFFER_SIZE } from '../../../constants'; sentryTest('Basic test with eviction, update, and no async tasks', async ({ getLocalTestUrl, page }) => { if (shouldSkipFeatureFlagsTest()) { diff --git a/dev-packages/browser-integration-tests/suites/integrations/featureFlags/featureFlags/onSpan/test.ts b/dev-packages/browser-integration-tests/suites/integrations/featureFlags/featureFlags/onSpan/test.ts index 476b76d03475..6516d8e36abb 100644 --- a/dev-packages/browser-integration-tests/suites/integrations/featureFlags/featureFlags/onSpan/test.ts +++ b/dev-packages/browser-integration-tests/suites/integrations/featureFlags/featureFlags/onSpan/test.ts @@ -1,4 +1,5 @@ import { expect } from '@playwright/test'; +import { _INTERNAL_MAX_FLAGS_PER_SPAN as MAX_FLAGS_PER_SPAN } from '@sentry/core'; import { sentryTest } from '../../../../../utils/fixtures'; import { type EventAndTraceHeader, @@ -7,7 +8,6 @@ import { shouldSkipFeatureFlagsTest, shouldSkipTracingTest, } from '../../../../../utils/helpers'; -import { MAX_FLAGS_PER_SPAN } from '../../constants'; sentryTest("Feature flags are added to active span's attributes on span end.", async ({ getLocalTestUrl, page }) => { if (shouldSkipFeatureFlagsTest() || shouldSkipTracingTest()) { diff --git a/dev-packages/browser-integration-tests/suites/integrations/featureFlags/launchdarkly/onError/basic/test.ts b/dev-packages/browser-integration-tests/suites/integrations/featureFlags/launchdarkly/onError/basic/test.ts index 1c1a04595187..bc3e0afdc292 100644 --- a/dev-packages/browser-integration-tests/suites/integrations/featureFlags/launchdarkly/onError/basic/test.ts +++ b/dev-packages/browser-integration-tests/suites/integrations/featureFlags/launchdarkly/onError/basic/test.ts @@ -1,11 +1,11 @@ import { expect } from '@playwright/test'; +import { _INTERNAL_FLAG_BUFFER_SIZE as FLAG_BUFFER_SIZE } from '@sentry/core'; import { sentryTest } from '../../../../../../utils/fixtures'; import { envelopeRequestParser, shouldSkipFeatureFlagsTest, waitForErrorRequest, } from '../../../../../../utils/helpers'; -import { FLAG_BUFFER_SIZE } from '../../../constants'; sentryTest('Basic test with eviction, update, and no async tasks', async ({ getLocalTestUrl, page }) => { if (shouldSkipFeatureFlagsTest()) { diff --git a/dev-packages/browser-integration-tests/suites/integrations/featureFlags/launchdarkly/onSpan/test.ts b/dev-packages/browser-integration-tests/suites/integrations/featureFlags/launchdarkly/onSpan/test.ts index 965f00f91fa0..eb7eb003c838 100644 --- a/dev-packages/browser-integration-tests/suites/integrations/featureFlags/launchdarkly/onSpan/test.ts +++ b/dev-packages/browser-integration-tests/suites/integrations/featureFlags/launchdarkly/onSpan/test.ts @@ -1,4 +1,5 @@ import { expect } from '@playwright/test'; +import { _INTERNAL_MAX_FLAGS_PER_SPAN as MAX_FLAGS_PER_SPAN } from '@sentry/core'; import { sentryTest } from '../../../../../utils/fixtures'; import { type EventAndTraceHeader, @@ -7,7 +8,6 @@ import { shouldSkipFeatureFlagsTest, shouldSkipTracingTest, } from '../../../../../utils/helpers'; -import { MAX_FLAGS_PER_SPAN } from '../../constants'; sentryTest("Feature flags are added to active span's attributes on span end.", async ({ getLocalTestUrl, page }) => { if (shouldSkipFeatureFlagsTest() || shouldSkipTracingTest()) { diff --git a/dev-packages/browser-integration-tests/suites/integrations/featureFlags/openfeature/onError/basic/test.ts b/dev-packages/browser-integration-tests/suites/integrations/featureFlags/openfeature/onError/basic/test.ts index 84deca47415d..5953f1e0b087 100644 --- a/dev-packages/browser-integration-tests/suites/integrations/featureFlags/openfeature/onError/basic/test.ts +++ b/dev-packages/browser-integration-tests/suites/integrations/featureFlags/openfeature/onError/basic/test.ts @@ -1,11 +1,11 @@ import { expect } from '@playwright/test'; +import { _INTERNAL_FLAG_BUFFER_SIZE as FLAG_BUFFER_SIZE } from '@sentry/core'; import { sentryTest } from '../../../../../../utils/fixtures'; import { envelopeRequestParser, shouldSkipFeatureFlagsTest, waitForErrorRequest, } from '../../../../../../utils/helpers'; -import { FLAG_BUFFER_SIZE } from '../../../constants'; sentryTest('Basic test with eviction, update, and no async tasks', async ({ getLocalTestUrl, page }) => { if (shouldSkipFeatureFlagsTest()) { diff --git a/dev-packages/browser-integration-tests/suites/integrations/featureFlags/openfeature/onError/errorHook/test.ts b/dev-packages/browser-integration-tests/suites/integrations/featureFlags/openfeature/onError/errorHook/test.ts index c2de7f54abd7..89654c82eda1 100644 --- a/dev-packages/browser-integration-tests/suites/integrations/featureFlags/openfeature/onError/errorHook/test.ts +++ b/dev-packages/browser-integration-tests/suites/integrations/featureFlags/openfeature/onError/errorHook/test.ts @@ -1,11 +1,11 @@ import { expect } from '@playwright/test'; +import { _INTERNAL_FLAG_BUFFER_SIZE as FLAG_BUFFER_SIZE } from '@sentry/core'; import { sentryTest } from '../../../../../../utils/fixtures'; import { envelopeRequestParser, shouldSkipFeatureFlagsTest, waitForErrorRequest, } from '../../../../../../utils/helpers'; -import { FLAG_BUFFER_SIZE } from '../../../constants'; sentryTest('Flag evaluation error hook', async ({ getLocalTestUrl, page }) => { if (shouldSkipFeatureFlagsTest()) { diff --git a/dev-packages/browser-integration-tests/suites/integrations/featureFlags/openfeature/onSpan/test.ts b/dev-packages/browser-integration-tests/suites/integrations/featureFlags/openfeature/onSpan/test.ts index f3b43425477f..5ade5d01b3d5 100644 --- a/dev-packages/browser-integration-tests/suites/integrations/featureFlags/openfeature/onSpan/test.ts +++ b/dev-packages/browser-integration-tests/suites/integrations/featureFlags/openfeature/onSpan/test.ts @@ -1,4 +1,5 @@ import { expect } from '@playwright/test'; +import { _INTERNAL_MAX_FLAGS_PER_SPAN as MAX_FLAGS_PER_SPAN } from '@sentry/core'; import { sentryTest } from '../../../../../utils/fixtures'; import { type EventAndTraceHeader, @@ -7,7 +8,6 @@ import { shouldSkipFeatureFlagsTest, shouldSkipTracingTest, } from '../../../../../utils/helpers'; -import { MAX_FLAGS_PER_SPAN } from '../../constants'; sentryTest("Feature flags are added to active span's attributes on span end.", async ({ getLocalTestUrl, page }) => { if (shouldSkipFeatureFlagsTest() || shouldSkipTracingTest()) { diff --git a/dev-packages/browser-integration-tests/suites/integrations/featureFlags/statsig/onError/basic/test.ts b/dev-packages/browser-integration-tests/suites/integrations/featureFlags/statsig/onError/basic/test.ts index 331dbb8ad433..134b29417d53 100644 --- a/dev-packages/browser-integration-tests/suites/integrations/featureFlags/statsig/onError/basic/test.ts +++ b/dev-packages/browser-integration-tests/suites/integrations/featureFlags/statsig/onError/basic/test.ts @@ -1,11 +1,11 @@ import { expect } from '@playwright/test'; +import { _INTERNAL_FLAG_BUFFER_SIZE as FLAG_BUFFER_SIZE } from '@sentry/core'; import { sentryTest } from '../../../../../../utils/fixtures'; import { envelopeRequestParser, shouldSkipFeatureFlagsTest, waitForErrorRequest, } from '../../../../../../utils/helpers'; -import { FLAG_BUFFER_SIZE } from '../../../constants'; sentryTest('Basic test with eviction, update, and no async tasks', async ({ getLocalTestUrl, page }) => { if (shouldSkipFeatureFlagsTest()) { diff --git a/dev-packages/browser-integration-tests/suites/integrations/featureFlags/statsig/onSpan/test.ts b/dev-packages/browser-integration-tests/suites/integrations/featureFlags/statsig/onSpan/test.ts index dec534f9ffab..1ea192f98850 100644 --- a/dev-packages/browser-integration-tests/suites/integrations/featureFlags/statsig/onSpan/test.ts +++ b/dev-packages/browser-integration-tests/suites/integrations/featureFlags/statsig/onSpan/test.ts @@ -1,4 +1,5 @@ import { expect } from '@playwright/test'; +import { _INTERNAL_MAX_FLAGS_PER_SPAN as MAX_FLAGS_PER_SPAN } from '@sentry/core'; import { sentryTest } from '../../../../../utils/fixtures'; import { type EventAndTraceHeader, @@ -7,7 +8,6 @@ import { shouldSkipFeatureFlagsTest, shouldSkipTracingTest, } from '../../../../../utils/helpers'; -import { MAX_FLAGS_PER_SPAN } from '../../constants'; sentryTest("Feature flags are added to active span's attributes on span end.", async ({ getLocalTestUrl, page }) => { if (shouldSkipFeatureFlagsTest() || shouldSkipTracingTest()) { diff --git a/dev-packages/browser-integration-tests/suites/integrations/featureFlags/unleash/onError/basic/test.ts b/dev-packages/browser-integration-tests/suites/integrations/featureFlags/unleash/onError/basic/test.ts index 341bbbd03e96..6e2760b69600 100644 --- a/dev-packages/browser-integration-tests/suites/integrations/featureFlags/unleash/onError/basic/test.ts +++ b/dev-packages/browser-integration-tests/suites/integrations/featureFlags/unleash/onError/basic/test.ts @@ -1,11 +1,11 @@ import { expect } from '@playwright/test'; +import { _INTERNAL_FLAG_BUFFER_SIZE as FLAG_BUFFER_SIZE } from '@sentry/core'; import { sentryTest } from '../../../../../../utils/fixtures'; import { envelopeRequestParser, shouldSkipFeatureFlagsTest, waitForErrorRequest, } from '../../../../../../utils/helpers'; -import { FLAG_BUFFER_SIZE } from '../../../constants'; sentryTest('Basic test with eviction, update, and no async tasks', async ({ getLocalTestUrl, page }) => { if (shouldSkipFeatureFlagsTest()) { diff --git a/dev-packages/browser-integration-tests/suites/integrations/featureFlags/unleash/onSpan/test.ts b/dev-packages/browser-integration-tests/suites/integrations/featureFlags/unleash/onSpan/test.ts index b2607ffa4c07..984bba3bc0e3 100644 --- a/dev-packages/browser-integration-tests/suites/integrations/featureFlags/unleash/onSpan/test.ts +++ b/dev-packages/browser-integration-tests/suites/integrations/featureFlags/unleash/onSpan/test.ts @@ -1,4 +1,5 @@ import { expect } from '@playwright/test'; +import { _INTERNAL_MAX_FLAGS_PER_SPAN as MAX_FLAGS_PER_SPAN } from '@sentry/core'; import { sentryTest } from '../../../../../utils/fixtures'; import { type EventAndTraceHeader, @@ -7,7 +8,6 @@ import { shouldSkipFeatureFlagsTest, shouldSkipTracingTest, } from '../../../../../utils/helpers'; -import { MAX_FLAGS_PER_SPAN } from '../../constants'; sentryTest("Feature flags are added to active span's attributes on span end.", async ({ getLocalTestUrl, page }) => { if (shouldSkipFeatureFlagsTest() || shouldSkipTracingTest()) { diff --git a/dev-packages/node-integration-tests/suites/featureFlags/featureFlagsIntegration/onError/basic/scenario.ts b/dev-packages/node-integration-tests/suites/featureFlags/featureFlagsIntegration/onError/basic/scenario.ts new file mode 100644 index 000000000000..6d1b88137b87 --- /dev/null +++ b/dev-packages/node-integration-tests/suites/featureFlags/featureFlagsIntegration/onError/basic/scenario.ts @@ -0,0 +1,19 @@ +import { _INTERNAL_FLAG_BUFFER_SIZE as FLAG_BUFFER_SIZE } from '@sentry/core'; +import * as Sentry from '@sentry/node'; +import { loggingTransport } from '@sentry-internal/node-integration-tests'; + +Sentry.init({ + dsn: 'https://public@dsn.ingest.sentry.io/1337', + sampleRate: 1.0, + transport: loggingTransport, + integrations: [Sentry.featureFlagsIntegration()], +}); + +const flagsIntegration = Sentry.getClient()?.getIntegrationByName('FeatureFlags'); +for (let i = 1; i <= FLAG_BUFFER_SIZE; i++) { + flagsIntegration?.addFeatureFlag(`feat${i}`, false); +} +flagsIntegration?.addFeatureFlag(`feat${FLAG_BUFFER_SIZE + 1}`, true); // eviction +flagsIntegration?.addFeatureFlag('feat3', true); // update + +throw new Error('Test error'); diff --git a/dev-packages/node-integration-tests/suites/featureFlags/featureFlagsIntegration/onError/basic/test.ts b/dev-packages/node-integration-tests/suites/featureFlags/featureFlagsIntegration/onError/basic/test.ts new file mode 100644 index 000000000000..74ff1c125b45 --- /dev/null +++ b/dev-packages/node-integration-tests/suites/featureFlags/featureFlagsIntegration/onError/basic/test.ts @@ -0,0 +1,31 @@ +import { _INTERNAL_FLAG_BUFFER_SIZE as FLAG_BUFFER_SIZE } from '@sentry/core'; +import { afterAll, test } from 'vitest'; +import { cleanupChildProcesses, createRunner } from '../../../../../utils/runner'; + +afterAll(() => { + cleanupChildProcesses(); +}); + +test('Flags captured on error with eviction, update, and no async tasks', async () => { + // Based on scenario.ts. + const expectedFlags = [{ flag: 'feat2', result: false }]; + for (let i = 4; i <= FLAG_BUFFER_SIZE; i++) { + expectedFlags.push({ flag: `feat${i}`, result: false }); + } + expectedFlags.push({ flag: `feat${FLAG_BUFFER_SIZE + 1}`, result: true }); + expectedFlags.push({ flag: 'feat3', result: true }); + + await createRunner(__dirname, 'scenario.ts') + .expect({ + event: { + exception: { values: [{ type: 'Error', value: 'Test error' }] }, + contexts: { + flags: { + values: expectedFlags, + }, + }, + }, + }) + .start() + .completed(); +}); diff --git a/dev-packages/node-integration-tests/suites/featureFlags/featureFlagsIntegration/onError/withScope/scenario.ts b/dev-packages/node-integration-tests/suites/featureFlags/featureFlagsIntegration/onError/withScope/scenario.ts new file mode 100644 index 000000000000..b6537011a87f --- /dev/null +++ b/dev-packages/node-integration-tests/suites/featureFlags/featureFlagsIntegration/onError/withScope/scenario.ts @@ -0,0 +1,22 @@ +import type { Scope } from '@sentry/node'; +import * as Sentry from '@sentry/node'; +import { loggingTransport } from '@sentry-internal/node-integration-tests'; + +Sentry.init({ + dsn: 'https://public@dsn.ingest.sentry.io/1337', + sampleRate: 1.0, + transport: loggingTransport, + integrations: [Sentry.featureFlagsIntegration()], +}); + +const flagsIntegration = Sentry.getClient()?.getIntegrationByName('FeatureFlags'); +flagsIntegration?.addFeatureFlag('shared', true); + +Sentry.withScope((_scope: Scope) => { + flagsIntegration?.addFeatureFlag('forked', true); + flagsIntegration?.addFeatureFlag('shared', false); + Sentry.captureException(new Error('Error in forked scope')); +}); + +flagsIntegration?.addFeatureFlag('main', true); +throw new Error('Error in main scope'); diff --git a/dev-packages/node-integration-tests/suites/featureFlags/featureFlagsIntegration/onError/withScope/test.ts b/dev-packages/node-integration-tests/suites/featureFlags/featureFlagsIntegration/onError/withScope/test.ts new file mode 100644 index 000000000000..947b299923e7 --- /dev/null +++ b/dev-packages/node-integration-tests/suites/featureFlags/featureFlagsIntegration/onError/withScope/test.ts @@ -0,0 +1,38 @@ +import { afterAll, test } from 'vitest'; +import { cleanupChildProcesses, createRunner } from '../../../../../utils/runner'; + +afterAll(() => { + cleanupChildProcesses(); +}); + +test('Flags captured on error are isolated by current scope', async () => { + await createRunner(__dirname, 'scenario.ts') + .expect({ + event: { + exception: { values: [{ type: 'Error', value: 'Error in forked scope' }] }, + contexts: { + flags: { + values: [ + { flag: 'forked', result: true }, + { flag: 'shared', result: false }, + ], + }, + }, + }, + }) + .expect({ + event: { + exception: { values: [{ type: 'Error', value: 'Error in main scope' }] }, + contexts: { + flags: { + values: [ + { flag: 'shared', result: true }, + { flag: 'main', result: true }, + ], + }, + }, + }, + }) + .start() + .completed(); +}); diff --git a/dev-packages/node-integration-tests/suites/featureFlags/featureFlagsIntegration/onSpan/scenario.ts b/dev-packages/node-integration-tests/suites/featureFlags/featureFlagsIntegration/onSpan/scenario.ts new file mode 100644 index 000000000000..2c07e46b40ed --- /dev/null +++ b/dev-packages/node-integration-tests/suites/featureFlags/featureFlagsIntegration/onSpan/scenario.ts @@ -0,0 +1,25 @@ +import { _INTERNAL_MAX_FLAGS_PER_SPAN as MAX_FLAGS_PER_SPAN } from '@sentry/core'; +import * as Sentry from '@sentry/node'; +import { loggingTransport } from '@sentry-internal/node-integration-tests'; + +Sentry.init({ + dsn: 'https://public@dsn.ingest.sentry.io/1337', + sampleRate: 1.0, + tracesSampleRate: 1.0, + transport: loggingTransport, + integrations: [Sentry.featureFlagsIntegration()], +}); + +const flagsIntegration = Sentry.getClient()?.getIntegrationByName('FeatureFlags'); + +Sentry.startSpan({ name: 'test-root-span' }, () => { + Sentry.startSpan({ name: 'test-span' }, () => { + Sentry.startSpan({ name: 'test-nested-span' }, () => { + for (let i = 1; i <= MAX_FLAGS_PER_SPAN; i++) { + flagsIntegration?.addFeatureFlag(`feat${i}`, false); + } + flagsIntegration?.addFeatureFlag(`feat${MAX_FLAGS_PER_SPAN + 1}`, true); // dropped flag + flagsIntegration?.addFeatureFlag('feat3', true); // update + }); + }); +}); diff --git a/dev-packages/node-integration-tests/suites/featureFlags/featureFlagsIntegration/onSpan/test.ts b/dev-packages/node-integration-tests/suites/featureFlags/featureFlagsIntegration/onSpan/test.ts new file mode 100644 index 000000000000..4a417a3c3959 --- /dev/null +++ b/dev-packages/node-integration-tests/suites/featureFlags/featureFlagsIntegration/onSpan/test.ts @@ -0,0 +1,33 @@ +import { _INTERNAL_MAX_FLAGS_PER_SPAN as MAX_FLAGS_PER_SPAN } from '@sentry/core'; +import { afterAll, expect, test } from 'vitest'; +import { cleanupChildProcesses, createRunner } from '../../../../utils/runner'; + +afterAll(() => { + cleanupChildProcesses(); +}); + +test('Flags captured on span attributes with max limit', async () => { + // Based on scenario.ts. + const expectedFlags: Record = {}; + for (let i = 1; i <= MAX_FLAGS_PER_SPAN; i++) { + expectedFlags[`flag.evaluation.feat${i}`] = i === 3; + } + + await createRunner(__dirname, 'scenario.ts') + .expect({ + transaction: { + spans: [ + expect.objectContaining({ + description: 'test-span', + data: expect.objectContaining({}), + }), + expect.objectContaining({ + description: 'test-nested-span', + data: expect.objectContaining(expectedFlags), + }), + ], + }, + }) + .start() + .completed(); +}); diff --git a/packages/astro/src/index.server.ts b/packages/astro/src/index.server.ts index ac222eca825b..750eb05d8b10 100644 --- a/packages/astro/src/index.server.ts +++ b/packages/astro/src/index.server.ts @@ -135,6 +135,8 @@ export { consoleLoggingIntegration, wrapMcpServerWithSentry, NODE_VERSION, + featureFlagsIntegration, + type FeatureFlagsIntegration, } from '@sentry/node'; export { init } from './server/sdk'; diff --git a/packages/aws-serverless/src/index.ts b/packages/aws-serverless/src/index.ts index 24513a325188..b13f69a9b6ce 100644 --- a/packages/aws-serverless/src/index.ts +++ b/packages/aws-serverless/src/index.ts @@ -121,6 +121,8 @@ export { consoleLoggingIntegration, wrapMcpServerWithSentry, NODE_VERSION, + featureFlagsIntegration, + type FeatureFlagsIntegration, } from '@sentry/node'; export { diff --git a/packages/browser/src/index.ts b/packages/browser/src/index.ts index 2b2279c099b3..963d8ab38546 100644 --- a/packages/browser/src/index.ts +++ b/packages/browser/src/index.ts @@ -61,13 +61,13 @@ export { instrumentSupabaseClient, zodErrorsIntegration, thirdPartyErrorFilterIntegration, + featureFlagsIntegration, } from '@sentry/core'; -export type { Span } from '@sentry/core'; +export type { Span, FeatureFlagsIntegration } from '@sentry/core'; export { makeBrowserOfflineTransport } from './transports/offline'; export { browserProfilingIntegration } from './profiling/integration'; export { spotlightBrowserIntegration } from './integrations/spotlight'; export { browserSessionIntegration } from './integrations/browsersession'; -export { featureFlagsIntegration, type FeatureFlagsIntegration } from './integrations/featureFlags'; export { launchDarklyIntegration, buildLaunchDarklyFlagUsedHandler } from './integrations/featureFlags/launchdarkly'; export { openFeatureIntegration, OpenFeatureIntegrationHook } from './integrations/featureFlags/openfeature'; export { unleashIntegration } from './integrations/featureFlags/unleash'; diff --git a/packages/browser/src/integrations/featureFlags/launchdarkly/integration.ts b/packages/browser/src/integrations/featureFlags/launchdarkly/integration.ts index eeb20dc07cf9..822e4b1d7f80 100644 --- a/packages/browser/src/integrations/featureFlags/launchdarkly/integration.ts +++ b/packages/browser/src/integrations/featureFlags/launchdarkly/integration.ts @@ -1,6 +1,10 @@ import type { Client, Event, EventHint, IntegrationFn } from '@sentry/core'; -import { defineIntegration } from '@sentry/core'; -import { addFeatureFlagToActiveSpan, copyFlagsFromScopeToEvent, insertFlagToScope } from '../../../utils/featureFlags'; +import { + _INTERNAL_addFeatureFlagToActiveSpan, + _INTERNAL_copyFlagsFromScopeToEvent, + _INTERNAL_insertFlagToScope, + defineIntegration, +} from '@sentry/core'; import type { LDContext, LDEvaluationDetail, LDInspectionFlagUsedHandler } from './types'; /** @@ -23,7 +27,7 @@ export const launchDarklyIntegration = defineIntegration(() => { name: 'LaunchDarkly', processEvent(event: Event, _hint: EventHint, _client: Client): Event { - return copyFlagsFromScopeToEvent(event); + return _INTERNAL_copyFlagsFromScopeToEvent(event); }, }; }) satisfies IntegrationFn; @@ -45,8 +49,8 @@ export function buildLaunchDarklyFlagUsedHandler(): LDInspectionFlagUsedHandler * Handle a flag evaluation by storing its name and value on the current scope. */ method: (flagKey: string, flagDetail: LDEvaluationDetail, _context: LDContext) => { - insertFlagToScope(flagKey, flagDetail.value); - addFeatureFlagToActiveSpan(flagKey, flagDetail.value); + _INTERNAL_insertFlagToScope(flagKey, flagDetail.value); + _INTERNAL_addFeatureFlagToActiveSpan(flagKey, flagDetail.value); }, }; } diff --git a/packages/browser/src/integrations/featureFlags/openfeature/integration.ts b/packages/browser/src/integrations/featureFlags/openfeature/integration.ts index 79dc97394cce..85aedbf779f9 100644 --- a/packages/browser/src/integrations/featureFlags/openfeature/integration.ts +++ b/packages/browser/src/integrations/featureFlags/openfeature/integration.ts @@ -14,8 +14,12 @@ * ``` */ import type { Client, Event, EventHint, IntegrationFn } from '@sentry/core'; -import { defineIntegration } from '@sentry/core'; -import { addFeatureFlagToActiveSpan, copyFlagsFromScopeToEvent, insertFlagToScope } from '../../../utils/featureFlags'; +import { + _INTERNAL_addFeatureFlagToActiveSpan, + _INTERNAL_copyFlagsFromScopeToEvent, + _INTERNAL_insertFlagToScope, + defineIntegration, +} from '@sentry/core'; import type { EvaluationDetails, HookContext, HookHints, JsonValue, OpenFeatureHook } from './types'; export const openFeatureIntegration = defineIntegration(() => { @@ -23,7 +27,7 @@ export const openFeatureIntegration = defineIntegration(() => { name: 'OpenFeature', processEvent(event: Event, _hint: EventHint, _client: Client): Event { - return copyFlagsFromScopeToEvent(event); + return _INTERNAL_copyFlagsFromScopeToEvent(event); }, }; }) satisfies IntegrationFn; @@ -36,15 +40,15 @@ export class OpenFeatureIntegrationHook implements OpenFeatureHook { * Successful evaluation result. */ public after(_hookContext: Readonly>, evaluationDetails: EvaluationDetails): void { - insertFlagToScope(evaluationDetails.flagKey, evaluationDetails.value); - addFeatureFlagToActiveSpan(evaluationDetails.flagKey, evaluationDetails.value); + _INTERNAL_insertFlagToScope(evaluationDetails.flagKey, evaluationDetails.value); + _INTERNAL_addFeatureFlagToActiveSpan(evaluationDetails.flagKey, evaluationDetails.value); } /** * On error evaluation result. */ public error(hookContext: Readonly>, _error: unknown, _hookHints?: HookHints): void { - insertFlagToScope(hookContext.flagKey, hookContext.defaultValue); - addFeatureFlagToActiveSpan(hookContext.flagKey, hookContext.defaultValue); + _INTERNAL_insertFlagToScope(hookContext.flagKey, hookContext.defaultValue); + _INTERNAL_addFeatureFlagToActiveSpan(hookContext.flagKey, hookContext.defaultValue); } } diff --git a/packages/browser/src/integrations/featureFlags/statsig/integration.ts b/packages/browser/src/integrations/featureFlags/statsig/integration.ts index ee472d77669a..9aef234045b5 100644 --- a/packages/browser/src/integrations/featureFlags/statsig/integration.ts +++ b/packages/browser/src/integrations/featureFlags/statsig/integration.ts @@ -1,6 +1,10 @@ import type { Client, Event, EventHint, IntegrationFn } from '@sentry/core'; -import { defineIntegration } from '@sentry/core'; -import { addFeatureFlagToActiveSpan, copyFlagsFromScopeToEvent, insertFlagToScope } from '../../../utils/featureFlags'; +import { + _INTERNAL_addFeatureFlagToActiveSpan, + _INTERNAL_copyFlagsFromScopeToEvent, + _INTERNAL_insertFlagToScope, + defineIntegration, +} from '@sentry/core'; import type { FeatureGate, StatsigClient } from './types'; /** @@ -33,13 +37,13 @@ export const statsigIntegration = defineIntegration( setup(_client: Client) { statsigClient.on('gate_evaluation', (event: { gate: FeatureGate }) => { - insertFlagToScope(event.gate.name, event.gate.value); - addFeatureFlagToActiveSpan(event.gate.name, event.gate.value); + _INTERNAL_insertFlagToScope(event.gate.name, event.gate.value); + _INTERNAL_addFeatureFlagToActiveSpan(event.gate.name, event.gate.value); }); }, processEvent(event: Event, _hint: EventHint, _client: Client): Event { - return copyFlagsFromScopeToEvent(event); + return _INTERNAL_copyFlagsFromScopeToEvent(event); }, }; }, diff --git a/packages/browser/src/integrations/featureFlags/unleash/integration.ts b/packages/browser/src/integrations/featureFlags/unleash/integration.ts index ee7e2a3a0d4d..699c797edecf 100644 --- a/packages/browser/src/integrations/featureFlags/unleash/integration.ts +++ b/packages/browser/src/integrations/featureFlags/unleash/integration.ts @@ -1,7 +1,13 @@ import type { Client, Event, EventHint, IntegrationFn } from '@sentry/core'; -import { defineIntegration, fill, logger } from '@sentry/core'; +import { + _INTERNAL_addFeatureFlagToActiveSpan, + _INTERNAL_copyFlagsFromScopeToEvent, + _INTERNAL_insertFlagToScope, + defineIntegration, + fill, + logger, +} from '@sentry/core'; import { DEBUG_BUILD } from '../../../debug-build'; -import { addFeatureFlagToActiveSpan, copyFlagsFromScopeToEvent, insertFlagToScope } from '../../../utils/featureFlags'; import type { UnleashClient, UnleashClientClass } from './types'; type UnleashIntegrationOptions = { @@ -41,7 +47,7 @@ export const unleashIntegration = defineIntegration( }, processEvent(event: Event, _hint: EventHint, _client: Client): Event { - return copyFlagsFromScopeToEvent(event); + return _INTERNAL_copyFlagsFromScopeToEvent(event); }, }; }, @@ -64,8 +70,8 @@ function _wrappedIsEnabled( const result = original.apply(this, args); if (typeof toggleName === 'string' && typeof result === 'boolean') { - insertFlagToScope(toggleName, result); - addFeatureFlagToActiveSpan(toggleName, result); + _INTERNAL_insertFlagToScope(toggleName, result); + _INTERNAL_addFeatureFlagToActiveSpan(toggleName, result); } else if (DEBUG_BUILD) { logger.error( `[Feature Flags] UnleashClient.isEnabled does not match expected signature. arg0: ${toggleName} (${typeof toggleName}), result: ${result} (${typeof result})`, diff --git a/packages/bun/src/index.ts b/packages/bun/src/index.ts index 70104de6d7c3..14a44e2d38fc 100644 --- a/packages/bun/src/index.ts +++ b/packages/bun/src/index.ts @@ -15,6 +15,7 @@ export type { Stacktrace, Thread, User, + FeatureFlagsIntegration, } from '@sentry/core'; export { @@ -139,6 +140,7 @@ export { consoleLoggingIntegration, createSentryWinstonTransport, wrapMcpServerWithSentry, + featureFlagsIntegration, } from '@sentry/node'; export { diff --git a/packages/cloudflare/src/index.ts b/packages/cloudflare/src/index.ts index 6754ffd04f7c..1a366aeb5dd0 100644 --- a/packages/cloudflare/src/index.ts +++ b/packages/cloudflare/src/index.ts @@ -8,6 +8,7 @@ export type { EventHint, ErrorEvent, Exception, + FeatureFlagsIntegration, Session, SeverityLevel, Span, @@ -91,6 +92,7 @@ export { updateSpanName, wrapMcpServerWithSentry, consoleLoggingIntegration, + featureFlagsIntegration, } from '@sentry/core'; export * as logger from './logs/exports'; diff --git a/packages/core/src/featureFlags.ts b/packages/core/src/featureFlags.ts deleted file mode 100644 index f80e17ef7f9d..000000000000 --- a/packages/core/src/featureFlags.ts +++ /dev/null @@ -1 +0,0 @@ -export type FeatureFlag = { readonly flag: string; readonly result: boolean }; diff --git a/packages/core/src/index.ts b/packages/core/src/index.ts index d418b8a28c19..fdbcd8bffbef 100644 --- a/packages/core/src/index.ts +++ b/packages/core/src/index.ts @@ -113,6 +113,7 @@ export { supabaseIntegration, instrumentSupabaseClient } from './integrations/su export { zodErrorsIntegration } from './integrations/zoderrors'; export { thirdPartyErrorFilterIntegration } from './integrations/third-party-errors-filter'; export { consoleIntegration } from './integrations/console'; +export { featureFlagsIntegration, type FeatureFlagsIntegration } from './integrations/featureFlags'; export { profiler } from './profiling'; export { instrumentFetchRequest } from './fetch'; @@ -123,7 +124,14 @@ export type { ReportDialogOptions } from './report-dialog'; export { _INTERNAL_captureLog, _INTERNAL_flushLogsBuffer, _INTERNAL_captureSerializedLog } from './logs/exports'; export { consoleLoggingIntegration } from './logs/console-integration'; -export type { FeatureFlag } from './featureFlags'; +export type { FeatureFlag } from './utils/featureFlags'; +export { + _INTERNAL_copyFlagsFromScopeToEvent, + _INTERNAL_insertFlagToScope, + _INTERNAL_addFeatureFlagToActiveSpan, + _INTERNAL_FLAG_BUFFER_SIZE, + _INTERNAL_MAX_FLAGS_PER_SPAN, +} from './utils/featureFlags'; export { applyAggregateErrorsToEvent } from './utils-hoist/aggregate-errors'; export { getBreadcrumbLogLevelFromHttpStatusCode } from './utils-hoist/breadcrumb-log-level'; diff --git a/packages/browser/src/integrations/featureFlags/featureFlagsIntegration.ts b/packages/core/src/integrations/featureFlags/featureFlagsIntegration.ts similarity index 69% rename from packages/browser/src/integrations/featureFlags/featureFlagsIntegration.ts rename to packages/core/src/integrations/featureFlags/featureFlagsIntegration.ts index e11084c84c2d..fddc1944ceab 100644 --- a/packages/browser/src/integrations/featureFlags/featureFlagsIntegration.ts +++ b/packages/core/src/integrations/featureFlags/featureFlagsIntegration.ts @@ -1,6 +1,12 @@ -import type { Client, Event, EventHint, Integration, IntegrationFn } from '@sentry/core'; -import { defineIntegration } from '@sentry/core'; -import { addFeatureFlagToActiveSpan, copyFlagsFromScopeToEvent, insertFlagToScope } from '../../utils/featureFlags'; +import { type Client } from '../../client'; +import { defineIntegration } from '../../integration'; +import { type Event, type EventHint } from '../../types-hoist/event'; +import { type Integration, type IntegrationFn } from '../../types-hoist/integration'; +import { + _INTERNAL_addFeatureFlagToActiveSpan, + _INTERNAL_copyFlagsFromScopeToEvent, + _INTERNAL_insertFlagToScope, +} from '../../utils/featureFlags'; export interface FeatureFlagsIntegration extends Integration { addFeatureFlag: (name: string, value: unknown) => void; @@ -35,12 +41,12 @@ export const featureFlagsIntegration = defineIntegration(() => { name: 'FeatureFlags', processEvent(event: Event, _hint: EventHint, _client: Client): Event { - return copyFlagsFromScopeToEvent(event); + return _INTERNAL_copyFlagsFromScopeToEvent(event); }, addFeatureFlag(name: string, value: unknown): void { - insertFlagToScope(name, value); - addFeatureFlagToActiveSpan(name, value); + _INTERNAL_insertFlagToScope(name, value); + _INTERNAL_addFeatureFlagToActiveSpan(name, value); }, }; }) as IntegrationFn; diff --git a/packages/browser/src/integrations/featureFlags/index.ts b/packages/core/src/integrations/featureFlags/index.ts similarity index 100% rename from packages/browser/src/integrations/featureFlags/index.ts rename to packages/core/src/integrations/featureFlags/index.ts diff --git a/packages/core/src/types-hoist/context.ts b/packages/core/src/types-hoist/context.ts index 0ad6eebf6ac3..a3673f84a968 100644 --- a/packages/core/src/types-hoist/context.ts +++ b/packages/core/src/types-hoist/context.ts @@ -1,4 +1,4 @@ -import type { FeatureFlag } from '../featureFlags'; +import type { FeatureFlag } from '../utils/featureFlags'; import type { SpanLinkJSON } from './link'; import type { Primitive } from './misc'; import type { SpanOrigin } from './span'; diff --git a/packages/browser/src/utils/featureFlags.ts b/packages/core/src/utils/featureFlags.ts similarity index 80% rename from packages/browser/src/utils/featureFlags.ts rename to packages/core/src/utils/featureFlags.ts index 9ae389773bcd..388613394148 100644 --- a/packages/browser/src/utils/featureFlags.ts +++ b/packages/core/src/utils/featureFlags.ts @@ -1,6 +1,10 @@ -import type { Event, FeatureFlag, Span } from '@sentry/core'; -import { getActiveSpan, getCurrentScope, GLOBAL_OBJ, logger } from '@sentry/core'; +import { getCurrentScope } from '../currentScopes'; import { DEBUG_BUILD } from '../debug-build'; +import { type Event } from '../types-hoist/event'; +import { type Span } from '../types-hoist/span'; +import { logger } from '../utils-hoist/logger'; +import { GLOBAL_OBJ } from '../utils-hoist/worldwide'; +import { getActiveSpan } from './spanUtils'; /** * Ordered LRU cache for storing feature flags in the scope context. The name @@ -8,15 +12,17 @@ import { DEBUG_BUILD } from '../debug-build'; * from oldest to newest. */ +export type FeatureFlag = { readonly flag: string; readonly result: boolean }; + /** * Max size of the LRU flag buffer stored in Sentry scope and event contexts. */ -export const FLAG_BUFFER_SIZE = 100; +export const _INTERNAL_FLAG_BUFFER_SIZE = 100; /** * Max number of flag evaluations to record per span. */ -export const MAX_FLAGS_PER_SPAN = 10; +export const _INTERNAL_MAX_FLAGS_PER_SPAN = 10; // Global map of spans to feature flag buffers. Populated by feature flag integrations. GLOBAL_OBJ._spanToFlagBufferMap = new WeakMap>(); @@ -26,7 +32,7 @@ const SPAN_FLAG_ATTRIBUTE_PREFIX = 'flag.evaluation.'; /** * Copies feature flags that are in current scope context to the event context */ -export function copyFlagsFromScopeToEvent(event: Event): Event { +export function _INTERNAL_copyFlagsFromScopeToEvent(event: Event): Event { const scope = getCurrentScope(); const flagContext = scope.getScopeData().contexts.flags; const flagBuffer = flagContext ? flagContext.values : []; @@ -53,13 +59,17 @@ export function copyFlagsFromScopeToEvent(event: Event): Event { * @param value Value of the feature flag. * @param maxSize Max number of flags the buffer should store. Default value should always be used in production. */ -export function insertFlagToScope(name: string, value: unknown, maxSize: number = FLAG_BUFFER_SIZE): void { +export function _INTERNAL_insertFlagToScope( + name: string, + value: unknown, + maxSize: number = _INTERNAL_FLAG_BUFFER_SIZE, +): void { const scopeContexts = getCurrentScope().getScopeData().contexts; if (!scopeContexts.flags) { scopeContexts.flags = { values: [] }; } const flags = scopeContexts.flags.values as FeatureFlag[]; - insertToFlagBuffer(flags, name, value, maxSize); + _INTERNAL_insertToFlagBuffer(flags, name, value, maxSize); } /** @@ -74,7 +84,12 @@ export function insertFlagToScope(name: string, value: unknown, maxSize: number * @param value Value of the feature flag. * @param maxSize Max number of flags the buffer should store. Default value should always be used in production. */ -export function insertToFlagBuffer(flags: FeatureFlag[], name: string, value: unknown, maxSize: number): void { +export function _INTERNAL_insertToFlagBuffer( + flags: FeatureFlag[], + name: string, + value: unknown, + maxSize: number, +): void { if (typeof value !== 'boolean') { return; } @@ -113,10 +128,10 @@ export function insertToFlagBuffer(flags: FeatureFlag[], name: string, value: un * @param value Value of the feature flag. Non-boolean values are ignored. * @param maxFlagsPerSpan Max number of flags a buffer should store. Default value should always be used in production. */ -export function addFeatureFlagToActiveSpan( +export function _INTERNAL_addFeatureFlagToActiveSpan( name: string, value: unknown, - maxFlagsPerSpan: number = MAX_FLAGS_PER_SPAN, + maxFlagsPerSpan: number = _INTERNAL_MAX_FLAGS_PER_SPAN, ): void { const spanFlagMap = GLOBAL_OBJ._spanToFlagBufferMap; if (!spanFlagMap || typeof value !== 'boolean') { diff --git a/packages/browser/test/utils/featureFlags.test.ts b/packages/core/test/lib/utils/featureFlags.test.ts similarity index 62% rename from packages/browser/test/utils/featureFlags.test.ts rename to packages/core/test/lib/utils/featureFlags.test.ts index 1c0bed312590..14258c2caf36 100644 --- a/packages/browser/test/utils/featureFlags.test.ts +++ b/packages/core/test/lib/utils/featureFlags.test.ts @@ -1,16 +1,20 @@ -import type { FeatureFlag } from '@sentry/core'; -import { getCurrentScope, logger } from '@sentry/core'; import { afterEach, describe, expect, it, vi } from 'vitest'; -import { insertFlagToScope, insertToFlagBuffer } from '../../src/utils/featureFlags'; +import { getCurrentScope } from '../../../src/currentScopes'; +import { + type FeatureFlag, + _INTERNAL_insertFlagToScope, + _INTERNAL_insertToFlagBuffer, +} from '../../../src/utils/featureFlags'; +import { logger } from '../../../src/utils-hoist/logger'; describe('flags', () => { describe('insertFlagToScope()', () => { it('adds flags to the current scope context', () => { const maxSize = 3; - insertFlagToScope('feat1', true, maxSize); - insertFlagToScope('feat2', true, maxSize); - insertFlagToScope('feat3', true, maxSize); - insertFlagToScope('feat4', true, maxSize); + _INTERNAL_insertFlagToScope('feat1', true, maxSize); + _INTERNAL_insertFlagToScope('feat2', true, maxSize); + _INTERNAL_insertFlagToScope('feat3', true, maxSize); + _INTERNAL_insertFlagToScope('feat4', true, maxSize); const scope = getCurrentScope(); expect(scope.getScopeData().contexts.flags?.values).toEqual([ @@ -31,10 +35,10 @@ describe('flags', () => { it('maintains ordering and evicts the oldest entry', () => { const buffer: FeatureFlag[] = []; const maxSize = 3; - insertToFlagBuffer(buffer, 'feat1', true, maxSize); - insertToFlagBuffer(buffer, 'feat2', true, maxSize); - insertToFlagBuffer(buffer, 'feat3', true, maxSize); - insertToFlagBuffer(buffer, 'feat4', true, maxSize); + _INTERNAL_insertToFlagBuffer(buffer, 'feat1', true, maxSize); + _INTERNAL_insertToFlagBuffer(buffer, 'feat2', true, maxSize); + _INTERNAL_insertToFlagBuffer(buffer, 'feat3', true, maxSize); + _INTERNAL_insertToFlagBuffer(buffer, 'feat4', true, maxSize); expect(buffer).toEqual([ { flag: 'feat2', result: true }, @@ -46,11 +50,11 @@ describe('flags', () => { it('does not duplicate same-name flags and updates order and values', () => { const buffer: FeatureFlag[] = []; const maxSize = 3; - insertToFlagBuffer(buffer, 'feat1', true, maxSize); - insertToFlagBuffer(buffer, 'feat2', true, maxSize); - insertToFlagBuffer(buffer, 'feat3', true, maxSize); - insertToFlagBuffer(buffer, 'feat3', false, maxSize); - insertToFlagBuffer(buffer, 'feat1', false, maxSize); + _INTERNAL_insertToFlagBuffer(buffer, 'feat1', true, maxSize); + _INTERNAL_insertToFlagBuffer(buffer, 'feat2', true, maxSize); + _INTERNAL_insertToFlagBuffer(buffer, 'feat3', true, maxSize); + _INTERNAL_insertToFlagBuffer(buffer, 'feat3', false, maxSize); + _INTERNAL_insertToFlagBuffer(buffer, 'feat1', false, maxSize); expect(buffer).toEqual([ { flag: 'feat2', result: true }, @@ -62,8 +66,8 @@ describe('flags', () => { it('does not allocate unnecessary space', () => { const buffer: FeatureFlag[] = []; const maxSize = 1000; - insertToFlagBuffer(buffer, 'feat1', true, maxSize); - insertToFlagBuffer(buffer, 'feat2', true, maxSize); + _INTERNAL_insertToFlagBuffer(buffer, 'feat1', true, maxSize); + _INTERNAL_insertToFlagBuffer(buffer, 'feat2', true, maxSize); expect(buffer).toEqual([ { flag: 'feat1', result: true }, @@ -74,8 +78,8 @@ describe('flags', () => { it('does not accept non-boolean values', () => { const buffer: FeatureFlag[] = []; const maxSize = 1000; - insertToFlagBuffer(buffer, 'feat1', 1, maxSize); - insertToFlagBuffer(buffer, 'feat2', 'string', maxSize); + _INTERNAL_insertToFlagBuffer(buffer, 'feat1', 1, maxSize); + _INTERNAL_insertToFlagBuffer(buffer, 'feat2', 'string', maxSize); expect(buffer).toEqual([]); }); @@ -86,7 +90,7 @@ describe('flags', () => { { flag: 'feat2', result: true }, ]; - insertToFlagBuffer(buffer, 'feat1', true, 1); + _INTERNAL_insertToFlagBuffer(buffer, 'feat1', true, 1); expect(loggerSpy).toHaveBeenCalledWith( expect.stringContaining('[Feature Flags] insertToFlagBuffer called on a buffer larger than maxSize'), ); @@ -95,7 +99,7 @@ describe('flags', () => { { flag: 'feat2', result: true }, ]); - insertToFlagBuffer(buffer, 'feat1', true, -2); + _INTERNAL_insertToFlagBuffer(buffer, 'feat1', true, -2); expect(loggerSpy).toHaveBeenCalledWith( expect.stringContaining('[Feature Flags] insertToFlagBuffer called on a buffer larger than maxSize'), ); diff --git a/packages/deno/src/index.ts b/packages/deno/src/index.ts index f388de7cb5ee..12bcedc35270 100644 --- a/packages/deno/src/index.ts +++ b/packages/deno/src/index.ts @@ -8,6 +8,7 @@ export type { EventHint, ErrorEvent, Exception, + FeatureFlagsIntegration, Session, SeverityLevel, Span, @@ -87,6 +88,7 @@ export { spanToBaggageHeader, updateSpanName, wrapMcpServerWithSentry, + featureFlagsIntegration, } from '@sentry/core'; export { DenoClient } from './client'; diff --git a/packages/google-cloud-serverless/src/index.ts b/packages/google-cloud-serverless/src/index.ts index f2622e591497..e9586a9bd820 100644 --- a/packages/google-cloud-serverless/src/index.ts +++ b/packages/google-cloud-serverless/src/index.ts @@ -121,6 +121,8 @@ export { consoleLoggingIntegration, wrapMcpServerWithSentry, NODE_VERSION, + featureFlagsIntegration, + type FeatureFlagsIntegration, } from '@sentry/node'; export { diff --git a/packages/node/src/index.ts b/packages/node/src/index.ts index 589937b21fd4..cf951c3db8b6 100644 --- a/packages/node/src/index.ts +++ b/packages/node/src/index.ts @@ -139,6 +139,7 @@ export { consoleLoggingIntegration, consoleIntegration, wrapMcpServerWithSentry, + featureFlagsIntegration, } from '@sentry/core'; export type { @@ -158,6 +159,7 @@ export type { Thread, User, Span, + FeatureFlagsIntegration, } from '@sentry/core'; export { logger }; diff --git a/packages/remix/src/cloudflare/index.ts b/packages/remix/src/cloudflare/index.ts index 3d3d17e1da27..46c443cac39f 100644 --- a/packages/remix/src/cloudflare/index.ts +++ b/packages/remix/src/cloudflare/index.ts @@ -32,6 +32,7 @@ export type { EventHint, ErrorEvent, Exception, + FeatureFlagsIntegration, Session, SeverityLevel, Span, @@ -109,4 +110,5 @@ export { spanToTraceHeader, spanToBaggageHeader, updateSpanName, + featureFlagsIntegration, } from '@sentry/core'; diff --git a/packages/sveltekit/src/worker/index.ts b/packages/sveltekit/src/worker/index.ts index 3614922072ec..2dde8c61a1dc 100644 --- a/packages/sveltekit/src/worker/index.ts +++ b/packages/sveltekit/src/worker/index.ts @@ -82,6 +82,8 @@ export { supabaseIntegration, instrumentSupabaseClient, zodErrorsIntegration, + featureFlagsIntegration, + type FeatureFlagsIntegration, } from '@sentry/cloudflare'; /** diff --git a/packages/vercel-edge/src/index.ts b/packages/vercel-edge/src/index.ts index ff1231c8a1f8..303d40144ec3 100644 --- a/packages/vercel-edge/src/index.ts +++ b/packages/vercel-edge/src/index.ts @@ -8,6 +8,7 @@ export type { EventHint, ErrorEvent, Exception, + FeatureFlagsIntegration, Session, SeverityLevel, Span, @@ -90,6 +91,7 @@ export { spanToBaggageHeader, wrapMcpServerWithSentry, consoleLoggingIntegration, + featureFlagsIntegration, } from '@sentry/core'; export { VercelEdgeClient } from './client';