vinext 1.0.1 → 1.1.0

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 (159) hide show
  1. package/README.md +72 -1
  2. package/dist/build/app-page-static-eligibility.d.ts +15 -0
  3. package/dist/build/app-page-static-eligibility.js +82 -0
  4. package/dist/build/prerender-paths.js +3 -78
  5. package/dist/build/prerender.js +33 -8
  6. package/dist/build/server-manifest.d.ts +7 -0
  7. package/dist/build/server-manifest.js +11 -1
  8. package/dist/cache/cache-adapters-virtual.d.ts +7 -1
  9. package/dist/cache/cache-adapters-virtual.js +3 -3
  10. package/dist/client/navigation-runtime.d.ts +11 -0
  11. package/dist/client/navigation-runtime.js +1 -1
  12. package/dist/config/config-matchers.js +3 -1
  13. package/dist/config/next-config.d.ts +7 -0
  14. package/dist/config/next-config.js +18 -3
  15. package/dist/entries/app-rsc-entry.d.ts +2 -0
  16. package/dist/entries/app-rsc-entry.js +64 -43
  17. package/dist/index.d.ts +4 -1
  18. package/dist/index.js +59 -30
  19. package/dist/plugins/cache-flight-codec.d.ts +19 -0
  20. package/dist/plugins/cache-flight-codec.js +68 -0
  21. package/dist/plugins/commonjs-esm-facade.d.ts +35 -0
  22. package/dist/plugins/commonjs-esm-facade.js +80 -0
  23. package/dist/plugins/fonts.d.ts +8 -11
  24. package/dist/plugins/fonts.js +71 -34
  25. package/dist/routing/file-matcher.d.ts +1 -0
  26. package/dist/routing/file-matcher.js +1 -1
  27. package/dist/server/api-handler.js +17 -7
  28. package/dist/server/app-browser-entry.js +11 -7
  29. package/dist/server/app-browser-history-controller.d.ts +2 -0
  30. package/dist/server/app-browser-history-controller.js +8 -0
  31. package/dist/server/app-browser-interception-context.js +26 -2
  32. package/dist/server/app-browser-navigation-controller.d.ts +7 -1
  33. package/dist/server/app-browser-navigation-controller.js +93 -10
  34. package/dist/server/app-fallback-renderer.d.ts +2 -0
  35. package/dist/server/app-fallback-renderer.js +1 -0
  36. package/dist/server/app-history-state.d.ts +3 -0
  37. package/dist/server/app-history-state.js +7 -0
  38. package/dist/server/app-middleware.js +2 -1
  39. package/dist/server/app-optimistic-routing.js +1 -1
  40. package/dist/server/app-page-boundary-render.d.ts +5 -0
  41. package/dist/server/app-page-boundary-render.js +6 -4
  42. package/dist/server/app-page-cache-finalizer.d.ts +11 -0
  43. package/dist/server/app-page-cache-finalizer.js +41 -7
  44. package/dist/server/app-page-cache-render.d.ts +18 -0
  45. package/dist/server/app-page-cache-render.js +63 -43
  46. package/dist/server/app-page-cache.d.ts +5 -0
  47. package/dist/server/app-page-cache.js +27 -7
  48. package/dist/server/app-page-dispatch.d.ts +9 -1
  49. package/dist/server/app-page-dispatch.js +47 -30
  50. package/dist/server/app-page-element-builder.d.ts +7 -0
  51. package/dist/server/app-page-element-builder.js +27 -42
  52. package/dist/server/app-page-execution.d.ts +15 -12
  53. package/dist/server/app-page-execution.js +30 -40
  54. package/dist/server/app-page-head.d.ts +5 -0
  55. package/dist/server/app-page-head.js +9 -3
  56. package/dist/server/app-page-probe.d.ts +0 -109
  57. package/dist/server/app-page-probe.js +3 -136
  58. package/dist/server/app-page-render-identity.d.ts +0 -1
  59. package/dist/server/app-page-render-identity.js +1 -1
  60. package/dist/server/app-page-render-observation.js +2 -2
  61. package/dist/server/app-page-render.d.ts +13 -5
  62. package/dist/server/app-page-render.js +219 -119
  63. package/dist/server/app-page-request.d.ts +0 -1
  64. package/dist/server/app-page-request.js +1 -2
  65. package/dist/server/app-page-response.d.ts +4 -3
  66. package/dist/server/app-page-response.js +4 -3
  67. package/dist/server/app-page-route-wiring.d.ts +17 -5
  68. package/dist/server/app-page-route-wiring.js +55 -33
  69. package/dist/server/app-page-rsc-render-status.d.ts +48 -0
  70. package/dist/server/app-page-rsc-render-status.js +54 -0
  71. package/dist/server/app-page-stream.d.ts +15 -8
  72. package/dist/server/app-page-stream.js +6 -0
  73. package/dist/server/app-request-stage-dispatch.js +1 -1
  74. package/dist/server/app-request-stage-independent-entry.js +4 -4
  75. package/dist/server/app-response-header-provenance.d.ts +2 -0
  76. package/dist/server/app-response-header-provenance.js +10 -4
  77. package/dist/server/app-route-handler-dispatch.js +1 -1
  78. package/dist/server/app-router-entry.js +3 -3
  79. package/dist/server/app-rsc-handler.d.ts +4 -0
  80. package/dist/server/app-rsc-handler.js +212 -96
  81. package/dist/server/app-rsc-request-normalization.d.ts +3 -1
  82. package/dist/server/app-rsc-request-normalization.js +4 -2
  83. package/dist/server/app-rsc-response-stage.d.ts +1 -1
  84. package/dist/server/app-rsc-response-stage.js +6 -4
  85. package/dist/server/app-rsc-route-matching.d.ts +9 -1
  86. package/dist/server/app-rsc-route-matching.js +36 -19
  87. package/dist/server/app-server-action-execution.js +1 -1
  88. package/dist/server/app-ssr-entry.d.ts +3 -2
  89. package/dist/server/app-ssr-entry.js +6 -4
  90. package/dist/server/app-ssr-stream.d.ts +6 -1
  91. package/dist/server/app-ssr-stream.js +34 -15
  92. package/dist/server/app-worker-stages.d.ts +2 -2
  93. package/dist/server/app-worker-stages.js +3 -3
  94. package/dist/server/cacheability-request.js +9 -5
  95. package/dist/server/config-headers.js +2 -0
  96. package/dist/server/dev-server.js +12 -2
  97. package/dist/server/headers.d.ts +14 -0
  98. package/dist/server/headers.js +15 -1
  99. package/dist/server/image-optimization.d.ts +16 -0
  100. package/dist/server/image-optimization.js +26 -6
  101. package/dist/server/isr-cache.js +2 -2
  102. package/dist/server/middleware-matcher-pattern.js +8 -4
  103. package/dist/server/middleware-response-headers.js +2 -0
  104. package/dist/server/multi-stage.d.ts +8 -0
  105. package/dist/server/navigation-planner.js +1 -1
  106. package/dist/server/node-response-signal.d.ts +16 -0
  107. package/dist/server/node-response-signal.js +26 -0
  108. package/dist/server/open-redirect.d.ts +47 -0
  109. package/dist/server/open-redirect.js +74 -1
  110. package/dist/server/pages-page-handler.js +21 -4
  111. package/dist/server/pages-request-stage-entry.js +8 -4
  112. package/dist/server/prod-server.d.ts +8 -7
  113. package/dist/server/prod-server.js +153 -69
  114. package/dist/server/request-pipeline.d.ts +20 -8
  115. package/dist/server/request-pipeline.js +21 -7
  116. package/dist/server/response-compression.d.ts +43 -0
  117. package/dist/server/response-compression.js +111 -0
  118. package/dist/server/seed-cache.js +10 -7
  119. package/dist/server/socket-error-backstop.d.ts +14 -0
  120. package/dist/server/socket-error-backstop.js +27 -4
  121. package/dist/shims/cache-adapter-instantiate.d.ts +50 -0
  122. package/dist/shims/cache-adapter-instantiate.js +118 -0
  123. package/dist/shims/cache-callable-runtime.d.ts +2 -40
  124. package/dist/shims/cache-callable-runtime.js +50 -168
  125. package/dist/shims/cache-flight-arguments.d.ts +22 -0
  126. package/dist/shims/cache-flight-arguments.js +56 -0
  127. package/dist/shims/cache-request-state.d.ts +15 -0
  128. package/dist/shims/cache-request-state.js +31 -8
  129. package/dist/shims/cache-runtime.d.ts +6 -19
  130. package/dist/shims/cache-runtime.js +165 -225
  131. package/dist/shims/cache.d.ts +2 -2
  132. package/dist/shims/cache.js +5 -4
  133. package/dist/shims/cacheability-classification.d.ts +7 -0
  134. package/dist/shims/error-boundary-navigation.d.ts +7 -0
  135. package/dist/shims/error-boundary-navigation.js +19 -1
  136. package/dist/shims/error-boundary.d.ts +10 -10
  137. package/dist/shims/error-boundary.js +26 -7
  138. package/dist/shims/fetch-cache.d.ts +6 -0
  139. package/dist/shims/fetch-cache.js +19 -1
  140. package/dist/shims/form.js +1 -1
  141. package/dist/shims/headers.d.ts +13 -0
  142. package/dist/shims/headers.js +47 -4
  143. package/dist/shims/internal/app-page-props-cache-key.d.ts +5 -2
  144. package/dist/shims/internal/app-page-props-cache-key.js +24 -15
  145. package/dist/shims/internal/headers-state.d.ts +0 -1
  146. package/dist/shims/metadata.js +3 -3
  147. package/dist/shims/navigation-errors.d.ts +6 -0
  148. package/dist/shims/navigation-errors.js +11 -1
  149. package/dist/shims/navigation.d.ts +7 -0
  150. package/dist/shims/navigation.js +14 -4
  151. package/dist/shims/request-context.d.ts +9 -0
  152. package/dist/shims/thenable-params.d.ts +4 -0
  153. package/dist/shims/thenable-params.js +25 -3
  154. package/dist/shims/unified-request-context.js +1 -0
  155. package/dist/utils/mdx-scan.d.ts +5 -0
  156. package/dist/utils/mdx-scan.js +8 -1
  157. package/dist/utils/regex-safety.d.ts +10 -0
  158. package/dist/utils/regex-safety.js +70 -18
  159. package/package.json +3 -3
@@ -1,4 +1,4 @@
1
- import { analyzeRegexSafety, regexAtomsMayOverlap } from "../utils/regex-safety.js";
1
+ import { analyzeRegexSafety, mayHaveNamedGroups, regexAtomsMayOverlap } from "../utils/regex-safety.js";
2
2
  import { middlewarePathTokensToRegExp, normalizeMiddlewarePathTokens, parseMiddlewarePath } from "./middleware-path-to-regexp.js";
3
3
  //#region src/server/middleware-matcher-pattern.ts
4
4
  const MATCHER_OBJECT_KEYS = /* @__PURE__ */ new Set([
@@ -138,8 +138,11 @@ function hasOverlappingSequentialRepetition(pattern) {
138
138
  }
139
139
  return false;
140
140
  }
141
- function unsafeTokenReason(token) {
142
- const regexSafetyIssue = analyzeRegexSafety(token.pattern, { ignoreCase: true });
141
+ function unsafeTokenReason(token, namedGroups) {
142
+ const regexSafetyIssue = analyzeRegexSafety(token.pattern, {
143
+ ignoreCase: true,
144
+ namedGroups
145
+ });
143
146
  if (regexSafetyIssue) {
144
147
  if (regexSafetyIssue === "analysis budget exceeded") return `parameter "${token.name}" exceeds the regex analysis budget`;
145
148
  return `parameter "${token.name}" contains ${regexSafetyIssue}`;
@@ -150,9 +153,10 @@ function unsafeTokenReason(token) {
150
153
  return null;
151
154
  }
152
155
  function validateTokens(tokens) {
156
+ const namedGroups = tokens.some((token) => typeof token !== "string" && mayHaveNamedGroups(token.pattern));
153
157
  for (const token of tokens) {
154
158
  if (typeof token === "string") continue;
155
- const reason = unsafeTokenReason(token);
159
+ const reason = unsafeTokenReason(token, namedGroups);
156
160
  if (reason) return reason;
157
161
  }
158
162
  return null;
@@ -1,3 +1,4 @@
1
+ import "./headers.js";
1
2
  //#region src/server/middleware-response-headers.ts
2
3
  const ADDITIVE_RESPONSE_HEADER_NAMES = /* @__PURE__ */ new Set(["set-cookie", "vary"]);
3
4
  function mergeVaryHeader(target, value) {
@@ -28,6 +29,7 @@ function mergeMiddlewareResponseHeaders(target, middlewareHeaders) {
28
29
  if (!middlewareHeaders) return;
29
30
  for (const [key, value] of middlewareHeaders) {
30
31
  const lowerName = key.toLowerCase();
32
+ if (lowerName === "x-vinext-special-error-status") continue;
31
33
  if (lowerName === "link" && !value) continue;
32
34
  if (lowerName === "vary") {
33
35
  mergeVaryHeader(target, value);
@@ -21,6 +21,14 @@ export type VinextResponseStageDispatchOptions = {
21
21
  * opt into completed-response admission and honor core's `no-store` policy.
22
22
  */
23
23
  cache: "shared" | "bypass";
24
+ /**
25
+ * Render an App page document whole before its first byte, as Next.js
26
+ * renders a static page, so its streamed metadata is in `<head>`. An adapter
27
+ * sets this for a render no client is waiting on, such as a regeneration or
28
+ * a deployment warm-up. It is not a cache selector: a transport must not
29
+ * partition its lookup or stored entry by it.
30
+ */
31
+ renderWholeDocument?: boolean;
24
32
  /**
25
33
  * Query-free identity of a shared App page GET/HEAD dispatch, supplied only
26
34
  * to adapters that declare `responseStageCacheIdentity: "query-free"` and
@@ -1,8 +1,8 @@
1
1
  import { splitPathnameForRouteMatch } from "../routing/utils.js";
2
2
  import { stripBasePath } from "../utils/base-path.js";
3
+ import { matchRoutePattern, matchRoutePatternPrefix, matchRoutePatternWithOptionalDynamicSegments } from "../routing/route-pattern.js";
3
4
  import { AppElementsWire, compareAppElementsSlotIds } from "./app-elements-wire.js";
4
5
  import "./app-elements.js";
5
- import { matchRoutePattern, matchRoutePatternPrefix, matchRoutePatternWithOptionalDynamicSegments } from "../routing/route-pattern.js";
6
6
  import { resolveHardNavigationTargetFromRscResponse, resolveRscCompatibilityNavigationDecision } from "./app-rsc-cache-busting.js";
7
7
  import { resolveRscRedirectLifecycleHop, resolveStreamedRscRedirectLifecycleHop } from "./app-browser-rsc-redirect.js";
8
8
  import { NavigationTraceReasonCodes, createNavigationLifecycleTraceFields, createNavigationTrace } from "./navigation-trace.js";
@@ -0,0 +1,16 @@
1
+ import { Writable } from "node:stream";
2
+ //#region src/server/node-response-signal.d.ts
3
+ export declare class ResponseAborted extends Error {
4
+ readonly name = "ResponseAborted";
5
+ }
6
+ /**
7
+ * Creates an AbortSignal tied to the closing of a ServerResponse.
8
+ *
9
+ * The incoming request cannot be used: once its body has been fully read the
10
+ * readable side is done, so a later client disconnect fires nothing on it
11
+ * (and its `close` event also fires on normal requests). If `finish` fires
12
+ * first, `res.end()` completed and the following `close` is our own teardown;
13
+ * if `close` fires first, the client disconnected before we finished.
14
+ */
15
+ export declare function signalFromNodeResponse(response: Writable): AbortSignal;
16
+ //#endregion
@@ -0,0 +1,26 @@
1
+ //#region src/server/node-response-signal.ts
2
+ const ResponseAbortedName = "ResponseAborted";
3
+ var ResponseAborted = class extends Error {
4
+ name = ResponseAbortedName;
5
+ };
6
+ /**
7
+ * Creates an AbortSignal tied to the closing of a ServerResponse.
8
+ *
9
+ * The incoming request cannot be used: once its body has been fully read the
10
+ * readable side is done, so a later client disconnect fires nothing on it
11
+ * (and its `close` event also fires on normal requests). If `finish` fires
12
+ * first, `res.end()` completed and the following `close` is our own teardown;
13
+ * if `close` fires first, the client disconnected before we finished.
14
+ */
15
+ function signalFromNodeResponse(response) {
16
+ const { errored, destroyed } = response;
17
+ if (errored || destroyed) return AbortSignal.abort(errored ?? new ResponseAborted());
18
+ const controller = new AbortController();
19
+ response.once("close", () => {
20
+ if (response.writableFinished) return;
21
+ controller.abort(new ResponseAborted());
22
+ });
23
+ return controller.signal;
24
+ }
25
+ //#endregion
26
+ export { ResponseAborted, signalFromNodeResponse };
@@ -8,4 +8,51 @@
8
8
  * to resolve a `Location` containing the pathname as protocol-relative.
9
9
  */
10
10
  export declare function isOpenRedirectShaped(rawPathname: string): boolean;
11
+ /**
12
+ * What a request entry answers for a raw path containing a backslash or a
13
+ * repeated slash: Next.js's 308, or a 404 when the collapsed path would still
14
+ * be open-redirect shaped.
15
+ */
16
+ export type RepeatedSlashRedirect = {
17
+ status: 308;
18
+ location: string;
19
+ } | {
20
+ status: 404;
21
+ };
22
+ /**
23
+ * Returns Next.js's redirect for a raw request path containing a backslash or
24
+ * a repeated slash, or `null` when the path needs no change.
25
+ *
26
+ * Ported from Next.js's request entry (`base-server.ts` / `resolve-routes.ts`,
27
+ * via `normalizeRepeatedSlashes` in `shared/lib/utils.ts`): backslashes in the
28
+ * path become `/`, runs of slashes collapse to one and the query is kept.
29
+ * `next start` then builds the Location with `url.format(parseUrl(...))` in
30
+ * `router-server.ts`, so the result is serialized through a WHATWG URL here
31
+ * too (`/a/%2e%2e//b` → `/b`, `?q="x"` → `?q=%22x%22`, as observed). It runs
32
+ * before basePath handling, so `//` → `/`, `/docs//` → `/docs/` and
33
+ * `//evil.com` → `/evil.com`. Percent-encoded `%2F`/`%5C` are not touched and
34
+ * keep falling through to `isOpenRedirectShaped` (404), as in Next.js.
35
+ *
36
+ * The Location is always same-origin. Absolute-form request targets
37
+ * (`GET http://host//x`) are left alone. A collapsed path that still starts
38
+ * with an encoded slash or backslash (`//%2Fevil.com`, `/.//%2Fevil.com`) is
39
+ * never echoed into a Location or routed: it gets a 404 instead.
40
+ *
41
+ * @param rawUrl - The raw request target: pathname plus optional `?query`
42
+ */
43
+ export declare function getRepeatedSlashRedirect(rawUrl: string): RepeatedSlashRedirect | null;
44
+ /**
45
+ * Build the Response for a raw request path containing a backslash or a
46
+ * repeated slash. See `getRepeatedSlashRedirect`.
47
+ */
48
+ export declare function repeatedSlashRedirectResponse(rawUrl: string): Response | null;
49
+ /**
50
+ * Node `ServerResponse` counterpart of `repeatedSlashRedirectResponse`, for
51
+ * entries that see the raw request target (dev and `vinext start`). Returns
52
+ * true when a response was sent.
53
+ */
54
+ export declare function sendRepeatedSlashRedirect(rawUrl: string, res: {
55
+ writeHead(status: number, headers?: Record<string, string>): unknown;
56
+ end(body: string): unknown;
57
+ }): boolean;
11
58
  //#endregion
@@ -1,3 +1,4 @@
1
+ import { notFoundResponse } from "./http-error-responses.js";
1
2
  //#region src/server/open-redirect.ts
2
3
  /**
3
4
  * Returns true if a request pathname looks like a protocol-relative open
@@ -17,5 +18,77 @@ function isOpenRedirectShaped(rawPathname) {
17
18
  }
18
19
  return false;
19
20
  }
21
+ const REPEATED_SLASH_OR_BACKSLASH_RE = /\\|\/\//;
22
+ const REDIRECT_LOCATION_BASE = "http://vinext.invalid";
23
+ /**
24
+ * Returns Next.js's redirect for a raw request path containing a backslash or
25
+ * a repeated slash, or `null` when the path needs no change.
26
+ *
27
+ * Ported from Next.js's request entry (`base-server.ts` / `resolve-routes.ts`,
28
+ * via `normalizeRepeatedSlashes` in `shared/lib/utils.ts`): backslashes in the
29
+ * path become `/`, runs of slashes collapse to one and the query is kept.
30
+ * `next start` then builds the Location with `url.format(parseUrl(...))` in
31
+ * `router-server.ts`, so the result is serialized through a WHATWG URL here
32
+ * too (`/a/%2e%2e//b` → `/b`, `?q="x"` → `?q=%22x%22`, as observed). It runs
33
+ * before basePath handling, so `//` → `/`, `/docs//` → `/docs/` and
34
+ * `//evil.com` → `/evil.com`. Percent-encoded `%2F`/`%5C` are not touched and
35
+ * keep falling through to `isOpenRedirectShaped` (404), as in Next.js.
36
+ *
37
+ * The Location is always same-origin. Absolute-form request targets
38
+ * (`GET http://host//x`) are left alone. A collapsed path that still starts
39
+ * with an encoded slash or backslash (`//%2Fevil.com`, `/.//%2Fevil.com`) is
40
+ * never echoed into a Location or routed: it gets a 404 instead.
41
+ *
42
+ * @param rawUrl - The raw request target: pathname plus optional `?query`
43
+ */
44
+ function getRepeatedSlashRedirect(rawUrl) {
45
+ if (!rawUrl.startsWith("/")) return null;
46
+ const urlParts = rawUrl.split("?");
47
+ const pathname = urlParts[0];
48
+ if (!REPEATED_SLASH_OR_BACKSLASH_RE.test(pathname)) return null;
49
+ const cleanUrl = pathname.replaceAll("\\", "/").replace(/\/\/+/g, "/") + (urlParts[1] ? `?${urlParts.slice(1).join("?")}` : "");
50
+ const parsed = new URL(REDIRECT_LOCATION_BASE + cleanUrl);
51
+ const location = parsed.pathname + parsed.search + parsed.hash;
52
+ return isOpenRedirectShaped(location) ? { status: 404 } : {
53
+ status: 308,
54
+ location
55
+ };
56
+ }
57
+ function repeatedSlashRedirectHeaders(location) {
58
+ return {
59
+ Location: location,
60
+ Refresh: `0;url=${location}`
61
+ };
62
+ }
63
+ /**
64
+ * Build the Response for a raw request path containing a backslash or a
65
+ * repeated slash. See `getRepeatedSlashRedirect`.
66
+ */
67
+ function repeatedSlashRedirectResponse(rawUrl) {
68
+ const redirect = getRepeatedSlashRedirect(rawUrl);
69
+ if (!redirect) return null;
70
+ if (redirect.status === 404) return notFoundResponse();
71
+ return new Response(redirect.location, {
72
+ status: 308,
73
+ headers: repeatedSlashRedirectHeaders(redirect.location)
74
+ });
75
+ }
76
+ /**
77
+ * Node `ServerResponse` counterpart of `repeatedSlashRedirectResponse`, for
78
+ * entries that see the raw request target (dev and `vinext start`). Returns
79
+ * true when a response was sent.
80
+ */
81
+ function sendRepeatedSlashRedirect(rawUrl, res) {
82
+ const redirect = getRepeatedSlashRedirect(rawUrl);
83
+ if (!redirect) return false;
84
+ if (redirect.status === 404) {
85
+ res.writeHead(404);
86
+ res.end("This page could not be found");
87
+ return true;
88
+ }
89
+ res.writeHead(308, repeatedSlashRedirectHeaders(redirect.location));
90
+ res.end(redirect.location);
91
+ return true;
92
+ }
20
93
  //#endregion
21
- export { isOpenRedirectShaped };
94
+ export { getRepeatedSlashRedirect, isOpenRedirectShaped, repeatedSlashRedirectResponse, sendRepeatedSlashRedirect };
@@ -1,6 +1,7 @@
1
1
  import { preserveFullyBufferedBodyMetadata } from "./fully-buffered-response.js";
2
2
  import { closeAfterResponse, closeAfterResponseWithBody, createRequestContext, runWithRequestContext } from "../shims/unified-request-context.js";
3
3
  import { PRERENDER_REVALIDATE_HEADER } from "../utils/protocol-headers.js";
4
+ import { normalizePathnameForRouteMatch } from "../routing/utils.js";
4
5
  import { patternToNextFormat } from "../routing/route-validation.js";
5
6
  import { getRequestExecutionContext } from "../shims/request-context.js";
6
7
  import { NEXTJS_CACHE_HEADER, NEXTJS_DEPLOYMENT_ID_HEADER, VINEXT_CACHE_HEADER, VINEXT_REVALIDATED_CACHE_TAG_HEADER } from "./headers.js";
@@ -187,7 +188,7 @@ function createPagesPageHandler(opts) {
187
188
  redirectUrl: void 0
188
189
  };
189
190
  const locale = localeInfo.locale;
190
- const routeUrl = localeInfo.url;
191
+ let routeUrl = localeInfo.url;
191
192
  const currentDefaultLocale = i18nConfig ? localeInfo.domainLocale ? localeInfo.domainLocale.defaultLocale : i18nConfig.defaultLocale : void 0;
192
193
  const domainLocales = i18nConfig ? i18nConfig.domains : void 0;
193
194
  const pageIsrCacheKey = isrCacheKeyForRequest(i18nConfig ? localeInfo.domainLocale ? JSON.stringify([String(localeInfo.domainLocale.domain).toLowerCase(), locale]) : "locale:" + String(locale) : null);
@@ -216,6 +217,22 @@ function createPagesPageHandler(opts) {
216
217
  renderAsPath = routeUrl;
217
218
  } else return buildDefaultPagesNotFoundResponse();
218
219
  }
220
+ const routePathname = routeUrl.split("?")[0];
221
+ const cachePathname = normalizePathnameForRouteMatch(routePathname);
222
+ let cacheIdentityDiverges = false;
223
+ if (!isRouteMissErrorRender && options?.__forcedRoute === void 0 && cachePathname !== routePathname) {
224
+ const cachePathRoute = matchRoute(cachePathname, pageRoutes)?.route;
225
+ if (cachePathRoute !== match.route) {
226
+ const cachePathModule = cachePathRoute?.module;
227
+ if (match.route.isDynamic && typeof match.route.module.getStaticProps === "function" && cachePathRoute && !cachePathRoute.isDynamic && typeof cachePathModule?.getStaticProps === "function" && typeof cachePathModule.getServerSideProps !== "function" && getPagesPreviewState(request.headers.get("cookie"), { isOnDemandRevalidate: isOnDemandRevalidateRequest(request.headers.get("x-prerender-revalidate")) }).data === false) {
228
+ match = {
229
+ route: cachePathRoute,
230
+ params: {}
231
+ };
232
+ routeUrl = cachePathname + routeUrl.slice(routePathname.length);
233
+ } else cacheIdentityDiverges = true;
234
+ }
235
+ }
219
236
  const { route, params } = match;
220
237
  const pageModule = traceFindPageComponents(route.pattern, () => route.module);
221
238
  const isStaticPropsRoute = typeof pageModule.getStaticProps === "function";
@@ -234,10 +251,10 @@ function createPagesPageHandler(opts) {
234
251
  });
235
252
  else recordRouteCacheability({ cacheable: false });
236
253
  }
237
- const routeIsrGet = isCacheabilityProbe ? async () => null : isrGet;
238
- const routeIsrSet = isCacheabilityProbe ? async () => {} : isrSet;
254
+ const routeIsrGet = isCacheabilityProbe || cacheIdentityDiverges ? async () => null : isrGet;
255
+ const routeIsrSet = isCacheabilityProbe || cacheIdentityDiverges ? async () => {} : isrSet;
239
256
  const isStaticPropsRender = isStaticPropsRoute && typeof pageModule.getServerSideProps !== "function";
240
- if (!options?.__skipOnDemandCoalesce && !options?.__forcedRoute && isStaticPropsRoute && isOnDemandRevalidateRequest(request.headers.get("x-prerender-revalidate"))) {
257
+ if (!options?.__skipOnDemandCoalesce && !options?.__forcedRoute && !cacheIdentityDiverges && isStaticPropsRoute && isOnDemandRevalidateRequest(request.headers.get("x-prerender-revalidate"))) {
241
258
  const cacheKey = pageIsrCacheKey("pages", routeUrl.split("?")[0]);
242
259
  const snapshot = await coalesceOnDemandRevalidation(cacheKey, async () => {
243
260
  const response = await renderPage(request, url, manifest, middlewareHeaders, {
@@ -6,8 +6,7 @@ import { hasBasePath, stripBasePath } from "../utils/base-path.js";
6
6
  import { runWithExecutionContext } from "../shims/request-context.js";
7
7
  import { VINEXT_CACHEABILITY_PROBE_QUERY_PARAM, VINEXT_EXPECTED_WORKER_VERSION_HEADER } from "./headers.js";
8
8
  import { notFoundStaticAssetResponse } from "./http-error-responses.js";
9
- import { isOpenRedirectShaped } from "./open-redirect.js";
10
- import { attachRequestCfMetadata, cloneRequestWithHeaders, cloneRequestWithUrl, filterInternalHeaders } from "./request-pipeline.js";
9
+ import { attachRequestCfMetadata, cloneRequestWithHeaders, cloneRequestWithUrl, filterInternalHeaders, guardProtocolRelativeUrl } from "./request-pipeline.js";
11
10
  import { normalizeDefaultLocalePathname } from "./pages-i18n.js";
12
11
  import { getExplicitCdnCacheAdapter } from "../shims/cdn-cache-state.js";
13
12
  import { CACHEABILITY_REQUEST_STATE } from "../shims/cacheability-classification.js";
@@ -148,7 +147,8 @@ async function handleRequestImpl(request, env, platformCtx, dispatchResponseStag
148
147
  requestHost: url.host,
149
148
  stagedHeaders: null
150
149
  }, { cache: "bypass" }, env, ctx);
151
- if (isOpenRedirectShaped(pathname)) return new Response("This page could not be found", { status: 404 });
150
+ const protocolRelativeGuard = guardProtocolRelativeUrl(pathname, url.search);
151
+ if (protocolRelativeGuard) return protocolRelativeGuard;
152
152
  try {
153
153
  normalizePathnameForRouteMatchStrict(pathname);
154
154
  } catch {
@@ -166,6 +166,7 @@ async function handleRequestImpl(request, env, platformCtx, dispatchResponseStag
166
166
  }
167
167
  }
168
168
  const middlewareRequest = request;
169
+ const originalRenderUrl = pathname + new URL(request.url).search;
169
170
  const dataNorm = normalizeDataRequest(request);
170
171
  if (dataNorm.notFoundResponse && !vinextConfig?.skipProxyUrlNormalize) return dataNorm.notFoundResponse;
171
172
  const isDataReq = dataNorm.isDataReq;
@@ -229,7 +230,10 @@ async function handleRequestImpl(request, env, platformCtx, dispatchResponseStag
229
230
  kind: "pages-page",
230
231
  protocolVersion: 4,
231
232
  requestHost: new URL(req.url).host,
232
- renderOptions: options ?? null,
233
+ renderOptions: isDataReq ? {
234
+ ...options,
235
+ originalUrl: originalRenderUrl
236
+ } : options ?? null,
233
237
  resolvedUrl,
234
238
  stagedHeaders: isSharedStaticPage ? null : [...stagedHeaders ?? new Headers()]
235
239
  });
@@ -2,6 +2,7 @@ import { NextI18nConfig } from "../config/next-config.js";
2
2
  import { negotiateEncoding } from "./accept-encoding.js";
3
3
  import { StaticFileCache } from "./static-file-cache.js";
4
4
  import { resolveRequestHost, trustProxy, trustedHosts } from "./proxy-trust.js";
5
+ import { COMPRESS_THRESHOLD, isCompressibleContentType } from "./response-compression.js";
5
6
  import { IncomingMessage, ServerResponse } from "node:http";
6
7
  //#region src/server/prod-server.d.ts
7
8
  /**
@@ -68,10 +69,6 @@ export type ProdServerOptions = {
68
69
  /** Suppress the startup log for internal child-process servers. */
69
70
  silent?: boolean;
70
71
  };
71
- /** Content types that benefit from compression. */
72
- declare const COMPRESSIBLE_TYPES: Set<string>;
73
- /** Minimum size threshold for compression (in bytes). Below this, compression overhead isn't worth it. */
74
- declare const COMPRESS_THRESHOLD = 1024;
75
72
  /**
76
73
  * Merge middleware headers and a Web Response's headers into a single
77
74
  * record suitable for Node.js `res.writeHead()`. Uses `getSetCookie()`
@@ -104,15 +101,19 @@ declare function sendCompressed(req: IncomingMessage, res: ServerResponse, body:
104
101
  *
105
102
  * Without a cache, falls back to async filesystem probing (still non-blocking,
106
103
  * unlike the old sync existsSync/statSync approach).
104
+ *
105
+ * `extraHeaders` go on every response for the file. `representationHeaders` go
106
+ * only on responses that carry the file's bytes (200 and 206), never on a 304,
107
+ * 412, 416 or 405.
107
108
  */
108
- declare function tryServeStatic(req: IncomingMessage, res: ServerResponse, clientDir: string, pathname: string, compress: boolean, cache?: StaticFileCache, extraHeaders?: Record<string, string | string[]>, statusCode?: number): Promise<boolean>;
109
+ declare function tryServeStatic(req: IncomingMessage, res: ServerResponse, clientDir: string, pathname: string, compress: boolean, cache?: StaticFileCache, extraHeaders?: Record<string, string | string[]>, statusCode?: number, representationHeaders?: Record<string, string>): Promise<boolean>;
109
110
  /**
110
111
  * Convert a Node.js IncomingMessage to a Web Request object.
111
112
  *
112
113
  * When `urlOverride` is provided, it is used as the path + query string
113
114
  * instead of `req.url`.
114
115
  */
115
- declare function nodeToWebRequest(req: IncomingMessage, urlOverride?: string, prerenderSecret?: string, i18nConfig?: NextI18nConfig | null, authorizeOnDemandRevalidate?: (headerValue: string | null) => boolean): Request;
116
+ declare function nodeToWebRequest(req: IncomingMessage, urlOverride?: string, prerenderSecret?: string, i18nConfig?: NextI18nConfig | null, authorizeOnDemandRevalidate?: (headerValue: string | null) => boolean, signal?: AbortSignal): Request;
116
117
  /**
117
118
  * Stream a Web Response back to a Node.js ServerResponse.
118
119
  * Supports streaming compression for SSR responses.
@@ -155,4 +156,4 @@ export declare function resolveAppRouterPrerenderSeeder(entryModule: unknown): A
155
156
  */
156
157
  export declare function resolveAppRouterAssetPath(pathname: string, assetPathPrefix: string, assetPrefix: string): string | null;
157
158
  //#endregion
158
- export { COMPRESSIBLE_TYPES, COMPRESS_THRESHOLD, mergeResponseHeaders, mergeWebResponse, negotiateEncoding, nodeToWebRequest, resolveRequestHost as resolveHost, sendCompressed, sendWebResponse, trustProxy, trustedHosts, tryServeStatic, waitForNodeResponseCompletion };
159
+ export { COMPRESS_THRESHOLD, isCompressibleContentType, mergeResponseHeaders, mergeWebResponse, negotiateEncoding, nodeToWebRequest, resolveRequestHost as resolveHost, sendCompressed, sendWebResponse, trustProxy, trustedHosts, tryServeStatic, waitForNodeResponseCompletion };