diff --git a/MIGRATION.md b/MIGRATION.md index 95aa940265f3..302aa1e52ec1 100644 --- a/MIGRATION.md +++ b/MIGRATION.md @@ -8,6 +8,15 @@ npx @sentry/migr8@latest This will let you select which updates to run, and automatically update your code. Make sure to still review all code changes! +## Changed integration interface + +In v8, integrations passed to a client will have an optional `setupOnce()` hook. +Currently, this hook is always present, but in v8 you will not be able to rely on this always existing anymore - +any integration _may_ have a `setup` and/or a `setupOnce` hook. Additionally, `setupOnce()` will not receive any arguments anymore. + +This should not affect most people, but in the case that you are manually calling `integration.setupOnce()` right now, +make sure to guard it's existence properly. + ## Deprecate `Hub` The `Hub` has been a very important part of the Sentry SDK API up until now. diff --git a/packages/browser/src/integrations/breadcrumbs.ts b/packages/browser/src/integrations/breadcrumbs.ts index 831059e97756..9799e6e73b4f 100644 --- a/packages/browser/src/integrations/breadcrumbs.ts +++ b/packages/browser/src/integrations/breadcrumbs.ts @@ -68,6 +68,8 @@ const breadcrumbsIntegration: IntegrationFn = (options: Partial { return { name: INTEGRATION_NAME, + // TODO v8: Remove this + setupOnce() {}, // eslint-disable-line @typescript-eslint/no-empty-function processEvent(currentEvent) { // We want to ignore any non-error type events, e.g. transactions or replays // These should never be deduped, and also not be compared against as _previousEvent. diff --git a/packages/browser/src/integrations/httpcontext.ts b/packages/browser/src/integrations/httpcontext.ts index 2347c7cb1971..d46f29257306 100644 --- a/packages/browser/src/integrations/httpcontext.ts +++ b/packages/browser/src/integrations/httpcontext.ts @@ -8,6 +8,8 @@ const INTEGRATION_NAME = 'HttpContext'; const httpContextIntegration: IntegrationFn = () => { return { name: INTEGRATION_NAME, + // TODO v8: Remove this + setupOnce() {}, // eslint-disable-line @typescript-eslint/no-empty-function preprocessEvent(event) { // if none of the information we want exists, don't bother if (!WINDOW.navigator && !WINDOW.location && !WINDOW.document) { diff --git a/packages/browser/src/integrations/linkederrors.ts b/packages/browser/src/integrations/linkederrors.ts index e74e6252a4ce..bb351bcdc3c6 100644 --- a/packages/browser/src/integrations/linkederrors.ts +++ b/packages/browser/src/integrations/linkederrors.ts @@ -19,6 +19,8 @@ const linkedErrorsIntegration: IntegrationFn = (options: LinkedErrorsOptions = { return { name: INTEGRATION_NAME, + // TODO v8: Remove this + setupOnce() {}, // eslint-disable-line @typescript-eslint/no-empty-function preprocessEvent(event, hint, client) { const options = client.getOptions(); diff --git a/packages/browser/src/profiling/integration.ts b/packages/browser/src/profiling/integration.ts index b50c60552a7f..c6e478e8554d 100644 --- a/packages/browser/src/profiling/integration.ts +++ b/packages/browser/src/profiling/integration.ts @@ -21,6 +21,8 @@ const INTEGRATION_NAME = 'BrowserProfiling'; const browserProfilingIntegration: IntegrationFn = () => { return { name: INTEGRATION_NAME, + // TODO v8: Remove this + setupOnce() {}, // eslint-disable-line @typescript-eslint/no-empty-function setup(client) { const scope = getCurrentScope(); diff --git a/packages/core/src/integration.ts b/packages/core/src/integration.ts index 16b0c145b0b4..4b4a3177c428 100644 --- a/packages/core/src/integration.ts +++ b/packages/core/src/integration.ts @@ -1,4 +1,4 @@ -import type { Client, Event, EventHint, EventProcessor, Hub, Integration, IntegrationFn, Options } from '@sentry/types'; +import type { Client, Event, EventHint, Integration, IntegrationFn, Options } from '@sentry/types'; import { arrayify, logger } from '@sentry/utils'; import { DEBUG_BUILD } from './debug-build'; @@ -171,26 +171,16 @@ export function convertIntegrationFnToClass( fn: Fn, ): Integration & { id: string; - new (...args: Parameters): Integration & - ReturnType & { - setupOnce: (addGlobalEventProcessor?: (callback: EventProcessor) => void, getCurrentHub?: () => Hub) => void; - }; + new (...args: Parameters): Integration & ReturnType; } { return Object.assign( // eslint-disable-next-line @typescript-eslint/no-explicit-any function ConvertedIntegration(...rest: Parameters) { - return { - // eslint-disable-next-line @typescript-eslint/no-empty-function - setupOnce: () => {}, - ...fn(...rest), - }; + return fn(...rest); }, { id: name }, ) as unknown as Integration & { id: string; - new (...args: Parameters): Integration & - ReturnType & { - setupOnce: (addGlobalEventProcessor?: (callback: EventProcessor) => void, getCurrentHub?: () => Hub) => void; - }; + new (...args: Parameters): Integration & ReturnType; }; } diff --git a/packages/core/src/integrations/inboundfilters.ts b/packages/core/src/integrations/inboundfilters.ts index 57c0387b25e4..6d2ec6dfc799 100644 --- a/packages/core/src/integrations/inboundfilters.ts +++ b/packages/core/src/integrations/inboundfilters.ts @@ -33,6 +33,8 @@ const INTEGRATION_NAME = 'InboundFilters'; const inboundFiltersIntegration: IntegrationFn = (options: Partial) => { return { name: INTEGRATION_NAME, + // TODO v8: Remove this + setupOnce() {}, // eslint-disable-line @typescript-eslint/no-empty-function processEvent(event, _hint, client) { const clientOptions = client.getOptions(); const mergedOptions = _mergeOptions(options, clientOptions); diff --git a/packages/core/src/integrations/linkederrors.ts b/packages/core/src/integrations/linkederrors.ts index aa9df808e2d8..25e6417023f2 100644 --- a/packages/core/src/integrations/linkederrors.ts +++ b/packages/core/src/integrations/linkederrors.ts @@ -18,6 +18,8 @@ const linkedErrorsIntegration: IntegrationFn = (options: LinkedErrorsOptions = { return { name: INTEGRATION_NAME, + // TODO v8: Remove this + setupOnce() {}, // eslint-disable-line @typescript-eslint/no-empty-function preprocessEvent(event, hint, client) { const options = client.getOptions(); diff --git a/packages/core/src/integrations/metadata.ts b/packages/core/src/integrations/metadata.ts index e89cffbc8a0a..a18fc5dffebc 100644 --- a/packages/core/src/integrations/metadata.ts +++ b/packages/core/src/integrations/metadata.ts @@ -9,6 +9,8 @@ const INTEGRATION_NAME = 'ModuleMetadata'; const moduleMetadataIntegration: IntegrationFn = () => { return { name: INTEGRATION_NAME, + // TODO v8: Remove this + setupOnce() {}, // eslint-disable-line @typescript-eslint/no-empty-function setup(client) { if (typeof client.on !== 'function') { return; diff --git a/packages/core/src/integrations/requestdata.ts b/packages/core/src/integrations/requestdata.ts index 1deab79df241..76d29b4751b7 100644 --- a/packages/core/src/integrations/requestdata.ts +++ b/packages/core/src/integrations/requestdata.ts @@ -71,7 +71,8 @@ const requestDataIntegration: IntegrationFn = (options: RequestDataIntegrationOp return { name: INTEGRATION_NAME, - + // TODO v8: Remove this + setupOnce() {}, // eslint-disable-line @typescript-eslint/no-empty-function processEvent(event, _hint, client) { // Note: In the long run, most of the logic here should probably move into the request data utility functions. For // the moment it lives here, though, until https://github.com/getsentry/sentry-javascript/issues/5718 is addressed. diff --git a/packages/core/src/metrics/integration.ts b/packages/core/src/metrics/integration.ts index 531b0aa698b2..5cadf47ce3f2 100644 --- a/packages/core/src/metrics/integration.ts +++ b/packages/core/src/metrics/integration.ts @@ -8,6 +8,8 @@ const INTEGRATION_NAME = 'MetricsAggregator'; const metricsAggregatorIntegration: IntegrationFn = () => { return { name: INTEGRATION_NAME, + // TODO v8: Remove this + setupOnce() {}, // eslint-disable-line @typescript-eslint/no-empty-function setup(client: BaseClient) { client.metricsAggregator = new BrowserMetricsAggregator(client); }, diff --git a/packages/core/test/lib/integration.test.ts b/packages/core/test/lib/integration.test.ts index 19e22773b59b..829ef4de991b 100644 --- a/packages/core/test/lib/integration.test.ts +++ b/packages/core/test/lib/integration.test.ts @@ -649,7 +649,10 @@ describe('addIntegration', () => { describe('convertIntegrationFnToClass', () => { /* eslint-disable deprecation/deprecation */ it('works with a minimal integration', () => { - const integrationFn = () => ({ name: 'testName' }); + const integrationFn = () => ({ + name: 'testName', + setupOnce: () => {}, + }); const IntegrationClass = convertIntegrationFnToClass('testName', integrationFn); @@ -663,7 +666,10 @@ describe('convertIntegrationFnToClass', () => { }); it('works with options', () => { - const integrationFn = (_options: { num: number }) => ({ name: 'testName' }); + const integrationFn = (_options: { num: number }) => ({ + name: 'testName', + setupOnce: () => {}, + }); const IntegrationClass = convertIntegrationFnToClass('testName', integrationFn); diff --git a/packages/deno/src/integrations/context.ts b/packages/deno/src/integrations/context.ts index e54f4ec21a87..426d296f6efc 100644 --- a/packages/deno/src/integrations/context.ts +++ b/packages/deno/src/integrations/context.ts @@ -55,6 +55,8 @@ async function addDenoRuntimeContext(event: Event): Promise { const denoContextIntegration: IntegrationFn = () => { return { name: INTEGRATION_NAME, + // TODO v8: Remove this + setupOnce() {}, // eslint-disable-line @typescript-eslint/no-empty-function processEvent(event) { return addDenoRuntimeContext(event); }, diff --git a/packages/deno/src/integrations/contextlines.ts b/packages/deno/src/integrations/contextlines.ts index 38fe0efd3433..f3099151908a 100644 --- a/packages/deno/src/integrations/contextlines.ts +++ b/packages/deno/src/integrations/contextlines.ts @@ -52,6 +52,8 @@ const denoContextLinesIntegration: IntegrationFn = (options: ContextLinesOptions return { name: INTEGRATION_NAME, + // TODO v8: Remove this + setupOnce() {}, // eslint-disable-line @typescript-eslint/no-empty-function processEvent(event) { return addSourceContext(event, contextLines); }, diff --git a/packages/deno/src/integrations/globalhandlers.ts b/packages/deno/src/integrations/globalhandlers.ts index 06194037b6d1..05b46b58c7a9 100644 --- a/packages/deno/src/integrations/globalhandlers.ts +++ b/packages/deno/src/integrations/globalhandlers.ts @@ -22,6 +22,8 @@ const globalHandlersIntegration: IntegrationFn = (options?: GlobalHandlersIntegr return { name: INTEGRATION_NAME, + // TODO v8: Remove this + setupOnce() {}, // eslint-disable-line @typescript-eslint/no-empty-function setup(client) { if (_options.error) { installGlobalErrorHandler(client); diff --git a/packages/deno/src/integrations/normalizepaths.ts b/packages/deno/src/integrations/normalizepaths.ts index a8143c8b078b..39a3bab5a1c5 100644 --- a/packages/deno/src/integrations/normalizepaths.ts +++ b/packages/deno/src/integrations/normalizepaths.ts @@ -69,6 +69,8 @@ const normalizePathsIntegration: IntegrationFn = () => { return { name: INTEGRATION_NAME, + // TODO v8: Remove this + setupOnce() {}, // eslint-disable-line @typescript-eslint/no-empty-function processEvent(event) { // This error.stack hopefully contains paths that traverse the app cwd const error = new Error(); diff --git a/packages/integrations/src/captureconsole.ts b/packages/integrations/src/captureconsole.ts index 4f37ecb1011a..4d9aa04ce116 100644 --- a/packages/integrations/src/captureconsole.ts +++ b/packages/integrations/src/captureconsole.ts @@ -20,6 +20,8 @@ const captureConsoleIntegration = ((options: CaptureConsoleOptions = {}) => { return { name: INTEGRATION_NAME, + // TODO v8: Remove this + setupOnce() {}, // eslint-disable-line @typescript-eslint/no-empty-function setup(client) { if (!('console' in GLOBAL_OBJ)) { return; diff --git a/packages/integrations/src/contextlines.ts b/packages/integrations/src/contextlines.ts index 656080ec3182..143fb0232bec 100644 --- a/packages/integrations/src/contextlines.ts +++ b/packages/integrations/src/contextlines.ts @@ -23,6 +23,8 @@ const contextLinesIntegration: IntegrationFn = (options: ContextLinesOptions = { return { name: INTEGRATION_NAME, + // TODO v8: Remove this + setupOnce() {}, // eslint-disable-line @typescript-eslint/no-empty-function processEvent(event) { return addSourceContext(event, contextLines); }, diff --git a/packages/integrations/src/debug.ts b/packages/integrations/src/debug.ts index 6edb9939269a..8e20ce7f9dfc 100644 --- a/packages/integrations/src/debug.ts +++ b/packages/integrations/src/debug.ts @@ -20,6 +20,8 @@ const debugIntegration = ((options: DebugOptions = {}) => { return { name: INTEGRATION_NAME, + // TODO v8: Remove this + setupOnce() {}, // eslint-disable-line @typescript-eslint/no-empty-function setup(client) { if (!client.on) { return; diff --git a/packages/integrations/src/dedupe.ts b/packages/integrations/src/dedupe.ts index 1e3ae1be7626..97a47193b19a 100644 --- a/packages/integrations/src/dedupe.ts +++ b/packages/integrations/src/dedupe.ts @@ -11,6 +11,8 @@ const dedupeIntegration = (() => { return { name: INTEGRATION_NAME, + // TODO v8: Remove this + setupOnce() {}, // eslint-disable-line @typescript-eslint/no-empty-function processEvent(currentEvent) { // We want to ignore any non-error type events, e.g. transactions or replays // These should never be deduped, and also not be compared against as _previousEvent. diff --git a/packages/integrations/src/extraerrordata.ts b/packages/integrations/src/extraerrordata.ts index 8d9d72cb81d7..6b340a1283bc 100644 --- a/packages/integrations/src/extraerrordata.ts +++ b/packages/integrations/src/extraerrordata.ts @@ -28,6 +28,8 @@ const extraErrorDataIntegration = ((options: Partial = {} return { name: INTEGRATION_NAME, + // TODO v8: Remove this + setupOnce() {}, // eslint-disable-line @typescript-eslint/no-empty-function processEvent(event, hint) { return _enhanceEventWithErrorData(event, hint, depth, captureErrorCause); }, diff --git a/packages/integrations/src/httpclient.ts b/packages/integrations/src/httpclient.ts index 74142487473a..0ef2dcca3f27 100644 --- a/packages/integrations/src/httpclient.ts +++ b/packages/integrations/src/httpclient.ts @@ -47,6 +47,8 @@ const httpClientIntegration = ((options: Partial = {}) => { return { name: INTEGRATION_NAME, + // TODO v8: Remove this + setupOnce() {}, // eslint-disable-line @typescript-eslint/no-empty-function setup(client): void { _wrapFetch(client, _options); _wrapXHR(client, _options); diff --git a/packages/integrations/src/rewriteframes.ts b/packages/integrations/src/rewriteframes.ts index d82bde5728c6..7bb5957607b2 100644 --- a/packages/integrations/src/rewriteframes.ts +++ b/packages/integrations/src/rewriteframes.ts @@ -69,6 +69,8 @@ const rewriteFramesIntegration = ((options: RewriteFramesOptions = {}) => { return { name: INTEGRATION_NAME, + // TODO v8: Remove this + setupOnce() {}, // eslint-disable-line @typescript-eslint/no-empty-function processEvent(originalEvent) { let processedEvent = originalEvent; diff --git a/packages/integrations/src/sessiontiming.ts b/packages/integrations/src/sessiontiming.ts index 4398d170a981..b3511e6c3527 100644 --- a/packages/integrations/src/sessiontiming.ts +++ b/packages/integrations/src/sessiontiming.ts @@ -8,6 +8,8 @@ const sessionTimingIntegration = (() => { return { name: INTEGRATION_NAME, + // TODO v8: Remove this + setupOnce() {}, // eslint-disable-line @typescript-eslint/no-empty-function processEvent(event) { const now = Date.now(); diff --git a/packages/integrations/src/transaction.ts b/packages/integrations/src/transaction.ts index c44c94c7fe06..a7e5aa5a1745 100644 --- a/packages/integrations/src/transaction.ts +++ b/packages/integrations/src/transaction.ts @@ -6,6 +6,8 @@ const INTEGRATION_NAME = 'Transaction'; const transactionIntegration = (() => { return { name: INTEGRATION_NAME, + // TODO v8: Remove this + setupOnce() {}, // eslint-disable-line @typescript-eslint/no-empty-function processEvent(event) { const frames = _getFramesFromEvent(event); diff --git a/packages/node/src/integrations/anr/index.ts b/packages/node/src/integrations/anr/index.ts index 549e483b51d0..1ebca2fdff57 100644 --- a/packages/node/src/integrations/anr/index.ts +++ b/packages/node/src/integrations/anr/index.ts @@ -55,6 +55,8 @@ const INTEGRATION_NAME = 'Anr'; const anrIntegration = ((options: Partial = {}) => { return { name: INTEGRATION_NAME, + // TODO v8: Remove this + setupOnce() {}, // eslint-disable-line @typescript-eslint/no-empty-function setup(client: NodeClient) { if (NODE_VERSION.major < 16 || (NODE_VERSION.major === 16 && NODE_VERSION.minor < 17)) { throw new Error('ANR detection requires Node 16.17.0 or later'); diff --git a/packages/node/src/integrations/console.ts b/packages/node/src/integrations/console.ts index d0243d7e3985..eb46b58adf74 100644 --- a/packages/node/src/integrations/console.ts +++ b/packages/node/src/integrations/console.ts @@ -8,6 +8,8 @@ const INTEGRATION_NAME = 'Console'; const consoleIntegration = (() => { return { name: INTEGRATION_NAME, + // TODO v8: Remove this + setupOnce() {}, // eslint-disable-line @typescript-eslint/no-empty-function setup(client) { addConsoleInstrumentationHandler(({ args, level }) => { if (getClient() !== client) { diff --git a/packages/node/src/integrations/context.ts b/packages/node/src/integrations/context.ts index 2b16ec6a4527..461f85297e4b 100644 --- a/packages/node/src/integrations/context.ts +++ b/packages/node/src/integrations/context.ts @@ -98,6 +98,8 @@ const contextIntegration = ((options: ContextOptions = {}) => { return { name: INTEGRATION_NAME, + // TODO v8: Remove this + setupOnce() {}, // eslint-disable-line @typescript-eslint/no-empty-function processEvent(event) { return addContext(event); }, diff --git a/packages/node/src/integrations/contextlines.ts b/packages/node/src/integrations/contextlines.ts index 0a961ddbc259..4f7ab81f063e 100644 --- a/packages/node/src/integrations/contextlines.ts +++ b/packages/node/src/integrations/contextlines.ts @@ -40,6 +40,8 @@ const contextLinesIntegration = ((options: ContextLinesOptions = {}) => { return { name: INTEGRATION_NAME, + // TODO v8: Remove this + setupOnce() {}, // eslint-disable-line @typescript-eslint/no-empty-function processEvent(event) { return addSourceContext(event, contextLines); }, diff --git a/packages/node/src/integrations/local-variables/local-variables-async.ts b/packages/node/src/integrations/local-variables/local-variables-async.ts index c3072c6c3f11..360d553e48a2 100644 --- a/packages/node/src/integrations/local-variables/local-variables-async.ts +++ b/packages/node/src/integrations/local-variables/local-variables-async.ts @@ -214,6 +214,8 @@ export const localVariablesAsync: IntegrationFn = (options: Options = {}) => { return { name: INTEGRATION_NAME, + // TODO v8: Remove this + setupOnce() {}, // eslint-disable-line @typescript-eslint/no-empty-function setup(client: NodeClient) { const clientOptions = client.getOptions(); diff --git a/packages/node/src/integrations/local-variables/local-variables-sync.ts b/packages/node/src/integrations/local-variables/local-variables-sync.ts index 32dae4599c02..45d17748ec78 100644 --- a/packages/node/src/integrations/local-variables/local-variables-sync.ts +++ b/packages/node/src/integrations/local-variables/local-variables-sync.ts @@ -326,6 +326,8 @@ export const localVariablesSync: IntegrationFn = ( return { name: INTEGRATION_NAME, + // TODO v8: Remove this + setupOnce() {}, // eslint-disable-line @typescript-eslint/no-empty-function setup(client: NodeClient) { const clientOptions = client.getOptions(); diff --git a/packages/node/src/integrations/modules.ts b/packages/node/src/integrations/modules.ts index ad6549dd3b3b..85bb792f60b2 100644 --- a/packages/node/src/integrations/modules.ts +++ b/packages/node/src/integrations/modules.ts @@ -79,6 +79,8 @@ function _getModules(): { [key: string]: string } { const modulesIntegration = (() => { return { name: INTEGRATION_NAME, + // TODO v8: Remove this + setupOnce() {}, // eslint-disable-line @typescript-eslint/no-empty-function processEvent(event) { event.modules = { ...event.modules, diff --git a/packages/node/src/integrations/onuncaughtexception.ts b/packages/node/src/integrations/onuncaughtexception.ts index e9c724f89e7f..bdee16d3e953 100644 --- a/packages/node/src/integrations/onuncaughtexception.ts +++ b/packages/node/src/integrations/onuncaughtexception.ts @@ -48,6 +48,8 @@ const onUncaughtExceptionIntegration = ((options: Partial = {} return { name: INTEGRATION_NAME, + // TODO v8: Remove this + setupOnce() {}, // eslint-disable-line @typescript-eslint/no-empty-function setup(client) { if (typeof process === 'object' && process.env && process.env.NODE_ENV !== 'development') { logger.warn("[Spotlight] It seems you're not in dev mode. Do you really want to have Spotlight enabled?"); diff --git a/packages/types/src/integration.ts b/packages/types/src/integration.ts index a4108a60c749..44c49ab375aa 100644 --- a/packages/types/src/integration.ts +++ b/packages/types/src/integration.ts @@ -13,12 +13,8 @@ export interface IntegrationClass { new (...args: any[]): T; } -/** - * An integration in function form. - * This is expected to return an integration result, - */ -export type IntegrationFn = (...rest: any[]) => IntegrationFnResult; - +/** Integration interface. + * This is more or less the same as `Integration`, but with a slimmer `setupOnce` siganture. */ export interface IntegrationFnResult { /** * The name of the integration. @@ -28,8 +24,10 @@ export interface IntegrationFnResult { /** * This hook is only called once, even if multiple clients are created. * It does not receives any arguments, and should only use for e.g. global monkey patching and similar things. + * + * NOTE: In v8, this will become optional. */ - setupOnce?(): void; + setupOnce(): void; /** * Set up an integration for the given client. @@ -54,16 +52,24 @@ export interface IntegrationFnResult { processEvent?(event: Event, hint: EventHint, client: Client): Event | null | PromiseLike; } +/** + * An integration in function form. + * This is expected to return an integration. + */ +export type IntegrationFn = (...rest: any[]) => IntegrationFnResult; + /** Integration interface */ export interface Integration { /** - * Returns {@link IntegrationClass.id} + * The name of the integration. */ name: string; /** - * Sets the integration up only once. - * This takes no options on purpose, options should be passed in the constructor + * This hook is only called once, even if multiple clients are created. + * It does not receives any arguments, and should only use for e.g. global monkey patching and similar things. + * + * NOTE: In v8, this will become optional, and not receive any arguments anymore. */ setupOnce(addGlobalEventProcessor: (callback: EventProcessor) => void, getCurrentHub: () => Hub): void;