import type { NextConfig } from 'next'; import { existsSync } from 'node:fs'; import path from 'node:path'; // Monorepo: the single `.env` lives at the repository root; Next only reads the app directory. for (const candidate of [path.resolve(process.cwd(), '../../.env'), path.resolve(process.cwd(), '.env')]) { if (existsSync(candidate)) { try { process.loadEnvFile(candidate); } catch { /* ignore malformed env */ } } } const nextConfig: NextConfig = { reactStrictMode: true, poweredByHeader: false, agentRules: false, // Workspace packages are consumed as TypeScript sources. transpilePackages: ['@rareindex/shared', '@rareindex/database', '@rareindex/taxonomy', '@rareindex/connectors', '@rareindex/ai', '@rareindex/notify'], serverExternalPackages: ['postgres', 'pino', 'cheerio', 'pg-boss', '@anthropic-ai/sdk', 'openai'], // Public API: /api/v1/* is proxied to the Fastify service (apps/api) so one public host serves web + API. async rewrites() { const api = process.env.RI_API_URL ?? `http://127.0.0.1:${process.env.API_PORT ?? 8211}`; return [{ source: '/api/v1/:path*', destination: `${api}/v1/:path*` }]; }, images: { remotePatterns: [{ protocol: 'https', hostname: '**' }], formats: ['image/avif', 'image/webp'], }, // Static pages query a large database; give the prerender workers room (default 60 s). staticPageGenerationTimeout: 240, experimental: { optimizePackageImports: ['lucide-react'], // The image cache core is shared with the worker (workers/image-processing/core.ts) via a relative re-export. externalDir: true, // Workspace packages use NodeNext-style `./file.js` imports that resolve to .ts sources. // Turbopack does not rewrite these outside the app root, so the app is built with webpack // (`next dev/build --webpack`) where extensionAlias handles the mapping. extensionAlias: { '.js': ['.ts', '.tsx', '.js'], '.mjs': ['.mts', '.mjs'] }, }, outputFileTracingRoot: path.resolve(__dirname, '../..'), async headers() { return [ { source: '/(.*)', headers: [ { key: 'X-Content-Type-Options', value: 'nosniff' }, { key: 'Referrer-Policy', value: 'strict-origin-when-cross-origin' }, { key: 'X-Frame-Options', value: 'SAMEORIGIN' }, { key: 'Permissions-Policy', value: 'camera=(self), microphone=(), geolocation=()' }, ], }, ]; }, }; export default nextConfig;