UNPKG

@sentry/nextjs

Version:
672 lines (597 loc) • 27.6 kB
import { isThenable, parseSemver } from '@sentry/core'; import { getSentryRelease } from '@sentry/node'; import * as childProcess from 'child_process'; import * as fs from 'fs'; import * as path from 'path'; import { handleRunAfterProductionCompile } from './handleRunAfterProductionCompile.js'; import { createRouteManifest } from './manifest/createRouteManifest.js'; import { constructTurbopackConfig } from './turbopack/constructTurbopackConfig.js'; import { getNextjsVersion, requiresInstrumentationHook, detectActiveBundler, supportsProductionCompileHook } from './util.js'; import { constructWebpackConfigFunction } from './webpack.js'; /* eslint-disable max-lines */ /* eslint-disable complexity */ let showedExportModeTunnelWarning = false; let showedExperimentalBuildModeWarning = false; // Packages we auto-instrument need to be external for instrumentation to work // Next.js externalizes some packages by default, see: https://nextjs.org/docs/app/api-reference/config/next-config-js/serverExternalPackages // Others we need to add ourselves // // NOTE: 'ai' (Vercel AI SDK) is intentionally NOT included in this list. // When externalized, Next.js doesn't properly handle the package's conditional exports, // specifically the "react-server" export condition. This causes client-side code to be // loaded in server components instead of the appropriate server-side functions. const DEFAULT_SERVER_EXTERNAL_PACKAGES = [ 'amqplib', 'connect', 'dataloader', 'express', 'generic-pool', 'graphql', '@hapi/hapi', 'ioredis', 'kafkajs', 'koa', 'lru-memoizer', 'mongodb', 'mongoose', 'mysql', 'mysql2', 'knex', 'pg', 'pg-pool', '@node-redis/client', '@redis/client', 'redis', 'tedious', ]; /** * Modifies the passed in Next.js configuration with automatic build-time instrumentation and source map upload. * * @param nextConfig A Next.js configuration object, as usually exported in `next.config.js` or `next.config.mjs`. * @param sentryBuildOptions Additional options to configure instrumentation and * @returns The modified config to be exported */ function withSentryConfig(nextConfig, sentryBuildOptions = {}) { const castNextConfig = (nextConfig ) || {}; if (typeof castNextConfig === 'function') { return function ( ...webpackConfigFunctionArgs) { const maybePromiseNextConfig = castNextConfig.apply( this, webpackConfigFunctionArgs, ); if (isThenable(maybePromiseNextConfig)) { return maybePromiseNextConfig.then(promiseResultNextConfig => { return getFinalConfigObject(promiseResultNextConfig, sentryBuildOptions); }); } return getFinalConfigObject(maybePromiseNextConfig, sentryBuildOptions); } ; } else { return getFinalConfigObject(castNextConfig, sentryBuildOptions) ; } } /** * Generates a random tunnel route path that's less likely to be blocked by ad-blockers */ function generateRandomTunnelRoute() { // Generate a random 8-character alphanumeric string // eslint-disable-next-line @sentry-internal/sdk/no-unsafe-random-apis const randomString = Math.random().toString(36).substring(2, 10); return `/${randomString}`; } /** * Migrates deprecated top-level webpack options to the new `webpack.*` path for backward compatibility. * The new path takes precedence over deprecated options. This mutates the userSentryOptions object. */ function migrateDeprecatedWebpackOptions(userSentryOptions) { // Initialize webpack options if not present userSentryOptions.webpack = userSentryOptions.webpack || {}; const webpack = userSentryOptions.webpack; const withDeprecatedFallback = ( newValue, deprecatedValue, message, ) => { if (deprecatedValue !== undefined) { // eslint-disable-next-line no-console console.warn(message); } return newValue ?? deprecatedValue; }; const deprecatedMessage = (deprecatedPath, newPath) => { const message = `[@sentry/nextjs] DEPRECATION WARNING: ${deprecatedPath} is deprecated and will be removed in a future version. Use ${newPath} instead.`; // In Turbopack builds, webpack configuration is not applied, so webpack-scoped options won't have any effect. if (detectActiveBundler() === 'turbopack' && newPath.startsWith('webpack.')) { return `${message} (Not supported with Turbopack.)`; } return message; }; /* eslint-disable deprecation/deprecation */ // Migrate each deprecated option to the new path, but only if the new path isn't already set webpack.autoInstrumentServerFunctions = withDeprecatedFallback( webpack.autoInstrumentServerFunctions, userSentryOptions.autoInstrumentServerFunctions, deprecatedMessage('autoInstrumentServerFunctions', 'webpack.autoInstrumentServerFunctions'), ); webpack.autoInstrumentMiddleware = withDeprecatedFallback( webpack.autoInstrumentMiddleware, userSentryOptions.autoInstrumentMiddleware, deprecatedMessage('autoInstrumentMiddleware', 'webpack.autoInstrumentMiddleware'), ); webpack.autoInstrumentAppDirectory = withDeprecatedFallback( webpack.autoInstrumentAppDirectory, userSentryOptions.autoInstrumentAppDirectory, deprecatedMessage('autoInstrumentAppDirectory', 'webpack.autoInstrumentAppDirectory'), ); webpack.excludeServerRoutes = withDeprecatedFallback( webpack.excludeServerRoutes, userSentryOptions.excludeServerRoutes, deprecatedMessage('excludeServerRoutes', 'webpack.excludeServerRoutes'), ); webpack.unstable_sentryWebpackPluginOptions = withDeprecatedFallback( webpack.unstable_sentryWebpackPluginOptions, userSentryOptions.unstable_sentryWebpackPluginOptions, deprecatedMessage('unstable_sentryWebpackPluginOptions', 'webpack.unstable_sentryWebpackPluginOptions'), ); webpack.disableSentryConfig = withDeprecatedFallback( webpack.disableSentryConfig, userSentryOptions.disableSentryWebpackConfig, deprecatedMessage('disableSentryWebpackConfig', 'webpack.disableSentryConfig'), ); // Handle treeshake.removeDebugLogging specially since it's nested if (userSentryOptions.disableLogger !== undefined) { webpack.treeshake = webpack.treeshake || {}; webpack.treeshake.removeDebugLogging = withDeprecatedFallback( webpack.treeshake.removeDebugLogging, userSentryOptions.disableLogger, deprecatedMessage('disableLogger', 'webpack.treeshake.removeDebugLogging'), ); } webpack.automaticVercelMonitors = withDeprecatedFallback( webpack.automaticVercelMonitors, userSentryOptions.automaticVercelMonitors, deprecatedMessage('automaticVercelMonitors', 'webpack.automaticVercelMonitors'), ); webpack.reactComponentAnnotation = withDeprecatedFallback( webpack.reactComponentAnnotation, userSentryOptions.reactComponentAnnotation, deprecatedMessage('reactComponentAnnotation', 'webpack.reactComponentAnnotation'), ); } // Modify the materialized object form of the user's next config by deleting the `sentry` property and wrapping the // `webpack` property function getFinalConfigObject( incomingUserNextConfigObject, userSentryOptions, ) { // Migrate deprecated webpack options to new webpack path for backward compatibility migrateDeprecatedWebpackOptions(userSentryOptions); // Only determine a release name if release creation is not explicitly disabled // This prevents injection of Git commit hashes that break build determinism const shouldCreateRelease = userSentryOptions.release?.create !== false; const releaseName = shouldCreateRelease ? (userSentryOptions.release?.name ?? getSentryRelease() ?? getGitRevision()) : userSentryOptions.release?.name; if (userSentryOptions?.tunnelRoute) { if (incomingUserNextConfigObject.output === 'export') { if (!showedExportModeTunnelWarning) { showedExportModeTunnelWarning = true; // eslint-disable-next-line no-console console.warn( '[@sentry/nextjs] The Sentry Next.js SDK `tunnelRoute` option will not work in combination with Next.js static exports. The `tunnelRoute` option uses server-side features that cannot be accessed in export mode. If you still want to tunnel Sentry events, set up your own tunnel: https://docs.sentry.io/platforms/javascript/troubleshooting/#using-the-tunnel-option', ); } } else { // Update the global options object to use the resolved value everywhere const resolvedTunnelRoute = resolveTunnelRoute(userSentryOptions.tunnelRoute); userSentryOptions.tunnelRoute = resolvedTunnelRoute || undefined; setUpTunnelRewriteRules(incomingUserNextConfigObject, resolvedTunnelRoute); } } if (process.argv.includes('--experimental-build-mode')) { if (!showedExperimentalBuildModeWarning) { showedExperimentalBuildModeWarning = true; // eslint-disable-next-line no-console console.warn( '[@sentry/nextjs] The Sentry Next.js SDK does not currently fully support next build --experimental-build-mode', ); } if (process.argv.includes('generate')) { // Next.js v15.3.0-canary.1 splits the experimental build into two phases: // 1. compile: Code compilation // 2. generate: Environment variable inlining and prerendering (We don't instrument this phase, we inline in the compile phase) // // We assume a single "full" build and reruns Webpack instrumentation in both phases. // During the generate step it collides with Next.js's inliner // producing malformed JS and build failures. // We skip Sentry processing during generate to avoid this issue. return incomingUserNextConfigObject; } } let routeManifest; if (!userSentryOptions.disableManifestInjection) { routeManifest = createRouteManifest({ basePath: incomingUserNextConfigObject.basePath, }); } setUpBuildTimeVariables(incomingUserNextConfigObject, userSentryOptions, releaseName); const nextJsVersion = getNextjsVersion(); // Add the `clientTraceMetadata` experimental option based on Next.js version. The option got introduced in Next.js version 15.0.0 (actually 14.3.0-canary.64). // Adding the option on lower versions will cause Next.js to print nasty warnings we wouldn't confront our users with. if (nextJsVersion) { const { major, minor } = parseSemver(nextJsVersion); if (major !== undefined && minor !== undefined && (major >= 15 || (major === 14 && minor >= 3))) { incomingUserNextConfigObject.experimental = incomingUserNextConfigObject.experimental || {}; incomingUserNextConfigObject.experimental.clientTraceMetadata = [ 'baggage', 'sentry-trace', ...(incomingUserNextConfigObject.experimental?.clientTraceMetadata || []), ]; } } else { // eslint-disable-next-line no-console console.log( "[@sentry/nextjs] The Sentry SDK was not able to determine your Next.js version. If you are using Next.js version 15 or greater, please add `experimental.clientTraceMetadata: ['sentry-trace', 'baggage']` to your Next.js config to enable pageload tracing for App Router.", ); } // From Next.js version (15.0.0-canary.124) onwards, Next.js does no longer require the `experimental.instrumentationHook` option and will // print a warning when it is set, so we need to conditionally provide it for lower versions. if (nextJsVersion && requiresInstrumentationHook(nextJsVersion)) { if (incomingUserNextConfigObject.experimental?.instrumentationHook === false) { // eslint-disable-next-line no-console console.warn( '[@sentry/nextjs] You turned off the `experimental.instrumentationHook` option. Note that Sentry will not be initialized if you did not set it up inside `instrumentation.(js|ts)`.', ); } incomingUserNextConfigObject.experimental = { instrumentationHook: true, ...incomingUserNextConfigObject.experimental, }; } else if (!nextJsVersion) { // If we cannot detect a Next.js version for whatever reason, the sensible default is to set the `experimental.instrumentationHook`, even though it may create a warning. if ( incomingUserNextConfigObject.experimental && 'instrumentationHook' in incomingUserNextConfigObject.experimental ) { if (incomingUserNextConfigObject.experimental.instrumentationHook === false) { // eslint-disable-next-line no-console console.warn( '[@sentry/nextjs] You set `experimental.instrumentationHook` to `false`. If you are using Next.js version 15 or greater, you can remove that option. If you are using Next.js version 14 or lower, you need to set `experimental.instrumentationHook` in your `next.config.(js|mjs)` to `true` for the SDK to be properly initialized in combination with `instrumentation.(js|ts)`.', ); } } else { // eslint-disable-next-line no-console console.log( "[@sentry/nextjs] The Sentry SDK was not able to determine your Next.js version. If you are using Next.js version 15 or greater, Next.js will probably show you a warning about the `experimental.instrumentationHook` being set. To silence Next.js' warning, explicitly set the `experimental.instrumentationHook` option in your `next.config.(js|mjs|ts)` to `undefined`. If you are on Next.js version 14 or lower, you can silence this particular warning by explicitly setting the `experimental.instrumentationHook` option in your `next.config.(js|mjs)` to `true`.", ); incomingUserNextConfigObject.experimental = { instrumentationHook: true, ...incomingUserNextConfigObject.experimental, }; } } // We wanna check whether the user added a `onRouterTransitionStart` handler to their client instrumentation file. const instrumentationClientFileContents = getInstrumentationClientFileContents(); if ( instrumentationClientFileContents !== undefined && !instrumentationClientFileContents.includes('onRouterTransitionStart') && !userSentryOptions.suppressOnRouterTransitionStartWarning ) { // eslint-disable-next-line no-console console.warn( '[@sentry/nextjs] ACTION REQUIRED: To instrument navigations, the Sentry SDK requires you to export an `onRouterTransitionStart` hook from your `instrumentation-client.(js|ts)` file. You can do so by adding `export const onRouterTransitionStart = Sentry.captureRouterTransitionStart;` to the file.', ); } let nextMajor; if (nextJsVersion) { const { major } = parseSemver(nextJsVersion); nextMajor = major; } const activeBundler = detectActiveBundler(); const isTurbopack = activeBundler === 'turbopack'; const isWebpack = activeBundler === 'webpack'; const isTurbopackSupported = supportsProductionCompileHook(nextJsVersion ?? ''); // Warn if using turbopack with an unsupported Next.js version if (!isTurbopackSupported && isTurbopack) { // eslint-disable-next-line no-console console.warn( `[@sentry/nextjs] WARNING: You are using the Sentry SDK with Turbopack. The Sentry SDK is compatible with Turbopack on Next.js version 15.4.1 or later. You are currently on ${nextJsVersion}. Please upgrade to a newer Next.js version to use the Sentry SDK with Turbopack.`, ); } // Webpack case - warn if trying to use runAfterProductionCompile hook with unsupported Next.js version if ( userSentryOptions.useRunAfterProductionCompileHook && !supportsProductionCompileHook(nextJsVersion ?? '') && isWebpack ) { // eslint-disable-next-line no-console console.warn( '[@sentry/nextjs] The configured `useRunAfterProductionCompileHook` option is not compatible with your current Next.js version. This option is only supported on Next.js version 15.4.1 or later. Will not run source map and release management logic.', ); } let turboPackConfig; if (isTurbopack) { turboPackConfig = constructTurbopackConfig({ userNextConfig: incomingUserNextConfigObject, userSentryOptions, routeManifest, nextJsVersion, }); } // If not explicitly set, turbopack uses the runAfterProductionCompile hook (as there are no alternatives), webpack does not. const shouldUseRunAfterProductionCompileHook = userSentryOptions?.useRunAfterProductionCompileHook ?? (isTurbopack ? true : false); if (shouldUseRunAfterProductionCompileHook && supportsProductionCompileHook(nextJsVersion ?? '')) { if (incomingUserNextConfigObject?.compiler?.runAfterProductionCompile === undefined) { incomingUserNextConfigObject.compiler ??= {}; incomingUserNextConfigObject.compiler.runAfterProductionCompile = async ({ distDir }) => { await handleRunAfterProductionCompile( { releaseName, distDir, buildTool: isTurbopack ? 'turbopack' : 'webpack', usesNativeDebugIds: isTurbopack ? turboPackConfig?.debugIds : undefined, }, userSentryOptions, ); }; } else if (typeof incomingUserNextConfigObject.compiler.runAfterProductionCompile === 'function') { incomingUserNextConfigObject.compiler.runAfterProductionCompile = new Proxy( incomingUserNextConfigObject.compiler.runAfterProductionCompile, { async apply(target, thisArg, argArray) { const { distDir } = argArray[0] ?? { distDir: '.next' }; await target.apply(thisArg, argArray); await handleRunAfterProductionCompile( { releaseName, distDir, buildTool: isTurbopack ? 'turbopack' : 'webpack', usesNativeDebugIds: isTurbopack ? turboPackConfig?.debugIds : undefined, }, userSentryOptions, ); }, }, ); } else { // eslint-disable-next-line no-console console.warn( '[@sentry/nextjs] The configured `compiler.runAfterProductionCompile` option is not a function. Will not run source map and release management logic.', ); } } // Enable source maps for turbopack builds if (isTurbopackSupported && isTurbopack && !userSentryOptions.sourcemaps?.disable) { // Only set if not already configured by user if (incomingUserNextConfigObject.productionBrowserSourceMaps === undefined) { if (userSentryOptions.debug) { // eslint-disable-next-line no-console console.log('[@sentry/nextjs] Automatically enabling browser source map generation for turbopack build.'); } incomingUserNextConfigObject.productionBrowserSourceMaps = true; // Enable source map deletion if not explicitly disabled if (userSentryOptions.sourcemaps?.deleteSourcemapsAfterUpload === undefined) { if (userSentryOptions.debug) { // eslint-disable-next-line no-console console.warn( '[@sentry/nextjs] Source maps will be automatically deleted after being uploaded to Sentry. If you want to keep the source maps, set the `sourcemaps.deleteSourcemapsAfterUpload` option to false in `withSentryConfig()`. If you do not want to generate and upload sourcemaps at all, set the `sourcemaps.disable` option to true.', ); } userSentryOptions.sourcemaps = { ...userSentryOptions.sourcemaps, deleteSourcemapsAfterUpload: true, }; } } } return { ...incomingUserNextConfigObject, ...(nextMajor && nextMajor >= 15 ? { serverExternalPackages: [ ...(incomingUserNextConfigObject.serverExternalPackages || []), ...DEFAULT_SERVER_EXTERNAL_PACKAGES, ], } : { experimental: { ...incomingUserNextConfigObject.experimental, serverComponentsExternalPackages: [ ...(incomingUserNextConfigObject.experimental?.serverComponentsExternalPackages || []), ...DEFAULT_SERVER_EXTERNAL_PACKAGES, ], }, }), ...(isWebpack && !userSentryOptions.webpack?.disableSentryConfig ? { webpack: constructWebpackConfigFunction({ userNextConfig: incomingUserNextConfigObject, userSentryOptions, releaseName, routeManifest, nextJsVersion, useRunAfterProductionCompileHook: shouldUseRunAfterProductionCompileHook, }), } : {}), ...(isTurbopackSupported && isTurbopack ? { turbopack: turboPackConfig, } : {}), }; } /** * Injects rewrite rules into the Next.js config provided by the user to tunnel * requests from the `tunnelPath` to Sentry. * * See https://nextjs.org/docs/api-reference/next.config.js/rewrites. */ function setUpTunnelRewriteRules(userNextConfig, tunnelPath) { const originalRewrites = userNextConfig.rewrites; // Allow overriding the tunnel destination for E2E tests via environment variable const destinationOverride = process.env._SENTRY_TUNNEL_DESTINATION_OVERRIDE; // Make sure destinations are statically defined at build time const destination = destinationOverride || 'https://o:orgid.ingest.sentry.io/api/:projectid/envelope/?hsts=0'; const destinationWithRegion = destinationOverride || 'https://o:orgid.ingest.:region.sentry.io/api/:projectid/envelope/?hsts=0'; // This function doesn't take any arguments at the time of writing but we future-proof // here in case Next.js ever decides to pass some userNextConfig.rewrites = async (...args) => { const tunnelRouteRewrite = { // Matched rewrite routes will look like the following: `[tunnelPath]?o=[orgid]&p=[projectid]` // Nextjs will automatically convert `source` into a regex for us source: `${tunnelPath}(/?)`, has: [ { type: 'query', key: 'o', // short for orgId - we keep it short so matching is harder for ad-blockers value: '(?<orgid>\\d*)', }, { type: 'query', key: 'p', // short for projectId - we keep it short so matching is harder for ad-blockers value: '(?<projectid>\\d*)', }, ], destination, }; const tunnelRouteRewriteWithRegion = { // Matched rewrite routes will look like the following: `[tunnelPath]?o=[orgid]&p=[projectid]?r=[region]` // Nextjs will automatically convert `source` into a regex for us source: `${tunnelPath}(/?)`, has: [ { type: 'query', key: 'o', // short for orgId - we keep it short so matching is harder for ad-blockers value: '(?<orgid>\\d*)', }, { type: 'query', key: 'p', // short for projectId - we keep it short so matching is harder for ad-blockers value: '(?<projectid>\\d*)', }, { type: 'query', key: 'r', // short for region - we keep it short so matching is harder for ad-blockers value: '(?<region>[a-z]{2})', }, ], destination: destinationWithRegion, }; // Order of these is important, they get applied first to last. const newRewrites = [tunnelRouteRewriteWithRegion, tunnelRouteRewrite]; if (typeof originalRewrites !== 'function') { return newRewrites; } // @ts-expect-error Expected 0 arguments but got 1 - this is from the future-proofing mentioned above, so we don't care about it const originalRewritesResult = await originalRewrites(...args); if (Array.isArray(originalRewritesResult)) { return [...newRewrites, ...originalRewritesResult]; } else { return { ...originalRewritesResult, beforeFiles: [...newRewrites, ...(originalRewritesResult.beforeFiles || [])], }; } }; } function setUpBuildTimeVariables( userNextConfig, userSentryOptions, releaseName, ) { const assetPrefix = userNextConfig.assetPrefix || userNextConfig.basePath || ''; const basePath = userNextConfig.basePath ?? ''; const rewritesTunnelPath = userSentryOptions.tunnelRoute !== undefined && userNextConfig.output !== 'export' && typeof userSentryOptions.tunnelRoute === 'string' ? `${basePath}${userSentryOptions.tunnelRoute}` : undefined; const buildTimeVariables = { // Make sure that if we have a windows path, the backslashes are interpreted as such (rather than as escape // characters) _sentryRewriteFramesDistDir: userNextConfig.distDir?.replace(/\\/g, '\\\\') || '.next', // Get the path part of `assetPrefix`, minus any trailing slash. (We use a placeholder for the origin if // `assetPrefix` doesn't include one. Since we only care about the path, it doesn't matter what it is.) _sentryRewriteFramesAssetPrefixPath: assetPrefix ? new URL(assetPrefix, 'http://dogs.are.great').pathname.replace(/\/$/, '') : '', }; if (userNextConfig.assetPrefix) { buildTimeVariables._assetsPrefix = userNextConfig.assetPrefix; } if (userSentryOptions._experimental?.thirdPartyOriginStackFrames) { buildTimeVariables._experimentalThirdPartyOriginStackFrames = 'true'; } if (rewritesTunnelPath) { buildTimeVariables._sentryRewritesTunnelPath = rewritesTunnelPath; } if (basePath) { buildTimeVariables._sentryBasePath = basePath; } if (userNextConfig.assetPrefix) { buildTimeVariables._sentryAssetPrefix = userNextConfig.assetPrefix; } if (userSentryOptions._experimental?.thirdPartyOriginStackFrames) { buildTimeVariables._experimentalThirdPartyOriginStackFrames = 'true'; } if (releaseName) { buildTimeVariables._sentryRelease = releaseName; } if (typeof userNextConfig.env === 'object') { userNextConfig.env = { ...buildTimeVariables, ...userNextConfig.env }; } else if (userNextConfig.env === undefined) { userNextConfig.env = buildTimeVariables; } } function getGitRevision() { let gitRevision; try { gitRevision = childProcess .execSync('git rev-parse HEAD', { stdio: ['ignore', 'pipe', 'ignore'] }) .toString() .trim(); } catch { // noop } return gitRevision; } function getInstrumentationClientFileContents() { const potentialInstrumentationClientFileLocations = [ ['src', 'instrumentation-client.ts'], ['src', 'instrumentation-client.js'], ['instrumentation-client.ts'], ['instrumentation-client.js'], ]; for (const pathSegments of potentialInstrumentationClientFileLocations) { try { return fs.readFileSync(path.join(process.cwd(), ...pathSegments), 'utf-8'); } catch { // noop } } } /** * Resolves the tunnel route based on the user's configuration and the environment. * @param tunnelRoute - The user-provided tunnel route option */ function resolveTunnelRoute(tunnelRoute) { if (process.env.__SENTRY_TUNNEL_ROUTE__) { // Reuse cached value from previous build (server/client) return process.env.__SENTRY_TUNNEL_ROUTE__; } const resolvedTunnelRoute = typeof tunnelRoute === 'string' ? tunnelRoute : generateRandomTunnelRoute(); // Cache for subsequent builds (only during build time) // Turbopack runs the config twice, so we need a shared context to avoid generating a new tunnel route for each build. // env works well here // https://linear.app/getsentry/issue/JS-549/adblock-plus-blocking-requests-to-sentry-and-monitoring-tunnel if (resolvedTunnelRoute) { process.env.__SENTRY_TUNNEL_ROUTE__ = resolvedTunnelRoute; } return resolvedTunnelRoute; } export { DEFAULT_SERVER_EXTERNAL_PACKAGES, withSentryConfig }; //# sourceMappingURL=withSentryConfig.js.map