From eb2d726f9c8dfc14f6d6941fedf76bc7e8449d3e Mon Sep 17 00:00:00 2001 From: Luca Forstner Date: Thu, 23 Nov 2023 16:58:50 +0100 Subject: [PATCH] feat(nextjs): Add request data to all edge-capable functionalities (#9636) --- .../app/edge-server-components/page.tsx | 7 ++++ .../nextjs-app-dir/sentry.client.config.ts | 1 + .../nextjs-app-dir/sentry.edge.config.ts | 1 + .../nextjs-app-dir/sentry.server.config.ts | 1 + .../nextjs-app-dir/tests/edge-route.test.ts | 7 +++- .../nextjs-app-dir/tests/edge.test.ts | 15 +++++++- .../tests/route-handlers.test.ts | 3 +- .../nextjs-app-dir/tests/transactions.test.ts | 2 ++ packages/nextjs/src/common/types.ts | 23 +++++++++++- .../src/common/utils/edgeWrapperUtils.ts | 9 ++++- .../src/common/wrapRouteHandlerWithSentry.ts | 13 +++++-- .../common/wrapServerComponentWithSentry.ts | 15 ++++++-- .../templates/requestAsyncStorageShim.ts | 6 ++-- .../serverComponentWrapperTemplate.ts | 4 +++ .../nextjs/test/edge/edgeWrapperUtils.test.ts | 6 +++- .../nextjs/test/edge/withSentryAPI.test.ts | 2 +- packages/types/src/index.ts | 1 + packages/types/src/instrument.ts | 11 +++--- packages/types/src/webfetchapi.ts | 17 +++++++++ packages/utils/src/requestdata.ts | 36 +++++++++++++++++++ packages/vercel-edge/src/index.ts | 5 +-- packages/vercel-edge/src/sdk.ts | 13 +++++-- 22 files changed, 172 insertions(+), 26 deletions(-) create mode 100644 packages/e2e-tests/test-applications/nextjs-app-dir/app/edge-server-components/page.tsx create mode 100644 packages/types/src/webfetchapi.ts diff --git a/packages/e2e-tests/test-applications/nextjs-app-dir/app/edge-server-components/page.tsx b/packages/e2e-tests/test-applications/nextjs-app-dir/app/edge-server-components/page.tsx new file mode 100644 index 000000000000..c7a6a8887e90 --- /dev/null +++ b/packages/e2e-tests/test-applications/nextjs-app-dir/app/edge-server-components/page.tsx @@ -0,0 +1,7 @@ +export const dynamic = 'force-dynamic'; + +export const runtime = 'edge'; + +export default async function Page() { + return

Hello world!

; +} diff --git a/packages/e2e-tests/test-applications/nextjs-app-dir/sentry.client.config.ts b/packages/e2e-tests/test-applications/nextjs-app-dir/sentry.client.config.ts index ad780407a5b7..85bd765c9c44 100644 --- a/packages/e2e-tests/test-applications/nextjs-app-dir/sentry.client.config.ts +++ b/packages/e2e-tests/test-applications/nextjs-app-dir/sentry.client.config.ts @@ -5,4 +5,5 @@ Sentry.init({ dsn: process.env.NEXT_PUBLIC_E2E_TEST_DSN, tunnel: `http://localhost:3031/`, // proxy server tracesSampleRate: 1.0, + sendDefaultPii: true, }); diff --git a/packages/e2e-tests/test-applications/nextjs-app-dir/sentry.edge.config.ts b/packages/e2e-tests/test-applications/nextjs-app-dir/sentry.edge.config.ts index ad780407a5b7..85bd765c9c44 100644 --- a/packages/e2e-tests/test-applications/nextjs-app-dir/sentry.edge.config.ts +++ b/packages/e2e-tests/test-applications/nextjs-app-dir/sentry.edge.config.ts @@ -5,4 +5,5 @@ Sentry.init({ dsn: process.env.NEXT_PUBLIC_E2E_TEST_DSN, tunnel: `http://localhost:3031/`, // proxy server tracesSampleRate: 1.0, + sendDefaultPii: true, }); diff --git a/packages/e2e-tests/test-applications/nextjs-app-dir/sentry.server.config.ts b/packages/e2e-tests/test-applications/nextjs-app-dir/sentry.server.config.ts index ad780407a5b7..85bd765c9c44 100644 --- a/packages/e2e-tests/test-applications/nextjs-app-dir/sentry.server.config.ts +++ b/packages/e2e-tests/test-applications/nextjs-app-dir/sentry.server.config.ts @@ -5,4 +5,5 @@ Sentry.init({ dsn: process.env.NEXT_PUBLIC_E2E_TEST_DSN, tunnel: `http://localhost:3031/`, // proxy server tracesSampleRate: 1.0, + sendDefaultPii: true, }); diff --git a/packages/e2e-tests/test-applications/nextjs-app-dir/tests/edge-route.test.ts b/packages/e2e-tests/test-applications/nextjs-app-dir/tests/edge-route.test.ts index 0f5ee6100a19..0f02274f9e4c 100644 --- a/packages/e2e-tests/test-applications/nextjs-app-dir/tests/edge-route.test.ts +++ b/packages/e2e-tests/test-applications/nextjs-app-dir/tests/edge-route.test.ts @@ -8,7 +8,11 @@ test('Should create a transaction for edge routes', async ({ request }) => { ); }); - const response = await request.get('/api/edge-endpoint'); + const response = await request.get('/api/edge-endpoint', { + headers: { + 'x-yeet': 'test-value', + }, + }); expect(await response.json()).toStrictEqual({ name: 'Jim Halpert' }); const edgerouteTransaction = await edgerouteTransactionPromise; @@ -16,6 +20,7 @@ test('Should create a transaction for edge routes', async ({ request }) => { expect(edgerouteTransaction.contexts?.trace?.status).toBe('ok'); expect(edgerouteTransaction.contexts?.trace?.op).toBe('http.server'); expect(edgerouteTransaction.contexts?.runtime?.name).toBe('vercel-edge'); + expect(edgerouteTransaction.request?.headers?.['x-yeet']).toBe('test-value'); }); test('Should create a transaction with error status for faulty edge routes', async ({ request }) => { diff --git a/packages/e2e-tests/test-applications/nextjs-app-dir/tests/edge.test.ts b/packages/e2e-tests/test-applications/nextjs-app-dir/tests/edge.test.ts index 26a9e7fed310..39cdd753e85c 100644 --- a/packages/e2e-tests/test-applications/nextjs-app-dir/tests/edge.test.ts +++ b/packages/e2e-tests/test-applications/nextjs-app-dir/tests/edge.test.ts @@ -1,5 +1,5 @@ import { test, expect } from '@playwright/test'; -import { waitForError } from '../event-proxy-server'; +import { waitForError, waitForTransaction } from '../event-proxy-server'; test('Should record exceptions for faulty edge server components', async ({ page }) => { const errorEventPromise = waitForError('nextjs-13-app-dir', errorEvent => { @@ -10,3 +10,16 @@ test('Should record exceptions for faulty edge server components', async ({ page expect(await errorEventPromise).toBeDefined(); }); + +test('Should record transaction for edge server components', async ({ page }) => { + const serverComponentTransactionPromise = waitForTransaction('nextjs-13-app-dir', async transactionEvent => { + return transactionEvent?.transaction === 'Page Server Component (/edge-server-components)'; + }); + + await page.goto('/edge-server-components'); + + const serverComponentTransaction = await serverComponentTransactionPromise; + + expect(serverComponentTransaction).toBeDefined(); + expect(serverComponentTransaction.request?.headers).toBeDefined(); +}); diff --git a/packages/e2e-tests/test-applications/nextjs-app-dir/tests/route-handlers.test.ts b/packages/e2e-tests/test-applications/nextjs-app-dir/tests/route-handlers.test.ts index cbd4414436f6..775cbe462e89 100644 --- a/packages/e2e-tests/test-applications/nextjs-app-dir/tests/route-handlers.test.ts +++ b/packages/e2e-tests/test-applications/nextjs-app-dir/tests/route-handlers.test.ts @@ -6,13 +6,14 @@ test('Should create a transaction for route handlers', async ({ request }) => { return transactionEvent?.transaction === 'GET /route-handlers/[param]'; }); - const response = await request.get('/route-handlers/foo'); + const response = await request.get('/route-handlers/foo', { headers: { 'x-yeet': 'test-value' } }); expect(await response.json()).toStrictEqual({ name: 'John Doe' }); const routehandlerTransaction = await routehandlerTransactionPromise; expect(routehandlerTransaction.contexts?.trace?.status).toBe('ok'); expect(routehandlerTransaction.contexts?.trace?.op).toBe('http.server'); + expect(routehandlerTransaction.request?.headers?.['x-yeet']).toBe('test-value'); }); test('Should create a transaction for route handlers and correctly set span status depending on http status', async ({ diff --git a/packages/e2e-tests/test-applications/nextjs-app-dir/tests/transactions.test.ts b/packages/e2e-tests/test-applications/nextjs-app-dir/tests/transactions.test.ts index 597b9ad66072..f47b5e60c85d 100644 --- a/packages/e2e-tests/test-applications/nextjs-app-dir/tests/transactions.test.ts +++ b/packages/e2e-tests/test-applications/nextjs-app-dir/tests/transactions.test.ts @@ -63,6 +63,8 @@ if (process.env.TEST_ENV === 'production') { const transactionEvent = await serverComponentTransactionPromise; const transactionEventId = transactionEvent.event_id; + expect(transactionEvent.request?.headers).toBeDefined(); + await expect .poll( async () => { diff --git a/packages/nextjs/src/common/types.ts b/packages/nextjs/src/common/types.ts index 7c1ce7425108..9e3218959b34 100644 --- a/packages/nextjs/src/common/types.ts +++ b/packages/nextjs/src/common/types.ts @@ -1,17 +1,38 @@ -import type { Transaction, WrappedFunction } from '@sentry/types'; +import type { Transaction, WebFetchHeaders, WrappedFunction } from '@sentry/types'; import type { NextApiRequest, NextApiResponse } from 'next'; export type ServerComponentContext = { componentRoute: string; componentType: string; + // TODO(v8): Remove + /** + * @deprecated pass a complete `Headers` object with the `headers` field instead. + */ sentryTraceHeader?: string; + // TODO(v8): Remove + /** + * @deprecated pass a complete `Headers` object with the `headers` field instead. + */ baggageHeader?: string; + headers?: WebFetchHeaders; }; export interface RouteHandlerContext { + // TODO(v8): Remove + /** + * @deprecated The SDK will automatically pick up the method from the incoming Request object instead. + */ method: 'GET' | 'POST' | 'PUT' | 'PATCH' | 'DELETE' | 'HEAD' | 'OPTIONS'; parameterizedRoute: string; + // TODO(v8): Remove + /** + * @deprecated The SDK will automatically pick up the `sentry-trace` header from the incoming Request object instead. + */ sentryTraceHeader?: string; + // TODO(v8): Remove + /** + * @deprecated The SDK will automatically pick up the `baggage` header from the incoming Request object instead. + */ baggageHeader?: string; } diff --git a/packages/nextjs/src/common/utils/edgeWrapperUtils.ts b/packages/nextjs/src/common/utils/edgeWrapperUtils.ts index df169a6f7e2d..bdf7b389fa00 100644 --- a/packages/nextjs/src/common/utils/edgeWrapperUtils.ts +++ b/packages/nextjs/src/common/utils/edgeWrapperUtils.ts @@ -1,6 +1,12 @@ import { addTracingExtensions, captureException, flush, getCurrentHub, startTransaction } from '@sentry/core'; import type { Span } from '@sentry/types'; -import { addExceptionMechanism, logger, objectify, tracingContextFromHeaders } from '@sentry/utils'; +import { + addExceptionMechanism, + logger, + objectify, + tracingContextFromHeaders, + winterCGRequestToRequestData, +} from '@sentry/utils'; import type { EdgeRouteHandler } from '../../edge/types'; @@ -44,6 +50,7 @@ export function withEdgeWrapping( origin: 'auto.ui.nextjs.withEdgeWrapping', ...traceparentData, metadata: { + request: winterCGRequestToRequestData(req), dynamicSamplingContext: traceparentData && !dynamicSamplingContext ? {} : dynamicSamplingContext, source: 'route', }, diff --git a/packages/nextjs/src/common/wrapRouteHandlerWithSentry.ts b/packages/nextjs/src/common/wrapRouteHandlerWithSentry.ts index d407a2578b5b..c03bb3db0dbf 100644 --- a/packages/nextjs/src/common/wrapRouteHandlerWithSentry.ts +++ b/packages/nextjs/src/common/wrapRouteHandlerWithSentry.ts @@ -1,5 +1,5 @@ import { addTracingExtensions, captureException, flush, getCurrentHub, runWithAsyncContext, trace } from '@sentry/core'; -import { tracingContextFromHeaders } from '@sentry/utils'; +import { tracingContextFromHeaders, winterCGRequestToRequestData } from '@sentry/utils'; import { isRedirectNavigationError } from './nextNavigationErrorUtils'; import type { RouteHandlerContext } from './types'; @@ -14,6 +14,7 @@ export function wrapRouteHandlerWithSentry any>( context: RouteHandlerContext, ): (...args: Parameters) => ReturnType extends Promise ? ReturnType : Promise> { addTracingExtensions(); + // eslint-disable-next-line deprecation/deprecation const { method, parameterizedRoute, baggageHeader, sentryTraceHeader } = context; return new Proxy(routeHandler, { apply: (originalFunction, thisArg, args) => { @@ -21,6 +22,13 @@ export function wrapRouteHandlerWithSentry any>( const hub = getCurrentHub(); const currentScope = hub.getScope(); + let req: Request | undefined; + let reqMethod: string | undefined; + if (args[0] instanceof Request) { + req = args[0]; + reqMethod = req.method; + } + const { traceparentData, dynamicSamplingContext, propagationContext } = tracingContextFromHeaders( sentryTraceHeader, baggageHeader, @@ -32,10 +40,11 @@ export function wrapRouteHandlerWithSentry any>( res = await trace( { op: 'http.server', - name: `${method} ${parameterizedRoute}`, + name: `${reqMethod ?? method} ${parameterizedRoute}`, status: 'ok', ...traceparentData, metadata: { + request: req ? winterCGRequestToRequestData(req) : undefined, source: 'route', dynamicSamplingContext: traceparentData && !dynamicSamplingContext ? {} : dynamicSamplingContext, }, diff --git a/packages/nextjs/src/common/wrapServerComponentWithSentry.ts b/packages/nextjs/src/common/wrapServerComponentWithSentry.ts index fc215e495b58..beb17980de5e 100644 --- a/packages/nextjs/src/common/wrapServerComponentWithSentry.ts +++ b/packages/nextjs/src/common/wrapServerComponentWithSentry.ts @@ -6,7 +6,7 @@ import { runWithAsyncContext, startTransaction, } from '@sentry/core'; -import { tracingContextFromHeaders } from '@sentry/utils'; +import { tracingContextFromHeaders, winterCGHeadersToDict } from '@sentry/utils'; import { isNotFoundNavigationError, isRedirectNavigationError } from '../common/nextNavigationErrorUtils'; import type { ServerComponentContext } from '../common/types'; @@ -33,9 +33,15 @@ export function wrapServerComponentWithSentry any> let maybePromiseResult; + const completeHeadersDict: Record = context.headers + ? winterCGHeadersToDict(context.headers) + : {}; + const { traceparentData, dynamicSamplingContext, propagationContext } = tracingContextFromHeaders( - context.sentryTraceHeader, - context.baggageHeader, + // eslint-disable-next-line deprecation/deprecation + context.sentryTraceHeader ?? completeHeadersDict['sentry-trace'], + // eslint-disable-next-line deprecation/deprecation + context.baggageHeader ?? completeHeadersDict['baggage'], ); currentScope.setPropagationContext(propagationContext); @@ -46,6 +52,9 @@ export function wrapServerComponentWithSentry any> origin: 'auto.function.nextjs', ...traceparentData, metadata: { + request: { + headers: completeHeadersDict, + }, source: 'component', dynamicSamplingContext: traceparentData && !dynamicSamplingContext ? {} : dynamicSamplingContext, }, diff --git a/packages/nextjs/src/config/templates/requestAsyncStorageShim.ts b/packages/nextjs/src/config/templates/requestAsyncStorageShim.ts index 44222403d026..4acb61e78444 100644 --- a/packages/nextjs/src/config/templates/requestAsyncStorageShim.ts +++ b/packages/nextjs/src/config/templates/requestAsyncStorageShim.ts @@ -1,9 +1,9 @@ +import type { WebFetchHeaders } from '@sentry/types'; + export interface RequestAsyncStorage { getStore: () => | { - headers: { - get: Headers['get']; - }; + headers: WebFetchHeaders; } | undefined; } diff --git a/packages/nextjs/src/config/templates/serverComponentWrapperTemplate.ts b/packages/nextjs/src/config/templates/serverComponentWrapperTemplate.ts index 6730202ecaa5..d4251c5a521a 100644 --- a/packages/nextjs/src/config/templates/serverComponentWrapperTemplate.ts +++ b/packages/nextjs/src/config/templates/serverComponentWrapperTemplate.ts @@ -7,6 +7,7 @@ import { requestAsyncStorage } from '__SENTRY_NEXTJS_REQUEST_ASYNC_STORAGE_SHIM_ import * as serverComponentModule from '__SENTRY_WRAPPING_TARGET_FILE__'; // eslint-disable-next-line import/no-extraneous-dependencies import * as Sentry from '@sentry/nextjs'; +import type { WebFetchHeaders } from '@sentry/types'; import type { RequestAsyncStorage } from './requestAsyncStorageShim'; @@ -27,12 +28,14 @@ if (typeof serverComponent === 'function') { apply: (originalFunction, thisArg, args) => { let sentryTraceHeader: string | undefined | null = undefined; let baggageHeader: string | undefined | null = undefined; + let headers: WebFetchHeaders | undefined = undefined; // We try-catch here just in `requestAsyncStorage` is undefined since it may not be defined try { const requestAsyncStore = requestAsyncStorage.getStore(); sentryTraceHeader = requestAsyncStore?.headers.get('sentry-trace'); baggageHeader = requestAsyncStore?.headers.get('baggage'); + headers = requestAsyncStore?.headers; } catch (e) { /** empty */ } @@ -42,6 +45,7 @@ if (typeof serverComponent === 'function') { componentType: '__COMPONENT_TYPE__', sentryTraceHeader, baggageHeader, + headers, }).apply(thisArg, args); }, }); diff --git a/packages/nextjs/test/edge/edgeWrapperUtils.test.ts b/packages/nextjs/test/edge/edgeWrapperUtils.test.ts index 872277339c68..7ef9b6f5a85f 100644 --- a/packages/nextjs/test/edge/edgeWrapperUtils.test.ts +++ b/packages/nextjs/test/edge/edgeWrapperUtils.test.ts @@ -87,7 +87,11 @@ describe('withEdgeWrapping', () => { await wrappedFunction(request); expect(startTransactionSpy).toHaveBeenCalledTimes(1); expect(startTransactionSpy).toHaveBeenCalledWith( - expect.objectContaining({ metadata: { source: 'route' }, name: 'some label', op: 'some op' }), + expect.objectContaining({ + metadata: expect.objectContaining({ source: 'route' }), + name: 'some label', + op: 'some op', + }), ); }); diff --git a/packages/nextjs/test/edge/withSentryAPI.test.ts b/packages/nextjs/test/edge/withSentryAPI.test.ts index 0fb111df57a6..d2dbc9385c44 100644 --- a/packages/nextjs/test/edge/withSentryAPI.test.ts +++ b/packages/nextjs/test/edge/withSentryAPI.test.ts @@ -52,7 +52,7 @@ describe('wrapApiHandlerWithSentry', () => { expect(startTransactionSpy).toHaveBeenCalledTimes(1); expect(startTransactionSpy).toHaveBeenCalledWith( expect.objectContaining({ - metadata: { source: 'route' }, + metadata: expect.objectContaining({ source: 'route' }), name: 'POST /user/[userId]/post/[postId]', op: 'http.server', }), diff --git a/packages/types/src/index.ts b/packages/types/src/index.ts index 4c648f35363e..cf7d801dee8b 100644 --- a/packages/types/src/index.ts +++ b/packages/types/src/index.ts @@ -122,6 +122,7 @@ export type { TransportRequestExecutor, } from './transport'; export type { User, UserFeedback } from './user'; +export type { WebFetchHeaders, WebFetchRequest } from './webfetchapi'; export type { WrappedFunction } from './wrappedfunction'; export type { Instrumenter } from './instrumenter'; export type { diff --git a/packages/types/src/instrument.ts b/packages/types/src/instrument.ts index e51f9a3eec44..16ec5a3b75b7 100644 --- a/packages/types/src/instrument.ts +++ b/packages/types/src/instrument.ts @@ -1,5 +1,8 @@ // This should be: null | Blob | BufferSource | FormData | URLSearchParams | string // But since not all of those are available in node, we just export `unknown` here for now + +import type { WebFetchHeaders } from './webfetchapi'; + // Make sure to cast it where needed! type XHRSendInput = unknown; @@ -54,13 +57,7 @@ export interface HandlerDataFetch { readonly ok: boolean; readonly status: number; readonly url: string; - headers: { - append(name: string, value: string): void; - delete(name: string): void; - get(name: string): string | null; - has(name: string): boolean; - set(name: string, value: string): void; - }; + headers: WebFetchHeaders; }; error?: unknown; } diff --git a/packages/types/src/webfetchapi.ts b/packages/types/src/webfetchapi.ts new file mode 100644 index 000000000000..78b7d464ea71 --- /dev/null +++ b/packages/types/src/webfetchapi.ts @@ -0,0 +1,17 @@ +// These are vendored types for the standard web fetch API types because typescript needs the DOM types to be able to understand the `Request`, `Headers`, ... types and not everybody has those. + +export interface WebFetchHeaders { + append(name: string, value: string): void; + delete(name: string): void; + get(name: string): string | null; + has(name: string): boolean; + set(name: string, value: string): void; + forEach(callbackfn: (value: string, key: string, parent: WebFetchHeaders) => void): void; +} + +export interface WebFetchRequest { + readonly headers: WebFetchHeaders; + readonly method: string; + readonly url: string; + clone(): WebFetchRequest; +} diff --git a/packages/utils/src/requestdata.ts b/packages/utils/src/requestdata.ts index f5c39292bd54..a138ecf3141b 100644 --- a/packages/utils/src/requestdata.ts +++ b/packages/utils/src/requestdata.ts @@ -4,10 +4,13 @@ import type { PolymorphicRequest, Transaction, TransactionSource, + WebFetchHeaders, + WebFetchRequest, } from '@sentry/types'; import { parseCookie } from './cookie'; import { isPlainObject, isString } from './is'; +import { logger } from './logger'; import { normalize } from './normalize'; import { stripUrlQueryAndFragment } from './url'; @@ -361,3 +364,36 @@ function extractQueryParams( undefined ); } + +/** + * Transforms a `Headers` object that implements the `Web Fetch API` (https://developer.mozilla.org/en-US/docs/Web/API/Headers) into a simple key-value dict. + * The header keys will be lower case: e.g. A "Content-Type" header will be stored as "content-type". + */ +export function winterCGHeadersToDict(winterCGHeaders: WebFetchHeaders): Record { + const headers: Record = {}; + try { + winterCGHeaders.forEach((value, key) => { + if (typeof value === 'string') { + // We check that value is a string even though it might be redundant to make sure prototype pollution is not possible. + headers[key] = value; + } + }); + } catch (e) { + __DEBUG_BUILD__ && + logger.warn('Sentry failed extracting headers from a request object. If you see this, please file an issue.'); + } + + return headers; +} + +/** + * Converts a `Request` object that implements the `Web Fetch API` (https://developer.mozilla.org/en-US/docs/Web/API/Headers) into the format that the `RequestData` integration understands. + */ +export function winterCGRequestToRequestData(req: WebFetchRequest): PolymorphicRequest { + const headers = winterCGHeadersToDict(req.headers); + return { + method: req.method, + url: req.url, + headers, + }; +} diff --git a/packages/vercel-edge/src/index.ts b/packages/vercel-edge/src/index.ts index fcb09ebd3094..bd1ce1ba6526 100644 --- a/packages/vercel-edge/src/index.ts +++ b/packages/vercel-edge/src/index.ts @@ -69,13 +69,14 @@ export type { SpanStatusType } from '@sentry/core'; export { VercelEdgeClient } from './client'; export { defaultIntegrations, init } from './sdk'; -import { Integrations as CoreIntegrations } from '@sentry/core'; +import { Integrations as CoreIntegrations, RequestData } from '@sentry/core'; import { WinterCGFetch } from './integrations/wintercg-fetch'; const INTEGRATIONS = { ...CoreIntegrations, - ...WinterCGFetch, + WinterCGFetch, + RequestData, }; export { INTEGRATIONS as Integrations }; diff --git a/packages/vercel-edge/src/sdk.ts b/packages/vercel-edge/src/sdk.ts index f49115452a5d..5f1640bdf3c8 100644 --- a/packages/vercel-edge/src/sdk.ts +++ b/packages/vercel-edge/src/sdk.ts @@ -1,4 +1,5 @@ -import { getIntegrationsToSetup, initAndBind, Integrations as CoreIntegrations } from '@sentry/core'; +import { getIntegrationsToSetup, initAndBind, Integrations as CoreIntegrations, RequestData } from '@sentry/core'; +import type { Integration } from '@sentry/types'; import { createStackParser, GLOBAL_OBJ, nodeStackLineParser, stackParserFromStackParserOptions } from '@sentry/utils'; import { setAsyncLocalStorageAsyncContextStrategy } from './async'; @@ -25,8 +26,16 @@ export const defaultIntegrations = [ export function init(options: VercelEdgeOptions = {}): void { setAsyncLocalStorageAsyncContextStrategy(); + const sdkDefaultIntegrations: Integration[] = [...defaultIntegrations]; + + // TODO(v8): Add the request data integration by default. + // We don't want to add this functionality OOTB without a breaking change because it might contain PII + if (options.sendDefaultPii) { + sdkDefaultIntegrations.push(new RequestData()); + } + if (options.defaultIntegrations === undefined) { - options.defaultIntegrations = defaultIntegrations; + options.defaultIntegrations = sdkDefaultIntegrations; } if (options.dsn === undefined && process.env.SENTRY_DSN) {