From 7dc13b318e38a62cf7c536309a8b58db1d26ccc9 Mon Sep 17 00:00:00 2001 From: Charly Gomez Date: Wed, 25 Jun 2025 14:07:12 +0200 Subject: [PATCH 01/10] export launchdarkly integration shims in nextjs --- packages/integration-shims/src/index.ts | 1 + .../integration-shims/src/launchDarkly.ts | 38 +++++++++++++++++++ packages/nextjs/package.json | 3 +- packages/nextjs/src/index.types.ts | 3 ++ packages/nextjs/src/server/index.ts | 5 +++ yarn.lock | 12 +----- 6 files changed, 50 insertions(+), 12 deletions(-) create mode 100644 packages/integration-shims/src/launchDarkly.ts diff --git a/packages/integration-shims/src/index.ts b/packages/integration-shims/src/index.ts index 510b26ddbb76..7b03aae9f792 100644 --- a/packages/integration-shims/src/index.ts +++ b/packages/integration-shims/src/index.ts @@ -1,3 +1,4 @@ export { feedbackIntegrationShim } from './Feedback'; export { replayIntegrationShim } from './Replay'; export { browserTracingIntegrationShim } from './BrowserTracing'; +export { launchDarklyIntegrationShim, buildLaunchDarklyFlagUsedHandlerShim } from './launchdarkly'; diff --git a/packages/integration-shims/src/launchDarkly.ts b/packages/integration-shims/src/launchDarkly.ts new file mode 100644 index 000000000000..8254ac8e4b80 --- /dev/null +++ b/packages/integration-shims/src/launchDarkly.ts @@ -0,0 +1,38 @@ +import { consoleSandbox, defineIntegration, isBrowser } from '@sentry/core'; +import { FAKE_FUNCTION } from './common'; + +/** + * This is a shim for the LaunchDarkly integration. + * We need this in order to not throw runtime errors when accidentally importing this on the server through a meta framework like Next.js. + */ +export const launchDarklyIntegrationShim = defineIntegration((_options?: unknown) => { + if (!isBrowser()) { + consoleSandbox(() => { + // eslint-disable-next-line no-console + console.warn('The launchDarklyIntegration() can only be used in the browser.'); + }); + } + + return { + name: 'LaunchDarkly', + }; +}); + +/** + * This is a shim for the LaunchDarkly flag used handler. + */ +export function buildLaunchDarklyFlagUsedHandlerShim(): unknown { + if (!isBrowser()) { + consoleSandbox(() => { + // eslint-disable-next-line no-console + console.warn('The buildLaunchDarklyFlagUsedHandler() should only be used in the browser.'); + }); + } + + return { + name: 'sentry-flag-auditor', + type: 'flag-used', + synchronous: true, + method: FAKE_FUNCTION, + }; +} diff --git a/packages/nextjs/package.json b/packages/nextjs/package.json index d4225f1bbf1d..86db73c07b96 100644 --- a/packages/nextjs/package.json +++ b/packages/nextjs/package.json @@ -94,7 +94,8 @@ "devDependencies": { "@types/resolve": "1.20.3", "eslint-plugin-react": "^7.31.11", - "next": "13.2.0" + "next": "13.2.0", + "@sentry-internal/integration-shims": "9.31.0" }, "peerDependencies": { "next": "^13.2.0 || ^14.0 || ^15.0.0-rc.0" diff --git a/packages/nextjs/src/index.types.ts b/packages/nextjs/src/index.types.ts index c630d545061c..479c0b462c9f 100644 --- a/packages/nextjs/src/index.types.ts +++ b/packages/nextjs/src/index.types.ts @@ -138,3 +138,6 @@ export declare function wrapApiHandlerWithSentryVercelCrons(WrappingTarget: C): C; export { captureRequestError } from './common/captureRequestError'; + +export declare const launchDarklyIntegration: typeof clientSdk.launchDarklyIntegration; +export declare const buildLaunchDarklyFlagUsedHandler: typeof clientSdk.buildLaunchDarklyFlagUsedHandler; diff --git a/packages/nextjs/src/server/index.ts b/packages/nextjs/src/server/index.ts index a6594e7fae1e..255937273859 100644 --- a/packages/nextjs/src/server/index.ts +++ b/packages/nextjs/src/server/index.ts @@ -389,3 +389,8 @@ function sdkAlreadyInitialized(): boolean { export * from '../common'; export { wrapApiHandlerWithSentry } from '../common/pages-router-instrumentation/wrapApiHandlerWithSentry'; + +export { + launchDarklyIntegrationShim as launchDarklyIntegration, + buildLaunchDarklyFlagUsedHandlerShim as buildLaunchDarklyFlagUsedHandler, +} from '@sentry-internal/integration-shims'; diff --git a/yarn.lock b/yarn.lock index f696182ab447..17cc1462f382 100644 --- a/yarn.lock +++ b/yarn.lock @@ -24560,16 +24560,6 @@ quick-format-unescaped@^4.0.3: resolved "https://registry.yarnpkg.com/quick-format-unescaped/-/quick-format-unescaped-4.0.4.tgz#93ef6dd8d3453cbc7970dd614fad4c5954d6b5a7" integrity sha512-tYC1Q1hgyRuHgloV/YXs2w15unPVh8qfu/qCTfhTYamaw7fyhumKa2yGpdSo87vY32rIclj+4fWYQXUMs9EHvg== -quick-format-unescaped@^4.0.3: - version "4.0.4" - resolved "https://registry.yarnpkg.com/quick-format-unescaped/-/quick-format-unescaped-4.0.4.tgz#93ef6dd8d3453cbc7970dd614fad4c5954d6b5a7" - integrity sha512-tYC1Q1hgyRuHgloV/YXs2w15unPVh8qfu/qCTfhTYamaw7fyhumKa2yGpdSo87vY32rIclj+4fWYQXUMs9EHvg== - -quick-format-unescaped@^4.0.3: - version "4.0.4" - resolved "https://registry.yarnpkg.com/quick-format-unescaped/-/quick-format-unescaped-4.0.4.tgz#93ef6dd8d3453cbc7970dd614fad4c5954d6b5a7" - integrity sha512-tYC1Q1hgyRuHgloV/YXs2w15unPVh8qfu/qCTfhTYamaw7fyhumKa2yGpdSo87vY32rIclj+4fWYQXUMs9EHvg== - quick-lru@^4.0.1: version "4.0.1" resolved "https://registry.yarnpkg.com/quick-lru/-/quick-lru-4.0.1.tgz#5b8878f113a58217848c6482026c73e1ba57727f" @@ -29289,7 +29279,7 @@ vite@^5.0.0, vite@^5.4.11, vite@^5.4.5: optionalDependencies: fsevents "~2.3.3" -vitefu@^0.2.2, vitefu@^0.2.4, vitefu@^0.2.5: +vitefu@^0.2.2, vitefu@^0.2.4: version "0.2.5" resolved "https://registry.yarnpkg.com/vitefu/-/vitefu-0.2.5.tgz#c1b93c377fbdd3e5ddd69840ea3aa70b40d90969" integrity sha512-SgHtMLoqaeeGnd2evZ849ZbACbnwQCIwRH57t18FxcXoZop0uQu0uzlIhJBlF/eWVzuce0sHeqPcDo+evVcg8Q== From c1d9b8a8cd927126ab70696a43a0c86f7135999d Mon Sep 17 00:00:00 2001 From: Charly Gomez Date: Wed, 25 Jun 2025 14:14:54 +0200 Subject: [PATCH 02/10] . --- packages/nextjs/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/nextjs/package.json b/packages/nextjs/package.json index 2e934fcafb3e..e5d9a466ac8d 100644 --- a/packages/nextjs/package.json +++ b/packages/nextjs/package.json @@ -95,7 +95,7 @@ "@types/resolve": "1.20.3", "eslint-plugin-react": "^7.31.11", "next": "13.2.0", - "@sentry-internal/integration-shims": "9.31.0" + "@sentry-internal/integration-shims": "9.32.0" }, "peerDependencies": { "next": "^13.2.0 || ^14.0 || ^15.0.0-rc.0" From 45efe996ee3c9ce20ce272e4de394d3824c9a8b4 Mon Sep 17 00:00:00 2001 From: Charly Gomez Date: Wed, 25 Jun 2025 15:43:24 +0200 Subject: [PATCH 03/10] oh lord --- packages/integration-shims/src/index.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/integration-shims/src/index.ts b/packages/integration-shims/src/index.ts index 7b03aae9f792..e887ac725023 100644 --- a/packages/integration-shims/src/index.ts +++ b/packages/integration-shims/src/index.ts @@ -1,4 +1,4 @@ export { feedbackIntegrationShim } from './Feedback'; export { replayIntegrationShim } from './Replay'; export { browserTracingIntegrationShim } from './BrowserTracing'; -export { launchDarklyIntegrationShim, buildLaunchDarklyFlagUsedHandlerShim } from './launchdarkly'; +export { launchDarklyIntegrationShim, buildLaunchDarklyFlagUsedHandlerShim } from './launchDarkly'; From db253bdd386767ff2a38cb503896c7d90b567ba1 Mon Sep 17 00:00:00 2001 From: Charly Gomez Date: Tue, 1 Jul 2025 13:27:38 +0200 Subject: [PATCH 04/10] try shim in node --- .../app/server-component/featureFlag/page.tsx | 9 +++++ .../tests/server-components.test.ts | 7 ++++ packages/astro/src/index.types.ts | 3 ++ packages/nextjs/package.json | 1 - packages/nextjs/src/server/index.ts | 5 --- packages/node/src/index.ts | 1 + .../integrations/featureFlagShims/index.ts | 4 ++ .../featureFlagShims/launchDarkly.ts | 37 +++++++++++++++++++ packages/nuxt/src/index.types.ts | 3 ++ packages/react-router/src/index.types.ts | 3 ++ packages/remix/src/index.types.ts | 3 ++ packages/solidstart/src/index.types.ts | 3 ++ packages/sveltekit/src/index.types.ts | 3 ++ .../tanstackstart-react/src/index.types.ts | 3 ++ 14 files changed, 79 insertions(+), 6 deletions(-) create mode 100644 dev-packages/e2e-tests/test-applications/nextjs-app-dir/app/server-component/featureFlag/page.tsx create mode 100644 packages/node/src/integrations/featureFlagShims/index.ts create mode 100644 packages/node/src/integrations/featureFlagShims/launchDarkly.ts diff --git a/dev-packages/e2e-tests/test-applications/nextjs-app-dir/app/server-component/featureFlag/page.tsx b/dev-packages/e2e-tests/test-applications/nextjs-app-dir/app/server-component/featureFlag/page.tsx new file mode 100644 index 000000000000..e6bffbf5d31f --- /dev/null +++ b/dev-packages/e2e-tests/test-applications/nextjs-app-dir/app/server-component/featureFlag/page.tsx @@ -0,0 +1,9 @@ +import * as Sentry from '@sentry/nextjs'; + +export const dynamic = 'force-dynamic'; + +export default async function FeatureFlagServerComponent() { + Sentry.buildLaunchDarklyFlagUsedHandler(); + + return
FeatureFlagServerComponent
; +} diff --git a/dev-packages/e2e-tests/test-applications/nextjs-app-dir/tests/server-components.test.ts b/dev-packages/e2e-tests/test-applications/nextjs-app-dir/tests/server-components.test.ts index 0a32972b0e6a..e2acfdcc8223 100644 --- a/dev-packages/e2e-tests/test-applications/nextjs-app-dir/tests/server-components.test.ts +++ b/dev-packages/e2e-tests/test-applications/nextjs-app-dir/tests/server-components.test.ts @@ -133,3 +133,10 @@ test('Should capture an error and transaction for a app router page', async ({ p }), ); }); + +test.only('Should not throw error on server component when importing shimmed feature flag function', async ({ + page, +}) => { + await page.goto('/server-component/featureFlag'); + await expect(page.locator('body')).toContainText('FeatureFlagServerComponent'); +}); diff --git a/packages/astro/src/index.types.ts b/packages/astro/src/index.types.ts index d74a885c2b37..a04d05d67b8d 100644 --- a/packages/astro/src/index.types.ts +++ b/packages/astro/src/index.types.ts @@ -29,4 +29,7 @@ export declare const Span: clientSdk.Span; export declare const logger: typeof clientSdk.logger | typeof serverSdk.logger; +export declare const launchDarklyIntegration: typeof clientSdk.launchDarklyIntegration; +export declare const buildLaunchDarklyFlagUsedHandler: typeof clientSdk.buildLaunchDarklyFlagUsedHandler; + export default sentryAstro; diff --git a/packages/nextjs/package.json b/packages/nextjs/package.json index 459f013bc3c3..9ab46ee4b98b 100644 --- a/packages/nextjs/package.json +++ b/packages/nextjs/package.json @@ -94,7 +94,6 @@ "devDependencies": { "@types/resolve": "1.20.3", "eslint-plugin-react": "^7.31.11", - "@sentry-internal/integration-shims": "9.32.0", "next": "13.5.9" }, "peerDependencies": { diff --git a/packages/nextjs/src/server/index.ts b/packages/nextjs/src/server/index.ts index 255937273859..a6594e7fae1e 100644 --- a/packages/nextjs/src/server/index.ts +++ b/packages/nextjs/src/server/index.ts @@ -389,8 +389,3 @@ function sdkAlreadyInitialized(): boolean { export * from '../common'; export { wrapApiHandlerWithSentry } from '../common/pages-router-instrumentation/wrapApiHandlerWithSentry'; - -export { - launchDarklyIntegrationShim as launchDarklyIntegration, - buildLaunchDarklyFlagUsedHandlerShim as buildLaunchDarklyFlagUsedHandler, -} from '@sentry-internal/integration-shims'; diff --git a/packages/node/src/index.ts b/packages/node/src/index.ts index cf951c3db8b6..c194f873acc8 100644 --- a/packages/node/src/index.ts +++ b/packages/node/src/index.ts @@ -36,6 +36,7 @@ export { amqplibIntegration } from './integrations/tracing/amqplib'; export { vercelAIIntegration } from './integrations/tracing/vercelai'; export { childProcessIntegration } from './integrations/childProcess'; export { createSentryWinstonTransport } from './integrations/winston'; +export { launchDarklyIntegration, buildLaunchDarklyFlagUsedHandler } from './integrations/featureFlagShims'; export { SentryContextManager } from './otel/contextManager'; export { generateInstrumentOnce } from './otel/instrument'; diff --git a/packages/node/src/integrations/featureFlagShims/index.ts b/packages/node/src/integrations/featureFlagShims/index.ts new file mode 100644 index 000000000000..c8d5f67101c7 --- /dev/null +++ b/packages/node/src/integrations/featureFlagShims/index.ts @@ -0,0 +1,4 @@ +export { + launchDarklyIntegrationShim as launchDarklyIntegration, + buildLaunchDarklyFlagUsedHandlerShim as buildLaunchDarklyFlagUsedHandler, +} from './launchDarkly'; diff --git a/packages/node/src/integrations/featureFlagShims/launchDarkly.ts b/packages/node/src/integrations/featureFlagShims/launchDarkly.ts new file mode 100644 index 000000000000..c525e2f366ac --- /dev/null +++ b/packages/node/src/integrations/featureFlagShims/launchDarkly.ts @@ -0,0 +1,37 @@ +import { consoleSandbox, defineIntegration, isBrowser } from '@sentry/core'; + +/** + * This is a shim for the LaunchDarkly integration. + * We need this in order to not throw runtime errors when accidentally importing this on the server through a meta framework like Next.js. + */ +export const launchDarklyIntegrationShim = defineIntegration((_options?: unknown) => { + if (!isBrowser()) { + consoleSandbox(() => { + // eslint-disable-next-line no-console + console.warn('The launchDarklyIntegration() can only be used in the browser.'); + }); + } + + return { + name: 'LaunchDarkly', + }; +}); + +/** + * This is a shim for the LaunchDarkly flag used handler. + */ +export function buildLaunchDarklyFlagUsedHandlerShim(): unknown { + if (!isBrowser()) { + consoleSandbox(() => { + // eslint-disable-next-line no-console + console.warn('The buildLaunchDarklyFlagUsedHandler() can only be used in the browser.'); + }); + } + + return { + name: 'sentry-flag-auditor', + type: 'flag-used', + synchronous: true, + method: () => null, + }; +} diff --git a/packages/nuxt/src/index.types.ts b/packages/nuxt/src/index.types.ts index c6cdb01d280e..4e9f887cac45 100644 --- a/packages/nuxt/src/index.types.ts +++ b/packages/nuxt/src/index.types.ts @@ -18,3 +18,6 @@ export declare const getDefaultIntegrations: (options: Options) => Integration[] export declare const defaultStackParser: StackParser; export declare const logger: typeof clientSdk.logger | typeof serverSdk.logger; + +export declare const launchDarklyIntegration: typeof clientSdk.launchDarklyIntegration; +export declare const buildLaunchDarklyFlagUsedHandler: typeof clientSdk.buildLaunchDarklyFlagUsedHandler; diff --git a/packages/react-router/src/index.types.ts b/packages/react-router/src/index.types.ts index 45f4fe10fa31..72988e3afc8f 100644 --- a/packages/react-router/src/index.types.ts +++ b/packages/react-router/src/index.types.ts @@ -18,3 +18,6 @@ export declare const defaultStackParser: StackParser; export declare const getDefaultIntegrations: (options: Options) => Integration[]; export declare const logger: typeof clientSdk.logger | typeof serverSdk.logger; + +export declare const launchDarklyIntegration: typeof clientSdk.launchDarklyIntegration; +export declare const buildLaunchDarklyFlagUsedHandler: typeof clientSdk.buildLaunchDarklyFlagUsedHandler; diff --git a/packages/remix/src/index.types.ts b/packages/remix/src/index.types.ts index 697fc3813045..3701216fb65d 100644 --- a/packages/remix/src/index.types.ts +++ b/packages/remix/src/index.types.ts @@ -32,3 +32,6 @@ declare const runtime: 'client' | 'server'; export const close = runtime === 'client' ? clientSdk.close : serverSdk.close; export const flush = runtime === 'client' ? clientSdk.flush : serverSdk.flush; export const lastEventId = runtime === 'client' ? clientSdk.lastEventId : serverSdk.lastEventId; + +export declare const launchDarklyIntegration: typeof clientSdk.launchDarklyIntegration; +export declare const buildLaunchDarklyFlagUsedHandler: typeof clientSdk.buildLaunchDarklyFlagUsedHandler; diff --git a/packages/solidstart/src/index.types.ts b/packages/solidstart/src/index.types.ts index e4cd974ed00e..4dd127608a5c 100644 --- a/packages/solidstart/src/index.types.ts +++ b/packages/solidstart/src/index.types.ts @@ -25,3 +25,6 @@ export declare function flush(timeout?: number | undefined): PromiseLike; export declare const logger: typeof clientSdk.logger | typeof serverSdk.logger; + +export declare const launchDarklyIntegration: typeof clientSdk.launchDarklyIntegration; +export declare const buildLaunchDarklyFlagUsedHandler: typeof clientSdk.buildLaunchDarklyFlagUsedHandler; diff --git a/packages/tanstackstart-react/src/index.types.ts b/packages/tanstackstart-react/src/index.types.ts index 85bbe9df63fd..b65e836e0691 100644 --- a/packages/tanstackstart-react/src/index.types.ts +++ b/packages/tanstackstart-react/src/index.types.ts @@ -28,3 +28,6 @@ export declare const showReportDialog: typeof clientSdk.showReportDialog; export declare const withErrorBoundary: typeof clientSdk.withErrorBoundary; export declare const logger: typeof clientSdk.logger | typeof serverSdk.logger; + +export declare const launchDarklyIntegration: typeof clientSdk.launchDarklyIntegration; +export declare const buildLaunchDarklyFlagUsedHandler: typeof clientSdk.buildLaunchDarklyFlagUsedHandler; From 8b229f02559e329243cfbc20a878f6659502c25e Mon Sep 17 00:00:00 2001 From: Charly Gomez Date: Tue, 1 Jul 2025 14:01:11 +0200 Subject: [PATCH 05/10] . --- .../nextjs-app-dir/tests/server-components.test.ts | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/dev-packages/e2e-tests/test-applications/nextjs-app-dir/tests/server-components.test.ts b/dev-packages/e2e-tests/test-applications/nextjs-app-dir/tests/server-components.test.ts index e2acfdcc8223..b9fa6f8fb201 100644 --- a/dev-packages/e2e-tests/test-applications/nextjs-app-dir/tests/server-components.test.ts +++ b/dev-packages/e2e-tests/test-applications/nextjs-app-dir/tests/server-components.test.ts @@ -134,9 +134,7 @@ test('Should capture an error and transaction for a app router page', async ({ p ); }); -test.only('Should not throw error on server component when importing shimmed feature flag function', async ({ - page, -}) => { +test('Should not throw error on server component when importing shimmed feature flag function', async ({ page }) => { await page.goto('/server-component/featureFlag'); await expect(page.locator('body')).toContainText('FeatureFlagServerComponent'); }); From 35d682c18d791742168f4548971f25c039538521 Mon Sep 17 00:00:00 2001 From: Charly Gomez Date: Tue, 1 Jul 2025 14:03:20 +0200 Subject: [PATCH 06/10] add missing node exports --- packages/astro/src/index.server.ts | 2 ++ packages/aws-serverless/src/index.ts | 2 ++ packages/bun/src/index.ts | 2 ++ packages/google-cloud-serverless/src/index.ts | 2 ++ 4 files changed, 8 insertions(+) diff --git a/packages/astro/src/index.server.ts b/packages/astro/src/index.server.ts index 750eb05d8b10..d096e90270ff 100644 --- a/packages/astro/src/index.server.ts +++ b/packages/astro/src/index.server.ts @@ -137,6 +137,8 @@ export { NODE_VERSION, featureFlagsIntegration, type FeatureFlagsIntegration, + launchDarklyIntegration, + buildLaunchDarklyFlagUsedHandler, } 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 b13f69a9b6ce..1a3e38e80265 100644 --- a/packages/aws-serverless/src/index.ts +++ b/packages/aws-serverless/src/index.ts @@ -123,6 +123,8 @@ export { NODE_VERSION, featureFlagsIntegration, type FeatureFlagsIntegration, + launchDarklyIntegration, + buildLaunchDarklyFlagUsedHandler, } from '@sentry/node'; export { diff --git a/packages/bun/src/index.ts b/packages/bun/src/index.ts index 14a44e2d38fc..e5c653433e0b 100644 --- a/packages/bun/src/index.ts +++ b/packages/bun/src/index.ts @@ -141,6 +141,8 @@ export { createSentryWinstonTransport, wrapMcpServerWithSentry, featureFlagsIntegration, + launchDarklyIntegration, + buildLaunchDarklyFlagUsedHandler, } from '@sentry/node'; export { diff --git a/packages/google-cloud-serverless/src/index.ts b/packages/google-cloud-serverless/src/index.ts index e9586a9bd820..e2aa18e60a68 100644 --- a/packages/google-cloud-serverless/src/index.ts +++ b/packages/google-cloud-serverless/src/index.ts @@ -123,6 +123,8 @@ export { NODE_VERSION, featureFlagsIntegration, type FeatureFlagsIntegration, + launchDarklyIntegration, + buildLaunchDarklyFlagUsedHandler, } from '@sentry/node'; export { From 2b7f7083496feafea5dbada497a880835cd3ad1c Mon Sep 17 00:00:00 2001 From: Charly Gomez Date: Tue, 1 Jul 2025 15:34:46 +0200 Subject: [PATCH 07/10] add all integrations --- .../app/server-component/featureFlag/page.tsx | 5 ++ .../tests/server-components.test.ts | 1 + packages/astro/src/index.server.ts | 4 ++ packages/astro/src/index.types.ts | 4 ++ packages/aws-serverless/src/index.ts | 4 ++ packages/bun/src/index.ts | 4 ++ packages/google-cloud-serverless/src/index.ts | 4 ++ packages/nextjs/src/index.types.ts | 4 ++ packages/node/src/index.ts | 9 +++- .../integrations/featureFlagShims/index.ts | 9 ++++ .../featureFlagShims/openFeature.ts | 49 +++++++++++++++++++ .../integrations/featureFlagShims/statsig.ts | 18 +++++++ .../integrations/featureFlagShims/unleash.ts | 18 +++++++ packages/nuxt/src/index.types.ts | 4 ++ packages/react-router/src/index.types.ts | 4 ++ packages/remix/src/index.types.ts | 4 ++ packages/solidstart/src/index.types.ts | 4 ++ packages/sveltekit/src/index.types.ts | 4 ++ .../tanstackstart-react/src/index.types.ts | 4 ++ 19 files changed, 156 insertions(+), 1 deletion(-) create mode 100644 packages/node/src/integrations/featureFlagShims/openFeature.ts create mode 100644 packages/node/src/integrations/featureFlagShims/statsig.ts create mode 100644 packages/node/src/integrations/featureFlagShims/unleash.ts diff --git a/dev-packages/e2e-tests/test-applications/nextjs-app-dir/app/server-component/featureFlag/page.tsx b/dev-packages/e2e-tests/test-applications/nextjs-app-dir/app/server-component/featureFlag/page.tsx index e6bffbf5d31f..a3c97dc99fdf 100644 --- a/dev-packages/e2e-tests/test-applications/nextjs-app-dir/app/server-component/featureFlag/page.tsx +++ b/dev-packages/e2e-tests/test-applications/nextjs-app-dir/app/server-component/featureFlag/page.tsx @@ -4,6 +4,11 @@ export const dynamic = 'force-dynamic'; export default async function FeatureFlagServerComponent() { Sentry.buildLaunchDarklyFlagUsedHandler(); + Sentry.launchDarklyIntegration(); + Sentry.openFeatureIntegration(); + Sentry.statsigIntegration(); + Sentry.unleashIntegration(); + Sentry.OpenFeatureIntegrationHook(); return
FeatureFlagServerComponent
; } diff --git a/dev-packages/e2e-tests/test-applications/nextjs-app-dir/tests/server-components.test.ts b/dev-packages/e2e-tests/test-applications/nextjs-app-dir/tests/server-components.test.ts index b9fa6f8fb201..52f6ae13875a 100644 --- a/dev-packages/e2e-tests/test-applications/nextjs-app-dir/tests/server-components.test.ts +++ b/dev-packages/e2e-tests/test-applications/nextjs-app-dir/tests/server-components.test.ts @@ -136,5 +136,6 @@ test('Should capture an error and transaction for a app router page', async ({ p test('Should not throw error on server component when importing shimmed feature flag function', async ({ page }) => { await page.goto('/server-component/featureFlag'); + // tests that none of the feature flag functions throw an error when imported in a node environment await expect(page.locator('body')).toContainText('FeatureFlagServerComponent'); }); diff --git a/packages/astro/src/index.server.ts b/packages/astro/src/index.server.ts index 85102ef919a5..90ea06e0ef07 100644 --- a/packages/astro/src/index.server.ts +++ b/packages/astro/src/index.server.ts @@ -140,6 +140,10 @@ export { type FeatureFlagsIntegration, launchDarklyIntegration, buildLaunchDarklyFlagUsedHandler, + openFeatureIntegration, + OpenFeatureIntegrationHook, + statsigIntegration, + unleashIntegration, } from '@sentry/node'; export { init } from './server/sdk'; diff --git a/packages/astro/src/index.types.ts b/packages/astro/src/index.types.ts index a04d05d67b8d..ceb4fc6d8a51 100644 --- a/packages/astro/src/index.types.ts +++ b/packages/astro/src/index.types.ts @@ -31,5 +31,9 @@ export declare const logger: typeof clientSdk.logger | typeof serverSdk.logger; export declare const launchDarklyIntegration: typeof clientSdk.launchDarklyIntegration; export declare const buildLaunchDarklyFlagUsedHandler: typeof clientSdk.buildLaunchDarklyFlagUsedHandler; +export declare const openFeatureIntegration: typeof clientSdk.openFeatureIntegration; +export declare const OpenFeatureIntegrationHook: typeof clientSdk.OpenFeatureIntegrationHook; +export declare const statsigIntegration: typeof clientSdk.statsigIntegration; +export declare const unleashIntegration: typeof clientSdk.unleashIntegration; export default sentryAstro; diff --git a/packages/aws-serverless/src/index.ts b/packages/aws-serverless/src/index.ts index a347e65b4e50..f7a5f77ac0fb 100644 --- a/packages/aws-serverless/src/index.ts +++ b/packages/aws-serverless/src/index.ts @@ -126,6 +126,10 @@ export { type FeatureFlagsIntegration, launchDarklyIntegration, buildLaunchDarklyFlagUsedHandler, + openFeatureIntegration, + OpenFeatureIntegrationHook, + statsigIntegration, + unleashIntegration, } from '@sentry/node'; export { diff --git a/packages/bun/src/index.ts b/packages/bun/src/index.ts index 6d716b6e1804..38bd74fda8c2 100644 --- a/packages/bun/src/index.ts +++ b/packages/bun/src/index.ts @@ -144,6 +144,10 @@ export { featureFlagsIntegration, launchDarklyIntegration, buildLaunchDarklyFlagUsedHandler, + openFeatureIntegration, + OpenFeatureIntegrationHook, + statsigIntegration, + unleashIntegration, } from '@sentry/node'; export { diff --git a/packages/google-cloud-serverless/src/index.ts b/packages/google-cloud-serverless/src/index.ts index 067b5611cebc..14797a9af008 100644 --- a/packages/google-cloud-serverless/src/index.ts +++ b/packages/google-cloud-serverless/src/index.ts @@ -126,6 +126,10 @@ export { type FeatureFlagsIntegration, launchDarklyIntegration, buildLaunchDarklyFlagUsedHandler, + openFeatureIntegration, + OpenFeatureIntegrationHook, + statsigIntegration, + unleashIntegration, } from '@sentry/node'; export { diff --git a/packages/nextjs/src/index.types.ts b/packages/nextjs/src/index.types.ts index 3e8e74b6e347..fe5a75bd5c8b 100644 --- a/packages/nextjs/src/index.types.ts +++ b/packages/nextjs/src/index.types.ts @@ -144,3 +144,7 @@ export { captureRequestError } from './common/captureRequestError'; export declare const launchDarklyIntegration: typeof clientSdk.launchDarklyIntegration; export declare const buildLaunchDarklyFlagUsedHandler: typeof clientSdk.buildLaunchDarklyFlagUsedHandler; +export declare const openFeatureIntegration: typeof clientSdk.openFeatureIntegration; +export declare const OpenFeatureIntegrationHook: typeof clientSdk.OpenFeatureIntegrationHook; +export declare const statsigIntegration: typeof clientSdk.statsigIntegration; +export declare const unleashIntegration: typeof clientSdk.unleashIntegration; diff --git a/packages/node/src/index.ts b/packages/node/src/index.ts index 072db5c80ba0..71970174721c 100644 --- a/packages/node/src/index.ts +++ b/packages/node/src/index.ts @@ -37,7 +37,14 @@ export { amqplibIntegration } from './integrations/tracing/amqplib'; export { vercelAIIntegration } from './integrations/tracing/vercelai'; export { childProcessIntegration } from './integrations/childProcess'; export { createSentryWinstonTransport } from './integrations/winston'; -export { launchDarklyIntegration, buildLaunchDarklyFlagUsedHandler } from './integrations/featureFlagShims'; +export { + launchDarklyIntegration, + buildLaunchDarklyFlagUsedHandler, + openFeatureIntegration, + OpenFeatureIntegrationHook, + statsigIntegration, + unleashIntegration, +} from './integrations/featureFlagShims'; export { SentryContextManager } from './otel/contextManager'; export { generateInstrumentOnce } from './otel/instrument'; diff --git a/packages/node/src/integrations/featureFlagShims/index.ts b/packages/node/src/integrations/featureFlagShims/index.ts index c8d5f67101c7..230dbaeeb7e8 100644 --- a/packages/node/src/integrations/featureFlagShims/index.ts +++ b/packages/node/src/integrations/featureFlagShims/index.ts @@ -2,3 +2,12 @@ export { launchDarklyIntegrationShim as launchDarklyIntegration, buildLaunchDarklyFlagUsedHandlerShim as buildLaunchDarklyFlagUsedHandler, } from './launchDarkly'; + +export { + openFeatureIntegrationShim as openFeatureIntegration, + OpenFeatureIntegrationHookShim as OpenFeatureIntegrationHook, +} from './openFeature'; + +export { statsigIntegrationShim as statsigIntegration } from './statsig'; + +export { unleashIntegrationShim as unleashIntegration } from './unleash'; diff --git a/packages/node/src/integrations/featureFlagShims/openFeature.ts b/packages/node/src/integrations/featureFlagShims/openFeature.ts new file mode 100644 index 000000000000..d0768fb618de --- /dev/null +++ b/packages/node/src/integrations/featureFlagShims/openFeature.ts @@ -0,0 +1,49 @@ +import { consoleSandbox, defineIntegration, isBrowser } from '@sentry/core'; + +/** + * This is a shim for the OpenFeature integration. + * We need this in order to not throw runtime errors when accidentally importing this on the server through a meta framework like Next.js. + */ +export const openFeatureIntegrationShim = defineIntegration((_options?: unknown) => { + if (!isBrowser()) { + consoleSandbox(() => { + // eslint-disable-next-line no-console + console.warn('The openFeatureIntegration() can only be used in the browser.'); + }); + } + + return { + name: 'OpenFeature', + }; +}); + +/** + * This is a shim for the OpenFeature integration hook. + */ +export class OpenFeatureIntegrationHookShim { + /** + * + */ + public constructor() { + if (!isBrowser()) { + consoleSandbox(() => { + // eslint-disable-next-line no-console + console.warn('The OpenFeatureIntegrationHook can only be used in the browser.'); + }); + } + } + + /** + * + */ + public after(): void { + // No-op + } + + /** + * + */ + public error(): void { + // No-op + } +} diff --git a/packages/node/src/integrations/featureFlagShims/statsig.ts b/packages/node/src/integrations/featureFlagShims/statsig.ts new file mode 100644 index 000000000000..8a74170d2b1c --- /dev/null +++ b/packages/node/src/integrations/featureFlagShims/statsig.ts @@ -0,0 +1,18 @@ +import { consoleSandbox, defineIntegration, isBrowser } from '@sentry/core'; + +/** + * This is a shim for the Statsig integration. + * We need this in order to not throw runtime errors when accidentally importing this on the server through a meta framework like Next.js. + */ +export const statsigIntegrationShim = defineIntegration((_options?: unknown) => { + if (!isBrowser()) { + consoleSandbox(() => { + // eslint-disable-next-line no-console + console.warn('The statsigIntegration() can only be used in the browser.'); + }); + } + + return { + name: 'Statsig', + }; +}); diff --git a/packages/node/src/integrations/featureFlagShims/unleash.ts b/packages/node/src/integrations/featureFlagShims/unleash.ts new file mode 100644 index 000000000000..748e63b71040 --- /dev/null +++ b/packages/node/src/integrations/featureFlagShims/unleash.ts @@ -0,0 +1,18 @@ +import { consoleSandbox, defineIntegration, isBrowser } from '@sentry/core'; + +/** + * This is a shim for the Unleash integration. + * We need this in order to not throw runtime errors when accidentally importing this on the server through a meta framework like Next.js. + */ +export const unleashIntegrationShim = defineIntegration((_options?: unknown) => { + if (!isBrowser()) { + consoleSandbox(() => { + // eslint-disable-next-line no-console + console.warn('The unleashIntegration() can only be used in the browser.'); + }); + } + + return { + name: 'Unleash', + }; +}); diff --git a/packages/nuxt/src/index.types.ts b/packages/nuxt/src/index.types.ts index 4e9f887cac45..4f006e0b5b07 100644 --- a/packages/nuxt/src/index.types.ts +++ b/packages/nuxt/src/index.types.ts @@ -21,3 +21,7 @@ export declare const logger: typeof clientSdk.logger | typeof serverSdk.logger; export declare const launchDarklyIntegration: typeof clientSdk.launchDarklyIntegration; export declare const buildLaunchDarklyFlagUsedHandler: typeof clientSdk.buildLaunchDarklyFlagUsedHandler; +export declare const openFeatureIntegration: typeof clientSdk.openFeatureIntegration; +export declare const OpenFeatureIntegrationHook: typeof clientSdk.OpenFeatureIntegrationHook; +export declare const statsigIntegration: typeof clientSdk.statsigIntegration; +export declare const unleashIntegration: typeof clientSdk.unleashIntegration; diff --git a/packages/react-router/src/index.types.ts b/packages/react-router/src/index.types.ts index 72988e3afc8f..150fc45a1e63 100644 --- a/packages/react-router/src/index.types.ts +++ b/packages/react-router/src/index.types.ts @@ -21,3 +21,7 @@ export declare const logger: typeof clientSdk.logger | typeof serverSdk.logger; export declare const launchDarklyIntegration: typeof clientSdk.launchDarklyIntegration; export declare const buildLaunchDarklyFlagUsedHandler: typeof clientSdk.buildLaunchDarklyFlagUsedHandler; +export declare const openFeatureIntegration: typeof clientSdk.openFeatureIntegration; +export declare const OpenFeatureIntegrationHook: typeof clientSdk.OpenFeatureIntegrationHook; +export declare const statsigIntegration: typeof clientSdk.statsigIntegration; +export declare const unleashIntegration: typeof clientSdk.unleashIntegration; diff --git a/packages/remix/src/index.types.ts b/packages/remix/src/index.types.ts index 3701216fb65d..d0df7397f612 100644 --- a/packages/remix/src/index.types.ts +++ b/packages/remix/src/index.types.ts @@ -35,3 +35,7 @@ export const lastEventId = runtime === 'client' ? clientSdk.lastEventId : server export declare const launchDarklyIntegration: typeof clientSdk.launchDarklyIntegration; export declare const buildLaunchDarklyFlagUsedHandler: typeof clientSdk.buildLaunchDarklyFlagUsedHandler; +export declare const openFeatureIntegration: typeof clientSdk.openFeatureIntegration; +export declare const OpenFeatureIntegrationHook: typeof clientSdk.OpenFeatureIntegrationHook; +export declare const statsigIntegration: typeof clientSdk.statsigIntegration; +export declare const unleashIntegration: typeof clientSdk.unleashIntegration; diff --git a/packages/solidstart/src/index.types.ts b/packages/solidstart/src/index.types.ts index 4dd127608a5c..7725d1ad3d3c 100644 --- a/packages/solidstart/src/index.types.ts +++ b/packages/solidstart/src/index.types.ts @@ -28,3 +28,7 @@ export declare const logger: typeof clientSdk.logger | typeof serverSdk.logger; export declare const launchDarklyIntegration: typeof clientSdk.launchDarklyIntegration; export declare const buildLaunchDarklyFlagUsedHandler: typeof clientSdk.buildLaunchDarklyFlagUsedHandler; +export declare const openFeatureIntegration: typeof clientSdk.openFeatureIntegration; +export declare const OpenFeatureIntegrationHook: typeof clientSdk.OpenFeatureIntegrationHook; +export declare const statsigIntegration: typeof clientSdk.statsigIntegration; +export declare const unleashIntegration: typeof clientSdk.unleashIntegration; diff --git a/packages/sveltekit/src/index.types.ts b/packages/sveltekit/src/index.types.ts index 5853d4bc7a96..108e262f9992 100644 --- a/packages/sveltekit/src/index.types.ts +++ b/packages/sveltekit/src/index.types.ts @@ -62,3 +62,7 @@ export declare const logger: typeof clientSdk.logger | typeof serverSdk.logger; export declare const launchDarklyIntegration: typeof clientSdk.launchDarklyIntegration; export declare const buildLaunchDarklyFlagUsedHandler: typeof clientSdk.buildLaunchDarklyFlagUsedHandler; +export declare const openFeatureIntegration: typeof clientSdk.openFeatureIntegration; +export declare const OpenFeatureIntegrationHook: typeof clientSdk.OpenFeatureIntegrationHook; +export declare const statsigIntegration: typeof clientSdk.statsigIntegration; +export declare const unleashIntegration: typeof clientSdk.unleashIntegration; diff --git a/packages/tanstackstart-react/src/index.types.ts b/packages/tanstackstart-react/src/index.types.ts index b65e836e0691..448ea35f637b 100644 --- a/packages/tanstackstart-react/src/index.types.ts +++ b/packages/tanstackstart-react/src/index.types.ts @@ -31,3 +31,7 @@ export declare const logger: typeof clientSdk.logger | typeof serverSdk.logger; export declare const launchDarklyIntegration: typeof clientSdk.launchDarklyIntegration; export declare const buildLaunchDarklyFlagUsedHandler: typeof clientSdk.buildLaunchDarklyFlagUsedHandler; +export declare const openFeatureIntegration: typeof clientSdk.openFeatureIntegration; +export declare const OpenFeatureIntegrationHook: typeof clientSdk.OpenFeatureIntegrationHook; +export declare const statsigIntegration: typeof clientSdk.statsigIntegration; +export declare const unleashIntegration: typeof clientSdk.unleashIntegration; From 7f830807eec07811aeda6b4e92898780857400a1 Mon Sep 17 00:00:00 2001 From: Charly Gomez Date: Tue, 1 Jul 2025 15:59:14 +0200 Subject: [PATCH 08/10] ts-ignore the args --- .../nextjs-app-dir/app/server-component/featureFlag/page.tsx | 3 +++ 1 file changed, 3 insertions(+) diff --git a/dev-packages/e2e-tests/test-applications/nextjs-app-dir/app/server-component/featureFlag/page.tsx b/dev-packages/e2e-tests/test-applications/nextjs-app-dir/app/server-component/featureFlag/page.tsx index a3c97dc99fdf..9704ef80e6d5 100644 --- a/dev-packages/e2e-tests/test-applications/nextjs-app-dir/app/server-component/featureFlag/page.tsx +++ b/dev-packages/e2e-tests/test-applications/nextjs-app-dir/app/server-component/featureFlag/page.tsx @@ -6,8 +6,11 @@ export default async function FeatureFlagServerComponent() { Sentry.buildLaunchDarklyFlagUsedHandler(); Sentry.launchDarklyIntegration(); Sentry.openFeatureIntegration(); + // @ts-ignore - we just want to test that the statsigIntegration is imported Sentry.statsigIntegration(); + // @ts-ignore - we just want to test that the unleashIntegration is imported Sentry.unleashIntegration(); + // @ts-ignore - we just want to test that the OpenFeatureIntegrationHook is imported Sentry.OpenFeatureIntegrationHook(); return
FeatureFlagServerComponent
; From d566ee7df0715fcd94f390611d3debe7344c4dbe Mon Sep 17 00:00:00 2001 From: Charly Gomez Date: Tue, 1 Jul 2025 16:25:48 +0200 Subject: [PATCH 09/10] beep --- .../nextjs-app-dir/app/server-component/featureFlag/page.tsx | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/dev-packages/e2e-tests/test-applications/nextjs-app-dir/app/server-component/featureFlag/page.tsx b/dev-packages/e2e-tests/test-applications/nextjs-app-dir/app/server-component/featureFlag/page.tsx index 9704ef80e6d5..3db71ae022ef 100644 --- a/dev-packages/e2e-tests/test-applications/nextjs-app-dir/app/server-component/featureFlag/page.tsx +++ b/dev-packages/e2e-tests/test-applications/nextjs-app-dir/app/server-component/featureFlag/page.tsx @@ -6,12 +6,11 @@ export default async function FeatureFlagServerComponent() { Sentry.buildLaunchDarklyFlagUsedHandler(); Sentry.launchDarklyIntegration(); Sentry.openFeatureIntegration(); + new Sentry.OpenFeatureIntegrationHook(); // @ts-ignore - we just want to test that the statsigIntegration is imported Sentry.statsigIntegration(); // @ts-ignore - we just want to test that the unleashIntegration is imported Sentry.unleashIntegration(); - // @ts-ignore - we just want to test that the OpenFeatureIntegrationHook is imported - Sentry.OpenFeatureIntegrationHook(); return
FeatureFlagServerComponent
; } From 5e55bef6cb230d5824cb5d62f0c633319985e3bd Mon Sep 17 00:00:00 2001 From: Charly Gomez Date: Wed, 2 Jul 2025 10:41:31 +0200 Subject: [PATCH 10/10] Update packages/integration-shims/src/launchDarkly.ts Co-authored-by: Andrei <168741329+andreiborza@users.noreply.github.com> --- packages/integration-shims/src/launchDarkly.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/integration-shims/src/launchDarkly.ts b/packages/integration-shims/src/launchDarkly.ts index 8254ac8e4b80..76750f5c863c 100644 --- a/packages/integration-shims/src/launchDarkly.ts +++ b/packages/integration-shims/src/launchDarkly.ts @@ -25,7 +25,7 @@ export function buildLaunchDarklyFlagUsedHandlerShim(): unknown { if (!isBrowser()) { consoleSandbox(() => { // eslint-disable-next-line no-console - console.warn('The buildLaunchDarklyFlagUsedHandler() should only be used in the browser.'); + console.warn('The buildLaunchDarklyFlagUsedHandler() can only be used in the browser.'); }); }