diff --git a/packages/nextjs/src/client/index.ts b/packages/nextjs/src/client/index.ts index 665b557e4e7b..0adda732916b 100644 --- a/packages/nextjs/src/client/index.ts +++ b/packages/nextjs/src/client/index.ts @@ -130,38 +130,4 @@ export function withSentryConfig(exportedUserNextConfig: T): T { return exportedUserNextConfig; } -export { - // eslint-disable-next-line deprecation/deprecation - withSentryServerSideGetInitialProps, - wrapGetInitialPropsWithSentry, -} from './wrapGetInitialPropsWithSentry'; - -export { - // eslint-disable-next-line deprecation/deprecation - withSentryServerSideAppGetInitialProps, - wrapAppGetInitialPropsWithSentry, -} from './wrapAppGetInitialPropsWithSentry'; - -export { - // eslint-disable-next-line deprecation/deprecation - withSentryServerSideDocumentGetInitialProps, - wrapDocumentGetInitialPropsWithSentry, -} from './wrapDocumentGetInitialPropsWithSentry'; - -export { - // eslint-disable-next-line deprecation/deprecation - withSentryServerSideErrorGetInitialProps, - wrapErrorGetInitialPropsWithSentry, -} from './wrapErrorGetInitialPropsWithSentry'; - -export { - // eslint-disable-next-line deprecation/deprecation - withSentryGetServerSideProps, - wrapGetServerSidePropsWithSentry, -} from './wrapGetServerSidePropsWithSentry'; - -export { - // eslint-disable-next-line deprecation/deprecation - withSentryGetStaticProps, - wrapGetStaticPropsWithSentry, -} from './wrapGetStaticPropsWithSentry'; +export * from '../common'; diff --git a/packages/nextjs/src/client/wrapAppGetInitialPropsWithSentry.ts b/packages/nextjs/src/client/wrapAppGetInitialPropsWithSentry.ts deleted file mode 100644 index e5f8c40847ff..000000000000 --- a/packages/nextjs/src/client/wrapAppGetInitialPropsWithSentry.ts +++ /dev/null @@ -1,20 +0,0 @@ -import type App from 'next/app'; - -type AppGetInitialProps = (typeof App)['getInitialProps']; - -/** - * A passthrough function in case this function is used on the clientside. We need to make the returned function async - * so we are consistent with the serverside implementation. - */ -export function wrapAppGetInitialPropsWithSentry(origAppGetInitialProps: AppGetInitialProps): AppGetInitialProps { - return new Proxy(origAppGetInitialProps, { - apply: (wrappingTarget, thisArg, args: Parameters) => { - return wrappingTarget.apply(thisArg, args); - }, - }); -} - -/** - * @deprecated Use `wrapAppGetInitialPropsWithSentry` instead. - */ -export const withSentryServerSideAppGetInitialProps = wrapAppGetInitialPropsWithSentry; diff --git a/packages/nextjs/src/client/wrapDocumentGetInitialPropsWithSentry.ts b/packages/nextjs/src/client/wrapDocumentGetInitialPropsWithSentry.ts deleted file mode 100644 index 20669a0af9f6..000000000000 --- a/packages/nextjs/src/client/wrapDocumentGetInitialPropsWithSentry.ts +++ /dev/null @@ -1,22 +0,0 @@ -import type Document from 'next/document'; - -type DocumentGetInitialProps = typeof Document.getInitialProps; - -/** - * A passthrough function in case this function is used on the clientside. We need to make the returned function async - * so we are consistent with the serverside implementation. - */ -export function wrapDocumentGetInitialPropsWithSentry( - origDocumentGetInitialProps: DocumentGetInitialProps, -): DocumentGetInitialProps { - return new Proxy(origDocumentGetInitialProps, { - apply: (wrappingTarget, thisArg, args: Parameters) => { - return wrappingTarget.apply(thisArg, args); - }, - }); -} - -/** - * @deprecated Use `wrapDocumentGetInitialPropsWithSentry` instead. - */ -export const withSentryServerSideDocumentGetInitialProps = wrapDocumentGetInitialPropsWithSentry; diff --git a/packages/nextjs/src/client/wrapErrorGetInitialPropsWithSentry.ts b/packages/nextjs/src/client/wrapErrorGetInitialPropsWithSentry.ts deleted file mode 100644 index ab32a2bf93cc..000000000000 --- a/packages/nextjs/src/client/wrapErrorGetInitialPropsWithSentry.ts +++ /dev/null @@ -1,23 +0,0 @@ -import type { NextPageContext } from 'next'; -import type { ErrorProps } from 'next/error'; - -type ErrorGetInitialProps = (context: NextPageContext) => Promise; - -/** - * A passthrough function in case this function is used on the clientside. We need to make the returned function async - * so we are consistent with the serverside implementation. - */ -export function wrapErrorGetInitialPropsWithSentry( - origErrorGetInitialProps: ErrorGetInitialProps, -): ErrorGetInitialProps { - return new Proxy(origErrorGetInitialProps, { - apply: (wrappingTarget, thisArg, args: Parameters) => { - return wrappingTarget.apply(thisArg, args); - }, - }); -} - -/** - * @deprecated Use `wrapErrorGetInitialPropsWithSentry` instead. - */ -export const withSentryServerSideErrorGetInitialProps = wrapErrorGetInitialPropsWithSentry; diff --git a/packages/nextjs/src/client/wrapGetInitialPropsWithSentry.ts b/packages/nextjs/src/client/wrapGetInitialPropsWithSentry.ts deleted file mode 100644 index 37004f04bc6e..000000000000 --- a/packages/nextjs/src/client/wrapGetInitialPropsWithSentry.ts +++ /dev/null @@ -1,20 +0,0 @@ -import type { NextPage } from 'next'; - -type GetInitialProps = Required['getInitialProps']; - -/** - * A passthrough function in case this function is used on the clientside. We need to make the returned function async - * so we are consistent with the serverside implementation. - */ -export function wrapGetInitialPropsWithSentry(origGetInitialProps: GetInitialProps): GetInitialProps { - return new Proxy(origGetInitialProps, { - apply: (wrappingTarget, thisArg, args: Parameters) => { - return wrappingTarget.apply(thisArg, args); - }, - }); -} - -/** - * @deprecated Use `wrapGetInitialPropsWithSentry` instead. - */ -export const withSentryServerSideGetInitialProps = wrapGetInitialPropsWithSentry; diff --git a/packages/nextjs/src/client/wrapGetServerSidePropsWithSentry.ts b/packages/nextjs/src/client/wrapGetServerSidePropsWithSentry.ts deleted file mode 100644 index 50450c053a15..000000000000 --- a/packages/nextjs/src/client/wrapGetServerSidePropsWithSentry.ts +++ /dev/null @@ -1,18 +0,0 @@ -import type { GetServerSideProps } from 'next'; - -/** - * A passthrough function in case this function is used on the clientside. We need to make the returned function async - * so we are consistent with the serverside implementation. - */ -export function wrapGetServerSidePropsWithSentry(origGetServerSideProps: GetServerSideProps): GetServerSideProps { - return new Proxy(origGetServerSideProps, { - apply: (wrappingTarget, thisArg, args: Parameters) => { - return wrappingTarget.apply(thisArg, args); - }, - }); -} - -/** - * @deprecated Use `withSentryGetServerSideProps` instead. - */ -export const withSentryGetServerSideProps = wrapGetServerSidePropsWithSentry; diff --git a/packages/nextjs/src/client/wrapGetStaticPropsWithSentry.ts b/packages/nextjs/src/client/wrapGetStaticPropsWithSentry.ts deleted file mode 100644 index 3b99737bcf20..000000000000 --- a/packages/nextjs/src/client/wrapGetStaticPropsWithSentry.ts +++ /dev/null @@ -1,20 +0,0 @@ -import type { GetStaticProps } from 'next'; - -type Props = { [key: string]: unknown }; - -/** - * A passthrough function in case this function is used on the clientside. We need to make the returned function async - * so we are consistent with the serverside implementation. - */ -export function wrapGetStaticPropsWithSentry(origGetStaticProps: GetStaticProps): GetStaticProps { - return new Proxy(origGetStaticProps, { - apply: (wrappingTarget, thisArg, args: Parameters>) => { - return wrappingTarget.apply(thisArg, args); - }, - }); -} - -/** - * @deprecated Use `wrapGetStaticPropsWithSentry` instead. - */ -export const withSentryGetStaticProps = wrapGetStaticPropsWithSentry; diff --git a/packages/nextjs/src/common/index.ts b/packages/nextjs/src/common/index.ts new file mode 100644 index 000000000000..ccd4a628634e --- /dev/null +++ b/packages/nextjs/src/common/index.ts @@ -0,0 +1,41 @@ +export { + // eslint-disable-next-line deprecation/deprecation + withSentryGetStaticProps, + wrapGetStaticPropsWithSentry, +} from './wrapGetStaticPropsWithSentry'; + +export { + // eslint-disable-next-line deprecation/deprecation + withSentryServerSideGetInitialProps, + wrapGetInitialPropsWithSentry, +} from './wrapGetInitialPropsWithSentry'; + +export { + // eslint-disable-next-line deprecation/deprecation + withSentryServerSideAppGetInitialProps, + wrapAppGetInitialPropsWithSentry, +} from './wrapAppGetInitialPropsWithSentry'; + +export { + // eslint-disable-next-line deprecation/deprecation + withSentryServerSideDocumentGetInitialProps, + wrapDocumentGetInitialPropsWithSentry, +} from './wrapDocumentGetInitialPropsWithSentry'; + +export { + // eslint-disable-next-line deprecation/deprecation + withSentryServerSideErrorGetInitialProps, + wrapErrorGetInitialPropsWithSentry, +} from './wrapErrorGetInitialPropsWithSentry'; + +export { + // eslint-disable-next-line deprecation/deprecation + withSentryGetServerSideProps, + wrapGetServerSidePropsWithSentry, +} from './wrapGetServerSidePropsWithSentry'; + +export { wrapServerComponentWithSentry } from './wrapServerComponentWithSentry'; + +export { wrapApiHandlerWithSentryVercelCrons } from './wrapApiHandlerWithSentryVercelCrons'; + +export { wrapMiddlewareWithSentry } from './wrapMiddlewareWithSentry'; diff --git a/packages/nextjs/src/common/types.ts b/packages/nextjs/src/common/types.ts index 6f1c9e5b2c4b..dc838b214276 100644 --- a/packages/nextjs/src/common/types.ts +++ b/packages/nextjs/src/common/types.ts @@ -1,3 +1,6 @@ +import type { Transaction, WrappedFunction } from '@sentry/types'; +import type { NextApiRequest, NextApiResponse } from 'next'; + export type ServerComponentContext = { componentRoute: string; componentType: string; @@ -6,3 +9,51 @@ export type ServerComponentContext = { }; export type VercelCronsConfig = { path?: string; schedule?: string }[] | undefined; + +// The `NextApiHandler` and `WrappedNextApiHandler` types are the same as the official `NextApiHandler` type, except: +// +// a) The wrapped version returns only promises, because wrapped handlers are always async. +// +// b) Instead of having a return types based on `void` (Next < 12.1.6) or `unknown` (Next 12.1.6+), both the wrapped and +// unwrapped versions of the type have both. This doesn't matter to users, because they exist solely on one side of that +// version divide or the other. For us, though, it's entirely possible to have one version of Next installed in our +// local repo (as a dev dependency) and have another Next version installed in a test app which also has the local SDK +// linked in. +// +// In that case, if those two versions are on either side of the 12.1.6 divide, importing the official `NextApiHandler` +// type here would break the test app's build, because it would set up a situation in which the linked SDK's +// `withSentry` would refer to one version of the type (from the local repo's `node_modules`) while any typed handler in +// the test app would refer to the other version of the type (from the test app's `node_modules`). By using a custom +// version of the type compatible with both the old and new official versions, we can use any Next version we want in a +// test app without worrying about type errors. +// +// c) These have internal SDK flags which the official Next types obviously don't have, one to allow our auto-wrapping +// function, `withSentryAPI`, to pass the parameterized route into `withSentry`, and the other to prevent a manually +// wrapped route from being wrapped again by the auto-wrapper. + +export type NextApiHandler = { + (req: NextApiRequest, res: NextApiResponse): void | Promise | unknown | Promise; + __sentry_route__?: string; + + /** + * A property we set in our integration tests to simulate running an API route on platforms that don't support streaming. + */ + __sentry_test_doesnt_support_streaming__?: true; +}; + +export type WrappedNextApiHandler = { + (req: NextApiRequest, res: NextApiResponse): Promise | Promise; + __sentry_route__?: string; + __sentry_wrapped__?: boolean; +}; + +export type AugmentedNextApiRequest = NextApiRequest & { + __withSentry_applied__?: boolean; +}; + +export type AugmentedNextApiResponse = NextApiResponse & { + __sentryTransaction?: Transaction; +}; + +export type ResponseEndMethod = AugmentedNextApiResponse['end']; +export type WrappedResponseEndMethod = AugmentedNextApiResponse['end'] & WrappedFunction; diff --git a/packages/nextjs/src/edge/utils/edgeWrapperUtils.ts b/packages/nextjs/src/common/utils/edgeWrapperUtils.ts similarity index 93% rename from packages/nextjs/src/edge/utils/edgeWrapperUtils.ts rename to packages/nextjs/src/common/utils/edgeWrapperUtils.ts index 256499c97185..f3023665d106 100644 --- a/packages/nextjs/src/edge/utils/edgeWrapperUtils.ts +++ b/packages/nextjs/src/common/utils/edgeWrapperUtils.ts @@ -1,9 +1,8 @@ -import { captureException, getCurrentHub, hasTracingEnabled, startTransaction } from '@sentry/core'; +import { captureException, flush, getCurrentHub, hasTracingEnabled, startTransaction } from '@sentry/core'; import type { Span } from '@sentry/types'; import { addExceptionMechanism, logger, objectify, tracingContextFromHeaders } from '@sentry/utils'; -import type { EdgeRouteHandler } from '../types'; -import { flush } from './flush'; +import type { EdgeRouteHandler } from '../../edge/types'; /** * Wraps a function on the edge runtime with error and performance monitoring. diff --git a/packages/nextjs/src/server/utils/isBuild.ts b/packages/nextjs/src/common/utils/isBuild.ts similarity index 100% rename from packages/nextjs/src/server/utils/isBuild.ts rename to packages/nextjs/src/common/utils/isBuild.ts diff --git a/packages/nextjs/src/server/utils/platformSupportsStreaming.ts b/packages/nextjs/src/common/utils/platformSupportsStreaming.ts similarity index 100% rename from packages/nextjs/src/server/utils/platformSupportsStreaming.ts rename to packages/nextjs/src/common/utils/platformSupportsStreaming.ts diff --git a/packages/nextjs/src/server/utils/responseEnd.ts b/packages/nextjs/src/common/utils/responseEnd.ts similarity index 80% rename from packages/nextjs/src/server/utils/responseEnd.ts rename to packages/nextjs/src/common/utils/responseEnd.ts index ee2d9f803d3b..4cc31de89fb5 100644 --- a/packages/nextjs/src/server/utils/responseEnd.ts +++ b/packages/nextjs/src/common/utils/responseEnd.ts @@ -1,4 +1,4 @@ -import { flush } from '@sentry/node'; +import { flush } from '@sentry/core'; import type { Transaction } from '@sentry/types'; import { fill, logger } from '@sentry/utils'; import type { ServerResponse } from 'http'; @@ -41,18 +41,7 @@ export function autoEndTransactionOnResponseEnd(transaction: Transaction, res: S export async function finishTransaction(transaction: Transaction | undefined, res: ServerResponse): Promise { if (transaction) { transaction.setHttpStatus(res.statusCode); - - // If any open spans are set to finish when the response ends, it sets up a race condition between their `finish` - // calls and the transaction's `finish` call - and any spans which lose the race will get dropped from the - // transaction. To prevent this, push `transaction.finish` to the next event loop so that it's guaranteed to lose - // the race, and wait for it to be done before flushing events. - const transactionFinished: Promise = new Promise(resolve => { - setImmediate(() => { - transaction.finish(); - resolve(); - }); - }); - await transactionFinished; + transaction.finish(); } } diff --git a/packages/nextjs/src/server/utils/wrapperUtils.ts b/packages/nextjs/src/common/utils/wrapperUtils.ts similarity index 100% rename from packages/nextjs/src/server/utils/wrapperUtils.ts rename to packages/nextjs/src/common/utils/wrapperUtils.ts diff --git a/packages/nextjs/src/server/wrapApiHandlerWithSentry.ts b/packages/nextjs/src/common/wrapApiHandlerWithSentry.ts similarity index 98% rename from packages/nextjs/src/server/wrapApiHandlerWithSentry.ts rename to packages/nextjs/src/common/wrapApiHandlerWithSentry.ts index 524e29fa0ca0..b4af7d47893e 100644 --- a/packages/nextjs/src/server/wrapApiHandlerWithSentry.ts +++ b/packages/nextjs/src/common/wrapApiHandlerWithSentry.ts @@ -1,5 +1,10 @@ -import { getCurrentHub, hasTracingEnabled, runWithAsyncContext } from '@sentry/core'; -import { captureException, startTransaction } from '@sentry/node'; +import { + captureException, + getCurrentHub, + hasTracingEnabled, + runWithAsyncContext, + startTransaction, +} from '@sentry/core'; import type { Transaction } from '@sentry/types'; import { addExceptionMechanism, diff --git a/packages/nextjs/src/server/wrapAppGetInitialPropsWithSentry.ts b/packages/nextjs/src/common/wrapAppGetInitialPropsWithSentry.ts similarity index 97% rename from packages/nextjs/src/server/wrapAppGetInitialPropsWithSentry.ts rename to packages/nextjs/src/common/wrapAppGetInitialPropsWithSentry.ts index e1ecf50dad54..c39b903f717b 100644 --- a/packages/nextjs/src/server/wrapAppGetInitialPropsWithSentry.ts +++ b/packages/nextjs/src/common/wrapAppGetInitialPropsWithSentry.ts @@ -1,5 +1,4 @@ -import { hasTracingEnabled } from '@sentry/core'; -import { getCurrentHub } from '@sentry/node'; +import { getCurrentHub, hasTracingEnabled } from '@sentry/core'; import { dynamicSamplingContextToSentryBaggageHeader } from '@sentry/utils'; import type App from 'next/app'; diff --git a/packages/nextjs/src/server/wrapDocumentGetInitialPropsWithSentry.ts b/packages/nextjs/src/common/wrapDocumentGetInitialPropsWithSentry.ts similarity index 95% rename from packages/nextjs/src/server/wrapDocumentGetInitialPropsWithSentry.ts rename to packages/nextjs/src/common/wrapDocumentGetInitialPropsWithSentry.ts index 1d821d86dcda..54b8c614f799 100644 --- a/packages/nextjs/src/server/wrapDocumentGetInitialPropsWithSentry.ts +++ b/packages/nextjs/src/common/wrapDocumentGetInitialPropsWithSentry.ts @@ -1,5 +1,4 @@ -import { hasTracingEnabled } from '@sentry/core'; -import { getCurrentHub } from '@sentry/node'; +import { getCurrentHub, hasTracingEnabled } from '@sentry/core'; import type Document from 'next/document'; import { isBuild } from './utils/isBuild'; diff --git a/packages/nextjs/src/server/wrapErrorGetInitialPropsWithSentry.ts b/packages/nextjs/src/common/wrapErrorGetInitialPropsWithSentry.ts similarity index 96% rename from packages/nextjs/src/server/wrapErrorGetInitialPropsWithSentry.ts rename to packages/nextjs/src/common/wrapErrorGetInitialPropsWithSentry.ts index b99b04524d46..9ebf810e5ee0 100644 --- a/packages/nextjs/src/server/wrapErrorGetInitialPropsWithSentry.ts +++ b/packages/nextjs/src/common/wrapErrorGetInitialPropsWithSentry.ts @@ -1,5 +1,4 @@ -import { hasTracingEnabled } from '@sentry/core'; -import { getCurrentHub } from '@sentry/node'; +import { getCurrentHub, hasTracingEnabled } from '@sentry/core'; import { dynamicSamplingContextToSentryBaggageHeader } from '@sentry/utils'; import type { NextPageContext } from 'next'; import type { ErrorProps } from 'next/error'; diff --git a/packages/nextjs/src/server/wrapGetInitialPropsWithSentry.ts b/packages/nextjs/src/common/wrapGetInitialPropsWithSentry.ts similarity index 96% rename from packages/nextjs/src/server/wrapGetInitialPropsWithSentry.ts rename to packages/nextjs/src/common/wrapGetInitialPropsWithSentry.ts index d1613a80d2c9..601105bab7be 100644 --- a/packages/nextjs/src/server/wrapGetInitialPropsWithSentry.ts +++ b/packages/nextjs/src/common/wrapGetInitialPropsWithSentry.ts @@ -1,5 +1,4 @@ -import { hasTracingEnabled } from '@sentry/core'; -import { getCurrentHub } from '@sentry/node'; +import { getCurrentHub, hasTracingEnabled } from '@sentry/core'; import { dynamicSamplingContextToSentryBaggageHeader } from '@sentry/utils'; import type { NextPage } from 'next'; diff --git a/packages/nextjs/src/server/wrapGetServerSidePropsWithSentry.ts b/packages/nextjs/src/common/wrapGetServerSidePropsWithSentry.ts similarity index 96% rename from packages/nextjs/src/server/wrapGetServerSidePropsWithSentry.ts rename to packages/nextjs/src/common/wrapGetServerSidePropsWithSentry.ts index f37068bae206..f0a0517960fc 100644 --- a/packages/nextjs/src/server/wrapGetServerSidePropsWithSentry.ts +++ b/packages/nextjs/src/common/wrapGetServerSidePropsWithSentry.ts @@ -1,5 +1,4 @@ -import { hasTracingEnabled } from '@sentry/core'; -import { getCurrentHub } from '@sentry/node'; +import { getCurrentHub, hasTracingEnabled } from '@sentry/core'; import { dynamicSamplingContextToSentryBaggageHeader } from '@sentry/utils'; import type { GetServerSideProps } from 'next'; diff --git a/packages/nextjs/src/server/wrapGetStaticPropsWithSentry.ts b/packages/nextjs/src/common/wrapGetStaticPropsWithSentry.ts similarity index 93% rename from packages/nextjs/src/server/wrapGetStaticPropsWithSentry.ts rename to packages/nextjs/src/common/wrapGetStaticPropsWithSentry.ts index 78f910dfb0e4..e65d0ba8f90a 100644 --- a/packages/nextjs/src/server/wrapGetStaticPropsWithSentry.ts +++ b/packages/nextjs/src/common/wrapGetStaticPropsWithSentry.ts @@ -1,5 +1,4 @@ -import { hasTracingEnabled } from '@sentry/core'; -import { getCurrentHub } from '@sentry/node'; +import { getCurrentHub, hasTracingEnabled } from '@sentry/core'; import type { GetStaticProps } from 'next'; import { isBuild } from './utils/isBuild'; diff --git a/packages/nextjs/src/edge/wrapMiddlewareWithSentry.ts b/packages/nextjs/src/common/wrapMiddlewareWithSentry.ts similarity index 92% rename from packages/nextjs/src/edge/wrapMiddlewareWithSentry.ts rename to packages/nextjs/src/common/wrapMiddlewareWithSentry.ts index 831a50eb8629..66cbbb046300 100644 --- a/packages/nextjs/src/edge/wrapMiddlewareWithSentry.ts +++ b/packages/nextjs/src/common/wrapMiddlewareWithSentry.ts @@ -1,4 +1,4 @@ -import type { EdgeRouteHandler } from './types'; +import type { EdgeRouteHandler } from '../edge/types'; import { withEdgeWrapping } from './utils/edgeWrapperUtils'; /** diff --git a/packages/nextjs/src/server/wrapServerComponentWithSentry.ts b/packages/nextjs/src/common/wrapServerComponentWithSentry.ts similarity index 100% rename from packages/nextjs/src/server/wrapServerComponentWithSentry.ts rename to packages/nextjs/src/common/wrapServerComponentWithSentry.ts diff --git a/packages/nextjs/src/config/templates/apiWrapperTemplate.ts b/packages/nextjs/src/config/templates/apiWrapperTemplate.ts index 0eccf3024a76..28d5e4efc806 100644 --- a/packages/nextjs/src/config/templates/apiWrapperTemplate.ts +++ b/packages/nextjs/src/config/templates/apiWrapperTemplate.ts @@ -13,10 +13,7 @@ import * as origModule from '__SENTRY_WRAPPING_TARGET_FILE__'; import * as Sentry from '@sentry/nextjs'; import type { PageConfig } from 'next'; -import type { VercelCronsConfig } from '../../common/types'; -// We import this from `wrappers` rather than directly from `next` because our version can work simultaneously with -// multiple versions of next. See note in `wrappers/types` for more. -import type { NextApiHandler } from '../../server/types'; +import type { NextApiHandler, VercelCronsConfig } from '../../common/types'; type NextApiModule = ( | { diff --git a/packages/nextjs/src/edge/index.ts b/packages/nextjs/src/edge/index.ts index 70cba9c356e6..6c2967d30f9b 100644 --- a/packages/nextjs/src/edge/index.ts +++ b/packages/nextjs/src/edge/index.ts @@ -1,12 +1,6 @@ -import { getCurrentHub, getIntegrationsToSetup, initAndBind, Integrations as CoreIntegrations } from '@sentry/core'; +import { getIntegrationsToSetup, initAndBind, Integrations as CoreIntegrations } from '@sentry/core'; import type { Options } from '@sentry/types'; -import { - createStackParser, - GLOBAL_OBJ, - logger, - nodeStackLineParser, - stackParserFromStackParserOptions, -} from '@sentry/utils'; +import { createStackParser, GLOBAL_OBJ, nodeStackLineParser, stackParserFromStackParserOptions } from '@sentry/utils'; import { getVercelEnv } from '../common/getVercelEnv'; import { setAsyncLocalStorageAsyncContextStrategy } from './asyncLocalStorageAsyncContextStrategy'; @@ -103,32 +97,6 @@ export function getSentryRelease(fallback?: string): string | undefined { ); } -/** - * Call `close()` on the current client, if there is one. See {@link Client.close}. - * - * @param timeout Maximum time in ms the client should wait to flush its event queue before shutting down. Omitting this - * parameter will cause the client to wait until all events are sent before disabling itself. - * @returns A promise which resolves to `true` if the queue successfully drains before the timeout, or `false` if it - * doesn't (or if there's no client defined). - */ -export async function close(timeout?: number): Promise { - const client = getCurrentHub().getClient(); - if (client) { - return client.close(timeout); - } - __DEBUG_BUILD__ && logger.warn('Cannot flush events and disable SDK. No client defined.'); - return Promise.resolve(false); -} - -/** - * This is the getter for lastEventId. - * - * @returns The last event id of a captured event. - */ -export function lastEventId(): string | undefined { - return getCurrentHub().lastEventId(); -} - /** * Just a passthrough in case this is imported from the client. */ @@ -136,18 +104,14 @@ export function withSentryConfig(exportedUserNextConfig: T): T { return exportedUserNextConfig; } -export { flush } from './utils/flush'; - export * from '@sentry/core'; +// eslint-disable-next-line import/export +export * from '../common'; + export { - // eslint-disable-next-line deprecation/deprecation + // eslint-disable-next-line deprecation/deprecation, import/export withSentryAPI, + // eslint-disable-next-line import/export wrapApiHandlerWithSentry, } from './wrapApiHandlerWithSentry'; - -export { wrapApiHandlerWithSentryVercelCrons } from '../common/wrapApiHandlerWithSentryVercelCrons'; - -export { wrapMiddlewareWithSentry } from './wrapMiddlewareWithSentry'; - -export { wrapServerComponentWithSentry } from './wrapServerComponentWithSentry'; diff --git a/packages/nextjs/src/edge/utils/flush.ts b/packages/nextjs/src/edge/utils/flush.ts deleted file mode 100644 index 5daa52936391..000000000000 --- a/packages/nextjs/src/edge/utils/flush.ts +++ /dev/null @@ -1,20 +0,0 @@ -import { getCurrentHub } from '@sentry/core'; -import type { Client } from '@sentry/types'; -import { logger } from '@sentry/utils'; - -/** - * Call `flush()` on the current client, if there is one. See {@link Client.flush}. - * - * @param timeout Maximum time in ms the client should wait to flush its event queue. Omitting this parameter will cause - * the client to wait until all events are sent before resolving the promise. - * @returns A promise which resolves to `true` if the queue successfully drains before the timeout, or `false` if it - * doesn't (or if there's no client defined). - */ -export async function flush(timeout?: number): Promise { - const client = getCurrentHub().getClient(); - if (client) { - return client.flush(timeout); - } - __DEBUG_BUILD__ && logger.warn('Cannot flush events. No client defined.'); - return Promise.resolve(false); -} diff --git a/packages/nextjs/src/edge/wrapApiHandlerWithSentry.ts b/packages/nextjs/src/edge/wrapApiHandlerWithSentry.ts index f903d77f46c4..46691b3cdce5 100644 --- a/packages/nextjs/src/edge/wrapApiHandlerWithSentry.ts +++ b/packages/nextjs/src/edge/wrapApiHandlerWithSentry.ts @@ -1,7 +1,7 @@ import { getCurrentHub } from '@sentry/core'; +import { withEdgeWrapping } from '../common/utils/edgeWrapperUtils'; import type { EdgeRouteHandler } from './types'; -import { withEdgeWrapping } from './utils/edgeWrapperUtils'; /** * Wraps a Next.js edge route handler with Sentry error and performance instrumentation. diff --git a/packages/nextjs/src/edge/wrapServerComponentWithSentry.ts b/packages/nextjs/src/edge/wrapServerComponentWithSentry.ts deleted file mode 100644 index 349207e7b039..000000000000 --- a/packages/nextjs/src/edge/wrapServerComponentWithSentry.ts +++ /dev/null @@ -1,37 +0,0 @@ -import { captureException } from '@sentry/core'; - -import type { ServerComponentContext } from '../common/types'; - -/** - * Wraps an `app` directory server component with Sentry error instrumentation. - */ -export function wrapServerComponentWithSentry any>( - appDirComponent: F, - _context: ServerComponentContext, -): F { - // Even though users may define server components as async functions, for the client bundles - // Next.js will turn them into synchronous functions and it will transform any`await`s into instances of the`use` - // hook. 🤯 - return new Proxy(appDirComponent, { - apply: (originalFunction, thisArg, args) => { - let maybePromiseResult; - - try { - maybePromiseResult = originalFunction.apply(thisArg, args); - } catch (e) { - captureException(e); - throw e; - } - - if (typeof maybePromiseResult === 'object' && maybePromiseResult !== null && 'then' in maybePromiseResult) { - // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access - return maybePromiseResult.then(null, (e: Error) => { - captureException(e); - throw e; - }); - } else { - return maybePromiseResult; - } - }, - }); -} diff --git a/packages/nextjs/src/index.types.ts b/packages/nextjs/src/index.types.ts index 6fcf7436399b..5fbcf683ec38 100644 --- a/packages/nextjs/src/index.types.ts +++ b/packages/nextjs/src/index.types.ts @@ -27,9 +27,6 @@ export declare const Integrations: typeof clientSdk.Integrations & export declare const defaultIntegrations: Integration[]; export declare const defaultStackParser: StackParser; -export declare function close(timeout?: number | undefined): PromiseLike; -export declare function flush(timeout?: number | undefined): PromiseLike; -export declare function lastEventId(): string | undefined; export declare function getSentryRelease(fallback?: string): string | undefined; export declare const ErrorBoundary: typeof clientSdk.ErrorBoundary; diff --git a/packages/nextjs/src/server/index.ts b/packages/nextjs/src/server/index.ts index d92e406cda3f..0e43291d019d 100644 --- a/packages/nextjs/src/server/index.ts +++ b/packages/nextjs/src/server/index.ts @@ -10,7 +10,7 @@ import * as path from 'path'; import { devErrorSymbolicationEventProcessor } from '../common/devErrorSymbolicationEventProcessor'; import { getVercelEnv } from '../common/getVercelEnv'; import { buildMetadata } from '../common/metadata'; -import { isBuild } from './utils/isBuild'; +import { isBuild } from '../common/utils/isBuild'; export * from '@sentry/node'; export { captureUnderscoreErrorException } from '../common/_error'; @@ -157,41 +157,7 @@ const deprecatedIsBuild = (): boolean => isBuild(); // eslint-disable-next-line deprecation/deprecation export { deprecatedIsBuild as isBuild }; -export { wrapApiHandlerWithSentryVercelCrons } from '../common/wrapApiHandlerWithSentryVercelCrons'; - -export { - // eslint-disable-next-line deprecation/deprecation - withSentryGetStaticProps, - wrapGetStaticPropsWithSentry, -} from './wrapGetStaticPropsWithSentry'; - -export { - // eslint-disable-next-line deprecation/deprecation - withSentryServerSideGetInitialProps, - wrapGetInitialPropsWithSentry, -} from './wrapGetInitialPropsWithSentry'; - -export { - // eslint-disable-next-line deprecation/deprecation - withSentryServerSideAppGetInitialProps, - wrapAppGetInitialPropsWithSentry, -} from './wrapAppGetInitialPropsWithSentry'; -export { - // eslint-disable-next-line deprecation/deprecation - withSentryServerSideDocumentGetInitialProps, - wrapDocumentGetInitialPropsWithSentry, -} from './wrapDocumentGetInitialPropsWithSentry'; -export { - // eslint-disable-next-line deprecation/deprecation - withSentryServerSideErrorGetInitialProps, - wrapErrorGetInitialPropsWithSentry, -} from './wrapErrorGetInitialPropsWithSentry'; - -export { - // eslint-disable-next-line deprecation/deprecation - withSentryGetServerSideProps, - wrapGetServerSidePropsWithSentry, -} from './wrapGetServerSidePropsWithSentry'; +export * from '../common'; export { // eslint-disable-next-line deprecation/deprecation @@ -199,6 +165,4 @@ export { // eslint-disable-next-line deprecation/deprecation withSentryAPI, wrapApiHandlerWithSentry, -} from './wrapApiHandlerWithSentry'; - -export { wrapServerComponentWithSentry } from './wrapServerComponentWithSentry'; +} from '../common/wrapApiHandlerWithSentry'; diff --git a/packages/nextjs/src/server/types.ts b/packages/nextjs/src/server/types.ts deleted file mode 100644 index a411c4ea62cf..000000000000 --- a/packages/nextjs/src/server/types.ts +++ /dev/null @@ -1,50 +0,0 @@ -import type { Transaction, WrappedFunction } from '@sentry/types'; -import type { NextApiRequest, NextApiResponse } from 'next'; - -// The `NextApiHandler` and `WrappedNextApiHandler` types are the same as the official `NextApiHandler` type, except: -// -// a) The wrapped version returns only promises, because wrapped handlers are always async. -// -// b) Instead of having a return types based on `void` (Next < 12.1.6) or `unknown` (Next 12.1.6+), both the wrapped and -// unwrapped versions of the type have both. This doesn't matter to users, because they exist solely on one side of that -// version divide or the other. For us, though, it's entirely possible to have one version of Next installed in our -// local repo (as a dev dependency) and have another Next version installed in a test app which also has the local SDK -// linked in. -// -// In that case, if those two versions are on either side of the 12.1.6 divide, importing the official `NextApiHandler` -// type here would break the test app's build, because it would set up a situation in which the linked SDK's -// `withSentry` would refer to one version of the type (from the local repo's `node_modules`) while any typed handler in -// the test app would refer to the other version of the type (from the test app's `node_modules`). By using a custom -// version of the type compatible with both the old and new official versions, we can use any Next version we want in a -// test app without worrying about type errors. -// -// c) These have internal SDK flags which the official Next types obviously don't have, one to allow our auto-wrapping -// function, `withSentryAPI`, to pass the parameterized route into `withSentry`, and the other to prevent a manually -// wrapped route from being wrapped again by the auto-wrapper. - -export type NextApiHandler = { - (req: NextApiRequest, res: NextApiResponse): void | Promise | unknown | Promise; - __sentry_route__?: string; - - /** - * A property we set in our integration tests to simulate running an API route on platforms that don't support streaming. - */ - __sentry_test_doesnt_support_streaming__?: true; -}; - -export type WrappedNextApiHandler = { - (req: NextApiRequest, res: NextApiResponse): Promise | Promise; - __sentry_route__?: string; - __sentry_wrapped__?: boolean; -}; - -export type AugmentedNextApiRequest = NextApiRequest & { - __withSentry_applied__?: boolean; -}; - -export type AugmentedNextApiResponse = NextApiResponse & { - __sentryTransaction?: Transaction; -}; - -export type ResponseEndMethod = AugmentedNextApiResponse['end']; -export type WrappedResponseEndMethod = AugmentedNextApiResponse['end'] & WrappedFunction; diff --git a/packages/nextjs/test/config/withSentry.test.ts b/packages/nextjs/test/config/withSentry.test.ts index 92315374836b..c7862e1473df 100644 --- a/packages/nextjs/test/config/withSentry.test.ts +++ b/packages/nextjs/test/config/withSentry.test.ts @@ -1,11 +1,10 @@ -import * as hub from '@sentry/core'; +import * as SentryCore from '@sentry/core'; import { addTracingExtensions } from '@sentry/core'; -import * as Sentry from '@sentry/node'; import type { Client, ClientOptions } from '@sentry/types'; import type { NextApiRequest, NextApiResponse } from 'next'; +import type { AugmentedNextApiResponse, NextApiHandler } from '../../src/common/types'; import { withSentry } from '../../src/server'; -import type { AugmentedNextApiResponse, NextApiHandler } from '../../src/server/types'; // The wrap* functions require the hub to have tracing extensions. This is normally called by the NodeClient // constructor but the client isn't used in these tests. @@ -41,7 +40,7 @@ async function callWrappedHandler(wrappedHandler: NextApiHandler, req: NextApiRe } } -const startTransactionSpy = jest.spyOn(Sentry, 'startTransaction'); +const startTransactionSpy = jest.spyOn(SentryCore, 'startTransaction'); describe('withSentry', () => { let req: NextApiRequest, res: NextApiResponse; @@ -71,7 +70,7 @@ describe('withSentry', () => { describe('tracing', () => { it('starts a transaction and sets metadata when tracing is enabled', async () => { - jest.spyOn(hub.Hub.prototype, 'getClient').mockReturnValueOnce({ + jest.spyOn(SentryCore.Hub.prototype, 'getClient').mockReturnValueOnce({ getOptions: () => ({ tracesSampleRate: 1, instrumenter: 'sentry' } as ClientOptions), } as Client); diff --git a/packages/nextjs/test/config/wrappers.test.ts b/packages/nextjs/test/config/wrappers.test.ts index 9195154991be..44528ec17cc1 100644 --- a/packages/nextjs/test/config/wrappers.test.ts +++ b/packages/nextjs/test/config/wrappers.test.ts @@ -1,9 +1,8 @@ import * as SentryCore from '@sentry/core'; import { addTracingExtensions } from '@sentry/core'; -import * as SentryNode from '@sentry/node'; import type { IncomingMessage, ServerResponse } from 'http'; -import { wrapGetInitialPropsWithSentry, wrapGetServerSidePropsWithSentry } from '../../src/server'; +import { wrapGetInitialPropsWithSentry, wrapGetServerSidePropsWithSentry } from '../../src/common'; const startTransactionSpy = jest.spyOn(SentryCore, 'startTransaction'); const originalGetCurrentHub = jest.requireActual('@sentry/node').getCurrentHub; @@ -23,7 +22,7 @@ describe('data-fetching function wrappers', () => { res = { end: jest.fn() } as unknown as ServerResponse; jest.spyOn(SentryCore, 'hasTracingEnabled').mockReturnValue(true); - jest.spyOn(SentryNode, 'getCurrentHub').mockImplementation(() => { + jest.spyOn(SentryCore, 'getCurrentHub').mockImplementation(() => { const hub = originalGetCurrentHub(); hub.getClient = () => diff --git a/packages/nextjs/test/edge/edgeWrapperUtils.test.ts b/packages/nextjs/test/edge/edgeWrapperUtils.test.ts index cdc7cc4986e2..3dd963077a00 100644 --- a/packages/nextjs/test/edge/edgeWrapperUtils.test.ts +++ b/packages/nextjs/test/edge/edgeWrapperUtils.test.ts @@ -1,7 +1,7 @@ import * as coreSdk from '@sentry/core'; import { addTracingExtensions } from '@sentry/core'; -import { withEdgeWrapping } from '../../src/edge/utils/edgeWrapperUtils'; +import { withEdgeWrapping } from '../../src/common/utils/edgeWrapperUtils'; // The wrap* functions require the hub to have tracing extensions. This is normally called by the EdgeClient // constructor but the client isn't used in these tests.