vinext 0.1.3 → 0.1.5

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 (185) hide show
  1. package/dist/build/client-build-config.d.ts +11 -2
  2. package/dist/build/client-build-config.js +17 -6
  3. package/dist/build/css-url-assets.d.ts +1 -1
  4. package/dist/build/css-url-assets.js +9 -7
  5. package/dist/build/prerender.js +3 -1
  6. package/dist/cache/cache-adapters-virtual.js +1 -1
  7. package/dist/client/pages-router-link-navigation.d.ts +33 -7
  8. package/dist/client/pages-router-link-navigation.js +32 -2
  9. package/dist/client/vinext-next-data.js +2 -0
  10. package/dist/cloudflare/src/cache/kv-data-adapter.runtime.d.ts +1 -1
  11. package/dist/config/config-matchers.d.ts +11 -1
  12. package/dist/config/config-matchers.js +14 -2
  13. package/dist/config/tsconfig-paths.js +14 -1
  14. package/dist/deploy.js +20 -13
  15. package/dist/entries/app-rsc-entry.js +27 -22
  16. package/dist/entries/pages-client-entry.js +14 -13
  17. package/dist/entries/pages-server-entry.js +8 -27
  18. package/dist/index.js +365 -147
  19. package/dist/plugins/css-data-url.js +30 -26
  20. package/dist/plugins/dynamic-preload-metadata.js +2 -4
  21. package/dist/plugins/extensionless-dynamic-import.js +27 -24
  22. package/dist/plugins/fonts.js +5 -4
  23. package/dist/plugins/import-meta-url.js +21 -15
  24. package/dist/plugins/instrumentation-client.js +1 -1
  25. package/dist/plugins/middleware-server-only.js +7 -6
  26. package/dist/plugins/og-assets.js +48 -46
  27. package/dist/plugins/optimize-imports.js +9 -3
  28. package/dist/plugins/remove-console.d.ts +7 -1
  29. package/dist/plugins/remove-console.js +4 -1
  30. package/dist/plugins/require-context.js +21 -20
  31. package/dist/plugins/strip-server-exports.d.ts +16 -8
  32. package/dist/plugins/strip-server-exports.js +496 -46
  33. package/dist/routing/app-route-graph.js +2 -2
  34. package/dist/server/app-bfcache-identity.d.ts +26 -0
  35. package/dist/server/app-bfcache-identity.js +127 -0
  36. package/dist/server/app-browser-action-result.js +1 -1
  37. package/dist/server/app-browser-entry.js +22 -12
  38. package/dist/server/app-browser-navigation-controller.d.ts +1 -1
  39. package/dist/server/app-browser-navigation-controller.js +1 -1
  40. package/dist/server/app-browser-state.d.ts +3 -22
  41. package/dist/server/app-browser-state.js +23 -139
  42. package/dist/server/app-browser-stream.js +1 -1
  43. package/dist/server/app-browser-visible-commit.d.ts +1 -1
  44. package/dist/server/app-browser-visible-commit.js +3 -2
  45. package/dist/server/app-fallback-renderer.d.ts +1 -1
  46. package/dist/server/app-layout-param-observation.d.ts +1 -1
  47. package/dist/server/app-layout-param-observation.js +1 -1
  48. package/dist/server/app-middleware.js +2 -1
  49. package/dist/server/app-page-boundary-render.d.ts +1 -1
  50. package/dist/server/app-page-boundary.js +1 -1
  51. package/dist/server/app-page-cache-finalizer.d.ts +62 -0
  52. package/dist/server/app-page-cache-finalizer.js +122 -0
  53. package/dist/server/app-page-cache-render.d.ts +2 -2
  54. package/dist/server/app-page-cache-render.js +1 -1
  55. package/dist/server/app-page-cache.d.ts +2 -53
  56. package/dist/server/app-page-cache.js +5 -131
  57. package/dist/server/app-page-dispatch.d.ts +2 -2
  58. package/dist/server/app-page-dispatch.js +10 -8
  59. package/dist/server/app-page-probe.js +3 -2
  60. package/dist/server/app-page-render-observation.js +2 -2
  61. package/dist/server/app-page-render.d.ts +3 -3
  62. package/dist/server/app-page-render.js +3 -2
  63. package/dist/server/app-page-stream.d.ts +2 -9
  64. package/dist/server/app-page-stream.js +1 -35
  65. package/dist/server/app-pages-bridge.d.ts +5 -1
  66. package/dist/server/app-pages-bridge.js +5 -13
  67. package/dist/server/app-request-context.d.ts +1 -2
  68. package/dist/server/app-request-context.js +2 -1
  69. package/dist/server/app-route-handler-dispatch.js +3 -2
  70. package/dist/server/app-route-handler-execution.d.ts +1 -1
  71. package/dist/server/app-route-handler-execution.js +1 -1
  72. package/dist/server/app-route-handler-response.d.ts +1 -1
  73. package/dist/server/app-router-entry.js +2 -1
  74. package/dist/server/app-rsc-handler.d.ts +3 -0
  75. package/dist/server/app-rsc-handler.js +73 -31
  76. package/dist/server/app-rsc-response-finalizer.js +1 -1
  77. package/dist/server/app-rsc-route-matching.js +6 -2
  78. package/dist/server/app-server-action-execution.d.ts +1 -1
  79. package/dist/server/app-server-action-execution.js +10 -6
  80. package/dist/server/app-ssr-entry.d.ts +1 -1
  81. package/dist/server/app-ssr-entry.js +12 -38
  82. package/dist/server/app-ssr-router-instance.d.ts +6 -0
  83. package/dist/server/app-ssr-router-instance.js +24 -0
  84. package/dist/server/app-ssr-stream.js +1 -1
  85. package/dist/server/artifact-compatibility.js +1 -1
  86. package/dist/server/before-interactive-head.d.ts +17 -0
  87. package/dist/server/before-interactive-head.js +35 -0
  88. package/dist/server/client-reuse-manifest.js +1 -1
  89. package/dist/server/csp.js +1 -4
  90. package/dist/server/defer-until-stream-consumed.d.ts +7 -0
  91. package/dist/server/defer-until-stream-consumed.js +34 -0
  92. package/dist/server/dev-server.js +82 -37
  93. package/dist/server/instrumentation.js +1 -1
  94. package/dist/server/isr-cache.d.ts +1 -1
  95. package/dist/server/isr-cache.js +1 -1
  96. package/dist/server/isr-decision.d.ts +1 -1
  97. package/dist/server/middleware-matcher.js +20 -9
  98. package/dist/server/middleware-runtime.d.ts +3 -4
  99. package/dist/server/middleware-runtime.js +4 -2
  100. package/dist/server/navigation-planner.d.ts +3 -12
  101. package/dist/server/navigation-planner.js +24 -0
  102. package/dist/server/navigation-trace.d.ts +2 -1
  103. package/dist/server/navigation-trace.js +1 -0
  104. package/dist/server/open-redirect.d.ts +12 -0
  105. package/dist/server/open-redirect.js +21 -0
  106. package/dist/server/operation-token.d.ts +40 -0
  107. package/dist/server/operation-token.js +85 -0
  108. package/dist/server/pages-data-route.d.ts +1 -1
  109. package/dist/server/pages-data-route.js +7 -4
  110. package/dist/server/pages-dev-module-url.d.ts +4 -0
  111. package/dist/server/pages-dev-module-url.js +15 -0
  112. package/dist/server/pages-document-initial-props.d.ts +4 -15
  113. package/dist/server/pages-document-initial-props.js +27 -56
  114. package/dist/server/pages-i18n.js +2 -2
  115. package/dist/server/pages-page-data.d.ts +1 -1
  116. package/dist/server/pages-page-data.js +3 -1
  117. package/dist/server/pages-page-handler.js +3 -1
  118. package/dist/server/pages-page-response.d.ts +3 -1
  119. package/dist/server/pages-page-response.js +6 -6
  120. package/dist/server/pages-readiness.js +1 -1
  121. package/dist/server/pages-request-pipeline.d.ts +7 -7
  122. package/dist/server/pages-request-pipeline.js +63 -21
  123. package/dist/server/prod-server.d.ts +3 -1
  124. package/dist/server/prod-server.js +43 -11
  125. package/dist/server/request-pipeline.d.ts +1 -24
  126. package/dist/server/request-pipeline.js +1 -33
  127. package/dist/server/seed-cache.d.ts +1 -1
  128. package/dist/server/static-file-cache.js +16 -4
  129. package/dist/shims/before-interactive-context.d.ts +14 -3
  130. package/dist/shims/cache-handler.d.ts +106 -0
  131. package/dist/shims/cache-handler.js +176 -0
  132. package/dist/shims/cache-request-state.d.ts +47 -0
  133. package/dist/shims/cache-request-state.js +126 -0
  134. package/dist/shims/cache-runtime.d.ts +2 -2
  135. package/dist/shims/cache-runtime.js +3 -14
  136. package/dist/shims/cache.d.ts +3 -231
  137. package/dist/shims/cache.js +17 -383
  138. package/dist/shims/cdn-cache.d.ts +1 -1
  139. package/dist/shims/cdn-cache.js +1 -1
  140. package/dist/shims/document.d.ts +15 -20
  141. package/dist/shims/document.js +5 -8
  142. package/dist/shims/error-boundary-navigation.d.ts +7 -0
  143. package/dist/shims/error-boundary-navigation.js +44 -0
  144. package/dist/shims/error-boundary.js +10 -8
  145. package/dist/shims/error.js +2 -1
  146. package/dist/shims/fetch-cache.js +1 -1
  147. package/dist/shims/form.js +1 -1
  148. package/dist/shims/image.js +74 -9
  149. package/dist/shims/internal/app-page-props-cache-key.d.ts +5 -0
  150. package/dist/shims/internal/app-page-props-cache-key.js +16 -0
  151. package/dist/shims/internal/navigation-untracked.js +2 -1
  152. package/dist/shims/internal/pages-data-fetch-dedup.d.ts +6 -7
  153. package/dist/shims/internal/pages-data-fetch-dedup.js +67 -14
  154. package/dist/shims/internal/pages-data-target.js +1 -1
  155. package/dist/shims/layout-segment-context.d.ts +1 -1
  156. package/dist/shims/layout-segment-context.js +2 -1
  157. package/dist/shims/link.js +38 -17
  158. package/dist/shims/metadata.js +4 -4
  159. package/dist/shims/navigation-context-state.d.ts +40 -0
  160. package/dist/shims/navigation-context-state.js +116 -0
  161. package/dist/shims/navigation-errors.d.ts +55 -0
  162. package/dist/shims/navigation-errors.js +110 -0
  163. package/dist/shims/navigation-server.d.ts +3 -0
  164. package/dist/shims/navigation-server.js +3 -0
  165. package/dist/shims/navigation-state.d.ts +1 -2
  166. package/dist/shims/navigation-state.js +2 -1
  167. package/dist/shims/navigation.d.ts +3 -291
  168. package/dist/shims/navigation.js +16 -445
  169. package/dist/shims/navigation.react-server.d.ts +2 -2
  170. package/dist/shims/navigation.react-server.js +3 -1
  171. package/dist/shims/request-state-types.d.ts +3 -3
  172. package/dist/shims/router.d.ts +6 -2
  173. package/dist/shims/router.js +99 -20
  174. package/dist/shims/script.js +9 -5
  175. package/dist/shims/slot.js +3 -1
  176. package/dist/shims/unified-request-context.d.ts +2 -2
  177. package/dist/utils/has-trailing-comma.d.ts +24 -0
  178. package/dist/utils/has-trailing-comma.js +62 -0
  179. package/dist/utils/text-stream.d.ts +1 -1
  180. package/dist/utils/text-stream.js +2 -2
  181. package/dist/utils/virtual-module.d.ts +5 -0
  182. package/dist/utils/virtual-module.js +0 -0
  183. package/dist/utils/vite-version.d.ts +12 -1
  184. package/dist/utils/vite-version.js +9 -1
  185. package/package.json +5 -1
@@ -0,0 +1,24 @@
1
+ import { assertSafeNavigationUrl } from "../shims/url-safety.js";
2
+ import "./app-bfcache-id.js";
3
+ //#region src/server/app-ssr-router-instance.ts
4
+ function validateNavigationHref(href) {
5
+ assertSafeNavigationUrl(href);
6
+ }
7
+ const ssrAppRouterInstance = {
8
+ bfcacheId: "0",
9
+ back() {},
10
+ forward() {},
11
+ refresh() {},
12
+ push(href, _options) {
13
+ validateNavigationHref(href);
14
+ },
15
+ replace(href, _options) {
16
+ validateNavigationHref(href);
17
+ },
18
+ prefetch(href) {
19
+ validateNavigationHref(href);
20
+ }
21
+ };
22
+ if (process.env.__NEXT_GESTURE_TRANSITION) ssrAppRouterInstance.experimental_gesturePush = validateNavigationHref;
23
+ //#endregion
24
+ export { ssrAppRouterInstance };
@@ -1,6 +1,6 @@
1
- import { NAVIGATION_RUNTIME_SYMBOL_DESCRIPTION } from "../client/navigation-runtime.js";
2
1
  import { createInlineScriptTag, escapeHtmlAttr, htmlTokenListContains, safeJsonStringify } from "./html.js";
3
2
  import { bytesToBase64, concatUint8Arrays } from "./app-rsc-embedded-chunks.js";
3
+ import { NAVIGATION_RUNTIME_SYMBOL_DESCRIPTION } from "../client/navigation-runtime.js";
4
4
  //#region src/server/app-ssr-stream.ts
5
5
  const NAVIGATION_RUNTIME_REFERENCE = `self[Symbol.for(${safeJsonStringify(NAVIGATION_RUNTIME_SYMBOL_DESCRIPTION)})]`;
6
6
  function navigationRuntimeRscBootstrapExpression() {
@@ -1,5 +1,5 @@
1
- import { fnv1a64 } from "../utils/hash.js";
2
1
  import { isUnknownRecord } from "../utils/record.js";
2
+ import { fnv1a64 } from "../utils/hash.js";
3
3
  //#region src/server/artifact-compatibility.ts
4
4
  const ARTIFACT_COMPATIBILITY_SCHEMA_VERSION = 1;
5
5
  const APP_ELEMENTS_SCHEMA_VERSION = 1;
@@ -0,0 +1,17 @@
1
+ import { BeforeInteractiveInlineScript } from "../shims/before-interactive-context.js";
2
+
3
+ //#region src/server/before-interactive-head.d.ts
4
+ /**
5
+ * Render captured `<Script strategy="beforeInteractive">` scripts to HTML,
6
+ * ready to splice immediately after `<head ...>` opens. Each entry has already
7
+ * had its inline content escaped via `escapeInlineContent(..., "script")`
8
+ * inside the Script shim, so this function only quotes the attributes that
9
+ * actually go on the tag (id, src, nonce, plus the residual passthroughs).
10
+ *
11
+ * Keeping this function in its own module makes the boundary obvious: anything
12
+ * passed through here is being concatenated directly into HTML; treat the
13
+ * inputs accordingly.
14
+ */
15
+ declare function renderBeforeInteractiveInlineScripts(scripts: readonly BeforeInteractiveInlineScript[]): string;
16
+ //#endregion
17
+ export { renderBeforeInteractiveInlineScripts };
@@ -0,0 +1,35 @@
1
+ import { createNonceAttribute, escapeHtmlAttr } from "./html.js";
2
+ //#region src/server/before-interactive-head.ts
3
+ const VALID_ATTR_NAME = /^[a-zA-Z][\w.-]*$/;
4
+ /**
5
+ * Render captured `<Script strategy="beforeInteractive">` scripts to HTML,
6
+ * ready to splice immediately after `<head ...>` opens. Each entry has already
7
+ * had its inline content escaped via `escapeInlineContent(..., "script")`
8
+ * inside the Script shim, so this function only quotes the attributes that
9
+ * actually go on the tag (id, src, nonce, plus the residual passthroughs).
10
+ *
11
+ * Keeping this function in its own module makes the boundary obvious: anything
12
+ * passed through here is being concatenated directly into HTML; treat the
13
+ * inputs accordingly.
14
+ */
15
+ function renderBeforeInteractiveInlineScripts(scripts) {
16
+ if (scripts.length === 0) return "";
17
+ let html = "";
18
+ for (const script of scripts) {
19
+ let attrs = "";
20
+ if (script.id) attrs += ` id="${escapeHtmlAttr(script.id)}"`;
21
+ if (script.src) attrs += ` src="${escapeHtmlAttr(script.src)}"`;
22
+ attrs += createNonceAttribute(script.nonce);
23
+ if (script.attributes) for (const [key, value] of Object.entries(script.attributes)) {
24
+ if (!VALID_ATTR_NAME.test(key)) continue;
25
+ if (key === "data-nscript") continue;
26
+ if (value === true) attrs += ` ${key}`;
27
+ else if (typeof value === "string") attrs += ` ${key}="${escapeHtmlAttr(value)}"`;
28
+ }
29
+ attrs += ` data-nscript="beforeInteractive"`;
30
+ html += `<script${attrs}>${script.innerHTML ?? ""}<\/script>`;
31
+ }
32
+ return html;
33
+ }
34
+ //#endregion
35
+ export { renderBeforeInteractiveInlineScripts };
@@ -1,5 +1,5 @@
1
- import { fnv1a64 } from "../utils/hash.js";
2
1
  import { isUnknownRecord } from "../utils/record.js";
2
+ import { fnv1a64 } from "../utils/hash.js";
3
3
  import { parseArtifactCompatibilityEnvelope } from "./artifact-compatibility.js";
4
4
  import { AppElementsWire } from "./app-elements-wire.js";
5
5
  import { isNonNegativeSafeInteger } from "../utils/number.js";
@@ -1,8 +1,5 @@
1
1
  //#region src/server/csp.ts
2
2
  const ESCAPE_REGEX = /[&><\u2028\u2029]/;
3
- function matchesDirectiveName(directive, name) {
4
- return directive === name || directive.startsWith(`${name} `);
5
- }
6
3
  function getNodeHeaderValue(headers, key) {
7
4
  const value = headers?.[key];
8
5
  if (Array.isArray(value)) return value.join(", ");
@@ -11,7 +8,7 @@ function getNodeHeaderValue(headers, key) {
11
8
  }
12
9
  function getScriptNonceFromHeader(cspHeaderValue) {
13
10
  const directives = cspHeaderValue.split(";").map((directive) => directive.trim());
14
- const directive = directives.find((value) => matchesDirectiveName(value, "script-src")) ?? directives.find((value) => matchesDirectiveName(value, "default-src"));
11
+ const directive = directives.find((value) => value.startsWith("script-src")) ?? directives.find((value) => value.startsWith("default-src"));
15
12
  if (!directive) return;
16
13
  const nonce = directive.split(" ").slice(1).map((source) => source.trim()).find((source) => source.startsWith("'nonce-") && source.length > 8 && source.endsWith("'"))?.slice(7, -1);
17
14
  if (!nonce) return;
@@ -0,0 +1,7 @@
1
+ //#region src/server/defer-until-stream-consumed.d.ts
2
+ /**
3
+ * Defers cleanup until the downstream consumer drains or cancels the stream.
4
+ */
5
+ declare function deferUntilStreamConsumed(stream: ReadableStream<Uint8Array>, onFlush: () => void): ReadableStream<Uint8Array>;
6
+ //#endregion
7
+ export { deferUntilStreamConsumed };
@@ -0,0 +1,34 @@
1
+ //#region src/server/defer-until-stream-consumed.ts
2
+ /**
3
+ * Defers cleanup until the downstream consumer drains or cancels the stream.
4
+ */
5
+ function deferUntilStreamConsumed(stream, onFlush) {
6
+ let called = false;
7
+ const once = () => {
8
+ if (!called) {
9
+ called = true;
10
+ onFlush();
11
+ }
12
+ };
13
+ const cleanup = new TransformStream({ flush() {
14
+ once();
15
+ } });
16
+ const reader = stream.pipeThrough(cleanup).getReader();
17
+ return new ReadableStream({
18
+ pull(controller) {
19
+ return reader.read().then(({ done, value }) => {
20
+ if (done) controller.close();
21
+ else controller.enqueue(value);
22
+ }, (error) => {
23
+ once();
24
+ controller.error(error);
25
+ });
26
+ },
27
+ cancel(reason) {
28
+ once();
29
+ return reader.cancel(reason);
30
+ }
31
+ });
32
+ }
33
+ //#endregion
34
+ export { deferUntilStreamConsumed };
@@ -7,10 +7,10 @@ import { normalizeStaticPathname } from "../routing/route-pattern.js";
7
7
  import { importModule, reportRequestError } from "./instrumentation.js";
8
8
  import { buildCacheStateHeaders } from "./cache-headers.js";
9
9
  import { isUnknownRecord } from "../utils/record.js";
10
- import { _runWithCacheState } from "../shims/cache.js";
11
10
  import { NEVER_CACHE_CONTROL, NO_STORE_CACHE_CONTROL } from "./cache-control.js";
12
11
  import { buildMissIsrCacheControl, decideIsr } from "./isr-decision.js";
13
12
  import { PRERENDER_REVALIDATE_HEADER, buildPagesCacheValue, getRevalidateDuration, isOnDemandRevalidateRequest, isrCacheKey, isrGet, isrSet, setRevalidateDuration, triggerBackgroundRegeneration } from "./isr-cache.js";
13
+ import { _runWithCacheState } from "../shims/cache-request-state.js";
14
14
  import { ensureFetchPatch, runWithFetchCache } from "../shims/fetch-cache.js";
15
15
  import { runWithPrivateCache } from "../shims/cache-runtime.js";
16
16
  import { mergeRouteParamsIntoQuery, parseQueryString } from "../utils/query.js";
@@ -26,6 +26,7 @@ import { detectLocaleFromAcceptLanguage, extractLocaleFromUrl as extractLocaleFr
26
26
  import { buildDefaultPagesNotFoundResponse } from "./pages-default-404.js";
27
27
  import { buildPagesReadinessNextData } from "./pages-readiness.js";
28
28
  import { resolvePagesPageMethodResponse } from "./pages-page-method.js";
29
+ import { createPagesDevModuleUrl } from "./pages-dev-module-url.js";
29
30
  import { isSerializableProps } from "./pages-serializable-props.js";
30
31
  import { loadUserDocumentInitialProps, runDocumentRenderPage } from "./pages-document-initial-props.js";
31
32
  import { callDocumentGetInitialProps } from "./document-initial-head.js";
@@ -100,7 +101,7 @@ const STREAM_BODY_MARKER = "<!--VINEXT_STREAM_BODY-->";
100
101
  * shell sooner).
101
102
  */
102
103
  async function streamPageToResponse(res, element, options) {
103
- const { url, server, fontHeadHTML, scripts, DocumentComponent, statusCode = 200, extraHeaders, getHeadHTML, enhancePageElement, scriptNonce, documentContext, setDocumentInitialHead } = options;
104
+ const { url, server, fontHeadHTML, scripts, DocumentComponent, statusCode = 200, extraHeaders, getHeadHTML, enhancePageElement, scriptNonce, documentContext, setDocumentInitialHead, bufferBodyBeforeHeaders = false } = options;
104
105
  const documentRenderPage = await runDocumentRenderPage({
105
106
  DocumentComponent,
106
107
  enhancePageElement,
@@ -144,6 +145,7 @@ async function streamPageToResponse(res, element, options) {
144
145
  const markerIdx = transformedShell.indexOf(STREAM_BODY_MARKER);
145
146
  const prefix = transformedShell.slice(0, markerIdx);
146
147
  const suffix = transformedShell.slice(markerIdx + 25);
148
+ const bufferedBody = bufferBodyBeforeHeaders ? await new Response(bodyStream).text() : null;
147
149
  const headers = {
148
150
  "Content-Type": "text/html",
149
151
  "Transfer-Encoding": "chunked"
@@ -152,6 +154,10 @@ async function streamPageToResponse(res, element, options) {
152
154
  else headers[key] = val;
153
155
  res.writeHead(statusCode, headers);
154
156
  res.write(prefix);
157
+ if (bufferedBody !== null) {
158
+ res.end(bufferedBody + suffix);
159
+ return;
160
+ }
155
161
  const reader = bodyStream.getReader();
156
162
  try {
157
163
  for (;;) {
@@ -622,9 +628,10 @@ function createSSRHandler(server, runner, routes, pagesDir, i18nConfig, fileMatc
622
628
  }) : React.createElement(pageModule.default, freshPageProps);
623
629
  if (routerShim.wrapWithRouterContext) el = routerShim.wrapWithRouterContext(el);
624
630
  const freshBody = await renderIsrPassToStringAsync(withScriptNonce(el, scriptNonce));
625
- const viteRoot = server.config?.root;
626
- const regenPageUrl = viteRoot ? "/" + path.relative(viteRoot, route.filePath) : route.filePath;
627
- const regenAppUrl = RegenApp ? viteRoot ? "/" + path.relative(viteRoot, path.join(pagesDir, "_app")) : path.join(pagesDir, "_app") : null;
631
+ const viteRoot = server.config.root;
632
+ const viteBase = server.config.base;
633
+ const regenPageUrl = createPagesDevModuleUrl(viteRoot, route.filePath, viteBase);
634
+ const regenAppUrl = RegenApp ? createPagesDevModuleUrl(viteRoot, path.join(pagesDir, "_app"), viteBase) : null;
628
635
  const freshPagesNextData = {
629
636
  ...pagesNextData,
630
637
  __vinext: {
@@ -634,7 +641,7 @@ function createSSRHandler(server, runner, routes, pagesDir, i18nConfig, fileMatc
634
641
  hasMiddleware
635
642
  }
636
643
  };
637
- await isrSet(cacheKey, buildPagesCacheValue(`<!DOCTYPE html><html><head></head><body><div id="__next">${freshBody}</div>${`<script>window.__NEXT_DATA__ = ${safeJsonStringify({
644
+ await isrSet(cacheKey, buildPagesCacheValue(`<!DOCTYPE html><html><head></head><body><div id="__next">${freshBody}</div>${`<script id="__NEXT_DATA__" type="application/json">${safeJsonStringify({
638
645
  props: freshRenderProps,
639
646
  page: patternToNextFormat(route.pattern),
640
647
  query: params,
@@ -645,7 +652,7 @@ function createSSRHandler(server, runner, routes, pagesDir, i18nConfig, fileMatc
645
652
  defaultLocale: currentDefaultLocale,
646
653
  domainLocales,
647
654
  ...freshPagesNextData
648
- })}${i18nConfig ? `;window.__VINEXT_LOCALE__=${safeJsonStringify(locale ?? currentDefaultLocale)};window.__VINEXT_LOCALES__=${safeJsonStringify(i18nConfig.locales)};window.__VINEXT_DEFAULT_LOCALE__=${safeJsonStringify(currentDefaultLocale)}` : ""}<\/script>`}\n ${cachedHtml.match(/<script type="module">[\s\S]*?<\/script>/)?.[0] ?? ""}</body></html>`, freshRenderProps), revalidate);
655
+ })}<\/script>`}\n ${cachedHtml.match(/<script type="module">[\s\S]*?<\/script>/)?.[0] ?? ""}</body></html>`, freshRenderProps), revalidate);
649
656
  setRevalidateDuration(cacheKey, revalidate);
650
657
  }
651
658
  }
@@ -809,8 +816,11 @@ function createSSRHandler(server, runner, routes, pagesDir, i18nConfig, fileMatc
809
816
  }
810
817
  if (allFontStyles.length > 0) fontHeadHTML += `<style data-vinext-fonts${nonceAttr}>${allFontStyles.join("\n")}</style>\n `;
811
818
  const viteRoot = server.config.root;
812
- const pageModuleUrl = "/" + path.relative(viteRoot, route.filePath);
813
- const appModuleUrl = AppComponent ? "/" + path.relative(viteRoot, path.join(pagesDir, "_app")) : null;
819
+ const viteBase = server.config.base;
820
+ const pageModuleUrl = createPagesDevModuleUrl(viteRoot, route.filePath, viteBase);
821
+ const pageModuleSource = createPagesDevModuleUrl(viteRoot, route.filePath, "/");
822
+ const appModuleUrl = AppComponent ? createPagesDevModuleUrl(viteRoot, path.join(pagesDir, "_app"), viteBase) : null;
823
+ const appModuleSource = AppComponent ? createPagesDevModuleUrl(viteRoot, path.join(pagesDir, "_app"), "/") : null;
814
824
  const serializedPagesNextData = {
815
825
  ...pagesNextData,
816
826
  __vinext: {
@@ -825,14 +835,22 @@ function createSSRHandler(server, runner, routes, pagesDir, i18nConfig, fileMatc
825
835
  import "vinext/instrumentation-client";
826
836
  import React from "react";
827
837
  import { hydrateRoot } from "react-dom/client";
828
- import Router, { wrapWithRouterContext } from "next/router";
838
+ import Router, { wrapWithRouterContext, _initializePagesRouterReadyFromNextData } from "next/router";
829
839
 
840
+ const nextDataElement = document.getElementById("__NEXT_DATA__");
841
+ if (nextDataElement?.textContent) {
842
+ window.__NEXT_DATA__ = JSON.parse(nextDataElement.textContent);
843
+ window.__VINEXT_LOCALE__ = window.__NEXT_DATA__.locale;
844
+ window.__VINEXT_LOCALES__ = window.__NEXT_DATA__.locales;
845
+ window.__VINEXT_DEFAULT_LOCALE__ = window.__NEXT_DATA__.defaultLocale;
846
+ }
830
847
  const nextData = window.__NEXT_DATA__;
848
+ _initializePagesRouterReadyFromNextData(nextData);
831
849
  const props = nextData.props && typeof nextData.props === "object" ? nextData.props : {};
832
850
  const rawPageProps = props.pageProps;
833
851
  const pageProps = rawPageProps && typeof rawPageProps === "object" ? rawPageProps : {};
834
- window.__VINEXT_PAGE_LOADERS__ = { [nextData.page]: () => import("${pageModuleUrl}") };
835
- window.__VINEXT_APP_LOADER__ = ${appModuleUrl ? `() => import("${appModuleUrl}")` : "undefined"};
852
+ window.__VINEXT_PAGE_LOADERS__ = { [nextData.page]: () => import("${pageModuleSource}") };
853
+ window.__VINEXT_APP_LOADER__ = ${appModuleSource ? `() => import("${appModuleSource}")` : "undefined"};
836
854
 
837
855
  async function hydrate() {
838
856
  let hydrateRootOptions;
@@ -847,11 +865,11 @@ async function hydrate() {
847
865
  };
848
866
  }
849
867
 
850
- const pageModule = await import("${pageModuleUrl}");
868
+ const pageModule = await import("${pageModuleSource}");
851
869
  const PageComponent = pageModule.default;
852
870
  let element;
853
- ${appModuleUrl ? `
854
- const appModule = await import("${appModuleUrl}");
871
+ ${appModuleSource ? `
872
+ const appModule = await import("${appModuleSource}");
855
873
  const AppComponent = appModule.default;
856
874
  window.__VINEXT_APP__ = AppComponent;
857
875
  element = React.createElement(AppComponent, {
@@ -880,7 +898,7 @@ async function hydrate() {
880
898
  }
881
899
  hydrate();
882
900
  <\/script>`;
883
- const nextDataScript = createInlineScriptTag(`window.__NEXT_DATA__ = ${safeJsonStringify({
901
+ const nextDataScript = `<script id="__NEXT_DATA__" type="application/json"${nonceAttr}>${safeJsonStringify({
884
902
  props: renderProps,
885
903
  page: patternToNextFormat(route.pattern),
886
904
  query: params,
@@ -891,7 +909,7 @@ hydrate();
891
909
  defaultLocale: currentDefaultLocale,
892
910
  domainLocales,
893
911
  ...serializedPagesNextData
894
- })}${i18nConfig ? `;window.__VINEXT_LOCALE__=${safeJsonStringify(locale ?? currentDefaultLocale)};window.__VINEXT_LOCALES__=${safeJsonStringify(i18nConfig.locales)};window.__VINEXT_DEFAULT_LOCALE__=${safeJsonStringify(currentDefaultLocale)}` : ""}`, scriptNonce);
912
+ })}<\/script>`;
895
913
  const docPath = path.join(pagesDir, "_document");
896
914
  let DocumentComponent = null;
897
915
  if (findFileWithExtensions(docPath, matcher)) try {
@@ -939,7 +957,8 @@ hydrate();
939
957
  const traceHTML = getClientTraceMetadataHTML(clientTraceMetadata);
940
958
  return traceHTML ? `${headHTML}\n ${traceHTML}` : headHTML;
941
959
  },
942
- setDocumentInitialHead: typeof headShim.setDocumentInitialHead === "function" ? headShim.setDocumentInitialHead : void 0
960
+ setDocumentInitialHead: typeof headShim.setDocumentInitialHead === "function" ? headShim.setDocumentInitialHead : void 0,
961
+ bufferBodyBeforeHeaders: true
943
962
  });
944
963
  _renderEnd = now();
945
964
  if (typeof routerShim.setSSRContext === "function") routerShim.setSSRContext(null);
@@ -1015,41 +1034,67 @@ async function renderErrorPage(server, runner, req, res, url, pagesDir, statusCo
1015
1034
  if (!wrapFn) try {
1016
1035
  wrapFn = (await importModule(runner, "next/router")).wrapWithRouterContext;
1017
1036
  } catch {}
1018
- let element;
1019
- if (AppComponent) element = createElement(AppComponent, {
1020
- Component: ErrorComponent,
1021
- pageProps: errorProps
1022
- });
1023
- else element = createElement(ErrorComponent, errorProps);
1024
- if (wrapFn) element = wrapFn(element);
1025
- const bodyHtml = await renderToStringAsync(element);
1026
- let html;
1027
1037
  let DocumentComponent = null;
1028
1038
  const docPathErr = path.join(pagesDir, "_document");
1029
1039
  if (findFileWithExtensions(docPathErr, matcher)) try {
1030
1040
  DocumentComponent = (await importModule(runner, docPathErr)).default ?? null;
1031
1041
  } catch {}
1042
+ const createErrorElement = (FinalApp, FinalComponent) => {
1043
+ let errorElement = FinalApp ? createElement(FinalApp, {
1044
+ Component: FinalComponent,
1045
+ pageProps: errorProps
1046
+ }) : createElement(FinalComponent, errorProps);
1047
+ if (wrapFn) errorElement = wrapFn(errorElement);
1048
+ return errorElement;
1049
+ };
1050
+ const element = createErrorElement(AppComponent, ErrorComponent);
1051
+ const headShim = await importModule(runner, "next/head");
1052
+ if (typeof headShim.resetSSRHead === "function") headShim.resetSSRHead();
1032
1053
  if (DocumentComponent) {
1033
- const docProps = await loadUserDocumentInitialProps(DocumentComponent);
1034
- let docHtml = await renderToStringAsync(docProps ? createElement(DocumentComponent, docProps) : createElement(DocumentComponent));
1035
- docHtml = docHtml.replace("__NEXT_MAIN__", bodyHtml);
1036
- docHtml = docHtml.replace("<!-- __NEXT_SCRIPTS__ -->", "");
1037
- html = docHtml;
1038
- } else html = `<!DOCTYPE html>
1054
+ const errorPathname = candidate === "_error" ? "/_error" : `/${candidate}`;
1055
+ await streamPageToResponse(res, element, {
1056
+ url,
1057
+ server,
1058
+ fontHeadHTML: "",
1059
+ scripts: "",
1060
+ DocumentComponent,
1061
+ statusCode,
1062
+ documentContext: {
1063
+ err,
1064
+ pathname: errorPathname,
1065
+ query: parseQueryString(url),
1066
+ asPath: url,
1067
+ req,
1068
+ res
1069
+ },
1070
+ enhancePageElement: (renderPageOpts) => {
1071
+ let FinalApp = AppComponent;
1072
+ let FinalComponent = ErrorComponent;
1073
+ if (renderPageOpts.enhanceApp && FinalApp) FinalApp = renderPageOpts.enhanceApp(FinalApp);
1074
+ if (renderPageOpts.enhanceComponent) FinalComponent = renderPageOpts.enhanceComponent(FinalComponent);
1075
+ return createErrorElement(FinalApp, FinalComponent);
1076
+ },
1077
+ getHeadHTML: () => typeof headShim.getSSRHeadHTML === "function" ? headShim.getSSRHeadHTML() : "",
1078
+ setDocumentInitialHead: typeof headShim.setDocumentInitialHead === "function" ? headShim.setDocumentInitialHead : void 0
1079
+ });
1080
+ } else {
1081
+ const html = `<!DOCTYPE html>
1039
1082
  <html>
1040
1083
  <head>
1041
1084
  <meta charset="utf-8" />
1042
1085
  <meta name="viewport" content="width=device-width, initial-scale=1" />
1043
1086
  </head>
1044
1087
  <body>
1045
- <div id="__next">${bodyHtml}</div>
1088
+ <div id="__next">${await renderToStringAsync(element)}</div>
1046
1089
  </body>
1047
1090
  </html>`;
1048
- const transformedHtml = await server.transformIndexHtml(url, html);
1049
- res.writeHead(statusCode, { "Content-Type": "text/html" });
1050
- res.end(transformedHtml);
1091
+ const transformedHtml = await server.transformIndexHtml(url, html);
1092
+ res.writeHead(statusCode, { "Content-Type": "text/html" });
1093
+ res.end(transformedHtml);
1094
+ }
1051
1095
  return;
1052
1096
  } catch {
1097
+ if (res.headersSent || res.writableEnded) return;
1053
1098
  continue;
1054
1099
  }
1055
1100
  if (statusCode === 404) {
@@ -51,7 +51,7 @@ async function importModule(runner, id) {
51
51
  const INSTRUMENTATION_LOCATIONS = ["", "src/"];
52
52
  function findInstrumentationHookFile(root, basename, fileMatcher) {
53
53
  for (const dir of INSTRUMENTATION_LOCATIONS) for (const ext of fileMatcher.dottedExtensions) {
54
- const fullPath = path.join(root, dir, `${basename}${ext}`);
54
+ const fullPath = path.posix.join(root, dir, `${basename}${ext}`);
55
55
  if (fs.existsSync(fullPath)) return fullPath;
56
56
  }
57
57
  return null;
@@ -1,5 +1,5 @@
1
1
  import { RenderObservation } from "./cache-proof.js";
2
- import { CacheHandlerValue, CachedAppPageValue, CachedPagesValue, IncrementalCacheValue } from "../shims/cache.js";
2
+ import { CacheHandlerValue, CachedAppPageValue, CachedPagesValue, IncrementalCacheValue } from "../shims/cache-handler.js";
3
3
  import { OnRequestErrorContext } from "./instrumentation.js";
4
4
  import { normalizeMountedSlotsHeader } from "./app-mounted-slots-header.js";
5
5
  import { AppRscRenderMode } from "./app-rsc-render-mode.js";
@@ -1,7 +1,7 @@
1
1
  import { getRequestExecutionContext } from "../shims/request-context.js";
2
2
  import { reportRequestError } from "./instrumentation.js";
3
- import { fnv1a64 } from "../utils/hash.js";
4
3
  import { getCdnCacheAdapter } from "../shims/cdn-cache.js";
4
+ import { fnv1a64 } from "../utils/hash.js";
5
5
  import { normalizeMountedSlotsHeader } from "./app-mounted-slots-header.js";
6
6
  import { APP_RSC_RENDER_MODE_NAVIGATION, getRscRenderModeCacheVariant } from "./app-rsc-render-mode.js";
7
7
  import { normalizeAppPageInterceptionProofPathname } from "./app-page-render-identity.js";
@@ -1,4 +1,4 @@
1
- import { CacheControlMetadata } from "../shims/cache.js";
1
+ import { CacheControlMetadata } from "../shims/cache-handler.js";
2
2
  import { NEVER_CACHE_CONTROL, NO_STORE_CACHE_CONTROL } from "./cache-control.js";
3
3
 
4
4
  //#region src/server/isr-decision.d.ts
@@ -1,5 +1,5 @@
1
1
  import { removeTrailingSlash } from "../utils/base-path.js";
2
- import { checkHasConditions, requestContextFromRequest, safeRegExp } from "../config/config-matchers.js";
2
+ import { checkHasConditions, isSafeRegex, requestContextFromRequest, safeRegExp } from "../config/config-matchers.js";
3
3
  //#region src/server/middleware-matcher.ts
4
4
  const EMPTY_MIDDLEWARE_REQUEST_CONTEXT = {
5
5
  headers: new Headers(),
@@ -7,6 +7,7 @@ const EMPTY_MIDDLEWARE_REQUEST_CONTEXT = {
7
7
  query: new URLSearchParams(),
8
8
  host: ""
9
9
  };
10
+ const UNSAFE_MATCHER_PATTERN = Symbol("unsafe matcher pattern");
10
11
  const _mwPatternCache = /* @__PURE__ */ new Map();
11
12
  function matchesMiddleware(pathname, matcher, request, i18nConfig) {
12
13
  if (!matcher) return true;
@@ -47,16 +48,19 @@ function stripLocalePrefix(pathname, i18nConfig) {
47
48
  const segments = pathname.split("/");
48
49
  const firstSegment = segments[1];
49
50
  if (!firstSegment || !i18nConfig.locales.includes(firstSegment)) return null;
50
- return removeTrailingSlash("/" + segments.slice(2).join("/"));
51
+ return "/" + segments.slice(2).join("/");
51
52
  }
52
53
  function matchPattern(pathname, pattern) {
53
- let cached = _mwPatternCache.get(pattern);
54
+ const normalizedPattern = /[\\():*+?]/.test(pattern) ? pattern : removeTrailingSlash(pattern);
55
+ let cached = _mwPatternCache.get(normalizedPattern);
54
56
  if (cached === void 0) {
55
- cached = compileMatcherPattern(pattern);
56
- _mwPatternCache.set(pattern, cached);
57
+ cached = compileMatcherPattern(normalizedPattern);
58
+ _mwPatternCache.set(normalizedPattern, cached);
57
59
  }
58
- if (cached === null) return pathname === pattern;
59
- return cached.test(pathname);
60
+ if (cached === UNSAFE_MATCHER_PATTERN) return true;
61
+ if (cached === null) return removeTrailingSlash(pathname) === normalizedPattern;
62
+ if (cached.test(pathname)) return true;
63
+ return pathname.endsWith("/") && cached.test(removeTrailingSlash(pathname));
60
64
  }
61
65
  function extractConstraint(str, re) {
62
66
  if (str[re.lastIndex] !== "(") return null;
@@ -74,7 +78,7 @@ function extractConstraint(str, re) {
74
78
  }
75
79
  function compileMatcherPattern(pattern) {
76
80
  const hasConstraints = /:[\w-]+[*+]?\(/.test(pattern);
77
- if (!hasConstraints && (pattern.includes("(") || pattern.includes("\\"))) return safeRegExp("^" + pattern + "$");
81
+ if (!hasConstraints && (pattern.includes("(") || pattern.includes("\\"))) return compileMatcherRegExp("^" + pattern + "$", pattern);
78
82
  let regexStr = "";
79
83
  const tokenRe = /\/:([\w-]+)\*|\/:([\w-]+)\+|:([\w-]+)|[.]|[^/:.]+|./g;
80
84
  let tok;
@@ -94,7 +98,14 @@ function compileMatcherPattern(pattern) {
94
98
  else regexStr += group;
95
99
  } else if (tok[0] === ".") regexStr += "\\.";
96
100
  else regexStr += tok[0];
97
- return safeRegExp("^" + regexStr + "$");
101
+ return compileMatcherRegExp("^" + regexStr + "$", pattern);
102
+ }
103
+ function compileMatcherRegExp(regexPattern, sourcePattern) {
104
+ if (!isSafeRegex(regexPattern)) {
105
+ console.warn(`[vinext] Rejecting potentially unsafe middleware matcher (ReDoS risk): ${sourcePattern}\n Middleware will run for all paths to avoid bypassing request guards.\n Simplify the matcher to avoid nested repetition.`);
106
+ return UNSAFE_MATCHER_PATTERN;
107
+ }
108
+ return safeRegExp(regexPattern);
98
109
  }
99
110
  //#endregion
100
111
  export { matchPattern, matchesMiddleware };
@@ -35,10 +35,9 @@ type ExecuteMiddlewareOptions = {
35
35
  i18nConfig?: NextI18nConfig | null;
36
36
  includeErrorDetails?: boolean;
37
37
  /**
38
- * Whether the incoming request was a Next.js `_next/data` fetch (carried
39
- * `x-nextjs-data: 1`). The header itself is stripped by `filterInternalHeaders`
40
- * before the middleware request is constructed, so callers must capture this
41
- * flag from the raw incoming headers and forward it explicitly.
38
+ * Whether the incoming request was recognized as a Next.js `_next/data`
39
+ * fetch. Internal headers are stripped before middleware runs, so adapters
40
+ * must derive and forward this from trusted URL normalization.
42
41
  */
43
42
  isDataRequest?: boolean;
44
43
  isProxy: boolean;
@@ -1,5 +1,5 @@
1
1
  import { normalizePathnameForRouteMatchStrict } from "../routing/utils.js";
2
- import { addBasePathToPathname, hasBasePath, stripBasePath } from "../utils/base-path.js";
2
+ import { addBasePathToPathname, hasBasePath, removeTrailingSlash, stripBasePath } from "../utils/base-path.js";
3
3
  import "./server-globals.js";
4
4
  import { getRequestExecutionContext, runWithExecutionContext } from "../shims/request-context.js";
5
5
  import { MIDDLEWARE_REWRITE_HEADER } from "./headers.js";
@@ -130,7 +130,7 @@ async function executeMiddleware(options) {
130
130
  const matchPathname = options.basePath ? stripBasePath(normalizedPathname, options.basePath) : normalizedPathname;
131
131
  if (!matchesMiddleware(matchPathname, middlewareMatcher(options.module), options.request, options.i18nConfig)) return { continue: true };
132
132
  const nextRequest = createNextRequest(options.request, normalizedPathname, options.i18nConfig, options.basePath, options.trailingSlash, hadBasePath);
133
- const fetchEvent = new NextFetchEvent({ page: matchPathname });
133
+ const fetchEvent = new NextFetchEvent({ page: removeTrailingSlash(matchPathname) });
134
134
  let response;
135
135
  try {
136
136
  response = await middlewareFn(nextRequest, fetchEvent);
@@ -142,6 +142,8 @@ async function executeMiddleware(options) {
142
142
  response: internalServerErrorResponse(options.includeErrorDetails ? "Middleware Error: " + (e instanceof Error ? e.message : String(e)) : "Internal Server Error"),
143
143
  waitUntilPromises
144
144
  };
145
+ } finally {
146
+ if (process.env.NODE_ENV !== "development" && nextRequest.body) nextRequest.body.cancel().catch(() => {});
145
147
  }
146
148
  const waitUntilPromises = drainFetchEvent(fetchEvent);
147
149
  if (!response) return {
@@ -2,18 +2,9 @@ import { RouteManifest } from "../routing/app-route-graph.js";
2
2
  import { CacheEntryReuseDecision, CacheEntryReuseProof } from "./cache-proof.js";
3
3
  import { AppElementsSlotBinding } from "./app-elements-wire.js";
4
4
  import { NavigationTrace, NavigationTraceFields } from "./navigation-trace.js";
5
+ import { OperationLane, OperationToken } from "./operation-token.js";
5
6
 
6
7
  //#region src/server/navigation-planner.d.ts
7
- type OperationLane = "hmr" | "navigation" | "prefetch" | "refresh" | "server-action" | "traverse";
8
- type OperationToken = {
9
- operationId: number;
10
- lane: OperationLane;
11
- baseVisibleCommitVersion: number;
12
- graphVersion: string | null;
13
- deploymentVersion: string | null;
14
- targetSnapshotFingerprint: string;
15
- cacheVariantFingerprint?: string;
16
- };
17
8
  type RouteSnapshot = {
18
9
  interception: InterceptionSnapshot | null;
19
10
  interceptionContext: string | null;
@@ -85,7 +76,7 @@ type CommitProposal = {
85
76
  targetSnapshot: RouteSnapshot;
86
77
  };
87
78
  type NoCommitReason = "prefetchOnly";
88
- type HardNavigationReason = "cacheProofRejected" | "interceptionProofRejected" | "rootBoundaryChanged";
79
+ type HardNavigationReason = "cacheProofRejected" | "cacheReuseTokenRejected" | "interceptionProofRejected" | "rootBoundaryChanged";
89
80
  type RootBoundaryTransition = "currentRootBoundary" | "rootBoundaryChanged" | "rootBoundaryUnknown";
90
81
  type NavigationDecision = {
91
82
  kind: "requestWork";
@@ -314,4 +305,4 @@ declare const navigationPlanner: {
314
305
  resolveSameLayoutAncestorPersistence: typeof resolveSameLayoutAncestorPersistence;
315
306
  };
316
307
  //#endregion
317
- export { EarlyNavigationIntentDecision, EarlyNavigationIntentFacts, FlightResult, InterceptionSnapshot, MountedParallelSlotSnapshot, NavigationDecision, NavigationEvent, NavigationPlannerInput, NavigationPlannerState, NavigationReuseDecision, NavigationReuseFacts, OperationLane, OperationToken, ParallelSlotBindingSnapshot, RefreshScope, RootBoundaryTransition, RouteSnapshot, RscFetchResultDecision, RscFetchResultFacts, RscNavigationErrorDecision, RscNavigationErrorFacts, ServerActionResultDecision, ServerActionResultFacts, TraverseDirection, VisitedResponseCacheCandidateFacts, navigationPlanner, resolveDefaultOrUnmatchedSlotPersistenceForLayouts };
308
+ export { EarlyNavigationIntentDecision, EarlyNavigationIntentFacts, FlightResult, InterceptionSnapshot, MountedParallelSlotSnapshot, NavigationDecision, NavigationEvent, NavigationPlannerInput, NavigationPlannerState, NavigationReuseDecision, NavigationReuseFacts, type OperationLane, type OperationToken, ParallelSlotBindingSnapshot, RefreshScope, RootBoundaryTransition, RouteSnapshot, RscFetchResultDecision, RscFetchResultFacts, RscNavigationErrorDecision, RscNavigationErrorFacts, ServerActionResultDecision, ServerActionResultFacts, TraverseDirection, VisitedResponseCacheCandidateFacts, navigationPlanner, resolveDefaultOrUnmatchedSlotPersistenceForLayouts };
@@ -6,6 +6,7 @@ import "./app-elements.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";
9
+ import { verifyOperationTokenForCacheReuse } from "./operation-token.js";
9
10
  //#region src/server/navigation-planner.ts
10
11
  const ROUTE_INTERCEPTION_CONTEXT_SEPARATOR = "\0";
11
12
  const CACHE_ENTRY_PROOF_MISSING_CODE = "CP_CACHE_ENTRY_PROOF_MISSING";
@@ -531,6 +532,18 @@ function createCacheProofRejectedDecision(options) {
531
532
  url: options.event.result.href
532
533
  };
533
534
  }
535
+ function createCacheReuseTokenRejectedDecision(options) {
536
+ return {
537
+ kind: "hardNavigate",
538
+ reason: "cacheReuseTokenRejected",
539
+ token: options.event.token,
540
+ trace: createNavigationTrace(NavigationTraceReasonCodes.cacheReuseTokenRejected, {
541
+ ...options.traceFields,
542
+ cacheReuseTokenReason: options.reason
543
+ }),
544
+ url: options.event.result.href
545
+ };
546
+ }
534
547
  function createAcceptedCacheProofTraceFields(traceFields, decision) {
535
548
  if (decision === null) return traceFields;
536
549
  return {
@@ -619,6 +632,17 @@ function planFlightResponseArrived(options) {
619
632
  traceFields
620
633
  });
621
634
  const acceptedCacheEntryDecision = cacheEntryProofEvaluation.decision;
635
+ if (acceptedCacheEntryDecision !== null) {
636
+ const reuseVerdict = verifyOperationTokenForCacheReuse(options.event.token, {
637
+ graphVersion: options.routeManifest?.graphVersion ?? null,
638
+ installedCacheVariantFingerprint: null
639
+ });
640
+ if (!reuseVerdict.authorized) return createCacheReuseTokenRejectedDecision({
641
+ event: options.event,
642
+ reason: reuseVerdict.reason,
643
+ traceFields
644
+ });
645
+ }
622
646
  const commitTraceFields = createAcceptedCacheProofTraceFields(traceFields, acceptedCacheEntryDecision);
623
647
  const cacheEntryProposalFields = createCacheEntryProposalFields(acceptedCacheEntryDecision);
624
648
  if (targetSnapshot.interception !== null) {