vinext 1.2.0 → 1.2.1

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 (110) hide show
  1. package/README.md +1 -1
  2. package/dist/build/output-root-externals.d.ts +21 -0
  3. package/dist/build/output-root-externals.js +40 -0
  4. package/dist/build/prerender-paths.js +1 -1
  5. package/dist/build/prerender.js +1 -1
  6. package/dist/check.js +5 -6
  7. package/dist/cli-dev-config.js +3 -3
  8. package/dist/cli.js +1 -1
  9. package/dist/config/config-matchers.js +12 -7
  10. package/dist/index.js +113 -94
  11. package/dist/init.d.ts +1 -1
  12. package/dist/init.js +86 -6
  13. package/dist/plugins/ast-utils.d.ts +53 -2
  14. package/dist/plugins/ast-utils.js +90 -2
  15. package/dist/plugins/cloudflare-dev-module-urls.d.ts +41 -0
  16. package/dist/plugins/cloudflare-dev-module-urls.js +271 -0
  17. package/dist/plugins/commonjs-esm-facade.js +2 -2
  18. package/dist/plugins/css-module-imports.js +13 -20
  19. package/dist/plugins/dynamic-preload-metadata.js +2 -3
  20. package/dist/plugins/extensionless-dynamic-import.js +2 -3
  21. package/dist/plugins/fonts.d.ts +1 -1
  22. package/dist/plugins/fonts.js +3 -3
  23. package/dist/plugins/ignore-dynamic-requests.js +2 -3
  24. package/dist/plugins/import-meta-url.js +3 -4
  25. package/dist/plugins/instrumentation-client.js +3 -2
  26. package/dist/plugins/middleware-export-validation.js +2 -3
  27. package/dist/plugins/og-harfbuzz.js +2 -3
  28. package/dist/plugins/optimize-imports.js +3 -4
  29. package/dist/plugins/pages-node-externals.js +2 -3
  30. package/dist/plugins/remove-console.js +2 -18
  31. package/dist/plugins/require-condition-resolution.js +3 -3
  32. package/dist/plugins/require-context.js +2 -3
  33. package/dist/plugins/strip-server-exports.js +3 -4
  34. package/dist/plugins/styled-jsx.js +2 -3
  35. package/dist/plugins/use-cache-callable.d.ts +21 -2
  36. package/dist/plugins/use-cache-callable.js +76 -33
  37. package/dist/plugins/worker-image-imports.js +30 -33
  38. package/dist/plugins/worker-use-server.js +38 -14
  39. package/dist/server/api-handler.js +1 -1
  40. package/dist/server/app-action-forwarding.js +2 -2
  41. package/dist/server/app-browser-entry.js +3 -3
  42. package/dist/server/app-browser-interception-context.js +1 -1
  43. package/dist/server/app-browser-prefetch-response.js +2 -2
  44. package/dist/server/app-browser-state.js +1 -1
  45. package/dist/server/app-browser-supplemental-refresh.js +1 -1
  46. package/dist/server/app-optimistic-routing.js +1 -1
  47. package/dist/server/app-page-dispatch.js +1 -1
  48. package/dist/server/app-page-render.js +1 -1
  49. package/dist/server/app-pages-bridge.js +1 -1
  50. package/dist/server/app-prerender-endpoints.js +1 -1
  51. package/dist/server/app-render-abort-error.d.ts +19 -0
  52. package/dist/server/app-render-abort-error.js +31 -1
  53. package/dist/server/app-request-stage-independent-entry.js +5 -5
  54. package/dist/server/app-response-stage-entry.js +2 -2
  55. package/dist/server/app-route-handler-runtime.js +1 -1
  56. package/dist/server/app-router-entry.js +5 -5
  57. package/dist/server/app-rsc-cache-busting.d.ts +16 -0
  58. package/dist/server/app-rsc-cache-busting.js +29 -12
  59. package/dist/server/app-rsc-handler.js +5 -5
  60. package/dist/server/app-rsc-request-normalization.js +3 -3
  61. package/dist/server/app-rsc-response-stage.js +2 -2
  62. package/dist/server/app-server-action-execution.js +1 -1
  63. package/dist/server/app-ssr-entry.js +5 -4
  64. package/dist/server/app-visited-response-cache.js +2 -2
  65. package/dist/server/config-headers.js +1 -1
  66. package/dist/server/defer-until-stream-consumed.d.ts +2 -1
  67. package/dist/server/defer-until-stream-consumed.js +3 -1
  68. package/dist/server/dev-server.js +1 -1
  69. package/dist/server/image-optimization.js +1 -0
  70. package/dist/server/metadata-route-response.js +2 -2
  71. package/dist/server/middleware-path-to-regexp.js +1 -1
  72. package/dist/server/middleware-runtime.js +5 -5
  73. package/dist/server/navigation-planner.js +1 -1
  74. package/dist/server/open-redirect.d.ts +11 -4
  75. package/dist/server/open-redirect.js +11 -4
  76. package/dist/server/pages-api-route.js +2 -2
  77. package/dist/server/pages-data-route.js +2 -1
  78. package/dist/server/pages-page-data.js +1 -1
  79. package/dist/server/pages-page-handler.js +1 -1
  80. package/dist/server/pages-page-response.js +1 -1
  81. package/dist/server/pages-request-pipeline.js +5 -5
  82. package/dist/server/pages-request-stage-entry.js +4 -4
  83. package/dist/server/pages-revalidate.js +2 -2
  84. package/dist/server/pages-router-entry.js +1 -1
  85. package/dist/server/prod-server.js +5 -5
  86. package/dist/server/rsc-stream-hints.d.ts +1 -1
  87. package/dist/server/rsc-stream-hints.js +21 -3
  88. package/dist/server/worker-image-optimization.js +19 -6
  89. package/dist/shims/head.js +1 -1
  90. package/dist/shims/headers.js +2 -2
  91. package/dist/shims/internal/hybrid-client-route-owner-direct.js +1 -1
  92. package/dist/shims/internal/hybrid-client-route-owner.js +1 -0
  93. package/dist/shims/link-prefetch.js +5 -4
  94. package/dist/shims/link.js +2 -2
  95. package/dist/shims/navigation.js +4 -5
  96. package/dist/shims/router.js +9 -12
  97. package/dist/shims/server.d.ts +11 -4
  98. package/dist/shims/server.js +41 -5
  99. package/dist/shims/url-utils.d.ts +7 -0
  100. package/dist/shims/url-utils.js +12 -3
  101. package/dist/utils/domain-locale.js +1 -1
  102. package/dist/utils/plugin-rsc-version.d.ts +2 -0
  103. package/dist/utils/plugin-rsc-version.js +28 -2
  104. package/dist/utils/react-version.d.ts +7 -0
  105. package/dist/utils/react-version.js +39 -16
  106. package/package.json +6 -6
  107. package/dist/build/nitro-route-rules.d.ts +0 -47
  108. package/dist/build/nitro-route-rules.js +0 -79
  109. package/dist/plugins/cache-flight-codec.d.ts +0 -19
  110. package/dist/plugins/cache-flight-codec.js +0 -68
@@ -1,5 +1,5 @@
1
- import { matchHeaders } from "../config/config-matchers.js";
2
1
  import "./headers.js";
2
+ import { matchHeaders } from "../config/config-matchers.js";
3
3
  import { markRouteCacheabilityConfigCdnCachePolicy, markRouteCacheabilityDynamic, markRouteCacheabilityFinalResponseUncacheable } from "../shims/cacheability-classification.js";
4
4
  import { isCdnResponsePolicyHeader, isNonCacheableCdnResponsePolicy } from "./cache-control.js";
5
5
  import { mergeVaryHeader } from "./middleware-response-headers.js";
@@ -1,6 +1,7 @@
1
1
  //#region src/server/defer-until-stream-consumed.d.ts
2
2
  /**
3
3
  * Defers cleanup until the downstream consumer drains or cancels the stream.
4
+ * `onCancel` runs before the cancellation reaches the source stream.
4
5
  */
5
- export declare function deferUntilStreamConsumed(stream: ReadableStream<Uint8Array>, onFlush: () => void, onError?: (error: unknown) => void): ReadableStream<Uint8Array>;
6
+ export declare function deferUntilStreamConsumed(stream: ReadableStream<Uint8Array>, onFlush: () => void, onError?: (error: unknown) => void, onCancel?: (reason: unknown) => void): ReadableStream<Uint8Array>;
6
7
  //#endregion
@@ -1,8 +1,9 @@
1
1
  //#region src/server/defer-until-stream-consumed.ts
2
2
  /**
3
3
  * Defers cleanup until the downstream consumer drains or cancels the stream.
4
+ * `onCancel` runs before the cancellation reaches the source stream.
4
5
  */
5
- function deferUntilStreamConsumed(stream, onFlush, onError) {
6
+ function deferUntilStreamConsumed(stream, onFlush, onError, onCancel) {
6
7
  let called = false;
7
8
  const once = () => {
8
9
  if (!called) {
@@ -26,6 +27,7 @@ function deferUntilStreamConsumed(stream, onFlush, onError) {
26
27
  });
27
28
  },
28
29
  cancel(reason) {
30
+ onCancel?.(reason);
29
31
  once();
30
32
  return reader.cancel(reason);
31
33
  }
@@ -2,10 +2,10 @@ import { closeAfterResponse, createRequestContext, runWithRequestContext } from
2
2
  import { createValidFileMatcher, findFileWithExts } from "../routing/file-matcher.js";
3
3
  import { isBotUserAgent } from "../utils/html-limited-bots.js";
4
4
  import { PRERENDER_REVALIDATE_HEADER } from "../utils/protocol-headers.js";
5
+ import { NEXTJS_CACHE_HEADER, NEXTJS_DEPLOYMENT_ID_HEADER } from "./headers.js";
5
6
  import { sanitizeDestination } from "../config/config-matchers.js";
6
7
  import { patternToNextFormat } from "../routing/route-validation.js";
7
8
  import { matchRoute } from "../routing/pages-router.js";
8
- import { NEXTJS_CACHE_HEADER, NEXTJS_DEPLOYMENT_ID_HEADER } from "./headers.js";
9
9
  import { isDangerousScheme } from "../shims/url-safety.js";
10
10
  import { extractLocaleFromUrl as extractLocaleFromUrl$1, resolvePagesI18nRequest } from "./pages-i18n.js";
11
11
  import { importModule, reportRequestError } from "./instrumentation.js";
@@ -202,6 +202,7 @@ function parseImageParams(url, allowedWidths = [...DEFAULT_DEVICE_SIZES, ...DEFA
202
202
  const base = "https://localhost";
203
203
  const resolved = new URL(normalizedUrl, base);
204
204
  if (resolved.origin !== base) return null;
205
+ if (resolved.pathname.startsWith("//")) return null;
205
206
  if (RECURSIVE_IMAGE_URL_RE.test(decodeURIComponent(resolved.pathname))) return null;
206
207
  } catch {
207
208
  return null;
@@ -1,10 +1,10 @@
1
1
  import { markFullyBufferedBody } from "./fully-buffered-response.js";
2
2
  import { closeAfterResponse, createRequestContext, runWithRequestContext } from "../shims/unified-request-context.js";
3
+ import { VINEXT_METADATA_ROUTE_CACHE_HEADER } from "./headers.js";
4
+ import { notFoundResponse } from "./http-error-responses.js";
3
5
  import { decodeMatchedParams } from "../routing/utils.js";
4
6
  import { getRequestExecutionContext } from "../shims/request-context.js";
5
- import { VINEXT_METADATA_ROUTE_CACHE_HEADER } from "./headers.js";
6
7
  import { runInWorkStoreScope } from "../shims/internal/work-store-scope.js";
7
- import { notFoundResponse } from "./http-error-responses.js";
8
8
  import { beginRouteCacheability, markRouteCacheabilityExplicitResponsePolicy, markRouteCacheabilityResponseBodyComplete, recordRouteCacheability } from "../shims/cacheability-classification.js";
9
9
  import { NEVER_CACHE_CONTROL, applyCdnResponseHeaders, hasCdnResponsePolicy } from "./cache-control.js";
10
10
  import { buildAppRouteMissIsrCacheControl } from "./isr-decision.js";
@@ -224,7 +224,7 @@ function middlewarePathTokensToRegExp(tokens) {
224
224
  } else route += `(?:${prefix}${suffix})${token.modifier}`;
225
225
  }
226
226
  route += `${delimiterRegex}?$`;
227
- return new RegExp(route, "i");
227
+ return new RegExp(route);
228
228
  }
229
229
  //#endregion
230
230
  export { middlewarePathTokensToRegExp, normalizeMiddlewarePathTokens, parseMiddlewarePath };
@@ -2,18 +2,18 @@ import path from "../deps/.pnpm/pathslash@0.1.0/deps/pathslash/dist/index.js";
2
2
  import "../utils/protocol-headers.js";
3
3
  import { shouldKeepMiddlewareHeader } from "../utils/middleware-request-headers.js";
4
4
  import { normalizeHost } from "../config/request-context.js";
5
- import { normalizePathnameForRouteMatchStrict } from "../routing/utils.js";
6
5
  import { addBasePathToPathname, hasBasePath, removeTrailingSlash, stripBasePath } from "../utils/base-path.js";
6
+ import { MIDDLEWARE_REWRITE_HEADER } from "./headers.js";
7
+ import { badRequestResponse, internalServerErrorResponse } from "./http-error-responses.js";
8
+ import { isOpenRedirectShaped } from "./open-redirect.js";
9
+ import { processMiddlewareHeaders } from "./request-pipeline.js";
10
+ import { normalizePathnameForRouteMatchStrict } from "../routing/utils.js";
7
11
  import { getRequestExecutionContext, runWithExecutionContext } from "../shims/request-context.js";
8
12
  import "./server-globals.js";
9
- import { MIDDLEWARE_REWRITE_HEADER } from "./headers.js";
10
13
  import { NextFetchEvent, NextRequest } from "../shims/server.js";
11
14
  import { runInWorkStoreScope } from "../shims/internal/work-store-scope.js";
12
15
  import { normalizePath } from "./normalize-path.js";
13
16
  import { matchesMiddleware, matchesMiddlewarePathname } from "./middleware-matcher.js";
14
- import { badRequestResponse, internalServerErrorResponse } from "./http-error-responses.js";
15
- import { isOpenRedirectShaped } from "./open-redirect.js";
16
- import { processMiddlewareHeaders } from "./request-pipeline.js";
17
17
  import { normalizeDefaultLocalePathname } from "./pages-i18n.js";
18
18
  import { nextDataMiddlewareMatchPathname } from "./pages-data-route.js";
19
19
  import { reportRequestError } from "./instrumentation.js";
@@ -1,5 +1,5 @@
1
- import { splitPathnameForRouteMatch } from "../routing/utils.js";
2
1
  import { stripBasePath } from "../utils/base-path.js";
2
+ import { splitPathnameForRouteMatch } from "../routing/utils.js";
3
3
  import { matchRoutePattern, matchRoutePatternPrefix, matchRoutePatternWithOptionalDynamicSegments } from "../routing/route-pattern.js";
4
4
  import { AppElementsWire, compareAppElementsSlotIds } from "./app-elements-wire.js";
5
5
  import "./app-elements.js";
@@ -11,7 +11,7 @@ export declare function isOpenRedirectShaped(rawPathname: string): boolean;
11
11
  /**
12
12
  * What a request entry answers for a raw path containing a backslash or a
13
13
  * repeated slash: Next.js's 308, or a 404 when the collapsed path would still
14
- * be open-redirect shaped.
14
+ * be open-redirect shaped or the raw target is not origin-form.
15
15
  */
16
16
  export type RepeatedSlashRedirect = {
17
17
  status: 308;
@@ -33,17 +33,24 @@ export type RepeatedSlashRedirect = {
33
33
  * `//evil.com` → `/evil.com`. Percent-encoded `%2F`/`%5C` are not touched and
34
34
  * keep falling through to `isOpenRedirectShaped` (404), as in Next.js.
35
35
  *
36
- * The Location is always same-origin. Absolute-form request targets
37
- * (`GET http://host//x`) are left alone. A collapsed path that still starts
36
+ * The Location is always same-origin. A collapsed path that still starts
38
37
  * with an encoded slash or backslash (`//%2Fevil.com`, `/.//%2Fevil.com`) is
39
38
  * never echoed into a Location or routed: it gets a 404 instead.
40
39
  *
40
+ * A raw Node request target that is not origin-form (`GET http://evil.example/x`,
41
+ * which Node's parser accepts, or `OPTIONS *`) also gets a 404. Next.js never
42
+ * routes these either: it 308s absolute-form targets to their own URL (the
43
+ * `//` after the scheme trips the same check) and 500s `OPTIONS *`. Routing
44
+ * them would let the request line choose the origin of `request.url` that
45
+ * middleware and handlers see, bypassing the Host trust policy. Request-based
46
+ * entries always pass a pathname.
47
+ *
41
48
  * @param rawUrl - The raw request target: pathname plus optional `?query`
42
49
  */
43
50
  export declare function getRepeatedSlashRedirect(rawUrl: string): RepeatedSlashRedirect | null;
44
51
  /**
45
52
  * Build the Response for a raw request path containing a backslash or a
46
- * repeated slash. See `getRepeatedSlashRedirect`.
53
+ * repeated slash, or a non-origin-form target. See `getRepeatedSlashRedirect`.
47
54
  */
48
55
  export declare function repeatedSlashRedirectResponse(rawUrl: string): Response | null;
49
56
  /**
@@ -34,15 +34,22 @@ const REDIRECT_LOCATION_BASE = "http://vinext.invalid";
34
34
  * `//evil.com` → `/evil.com`. Percent-encoded `%2F`/`%5C` are not touched and
35
35
  * keep falling through to `isOpenRedirectShaped` (404), as in Next.js.
36
36
  *
37
- * The Location is always same-origin. Absolute-form request targets
38
- * (`GET http://host//x`) are left alone. A collapsed path that still starts
37
+ * The Location is always same-origin. A collapsed path that still starts
39
38
  * with an encoded slash or backslash (`//%2Fevil.com`, `/.//%2Fevil.com`) is
40
39
  * never echoed into a Location or routed: it gets a 404 instead.
41
40
  *
41
+ * A raw Node request target that is not origin-form (`GET http://evil.example/x`,
42
+ * which Node's parser accepts, or `OPTIONS *`) also gets a 404. Next.js never
43
+ * routes these either: it 308s absolute-form targets to their own URL (the
44
+ * `//` after the scheme trips the same check) and 500s `OPTIONS *`. Routing
45
+ * them would let the request line choose the origin of `request.url` that
46
+ * middleware and handlers see, bypassing the Host trust policy. Request-based
47
+ * entries always pass a pathname.
48
+ *
42
49
  * @param rawUrl - The raw request target: pathname plus optional `?query`
43
50
  */
44
51
  function getRepeatedSlashRedirect(rawUrl) {
45
- if (!rawUrl.startsWith("/")) return null;
52
+ if (!rawUrl.startsWith("/")) return { status: 404 };
46
53
  const urlParts = rawUrl.split("?");
47
54
  const pathname = urlParts[0];
48
55
  if (!REPEATED_SLASH_OR_BACKSLASH_RE.test(pathname)) return null;
@@ -62,7 +69,7 @@ function repeatedSlashRedirectHeaders(location) {
62
69
  }
63
70
  /**
64
71
  * Build the Response for a raw request path containing a backslash or a
65
- * repeated slash. See `getRepeatedSlashRedirect`.
72
+ * repeated slash, or a non-origin-form target. See `getRepeatedSlashRedirect`.
66
73
  */
67
74
  function repeatedSlashRedirectResponse(rawUrl) {
68
75
  const redirect = getRepeatedSlashRedirect(rawUrl);
@@ -1,8 +1,8 @@
1
+ import { internalServerErrorResponse } from "./http-error-responses.js";
2
+ import { cloneRequestWithUrl } from "./request-pipeline.js";
1
3
  import { getRequestExecutionContext, runWithExecutionContext } from "../shims/request-context.js";
2
4
  import "./server-globals.js";
3
5
  import { NextRequest } from "../shims/server.js";
4
- import { internalServerErrorResponse } from "./http-error-responses.js";
5
- import { cloneRequestWithUrl } from "./request-pipeline.js";
6
6
  import { markRouteCacheabilityExplicitResponsePolicy } from "../shims/cacheability-classification.js";
7
7
  import { hasCdnResponsePolicy } from "./cache-control.js";
8
8
  import { mergeRouteParamsIntoQuery, parseQueryString, urlQueryToSearchParams } from "../utils/query.js";
@@ -1,6 +1,7 @@
1
1
  import { MIDDLEWARE_SKIP_HEADER } from "../utils/protocol-headers.js";
2
2
  import { addBasePathToPathname, hasBasePath, stripBasePath } from "../utils/base-path.js";
3
3
  import { NEXTJS_DEPLOYMENT_ID_HEADER } from "./headers.js";
4
+ import { cloneRequestWithUrl } from "./request-pipeline.js";
4
5
  //#region src/server/pages-data-route.ts
5
6
  /**
6
7
  * Helpers for the Pages Router `/_next/data/{buildId}/{...page}.json` endpoint.
@@ -191,7 +192,7 @@ function normalizePagesDataRequest(request, buildId, basePath = "", trailingSlas
191
192
  normalizedUrl.pathname = hadBasePath ? addBasePathToPathname(pagePathname, basePath) : pagePathname;
192
193
  return {
193
194
  isDataReq: true,
194
- request: new Request(normalizedUrl, request),
195
+ request: cloneRequestWithUrl(request, normalizedUrl.href),
195
196
  normalizedPathname: pagePathname,
196
197
  search: reqUrl.search,
197
198
  notFoundResponse: null
@@ -1,7 +1,7 @@
1
1
  import { isUnknownRecord } from "../utils/record.js";
2
2
  import { isBotUserAgent } from "../utils/html-limited-bots.js";
3
- import { normalizePathnameForRouteMatch } from "../routing/utils.js";
4
3
  import { NEXTJS_CACHE_HEADER, NEXTJS_DEPLOYMENT_ID_HEADER } from "./headers.js";
4
+ import { normalizePathnameForRouteMatch } from "../routing/utils.js";
5
5
  import { isDangerousScheme } from "../shims/url-safety.js";
6
6
  import { extractLocaleFromUrl } from "./pages-i18n.js";
7
7
  import { buildNextDataPropsJsonResponse } from "./pages-data-route.js";
@@ -1,10 +1,10 @@
1
1
  import { preserveFullyBufferedBodyMetadata } from "./fully-buffered-response.js";
2
2
  import { closeAfterResponse, closeAfterResponseWithBody, createRequestContext, runWithRequestContext } from "../shims/unified-request-context.js";
3
3
  import { PRERENDER_REVALIDATE_HEADER } from "../utils/protocol-headers.js";
4
+ import { NEXTJS_CACHE_HEADER, NEXTJS_DEPLOYMENT_ID_HEADER, VINEXT_CACHE_HEADER, VINEXT_REVALIDATED_CACHE_TAG_HEADER } from "./headers.js";
4
5
  import { normalizePathnameForRouteMatch } from "../routing/utils.js";
5
6
  import { patternToNextFormat } from "../routing/route-validation.js";
6
7
  import { getRequestExecutionContext } from "../shims/request-context.js";
7
- import { NEXTJS_CACHE_HEADER, NEXTJS_DEPLOYMENT_ID_HEADER, VINEXT_CACHE_HEADER, VINEXT_REVALIDATED_CACHE_TAG_HEADER } from "./headers.js";
8
8
  import { extractLocaleFromUrl, resolvePagesI18nRequest } from "./pages-i18n.js";
9
9
  import { buildNextDataNotFoundResponse, buildNextDataPropsJsonResponse, normalizePagesDataRequest, parseNextDataPathname } from "./pages-data-route.js";
10
10
  import { reportRequestError } from "./instrumentation.js";
@@ -1,6 +1,6 @@
1
1
  import { isBotUserAgent } from "../utils/html-limited-bots.js";
2
- import { getRequestExecutionContext } from "../shims/request-context.js";
3
2
  import { NEXTJS_CACHE_HEADER, VINEXT_REVALIDATED_CACHE_TAG_HEADER } from "./headers.js";
3
+ import { getRequestExecutionContext } from "../shims/request-context.js";
4
4
  import { reportRequestError } from "./instrumentation.js";
5
5
  import { getCdnCacheAdapter } from "../shims/cdn-cache.js";
6
6
  import { markRouteCacheabilityExplicitResponsePolicy } from "../shims/cacheability-classification.js";
@@ -1,14 +1,14 @@
1
1
  import { PRERENDER_REVALIDATE_HEADER } from "../utils/protocol-headers.js";
2
2
  import { requestContextFromRequest } from "../config/request-context.js";
3
3
  import { isExternalUrl } from "../utils/external-url.js";
4
- import { applyMiddlewareRequestHeaders, matchRedirect, matchRewrite, preserveRedirectDestinationQuery, proxyExternalRequest, sanitizeDestination } from "../config/config-matchers.js";
5
- import { assetPrefixPathname, isNextStaticPath } from "../utils/asset-prefix.js";
6
- import { normalizePathnameForRouteMatchStrict } from "../routing/utils.js";
7
4
  import { addBasePathToPathname, hasBasePath } from "../utils/base-path.js";
8
- import { patternToNextFormat } from "../routing/route-validation.js";
9
5
  import { VINEXT_PRERENDER_REWRITTEN_HEADER } from "./headers.js";
10
6
  import { methodNotAllowedResponse, notFoundStaticAssetResponse, sanitizeMethodNotAllowedHeaders } from "./http-error-responses.js";
11
7
  import { cloneRequestWithUrl, normalizeTrailingSlash } from "./request-pipeline.js";
8
+ import { applyMiddlewareRequestHeaders, matchRedirect, matchRewrite, preserveRedirectDestinationQuery, proxyExternalRequest, sanitizeDestination } from "../config/config-matchers.js";
9
+ import { assetPrefixPathname, isNextStaticPath } from "../utils/asset-prefix.js";
10
+ import { normalizePathnameForRouteMatchStrict } from "../routing/utils.js";
11
+ import { patternToNextFormat } from "../routing/route-validation.js";
12
12
  import { detectPathnameLocale, normalizeDefaultLocalePathname, stripI18nLocaleForApiRoute } from "./pages-i18n.js";
13
13
  import { buildMiddlewarePrefetchSkipResponse } from "./pages-data-route.js";
14
14
  import { markRouteCacheabilityDynamic } from "../shims/cacheability-classification.js";
@@ -79,7 +79,7 @@ function wrapMiddlewareWithBasePath(runMiddleware, basePath, hadBasePath) {
79
79
  return (request, ctx, opts) => {
80
80
  const mwUrl = new URL(request.url);
81
81
  mwUrl.pathname = addBasePathToPathname(mwUrl.pathname, basePath);
82
- return runMiddleware(new Request(mwUrl, request), ctx, opts);
82
+ return runMiddleware(cloneRequestWithUrl(request, mwUrl.toString()), ctx, opts);
83
83
  };
84
84
  }
85
85
  /**
@@ -1,12 +1,12 @@
1
1
  import { VINEXT_PRERENDER_SECRET_HEADER, VINEXT_REVALIDATE_HOST_HEADER } from "../utils/protocol-headers.js";
2
2
  import { requestContextFromRequest } from "../config/request-context.js";
3
- import { assetPrefixPathname, isNextStaticPath } from "../utils/asset-prefix.js";
4
- import { normalizePathnameForRouteMatchStrict } from "../routing/utils.js";
5
3
  import { hasBasePath, stripBasePath } from "../utils/base-path.js";
6
- import { runWithExecutionContext } from "../shims/request-context.js";
7
4
  import { VINEXT_CACHEABILITY_PROBE_QUERY_PARAM, VINEXT_EXPECTED_WORKER_VERSION_HEADER } from "./headers.js";
8
5
  import { notFoundStaticAssetResponse } from "./http-error-responses.js";
9
6
  import { attachRequestCfMetadata, cloneRequestWithHeaders, cloneRequestWithUrl, filterInternalHeaders, guardProtocolRelativeUrl } from "./request-pipeline.js";
7
+ import { assetPrefixPathname, isNextStaticPath } from "../utils/asset-prefix.js";
8
+ import { normalizePathnameForRouteMatchStrict } from "../routing/utils.js";
9
+ import { runWithExecutionContext } from "../shims/request-context.js";
10
10
  import { normalizeDefaultLocalePathname } from "./pages-i18n.js";
11
11
  import { isNextDataPathname } from "./pages-data-route.js";
12
12
  import { getExplicitCdnCacheAdapter } from "../shims/cdn-cache-state.js";
@@ -128,7 +128,7 @@ async function handleRequestImpl(request, env, platformCtx, dispatchResponseStag
128
128
  probeRoute = readWorkerCacheabilityProbeRoute(request);
129
129
  const probeUrl = new URL(request.url);
130
130
  probeUrl.searchParams.delete(VINEXT_CACHEABILITY_PROBE_QUERY_PARAM);
131
- request = new Request(probeUrl, request);
131
+ request = cloneRequestWithUrl(request, probeUrl.href);
132
132
  }
133
133
  }
134
134
  let responseStageDispatched = false;
@@ -1,11 +1,11 @@
1
1
  import { PRERENDER_REVALIDATE_HEADER, PRERENDER_REVALIDATE_ONLY_GENERATED_HEADER, VINEXT_REVALIDATE_HOST_HEADER } from "../utils/protocol-headers.js";
2
- import { getRequestExecutionContext } from "../shims/request-context.js";
3
2
  import { VINEXT_REVALIDATED_CACHE_TAG_HEADER } from "./headers.js";
3
+ import { getRequestExecutionContext } from "../shims/request-context.js";
4
+ import { resolveRequestHost, resolveRequestProtocol } from "./proxy-trust.js";
4
5
  import { normalizeDomainHostname } from "../utils/domain-locale.js";
5
6
  import { getCdnCacheAdapter } from "../shims/cdn-cache.js";
6
7
  import { getRevalidateSecret, isOnDemandRevalidateRequest } from "./revalidation-request.js";
7
8
  import "./isr-cache.js";
8
- import { resolveRequestHost, resolveRequestProtocol } from "./proxy-trust.js";
9
9
  //#region src/server/pages-revalidate.ts
10
10
  async function performOnDemandRevalidate(source, urlPath, opts = {}, trustedOrigin, allowedRevalidateHeaderKeys = [], dev = false) {
11
11
  if (typeof urlPath !== "string" || !urlPath.startsWith("/")) throw new Error(`Invalid urlPath provided to revalidate(), must be a path e.g. /blog/post-1, received ${urlPath}`);
@@ -1,6 +1,6 @@
1
- import { runWithExecutionContext } from "../shims/request-context.js";
2
1
  import { VINEXT_CACHEABILITY_PROBE_HEADER, VINEXT_CACHEABILITY_PROBE_QUERY_PARAM } from "./headers.js";
3
2
  import { cloneRequestWithHeaders, cloneRequestWithUrl } from "./request-pipeline.js";
3
+ import { runWithExecutionContext } from "../shims/request-context.js";
4
4
  import { getCdnCacheAdapter } from "../shims/cdn-cache.js";
5
5
  import { validateCdnRequest } from "./cache-control.js";
6
6
  import { createWorkerRevalidationContext } from "./worker-revalidation-context.js";
@@ -1,22 +1,22 @@
1
1
  import path from "../deps/.pnpm/pathslash@0.1.0/deps/pathslash/dist/index.js";
2
2
  import { isUnknownRecord } from "../utils/record.js";
3
3
  import { VINEXT_PRERENDER_ROUTE_PARAMS_HEADER, VINEXT_PRERENDER_SECRET_HEADER, VINEXT_PRERENDER_SPECULATIVE_HEADER } from "../utils/protocol-headers.js";
4
- import { ASSET_PREFIX_URL_DIR, assetPrefixPathname, isAbsoluteAssetPrefix } from "../utils/asset-prefix.js";
5
- import { StaticFileCache, contentTypeForPath, etagFromFilenameHash } from "./static-file-cache.js";
6
- import { normalizePathnameForRouteMatchStrict } from "../routing/utils.js";
7
4
  import { hasBasePath, stripBasePath } from "../utils/base-path.js";
8
5
  import { VINEXT_PRERENDER_RENDER_ERROR_HEADER, VINEXT_PRERENDER_SPECIAL_ERROR_HEADER } from "./headers.js";
9
- import { normalizePath } from "./normalize-path.js";
10
6
  import { notFoundResponse } from "./http-error-responses.js";
11
7
  import { getRepeatedSlashRedirect, isOpenRedirectShaped, sendRepeatedSlashRedirect } from "./open-redirect.js";
12
8
  import { readStaticFileSignal } from "./static-file-signal.js";
13
9
  import { canonicalizeRequestPathname, filterInternalHeaders } from "./request-pipeline.js";
10
+ import { ASSET_PREFIX_URL_DIR, assetPrefixPathname, isAbsoluteAssetPrefix } from "../utils/asset-prefix.js";
11
+ import { StaticFileCache, contentTypeForPath, etagFromFilenameHash } from "./static-file-cache.js";
12
+ import { normalizePathnameForRouteMatchStrict } from "../routing/utils.js";
13
+ import { resolveRequestHost, resolveRequestProtocol, trustProxy, trustedHosts } from "./proxy-trust.js";
14
+ import { normalizePath } from "./normalize-path.js";
14
15
  import { buildNextDataNotFoundResponse, encodeUrlParserIgnoredCharacters, isNextDataPathname, normalizeNextDataPagePathname, parseNextDataPathname, shouldAddTrailingSlashToPagesDataPath, urlParserCreatesPagesDataPath } from "./pages-data-route.js";
15
16
  import { buildCacheStateHeaders } from "./cache-headers.js";
16
17
  import { parseHttpDate } from "./http-date.js";
17
18
  import { evaluateStaticPreconditions } from "./http-conditional.js";
18
19
  import { setPagesClientAssets } from "./pages-client-assets.js";
19
- import { resolveRequestHost, resolveRequestProtocol, trustProxy, trustedHosts } from "./proxy-trust.js";
20
20
  import { signalFromNodeResponse } from "./node-response-signal.js";
21
21
  import { DEFAULT_DEVICE_SIZES, DEFAULT_IMAGE_SIZES, IMAGE_RESPONSE_CACHE_STATE, INVALID_IMAGE_SOURCE_MESSAGE, isImageOptimizationPath, isImageSourceBodyStatus, isSafeImageContentType, parseImageParams, readImageOptimizationSignal } from "./image-optimization.js";
22
22
  import { installSocketErrorBackstop } from "./socket-error-backstop.js";
@@ -11,7 +11,7 @@
11
11
  * tag is `HL` are decoded and inspected. Length-prefixed bodies remain separate
12
12
  * so binary bytes do not force adjacent text through the embed's base64 path.
13
13
  */
14
- export declare function normalizeReactFlightPreloadHints(stream: ReadableStream<Uint8Array>): ReadableStream<Uint8Array>;
14
+ export declare function normalizeReactFlightPreloadHints(stream: ReadableStream<Uint8Array>, onCancel?: (reason: unknown) => void): ReadableStream<Uint8Array>;
15
15
  export type RscRawRenderer = (model: unknown, options?: unknown) => ReadableStream<Uint8Array>;
16
16
  export type RscRawPrerenderer = (model: unknown, options?: unknown) => Promise<{
17
17
  prelude: ReadableStream<Uint8Array>;
@@ -1,3 +1,4 @@
1
+ import { createRenderStreamCancellation } from "./app-render-abort-error.js";
1
2
  //#region src/server/rsc-stream-hints.ts
2
3
  const REACT_FLIGHT_STYLESHEET_PRELOAD_HINT = /^([0-9a-f]*:HL\[.*?),"stylesheet"(\]|,)/;
3
4
  const STYLESHEET_TO_STYLE_JSON_PADDING = " ".repeat(5);
@@ -106,7 +107,7 @@ function isTagInTable(table, byte) {
106
107
  * tag is `HL` are decoded and inspected. Length-prefixed bodies remain separate
107
108
  * so binary bytes do not force adjacent text through the embed's base64 path.
108
109
  */
109
- function normalizeReactFlightPreloadHints(stream) {
110
+ function normalizeReactFlightPreloadHints(stream, onCancel) {
110
111
  let carry = null;
111
112
  let rawBytesRemaining = 0;
112
113
  let passThrough = false;
@@ -194,11 +195,28 @@ function normalizeReactFlightPreloadHints(stream) {
194
195
  },
195
196
  flush(controller) {
196
197
  if (carry !== null && carry.byteLength > 0) controller.enqueue(rawBytesRemaining > 0 ? carry : normalizeReactFlightHintLine(carry));
197
- }
198
+ },
199
+ cancel: onCancel
198
200
  }));
199
201
  }
200
202
  function createRscRenderer(render) {
201
- return (model, options) => normalizeReactFlightPreloadHints(render(model, options));
203
+ return (model, options) => {
204
+ const cancellation = createRenderStreamCancellation();
205
+ return normalizeReactFlightPreloadHints(render(model, ignoreCancellationErrors(options, cancellation)), cancellation.record);
206
+ };
207
+ }
208
+ /** A consumer cancelling the payload is not a render failure. */
209
+ function ignoreCancellationErrors(options, cancellation) {
210
+ if (!options || typeof options !== "object") return options;
211
+ const onError = Reflect.get(options, "onError");
212
+ if (typeof onError !== "function") return options;
213
+ return {
214
+ ...options,
215
+ onError(error, ...rest) {
216
+ if (cancellation.isReason(error)) return void 0;
217
+ return Reflect.apply(onError, options, [error, ...rest]);
218
+ }
219
+ };
202
220
  }
203
221
  function createRscPrerenderer(prerender) {
204
222
  return async (model, options) => {
@@ -1,9 +1,7 @@
1
- import { isNextStaticPath } from "../utils/asset-prefix.js";
2
- import { normalizePathnameForRouteMatchStrict } from "../routing/utils.js";
3
1
  import { VINEXT_PRERENDER_SPECIAL_ERROR_HEADER } from "./headers.js";
4
- import { normalizePath } from "./normalize-path.js";
5
2
  import { readStaticFileSignal, readStaticFileSignalRequestHeaders } from "./static-file-signal.js";
6
3
  import { attachRequestCfMetadata, cloneRequestWithUrl } from "./request-pipeline.js";
4
+ import { isNextStaticPath } from "../utils/asset-prefix.js";
7
5
  import { handleConfiguredImageOptimization, invalidImageSourceResponse, isImageSourceBodyStatus } from "./image-optimization.js";
8
6
  import { createStaticAssetRequest, mergeHeaders } from "./worker-utils.js";
9
7
  import { resolveClientBuildFile } from "./client-build-files.js";
@@ -28,19 +26,34 @@ function cancelResponseBody(response) {
28
26
  body.cancel().catch(() => {});
29
27
  }
30
28
  /**
29
+ * Return the pathname of the `_next/static` file a source's decoded pathname
30
+ * names, or null. Next.js matches the decoded pathname against its
31
+ * `.next/static` files literally, so a `.`, `..` or empty segment names no
32
+ * file. The asset binding decodes each segment once, so each segment is
33
+ * encoded once from the name checked here. Parsing the decoded pathname as a
34
+ * URL again would resolve a segment that decoded to `%2E%2E` as `..`, outside
35
+ * `_next/static`.
36
+ */
37
+ function resolveNextStaticFile(decodedPathname, options) {
38
+ if (!isNextStaticPath(decodedPathname, options.basePath, options.assetPathPrefix)) return null;
39
+ const segments = decodedPathname.split("/").slice(1);
40
+ if (segments.some((segment) => segment === "" || segment === "." || segment === "..")) return null;
41
+ return "/" + segments.map(encodeURIComponent).join("/");
42
+ }
43
+ /**
31
44
  * Fetch a local image source. Build output is fetched from the asset binding,
32
45
  * which serves it before the Worker and its middleware. Any other source is
33
46
  * served only when the application pipeline selects a public file for it.
34
47
  */
35
48
  async function fetchWorkerImageSource(imageUrl, request, options) {
36
49
  const sourceUrl = new URL(imageUrl, request.url);
37
- let sourcePathname;
50
+ let decodedPathname;
38
51
  try {
39
- sourcePathname = normalizePath(normalizePathnameForRouteMatchStrict(sourceUrl.pathname));
52
+ decodedPathname = decodeURIComponent(sourceUrl.pathname);
40
53
  } catch {
41
54
  return invalidImageSourceResponse();
42
55
  }
43
- const buildAssetPath = isNextStaticPath(sourcePathname, options.basePath, options.assetPathPrefix) ? sourcePathname : resolveClientBuildFile(sourceUrl.pathname, options.basePath, options.clientBuildFiles);
56
+ const buildAssetPath = resolveNextStaticFile(decodedPathname, options) ?? resolveClientBuildFile(sourceUrl.pathname, options.basePath, options.clientBuildFiles);
44
57
  if (buildAssetPath !== null) return options.assets.fetch(new Request(new URL(buildAssetPath, request.url)));
45
58
  const sourceResponse = await options.fetchPublicFile(attachRequestCfMetadata(new Request(sourceUrl, { signal: request.signal }), request));
46
59
  if (sourceResponse && isImageSourceBodyStatus(sourceResponse.status)) return sourceResponse;
@@ -183,7 +183,7 @@ function reduceHeadChildren(headChildren) {
183
183
  const SAFE_ATTR_NAME_RE = /^[a-zA-Z][a-zA-Z0-9\-:.]*$/;
184
184
  function isSafeAttrName(name) {
185
185
  if (!SAFE_ATTR_NAME_RE.test(name)) return false;
186
- if (name.length > 2 && name[0] === "o" && name[1] === "n" && name[2] >= "A" && name[2] <= "z") return false;
186
+ if (name.length > 2 && (name[0] === "o" || name[0] === "O") && (name[1] === "n" || name[1] === "N")) return false;
187
187
  return true;
188
188
  }
189
189
  /**
@@ -749,8 +749,8 @@ async function draftMode() {
749
749
  }
750
750
  };
751
751
  }
752
- const APPLY_RESPONSE_COOKIE = Symbol("vinext.apply-response-cookie");
753
- const SYNCHRONIZE_REQUEST_COOKIES = Symbol("vinext.synchronize-request-cookies");
752
+ const APPLY_RESPONSE_COOKIE = Symbol.for("vinext.apply-response-cookie");
753
+ const SYNCHRONIZE_REQUEST_COOKIES = Symbol.for("vinext.synchronize-request-cookies");
754
754
  var RequestCookies = class {
755
755
  _cookies;
756
756
  _responseCookies;
@@ -1,5 +1,5 @@
1
- import { compareHybridRoutePatterns } from "../../routing/utils.js";
2
1
  import { stripBasePath } from "../../utils/base-path.js";
2
+ import { compareHybridRoutePatterns } from "../../routing/utils.js";
3
3
  import { createRouteTrieCache, matchRouteWithTrie } from "../../routing/route-matching.js";
4
4
  import { getLocalePathPrefix } from "../../utils/domain-locale.js";
5
5
  //#region src/shims/internal/hybrid-client-route-owner-direct.ts
@@ -1,4 +1,5 @@
1
1
  import { parseCookies } from "../../config/request-context.js";
2
+ import "../../config/config-matchers.js";
2
3
  import { matchDirectHybridClientRoutes, resolveMatchedHybridClientRouteOwner, resolveSameOriginAppPathname } from "./hybrid-client-route-owner-direct.js";
3
4
  import { mergeRewriteQuery } from "../../utils/query.js";
4
5
  import { matchClientRewrite } from "../../client/client-rewrite-matcher.js";
@@ -1,5 +1,5 @@
1
1
  import { hasBasePath, stripBasePath } from "../utils/base-path.js";
2
- import { isAbsoluteOrProtocolRelativeUrl } from "./url-utils.js";
2
+ import { collapseLeadingSlashes, isAbsoluteOrProtocolRelativeUrl } from "./url-utils.js";
3
3
  //#region src/shims/link-prefetch.ts
4
4
  function canLinkPrefetch(input) {
5
5
  return input.nodeEnv === "production" && input.prefetch !== false && !input.isDangerous;
@@ -32,9 +32,10 @@ function getLinkPrefetchHref(input) {
32
32
  const current = new URL(currentOrigin);
33
33
  const parsed = href.startsWith("//") ? new URL(href, current.origin) : new URL(href);
34
34
  if (parsed.origin !== current.origin) return null;
35
- if (!basePath) return parsed.pathname + parsed.search + parsed.hash;
36
- if (!hasBasePath(parsed.pathname, basePath)) return null;
37
- return stripBasePath(parsed.pathname, basePath) + parsed.search + parsed.hash;
35
+ const pathname = collapseLeadingSlashes(parsed.pathname);
36
+ if (!basePath) return pathname + parsed.search + parsed.hash;
37
+ if (!hasBasePath(pathname, basePath)) return null;
38
+ return collapseLeadingSlashes(stripBasePath(pathname, basePath)) + parsed.search + parsed.hash;
38
39
  } catch {
39
40
  return null;
40
41
  }
@@ -317,7 +317,7 @@ function prefetchUrl(href, mode, priority = "low", pagesRouteHref, locale) {
317
317
  async function promotePrefetchEntriesForNavigation(href) {
318
318
  if (typeof window === "undefined") return;
319
319
  if (!hasAppNavigationRuntime()) return;
320
- const [{ getPrefetchCache }, { stripRscCacheBustingSearchParam, stripRscSuffix }] = await Promise.all([loadNavigationModule(), import("../server/app-rsc-cache-busting.js")]);
320
+ const [{ getPrefetchCache }, { parseRscRequestPath, stripRscCacheBustingSearchParam, stripRscSuffix }] = await Promise.all([loadNavigationModule(), import("../server/app-rsc-cache-busting.js")]);
321
321
  let target;
322
322
  try {
323
323
  target = new URL(toCanonicalBrowserNavigationHref(href, window.location.href, __basePath, __trailingSlash), window.location.href);
@@ -330,7 +330,7 @@ async function promotePrefetchEntriesForNavigation(href) {
330
330
  const [rscUrl] = cacheKey.split("\0", 1);
331
331
  let cached;
332
332
  try {
333
- cached = new URL(rscUrl, window.location.href);
333
+ cached = parseRscRequestPath(rscUrl);
334
334
  } catch {
335
335
  continue;
336
336
  }
@@ -17,7 +17,7 @@ import { GLOBAL_ACCESSORS_KEY, ServerInsertedHTMLContext, _registerStateAccessor
17
17
  import { BailoutToCSRError, DynamicServerError, HTTP_ERROR_FALLBACK_ERROR_CODE, RedirectType, decodeRedirectError, forbidden, getAccessFallbackHTTPStatus, isBailoutToCSRError, isDynamicServerError, isHTTPAccessFallbackError, isNextRouterError, isRedirectError, notFound, permanentRedirect, redirect, unauthorized, unstable_rethrow } from "./navigation-errors.js";
18
18
  import { clearAppNavigationFailureTarget, stageAppNavigationFailureTarget } from "../client/app-nav-failure-handler.js";
19
19
  import { beginAppRouterScrollIntent, clearAppRouterScrollIntent, consumeAppRouterScrollIntent, getPendingAppRouterScrollIntent, isLatestAppRouterScrollIntent } from "./app-router-scroll-state.js";
20
- import { VINEXT_RSC_COMPATIBILITY_ID_HEADER, VINEXT_RSC_CONTENT_TYPE, createRscRequestHeaders, createRscRequestUrl, stripRscCacheBustingSearchParam, stripRscSuffix } from "../server/app-rsc-cache-busting.js";
20
+ import { VINEXT_RSC_COMPATIBILITY_ID_HEADER, VINEXT_RSC_CONTENT_TYPE, createRscRequestHeaders, createRscRequestUrl, formatRscRequestPath, parseRscRequestPath, stripRscCacheBustingSearchParam, stripRscSuffix } from "../server/app-rsc-cache-busting.js";
21
21
  import { notifyAppRouterTransitionStart } from "../client/instrumentation-client-state.js";
22
22
  import { getNavigationRuntime, hasAppNavigationRuntime } from "../client/navigation-runtime.js";
23
23
  import { UnrecognizedActionError, unstable_isUnrecognizedActionError } from "./unrecognized-action-error.js";
@@ -236,7 +236,7 @@ function resolvePrefetchCacheEntryMountedSlotsHeader(entry) {
236
236
  }
237
237
  function normalizeRscCacheLookupUrl(rscUrl) {
238
238
  try {
239
- const url = new URL(rscUrl, "http://vinext.local");
239
+ const url = parseRscRequestPath(rscUrl);
240
240
  stripRscCacheBustingSearchParam(url);
241
241
  return `${url.pathname}${url.search}`;
242
242
  } catch {
@@ -245,7 +245,7 @@ function normalizeRscCacheLookupUrl(rscUrl) {
245
245
  }
246
246
  function normalizeRscCacheLookupPathname(rscUrl) {
247
247
  try {
248
- const url = new URL(rscUrl, "http://vinext.local");
248
+ const url = parseRscRequestPath(rscUrl);
249
249
  return stripRscSuffix(url.pathname);
250
250
  } catch {
251
251
  return null;
@@ -435,8 +435,7 @@ function toAppPrefetchDestination(href) {
435
435
  }
436
436
  const browserHref = toCanonicalBrowserNavigationHref(localHref, window.location.href, __basePath, __trailingSlash);
437
437
  try {
438
- const url = new URL(browserHref, window.location.href);
439
- return `${url.pathname}${url.search}`;
438
+ return formatRscRequestPath(new URL(browserHref, window.location.href));
440
439
  } catch {
441
440
  return browserHref.split("#", 1)[0];
442
441
  }