Skip to content

Commit ce18641

Browse files
committed
feat(core): Allow to configure disableIntegrations to opt-out
This new top-level config allows to opt-out of any added integration, ensuring it is not actually added. This is mainly designed to opt-out of default integrations, but will also apply to any manually added integration. There are type hints that should help with usage there, but any key is allowed to keep this flexible. Type hints are manually configured (could not find a way to infer this from integrations, as the names are not statically exposed there...) for now. Usage: ```js Sentry.init({ disableIntegrations: { BrowserSession: true, InboundFilters: false } }); ``` Will disable the browserSessionIntegration but not the inboundFilters one.
1 parent 3e7ac9a commit ce18641

File tree

23 files changed

+226
-25
lines changed

23 files changed

+226
-25
lines changed

Diff for: packages/aws-serverless/src/sdk.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -71,7 +71,7 @@ export function getDefaultIntegrations(_options: Options): Integration[] {
7171
*
7272
* @param options Configuration options for the SDK, @see {@link AWSLambdaOptions}.
7373
*/
74-
export function init(options: NodeOptions = {}): NodeClient | undefined {
74+
export function init(options: NodeOptions<['Aws', 'AwsLambda']> = {}): NodeClient | undefined {
7575
const opts = {
7676
_metadata: {} as SdkMetadata,
7777
defaultIntegrations: getDefaultIntegrations(options),

Diff for: packages/browser/src/client.ts

+16-1
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,22 @@ import type { BrowserTransportOptions } from './transports/types';
2424
* Configuration options for the Sentry Browser SDK.
2525
* @see @sentry/core Options for more information.
2626
*/
27-
export type BrowserOptions = Options<BrowserTransportOptions> &
27+
export type BrowserOptions<AdditionalDefaultIntegrations extends string[] = []> = Options<
28+
BrowserTransportOptions,
29+
[
30+
'InboundFilters',
31+
'FunctionToString',
32+
'BrowserApiErrors',
33+
'Breadcrumbs',
34+
'GlobalHandlers',
35+
'LinkedErrors',
36+
'Dedupe',
37+
'HttpContext',
38+
'BrowserSession',
39+
'BrowserTracing',
40+
...AdditionalDefaultIntegrations,
41+
]
42+
> &
2843
BrowserClientReplayOptions &
2944
BrowserClientProfilingOptions & {
3045
/**

Diff for: packages/browser/src/sdk.ts

+2
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,8 @@ export function getDefaultIntegrations(_options: Options): Integration[] {
3232
/**
3333
* Note: Please make sure this stays in sync with Angular SDK, which re-exports
3434
* `getDefaultIntegrations` but with an adjusted set of integrations.
35+
*
36+
* Ensure to keep this in sync with `BrowserOptions`!
3537
*/
3638
return [
3739
inboundFiltersIntegration(),

Diff for: packages/bun/src/sdk.ts

+4-1
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,10 @@ import { bunServerIntegration } from './integrations/bunserver';
2323
import { makeFetchTransport } from './transports';
2424
import type { BunOptions } from './types';
2525

26-
/** Get the default integrations for the Bun SDK. */
26+
/**
27+
* Get the default integrations for the Bun SDK.
28+
* Ensure to keep this in sync with `BunOptions`!
29+
*/
2730
export function getDefaultIntegrations(_options: Options): Integration[] {
2831
// We return a copy of the defaultIntegrations here to avoid mutating this
2932
return [

Diff for: packages/bun/src/types.ts

+20-1
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,26 @@ export interface BaseBunOptions {
4141
* Configuration options for the Sentry Bun SDK
4242
* @see @sentry/core Options for more information.
4343
*/
44-
export interface BunOptions extends Options<BunTransportOptions>, BaseBunOptions {}
44+
export interface BunOptions
45+
extends Options<
46+
BunTransportOptions,
47+
[
48+
'InboundFilters',
49+
'FunctionToString',
50+
'LinkedErrors',
51+
'RequestData',
52+
'Console',
53+
'Http',
54+
'NodeFetch',
55+
'OnUncaughtException',
56+
'OnUnhandledRejection',
57+
'ContextLines',
58+
'Context',
59+
'Modules',
60+
'BunServer',
61+
]
62+
>,
63+
BaseBunOptions {}
4564

4665
/**
4766
* Configuration options for the Sentry Bun SDK Client class

Diff for: packages/cloudflare/src/client.ts

+6-1
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,12 @@ interface BaseCloudflareOptions {}
3737
*
3838
* @see @sentry/core Options for more information.
3939
*/
40-
export interface CloudflareOptions extends Options<CloudflareTransportOptions>, BaseCloudflareOptions {}
40+
export interface CloudflareOptions
41+
extends Options<
42+
CloudflareTransportOptions,
43+
['Dedupe', 'InboundFilters', 'FunctionToString', 'LinkedErrors', 'Fetch', 'RequestData']
44+
>,
45+
BaseCloudflareOptions {}
4146

4247
/**
4348
* Configuration options for the Sentry Cloudflare SDK Client class

Diff for: packages/cloudflare/src/sdk.ts

+4-1
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,10 @@ import { fetchIntegration } from './integrations/fetch';
1515
import { makeCloudflareTransport } from './transport';
1616
import { defaultStackParser } from './vendor/stacktrace';
1717

18-
/** Get the default integrations for the Cloudflare SDK. */
18+
/**
19+
* Get the default integrations for the Cloudflare SDK.
20+
* Ensure to keep this in sync with `CloudflareOptions`!
21+
*/
1922
export function getDefaultIntegrations(options: CloudflareOptions): Integration[] {
2023
const sendDefaultPii = options.sendDefaultPii ?? false;
2124
return [

Diff for: packages/core/src/integration.ts

+7-1
Original file line numberDiff line numberDiff line change
@@ -40,9 +40,12 @@ function filterDuplicates(integrations: Integration[]): Integration[] {
4040
}
4141

4242
/** Gets integrations to install */
43-
export function getIntegrationsToSetup(options: Pick<Options, 'defaultIntegrations' | 'integrations'>): Integration[] {
43+
export function getIntegrationsToSetup(
44+
options: Pick<Options, 'defaultIntegrations' | 'integrations' | 'disableIntegrations'>,
45+
): Integration[] {
4446
const defaultIntegrations = options.defaultIntegrations || [];
4547
const userIntegrations = options.integrations;
48+
const disableIntegrations = options.disableIntegrations || {};
4649

4750
// We flag default instances, so that later we can tell them apart from any user-created instances of the same class
4851
defaultIntegrations.forEach((integration: IntegrationWithDefaultInstance) => {
@@ -60,6 +63,9 @@ export function getIntegrationsToSetup(options: Pick<Options, 'defaultIntegratio
6063
integrations = defaultIntegrations;
6164
}
6265

66+
// Remove disabled integrations
67+
integrations = integrations.filter(integration => !disableIntegrations[integration.name]);
68+
6369
return filterDuplicates(integrations);
6470
}
6571

Diff for: packages/core/src/types-hoist/integration.ts

+7
Original file line numberDiff line numberDiff line change
@@ -48,3 +48,10 @@ export interface Integration {
4848
* This is expected to return an integration.
4949
*/
5050
export type IntegrationFn<IntegrationType = Integration> = (...rest: any[]) => IntegrationType;
51+
52+
/**
53+
* A map of integration names to true/false.
54+
*/
55+
export type IntegrationsMapping<KnownIntegrationNames extends string[] = []> = Record<string, boolean | undefined> & {
56+
[key in KnownIntegrationNames[number]]?: boolean | undefined;
57+
};

Diff for: packages/core/src/types-hoist/options.ts

+16-3
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import type { CaptureContext } from '../scope';
22
import type { Breadcrumb, BreadcrumbHint } from './breadcrumb';
33
import type { ErrorEvent, EventHint, TransactionEvent } from './event';
4-
import type { Integration } from './integration';
4+
import type { Integration, IntegrationsMapping } from './integration';
55
import type { TracesSamplerSamplingContext } from './samplingcontext';
66
import type { SdkMetadata } from './sdkmetadata';
77
import type { SpanJSON } from './span';
@@ -302,14 +302,27 @@ export interface ClientOptions<TO extends BaseTransportOptions = BaseTransportOp
302302
}
303303

304304
/** Base configuration options for every SDK. */
305-
export interface Options<TO extends BaseTransportOptions = BaseTransportOptions>
306-
extends Omit<Partial<ClientOptions<TO>>, 'integrations' | 'transport' | 'stackParser'> {
305+
export interface Options<
306+
TO extends BaseTransportOptions = BaseTransportOptions,
307+
DefaultIntegrationNames extends string[] = [],
308+
> extends Omit<Partial<ClientOptions<TO>>, 'integrations' | 'transport' | 'stackParser'> {
307309
/**
308310
* If this is set to false, default integrations will not be added, otherwise this will internally be set to the
309311
* recommended default integrations.
310312
*/
311313
defaultIntegrations?: false | Integration[];
312314

315+
/**
316+
* Pass a map of integrations that should be explicitly disabled.
317+
* This allows you to e.g. opt out of default integrations easily.
318+
* For example, if you do not want to add the `inboundFiltersIntegration`, you can configure:
319+
*
320+
* ```js
321+
* disableIntegrations: { InboundFilters: true }
322+
* ```
323+
*/
324+
disableIntegrations?: IntegrationsMapping<DefaultIntegrationNames>;
325+
313326
/**
314327
* List of integrations that should be installed after SDK was initialized.
315328
* Accepts either a list of integrations or a function that receives

Diff for: packages/core/test/lib/integration.test.ts

+57
Original file line numberDiff line numberDiff line change
@@ -189,6 +189,63 @@ describe('getIntegrationsToSetup', () => {
189189
});
190190
});
191191

192+
describe('disableIntegrations', () => {
193+
it('works without integrations', () => {
194+
const integrations = getIntegrationsToSetup({
195+
integrations: [],
196+
disableIntegrations: {},
197+
});
198+
199+
expect(integrations.map(i => i.name)).toEqual([]);
200+
});
201+
202+
it('ignores unknown integration names', () => {
203+
const integrations = getIntegrationsToSetup({
204+
integrations: [new MockIntegration('foo'), new MockIntegration('bar')],
205+
disableIntegrations: {
206+
foo2: true,
207+
bar2: true,
208+
},
209+
});
210+
211+
expect(integrations.map(i => i.name)).toEqual(['foo', 'bar']);
212+
});
213+
214+
it('removes default integrations', () => {
215+
const integrations = getIntegrationsToSetup({
216+
defaultIntegrations: [new MockIntegration('foo'), new MockIntegration('bar'), new MockIntegration('baz')],
217+
disableIntegrations: {
218+
bar: true,
219+
},
220+
});
221+
222+
expect(integrations.map(i => i.name)).toEqual(['foo', 'baz']);
223+
});
224+
225+
it('removes default integrations', () => {
226+
const integrations = getIntegrationsToSetup({
227+
defaultIntegrations: [new MockIntegration('foo'), new MockIntegration('bar'), new MockIntegration('baz')],
228+
disableIntegrations: {
229+
bar: true,
230+
},
231+
});
232+
233+
expect(integrations.map(i => i.name)).toEqual(['foo', 'baz']);
234+
});
235+
236+
it('ignores default integrations when setting false or undefined', () => {
237+
const integrations = getIntegrationsToSetup({
238+
defaultIntegrations: [new MockIntegration('foo'), new MockIntegration('bar'), new MockIntegration('baz')],
239+
disableIntegrations: {
240+
bar: false,
241+
foo: undefined,
242+
},
243+
});
244+
245+
expect(integrations.map(i => i.name)).toEqual(['foo', 'bar', 'baz']);
246+
});
247+
});
248+
192249
it('works with empty array', () => {
193250
const integrations = getIntegrationsToSetup({
194251
integrations: [],

Diff for: packages/deno/src/sdk.ts

+6-3
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import type { Client, Integration, Options, ServerRuntimeClientOptions, StackParser } from '@sentry/core';
1+
import type { Client, Integration, ServerRuntimeClientOptions, StackParser } from '@sentry/core';
22
import {
33
createStackParser,
44
dedupeIntegration,
@@ -19,8 +19,11 @@ import { normalizePathsIntegration } from './integrations/normalizepaths';
1919
import { makeFetchTransport } from './transports';
2020
import type { DenoOptions } from './types';
2121

22-
/** Get the default integrations for the Deno SDK. */
23-
export function getDefaultIntegrations(_options: Options): Integration[] {
22+
/**
23+
* Get the default integrations for the Deno SDK.
24+
* Ensure to keep this in sync with `DenoOptions`!
25+
*/
26+
export function getDefaultIntegrations(_options: DenoOptions): Integration[] {
2427
// We return a copy of the defaultIntegrations here to avoid mutating this
2528
return [
2629
// Common

Diff for: packages/deno/src/types.ts

+16-1
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,22 @@ export interface BaseDenoOptions {
3131
* Configuration options for the Sentry Deno SDK
3232
* @see @sentry/core Options for more information.
3333
*/
34-
export interface DenoOptions extends Options<DenoTransportOptions>, BaseDenoOptions {}
34+
export interface DenoOptions
35+
extends Options<
36+
DenoTransportOptions,
37+
[
38+
'InboundFilters',
39+
'FunctionToString',
40+
'LinkedErrors',
41+
'Dedupe',
42+
'Breadcrumbs',
43+
'DenoContext',
44+
'ContextLines',
45+
'NormalizePaths',
46+
'GLobalHandlers',
47+
]
48+
>,
49+
BaseDenoOptions {}
3550

3651
/**
3752
* Configuration options for the Sentry Deno SDK Client class

Diff for: packages/google-cloud-serverless/src/sdk.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@ export function getDefaultIntegrations(_options: Options): Integration[] {
2626
/**
2727
* @see {@link Sentry.init}
2828
*/
29-
export function init(options: NodeOptions = {}): NodeClient | undefined {
29+
export function init(options: NodeOptions<['GoogleCloudHttp', 'GoogleCloudGrpc']> = {}): NodeClient | undefined {
3030
const opts = {
3131
_metadata: {} as SdkMetadata,
3232
defaultIntegrations: getDefaultIntegrations(options),

Diff for: packages/nestjs/src/sdk.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ import { nestIntegration } from './integrations/nest';
1212
/**
1313
* Initializes the NestJS SDK
1414
*/
15-
export function init(options: NodeOptions | undefined = {}): NodeClient | undefined {
15+
export function init(options: NodeOptions<['Nest']> | undefined = {}): NodeClient | undefined {
1616
const opts: NodeOptions = {
1717
defaultIntegrations: getDefaultIntegrations(options),
1818
...options,

Diff for: packages/nextjs/src/client/index.ts

+2-2
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ const globalWithInjectedValues = GLOBAL_OBJ as typeof GLOBAL_OBJ & {
2323
declare const __SENTRY_TRACING__: boolean;
2424

2525
/** Inits the Sentry NextJS SDK on the browser with the React SDK. */
26-
export function init(options: BrowserOptions): Client | undefined {
26+
export function init(options: BrowserOptions<['NextjsClientStackFrameNormalization']>): Client | undefined {
2727
const opts = {
2828
environment: getVercelEnv(true) || process.env.NODE_ENV,
2929
defaultIntegrations: getDefaultIntegrations(options),
@@ -59,7 +59,7 @@ export function init(options: BrowserOptions): Client | undefined {
5959
return client;
6060
}
6161

62-
function getDefaultIntegrations(options: BrowserOptions): Integration[] {
62+
function getDefaultIntegrations(options: BrowserOptions<['NextjsClientStackFrameNormalization']>): Integration[] {
6363
const customDefaultIntegrations = getReactDefaultIntegrations(options);
6464
// This evaluates to true unless __SENTRY_TRACING__ is text-replaced with "false",
6565
// in which case everything inside will get tree-shaken away

Diff for: packages/node/src/integrations/tracing/index.ts

+2
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,8 @@ import { instrumentVercelAi, vercelAIIntegration } from './vercelai';
2222

2323
/**
2424
* With OTEL, all performance integrations will be added, as OTEL only initializes them when the patched package is actually required.
25+
*
26+
* Ensure to keep this in sync with `NodeOptions`!
2527
*/
2628
export function getAutoPerformanceIntegrations(): Integration[] {
2729
return [

Diff for: packages/node/src/integrations/tracing/vercelai/index.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ export const instrumentVercelAi = generateInstrumentOnce('vercelAI', () => new S
99

1010
const _vercelAIIntegration = (() => {
1111
return {
12-
name: 'vercelAI',
12+
name: 'VercelAI',
1313
setupOnce() {
1414
instrumentVercelAi();
1515
},

Diff for: packages/node/src/sdk/index.ts

+2
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,8 @@ function getCjsOnlyIntegrations(): Integration[] {
4747

4848
/**
4949
* Get default integrations, excluding performance.
50+
*
51+
* Ensure to keep this in sync with `NodeOptions`!
5052
*/
5153
export function getDefaultIntegrationsWithoutPerformance(): Integration[] {
5254
return [

0 commit comments

Comments
 (0)