import type { NextConfig } from "next"; import { HTML_LIMITED_BOT_UA_RE } from "next/dist/shared/lib/router/utils/html-bots"; // Bağımlılıksız veri dosyası; config uygulama paketinden önce yüklendiği için // göreli yolla alınır (Docker builder'da src/ build'den önce kopyalanır). import { kapananYonlendirmeleri } from "./src/lib/kapanan-sluglar"; // Dockerfile builder katmanı BUILD_KAYNAK_KISITLI=1 verir: imaj build'i prod // VPS'te çalışan uygulamayla aynı makinede koştuğu için bellek/paralellik kısılır. // Lokal build ve dev bu kısıtlardan etkilenmez. const kaynakKisitli = process.env.BUILD_KAYNAK_KISITLI === "1"; // Arama/AI botları JS çalıştırmaz. Next, metadata'sı istek anına kalan // sayfalarda /canonical/robots etiketlerini <body>'ye akıtır; yalnızca // bu listedeki user-agent'lara <head> içinde, bloklayarak verir. Varsayılan // listede AI botları yok. Şu an herkese açık sayfaların hepsi prerender // edildiği için etkisi yok; ileride dinamik bir sayfa eklenirse sigortadır. // `htmlLimitedBots` varsayılanı EZDİĞİ için Next'in kendi listesi de eklenir. const AI_BOTLARI = "GPTBot|OAI-SearchBot|ChatGPT-User|ClaudeBot|Claude-User|Claude-SearchBot|PerplexityBot|Perplexity-User|meta-externalagent|meta-externalfetcher|meta-webindexer|Amazonbot|Amzn-SearchBot|Amzn-User|DuckAssistBot|MistralAI-User|CCBot|Bytespider"; const nextConfig: NextConfig = { htmlLimitedBots: new RegExp( `${HTML_LIMITED_BOT_UA_RE.source}|${AI_BOTLARI}`, "i", ), // PPR: statik kabuk anında servis edilir, dinamik parçalar (UserNav vb.) // Suspense sınırlarının içinde akar. Bkz. docs/cacheComponents.md. cacheComponents: true, output: "standalone", // Server-only paketler bundle dışında tutulur: Turbopack bunları derlemez // (build belleği ciddi düşer), runtime'da native require ile yüklenir ve // standalone trace'i imaja kopyalar. (Tarihçe: @google/genai'nin sürüklediği // bağımlılık zinciri VPS'te build OOM'unu tetiklemişti; AI katmanı artık // yalnızca openai paketiyle OpenRouter'a gidiyor.) serverExternalPackages: [ "better-sqlite3", "iyzipay", "@libsql/client", "openai", "resend", ], // iyzipay lib/'ini runtime'da readdirSync ile dinamik yükler; nft trace bunu // göremediği için standalone'a yalnızca Iyzipay.js düşer ve initialize // "ENOENT ... lib/resources" ile patlar. Tüm lib/ zorla imaja alınır. outputFileTracingIncludes: { "/*": ["node_modules/.pnpm/iyzipay@*/node_modules/iyzipay/lib/**"], }, experimental: { // radix-ui kök barrel'ı 36 alt paketi çeker ve Next'in varsayılan // optimize listesinde yoktur; named import'lar doğrudan alt modüle // çevrilir (dev boot / build modül grafiği küçülür). optimizePackageImports: ["radix-ui"], ...(kaynakKisitli ? { // Turbopack'ın Rust tarafı NODE_OPTIONS heap sınırını görmez; bellek // hedefi byte cinsinden ayrıca verilir (1 GB). 1.5 GB hedef + Node // heap'i, ~1.5 GB boş belleği olan VPS'te derleme fazında OOM-kill // üretiyordu (lokalde --memory=1536m ile reprodüke edildi). turbopackMemoryLimit: 1_073_741_824, // Statik üretim: varsayılan 4 worker × 8 eşzamanlı sayfa yerine tek // worker × 4 sayfa. Her worker ayrı bir Node süreci/heap'idir. cpus: 1, staticGenerationMaxConcurrency: 4, } : {}), }, ...(kaynakKisitli ? { // cacheComponents açıkken prerender fazında source map üretimi varsayılan // açık; 1900+ statik sayfada ciddi bellek maliyeti var. enablePrerenderSourceMaps: false, } : {}), async redirects() { // Eski akış /sonuc modalına taşındı. /rapor/yazdir korunur (tam eşleşme). return [ { source: "/rapor", destination: "/sonuc", permanent: false }, { source: "/sohbet", destination: "/listem", permanent: false }, // Ayrı robot sayfası kaldırıldı; rehber içi linkler hero formuna düşer. { source: "/tercih-robotu", destination: "/#hero-form", permanent: true }, // Veri güncellemesiyle tamamen kapanan /bolum ve /universite sayfaları // (sayfalama alt yolları dahil) yaşayan en yakın sayfaya kalıcı (308) // gider. Eşlemenin tek kaynağı src/lib/kapanan-sluglar.ts. ...kapananYonlendirmeleri(), ]; }, }; export default nextConfig;