import type { NextConfig } from "next"; // eslint-disable-next-line @typescript-eslint/no-require-imports const { withPayload } = require("@payloadcms/next/withPayload"); import env from "./env.config"; const INTERNAL_PACKAGES = [ "@turbostarter/analytics-web", "@turbostarter/api", "@turbostarter/auth", "@turbostarter/billing", "@turbostarter/cms", "@turbostarter/email", "@turbostarter/db", "@turbostarter/i18n", "@turbostarter/monitoring-web", "@turbostarter/shared", "@turbostarter/storage", "@turbostarter/ui", "@turbostarter/ui-web", ]; // Security headers for production const securityHeaders = [ { key: "X-DNS-Prefetch-Control", value: "on", }, { key: "Strict-Transport-Security", value: "max-age=63072000; includeSubDomains; preload", }, { key: "X-Frame-Options", value: "SAMEORIGIN", }, { key: "X-Content-Type-Options", value: "nosniff", }, { key: "X-XSS-Protection", value: "1; mode=block", }, { key: "Referrer-Policy", value: "strict-origin-when-cross-origin", }, { key: "Permissions-Policy", value: "camera=(), microphone=(), geolocation=(), interest-cohort=()", }, // Content-Security-Policy - configured for development flexibility // In production, tighten 'unsafe-inline' and 'unsafe-eval' as needed { key: "Content-Security-Policy", value: [ "default-src 'self'", "script-src 'self' 'unsafe-inline' 'unsafe-eval' https://js.stripe.com https://challenges.cloudflare.com", "style-src 'self' 'unsafe-inline'", "img-src 'self' data: blob: https: http:", "font-src 'self' data:", "connect-src 'self' https://api.stripe.com https://*.sentry.io wss:", "frame-src 'self' https://js.stripe.com https://hooks.stripe.com https://challenges.cloudflare.com", "object-src 'none'", "base-uri 'self'", "form-action 'self'", "frame-ancestors 'self'", "upgrade-insecure-requests", ].join("; "), }, ]; const config: NextConfig = { reactStrictMode: true, output: "standalone", // TEMPORARY: Hono RPC + TanStack Query type inference whack-a-mole blocking production deploy. // Ship now, fix types post-launch as dedicated tech-debt sprint. typescript: { ignoreBuildErrors: true, }, serverExternalPackages: [ "better-sqlite3", "@mapbox/node-pre-gyp", ], images: { remotePatterns: [ { hostname: "images.unsplash.com", }, ], }, /** Enables hot reloading for local packages without a build step */ transpilePackages: INTERNAL_PACKAGES, experimental: { optimizePackageImports: INTERNAL_PACKAGES, }, // Apply security headers only in production // CSP with upgrade-insecure-requests breaks Next.js client-side navigation in dev async headers() { if (process.env.NODE_ENV !== "production") { return []; } return [ { // Apply to all routes source: "/:path*", headers: securityHeaders, }, ]; }, }; const withBundleAnalyzer = require("@next/bundle-analyzer")({ enabled: env.ANALYZE, }); export default withPayload(withBundleAnalyzer(config));