import type { NextConfig } from "next"; const nextConfig: NextConfig = { output: "standalone", // Server-only deps we don't want webpack to touch. Both pdf-parse and // its transitive pdfjs-dist use ES2022 features + top-level `__esModule` // marker that webpack's RSC runtime chokes on // ("Object.defineProperty called on non-object"). Marking them external // means Node resolves them at runtime from node_modules instead of going // through webpack's loader — so pdfjs-dist's init runs in a clean CJS // context, the way its author expects. serverExternalPackages: ["pdf-parse", "pdfjs-dist", "@napi-rs/canvas"], // Dev server blocks cross-origin requests to its own dev resources by // default in Next 16, which shows up as a page stuck on the loading // spinner when you open the app from your phone on the LAN. // // IMPORTANT — this list is matched with `isCsrfOriginAllowed`, which // splits the host on "." and compares label by label, treating `*` as // exactly ONE label. Two consequences, both learned the hard way: // // - A bare "*" is useless. It does not mean "any host" — checked // against Next 16.2.6, isCsrfOriginAllowed("192.168.1.35", ["*"]) // is false. It was in this list for months doing nothing. // - "192.168.*" is also useless for the same reason: it is two // labels against four. // - "192.168.1.*" DOES work, and survives the router handing out a // different address on a new DHCP lease. The previous version // hard-coded individual IPs (.42, .122, .128); when the lease // moved to .35 the phone silently stopped loading the app. // // So: wildcard the last label of the LAN subnet, keep the real // hostname, and leave the stale per-address entries out. allowedDevOrigins: ["127.0.0.1", "localhost", "192.168.1.*", "coolify.mywire.org"], // The agent (and any external tools) sometimes reach for the PocketBase // collection name verbatim — `order_items`, `stock_items`, `quote_items` // — and get a 404 because the route folders use hyphens (`order-items`). // Redirect the underscore variants to the hyphenated route so the agent // stops confusing the PB collection name with the URL path. async redirects() { return [ { source: "/api/v1/order_items", destination: "/api/v1/order-items", permanent: true }, { source: "/api/v1/stock_items", destination: "/api/v1/stock-items", permanent: true }, { source: "/api/v1/quote_items", destination: "/api/v1/quote-items", permanent: true }, { source: "/api/v1/stock_purchases", destination: "/api/v1/stock-purchases", permanent: true }, { source: "/api/v1/stock_quick_draft", destination: "/api/v1/stock-quick-draft", permanent: true }, // Counter PWA shortcut — keeps the install URL short. { source: "/counter", destination: "/new-order", permanent: true }, // TV dashboard shortcut — /tv → /pin (which itself bounces // to /dashboard once the cookie is valid). { source: "/tv", destination: "/pin", permanent: true }, ]; }, }; export default nextConfig;