Skip to content

feat(node): Export server-side feature flag integration shims #16735

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 13 commits into from
Jul 2, 2025
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
import * as Sentry from '@sentry/nextjs';

export const dynamic = 'force-dynamic';

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();

return <div>FeatureFlagServerComponent</div>;
}
Original file line number Diff line number Diff line change
Expand Up @@ -133,3 +133,9 @@ 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');
});
6 changes: 6 additions & 0 deletions packages/astro/src/index.server.ts
Original file line number Diff line number Diff line change
Expand Up @@ -138,6 +138,12 @@ export {
NODE_VERSION,
featureFlagsIntegration,
type FeatureFlagsIntegration,
launchDarklyIntegration,
buildLaunchDarklyFlagUsedHandler,
openFeatureIntegration,
OpenFeatureIntegrationHook,
statsigIntegration,
unleashIntegration,
} from '@sentry/node';

export { init } from './server/sdk';
Expand Down
7 changes: 7 additions & 0 deletions packages/astro/src/index.types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -29,4 +29,11 @@ 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 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;
6 changes: 6 additions & 0 deletions packages/aws-serverless/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -124,6 +124,12 @@ export {
NODE_VERSION,
featureFlagsIntegration,
type FeatureFlagsIntegration,
launchDarklyIntegration,
buildLaunchDarklyFlagUsedHandler,
openFeatureIntegration,
OpenFeatureIntegrationHook,
statsigIntegration,
unleashIntegration,
} from '@sentry/node';

export {
Expand Down
6 changes: 6 additions & 0 deletions packages/bun/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -142,6 +142,12 @@ export {
createSentryWinstonTransport,
wrapMcpServerWithSentry,
featureFlagsIntegration,
launchDarklyIntegration,
buildLaunchDarklyFlagUsedHandler,
openFeatureIntegration,
OpenFeatureIntegrationHook,
statsigIntegration,
unleashIntegration,
} from '@sentry/node';

export {
Expand Down
6 changes: 6 additions & 0 deletions packages/google-cloud-serverless/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -124,6 +124,12 @@ export {
NODE_VERSION,
featureFlagsIntegration,
type FeatureFlagsIntegration,
launchDarklyIntegration,
buildLaunchDarklyFlagUsedHandler,
openFeatureIntegration,
OpenFeatureIntegrationHook,
statsigIntegration,
unleashIntegration,
} from '@sentry/node';

export {
Expand Down
1 change: 1 addition & 0 deletions packages/integration-shims/src/index.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
export { feedbackIntegrationShim } from './Feedback';
export { replayIntegrationShim } from './Replay';
export { browserTracingIntegrationShim } from './BrowserTracing';
export { launchDarklyIntegrationShim, buildLaunchDarklyFlagUsedHandlerShim } from './launchDarkly';
38 changes: 38 additions & 0 deletions packages/integration-shims/src/launchDarkly.ts
Original file line number Diff line number Diff line change
@@ -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,
};
}
7 changes: 7 additions & 0 deletions packages/nextjs/src/index.types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -141,3 +141,10 @@ export declare function wrapApiHandlerWithSentryVercelCrons<F extends (...args:
export declare function wrapPageComponentWithSentry<C>(WrappingTarget: C): C;

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;
8 changes: 8 additions & 0 deletions packages/node/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +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,
openFeatureIntegration,
OpenFeatureIntegrationHook,
statsigIntegration,
unleashIntegration,
} from './integrations/featureFlagShims';

export { SentryContextManager } from './otel/contextManager';
export { generateInstrumentOnce } from './otel/instrument';
Expand Down
13 changes: 13 additions & 0 deletions packages/node/src/integrations/featureFlagShims/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
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';
37 changes: 37 additions & 0 deletions packages/node/src/integrations/featureFlagShims/launchDarkly.ts
Original file line number Diff line number Diff line change
@@ -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,
};
}
49 changes: 49 additions & 0 deletions packages/node/src/integrations/featureFlagShims/openFeature.ts
Original file line number Diff line number Diff line change
@@ -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
}
}
18 changes: 18 additions & 0 deletions packages/node/src/integrations/featureFlagShims/statsig.ts
Original file line number Diff line number Diff line change
@@ -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',
};
});
18 changes: 18 additions & 0 deletions packages/node/src/integrations/featureFlagShims/unleash.ts
Original file line number Diff line number Diff line change
@@ -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',
};
});
7 changes: 7 additions & 0 deletions packages/nuxt/src/index.types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,3 +18,10 @@ 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;
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;
7 changes: 7 additions & 0 deletions packages/react-router/src/index.types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,3 +18,10 @@ 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;
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;
7 changes: 7 additions & 0 deletions packages/remix/src/index.types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -32,3 +32,10 @@ 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;
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;
7 changes: 7 additions & 0 deletions packages/solidstart/src/index.types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,3 +25,10 @@ export declare function flush(timeout?: number | undefined): PromiseLike<boolean
export declare function lastEventId(): string | undefined;

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;
7 changes: 7 additions & 0 deletions packages/sveltekit/src/index.types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -59,3 +59,10 @@ export declare function lastEventId(): string | undefined;
export declare function trackComponent(options: clientSdk.TrackingOptions): ReturnType<typeof clientSdk.trackComponent>;

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;
7 changes: 7 additions & 0 deletions packages/tanstackstart-react/src/index.types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -28,3 +28,10 @@ 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;
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;
Loading