vinext 1.4.0 → 1.4.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (156) hide show
  1. package/README.md +21 -21
  2. package/dist/build/action-owner-manifest.js +1 -0
  3. package/dist/build/prerender.js +3 -2
  4. package/dist/check.js +4 -7
  5. package/dist/client/dev-error-overlay-store.d.ts +1 -0
  6. package/dist/client/dev-error-overlay.d.ts +2 -0
  7. package/dist/client/dev-error-overlay.js +19 -3
  8. package/dist/config/config-matchers.js +4 -2
  9. package/dist/config/destination-params.d.ts +2 -1
  10. package/dist/config/destination-params.js +55 -2
  11. package/dist/config/next-config.d.ts +22 -1
  12. package/dist/config/next-config.js +49 -4
  13. package/dist/entries/app-rsc-entry.js +41 -19
  14. package/dist/entries/app-rsc-manifest.js +4 -0
  15. package/dist/entries/pages-server-entry.js +5 -1
  16. package/dist/index.js +44 -32
  17. package/dist/init-cloudflare.js +18 -2
  18. package/dist/plugins/css-target.d.ts +18 -0
  19. package/dist/plugins/css-target.js +93 -0
  20. package/dist/plugins/strip-server-exports.js +2 -1
  21. package/dist/routing/app-route-graph.d.ts +10 -0
  22. package/dist/routing/app-route-graph.js +15 -1
  23. package/dist/server/api-handler.d.ts +2 -0
  24. package/dist/server/api-handler.js +5 -6
  25. package/dist/server/app-action-forwarding.js +3 -1
  26. package/dist/server/app-browser-entry.js +28 -7
  27. package/dist/server/app-browser-rsc-redirect.js +3 -3
  28. package/dist/server/app-browser-server-action-client.js +2 -2
  29. package/dist/server/app-browser-visible-commit.js +2 -2
  30. package/dist/server/app-elements.d.ts +3 -0
  31. package/dist/server/app-elements.js +14 -1
  32. package/dist/server/app-fallback-renderer.js +2 -2
  33. package/dist/server/app-optimistic-routing.d.ts +12 -1
  34. package/dist/server/app-optimistic-routing.js +40 -19
  35. package/dist/server/app-page-boundary-render.js +7 -12
  36. package/dist/server/app-page-cache-render.js +1 -1
  37. package/dist/server/app-page-dispatch.d.ts +10 -2
  38. package/dist/server/app-page-dispatch.js +73 -31
  39. package/dist/server/app-page-element-builder.d.ts +9 -20
  40. package/dist/server/app-page-element-builder.js +40 -52
  41. package/dist/server/app-page-execution.js +9 -6
  42. package/dist/server/app-page-intercept-segment-config.d.ts +56 -0
  43. package/dist/server/app-page-intercept-segment-config.js +90 -0
  44. package/dist/server/app-page-navigation-params.d.ts +23 -0
  45. package/dist/server/app-page-navigation-params.js +49 -0
  46. package/dist/server/app-page-render.d.ts +5 -3
  47. package/dist/server/app-page-render.js +31 -17
  48. package/dist/server/app-page-request.d.ts +24 -2
  49. package/dist/server/app-page-request.js +10 -10
  50. package/dist/server/app-page-response.js +1 -1
  51. package/dist/server/app-page-route-wiring.d.ts +10 -0
  52. package/dist/server/app-page-route-wiring.js +34 -13
  53. package/dist/server/app-page-segment-plan.js +9 -1
  54. package/dist/server/app-page-segment-state.d.ts +9 -0
  55. package/dist/server/app-page-segment-state.js +54 -1
  56. package/dist/server/app-page-stream.d.ts +10 -8
  57. package/dist/server/app-page-stream.js +2 -1
  58. package/dist/server/app-request-stage-independent-entry.js +1 -1
  59. package/dist/server/app-response-stage-entry.js +1 -0
  60. package/dist/server/app-route-handler-execution.js +3 -2
  61. package/dist/server/app-route-handler-runtime.d.ts +1 -1
  62. package/dist/server/app-route-handler-runtime.js +11 -7
  63. package/dist/server/app-route-module-loader.d.ts +2 -0
  64. package/dist/server/app-route-module-loader.js +1 -0
  65. package/dist/server/app-router-entry.js +1 -1
  66. package/dist/server/app-rsc-cache-busting.d.ts +10 -10
  67. package/dist/server/app-rsc-cache-busting.js +27 -68
  68. package/dist/server/app-rsc-error-handler.js +1 -1
  69. package/dist/server/app-rsc-errors.js +4 -1
  70. package/dist/server/app-rsc-handler.d.ts +10 -1
  71. package/dist/server/app-rsc-handler.js +126 -103
  72. package/dist/server/app-rsc-request-normalization.d.ts +10 -12
  73. package/dist/server/app-rsc-request-normalization.js +11 -14
  74. package/dist/server/app-rsc-response-finalizer.d.ts +3 -0
  75. package/dist/server/app-rsc-response-finalizer.js +11 -1
  76. package/dist/server/app-rsc-response-stage.js +4 -12
  77. package/dist/server/app-rsc-route-matching.d.ts +14 -0
  78. package/dist/server/app-rsc-route-matching.js +42 -18
  79. package/dist/server/app-server-action-execution.js +24 -14
  80. package/dist/server/app-ssr-entry.d.ts +6 -0
  81. package/dist/server/app-ssr-entry.js +13 -8
  82. package/dist/server/app-worker-stages.d.ts +12 -3
  83. package/dist/server/app-worker-stages.js +37 -4
  84. package/dist/server/bfcache-identity.d.ts +7 -0
  85. package/dist/server/bfcache-identity.js +10 -1
  86. package/dist/server/cache-control.d.ts +2 -0
  87. package/dist/server/cache-control.js +3 -1
  88. package/dist/server/cacheability-manifest.js +1 -2
  89. package/dist/server/dev-origin-check.d.ts +2 -1
  90. package/dist/server/dev-origin-check.js +2 -1
  91. package/dist/server/dev-server.js +2 -1
  92. package/dist/server/image-local-patterns.d.ts +21 -0
  93. package/dist/server/image-local-patterns.js +59 -0
  94. package/dist/server/image-optimization.d.ts +28 -22
  95. package/dist/server/image-optimization.js +17 -2
  96. package/dist/server/isr-cache.js +4 -1
  97. package/dist/server/metadata-route-response.js +25 -7
  98. package/dist/server/middleware-matcher.js +19 -6
  99. package/dist/server/middleware-runtime.js +6 -2
  100. package/dist/server/multi-stage.d.ts +7 -3
  101. package/dist/server/nitro-public-files-plugin.js +20 -8
  102. package/dist/server/pages-data-route.d.ts +5 -1
  103. package/dist/server/pages-data-route.js +14 -6
  104. package/dist/server/pages-node-compat.d.ts +1 -1
  105. package/dist/server/pages-node-compat.js +2 -0
  106. package/dist/server/pages-page-data.js +2 -1
  107. package/dist/server/pages-page-handler.js +17 -6
  108. package/dist/server/pages-page-response.d.ts +9 -0
  109. package/dist/server/pages-page-response.js +18 -4
  110. package/dist/server/pages-request-pipeline.js +6 -5
  111. package/dist/server/pages-request-stage-entry.js +25 -4
  112. package/dist/server/pages-router-entry.js +3 -2
  113. package/dist/server/prod-server.d.ts +2 -2
  114. package/dist/server/prod-server.js +59 -17
  115. package/dist/server/request-error-log.d.ts +9 -0
  116. package/dist/server/request-error-log.js +13 -0
  117. package/dist/server/request-pipeline.d.ts +14 -4
  118. package/dist/server/request-pipeline.js +33 -15
  119. package/dist/server/response-stage-cacheability.d.ts +7 -0
  120. package/dist/server/response-stage-cacheability.js +6 -5
  121. package/dist/server/rsc-completion-metadata.js +3 -0
  122. package/dist/server/static-file-cache.d.ts +9 -0
  123. package/dist/server/static-file-cache.js +13 -2
  124. package/dist/server/worker-image-optimization.d.ts +2 -2
  125. package/dist/server/worker-image-optimization.js +18 -3
  126. package/dist/server/worker-utils.d.ts +8 -0
  127. package/dist/server/worker-utils.js +19 -2
  128. package/dist/shims/cache-request-state.d.ts +6 -6
  129. package/dist/shims/cache-request-state.js +6 -36
  130. package/dist/shims/cache-runtime.d.ts +3 -1
  131. package/dist/shims/cache-runtime.js +77 -47
  132. package/dist/shims/cache.d.ts +10 -5
  133. package/dist/shims/cache.js +35 -22
  134. package/dist/shims/client-hook-error.d.ts +2 -0
  135. package/dist/shims/client-hook-error.js +16 -1
  136. package/dist/shims/fetch-cache.js +6 -2
  137. package/dist/shims/headers.d.ts +2 -1
  138. package/dist/shims/headers.js +5 -3
  139. package/dist/shims/image-props.d.ts +6 -0
  140. package/dist/shims/image-props.js +39 -3
  141. package/dist/shims/image.js +3 -3
  142. package/dist/shims/internal/cache-context-accessor.d.ts +1 -1
  143. package/dist/shims/link.js +5 -10
  144. package/dist/shims/navigation.js +2 -3
  145. package/dist/shims/request-state-types.d.ts +2 -1
  146. package/dist/shims/slot.d.ts +9 -2
  147. package/dist/shims/slot.js +76 -11
  148. package/dist/shims/unified-request-context.d.ts +6 -0
  149. package/dist/shims/unified-request-context.js +1 -0
  150. package/dist/utils/base-path.d.ts +7 -0
  151. package/dist/utils/base-path.js +11 -1
  152. package/dist/utils/cache-life-profiles.d.ts +33 -0
  153. package/dist/utils/cache-life-profiles.js +111 -0
  154. package/dist/utils/middleware-request-headers.d.ts +14 -3
  155. package/dist/utils/middleware-request-headers.js +54 -7
  156. package/package.json +1 -1
@@ -21,6 +21,7 @@ const _pagesI18nPath = resolveEntryPath("../server/pages-i18n.js", import.meta.u
21
21
  const _pagesDataRoutePath = resolveEntryPath("../server/pages-data-route.js", import.meta.url);
22
22
  const _pagesDefault404Path = resolveEntryPath("../server/pages-default-404.js", import.meta.url);
23
23
  const _pagesApiRoutePath = resolveEntryPath("../server/pages-api-route.js", import.meta.url);
24
+ const _requestErrorLogPath = resolveEntryPath("../server/request-error-log.js", import.meta.url);
24
25
  const _serverGlobalsPath = resolveEntryPath("../server/server-globals.js", import.meta.url);
25
26
  const _queryUtilsPath = resolveEntryPath("../utils/query.js", import.meta.url);
26
27
  const _pagesPageHandlerPath = resolveEntryPath("../server/pages-page-handler.js", import.meta.url);
@@ -103,6 +104,7 @@ async function generatePagesRequestEntry(pagesDir, nextConfig, fileMatcher, midd
103
104
  deviceSizes: nextConfig?.images?.deviceSizes,
104
105
  imageSizes: nextConfig?.images?.imageSizes,
105
106
  qualities: nextConfig?.images?.qualities,
107
+ localPatterns: nextConfig?.images?.localPatterns,
106
108
  dangerouslyAllowSVG: nextConfig?.images?.dangerouslyAllowSVG,
107
109
  dangerouslyAllowLocalIP: nextConfig?.images?.dangerouslyAllowLocalIP,
108
110
  contentDispositionType: nextConfig?.images?.contentDispositionType,
@@ -280,6 +282,7 @@ async function generateServerEntry(pagesDir, nextConfig, fileMatcher, middleware
280
282
  deviceSizes: nextConfig?.images?.deviceSizes,
281
283
  imageSizes: nextConfig?.images?.imageSizes,
282
284
  qualities: nextConfig?.images?.qualities,
285
+ localPatterns: nextConfig?.images?.localPatterns,
283
286
  dangerouslyAllowSVG: nextConfig?.images?.dangerouslyAllowSVG,
284
287
  dangerouslyAllowLocalIP: nextConfig?.images?.dangerouslyAllowLocalIP,
285
288
  contentDispositionType: nextConfig?.images?.contentDispositionType,
@@ -370,6 +373,7 @@ import { buildRouteTrie as _buildRouteTrie, trieMatch as _trieMatch } from ${JSO
370
373
  import { reportRequestError as _reportRequestError } from "vinext/instrumentation";
371
374
  import { resolvePagesI18nRequest } from ${JSON.stringify(_pagesI18nPath)};
372
375
  import { handlePagesApiRoute as __handlePagesApiRoute } from ${JSON.stringify(_pagesApiRoutePath)};
376
+ import { logRequestError as __logRequestError } from ${JSON.stringify(_requestErrorLogPath)};
373
377
  import { normalizePagesDataRequest as __normalizePagesDataRequest, shouldAddTrailingSlashToPagesDataPath as __shouldAddTrailingSlashToPagesDataPath, buildNextDataNotFoundResponse as __buildNextDataNotFoundResponse } from ${JSON.stringify(_pagesDataRoutePath)};
374
378
  import { buildDefaultPagesNotFoundResponse as __buildDefaultPagesNotFoundResponse } from ${JSON.stringify(_pagesDefault404Path)};
375
379
  import { createPagesPageHandler as __createPagesPageHandler } from ${JSON.stringify(_pagesPageHandlerPath)};
@@ -618,7 +622,7 @@ export async function handleApiRoute(request, url, ctx, trustedRevalidateOrigin,
618
622
  trustedRevalidateOrigin,
619
623
  url,
620
624
  reportRequestError(error, routePattern) {
621
- console.error("[vinext] API error:", error);
625
+ __logRequestError("[vinext] API error:", error);
622
626
  return _reportRequestError(
623
627
  error,
624
628
  { path: url, method: request.method, headers: Object.fromEntries(request.headers.entries()) },
package/dist/index.js CHANGED
@@ -11,9 +11,10 @@ import { getReactUpgradeDeps, reactUpgradeRequiredMessage } from "./utils/react-
11
11
  import { resolvePluginReactPath } from "./utils/plugin-react-version.js";
12
12
  import { loadDotenv } from "./config/dotenv.js";
13
13
  import { PHASE_DEVELOPMENT_SERVER, PHASE_PRODUCTION_BUILD } from "./shims/constants.js";
14
+ import { serializeCacheLifeProfiles } from "./utils/cache-life-profiles.js";
14
15
  import { VINEXT_MW_CTX_HEADER } from "./utils/protocol-headers.js";
15
16
  import { encodeMiddlewareRequestHeaders } from "./utils/middleware-request-headers.js";
16
- import { hasBasePath, stripBasePath } from "./utils/base-path.js";
17
+ import { hasBasePath, restoreBasePath, stripBasePath } from "./utils/base-path.js";
17
18
  import { INTERNAL_HEADERS, NEXTJS_DEPLOYMENT_ID_HEADER, VINEXT_INTERNAL_HEADERS, VINEXT_TIMING_HEADER } from "./server/headers.js";
18
19
  import { methodNotAllowedResponse, notFoundStaticAssetResponse } from "./server/http-error-responses.js";
19
20
  import { isOpenRedirectShaped, sendRepeatedSlashRedirect } from "./server/open-redirect.js";
@@ -88,6 +89,7 @@ import { createClientCompileHintsPlugin } from "./plugins/client-compile-hints.j
88
89
  import { clientReferenceDedupPlugin } from "./plugins/client-reference-dedup.js";
89
90
  import { dataUrlCssPlugin } from "./plugins/css-data-url.js";
90
91
  import { createCssModuleImportCompatibilityPlugin } from "./plugins/css-module-imports.js";
92
+ import { createCssTargetPlugin } from "./plugins/css-target.js";
91
93
  import { createWorkerImageImportsPlugin } from "./plugins/worker-image-imports.js";
92
94
  import { assertClientOutDirIsSeparate, createClientAssetFileNames, createClientCodeSplittingConfig, createClientManualChunks, createClientOutputFileNameDefaults, createMultiStageChunkFileNames, createMultiStageCodeSplittingConfig, createRscFrameworkChunkOutputConfig, createWorkerFileNames, findUnsupportedClientOutputFileNames, getBuildBundlerOptions, getClientTreeshakeConfig, getOutputFileNames, withBuildBundlerOptions } from "./build/client-build-config.js";
93
95
  import { createRouteClientReferencesFallbackPlugin, createRscClientReferenceLoadersPlugin } from "./plugins/rsc-client-reference-loaders.js";
@@ -1373,6 +1375,29 @@ function vinext(options = {}) {
1373
1375
  });
1374
1376
  const plugins = [
1375
1377
  buildLifecyclePlugins[0],
1378
+ {
1379
+ name: "vinext:dev-origin-check",
1380
+ apply: "serve",
1381
+ enforce: "pre",
1382
+ configureServer(server) {
1383
+ server.middlewares.use((req, res, next) => {
1384
+ const blockReason = validateDevRequest({
1385
+ origin: req.headers.origin,
1386
+ host: req.headers.host,
1387
+ "x-forwarded-host": req.headers["x-forwarded-host"],
1388
+ "sec-fetch-site": req.headers["sec-fetch-site"],
1389
+ "sec-fetch-mode": req.headers["sec-fetch-mode"]
1390
+ }, nextConfig?.allowedDevOrigins);
1391
+ if (blockReason) {
1392
+ console.warn(`[vinext] Blocked dev request: ${blockReason} (${req.url})`);
1393
+ res.writeHead(403, { "Content-Type": "text/plain" });
1394
+ res.end("Forbidden");
1395
+ return;
1396
+ }
1397
+ next();
1398
+ });
1399
+ }
1400
+ },
1376
1401
  createStyledJsxPlugin(earlyBaseDir),
1377
1402
  mdxProxyPlugin,
1378
1403
  reactPluginPromise,
@@ -1442,6 +1467,7 @@ function vinext(options = {}) {
1442
1467
  }),
1443
1468
  dataUrlCssPlugin(),
1444
1469
  createCssModuleImportCompatibilityPlugin(),
1470
+ createCssTargetPlugin(),
1445
1471
  createDevServerLifecyclePlugin({}, (config) => config[VINEXT_DEV_CLI_LIFECYCLE] === true),
1446
1472
  {
1447
1473
  name: "vinext:config",
@@ -1595,6 +1621,7 @@ function vinext(options = {}) {
1595
1621
  defines["process.env.__VINEXT_TRAILING_SLASH"] = JSON.stringify(nextConfig.trailingSlash ? "true" : "false");
1596
1622
  defines["process.env.__VINEXT_MANUAL_TRAILING_SLASH"] = JSON.stringify(nextConfig.skipTrailingSlashRedirect ? "true" : "false");
1597
1623
  defines["process.env.__NEXT_CONFIG_OUTPUT"] = JSON.stringify(nextConfig.output ?? "");
1624
+ defines["process.env.__VINEXT_CACHE_LIFE_PROFILES"] = JSON.stringify(serializeCacheLifeProfiles(nextConfig.cacheLife));
1598
1625
  defines["process.env.__VINEXT_IMAGE_REMOTE_PATTERNS"] = JSON.stringify(JSON.stringify(nextConfig.images?.remotePatterns ?? []));
1599
1626
  defines["process.env.__VINEXT_IMAGE_DOMAINS"] = JSON.stringify(JSON.stringify(nextConfig.images?.domains ?? []));
1600
1627
  {
@@ -1739,12 +1766,6 @@ function vinext(options = {}) {
1739
1766
  } } : {},
1740
1767
  build: {
1741
1768
  ssrEmitAssets: true,
1742
- cssTarget: [
1743
- "chrome111",
1744
- "edge111",
1745
- "firefox114",
1746
- "safari15"
1747
- ],
1748
1769
  assetsDir: clientAssetsDir,
1749
1770
  ...!isSSR && !hasClientBuildEnvironment ? { assetsInlineLimit: clientAssetsInlineLimit } : {},
1750
1771
  ...withBuildBundlerOptions({
@@ -2428,6 +2449,7 @@ function vinext(options = {}) {
2428
2449
  deviceSizes: nextConfig?.images?.deviceSizes,
2429
2450
  imageSizes: nextConfig?.images?.imageSizes,
2430
2451
  qualities: nextConfig?.images?.qualities,
2452
+ localPatterns: nextConfig?.images?.localPatterns,
2431
2453
  dangerouslyAllowSVG: nextConfig?.images?.dangerouslyAllowSVG,
2432
2454
  dangerouslyAllowLocalIP: nextConfig?.images?.dangerouslyAllowLocalIP,
2433
2455
  contentDispositionType: nextConfig?.images?.contentDispositionType,
@@ -3147,22 +3169,6 @@ export const loadServerActionClient = ${hasServerActions ? `() => import(${JSON.
3147
3169
  }
3148
3170
  return isExistingDevPublicFile(pathname);
3149
3171
  };
3150
- server.middlewares.use((req, res, next) => {
3151
- const blockReason = validateDevRequest({
3152
- origin: req.headers.origin,
3153
- host: req.headers.host,
3154
- "x-forwarded-host": req.headers["x-forwarded-host"],
3155
- "sec-fetch-site": req.headers["sec-fetch-site"],
3156
- "sec-fetch-mode": req.headers["sec-fetch-mode"]
3157
- }, nextConfig?.allowedDevOrigins);
3158
- if (blockReason) {
3159
- console.warn(`[vinext] Blocked dev request: ${blockReason} (${req.url})`);
3160
- res.writeHead(403, { "Content-Type": "text/plain" });
3161
- res.end("Forbidden");
3162
- return;
3163
- }
3164
- next();
3165
- });
3166
3172
  server.middlewares.use((req, res, next) => {
3167
3173
  if (!sendRepeatedSlashRedirect(req.url ?? "/", res)) next();
3168
3174
  });
@@ -3253,7 +3259,7 @@ export const loadServerActionClient = ${hasServerActions ? `() => import(${JSON.
3253
3259
  if (hasAppDir) server.middlewares.use((req, res, next) => {
3254
3260
  const url = req.url ?? "/";
3255
3261
  const [pathname] = url.split("?");
3256
- if (url.startsWith("/@") || url.startsWith("/__vite") || url.startsWith("/node_modules") || url.includes(".") && !pathname.endsWith(".html") && !pathname.endsWith(".rsc")) return next();
3262
+ if (url.startsWith("/@") || url.startsWith("/__vite") || url.startsWith("/node_modules") || url.includes(".") && !pathname.endsWith(".html")) return next();
3257
3263
  const _reqStart = now();
3258
3264
  const _reqWallStart = Date.now();
3259
3265
  let _compileMs;
@@ -3287,12 +3293,11 @@ export const loadServerActionClient = ${hasServerActions ? `() => import(${JSON.
3287
3293
  return _origWriteHead(statusCode, ...args);
3288
3294
  };
3289
3295
  res.on("finish", () => {
3290
- const logUrl = url.replace(/\.rsc(\?|$)/, "$1");
3291
3296
  const totalMs = now() - _reqStart;
3292
3297
  const resolvedRenderMs = _renderMs !== void 0 ? _renderMs : _compileMs !== void 0 ? Math.max(0, Math.round(totalMs - _compileMs)) : void 0;
3293
3298
  logRequest({
3294
3299
  method: req.method ?? "GET",
3295
- url: logUrl,
3300
+ url,
3296
3301
  status: res.statusCode,
3297
3302
  totalMs,
3298
3303
  compileMs: _compileMs,
@@ -3370,14 +3375,17 @@ export const loadServerActionClient = ${hasServerActions ? `() => import(${JSON.
3370
3375
  const viteBase = server.config.base;
3371
3376
  const viteBasePath = viteBase.startsWith("/") && viteBase !== "/" ? viteBase.replace(/\/+$/, "") : "";
3372
3377
  const routingBasePath = bp || viteBasePath;
3378
+ const viteStrippedBasePath = originalRequestUrl !== originalEncodedUrl;
3379
+ let originalUrlHadBasePath = false;
3373
3380
  if (routingBasePath) {
3374
- if (hasBasePath(pathname, routingBasePath)) {
3381
+ if (!viteStrippedBasePath && hasBasePath(pathname, routingBasePath)) {
3375
3382
  const stripped = stripBasePath(pathname, routingBasePath);
3376
3383
  url = stripped + (url.includes("?") ? url.slice(url.indexOf("?")) : "");
3377
3384
  pathname = stripped;
3378
3385
  }
3379
3386
  const middlewarePathname = middlewareUrl.split("?")[0];
3380
3387
  if (hasBasePath(middlewarePathname, routingBasePath)) {
3388
+ originalUrlHadBasePath = true;
3381
3389
  const middlewareQs = middlewareUrl.includes("?") ? middlewareUrl.slice(middlewareUrl.indexOf("?")) : "";
3382
3390
  middlewareUrl = stripBasePath(middlewarePathname, routingBasePath) + middlewareQs;
3383
3391
  }
@@ -3387,7 +3395,7 @@ export const loadServerActionClient = ${hasServerActions ? `() => import(${JSON.
3387
3395
  routeUrl = stripBasePath(routePathname, routingBasePath) + routeQs;
3388
3396
  }
3389
3397
  }
3390
- let configMatchPathname = stripBasePath(middlewareUrl.split("?")[0], routingBasePath);
3398
+ let configMatchPathname = middlewareUrl.split("?")[0];
3391
3399
  if (nextConfig && !nextConfig.skipTrailingSlashRedirect) {
3392
3400
  const qs = url.includes("?") ? url.slice(url.indexOf("?")) : "";
3393
3401
  const trailingSlashRedirect = normalizeTrailingSlash(routeUrl.split("?")[0], bp, nextConfig.trailingSlash, qs);
@@ -3404,7 +3412,7 @@ export const loadServerActionClient = ${hasServerActions ? `() => import(${JSON.
3404
3412
  const rawRoutePathname = routeUrl.split("?")[0];
3405
3413
  if (isNextDataPathname(rawRoutePathname)) {
3406
3414
  const rawDataMatch = parseNextDataPathname(rawRoutePathname, devBuildId);
3407
- const dataMatch = rawDataMatch ? parseNextDataPathname(pathname, devBuildId) : null;
3415
+ const dataMatch = rawDataMatch ? parseNextDataPathname(pathname, devBuildId, true) : null;
3408
3416
  if (dataMatch && rawDataMatch) {
3409
3417
  isDataReq = true;
3410
3418
  const qs = url.includes("?") ? url.slice(url.indexOf("?")) : "";
@@ -3468,11 +3476,15 @@ export const loadServerActionClient = ${hasServerActions ? `() => import(${JSON.
3468
3476
  removeForwardedMiddlewareContext(req);
3469
3477
  for (const key of Object.keys(req.headers)) delete req.headers[key];
3470
3478
  for (const [key, value] of nextRequestHeaders) req.headers[key] = value;
3479
+ const setCookies = nextRequestHeaders.getSetCookie();
3480
+ if (setCookies.length > 1) req.headers["set-cookie"] = setCookies;
3471
3481
  };
3472
3482
  const devRunMiddlewareAdapter = capturedMiddlewarePath ? async (_request, _ctx, opts) => {
3473
3483
  const rawProto = process.env.VINEXT_TRUST_PROXY === "1" || (process.env.VINEXT_TRUSTED_HOSTS ?? "").split(",").some((h) => h.trim()) ? String(req.headers["x-forwarded-proto"] || "").split(",")[0].trim() : "";
3474
3484
  const mwOrigin = `${rawProto === "https" || rawProto === "http" ? rawProto : "http"}://${requestHost}`;
3475
- const middlewareRequest = new Request(new URL(middlewareUrl, mwOrigin), {
3485
+ const middlewareRequestUrl = new URL(middlewareUrl, mwOrigin);
3486
+ if (originalUrlHadBasePath) middlewareRequestUrl.pathname = restoreBasePath(middlewareRequestUrl.pathname, bp);
3487
+ const middlewareRequest = new Request(middlewareRequestUrl, {
3476
3488
  method: req.method,
3477
3489
  headers: nodeRequestHeaders,
3478
3490
  signal: webRequest.signal
@@ -3618,7 +3630,8 @@ export const loadServerActionClient = ${hasServerActions ? `() => import(${JSON.
3618
3630
  trustedRevalidateOrigin: getTrustedDevRevalidateOrigin(),
3619
3631
  allowedRevalidateHeaderKeys: nextConfig?.allowedRevalidateHeaderKeys,
3620
3632
  trailingSlash: nextConfig?.trailingSlash,
3621
- skipTrailingSlashRedirect: nextConfig?.skipTrailingSlashRedirect
3633
+ skipTrailingSlashRedirect: nextConfig?.skipTrailingSlashRedirect,
3634
+ requestPathname: originalUrlHadBasePath ? restoreBasePath(routeUrl.split("?")[0], bp) : routeUrl.split("?")[0]
3622
3635
  })) return;
3623
3636
  if (hasAppDir) {
3624
3637
  if (pipelineResult.configRewriteFired) forwardInternalApiRewriteToApp(pipelineResult.apiUrl);
@@ -3662,7 +3675,6 @@ export const loadServerActionClient = ${hasServerActions ? `() => import(${JSON.
3662
3675
  const url = req.url ?? "/";
3663
3676
  if (!hasPagesDir) return next();
3664
3677
  if (url.startsWith("/@") || url.startsWith("/__vite") || url.startsWith("/node_modules")) return next();
3665
- if (url.split("?")[0].endsWith(".rsc")) return next();
3666
3678
  if (hasCloudflarePlugin) return next();
3667
3679
  await pagesInstrumentationReady;
3668
3680
  const traceHeaders = new Headers();
@@ -140,7 +140,7 @@ function setupCloudflarePlatform(context, cloudflare) {
140
140
  generatedPlatformFiles.push(path.relative(context.root, responseStorePath) || RESPONSE_STORE_WRANGLER_CONFIG);
141
141
  }
142
142
  }
143
- const nextSteps = [];
143
+ const nextSteps = !wranglerCode || !findTopLevelJsonProperty(wranglerCode, "main") ? workerEntryWarning(context.root, context.isStaticExport) : [];
144
144
  if (cloudflare.cdnCache === "response-store" && (cloudflare.responseStoreMode ?? "service-binding") === "service-binding") nextSteps.push("Deploy Workers Response Store before deploying the application:", ` ${context.packageManager ?? "npm"} run deploy:response-store`);
145
145
  if (cloudflare.prerender && cloudflare.cdnCache !== "static-assets") nextSteps.push("Pre-rendered routes are built, but Cloudflare deploys do not serve them.", " Use the Static Assets cache to serve them, or cache warming to fill another cache.");
146
146
  return {
@@ -193,14 +193,15 @@ function setupCfPlatform(context, cloudflare) {
193
193
  }
194
194
  const configPath = path.join(context.root, "cloudflare.config.ts");
195
195
  const generatedPlatformFiles = [];
196
+ const nextSteps = [];
196
197
  if (!fs.existsSync(configPath)) {
197
198
  fs.writeFileSync(configPath, generateTypedCloudflareConfig({
198
199
  ...projectInfo,
199
200
  isStaticExport: context.isStaticExport
200
201
  }, cloudflare, context.today));
201
202
  generatedPlatformFiles.push("cloudflare.config.ts");
203
+ nextSteps.push(...workerEntryWarning(context.root, context.isStaticExport));
202
204
  }
203
- const nextSteps = [];
204
205
  if (serviceBinding) nextSteps.push("After `vinext build`, deploy the Response Store Worker explicitly (and repeat when its code/config changes):", ` ${context.packageManager ?? "npm"} run deploy:response-store`, "vinext-cloudflare deploy only deploys the application Worker.");
205
206
  if (cloudflare.prerender && cloudflare.cdnCache !== "static-assets") nextSteps.push("Pre-rendered routes are built, but Cloudflare deploys do not serve them.", " Use the Static Assets cache to serve them, or cache warming to fill another cache.");
206
207
  nextSteps.push("For TypeScript, add \".cloudflare/types\" to the include list in tsconfig.json.", "Worker types are generated during dev/build; run `cf workers types` before standalone type-checks.");
@@ -280,6 +281,21 @@ function resolveWorkerEntry(root) {
280
281
  return "vinext/server/fetch-handler";
281
282
  }
282
283
  /**
284
+ * A `worker/index.*` that never reaches vinext's handler (say, a migrated Vite
285
+ * SPA's API Worker) answers every page route itself, so each one 404s (#4460).
286
+ */
287
+ function workerEntryWarning(root, isStaticExport) {
288
+ if (isStaticExport) return [];
289
+ const entry = resolveWorkerEntry(root);
290
+ if (entry === "vinext/server/fetch-handler") return [];
291
+ if (fs.readFileSync(path.join(root, entry), "utf-8").includes("vinext/server/fetch-handler")) return [];
292
+ return [
293
+ `Warning: ${entry.slice(2)} does not import vinext/server/fetch-handler. As the Worker entry`,
294
+ " it must hand requests to vinext, or every page 404s. Otherwise point the Worker entry",
295
+ " (entrypoint in cloudflare.config.ts, or main in wrangler.jsonc) at vinext/server/fetch-handler."
296
+ ];
297
+ }
298
+ /**
283
299
  * Next.js runs no middleware for a static export, and an export with
284
300
  * `trailingSlash` relies on the default html_handling to serve `/about/` from
285
301
  * `about/index.html`, so export apps keep asset-first serving.
@@ -0,0 +1,18 @@
1
+ import { Plugin } from "vite";
2
+ //#region src/plugins/css-target.d.ts
3
+ /**
4
+ * Applies {@link DEFAULT_CSS_TARGET} only where nobody chose a target (#4413).
5
+ *
6
+ * Vite merges config by concatenating arrays, and the CSS minifier keeps the
7
+ * oldest version of each browser, so a pin merged onto a chosen target would
8
+ * stop an app from ever raising it. Instead nothing is set while config hooks
9
+ * run, so the app and every plugin hook see and merge plain Vite values. The
10
+ * post-ordered `config` and `configEnvironment` hooks only note where a target
11
+ * was set, and the pin is applied once config is resolved.
12
+ *
13
+ * Both the top-level `build`, which Vite's CSS plugin minifies with (a copy of
14
+ * the environment's in per-environment builds), and every environment's `build`
15
+ * are checked.
16
+ */
17
+ export declare function createCssTargetPlugin(): Plugin;
18
+ //#endregion
@@ -0,0 +1,93 @@
1
+ //#region src/plugins/css-target.ts
2
+ /**
3
+ * CSS minification target. Vite defaults it to the same target as JS
4
+ * (modern evergreens), which lets the CSS minifier (Lightning CSS by
5
+ * default, or esbuild) rewrite
6
+ * `@media (max-width: 768px)` to the Media Queries Level 4 range
7
+ * syntax `@media (width <= 768px)`. Both forms are semantically
8
+ * equivalent in modern browsers, but the rewrite is observable to
9
+ * user code that inspects `cssText` of `CSSMediaRule`s and breaks
10
+ * tools that pattern-match the raw query string. Next.js does not
11
+ * perform this rewrite by default (its webpack/lightningcss CSS
12
+ * pipeline preserves the original syntax), so user code carried
13
+ * over from Next.js can break when migrating to vinext.
14
+ *
15
+ * The minifier lowers a CSS feature when ANY target in the list lacks
16
+ * support, so we only need to pin one engine below the range-syntax
17
+ * baseline. Range syntax shipped in Chrome 104, Edge 104, Firefox 63,
18
+ * and Safari 16.4 (per esbuild's `internal/compat/css_table.go`
19
+ * MediaRange entry — and caniuse). Of those, Safari is the latest:
20
+ * pinning `safari15` (semantically Safari 15.0, which predates
21
+ * Safari 16.4) is sufficient to suppress the rewrite on its own.
22
+ *
23
+ * The other three targets are pinned to ~2023 baselines instead of
24
+ * the absolute oldest supported version so the minifier does NOT
25
+ * collaterally downlevel unrelated modern CSS features. With
26
+ * chrome111/edge111/firefox114 we keep through:
27
+ * - `:is()` pseudo-class (Chrome 88, Firefox 78)
28
+ * - `hwb()` colors (Chrome 101, Firefox 96)
29
+ * - `lab()`, `oklch()`, `color()` (Chrome 111, Firefox 113)
30
+ * - gradient interpolation hints (Chrome 111)
31
+ * CSS Nesting (Chrome 120, Safari 17.2) and Firefox-137 gradient
32
+ * interpolation will still be lowered; that is an intentional
33
+ * trade-off — those features are newer than the baseline and
34
+ * lowering them is the correct behavior for our target audience.
35
+ *
36
+ * Mirrors the Next.js fixture
37
+ * test/e2e/app-dir/css-media-query/css-media-query.test.ts which
38
+ * asserts `cssText` preserves `max-width: 768px`.
39
+ */
40
+ const DEFAULT_CSS_TARGET = [
41
+ "chrome111",
42
+ "edge111",
43
+ "firefox114",
44
+ "safari15"
45
+ ];
46
+ /**
47
+ * Applies {@link DEFAULT_CSS_TARGET} only where nobody chose a target (#4413).
48
+ *
49
+ * Vite merges config by concatenating arrays, and the CSS minifier keeps the
50
+ * oldest version of each browser, so a pin merged onto a chosen target would
51
+ * stop an app from ever raising it. Instead nothing is set while config hooks
52
+ * run, so the app and every plugin hook see and merge plain Vite values. The
53
+ * post-ordered `config` and `configEnvironment` hooks only note where a target
54
+ * was set, and the pin is applied once config is resolved.
55
+ *
56
+ * Both the top-level `build`, which Vite's CSS plugin minifies with (a copy of
57
+ * the environment's in per-environment builds), and every environment's `build`
58
+ * are checked.
59
+ */
60
+ function createCssTargetPlugin() {
61
+ let topLevelConfigured = false;
62
+ const configuredEnvironments = /* @__PURE__ */ new Set();
63
+ return {
64
+ name: "vinext:css-target",
65
+ enforce: "post",
66
+ config: {
67
+ order: "post",
68
+ handler(config) {
69
+ topLevelConfigured = config.build?.cssTarget !== void 0;
70
+ configuredEnvironments.clear();
71
+ }
72
+ },
73
+ configEnvironment: {
74
+ order: "post",
75
+ handler(name, config) {
76
+ if (config.build?.cssTarget !== void 0) configuredEnvironments.add(name);
77
+ }
78
+ },
79
+ configResolved: {
80
+ order: "pre",
81
+ handler(config) {
82
+ pinUnsetCssTarget(config.build, topLevelConfigured);
83
+ for (const [name, environment] of Object.entries(config.environments)) pinUnsetCssTarget(environment.build, configuredEnvironments.has(name));
84
+ }
85
+ }
86
+ };
87
+ }
88
+ function pinUnsetCssTarget(build, configured) {
89
+ if (configured || build.cssTarget !== build.target) return;
90
+ build.cssTarget = [...DEFAULT_CSS_TARGET];
91
+ }
92
+ //#endregion
93
+ export { createCssTargetPlugin };
@@ -89,7 +89,8 @@ function isReferenceIdentifier(node, parent) {
89
89
  if (parent.type === "ImportSpecifier" || parent.type === "ImportDefaultSpecifier" || parent.type === "ImportNamespaceSpecifier") return false;
90
90
  if (parent.type === "ExportSpecifier") return false;
91
91
  if (parent.type === "MemberExpression" && parent.property === node && !parent.computed) return false;
92
- if ((parent.type === "Property" || parent.type === "MethodDefinition") && parent.key === node && !parent.computed && !(parent.type === "Property" && parent.shorthand)) return false;
92
+ if ((parent.type === "Property" || parent.type === "MethodDefinition" || parent.type === "PropertyDefinition" || parent.type === "AccessorProperty" || parent.type === "ImportAttribute") && parent.key === node && !parent.computed && !(parent.type === "Property" && parent.shorthand)) return false;
93
+ if (parent.type === "MetaProperty") return false;
93
94
  if ((parent.type === "LabeledStatement" || parent.type === "BreakStatement" || parent.type === "ContinueStatement") && parent.label === node) return false;
94
95
  const parameters = "params" in parent ? parent.params : void 0;
95
96
  if (Array.isArray(parameters) && parameters.includes(node)) return false;
@@ -109,6 +109,10 @@ type ParallelSlot = {
109
109
  loadingTreePositions?: number[];
110
110
  /** Absolute path to the slot's error component */
111
111
  errorPath: string | null;
112
+ /** Every error convention from the slot root to its active page, outermost first. */
113
+ errorPaths?: string[];
114
+ /** Slot-root-relative tree positions aligned with errorPaths. */
115
+ errorTreePositions?: number[];
112
116
  /** Nearest not-found convention for the slot's active branch. */
113
117
  notFoundPath?: string | null;
114
118
  /** Tree position of that convention relative to the slot root. */
@@ -167,6 +171,12 @@ export type AppRoute = {
167
171
  id: string;
168
172
  ownerTreePath: string;
169
173
  state: "active" | "default" | "unmatched";
174
+ /**
175
+ * Set on a sub-route minted for a slot page with no children page of its
176
+ * own: its children render a default, a sibling catch-all, or nothing, so
177
+ * its route segments don't describe them.
178
+ */
179
+ synthetic?: true;
170
180
  };
171
181
  /**
172
182
  * Interception markers not wrapped in an `@slot` directory.
@@ -594,7 +594,8 @@ function discoverSlotSubRoutes(routes, matcher, ghostParents = []) {
594
594
  childrenSlot: {
595
595
  id: childrenSlotId,
596
596
  ownerTreePath: childrenOwnerTreePath,
597
- state: childrenDefault ? "default" : childrenCatchAll ? "active" : "unmatched"
597
+ state: childrenDefault ? "default" : childrenCatchAll ? "active" : "unmatched",
598
+ synthetic: true
598
599
  },
599
600
  loadingPath: null,
600
601
  loadingPaths: parentRoute.loadingPaths,
@@ -742,10 +743,22 @@ function withSlotLoadingEntries(slot, pagePath, matcher) {
742
743
  ...slot,
743
744
  loadingPaths: loadingEntries.map((entry) => entry.path),
744
745
  loadingTreePositions: loadingEntries.map((entry) => entry.treePosition),
746
+ ...findSlotErrorFields(slot.ownerDir, pagePath, matcher),
745
747
  ...findSlotNotFoundFields(slot.ownerDir, pagePath, matcher)
746
748
  };
747
749
  }
748
750
  /**
751
+ * Every error convention on the slot's active branch. Each catches errors in
752
+ * its own segment, below that segment's layout.
753
+ */
754
+ function findSlotErrorFields(slotDir, pagePath, matcher) {
755
+ const entries = findSlotSegmentFileEntries(slotDir, pagePath, "error", matcher);
756
+ return {
757
+ errorPaths: entries.map((entry) => entry.path),
758
+ errorTreePositions: entries.map((entry) => entry.treePosition)
759
+ };
760
+ }
761
+ /**
749
762
  * Every not-found convention on the slot's active branch, plus the nearest one,
750
763
  * which supplies the slot's not-found metadata.
751
764
  */
@@ -1347,6 +1360,7 @@ function discoverParallelSlots(dir, appDir, matcher, includeNestedOnlySlots = fa
1347
1360
  loadingPaths: loadingEntries.map((loading) => loading.path),
1348
1361
  loadingTreePositions: loadingEntries.map((loading) => loading.treePosition),
1349
1362
  errorPath: findFile(slotDir, "error", matcher),
1363
+ ...findSlotErrorFields(slotDir, pagePath, matcher),
1350
1364
  ...findSlotNotFoundFields(slotDir, pagePath, matcher),
1351
1365
  interceptingRoutes,
1352
1366
  layoutIndex: -1,
@@ -14,5 +14,7 @@ export declare function handleApiRoute(runner: ModuleImporter, req: IncomingMess
14
14
  allowedRevalidateHeaderKeys?: readonly string[];
15
15
  trailingSlash?: boolean;
16
16
  skipTrailingSlashRedirect?: boolean;
17
+ /** Edge API request pathname, with any basePath the dev server stripped restored. */
18
+ requestPathname?: string;
17
19
  }): Promise<boolean>;
18
20
  //#endregion
@@ -1,9 +1,9 @@
1
- import { hasBasePath } from "../utils/base-path.js";
2
1
  import { matchRoute } from "../routing/pages-router.js";
3
2
  import "./server-globals.js";
4
3
  import { resolveRequestHost, resolveRequestProtocol } from "./proxy-trust.js";
5
4
  import { NextRequest } from "../shims/server.js";
6
5
  import { importModule, reportRequestError } from "./instrumentation.js";
6
+ import { logRequestError } from "./request-error-log.js";
7
7
  import { mergeRouteParamsIntoQuery, parseQueryString, urlQueryToSearchParams } from "../utils/query.js";
8
8
  import { tracePagesApiHandler } from "./pages-execution-tracing.js";
9
9
  import { DEFAULT_PAGES_API_BODY_SIZE_LIMIT, resolveBodyParserConfig } from "./pages-body-parser-config.js";
@@ -93,7 +93,7 @@ function readEdgeRequestBody(req) {
93
93
  req.on("error", (error) => controller.error(error));
94
94
  } });
95
95
  }
96
- function createEdgeApiRequest(req, res, url, params, nextConfig) {
96
+ function createEdgeApiRequest(req, res, url, params, requestPathname) {
97
97
  const headers = new Headers();
98
98
  for (const [name, value] of Object.entries(req.headers)) {
99
99
  if (name.startsWith(":")) continue;
@@ -103,8 +103,7 @@ function createEdgeApiRequest(req, res, url, params, nextConfig) {
103
103
  const proto = resolveRequestProtocol(req);
104
104
  const host = resolveRequestHost(req, "localhost");
105
105
  const requestUrl = new URL(req.url ?? url, `${proto}://${host}`);
106
- const basePath = nextConfig?.basePath;
107
- if (basePath && !hasBasePath(requestUrl.pathname, basePath)) requestUrl.pathname = `${basePath}${requestUrl.pathname}`;
106
+ if (requestPathname !== void 0) requestUrl.pathname = requestPathname;
108
107
  const query = mergeRouteParamsIntoQuery(parseQueryString(url), params);
109
108
  requestUrl.search = urlQueryToSearchParams(query).toString();
110
109
  const signal = signalFromNodeResponse(res);
@@ -249,7 +248,7 @@ async function handleApiRoute(runner, req, res, url, apiRoutes, nextConfig) {
249
248
  try {
250
249
  const apiModule = await importModule(runner, route.filePath);
251
250
  if (isEdgeApiRouteModule(apiModule)) {
252
- const nextRequest = new NextRequest(createEdgeApiRequest(req, res, url, params, nextConfig), nextConfig ? { nextConfig: {
251
+ const nextRequest = new NextRequest(createEdgeApiRequest(req, res, url, params, nextConfig?.requestPathname), nextConfig ? { nextConfig: {
253
252
  basePath: nextConfig.basePath,
254
253
  i18n: nextConfig.i18n ?? void 0,
255
254
  trailingSlash: nextConfig.trailingSlash,
@@ -287,7 +286,7 @@ async function handleApiRoute(runner, req, res, url, apiRoutes, nextConfig) {
287
286
  res.end(e.message);
288
287
  return true;
289
288
  }
290
- console.error(e);
289
+ logRequestError(e);
291
290
  await reportRequestError(e, {
292
291
  path: url,
293
292
  method: req.method ?? "GET",
@@ -6,6 +6,7 @@ import { RequestCookies, ResponseCookies } from "../shims/server.js";
6
6
  import { mergeMiddlewareResponseHeaders } from "./middleware-response-headers.js";
7
7
  import { getSetCookieName } from "./cookie-utils.js";
8
8
  import "../shims/internal/cookies.js";
9
+ import { isStreamedActionRedirectResponse, markStreamedActionRedirectResponse } from "./app-rsc-response-finalizer.js";
9
10
  import { createServerActionNotFoundResponse, getServerActionNotFoundMessage } from "./server-action-not-found.js";
10
11
  //#region src/server/app-action-forwarding.ts
11
12
  const ACTION_FORWARD_FORBIDDEN_HEADERS = /* @__PURE__ */ new Set([
@@ -71,11 +72,12 @@ function filterActionForwardResponse(response, sourceMiddlewareHeaders, sourceMi
71
72
  for (const [key, value] of response.headers) if (!ACTION_FORWARD_FORBIDDEN_HEADERS.has(key.toLowerCase())) headers.set(key, value);
72
73
  for (const setCookie of response.headers.getSetCookie()) headers.append("set-cookie", setCookie);
73
74
  const preserveResponseStatus = response.headers.get("x-nextjs-action-not-found") === "1" || response.headers.has("x-action-redirect");
74
- return new Response(response.body, {
75
+ const forwardedResponse = new Response(response.body, {
75
76
  headers,
76
77
  status: preserveResponseStatus ? response.status : sourceMiddlewareStatus ?? response.status,
77
78
  statusText: response.statusText
78
79
  });
80
+ return isStreamedActionRedirectResponse(response) ? markStreamedActionRedirectResponse(forwardedResponse) : forwardedResponse;
79
81
  }
80
82
  function emptyActionForwardResponse(middlewareContext) {
81
83
  const headers = new Headers(middlewareContext.headers ?? void 0);