vinext 1.3.1 → 1.4.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 (234) hide show
  1. package/README.md +5 -2
  2. package/THIRD_PARTY_LICENSES.md +47 -0
  3. package/dist/_virtual/_rolldown/runtime.js +3 -2
  4. package/dist/build/prerender.js +1 -1
  5. package/dist/build/report.d.ts +0 -14
  6. package/dist/build/report.js +1 -51
  7. package/dist/check.js +1 -1
  8. package/dist/cli.js +3 -0
  9. package/dist/client/dev-error-overlay.js +1 -1
  10. package/dist/client/navigation-runtime.d.ts +13 -1
  11. package/dist/client/navigation-runtime.js +1 -1
  12. package/dist/config/next-config.d.ts +19 -0
  13. package/dist/config/next-config.js +64 -1
  14. package/dist/deps/.pnpm/iconv-lite@0.4.24/deps/iconv-lite/encodings/dbcs-codec.js +327 -0
  15. package/dist/deps/.pnpm/iconv-lite@0.4.24/deps/iconv-lite/encodings/dbcs-data.js +123 -0
  16. package/dist/deps/.pnpm/iconv-lite@0.4.24/deps/iconv-lite/encodings/index.js +29 -0
  17. package/dist/deps/.pnpm/iconv-lite@0.4.24/deps/iconv-lite/encodings/internal.js +134 -0
  18. package/dist/deps/.pnpm/iconv-lite@0.4.24/deps/iconv-lite/encodings/sbcs-codec.js +50 -0
  19. package/dist/deps/.pnpm/iconv-lite@0.4.24/deps/iconv-lite/encodings/sbcs-data-generated.js +455 -0
  20. package/dist/deps/.pnpm/iconv-lite@0.4.24/deps/iconv-lite/encodings/sbcs-data.js +147 -0
  21. package/dist/deps/.pnpm/iconv-lite@0.4.24/deps/iconv-lite/encodings/tables/big5-added.json.js +169 -0
  22. package/dist/deps/.pnpm/iconv-lite@0.4.24/deps/iconv-lite/encodings/tables/cp936.json.js +2584 -0
  23. package/dist/deps/.pnpm/iconv-lite@0.4.24/deps/iconv-lite/encodings/tables/cp949.json.js +2220 -0
  24. package/dist/deps/.pnpm/iconv-lite@0.4.24/deps/iconv-lite/encodings/tables/cp950.json.js +227 -0
  25. package/dist/deps/.pnpm/iconv-lite@0.4.24/deps/iconv-lite/encodings/tables/eucjp.json.js +425 -0
  26. package/dist/deps/.pnpm/iconv-lite@0.4.24/deps/iconv-lite/encodings/tables/gb18030-ranges.json.js +427 -0
  27. package/dist/deps/.pnpm/iconv-lite@0.4.24/deps/iconv-lite/encodings/tables/gbk-added.json.js +229 -0
  28. package/dist/deps/.pnpm/iconv-lite@0.4.24/deps/iconv-lite/encodings/tables/shiftjis.json.js +292 -0
  29. package/dist/deps/.pnpm/iconv-lite@0.4.24/deps/iconv-lite/encodings/utf16.js +106 -0
  30. package/dist/deps/.pnpm/iconv-lite@0.4.24/deps/iconv-lite/encodings/utf7.js +182 -0
  31. package/dist/deps/.pnpm/iconv-lite@0.4.24/deps/iconv-lite/lib/bom-handling.js +42 -0
  32. package/dist/deps/.pnpm/iconv-lite@0.4.24/deps/iconv-lite/lib/extend-node.js +147 -0
  33. package/dist/deps/.pnpm/iconv-lite@0.4.24/deps/iconv-lite/lib/index.js +94 -0
  34. package/dist/deps/.pnpm/iconv-lite@0.4.24/deps/iconv-lite/lib/streams.js +95 -0
  35. package/dist/deps/.pnpm/safer-buffer@2.1.2/deps/safer-buffer/safer.js +45 -0
  36. package/dist/entries/app-rsc-entry.js +17 -48
  37. package/dist/entries/app-rsc-manifest.js +3 -4
  38. package/dist/entries/app-ssr-entry.js +1 -0
  39. package/dist/entries/pages-server-entry.js +33 -8
  40. package/dist/index.js +83 -70
  41. package/dist/init-cloudflare.d.ts +1 -3
  42. package/dist/init-cloudflare.js +10 -48
  43. package/dist/init-platform.d.ts +2 -0
  44. package/dist/init-platform.js +4 -0
  45. package/dist/plugins/extensionless-dynamic-import.js +6 -2
  46. package/dist/plugins/next-intl-catalog.d.ts +12 -0
  47. package/dist/plugins/next-intl-catalog.js +85 -0
  48. package/dist/plugins/pages-node-externals.d.ts +1 -0
  49. package/dist/plugins/pages-node-externals.js +1 -1
  50. package/dist/plugins/use-cache-callable.d.ts +1 -1
  51. package/dist/plugins/use-cache-callable.js +136 -94
  52. package/dist/routing/app-route-graph.d.ts +6 -0
  53. package/dist/routing/app-route-graph.js +9 -1
  54. package/dist/server/api-handler.js +19 -4
  55. package/dist/server/app-bfcache-identity.js +1 -1
  56. package/dist/server/app-browser-entry.js +79 -63
  57. package/dist/server/app-browser-history-controller.d.ts +5 -4
  58. package/dist/server/app-browser-history-controller.js +93 -15
  59. package/dist/server/app-browser-navigation-controller.d.ts +2 -2
  60. package/dist/server/app-browser-navigation-controller.js +1 -2
  61. package/dist/server/app-browser-rsc-redirect.js +4 -4
  62. package/dist/server/app-browser-server-action-client.d.ts +2 -0
  63. package/dist/server/app-browser-server-action-client.js +4 -3
  64. package/dist/server/app-browser-state.d.ts +2 -4
  65. package/dist/server/app-browser-state.js +9 -4
  66. package/dist/server/app-browser-supplemental-refresh.js +16 -1
  67. package/dist/server/app-browser-visible-commit.d.ts +1 -1
  68. package/dist/server/app-browser-visible-commit.js +2 -12
  69. package/dist/server/app-elements-wire.d.ts +6 -38
  70. package/dist/server/app-elements-wire.js +4 -41
  71. package/dist/server/app-elements.d.ts +2 -2
  72. package/dist/server/app-elements.js +2 -2
  73. package/dist/server/app-history-state.d.ts +2 -2
  74. package/dist/server/app-history-state.js +5 -2
  75. package/dist/server/app-mounted-layouts.d.ts +21 -0
  76. package/dist/server/app-mounted-layouts.js +67 -0
  77. package/dist/server/app-optimistic-routing.d.ts +19 -1
  78. package/dist/server/app-optimistic-routing.js +42 -2
  79. package/dist/server/app-page-boundary-render.js +7 -6
  80. package/dist/server/app-page-boundary.js +2 -2
  81. package/dist/server/app-page-cache-finalizer.d.ts +12 -1
  82. package/dist/server/app-page-cache-finalizer.js +70 -29
  83. package/dist/server/app-page-cache.d.ts +13 -1
  84. package/dist/server/app-page-cache.js +31 -9
  85. package/dist/server/app-page-dispatch.d.ts +5 -10
  86. package/dist/server/app-page-dispatch.js +84 -154
  87. package/dist/server/app-page-element-builder.d.ts +2 -2
  88. package/dist/server/app-page-element-builder.js +10 -9
  89. package/dist/server/app-page-execution.d.ts +17 -52
  90. package/dist/server/app-page-execution.js +99 -95
  91. package/dist/server/app-page-head.d.ts +13 -0
  92. package/dist/server/app-page-head.js +44 -16
  93. package/dist/server/app-page-http-access-fallback-metadata.d.ts +1 -0
  94. package/dist/server/app-page-http-access-fallback-metadata.js +2 -2
  95. package/dist/server/app-page-layout-gate.d.ts +35 -0
  96. package/dist/server/app-page-layout-gate.js +50 -0
  97. package/dist/server/app-page-params.d.ts +0 -1
  98. package/dist/server/app-page-params.js +1 -14
  99. package/dist/server/app-page-probe.d.ts +3 -21
  100. package/dist/server/app-page-probe.js +4 -37
  101. package/dist/server/app-page-render.d.ts +2 -12
  102. package/dist/server/app-page-render.js +89 -220
  103. package/dist/server/app-page-request.d.ts +1 -3
  104. package/dist/server/app-page-request.js +1 -1
  105. package/dist/server/app-page-response.js +3 -3
  106. package/dist/server/app-page-route-wiring.d.ts +13 -6
  107. package/dist/server/app-page-route-wiring.js +67 -61
  108. package/dist/server/app-page-rsc-render-status.d.ts +27 -1
  109. package/dist/server/app-page-rsc-render-status.js +121 -13
  110. package/dist/server/app-page-stream.d.ts +2 -0
  111. package/dist/server/app-page-stream.js +2 -2
  112. package/dist/server/app-pages-bridge.d.ts +4 -0
  113. package/dist/server/app-pages-bridge.js +7 -3
  114. package/dist/server/app-render-dependency.d.ts +11 -4
  115. package/dist/server/app-render-dependency.js +86 -38
  116. package/dist/server/app-request-stage-dispatch.js +1 -1
  117. package/dist/server/app-route-handler-cache.js +2 -2
  118. package/dist/server/app-route-handler-dispatch.js +14 -9
  119. package/dist/server/app-route-handler-execution.js +8 -3
  120. package/dist/server/app-route-handler-policy.d.ts +1 -1
  121. package/dist/server/app-route-handler-policy.js +3 -3
  122. package/dist/server/app-route-handler-response.js +1 -1
  123. package/dist/server/app-route-handler-runtime.js +17 -4
  124. package/dist/server/app-rsc-cache-busting.d.ts +2 -2
  125. package/dist/server/app-rsc-cache-busting.js +4 -4
  126. package/dist/server/app-rsc-error-handler.d.ts +2 -1
  127. package/dist/server/app-rsc-error-handler.js +9 -1
  128. package/dist/server/app-rsc-errors.d.ts +8 -0
  129. package/dist/server/app-rsc-errors.js +8 -1
  130. package/dist/server/app-rsc-handler.d.ts +5 -5
  131. package/dist/server/app-rsc-handler.js +30 -24
  132. package/dist/server/app-rsc-request-normalization.d.ts +0 -4
  133. package/dist/server/app-rsc-request-normalization.js +1 -4
  134. package/dist/server/app-rsc-response-finalizer.js +2 -2
  135. package/dist/server/app-rsc-response-stage.js +5 -3
  136. package/dist/server/app-server-action-execution.d.ts +3 -1
  137. package/dist/server/app-server-action-execution.js +7 -4
  138. package/dist/server/app-ssr-entry.d.ts +2 -0
  139. package/dist/server/app-ssr-entry.js +9 -8
  140. package/dist/server/app-ssr-stream.js +1 -1
  141. package/dist/server/app-visited-response-cache.d.ts +36 -1
  142. package/dist/server/app-visited-response-cache.js +46 -5
  143. package/dist/server/app-worker-stages.d.ts +25 -3
  144. package/dist/server/app-worker-stages.js +45 -6
  145. package/dist/server/artifact-compatibility.d.ts +0 -1
  146. package/dist/server/artifact-compatibility.js +1 -10
  147. package/dist/server/cache-proof.d.ts +1 -115
  148. package/dist/server/cache-proof.js +1 -544
  149. package/dist/server/cacheability-request.js +13 -8
  150. package/dist/server/dev-server.js +4 -3
  151. package/dist/server/headers.d.ts +6 -3
  152. package/dist/server/headers.js +9 -5
  153. package/dist/server/isr-cache.d.ts +8 -2
  154. package/dist/server/isr-cache.js +26 -1
  155. package/dist/server/metadata-route-response.js +32 -15
  156. package/dist/server/multi-stage.d.ts +52 -11
  157. package/dist/server/multi-stage.js +13 -1
  158. package/dist/server/navigation-planner.js +1 -1
  159. package/dist/server/pages-api-route.js +28 -19
  160. package/dist/server/pages-media-type.d.ts +21 -1
  161. package/dist/server/pages-media-type.js +69 -5
  162. package/dist/server/pages-node-compat.js +52 -27
  163. package/dist/server/pages-page-handler.js +11 -20
  164. package/dist/server/pages-request-pipeline.d.ts +8 -0
  165. package/dist/server/pages-request-pipeline.js +2 -2
  166. package/dist/server/pages-request-stage-entry.js +14 -12
  167. package/dist/server/pages-response-stage-entry.js +9 -3
  168. package/dist/server/prod-server.js +5 -1
  169. package/dist/server/request-pipeline.d.ts +6 -0
  170. package/dist/server/request-pipeline.js +21 -1
  171. package/dist/server/shared-cache-identity.d.ts +23 -0
  172. package/dist/server/shared-cache-identity.js +36 -0
  173. package/dist/server/worker-stages.d.ts +16 -3
  174. package/dist/server/worker-stages.js +23 -4
  175. package/dist/shims/cache-handler.js +2 -2
  176. package/dist/shims/cache-request-state.d.ts +17 -20
  177. package/dist/shims/cache-request-state.js +16 -21
  178. package/dist/shims/cache-runtime.js +27 -11
  179. package/dist/shims/cache.d.ts +8 -7
  180. package/dist/shims/cache.js +17 -19
  181. package/dist/shims/cacheability-classification.d.ts +2 -0
  182. package/dist/shims/cdn-cache.d.ts +22 -5
  183. package/dist/shims/client-page-root.js +2 -2
  184. package/dist/shims/dynamic.js +9 -6
  185. package/dist/shims/error-boundary.js +2 -2
  186. package/dist/shims/fetch-cache-state.d.ts +5 -1
  187. package/dist/shims/fetch-cache-state.js +1 -3
  188. package/dist/shims/fetch-cache.d.ts +2 -1
  189. package/dist/shims/fetch-cache.js +34 -24
  190. package/dist/shims/form.js +2 -2
  191. package/dist/shims/headers.d.ts +8 -23
  192. package/dist/shims/headers.js +17 -67
  193. package/dist/shims/internal/navigation-untracked.js +1 -1
  194. package/dist/shims/link.js +3 -1
  195. package/dist/shims/metadata.d.ts +23 -21
  196. package/dist/shims/metadata.js +62 -24
  197. package/dist/shims/navigation-server.js +1 -1
  198. package/dist/shims/navigation.d.ts +74 -7
  199. package/dist/shims/navigation.js +183 -29
  200. package/dist/shims/navigation.react-server.js +2 -2
  201. package/dist/shims/request-state-types.d.ts +2 -2
  202. package/dist/shims/slot.d.ts +12 -0
  203. package/dist/shims/slot.js +37 -9
  204. package/dist/shims/unified-request-context.d.ts +1 -1
  205. package/dist/shims/unified-request-context.js +2 -3
  206. package/dist/utils/cache-control-metadata.d.ts +5 -0
  207. package/dist/utils/cache-control-metadata.js +9 -1
  208. package/dist/utils/project.d.ts +2 -2
  209. package/dist/utils/project.js +3 -16
  210. package/dist/utils/regex.d.ts +5 -0
  211. package/dist/utils/regex.js +11 -1
  212. package/dist/utils/text-stream.d.ts +10 -5
  213. package/dist/utils/text-stream.js +6 -6
  214. package/package.json +2 -1
  215. package/dist/build/layout-classification-types.d.ts +0 -60
  216. package/dist/build/layout-classification-types.js +0 -1
  217. package/dist/build/layout-classification.d.ts +0 -62
  218. package/dist/build/layout-classification.js +0 -98
  219. package/dist/build/route-classification-injector.d.ts +0 -33
  220. package/dist/build/route-classification-injector.js +0 -59
  221. package/dist/build/route-classification-manifest.d.ts +0 -54
  222. package/dist/build/route-classification-manifest.js +0 -143
  223. package/dist/server/app-browser-client-reuse-manifest.d.ts +0 -11
  224. package/dist/server/app-browser-client-reuse-manifest.js +0 -101
  225. package/dist/server/app-layout-param-observation.d.ts +0 -42
  226. package/dist/server/app-layout-param-observation.js +0 -168
  227. package/dist/server/client-reuse-manifest.d.ts +0 -103
  228. package/dist/server/client-reuse-manifest.js +0 -209
  229. package/dist/server/skip-cache-proof.d.ts +0 -60
  230. package/dist/server/skip-cache-proof.js +0 -168
  231. package/dist/server/static-layout-client-reuse-proof.d.ts +0 -14
  232. package/dist/server/static-layout-client-reuse-proof.js +0 -33
  233. package/dist/utils/sorted-array.d.ts +0 -8
  234. package/dist/utils/sorted-array.js +0 -20
@@ -21,29 +21,74 @@ function isReactOwnedAppComponent(component) {
21
21
  * errors this call throws or rejects with.
22
22
  */
23
23
  function invokeAppComponent(component, props, mapError) {
24
+ return invokeAppComponentObserved(component, props, mapError, void 0);
25
+ }
26
+ /**
27
+ * Like `invokeAppComponent()`. `onSettled` hears of a value returned
28
+ * synchronously; the caller reports a promise result or a thrown error.
29
+ */
30
+ function invokeAppComponentObserved(component, props, mapError, onSettled) {
24
31
  const candidate = component;
25
- if (candidate.$$typeof === REACT_MEMO && candidate.type) return invokeAppComponent(candidate.type, props, mapError);
26
- if (candidate.$$typeof === REACT_LAZY && candidate._init) return invokeAppComponent(candidate._init(candidate._payload), props, mapError);
27
- if (candidate.$$typeof === REACT_FORWARD_REF && candidate.render) return candidate.render(props, void 0);
28
- if (isReactOwnedAppComponent(candidate)) return createElement(candidate, props);
32
+ if (candidate.$$typeof === REACT_MEMO && candidate.type) return invokeAppComponentObserved(candidate.type, props, mapError, onSettled);
33
+ if (candidate.$$typeof === REACT_LAZY && candidate._init) return invokeAppComponentObserved(candidate._init(candidate._payload), props, mapError, onSettled);
34
+ if (candidate.$$typeof === REACT_FORWARD_REF && candidate.render) {
35
+ const result = candidate.render(props, void 0);
36
+ if (!isPromiseLike(result)) onSettled?.();
37
+ return result;
38
+ }
39
+ if (isReactOwnedAppComponent(candidate)) {
40
+ onSettled?.();
41
+ return createElement(candidate, props);
42
+ }
29
43
  const result = candidate(props);
30
- return isPromiseLike(result) ? result : invokeCssWrappedComponent(result, props, mapError);
44
+ return isPromiseLike(result) ? result : invokeCssWrappedComponent(result, props, mapError, onSettled);
31
45
  }
32
46
  /**
33
47
  * `invokeAppComponent()`, with every error the component throws or rejects
34
48
  * with replaced by `mapError(error)`, including its CSS-wrapped inner one's.
49
+ * `onSettled` hears once how the component, or that inner one, ended.
35
50
  */
36
- function invokeAppComponentMappingErrors(component, props, mapError) {
51
+ function invokeAppComponentMappingErrors(component, props, mapError, onSettled) {
52
+ const fail = (error) => {
53
+ if (isAppRenderSuspension(error)) return error;
54
+ const mapped = mapError(error);
55
+ onSettled?.({ error: mapped });
56
+ return mapped;
57
+ };
37
58
  try {
38
- const result = invokeAppComponent(component, props, mapError);
39
- return isPromiseLike(result) ? Promise.resolve(result).catch((error) => {
40
- throw mapError(error);
41
- }) : result;
59
+ const result = invokeAppComponentObserved(component, props, mapError, onSettled);
60
+ return isPromiseLike(result) ? trackAppComponentResult(result, fail, onSettled) : result;
42
61
  } catch (error) {
43
- throw mapError(error);
62
+ throw fail(error);
44
63
  }
45
64
  }
46
65
  /**
66
+ * Records how `result` settles on the promise itself, as React's thenables
67
+ * do, with `fail(error)` as a rejection's reason. Flight then reads that
68
+ * outcome rather than tracking the promise, so the result reaches it in the
69
+ * same tick as an unmapped one. A promise chained with `.then()` resolves a
70
+ * tick later, which costs a large page about a tenth more render work.
71
+ */
72
+ function trackAppComponentResult(result, fail, onSettled) {
73
+ const thenable = result;
74
+ if (thenable.status !== void 0) return Promise.resolve(result).then((value) => {
75
+ onSettled?.();
76
+ return value;
77
+ }, (error) => {
78
+ throw fail(error);
79
+ });
80
+ thenable.status = "pending";
81
+ thenable.then((value) => {
82
+ thenable.status = "fulfilled";
83
+ thenable.value = value;
84
+ onSettled?.();
85
+ }, (error) => {
86
+ thenable.status = "rejected";
87
+ thenable.reason = fail(error);
88
+ });
89
+ return thenable;
90
+ }
91
+ /**
47
92
  * Whether `elementProps` are what `createElement(type, props)` produces: the
48
93
  * same values, plus any `type.defaultProps` for keys that are `undefined`.
49
94
  * Defaulted values aren't read again, since a default can be a getter.
@@ -67,28 +112,33 @@ function isElementOfProps(elementProps, props, defaultProps) {
67
112
  * component from an element without props instead, as for an export without
68
113
  * CSS.
69
114
  */
70
- function invokeCssWrappedComponent(result, props, mapError) {
71
- if (!isValidElement(result) || result.type !== Fragment || result.key !== null) return result;
72
- const children = result.props.children;
73
- if (!Array.isArray(children) || children.length !== 2) return result;
74
- const [css, inner] = children;
75
- if (!isValidElement(inner) || typeof inner.type !== "function" || inner.key !== null) return result;
76
- const component = inner.type;
77
- if (!isElementOfProps(inner.props, props, component.defaultProps)) return result;
78
- const componentProps = inner.props;
115
+ function invokeCssWrappedComponent(result, props, mapError, onSettled) {
116
+ const wrapped = readCssWrappedComponent(result, props);
117
+ if (!wrapped) {
118
+ onSettled?.();
119
+ return result;
120
+ }
121
+ const { component, componentProps, css } = wrapped;
79
122
  function CssWrappedComponent() {
80
- return mapError ? invokeAppComponentMappingErrors(component, componentProps, mapError) : invokeAppComponent(component, componentProps);
123
+ return mapError ? invokeAppComponentMappingErrors(component, componentProps, mapError, onSettled) : invokeAppComponent(component, componentProps);
81
124
  }
82
125
  CssWrappedComponent.displayName = component.displayName ?? component.name;
83
126
  return createElement(Fragment, null, css, createElement(CssWrappedComponent));
84
127
  }
85
- const appElementRenderDependencies = /* @__PURE__ */ new WeakMap();
86
- function registerAppElementRenderDependencies(elements, dependenciesByElementId) {
87
- if (dependenciesByElementId.size === 0) return;
88
- appElementRenderDependencies.set(elements, dependenciesByElementId);
89
- }
90
- function releaseAppElementRenderDependency(elements, elementId) {
91
- appElementRenderDependencies.get(elements)?.get(elementId)?.release();
128
+ /** The parts of `result`, if it's a CSS-wrapped export's element for `props`. */
129
+ function readCssWrappedComponent(result, props) {
130
+ if (!isValidElement(result) || result.type !== Fragment || result.key !== null) return null;
131
+ const children = result.props.children;
132
+ if (!Array.isArray(children) || children.length !== 2) return null;
133
+ const [css, inner] = children;
134
+ if (!isValidElement(inner) || typeof inner.type !== "function" || inner.key !== null) return null;
135
+ const component = inner.type;
136
+ if (!isElementOfProps(inner.props, props, component.defaultProps)) return null;
137
+ return {
138
+ component,
139
+ componentProps: inner.props,
140
+ css
141
+ };
92
142
  }
93
143
  function createAppRenderDependency() {
94
144
  let released = false;
@@ -130,17 +180,15 @@ function renderAfterAppDependencies(children, dependencies) {
130
180
  }
131
181
  return /* @__PURE__ */ jsx(AwaitAppRenderDependencies, {});
132
182
  }
133
- function renderAppComponentWithDependencyBarrier(component, props, dependency) {
183
+ function renderAppComponentWithDependencyBarrier(component, props, dependency, mapError, onSettled) {
134
184
  function AppComponentDependencyBarrier() {
135
185
  try {
136
- const result = invokeAppComponent(component, props);
137
- if (isPromiseLike(result)) return Promise.resolve(result).then((resolvedResult) => {
138
- dependency.release();
139
- return resolvedResult;
140
- }, (error) => {
141
- dependency.release();
142
- throw error;
143
- });
186
+ const result = mapError ? invokeAppComponentMappingErrors(component, props, mapError, onSettled) : invokeAppComponent(component, props);
187
+ if (isPromiseLike(result)) {
188
+ const release = () => dependency.release();
189
+ result.then(release, release);
190
+ return result;
191
+ }
144
192
  dependency.release();
145
193
  return result;
146
194
  } catch (error) {
@@ -151,4 +199,4 @@ function renderAppComponentWithDependencyBarrier(component, props, dependency) {
151
199
  return createElement(AppComponentDependencyBarrier);
152
200
  }
153
201
  //#endregion
154
- export { createAppPageRenderDependency, createAppRenderDependency, invokeAppComponent, invokeAppComponentMappingErrors, isAppClientReference, isAppRenderSuspension, isReactOwnedAppComponent, registerAppElementRenderDependencies, releaseAppElementRenderDependency, renderAfterAppDependencies, renderAppComponentWithDependencyBarrier };
202
+ export { createAppPageRenderDependency, createAppRenderDependency, invokeAppComponent, invokeAppComponentMappingErrors, isAppClientReference, isAppRenderSuspension, isReactOwnedAppComponent, renderAfterAppDependencies, renderAppComponentWithDependencyBarrier };
@@ -44,7 +44,7 @@ async function dispatchAppRequestStage(request, ctx, dispatchResponseStage, opti
44
44
  headRequest,
45
45
  middlewareCookieOverlay: null,
46
46
  prerenderDiscovery: options.prerenderDiscovery,
47
- protocolVersion: 11,
47
+ protocolVersion: 13,
48
48
  requestOrigin: new URL(request.url).origin,
49
49
  scriptNonce: null,
50
50
  staticFileSignalToken,
@@ -1,6 +1,6 @@
1
1
  import { isrCacheControl, resolveRouteExpireSeconds } from "./isr-cache.js";
2
- import { makeThenableParams } from "../shims/thenable-params.js";
3
2
  import { applyRouteHandlerMiddlewareContext, assertSupportedAppRouteHandlerResponse, buildAppRouteCacheValue, buildRouteHandlerCachedResponse } from "./app-route-handler-response.js";
3
+ import { makeThenableParams } from "../shims/thenable-params.js";
4
4
  import { markKnownDynamicAppRoute } from "./app-route-handler-runtime.js";
5
5
  import { resolveAppRouteHandlerSpecialError } from "./app-route-handler-policy.js";
6
6
  import { completeAppRouteHandlerResponse, runAppRouteHandler, traceAppRouteHandlerExecution } from "./app-route-handler-execution.js";
@@ -57,7 +57,7 @@ async function readAppRouteHandlerCacheResponse(options) {
57
57
  setHeadersAccessPhase: options.setHeadersAccessPhase
58
58
  }) };
59
59
  } catch (error) {
60
- if (resolveAppRouteHandlerSpecialError(error, options.requestUrl)) return { specialError: error };
60
+ if (resolveAppRouteHandlerSpecialError(error)) return { specialError: error };
61
61
  throw error;
62
62
  }
63
63
  });
@@ -1,21 +1,22 @@
1
1
  import { closeAfterResponse, createRequestContext, getRequestContext, runWithRequestContext } from "../shims/unified-request-context.js";
2
- import "../utils/protocol-headers.js";
2
+ import { PRERENDER_REVALIDATE_HEADER } from "../utils/protocol-headers.js";
3
3
  import { getRequestExecutionContext } from "../shims/request-context.js";
4
4
  import { runInWorkStoreScope } from "../shims/internal/work-store-scope.js";
5
5
  import { reportRequestError } from "./instrumentation.js";
6
6
  import { beginRouteCacheability, getRouteCacheabilityDynamicReason } from "../shims/cacheability-classification.js";
7
7
  import { applyCdnResponseBuildIdentityHeaders } from "./cache-control.js";
8
8
  import { isOnDemandRevalidateRequest } from "./revalidation-request.js";
9
+ import { coalesceOnDemandResponse } from "./isr-cache.js";
9
10
  import { markDynamicUsage } from "../shims/internal/headers-state.js";
10
11
  import { consumeDynamicUsage, getActiveDraftModeState, getAndClearPendingCookies, getDraftModeCookieHeader, isDraftModeRequest, setHeadersAccessPhase } from "../shims/headers.js";
11
12
  import { _drainPendingRevalidations } from "../shims/cache-request-state.js";
12
13
  import { ensureFetchPatch, getCollectedFetchTags, setCurrentFetchCacheMode, setCurrentFetchRevalidate, setCurrentFetchSoftTags, setCurrentForceDynamicFetchDefault } from "../shims/fetch-cache.js";
13
14
  import { resolveAppRouteHandlerFetchCacheMode } from "./app-segment-config.js";
14
- import { setNavigationContext } from "../shims/navigation-context-state.js";
15
- import { makeThenableParams } from "../shims/thenable-params.js";
16
15
  import { traceResponseStart } from "./response-start-tracing.js";
17
16
  import { applyRouteHandlerMiddlewareContext, finalizeRouteHandlerResponse } from "./app-route-handler-response.js";
17
+ import { setNavigationContext } from "../shims/navigation-context-state.js";
18
18
  import "../shims/navigation.js";
19
+ import { makeThenableParams } from "../shims/thenable-params.js";
19
20
  import { buildPageCacheTags } from "./implicit-tags.js";
20
21
  import { isKnownDynamicAppRoute, isValidHTTPMethod } from "./app-route-handler-runtime.js";
21
22
  import { getAppRouteHandlerRevalidateSeconds, hasAppRouteHandlerDefaultExport, hasNonStaticAppRouteHandlerMethods, resolveAppRouteHandlerMethod, shouldReadAppRouteHandlerCache } from "./app-route-handler-policy.js";
@@ -94,7 +95,8 @@ async function dispatchAppRouteHandlerImpl(options) {
94
95
  headers: { Allow: allowHeaderForOptions }
95
96
  }));
96
97
  const resolvedHandlerFn = isAppRouteHandlerFunction(handlerFn) ? handlerFn : void 0;
97
- const shouldReadRouteCache = options.bypassSharedCache !== true && revalidateSeconds !== null && !getRouteCacheabilityDynamicReason() && shouldReadAppRouteHandlerCache({
98
+ const isOnDemandRevalidate = isOnDemandRevalidateRequest(options.request.headers.get(PRERENDER_REVALIDATE_HEADER));
99
+ const isStoredRoute = options.bypassSharedCache !== true && revalidateSeconds !== null && !getRouteCacheabilityDynamicReason() && shouldReadAppRouteHandlerCache({
98
100
  dynamicConfig: handler.dynamic,
99
101
  handlerFn: resolvedHandlerFn,
100
102
  isAutoHead,
@@ -104,12 +106,13 @@ async function dispatchAppRouteHandlerImpl(options) {
104
106
  method,
105
107
  revalidateSeconds
106
108
  });
107
- const revalidateReason = isOnDemandRevalidateRequest(options.request.headers.get("x-prerender-revalidate")) ? "on-demand" : shouldReadRouteCache && revalidateSeconds !== Infinity ? "stale" : void 0;
109
+ const shouldReadRouteCache = isStoredRoute && !isOnDemandRevalidate;
110
+ const revalidateReason = isOnDemandRevalidate ? "on-demand" : shouldReadRouteCache && revalidateSeconds !== Infinity ? "stale" : void 0;
108
111
  const fetchCacheMode = resolveAppRouteHandlerFetchCacheMode(handler);
109
112
  setCurrentFetchCacheMode(fetchCacheMode);
110
113
  setCurrentFetchRevalidate(configuredRevalidateSeconds);
111
114
  setCurrentForceDynamicFetchDefault(handler.dynamic === "force-dynamic");
112
- let isRevalidation = false;
115
+ let isRevalidation = isStoredRoute && isOnDemandRevalidate;
113
116
  if (shouldReadRouteCache && resolvedHandlerFn) {
114
117
  const cachedRouteResponse = await readAppRouteHandlerCacheResponse({
115
118
  basePath: options.basePath,
@@ -167,7 +170,7 @@ async function dispatchAppRouteHandlerImpl(options) {
167
170
  if (cachedRouteResponse) return applyCdnResponseBuildIdentityHeaders(cachedRouteResponse);
168
171
  }
169
172
  if (resolvedHandlerFn) {
170
- const response = await executeAppRouteHandler({
173
+ const handlerOptions = {
171
174
  isRevalidation,
172
175
  basePath: options.basePath,
173
176
  bypassSharedCache: options.bypassSharedCache,
@@ -210,8 +213,10 @@ async function dispatchAppRouteHandlerImpl(options) {
210
213
  revalidateReason,
211
214
  routePattern: route.pattern,
212
215
  setHeadersAccessPhase
213
- });
214
- return applyCdnResponseBuildIdentityHeaders(response);
216
+ };
217
+ const runHandler = async () => applyCdnResponseBuildIdentityHeaders(await executeAppRouteHandler(handlerOptions));
218
+ if (isOnDemandRevalidate && isStoredRoute) return coalesceOnDemandResponse(`${method}:${options.isrRouteKey(options.cleanPathname)}`, method, runHandler);
219
+ return runHandler();
215
220
  }
216
221
  return finalizeFrameworkResponse(new Response(null, { status: 405 }));
217
222
  }
@@ -167,7 +167,7 @@ async function executeAppRouteHandlerImpl(options) {
167
167
  })
168
168
  };
169
169
  } catch (error) {
170
- if (resolveAppRouteHandlerSpecialError(error, options.request.url, { isAction: isPossibleAppRouteActionRequest(options.request) })) return {
170
+ if (resolveAppRouteHandlerSpecialError(error, { isAction: isPossibleAppRouteActionRequest(options.request) })) return {
171
171
  kind: "special",
172
172
  error
173
173
  };
@@ -284,7 +284,8 @@ async function executeAppRouteHandlerImpl(options) {
284
284
  console.error("[vinext] ISR route cache write error:", cacheErr);
285
285
  }
286
286
  })();
287
- options.executionContext?.waitUntil(routeWritePromise);
287
+ if (options.revalidateReason === "on-demand") await routeWritePromise;
288
+ else options.executionContext?.waitUntil(routeWritePromise);
288
289
  }
289
290
  let finalized = applyDraftModeCachePolicy(applyRouteHandlerMiddlewareContext(finalizeRouteHandlerResponse(response, {
290
291
  pendingCookies,
@@ -321,7 +322,11 @@ async function executeAppRouteHandlerImpl(options) {
321
322
  const draftCookie = handlerDraftCookie ?? options.initialDraftModeCookie;
322
323
  const shouldApplyDraftPolicy = (options.getActiveDraftModeState?.() ?? options.isDraftMode === true) || draftCookie != null;
323
324
  if (handlerDraftCookie != null) markKnownDynamicAppRoute(options.routePattern);
324
- const specialError = resolveAppRouteHandlerSpecialError(error, options.request.url, { isAction: isPossibleAppRouteActionRequest(options.request) });
325
+ const specialError = resolveAppRouteHandlerSpecialError(error, { isAction: isPossibleAppRouteActionRequest(options.request) });
326
+ if (specialError && options.consumeDynamicUsage()) recordRouteCacheability({
327
+ cacheable: false,
328
+ dynamicUsage: true
329
+ });
325
330
  options.clearRequestContext();
326
331
  if (specialError) {
327
332
  if (specialError.kind === "redirect") return applyDraftModeCachePolicy(applyRouteHandlerMiddlewareContext(finalizeRouteHandlerResponse(new Response(null, {
@@ -64,6 +64,6 @@ export declare function shouldApplyAppRouteHandlerRevalidateHeader(options: Omit
64
64
  */
65
65
  export declare function shouldCompleteAppRouteHandlerResponse(options: AppRouteHandlerResponseCacheOptions): boolean;
66
66
  export declare function shouldWriteAppRouteHandlerCache(options: AppRouteHandlerResponseCacheOptions): boolean;
67
- export declare function resolveAppRouteHandlerSpecialError(error: unknown, requestUrl: string, options?: AppRouteHandlerSpecialErrorOptions): AppRouteHandlerSpecialError | null;
67
+ export declare function resolveAppRouteHandlerSpecialError(error: unknown, options?: AppRouteHandlerSpecialErrorOptions): AppRouteHandlerSpecialError | null;
68
68
  //#endregion
69
69
  export { isPossibleAppRouteActionRequest };
@@ -1,5 +1,5 @@
1
- import { parseNextHttpErrorDigest, parseNextRedirectDigest } from "./next-error-digest.js";
2
1
  import { isPossibleAppRouteActionRequest } from "./app-action-request.js";
2
+ import { parseNextHttpErrorDigest, parseNextRedirectDigest } from "./next-error-digest.js";
3
3
  import { buildRouteHandlerAllowHeader, collectRouteHandlerMethods } from "./app-route-handler-runtime.js";
4
4
  //#region src/server/app-route-handler-policy.ts
5
5
  function getAppRouteHandlerRevalidateSeconds(handler) {
@@ -53,13 +53,13 @@ function shouldCompleteAppRouteHandlerResponse(options) {
53
53
  function shouldWriteAppRouteHandlerCache(options) {
54
54
  return options.isProduction && options.revalidateSeconds !== null && options.revalidateSeconds > 0 && options.dynamicConfig !== "force-dynamic" && !options.isDraftMode && !options.dynamicUsedInHandler && (options.method === "GET" || options.isAutoHead);
55
55
  }
56
- function resolveAppRouteHandlerSpecialError(error, requestUrl, options) {
56
+ function resolveAppRouteHandlerSpecialError(error, options) {
57
57
  if (!(error && typeof error === "object" && "digest" in error)) return null;
58
58
  const digest = String(error.digest);
59
59
  const redirect = parseNextRedirectDigest(digest);
60
60
  if (redirect) return {
61
61
  kind: "redirect",
62
- location: new URL(redirect.url, requestUrl).toString(),
62
+ location: redirect.url,
63
63
  statusCode: options?.isAction ? 303 : redirect.status
64
64
  };
65
65
  const httpError = parseNextHttpErrorDigest(digest);
@@ -5,8 +5,8 @@ import { markRouteCacheabilityExplicitResponsePolicy, recordRouteCacheability }
5
5
  import { applyCdnResponseHeaders } from "./cache-control.js";
6
6
  import { buildAppRouteMissIsrCacheControl, decideIsr } from "./isr-decision.js";
7
7
  import { setCacheStateHeaders } from "./cache-headers.js";
8
- import { mergeMiddlewareResponseHeadersOntoResponse } from "./middleware-response-headers.js";
9
8
  import { markEdgeRouteHandlerLinkHeaders } from "./app-response-header-provenance.js";
9
+ import { mergeMiddlewareResponseHeadersOntoResponse } from "./middleware-response-headers.js";
10
10
  import { getSetCookieName } from "./cookie-utils.js";
11
11
  //#region src/server/app-route-handler-response.ts
12
12
  const APP_ROUTE_REWRITE_ERROR = "NextResponse.rewrite() was used in a app route handler, this is not currently supported. Please remove the invocation to continue.";
@@ -76,8 +76,14 @@ function buildNextConfig(options) {
76
76
  * Undici's Request follows its input's signal through a WeakRef, and so does
77
77
  * every clone between the host signal and the handler's request. A handler
78
78
  * that only listens on `request.signal` (e.g. to stop a stream) would miss the
79
- * abort once GC collects any of them, so the request follows the host signal
80
- * directly and stays reachable for as long as that signal is.
79
+ * abort once GC collects any of them, so the request stays reachable for as
80
+ * long as the host signal is.
81
+ *
82
+ * The request must not follow the host signal itself, though. Undici registers
83
+ * every Request in a FinalizationRegistry whose held value strongly references
84
+ * the followed signal, so the registry would keep the host signal (the WeakMap
85
+ * key) and therefore the request alive forever. It follows a relay instead,
86
+ * which the host signal aborts through a strong listener.
81
87
  */
82
88
  const requestsByHostSignal = /* @__PURE__ */ new WeakMap();
83
89
  function retainWhileSignalLives(signal, request) {
@@ -85,6 +91,12 @@ function retainWhileSignalLives(signal, request) {
85
91
  if (requests) requests.push(request);
86
92
  else requestsByHostSignal.set(signal, [request]);
87
93
  }
94
+ function followHostSignal(signal) {
95
+ const relay = new AbortController();
96
+ if (signal.aborted) relay.abort(signal.reason);
97
+ else signal.addEventListener("abort", () => relay.abort(signal.reason), { once: true });
98
+ return relay.signal;
99
+ }
88
100
  function createForceStaticNextRequest(input, nextConfig, signal) {
89
101
  const read = (key) => {
90
102
  const descriptor = BUILT_IN_REQUEST_PROPERTY_MAP.get(key)?.descriptor;
@@ -228,9 +240,10 @@ function createTrackedAppRouteRequest(request, options = {}) {
228
240
  const sourceCfDescriptor = Reflect.getOwnPropertyDescriptor(requestWithOverrides, "cf");
229
241
  const sourceCfAccessor = sourceCfDescriptor?.get;
230
242
  const sourceCfMetadata = sourceCfAccessor ? void 0 : Reflect.get(requestWithOverrides, "cf", requestWithOverrides);
231
- const nextRequest = requestMode === "force-static" ? createForceStaticNextRequest(requestWithOverrides, nextConfig, options.signal) : requestWithOverrides instanceof NextRequest && sourceCfDescriptor?.configurable !== false && !options.signal ? requestWithOverrides : new NextRequest(requestWithOverrides, {
243
+ const signal = options.signal ? followHostSignal(options.signal) : void 0;
244
+ const nextRequest = requestMode === "force-static" ? createForceStaticNextRequest(requestWithOverrides, nextConfig, signal) : requestWithOverrides instanceof NextRequest && sourceCfDescriptor?.configurable !== false && !signal ? requestWithOverrides : new NextRequest(requestWithOverrides, {
232
245
  nextConfig: nextConfig ?? void 0,
233
- signal: options.signal
246
+ signal
234
247
  });
235
248
  if (options.signal) retainWhileSignalLives(options.signal, nextRequest);
236
249
  const rawCfMetadata = sourceCfAccessor ? void 0 : sourceCfMetadata === void 0 ? Reflect.get(nextRequest, "cf", nextRequest) : sourceCfMetadata;
@@ -1,5 +1,5 @@
1
- import { VINEXT_RSC_RENDER_MODE_HEADER, VINEXT_RSC_VARY_HEADER } from "./app-rsc-vary.js";
2
1
  import { AppRscRenderMode } from "./app-rsc-render-mode.js";
2
+ import { VINEXT_RSC_RENDER_MODE_HEADER, VINEXT_RSC_VARY_HEADER } from "./app-rsc-vary.js";
3
3
  import { AppRscStateFingerprintInput } from "./app-rsc-state-fingerprint.js";
4
4
  //#region src/server/app-rsc-cache-busting.d.ts
5
5
  /**
@@ -13,10 +13,10 @@ export declare const VINEXT_RSC_BUILD_ID_HEADER = "X-Vinext-RSC-Build-Id";
13
13
  export declare const VINEXT_RSC_COMPATIBILITY_ID_HEADER = "X-Vinext-RSC-Compatibility-Id";
14
14
  export declare const VINEXT_RSC_CONTENT_TYPE = "text/x-component";
15
15
  type CreateRscRequestHeadersOptions = {
16
- clientReuseManifestHeader?: string | null;
17
16
  interceptionContext?: string | null;
18
17
  interceptionId?: string | null;
19
18
  mountedSlotsHeader?: string | null;
19
+ mountedLayoutsHeader?: string | null;
20
20
  includePrefetchHeader?: boolean;
21
21
  renderMode?: AppRscRenderMode;
22
22
  fetchPriority?: "auto" | "high" | "low";
@@ -1,5 +1,5 @@
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
- import { NEXTJS_DEPLOYMENT_ID_HEADER, VINEXT_CLIENT_REUSE_MANIFEST_HEADER } from "./headers.js";
2
+ import { NEXTJS_DEPLOYMENT_ID_HEADER, VINEXT_MOUNTED_LAYOUTS_HEADER } from "./headers.js";
3
3
  import { APP_RSC_RENDER_MODE_PREFETCH_LOADING_SHELL, parseAppRscRenderMode } from "./app-rsc-render-mode.js";
4
4
  import { fnv1a64 } from "../utils/hash.js";
5
5
  import { applyDeploymentIdHeader, getDeploymentId } from "../utils/deployment-id.js";
@@ -183,7 +183,7 @@ function createRscRequestHeaders(options = {}) {
183
183
  if (options.interceptionContext !== void 0 && options.interceptionContext !== null) headers.set(VINEXT_INTERCEPTION_CONTEXT_HEADER, options.interceptionContext);
184
184
  if (options.interceptionId !== void 0 && options.interceptionId !== null) headers.set(VINEXT_INTERCEPTION_ID_HEADER, options.interceptionId);
185
185
  if (options.mountedSlotsHeader !== void 0 && options.mountedSlotsHeader !== null) headers.set(VINEXT_MOUNTED_SLOTS_HEADER, options.mountedSlotsHeader);
186
- if (options.clientReuseManifestHeader !== void 0 && options.clientReuseManifestHeader !== null) headers.set(VINEXT_CLIENT_REUSE_MANIFEST_HEADER, options.clientReuseManifestHeader);
186
+ if (options.mountedLayoutsHeader !== void 0 && options.mountedLayoutsHeader !== null) headers.set(VINEXT_MOUNTED_LAYOUTS_HEADER, options.mountedLayoutsHeader);
187
187
  const renderMode = options.renderMode ?? "navigation";
188
188
  if (renderMode !== "navigation") headers.set(VINEXT_RSC_RENDER_MODE_HEADER, renderMode);
189
189
  return headers;
@@ -199,8 +199,8 @@ function canonicalizePrewarmableRscRequestHeaders(headers) {
199
199
  headers.delete(NEXT_ROUTER_SEGMENT_PREFETCH_HEADER);
200
200
  headers.delete(NEXT_ROUTER_STATE_TREE_HEADER);
201
201
  headers.delete(NEXT_URL_HEADER);
202
+ headers.delete(VINEXT_MOUNTED_LAYOUTS_HEADER);
202
203
  headers.delete(VINEXT_RSC_STATE_FINGERPRINT_HEADER);
203
- headers.delete(VINEXT_CLIENT_REUSE_MANIFEST_HEADER);
204
204
  return true;
205
205
  }
206
206
  /**
@@ -214,8 +214,8 @@ function canonicalizeLoadingShellRscRequestHeaders(headers) {
214
214
  headers.set(NEXT_ROUTER_SEGMENT_PREFETCH_HEADER, "1");
215
215
  headers.delete(NEXT_ROUTER_STATE_TREE_HEADER);
216
216
  headers.delete(NEXT_URL_HEADER);
217
+ headers.delete(VINEXT_MOUNTED_LAYOUTS_HEADER);
217
218
  headers.delete(VINEXT_RSC_STATE_FINGERPRINT_HEADER);
218
- headers.delete(VINEXT_CLIENT_REUSE_MANIFEST_HEADER);
219
219
  return true;
220
220
  }
221
221
  const RSC_REQUEST_URL_BASE = "http://vinext.local";
@@ -20,7 +20,8 @@ export type AppRenderErrorContextOverrides = {
20
20
  * the incoming Web `Request`, wires it into a `createRscOnErrorHandler` call,
21
21
  * and binds the configured `reportRequestError` reporter.
22
22
  *
23
- * Pure factory: takes all deps explicitly — no closure over module-level state.
23
+ * Takes all deps explicitly. The only module-level state is the set of errors
24
+ * each request already logged, keyed weakly by its `Request`.
24
25
  */
25
26
  export declare function createAppRscOnErrorHandler(reportRequestError: ReportRequestError, request: Request, pathname: string, routePath: string, overrides?: AppRenderErrorContextOverrides): (error: unknown) => string | undefined;
26
27
  //#endregion
@@ -3,12 +3,14 @@ import "./app-rsc-vary.js";
3
3
  import { isOnDemandRevalidateRequest } from "./revalidation-request.js";
4
4
  import { createRscOnErrorHandler } from "./app-rsc-errors.js";
5
5
  //#region src/server/app-rsc-error-handler.ts
6
+ const loggedErrorsByRequest = /* @__PURE__ */ new WeakMap();
6
7
  /**
7
8
  * Build a per-request RSC error handler that extracts request metadata from
8
9
  * the incoming Web `Request`, wires it into a `createRscOnErrorHandler` call,
9
10
  * and binds the configured `reportRequestError` reporter.
10
11
  *
11
- * Pure factory: takes all deps explicitly — no closure over module-level state.
12
+ * Takes all deps explicitly. The only module-level state is the set of errors
13
+ * each request already logged, keyed weakly by its `Request`.
12
14
  */
13
15
  function createAppRscOnErrorHandler(reportRequestError, request, pathname, routePath, overrides) {
14
16
  const requestHeaders = Object.fromEntries(request.headers.entries());
@@ -24,9 +26,15 @@ function createAppRscOnErrorHandler(reportRequestError, request, pathname, route
24
26
  renderSource: overrides?.renderSource ?? (request.headers.get("RSC") === "1" || new URL(request.url).pathname.endsWith(".rsc") ? "react-server-components-payload" : "react-server-components"),
25
27
  revalidateReason: isOnDemandRevalidateRequest(request.headers.get("x-prerender-revalidate")) ? "on-demand" : overrides?.revalidateReason
26
28
  };
29
+ let loggedErrors = loggedErrorsByRequest.get(request);
30
+ if (!loggedErrors) {
31
+ loggedErrors = /* @__PURE__ */ new WeakSet();
32
+ loggedErrorsByRequest.set(request, loggedErrors);
33
+ }
27
34
  return createRscOnErrorHandler({
28
35
  attachDigest: errorContext.renderSource !== "server-rendering",
29
36
  errorContext,
37
+ loggedErrors,
30
38
  reportRequestError(error, info, context) {
31
39
  reportRequestError(error, info, context);
32
40
  },
@@ -15,6 +15,14 @@ type RscErrorReporter = (error: unknown, requestInfo: RscRequestInfo, errorConte
15
15
  type CreateRscOnErrorHandlerOptions = {
16
16
  attachDigest?: boolean;
17
17
  errorContext: RscErrorContext | null;
18
+ /**
19
+ * The errors this request already logged, shared by its handlers so an error
20
+ * reaching more than one, such as in the RSC and then the SSR pass, is logged
21
+ * once. Like Next.js, a digest alone does not mean an error was logged: user
22
+ * code can set its own. Request-scoped, as a failed module evaluation throws
23
+ * the same error on every request. Defaults to this handler's own.
24
+ */
25
+ loggedErrors?: WeakSet<object>;
18
26
  nodeEnv?: string;
19
27
  reportRequestError: RscErrorReporter;
20
28
  requestInfo: RscRequestInfo | null;
@@ -63,6 +63,13 @@ function sanitizeErrorForClient(error, nodeEnv = process.env.NODE_ENV) {
63
63
  }
64
64
  function createRscOnErrorHandler(options) {
65
65
  let loggedPrerenderErrors;
66
+ const loggedErrors = options.loggedErrors ?? /* @__PURE__ */ new WeakSet();
67
+ const wasLogged = (error) => {
68
+ if (typeof error !== "object" && typeof error !== "function" || error === null) return false;
69
+ if (loggedErrors.has(error)) return true;
70
+ loggedErrors.add(error);
71
+ return false;
72
+ };
66
73
  return (error) => {
67
74
  const nodeEnv = options.nodeEnv ?? process.env.NODE_ENV;
68
75
  const reportableError = error && typeof error === "object" && ORIGINAL_SERVER_ERROR in error ? Reflect.get(error, ORIGINAL_SERVER_ERROR) : error;
@@ -87,7 +94,7 @@ function createRscOnErrorHandler(options) {
87
94
  const route = options.requestInfo?.path ?? options.errorContext?.routePath ?? "<unknown>";
88
95
  console.error(`[vinext] Error prerendering route ${JSON.stringify(route)}:`, reportableError);
89
96
  }
90
- } else if (nodeEnv !== "production" && !thrownHasDigest) console.error("[vinext] Server render error:", properError);
97
+ } else if (!(reportableError !== error && thrownHasDigest) && !wasLogged(reportableError)) console.error("[vinext] Server render error:", properError);
91
98
  return digest;
92
99
  };
93
100
  }
@@ -1,14 +1,13 @@
1
1
  import { VinextCacheabilityProbeMode } from "./multi-stage.js";
2
2
  import { NextHeader, NextI18nConfig, NextRedirect, NextRewrite } from "../config/next-config.js";
3
+ import { AppRscRenderMode } from "./app-rsc-render-mode.js";
3
4
  import { ImageConfig } from "./image-optimization.js";
4
5
  import { ExecutionContextLike } from "../shims/request-context.js";
5
6
  import { AppMiddlewareContext, ApplyAppMiddlewareResult } from "./app-middleware.js";
6
- import { ClientReuseManifestParseResult } from "./client-reuse-manifest.js";
7
- import { AppRscRenderMode } from "./app-rsc-render-mode.js";
8
7
  import { RootParams } from "../shims/root-params.js";
9
8
  import { AppPagePprFallbackCacheShell } from "./app-ppr-fallback-shell.js";
10
- import { AppPrerenderRootParamNamesMap, AppPrerenderStaticParamsMap } from "./app-prerender-endpoints.js";
11
9
  import { PagesRouteDataKind } from "./pages-route-data-kind.js";
10
+ import { AppPrerenderRootParamNamesMap, AppPrerenderStaticParamsMap } from "./app-prerender-endpoints.js";
12
11
  import { TrustedPrerenderState } from "./prerender-route-params.js";
13
12
  import { AppRouteTreePrefetchRoute, PrefetchInliningConfig } from "./app-route-tree-prefetch.js";
14
13
  import { DispatchAppWorkerResponseStage, RenderAppWorkerResponseStageLocally } from "./app-worker-stages.js";
@@ -52,7 +51,6 @@ type AppRscRouteMatch<TRoute> = {
52
51
  type DispatchMatchedPageOptions<TRoute> = {
53
52
  bypassInterceptionContextCache: boolean;
54
53
  cachePathname: string;
55
- clientReuseManifest: ClientReuseManifestParseResult;
56
54
  cleanPathname: string;
57
55
  displayPathname: string;
58
56
  formState: ReactFormState | null;
@@ -170,7 +168,7 @@ type RenderPagesFallbackOptions = {
170
168
  pattern: string;
171
169
  };
172
170
  } | null;
173
- dispatchPagesResponseStage?: (request: Request, resourceKind: "api" | "page", dataKind?: PagesRouteDataKind, hasRequestAwareDocument?: boolean) => Promise<Response>;
171
+ dispatchPagesResponseStage?: (request: Request, resourceKind: "api" | "page", dataKind?: PagesRouteDataKind, hasRequestAwareDocument?: boolean, runsInitialProps?: boolean) => Promise<Response>;
174
172
  isDataRequest?: boolean;
175
173
  isRscRequest: boolean;
176
174
  initialResponseHeaders?: Headers;
@@ -179,6 +177,8 @@ type RenderPagesFallbackOptions = {
179
177
  pathname?: string;
180
178
  pagesDataRequest?: Request | null;
181
179
  request: Request;
180
+ /** The shared cache entry answers every host; a request-time render is not stored. */
181
+ sharedAcrossHosts?: boolean;
182
182
  url: URL;
183
183
  };
184
184
  type NavigationContextValue = {