vinext 1.1.0 → 1.2.1

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 (288) hide show
  1. package/README.md +22 -10
  2. package/dist/build/client-build-config.d.ts +10 -0
  3. package/dist/build/client-build-config.js +18 -2
  4. package/dist/build/output-root-externals.d.ts +21 -0
  5. package/dist/build/output-root-externals.js +40 -0
  6. package/dist/build/pages-client-assets-module.d.ts +8 -0
  7. package/dist/build/pages-client-assets-module.js +21 -1
  8. package/dist/build/prerender-paths.js +6 -4
  9. package/dist/build/prerender.js +1 -1
  10. package/dist/build/run-prerender.js +1 -1
  11. package/dist/build/ssr-manifest.d.ts +2 -0
  12. package/dist/build/ssr-manifest.js +6 -2
  13. package/dist/check.js +12 -12
  14. package/dist/cli-dev-config.js +3 -3
  15. package/dist/cli.js +3 -2
  16. package/dist/client/client-redirect-matcher.d.ts +14 -0
  17. package/dist/client/client-redirect-matcher.js +37 -0
  18. package/dist/client/client-redirects.d.ts +27 -0
  19. package/dist/client/client-redirects.js +28 -0
  20. package/dist/client/client-rewrites.d.ts +2 -1
  21. package/dist/client/client-rewrites.js +1 -1
  22. package/dist/config/config-matchers.d.ts +26 -11
  23. package/dist/config/config-matchers.js +90 -88
  24. package/dist/config/destination-params.d.ts +47 -0
  25. package/dist/config/destination-params.js +116 -0
  26. package/dist/config/next-config.d.ts +7 -0
  27. package/dist/config/next-config.js +3 -0
  28. package/dist/entries/app-rsc-entry.d.ts +4 -0
  29. package/dist/entries/app-rsc-entry.js +20 -6
  30. package/dist/entries/pages-client-entry.js +3 -1
  31. package/dist/entries/pages-server-entry.js +13 -37
  32. package/dist/index.d.ts +3 -1
  33. package/dist/index.js +246 -136
  34. package/dist/init-cloudflare.d.ts +9 -0
  35. package/dist/init-cloudflare.js +107 -31
  36. package/dist/init-platform.d.ts +4 -0
  37. package/dist/init-platform.js +27 -2
  38. package/dist/init.d.ts +2 -1
  39. package/dist/init.js +153 -9
  40. package/dist/plugins/ast-scope.d.ts +7 -0
  41. package/dist/plugins/ast-scope.js +59 -3
  42. package/dist/plugins/ast-utils.d.ts +53 -2
  43. package/dist/plugins/ast-utils.js +90 -2
  44. package/dist/plugins/cloudflare-dev-module-urls.d.ts +41 -0
  45. package/dist/plugins/cloudflare-dev-module-urls.js +271 -0
  46. package/dist/plugins/commonjs-esm-facade.js +2 -2
  47. package/dist/plugins/css-module-imports.js +13 -20
  48. package/dist/plugins/dynamic-preload-metadata.d.ts +1 -1
  49. package/dist/plugins/dynamic-preload-metadata.js +11 -11
  50. package/dist/plugins/extensionless-dynamic-import.js +2 -3
  51. package/dist/plugins/fonts.d.ts +1 -1
  52. package/dist/plugins/fonts.js +3 -3
  53. package/dist/plugins/ignore-dynamic-requests.d.ts +1 -1
  54. package/dist/plugins/ignore-dynamic-requests.js +5 -7
  55. package/dist/plugins/import-meta-url.js +3 -4
  56. package/dist/plugins/instrumentation-client.js +3 -2
  57. package/dist/plugins/middleware-export-validation.js +2 -3
  58. package/dist/plugins/middleware-server-only.d.ts +5 -4
  59. package/dist/plugins/middleware-server-only.js +8 -5
  60. package/dist/plugins/og-harfbuzz.js +2 -3
  61. package/dist/plugins/optimize-imports.js +3 -4
  62. package/dist/plugins/pages-node-externals.js +3 -4
  63. package/dist/plugins/remove-console.js +2 -18
  64. package/dist/plugins/require-condition-resolution.js +3 -3
  65. package/dist/plugins/require-context.js +2 -3
  66. package/dist/plugins/rsc-scan-glob-imports.d.ts +19 -0
  67. package/dist/plugins/rsc-scan-glob-imports.js +50 -0
  68. package/dist/plugins/strip-server-exports.js +265 -102
  69. package/dist/plugins/styled-jsx.js +2 -3
  70. package/dist/plugins/transform-result.d.ts +9 -5
  71. package/dist/plugins/transitive-externals.js +1 -1
  72. package/dist/plugins/typeof-window.d.ts +2 -2
  73. package/dist/plugins/use-cache-callable.d.ts +21 -2
  74. package/dist/plugins/use-cache-callable.js +84 -35
  75. package/dist/plugins/worker-image-imports.js +30 -33
  76. package/dist/plugins/worker-server-only-imports.d.ts +20 -0
  77. package/dist/plugins/worker-server-only-imports.js +31 -0
  78. package/dist/plugins/worker-use-server.d.ts +25 -0
  79. package/dist/plugins/worker-use-server.js +153 -0
  80. package/dist/server/api-handler.d.ts +1 -0
  81. package/dist/server/api-handler.js +3 -2
  82. package/dist/server/app-action-forwarding.js +2 -2
  83. package/dist/server/app-browser-action-result.d.ts +1 -1
  84. package/dist/server/app-browser-action-result.js +9 -2
  85. package/dist/server/app-browser-entry.js +3 -3
  86. package/dist/server/app-browser-interception-context.js +1 -1
  87. package/dist/server/app-browser-navigation-controller.js +1 -1
  88. package/dist/server/app-browser-prefetch-response.js +2 -2
  89. package/dist/server/app-browser-server-action-client.js +1 -1
  90. package/dist/server/app-browser-state.js +1 -1
  91. package/dist/server/app-browser-supplemental-refresh.js +1 -1
  92. package/dist/server/app-middleware.d.ts +11 -0
  93. package/dist/server/app-middleware.js +17 -2
  94. package/dist/server/app-optimistic-routing.js +1 -1
  95. package/dist/server/app-page-cache.d.ts +1 -1
  96. package/dist/server/app-page-cache.js +1 -1
  97. package/dist/server/app-page-dispatch.d.ts +3 -1
  98. package/dist/server/app-page-dispatch.js +5 -4
  99. package/dist/server/app-page-ppr-runtime.js +1 -1
  100. package/dist/server/app-page-render-observation.js +1 -1
  101. package/dist/server/app-page-render.js +1 -1
  102. package/dist/server/app-page-request.js +53 -15
  103. package/dist/server/app-page-route-wiring.js +2 -2
  104. package/dist/server/app-pages-bridge.d.ts +1 -1
  105. package/dist/server/app-pages-bridge.js +3 -4
  106. package/dist/server/app-prerender-endpoints.js +9 -6
  107. package/dist/server/app-render-abort-error.d.ts +19 -0
  108. package/dist/server/app-render-abort-error.js +31 -1
  109. package/dist/server/app-request-stage-dispatch.js +6 -2
  110. package/dist/server/app-request-stage-independent-entry.js +29 -9
  111. package/dist/server/app-response-stage-entry.js +11 -3
  112. package/dist/server/app-route-handler-cache.d.ts +1 -0
  113. package/dist/server/app-route-handler-cache.js +1 -0
  114. package/dist/server/app-route-handler-dispatch.d.ts +1 -0
  115. package/dist/server/app-route-handler-dispatch.js +5 -2
  116. package/dist/server/app-route-handler-execution.d.ts +1 -0
  117. package/dist/server/app-route-handler-execution.js +1 -0
  118. package/dist/server/app-route-handler-runtime.d.ts +4 -1
  119. package/dist/server/app-route-handler-runtime.js +46 -25
  120. package/dist/server/app-router-entry.js +29 -10
  121. package/dist/server/app-rsc-cache-busting.d.ts +16 -0
  122. package/dist/server/app-rsc-cache-busting.js +30 -13
  123. package/dist/server/app-rsc-errors.js +20 -16
  124. package/dist/server/app-rsc-handler.d.ts +7 -0
  125. package/dist/server/app-rsc-handler.js +89 -39
  126. package/dist/server/app-rsc-request-normalization.js +3 -3
  127. package/dist/server/app-rsc-response-stage.js +5 -3
  128. package/dist/server/app-segment-config.d.ts +2 -1
  129. package/dist/server/app-server-action-execution.d.ts +2 -1
  130. package/dist/server/app-server-action-execution.js +62 -23
  131. package/dist/server/app-ssr-entry.js +7 -5
  132. package/dist/server/app-visited-response-cache.js +2 -2
  133. package/dist/server/app-worker-stages.d.ts +5 -0
  134. package/dist/server/app-worker-stages.js +1 -1
  135. package/dist/server/cacheability-request.js +2 -2
  136. package/dist/server/client-build-files.d.ts +16 -0
  137. package/dist/server/client-build-files.js +36 -0
  138. package/dist/server/client-reuse-manifest.js +1 -1
  139. package/dist/server/config-headers.js +1 -1
  140. package/dist/server/defer-until-stream-consumed.d.ts +2 -1
  141. package/dist/server/defer-until-stream-consumed.js +3 -1
  142. package/dist/server/dev-server.js +39 -28
  143. package/dist/server/dev-stack-sourcemap.js +18 -23
  144. package/dist/server/document-initial-head.d.ts +0 -11
  145. package/dist/server/document-initial-head.js +1 -1
  146. package/dist/server/forwarded-middleware-context.d.ts +5 -0
  147. package/dist/server/forwarded-middleware-context.js +22 -0
  148. package/dist/server/headers.d.ts +4 -1
  149. package/dist/server/headers.js +4 -1
  150. package/dist/server/http-conditional.d.ts +11 -0
  151. package/dist/server/http-conditional.js +10 -1
  152. package/dist/server/http-error-responses.d.ts +1 -1
  153. package/dist/server/image-optimization.d.ts +38 -0
  154. package/dist/server/image-optimization.js +94 -4
  155. package/dist/server/isr-cache.d.ts +10 -0
  156. package/dist/server/isr-cache.js +41 -6
  157. package/dist/server/metadata-route-response.js +11 -10
  158. package/dist/server/middleware-matcher.d.ts +2 -0
  159. package/dist/server/middleware-matcher.js +23 -2
  160. package/dist/server/middleware-path-to-regexp.js +1 -1
  161. package/dist/server/middleware-runtime.d.ts +13 -0
  162. package/dist/server/middleware-runtime.js +38 -25
  163. package/dist/server/middleware.d.ts +1 -1
  164. package/dist/server/middleware.js +3 -1
  165. package/dist/server/multi-stage.d.ts +11 -0
  166. package/dist/server/navigation-planner.js +1 -1
  167. package/dist/server/normalize-path.d.ts +0 -13
  168. package/dist/server/normalize-path.js +1 -34
  169. package/dist/server/open-redirect.d.ts +11 -4
  170. package/dist/server/open-redirect.js +11 -4
  171. package/dist/server/pages-api-route.d.ts +3 -2
  172. package/dist/server/pages-api-route.js +6 -5
  173. package/dist/server/pages-cache-tag.d.ts +12 -0
  174. package/dist/server/pages-cache-tag.js +19 -0
  175. package/dist/server/pages-client-assets.d.ts +2 -0
  176. package/dist/server/pages-data-route.d.ts +10 -0
  177. package/dist/server/pages-data-route.js +21 -3
  178. package/dist/server/pages-document-asset-props.d.ts +5 -1
  179. package/dist/server/pages-document-asset-props.js +13 -5
  180. package/dist/server/pages-document-splice.d.ts +19 -0
  181. package/dist/server/pages-document-splice.js +55 -0
  182. package/dist/server/pages-i18n.d.ts +12 -1
  183. package/dist/server/pages-i18n.js +18 -4
  184. package/dist/server/pages-node-compat.d.ts +16 -5
  185. package/dist/server/pages-node-compat.js +20 -12
  186. package/dist/server/pages-page-data.d.ts +32 -24
  187. package/dist/server/pages-page-data.js +51 -121
  188. package/dist/server/pages-page-handler.d.ts +0 -6
  189. package/dist/server/pages-page-handler.js +189 -127
  190. package/dist/server/pages-page-response.d.ts +12 -6
  191. package/dist/server/pages-page-response.js +42 -35
  192. package/dist/server/pages-request-pipeline.d.ts +10 -2
  193. package/dist/server/pages-request-pipeline.js +41 -22
  194. package/dist/server/pages-request-stage-entry.d.ts +6 -0
  195. package/dist/server/pages-request-stage-entry.js +75 -16
  196. package/dist/server/pages-response-stage-entry.js +4 -0
  197. package/dist/server/pages-revalidate.js +3 -3
  198. package/dist/server/pages-router-entry.js +5 -4
  199. package/dist/server/prerender-manifest.d.ts +3 -3
  200. package/dist/server/prerender-manifest.js +4 -3
  201. package/dist/server/prerender-work-unit-setup.js +4 -0
  202. package/dist/server/prod-server.d.ts +12 -0
  203. package/dist/server/prod-server.js +204 -74
  204. package/dist/server/proper-error.d.ts +4 -0
  205. package/dist/server/proper-error.js +31 -0
  206. package/dist/server/request-pipeline.d.ts +4 -1
  207. package/dist/server/request-pipeline.js +3 -2
  208. package/dist/server/revalidation-request.d.ts +1 -0
  209. package/dist/server/revalidation-request.js +1 -1
  210. package/dist/server/rsc-stream-hints.d.ts +1 -1
  211. package/dist/server/rsc-stream-hints.js +52 -5
  212. package/dist/server/seed-cache.d.ts +1 -0
  213. package/dist/server/seed-cache.js +5 -4
  214. package/dist/server/server-reference-loader.d.ts +4 -0
  215. package/dist/server/server-reference-loader.js +29 -0
  216. package/dist/server/socket-error-backstop.d.ts +9 -1
  217. package/dist/server/socket-error-backstop.js +46 -17
  218. package/dist/server/static-file-signal.d.ts +15 -4
  219. package/dist/server/static-file-signal.js +46 -12
  220. package/dist/server/worker-image-optimization.d.ts +34 -0
  221. package/dist/server/worker-image-optimization.js +99 -0
  222. package/dist/server/worker-stages.d.ts +1 -1
  223. package/dist/server/worker-stages.js +2 -2
  224. package/dist/server/worker-utils.d.ts +8 -3
  225. package/dist/server/worker-utils.js +12 -7
  226. package/dist/shims/app-router-scroll.js +4 -1
  227. package/dist/shims/app-router-tree-context.d.ts +5 -0
  228. package/dist/shims/app-router-tree-context.js +27 -0
  229. package/dist/shims/cache-callable-runtime.d.ts +4 -2
  230. package/dist/shims/cache-callable-runtime.js +21 -12
  231. package/dist/shims/cache-handler.js +1 -9
  232. package/dist/shims/cache-runtime.d.ts +6 -1
  233. package/dist/shims/cache-runtime.js +2 -1
  234. package/dist/shims/cache.d.ts +2 -2
  235. package/dist/shims/cache.js +40 -13
  236. package/dist/shims/cdn-cache.d.ts +8 -0
  237. package/dist/shims/cdn-cache.js +17 -1
  238. package/dist/shims/disable-smooth-scroll.d.ts +3 -0
  239. package/dist/shims/disable-smooth-scroll.js +36 -0
  240. package/dist/shims/document-placeholders.d.ts +19 -0
  241. package/dist/shims/document-placeholders.js +45 -0
  242. package/dist/shims/document.js +21 -5
  243. package/dist/shims/dynamic-preload-chunks.d.ts +1 -0
  244. package/dist/shims/dynamic-preload-chunks.js +13 -9
  245. package/dist/shims/dynamic.d.ts +1 -1
  246. package/dist/shims/dynamic.js +60 -8
  247. package/dist/shims/fetch-cache-state.d.ts +66 -0
  248. package/dist/shims/fetch-cache-state.js +92 -0
  249. package/dist/shims/fetch-cache.d.ts +3 -67
  250. package/dist/shims/fetch-cache.js +160 -125
  251. package/dist/shims/head.d.ts +10 -6
  252. package/dist/shims/head.js +16 -14
  253. package/dist/shims/headers.js +2 -2
  254. package/dist/shims/image.js +2 -1
  255. package/dist/shims/internal/hybrid-client-route-owner-direct.js +1 -1
  256. package/dist/shims/internal/hybrid-client-route-owner.js +1 -0
  257. package/dist/shims/internal/work-store-scope.d.ts +5 -0
  258. package/dist/shims/internal/work-store-scope.js +24 -0
  259. package/dist/shims/link-prefetch.js +5 -4
  260. package/dist/shims/link.js +2 -2
  261. package/dist/shims/navigation.js +34 -9
  262. package/dist/shims/request-context.d.ts +2 -0
  263. package/dist/shims/request-state-types.d.ts +2 -1
  264. package/dist/shims/router.js +21 -32
  265. package/dist/shims/server.d.ts +27 -4
  266. package/dist/shims/server.js +65 -9
  267. package/dist/shims/unified-request-context.d.ts +1 -1
  268. package/dist/shims/url-utils.d.ts +9 -0
  269. package/dist/shims/url-utils.js +15 -4
  270. package/dist/utils/client-runtime-metadata.js +1 -1
  271. package/dist/utils/domain-locale.js +1 -1
  272. package/dist/utils/hash.d.ts +7 -0
  273. package/dist/utils/hash.js +11 -1
  274. package/dist/utils/package-name.d.ts +6 -0
  275. package/dist/utils/package-name.js +12 -1
  276. package/dist/utils/plugin-rsc-version.d.ts +6 -0
  277. package/dist/utils/plugin-rsc-version.js +41 -0
  278. package/dist/utils/prerender-output-paths.js +15 -4
  279. package/dist/utils/public-file-path.d.ts +10 -2
  280. package/dist/utils/public-file-path.js +13 -9
  281. package/dist/utils/public-routes.js +2 -2
  282. package/dist/utils/react-version.d.ts +10 -0
  283. package/dist/utils/react-version.js +39 -16
  284. package/package.json +11 -7
  285. package/dist/build/nitro-route-rules.d.ts +0 -47
  286. package/dist/build/nitro-route-rules.js +0 -79
  287. package/dist/plugins/cache-flight-codec.d.ts +0 -19
  288. package/dist/plugins/cache-flight-codec.js +0 -68
@@ -1,8 +1,11 @@
1
1
  import { createRequestContext, runWithRequestContext } from "../shims/unified-request-context.js";
2
- import { runWithExecutionContext } from "../shims/request-context.js";
3
2
  import { serializeStaticFileSignalForTransport } from "./static-file-signal.js";
3
+ import { attachRequestCfMetadata } from "./request-pipeline.js";
4
+ import { runWithExecutionContext } from "../shims/request-context.js";
5
+ import { runInWorkStoreScope } from "../shims/internal/work-store-scope.js";
4
6
  import { validateCdnRequest } from "./cache-control.js";
5
7
  import { ensureFetchPatch } from "../shims/fetch-cache.js";
8
+ import { serializeImageOptimizationSignalForTransport } from "./image-optimization.js";
6
9
  import { serializeResponseStageLinkProvenance } from "./app-response-header-provenance.js";
7
10
  import { attachFrameworkRequestError, attachFrameworkRequestRoute, captureFrameworkRequestRoute, clearFrameworkRequestError } from "./request-tracing.js";
8
11
  import { isAppWorkerResponseStageProps } from "./app-worker-stages.js";
@@ -26,10 +29,11 @@ async function invokeCacheFunction(invocation, env, platformCtx, dispatchRequest
26
29
  executionContext,
27
30
  rootParams: invocation.rootParams
28
31
  });
29
- await runWithRequestContext(context, () => invokeRegisteredCacheFunction(invocation, loadServerAction));
32
+ await runWithRequestContext(context, () => runInWorkStoreScope(() => invokeRegisteredCacheFunction(invocation, loadServerAction)));
30
33
  }
31
34
  async function handleResponseStage(request, env, platformCtx, props, dispatchRequestStage, options = { cache: "bypass" }) {
32
35
  if (!isAppWorkerResponseStageProps(props)) return new Response("Invalid vinext App response stage", { status: 400 });
36
+ if (props.kind === "app-full-request" && props.headRequest) request = attachRequestCfMetadata(new Request(request, { method: "HEAD" }), request);
33
37
  if (props.requestOrigin !== new URL(request.url).origin) return new Response("Invalid vinext App response stage", { status: 400 });
34
38
  const currentBuildId = process.env.__VINEXT_BUILD_ID ?? null;
35
39
  if (props.buildId !== currentBuildId) return new Response("Incompatible vinext App response stage", { status: 409 });
@@ -41,6 +45,10 @@ async function handleResponseStage(request, env, platformCtx, props, dispatchReq
41
45
  ...ctx,
42
46
  isPrerenderPathDiscovery: true
43
47
  };
48
+ else if (props.kind === "hybrid-pages" && options.regeneratesStoredEntry === true) ctx = {
49
+ ...ctx,
50
+ isStoredEntryRegeneration: true
51
+ };
44
52
  return withResponseStageCacheability({
45
53
  buildId: process.env.__VINEXT_BUILD_ID,
46
54
  cache: options.cache,
@@ -76,7 +84,7 @@ async function handleResponseStage(request, env, platformCtx, props, dispatchReq
76
84
  failure = error;
77
85
  result = new Response("Internal Server Error", { status: 500 });
78
86
  }
79
- const serialized = serializeStaticFileSignalForTransport(result, props.staticFileSignalToken);
87
+ const serialized = serializeImageOptimizationSignalForTransport(serializeStaticFileSignalForTransport(result, props.staticFileSignalToken), props.staticFileSignalToken);
80
88
  return attachFrameworkRequestRoute(failed ? attachFrameworkRequestError(serialized, failure) : clearFrameworkRequestError(serialized), route);
81
89
  }
82
90
  const render = () => rscHandler.handleResponseStage(request, cacheabilityContext, props, options);
@@ -19,6 +19,7 @@ type ReadAppRouteHandlerCacheOptions = {
19
19
  handlerFn: AppRouteHandlerFunction;
20
20
  i18n?: NextI18nConfig | null;
21
21
  trailingSlash?: boolean;
22
+ skipTrailingSlashRedirect?: boolean;
22
23
  isAutoHead: boolean;
23
24
  appendResponseLink?: boolean;
24
25
  isrDebug?: AppRouteDebugLogger;
@@ -49,6 +49,7 @@ async function readAppRouteHandlerCacheResponse(options) {
49
49
  handlerFn: options.handlerFn,
50
50
  i18n: options.i18n,
51
51
  trailingSlash: options.trailingSlash,
52
+ skipTrailingSlashRedirect: options.skipTrailingSlashRedirect,
52
53
  markDynamicUsage: options.markDynamicUsage,
53
54
  params: options.params === null ? null : makeThenableParams(options.params),
54
55
  request: new Request(options.requestUrl, { method: "GET" }),
@@ -31,6 +31,7 @@ type DispatchAppRouteHandlerOptions = {
31
31
  isrRouteKey: (pathname: string) => string;
32
32
  isrSet: RouteHandlerCacheSetter;
33
33
  trailingSlash?: boolean;
34
+ skipTrailingSlashRedirect?: boolean;
34
35
  middlewareContext: RouteHandlerMiddlewareContext;
35
36
  middlewareRequestHeaders?: Headers | null;
36
37
  /**
@@ -1,6 +1,7 @@
1
1
  import { closeAfterResponse, createRequestContext, runWithRequestContext } from "../shims/unified-request-context.js";
2
2
  import "../utils/protocol-headers.js";
3
3
  import { getRequestExecutionContext } from "../shims/request-context.js";
4
+ import { runInWorkStoreScope } from "../shims/internal/work-store-scope.js";
4
5
  import { reportRequestError } from "./instrumentation.js";
5
6
  import { beginRouteCacheability, getRouteCacheabilityDynamicReason } from "../shims/cacheability-classification.js";
6
7
  import { applyCdnResponseBuildIdentityHeaders } from "./cache-control.js";
@@ -41,7 +42,7 @@ async function runInRouteHandlerRevalidationContext(options, renderFn) {
41
42
  executionContext: getRequestExecutionContext(),
42
43
  unstableCacheRevalidation: "foreground"
43
44
  });
44
- const revalidation = runWithRequestContext(requestContext, async () => {
45
+ const revalidation = runWithRequestContext(requestContext, () => runInWorkStoreScope(async () => {
45
46
  ensureFetchPatch();
46
47
  setCurrentFetchSoftTags(buildRouteHandlerPageCacheTags(options.cleanPathname, [], options.routeSegments));
47
48
  setCurrentFetchCacheMode(options.fetchCacheMode);
@@ -52,7 +53,7 @@ async function runInRouteHandlerRevalidationContext(options, renderFn) {
52
53
  } finally {
53
54
  await _drainPendingRevalidations();
54
55
  }
55
- });
56
+ }));
56
57
  try {
57
58
  await revalidation;
58
59
  } finally {
@@ -122,6 +123,7 @@ async function dispatchAppRouteHandlerImpl(options) {
122
123
  handlerFn: resolvedHandlerFn,
123
124
  i18n: options.i18n,
124
125
  trailingSlash: options.trailingSlash,
126
+ skipTrailingSlashRedirect: options.skipTrailingSlashRedirect,
125
127
  isAutoHead,
126
128
  appendResponseLink,
127
129
  isrDebug: options.isrDebug,
@@ -184,6 +186,7 @@ async function dispatchAppRouteHandlerImpl(options) {
184
186
  handlerFn: resolvedHandlerFn,
185
187
  i18n: options.i18n,
186
188
  trailingSlash: options.trailingSlash,
189
+ skipTrailingSlashRedirect: options.skipTrailingSlashRedirect,
187
190
  isAutoHead,
188
191
  initialDraftModeCookie,
189
192
  isDraftMode,
@@ -47,6 +47,7 @@ type RunAppRouteHandlerOptions = {
47
47
  i18n?: NextI18nConfig | null;
48
48
  isDraftMode?: boolean;
49
49
  trailingSlash?: boolean;
50
+ skipTrailingSlashRedirect?: boolean;
50
51
  markDynamicUsage: MarkAppRouteDynamicUsageFn;
51
52
  middlewareRequestHeaders?: Headers | null;
52
53
  /**
@@ -112,6 +112,7 @@ async function runAppRouteHandler(options) {
112
112
  basePath: options.basePath,
113
113
  i18n: options.i18n,
114
114
  trailingSlash: options.trailingSlash,
115
+ skipTrailingSlashRedirect: options.skipTrailingSlashRedirect,
115
116
  middlewareHeaders: options.middlewareRequestHeaders,
116
117
  onDynamicAccess() {
117
118
  options.markDynamicUsage();
@@ -17,13 +17,16 @@ export declare function buildRouteHandlerAllowHeader(exportedMethods: readonly s
17
17
  export declare function isKnownDynamicAppRoute(pattern: string): boolean;
18
18
  export declare function markKnownDynamicAppRoute(pattern: string): void;
19
19
  type RequestDynamicAccess = "request.headers" | "request.cookies" | "request.cf" | "request.ip" | "request.geo" | "request.url" | "request.body" | "request.blob" | "request.bytes" | "request.json" | "request.text" | "request.arrayBuffer" | "request.formData";
20
- type NextUrlDynamicAccess = "nextUrl.search" | "nextUrl.searchParams" | "nextUrl.url" | "nextUrl.href" | "nextUrl.toJSON" | "nextUrl.toString" | "nextUrl.origin";
20
+ type NextUrlDynamicAccess = "nextUrl.search" | "nextUrl.searchParams" | "nextUrl.url" | "nextUrl.href" | "nextUrl.toJSON" | "nextUrl.toString" | "nextUrl.origin" | HostDerivedNextUrlDynamicAccess;
21
+ type HostDerivedNextUrlDynamicAccess = "nextUrl.protocol" | "nextUrl.username" | "nextUrl.password" | "nextUrl.host" | "nextUrl.hostname" | "nextUrl.port" | DomainLocaleNextUrlDynamicAccess;
22
+ type DomainLocaleNextUrlDynamicAccess = "nextUrl.locale" | "nextUrl.defaultLocale" | "nextUrl.domainLocale";
21
23
  type AppRouteDynamicRequestAccess = RequestDynamicAccess | NextUrlDynamicAccess;
22
24
  type AppRouteRequestMode = "auto" | "force-static" | "error";
23
25
  type TrackedAppRouteRequestOptions = {
24
26
  basePath?: string;
25
27
  i18n?: NextI18nConfig | null;
26
28
  trailingSlash?: boolean;
29
+ skipTrailingSlashRedirect?: boolean;
27
30
  middlewareHeaders?: Headers | null;
28
31
  onDynamicAccess?: (access: AppRouteDynamicRequestAccess) => void;
29
32
  requestMode?: AppRouteRequestMode;
@@ -1,7 +1,7 @@
1
1
  import { buildRequestHeadersFromMiddlewareResponse } from "../utils/middleware-request-headers.js";
2
2
  import { addBasePathToPathname } from "../utils/base-path.js";
3
- import { NextRequest, RequestCookies, sealRequestCookies, sealRequestHeaders } from "../shims/server.js";
4
3
  import { cloneRequestWithHeaders, cloneRequestWithUrl } from "./request-pipeline.js";
4
+ import { NextRequest, RequestCookies, sealRequestCookies, sealRequestHeaders } from "../shims/server.js";
5
5
  import { BUILT_IN_REQUEST_PROPERTIES } from "./app-route-request-built-ins.js";
6
6
  //#region src/server/app-route-handler-runtime.ts
7
7
  const BUILT_IN_REQUEST_PROPERTY_MAP = new Map(BUILT_IN_REQUEST_PROPERTIES.map((property) => [property.key, property]));
@@ -64,11 +64,12 @@ function getRequestProperty(target, prop, receiver, builtInProperties) {
64
64
  return bindMethodIfNeeded(Reflect.get(target, prop, bindingTarget), bindingTarget);
65
65
  }
66
66
  function buildNextConfig(options) {
67
- if (!options.basePath && !options.i18n && !options.trailingSlash) return null;
67
+ if (!options.basePath && !options.i18n && !options.trailingSlash && !options.skipTrailingSlashRedirect) return null;
68
68
  return {
69
69
  basePath: options.basePath,
70
70
  i18n: options.i18n ?? void 0,
71
- trailingSlash: options.trailingSlash
71
+ trailingSlash: options.trailingSlash,
72
+ skipTrailingSlashRedirect: options.skipTrailingSlashRedirect
72
73
  };
73
74
  }
74
75
  function createForceStaticNextRequest(input, nextConfig) {
@@ -128,21 +129,41 @@ function createTrackedAppRouteRequest(request, options = {}) {
128
129
  didAccessDynamicRequest = true;
129
130
  options.onDynamicAccess?.(access);
130
131
  };
132
+ const isNextDynamicNextUrlProperty = (prop) => {
133
+ switch (prop) {
134
+ case "search":
135
+ case "searchParams":
136
+ case "url":
137
+ case "href":
138
+ case "toJSON":
139
+ case "toString":
140
+ case "origin": return true;
141
+ default: return false;
142
+ }
143
+ };
144
+ const hasI18nDomains = (options.i18n?.domains?.length ?? 0) > 0;
145
+ const isHostDerivedNextUrlProperty = (prop) => {
146
+ switch (prop) {
147
+ case "protocol":
148
+ case "username":
149
+ case "password":
150
+ case "host":
151
+ case "hostname":
152
+ case "port": return true;
153
+ case "locale":
154
+ case "defaultLocale":
155
+ case "domainLocale": return hasI18nDomains;
156
+ default: return false;
157
+ }
158
+ };
131
159
  const wrapNextUrl = (nextUrl) => {
132
160
  return new Proxy(nextUrl, { get(target, prop) {
133
- switch (prop) {
134
- case "search":
135
- case "searchParams":
136
- case "url":
137
- case "href":
138
- case "toJSON":
139
- case "toString":
140
- case "origin":
141
- markDynamicAccess(`nextUrl.${String(prop)}`);
142
- return bindMethodIfNeeded(Reflect.get(target, prop, target), target);
143
- case "clone": return () => wrapNextUrl(target.clone());
144
- default: return bindMethodIfNeeded(Reflect.get(target, prop, target), target);
161
+ if (isNextDynamicNextUrlProperty(prop) || isHostDerivedNextUrlProperty(prop)) {
162
+ markDynamicAccess(`nextUrl.${String(prop)}`);
163
+ return bindMethodIfNeeded(Reflect.get(target, prop, target), target);
145
164
  }
165
+ if (prop === "clone") return () => wrapNextUrl(target.clone());
166
+ return bindMethodIfNeeded(Reflect.get(target, prop, target), target);
146
167
  } });
147
168
  };
148
169
  const wrapForceStaticNextUrl = (nextUrl) => {
@@ -165,18 +186,18 @@ function createTrackedAppRouteRequest(request, options = {}) {
165
186
  throw new Error(options.staticGenerationErrorMessage?.(expression) ?? `Route handler with \`dynamic = "error"\` used ${expression}.`);
166
187
  };
167
188
  const wrapRequireStaticNextUrl = (nextUrl) => {
189
+ let staticNextUrl = null;
168
190
  return new Proxy(nextUrl, { get(target, prop) {
169
- switch (prop) {
170
- case "search":
171
- case "searchParams":
172
- case "url":
173
- case "href":
174
- case "toJSON":
175
- case "toString":
176
- case "origin": return throwStaticGenerationError(`nextUrl.${String(prop)}`);
177
- case "clone": return () => wrapRequireStaticNextUrl(target.clone());
178
- default: return bindMethodIfNeeded(Reflect.get(target, prop, target), target);
191
+ if (isNextDynamicNextUrlProperty(prop)) return throwStaticGenerationError(`nextUrl.${String(prop)}`);
192
+ if (isHostDerivedNextUrlProperty(prop)) {
193
+ if (staticNextUrl === null) {
194
+ staticNextUrl = target.clone();
195
+ staticNextUrl.href = cleanStaticUrl(target.href);
196
+ }
197
+ return Reflect.get(staticNextUrl, prop, staticNextUrl);
179
198
  }
199
+ if (prop === "clone") return () => wrapRequireStaticNextUrl(target.clone());
200
+ return bindMethodIfNeeded(Reflect.get(target, prop, target), target);
180
201
  } });
181
202
  };
182
203
  const wrapRequest = (rawInput) => {
@@ -1,24 +1,26 @@
1
1
  import { VINEXT_PRERENDER_ROUTE_PARAMS_HEADER, VINEXT_PRERENDER_SECRET_HEADER, VINEXT_REVALIDATE_HOST_HEADER } from "../utils/protocol-headers.js";
2
- import { assetPrefixPathname, isNextStaticPath } from "../utils/asset-prefix.js";
3
- import { runWithExecutionContext } from "../shims/request-context.js";
4
- import "./server-globals.js";
5
2
  import "./app-rsc-vary.js";
6
3
  import { VINEXT_CACHEABILITY_PROBE_QUERY_PARAM } from "./headers.js";
7
4
  import { badRequestResponse, notFoundResponse, notFoundStaticAssetResponse } from "./http-error-responses.js";
8
- import { cloneRequestWithHeaders, filterInternalHeaders, guardProtocolRelativeUrl } from "./request-pipeline.js";
5
+ import { cloneRequestWithHeaders, cloneRequestWithUrl, filterInternalHeaders, guardProtocolRelativeUrl } from "./request-pipeline.js";
6
+ import { assetPrefixPathname, isNextStaticPath } from "../utils/asset-prefix.js";
7
+ import { runWithExecutionContext } from "../shims/request-context.js";
8
+ import "./server-globals.js";
9
9
  import { getCdnCacheAdapter } from "../shims/cdn-cache.js";
10
10
  import { applyCdnResponseIdentityHeaders, validateCdnRequest } from "./cache-control.js";
11
- import { getImageOptimizer, handleConfiguredImageOptimization, isImageOptimizationPath } from "./image-optimization.js";
11
+ import { getImageOptimizer, readImageOptimizationSignal } from "./image-optimization.js";
12
12
  import { traceFrameworkRequest } from "./request-tracing.js";
13
13
  import { createStaticAssetRequest, resolveStaticAssetSignal } from "./worker-utils.js";
14
14
  import { readTrustedPrerenderRouteParams, serializePrerenderRouteParamsHeader } from "./prerender-route-params.js";
15
+ import { resolveClientBuildFile } from "./client-build-files.js";
15
16
  import { createWorkerRevalidationContext } from "./worker-revalidation-context.js";
16
17
  import { createWorkerPrerenderDiscoveryContext, createWorkerPrerenderReadinessResponse } from "./worker-prerender-discovery.js";
17
18
  import { registerConfiguredImageOptimizer } from "virtual:vinext-image-adapters";
18
19
  import { registerConfiguredCacheAdapters } from "virtual:vinext-cache-adapters";
19
- import rscHandler, { __assetPrefix, __basePath, __cacheabilityManifest, __ensureHybridPagesApplication, __ensureInstrumentation, __imageAllowedWidths, __imageConfig, __prerenderSecret } from "virtual:vinext-rsc-entry";
20
+ import rscHandler, { __assetPrefix, __basePath, __cacheabilityManifest, __clientBuildFiles, __ensureHybridPagesApplication, __ensureInstrumentation, __imageAllowedWidths, __imageConfig, __prerenderSecret } from "virtual:vinext-rsc-entry";
20
21
  //#region src/server/app-router-entry.ts
21
22
  const __workerBasePath = typeof __basePath === "string" ? __basePath : "";
23
+ const __workerClientBuildFiles = new Set(Array.isArray(__clientBuildFiles) ? __clientBuildFiles : []);
22
24
  const __workerAssetPathPrefix = assetPrefixPathname(typeof __assetPrefix === "string" ? __assetPrefix : "");
23
25
  function isPotentialCompletedAdmissionRequest(request) {
24
26
  if (request.method !== "GET" && request.method !== "HEAD") return false;
@@ -64,7 +66,7 @@ async function handleRequest(request, env, platformCtx) {
64
66
  const probeUrl = new URL(request.url);
65
67
  if (probeUrl.searchParams.has("__vinext_cacheability_probe")) {
66
68
  probeUrl.searchParams.delete(VINEXT_CACHEABILITY_PROBE_QUERY_PARAM);
67
- request = new Request(probeUrl, request);
69
+ request = cloneRequestWithUrl(request, probeUrl.href);
68
70
  }
69
71
  }
70
72
  }
@@ -81,7 +83,7 @@ async function handleRequest(request, env, platformCtx) {
81
83
  const cdnValidationResponse = await validateCdnRequest(request);
82
84
  if (cdnValidationResponse) return cdnValidationResponse;
83
85
  const url = new URL(request.url);
84
- if (isImageOptimizationPath(url.pathname) && assets && getImageOptimizer()) return handleConfiguredImageOptimization(request, (assetPath) => Promise.resolve(assets.fetch(new Request(new URL(assetPath, request.url)))), __imageAllowedWidths, __imageConfig);
86
+ const servesImages = Boolean(assets) && getImageOptimizer() !== null;
85
87
  const protocolRelativeGuard = guardProtocolRelativeUrl(url.pathname, url.search);
86
88
  if (protocolRelativeGuard) return protocolRelativeGuard;
87
89
  try {
@@ -89,6 +91,10 @@ async function handleRequest(request, env, platformCtx) {
89
91
  } catch {
90
92
  return badRequestResponse();
91
93
  }
94
+ if (assets && (request.method === "GET" || request.method === "HEAD")) {
95
+ const clientBuildFile = resolveClientBuildFile(url.pathname, __workerBasePath, __workerClientBuildFiles);
96
+ if (clientBuildFile) return await assets.fetch(createStaticAssetRequest(clientBuildFile, request));
97
+ }
92
98
  const missingBuildAsset = isNextStaticPath(url.pathname, __workerBasePath, __workerAssetPathPrefix);
93
99
  {
94
100
  const trustedPrerenderRouteParams = ctx.hostRuntime === "node" ? readTrustedPrerenderRouteParams(request) : null;
@@ -103,8 +109,21 @@ async function handleRequest(request, env, platformCtx) {
103
109
  const result = await (ctx ? runWithExecutionContext(ctx, handleFn) : handleFn());
104
110
  if (result instanceof Response) {
105
111
  let response = result;
106
- if (assets) {
107
- const assetResponse = await resolveStaticAssetSignal(response, { fetchAsset: (path) => Promise.resolve(assets.fetch(createStaticAssetRequest(path, request))) });
112
+ const imageSignal = servesImages ? readImageOptimizationSignal(response) : null;
113
+ if (imageSignal && assets) {
114
+ const { fetchSignalledPublicFile, serveWorkerImageOptimization } = await import("./worker-image-optimization.js");
115
+ response = await serveWorkerImageOptimization(request, imageSignal, __imageAllowedWidths, __imageConfig, {
116
+ assets,
117
+ basePath: __workerBasePath,
118
+ assetPathPrefix: __workerAssetPathPrefix,
119
+ clientBuildFiles: __workerClientBuildFiles,
120
+ fetchPublicFile: async (sourceRequest) => {
121
+ const sourceResult = await runWithExecutionContext(requestCtx, () => rscHandler(sourceRequest, requestCtx));
122
+ return sourceResult instanceof Response ? fetchSignalledPublicFile(sourceResult, sourceRequest, assets) : null;
123
+ }
124
+ });
125
+ } else if (assets) {
126
+ const assetResponse = await resolveStaticAssetSignal(response, { fetchAsset: (path, requestHeaders) => Promise.resolve(assets.fetch(createStaticAssetRequest(path, request, requestHeaders))) });
108
127
  if (assetResponse) response = assetResponse;
109
128
  }
110
129
  return finalizeCacheabilityResponse ? finalizeCacheabilityResponse(response, ctx) : response;
@@ -89,6 +89,22 @@ export declare function canonicalizePrewarmableRscRequestHeaders(headers: Header
89
89
  * router state is transport context and must not fragment the shared response.
90
90
  */
91
91
  export declare function canonicalizeLoadingShellRscRequestHeaders(headers: Headers): boolean;
92
+ /**
93
+ * Callers pass a path (usually a parsed URL's pathname + search) or an
94
+ * absolute URL. A pathname can begin with "//", and resolving that as a
95
+ * relative reference would read its first segment as a host, so a path is
96
+ * appended to the base instead of resolved against it.
97
+ */
98
+ export declare function parseRscRequestPath(href: string): URL;
99
+ /**
100
+ * Serialize the path-absolute reference callers fetch against the app origin.
101
+ * A pathname that begins with "//" would serialize as a protocol-relative URL
102
+ * and send the request to another host, so it keeps a "/." prefix, as the
103
+ * WHATWG URL serializer does. URL resolution drops the dot segment, so the
104
+ * request still targets the same pathname on the app origin, as Next.js's
105
+ * full-URL fetch does.
106
+ */
107
+ export declare function formatRscRequestPath(url: URL): string;
92
108
  export declare function createRscRequestUrl(href: string, headers: Headers): Promise<string>;
93
109
  /** Build the definitive full-route RSC URL shared by prefetch and navigation. */
94
110
  export declare function createCanonicalRscRequestUrl(href: string): string;
@@ -1,7 +1,7 @@
1
1
  import { NEXT_ROUTER_PREFETCH_HEADER, NEXT_ROUTER_SEGMENT_PREFETCH_HEADER, NEXT_ROUTER_STATE_TREE_HEADER, NEXT_URL_HEADER, VINEXT_INTERCEPTION_CONTEXT_HEADER, VINEXT_INTERCEPTION_ID_HEADER, VINEXT_MOUNTED_SLOTS_HEADER, VINEXT_RSC_RENDER_MODE_HEADER, VINEXT_RSC_STATE_FINGERPRINT_HEADER, VINEXT_RSC_VARY_HEADER } from "./app-rsc-vary.js";
2
2
  import { NEXTJS_DEPLOYMENT_ID_HEADER, VINEXT_CLIENT_REUSE_MANIFEST_HEADER } from "./headers.js";
3
- import { fnv1a64 } from "../utils/hash.js";
4
3
  import { APP_RSC_RENDER_MODE_PREFETCH_LOADING_SHELL, parseAppRscRenderMode } from "./app-rsc-render-mode.js";
4
+ import { fnv1a64 } from "../utils/hash.js";
5
5
  import { applyDeploymentIdHeader, getDeploymentId } from "../utils/deployment-id.js";
6
6
  import { createAppRscStateFingerprint } from "./app-rsc-state-fingerprint.js";
7
7
  //#region src/server/app-rsc-cache-busting.ts
@@ -218,33 +218,50 @@ function canonicalizeLoadingShellRscRequestHeaders(headers) {
218
218
  headers.delete(VINEXT_CLIENT_REUSE_MANIFEST_HEADER);
219
219
  return true;
220
220
  }
221
- function toRscRequestPath(href) {
221
+ const RSC_REQUEST_URL_BASE = "http://vinext.local";
222
+ /**
223
+ * Callers pass a path (usually a parsed URL's pathname + search) or an
224
+ * absolute URL. A pathname can begin with "//", and resolving that as a
225
+ * relative reference would read its first segment as a host, so a path is
226
+ * appended to the base instead of resolved against it.
227
+ */
228
+ function parseRscRequestPath(href) {
222
229
  const hashIndex = href.indexOf("#");
223
- return hashIndex === -1 ? href : href.slice(0, hashIndex);
230
+ const beforeHash = hashIndex === -1 ? href : href.slice(0, hashIndex);
231
+ if (beforeHash.startsWith("/") || beforeHash.startsWith("\\")) return new URL(`${RSC_REQUEST_URL_BASE}${beforeHash}`);
232
+ return new URL(beforeHash, RSC_REQUEST_URL_BASE);
233
+ }
234
+ /**
235
+ * Serialize the path-absolute reference callers fetch against the app origin.
236
+ * A pathname that begins with "//" would serialize as a protocol-relative URL
237
+ * and send the request to another host, so it keeps a "/." prefix, as the
238
+ * WHATWG URL serializer does. URL resolution drops the dot segment, so the
239
+ * request still targets the same pathname on the app origin, as Next.js's
240
+ * full-URL fetch does.
241
+ */
242
+ function formatRscRequestPath(url) {
243
+ return `${url.pathname.startsWith("//") ? `/.${url.pathname}` : url.pathname}${url.search}`;
224
244
  }
225
245
  async function createRscRequestUrl(href, headers) {
226
- const url = new URL(toRscRequestPath(href), "http://vinext.local");
246
+ const url = parseRscRequestPath(href);
227
247
  if (typeof window !== "undefined" && process.env.NODE_ENV === "production" && process.env.__NEXT_CONFIG_OUTPUT === "export") {
228
248
  const basePath = process.env.__NEXT_ROUTER_BASEPATH ?? "";
229
249
  const isBasePathRoot = basePath !== "" && url.pathname === basePath;
230
250
  if (url.pathname.endsWith("/")) url.pathname += "index.txt";
231
251
  else url.pathname += isBasePathRoot ? "/index.txt" : ".txt";
232
- return `${url.pathname}${url.search}`;
252
+ return formatRscRequestPath(url);
233
253
  }
234
254
  setRscCacheBustingSearchParam(url, await computeRscCacheBustingSearchParam(headers));
235
- return `${url.pathname}${url.search}`;
255
+ return formatRscRequestPath(url);
236
256
  }
237
257
  /** Build the definitive full-route RSC URL shared by prefetch and navigation. */
238
258
  function createCanonicalRscRequestUrl(href) {
239
- const url = new URL(toRscRequestPath(href), "http://vinext.local");
259
+ const url = parseRscRequestPath(href);
240
260
  setRscCacheBustingSearchParam(url, "");
241
- return `${url.pathname}${url.search}`;
261
+ return formatRscRequestPath(url);
242
262
  }
243
263
  function createServerActionRequestUrl(href) {
244
- const hashIndex = href.indexOf("#");
245
- const beforeHash = hashIndex === -1 ? href : href.slice(0, hashIndex);
246
- const url = new URL(beforeHash, "http://vinext.local");
247
- return `${url.pathname}${url.search}`;
264
+ return formatRscRequestPath(parseRscRequestPath(href));
248
265
  }
249
266
  async function createRscRedirectLocation(location, request) {
250
267
  const requestUrl = new URL(request.url);
@@ -309,4 +326,4 @@ async function resolveInvalidRscCacheBustingRequest(options) {
309
326
  });
310
327
  }
311
328
  //#endregion
312
- export { VINEXT_RSC_BUILD_ID_HEADER, VINEXT_RSC_CACHE_BUSTING_SEARCH_PARAM, VINEXT_RSC_COMPATIBILITY_ID_HEADER, VINEXT_RSC_CONTENT_TYPE, VINEXT_RSC_RENDER_MODE_HEADER, VINEXT_RSC_VARY_HEADER, applyRscCompatibilityIdHeader, applyRscDeploymentIdHeader, canonicalizeLoadingShellRscRequestHeaders, canonicalizePrewarmableRscRequestHeaders, computeRscCacheBustingSearchParam, createCanonicalLoadingShellRscRequestHeaders, createCanonicalRscRequestHeaders, createCanonicalRscRequestUrl, createRscRedirectLocation, createRscRequestHeaders, createRscRequestUrl, createServerActionRequestUrl, getVinextRscCompatibilityId, hasRscCacheBustingSearchParam, isRscCompatibilityIdCompatible, keepOnlyValidatedRscCacheBustingSearchParam, resolveHardNavigationTargetFromRscResponse, resolveInvalidRscCacheBustingRequest, resolveRscCompatibilityNavigationDecision, setRscCacheBustingSearchParam, stripRscCacheBustingSearchParam, stripRscSuffix };
329
+ export { VINEXT_RSC_BUILD_ID_HEADER, VINEXT_RSC_CACHE_BUSTING_SEARCH_PARAM, VINEXT_RSC_COMPATIBILITY_ID_HEADER, VINEXT_RSC_CONTENT_TYPE, VINEXT_RSC_RENDER_MODE_HEADER, VINEXT_RSC_VARY_HEADER, applyRscCompatibilityIdHeader, applyRscDeploymentIdHeader, canonicalizeLoadingShellRscRequestHeaders, canonicalizePrewarmableRscRequestHeaders, computeRscCacheBustingSearchParam, createCanonicalLoadingShellRscRequestHeaders, createCanonicalRscRequestHeaders, createCanonicalRscRequestUrl, createRscRedirectLocation, createRscRequestHeaders, createRscRequestUrl, createServerActionRequestUrl, formatRscRequestPath, getVinextRscCompatibilityId, hasRscCacheBustingSearchParam, isRscCompatibilityIdCompatible, keepOnlyValidatedRscCacheBustingSearchParam, parseRscRequestPath, resolveHardNavigationTargetFromRscResponse, resolveInvalidRscCacheBustingRequest, resolveRscCompatibilityNavigationDecision, setRscCacheBustingSearchParam, stripRscCacheBustingSearchParam, stripRscSuffix };
@@ -1,6 +1,7 @@
1
1
  import { isNavigationSignalError } from "../utils/navigation-signal.js";
2
2
  import { resolveAppPageSpecialError } from "./app-page-execution.js";
3
3
  import { isAppRenderAbortError } from "./app-render-abort-error.js";
4
+ import { getProperError } from "./proper-error.js";
4
5
  //#region src/server/app-rsc-errors.ts
5
6
  const ORIGINAL_SERVER_ERROR = Symbol.for("vinext.originalServerError");
6
7
  function hasDigest(error) {
@@ -23,11 +24,14 @@ function getDigestForWellKnownError(error) {
23
24
  const digest = String(error.digest);
24
25
  if (isNavigationSignalError(error) || digest === BAILOUT_TO_CSR_DIGEST || digest === DYNAMIC_SERVER_USAGE_DIGEST) return digest;
25
26
  }
26
- function getThrownValueMessage(error) {
27
- return error instanceof Error ? error.message : String(error);
28
- }
29
- function getThrownValueStack(error) {
30
- return error instanceof Error ? error.stack || "" : "";
27
+ /**
28
+ * Next.js hashes a thrown string as is, and other values once they are an
29
+ * Error (packages/next/src/server/app-render/create-error-handler.tsx).
30
+ */
31
+ function getThrownValueDigest(error) {
32
+ if (typeof error === "string") return errorDigest(error);
33
+ const properError = getProperError(error);
34
+ return errorDigest(properError.message + (properError.stack || ""));
31
35
  }
32
36
  /**
33
37
  * djb2 hash matching Next.js's string-hash package for RSC error digests.
@@ -41,7 +45,7 @@ function sanitizeErrorForClient(error, nodeEnv = process.env.NODE_ENV) {
41
45
  if (resolveAppPageSpecialError(error)) return error;
42
46
  if (nodeEnv !== "production") return error;
43
47
  const sanitized = /* @__PURE__ */ new Error("An error occurred in the Server Components render. The specific message is omitted in production builds to avoid leaking sensitive details. A digest property is included on this error instance which may provide additional details about the nature of the error.");
44
- sanitized.digest = hasDigest(error) ? String(error.digest) : errorDigest(getThrownValueMessage(error) + getThrownValueStack(error));
48
+ sanitized.digest = hasDigest(error) ? String(error.digest) : getThrownValueDigest(error);
45
49
  Object.defineProperty(sanitized, ORIGINAL_SERVER_ERROR, {
46
50
  configurable: false,
47
51
  enumerable: false,
@@ -55,6 +59,7 @@ function createRscOnErrorHandler(options) {
55
59
  return (error) => {
56
60
  const nodeEnv = options.nodeEnv ?? process.env.NODE_ENV;
57
61
  const reportableError = error && typeof error === "object" && ORIGINAL_SERVER_ERROR in error ? Reflect.get(error, ORIGINAL_SERVER_ERROR) : error;
62
+ if (typeof reportableError === "string") return hasDigest(error) ? String(error.digest) : errorDigest(reportableError);
58
63
  if (isAppRenderAbortError(error) || isAppRenderAbortError(reportableError)) return;
59
64
  const wellKnownDigest = getDigestForWellKnownError(error) ?? getDigestForWellKnownError(reportableError);
60
65
  if (wellKnownDigest !== void 0) return wellKnownDigest;
@@ -62,22 +67,21 @@ function createRscOnErrorHandler(options) {
62
67
  console.error("[vinext] RSC serialization error: a non-plain object was passed from a Server Component to a Client Component.\n\nCommon causes:\n * Passing a module namespace (import * as X) directly as a prop.\n Unlike Next.js (webpack), Vite produces real ESM module namespace objects\n which are not serializable. Fix: pass individual values instead,\n e.g. <Comp value={module.value} />\n * Passing a class instance (new Foo()) as a prop.\n Fix: convert to a plain object, e.g. { id: foo.id, name: foo.name }\n * Passing a Date, Map, or Set. Use .toISOString(), [...map.entries()], etc.\n * Passing Object.create(null). Use { ...obj } to restore a prototype.\n\nOriginal error:", error.message);
63
68
  return;
64
69
  }
65
- if (options.requestInfo && options.errorContext && error) options.reportRequestError(reportableError, options.requestInfo, options.errorContext);
70
+ const thrownHasDigest = hasDigest(error);
71
+ const properError = getProperError(reportableError);
72
+ const digest = thrownHasDigest ? String(error.digest) : errorDigest(properError.message + (properError.stack || ""));
73
+ if (!hasDigest(properError) && (properError !== reportableError || options.attachDigest !== false)) try {
74
+ Object.assign(properError, { digest });
75
+ } catch {}
76
+ if (options.requestInfo && options.errorContext) options.reportRequestError(properError, options.requestInfo, options.errorContext);
66
77
  if (process.env.VINEXT_PRERENDER === "1" && error) {
67
78
  if (!loggedPrerenderErrors?.has(reportableError)) {
68
79
  (loggedPrerenderErrors ??= /* @__PURE__ */ new Set()).add(reportableError);
69
80
  const route = options.requestInfo?.path ?? options.errorContext?.routePath ?? "<unknown>";
70
81
  console.error(`[vinext] Error prerendering route ${JSON.stringify(route)}:`, reportableError);
71
82
  }
72
- } else if (nodeEnv !== "production" && error && !hasDigest(error)) console.error("[vinext] Server render error:", reportableError);
73
- if (hasDigest(error)) return String(error.digest);
74
- if (error) {
75
- const digest = errorDigest(getThrownValueMessage(error) + getThrownValueStack(error));
76
- if (options.attachDigest !== false && error instanceof Error) try {
77
- Object.assign(error, { digest });
78
- } catch {}
79
- return digest;
80
- }
83
+ } else if (nodeEnv !== "production" && !thrownHasDigest) console.error("[vinext] Server render error:", properError);
84
+ return digest;
81
85
  };
82
86
  }
83
87
  //#endregion
@@ -204,6 +204,11 @@ export type CreateAppRscHandlerOptions<TRoute extends AppRscHandlerRoute> = {
204
204
  fallback: NextRewrite[];
205
205
  };
206
206
  draftModeSecret: string;
207
+ /**
208
+ * Dev-only key the hybrid Pages handler seals x-vinext-mw-ctx with. Null
209
+ * outside `vite dev`, so a forwarded middleware context is never honored.
210
+ */
211
+ devForwardedMiddlewareContextKey?: string | null;
207
212
  dispatchMatchedPage: (options: DispatchMatchedPageOptions<TRoute>) => Promise<Response>;
208
213
  dispatchMatchedRouteHandler: (options: DispatchMatchedRouteHandlerOptions<TRoute>) => Promise<Response>;
209
214
  /**
@@ -246,6 +251,8 @@ export type CreateAppRscHandlerOptions<TRoute extends AppRscHandlerRoute> = {
246
251
  renderPagesFallback?: (options: RenderPagesFallbackOptions) => Promise<Response | null>;
247
252
  rootParamNamesByPattern?: RootParamNamesMap;
248
253
  setNavigationContext: (context: NavigationContextValue) => void;
254
+ /** next.config `skipTrailingSlashRedirect`: serve both slash forms without a 308. */
255
+ skipTrailingSlashRedirect?: boolean;
249
256
  staticParamsMap: StaticParamsMap;
250
257
  trailingSlash: boolean;
251
258
  validateDevRequestOrigin?: (request: Request) => Response | null;