8000 feat: Stop passing `defaultIntegrations` as client option by mydea · Pull Request #15307 · getsentry/sentry-javascript · GitHub
[go: up one dir, main page]

Skip to content

feat: Stop passing defaultIntegrations as client option #15307

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

Open
wants to merge 5 commits into
base: develop
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
22 changes: 15 additions & 7 deletions packages/angular/src/sdk.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,19 +2,23 @@ import { VERSION } from '@angular/core';
import type { BrowserOptions } from '@sentry/browser';
import {
breadcrumbsIntegration,
BrowserClient,
browserSessionIntegration,
defaultStackParser,
globalHandlersIntegration,
httpContextIntegration,
init as browserInit,
linkedErrorsIntegration,
makeFetchTransport,
setContext,
} from '@sentry/browser';
import type { Client, Integration } from '@sentry/core';
import {
applySdkMetadata,
dedupeIntegration,
functionToStringIntegration,
getClientOptions,
inboundFiltersIntegration,
initAndBind,
logger,
} from '@sentry/core';
import { IS_DEBUG_BUILD } from './flags';
Expand Down Expand Up @@ -49,15 +53,19 @@ export function getDefaultIntegrations(_options: BrowserOptions = {}): Integrati
* Inits the Angular SDK
*/
export function init(options: BrowserOptions): Client | undefined {
const opts = {
defaultIntegrations: getDefaultIntegrations(),
.. F438 .options,
};
const clientOptions = getClientOptions(options, {
integrations: getDefaultIntegrations(options),
stackParser: defaultStackParser,
transport: makeFetchTransport,
});

applySdkMetadata(opts, 'angular');
applySdkMetadata(clientOptions, 'angular');

const client = initAndBind(BrowserClient, clientOptions);

checkAndSetAngularVersion();
return browserInit(opts);

return client;
}

function checkAndSetAngularVersion(): void {
Expand Down
19 changes: 11 additions & 8 deletions packages/astro/src/client/sdk.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,13 @@
import type { BrowserOptions } from '@sentry/browser';
import {
BrowserClient,
browserTracingIntegration,
defaultStackParser,
getDefaultIntegrations as getBrowserDefaultIntegrations,
init as initBrowserSdk,
makeFetchTransport,
} from '@sentry/browser';
import type { Client, Integration } from '@sentry/core';
import { applySdkMetadata } from '@sentry/core';
import { applySdkMetadata, getClientOptions, initAndBind } from '@sentry/core';

// Tree-shakable guard to remove all code related to tracing
declare const __SENTRY_TRACING__: boolean;
Expand All @@ -16,14 +18,15 @@ declare const __SENTRY_TRACING__: boolean;
* @param options Configuration options for the SDK.
*/
export function init(options: BrowserOptions): Client | undefined {
const opts = {
defaultIntegrations: getDefaultIntegrations(options),
...options,
};
const clientOptions = getClientOptions(options, {
integrations: getDefaultIntegrations(options),
stackParser: defaultStackParser,
transport: makeFetchTransport,
});

applySdkMetadata(opts, 'astro', ['astro', 'browser']);
applySdkMetadata(clientOptions, 'astro', ['astro', 'browser']);

return initBrowserSdk(opts);
return initAndBind(BrowserClient, clientOptions);
}

function getDefaultIntegrations(options: BrowserOptions): Integration[] {
Expand Down
40 changes: 22 additions & 18 deletions packages/astro/test/client/sdk.test.ts
Original file line number Diff line number Diff line change
@@ -1,18 +1,27 @@
import type { BrowserClient } from '@sentry/browser';
import {
BrowserClient,
browserTracingIntegration,
getActiveSpan,
getClient,
getCurrentScope,
getGlobalScope,
getIsolationScope,
SDK_VERSION,
} from '@sentry/browser';
import * as SentryBrowser from '@sentry/browser';
import * as SentryCore from '@sentry/core';
import { afterEach, describe, expect, it, vi } from 'vitest';
import { init } from '../../src/client/sdk';

const browserInit = vi.spyOn(SentryBrowser, 'init');
const initAndBind = vi.spyOn(SentryCore, 'initAndBind');

// Mock this to avoid the "duplicate integration" error message
vi.spyOn(SentryBrowser, 'browserTracingIntegration').mockImplementation(() => {
return {
name: 'BrowserTracing',
setupOnce: vi.fn(),
afterAllSetup: vi.fn(),
};
});

describe('Sentry client SDK', () => {
describe('init', () => {
Expand All @@ -26,12 +35,13 @@ describe('Sentry client SDK', () => {
});

it('adds Astro metadata to the SDK options', () => {
expect(browserInit).not.toHaveBeenCalled();
expect(initAndBind).not.toHaveBeenCalled();

init({});

expect(browserInit).toHaveBeenCalledTimes(1);
expect(browserInit).toHaveBeenCalledWith(
expect(initAndBind).toHaveBeenCalledTimes(1);
expect(initAndBind).toHaveBeenCalledWith(
BrowserClient,
expect.objectContaining({
_metadata: {
sdk: {
Expand All @@ -53,45 +63,39 @@ describe('Sentry client SDK', () => {
['tracesSampler', { tracesSampler: () => 1.0 }],
['no tracing option set', {}],
])('adds browserTracingIntegration if tracing is enabled via %s', (_, tracingOptions) => {
init({
const client = init({
dsn: 'https://public@dsn.ingest.sentry.io/1337',
...tracingOptions,
});

const integrationsToInit = browserInit.mock.calls[0]![0]?.defaultIntegrations;
const browserTracing = getClient<BrowserClient>()?.getIntegrationByName('BrowserTracing');

expect(integrationsToInit).toContainEqual(expect.objectContaining({ name: 'BrowserTracing' }));
const browserTracing = client?.getIntegrationByName('BrowserTracing');
expect(browserTracing).toBeDefined();
});

it("doesn't add browserTracingIntegration if `__SENTRY_TRACING__` is set to false", () => {
(globalThis as any).__SENTRY_TRACING__ = false;

init({
const client = init({
dsn: 'https://public@dsn.ingest.sentry.io/1337',
tracesSampleRate: 1,
});

const integrationsToInit = browserInit.mock.calls[0]![0]?.defaultIntegrations || [];
const browserTracing = getClient<BrowserClient>()?.getIntegrationByName('BrowserTracing');

expect(integrationsToInit).not.toContainEqual(expect.objectContaining({ name: 'BrowserTracing' }));
const browserTracing = client?.getIntegrationByName('BrowserTracing');
expect(browserTracing).toBeUndefined();

delete (globalThis as any).__SENTRY_TRACING__;
});

it('Overrides the automatically default browserTracingIntegration instance with a a user-provided browserTracingIntegration instance', () => {
init({
const client = init({
dsn: 'https://public@dsn.ingest.sentry.io/1337',
integrations: [
browserTracingIntegration({ finalTimeout: 10, instrumentNavigation: false, instrumentPageLoad: false }),
],
tracesSampleRate: 1,
});

const browserTracing = getClient<BrowserClient>()?.getIntegrationByName('BrowserTracing');
const browserTracing = client?.getIntegrationByName('BrowserTracing');
expect(browserTracing).toBeDefined();

// no active span means the settings were respected
Expand Down
3 changes: 1 addition & 2 deletions packages/aws-serverless/src/sdk.ts
Original file line number Diff line number Diff line change
Expand Up @@ -77,13 +77,12 @@ export function getDefaultIntegrations(_options: Options): Integration[] {
*/
export function init(options: NodeOptions = {}): NodeClient | undefined {
const opts = {
defaultIntegrations: getDefaultIntegrations(options),
...options,
};

applySdkMetadata(opts, 'aws-serverless');

return initWithoutDefaultIntegrations(opts);
return initWithoutDefaultIntegrations(opts, getDefaultIntegrations);
}

/** */
Expand Down
112 changes: 96 additions & 16 deletions packages/browser/src/client.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,20 +15,34 @@ import {
addAutoIpAddressToUser,
applySdkMetadata,
Client,
consoleSandbox,
getLocationHref,
getSDKSource,
} from '@sentry/core';
import { DEBUG_BUILD } from './debug-build';
import { eventFromException, eventFromMessage } from './eventbuilder';
import { WINDOW } from './helpers';
import type { BrowserTransportOptions } from './transports/types';

const DEFAULT_FLUSH_INTERVAL = 5000;
type ExtensionRuntime = {
runtime?: {
id?: string;
};
};
type ExtensionProperties = {
chrome?: ExtensionRuntime;
browser?: ExtensionRuntime;
nw?: unknown;
};

/**
* Configuration options for the Sentry Browser SDK.
* @see @sentry/core Options for more information.
* A magic string that build tooling can leverage in order to inject a release value into the SDK.
*/
export type BrowserOptions = Options<BrowserTransportOptions> &
BrowserClientReplayOptions &
declare const __SENTRY_RELEASE__: string | undefined;

const DEFAULT_FLUSH_INTERVAL = 5000;

type BrowserSpecificOptions = BrowserClientReplayOptions &
BrowserClientProfilingOptions & {
/**
* Important: Only set this option if you know what you are doing!
Expand All @@ -48,18 +62,21 @@ export type BrowserOptions = Options<BrowserTransportOptions> &
* @default false
*/
skipBrowserExtensionCheck?: boolean;

/** If configured, this URL will be used as base URL for lazy loading integration. */
cdnBaseUrl?: string;
};
/**
* Configuration options for the Sentry Browser SDK.
* @see @sentry/core Options for more information.
*/
export type BrowserOptions = Options<BrowserTransportOptions> & BrowserSpecificOptions;

/**
* Configuration options for the Sentry Browser SDK Client class
* @see BrowserClient for more information.
*/
export type BrowserClientOptions = ClientOptions<BrowserTransportOptions> &
BrowserClientReplayOptions &
BrowserClientProfilingOptions & {
/** If configured, this URL will be used as base URL for lazy loading integration. */
cdnBaseUrl?: string;
};
export type BrowserClientOptions = ClientOptions<BrowserTransportOptions> & BrowserSpecificOptions;

/**
* The Sentry Browser SDK Client.
Expand All @@ -75,14 +92,14 @@ export class BrowserClient extends Client<BrowserClientOptions> {
* @param options Configuration options for this SDK.
*/
public constructor(options: BrowserClientOptions) {
const opts = {
// We default this to true, as it is the safer scenario
parentSpanIsAlwaysRootSpan: true,
...options,
};
const opts = applyDefaultOptions(options);
const sdkSource = WINDOW.SENTRY_SDK_SOURCE || getSDKSource();
applySdkMetadata(opts, 'browser', ['browser'], sdkSource);

if (!opts.skipBrowserExtensionCheck && checkIfEmbeddedBrowserExtension()) {
opts.enabled = false;
}

super(opts);

// eslint-disable-next-line @typescript-eslint/no-this-alias
Expand Down Expand Up @@ -155,3 +172,66 @@ export class BrowserClient extends Client<BrowserClientOptions> {
return super._prepareEvent(event, hint, currentScope, isolationScope);
}
}

/** Exported only for tests. */
export function applyDefaultOptions<T extends Partial<BrowserClientOptions>>(optionsArg: T): T {
return {
release:
typeof __SENTRY_RELEASE__ === 'string' // This allows build tooling to find-and-replace __SENTRY_RELEASE__ to inject a release value
? __SENTRY_RELEASE__
: WINDOW.SENTRY_RELEASE?.id, // This supports the variable that sentry-webpack-plugin injects
sendClientReports: true,
// We default this to true, as it is the safer scenario
parentSpanIsAlwaysRootSpan: true,
...optionsArg,
};
}

/**
* Returns true if the SDK is running in an embedded browser extension.
* Stand-alone browser extensions (which do not share the same data as the main browser page) are fine.
*/
function checkIfEmbeddedBrowserExtension(): true | void {
if (_isEmbeddedBrowserExtension()) {
if (DEBUG_BUILD) {
consoleSandbox(() => {
// eslint-disable-next-line no-console
console.error(
'[Sentry] You cannot use Sentry.init() in a browser extension, see: https://docs.sentry.io/platforms/javascript/best-practices/browser-extensions/',
);
});
}

return true;
}
}

function _isEmbeddedBrowserExtension(): boolean {
if (typeof WINDOW.window === 'undefined') {
// No need to show the error if we're not in a browser window environment (e.g. service workers)
return false;
}

const _window = WINDOW as typeof WINDOW & ExtensionProperties;

// Running the SDK in NW.js, which appears like a browser extension but isn't, is also fine
// see: https://github.com/getsentry/sentry-javascript/issues/12668
if (_window.nw) {
return false;
}

const extensionObject = _window['chrome'] || _window['browser'];

if (!extensionObject?.runtime?.id) {
return false;
}

const href = getLocationHref();
const extensionProtocols = ['chrome-extension', 'moz-extension', 'ms-browser-extension', 'safari-web-extension'];

// Running the SDK in a dedicated extension page and calling Sentry.init is fine; no risk of data leakage
const isDedicatedExtensionPage =
WINDOW === WINDOW.top && extensionProtocols.some(protocol => href.startsWith(`${protocol}://`));

return !isDedicatedExtensionPage;
}
Loading
Loading
0