studio/next.config.mjs (170 lines of code) (raw):

import withMarkdoc from "@markdoc/next.js"; import { withSentryConfig } from "@sentry/nextjs"; import pkg from "./package.json" with { type: "json" }; const isPreview = process.env.VERCEL_ENV === "preview"; // Allow it only for development once https://github.com/vercel/next.js/issues/23587 is fixed const allowUnsafeEval = true; // Report CSP violations to the console instead of blocking them const debugCSP = false; // Enable or disable the sentry integration const isSentryEnabled = process.env.NEXT_PUBLIC_SENTRY_ENABLED === "true"; const isSentryFeatureReplayEnabled = isSentryEnabled && process.env.NEXT_PUBLIC_SENTRY_REPLAY_ENABLED === "true"; const clientTracesSampleRate = parseFloat( process.env.NEXT_PUBLIC_SENTRY_CLIENT_TRACES_SAMPLE_RATE || "0", ); const serverSampleRate = parseFloat( process.env.SENTRY_SERVER_SAMPLE_RATE || "0", ); const isSentryTracesEnabled = clientTracesSampleRate > 0 || serverSampleRate > 0; const sentryDebugEnabled = process.env.SENTRY_DEBUG === "true"; const sentryOrganization = process.env.SENTRY_ORG || ""; const sentryProject = process.env.SENTRY_PROJECT || ""; const sentryAuthToken = process.env.SENTRY_AUTH_TOKEN || ""; if (isSentryEnabled) { if (sentryAuthToken === "") { throw Error( "SENTRY_AUTH_TOKEN please ensure it's set during build time when using sentry!", ); } if (sentryOrganization === "" || sentryProject === "") { throw Error( "SENTRY_ORG or SENTRY_PROJECT not set please check your environment variables!", ); } } // Content Security Policy (CSP) is a security standard that helps prevent cross-site scripting (XSS), // clickjacking, and other code injection attacks resulting from execution of malicious content // in the trusted web page context. // For more information see https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Security-Policy // Known provider content security policies: // For Stripe see https://docs.stripe.com/security/guide?csp=csp-js#content-security-policy // Vercel Preview Environment see https://vercel.com/docs/workflow-collaboration/comments/specialized-usage#using-a-content-security-policy // Entry session replay worker https://docs.sentry.io/platforms/javascript/session-replay/#content-security-policy-csp // Important: 'unsafe-eval' is only used in development mode, when script is injected by Next.js const lightweightCspHeader = ` style-src 'report-sample' 'self' 'unsafe-inline' data:; object-src 'none'; base-uri 'self'; font-src 'self' data:; frame-src 'self' https://js.stripe.com https://hooks.stripe.com ${ isPreview ? "https://vercel.live/ https://vercel.com" : "" }; img-src 'self' ${ isPreview ? "https://vercel.live/ https://vercel.com *.pusher.com/ data: blob:" : "" }; script-src 'report-sample' 'self' 'unsafe-inline' ${ allowUnsafeEval ? "'unsafe-eval'" : "" } https://*.wundergraph.com https://js.stripe.com https://maps.googleapis.com https://plausible.io https://wundergraph.com https://static.reo.dev ${ isPreview ? "https://vercel.live https://vercel.com" : "" }; manifest-src 'self'; media-src 'self'; worker-src 'self' ${isSentryFeatureReplayEnabled ? "blob:" : ""}; `; /** * We can't enforce connect directives yet because the studio can connect to any public router. * Leave it open for now. */ // const fullCspHeader = ` // default-src 'self' ${process.env.NEXT_PUBLIC_COSMO_STUDIO_URL} ${ // process.env.NEXT_PUBLIC_COSMO_CP_URL // }; // connect-src 'self' ${process.env.NEXT_PUBLIC_COSMO_STUDIO_URL} ${ // process.env.NEXT_PUBLIC_COSMO_CP_URL // } https://*.wundergraph.com wss://*.wundergraph.com https://plausible.io https://api.stripe.com https://maps.googleapis.com ${ // isPreview // ? "https://vercel.live https://vercel.com *.pusher.com *.pusherapp.com" // : "" // }; // ${lightweightCspHeader} // `; /** @type {import("next").NextConfig} */ const config = { output: "standalone", // This is done to reduce the production build size // see: https://docs.sentry.io/platforms/javascript/guides/nextjs/configuration/tree-shaking/ webpack: (config, { webpack }) => { config.plugins.push( new webpack.DefinePlugin({ __SENTRY_TRACING__: !isSentryTracesEnabled, __RRWEB_EXCLUDE_IFRAME__: !isSentryFeatureReplayEnabled, __RRWEB_EXCLUDE_SHADOW_DOM__: !isSentryFeatureReplayEnabled, __SENTRY_EXCLUDE_REPLAY_WORKER__: !isSentryFeatureReplayEnabled, }), ); config.resolve.alias = { ...config.resolve.alias, graphql$: "graphql/index.js", }; return config; }, pageExtensions: ["md", "mdoc", "js", "jsx", "ts", "tsx"], publicRuntimeConfig: { version: pkg.version, }, async headers() { return [ { source: "/(.*)", headers: [ { key: debugCSP ? "Content-Security-Policy-Report-Only" : "Content-Security-Policy", value: lightweightCspHeader.replace(/\n/g, ""), }, ], }, ]; }, }; const withOptionalSentryConfig = (org, project, config) => withSentryConfig(config, { // For all available options, see: // https://github.com/getsentry/sentry-webpack-plugin#options org: org, project: project, sourcemaps: { disable: false, deleteSourcemapsAfterUpload: true, }, telemetry: false, // Only print logs for uploading source maps in CI silent: !process.env.CI, // For all available options, see: // https://docs.sentry.io/platforms/javascript/guides/nextjs/manual-setup/ // Upload a larger set of source maps for prettier stack traces (increases build time) widenClientFileUpload: true, // Uncomment to route browser requests to Sentry through a Next.js rewrite to circumvent ad-blockers. // This can increase your server load as well as your hosting bill. // Note: Check that the configured route will not match with your Next.js middleware, otherwise reporting of client- // side errors will fail. // tunnelRoute: "/monitoring", // Hides source maps from generated client bundles hideSourceMaps: true, reactComponentAnnotation: { enabled: true, }, // Automatically tree-shake Sentry logger statements to reduce bundle size disableLogger: !sentryDebugEnabled, // Enables automatic instrumentation of Vercel Cron Monitors. (Does not yet work with App Router route handlers.) // See the following for more information: // https://docs.sentry.io/product/crons/ // https://vercel.com/docs/cron-jobs automaticVercelMonitors: false, }); const withOptionalFeatures = (config) => { if (isSentryEnabled) { config = withOptionalSentryConfig( sentryOrganization, sentryProject, config, ); } return config; }; export default withOptionalFeatures(withMarkdoc({ mode: "static" })(config));