vinext 1.0.0-beta.1 → 1.0.0-beta.2

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 (175) hide show
  1. package/README.md +4 -1
  2. package/dist/build/client-build-config.js +1 -0
  3. package/dist/build/report.d.ts +8 -1
  4. package/dist/build/report.js +12 -2
  5. package/dist/check.js +15 -2
  6. package/dist/cli.js +3 -2
  7. package/dist/config/config-matchers.d.ts +13 -37
  8. package/dist/config/config-matchers.js +31 -121
  9. package/dist/config/next-config.d.ts +3 -2
  10. package/dist/config/next-config.js +3 -1
  11. package/dist/config/request-context.d.ts +14 -0
  12. package/dist/config/request-context.js +26 -0
  13. package/dist/entries/app-rsc-entry.js +27 -9
  14. package/dist/entries/app-rsc-manifest.js +17 -0
  15. package/dist/entries/pages-server-entry.js +3 -2
  16. package/dist/index.js +82 -58
  17. package/dist/init.d.ts +6 -4
  18. package/dist/init.js +20 -22
  19. package/dist/plugins/og-assets.js +1 -1
  20. package/dist/routing/app-route-graph.d.ts +13 -5
  21. package/dist/routing/app-route-graph.js +59 -13
  22. package/dist/routing/app-router.js +12 -6
  23. package/dist/routing/file-matcher.d.ts +11 -22
  24. package/dist/routing/file-matcher.js +21 -33
  25. package/dist/routing/pages-router.js +23 -29
  26. package/dist/routing/route-matching.d.ts +14 -1
  27. package/dist/routing/route-matching.js +13 -1
  28. package/dist/routing/route-pattern.d.ts +2 -1
  29. package/dist/routing/route-pattern.js +7 -4
  30. package/dist/routing/route-trie.d.ts +5 -1
  31. package/dist/routing/route-trie.js +5 -2
  32. package/dist/server/app-browser-entry.js +7 -6
  33. package/dist/server/app-browser-history-controller.d.ts +1 -1
  34. package/dist/server/app-browser-history-controller.js +3 -3
  35. package/dist/server/app-browser-navigation-controller.js +1 -1
  36. package/dist/server/app-browser-state.js +1 -1
  37. package/dist/server/app-browser-visible-commit.js +1 -1
  38. package/dist/server/app-fallback-renderer.d.ts +3 -0
  39. package/dist/server/app-fallback-renderer.js +4 -2
  40. package/dist/server/app-middleware.js +2 -1
  41. package/dist/server/app-page-boundary-render.d.ts +12 -1
  42. package/dist/server/app-page-boundary-render.js +84 -2
  43. package/dist/server/app-page-cache-finalizer.d.ts +1 -1
  44. package/dist/server/app-page-cache.d.ts +1 -1
  45. package/dist/server/app-page-dispatch.d.ts +11 -2
  46. package/dist/server/app-page-dispatch.js +14 -17
  47. package/dist/server/app-page-element-builder.d.ts +7 -2
  48. package/dist/server/app-page-element-builder.js +112 -27
  49. package/dist/server/app-page-head.d.ts +54 -2
  50. package/dist/server/app-page-head.js +164 -78
  51. package/dist/server/app-page-http-access-fallback-metadata.d.ts +29 -0
  52. package/dist/server/app-page-http-access-fallback-metadata.js +106 -0
  53. package/dist/server/app-page-render.d.ts +2 -2
  54. package/dist/server/app-page-render.js +1 -1
  55. package/dist/server/app-page-request.d.ts +12 -0
  56. package/dist/server/app-page-request.js +133 -13
  57. package/dist/server/app-page-route-wiring.d.ts +9 -0
  58. package/dist/server/app-page-route-wiring.js +43 -3
  59. package/dist/server/app-post-middleware-context.d.ts +1 -1
  60. package/dist/server/app-post-middleware-context.js +1 -1
  61. package/dist/server/app-route-handler-dispatch.js +31 -17
  62. package/dist/server/app-route-handler-execution.d.ts +6 -1
  63. package/dist/server/app-route-handler-execution.js +42 -16
  64. package/dist/server/app-route-handler-policy.d.ts +2 -0
  65. package/dist/server/app-route-handler-policy.js +3 -3
  66. package/dist/server/app-route-module-loader.d.ts +3 -1
  67. package/dist/server/app-route-module-loader.js +1 -0
  68. package/dist/server/app-rsc-cache-busting.d.ts +1 -1
  69. package/dist/server/app-rsc-errors.d.ts +1 -1
  70. package/dist/server/app-rsc-errors.js +12 -2
  71. package/dist/server/app-rsc-handler.d.ts +10 -5
  72. package/dist/server/app-rsc-handler.js +52 -24
  73. package/dist/server/app-rsc-request-normalization.d.ts +3 -2
  74. package/dist/server/app-rsc-request-normalization.js +8 -2
  75. package/dist/server/app-rsc-response-finalizer.d.ts +2 -2
  76. package/dist/server/app-rsc-response-finalizer.js +5 -11
  77. package/dist/server/app-rsc-route-matching.d.ts +20 -0
  78. package/dist/server/app-rsc-route-matching.js +87 -11
  79. package/dist/server/app-segment-config.js +13 -3
  80. package/dist/server/app-server-action-execution.d.ts +3 -1
  81. package/dist/server/app-server-action-execution.js +9 -7
  82. package/dist/server/app-ssr-entry.js +2 -2
  83. package/dist/server/app-ssr-stream.d.ts +1 -7
  84. package/dist/server/app-ssr-stream.js +2 -10
  85. package/dist/server/config-headers.d.ts +24 -0
  86. package/dist/server/config-headers.js +52 -0
  87. package/dist/server/dev-initial-server-error.d.ts +1 -1
  88. package/dist/server/dev-server.js +29 -13
  89. package/dist/server/isr-cache.d.ts +1 -1
  90. package/dist/server/middleware-matcher-pattern.d.ts +22 -0
  91. package/dist/server/middleware-matcher-pattern.js +219 -0
  92. package/dist/server/middleware-matcher.d.ts +2 -7
  93. package/dist/server/middleware-matcher.js +13 -60
  94. package/dist/server/middleware-path-to-regexp.d.ts +14 -0
  95. package/dist/server/middleware-path-to-regexp.js +228 -0
  96. package/dist/server/middleware-runtime.js +4 -3
  97. package/dist/server/middleware.d.ts +1 -1
  98. package/dist/server/middleware.js +2 -1
  99. package/dist/server/pages-data-route.d.ts +11 -1
  100. package/dist/server/pages-data-route.js +16 -1
  101. package/dist/server/pages-get-initial-props.d.ts +15 -2
  102. package/dist/server/pages-get-initial-props.js +16 -6
  103. package/dist/server/pages-page-data.d.ts +3 -1
  104. package/dist/server/pages-page-data.js +16 -7
  105. package/dist/server/pages-page-handler.d.ts +4 -2
  106. package/dist/server/pages-page-handler.js +16 -17
  107. package/dist/server/pages-page-response.js +1 -1
  108. package/dist/server/pages-readiness.d.ts +3 -3
  109. package/dist/server/pages-request-pipeline.d.ts +1 -0
  110. package/dist/server/pages-request-pipeline.js +25 -11
  111. package/dist/server/pages-router-entry.js +6 -0
  112. package/dist/server/prerender-route-params.js +2 -2
  113. package/dist/server/prod-server.d.ts +1 -4
  114. package/dist/server/prod-server.js +36 -23
  115. package/dist/server/request-pipeline.d.ts +15 -29
  116. package/dist/server/request-pipeline.js +24 -59
  117. package/dist/server/rsc-stream-hints.js +122 -18
  118. package/dist/shims/app.d.ts +1 -1
  119. package/dist/shims/cache-request-state.d.ts +27 -1
  120. package/dist/shims/cache-request-state.js +60 -1
  121. package/dist/shims/cache-runtime.d.ts +12 -1
  122. package/dist/shims/cache-runtime.js +22 -7
  123. package/dist/shims/cache.d.ts +5 -5
  124. package/dist/shims/cache.js +26 -10
  125. package/dist/shims/document.d.ts +42 -83
  126. package/dist/shims/document.js +62 -53
  127. package/dist/shims/dynamic.d.ts +5 -25
  128. package/dist/shims/dynamic.js +29 -8
  129. package/dist/shims/error.d.ts +1 -1
  130. package/dist/shims/error.js +1 -1
  131. package/dist/shims/fetch-cache.js +12 -3
  132. package/dist/shims/font-google-base.d.ts +11 -4
  133. package/dist/shims/font-local.d.ts +10 -3
  134. package/dist/shims/head.d.ts +13 -2
  135. package/dist/shims/head.js +3 -3
  136. package/dist/shims/headers.d.ts +28 -31
  137. package/dist/shims/headers.js +109 -49
  138. package/dist/shims/image.d.ts +27 -37
  139. package/dist/shims/image.js +21 -14
  140. package/dist/shims/internal/cookie-serialize.d.ts +3 -1
  141. package/dist/shims/internal/cookie-serialize.js +3 -1
  142. package/dist/shims/internal/hybrid-client-route-owner.js +3 -1
  143. package/dist/shims/internal/utils.d.ts +3 -3
  144. package/dist/shims/legacy-image.d.ts +3 -37
  145. package/dist/shims/legacy-image.js +6 -16
  146. package/dist/shims/link.d.ts +15 -22
  147. package/dist/shims/link.js +20 -11
  148. package/dist/shims/navigation-context-state.d.ts +1 -1
  149. package/dist/shims/navigation.d.ts +7 -3
  150. package/dist/shims/navigation.js +24 -13
  151. package/dist/shims/navigation.react-server.js +1 -1
  152. package/dist/shims/next-shims-augmentations.d.ts +1 -0
  153. package/dist/shims/next-shims-public.d.ts +1 -0
  154. package/dist/shims/public-shim-map.json.js +103 -0
  155. package/dist/shims/readonly-url-search-params.d.ts +3 -3
  156. package/dist/shims/readonly-url-search-params.js +3 -3
  157. package/dist/shims/request-state-types.d.ts +1 -1
  158. package/dist/shims/router.d.ts +55 -21
  159. package/dist/shims/router.js +162 -37
  160. package/dist/shims/script.d.ts +6 -10
  161. package/dist/shims/script.js +6 -3
  162. package/dist/shims/server.d.ts +45 -22
  163. package/dist/shims/server.js +44 -5
  164. package/dist/shims/unified-request-context.d.ts +1 -1
  165. package/dist/shims/unified-request-context.js +2 -0
  166. package/dist/shims/url-safety.d.ts +1 -1
  167. package/dist/shims/url-safety.js +2 -2
  168. package/dist/typegen.d.ts +8 -2
  169. package/dist/typegen.js +76 -15
  170. package/dist/utils/domain-locale.d.ts +2 -2
  171. package/dist/utils/external-url.d.ts +5 -0
  172. package/dist/utils/external-url.js +7 -0
  173. package/dist/utils/regex-safety.d.ts +8 -0
  174. package/dist/utils/regex-safety.js +737 -0
  175. package/package.json +13 -2
@@ -1,13 +1,11 @@
1
- import { normalizePathnameForRouteMatch } from "../routing/utils.js";
2
1
  import { hasBasePath, stripBasePath } from "../utils/base-path.js";
3
2
  import "./headers.js";
4
- import { normalizePath } from "./normalize-path.js";
5
- import { applyConfigHeadersToResponse } from "./request-pipeline.js";
6
3
  import { applyCdnResponseHeaders } from "./cache-control.js";
7
4
  import { normalizeDefaultLocalePathname } from "./pages-i18n.js";
8
5
  import { VINEXT_RSC_VARY_HEADER } from "./app-rsc-cache-busting.js";
9
6
  import { mergeVaryHeader } from "./middleware-response-headers.js";
10
7
  //#region src/server/app-rsc-response-finalizer.ts
8
+ const HAS_CONFIG_HEADERS = process.env.__VINEXT_HAS_CONFIG_HEADERS !== "false";
11
9
  /**
12
10
  * Apply App Router response finalization that must happen outside individual
13
11
  * route dispatchers.
@@ -20,7 +18,7 @@ import { mergeVaryHeader } from "./middleware-response-headers.js";
20
18
  * headers that throw on mutation, and Next.js does not apply config headers
21
19
  * to redirects regardless.
22
20
  */
23
- function finalizeAppRscResponse(response, request, options) {
21
+ async function finalizeAppRscResponse(response, request, options) {
24
22
  if (response.status >= 300 && response.status < 400) return response;
25
23
  if (!response.headers.has("x-vinext-static-file")) {
26
24
  const varyHeader = response.headers.get("Vary");
@@ -28,17 +26,13 @@ function finalizeAppRscResponse(response, request, options) {
28
26
  else if (varyHeader !== VINEXT_RSC_VARY_HEADER) mergeVaryHeader(response.headers, VINEXT_RSC_VARY_HEADER);
29
27
  }
30
28
  if (!response.headers.has("Cache-Control")) applyCdnResponseHeaders(response.headers, { cacheControl: "" });
31
- if (!options.configHeaders.length) return response;
29
+ if (!HAS_CONFIG_HEADERS || !options.configHeaders.length) return response;
32
30
  const url = new URL(request.url);
33
- let pathname;
34
- try {
35
- pathname = normalizePath(normalizePathnameForRouteMatch(url.pathname));
36
- } catch {
37
- pathname = url.pathname;
38
- }
31
+ let pathname = url.pathname;
39
32
  const hadBasePath = !options.basePath || hasBasePath(pathname, options.basePath);
40
33
  pathname = stripBasePath(pathname, options.basePath);
41
34
  const matchPathname = options.i18nConfig ? normalizeDefaultLocalePathname(pathname, options.i18nConfig, { hostname: url.hostname }) : pathname;
35
+ const { applyConfigHeadersToResponse } = await import("./config-headers.js");
42
36
  applyConfigHeadersToResponse(response.headers, {
43
37
  configHeaders: options.configHeaders,
44
38
  pathname: matchPathname,
@@ -33,9 +33,13 @@ type AppRscInterceptForMatching = {
33
33
  interceptLayouts: readonly unknown[];
34
34
  interceptLayoutSegments?: readonly (readonly string[])[];
35
35
  interceptBranchSegments?: readonly string[];
36
+ interceptNotFoundBranchSegments?: readonly string[];
36
37
  __loadInterceptLayouts?: readonly (() => Promise<unknown>)[] | null;
37
38
  page: unknown;
38
39
  __pageLoader?: (() => Promise<unknown>) | null;
40
+ notFound?: unknown;
41
+ __loadNotFound?: (() => Promise<unknown>) | null;
42
+ notFoundTreePosition?: number | null;
39
43
  params: readonly string[];
40
44
  };
41
45
  type AppRscSlotForMatching = {
@@ -50,14 +54,20 @@ type AppRscSiblingInterceptForMatching = {
50
54
  interceptLayouts: readonly unknown[];
51
55
  interceptLayoutSegments?: readonly (readonly string[])[];
52
56
  interceptBranchSegments?: readonly string[];
57
+ interceptNotFoundBranchSegments?: readonly string[];
53
58
  __loadInterceptLayouts?: readonly (() => Promise<unknown>)[] | null;
54
59
  page: unknown;
55
60
  __pageLoader?: (() => Promise<unknown>) | null;
61
+ notFound?: unknown;
62
+ __loadNotFound?: (() => Promise<unknown>) | null;
63
+ notFoundTreePosition?: number | null;
56
64
  params: readonly string[];
57
65
  };
58
66
  type AppRscRouteForMatching = {
67
+ __loadRouteHandler?: unknown;
59
68
  pattern: string;
60
69
  patternParts: string[];
70
+ routeHandler?: unknown;
61
71
  slots?: Record<string, AppRscSlotForMatching>;
62
72
  siblingIntercepts?: AppRscSiblingInterceptForMatching[];
63
73
  };
@@ -68,6 +78,8 @@ type AppRscInterceptMatch = AppRscInterceptLookupEntry & {
68
78
  type AppRscInterceptLoadState = {
69
79
  page: unknown;
70
80
  pageLoading: Promise<unknown> | null;
81
+ notFound: unknown;
82
+ notFoundLoading: Promise<unknown> | null;
71
83
  interceptLayoutsLoading: Promise<readonly unknown[]> | null;
72
84
  };
73
85
  type AppRscInterceptLookupEntry = {
@@ -81,9 +93,13 @@ type AppRscInterceptLookupEntry = {
81
93
  interceptLayouts: readonly unknown[];
82
94
  interceptLayoutSegments?: readonly (readonly string[])[];
83
95
  interceptBranchSegments?: readonly string[];
96
+ interceptNotFoundBranchSegments?: readonly string[];
84
97
  __loadInterceptLayouts?: readonly (() => Promise<unknown>)[] | null;
85
98
  page: unknown;
86
99
  __pageLoader?: (() => Promise<unknown>) | null;
100
+ notFound: unknown;
101
+ __loadNotFound?: (() => Promise<unknown>) | null;
102
+ notFoundTreePosition?: number | null;
87
103
  __loadState: AppRscInterceptLoadState;
88
104
  params: readonly string[];
89
105
  slotId: string | null;
@@ -93,6 +109,10 @@ declare function createAppRscRouteMatcher<Route extends AppRscRouteForMatching>(
93
109
  route: Route;
94
110
  params: AppRscRouteParams;
95
111
  } | null;
112
+ matchRequestRoute(url: string): {
113
+ route: Route;
114
+ params: AppRscRouteParams;
115
+ } | null;
96
116
  findIntercept(pathname: string, sourcePathname?: string | null): AppRscInterceptMatch | null;
97
117
  };
98
118
  declare function matchAppRscRoutePattern(urlParts: string[], patternParts: string[]): AppRscRouteParams | null;
@@ -1,6 +1,6 @@
1
- import { splitPathnameForRouteMatch } from "../routing/utils.js";
2
- import { buildRouteTrie, trieMatch } from "../routing/route-trie.js";
3
- import { matchRoutePattern, matchRoutePatternPrefix } from "../routing/route-pattern.js";
1
+ import { decodeMatchedParams, splitPathSegments, splitPathnameForRouteMatch } from "../routing/utils.js";
2
+ import { buildRouteTrie, trieMatchRaw } from "../routing/route-trie.js";
3
+ import { matchRoutePattern, matchRoutePatternPrefix, matchRoutePatternRaw } from "../routing/route-pattern.js";
4
4
  //#region src/server/app-rsc-route-matching.ts
5
5
  /**
6
6
  * Sentinel slot key used for sibling-style interception entries.
@@ -11,9 +11,61 @@ const SIBLING_PAGE_INTERCEPT_SLOT_KEY = "__vinext_page_intercept";
11
11
  function createRouteParams() {
12
12
  return Object.create(null);
13
13
  }
14
- function appRscPathnameParts(pathname) {
14
+ function appRscPathnameParts(pathname, isNormalized = false) {
15
15
  const pathOnly = pathname.split("?")[0];
16
- return splitPathnameForRouteMatch(pathOnly === "/" ? "/" : pathOnly.replace(/\/$/, ""));
16
+ const normalizedPathname = pathOnly === "/" ? "/" : pathOnly.replace(/\/$/, "");
17
+ return isNormalized ? splitPathSegments(normalizedPathname) : splitPathnameForRouteMatch(normalizedPathname);
18
+ }
19
+ function appRscInterceptionSourcePathnameParts(pathname) {
20
+ const pathOnly = pathname.split("?")[0];
21
+ return splitPathSegments(pathOnly === "/" ? "/" : pathOnly.replace(/\/$/, "")).map((segment) => {
22
+ try {
23
+ return decodeURIComponent(segment);
24
+ } catch {
25
+ return segment;
26
+ }
27
+ });
28
+ }
29
+ function canonicalizeAppPageParam(value) {
30
+ try {
31
+ return encodeURIComponent(decodeURIComponent(value));
32
+ } catch {
33
+ return value;
34
+ }
35
+ }
36
+ function canonicalizeAppPageParams(params) {
37
+ for (const key of Object.keys(params)) {
38
+ const value = params[key];
39
+ params[key] = Array.isArray(value) ? value.map(canonicalizeAppPageParam) : canonicalizeAppPageParam(value);
40
+ }
41
+ }
42
+ function isAppRouteHandlerRoute(route) {
43
+ return route.routeHandler != null || typeof route.__loadRouteHandler === "function";
44
+ }
45
+ function normalizeMatchedParamsForRoute(result) {
46
+ if (isAppRouteHandlerRoute(result.route)) decodeMatchedParams(result.params);
47
+ else canonicalizeAppPageParams(result.params);
48
+ }
49
+ function extractRawParamsForMatchedRoute(patternParts, pathnameParts) {
50
+ const params = createRouteParams();
51
+ let pathnameIndex = 0;
52
+ for (const part of patternParts) {
53
+ if (!part.startsWith(":")) {
54
+ pathnameIndex += 1;
55
+ continue;
56
+ }
57
+ const isCatchAll = part.endsWith("+") || part.endsWith("*");
58
+ const paramName = part.slice(1, isCatchAll ? -1 : void 0);
59
+ if (isCatchAll) {
60
+ const remaining = pathnameParts.slice(pathnameIndex);
61
+ if (remaining.length > 0) params[paramName] = [...remaining];
62
+ break;
63
+ }
64
+ const value = pathnameParts[pathnameIndex];
65
+ if (value !== void 0) params[paramName] = value;
66
+ pathnameIndex += 1;
67
+ }
68
+ return params;
17
69
  }
18
70
  function createAppRscRouteMatcher(routes) {
19
71
  const routeTrie = buildRouteTrie(routes);
@@ -21,20 +73,32 @@ function createAppRscRouteMatcher(routes) {
21
73
  const routeIndexes = new Map(routes.map((route, index) => [route, index]));
22
74
  return {
23
75
  matchRoute(url) {
24
- return trieMatch(routeTrie, appRscPathnameParts(url));
76
+ const rawParts = appRscPathnameParts(url, true);
77
+ const result = trieMatchRaw(routeTrie, appRscPathnameParts(url, false));
78
+ if (!result) return null;
79
+ result.params = extractRawParamsForMatchedRoute(result.route.patternParts, rawParts);
80
+ normalizeMatchedParamsForRoute(result);
81
+ return result;
82
+ },
83
+ matchRequestRoute(url) {
84
+ const result = trieMatchRaw(routeTrie, appRscPathnameParts(url, true));
85
+ if (!result) return null;
86
+ normalizeMatchedParamsForRoute(result);
87
+ return result;
25
88
  },
26
89
  findIntercept(pathname, sourcePathname = null) {
27
90
  if (sourcePathname === null) return null;
28
- const urlParts = appRscPathnameParts(pathname);
29
- const sourceParts = appRscPathnameParts(sourcePathname);
30
- const matchedSourceRoute = trieMatch(routeTrie, sourceParts);
91
+ const urlParts = appRscPathnameParts(pathname, true);
92
+ const sourceParts = appRscInterceptionSourcePathnameParts(sourcePathname);
93
+ const matchedSourceRoute = trieMatchRaw(routeTrie, sourceParts);
31
94
  for (const entry of interceptLookup) {
32
95
  if (!matchInterceptSource(sourceParts, entry)) continue;
33
- const params = matchAppRscRoutePattern(urlParts, entry.targetPatternParts);
96
+ const params = matchRoutePatternRaw(urlParts, entry.targetPatternParts);
34
97
  if (params === null) continue;
98
+ canonicalizeAppPageParams(params);
35
99
  const concreteSourceRouteIndex = matchedSourceRoute && entry.sourceMatchPatternParts !== null ? routeIndexes.get(matchedSourceRoute.route) ?? entry.sourceRouteIndex : entry.sourceRouteIndex;
36
100
  const sourceRoute = routes[concreteSourceRouteIndex];
37
- const matchedSourceParams = matchedSourceRoute && entry.sourceMatchPatternParts !== null ? matchedSourceRoute.params : sourceRoute ? matchAppRscRoutePattern(sourceParts, sourceRoute.patternParts) : null;
101
+ const matchedSourceParams = matchedSourceRoute && entry.sourceMatchPatternParts !== null ? matchedSourceRoute.params : sourceRoute ? matchRoutePatternRaw(sourceParts, sourceRoute.patternParts) : null;
38
102
  if (matchedSourceParams === null && entry.sourceMatchPatternParts === null) continue;
39
103
  const sourceParams = matchedSourceParams && entry.sourceMatchPatternParts !== null ? pickPatternParams(matchedSourceParams, entry.sourceMatchPatternParts) : matchedSourceParams ?? createRouteParams();
40
104
  return {
@@ -106,12 +170,18 @@ function createInterceptLookup(routes) {
106
170
  interceptLayouts: intercept.interceptLayouts,
107
171
  interceptLayoutSegments: intercept.interceptLayoutSegments,
108
172
  interceptBranchSegments: intercept.interceptBranchSegments,
173
+ interceptNotFoundBranchSegments: intercept.interceptNotFoundBranchSegments,
109
174
  __loadInterceptLayouts: intercept.__loadInterceptLayouts,
110
175
  page: intercept.page,
111
176
  __pageLoader: intercept.__pageLoader,
177
+ notFound: intercept.notFound,
178
+ __loadNotFound: intercept.__loadNotFound,
179
+ notFoundTreePosition: intercept.notFoundTreePosition,
112
180
  __loadState: {
113
181
  page: intercept.page,
114
182
  pageLoading: null,
183
+ notFound: intercept.notFound,
184
+ notFoundLoading: null,
115
185
  interceptLayoutsLoading: null
116
186
  },
117
187
  params: intercept.params
@@ -133,12 +203,18 @@ function createInterceptLookup(routes) {
133
203
  interceptLayouts: intercept.interceptLayouts,
134
204
  interceptLayoutSegments: intercept.interceptLayoutSegments,
135
205
  interceptBranchSegments: intercept.interceptBranchSegments,
206
+ interceptNotFoundBranchSegments: intercept.interceptNotFoundBranchSegments,
136
207
  __loadInterceptLayouts: intercept.__loadInterceptLayouts,
137
208
  page: intercept.page,
138
209
  __pageLoader: intercept.__pageLoader,
210
+ notFound: intercept.notFound,
211
+ __loadNotFound: intercept.__loadNotFound,
212
+ notFoundTreePosition: intercept.notFoundTreePosition,
139
213
  __loadState: {
140
214
  page: intercept.page,
141
215
  pageLoading: null,
216
+ notFound: intercept.notFound,
217
+ notFoundLoading: null,
142
218
  interceptLayoutsLoading: null
143
219
  },
144
220
  params: intercept.params
@@ -40,6 +40,15 @@ function resolveDynamicStaleTimeSeconds(current, value) {
40
40
  function isDynamicSegment(segment) {
41
41
  return segment.startsWith("[") && segment.endsWith("]");
42
42
  }
43
+ function resolveSegmentConfigOwnerPosition(routeSegments, treePosition) {
44
+ let ownerPosition = Math.min(treePosition - 1, routeSegments.length - 1);
45
+ while (ownerPosition >= 0) {
46
+ const segment = routeSegments[ownerPosition];
47
+ if (!segment.startsWith("@") && !(segment.startsWith("(") && segment.endsWith(")"))) break;
48
+ ownerPosition -= 1;
49
+ }
50
+ return ownerPosition;
51
+ }
43
52
  function getParallelSegments(options) {
44
53
  if (!options.parallelBranches) return options.parallelSegments ?? [];
45
54
  return options.parallelBranches.flatMap((branch) => branch ? [
@@ -59,8 +68,9 @@ function resolveDynamicParamsConfig(options) {
59
68
  for (const segment of segments) if (segment?.dynamicParams === false) dynamicParamsConfig = false;
60
69
  else if (segment?.dynamicParams === true && dynamicParamsConfig !== false) dynamicParamsConfig = true;
61
70
  if (dynamicParamsConfig !== false || !options.routeSegments) return dynamicParamsConfig;
71
+ const routeSegments = options.routeSegments;
62
72
  let lastDynamicPosition = -1;
63
- for (let index = options.routeSegments.length - 1; index >= 0; index--) if (isDynamicSegment(options.routeSegments[index])) {
73
+ for (let index = routeSegments.length - 1; index >= 0; index--) if (isDynamicSegment(routeSegments[index])) {
64
74
  lastDynamicPosition = index;
65
75
  break;
66
76
  }
@@ -70,7 +80,7 @@ function resolveDynamicParamsConfig(options) {
70
80
  let lastDynamicSegmentIsStaticOnly = false;
71
81
  let lastDynamicSegmentHasStaticParams = false;
72
82
  layouts.forEach((layout, index) => {
73
- if ((layoutPositions[index] ?? 0) - 1 !== lastDynamicPosition) return;
83
+ if (resolveSegmentConfigOwnerPosition(routeSegments, layoutPositions[index] ?? 0) !== lastDynamicPosition) return;
74
84
  if (layout?.dynamicParams === false) lastDynamicSegmentIsStaticOnly = true;
75
85
  if (typeof layout?.generateStaticParams === "function") lastDynamicSegmentHasStaticParams = true;
76
86
  });
@@ -78,7 +88,7 @@ function resolveDynamicParamsConfig(options) {
78
88
  if (typeof options.page?.generateStaticParams === "function") lastDynamicSegmentHasStaticParams = true;
79
89
  for (const branch of options.parallelBranches ?? []) {
80
90
  if (!branch) continue;
81
- const branchStartPosition = options.routeSegments.length - (branch.routeSegments?.length ?? 0);
91
+ const branchStartPosition = routeSegments.length - (branch.routeSegments?.length ?? 0);
82
92
  const checkSegment = (segment, ownerPosition) => {
83
93
  if (ownerPosition !== lastDynamicPosition) return;
84
94
  if (segment?.dynamicParams === false) lastDynamicSegmentIsStaticOnly = true;
@@ -146,7 +146,9 @@ type HandleServerActionRscRequestOptions<TElement, TRoute extends AppServerActio
146
146
  buildPageElement: (options: BuildServerActionPageElementOptions<TRoute, TInterceptOpts>) => TElement;
147
147
  cleanPathname: string;
148
148
  clearRequestContext: () => void;
149
- contentType: string;
149
+ contentType: string; /** Route selected at the request boundary before action execution. */
150
+ currentRouteMatch: AppServerActionMatch<TRoute> | null; /** Request-aware pathname identity used for current-route interception lookup. */
151
+ currentRoutePathname: string;
150
152
  createNotFoundElement: (routeId: string) => TElement;
151
153
  createPayloadRouteId: (pathname: string, interceptionContext: string | null) => string;
152
154
  createRscOnErrorHandler: (request: Request, pathname: string, pattern: string) => (error: unknown) => unknown;
@@ -1,25 +1,25 @@
1
1
  import { splitPathSegments } from "../routing/utils.js";
2
2
  import { addBasePathToPathname, hasBasePath, stripBasePath } from "../utils/base-path.js";
3
3
  import { ACTION_FORWARDED_HEADER, ACTION_REDIRECT_HEADER, ACTION_REDIRECT_STATUS_HEADER, ACTION_REDIRECT_TYPE_HEADER, ACTION_REVALIDATED_HEADER } from "./headers.js";
4
- import { isExternalUrl } from "../config/config-matchers.js";
4
+ import { isExternalUrl } from "../utils/external-url.js";
5
5
  import { internalServerErrorResponse, payloadTooLargeResponse } from "./http-error-responses.js";
6
6
  import { validateCsrfOrigin, validateServerActionPayload } from "./request-pipeline.js";
7
7
  import { APP_RSC_RENDER_MODE_NAVIGATION } from "./app-rsc-render-mode.js";
8
8
  import { headersContextFromRequest, isDraftModeRequest, setHeadersContext } from "../shims/headers.js";
9
- import { getAndClearActionRevalidationKind } from "../shims/cache-request-state.js";
9
+ import { _drainPendingRevalidations, getAndClearActionRevalidationKind } from "../shims/cache-request-state.js";
10
10
  import { setCurrentFetchCacheMode, setCurrentFetchSoftTags, setCurrentForceDynamicFetchDefault } from "../shims/fetch-cache.js";
11
11
  import { readStreamAsTextWithLimit } from "../utils/text-stream.js";
12
12
  import { VINEXT_RSC_CONTENT_TYPE, VINEXT_RSC_VARY_HEADER, applyRscCompatibilityIdHeader } from "./app-rsc-cache-busting.js";
13
13
  import { mergeMiddlewareResponseHeaders } from "./middleware-response-headers.js";
14
14
  import { applyEdgeRuntimeHeader } from "./app-page-response.js";
15
15
  import { getNextErrorDigest, parseNextHttpErrorDigest, parseNextRedirectDigest } from "./next-error-digest.js";
16
- import { createRootParamsUsageController, pickRootParams, runWithRootParamsScope, runWithRootParamsUsage } from "../shims/root-params.js";
17
16
  import { createServerActionNotFoundResponse, getServerActionNotFoundMessage, isServerActionNotFoundError } from "./server-action-not-found.js";
17
+ import { resolveAppPageNavigationParams } from "./app-page-element-builder.js";
18
18
  import { deferUntilStreamConsumed } from "./defer-until-stream-consumed.js";
19
19
  import "./app-page-stream.js";
20
20
  import { buildAppPageTags } from "./implicit-tags.js";
21
- import { resolveAppPageNavigationParams } from "./app-page-element-builder.js";
22
21
  import { resolveAppPageActionRerenderTarget } from "./app-page-request.js";
22
+ import { createRootParamsUsageController, pickRootParams, runWithRootParamsScope, runWithRootParamsUsage } from "../shims/root-params.js";
23
23
  import { getSetCookieName } from "./cookie-utils.js";
24
24
  import { createStaticGenerationHeadersContext } from "./app-static-generation.js";
25
25
  //#region src/server/app-server-action-execution.ts
@@ -403,6 +403,7 @@ async function handleProgressiveServerActionRequest(options) {
403
403
  options.setHeadersAccessPhase(previousHeadersPhase);
404
404
  if (actionThrew) rootParamsUsage.transitionToRender();
405
405
  }
406
+ await _drainPendingRevalidations();
406
407
  if (!actionRedirect) {
407
408
  if (!actionThrew) rootParamsUsage.transitionToRender();
408
409
  const actionPendingCookies = dedupePendingCookies(options.getAndClearPendingCookies());
@@ -612,6 +613,7 @@ async function handleServerActionRscRequest(options) {
612
613
  options.setHeadersAccessPhase(previousHeadersPhase);
613
614
  if (actionThrew && !actionWasForwarded) rootParamsUsage.transitionToRender();
614
615
  }
616
+ await _drainPendingRevalidations();
615
617
  if (actionRedirect) {
616
618
  const actionPendingCookies = dedupePendingCookies(options.getAndClearPendingCookies());
617
619
  const actionDraftCookie = options.getDraftModeCookieHeader();
@@ -648,7 +650,7 @@ async function handleServerActionRscRequest(options) {
648
650
  headers: withoutRscBodyHeaders(redirectHeaders)
649
651
  });
650
652
  }
651
- const currentMatch = options.matchRoute(options.cleanPathname);
653
+ const currentMatch = options.currentRouteMatch;
652
654
  if (currentMatch) await options.ensureRouteLoaded?.(currentMatch.route);
653
655
  const redirectRenderRequest = createActionRedirectRenderRequest({
654
656
  pendingCookies: [...actionPendingCookies, ...actionDraftCookie ? [actionDraftCookie] : []],
@@ -734,14 +736,14 @@ async function handleServerActionRscRequest(options) {
734
736
  }, options.clearRequestContext);
735
737
  }
736
738
  if (!actionThrew) rootParamsUsage.transitionToRender();
737
- const match = options.matchRoute(options.cleanPathname);
739
+ const match = options.currentRouteMatch;
738
740
  let element;
739
741
  let errorPattern = match ? match.route.pattern : options.cleanPathname;
740
742
  const actionRerenderIsRscRequest = true;
741
743
  if (match) {
742
744
  const { route: actionRoute, params: actionParams } = match;
743
745
  const actionRerenderTarget = await resolveAppPageActionRerenderTarget({
744
- cleanPathname: options.cleanPathname,
746
+ cleanPathname: options.currentRoutePathname,
745
747
  currentParams: actionParams,
746
748
  currentRoute: actionRoute,
747
749
  findIntercept: options.findIntercept,
@@ -17,12 +17,12 @@ import DefaultGlobalError from "../shims/default-global-error.js";
17
17
  import { BfcacheStateKeyMapContext, ElementsContext, Slot } from "../shims/slot.js";
18
18
  import { createSsrErrorMetaRenderer } from "./app-ssr-error-meta.js";
19
19
  import { createNavigationRuntimeRscMetadataScript, createRscEmbedTransform, createTickBufferedTransform, waitAtLeastOneReactRenderTask } from "./app-ssr-stream.js";
20
- import { BeforeInteractiveContext } from "../shims/before-interactive-context.js";
21
- import { runWithRootParamsScope } from "../shims/root-params.js";
22
20
  import { createInitialBfcacheMaps } from "./app-bfcache-identity.js";
23
21
  import { RSC_FORM_STATE_GLOBAL } from "./app-browser-hydration.js";
24
22
  import { createClientReferencePreloader } from "./app-client-reference-preloader.js";
25
23
  import { deferUntilStreamConsumed } from "./defer-until-stream-consumed.js";
24
+ import { runWithRootParamsScope } from "../shims/root-params.js";
25
+ import { BeforeInteractiveContext } from "../shims/before-interactive-context.js";
26
26
  import { renderBeforeInteractiveInlineScripts } from "./before-interactive-head.js";
27
27
  import { createInitialDevServerErrorScript } from "./dev-initial-server-error.js";
28
28
  import { ssrAppRouterInstance } from "./app-ssr-router-instance.js";
@@ -16,12 +16,6 @@ declare function createNavigationRuntimeRscMetadataScript(params: Record<string,
16
16
  pathname: string;
17
17
  searchParams: [string, string][];
18
18
  }, dynamicStaleTimeSeconds?: number): string;
19
- /**
20
- * Fix invalid preload "as" values in RSC Flight hint lines before they reach
21
- * the client. React Flight emits HL hints with as="stylesheet" for CSS, but
22
- * the HTML spec requires as="style" for <link rel="preload">.
23
- */
24
- declare function fixFlightHints(text: string): string;
25
19
  /**
26
20
  * Create a helper that progressively embeds RSC chunks as inline <script> tags.
27
21
  * The browser entry turns the embedded chunks back into Uint8Array data.
@@ -62,4 +56,4 @@ declare function fixPreloadAs(html: string): string;
62
56
  */
63
57
  declare function createTickBufferedTransform(rscEmbed: RscEmbedTransform, injectHTML?: HtmlInsertion, injectAfterHeadOpenHTML?: HtmlInsertion, inlineCssManifest?: InlineCssManifest, inlineCssPrependCss?: string, inlineCssPrependFallbackHTML?: string, inlineCssScriptNonce?: string): TransformStream<Uint8Array, Uint8Array>;
64
58
  //#endregion
65
- export { InitialNavigationCacheMetadata, createNavigationRuntimeRscMetadataScript, createRscEmbedTransform, createTickBufferedTransform, fixFlightHints, fixPreloadAs, navigationRuntimeRscBootstrapExpression, waitAtLeastOneReactRenderTask };
59
+ export { InitialNavigationCacheMetadata, createNavigationRuntimeRscMetadataScript, createRscEmbedTransform, createTickBufferedTransform, fixPreloadAs, navigationRuntimeRscBootstrapExpression, waitAtLeastOneReactRenderTask };
@@ -23,14 +23,6 @@ function createNavigationRuntimeRscDoneScript(metadata) {
23
23
  }) + ");") + bootstrap + ".done=true";
24
24
  }
25
25
  /**
26
- * Fix invalid preload "as" values in RSC Flight hint lines before they reach
27
- * the client. React Flight emits HL hints with as="stylesheet" for CSS, but
28
- * the HTML spec requires as="style" for <link rel="preload">.
29
- */
30
- function fixFlightHints(text) {
31
- return text.replace(/(\d*:HL\[.*?),"stylesheet"(\]|,)/g, "$1,\"style\"$2");
32
- }
33
- /**
34
26
  * Create a helper that progressively embeds RSC chunks as inline <script> tags.
35
27
  * The browser entry turns the embedded chunks back into Uint8Array data.
36
28
  */
@@ -49,7 +41,7 @@ function createRscEmbedTransform(embedStream, scriptNonce, getInitialNavigationC
49
41
  rawChunks.push(result.value);
50
42
  try {
51
43
  const text = new TextDecoder("utf-8", { fatal: true }).decode(result.value);
52
- pendingChunks.push(fixFlightHints(text));
44
+ pendingChunks.push(text);
53
45
  } catch {
54
46
  pendingChunks.push([3, bytesToBase64(result.value)]);
55
47
  }
@@ -412,4 +404,4 @@ function createTickBufferedTransform(rscEmbed, injectHTML = "", injectAfterHeadO
412
404
  });
413
405
  }
414
406
  //#endregion
415
- export { createNavigationRuntimeRscMetadataScript, createRscEmbedTransform, createTickBufferedTransform, fixFlightHints, fixPreloadAs, navigationRuntimeRscBootstrapExpression, waitAtLeastOneReactRenderTask };
407
+ export { createNavigationRuntimeRscMetadataScript, createRscEmbedTransform, createTickBufferedTransform, fixPreloadAs, navigationRuntimeRscBootstrapExpression, waitAtLeastOneReactRenderTask };
@@ -0,0 +1,24 @@
1
+ import { NextHeader } from "../config/next-config.js";
2
+ import { RequestContext } from "../config/request-context.js";
3
+ import { BasePathMatchState } from "../config/config-matchers.js";
4
+ import { HeaderRecord } from "./request-pipeline.js";
5
+
6
+ //#region src/server/config-headers.d.ts
7
+ type ApplyConfigHeadersOptions = {
8
+ configHeaders: NextHeader[];
9
+ pathname: string;
10
+ requestContext: RequestContext;
11
+ /**
12
+ * basePath gating state. When omitted, every rule is treated as a default
13
+ * (basePath: true) rule for backward compatibility — callers that need to
14
+ * support `basePath: false` headers must pass this in.
15
+ */
16
+ basePathState?: BasePathMatchState; /** Existing framework-generated headers that matching config rules may replace. */
17
+ overwriteExisting?: ReadonlySet<string>;
18
+ };
19
+ /** Apply matched next.config.js headers to a Web Headers object. */
20
+ declare function applyConfigHeadersToResponse(responseHeaders: Headers, options: ApplyConfigHeadersOptions): void;
21
+ /** Apply matched next.config.js headers to an early response header record. */
22
+ declare function applyConfigHeadersToHeaderRecord(headers: HeaderRecord, options: ApplyConfigHeadersOptions): void;
23
+ //#endregion
24
+ export { applyConfigHeadersToHeaderRecord, applyConfigHeadersToResponse };
@@ -0,0 +1,52 @@
1
+ import { matchHeaders } from "../config/config-matchers.js";
2
+ //#region src/server/config-headers.ts
3
+ function findHeaderRecordKey(headers, lowerName) {
4
+ for (const key of Object.keys(headers)) if (key.toLowerCase() === lowerName) return key;
5
+ }
6
+ function appendHeaderRecord(headers, lowerName, value) {
7
+ const key = findHeaderRecordKey(headers, lowerName) ?? lowerName;
8
+ const existing = headers[key];
9
+ if (existing === void 0) {
10
+ headers[key] = value;
11
+ return;
12
+ }
13
+ if (Array.isArray(existing)) {
14
+ existing.push(value);
15
+ return;
16
+ }
17
+ headers[key] = [existing, value];
18
+ }
19
+ function appendVaryHeaderRecord(headers, value) {
20
+ const key = findHeaderRecordKey(headers, "vary") ?? "vary";
21
+ const existing = headers[key];
22
+ if (existing === void 0) {
23
+ headers[key] = value;
24
+ return;
25
+ }
26
+ if (Array.isArray(existing)) {
27
+ existing.push(value);
28
+ return;
29
+ }
30
+ headers[key] = existing + ", " + value;
31
+ }
32
+ /** Apply matched next.config.js headers to a Web Headers object. */
33
+ function applyConfigHeadersToResponse(responseHeaders, options) {
34
+ const matched = matchHeaders(options.pathname, options.configHeaders, options.requestContext, options.basePathState);
35
+ for (const header of matched) {
36
+ const lowerName = header.key.toLowerCase();
37
+ if (lowerName === "vary" || lowerName === "set-cookie") responseHeaders.append(header.key, header.value);
38
+ else if (options.overwriteExisting?.has(lowerName) || !responseHeaders.has(lowerName)) responseHeaders.set(header.key, header.value);
39
+ }
40
+ }
41
+ /** Apply matched next.config.js headers to an early response header record. */
42
+ function applyConfigHeadersToHeaderRecord(headers, options) {
43
+ const matched = matchHeaders(options.pathname, options.configHeaders, options.requestContext, options.basePathState);
44
+ for (const header of matched) {
45
+ const lowerName = header.key.toLowerCase();
46
+ if (lowerName === "set-cookie") appendHeaderRecord(headers, lowerName, header.value);
47
+ else if (lowerName === "vary") appendVaryHeaderRecord(headers, header.value);
48
+ else if (findHeaderRecordKey(headers, lowerName) === void 0) headers[lowerName] = header.value;
49
+ }
50
+ }
51
+ //#endregion
52
+ export { applyConfigHeadersToHeaderRecord, applyConfigHeadersToResponse };
@@ -4,6 +4,6 @@ type InitialDevServerErrorPayload = {
4
4
  name?: string;
5
5
  stack?: string;
6
6
  };
7
- declare function createInitialDevServerErrorScript(error: unknown, scriptNonce?: string, nodeEnv?: string | undefined): string;
7
+ declare function createInitialDevServerErrorScript(error: unknown, scriptNonce?: string, nodeEnv?: "development" | "production" | "test"): string;
8
8
  //#endregion
9
9
  export { InitialDevServerErrorPayload, createInitialDevServerErrorScript };