vinext 0.1.0 → 0.1.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +2 -5
- package/dist/build/assets-ignore.d.ts +32 -0
- package/dist/build/assets-ignore.js +48 -0
- package/dist/build/client-build-config.d.ts +33 -1
- package/dist/build/client-build-config.js +66 -1
- package/dist/check.js +4 -3
- package/dist/cli.js +2 -0
- package/dist/client/navigation-runtime.d.ts +11 -2
- package/dist/client/navigation-runtime.js +1 -1
- package/dist/client/vinext-next-data.d.ts +2 -1
- package/dist/client/window-next.d.ts +6 -4
- package/dist/config/config-matchers.d.ts +31 -5
- package/dist/config/config-matchers.js +50 -3
- package/dist/config/next-config.d.ts +29 -3
- package/dist/config/next-config.js +32 -2
- package/dist/deploy.js +47 -304
- package/dist/entries/app-rsc-entry.d.ts +8 -2
- package/dist/entries/app-rsc-entry.js +61 -5
- package/dist/entries/app-rsc-manifest.js +20 -2
- package/dist/entries/pages-client-entry.js +1 -1
- package/dist/entries/pages-server-entry.js +16 -7
- package/dist/index.d.ts +0 -2
- package/dist/index.js +233 -280
- package/dist/plugins/dynamic-preload-metadata.d.ts +13 -0
- package/dist/plugins/dynamic-preload-metadata.js +415 -0
- package/dist/plugins/og-assets.js +2 -2
- package/dist/plugins/optimize-imports.d.ts +8 -4
- package/dist/plugins/optimize-imports.js +16 -12
- package/dist/plugins/postcss.js +18 -14
- package/dist/plugins/require-context.d.ts +6 -0
- package/dist/plugins/require-context.js +184 -0
- package/dist/plugins/sass.d.ts +53 -24
- package/dist/plugins/sass.js +249 -1
- package/dist/plugins/wasm-module-import.d.ts +15 -0
- package/dist/plugins/wasm-module-import.js +50 -0
- package/dist/routing/app-route-graph.d.ts +35 -2
- package/dist/routing/app-route-graph.js +179 -8
- package/dist/routing/file-matcher.js +1 -1
- package/dist/routing/route-pattern.d.ts +2 -1
- package/dist/routing/route-pattern.js +16 -1
- package/dist/server/api-handler.js +4 -0
- package/dist/server/app-browser-entry.js +155 -215
- package/dist/server/app-browser-error.d.ts +4 -1
- package/dist/server/app-browser-error.js +7 -1
- package/dist/server/app-browser-history-controller.d.ts +104 -0
- package/dist/server/app-browser-history-controller.js +210 -0
- package/dist/server/app-browser-interception-context.d.ts +2 -1
- package/dist/server/app-browser-interception-context.js +15 -2
- package/dist/server/app-browser-navigation-controller.d.ts +13 -2
- package/dist/server/app-browser-navigation-controller.js +83 -4
- package/dist/server/app-browser-popstate.d.ts +12 -3
- package/dist/server/app-browser-popstate.js +19 -4
- package/dist/server/app-browser-rsc-redirect.d.ts +11 -2
- package/dist/server/app-browser-rsc-redirect.js +30 -8
- package/dist/server/app-browser-state.d.ts +3 -0
- package/dist/server/app-browser-state.js +10 -10
- package/dist/server/app-browser-visible-commit.js +10 -8
- package/dist/server/app-fallback-renderer.d.ts +2 -1
- package/dist/server/app-fallback-renderer.js +3 -1
- package/dist/server/app-history-state.d.ts +45 -1
- package/dist/server/app-history-state.js +109 -1
- package/dist/server/app-middleware.js +1 -0
- package/dist/server/app-optimistic-routing.js +22 -1
- package/dist/server/app-page-boundary-render.d.ts +2 -1
- package/dist/server/app-page-boundary-render.js +45 -21
- package/dist/server/app-page-cache.js +9 -7
- package/dist/server/app-page-dispatch.d.ts +14 -0
- package/dist/server/app-page-dispatch.js +21 -6
- package/dist/server/app-page-element-builder.d.ts +23 -2
- package/dist/server/app-page-element-builder.js +58 -17
- package/dist/server/app-page-execution.d.ts +1 -1
- package/dist/server/app-page-execution.js +32 -17
- package/dist/server/app-page-render.d.ts +7 -1
- package/dist/server/app-page-render.js +11 -16
- package/dist/server/app-page-request.d.ts +9 -6
- package/dist/server/app-page-request.js +14 -10
- package/dist/server/app-page-response.d.ts +2 -2
- package/dist/server/app-page-response.js +2 -2
- package/dist/server/app-page-route-wiring.d.ts +3 -1
- package/dist/server/app-page-route-wiring.js +10 -8
- package/dist/server/app-page-stream.d.ts +37 -7
- package/dist/server/app-page-stream.js +36 -6
- package/dist/server/app-pages-bridge.d.ts +16 -0
- package/dist/server/app-pages-bridge.js +23 -3
- 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 +2 -0
- 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-response.js +11 -10
- package/dist/server/app-route-handler-runtime.d.ts +1 -0
- package/dist/server/app-route-handler-runtime.js +15 -3
- package/dist/server/app-rsc-handler.d.ts +1 -0
- package/dist/server/app-rsc-handler.js +5 -4
- package/dist/server/app-rsc-response-finalizer.js +1 -1
- package/dist/server/app-rsc-route-matching.d.ts +20 -1
- package/dist/server/app-rsc-route-matching.js +29 -4
- package/dist/server/app-server-action-execution.d.ts +22 -1
- package/dist/server/app-server-action-execution.js +73 -12
- package/dist/server/app-ssr-entry.d.ts +6 -0
- package/dist/server/app-ssr-entry.js +19 -3
- package/dist/server/app-ssr-stream.js +9 -1
- package/dist/server/dev-lockfile.js +2 -1
- package/dist/server/dev-server.d.ts +1 -1
- package/dist/server/dev-server.js +97 -43
- package/dist/server/headers.d.ts +8 -1
- package/dist/server/headers.js +8 -1
- package/dist/server/instrumentation-runtime.d.ts +6 -0
- package/dist/server/instrumentation-runtime.js +8 -0
- package/dist/server/isr-cache.d.ts +37 -1
- package/dist/server/isr-cache.js +85 -1
- package/dist/server/isr-decision.d.ts +79 -0
- package/dist/server/isr-decision.js +70 -0
- package/dist/server/metadata-route-response.js +5 -3
- package/dist/server/middleware-runtime.d.ts +13 -0
- package/dist/server/middleware-runtime.js +11 -7
- package/dist/server/middleware.js +1 -0
- package/dist/server/navigation-planner.d.ts +62 -1
- package/dist/server/navigation-planner.js +193 -3
- package/dist/server/navigation-trace.d.ts +12 -2
- package/dist/server/navigation-trace.js +11 -1
- package/dist/server/normalize-path.d.ts +0 -8
- package/dist/server/normalize-path.js +3 -1
- package/dist/server/otel-tracer-extension.d.ts +45 -0
- package/dist/server/otel-tracer-extension.js +89 -0
- package/dist/server/pages-api-route.d.ts +14 -3
- package/dist/server/pages-api-route.js +6 -1
- package/dist/server/pages-asset-tags.d.ts +15 -4
- package/dist/server/pages-asset-tags.js +18 -12
- package/dist/server/pages-data-route.js +5 -1
- package/dist/server/pages-node-compat.d.ts +5 -11
- package/dist/server/pages-node-compat.js +175 -118
- package/dist/server/pages-page-data.d.ts +38 -7
- package/dist/server/pages-page-data.js +64 -18
- package/dist/server/pages-page-handler.d.ts +10 -2
- package/dist/server/pages-page-handler.js +49 -20
- package/dist/server/pages-page-response.d.ts +55 -2
- package/dist/server/pages-page-response.js +74 -6
- package/dist/server/pages-readiness.d.ts +36 -0
- package/dist/server/pages-readiness.js +21 -0
- package/dist/server/pages-request-pipeline.d.ts +113 -0
- package/dist/server/pages-request-pipeline.js +230 -0
- package/dist/server/pages-revalidate.d.ts +15 -0
- package/dist/server/pages-revalidate.js +19 -0
- package/dist/server/prod-server.d.ts +45 -3
- package/dist/server/prod-server.js +182 -234
- package/dist/server/socket-error-backstop.d.ts +19 -1
- package/dist/server/socket-error-backstop.js +77 -4
- package/dist/shims/app-router-scroll.js +22 -4
- package/dist/shims/cache-runtime.js +39 -2
- package/dist/shims/dynamic-preload-chunks.d.ts +8 -0
- package/dist/shims/dynamic-preload-chunks.js +77 -0
- package/dist/shims/dynamic.d.ts +4 -0
- package/dist/shims/dynamic.js +4 -2
- package/dist/shims/error-boundary.d.ts +17 -7
- package/dist/shims/error-boundary.js +8 -1
- package/dist/shims/error.js +37 -11
- package/dist/shims/fetch-cache.d.ts +22 -1
- package/dist/shims/fetch-cache.js +28 -1
- package/dist/shims/hash-scroll.d.ts +1 -0
- package/dist/shims/hash-scroll.js +3 -1
- package/dist/shims/head.js +6 -1
- package/dist/shims/headers.d.ts +16 -2
- package/dist/shims/headers.js +37 -1
- package/dist/shims/image-config.js +7 -1
- package/dist/shims/internal/app-route-detection.d.ts +6 -3
- package/dist/shims/internal/app-route-detection.js +10 -6
- package/dist/shims/internal/app-router-context.d.ts +5 -0
- package/dist/shims/internal/link-status-registry.d.ts +43 -0
- package/dist/shims/internal/link-status-registry.js +42 -0
- package/dist/shims/internal/route-pattern-for-warning.d.ts +27 -0
- package/dist/shims/internal/route-pattern-for-warning.js +40 -0
- package/dist/shims/internal/utils.d.ts +1 -0
- package/dist/shims/link.js +20 -6
- package/dist/shims/metadata.d.ts +6 -2
- package/dist/shims/metadata.js +32 -14
- package/dist/shims/navigation.d.ts +9 -18
- package/dist/shims/navigation.js +96 -23
- package/dist/shims/router-state.d.ts +1 -0
- package/dist/shims/router-state.js +2 -0
- package/dist/shims/router.d.ts +6 -3
- package/dist/shims/router.js +156 -22
- package/dist/shims/script-nonce-context.d.ts +1 -1
- package/dist/shims/script-nonce-context.js +11 -3
- package/dist/shims/server.d.ts +17 -1
- package/dist/shims/server.js +31 -6
- package/dist/shims/slot.js +1 -1
- package/dist/shims/unified-request-context.js +1 -0
- package/dist/typegen.js +1 -0
- package/dist/utils/client-build-manifest.d.ts +8 -1
- package/dist/utils/client-build-manifest.js +41 -6
- package/dist/utils/client-entry-manifest.d.ts +11 -0
- package/dist/utils/client-entry-manifest.js +29 -0
- package/dist/utils/client-runtime-metadata.d.ts +45 -0
- package/dist/utils/client-runtime-metadata.js +63 -0
- package/dist/utils/hash.d.ts +17 -1
- package/dist/utils/hash.js +36 -1
- package/dist/utils/lazy-chunks.d.ts +27 -1
- package/dist/utils/lazy-chunks.js +65 -1
- package/dist/utils/manifest-paths.d.ts +20 -2
- package/dist/utils/manifest-paths.js +38 -3
- package/dist/utils/path.d.ts +2 -1
- package/dist/utils/path.js +5 -1
- package/package.json +6 -2
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import { VinextNextData } from "../client/vinext-next-data.js";
|
|
2
|
+
import { PagesPageModule } from "./pages-page-data.js";
|
|
3
|
+
|
|
4
|
+
//#region src/server/pages-readiness.d.ts
|
|
5
|
+
/**
|
|
6
|
+
* Shared Pages Router readiness modeling.
|
|
7
|
+
*
|
|
8
|
+
* The initial `router.isReady` value for the `next/navigation` compat hooks is
|
|
9
|
+
* derived from the page/_app data-fetching exports plus the configured-rewrites
|
|
10
|
+
* flag, serialized into `__NEXT_DATA__`. The dev SSR handler and the production
|
|
11
|
+
* Pages page handler must compute this identically so server HTML and client
|
|
12
|
+
* hydration agree — see `getPagesNavigationIsReadyFromSerializedState` in
|
|
13
|
+
* `shims/router.ts`.
|
|
14
|
+
*/
|
|
15
|
+
/**
|
|
16
|
+
* The serialized readiness flags (gssp/gsp/gip/appGip/autoExport +
|
|
17
|
+
* `__vinext.hasRewrites`) that gate the initial Pages Router `router.isReady`.
|
|
18
|
+
* The field names/types are projected from the canonical `VinextNextData` so
|
|
19
|
+
* this stays in lockstep with the `__NEXT_DATA__` shape it feeds into.
|
|
20
|
+
*/
|
|
21
|
+
type PagesReadinessNextData = Pick<VinextNextData, "gssp" | "gsp" | "gip" | "appGip" | "autoExport"> & {
|
|
22
|
+
__vinext: Pick<NonNullable<VinextNextData["__vinext"]>, "hasRewrites">;
|
|
23
|
+
};
|
|
24
|
+
/**
|
|
25
|
+
* Build the readiness flags for a Pages Router render. Shared by the dev and
|
|
26
|
+
* production Pages render paths.
|
|
27
|
+
*/
|
|
28
|
+
declare function buildPagesReadinessNextData(options: {
|
|
29
|
+
pageModule: PagesPageModule;
|
|
30
|
+
appComponent: {
|
|
31
|
+
getInitialProps?: unknown;
|
|
32
|
+
} | null | undefined;
|
|
33
|
+
hasRewrites: boolean;
|
|
34
|
+
}): PagesReadinessNextData;
|
|
35
|
+
//#endregion
|
|
36
|
+
export { buildPagesReadinessNextData };
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
//#region src/server/pages-readiness.ts
|
|
2
|
+
/**
|
|
3
|
+
* Build the readiness flags for a Pages Router render. Shared by the dev and
|
|
4
|
+
* production Pages render paths.
|
|
5
|
+
*/
|
|
6
|
+
function buildPagesReadinessNextData(options) {
|
|
7
|
+
const hasPageGssp = typeof options.pageModule.getServerSideProps === "function";
|
|
8
|
+
const hasPageGsp = typeof options.pageModule.getStaticProps === "function";
|
|
9
|
+
const hasPageGip = typeof options.pageModule.default?.getInitialProps === "function";
|
|
10
|
+
const hasAppGip = typeof options.appComponent?.getInitialProps === "function";
|
|
11
|
+
return {
|
|
12
|
+
gssp: hasPageGssp,
|
|
13
|
+
gsp: hasPageGsp,
|
|
14
|
+
gip: hasPageGip,
|
|
15
|
+
appGip: hasAppGip,
|
|
16
|
+
autoExport: !hasPageGssp && !hasPageGsp && !hasPageGip && !hasAppGip,
|
|
17
|
+
__vinext: { hasRewrites: options.hasRewrites }
|
|
18
|
+
};
|
|
19
|
+
}
|
|
20
|
+
//#endregion
|
|
21
|
+
export { buildPagesReadinessNextData };
|
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
import { NextHeader, NextI18nConfig, NextRedirect, NextRewrite } from "../config/next-config.js";
|
|
2
|
+
import { HeaderRecord } from "./request-pipeline.js";
|
|
3
|
+
|
|
4
|
+
//#region src/server/pages-request-pipeline.d.ts
|
|
5
|
+
type PagesRenderOptions = {
|
|
6
|
+
isDataReq?: boolean;
|
|
7
|
+
renderErrorPageOnMiss?: boolean;
|
|
8
|
+
};
|
|
9
|
+
type MiddlewareResult = {
|
|
10
|
+
continue: boolean;
|
|
11
|
+
redirectUrl?: string;
|
|
12
|
+
redirectStatus?: number;
|
|
13
|
+
rewriteUrl?: string;
|
|
14
|
+
rewriteStatus?: number;
|
|
15
|
+
status?: number;
|
|
16
|
+
responseHeaders?: Iterable<[string, string]>;
|
|
17
|
+
response?: Response;
|
|
18
|
+
waitUntilPromises?: Promise<unknown>[];
|
|
19
|
+
};
|
|
20
|
+
type PagesPipelineDeps = {
|
|
21
|
+
basePath: string;
|
|
22
|
+
trailingSlash: boolean;
|
|
23
|
+
i18nConfig: NextI18nConfig | null;
|
|
24
|
+
configRedirects: NextRedirect[];
|
|
25
|
+
configRewrites: {
|
|
26
|
+
beforeFiles: NextRewrite[];
|
|
27
|
+
afterFiles: NextRewrite[];
|
|
28
|
+
fallback: NextRewrite[];
|
|
29
|
+
};
|
|
30
|
+
configHeaders: NextHeader[];
|
|
31
|
+
hadBasePath: boolean;
|
|
32
|
+
isDataReq: boolean;
|
|
33
|
+
isDataRequest: boolean;
|
|
34
|
+
ctx?: unknown;
|
|
35
|
+
rawSearch?: string;
|
|
36
|
+
matchPageRoute?: ((pathname: string, request: Request) => {
|
|
37
|
+
route: {
|
|
38
|
+
isDynamic: boolean;
|
|
39
|
+
};
|
|
40
|
+
} | null) | null;
|
|
41
|
+
runMiddleware?: ((request: Request, ctx: unknown, opts: {
|
|
42
|
+
isDataRequest: boolean;
|
|
43
|
+
}) => Promise<MiddlewareResult>) | null;
|
|
44
|
+
renderPage?: ((request: Request, resolvedUrl: string, options?: PagesRenderOptions, stagedHeaders?: Headers) => Promise<Response>) | null;
|
|
45
|
+
handleApi?: ((request: Request, apiUrl: string, ctx: unknown) => Promise<Response>) | null;
|
|
46
|
+
/**
|
|
47
|
+
* Optional override for proxying external rewrite destinations.
|
|
48
|
+
* When supplied, the pipeline calls this instead of proxyExternalRequest(currentRequest, url).
|
|
49
|
+
* Receives the pipeline's current request (with post-middleware headers applied) and the
|
|
50
|
+
* external target URL. Dev adapters supply this to forward the original Node req body
|
|
51
|
+
* (which is not included in the pipeline's body-less Web Request).
|
|
52
|
+
*/
|
|
53
|
+
proxyExternal?: ((currentRequest: Request, externalUrl: string) => Promise<Response>) | null;
|
|
54
|
+
/**
|
|
55
|
+
* Optional public-directory static file server (Node prod only).
|
|
56
|
+
* Called post-middleware (so middleware can intercept/redirect public files) with the
|
|
57
|
+
* original basePath-stripped pathname and the staged middleware response headers.
|
|
58
|
+
* The callback writes the file to its own output (Node `res`) and resolves `true` when
|
|
59
|
+
* it served the request; the pipeline then returns `{ type: "handled" }`. Resolves `false`
|
|
60
|
+
* to fall through to rewrites/render. Worker/dev adapters omit this — their public files
|
|
61
|
+
* are served by the asset binding / Vite respectively.
|
|
62
|
+
*/
|
|
63
|
+
serveStaticFile?: ((requestPathname: string, stagedHeaders: HeaderRecord) => Promise<boolean>) | null;
|
|
64
|
+
};
|
|
65
|
+
/**
|
|
66
|
+
* Wrap an adapter's `runMiddleware` callback so middleware receives the original
|
|
67
|
+
* (pre-basePath-stripping) URL. Adapters strip the basePath before handing the
|
|
68
|
+
* request to `runPagesRequest`, but Next.js passes the un-stripped URL to the
|
|
69
|
+
* middleware adapter so `request.nextUrl.basePath` reflects whether the URL
|
|
70
|
+
* actually had the basePath prefix. Requests outside the basePath
|
|
71
|
+
* (`hadBasePath === false`) are passed through untouched so middleware sees
|
|
72
|
+
* `nextUrl.basePath === ""` and can redirect them into the basePath
|
|
73
|
+
* (see the middleware-base-path e2e test / #1830).
|
|
74
|
+
*
|
|
75
|
+
* Shared by the Node prod server (prod-server.ts) and the generated Pages
|
|
76
|
+
* Router worker entry (deploy.ts) to keep the two adapters in sync.
|
|
77
|
+
*/
|
|
78
|
+
declare function wrapMiddlewareWithBasePath(runMiddleware: NonNullable<PagesPipelineDeps["runMiddleware"]>, basePath: string, hadBasePath: boolean): NonNullable<PagesPipelineDeps["runMiddleware"]>;
|
|
79
|
+
type PagesPipelineResult = {
|
|
80
|
+
type: "response";
|
|
81
|
+
response: Response;
|
|
82
|
+
defaultContentType?: string;
|
|
83
|
+
} | {
|
|
84
|
+
type: "handled";
|
|
85
|
+
} | {
|
|
86
|
+
type: "render";
|
|
87
|
+
resolvedUrl: string;
|
|
88
|
+
renderOptions: PagesRenderOptions | undefined;
|
|
89
|
+
stagedHeaders: HeaderRecord; /** Post-middleware request headers — dev adapters apply these to req.headers before SSR. */
|
|
90
|
+
requestHeaders: Headers;
|
|
91
|
+
middlewareStatus: number | undefined;
|
|
92
|
+
isDataReq: boolean;
|
|
93
|
+
} | {
|
|
94
|
+
type: "api";
|
|
95
|
+
apiUrl: string;
|
|
96
|
+
stagedHeaders: HeaderRecord; /** Post-middleware request headers — dev adapters apply these to req.headers before API handler. */
|
|
97
|
+
requestHeaders: Headers;
|
|
98
|
+
middlewareStatus: number | undefined;
|
|
99
|
+
} | {
|
|
100
|
+
type: "next";
|
|
101
|
+
};
|
|
102
|
+
/**
|
|
103
|
+
* Run the Pages Router request pipeline.
|
|
104
|
+
*
|
|
105
|
+
* ASSUMPTION: request already has internal headers filtered and basePath stripped.
|
|
106
|
+
* The adapter is responsible for that pre-processing before calling runPagesRequest.
|
|
107
|
+
* The adapter also handles: open-redirect guard, _next/static 404, image optimization,
|
|
108
|
+
* _next/data normalization, Node decode/normalize/400, public-file serving.
|
|
109
|
+
* runPagesRequest receives a "clean" request with basePath-stripped URL.
|
|
110
|
+
*/
|
|
111
|
+
declare function runPagesRequest(request: Request, deps: PagesPipelineDeps): Promise<PagesPipelineResult>;
|
|
112
|
+
//#endregion
|
|
113
|
+
export { MiddlewareResult, PagesPipelineDeps, PagesPipelineResult, PagesRenderOptions, runPagesRequest, wrapMiddlewareWithBasePath };
|
|
@@ -0,0 +1,230 @@
|
|
|
1
|
+
import { addBasePathToPathname, hasBasePath } from "../utils/base-path.js";
|
|
2
|
+
import { applyMiddlewareRequestHeaders, isExternalUrl, matchRedirect, matchRewrite, preserveRedirectDestinationQuery, proxyExternalRequest, requestContextFromRequest, sanitizeDestination } from "../config/config-matchers.js";
|
|
3
|
+
import { applyConfigHeadersToHeaderRecord, normalizeTrailingSlash } from "./request-pipeline.js";
|
|
4
|
+
import { mergeRewriteQuery } from "../utils/query.js";
|
|
5
|
+
import { normalizeDefaultLocalePathname, stripI18nLocaleForApiRoute } from "./pages-i18n.js";
|
|
6
|
+
import { mergeHeaders } from "./worker-utils.js";
|
|
7
|
+
//#region src/server/pages-request-pipeline.ts
|
|
8
|
+
/**
|
|
9
|
+
* Wrap an adapter's `runMiddleware` callback so middleware receives the original
|
|
10
|
+
* (pre-basePath-stripping) URL. Adapters strip the basePath before handing the
|
|
11
|
+
* request to `runPagesRequest`, but Next.js passes the un-stripped URL to the
|
|
12
|
+
* middleware adapter so `request.nextUrl.basePath` reflects whether the URL
|
|
13
|
+
* actually had the basePath prefix. Requests outside the basePath
|
|
14
|
+
* (`hadBasePath === false`) are passed through untouched so middleware sees
|
|
15
|
+
* `nextUrl.basePath === ""` and can redirect them into the basePath
|
|
16
|
+
* (see the middleware-base-path e2e test / #1830).
|
|
17
|
+
*
|
|
18
|
+
* Shared by the Node prod server (prod-server.ts) and the generated Pages
|
|
19
|
+
* Router worker entry (deploy.ts) to keep the two adapters in sync.
|
|
20
|
+
*/
|
|
21
|
+
function wrapMiddlewareWithBasePath(runMiddleware, basePath, hadBasePath) {
|
|
22
|
+
if (!hadBasePath || !basePath) return runMiddleware;
|
|
23
|
+
return (request, ctx, opts) => {
|
|
24
|
+
const mwUrl = new URL(request.url);
|
|
25
|
+
mwUrl.pathname = addBasePathToPathname(mwUrl.pathname, basePath);
|
|
26
|
+
return runMiddleware(new Request(mwUrl, request), ctx, opts);
|
|
27
|
+
};
|
|
28
|
+
}
|
|
29
|
+
/**
|
|
30
|
+
* Run the Pages Router request pipeline.
|
|
31
|
+
*
|
|
32
|
+
* ASSUMPTION: request already has internal headers filtered and basePath stripped.
|
|
33
|
+
* The adapter is responsible for that pre-processing before calling runPagesRequest.
|
|
34
|
+
* The adapter also handles: open-redirect guard, _next/static 404, image optimization,
|
|
35
|
+
* _next/data normalization, Node decode/normalize/400, public-file serving.
|
|
36
|
+
* runPagesRequest receives a "clean" request with basePath-stripped URL.
|
|
37
|
+
*/
|
|
38
|
+
async function runPagesRequest(request, deps) {
|
|
39
|
+
const { basePath, trailingSlash, i18nConfig, configRedirects, configRewrites, configHeaders, hadBasePath, isDataReq, isDataRequest } = deps;
|
|
40
|
+
const proxyExternal = (currentReq, externalUrl) => deps.proxyExternal ? deps.proxyExternal(currentReq, externalUrl) : proxyExternalRequest(currentReq, externalUrl);
|
|
41
|
+
const url = new URL(request.url);
|
|
42
|
+
let pathname = url.pathname;
|
|
43
|
+
const search = url.search;
|
|
44
|
+
const basePathState = {
|
|
45
|
+
basePath,
|
|
46
|
+
hadBasePath
|
|
47
|
+
};
|
|
48
|
+
{
|
|
49
|
+
const trailingSlashRedirect = normalizeTrailingSlash(pathname, basePath, trailingSlash, search);
|
|
50
|
+
if (trailingSlashRedirect) return {
|
|
51
|
+
type: "response",
|
|
52
|
+
response: trailingSlashRedirect
|
|
53
|
+
};
|
|
54
|
+
}
|
|
55
|
+
const reqCtx = requestContextFromRequest(request);
|
|
56
|
+
const requestHostname = i18nConfig ? url.hostname : "";
|
|
57
|
+
const matchPathname = i18nConfig ? normalizeDefaultLocalePathname(pathname, i18nConfig, { hostname: requestHostname }) : pathname;
|
|
58
|
+
if (configRedirects.length) {
|
|
59
|
+
const redirect = matchRedirect(matchPathname, configRedirects, reqCtx, basePathState);
|
|
60
|
+
if (redirect) {
|
|
61
|
+
const location = preserveRedirectDestinationQuery(sanitizeDestination(basePath && hadBasePath && !isExternalUrl(redirect.destination) && !hasBasePath(redirect.destination, basePath) ? basePath + redirect.destination : redirect.destination), deps.rawSearch ?? search);
|
|
62
|
+
return {
|
|
63
|
+
type: "response",
|
|
64
|
+
response: new Response(null, {
|
|
65
|
+
status: redirect.permanent ? 308 : 307,
|
|
66
|
+
headers: { Location: location }
|
|
67
|
+
})
|
|
68
|
+
};
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
let resolvedUrl = pathname + search;
|
|
72
|
+
const middlewareHeaders = {};
|
|
73
|
+
let middlewareStatus;
|
|
74
|
+
if (typeof deps.runMiddleware === "function") {
|
|
75
|
+
const result = await deps.runMiddleware(request, deps.ctx ?? null, { isDataRequest });
|
|
76
|
+
if (result.waitUntilPromises && result.waitUntilPromises.length > 0) {
|
|
77
|
+
const ctx = deps.ctx;
|
|
78
|
+
if (ctx && typeof ctx.waitUntil === "function") for (const p of result.waitUntilPromises) ctx.waitUntil(p);
|
|
79
|
+
else Promise.allSettled(result.waitUntilPromises);
|
|
80
|
+
}
|
|
81
|
+
if (!result.continue) {
|
|
82
|
+
if (result.redirectUrl) {
|
|
83
|
+
const redirectHeaders = { Location: result.redirectUrl };
|
|
84
|
+
if (result.responseHeaders) for (const [key, value] of result.responseHeaders) {
|
|
85
|
+
const existing = redirectHeaders[key];
|
|
86
|
+
if (existing === void 0) redirectHeaders[key] = value;
|
|
87
|
+
else if (Array.isArray(existing)) existing.push(value);
|
|
88
|
+
else redirectHeaders[key] = [existing, value];
|
|
89
|
+
}
|
|
90
|
+
const headers = new Headers();
|
|
91
|
+
for (const [k, v] of Object.entries(redirectHeaders)) if (Array.isArray(v)) for (const item of v) headers.append(k, item);
|
|
92
|
+
else headers.set(k, v);
|
|
93
|
+
return {
|
|
94
|
+
type: "response",
|
|
95
|
+
response: new Response(null, {
|
|
96
|
+
status: result.redirectStatus ?? 307,
|
|
97
|
+
headers
|
|
98
|
+
})
|
|
99
|
+
};
|
|
100
|
+
}
|
|
101
|
+
if (result.response) return {
|
|
102
|
+
type: "response",
|
|
103
|
+
response: result.response
|
|
104
|
+
};
|
|
105
|
+
}
|
|
106
|
+
if (result.responseHeaders) for (const [key, value] of result.responseHeaders) if (key === "set-cookie") {
|
|
107
|
+
const existing = middlewareHeaders[key];
|
|
108
|
+
if (Array.isArray(existing)) existing.push(value);
|
|
109
|
+
else if (existing) middlewareHeaders[key] = [existing, value];
|
|
110
|
+
else middlewareHeaders[key] = [value];
|
|
111
|
+
} else middlewareHeaders[key] = value;
|
|
112
|
+
if (result.rewriteUrl) resolvedUrl = result.rewriteUrl;
|
|
113
|
+
middlewareStatus = result.status ?? result.rewriteStatus;
|
|
114
|
+
}
|
|
115
|
+
const { postMwReqCtx, request: postMwReq } = applyMiddlewareRequestHeaders(middlewareHeaders, request, { preserveCredentialHeaders: isExternalUrl(resolvedUrl) });
|
|
116
|
+
request = postMwReq;
|
|
117
|
+
let resolvedPathname = resolvedUrl.split("?")[0];
|
|
118
|
+
const matchResolvedPathname = (p) => i18nConfig ? normalizeDefaultLocalePathname(p, i18nConfig, { hostname: requestHostname }) : p;
|
|
119
|
+
if (configHeaders.length) applyConfigHeadersToHeaderRecord(middlewareHeaders, {
|
|
120
|
+
configHeaders,
|
|
121
|
+
pathname: matchPathname,
|
|
122
|
+
requestContext: reqCtx,
|
|
123
|
+
basePathState
|
|
124
|
+
});
|
|
125
|
+
if (isExternalUrl(resolvedUrl)) return {
|
|
126
|
+
type: "response",
|
|
127
|
+
response: mergeHeaders(await proxyExternal(request, resolvedUrl), middlewareHeaders, void 0)
|
|
128
|
+
};
|
|
129
|
+
if (deps.serveStaticFile) {
|
|
130
|
+
if (await deps.serveStaticFile(pathname, middlewareHeaders)) return { type: "handled" };
|
|
131
|
+
}
|
|
132
|
+
let configRewriteFired = false;
|
|
133
|
+
if (configRewrites.beforeFiles?.length) {
|
|
134
|
+
const rewritten = matchRewrite(matchResolvedPathname(resolvedPathname), configRewrites.beforeFiles, postMwReqCtx, basePathState);
|
|
135
|
+
if (rewritten) {
|
|
136
|
+
if (isExternalUrl(rewritten)) return {
|
|
137
|
+
type: "response",
|
|
138
|
+
response: await proxyExternal(request, rewritten)
|
|
139
|
+
};
|
|
140
|
+
resolvedUrl = mergeRewriteQuery(resolvedUrl, rewritten);
|
|
141
|
+
resolvedPathname = resolvedUrl.split("?")[0];
|
|
142
|
+
configRewriteFired = true;
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
if (basePath && !hadBasePath && !configRewriteFired) return {
|
|
146
|
+
type: "response",
|
|
147
|
+
response: new Response("This page could not be found", {
|
|
148
|
+
status: 404,
|
|
149
|
+
headers: { "Content-Type": "text/html; charset=utf-8" }
|
|
150
|
+
})
|
|
151
|
+
};
|
|
152
|
+
const apiLookupUrl = stripI18nLocaleForApiRoute(resolvedUrl, i18nConfig);
|
|
153
|
+
const apiLookupPathname = apiLookupUrl.split("?")[0];
|
|
154
|
+
if (apiLookupPathname.startsWith("/api/") || apiLookupPathname === "/api") if (typeof deps.handleApi === "function") return {
|
|
155
|
+
type: "response",
|
|
156
|
+
defaultContentType: "application/octet-stream",
|
|
157
|
+
response: mergeHeaders(await deps.handleApi(request, apiLookupUrl, deps.ctx ?? null), middlewareHeaders, middlewareStatus)
|
|
158
|
+
};
|
|
159
|
+
else return {
|
|
160
|
+
type: "api",
|
|
161
|
+
apiUrl: apiLookupUrl,
|
|
162
|
+
stagedHeaders: middlewareHeaders,
|
|
163
|
+
requestHeaders: request.headers,
|
|
164
|
+
middlewareStatus
|
|
165
|
+
};
|
|
166
|
+
const pageMatch = deps.matchPageRoute ? deps.matchPageRoute(resolvedPathname, request) : null;
|
|
167
|
+
let resolvedPathnameChanged = false;
|
|
168
|
+
if ((!pageMatch || pageMatch.route.isDynamic) && configRewrites.afterFiles?.length) {
|
|
169
|
+
const rewritten = matchRewrite(matchResolvedPathname(resolvedPathname), configRewrites.afterFiles, postMwReqCtx, basePathState);
|
|
170
|
+
if (rewritten) {
|
|
171
|
+
if (isExternalUrl(rewritten)) return {
|
|
172
|
+
type: "response",
|
|
173
|
+
response: await proxyExternal(request, rewritten)
|
|
174
|
+
};
|
|
175
|
+
resolvedUrl = mergeRewriteQuery(resolvedUrl, rewritten);
|
|
176
|
+
resolvedPathname = resolvedUrl.split("?")[0];
|
|
177
|
+
resolvedPathnameChanged = true;
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
if (typeof deps.renderPage === "function") {
|
|
181
|
+
const renderPageMatch = resolvedPathnameChanged ? deps.matchPageRoute ? deps.matchPageRoute(resolvedPathname, request) : null : pageMatch;
|
|
182
|
+
const shouldDeferErrorPageOnMiss = !isDataReq && !isDataRequest && !!deps.matchPageRoute && !renderPageMatch;
|
|
183
|
+
const initialRenderOptions = shouldDeferErrorPageOnMiss ? { renderErrorPageOnMiss: false } : isDataReq ? { isDataReq: true } : void 0;
|
|
184
|
+
const stagedHeaders = new Headers();
|
|
185
|
+
for (const [k, v] of Object.entries(middlewareHeaders)) if (Array.isArray(v)) for (const item of v) stagedHeaders.append(k, item);
|
|
186
|
+
else stagedHeaders.set(k, v);
|
|
187
|
+
let response = await deps.renderPage(request, resolvedUrl, initialRenderOptions, stagedHeaders);
|
|
188
|
+
let matchedFallbackRewrite = false;
|
|
189
|
+
if (response.status === 404 && shouldDeferErrorPageOnMiss && configRewrites.fallback?.length) {
|
|
190
|
+
const fallbackRewrite = matchRewrite(matchResolvedPathname(resolvedPathname), configRewrites.fallback, postMwReqCtx, basePathState);
|
|
191
|
+
if (fallbackRewrite) {
|
|
192
|
+
if (isExternalUrl(fallbackRewrite)) return {
|
|
193
|
+
type: "response",
|
|
194
|
+
response: await proxyExternal(request, fallbackRewrite)
|
|
195
|
+
};
|
|
196
|
+
response = await deps.renderPage(request, mergeRewriteQuery(resolvedUrl, fallbackRewrite), void 0, stagedHeaders);
|
|
197
|
+
matchedFallbackRewrite = true;
|
|
198
|
+
}
|
|
199
|
+
}
|
|
200
|
+
if (response.status === 404 && shouldDeferErrorPageOnMiss && !matchedFallbackRewrite) response = await deps.renderPage(request, resolvedUrl, void 0, stagedHeaders);
|
|
201
|
+
const merged = mergeHeaders(response, middlewareHeaders, middlewareStatus);
|
|
202
|
+
if (merged !== response) merged.__vinextStreamedHtmlResponse = response.__vinextStreamedHtmlResponse;
|
|
203
|
+
return {
|
|
204
|
+
type: "response",
|
|
205
|
+
response: merged,
|
|
206
|
+
defaultContentType: "text/html"
|
|
207
|
+
};
|
|
208
|
+
}
|
|
209
|
+
if (!(resolvedPathnameChanged ? deps.matchPageRoute ? deps.matchPageRoute(resolvedPathname, request) : null : pageMatch) && configRewrites.fallback?.length) {
|
|
210
|
+
const fallbackRewrite = matchRewrite(matchResolvedPathname(resolvedPathname), configRewrites.fallback, postMwReqCtx, basePathState);
|
|
211
|
+
if (fallbackRewrite) {
|
|
212
|
+
if (isExternalUrl(fallbackRewrite)) return {
|
|
213
|
+
type: "response",
|
|
214
|
+
response: await proxyExternal(request, fallbackRewrite)
|
|
215
|
+
};
|
|
216
|
+
resolvedUrl = mergeRewriteQuery(resolvedUrl, fallbackRewrite);
|
|
217
|
+
}
|
|
218
|
+
}
|
|
219
|
+
return {
|
|
220
|
+
type: "render",
|
|
221
|
+
resolvedUrl,
|
|
222
|
+
renderOptions: isDataReq ? { isDataReq: true } : void 0,
|
|
223
|
+
stagedHeaders: middlewareHeaders,
|
|
224
|
+
requestHeaders: request.headers,
|
|
225
|
+
middlewareStatus,
|
|
226
|
+
isDataReq
|
|
227
|
+
};
|
|
228
|
+
}
|
|
229
|
+
//#endregion
|
|
230
|
+
export { runPagesRequest, wrapMiddlewareWithBasePath };
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { IncomingMessage } from "node:http";
|
|
2
|
+
|
|
3
|
+
//#region src/server/pages-revalidate.d.ts
|
|
4
|
+
type RevalidateOptions = {
|
|
5
|
+
/**
|
|
6
|
+
* Only revalidate the path if it was already generated (cached). Mirrors
|
|
7
|
+
* Next.js's `unstable_onlyGenerated`: sets the
|
|
8
|
+
* `x-prerender-revalidate-if-generated` header and makes a 404 response count
|
|
9
|
+
* as a successful no-op rather than an error.
|
|
10
|
+
*/
|
|
11
|
+
unstable_onlyGenerated?: boolean;
|
|
12
|
+
};
|
|
13
|
+
declare function performOnDemandRevalidate(source: IncomingMessage | Headers, urlPath: string, opts?: RevalidateOptions): Promise<void>;
|
|
14
|
+
//#endregion
|
|
15
|
+
export { RevalidateOptions, performOnDemandRevalidate };
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import "./headers.js";
|
|
2
|
+
import { PRERENDER_REVALIDATE_HEADER, PRERENDER_REVALIDATE_ONLY_GENERATED_HEADER, getRevalidateSecret } from "./isr-cache.js";
|
|
3
|
+
import { resolveRequestHost, resolveRequestProtocol } from "./proxy-trust.js";
|
|
4
|
+
//#region src/server/pages-revalidate.ts
|
|
5
|
+
async function performOnDemandRevalidate(source, urlPath, opts = {}) {
|
|
6
|
+
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}`);
|
|
7
|
+
const proto = resolveRequestProtocol(source);
|
|
8
|
+
const host = resolveRequestHost(source, "localhost");
|
|
9
|
+
const target = new URL(urlPath, `${proto}://${host}`);
|
|
10
|
+
const headers = { [PRERENDER_REVALIDATE_HEADER]: getRevalidateSecret() };
|
|
11
|
+
if (opts.unstable_onlyGenerated) headers[PRERENDER_REVALIDATE_ONLY_GENERATED_HEADER] = "1";
|
|
12
|
+
const res = await fetch(target, {
|
|
13
|
+
method: "HEAD",
|
|
14
|
+
headers
|
|
15
|
+
});
|
|
16
|
+
if (!(res.headers.get("x-nextjs-cache")?.toUpperCase() === "REVALIDATED" || res.status === 200 || res.status === 404 && opts.unstable_onlyGenerated === true)) throw new Error(`Failed to revalidate ${urlPath}: ${res.status}`);
|
|
17
|
+
}
|
|
18
|
+
//#endregion
|
|
19
|
+
export { performOnDemandRevalidate };
|
|
@@ -3,6 +3,44 @@ import { resolveRequestHost, trustProxy, trustedHosts } from "./proxy-trust.js";
|
|
|
3
3
|
import { IncomingMessage, ServerResponse } from "node:http";
|
|
4
4
|
|
|
5
5
|
//#region src/server/prod-server.d.ts
|
|
6
|
+
/**
|
|
7
|
+
* Import a built server entry module (App Router RSC entry or Pages Router
|
|
8
|
+
* server entry) by absolute file path.
|
|
9
|
+
*
|
|
10
|
+
* The first import of a given path uses the plain file:// URL with NO query
|
|
11
|
+
* string. This is load-bearing: code-split builds emit lazy chunks that
|
|
12
|
+
* import the entry back by bare specifier (default Vite builds on both
|
|
13
|
+
* supported majors — Rollup on Vite 7 and Rolldown on Vite 8 — hoist modules
|
|
14
|
+
* shared between the entry's static graph and lazy route chunks into the
|
|
15
|
+
* entry chunk, which the chunks then import as e.g. "../../index.js").
|
|
16
|
+
* Node keys its ESM cache on the full URL including the query string, so if
|
|
17
|
+
* the server imported the entry as `index.js?t=<mtime>`, a chunk's bare
|
|
18
|
+
* back-import would evaluate the entire server bundle a second time and
|
|
19
|
+
* module-level singletons (db pools, service registries) would silently
|
|
20
|
+
* diverge between the two copies. See
|
|
21
|
+
* https://github.com/cloudflare/vinext/issues/1923.
|
|
22
|
+
*
|
|
23
|
+
* A `?t=<mtime>` query string is appended only when the same path is
|
|
24
|
+
* imported again after a rebuild (different mtime) — e.g. test suites that
|
|
25
|
+
* rebuild a fixture to the same output path within one process — where the
|
|
26
|
+
* bare URL's cache entry would return the stale previous build. Note this
|
|
27
|
+
* rebuild branch trades the single-instance guarantee back: chunks that
|
|
28
|
+
* import the entry by bare path still resolve to the FIRST build's cache
|
|
29
|
+
* entry, so freshness and single-instance only hold together on the first
|
|
30
|
+
* import of a path. Production processes import each entry path exactly
|
|
31
|
+
* once and always get both.
|
|
32
|
+
*
|
|
33
|
+
* The entry is imported via its canonical real path: the bundler
|
|
34
|
+
* canonicalizes module ids with fs.realpathSync.native, so chunks evaluate
|
|
35
|
+
* under realpath-based URLs and their relative imports resolve to realpath
|
|
36
|
+
* URLs too. Importing the entry through a symlinked path (macOS /var/...
|
|
37
|
+
* tmpdirs, symlinked deploy directories) would otherwise create a second
|
|
38
|
+
* instance keyed on the symlinked URL.
|
|
39
|
+
*
|
|
40
|
+
* Exported for direct unit testing of the URL choice.
|
|
41
|
+
*/
|
|
42
|
+
declare function resolveServerEntryImportUrl(entryPath: string): string;
|
|
43
|
+
declare function importServerEntryModule(entryPath: string): Promise<any>;
|
|
6
44
|
type ProdServerOptions = {
|
|
7
45
|
/** Port to listen on */port?: number; /** Host to bind to */
|
|
8
46
|
host?: string; /** Path to the build output directory */
|
|
@@ -29,8 +67,12 @@ declare function mergeResponseHeaders(middlewareHeaders: Record<string, string |
|
|
|
29
67
|
/**
|
|
30
68
|
* Merge middleware/config headers and an optional status override into a new
|
|
31
69
|
* Web Response while preserving the original body stream when allowed.
|
|
32
|
-
*
|
|
33
|
-
*
|
|
70
|
+
*
|
|
71
|
+
* This is the canonical {@link mergeHeaders} (server/worker-utils.ts) with the
|
|
72
|
+
* arguments in (headers, response) order. The request path now calls
|
|
73
|
+
* `runPagesRequest`, which uses `mergeHeaders` directly; this wrapper is retained
|
|
74
|
+
* only for its existing tests and any external callers, so there is a single
|
|
75
|
+
* implementation to keep in sync. (deploy.ts still emits its own generated copy.)
|
|
34
76
|
*/
|
|
35
77
|
declare function mergeWebResponse(middlewareHeaders: Record<string, string | string[]>, response: Response, statusOverride?: number): Response;
|
|
36
78
|
/**
|
|
@@ -101,4 +143,4 @@ declare function resolveAppRouterPrerenderSeeder(entryModule: unknown): AppRoute
|
|
|
101
143
|
*/
|
|
102
144
|
declare function resolveAppRouterAssetPath(pathname: string, assetPathPrefix: string, assetPrefix: string): string | null;
|
|
103
145
|
//#endregion
|
|
104
|
-
export { COMPRESSIBLE_TYPES, COMPRESS_THRESHOLD, ProdServerOptions, mergeResponseHeaders, mergeWebResponse, negotiateEncoding, nodeToWebRequest, resolveAppRouterAssetPath, resolveAppRouterPrerenderSeeder, resolveRequestHost as resolveHost, sendCompressed, sendWebResponse, startProdServer, trustProxy, trustedHosts, tryServeStatic };
|
|
146
|
+
export { COMPRESSIBLE_TYPES, COMPRESS_THRESHOLD, ProdServerOptions, importServerEntryModule, mergeResponseHeaders, mergeWebResponse, negotiateEncoding, nodeToWebRequest, resolveAppRouterAssetPath, resolveAppRouterPrerenderSeeder, resolveRequestHost as resolveHost, resolveServerEntryImportUrl, sendCompressed, sendWebResponse, startProdServer, trustProxy, trustedHosts, tryServeStatic };
|