vinext 0.1.2 → 0.1.4

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 (243) 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/prerender.d.ts +9 -1
  4. package/dist/build/prerender.js +42 -12
  5. package/dist/build/run-prerender.d.ts +10 -2
  6. package/dist/build/run-prerender.js +15 -1
  7. package/dist/client/app-nav-failure-handler.d.ts +8 -0
  8. package/dist/client/app-nav-failure-handler.js +44 -0
  9. package/dist/client/pages-router-link-navigation.d.ts +33 -7
  10. package/dist/client/pages-router-link-navigation.js +32 -2
  11. package/dist/client/vinext-next-data.d.ts +18 -1
  12. package/dist/client/vinext-next-data.js +2 -0
  13. package/dist/client/window-next.d.ts +2 -1
  14. package/dist/client/window-next.js +12 -1
  15. package/dist/cloudflare/src/cache/cdn-adapter.runtime.js +6 -1
  16. package/dist/config/config-matchers.d.ts +11 -1
  17. package/dist/config/config-matchers.js +87 -16
  18. package/dist/config/next-config.d.ts +46 -4
  19. package/dist/config/next-config.js +147 -48
  20. package/dist/config/tsconfig-paths.js +14 -1
  21. package/dist/deploy.d.ts +30 -11
  22. package/dist/deploy.js +200 -112
  23. package/dist/entries/app-browser-entry.d.ts +9 -3
  24. package/dist/entries/app-browser-entry.js +21 -3
  25. package/dist/entries/app-rsc-entry.d.ts +2 -0
  26. package/dist/entries/app-rsc-entry.js +65 -5
  27. package/dist/entries/app-rsc-manifest.js +2 -0
  28. package/dist/entries/app-ssr-entry.js +1 -1
  29. package/dist/entries/pages-client-entry.js +66 -20
  30. package/dist/entries/pages-server-entry.js +47 -31
  31. package/dist/index.js +417 -102
  32. package/dist/plugins/dynamic-preload-metadata.js +2 -4
  33. package/dist/plugins/extensionless-dynamic-import.d.ts +6 -0
  34. package/dist/plugins/extensionless-dynamic-import.js +152 -0
  35. package/dist/plugins/fonts.js +5 -4
  36. package/dist/plugins/optimize-imports.d.ts +2 -1
  37. package/dist/plugins/optimize-imports.js +11 -9
  38. package/dist/plugins/postcss.js +7 -7
  39. package/dist/plugins/strip-server-exports.d.ts +9 -7
  40. package/dist/plugins/strip-server-exports.js +493 -46
  41. package/dist/plugins/typeof-window.d.ts +14 -0
  42. package/dist/plugins/typeof-window.js +150 -0
  43. package/dist/routing/app-route-graph.d.ts +2 -1
  44. package/dist/routing/app-route-graph.js +46 -16
  45. package/dist/routing/file-matcher.d.ts +10 -1
  46. package/dist/routing/file-matcher.js +22 -1
  47. package/dist/routing/pages-router.js +3 -3
  48. package/dist/routing/utils.d.ts +35 -6
  49. package/dist/routing/utils.js +59 -7
  50. package/dist/server/api-handler.d.ts +6 -1
  51. package/dist/server/api-handler.js +21 -15
  52. package/dist/server/app-browser-action-result.d.ts +19 -6
  53. package/dist/server/app-browser-action-result.js +20 -11
  54. package/dist/server/app-browser-entry.js +175 -91
  55. package/dist/server/app-browser-error.d.ts +10 -6
  56. package/dist/server/app-browser-error.js +43 -8
  57. package/dist/server/app-browser-hydration.d.ts +2 -0
  58. package/dist/server/app-browser-hydration.js +1 -0
  59. package/dist/server/app-browser-navigation-controller.d.ts +5 -3
  60. package/dist/server/app-browser-navigation-controller.js +23 -2
  61. package/dist/server/app-browser-server-action-navigation.d.ts +6 -0
  62. package/dist/server/app-browser-server-action-navigation.js +9 -0
  63. package/dist/server/app-browser-state.d.ts +1 -1
  64. package/dist/server/app-browser-state.js +19 -11
  65. package/dist/server/app-browser-stream.js +86 -43
  66. package/dist/server/app-browser-visible-commit.d.ts +1 -1
  67. package/dist/server/app-elements-wire.d.ts +6 -1
  68. package/dist/server/app-elements-wire.js +14 -4
  69. package/dist/server/app-elements.d.ts +2 -2
  70. package/dist/server/app-elements.js +2 -2
  71. package/dist/server/app-fallback-renderer.d.ts +1 -0
  72. package/dist/server/app-fallback-renderer.js +3 -1
  73. package/dist/server/app-optimistic-routing.js +2 -2
  74. package/dist/server/app-page-boundary-render.d.ts +1 -0
  75. package/dist/server/app-page-boundary-render.js +27 -14
  76. package/dist/server/app-page-cache-render.d.ts +53 -0
  77. package/dist/server/app-page-cache-render.js +91 -0
  78. package/dist/server/app-page-cache.d.ts +16 -2
  79. package/dist/server/app-page-cache.js +62 -1
  80. package/dist/server/app-page-dispatch.d.ts +26 -0
  81. package/dist/server/app-page-dispatch.js +149 -92
  82. package/dist/server/app-page-element-builder.d.ts +1 -0
  83. package/dist/server/app-page-element-builder.js +5 -2
  84. package/dist/server/app-page-execution.d.ts +6 -1
  85. package/dist/server/app-page-execution.js +21 -1
  86. package/dist/server/app-page-probe.d.ts +1 -0
  87. package/dist/server/app-page-probe.js +4 -0
  88. package/dist/server/app-page-render-observation.d.ts +3 -1
  89. package/dist/server/app-page-render-observation.js +17 -1
  90. package/dist/server/app-page-render.d.ts +12 -1
  91. package/dist/server/app-page-render.js +42 -4
  92. package/dist/server/app-page-request.d.ts +2 -0
  93. package/dist/server/app-page-request.js +2 -1
  94. package/dist/server/app-page-route-wiring.d.ts +3 -1
  95. package/dist/server/app-page-route-wiring.js +14 -5
  96. package/dist/server/app-page-stream.d.ts +15 -3
  97. package/dist/server/app-page-stream.js +11 -5
  98. package/dist/server/app-pages-bridge.d.ts +23 -1
  99. package/dist/server/app-pages-bridge.js +26 -17
  100. package/dist/server/app-ppr-fallback-shell-render.d.ts +17 -0
  101. package/dist/server/app-ppr-fallback-shell-render.js +26 -0
  102. package/dist/server/app-ppr-fallback-shell.d.ts +13 -1
  103. package/dist/server/app-ppr-fallback-shell.js +8 -1
  104. package/dist/server/app-route-handler-dispatch.js +9 -2
  105. package/dist/server/app-route-handler-policy.d.ts +1 -0
  106. package/dist/server/app-router-entry.js +5 -0
  107. package/dist/server/app-rsc-cache-busting.js +2 -0
  108. package/dist/server/app-rsc-handler.d.ts +28 -0
  109. package/dist/server/app-rsc-handler.js +195 -59
  110. package/dist/server/app-rsc-route-matching.d.ts +3 -0
  111. package/dist/server/app-rsc-route-matching.js +8 -2
  112. package/dist/server/app-segment-config.d.ts +9 -1
  113. package/dist/server/app-segment-config.js +12 -3
  114. package/dist/server/app-server-action-execution.d.ts +1 -0
  115. package/dist/server/app-server-action-execution.js +47 -15
  116. package/dist/server/app-ssr-entry.d.ts +2 -0
  117. package/dist/server/app-ssr-entry.js +84 -39
  118. package/dist/server/before-interactive-head.d.ts +17 -0
  119. package/dist/server/before-interactive-head.js +35 -0
  120. package/dist/server/cache-control.js +4 -0
  121. package/dist/server/csp.js +1 -4
  122. package/dist/server/dev-server.d.ts +2 -2
  123. package/dist/server/dev-server.js +321 -83
  124. package/dist/server/hybrid-route-priority.d.ts +22 -0
  125. package/dist/server/hybrid-route-priority.js +33 -0
  126. package/dist/server/image-optimization.d.ts +18 -9
  127. package/dist/server/image-optimization.js +37 -23
  128. package/dist/server/implicit-tags.d.ts +2 -1
  129. package/dist/server/implicit-tags.js +4 -1
  130. package/dist/server/middleware-matcher.js +12 -3
  131. package/dist/server/middleware-runtime.d.ts +3 -4
  132. package/dist/server/middleware-runtime.js +2 -0
  133. package/dist/server/navigation-planner.d.ts +135 -41
  134. package/dist/server/navigation-planner.js +138 -0
  135. package/dist/server/navigation-trace.d.ts +9 -1
  136. package/dist/server/navigation-trace.js +9 -1
  137. package/dist/server/operation-token.d.ts +40 -0
  138. package/dist/server/operation-token.js +85 -0
  139. package/dist/server/pages-api-route.d.ts +6 -0
  140. package/dist/server/pages-api-route.js +13 -2
  141. package/dist/server/pages-asset-tags.d.ts +2 -1
  142. package/dist/server/pages-asset-tags.js +6 -2
  143. package/dist/server/pages-data-route.d.ts +9 -2
  144. package/dist/server/pages-data-route.js +18 -6
  145. package/dist/server/pages-dev-module-url.d.ts +4 -0
  146. package/dist/server/pages-dev-module-url.js +15 -0
  147. package/dist/server/pages-document-initial-props.d.ts +4 -15
  148. package/dist/server/pages-document-initial-props.js +27 -56
  149. package/dist/server/pages-get-initial-props.d.ts +54 -4
  150. package/dist/server/pages-get-initial-props.js +43 -1
  151. package/dist/server/pages-i18n.js +2 -2
  152. package/dist/server/pages-node-compat.js +2 -2
  153. package/dist/server/pages-page-data.d.ts +11 -2
  154. package/dist/server/pages-page-data.js +207 -34
  155. package/dist/server/pages-page-handler.d.ts +4 -2
  156. package/dist/server/pages-page-handler.js +62 -23
  157. package/dist/server/pages-page-response.d.ts +4 -1
  158. package/dist/server/pages-page-response.js +11 -8
  159. package/dist/server/pages-readiness.js +1 -1
  160. package/dist/server/pages-request-pipeline.d.ts +8 -7
  161. package/dist/server/pages-request-pipeline.js +126 -47
  162. package/dist/server/pregenerated-concrete-paths.d.ts +1 -17
  163. package/dist/server/pregenerated-concrete-paths.js +2 -19
  164. package/dist/server/prerender-manifest.d.ts +33 -0
  165. package/dist/server/prerender-manifest.js +54 -0
  166. package/dist/server/prerender-route-params.d.ts +1 -2
  167. package/dist/server/prod-server.d.ts +3 -1
  168. package/dist/server/prod-server.js +50 -13
  169. package/dist/server/request-pipeline.d.ts +3 -15
  170. package/dist/server/request-pipeline.js +58 -47
  171. package/dist/server/rsc-stream-hints.d.ts +5 -1
  172. package/dist/server/rsc-stream-hints.js +6 -1
  173. package/dist/server/seed-cache.js +10 -18
  174. package/dist/server/static-file-cache.js +16 -4
  175. package/dist/shims/app-router-scroll-state.d.ts +3 -1
  176. package/dist/shims/app-router-scroll-state.js +14 -2
  177. package/dist/shims/app-router-scroll.d.ts +3 -0
  178. package/dist/shims/app-router-scroll.js +28 -18
  179. package/dist/shims/before-interactive-context.d.ts +14 -3
  180. package/dist/shims/cache-runtime.js +3 -2
  181. package/dist/shims/cache.d.ts +1 -0
  182. package/dist/shims/cache.js +1 -1
  183. package/dist/shims/cdn-cache.d.ts +5 -5
  184. package/dist/shims/document.d.ts +15 -20
  185. package/dist/shims/document.js +5 -8
  186. package/dist/shims/dynamic-preload-chunks.js +6 -4
  187. package/dist/shims/error-boundary.d.ts +2 -0
  188. package/dist/shims/error-boundary.js +7 -0
  189. package/dist/shims/error.js +3 -2
  190. package/dist/shims/error.react-server.d.ts +9 -0
  191. package/dist/shims/error.react-server.js +6 -0
  192. package/dist/shims/fetch-cache.d.ts +3 -1
  193. package/dist/shims/fetch-cache.js +45 -20
  194. package/dist/shims/hash-scroll.js +6 -1
  195. package/dist/shims/headers.js +29 -4
  196. package/dist/shims/image.js +9 -2
  197. package/dist/shims/internal/als-registry.js +28 -1
  198. package/dist/shims/internal/app-route-detection.js +8 -17
  199. package/dist/shims/internal/hybrid-client-route-owner.d.ts +31 -0
  200. package/dist/shims/internal/hybrid-client-route-owner.js +143 -0
  201. package/dist/shims/internal/navigation-untracked.d.ts +35 -0
  202. package/dist/shims/internal/navigation-untracked.js +55 -0
  203. package/dist/shims/internal/pages-data-fetch-dedup.d.ts +6 -7
  204. package/dist/shims/internal/pages-data-fetch-dedup.js +67 -14
  205. package/dist/shims/internal/pages-data-target.d.ts +7 -2
  206. package/dist/shims/internal/pages-data-target.js +17 -8
  207. package/dist/shims/internal/pages-router-accessor.d.ts +19 -0
  208. package/dist/shims/internal/pages-router-accessor.js +13 -0
  209. package/dist/shims/internal/router-context.d.ts +2 -1
  210. package/dist/shims/internal/router-context.js +3 -1
  211. package/dist/shims/link.js +47 -19
  212. package/dist/shims/metadata.js +4 -4
  213. package/dist/shims/navigation.d.ts +8 -2
  214. package/dist/shims/navigation.js +63 -31
  215. package/dist/shims/ppr-fallback-shell.d.ts +5 -1
  216. package/dist/shims/ppr-fallback-shell.js +28 -7
  217. package/dist/shims/router.d.ts +18 -3
  218. package/dist/shims/router.js +512 -142
  219. package/dist/shims/script.js +8 -4
  220. package/dist/shims/server.d.ts +16 -1
  221. package/dist/shims/server.js +44 -12
  222. package/dist/shims/unified-request-context.js +1 -0
  223. package/dist/utils/built-asset-url.d.ts +4 -0
  224. package/dist/utils/built-asset-url.js +11 -0
  225. package/dist/utils/commonjs-loader.d.ts +16 -0
  226. package/dist/utils/commonjs-loader.js +100 -0
  227. package/dist/utils/deployment-id.d.ts +8 -0
  228. package/dist/utils/deployment-id.js +22 -0
  229. package/dist/utils/has-trailing-comma.d.ts +24 -0
  230. package/dist/utils/has-trailing-comma.js +62 -0
  231. package/dist/utils/html-limited-bots.d.ts +18 -1
  232. package/dist/utils/html-limited-bots.js +23 -1
  233. package/dist/utils/parse-cookie.d.ts +13 -0
  234. package/dist/utils/parse-cookie.js +52 -0
  235. package/dist/utils/path.d.ts +7 -1
  236. package/dist/utils/path.js +9 -1
  237. package/dist/utils/text-stream.d.ts +1 -1
  238. package/dist/utils/text-stream.js +2 -2
  239. package/dist/utils/vite-version.d.ts +12 -1
  240. package/dist/utils/vite-version.js +9 -1
  241. package/package.json +2 -2
  242. package/dist/shims/internal/parse-cookie-header.d.ts +0 -14
  243. package/dist/shims/internal/parse-cookie-header.js +0 -30
@@ -0,0 +1,150 @@
1
+ import { collectBindingNames, forEachAstChild, hasRange, isAstRecord, isIdentifierNamed, nodeArray } from "./ast-utils.js";
2
+ import { parseAst } from "vite";
3
+ import MagicString from "magic-string";
4
+ //#region src/plugins/typeof-window.ts
5
+ function createScope(parent) {
6
+ return {
7
+ parent,
8
+ bindings: /* @__PURE__ */ new Set()
9
+ };
10
+ }
11
+ function hasBinding(scope, name) {
12
+ for (let current = scope; current; current = current.parent) if (current.bindings.has(name)) return true;
13
+ return false;
14
+ }
15
+ function collectScopeBindings(node, scope) {
16
+ forEachAstChild(node, (child) => {
17
+ if (child.type === "ExportNamedDeclaration" || child.type === "ExportDefaultDeclaration") {
18
+ if (isAstRecord(child.declaration)) collectScopeBindings(child, scope);
19
+ return;
20
+ }
21
+ if (child.type === "FunctionDeclaration" || child.type === "ClassDeclaration") {
22
+ collectBindingNames(child.id, scope.bindings);
23
+ return;
24
+ }
25
+ if (child.type === "VariableDeclaration") {
26
+ for (const declaration of nodeArray(child.declarations)) if (isAstRecord(declaration)) collectBindingNames(declaration.id, scope.bindings);
27
+ return;
28
+ }
29
+ if (child.type === "ImportDeclaration") {
30
+ for (const specifier of nodeArray(child.specifiers)) if (isAstRecord(specifier)) collectBindingNames(specifier.local, scope.bindings);
31
+ }
32
+ });
33
+ }
34
+ function collectVarBindings(node, scope, isRoot = true) {
35
+ if (!isRoot && (node.type === "FunctionDeclaration" || node.type === "FunctionExpression" || node.type === "ArrowFunctionExpression")) return;
36
+ if (node.type === "VariableDeclaration" && node.kind === "var") {
37
+ for (const declaration of nodeArray(node.declarations)) if (isAstRecord(declaration)) collectBindingNames(declaration.id, scope.bindings);
38
+ }
39
+ forEachAstChild(node, (child) => collectVarBindings(child, scope, false));
40
+ }
41
+ function isFunctionNode(node) {
42
+ return node.type === "FunctionDeclaration" || node.type === "FunctionExpression" || node.type === "ArrowFunctionExpression";
43
+ }
44
+ function createChildScope(node, parent) {
45
+ if (node.type !== "Program" && node.type !== "BlockStatement" && node.type !== "StaticBlock" && node.type !== "SwitchStatement" && node.type !== "CatchClause" && node.type !== "ForStatement" && node.type !== "ForInStatement" && node.type !== "ForOfStatement" && node.type !== "ClassDeclaration" && node.type !== "ClassExpression") return null;
46
+ const scope = createScope(parent);
47
+ if (node.type === "ClassDeclaration" || node.type === "ClassExpression") collectBindingNames(node.id, scope.bindings);
48
+ else if (node.type === "CatchClause") collectBindingNames(node.param, scope.bindings);
49
+ collectScopeBindings(node, scope);
50
+ if (node.type === "SwitchStatement") {
51
+ for (const switchCase of nodeArray(node.cases)) if (isAstRecord(switchCase)) collectScopeBindings(switchCase, scope);
52
+ }
53
+ return scope;
54
+ }
55
+ function getTypeofWindowReplacement(environment) {
56
+ return environment.config.consumer === "client" ? "object" : "undefined";
57
+ }
58
+ function stringLiteralValue(node) {
59
+ if (!isAstRecord(node)) return null;
60
+ if (node.type === "Literal" && typeof node.value === "string") return node.value;
61
+ return null;
62
+ }
63
+ function evaluateTypeofWindowComparison(node, replacement, scope) {
64
+ if (!isAstRecord(node) || node.type !== "BinaryExpression") return null;
65
+ if (![
66
+ "==",
67
+ "===",
68
+ "!=",
69
+ "!=="
70
+ ].includes(String(node.operator))) return null;
71
+ const left = isAstRecord(node.left) ? node.left : null;
72
+ const right = isAstRecord(node.right) ? node.right : null;
73
+ const leftIsTypeofWindow = left?.type === "UnaryExpression" && left.operator === "typeof" && isIdentifierNamed(left.argument, "window") && !hasBinding(scope, "window");
74
+ const rightIsTypeofWindow = right?.type === "UnaryExpression" && right.operator === "typeof" && isIdentifierNamed(right.argument, "window") && !hasBinding(scope, "window");
75
+ const comparedValue = leftIsTypeofWindow ? stringLiteralValue(right) : rightIsTypeofWindow ? stringLiteralValue(left) : null;
76
+ if (comparedValue === null) return null;
77
+ const equal = replacement === comparedValue;
78
+ return node.operator === "==" || node.operator === "===" ? equal : !equal;
79
+ }
80
+ function replaceTypeofWindow(code, replacement) {
81
+ if (!/typeof\s+window/.test(code)) return null;
82
+ let ast;
83
+ try {
84
+ ast = parseAst(code);
85
+ } catch {
86
+ return null;
87
+ }
88
+ const output = new MagicString(code);
89
+ let changed = false;
90
+ if (!isAstRecord(ast)) return null;
91
+ const rootScope = createScope(null);
92
+ collectScopeBindings(ast, rootScope);
93
+ collectVarBindings(ast, rootScope);
94
+ function visit(node, parentScope) {
95
+ if (isFunctionNode(node)) {
96
+ const parameterScope = createScope(parentScope);
97
+ collectBindingNames(node.id, parameterScope.bindings);
98
+ for (const parameter of nodeArray(node.params)) {
99
+ collectBindingNames(parameter, parameterScope.bindings);
100
+ if (isAstRecord(parameter)) visit(parameter, parameterScope);
101
+ }
102
+ if (isAstRecord(node.body)) if (node.body.type === "BlockStatement") {
103
+ const bodyScope = createScope(parameterScope);
104
+ collectVarBindings(node.body, bodyScope);
105
+ visit(node.body, bodyScope);
106
+ } else visit(node.body, parameterScope);
107
+ return;
108
+ }
109
+ const scope = createChildScope(node, parentScope) ?? parentScope;
110
+ if (node.type === "IfStatement" && hasRange(node)) {
111
+ const result = evaluateTypeofWindowComparison(node.test, replacement, scope);
112
+ if (result !== null) {
113
+ const selected = result ? node.consequent : node.alternate;
114
+ if (isAstRecord(selected) && hasRange(selected)) {
115
+ output.remove(node.start, selected.start);
116
+ output.remove(selected.end, node.end);
117
+ visit(selected, scope);
118
+ } else output.overwrite(node.start, node.end, ";");
119
+ changed = true;
120
+ return;
121
+ }
122
+ }
123
+ if (node.type === "ConditionalExpression" && hasRange(node)) {
124
+ const result = evaluateTypeofWindowComparison(node.test, replacement, scope);
125
+ const selected = result ? node.consequent : node.alternate;
126
+ if (result !== null && isAstRecord(selected) && hasRange(selected)) {
127
+ output.overwrite(node.start, selected.start, "(");
128
+ if (selected.end < node.end) output.overwrite(selected.end, node.end, ")");
129
+ else output.appendLeft(selected.end, ")");
130
+ visit(selected, scope);
131
+ changed = true;
132
+ return;
133
+ }
134
+ }
135
+ if (node.type === "UnaryExpression" && node.operator === "typeof" && isIdentifierNamed(node.argument, "window") && !hasBinding(scope, "window") && hasRange(node)) {
136
+ output.overwrite(node.start, node.end, JSON.stringify(replacement));
137
+ changed = true;
138
+ return;
139
+ }
140
+ forEachAstChild(node, (child) => visit(child, scope));
141
+ }
142
+ for (const node of ast.body) if (isAstRecord(node)) visit(node, rootScope);
143
+ if (!changed) return null;
144
+ return {
145
+ code: output.toString(),
146
+ map: output.generateMap({ hires: "boundary" })
147
+ };
148
+ }
149
+ //#endregion
150
+ export { getTypeofWindowReplacement, replaceTypeofWindow };
@@ -21,7 +21,8 @@ type InterceptingRoute = {
21
21
  * @see https://github.com/vercel/next.js/blob/canary/packages/next/src/shared/lib/router/utils/interception-routes.ts
22
22
  */
23
23
  sourceMatchPattern: string; /** Absolute path to the intercepting page component */
24
- pagePath: string; /** Absolute layout paths inside the intercepting route tree, outermost to innermost */
24
+ pagePath: string; /** Filesystem segments from app/ root to the intercepting page directory. */
25
+ sourcePageSegments?: string[]; /** Absolute layout paths inside the intercepting route tree, outermost to innermost */
25
26
  layoutPaths: string[]; /** Parameter names for dynamic segments */
26
27
  params: string[];
27
28
  /**
@@ -1,5 +1,5 @@
1
1
  import { normalizePathSeparators } from "../utils/path.js";
2
- import { compareRoutes, decodeRouteSegment, isInvisibleSegment } from "./utils.js";
2
+ import { decodeRouteSegment, isInvisibleSegment, sortRoutes } from "./utils.js";
3
3
  import { findFileWithExts, scanWithExtensions } from "./file-matcher.js";
4
4
  import { validateRoutePatterns } from "./route-validation.js";
5
5
  import { compareStrings } from "../utils/compare.js";
@@ -346,23 +346,25 @@ function createRouteManifestGraphVersion(segmentGraph) {
346
346
  }
347
347
  async function buildAppRouteGraph(appDir, matcher) {
348
348
  const routes = [];
349
+ const scanMatcher = { ...matcher };
350
+ findFileProbeCache.set(scanMatcher, /* @__PURE__ */ new Map());
349
351
  const excludeDir = (name) => name.startsWith("@") && name !== "@children" || name.startsWith("_") || isInterceptionMarkerDir(name);
350
- for await (const file of scanWithExtensions("**/page", appDir, matcher.extensions, excludeDir)) {
351
- const route = fileToAppRoute(file, appDir, "page", matcher);
352
+ for await (const file of scanWithExtensions("**/page", appDir, scanMatcher.extensions, excludeDir)) {
353
+ const route = fileToAppRoute(file, appDir, "page", scanMatcher);
352
354
  if (route) routes.push(route);
353
355
  }
354
- for await (const file of scanWithExtensions("**/route", appDir, matcher.extensions, excludeDir)) {
355
- const route = fileToAppRoute(file, appDir, "route", matcher);
356
+ for await (const file of scanWithExtensions("**/route", appDir, scanMatcher.extensions, excludeDir)) {
357
+ const route = fileToAppRoute(file, appDir, "route", scanMatcher);
356
358
  if (route) routes.push(route);
357
359
  }
358
360
  const routePatterns = new Set(routes.map((route) => route.pattern));
359
361
  const ghostParentRoutes = [];
360
- for await (const file of scanWithExtensions("**/layout", appDir, matcher.extensions, excludeDir)) {
362
+ for await (const file of scanWithExtensions("**/layout", appDir, scanMatcher.extensions, excludeDir)) {
361
363
  const dir = path.dirname(file);
362
364
  const routeDir = dir === "." ? appDir : path.join(appDir, dir);
363
365
  if (!hasParallelSlotDirectory(routeDir)) continue;
364
- if (discoverParallelSlots(routeDir, appDir, matcher).length === 0) continue;
365
- const route = directoryToAppRoute(dir, appDir, matcher, null, null);
366
+ if (discoverParallelSlots(routeDir, appDir, scanMatcher).length === 0) continue;
367
+ const route = directoryToAppRoute(dir, appDir, scanMatcher, null, null);
366
368
  if (!route) continue;
367
369
  if (routePatterns.has(route.pattern)) {
368
370
  ghostParentRoutes.push(route);
@@ -371,13 +373,13 @@ async function buildAppRouteGraph(appDir, matcher) {
371
373
  routes.push(route);
372
374
  routePatterns.add(route.pattern);
373
375
  }
374
- const slotSubRoutes = discoverSlotSubRoutes(routes, matcher, ghostParentRoutes);
376
+ const slotSubRoutes = discoverSlotSubRoutes(routes, scanMatcher, ghostParentRoutes);
375
377
  routes.push(...slotSubRoutes);
376
- discoverSiblingInterceptingRoutes(routes, appDir, matcher);
378
+ discoverSiblingInterceptingRoutes(routes, appDir, scanMatcher);
377
379
  validatePageRouteConflicts(routes, appDir);
378
380
  validateRoutePatterns(routes.map((route) => route.pattern));
379
381
  validateRoutePatterns([...new Set(routes.flatMap((route) => [...route.parallelSlots.flatMap((slot) => slot.interceptingRoutes.map((intercept) => intercept.targetPattern)), ...route.siblingIntercepts.map((intercept) => intercept.targetPattern)]))]);
380
- routes.sort(compareRoutes);
382
+ sortRoutes(routes);
381
383
  return {
382
384
  routes,
383
385
  routeManifest: createRouteManifest(routes)
@@ -884,11 +886,11 @@ function discoverInheritedParallelSlots(segments, appDir, routeDir, matcher) {
884
886
  const routeHasLayout = layoutIdx >= 0;
885
887
  for (const { dir, layoutIdx: lvlLayoutIdx, segmentIndex } of dirsToCheck) {
886
888
  if (lvlLayoutIdx < 0 && routeHasLayout) continue;
887
- const isOwnDir = dir === routeDir;
888
889
  const slotLayoutIdx = Math.max(lvlLayoutIdx, 0);
889
890
  const slotsAtLevel = discoverParallelSlots(dir, appDir, matcher);
890
891
  const segmentsBelow = segments.slice(segmentIndex);
891
- for (const slot of slotsAtLevel) if (isOwnDir) {
892
+ const isActiveUrlLevel = dir === routeDir || segmentsBelow.every(isInvisibleSegment);
893
+ for (const slot of slotsAtLevel) if (isActiveUrlLevel) {
892
894
  slot.layoutIndex = slotLayoutIdx;
893
895
  slotMap.set(slot.key, slot);
894
896
  } else {
@@ -1275,6 +1277,7 @@ function collectInterceptingPages(currentDir, interceptRoot, convention, interce
1275
1277
  targetPattern: targetPattern.pattern,
1276
1278
  sourceMatchPattern,
1277
1279
  pagePath: page,
1280
+ sourcePageSegments: normalizePathSeparators(path.relative(appDir, path.dirname(page))).split("/").filter(Boolean),
1278
1281
  params: targetPattern.params
1279
1282
  });
1280
1283
  }
@@ -1384,10 +1387,37 @@ function markerForInterceptionConvention(convention) {
1384
1387
  }
1385
1388
  }
1386
1389
  /**
1387
- * Find a file by name (without extension) in a directory.
1388
- * Checks configured pageExtensions.
1390
+ * Scan-scoped cache of convention-file probes, keyed by the per-scan matcher
1391
+ * created in `buildAppRouteGraph`. A single scan walks the appDir→leaf chain
1392
+ * separately for every route (layouts, templates, errors, boundaries, slots),
1393
+ * so shared ancestor directories — the `app/` root above all — get re-probed
1394
+ * once per descendant route. The probe result is deterministic within one scan
1395
+ * (the filesystem does not change mid-build), so memoizing it removes the
1396
+ * dominant cross-route redundancy.
1397
+ *
1398
+ * Keyed by matcher so the cache lifetime is exactly one `buildAppRouteGraph`
1399
+ * call: the scan registers a fresh matcher clone, and the entry is unreachable
1400
+ * (and GC-eligible) once the scan returns. A fresh key per scan is also what
1401
+ * makes this concurrency-safe — overlapping builds never share probe state.
1402
+ */
1403
+ const findFileProbeCache = /* @__PURE__ */ new WeakMap();
1404
+ /**
1405
+ * Find a file by name (without extension) in a directory, checking configured
1406
+ * pageExtensions. Memoizes through `findFileProbeCache` when the matcher has a
1407
+ * registered per-scan cache; otherwise falls back to a direct probe (identical
1408
+ * result). The `null` "not found" outcome is cached too, so repeated misses on
1409
+ * shared ancestors cost a single set of `existsSync` calls per scan.
1389
1410
  */
1390
- const findFile = findFileWithExts;
1411
+ function findFile(dir, name, matcher) {
1412
+ const cache = findFileProbeCache.get(matcher);
1413
+ if (!cache) return findFileWithExts(dir, name, matcher);
1414
+ const key = `${dir}\0${name}`;
1415
+ const cached = cache.get(key);
1416
+ if (cached !== void 0) return cached;
1417
+ const result = findFileWithExts(dir, name, matcher);
1418
+ cache.set(key, result);
1419
+ return result;
1420
+ }
1391
1421
  /**
1392
1422
  * Convert filesystem path segments to URL route parts, skipping invisible segments
1393
1423
  * (route groups, @slots, ".") and converting dynamic segment syntax to Express-style
@@ -43,9 +43,18 @@ declare function findFileWithExts(dir: string, name: string, matcher: ValidFileM
43
43
  * See: cloudflare/vinext#1502
44
44
  */
45
45
  declare function buildViteResolveExtensions(pageExtensions?: readonly string[] | null, viteDefaults?: readonly string[]): string[];
46
+ /**
47
+ * Normalize an explicit Next.js resolver extension list for Vite.
48
+ *
49
+ * Unlike `pageExtensions`, both Turbopack's `resolveExtensions` and webpack's
50
+ * `resolve.extensions` replace their resolver defaults. The empty string is a
51
+ * webpack/Turbopack convention for trying the import exactly as written; Vite
52
+ * already does that before appending extensions, so it must be omitted here.
53
+ */
54
+ declare function normalizeViteResolveExtensions(extensions: readonly string[]): string[];
46
55
  /**
47
56
  * Use function-form exclude for Node < 22.14 compatibility.
48
57
  */
49
58
  declare function scanWithExtensions(stem: string, cwd: string, extensions: readonly string[], exclude?: (name: string) => boolean): AsyncGenerator<string>;
50
59
  //#endregion
51
- export { ValidFileMatcher, buildViteResolveExtensions, createValidFileMatcher, findFileWithExtensions, findFileWithExts, normalizePageExtensions, scanWithExtensions };
60
+ export { ValidFileMatcher, buildViteResolveExtensions, createValidFileMatcher, findFileWithExtensions, findFileWithExts, normalizePageExtensions, normalizeViteResolveExtensions, scanWithExtensions };
@@ -113,6 +113,27 @@ function buildViteResolveExtensions(pageExtensions, viteDefaults = [
113
113
  return result;
114
114
  }
115
115
  /**
116
+ * Normalize an explicit Next.js resolver extension list for Vite.
117
+ *
118
+ * Unlike `pageExtensions`, both Turbopack's `resolveExtensions` and webpack's
119
+ * `resolve.extensions` replace their resolver defaults. The empty string is a
120
+ * webpack/Turbopack convention for trying the import exactly as written; Vite
121
+ * already does that before appending extensions, so it must be omitted here.
122
+ */
123
+ function normalizeViteResolveExtensions(extensions) {
124
+ const seen = /* @__PURE__ */ new Set();
125
+ const result = [];
126
+ for (const extension of extensions) {
127
+ const trimmed = extension.trim();
128
+ if (!trimmed) continue;
129
+ const dotted = trimmed.startsWith(".") ? trimmed : `.${trimmed}`;
130
+ if (seen.has(dotted)) continue;
131
+ seen.add(dotted);
132
+ result.push(dotted);
133
+ }
134
+ return result;
135
+ }
136
+ /**
116
137
  * Use function-form exclude for Node < 22.14 compatibility.
117
138
  */
118
139
  async function* scanWithExtensions(stem, cwd, extensions, exclude) {
@@ -123,4 +144,4 @@ async function* scanWithExtensions(stem, cwd, extensions, exclude) {
123
144
  })) yield file;
124
145
  }
125
146
  //#endregion
126
- export { buildViteResolveExtensions, createValidFileMatcher, findFileWithExtensions, findFileWithExts, normalizePageExtensions, scanWithExtensions };
147
+ export { buildViteResolveExtensions, createValidFileMatcher, findFileWithExtensions, findFileWithExts, normalizePageExtensions, normalizeViteResolveExtensions, scanWithExtensions };
@@ -1,4 +1,4 @@
1
- import { compareRoutes, decodeRouteSegment } from "./utils.js";
1
+ import { decodeRouteSegment, sortRoutes } from "./utils.js";
2
2
  import { createValidFileMatcher, scanWithExtensions } from "./file-matcher.js";
3
3
  import { patternToNextFormat, validateRoutePatterns } from "./route-validation.js";
4
4
  import { createRouteTrieCache, matchRouteWithTrie } from "./route-matching.js";
@@ -54,7 +54,7 @@ async function scanPageRoutes(pagesDir, matcher) {
54
54
  if (route) routes.push(route);
55
55
  }
56
56
  validateRoutePatterns(routes.map((route) => route.pattern));
57
- routes.sort(compareRoutes);
57
+ sortRoutes(routes);
58
58
  return routes;
59
59
  }
60
60
  /**
@@ -156,7 +156,7 @@ async function scanApiRoutes(pagesDir, matcher) {
156
156
  if (route) routes.push(route);
157
157
  }
158
158
  validateRoutePatterns(routes.map((route) => route.pattern));
159
- routes.sort(compareRoutes);
159
+ sortRoutes(routes);
160
160
  return routes;
161
161
  }
162
162
  //#endregion
@@ -1,13 +1,42 @@
1
1
  //#region src/routing/utils.d.ts
2
2
  /**
3
- * Sort comparator for routes — lower precedence score sorts first (higher priority).
4
- * Lexicographic tiebreaker on pattern for determinism.
3
+ * Sort routes by precedence — lower score sorts first (higher priority), with a
4
+ * lexicographic tiebreaker on the pattern for determinism. Sorts in place and
5
+ * returns the same array (mirrors `Array.prototype.sort`).
5
6
  *
6
- * Usage: routes.sort(compareRoutes)
7
+ * `routePrecedence` is a pure function of the pattern, so each pattern's score
8
+ * is computed exactly once up front (decorate-sort) instead of ~2·log n times
9
+ * by a comparator that re-parses on every comparison. The `localeCompare`
10
+ * tiebreaker already guarantees a total order, so the result is byte-identical
11
+ * to comparing precedence inline.
12
+ *
13
+ * Usage: sortRoutes(routes)
7
14
  */
8
- declare function compareRoutes<T extends {
15
+ declare function sortRoutes<T extends {
9
16
  pattern: string;
10
- }>(a: T, b: T): number;
17
+ }>(routes: T[]): T[];
18
+ /**
19
+ * Single source of truth for hybrid App/Pages route ownership.
20
+ *
21
+ * Mirrors Next.js's DefaultRouteMatcherManager ordering: Pages providers
22
+ * are registered before App providers, then merged dynamic matchers sort
23
+ * together. Returns the router that should own a request/navigation to
24
+ * a URL that matched BOTH routers.
25
+ *
26
+ * Centralised so the server's request handling and the client's link /
27
+ * prefetch / programmatic-navigation paths all reach the same owner for
28
+ * the same (pages pattern, app pattern) pair. This intentionally implements
29
+ * Next.js's segment-tree ordering directly instead of vinext's broader
30
+ * `sortRoutes()` score heuristic. It only arbitrates two routes that already
31
+ * matched the same URL; each router's own trie ordering remains unchanged.
32
+ *
33
+ * Usage:
34
+ * compareHybridRoutePatterns("/:slug", true, "/:slug", true) // → "pages"
35
+ * compareHybridRoutePatterns("/_sites/:slug*", true, "/:slug*", true) // → "pages"
36
+ * compareHybridRoutePatterns("/:path+", true, "/dashboard", false) // → "app"
37
+ * compareHybridRoutePatterns("/", false, "/", false) // → "app"
38
+ */
39
+ declare function compareHybridRoutePatterns(pagesPattern: string, pagesIsDynamic: boolean, appPattern: string, appIsDynamic: boolean): "app" | "pages";
11
40
  /**
12
41
  * Decode a filesystem or URL path segment while preserving encoded path delimiters.
13
42
  * Mirrors Next.js segment-wise decoding so "%5F" becomes "_" but "%2F" stays "%2F".
@@ -85,4 +114,4 @@ declare function isOptionalCatchAllSegment(segment: string): boolean;
85
114
  */
86
115
  declare function countConsumedPathnameSegments(visibleTreePathSegments: readonly string[], pathnameSegmentCount: number): number;
87
116
  //#endregion
88
- export { buildParams, compareRoutes, countConsumedPathnameSegments, decodeMatchedParams, decodeRouteSegment, isCatchAllSegment, isInvisibleSegment, isOptionalCatchAllSegment, normalizePathnameForRouteMatch, normalizePathnameForRouteMatchStrict, splitPathSegments, splitPathnameForRouteMatch };
117
+ export { buildParams, compareHybridRoutePatterns, countConsumedPathnameSegments, decodeMatchedParams, decodeRouteSegment, isCatchAllSegment, isInvisibleSegment, isOptionalCatchAllSegment, normalizePathnameForRouteMatch, normalizePathnameForRouteMatchStrict, sortRoutes, splitPathSegments, splitPathnameForRouteMatch };
@@ -44,14 +44,66 @@ function routePrecedence(pattern) {
44
44
  return score;
45
45
  }
46
46
  /**
47
- * Sort comparator for routes — lower precedence score sorts first (higher priority).
48
- * Lexicographic tiebreaker on pattern for determinism.
47
+ * Sort routes by precedence — lower score sorts first (higher priority), with a
48
+ * lexicographic tiebreaker on the pattern for determinism. Sorts in place and
49
+ * returns the same array (mirrors `Array.prototype.sort`).
49
50
  *
50
- * Usage: routes.sort(compareRoutes)
51
+ * `routePrecedence` is a pure function of the pattern, so each pattern's score
52
+ * is computed exactly once up front (decorate-sort) instead of ~2·log n times
53
+ * by a comparator that re-parses on every comparison. The `localeCompare`
54
+ * tiebreaker already guarantees a total order, so the result is byte-identical
55
+ * to comparing precedence inline.
56
+ *
57
+ * Usage: sortRoutes(routes)
58
+ */
59
+ function sortRoutes(routes) {
60
+ const scores = /* @__PURE__ */ new Map();
61
+ for (const route of routes) if (!scores.has(route.pattern)) scores.set(route.pattern, routePrecedence(route.pattern));
62
+ return routes.sort((a, b) => {
63
+ const diff = (scores.get(a.pattern) ?? 0) - (scores.get(b.pattern) ?? 0);
64
+ return diff !== 0 ? diff : a.pattern.localeCompare(b.pattern);
65
+ });
66
+ }
67
+ /**
68
+ * Single source of truth for hybrid App/Pages route ownership.
69
+ *
70
+ * Mirrors Next.js's DefaultRouteMatcherManager ordering: Pages providers
71
+ * are registered before App providers, then merged dynamic matchers sort
72
+ * together. Returns the router that should own a request/navigation to
73
+ * a URL that matched BOTH routers.
74
+ *
75
+ * Centralised so the server's request handling and the client's link /
76
+ * prefetch / programmatic-navigation paths all reach the same owner for
77
+ * the same (pages pattern, app pattern) pair. This intentionally implements
78
+ * Next.js's segment-tree ordering directly instead of vinext's broader
79
+ * `sortRoutes()` score heuristic. It only arbitrates two routes that already
80
+ * matched the same URL; each router's own trie ordering remains unchanged.
81
+ *
82
+ * Usage:
83
+ * compareHybridRoutePatterns("/:slug", true, "/:slug", true) // → "pages"
84
+ * compareHybridRoutePatterns("/_sites/:slug*", true, "/:slug*", true) // → "pages"
85
+ * compareHybridRoutePatterns("/:path+", true, "/dashboard", false) // → "app"
86
+ * compareHybridRoutePatterns("/", false, "/", false) // → "app"
51
87
  */
52
- function compareRoutes(a, b) {
53
- const diff = routePrecedence(a.pattern) - routePrecedence(b.pattern);
54
- return diff !== 0 ? diff : a.pattern.localeCompare(b.pattern);
88
+ function compareHybridRoutePatterns(pagesPattern, pagesIsDynamic, appPattern, appIsDynamic) {
89
+ if (pagesPattern === appPattern) throw new Error(`Conflicting app and page routes found for "${pagesPattern}"`);
90
+ if (!pagesIsDynamic) return appIsDynamic ? "pages" : "app";
91
+ if (!appIsDynamic) return "app";
92
+ const pagesSegments = pagesPattern.split("/").filter(Boolean);
93
+ const appSegments = appPattern.split("/").filter(Boolean);
94
+ const segmentRank = (segment) => {
95
+ if (!segment.startsWith(":")) return 0;
96
+ if (segment.endsWith("*")) return 3;
97
+ if (segment.endsWith("+")) return 2;
98
+ return 1;
99
+ };
100
+ for (let index = 0; index < Math.min(pagesSegments.length, appSegments.length); index++) {
101
+ const pagesRank = segmentRank(pagesSegments[index]);
102
+ const appRank = segmentRank(appSegments[index]);
103
+ if (pagesRank !== appRank) return pagesRank < appRank ? "pages" : "app";
104
+ }
105
+ if (pagesSegments.length !== appSegments.length) return pagesSegments.length < appSegments.length ? "pages" : "app";
106
+ return "pages";
55
107
  }
56
108
  const PATH_DELIMITER_REGEX = /([/#?\\]|%(2f|23|3f|5c))/gi;
57
109
  function encodePathDelimiters(segment) {
@@ -187,4 +239,4 @@ function countConsumedPathnameSegments(visibleTreePathSegments, pathnameSegmentC
187
239
  return consumed;
188
240
  }
189
241
  //#endregion
190
- export { buildParams, compareRoutes, countConsumedPathnameSegments, decodeMatchedParams, decodeRouteSegment, isCatchAllSegment, isInvisibleSegment, isOptionalCatchAllSegment, normalizePathnameForRouteMatch, normalizePathnameForRouteMatchStrict, splitPathSegments, splitPathnameForRouteMatch };
242
+ export { buildParams, compareHybridRoutePatterns, countConsumedPathnameSegments, decodeMatchedParams, decodeRouteSegment, isCatchAllSegment, isInvisibleSegment, isOptionalCatchAllSegment, normalizePathnameForRouteMatch, normalizePathnameForRouteMatchStrict, sortRoutes, splitPathSegments, splitPathnameForRouteMatch };
@@ -1,3 +1,4 @@
1
+ import { NextI18nConfig } from "../config/next-config.js";
1
2
  import { Route } from "../routing/pages-router.js";
2
3
  import { ModuleImporter } from "./instrumentation.js";
3
4
  import { IncomingMessage, ServerResponse } from "node:http";
@@ -7,6 +8,10 @@ import { IncomingMessage, ServerResponse } from "node:http";
7
8
  * Handle an API route request.
8
9
  * Returns true if the request was handled, false if no API route matched.
9
10
  */
10
- declare function handleApiRoute(runner: ModuleImporter, req: IncomingMessage, res: ServerResponse, url: string, apiRoutes: Route[]): Promise<boolean>;
11
+ declare function handleApiRoute(runner: ModuleImporter, req: IncomingMessage, res: ServerResponse, url: string, apiRoutes: Route[], nextConfig?: {
12
+ basePath?: string;
13
+ i18n?: NextI18nConfig | null;
14
+ trailingSlash?: boolean;
15
+ }): Promise<boolean>;
11
16
  //#endregion
12
17
  export { handleApiRoute };
@@ -1,8 +1,10 @@
1
+ import { hasBasePath } from "../utils/base-path.js";
1
2
  import { matchRoute } from "../routing/pages-router.js";
2
3
  import "./server-globals.js";
4
+ import { parseCookieHeader } from "../utils/parse-cookie.js";
3
5
  import { NextRequest } from "../shims/server.js";
4
6
  import { importModule, reportRequestError } from "./instrumentation.js";
5
- import { mergeRouteParamsIntoQuery, parseQueryString } from "../utils/query.js";
7
+ import { mergeRouteParamsIntoQuery, parseQueryString, urlQueryToSearchParams } from "../utils/query.js";
6
8
  import { PagesBodyParseError, getMediaType, isJsonMediaType } from "./pages-media-type.js";
7
9
  import { isEdgeApiRuntime } from "./edge-api-runtime.js";
8
10
  import { DEFAULT_PAGES_API_BODY_SIZE_LIMIT, resolveBodyParserConfig } from "./pages-body-parser-config.js";
@@ -71,13 +73,7 @@ async function parseBody(req, sizeLimit = MAX_BODY_SIZE) {
71
73
  * Parse cookies from the Cookie header.
72
74
  */
73
75
  function parseCookies(req) {
74
- const header = req.headers.cookie ?? "";
75
- const cookies = {};
76
- for (const part of header.split(";")) {
77
- const [key, ...rest] = part.split("=");
78
- if (key) cookies[key.trim()] = rest.join("=").trim();
79
- }
80
- return cookies;
76
+ return parseCookieHeader(req.headers.cookie);
81
77
  }
82
78
  function isEdgeApiRouteModule(module) {
83
79
  if (typeof module.default !== "function") return false;
@@ -98,13 +94,19 @@ function readEdgeRequestBody(req) {
98
94
  req.on("error", (error) => controller.error(error));
99
95
  } });
100
96
  }
101
- function createEdgeApiRequest(req, url) {
97
+ function createEdgeApiRequest(req, url, params, nextConfig) {
102
98
  const headers = new Headers();
103
- for (const [name, value] of Object.entries(req.headers)) if (Array.isArray(value)) for (const item of value) headers.append(name, item);
104
- else if (value !== void 0) headers.set(name, value);
99
+ for (const [name, value] of Object.entries(req.headers)) {
100
+ if (name.startsWith(":")) continue;
101
+ if (Array.isArray(value)) for (const item of value) headers.append(name, item);
102
+ else if (value !== void 0) headers.set(name, value);
103
+ }
105
104
  const proto = resolveRequestProtocol(req);
106
105
  const host = resolveRequestHost(req, "localhost");
107
- const requestUrl = new URL(url, `${proto}://${host}`);
106
+ const requestUrl = new URL(req.url ?? url, `${proto}://${host}`);
107
+ const basePath = nextConfig?.basePath;
108
+ if (basePath && !hasBasePath(requestUrl.pathname, basePath)) requestUrl.pathname = `${basePath}${requestUrl.pathname}`;
109
+ requestUrl.search = urlQueryToSearchParams(mergeRouteParamsIntoQuery(parseQueryString(url), params)).toString();
108
110
  const body = readEdgeRequestBody(req);
109
111
  const init = {
110
112
  headers,
@@ -210,14 +212,18 @@ function enhanceApiObjects(req, res, query, body) {
210
212
  * Handle an API route request.
211
213
  * Returns true if the request was handled, false if no API route matched.
212
214
  */
213
- async function handleApiRoute(runner, req, res, url, apiRoutes) {
215
+ async function handleApiRoute(runner, req, res, url, apiRoutes, nextConfig) {
214
216
  const match = matchRoute(url, apiRoutes);
215
217
  if (!match) return false;
216
218
  const { route, params } = match;
217
219
  try {
218
220
  const apiModule = await importModule(runner, route.filePath);
219
221
  if (isEdgeApiRouteModule(apiModule)) {
220
- const nextRequest = new NextRequest(createEdgeApiRequest(req, url));
222
+ const nextRequest = new NextRequest(createEdgeApiRequest(req, url, params, nextConfig), nextConfig ? { nextConfig: {
223
+ basePath: nextConfig.basePath,
224
+ i18n: nextConfig.i18n ?? void 0,
225
+ trailingSlash: nextConfig.trailingSlash
226
+ } } : void 0);
221
227
  const response = await apiModule.default(nextRequest);
222
228
  if (!(response instanceof Response)) throw new Error("Edge API route did not return a Response");
223
229
  res.statusCode = response.status;
@@ -253,7 +259,7 @@ async function handleApiRoute(runner, req, res, url, apiRoutes) {
253
259
  reportRequestError(e instanceof Error ? e : new Error(String(e)), {
254
260
  path: url,
255
261
  method: req.method ?? "GET",
256
- headers: Object.fromEntries(Object.entries(req.headers).map(([k, v]) => [k, Array.isArray(v) ? v.join(", ") : String(v ?? "")]))
262
+ headers: Object.fromEntries(Object.entries(req.headers).filter(([k]) => !k.startsWith(":")).map(([k, v]) => [k, Array.isArray(v) ? v.join(", ") : String(v ?? "")]))
257
263
  }, {
258
264
  routerKind: "Pages Router",
259
265
  routePath: match.route.pattern,
@@ -1,3 +1,5 @@
1
+ import { ServerActionResultFacts } from "./navigation-planner.js";
2
+
1
3
  //#region src/server/app-browser-action-result.d.ts
2
4
  type AppBrowserServerActionResult<TRoot> = {
3
5
  root?: TRoot;
@@ -24,12 +26,23 @@ declare function shouldClearClientNavigationCachesForServerActionResult<TRoot>(r
24
26
  declare function parseServerActionRevalidationHeader(headers: Pick<Headers, "get">): ServerActionRevalidationKind;
25
27
  declare function normalizeServerActionThrownValue(data: unknown, responseStatus: number): unknown;
26
28
  declare function readInvalidServerActionResponseError(response: Pick<Response, "headers" | "status" | "text">, hasRedirectLocation: boolean): Promise<Error | null>;
27
- declare function shouldCheckRscCompatibilityForServerActionResponse(response: Pick<Response, "headers">): boolean;
28
- declare function resolveServerActionRedirectCompatibilityHardNavigationTarget(options: {
29
+ type ServerActionResultResponseFactsInput = {
29
30
  actionRedirectHref: string | null;
30
- clientCompatibilityId: string | null | undefined;
31
- response: Pick<Response, "headers">;
32
- }): string | null;
31
+ actionRedirectType: string | null;
32
+ clientCompatibilityId: string | null;
33
+ contentTypeHeader: string | null;
34
+ compatibilityIdHeader: string | null;
35
+ currentHref: string;
36
+ origin: string;
37
+ responseUrl: string | null;
38
+ };
39
+ /**
40
+ * Converts raw browser response data into the narrow facts expected by the
41
+ * navigation planner. This is the single place where redirect-type
42
+ * normalisation and RSC content-type detection happen for server-action
43
+ * compatibility checks.
44
+ */
45
+ declare function createServerActionResultFacts(input: ServerActionResultResponseFactsInput): ServerActionResultFacts;
33
46
  declare function shouldScheduleRefreshForDiscardedServerAction(revalidation: ServerActionRevalidationKind): boolean;
34
47
  declare function createServerActionInitiationSnapshot<TRouterState>(options: {
35
48
  href: string;
@@ -48,4 +61,4 @@ type DiscardedServerActionRefreshSchedulerOptions = {
48
61
  };
49
62
  declare function createDiscardedServerActionRefreshScheduler(options: DiscardedServerActionRefreshSchedulerOptions): DiscardedServerActionRefreshScheduler;
50
63
  //#endregion
51
- export { AppBrowserServerActionResult, ServerActionRevalidationKind, createDiscardedServerActionRefreshScheduler, createServerActionInitiationSnapshot, isServerActionResult, normalizeServerActionThrownValue, parseServerActionRevalidationHeader, readInvalidServerActionResponseError, resolveServerActionRedirectCompatibilityHardNavigationTarget, shouldCheckRscCompatibilityForServerActionResponse, shouldClearClientNavigationCachesForServerActionResult, shouldScheduleRefreshForDiscardedServerAction };
64
+ export { AppBrowserServerActionResult, ServerActionResultResponseFactsInput, ServerActionRevalidationKind, createDiscardedServerActionRefreshScheduler, createServerActionInitiationSnapshot, createServerActionResultFacts, isServerActionResult, normalizeServerActionThrownValue, parseServerActionRevalidationHeader, readInvalidServerActionResponseError, shouldClearClientNavigationCachesForServerActionResult, shouldScheduleRefreshForDiscardedServerAction };