Skip to content

fix(nextjs): Make all wrappers isomorphic and available in all runtimes #8743

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

Merged
merged 10 commits into from
Aug 8, 2023
Merged
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
36 changes: 1 addition & 35 deletions packages/nextjs/src/client/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -130,38 +130,4 @@ export function withSentryConfig<T>(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';
20 changes: 0 additions & 20 deletions packages/nextjs/src/client/wrapAppGetInitialPropsWithSentry.ts

This file was deleted.

This file was deleted.

23 changes: 0 additions & 23 deletions packages/nextjs/src/client/wrapErrorGetInitialPropsWithSentry.ts

This file was deleted.

20 changes: 0 additions & 20 deletions packages/nextjs/src/client/wrapGetInitialPropsWithSentry.ts

This file was deleted.

18 changes: 0 additions & 18 deletions packages/nextjs/src/client/wrapGetServerSidePropsWithSentry.ts

This file was deleted.

20 changes: 0 additions & 20 deletions packages/nextjs/src/client/wrapGetStaticPropsWithSentry.ts

This file was deleted.

41 changes: 41 additions & 0 deletions packages/nextjs/src/common/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
export {
Copy link
Contributor

@kamilogorek kamilogorek Aug 8, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

NIT: newline consistency, but ignore it, as CI is already green

// 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';
51 changes: 51 additions & 0 deletions packages/nextjs/src/common/types.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,6 @@
import type { Transaction, WrappedFunction } from '@sentry/types';
import type { NextApiRequest, NextApiResponse } from 'next';

export type ServerComponentContext = {
componentRoute: string;
componentType: string;
Expand All @@ -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<void> | unknown | Promise<unknown>;
__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<void> | Promise<unknown>;
__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;
Original file line number Diff line number Diff line change
@@ -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.
Expand Down
Original file line number Diff line number Diff line change
@@ -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';
Expand Down Expand Up @@ -41,18 +41,7 @@ export function autoEndTransactionOnResponseEnd(transaction: Transaction, res: S
export async function finishTransaction(transaction: Transaction | undefined, res: ServerResponse): Promise<void> {
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<void> = new Promise(resolve => {
setImmediate(() => {
transaction.finish();
resolve();
});
});
await transactionFinished;
transaction.finish();
}
}

Expand Down
Original file line number Diff line number Diff line change
@@ -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,
Expand Down
Original file line number Diff line number Diff line change
@@ -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';

Expand Down
Original file line number Diff line number Diff line change
@@ -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';
Expand Down
Original file line number Diff line number Diff line change
@@ -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';
Expand Down
Original file line number Diff line number Diff line change
@@ -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';

Expand Down
Original file line number Diff line number Diff line change
@@ -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';

Expand Down
Original file line number Diff line number Diff line change
@@ -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';
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import type { EdgeRouteHandler } from './types';
import type { EdgeRouteHandler } from '../edge/types';
import { withEdgeWrapping } from './utils/edgeWrapperUtils';

/**
Expand Down
5 changes: 1 addition & 4 deletions packages/nextjs/src/config/templates/apiWrapperTemplate.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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 = (
| {
Expand Down
Loading