vinext 1.0.1 → 1.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +93 -10
- package/dist/build/app-page-static-eligibility.d.ts +15 -0
- package/dist/build/app-page-static-eligibility.js +82 -0
- package/dist/build/client-build-config.d.ts +10 -0
- package/dist/build/client-build-config.js +18 -2
- package/dist/build/pages-client-assets-module.d.ts +8 -0
- package/dist/build/pages-client-assets-module.js +21 -1
- package/dist/build/prerender-paths.js +8 -81
- package/dist/build/prerender.js +33 -8
- package/dist/build/run-prerender.js +1 -1
- package/dist/build/server-manifest.d.ts +7 -0
- package/dist/build/server-manifest.js +11 -1
- package/dist/build/ssr-manifest.d.ts +2 -0
- package/dist/build/ssr-manifest.js +6 -2
- package/dist/cache/cache-adapters-virtual.d.ts +7 -1
- package/dist/cache/cache-adapters-virtual.js +3 -3
- package/dist/check.js +7 -6
- package/dist/cli.js +2 -1
- package/dist/client/client-redirect-matcher.d.ts +14 -0
- package/dist/client/client-redirect-matcher.js +37 -0
- package/dist/client/client-redirects.d.ts +27 -0
- package/dist/client/client-redirects.js +28 -0
- package/dist/client/client-rewrites.d.ts +2 -1
- package/dist/client/client-rewrites.js +1 -1
- package/dist/client/navigation-runtime.d.ts +11 -0
- package/dist/client/navigation-runtime.js +1 -1
- package/dist/config/config-matchers.d.ts +26 -11
- package/dist/config/config-matchers.js +81 -82
- package/dist/config/destination-params.d.ts +47 -0
- package/dist/config/destination-params.js +116 -0
- package/dist/config/next-config.d.ts +14 -0
- package/dist/config/next-config.js +21 -3
- package/dist/entries/app-rsc-entry.d.ts +6 -0
- package/dist/entries/app-rsc-entry.js +84 -49
- package/dist/entries/pages-client-entry.js +3 -1
- package/dist/entries/pages-server-entry.js +13 -37
- package/dist/index.d.ts +7 -2
- package/dist/index.js +193 -73
- package/dist/init-cloudflare.d.ts +9 -0
- package/dist/init-cloudflare.js +107 -31
- package/dist/init-platform.d.ts +4 -0
- package/dist/init-platform.js +27 -2
- package/dist/init.d.ts +1 -0
- package/dist/init.js +67 -3
- package/dist/plugins/ast-scope.d.ts +7 -0
- package/dist/plugins/ast-scope.js +59 -3
- package/dist/plugins/cache-flight-codec.d.ts +19 -0
- package/dist/plugins/cache-flight-codec.js +68 -0
- package/dist/plugins/commonjs-esm-facade.d.ts +35 -0
- package/dist/plugins/commonjs-esm-facade.js +80 -0
- package/dist/plugins/dynamic-preload-metadata.d.ts +1 -1
- package/dist/plugins/dynamic-preload-metadata.js +9 -8
- package/dist/plugins/fonts.d.ts +8 -11
- package/dist/plugins/fonts.js +71 -34
- package/dist/plugins/ignore-dynamic-requests.d.ts +1 -1
- package/dist/plugins/ignore-dynamic-requests.js +3 -4
- package/dist/plugins/middleware-server-only.d.ts +5 -4
- package/dist/plugins/middleware-server-only.js +8 -5
- package/dist/plugins/pages-node-externals.js +1 -1
- package/dist/plugins/rsc-scan-glob-imports.d.ts +19 -0
- package/dist/plugins/rsc-scan-glob-imports.js +50 -0
- package/dist/plugins/strip-server-exports.js +262 -98
- package/dist/plugins/transform-result.d.ts +9 -5
- package/dist/plugins/transitive-externals.js +1 -1
- package/dist/plugins/typeof-window.d.ts +2 -2
- package/dist/plugins/use-cache-callable.js +8 -2
- package/dist/plugins/worker-server-only-imports.d.ts +20 -0
- package/dist/plugins/worker-server-only-imports.js +31 -0
- package/dist/plugins/worker-use-server.d.ts +25 -0
- package/dist/plugins/worker-use-server.js +129 -0
- package/dist/routing/file-matcher.d.ts +1 -0
- package/dist/routing/file-matcher.js +1 -1
- package/dist/server/api-handler.d.ts +1 -0
- package/dist/server/api-handler.js +19 -8
- package/dist/server/app-browser-action-result.d.ts +1 -1
- package/dist/server/app-browser-action-result.js +9 -2
- package/dist/server/app-browser-entry.js +11 -7
- package/dist/server/app-browser-history-controller.d.ts +2 -0
- package/dist/server/app-browser-history-controller.js +8 -0
- package/dist/server/app-browser-interception-context.js +26 -2
- package/dist/server/app-browser-navigation-controller.d.ts +7 -1
- package/dist/server/app-browser-navigation-controller.js +94 -11
- package/dist/server/app-browser-server-action-client.js +1 -1
- package/dist/server/app-fallback-renderer.d.ts +2 -0
- package/dist/server/app-fallback-renderer.js +1 -0
- package/dist/server/app-history-state.d.ts +3 -0
- package/dist/server/app-history-state.js +7 -0
- package/dist/server/app-middleware.d.ts +11 -0
- package/dist/server/app-middleware.js +19 -3
- package/dist/server/app-optimistic-routing.js +1 -1
- package/dist/server/app-page-boundary-render.d.ts +5 -0
- package/dist/server/app-page-boundary-render.js +6 -4
- package/dist/server/app-page-cache-finalizer.d.ts +11 -0
- package/dist/server/app-page-cache-finalizer.js +41 -7
- package/dist/server/app-page-cache-render.d.ts +18 -0
- package/dist/server/app-page-cache-render.js +63 -43
- package/dist/server/app-page-cache.d.ts +6 -1
- package/dist/server/app-page-cache.js +28 -8
- package/dist/server/app-page-dispatch.d.ts +12 -2
- package/dist/server/app-page-dispatch.js +51 -33
- package/dist/server/app-page-element-builder.d.ts +7 -0
- package/dist/server/app-page-element-builder.js +27 -42
- package/dist/server/app-page-execution.d.ts +15 -12
- package/dist/server/app-page-execution.js +30 -40
- package/dist/server/app-page-head.d.ts +5 -0
- package/dist/server/app-page-head.js +9 -3
- package/dist/server/app-page-ppr-runtime.js +1 -1
- package/dist/server/app-page-probe.d.ts +0 -109
- package/dist/server/app-page-probe.js +3 -136
- package/dist/server/app-page-render-identity.d.ts +0 -1
- package/dist/server/app-page-render-identity.js +1 -1
- package/dist/server/app-page-render-observation.js +3 -3
- package/dist/server/app-page-render.d.ts +13 -5
- package/dist/server/app-page-render.js +219 -119
- package/dist/server/app-page-request.d.ts +0 -1
- package/dist/server/app-page-request.js +54 -17
- package/dist/server/app-page-response.d.ts +4 -3
- package/dist/server/app-page-response.js +4 -3
- package/dist/server/app-page-route-wiring.d.ts +17 -5
- package/dist/server/app-page-route-wiring.js +57 -35
- package/dist/server/app-page-rsc-render-status.d.ts +48 -0
- package/dist/server/app-page-rsc-render-status.js +54 -0
- package/dist/server/app-page-stream.d.ts +15 -8
- package/dist/server/app-page-stream.js +6 -0
- package/dist/server/app-pages-bridge.d.ts +1 -1
- package/dist/server/app-pages-bridge.js +2 -3
- package/dist/server/app-prerender-endpoints.js +9 -6
- package/dist/server/app-request-stage-dispatch.js +7 -3
- package/dist/server/app-request-stage-independent-entry.js +29 -9
- package/dist/server/app-response-header-provenance.d.ts +2 -0
- package/dist/server/app-response-header-provenance.js +10 -4
- package/dist/server/app-response-stage-entry.js +10 -2
- package/dist/server/app-route-handler-cache.d.ts +1 -0
- package/dist/server/app-route-handler-cache.js +1 -0
- package/dist/server/app-route-handler-dispatch.d.ts +1 -0
- package/dist/server/app-route-handler-dispatch.js +6 -3
- package/dist/server/app-route-handler-execution.d.ts +1 -0
- package/dist/server/app-route-handler-execution.js +1 -0
- package/dist/server/app-route-handler-runtime.d.ts +4 -1
- package/dist/server/app-route-handler-runtime.js +45 -24
- package/dist/server/app-router-entry.js +27 -8
- package/dist/server/app-rsc-cache-busting.js +1 -1
- package/dist/server/app-rsc-errors.js +20 -16
- package/dist/server/app-rsc-handler.d.ts +11 -0
- package/dist/server/app-rsc-handler.js +291 -125
- package/dist/server/app-rsc-request-normalization.d.ts +3 -1
- package/dist/server/app-rsc-request-normalization.js +4 -2
- package/dist/server/app-rsc-response-stage.d.ts +1 -1
- package/dist/server/app-rsc-response-stage.js +10 -6
- package/dist/server/app-rsc-route-matching.d.ts +9 -1
- package/dist/server/app-rsc-route-matching.js +36 -19
- package/dist/server/app-segment-config.d.ts +2 -1
- package/dist/server/app-server-action-execution.d.ts +2 -1
- package/dist/server/app-server-action-execution.js +62 -23
- package/dist/server/app-ssr-entry.d.ts +3 -2
- package/dist/server/app-ssr-entry.js +8 -5
- package/dist/server/app-ssr-stream.d.ts +6 -1
- package/dist/server/app-ssr-stream.js +34 -15
- package/dist/server/app-worker-stages.d.ts +7 -2
- package/dist/server/app-worker-stages.js +4 -4
- package/dist/server/cacheability-request.js +11 -7
- package/dist/server/client-build-files.d.ts +16 -0
- package/dist/server/client-build-files.js +36 -0
- package/dist/server/client-reuse-manifest.js +1 -1
- package/dist/server/config-headers.js +2 -0
- package/dist/server/dev-server.js +50 -29
- package/dist/server/dev-stack-sourcemap.js +18 -23
- package/dist/server/document-initial-head.d.ts +0 -11
- package/dist/server/document-initial-head.js +1 -1
- package/dist/server/forwarded-middleware-context.d.ts +5 -0
- package/dist/server/forwarded-middleware-context.js +22 -0
- package/dist/server/headers.d.ts +18 -1
- package/dist/server/headers.js +19 -2
- package/dist/server/http-conditional.d.ts +11 -0
- package/dist/server/http-conditional.js +10 -1
- package/dist/server/http-error-responses.d.ts +1 -1
- package/dist/server/image-optimization.d.ts +54 -0
- package/dist/server/image-optimization.js +118 -9
- package/dist/server/isr-cache.d.ts +10 -0
- package/dist/server/isr-cache.js +43 -8
- package/dist/server/metadata-route-response.js +9 -8
- package/dist/server/middleware-matcher-pattern.js +8 -4
- package/dist/server/middleware-matcher.d.ts +2 -0
- package/dist/server/middleware-matcher.js +23 -2
- package/dist/server/middleware-response-headers.js +2 -0
- package/dist/server/middleware-runtime.d.ts +13 -0
- package/dist/server/middleware-runtime.js +33 -20
- package/dist/server/middleware.d.ts +1 -1
- package/dist/server/middleware.js +3 -1
- package/dist/server/multi-stage.d.ts +19 -0
- package/dist/server/navigation-planner.js +1 -1
- package/dist/server/node-response-signal.d.ts +16 -0
- package/dist/server/node-response-signal.js +26 -0
- package/dist/server/normalize-path.d.ts +0 -13
- package/dist/server/normalize-path.js +1 -34
- package/dist/server/open-redirect.d.ts +47 -0
- package/dist/server/open-redirect.js +74 -1
- package/dist/server/pages-api-route.d.ts +3 -2
- package/dist/server/pages-api-route.js +4 -3
- package/dist/server/pages-cache-tag.d.ts +12 -0
- package/dist/server/pages-cache-tag.js +19 -0
- package/dist/server/pages-client-assets.d.ts +2 -0
- package/dist/server/pages-data-route.d.ts +10 -0
- package/dist/server/pages-data-route.js +19 -2
- package/dist/server/pages-document-asset-props.d.ts +5 -1
- package/dist/server/pages-document-asset-props.js +13 -5
- package/dist/server/pages-document-splice.d.ts +19 -0
- package/dist/server/pages-document-splice.js +55 -0
- package/dist/server/pages-i18n.d.ts +12 -1
- package/dist/server/pages-i18n.js +18 -4
- package/dist/server/pages-node-compat.d.ts +16 -5
- package/dist/server/pages-node-compat.js +20 -12
- package/dist/server/pages-page-data.d.ts +32 -24
- package/dist/server/pages-page-data.js +50 -120
- package/dist/server/pages-page-handler.d.ts +0 -6
- package/dist/server/pages-page-handler.js +209 -130
- package/dist/server/pages-page-response.d.ts +12 -6
- package/dist/server/pages-page-response.js +41 -34
- package/dist/server/pages-request-pipeline.d.ts +10 -2
- package/dist/server/pages-request-pipeline.js +37 -18
- package/dist/server/pages-request-stage-entry.d.ts +6 -0
- package/dist/server/pages-request-stage-entry.js +79 -16
- package/dist/server/pages-response-stage-entry.js +4 -0
- package/dist/server/pages-revalidate.js +1 -1
- package/dist/server/pages-router-entry.js +4 -3
- package/dist/server/prerender-manifest.d.ts +3 -3
- package/dist/server/prerender-manifest.js +4 -3
- package/dist/server/prerender-work-unit-setup.js +4 -0
- package/dist/server/prod-server.d.ts +20 -7
- package/dist/server/prod-server.js +343 -129
- package/dist/server/proper-error.d.ts +4 -0
- package/dist/server/proper-error.js +31 -0
- package/dist/server/request-pipeline.d.ts +24 -9
- package/dist/server/request-pipeline.js +24 -9
- package/dist/server/response-compression.d.ts +43 -0
- package/dist/server/response-compression.js +111 -0
- package/dist/server/revalidation-request.d.ts +1 -0
- package/dist/server/revalidation-request.js +1 -1
- package/dist/server/rsc-stream-hints.js +31 -2
- package/dist/server/seed-cache.d.ts +1 -0
- package/dist/server/seed-cache.js +15 -11
- package/dist/server/server-reference-loader.d.ts +4 -0
- package/dist/server/server-reference-loader.js +29 -0
- package/dist/server/socket-error-backstop.d.ts +23 -1
- package/dist/server/socket-error-backstop.js +69 -17
- package/dist/server/static-file-signal.d.ts +15 -4
- package/dist/server/static-file-signal.js +46 -12
- package/dist/server/worker-image-optimization.d.ts +34 -0
- package/dist/server/worker-image-optimization.js +86 -0
- package/dist/server/worker-stages.d.ts +1 -1
- package/dist/server/worker-stages.js +2 -2
- package/dist/server/worker-utils.d.ts +8 -3
- package/dist/server/worker-utils.js +12 -7
- package/dist/shims/app-router-scroll.js +4 -1
- package/dist/shims/app-router-tree-context.d.ts +5 -0
- package/dist/shims/app-router-tree-context.js +27 -0
- package/dist/shims/cache-adapter-instantiate.d.ts +50 -0
- package/dist/shims/cache-adapter-instantiate.js +118 -0
- package/dist/shims/cache-callable-runtime.d.ts +6 -42
- package/dist/shims/cache-callable-runtime.js +65 -174
- package/dist/shims/cache-flight-arguments.d.ts +22 -0
- package/dist/shims/cache-flight-arguments.js +56 -0
- package/dist/shims/cache-handler.js +1 -9
- package/dist/shims/cache-request-state.d.ts +15 -0
- package/dist/shims/cache-request-state.js +31 -8
- package/dist/shims/cache-runtime.d.ts +12 -20
- package/dist/shims/cache-runtime.js +166 -225
- package/dist/shims/cache.d.ts +4 -4
- package/dist/shims/cache.js +44 -16
- package/dist/shims/cacheability-classification.d.ts +7 -0
- package/dist/shims/cdn-cache.d.ts +8 -0
- package/dist/shims/cdn-cache.js +17 -1
- package/dist/shims/disable-smooth-scroll.d.ts +3 -0
- package/dist/shims/disable-smooth-scroll.js +36 -0
- package/dist/shims/document-placeholders.d.ts +19 -0
- package/dist/shims/document-placeholders.js +45 -0
- package/dist/shims/document.js +21 -5
- package/dist/shims/dynamic-preload-chunks.d.ts +1 -0
- package/dist/shims/dynamic-preload-chunks.js +13 -9
- package/dist/shims/dynamic.d.ts +1 -1
- package/dist/shims/dynamic.js +60 -8
- package/dist/shims/error-boundary-navigation.d.ts +7 -0
- package/dist/shims/error-boundary-navigation.js +19 -1
- package/dist/shims/error-boundary.d.ts +10 -10
- package/dist/shims/error-boundary.js +26 -7
- package/dist/shims/fetch-cache-state.d.ts +66 -0
- package/dist/shims/fetch-cache-state.js +92 -0
- package/dist/shims/fetch-cache.d.ts +3 -61
- package/dist/shims/fetch-cache.js +161 -108
- package/dist/shims/form.js +1 -1
- package/dist/shims/head.d.ts +10 -6
- package/dist/shims/head.js +15 -13
- package/dist/shims/headers.d.ts +13 -0
- package/dist/shims/headers.js +47 -4
- package/dist/shims/image.js +2 -1
- package/dist/shims/internal/app-page-props-cache-key.d.ts +5 -2
- package/dist/shims/internal/app-page-props-cache-key.js +24 -15
- package/dist/shims/internal/headers-state.d.ts +0 -1
- package/dist/shims/internal/work-store-scope.d.ts +5 -0
- package/dist/shims/internal/work-store-scope.js +24 -0
- package/dist/shims/metadata.js +3 -3
- package/dist/shims/navigation-errors.d.ts +6 -0
- package/dist/shims/navigation-errors.js +11 -1
- package/dist/shims/navigation.d.ts +7 -0
- package/dist/shims/navigation.js +45 -9
- package/dist/shims/request-context.d.ts +11 -0
- package/dist/shims/request-state-types.d.ts +2 -1
- package/dist/shims/router.js +12 -20
- package/dist/shims/server.d.ts +16 -0
- package/dist/shims/server.js +24 -4
- package/dist/shims/thenable-params.d.ts +4 -0
- package/dist/shims/thenable-params.js +25 -3
- package/dist/shims/unified-request-context.d.ts +1 -1
- package/dist/shims/unified-request-context.js +1 -0
- package/dist/shims/url-utils.d.ts +2 -0
- package/dist/shims/url-utils.js +3 -1
- package/dist/utils/client-runtime-metadata.js +1 -1
- package/dist/utils/hash.d.ts +7 -0
- package/dist/utils/hash.js +11 -1
- package/dist/utils/mdx-scan.d.ts +5 -0
- package/dist/utils/mdx-scan.js +8 -1
- package/dist/utils/package-name.d.ts +6 -0
- package/dist/utils/package-name.js +12 -1
- package/dist/utils/plugin-rsc-version.d.ts +4 -0
- package/dist/utils/plugin-rsc-version.js +15 -0
- package/dist/utils/prerender-output-paths.js +15 -4
- package/dist/utils/public-file-path.d.ts +10 -2
- package/dist/utils/public-file-path.js +13 -9
- package/dist/utils/public-routes.js +2 -2
- package/dist/utils/react-version.d.ts +3 -0
- package/dist/utils/react-version.js +1 -1
- package/dist/utils/regex-safety.d.ts +10 -0
- package/dist/utils/regex-safety.js +70 -18
- package/package.json +9 -5
|
@@ -10,6 +10,8 @@ export type MiddlewareResult = {
|
|
|
10
10
|
redirectUrl?: string;
|
|
11
11
|
redirectStatus?: number;
|
|
12
12
|
rewriteUrl?: string;
|
|
13
|
+
/** False when a same-origin rewrite target was outside the basePath. */
|
|
14
|
+
rewriteHadBasePath?: boolean;
|
|
13
15
|
rewriteStatus?: number;
|
|
14
16
|
status?: number;
|
|
15
17
|
responseHeaders?: Headers;
|
|
@@ -41,6 +43,11 @@ type ExecuteMiddlewareOptions = {
|
|
|
41
43
|
* must derive and forward this from trusted URL normalization.
|
|
42
44
|
*/
|
|
43
45
|
isDataRequest?: boolean;
|
|
46
|
+
/**
|
|
47
|
+
* The data request names a buildId other than the current build. Adapters
|
|
48
|
+
* pass it raw to middleware only under `skipProxyUrlNormalize`.
|
|
49
|
+
*/
|
|
50
|
+
isStaleDataRequest?: boolean;
|
|
44
51
|
isProxy: boolean;
|
|
45
52
|
module: MiddlewareModule;
|
|
46
53
|
normalizedPathname?: string;
|
|
@@ -63,6 +70,12 @@ type ExecuteMiddlewareOptions = {
|
|
|
63
70
|
* `new URL('/x', req.url)`.
|
|
64
71
|
*/
|
|
65
72
|
trailingSlash?: boolean;
|
|
73
|
+
/**
|
|
74
|
+
* next.config `skipTrailingSlashRedirect`. Middleware then sees requests in
|
|
75
|
+
* either slash form, so NextURL keeps each request's own trailing slash and
|
|
76
|
+
* redirect Locations keep theirs, as Next.js's `getNextPathnameInfo` does.
|
|
77
|
+
*/
|
|
78
|
+
skipTrailingSlashRedirect?: boolean;
|
|
66
79
|
};
|
|
67
80
|
type RunGeneratedMiddlewareOptions = ExecuteMiddlewareOptions & {
|
|
68
81
|
ctx?: ExecutionContextLike;
|
|
@@ -8,12 +8,14 @@ import { getRequestExecutionContext, runWithExecutionContext } from "../shims/re
|
|
|
8
8
|
import "./server-globals.js";
|
|
9
9
|
import { MIDDLEWARE_REWRITE_HEADER } from "./headers.js";
|
|
10
10
|
import { NextFetchEvent, NextRequest } from "../shims/server.js";
|
|
11
|
+
import { runInWorkStoreScope } from "../shims/internal/work-store-scope.js";
|
|
11
12
|
import { normalizePath } from "./normalize-path.js";
|
|
12
13
|
import { matchesMiddleware, matchesMiddlewarePathname } from "./middleware-matcher.js";
|
|
13
14
|
import { badRequestResponse, internalServerErrorResponse } from "./http-error-responses.js";
|
|
14
15
|
import { isOpenRedirectShaped } from "./open-redirect.js";
|
|
15
16
|
import { processMiddlewareHeaders } from "./request-pipeline.js";
|
|
16
17
|
import { normalizeDefaultLocalePathname } from "./pages-i18n.js";
|
|
18
|
+
import { nextDataMiddlewareMatchPathname } from "./pages-data-route.js";
|
|
17
19
|
import { reportRequestError } from "./instrumentation.js";
|
|
18
20
|
import { isValidNavigationSignalError } from "../utils/navigation-signal.js";
|
|
19
21
|
import { frameworkTracer } from "./tracer.js";
|
|
@@ -133,7 +135,7 @@ function resolveMiddlewarePathname(request) {
|
|
|
133
135
|
return badRequestResponse();
|
|
134
136
|
}
|
|
135
137
|
}
|
|
136
|
-
function createNextRequest(request, i18nConfig, basePath, trailingSlash, hadBasePath, requestBodyAlreadyIsolated = false) {
|
|
138
|
+
function createNextRequest(request, i18nConfig, basePath, trailingSlash, hadBasePath, requestBodyAlreadyIsolated = false, skipTrailingSlashRedirect = false) {
|
|
137
139
|
const url = new URL(request.url);
|
|
138
140
|
let mwRequest = !requestBodyAlreadyIsolated && request.body && !request.bodyUsed ? request.clone() : request;
|
|
139
141
|
const requestPathname = url.pathname;
|
|
@@ -143,10 +145,11 @@ function createNextRequest(request, i18nConfig, basePath, trailingSlash, hadBase
|
|
|
143
145
|
mwUrl.pathname = mwPathname;
|
|
144
146
|
mwRequest = new Request(mwUrl, mwRequest);
|
|
145
147
|
}
|
|
146
|
-
const nextConfig = basePath || i18nConfig || trailingSlash ? {
|
|
148
|
+
const nextConfig = basePath || i18nConfig || trailingSlash || skipTrailingSlashRedirect ? {
|
|
147
149
|
basePath: basePath ?? "",
|
|
148
150
|
i18n: i18nConfig ?? void 0,
|
|
149
|
-
trailingSlash: trailingSlash ?? void 0
|
|
151
|
+
trailingSlash: trailingSlash ?? void 0,
|
|
152
|
+
skipTrailingSlashRedirect
|
|
150
153
|
} : void 0;
|
|
151
154
|
const nextRequest = mwRequest instanceof NextRequest && (!mwRequest.body || mwRequest.bodyUsed) ? mwRequest : new NextRequest(mwRequest, nextConfig ? { nextConfig } : void 0);
|
|
152
155
|
if (mwRequest !== request && mwRequest.body && !mwRequest.bodyUsed && !mwRequest.body.locked) mwRequest.body.cancel().catch(() => {});
|
|
@@ -164,7 +167,10 @@ function releaseMiddlewareRequestBody(request, waitUntilPromises, retainedByResp
|
|
|
164
167
|
}
|
|
165
168
|
Promise.allSettled(waitUntilPromises).then(cancel);
|
|
166
169
|
}
|
|
167
|
-
|
|
170
|
+
function executeMiddleware(options) {
|
|
171
|
+
return runInWorkStoreScope(() => executeMiddlewareModule(options));
|
|
172
|
+
}
|
|
173
|
+
async function executeMiddlewareModule(options) {
|
|
168
174
|
const middlewareFn = resolveMiddlewareModuleHandler(options.module, {
|
|
169
175
|
filePath: options.filePath,
|
|
170
176
|
isProxy: options.isProxy
|
|
@@ -187,14 +193,17 @@ async function executeMiddleware(options) {
|
|
|
187
193
|
if (options.hadBasePath === true) return candidate;
|
|
188
194
|
return matcher === void 0 ? candidate : null;
|
|
189
195
|
};
|
|
190
|
-
const
|
|
196
|
+
const isStaleDataRequest = options.isDataRequest === true && options.isStaleDataRequest === true;
|
|
197
|
+
const toPageMatcherPathname = (candidate) => candidate !== null && options.isDataRequest && !isStaleDataRequest ? nextDataMiddlewareMatchPathname(candidate) ?? candidate : candidate;
|
|
198
|
+
const encodedMatchPathname = toPageMatcherPathname(prepareMatcherPathname(encodedRequestPathname));
|
|
191
199
|
let decodedMatchPathname = encodedMatchPathname;
|
|
192
200
|
try {
|
|
193
201
|
if (encodedMatchPathname !== null) decodedMatchPathname = decodeURIComponent(encodedMatchPathname);
|
|
194
|
-
else if (!options.i18nConfig) decodedMatchPathname = prepareMatcherPathname(decodeURIComponent(encodedRequestPathname));
|
|
202
|
+
else if (!options.i18nConfig) decodedMatchPathname = toPageMatcherPathname(prepareMatcherPathname(decodeURIComponent(encodedRequestPathname)));
|
|
195
203
|
} catch {}
|
|
196
204
|
let localeContext;
|
|
197
|
-
if (
|
|
205
|
+
if (isStaleDataRequest) localeContext = { kind: "next-data" };
|
|
206
|
+
else if (options.i18nConfig && encodedMatchPathname !== null) {
|
|
198
207
|
const hostname = normalizeHost(options.request.headers.get("host"), requestUrl.hostname);
|
|
199
208
|
const firstSegment = encodedMatchPathname.split("/", 3)[1];
|
|
200
209
|
if (firstSegment !== void 0 && options.i18nConfig.locales.some((locale) => locale.toLowerCase() === firstSegment.toLowerCase())) localeContext = { kind: "literal" };
|
|
@@ -210,7 +219,7 @@ async function executeMiddleware(options) {
|
|
|
210
219
|
const decodedMatches = !encodedMatches && decodedMatchPathname !== null && decodedMatchPathname !== encodedMatchPathname && matchesMiddleware(decodedMatchPathname, matcher, options.request, options.i18nConfig, localeContext);
|
|
211
220
|
if (!encodedMatches && !decodedMatches) return { continue: true };
|
|
212
221
|
options.onMatch?.();
|
|
213
|
-
const nextRequest = createNextRequest(options.request, options.i18nConfig, options.basePath, options.trailingSlash, hadBasePath, options.requestBodyAlreadyIsolated);
|
|
222
|
+
const nextRequest = createNextRequest(options.request, options.i18nConfig, options.basePath, options.trailingSlash, hadBasePath, options.requestBodyAlreadyIsolated, options.skipTrailingSlashRedirect);
|
|
214
223
|
if (options.isDataRequest) Object.defineProperty(nextRequest, "__isData", {
|
|
215
224
|
enumerable: false,
|
|
216
225
|
value: true
|
|
@@ -248,21 +257,12 @@ async function executeMiddleware(options) {
|
|
|
248
257
|
waitUntilPromises
|
|
249
258
|
};
|
|
250
259
|
}
|
|
251
|
-
if (response.headers.get("x-middleware-next") === "1") {
|
|
252
|
-
releaseMiddlewareRequestBody(nextRequest, waitUntilPromises);
|
|
253
|
-
return {
|
|
254
|
-
continue: true,
|
|
255
|
-
responseHeaders: collectMiddlewareHeaders(response),
|
|
256
|
-
status: response.status !== 200 ? response.status : void 0,
|
|
257
|
-
waitUntilPromises
|
|
258
|
-
};
|
|
259
|
-
}
|
|
260
260
|
if (response.status >= 300 && response.status < 400) {
|
|
261
261
|
const location = response.headers.get("Location") ?? response.headers.get("location");
|
|
262
262
|
if (location) {
|
|
263
263
|
const relativeLocation = relativizeLocation(location, options.request.url);
|
|
264
264
|
let normalizedLocation = relativeLocation;
|
|
265
|
-
if (options.trailingSlash !== void 0) try {
|
|
265
|
+
if (options.trailingSlash !== void 0 && !options.skipTrailingSlashRedirect) try {
|
|
266
266
|
const loc = new URL(relativeLocation, options.request.url);
|
|
267
267
|
if (loc.origin === new URL(options.request.url).origin) {
|
|
268
268
|
const p = loc.pathname;
|
|
@@ -309,11 +309,14 @@ async function executeMiddleware(options) {
|
|
|
309
309
|
const rewriteUrl = response.headers.get(MIDDLEWARE_REWRITE_HEADER);
|
|
310
310
|
if (rewriteUrl) {
|
|
311
311
|
let rewritePath;
|
|
312
|
+
let rewriteHadBasePath;
|
|
312
313
|
try {
|
|
313
314
|
const rewriteParsed = new URL(rewriteUrl, options.request.url);
|
|
314
315
|
const requestOrigin = new URL(options.request.url).origin;
|
|
315
|
-
if (rewriteParsed.origin === requestOrigin)
|
|
316
|
-
|
|
316
|
+
if (rewriteParsed.origin === requestOrigin) {
|
|
317
|
+
rewriteHadBasePath = !options.basePath || hasBasePath(rewriteParsed.pathname, options.basePath);
|
|
318
|
+
rewritePath = (options.basePath ? stripBasePath(rewriteParsed.pathname, options.basePath) : rewriteParsed.pathname) + rewriteParsed.search;
|
|
319
|
+
} else rewritePath = rewriteParsed.href;
|
|
317
320
|
} catch {
|
|
318
321
|
rewritePath = rewriteUrl;
|
|
319
322
|
}
|
|
@@ -321,12 +324,22 @@ async function executeMiddleware(options) {
|
|
|
321
324
|
return {
|
|
322
325
|
continue: true,
|
|
323
326
|
rewriteUrl: rewritePath,
|
|
327
|
+
rewriteHadBasePath,
|
|
324
328
|
rewriteStatus: response.status !== 200 ? response.status : void 0,
|
|
325
329
|
responseHeaders: collectMiddlewareHeaders(response),
|
|
326
330
|
status: response.status !== 200 ? response.status : void 0,
|
|
327
331
|
waitUntilPromises
|
|
328
332
|
};
|
|
329
333
|
}
|
|
334
|
+
if (response.headers.get("x-middleware-next") === "1") {
|
|
335
|
+
releaseMiddlewareRequestBody(nextRequest, waitUntilPromises);
|
|
336
|
+
return {
|
|
337
|
+
continue: true,
|
|
338
|
+
responseHeaders: collectMiddlewareHeaders(response),
|
|
339
|
+
status: response.status !== 200 ? response.status : void 0,
|
|
340
|
+
waitUntilPromises
|
|
341
|
+
};
|
|
342
|
+
}
|
|
330
343
|
releaseMiddlewareRequestBody(nextRequest, waitUntilPromises, response.body === nextRequest.body);
|
|
331
344
|
return {
|
|
332
345
|
continue: false,
|
|
@@ -44,6 +44,6 @@ export declare function findMiddlewareFile(root: string, fileMatcher: ValidFileM
|
|
|
44
44
|
* @param request - The incoming Request object
|
|
45
45
|
* @returns Middleware result describing what action to take
|
|
46
46
|
*/
|
|
47
|
-
export declare function runMiddleware(runner: ModuleRunner, middlewarePath: string, request: Request, i18nConfig?: NextI18nConfig | null, basePath?: string, trailingSlash?: boolean, isDataRequest?: boolean, normalizedPathname?: string): Promise<MiddlewareResult>;
|
|
47
|
+
export declare function runMiddleware(runner: ModuleRunner, middlewarePath: string, request: Request, i18nConfig?: NextI18nConfig | null, basePath?: string, trailingSlash?: boolean, isDataRequest?: boolean, normalizedPathname?: string, isStaleDataRequest?: boolean, skipTrailingSlashRedirect?: boolean): Promise<MiddlewareResult>;
|
|
48
48
|
//#endregion
|
|
49
49
|
export { matchPattern, matchesMiddleware };
|
|
@@ -77,7 +77,7 @@ function isMiddlewareModule(value) {
|
|
|
77
77
|
* @param request - The incoming Request object
|
|
78
78
|
* @returns Middleware result describing what action to take
|
|
79
79
|
*/
|
|
80
|
-
async function runMiddleware(runner, middlewarePath, request, i18nConfig, basePath, trailingSlash, isDataRequest, normalizedPathname) {
|
|
80
|
+
async function runMiddleware(runner, middlewarePath, request, i18nConfig, basePath, trailingSlash, isDataRequest, normalizedPathname, isStaleDataRequest, skipTrailingSlashRedirect) {
|
|
81
81
|
const mod = await runner.import(middlewarePath);
|
|
82
82
|
if (!isMiddlewareModule(mod)) throw new Error(`Middleware module "${middlewarePath}" did not evaluate to an object.`);
|
|
83
83
|
return runGeneratedMiddleware({
|
|
@@ -87,10 +87,12 @@ async function runMiddleware(runner, middlewarePath, request, i18nConfig, basePa
|
|
|
87
87
|
i18nConfig,
|
|
88
88
|
includeErrorDetails: process.env.NODE_ENV !== "production",
|
|
89
89
|
isDataRequest,
|
|
90
|
+
isStaleDataRequest,
|
|
90
91
|
isProxy: isProxyFile(middlewarePath),
|
|
91
92
|
module: mod,
|
|
92
93
|
normalizedPathname,
|
|
93
94
|
request,
|
|
95
|
+
skipTrailingSlashRedirect,
|
|
94
96
|
trailingSlash
|
|
95
97
|
});
|
|
96
98
|
}
|
|
@@ -21,6 +21,25 @@ export type VinextResponseStageDispatchOptions = {
|
|
|
21
21
|
* opt into completed-response admission and honor core's `no-store` policy.
|
|
22
22
|
*/
|
|
23
23
|
cache: "shared" | "bypass";
|
|
24
|
+
/**
|
|
25
|
+
* Render an App page document whole before its first byte, as Next.js
|
|
26
|
+
* renders a static page, so its streamed metadata is in `<head>`. An adapter
|
|
27
|
+
* sets this for a render no client is waiting on, such as a regeneration or
|
|
28
|
+
* a deployment warm-up. It is not a cache selector: a transport must not
|
|
29
|
+
* partition its lookup or stored entry by it.
|
|
30
|
+
*/
|
|
31
|
+
renderWholeDocument?: boolean;
|
|
32
|
+
/**
|
|
33
|
+
* Regenerate an entry the adapter already stores for this build. Set only
|
|
34
|
+
* from a platform-invoked regeneration callback or a platform cache's
|
|
35
|
+
* conditional revalidation of a stored entry, never from anything a client
|
|
36
|
+
* can send: core then treats the path as admitted and skips
|
|
37
|
+
* request-time admission work such as a Pages `getStaticPaths()` call, as
|
|
38
|
+
* Next.js regenerates a generated path without consulting it. It is not a
|
|
39
|
+
* cache selector: a transport must not partition its lookup or stored entry
|
|
40
|
+
* by it.
|
|
41
|
+
*/
|
|
42
|
+
regeneratesStoredEntry?: boolean;
|
|
24
43
|
/**
|
|
25
44
|
* Query-free identity of a shared App page GET/HEAD dispatch, supplied only
|
|
26
45
|
* to adapters that declare `responseStageCacheIdentity: "query-free"` and
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import { splitPathnameForRouteMatch } from "../routing/utils.js";
|
|
2
2
|
import { stripBasePath } from "../utils/base-path.js";
|
|
3
|
+
import { matchRoutePattern, matchRoutePatternPrefix, matchRoutePatternWithOptionalDynamicSegments } from "../routing/route-pattern.js";
|
|
3
4
|
import { AppElementsWire, compareAppElementsSlotIds } from "./app-elements-wire.js";
|
|
4
5
|
import "./app-elements.js";
|
|
5
|
-
import { matchRoutePattern, matchRoutePatternPrefix, matchRoutePatternWithOptionalDynamicSegments } from "../routing/route-pattern.js";
|
|
6
6
|
import { resolveHardNavigationTargetFromRscResponse, resolveRscCompatibilityNavigationDecision } from "./app-rsc-cache-busting.js";
|
|
7
7
|
import { resolveRscRedirectLifecycleHop, resolveStreamedRscRedirectLifecycleHop } from "./app-browser-rsc-redirect.js";
|
|
8
8
|
import { NavigationTraceReasonCodes, createNavigationLifecycleTraceFields, createNavigationTrace } from "./navigation-trace.js";
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { Writable } from "node:stream";
|
|
2
|
+
//#region src/server/node-response-signal.d.ts
|
|
3
|
+
export declare class ResponseAborted extends Error {
|
|
4
|
+
readonly name = "ResponseAborted";
|
|
5
|
+
}
|
|
6
|
+
/**
|
|
7
|
+
* Creates an AbortSignal tied to the closing of a ServerResponse.
|
|
8
|
+
*
|
|
9
|
+
* The incoming request cannot be used: once its body has been fully read the
|
|
10
|
+
* readable side is done, so a later client disconnect fires nothing on it
|
|
11
|
+
* (and its `close` event also fires on normal requests). If `finish` fires
|
|
12
|
+
* first, `res.end()` completed and the following `close` is our own teardown;
|
|
13
|
+
* if `close` fires first, the client disconnected before we finished.
|
|
14
|
+
*/
|
|
15
|
+
export declare function signalFromNodeResponse(response: Writable): AbortSignal;
|
|
16
|
+
//#endregion
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
//#region src/server/node-response-signal.ts
|
|
2
|
+
const ResponseAbortedName = "ResponseAborted";
|
|
3
|
+
var ResponseAborted = class extends Error {
|
|
4
|
+
name = ResponseAbortedName;
|
|
5
|
+
};
|
|
6
|
+
/**
|
|
7
|
+
* Creates an AbortSignal tied to the closing of a ServerResponse.
|
|
8
|
+
*
|
|
9
|
+
* The incoming request cannot be used: once its body has been fully read the
|
|
10
|
+
* readable side is done, so a later client disconnect fires nothing on it
|
|
11
|
+
* (and its `close` event also fires on normal requests). If `finish` fires
|
|
12
|
+
* first, `res.end()` completed and the following `close` is our own teardown;
|
|
13
|
+
* if `close` fires first, the client disconnected before we finished.
|
|
14
|
+
*/
|
|
15
|
+
function signalFromNodeResponse(response) {
|
|
16
|
+
const { errored, destroyed } = response;
|
|
17
|
+
if (errored || destroyed) return AbortSignal.abort(errored ?? new ResponseAborted());
|
|
18
|
+
const controller = new AbortController();
|
|
19
|
+
response.once("close", () => {
|
|
20
|
+
if (response.writableFinished) return;
|
|
21
|
+
controller.abort(new ResponseAborted());
|
|
22
|
+
});
|
|
23
|
+
return controller.signal;
|
|
24
|
+
}
|
|
25
|
+
//#endregion
|
|
26
|
+
export { ResponseAborted, signalFromNodeResponse };
|
|
@@ -1,17 +1,4 @@
|
|
|
1
1
|
//#region src/server/normalize-path.d.ts
|
|
2
|
-
export declare function escapePathDelimiters(segment: string, escapeEncoded?: boolean): string;
|
|
3
|
-
/**
|
|
4
|
-
* Decode a URL pathname segment-by-segment, preserving encoded path delimiters.
|
|
5
|
-
* Non-ASCII characters (e.g. %C3%A9 -> e) are decoded, but structural characters
|
|
6
|
-
* like %2F (/) %23 (#) %3F (?) %5C (\) are re-encoded after decoding.
|
|
7
|
-
*
|
|
8
|
-
* This prevents encoded slashes from changing the path structure (e.g.
|
|
9
|
-
* /admin%2Fpanel stays as a single segment, not /admin/panel).
|
|
10
|
-
*
|
|
11
|
-
* Ported from Next.js: packages/next/src/server/lib/router-utils/decode-path-params.ts
|
|
12
|
-
* https://github.com/vercel/next.js/blob/canary/packages/next/src/server/lib/router-utils/decode-path-params.ts
|
|
13
|
-
*/
|
|
14
|
-
export declare function decodePathParams(pathname: string): string;
|
|
15
2
|
export declare function isInterceptionMatchedUrlPath(value: string): boolean;
|
|
16
3
|
/**
|
|
17
4
|
* Path normalization utility for request handling.
|
|
@@ -1,37 +1,4 @@
|
|
|
1
1
|
//#region src/server/normalize-path.ts
|
|
2
|
-
/**
|
|
3
|
-
* Re-encode path delimiter characters that were decoded by decodeURIComponent.
|
|
4
|
-
* After decoding a URL segment, characters like / # ? \ need to be re-encoded
|
|
5
|
-
* so they don't change the path structure.
|
|
6
|
-
*
|
|
7
|
-
* Ported from Next.js: packages/next/src/shared/lib/router/utils/escape-path-delimiters.ts
|
|
8
|
-
* https://github.com/vercel/next.js/blob/canary/packages/next/src/shared/lib/router/utils/escape-path-delimiters.ts
|
|
9
|
-
*/
|
|
10
|
-
const PATH_DELIMITERS_RE = /([/#?])/gi;
|
|
11
|
-
const PATH_DELIMITERS_ENCODED_RE = /([/#?]|%(2f|23|3f|5c))/gi;
|
|
12
|
-
function escapePathDelimiters(segment, escapeEncoded) {
|
|
13
|
-
return segment.replace(escapeEncoded ? PATH_DELIMITERS_ENCODED_RE : PATH_DELIMITERS_RE, (char) => encodeURIComponent(char));
|
|
14
|
-
}
|
|
15
|
-
/**
|
|
16
|
-
* Decode a URL pathname segment-by-segment, preserving encoded path delimiters.
|
|
17
|
-
* Non-ASCII characters (e.g. %C3%A9 -> e) are decoded, but structural characters
|
|
18
|
-
* like %2F (/) %23 (#) %3F (?) %5C (\) are re-encoded after decoding.
|
|
19
|
-
*
|
|
20
|
-
* This prevents encoded slashes from changing the path structure (e.g.
|
|
21
|
-
* /admin%2Fpanel stays as a single segment, not /admin/panel).
|
|
22
|
-
*
|
|
23
|
-
* Ported from Next.js: packages/next/src/server/lib/router-utils/decode-path-params.ts
|
|
24
|
-
* https://github.com/vercel/next.js/blob/canary/packages/next/src/server/lib/router-utils/decode-path-params.ts
|
|
25
|
-
*/
|
|
26
|
-
function decodePathParams(pathname) {
|
|
27
|
-
return pathname.split("/").map((seg) => {
|
|
28
|
-
try {
|
|
29
|
-
return escapePathDelimiters(decodeURIComponent(seg), true);
|
|
30
|
-
} catch {
|
|
31
|
-
return seg;
|
|
32
|
-
}
|
|
33
|
-
}).join("/");
|
|
34
|
-
}
|
|
35
2
|
function isInterceptionMatchedUrlPath(value) {
|
|
36
3
|
return value.startsWith("/") && !value.startsWith("//") && !value.includes("?") && !value.includes("#") && !value.includes("\\") && !value.includes("\0");
|
|
37
4
|
}
|
|
@@ -67,4 +34,4 @@ function normalizePath(pathname) {
|
|
|
67
34
|
return "/" + resolved.join("/");
|
|
68
35
|
}
|
|
69
36
|
//#endregion
|
|
70
|
-
export {
|
|
37
|
+
export { isInterceptionMatchedUrlPath, normalizePath };
|
|
@@ -8,4 +8,51 @@
|
|
|
8
8
|
* to resolve a `Location` containing the pathname as protocol-relative.
|
|
9
9
|
*/
|
|
10
10
|
export declare function isOpenRedirectShaped(rawPathname: string): boolean;
|
|
11
|
+
/**
|
|
12
|
+
* What a request entry answers for a raw path containing a backslash or a
|
|
13
|
+
* repeated slash: Next.js's 308, or a 404 when the collapsed path would still
|
|
14
|
+
* be open-redirect shaped.
|
|
15
|
+
*/
|
|
16
|
+
export type RepeatedSlashRedirect = {
|
|
17
|
+
status: 308;
|
|
18
|
+
location: string;
|
|
19
|
+
} | {
|
|
20
|
+
status: 404;
|
|
21
|
+
};
|
|
22
|
+
/**
|
|
23
|
+
* Returns Next.js's redirect for a raw request path containing a backslash or
|
|
24
|
+
* a repeated slash, or `null` when the path needs no change.
|
|
25
|
+
*
|
|
26
|
+
* Ported from Next.js's request entry (`base-server.ts` / `resolve-routes.ts`,
|
|
27
|
+
* via `normalizeRepeatedSlashes` in `shared/lib/utils.ts`): backslashes in the
|
|
28
|
+
* path become `/`, runs of slashes collapse to one and the query is kept.
|
|
29
|
+
* `next start` then builds the Location with `url.format(parseUrl(...))` in
|
|
30
|
+
* `router-server.ts`, so the result is serialized through a WHATWG URL here
|
|
31
|
+
* too (`/a/%2e%2e//b` → `/b`, `?q="x"` → `?q=%22x%22`, as observed). It runs
|
|
32
|
+
* before basePath handling, so `//` → `/`, `/docs//` → `/docs/` and
|
|
33
|
+
* `//evil.com` → `/evil.com`. Percent-encoded `%2F`/`%5C` are not touched and
|
|
34
|
+
* keep falling through to `isOpenRedirectShaped` (404), as in Next.js.
|
|
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
|
|
38
|
+
* with an encoded slash or backslash (`//%2Fevil.com`, `/.//%2Fevil.com`) is
|
|
39
|
+
* never echoed into a Location or routed: it gets a 404 instead.
|
|
40
|
+
*
|
|
41
|
+
* @param rawUrl - The raw request target: pathname plus optional `?query`
|
|
42
|
+
*/
|
|
43
|
+
export declare function getRepeatedSlashRedirect(rawUrl: string): RepeatedSlashRedirect | null;
|
|
44
|
+
/**
|
|
45
|
+
* Build the Response for a raw request path containing a backslash or a
|
|
46
|
+
* repeated slash. See `getRepeatedSlashRedirect`.
|
|
47
|
+
*/
|
|
48
|
+
export declare function repeatedSlashRedirectResponse(rawUrl: string): Response | null;
|
|
49
|
+
/**
|
|
50
|
+
* Node `ServerResponse` counterpart of `repeatedSlashRedirectResponse`, for
|
|
51
|
+
* entries that see the raw request target (dev and `vinext start`). Returns
|
|
52
|
+
* true when a response was sent.
|
|
53
|
+
*/
|
|
54
|
+
export declare function sendRepeatedSlashRedirect(rawUrl: string, res: {
|
|
55
|
+
writeHead(status: number, headers?: Record<string, string>): unknown;
|
|
56
|
+
end(body: string): unknown;
|
|
57
|
+
}): boolean;
|
|
11
58
|
//#endregion
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { notFoundResponse } from "./http-error-responses.js";
|
|
1
2
|
//#region src/server/open-redirect.ts
|
|
2
3
|
/**
|
|
3
4
|
* Returns true if a request pathname looks like a protocol-relative open
|
|
@@ -17,5 +18,77 @@ function isOpenRedirectShaped(rawPathname) {
|
|
|
17
18
|
}
|
|
18
19
|
return false;
|
|
19
20
|
}
|
|
21
|
+
const REPEATED_SLASH_OR_BACKSLASH_RE = /\\|\/\//;
|
|
22
|
+
const REDIRECT_LOCATION_BASE = "http://vinext.invalid";
|
|
23
|
+
/**
|
|
24
|
+
* Returns Next.js's redirect for a raw request path containing a backslash or
|
|
25
|
+
* a repeated slash, or `null` when the path needs no change.
|
|
26
|
+
*
|
|
27
|
+
* Ported from Next.js's request entry (`base-server.ts` / `resolve-routes.ts`,
|
|
28
|
+
* via `normalizeRepeatedSlashes` in `shared/lib/utils.ts`): backslashes in the
|
|
29
|
+
* path become `/`, runs of slashes collapse to one and the query is kept.
|
|
30
|
+
* `next start` then builds the Location with `url.format(parseUrl(...))` in
|
|
31
|
+
* `router-server.ts`, so the result is serialized through a WHATWG URL here
|
|
32
|
+
* too (`/a/%2e%2e//b` → `/b`, `?q="x"` → `?q=%22x%22`, as observed). It runs
|
|
33
|
+
* before basePath handling, so `//` → `/`, `/docs//` → `/docs/` and
|
|
34
|
+
* `//evil.com` → `/evil.com`. Percent-encoded `%2F`/`%5C` are not touched and
|
|
35
|
+
* keep falling through to `isOpenRedirectShaped` (404), as in Next.js.
|
|
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
|
|
39
|
+
* with an encoded slash or backslash (`//%2Fevil.com`, `/.//%2Fevil.com`) is
|
|
40
|
+
* never echoed into a Location or routed: it gets a 404 instead.
|
|
41
|
+
*
|
|
42
|
+
* @param rawUrl - The raw request target: pathname plus optional `?query`
|
|
43
|
+
*/
|
|
44
|
+
function getRepeatedSlashRedirect(rawUrl) {
|
|
45
|
+
if (!rawUrl.startsWith("/")) return null;
|
|
46
|
+
const urlParts = rawUrl.split("?");
|
|
47
|
+
const pathname = urlParts[0];
|
|
48
|
+
if (!REPEATED_SLASH_OR_BACKSLASH_RE.test(pathname)) return null;
|
|
49
|
+
const cleanUrl = pathname.replaceAll("\\", "/").replace(/\/\/+/g, "/") + (urlParts[1] ? `?${urlParts.slice(1).join("?")}` : "");
|
|
50
|
+
const parsed = new URL(REDIRECT_LOCATION_BASE + cleanUrl);
|
|
51
|
+
const location = parsed.pathname + parsed.search + parsed.hash;
|
|
52
|
+
return isOpenRedirectShaped(location) ? { status: 404 } : {
|
|
53
|
+
status: 308,
|
|
54
|
+
location
|
|
55
|
+
};
|
|
56
|
+
}
|
|
57
|
+
function repeatedSlashRedirectHeaders(location) {
|
|
58
|
+
return {
|
|
59
|
+
Location: location,
|
|
60
|
+
Refresh: `0;url=${location}`
|
|
61
|
+
};
|
|
62
|
+
}
|
|
63
|
+
/**
|
|
64
|
+
* Build the Response for a raw request path containing a backslash or a
|
|
65
|
+
* repeated slash. See `getRepeatedSlashRedirect`.
|
|
66
|
+
*/
|
|
67
|
+
function repeatedSlashRedirectResponse(rawUrl) {
|
|
68
|
+
const redirect = getRepeatedSlashRedirect(rawUrl);
|
|
69
|
+
if (!redirect) return null;
|
|
70
|
+
if (redirect.status === 404) return notFoundResponse();
|
|
71
|
+
return new Response(redirect.location, {
|
|
72
|
+
status: 308,
|
|
73
|
+
headers: repeatedSlashRedirectHeaders(redirect.location)
|
|
74
|
+
});
|
|
75
|
+
}
|
|
76
|
+
/**
|
|
77
|
+
* Node `ServerResponse` counterpart of `repeatedSlashRedirectResponse`, for
|
|
78
|
+
* entries that see the raw request target (dev and `vinext start`). Returns
|
|
79
|
+
* true when a response was sent.
|
|
80
|
+
*/
|
|
81
|
+
function sendRepeatedSlashRedirect(rawUrl, res) {
|
|
82
|
+
const redirect = getRepeatedSlashRedirect(rawUrl);
|
|
83
|
+
if (!redirect) return false;
|
|
84
|
+
if (redirect.status === 404) {
|
|
85
|
+
res.writeHead(404);
|
|
86
|
+
res.end("This page could not be found");
|
|
87
|
+
return true;
|
|
88
|
+
}
|
|
89
|
+
res.writeHead(308, repeatedSlashRedirectHeaders(redirect.location));
|
|
90
|
+
res.end(redirect.location);
|
|
91
|
+
return true;
|
|
92
|
+
}
|
|
20
93
|
//#endregion
|
|
21
|
-
export { isOpenRedirectShaped };
|
|
94
|
+
export { getRepeatedSlashRedirect, isOpenRedirectShaped, repeatedSlashRedirectResponse, sendRepeatedSlashRedirect };
|
|
@@ -2,7 +2,7 @@ import { NextI18nConfig } from "../config/next-config.js";
|
|
|
2
2
|
import { Route } from "../routing/pages-router.js";
|
|
3
3
|
import { ExecutionContextLike } from "../shims/request-context.js";
|
|
4
4
|
import { EdgeApiExecutionRuntime } from "./edge-api-runtime.js";
|
|
5
|
-
import {
|
|
5
|
+
import { PagesApiReqResResponse, PagesReqResRequest, PagesRequestQuery } from "./pages-node-compat.js";
|
|
6
6
|
//#region src/server/pages-api-route.d.ts
|
|
7
7
|
type PagesApiRouteConfig = {
|
|
8
8
|
runtime?: string;
|
|
@@ -36,7 +36,7 @@ type PagesApiRouteConfig = {
|
|
|
36
36
|
externalResolver?: boolean;
|
|
37
37
|
};
|
|
38
38
|
};
|
|
39
|
-
type PagesNodeApiRouteHandler = (req: PagesReqResRequest, res:
|
|
39
|
+
type PagesNodeApiRouteHandler = (req: PagesReqResRequest, res: PagesApiReqResResponse) => unknown;
|
|
40
40
|
type PagesEdgeApiRouteHandler = (request: Request) => Response | Promise<Response>;
|
|
41
41
|
type PagesApiRouteModule = {
|
|
42
42
|
/**
|
|
@@ -80,6 +80,7 @@ type HandlePagesApiRouteOptions = {
|
|
|
80
80
|
allowedRevalidateHeaderKeys?: readonly string[];
|
|
81
81
|
i18n?: NextI18nConfig | null;
|
|
82
82
|
trailingSlash?: boolean;
|
|
83
|
+
skipTrailingSlashRedirect?: boolean;
|
|
83
84
|
};
|
|
84
85
|
trustedRevalidateOrigin?: string;
|
|
85
86
|
};
|
|
@@ -9,7 +9,7 @@ import { mergeRouteParamsIntoQuery, parseQueryString, urlQueryToSearchParams } f
|
|
|
9
9
|
import { tracePagesApiHandler } from "./pages-execution-tracing.js";
|
|
10
10
|
import { resolveBodyParserConfig } from "./pages-body-parser-config.js";
|
|
11
11
|
import { PagesBodyParseError } from "./pages-media-type.js";
|
|
12
|
-
import {
|
|
12
|
+
import { createPagesApiReqRes, markVinextStreamedApiResponse, parsePagesApiBody } from "./pages-node-compat.js";
|
|
13
13
|
import { finalizeEdgeApiResponse, isEdgeApiRuntime } from "./edge-api-runtime.js";
|
|
14
14
|
//#region src/server/pages-api-route.ts
|
|
15
15
|
function resolveModuleRuntime(module) {
|
|
@@ -47,7 +47,8 @@ async function _handlePagesApiRoute(options) {
|
|
|
47
47
|
const nextRequest = new NextRequest(createEdgeApiRequest(options.request, options.url, params), options.nextConfig ? { nextConfig: {
|
|
48
48
|
basePath: options.nextConfig.basePath,
|
|
49
49
|
i18n: options.nextConfig.i18n ?? void 0,
|
|
50
|
-
trailingSlash: options.nextConfig.trailingSlash
|
|
50
|
+
trailingSlash: options.nextConfig.trailingSlash,
|
|
51
|
+
skipTrailingSlashRedirect: options.nextConfig.skipTrailingSlashRedirect
|
|
51
52
|
} } : void 0);
|
|
52
53
|
const response = await tracePagesApiHandler(route.pattern, () => handler(nextRequest));
|
|
53
54
|
if (response instanceof Response) {
|
|
@@ -70,7 +71,7 @@ async function _handlePagesApiRoute(options) {
|
|
|
70
71
|
const query = buildPagesApiQuery(options.url, params);
|
|
71
72
|
const bodyParserConfig = resolveBodyParserConfig(route.module.config);
|
|
72
73
|
const body = bodyParserConfig.enabled ? await parsePagesApiBody(options.request, bodyParserConfig.sizeLimit) : void 0;
|
|
73
|
-
const { req, res, responsePromise } =
|
|
74
|
+
const { req, res, responsePromise } = createPagesApiReqRes({
|
|
74
75
|
allowedRevalidateHeaderKeys: options.nextConfig?.allowedRevalidateHeaderKeys,
|
|
75
76
|
body,
|
|
76
77
|
initialResponseHeaders: options.initialResponseHeaders,
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
//#region src/server/pages-cache-tag.d.ts
|
|
2
|
+
/**
|
|
3
|
+
* Build the edge path tag for a Pages ISR pathname.
|
|
4
|
+
*
|
|
5
|
+
* Every spelling of one pathname shares a single ISR entry, because
|
|
6
|
+
* `pagesIsrCacheKey()` decodes each segment first. The tag must use that same
|
|
7
|
+
* decoded identity. Otherwise `/users/%40alice` is stored at the edge under its
|
|
8
|
+
* own tag, and `res.revalidate("/users/@alice")` or
|
|
9
|
+
* `revalidatePath("/users/@alice")` never purges it.
|
|
10
|
+
*/
|
|
11
|
+
export declare function pagesPathCacheTag(isrPathname: string): string;
|
|
12
|
+
//#endregion
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { normalizePathnameForRouteMatch } from "../routing/utils.js";
|
|
2
|
+
import { encodeCacheTag } from "../utils/encode-cache-tag.js";
|
|
3
|
+
//#region src/server/pages-cache-tag.ts
|
|
4
|
+
/**
|
|
5
|
+
* Build the edge path tag for a Pages ISR pathname.
|
|
6
|
+
*
|
|
7
|
+
* Every spelling of one pathname shares a single ISR entry, because
|
|
8
|
+
* `pagesIsrCacheKey()` decodes each segment first. The tag must use that same
|
|
9
|
+
* decoded identity. Otherwise `/users/%40alice` is stored at the edge under its
|
|
10
|
+
* own tag, and `res.revalidate("/users/@alice")` or
|
|
11
|
+
* `revalidatePath("/users/@alice")` never purges it.
|
|
12
|
+
*/
|
|
13
|
+
function pagesPathCacheTag(isrPathname) {
|
|
14
|
+
const pathname = normalizePathnameForRouteMatch(isrPathname);
|
|
15
|
+
const stem = pathname.endsWith("/") ? pathname.slice(0, -1) : pathname;
|
|
16
|
+
return encodeCacheTag(`_N_T_${stem || "/"}`);
|
|
17
|
+
}
|
|
18
|
+
//#endregion
|
|
19
|
+
export { pagesPathCacheTag };
|
|
@@ -14,6 +14,8 @@ export type PagesClientAssets = {
|
|
|
14
14
|
dynamicPreloads?: Record<string, string[]>;
|
|
15
15
|
/** next.config crossOrigin, defaulting to anonymous for Vite's CORS-fetched assets. */
|
|
16
16
|
crossOrigin?: AssetCrossOrigin;
|
|
17
|
+
/** Client bundle files outside `_next/static`, as URL pathnames. */
|
|
18
|
+
clientBuildFiles?: string[];
|
|
17
19
|
};
|
|
18
20
|
export declare function setPagesClientAssets(assets: PagesClientAssets | undefined): void;
|
|
19
21
|
export declare function getPagesClientAssets(): PagesClientAssets;
|
|
@@ -61,6 +61,16 @@ export declare function encodeUrlParserIgnoredCharacters(pathname: string): stri
|
|
|
61
61
|
* `/_next/data/<buildId>/about` → null (missing .json suffix)
|
|
62
62
|
*/
|
|
63
63
|
export declare function parseNextDataPathname(pathname: string, buildId: string): NextDataMatch | null;
|
|
64
|
+
/**
|
|
65
|
+
* The page pathname a `_next/data` URL addresses, whatever its buildId. Next.js
|
|
66
|
+
* compiles every middleware matcher with an optional `/_next/data/<buildId>`
|
|
67
|
+
* prefix and `.json` suffix, so a matcher written for `/admin` also guards that
|
|
68
|
+
* page's data URL, even one with a stale buildId. Middleware matching uses this
|
|
69
|
+
* when the request reaches middleware with its raw data URL
|
|
70
|
+
* (`skipProxyUrlNormalize`).
|
|
71
|
+
* https://github.com/vercel/next.js/blob/v16.2.7/packages/next/src/build/analysis/get-page-static-info.ts
|
|
72
|
+
*/
|
|
73
|
+
export declare function nextDataMiddlewareMatchPathname(pathname: string): string | null;
|
|
64
74
|
export declare function normalizeNextDataPagePathname(pagePathname: string, trailingSlash?: boolean): string;
|
|
65
75
|
/**
|
|
66
76
|
* Next.js only applies `trailingSlash` while normalizing a data URL for
|
|
@@ -67,7 +67,24 @@ function parseNextDataPathname(pathname, buildId) {
|
|
|
67
67
|
if (!isNextDataPathname(pathname)) return null;
|
|
68
68
|
const expectedPrefix = `${NEXT_DATA_PREFIX}${buildId}/`;
|
|
69
69
|
if (!pathname.startsWith(expectedPrefix)) return null;
|
|
70
|
-
|
|
70
|
+
return pageFromNextDataRest(pathname.slice(expectedPrefix.length, -5));
|
|
71
|
+
}
|
|
72
|
+
/**
|
|
73
|
+
* The page pathname a `_next/data` URL addresses, whatever its buildId. Next.js
|
|
74
|
+
* compiles every middleware matcher with an optional `/_next/data/<buildId>`
|
|
75
|
+
* prefix and `.json` suffix, so a matcher written for `/admin` also guards that
|
|
76
|
+
* page's data URL, even one with a stale buildId. Middleware matching uses this
|
|
77
|
+
* when the request reaches middleware with its raw data URL
|
|
78
|
+
* (`skipProxyUrlNormalize`).
|
|
79
|
+
* https://github.com/vercel/next.js/blob/v16.2.7/packages/next/src/build/analysis/get-page-static-info.ts
|
|
80
|
+
*/
|
|
81
|
+
function nextDataMiddlewareMatchPathname(pathname) {
|
|
82
|
+
if (!isNextDataPathname(pathname)) return null;
|
|
83
|
+
const buildIdEnd = pathname.indexOf("/", 12);
|
|
84
|
+
if (buildIdEnd <= 12) return null;
|
|
85
|
+
return pageFromNextDataRest(pathname.slice(buildIdEnd + 1, -5))?.pagePathname ?? null;
|
|
86
|
+
}
|
|
87
|
+
function pageFromNextDataRest(rest) {
|
|
71
88
|
if (rest.length === 0) return null;
|
|
72
89
|
if (rest === "index") return { pagePathname: "/" };
|
|
73
90
|
if (rest.endsWith("/index")) return { pagePathname: `/${rest.slice(0, -6)}` };
|
|
@@ -181,4 +198,4 @@ function normalizePagesDataRequest(request, buildId, basePath = "", trailingSlas
|
|
|
181
198
|
};
|
|
182
199
|
}
|
|
183
200
|
//#endregion
|
|
184
|
-
export { buildMiddlewarePrefetchSkipResponse, buildNextDataNotFoundResponse, buildNextDataPropsJsonResponse, encodeUrlParserIgnoredCharacters, isNextDataPathname, normalizeNextDataPagePathname, normalizePagesDataRequest, parseNextDataPathname, shouldAddTrailingSlashToPagesDataPath, urlParserCreatesPagesDataPath };
|
|
201
|
+
export { buildMiddlewarePrefetchSkipResponse, buildNextDataNotFoundResponse, buildNextDataPropsJsonResponse, encodeUrlParserIgnoredCharacters, isNextDataPathname, nextDataMiddlewareMatchPathname, normalizeNextDataPagePathname, normalizePagesDataRequest, parseNextDataPathname, shouldAddTrailingSlashToPagesDataPath, urlParserCreatesPagesDataPath };
|