-
Notifications
You must be signed in to change notification settings - Fork 19
Expand file tree
/
Copy pathnext.config.ts
More file actions
286 lines (253 loc) · 11.8 KB
/
next.config.ts
File metadata and controls
286 lines (253 loc) · 11.8 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
// SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION. All rights reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
// SPDX-License-Identifier: Apache-2.0
import type { NextConfig } from "next";
import bundleAnalyzer from "@next/bundle-analyzer";
const withBundleAnalyzer = bundleAnalyzer({
enabled: process.env.ANALYZE === "true",
});
// =============================================================================
// Backend API Configuration
// =============================================================================
//
// This controls the default backend that the Next.js proxy forwards to.
// Set NEXT_PUBLIC_OSMO_API_HOSTNAME in .env.local
//
// For development, you can switch backends at runtime without restarting:
// - Sign out and use the environment selector on the login page
// - Or run in browser console: setBackend("https://your-backend.example.com")
//
// See: README.md "Local Development" section for full documentation.
// =============================================================================
// Base path for serving UI under a subpath (e.g., /v2)
// This allows src/ui to run alongside legacy UI on the same hostname
// - All routes become /v2/* (e.g., /v2/pools, /v2/workflows)
// - Static assets served from /v2/_next/static/*
// - API rewrites still forward to backend /api/* (no /v2 prefix)
//
// Set via NEXT_PUBLIC_BASE_PATH environment variable (configured in Helm chart values)
// Defaults to empty string (root path) for local development
const BASE_PATH = process.env.NEXT_PUBLIC_BASE_PATH || "";
const nextConfig: NextConfig = {
// =============================================================================
// Deployment Configuration
// =============================================================================
// Base path for serving UI under a subpath (e.g., /v2)
// This allows src/ui to run alongside legacy UI on the same hostname
// - All routes become /v2/* (e.g., /v2/pools, /v2/workflows)
// - Static assets served from /v2/_next/static/*
// - API rewrites still forward to backend /api/* (no /v2 prefix)
basePath: BASE_PATH,
// RuntimeEnvProvider injects runtime config for portability
// Enable standalone output for containerized deployments
output: "standalone",
// Disable Next.js image optimization — we don't use <Image> anywhere.
// This removes sharp and @img/sharp-libvips-* (LGPL-3.0-or-later) from
// the server image.
images: {
unoptimized: true,
},
// Partial Prerendering via cacheComponents (Next.js 16)
// This is the killer feature for mobile/slow networks:
// - Static shell (nav, layout) is prerendered at build time
// - Dynamic content streams in via React Suspense
// - Users see instant content, no blank loading screens
//
// React 19 Compatibility:
// - All async Server Components use React 19's use() hook for params/searchParams
// - This provides better TypeScript inference and clearer async boundaries
// - Combined with cacheComponents, this delivers optimal server-side rendering
//
// IMPORTANT: Only enable in production!
// In development, cacheComponents causes constant re-rendering and slow iteration
// as Next.js repeatedly analyzes which components can be cached on every file change.
cacheComponents: process.env.NODE_ENV === "production",
// Source maps in production for debugging (disable to speed up builds ~30%)
// Enable temporarily when debugging production issues
productionBrowserSourceMaps: process.env.ENABLE_SOURCE_MAPS === "true",
// =============================================================================
// Performance Optimizations
// =============================================================================
// Exclude MSW from server bundling - it's only used in instrumentation for dev mocking
// This prevents Turbopack from trying to bundle Node.js-specific MSW code for Edge runtime
serverExternalPackages: ["msw", "@mswjs/interceptors"],
experimental: {
// Stale times for client-side navigation caching
// This makes Back/Forward navigation instant by keeping prefetch cache warm
staleTimes: {
dynamic: 30, // 30s for dynamic routes (matches our Query staleTime)
static: 180, // 3min for static routes
},
// CSS optimization - extract and inline critical CSS
optimizeCss: true,
// Optimize package imports for libraries with many named exports
// This ensures only used exports are bundled, reducing bundle size
// Note: lucide-react is auto-optimized by Next.js
// See: https://nextjs.org/docs/app/api-reference/config/next-config-js/optimizePackageImports
optimizePackageImports: [
// Radix UI components (wildcard covers all packages)
"@radix-ui/*",
// TanStack libraries
"@tanstack/react-table",
// DAG visualization (large library with many exports)
"@xyflow/react",
// Drag and drop
"@dnd-kit/core",
"@dnd-kit/sortable",
"@dnd-kit/utilities",
// CodeMirror editor (lazy-loaded, but optimize when loaded)
"@codemirror/lang-yaml",
"@codemirror/view",
"@codemirror/state",
"@codemirror/language",
"@codemirror/search",
"@uiw/react-codemirror",
"@lezer/highlight",
// UI component libraries
"vaul", // Drawer component
"sonner", // Toast notifications
// Hooks libraries
"usehooks-ts",
"@react-hookz/web",
"react-hotkeys-hook",
"react-error-boundary",
// Other utilities
"cmdk",
"nuqs",
"class-variance-authority",
"immer", // Immutable state updates
],
},
// Compiler optimizations
compiler: {
// Remove console.log in production (keep errors/warnings)
removeConsole: process.env.NODE_ENV === "production" ? { exclude: ["error", "warn"] } : false,
},
// =============================================================================
// Turbopack Configuration (default bundler in Next.js 16+)
// =============================================================================
//
// Turbopack is the default bundler for BOTH dev and build in Next.js 16+.
// See: https://nextjs.org/docs/app/api-reference/turbopack
//
// Note: webpack() config is IGNORED by Turbopack. Use turbopack.resolveAlias instead.
turbopack: {
resolveAlias:
process.env.NODE_ENV === "production"
? {
// ============================================================
// MOCK CODE ELIMINATION
// ============================================================
// These aliases replace mock modules with production stubs.
// This completely eliminates MSW, faker, and all mock generators
// from the production bundle.
//
// IMPORTANT: Use @/ path aliases to match import specifiers.
// Turbopack resolveAlias matches the IMPORT SPECIFIER, not file paths.
// ============================================================
// MSW handlers - the root of all mock code
// handlers imports generators -> generators import faker
// Aliasing this eliminates the entire mock dependency tree
"@/mocks/handlers": "@/mocks/handlers.production",
// Client-side mock provider (eliminates faker, msw, server actions)
"@/mocks/mock-provider": "@/mocks/mock-provider.production",
// Server-side MSW server (used in instrumentation.ts)
"@/mocks/server": "@/mocks/server.production",
// Server API config (production version has zero mock awareness)
"@/lib/api/server/config": "@/lib/api/server/config.production",
// API proxy route implementation - alias to production version (zero mock code)
// The route.ts file is a thin wrapper that re-exports from route.impl.ts
// This allows Turbopack aliasing to work (aliases work for imports, not file discovery)
"@/app/api/[...path]/route.impl": "@/app/api/[...path]/route.impl.production",
// Dataset manifest server action - alias to production version (zero mock code)
"@/lib/api/server/dataset-actions": "@/lib/api/server/dataset-actions.production",
// Dataset file proxy route - alias to production version (zero mock code)
"@/app/proxy/dataset/file/route.impl":
"@/app/proxy/dataset/file/route.impl.production",
// Auth server utilities - alias to production version (zero env fallbacks)
"@/lib/auth/server": "@/lib/auth/server.production",
}
: {},
},
// =============================================================================
// Proxy & CORS Configuration
// =============================================================================
// API Proxying via Route Handler (app/api/[...path]/route.ts)
//
// We use a catch-all Route Handler instead of rewrites() for API proxying.
// This allows the backend hostname to be configured at RUNTIME via environment variables,
// making the Docker image portable across environments (critical for open source).
//
// The catch-all route uses ZERO-COPY STREAMING for all API requests:
// - Returns response.body (ReadableStream) directly with no buffering
// - Perfect for streaming logs, large responses, and real-time data
// - Minimal latency and memory usage
//
// Route Handler Precedence:
// - /api/health - Specific route (health check with custom logic)
// - /api/me - Specific route (JWT decoding on server)
// - /api/[...path] - Catch-all zero-copy proxy to backend
//
// Benefits:
// - ✅ Backend hostname configurable at runtime (not build time)
// - ✅ Single Docker image for all environments
// - ✅ Perfect for open source deployment
// - ✅ Environment variables read when request is processed
// - ✅ Zero-copy streaming for all proxied requests
//
// Note: Rewrites would require baking backend URL at build time, requiring
// different images per environment. Route Handler approach is superior.
//
// Previous approach (REMOVED):
// async rewrites() {
// return {
// afterFiles: [
// { source: "/api/:path*", destination: `${API_URL}/api/:path*` },
// ],
// };
// }
//
// CORS headers are not needed for /api/* because all requests go through the
// same-origin Next.js proxy route handler (app/api/[...path]/route.ts).
// The proxy makes server-to-server requests to the backend, so CORS never applies.
async headers() {
return [
// Performance headers for static assets - immutable cache for 1 year
{
source: "/:all*(svg|jpg|jpeg|png|gif|ico|webp|woff|woff2)",
headers: [
{ key: "Cache-Control", value: "public, max-age=31536000, immutable" },
],
},
// JavaScript and CSS - immutable for Next.js hashed assets
{
source: "/_next/static/:path*",
headers: [
{ key: "Cache-Control", value: "public, max-age=31536000, immutable" },
],
},
// Preconnect hints for faster connections
{
source: "/:path*",
headers: [
{ key: "X-DNS-Prefetch-Control", value: "on" },
// Enable early hints (103) for browsers that support it
{ key: "Link", value: "</api>; rel=preconnect" },
],
},
];
},
};
export default withBundleAnalyzer(nextConfig);