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,10 +1,13 @@
1
1
  import { addBasePathToPathname, hasBasePath } from "../utils/base-path.js";
2
2
  import { patternToNextFormat } from "../routing/route-validation.js";
3
- import { applyMiddlewareRequestHeaders, isExternalUrl, matchRedirect, matchRewrite, preserveRedirectDestinationQuery, proxyExternalRequest, requestContextFromRequest, sanitizeDestination } from "../config/config-matchers.js";
4
- import { applyConfigHeadersToHeaderRecord, cloneRequestWithUrl, normalizeTrailingSlash } from "./request-pipeline.js";
3
+ import { requestContextFromRequest } from "../config/request-context.js";
4
+ import { isExternalUrl } from "../utils/external-url.js";
5
+ import { applyMiddlewareRequestHeaders, matchRedirect, matchRewrite, preserveRedirectDestinationQuery, proxyExternalRequest, sanitizeDestination } from "../config/config-matchers.js";
6
+ import { cloneRequestWithUrl, normalizeTrailingSlash } from "./request-pipeline.js";
5
7
  import { mergeRewriteQuery } from "../utils/query.js";
6
8
  import { normalizeDefaultLocalePathname, stripI18nLocaleForApiRoute } from "./pages-i18n.js";
7
9
  import { buildMiddlewarePrefetchSkipResponse } from "./pages-data-route.js";
10
+ import { applyConfigHeadersToHeaderRecord } from "./config-headers.js";
8
11
  import { mergeHeaders } from "./worker-utils.js";
9
12
  //#region src/server/pages-request-pipeline.ts
10
13
  async function fetchWorkerFilesystemRoute(request, requestPathname, phase, fetchAsset) {
@@ -53,6 +56,7 @@ async function runPagesRequest(request, deps) {
53
56
  const url = new URL(request.url);
54
57
  let pathname = url.pathname;
55
58
  const search = url.search;
59
+ const requestConfigPathname = deps.configMatchPathname ?? pathname;
56
60
  const basePathState = {
57
61
  basePath,
58
62
  hadBasePath
@@ -66,9 +70,9 @@ async function runPagesRequest(request, deps) {
66
70
  }
67
71
  const reqCtx = requestContextFromRequest(request);
68
72
  const requestHostname = i18nConfig ? url.hostname : "";
69
- const matchPathname = i18nConfig ? normalizeDefaultLocalePathname(pathname, i18nConfig, { hostname: requestHostname }) : pathname;
73
+ const requestConfigMatchPathname = i18nConfig ? normalizeDefaultLocalePathname(requestConfigPathname, i18nConfig, { hostname: requestHostname }) : requestConfigPathname;
70
74
  if (configRedirects.length) {
71
- const redirect = matchRedirect(matchPathname, configRedirects, reqCtx, basePathState);
75
+ const redirect = matchRedirect(requestConfigMatchPathname, configRedirects, reqCtx, basePathState);
72
76
  if (redirect) {
73
77
  const location = preserveRedirectDestinationQuery(sanitizeDestination(basePath && hadBasePath && !isExternalUrl(redirect.destination) && !hasBasePath(redirect.destination, basePath) ? basePath + redirect.destination : redirect.destination), deps.rawSearch ?? search);
74
78
  return {
@@ -82,6 +86,7 @@ async function runPagesRequest(request, deps) {
82
86
  }
83
87
  const originalResolvedUrl = pathname + search;
84
88
  let resolvedUrl = originalResolvedUrl;
89
+ let resolvedPathnameIsRequestPathname = true;
85
90
  const middlewareHeaders = {};
86
91
  let middlewareStatus;
87
92
  const serveFilesystemRoute = async (requestPathname, phase) => {
@@ -131,7 +136,10 @@ async function runPagesRequest(request, deps) {
131
136
  else if (existing) middlewareHeaders[key] = [existing, value];
132
137
  else middlewareHeaders[key] = [value];
133
138
  } else middlewareHeaders[key] = value;
134
- if (result.rewriteUrl) resolvedUrl = result.rewriteUrl;
139
+ if (result.rewriteUrl) {
140
+ resolvedUrl = result.rewriteUrl;
141
+ resolvedPathnameIsRequestPathname = false;
142
+ }
135
143
  middlewareStatus = result.status ?? result.rewriteStatus;
136
144
  }
137
145
  const { postMwReqCtx, request: postMwReq } = applyMiddlewareRequestHeaders(middlewareHeaders, request, { preserveCredentialHeaders: isExternalUrl(resolvedUrl) });
@@ -143,6 +151,7 @@ async function runPagesRequest(request, deps) {
143
151
  });
144
152
  let resolvedPathname = pathnameForResolvedUrl(resolvedUrl);
145
153
  const matchResolvedPathname = (p) => i18nConfig ? normalizeDefaultLocalePathname(p, i18nConfig, { hostname: requestHostname }) : p;
154
+ const configSourcePathname = () => resolvedPathnameIsRequestPathname ? requestConfigMatchPathname : matchResolvedPathname(resolvedPathname);
146
155
  const matchedPathnameForRoute = (routePattern) => {
147
156
  const matchedPathname = routePattern ? patternToNextFormat(routePattern) : resolvedPathname;
148
157
  if (!i18nConfig) return matchedPathname;
@@ -161,7 +170,7 @@ async function runPagesRequest(request, deps) {
161
170
  };
162
171
  if (configHeaders.length) applyConfigHeadersToHeaderRecord(middlewareHeaders, {
163
172
  configHeaders,
164
- pathname: matchPathname,
173
+ pathname: requestConfigMatchPathname,
165
174
  requestContext: reqCtx,
166
175
  basePathState
167
176
  });
@@ -173,7 +182,7 @@ async function runPagesRequest(request, deps) {
173
182
  if (directFilesystemResult) return directFilesystemResult;
174
183
  let configRewriteFired = false;
175
184
  for (const rewrite of configRewrites.beforeFiles ?? []) {
176
- const rewritten = matchRewrite(matchResolvedPathname(resolvedPathname), [rewrite], rewriteRequestContext(), basePathState);
185
+ const rewritten = matchRewrite(configSourcePathname(), [rewrite], rewriteRequestContext(), basePathState);
177
186
  if (rewritten) {
178
187
  if (isExternalUrl(rewritten)) return {
179
188
  type: "response",
@@ -181,6 +190,7 @@ async function runPagesRequest(request, deps) {
181
190
  };
182
191
  resolvedUrl = mergeRewriteQuery(resolvedUrl, rewritten);
183
192
  resolvedPathname = pathnameForResolvedUrl(resolvedUrl);
193
+ resolvedPathnameIsRequestPathname = false;
184
194
  configRewriteFired = true;
185
195
  }
186
196
  }
@@ -227,7 +237,7 @@ async function runPagesRequest(request, deps) {
227
237
  let pageMatch = !isOutsideBasePathUnclaimed() && deps.matchPageRoute ? deps.matchPageRoute(resolvedPathname, request) : null;
228
238
  let resolvedPathnameChanged = false;
229
239
  if (!pageMatch || pageMatch.route.isDynamic) for (const rewrite of configRewrites.afterFiles ?? []) {
230
- const rewritten = matchRewrite(matchResolvedPathname(resolvedPathname), [rewrite], rewriteRequestContext(), basePathState);
240
+ const rewritten = matchRewrite(configSourcePathname(), [rewrite], rewriteRequestContext(), basePathState);
231
241
  if (rewritten) {
232
242
  if (isExternalUrl(rewritten)) return {
233
243
  type: "response",
@@ -235,6 +245,7 @@ async function runPagesRequest(request, deps) {
235
245
  };
236
246
  resolvedUrl = mergeRewriteQuery(resolvedUrl, rewritten);
237
247
  resolvedPathname = pathnameForResolvedUrl(resolvedUrl);
248
+ resolvedPathnameIsRequestPathname = false;
238
249
  configRewriteFired = true;
239
250
  resolvedPathnameChanged = true;
240
251
  const afterFilesFilesystemResult = await serveFilesystemRoute(resolvedPathname, "afterFiles");
@@ -253,7 +264,7 @@ async function runPagesRequest(request, deps) {
253
264
  if (typeof deps.renderPage === "function") {
254
265
  let renderPageMatch = pageMatch;
255
266
  if ((isOutsideBasePathUnclaimed() || isDataReq || isDataRequest) && !renderPageMatch && configRewrites.fallback?.length) for (const rewrite of configRewrites.fallback) {
256
- const fallbackRewrite = matchRewrite(matchResolvedPathname(resolvedPathname), [rewrite], rewriteRequestContext(), basePathState);
267
+ const fallbackRewrite = matchRewrite(configSourcePathname(), [rewrite], rewriteRequestContext(), basePathState);
257
268
  if (!fallbackRewrite) continue;
258
269
  if (isExternalUrl(fallbackRewrite)) return {
259
270
  type: "response",
@@ -261,6 +272,7 @@ async function runPagesRequest(request, deps) {
261
272
  };
262
273
  resolvedUrl = mergeRewriteQuery(resolvedUrl, fallbackRewrite);
263
274
  resolvedPathname = pathnameForResolvedUrl(resolvedUrl);
275
+ resolvedPathnameIsRequestPathname = false;
264
276
  configRewriteFired = true;
265
277
  const fallbackFilesystemResult = await serveFilesystemRoute(resolvedPathname, "fallback");
266
278
  if (fallbackFilesystemResult) return fallbackFilesystemResult;
@@ -281,7 +293,7 @@ async function runPagesRequest(request, deps) {
281
293
  let response = await deps.renderPage(request, resolvedUrl, initialRenderOptions, stagedHeaders);
282
294
  let matchedFallbackRewrite = false;
283
295
  if (response.status === 404 && shouldDeferErrorPageOnMiss && configRewrites.fallback?.length) for (const rewrite of configRewrites.fallback) {
284
- const fallbackRewrite = matchRewrite(matchResolvedPathname(resolvedPathname), [rewrite], rewriteRequestContext(), basePathState);
296
+ const fallbackRewrite = matchRewrite(configSourcePathname(), [rewrite], rewriteRequestContext(), basePathState);
285
297
  if (!fallbackRewrite) continue;
286
298
  if (isExternalUrl(fallbackRewrite)) return {
287
299
  type: "response",
@@ -289,6 +301,7 @@ async function runPagesRequest(request, deps) {
289
301
  };
290
302
  resolvedUrl = mergeRewriteQuery(resolvedUrl, fallbackRewrite);
291
303
  resolvedPathname = pathnameForResolvedUrl(resolvedUrl);
304
+ resolvedPathnameIsRequestPathname = false;
292
305
  configRewriteFired = true;
293
306
  const fallbackFilesystemResult = await serveFilesystemRoute(resolvedPathname, "fallback");
294
307
  if (fallbackFilesystemResult) return fallbackFilesystemResult;
@@ -325,7 +338,7 @@ async function runPagesRequest(request, deps) {
325
338
  }
326
339
  let devPageMatch = isOutsideBasePathUnclaimed() ? null : resolvedPathnameChanged ? deps.matchPageRoute ? deps.matchPageRoute(resolvedPathname, request) : null : pageMatch;
327
340
  if (!devPageMatch && configRewrites.fallback?.length) for (const rewrite of configRewrites.fallback) {
328
- const fallbackRewrite = matchRewrite(matchResolvedPathname(resolvedPathname), [rewrite], rewriteRequestContext(), basePathState);
341
+ const fallbackRewrite = matchRewrite(configSourcePathname(), [rewrite], rewriteRequestContext(), basePathState);
329
342
  if (!fallbackRewrite) continue;
330
343
  if (isExternalUrl(fallbackRewrite)) return {
331
344
  type: "response",
@@ -333,6 +346,7 @@ async function runPagesRequest(request, deps) {
333
346
  };
334
347
  resolvedUrl = mergeRewriteQuery(resolvedUrl, fallbackRewrite);
335
348
  resolvedPathname = pathnameForResolvedUrl(resolvedUrl);
349
+ resolvedPathnameIsRequestPathname = false;
336
350
  configRewriteFired = true;
337
351
  const fallbackFilesystemResult = await serveFilesystemRoute(resolvedPathname, "fallback");
338
352
  if (fallbackFilesystemResult) return fallbackFilesystemResult;
@@ -1,3 +1,4 @@
1
+ import { normalizePathnameForRouteMatchStrict } from "../routing/utils.js";
1
2
  import { hasBasePath, stripBasePath } from "../utils/base-path.js";
2
3
  import { notFoundStaticAssetResponse } from "./http-error-responses.js";
3
4
  import { isOpenRedirectShaped } from "./open-redirect.js";
@@ -52,6 +53,11 @@ async function handleRequest(request, env, ctx) {
52
53
  try {
53
54
  let pathname = new URL(request.url).pathname;
54
55
  if (isOpenRedirectShaped(pathname)) return new Response("This page could not be found", { status: 404 });
56
+ try {
57
+ normalizePathnameForRouteMatchStrict(pathname);
58
+ } catch {
59
+ return new Response("Bad Request", { status: 400 });
60
+ }
55
61
  const missingBuildAsset = isNextStaticPath(pathname, basePath, assetPathPrefix);
56
62
  request = cloneRequestWithHeaders(request, filterInternalHeaders(request.headers));
57
63
  const hadBasePath = !basePath || hasBasePath(pathname, basePath);
@@ -58,11 +58,11 @@ function decodedPrerenderRouteParamEquals(prerenderValue, matchedValue) {
58
58
  if (prerenderValue.length !== matchedValue.length) return false;
59
59
  return prerenderValue.every((item, index) => {
60
60
  const decoded = decodePrerenderRouteParam(item);
61
- return decoded !== null && decoded === matchedValue[index];
61
+ return item === matchedValue[index] || decoded !== null && decoded === matchedValue[index];
62
62
  });
63
63
  }
64
64
  const decoded = decodePrerenderRouteParam(prerenderValue);
65
- return decoded !== null && decoded === matchedValue;
65
+ return prerenderValue === matchedValue || decoded !== null && decoded === matchedValue;
66
66
  }
67
67
  function prerenderRouteParamsPayloadMatchesRoute(payload, routePattern, params) {
68
68
  return matchPrerenderRouteParamsPayload(payload, routePattern, params)?.kind === "exact";
@@ -101,10 +101,7 @@ declare function tryServeStatic(req: IncomingMessage, res: ServerResponse, clien
101
101
  * Convert a Node.js IncomingMessage to a Web Request object.
102
102
  *
103
103
  * When `urlOverride` is provided, it is used as the path + query string
104
- * instead of `req.url`. This avoids redundant path normalization when the
105
- * caller has already decoded and normalized the pathname (e.g. the App
106
- * Router prod server normalizes before static-asset lookup, and can pass
107
- * the result here so the downstream RSC handler doesn't re-normalize).
104
+ * instead of `req.url`.
108
105
  */
109
106
  declare function nodeToWebRequest(req: IncomingMessage, urlOverride?: string, prerenderSecret?: string): Request;
110
107
  /**
@@ -6,9 +6,9 @@ import { VINEXT_STATIC_FILE_HEADER } from "./headers.js";
6
6
  import { normalizePath } from "./normalize-path.js";
7
7
  import { notFoundResponse } from "./http-error-responses.js";
8
8
  import { isOpenRedirectShaped } from "./open-redirect.js";
9
- import { filterInternalHeaders } from "./request-pipeline.js";
9
+ import { canonicalizeRequestPathname, filterInternalHeaders } from "./request-pipeline.js";
10
10
  import { isUnknownRecord } from "../utils/record.js";
11
- import { buildNextDataNotFoundResponse, isNextDataPathname, normalizeNextDataPagePathname, parseNextDataPathname } from "./pages-data-route.js";
11
+ import { buildNextDataNotFoundResponse, encodeUrlParserIgnoredCharacters, isNextDataPathname, normalizeNextDataPagePathname, parseNextDataPathname, urlParserCreatesPagesDataPath } from "./pages-data-route.js";
12
12
  import { ASSET_PREFIX_URL_DIR, assetPrefixPathname, isAbsoluteAssetPrefix } from "../utils/asset-prefix.js";
13
13
  import { setPagesClientAssets } from "./pages-client-assets.js";
14
14
  import { resolveRequestHost, resolveRequestProtocol, trustProxy, trustedHosts } from "./proxy-trust.js";
@@ -555,10 +555,7 @@ async function statIfFile(filePath) {
555
555
  * Convert a Node.js IncomingMessage to a Web Request object.
556
556
  *
557
557
  * When `urlOverride` is provided, it is used as the path + query string
558
- * instead of `req.url`. This avoids redundant path normalization when the
559
- * caller has already decoded and normalized the pathname (e.g. the App
560
- * Router prod server normalizes before static-asset lookup, and can pass
561
- * the result here so the downstream RSC handler doesn't re-normalize).
558
+ * instead of `req.url`.
562
559
  */
563
560
  function nodeToWebRequest(req, urlOverride, prerenderSecret) {
564
561
  const origin = `${resolveRequestProtocol(req)}://${resolveRequestHost(req, "localhost")}`;
@@ -873,9 +870,14 @@ async function startAppRouterServer(options) {
873
870
  return;
874
871
  }
875
872
  try {
876
- const qs = rawUrl.includes("?") ? rawUrl.slice(rawUrl.indexOf("?")) : "";
877
- const request = nodeToWebRequest(req, pathname + qs, prerenderSecret);
873
+ const request = nodeToWebRequest(req, rawUrl, prerenderSecret);
878
874
  const response = await rscHandler(request);
875
+ if (missingBuildAsset && response.status === 404) {
876
+ cancelResponseBody(response);
877
+ res.writeHead(404, { "Content-Type": "text/plain; charset=utf-8" });
878
+ res.end("Not Found");
879
+ return;
880
+ }
879
881
  const staticFileSignal = response.headers.get(VINEXT_STATIC_FILE_HEADER);
880
882
  if (staticFileSignal) {
881
883
  let staticFilePath = "/";
@@ -891,12 +893,6 @@ async function startAppRouterServer(options) {
891
893
  await sendWebResponse(notFoundResponse({ headers: toWebHeaders(staticResponseHeaders) }), req, res, compress);
892
894
  return;
893
895
  }
894
- if (missingBuildAsset && response.status === 404) {
895
- cancelResponseBody(response);
896
- res.writeHead(404, { "Content-Type": "text/plain; charset=utf-8" });
897
- res.end("Not Found");
898
- return;
899
- }
900
896
  await sendWebResponse(response, req, res, compress);
901
897
  } catch (e) {
902
898
  console.error("[vinext] Server error:", e);
@@ -987,8 +983,9 @@ async function startPagesRouterServer(options) {
987
983
  res.end("This page could not be found");
988
984
  return;
989
985
  }
990
- const rawPagesPathname = rawPagesPathnameBeforeNormalize.replaceAll("\\", "/");
986
+ const rawPagesPathname = canonicalizeRequestPathname(rawPagesPathnameBeforeNormalize.replaceAll("\\", "/"));
991
987
  const rawQs = rawUrl.includes("?") ? rawUrl.slice(rawUrl.indexOf("?")) : "";
988
+ let requestPathname = normalizePath(rawPagesPathname);
992
989
  let pathname;
993
990
  try {
994
991
  pathname = normalizePath(normalizePathnameForRouteMatchStrict(rawPagesPathname));
@@ -997,7 +994,7 @@ async function startPagesRouterServer(options) {
997
994
  res.end("Bad Request");
998
995
  return;
999
996
  }
1000
- let url = pathname + rawQs;
997
+ let url = requestPathname + rawQs;
1001
998
  if (pathname === "/__vinext/prerender/pages-static-paths") {
1002
999
  const secret = req.headers[VINEXT_PRERENDER_SECRET_HEADER];
1003
1000
  if (!prerenderSecret || secret !== prerenderSecret) {
@@ -1058,18 +1055,31 @@ async function startPagesRouterServer(options) {
1058
1055
  return;
1059
1056
  }
1060
1057
  try {
1061
- const hadBasePath = !basePath || hasBasePath(pathname, basePath);
1058
+ const hadBasePath = !basePath || hasBasePath(requestPathname, basePath);
1059
+ let configMatchPathname = stripBasePath(requestPathname, basePath);
1062
1060
  {
1063
- const stripped = stripBasePath(pathname, basePath);
1064
- if (stripped !== pathname) {
1065
- url = stripped + (url.includes("?") ? url.slice(url.indexOf("?")) : "");
1066
- pathname = stripped;
1061
+ const strippedPathname = stripBasePath(pathname, basePath);
1062
+ const strippedRequestPathname = stripBasePath(requestPathname, basePath);
1063
+ pathname = strippedPathname;
1064
+ if (strippedRequestPathname !== requestPathname) {
1065
+ requestPathname = strippedRequestPathname;
1066
+ url = requestPathname + rawQs;
1067
1067
  }
1068
1068
  }
1069
+ if (urlParserCreatesPagesDataPath(pathname)) {
1070
+ res.writeHead(404);
1071
+ res.end("This page could not be found");
1072
+ return;
1073
+ }
1074
+ requestPathname = encodeUrlParserIgnoredCharacters(requestPathname);
1075
+ {
1076
+ const qs = url.includes("?") ? url.slice(url.indexOf("?")) : "";
1077
+ url = requestPathname + qs;
1078
+ }
1069
1079
  let isDataReq = false;
1070
1080
  const originalRenderUrl = url;
1071
- if (isNextDataPathname(pathname)) {
1072
- const dataMatch = pagesBuildId ? parseNextDataPathname(pathname, pagesBuildId) : null;
1081
+ if (isNextDataPathname(requestPathname)) {
1082
+ const dataMatch = pagesBuildId ? parseNextDataPathname(requestPathname, pagesBuildId) : null;
1073
1083
  if (!dataMatch) {
1074
1084
  await sendWebResponse(buildNextDataNotFoundResponse(), req, res, compress);
1075
1085
  return;
@@ -1078,7 +1088,9 @@ async function startPagesRouterServer(options) {
1078
1088
  const qs = url.includes("?") ? url.slice(url.indexOf("?")) : "";
1079
1089
  const pagePathname = normalizeNextDataPagePathname(dataMatch.pagePathname, hasMiddleware && trailingSlash);
1080
1090
  url = pagePathname + qs;
1091
+ requestPathname = pagePathname;
1081
1092
  pathname = pagePathname;
1093
+ configMatchPathname = pagePathname;
1082
1094
  }
1083
1095
  const protocol = resolveRequestProtocol(req);
1084
1096
  const hostHeader = resolveRequestHost(req, `${host}:${port}`);
@@ -1105,6 +1117,7 @@ async function startPagesRouterServer(options) {
1105
1117
  hasMiddleware,
1106
1118
  ctx: void 0,
1107
1119
  rawSearch: rawQs,
1120
+ configMatchPathname,
1108
1121
  matchPageRoute: matchPageRoute ?? null,
1109
1122
  runMiddleware: typeof runMiddleware === "function" ? wrapMiddlewareWithBasePath(runMiddleware, basePath, hadBasePath) : null,
1110
1123
  renderPage: typeof renderPage === "function" ? (request, resolvedUrl, options, stagedHeaders) => renderPage(request, resolvedUrl, ssrManifest, void 0, stagedHeaders, {
@@ -1,10 +1,21 @@
1
- import { NextHeader } from "../config/next-config.js";
2
- import { BasePathMatchState, RequestContext } from "../config/config-matchers.js";
3
1
  import { INTERNAL_HEADERS, VINEXT_INTERNAL_HEADERS } from "./headers.js";
4
- import { isOpenRedirectShaped } from "./open-redirect.js";
5
2
  import { hasBasePath, stripBasePath } from "../utils/base-path.js";
3
+ import { isOpenRedirectShaped } from "./open-redirect.js";
6
4
 
7
5
  //#region src/server/request-pipeline.d.ts
6
+ /**
7
+ * Apply the URL Standard's pathname canonicalization without decoding and
8
+ * re-encoding ordinary percent escapes.
9
+ *
10
+ * In particular, WHATWG URLs remove literal and percent-encoded dot segments
11
+ * (`/%2e/about` becomes `/about`) while preserving unrelated spellings such
12
+ * as `/%61bout`, `%2F`, `%5C`, and `%252F` byte-for-byte. Node request adapters
13
+ * must do this before comparing raw route/config/basePath identity so those
14
+ * comparisons agree with the `Request` that userland eventually receives.
15
+ */
16
+ declare function canonicalizeRequestPathname(pathname: string): string;
17
+ /** Canonicalize only the pathname portion while preserving the raw query. */
18
+ declare function canonicalizeRequestUrlPathname(url: string): string;
8
19
  /**
9
20
  * Shared request pipeline utilities.
10
21
  *
@@ -44,18 +55,6 @@ import { hasBasePath, stripBasePath } from "../utils/base-path.js";
44
55
  */
45
56
  declare function guardProtocolRelativeUrl(rawPathname: string): Response | null;
46
57
  type HeaderRecord = Record<string, string | string[]>;
47
- type ApplyConfigHeadersOptions = {
48
- configHeaders: NextHeader[];
49
- pathname: string;
50
- requestContext: RequestContext;
51
- /**
52
- * basePath gating state. When omitted, every rule is treated as a default
53
- * (basePath: true) rule for backward compatibility — callers that need to
54
- * support `basePath: false` headers must pass this in.
55
- */
56
- basePathState?: BasePathMatchState; /** Existing framework-generated headers that matching config rules may replace. */
57
- overwriteExisting?: ReadonlySet<string>;
58
- };
59
58
  type StaticFileSignalContext = {
60
59
  headers: Headers | null;
61
60
  status: number | null;
@@ -67,19 +66,6 @@ type ResolvePublicFileRouteOptions = {
67
66
  publicFiles: ReadonlySet<string>;
68
67
  request: Request;
69
68
  };
70
- /**
71
- * Apply matched next.config.js headers to a Web Headers object.
72
- *
73
- * Next.js evaluates config header match conditions against the original
74
- * request snapshot. Middleware response headers still win for the same
75
- * response key, while multi-value headers are additive.
76
- */
77
- declare function applyConfigHeadersToResponse(responseHeaders: Headers, options: ApplyConfigHeadersOptions): void;
78
- /**
79
- * Apply matched next.config.js headers to the early response header record used
80
- * by Node and Worker Pages Router pipelines before a concrete response exists.
81
- */
82
- declare function applyConfigHeadersToHeaderRecord(headers: HeaderRecord, options: ApplyConfigHeadersOptions): void;
83
69
  declare function createStaticFileSignal(pathname: string, context: StaticFileSignalContext): Response;
84
70
  /**
85
71
  * Resolve the public/ filesystem-route slot in the Next.js routing order.
@@ -184,4 +170,4 @@ declare function cloneRequestWithHeaders(request: Request, headers: Headers): Re
184
170
  */
185
171
  declare function cloneRequestWithUrl(request: Request, url: string): Request;
186
172
  //#endregion
187
- export { HeaderRecord, INTERNAL_HEADERS, VINEXT_INTERNAL_HEADERS, applyConfigHeadersToHeaderRecord, applyConfigHeadersToResponse, cloneRequestWithHeaders, cloneRequestWithUrl, createStaticFileSignal, filterInternalHeaders, guardProtocolRelativeUrl, hasBasePath, isOpenRedirectShaped, isOriginAllowed, normalizeTrailingSlash, normalizeTrailingSlashPathname, processMiddlewareHeaders, resolvePublicFileRoute, stripBasePath, validateCsrfOrigin, validateServerActionPayload };
173
+ export { HeaderRecord, INTERNAL_HEADERS, VINEXT_INTERNAL_HEADERS, canonicalizeRequestPathname, canonicalizeRequestUrlPathname, cloneRequestWithHeaders, cloneRequestWithUrl, createStaticFileSignal, filterInternalHeaders, guardProtocolRelativeUrl, hasBasePath, isOpenRedirectShaped, isOriginAllowed, normalizeTrailingSlash, normalizeTrailingSlashPathname, processMiddlewareHeaders, resolvePublicFileRoute, stripBasePath, validateCsrfOrigin, validateServerActionPayload };
@@ -1,10 +1,32 @@
1
1
  import { hasBasePath, removeTrailingSlash, stripBasePath } from "../utils/base-path.js";
2
2
  import "../utils/protocol-headers.js";
3
3
  import { INTERNAL_HEADERS, VINEXT_INTERNAL_HEADERS, VINEXT_STATIC_FILE_HEADER } from "./headers.js";
4
- import { matchHeaders } from "../config/config-matchers.js";
5
4
  import { forbiddenResponse, notFoundResponse } from "./http-error-responses.js";
6
5
  import { isOpenRedirectShaped } from "./open-redirect.js";
7
6
  //#region src/server/request-pipeline.ts
7
+ const PATHNAME_CANONICALIZATION_BASE = new URL("http://vinext.invalid/");
8
+ /**
9
+ * Apply the URL Standard's pathname canonicalization without decoding and
10
+ * re-encoding ordinary percent escapes.
11
+ *
12
+ * In particular, WHATWG URLs remove literal and percent-encoded dot segments
13
+ * (`/%2e/about` becomes `/about`) while preserving unrelated spellings such
14
+ * as `/%61bout`, `%2F`, `%5C`, and `%252F` byte-for-byte. Node request adapters
15
+ * must do this before comparing raw route/config/basePath identity so those
16
+ * comparisons agree with the `Request` that userland eventually receives.
17
+ */
18
+ function canonicalizeRequestPathname(pathname) {
19
+ const url = new URL(PATHNAME_CANONICALIZATION_BASE);
20
+ url.pathname = pathname;
21
+ return url.pathname;
22
+ }
23
+ /** Canonicalize only the pathname portion while preserving the raw query. */
24
+ function canonicalizeRequestUrlPathname(url) {
25
+ const queryIndex = url.indexOf("?");
26
+ const pathname = queryIndex === -1 ? url : url.slice(0, queryIndex);
27
+ const search = queryIndex === -1 ? "" : url.slice(queryIndex);
28
+ return canonicalizeRequestPathname(pathname) + search;
29
+ }
8
30
  /**
9
31
  * Shared request pipeline utilities.
10
32
  *
@@ -50,63 +72,6 @@ const FILE_LIKE_PATHNAME_RE = /\.[^/]+\/?$/;
50
72
  function isWellKnownPathname(pathname) {
51
73
  return pathname === "/.well-known" || pathname.startsWith("/.well-known/");
52
74
  }
53
- function findHeaderRecordKey(headers, lowerName) {
54
- for (const key of Object.keys(headers)) if (key.toLowerCase() === lowerName) return key;
55
- }
56
- function appendHeaderRecord(headers, lowerName, value) {
57
- const key = findHeaderRecordKey(headers, lowerName) ?? lowerName;
58
- const existing = headers[key];
59
- if (existing === void 0) {
60
- headers[key] = value;
61
- return;
62
- }
63
- if (Array.isArray(existing)) {
64
- existing.push(value);
65
- return;
66
- }
67
- headers[key] = [existing, value];
68
- }
69
- function appendVaryHeaderRecord(headers, value) {
70
- const key = findHeaderRecordKey(headers, "vary") ?? "vary";
71
- const existing = headers[key];
72
- if (existing === void 0) {
73
- headers[key] = value;
74
- return;
75
- }
76
- if (Array.isArray(existing)) {
77
- existing.push(value);
78
- return;
79
- }
80
- headers[key] = existing + ", " + value;
81
- }
82
- /**
83
- * Apply matched next.config.js headers to a Web Headers object.
84
- *
85
- * Next.js evaluates config header match conditions against the original
86
- * request snapshot. Middleware response headers still win for the same
87
- * response key, while multi-value headers are additive.
88
- */
89
- function applyConfigHeadersToResponse(responseHeaders, options) {
90
- const matched = matchHeaders(options.pathname, options.configHeaders, options.requestContext, options.basePathState);
91
- for (const header of matched) {
92
- const lowerName = header.key.toLowerCase();
93
- if (lowerName === "vary" || lowerName === "set-cookie") responseHeaders.append(header.key, header.value);
94
- else if (options.overwriteExisting?.has(lowerName) || !responseHeaders.has(lowerName)) responseHeaders.set(header.key, header.value);
95
- }
96
- }
97
- /**
98
- * Apply matched next.config.js headers to the early response header record used
99
- * by Node and Worker Pages Router pipelines before a concrete response exists.
100
- */
101
- function applyConfigHeadersToHeaderRecord(headers, options) {
102
- const matched = matchHeaders(options.pathname, options.configHeaders, options.requestContext, options.basePathState);
103
- for (const header of matched) {
104
- const lowerName = header.key.toLowerCase();
105
- if (lowerName === "set-cookie") appendHeaderRecord(headers, lowerName, header.value);
106
- else if (lowerName === "vary") appendVaryHeaderRecord(headers, header.value);
107
- else if (findHeaderRecordKey(headers, lowerName) === void 0) headers[lowerName] = header.value;
108
- }
109
- }
110
75
  function createStaticFileSignal(pathname, context) {
111
76
  const headers = new Headers({ [VINEXT_STATIC_FILE_HEADER]: encodeURIComponent(pathname) });
112
77
  if (context.headers) for (const [key, value] of context.headers) headers.append(key, value);
@@ -445,4 +410,4 @@ function cloneRequestWithUrl(request, url) {
445
410
  return cloned;
446
411
  }
447
412
  //#endregion
448
- export { INTERNAL_HEADERS, VINEXT_INTERNAL_HEADERS, applyConfigHeadersToHeaderRecord, applyConfigHeadersToResponse, cloneRequestWithHeaders, cloneRequestWithUrl, createStaticFileSignal, filterInternalHeaders, guardProtocolRelativeUrl, hasBasePath, isOpenRedirectShaped, isOriginAllowed, normalizeTrailingSlash, normalizeTrailingSlashPathname, processMiddlewareHeaders, resolvePublicFileRoute, stripBasePath, validateCsrfOrigin, validateServerActionPayload };
413
+ export { INTERNAL_HEADERS, VINEXT_INTERNAL_HEADERS, canonicalizeRequestPathname, canonicalizeRequestUrlPathname, cloneRequestWithHeaders, cloneRequestWithUrl, createStaticFileSignal, filterInternalHeaders, guardProtocolRelativeUrl, hasBasePath, isOpenRedirectShaped, isOriginAllowed, normalizeTrailingSlash, normalizeTrailingSlashPathname, processMiddlewareHeaders, resolvePublicFileRoute, stripBasePath, validateCsrfOrigin, validateServerActionPayload };
@@ -1,31 +1,135 @@
1
1
  //#region src/server/rsc-stream-hints.ts
2
- const REACT_FLIGHT_STYLESHEET_PRELOAD_HINT = /(\d*:HL\[.*?),"stylesheet"(\]|,)/g;
3
- /**
4
- * React Flight emits HL hints with "stylesheet" for CSS preloads, but the
5
- * HTML spec requires "style" for <link rel="preload">. Rewrite each complete
6
- * Flight line so SSR embeds, navigation, and server actions see valid hints.
7
- */
2
+ const REACT_FLIGHT_STYLESHEET_PRELOAD_HINT = /^([0-9a-f]*:HL\[.*?),"stylesheet"(\]|,)/;
3
+ const STYLESHEET_TO_STYLE_JSON_PADDING = " ".repeat(5);
4
+ const LENGTH_PREFIXED_ROW_TAGS = /* @__PURE__ */ new Set([
5
+ "T",
6
+ "A",
7
+ "O",
8
+ "o",
9
+ "b",
10
+ "U",
11
+ "S",
12
+ "s",
13
+ "L",
14
+ "l",
15
+ "G",
16
+ "g",
17
+ "M",
18
+ "m",
19
+ "V"
20
+ ]);
21
+ const NEWLINE_PREFIXED_ROW_TAGS = /* @__PURE__ */ new Set([
22
+ "I",
23
+ "H",
24
+ "E",
25
+ "N",
26
+ "D",
27
+ "J",
28
+ "W",
29
+ "R",
30
+ "r",
31
+ "X",
32
+ "x",
33
+ "C",
34
+ "P",
35
+ "#"
36
+ ]);
37
+ const decoder = new TextDecoder();
38
+ const encoder = new TextEncoder();
39
+ /** Rewrite only a complete React Flight stylesheet hint row. */
8
40
  function normalizeReactFlightHintLine(line) {
9
- return line.replace(REACT_FLIGHT_STYLESHEET_PRELOAD_HINT, "$1,\"style\"$2");
41
+ const text = decoder.decode(line);
42
+ const normalized = text.replace(REACT_FLIGHT_STYLESHEET_PRELOAD_HINT, `$1,"style"${STYLESHEET_TO_STYLE_JSON_PADDING}$2`);
43
+ if (normalized === text) return line;
44
+ const normalizedBytes = encoder.encode(normalized);
45
+ return normalizedBytes.byteLength === line.byteLength ? normalizedBytes : line;
46
+ }
47
+ function concatBytes(first, second) {
48
+ if (first.byteLength === 0) return second;
49
+ const combined = new Uint8Array(first.byteLength + second.byteLength);
50
+ combined.set(first);
51
+ combined.set(second, first.byteLength);
52
+ return combined;
53
+ }
54
+ function indexOfByte(bytes, byte, from = 0) {
55
+ for (let index = from; index < bytes.byteLength; index++) if (bytes[index] === byte) return index;
56
+ return -1;
57
+ }
58
+ function parseHexBytes(bytes, start, end) {
59
+ if (start === end) return null;
60
+ let value = 0;
61
+ for (let index = start; index < end; index++) {
62
+ const byte = bytes[index];
63
+ const digit = byte >= 48 && byte <= 57 ? byte - 48 : byte >= 97 && byte <= 102 ? byte - 87 : -1;
64
+ if (digit === -1) return null;
65
+ value = value * 16 + digit;
66
+ if (!Number.isSafeInteger(value)) return null;
67
+ }
68
+ return value;
69
+ }
70
+ function isUntaggedJsonRowStart(byte) {
71
+ return byte === 34 || byte === 45 || byte >= 48 && byte <= 57 || byte === 91 || byte === 102 || byte === 110 || byte === 116 || byte === 123;
10
72
  }
11
73
  function normalizeReactFlightPreloadHints(stream) {
12
- const decoder = new TextDecoder();
13
- const encoder = new TextEncoder();
14
- let carry = "";
74
+ let carry = /* @__PURE__ */ new Uint8Array();
75
+ let rawBytesRemaining = 0;
76
+ let passThrough = false;
15
77
  return stream.pipeThrough(new TransformStream({
16
78
  transform(chunk, controller) {
17
- const text = carry + decoder.decode(chunk, { stream: true });
18
- const lastNewline = text.lastIndexOf("\n");
19
- if (lastNewline === -1) {
20
- carry = text;
79
+ if (passThrough) {
80
+ controller.enqueue(chunk);
21
81
  return;
22
82
  }
23
- carry = text.slice(lastNewline + 1);
24
- controller.enqueue(encoder.encode(normalizeReactFlightHintLine(text.slice(0, lastNewline + 1))));
83
+ let bytes = concatBytes(carry, chunk);
84
+ carry = /* @__PURE__ */ new Uint8Array();
85
+ while (bytes.byteLength > 0) {
86
+ if (rawBytesRemaining > 0) {
87
+ const length = Math.min(rawBytesRemaining, bytes.byteLength);
88
+ controller.enqueue(bytes.slice(0, length));
89
+ rawBytesRemaining -= length;
90
+ bytes = bytes.subarray(length);
91
+ continue;
92
+ }
93
+ const colon = indexOfByte(bytes, 58);
94
+ if (colon === -1 || colon + 1 === bytes.byteLength) {
95
+ carry = bytes.slice();
96
+ return;
97
+ }
98
+ const tag = String.fromCharCode(bytes[colon + 1]);
99
+ if (LENGTH_PREFIXED_ROW_TAGS.has(tag)) {
100
+ const comma = indexOfByte(bytes, 44, colon + 2);
101
+ if (comma === -1) {
102
+ carry = bytes.slice();
103
+ return;
104
+ }
105
+ const length = parseHexBytes(bytes, colon + 2, comma);
106
+ if (length != null) {
107
+ controller.enqueue(bytes.slice(0, comma + 1));
108
+ rawBytesRemaining = length;
109
+ bytes = bytes.subarray(comma + 1);
110
+ continue;
111
+ }
112
+ passThrough = true;
113
+ controller.enqueue(bytes);
114
+ return;
115
+ }
116
+ const tagByte = bytes[colon + 1];
117
+ if (!NEWLINE_PREFIXED_ROW_TAGS.has(tag) && !isUntaggedJsonRowStart(tagByte)) {
118
+ passThrough = true;
119
+ controller.enqueue(bytes);
120
+ return;
121
+ }
122
+ const newline = indexOfByte(bytes, 10);
123
+ if (newline === -1) {
124
+ carry = bytes.slice();
125
+ return;
126
+ }
127
+ controller.enqueue(normalizeReactFlightHintLine(bytes.slice(0, newline + 1)));
128
+ bytes = bytes.subarray(newline + 1);
129
+ }
25
130
  },
26
131
  flush(controller) {
27
- const text = carry + decoder.decode();
28
- if (text) controller.enqueue(encoder.encode(normalizeReactFlightHintLine(text)));
132
+ if (carry.byteLength > 0) controller.enqueue(rawBytesRemaining > 0 ? carry : normalizeReactFlightHintLine(carry));
29
133
  }
30
134
  }));
31
135
  }