123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209 |
- var {
- _optionalChain
- } = require('@sentry/utils');
- Object.defineProperty(exports, '__esModule', { value: true });
- const core = require('@sentry/core');
- const utils = require('@sentry/utils');
- const platformSupportsStreaming = require('./utils/platformSupportsStreaming.js');
- const responseEnd = require('./utils/responseEnd.js');
- /**
- * Wrap the given API route handler for tracing and error capturing. Thin wrapper around `withSentry`, which only
- * applies it if it hasn't already been applied.
- *
- * @param apiHandler The handler exported from the user's API page route file, which may or may not already be
- * wrapped with `withSentry`
- * @param parameterizedRoute The page's route, passed in via the proxy loader
- * @returns The wrapped handler
- */
- function wrapApiHandlerWithSentry(apiHandler, parameterizedRoute) {
- return new Proxy(apiHandler, {
- apply: (wrappingTarget, thisArg, args) => {
- // eslint-disable-next-line deprecation/deprecation
- return withSentry(wrappingTarget, parameterizedRoute).apply(thisArg, args);
- },
- });
- }
- /**
- * @deprecated Use `wrapApiHandlerWithSentry()` instead
- */
- const withSentryAPI = wrapApiHandlerWithSentry;
- /**
- * Legacy function for manually wrapping API route handlers, now used as the innards of `wrapApiHandlerWithSentry`.
- *
- * @param apiHandler The user's original API route handler
- * @param parameterizedRoute The route whose handler is being wrapped. Meant for internal use only.
- * @returns A wrapped version of the handler
- *
- * @deprecated Use `wrapApiHandlerWithSentry()` instead
- */
- function withSentry(apiHandler, parameterizedRoute) {
- return new Proxy(apiHandler, {
- apply: (
- wrappingTarget,
- thisArg,
- args,
- ) => {
- const [req, res] = args;
- if (!req) {
- utils.logger.debug(
- `Wrapped API handler on route "${parameterizedRoute}" was not passed a request object. Will not instrument.`,
- );
- return wrappingTarget.apply(thisArg, args);
- } else if (!res) {
- utils.logger.debug(
- `Wrapped API handler on route "${parameterizedRoute}" was not passed a response object. Will not instrument.`,
- );
- return wrappingTarget.apply(thisArg, args);
- }
- // We're now auto-wrapping API route handlers using `wrapApiHandlerWithSentry` (which uses `withSentry` under the hood), but
- // users still may have their routes manually wrapped with `withSentry`. This check makes `sentryWrappedHandler`
- // idempotent so that those cases don't break anything.
- if (req.__withSentry_applied__) {
- return wrappingTarget.apply(thisArg, args);
- }
- req.__withSentry_applied__ = true;
- core.addTracingExtensions();
- return core.runWithAsyncContext(() => {
- return core.continueTrace(
- {
- sentryTrace: req.headers && utils.isString(req.headers['sentry-trace']) ? req.headers['sentry-trace'] : undefined,
- baggage: _optionalChain([req, 'access', _ => _.headers, 'optionalAccess', _2 => _2.baggage]),
- },
- () => {
- // prefer the parameterized route, if we have it (which we will if we've auto-wrapped the route handler)
- let reqPath = parameterizedRoute;
- // If not, fake it by just replacing parameter values with their names, hoping that none of them match either
- // each other or any hard-coded parts of the path
- if (!reqPath) {
- const url = `${req.url}`;
- // pull off query string, if any
- reqPath = utils.stripUrlQueryAndFragment(url);
- // Replace with placeholder
- if (req.query) {
- for (const [key, value] of Object.entries(req.query)) {
- reqPath = reqPath.replace(`${value}`, `[${key}]`);
- }
- }
- }
- const reqMethod = `${(req.method || 'GET').toUpperCase()} `;
- core.getCurrentScope().setSDKProcessingMetadata({ request: req });
- return core.startSpanManual(
- {
- name: `${reqMethod}${reqPath}`,
- op: 'http.server',
- attributes: {
- [core.SEMANTIC_ATTRIBUTE_SENTRY_SOURCE]: 'route',
- [core.SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN]: 'auto.http.nextjs',
- },
- metadata: {
- request: req,
- },
- },
- async span => {
- // eslint-disable-next-line @typescript-eslint/unbound-method
- res.end = new Proxy(res.end, {
- apply(target, thisArg, argArray) {
- if (span) {
- core.setHttpStatus(span, res.statusCode);
- span.end();
- }
- if (platformSupportsStreaming.platformSupportsStreaming() && !wrappingTarget.__sentry_test_doesnt_support_streaming__) {
- target.apply(thisArg, argArray);
- } else {
- // flushQueue will not reject
- // eslint-disable-next-line @typescript-eslint/no-floating-promises
- responseEnd.flushQueue().then(() => {
- target.apply(thisArg, argArray);
- });
- }
- },
- });
- try {
- const handlerResult = await wrappingTarget.apply(thisArg, args);
- if (
- process.env.NODE_ENV === 'development' &&
- !process.env.SENTRY_IGNORE_API_RESOLUTION_ERROR &&
- !res.finished
- // TODO(v8): Remove this warning?
- // This can only happen (not always) when the user is using `withSentry` manually, which we're deprecating.
- // Warning suppression on Next.JS is only necessary in that case.
- ) {
- utils.consoleSandbox(() => {
- // eslint-disable-next-line no-console
- console.warn(
- '[sentry] If Next.js logs a warning "API resolved without sending a response", it\'s a false positive, which may happen when you use `withSentry` manually to wrap your routes. To suppress this warning, set `SENTRY_IGNORE_API_RESOLUTION_ERROR` to 1 in your env. To suppress the nextjs warning, use the `externalResolver` API route option (see https://nextjs.org/docs/api-routes/api-middlewares#custom-config for details).',
- );
- });
- }
- return handlerResult;
- } catch (e) {
- // In case we have a primitive, wrap it in the equivalent wrapper class (string -> String, etc.) so that we can
- // store a seen flag on it. (Because of the one-way-on-Vercel-one-way-off-of-Vercel approach we've been forced
- // to take, it can happen that the same thrown object gets caught in two different ways, and flagging it is a
- // way to prevent it from actually being reported twice.)
- const objectifiedErr = utils.objectify(e);
- core.captureException(objectifiedErr, {
- mechanism: {
- type: 'instrument',
- handled: false,
- data: {
- wrapped_handler: wrappingTarget.name,
- function: 'withSentry',
- },
- },
- });
- // Because we're going to finish and send the transaction before passing the error onto nextjs, it won't yet
- // have had a chance to set the status to 500, so unless we do it ourselves now, we'll incorrectly report that
- // the transaction was error-free
- res.statusCode = 500;
- res.statusMessage = 'Internal Server Error';
- if (span) {
- core.setHttpStatus(span, res.statusCode);
- span.end();
- }
- // Make sure we have a chance to finish the transaction and flush events to Sentry before the handler errors
- // out. (Apps which are deployed on Vercel run their API routes in lambdas, and those lambdas will shut down the
- // moment they detect an error, so it's important to get this done before rethrowing the error. Apps not
- // deployed serverlessly will run into this cleanup code again in `res.end(), but the transaction will already
- // be finished and the queue will already be empty, so effectively it'll just no-op.)
- if (platformSupportsStreaming.platformSupportsStreaming() && !wrappingTarget.__sentry_test_doesnt_support_streaming__) {
- await responseEnd.flushQueue();
- }
- // We rethrow here so that nextjs can do with the error whatever it would normally do. (Sometimes "whatever it
- // would normally do" is to allow the error to bubble up to the global handlers - another reason we need to mark
- // the error as already having been captured.)
- throw objectifiedErr;
- }
- },
- );
- },
- );
- });
- },
- });
- }
- exports.withSentry = withSentry;
- exports.withSentryAPI = withSentryAPI;
- exports.wrapApiHandlerWithSentry = wrapApiHandlerWithSentry;
- //# sourceMappingURL=wrapApiHandlerWithSentry.js.map
|