vinext 0.1.1 → 0.1.3
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/client-build-config.d.ts +7 -1
- package/dist/build/client-build-config.js +9 -1
- package/dist/build/prerender.d.ts +9 -1
- package/dist/build/prerender.js +41 -12
- package/dist/build/run-prerender.d.ts +10 -2
- package/dist/build/run-prerender.js +15 -1
- package/dist/check.js +4 -3
- package/dist/client/app-nav-failure-handler.d.ts +8 -0
- package/dist/client/app-nav-failure-handler.js +44 -0
- package/dist/client/navigation-runtime.d.ts +3 -2
- package/dist/client/vinext-next-data.d.ts +18 -1
- package/dist/client/window-next.d.ts +8 -5
- package/dist/client/window-next.js +12 -1
- package/dist/cloudflare/src/cache/cdn-adapter.runtime.js +6 -1
- package/dist/config/config-matchers.d.ts +11 -4
- package/dist/config/config-matchers.js +88 -16
- package/dist/config/next-config.d.ts +59 -4
- package/dist/config/next-config.js +149 -48
- package/dist/deploy.d.ts +30 -11
- package/dist/deploy.js +189 -101
- package/dist/entries/app-browser-entry.d.ts +9 -3
- package/dist/entries/app-browser-entry.js +21 -3
- package/dist/entries/app-rsc-entry.d.ts +2 -0
- package/dist/entries/app-rsc-entry.js +71 -6
- package/dist/entries/app-rsc-manifest.js +2 -0
- package/dist/entries/app-ssr-entry.js +1 -1
- package/dist/entries/pages-client-entry.js +54 -9
- package/dist/entries/pages-server-entry.js +48 -11
- package/dist/index.d.ts +0 -2
- package/dist/index.js +285 -139
- package/dist/plugins/dynamic-preload-metadata.d.ts +13 -0
- package/dist/plugins/dynamic-preload-metadata.js +415 -0
- package/dist/plugins/extensionless-dynamic-import.d.ts +6 -0
- package/dist/plugins/extensionless-dynamic-import.js +152 -0
- package/dist/plugins/og-assets.js +2 -2
- package/dist/plugins/optimize-imports.d.ts +10 -5
- package/dist/plugins/optimize-imports.js +27 -21
- package/dist/plugins/postcss.js +7 -7
- package/dist/plugins/sass.d.ts +53 -24
- package/dist/plugins/sass.js +249 -1
- package/dist/plugins/typeof-window.d.ts +14 -0
- package/dist/plugins/typeof-window.js +150 -0
- 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 +25 -2
- package/dist/routing/app-route-graph.js +91 -22
- package/dist/routing/file-matcher.d.ts +10 -1
- package/dist/routing/file-matcher.js +23 -2
- package/dist/routing/pages-router.js +3 -3
- package/dist/routing/utils.d.ts +35 -6
- package/dist/routing/utils.js +59 -7
- package/dist/server/api-handler.d.ts +6 -1
- package/dist/server/api-handler.js +21 -15
- package/dist/server/app-browser-action-result.d.ts +19 -6
- package/dist/server/app-browser-action-result.js +19 -10
- package/dist/server/app-browser-entry.js +269 -297
- package/dist/server/app-browser-error.d.ts +10 -3
- package/dist/server/app-browser-error.js +47 -6
- 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-hydration.d.ts +2 -0
- package/dist/server/app-browser-hydration.js +1 -0
- package/dist/server/app-browser-navigation-controller.d.ts +7 -4
- package/dist/server/app-browser-navigation-controller.js +33 -9
- 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-server-action-navigation.d.ts +6 -0
- package/dist/server/app-browser-server-action-navigation.js +9 -0
- package/dist/server/app-browser-state.js +4 -7
- package/dist/server/app-browser-stream.js +86 -43
- package/dist/server/app-browser-visible-commit.js +1 -1
- package/dist/server/app-elements-wire.d.ts +6 -1
- package/dist/server/app-elements-wire.js +14 -4
- package/dist/server/app-elements.d.ts +2 -2
- package/dist/server/app-elements.js +2 -2
- package/dist/server/app-fallback-renderer.d.ts +3 -1
- package/dist/server/app-fallback-renderer.js +6 -2
- package/dist/server/app-middleware.js +1 -0
- package/dist/server/app-optimistic-routing.js +24 -3
- package/dist/server/app-page-boundary-render.d.ts +3 -1
- package/dist/server/app-page-boundary-render.js +31 -16
- package/dist/server/app-page-cache-render.d.ts +53 -0
- package/dist/server/app-page-cache-render.js +91 -0
- package/dist/server/app-page-cache.d.ts +16 -2
- package/dist/server/app-page-cache.js +71 -8
- package/dist/server/app-page-dispatch.d.ts +34 -0
- package/dist/server/app-page-dispatch.js +167 -97
- package/dist/server/app-page-element-builder.d.ts +23 -2
- package/dist/server/app-page-element-builder.js +42 -10
- package/dist/server/app-page-execution.d.ts +7 -2
- package/dist/server/app-page-execution.js +53 -18
- package/dist/server/app-page-probe.d.ts +1 -0
- package/dist/server/app-page-probe.js +4 -0
- package/dist/server/app-page-render-observation.d.ts +3 -1
- package/dist/server/app-page-render-observation.js +17 -1
- package/dist/server/app-page-render.d.ts +13 -2
- package/dist/server/app-page-render.js +48 -17
- package/dist/server/app-page-request.d.ts +3 -0
- package/dist/server/app-page-request.js +5 -3
- package/dist/server/app-page-response.js +1 -1
- package/dist/server/app-page-route-wiring.d.ts +5 -1
- package/dist/server/app-page-route-wiring.js +21 -11
- package/dist/server/app-page-stream.d.ts +16 -9
- package/dist/server/app-page-stream.js +12 -9
- package/dist/server/app-pages-bridge.d.ts +18 -0
- package/dist/server/app-pages-bridge.js +22 -5
- package/dist/server/app-ppr-fallback-shell-render.d.ts +17 -0
- package/dist/server/app-ppr-fallback-shell-render.js +26 -0
- package/dist/server/app-ppr-fallback-shell.d.ts +13 -1
- package/dist/server/app-ppr-fallback-shell.js +8 -1
- package/dist/server/app-route-handler-dispatch.js +9 -2
- package/dist/server/app-route-handler-policy.d.ts +1 -0
- package/dist/server/app-route-handler-response.js +11 -10
- package/dist/server/app-route-handler-runtime.js +12 -1
- package/dist/server/app-router-entry.js +5 -0
- package/dist/server/app-rsc-cache-busting.js +2 -0
- package/dist/server/app-rsc-handler.d.ts +25 -0
- package/dist/server/app-rsc-handler.js +153 -53
- package/dist/server/app-rsc-response-finalizer.js +1 -1
- package/dist/server/app-rsc-route-matching.d.ts +3 -0
- package/dist/server/app-rsc-route-matching.js +2 -0
- package/dist/server/app-segment-config.d.ts +9 -1
- package/dist/server/app-segment-config.js +12 -3
- package/dist/server/app-server-action-execution.d.ts +12 -0
- package/dist/server/app-server-action-execution.js +47 -15
- package/dist/server/app-ssr-entry.d.ts +2 -0
- package/dist/server/app-ssr-entry.js +81 -8
- package/dist/server/app-ssr-stream.js +9 -1
- package/dist/server/cache-control.js +4 -0
- package/dist/server/dev-lockfile.js +2 -1
- package/dist/server/dev-server.d.ts +2 -2
- package/dist/server/dev-server.js +287 -63
- package/dist/server/headers.d.ts +8 -1
- package/dist/server/headers.js +8 -1
- package/dist/server/hybrid-route-priority.d.ts +22 -0
- package/dist/server/hybrid-route-priority.js +33 -0
- package/dist/server/image-optimization.d.ts +18 -9
- package/dist/server/image-optimization.js +37 -23
- package/dist/server/implicit-tags.d.ts +2 -1
- package/dist/server/implicit-tags.js +4 -1
- package/dist/server/instrumentation-runtime.d.ts +6 -0
- package/dist/server/instrumentation-runtime.js +8 -0
- 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 +186 -22
- package/dist/server/navigation-planner.js +302 -0
- package/dist/server/navigation-trace.d.ts +18 -1
- package/dist/server/navigation-trace.js +18 -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 +20 -3
- package/dist/server/pages-api-route.js +19 -3
- package/dist/server/pages-asset-tags.d.ts +16 -4
- package/dist/server/pages-asset-tags.js +22 -12
- package/dist/server/pages-data-route.d.ts +8 -1
- package/dist/server/pages-data-route.js +16 -3
- package/dist/server/pages-get-initial-props.d.ts +54 -4
- package/dist/server/pages-get-initial-props.js +43 -1
- package/dist/server/pages-node-compat.d.ts +3 -11
- package/dist/server/pages-node-compat.js +175 -122
- package/dist/server/pages-page-data.d.ts +39 -2
- package/dist/server/pages-page-data.js +261 -46
- package/dist/server/pages-page-handler.d.ts +5 -2
- package/dist/server/pages-page-handler.js +78 -25
- package/dist/server/pages-page-response.d.ts +47 -2
- package/dist/server/pages-page-response.js +73 -9
- package/dist/server/pages-readiness.d.ts +1 -1
- package/dist/server/pages-request-pipeline.d.ts +16 -1
- package/dist/server/pages-request-pipeline.js +96 -38
- package/dist/server/pregenerated-concrete-paths.d.ts +1 -17
- package/dist/server/pregenerated-concrete-paths.js +2 -19
- package/dist/server/prerender-manifest.d.ts +33 -0
- package/dist/server/prerender-manifest.js +54 -0
- package/dist/server/prerender-route-params.d.ts +1 -2
- package/dist/server/prod-server.d.ts +39 -1
- package/dist/server/prod-server.js +107 -37
- package/dist/server/request-pipeline.d.ts +3 -15
- package/dist/server/request-pipeline.js +58 -47
- package/dist/server/rsc-stream-hints.d.ts +5 -1
- package/dist/server/rsc-stream-hints.js +6 -1
- package/dist/server/seed-cache.js +10 -18
- package/dist/shims/app-router-scroll-state.d.ts +3 -1
- package/dist/shims/app-router-scroll-state.js +14 -2
- package/dist/shims/app-router-scroll.d.ts +3 -0
- package/dist/shims/app-router-scroll.js +28 -18
- package/dist/shims/cache-runtime.js +12 -4
- package/dist/shims/cache.d.ts +1 -0
- package/dist/shims/cache.js +1 -1
- package/dist/shims/cdn-cache.d.ts +5 -5
- package/dist/shims/dynamic-preload-chunks.d.ts +8 -0
- package/dist/shims/dynamic-preload-chunks.js +79 -0
- package/dist/shims/dynamic.d.ts +4 -0
- package/dist/shims/dynamic.js +4 -2
- package/dist/shims/error-boundary.d.ts +6 -4
- package/dist/shims/error-boundary.js +7 -0
- package/dist/shims/error.js +38 -11
- package/dist/shims/error.react-server.d.ts +9 -0
- package/dist/shims/error.react-server.js +6 -0
- package/dist/shims/fetch-cache.d.ts +11 -1
- package/dist/shims/fetch-cache.js +55 -20
- package/dist/shims/hash-scroll.js +6 -1
- package/dist/shims/head.js +6 -1
- package/dist/shims/headers.d.ts +16 -2
- package/dist/shims/headers.js +66 -5
- package/dist/shims/image-config.js +7 -1
- package/dist/shims/internal/als-registry.js +28 -1
- package/dist/shims/internal/app-route-detection.d.ts +6 -3
- package/dist/shims/internal/app-route-detection.js +18 -23
- package/dist/shims/internal/app-router-context.d.ts +5 -0
- package/dist/shims/internal/hybrid-client-route-owner.d.ts +31 -0
- package/dist/shims/internal/hybrid-client-route-owner.js +143 -0
- package/dist/shims/internal/navigation-untracked.d.ts +35 -0
- package/dist/shims/internal/navigation-untracked.js +55 -0
- package/dist/shims/internal/pages-data-target.d.ts +7 -2
- package/dist/shims/internal/pages-data-target.js +17 -8
- package/dist/shims/internal/pages-router-accessor.d.ts +19 -0
- package/dist/shims/internal/pages-router-accessor.js +13 -0
- package/dist/shims/internal/router-context.d.ts +2 -1
- package/dist/shims/internal/router-context.js +3 -1
- package/dist/shims/link.js +12 -5
- package/dist/shims/metadata.d.ts +6 -2
- package/dist/shims/metadata.js +32 -14
- package/dist/shims/navigation.d.ts +14 -17
- package/dist/shims/navigation.js +93 -46
- package/dist/shims/ppr-fallback-shell.d.ts +5 -1
- package/dist/shims/ppr-fallback-shell.js +28 -7
- package/dist/shims/router.d.ts +13 -2
- package/dist/shims/router.js +434 -116
- 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 +33 -2
- package/dist/shims/server.js +75 -18
- package/dist/shims/slot.js +1 -1
- package/dist/shims/unified-request-context.js +2 -0
- package/dist/typegen.js +1 -0
- package/dist/utils/built-asset-url.d.ts +4 -0
- package/dist/utils/built-asset-url.js +11 -0
- package/dist/utils/client-build-manifest.js +15 -5
- package/dist/utils/client-runtime-metadata.d.ts +45 -0
- package/dist/utils/client-runtime-metadata.js +63 -0
- package/dist/utils/commonjs-loader.d.ts +16 -0
- package/dist/utils/commonjs-loader.js +100 -0
- package/dist/utils/deployment-id.d.ts +8 -0
- package/dist/utils/deployment-id.js +22 -0
- package/dist/utils/hash.d.ts +17 -1
- package/dist/utils/hash.js +36 -1
- package/dist/utils/html-limited-bots.d.ts +18 -1
- package/dist/utils/html-limited-bots.js +23 -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/parse-cookie.d.ts +13 -0
- package/dist/utils/parse-cookie.js +52 -0
- package/dist/utils/path.d.ts +8 -1
- package/dist/utils/path.js +13 -1
- package/package.json +2 -2
- package/dist/shims/internal/parse-cookie-header.d.ts +0 -14
- package/dist/shims/internal/parse-cookie-header.js +0 -30
package/dist/server/headers.js
CHANGED
|
@@ -60,6 +60,13 @@ const RSC_ACTION_HEADER = "x-rsc-action";
|
|
|
60
60
|
const NEXT_ACTION_HEADER = "next-action";
|
|
61
61
|
/** Next.js action-not-found indicator (value "1"). */
|
|
62
62
|
const NEXTJS_ACTION_NOT_FOUND_HEADER = "x-nextjs-action-not-found";
|
|
63
|
+
/**
|
|
64
|
+
* Deployment ID header used by the Pages Router for deployment-skew
|
|
65
|
+
* protection. Set on every `/_next/data/` response so the client can detect
|
|
66
|
+
* when a new deployment has been rolled out and trigger a hard navigation.
|
|
67
|
+
* Mirrors `NEXT_NAV_DEPLOYMENT_ID_HEADER` from Next.js `lib/constants.ts`.
|
|
68
|
+
*/
|
|
69
|
+
const NEXTJS_DEPLOYMENT_ID_HEADER = "x-nextjs-deployment-id";
|
|
63
70
|
/** Forwarded action marker — set when a request has already been forwarded between workers. */
|
|
64
71
|
const ACTION_FORWARDED_HEADER = "x-action-forwarded";
|
|
65
72
|
/** Indicates revalidation occurred — value is JSON kind (1 = path/tag, 2 = dynamic-only). */
|
|
@@ -122,4 +129,4 @@ const INTERNAL_HEADERS = [
|
|
|
122
129
|
/** Vinext-only internal headers stripped alongside Next.js protocol internals. */
|
|
123
130
|
const VINEXT_INTERNAL_HEADERS = [VINEXT_PRERENDER_ROUTE_PARAMS_HEADER];
|
|
124
131
|
//#endregion
|
|
125
|
-
export { ACTION_FORWARDED_HEADER, ACTION_REDIRECT_HEADER, ACTION_REDIRECT_STATUS_HEADER, ACTION_REDIRECT_TYPE_HEADER, ACTION_REVALIDATED_HEADER, FLIGHT_HEADERS, INTERNAL_HEADERS, MIDDLEWARE_HEADER_PREFIX, MIDDLEWARE_NEXT_HEADER, MIDDLEWARE_OVERRIDE_HEADERS, MIDDLEWARE_REQUEST_HEADER_PREFIX, MIDDLEWARE_REWRITE_HEADER, MIDDLEWARE_SET_COOKIE_HEADER, NEXTJS_ACTION_NOT_FOUND_HEADER, NEXTJS_CACHE_HEADER, NEXT_ACTION_HEADER, NEXT_ROUTER_PREFETCH_HEADER, NEXT_ROUTER_SEGMENT_PREFETCH_HEADER, NEXT_ROUTER_STATE_TREE_HEADER, NEXT_URL_HEADER, RSC_ACTION_HEADER, RSC_HEADER, VINEXT_CACHE_HEADER, VINEXT_CLIENT_REUSE_MANIFEST_HEADER, VINEXT_DYNAMIC_STALE_TIME_HEADER, VINEXT_INTERCEPTION_CONTEXT_HEADER, VINEXT_INTERNAL_HEADERS, VINEXT_MOUNTED_SLOTS_HEADER, VINEXT_MW_CTX_HEADER, VINEXT_PARAMS_HEADER, VINEXT_PRERENDER_ROUTE_PARAMS_HEADER, VINEXT_PRERENDER_SECRET_HEADER, VINEXT_REVALIDATE_HEADER, VINEXT_RSC_MARKER_HEADER, VINEXT_RSC_REDIRECT_HEADER, VINEXT_RSC_RENDER_MODE_HEADER, VINEXT_STATIC_FILE_HEADER, VINEXT_TIMING_HEADER };
|
|
132
|
+
export { ACTION_FORWARDED_HEADER, ACTION_REDIRECT_HEADER, ACTION_REDIRECT_STATUS_HEADER, ACTION_REDIRECT_TYPE_HEADER, ACTION_REVALIDATED_HEADER, FLIGHT_HEADERS, INTERNAL_HEADERS, MIDDLEWARE_HEADER_PREFIX, MIDDLEWARE_NEXT_HEADER, MIDDLEWARE_OVERRIDE_HEADERS, MIDDLEWARE_REQUEST_HEADER_PREFIX, MIDDLEWARE_REWRITE_HEADER, MIDDLEWARE_SET_COOKIE_HEADER, NEXTJS_ACTION_NOT_FOUND_HEADER, NEXTJS_CACHE_HEADER, NEXTJS_DEPLOYMENT_ID_HEADER, NEXT_ACTION_HEADER, NEXT_ROUTER_PREFETCH_HEADER, NEXT_ROUTER_SEGMENT_PREFETCH_HEADER, NEXT_ROUTER_STATE_TREE_HEADER, NEXT_URL_HEADER, RSC_ACTION_HEADER, RSC_HEADER, VINEXT_CACHE_HEADER, VINEXT_CLIENT_REUSE_MANIFEST_HEADER, VINEXT_DYNAMIC_STALE_TIME_HEADER, VINEXT_INTERCEPTION_CONTEXT_HEADER, VINEXT_INTERNAL_HEADERS, VINEXT_MOUNTED_SLOTS_HEADER, VINEXT_MW_CTX_HEADER, VINEXT_PARAMS_HEADER, VINEXT_PRERENDER_ROUTE_PARAMS_HEADER, VINEXT_PRERENDER_SECRET_HEADER, VINEXT_REVALIDATE_HEADER, VINEXT_RSC_MARKER_HEADER, VINEXT_RSC_REDIRECT_HEADER, VINEXT_RSC_RENDER_MODE_HEADER, VINEXT_STATIC_FILE_HEADER, VINEXT_TIMING_HEADER };
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
//#region src/server/hybrid-route-priority.d.ts
|
|
2
|
+
type HybridRoutePriorityRoute = {
|
|
3
|
+
isDynamic: boolean;
|
|
4
|
+
pattern: string;
|
|
5
|
+
sourcePath?: string | null;
|
|
6
|
+
};
|
|
7
|
+
declare function validateHybridRouteConflicts(pagesRoutes: readonly HybridRoutePriorityRoute[], appRoutes: readonly HybridRoutePriorityRoute[]): void;
|
|
8
|
+
/**
|
|
9
|
+
* Return whether a matched Pages Router route should own the request instead
|
|
10
|
+
* of a matched App Router route.
|
|
11
|
+
*
|
|
12
|
+
* Next.js registers Pages providers before App providers, then sorts all
|
|
13
|
+
* dynamic route pathnames together in DefaultRouteMatcherManager. Vinext keeps
|
|
14
|
+
* separate route tries for each router, so the hybrid boundary needs to apply
|
|
15
|
+
* that same cross-router ordering after both routers have produced their best
|
|
16
|
+
* local match. The decision itself lives in
|
|
17
|
+
* `routing/utils.ts#compareHybridRoutePatterns` so the server and client
|
|
18
|
+
* always reach the same answer.
|
|
19
|
+
*/
|
|
20
|
+
declare function pagesRouteHasPriorityOverAppRoute(pagesRoute: HybridRoutePriorityRoute, appRoute: HybridRoutePriorityRoute | null): boolean;
|
|
21
|
+
//#endregion
|
|
22
|
+
export { HybridRoutePriorityRoute, pagesRouteHasPriorityOverAppRoute, validateHybridRouteConflicts };
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import { compareHybridRoutePatterns } from "../routing/utils.js";
|
|
2
|
+
import { validateRoutePatterns } from "../routing/route-validation.js";
|
|
3
|
+
//#region src/server/hybrid-route-priority.ts
|
|
4
|
+
function validateHybridRouteConflicts(pagesRoutes, appRoutes) {
|
|
5
|
+
const pagesByPattern = new Map(pagesRoutes.map((route) => [route.pattern, route]));
|
|
6
|
+
const conflicts = appRoutes.flatMap((appRoute) => {
|
|
7
|
+
const pagesRoute = pagesByPattern.get(appRoute.pattern);
|
|
8
|
+
return pagesRoute === void 0 ? [] : [[pagesRoute, appRoute]];
|
|
9
|
+
});
|
|
10
|
+
if (conflicts.length > 0) {
|
|
11
|
+
const message = `Conflicting app and page file${conflicts.length === 1 ? " was" : "s were"} found, please remove the conflicting files to continue:`;
|
|
12
|
+
throw new Error(`${message}\n${conflicts.map(([pagesRoute, appRoute]) => ` "${pagesRoute.sourcePath ?? pagesRoute.pattern}" - "${appRoute.sourcePath ?? appRoute.pattern}"`).join("\n")}`);
|
|
13
|
+
}
|
|
14
|
+
validateRoutePatterns([...pagesRoutes.map((route) => route.pattern), ...appRoutes.map((route) => route.pattern)]);
|
|
15
|
+
}
|
|
16
|
+
/**
|
|
17
|
+
* Return whether a matched Pages Router route should own the request instead
|
|
18
|
+
* of a matched App Router route.
|
|
19
|
+
*
|
|
20
|
+
* Next.js registers Pages providers before App providers, then sorts all
|
|
21
|
+
* dynamic route pathnames together in DefaultRouteMatcherManager. Vinext keeps
|
|
22
|
+
* separate route tries for each router, so the hybrid boundary needs to apply
|
|
23
|
+
* that same cross-router ordering after both routers have produced their best
|
|
24
|
+
* local match. The decision itself lives in
|
|
25
|
+
* `routing/utils.ts#compareHybridRoutePatterns` so the server and client
|
|
26
|
+
* always reach the same answer.
|
|
27
|
+
*/
|
|
28
|
+
function pagesRouteHasPriorityOverAppRoute(pagesRoute, appRoute) {
|
|
29
|
+
if (appRoute === null) return true;
|
|
30
|
+
return compareHybridRoutePatterns(pagesRoute.pattern, pagesRoute.isDynamic, appRoute.pattern, appRoute.isDynamic) === "pages";
|
|
31
|
+
}
|
|
32
|
+
//#endregion
|
|
33
|
+
export { pagesRouteHasPriorityOverAppRoute, validateHybridRouteConflicts };
|
|
@@ -33,7 +33,15 @@ declare function isImageOptimizationPath(pathname: string): boolean;
|
|
|
33
33
|
* Controls SVG handling and security headers for the image endpoint.
|
|
34
34
|
*/
|
|
35
35
|
type ImageConfig = {
|
|
36
|
-
/**
|
|
36
|
+
/** Allowed device widths. Defaults to Next.js device sizes. */deviceSizes?: number[]; /** Allowed fixed-image widths. Defaults to Next.js image sizes. */
|
|
37
|
+
imageSizes?: number[];
|
|
38
|
+
/**
|
|
39
|
+
* Allowed output qualities. When unset, any quality from 1-100 is permitted
|
|
40
|
+
* (matches Next.js: an unset `images.qualities` is not restricted to a single
|
|
41
|
+
* value). When set, only the listed qualities are accepted.
|
|
42
|
+
*/
|
|
43
|
+
qualities?: number[]; /** Allow SVG through the image optimization endpoint. Default: false. */
|
|
44
|
+
dangerouslyAllowSVG?: boolean;
|
|
37
45
|
/**
|
|
38
46
|
* Allow image optimization for hostnames that resolve to private IP addresses.
|
|
39
47
|
* Default: false.
|
|
@@ -54,18 +62,19 @@ type ImageConfig = {
|
|
|
54
62
|
*/
|
|
55
63
|
declare const DEFAULT_DEVICE_SIZES: number[];
|
|
56
64
|
declare const DEFAULT_IMAGE_SIZES: number[];
|
|
65
|
+
type ParseImageParamsOptions = {
|
|
66
|
+
isDev?: boolean;
|
|
67
|
+
};
|
|
68
|
+
declare function resolveDevImageRedirect(requestUrl: URL, allowedWidths?: number[], allowedQualities?: number[], options?: ParseImageParamsOptions): string | null;
|
|
57
69
|
/**
|
|
58
70
|
* Parse and validate image optimization query parameters.
|
|
59
71
|
* Returns null if the request is malformed.
|
|
60
72
|
*
|
|
61
|
-
*
|
|
62
|
-
*
|
|
63
|
-
*
|
|
64
|
-
*
|
|
65
|
-
* When `allowedWidths` is not provided, any width from 0 to ABSOLUTE_MAX_WIDTH
|
|
66
|
-
* is accepted (backwards-compatible fallback).
|
|
73
|
+
* Ported from Next.js:
|
|
74
|
+
* test/integration/image-optimizer/test/index.test.ts
|
|
75
|
+
* https://github.com/vercel/next.js/blob/canary/test/integration/image-optimizer/test/index.test.ts
|
|
67
76
|
*/
|
|
68
|
-
declare function parseImageParams(url: URL, allowedWidths?: number[]): {
|
|
77
|
+
declare function parseImageParams(url: URL, allowedWidths?: number[], allowedQualities?: number[], options?: ParseImageParamsOptions): {
|
|
69
78
|
imageUrl: string;
|
|
70
79
|
width: number;
|
|
71
80
|
quality: number;
|
|
@@ -113,4 +122,4 @@ type ImageHandlers = {
|
|
|
113
122
|
*/
|
|
114
123
|
declare function handleImageOptimization(request: Request, handlers: ImageHandlers, allowedWidths?: number[], imageConfig?: ImageConfig): Promise<Response>;
|
|
115
124
|
//#endregion
|
|
116
|
-
export { DEFAULT_DEVICE_SIZES, DEFAULT_IMAGE_SIZES, IMAGE_CACHE_CONTROL, IMAGE_CONTENT_SECURITY_POLICY, IMAGE_OPTIMIZATION_PATH, ImageConfig, ImageHandlers, VINEXT_IMAGE_OPTIMIZATION_PATH, handleImageOptimization, isImageOptimizationPath, isSafeImageContentType, negotiateImageFormat, parseImageParams };
|
|
125
|
+
export { DEFAULT_DEVICE_SIZES, DEFAULT_IMAGE_SIZES, IMAGE_CACHE_CONTROL, IMAGE_CONTENT_SECURITY_POLICY, IMAGE_OPTIMIZATION_PATH, ImageConfig, ImageHandlers, ParseImageParamsOptions, VINEXT_IMAGE_OPTIMIZATION_PATH, handleImageOptimization, isImageOptimizationPath, isSafeImageContentType, negotiateImageFormat, parseImageParams, resolveDevImageRedirect };
|
|
@@ -56,32 +56,46 @@ const DEFAULT_IMAGE_SIZES = [
|
|
|
56
56
|
256,
|
|
57
57
|
384
|
|
58
58
|
];
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
59
|
+
const DEV_BLUR_MAX_WIDTH = 8;
|
|
60
|
+
const DEV_BLUR_QUALITY = 70;
|
|
61
|
+
function resolveDevImageRedirect(requestUrl, allowedWidths = [...DEFAULT_DEVICE_SIZES, ...DEFAULT_IMAGE_SIZES], allowedQualities, options = { isDev: true }) {
|
|
62
|
+
const params = parseImageParams(requestUrl, allowedWidths, allowedQualities, options);
|
|
63
|
+
if (!params) return null;
|
|
64
|
+
if (params.imageUrl.startsWith("/@") || params.imageUrl.startsWith("/__vite") || params.imageUrl.startsWith("/node_modules")) return null;
|
|
65
|
+
const resolved = new URL(params.imageUrl, requestUrl.origin);
|
|
66
|
+
if (resolved.origin !== requestUrl.origin) return null;
|
|
67
|
+
return resolved.pathname + resolved.search;
|
|
68
|
+
}
|
|
65
69
|
/**
|
|
66
70
|
* Parse and validate image optimization query parameters.
|
|
67
71
|
* Returns null if the request is malformed.
|
|
68
72
|
*
|
|
69
|
-
*
|
|
70
|
-
*
|
|
71
|
-
*
|
|
72
|
-
*
|
|
73
|
-
* When `allowedWidths` is not provided, any width from 0 to ABSOLUTE_MAX_WIDTH
|
|
74
|
-
* is accepted (backwards-compatible fallback).
|
|
73
|
+
* Ported from Next.js:
|
|
74
|
+
* test/integration/image-optimizer/test/index.test.ts
|
|
75
|
+
* https://github.com/vercel/next.js/blob/canary/test/integration/image-optimizer/test/index.test.ts
|
|
75
76
|
*/
|
|
76
|
-
function parseImageParams(url, allowedWidths) {
|
|
77
|
+
function parseImageParams(url, allowedWidths = [...DEFAULT_DEVICE_SIZES, ...DEFAULT_IMAGE_SIZES], allowedQualities, options = {}) {
|
|
78
|
+
const allowedParamNames = new Set([
|
|
79
|
+
"url",
|
|
80
|
+
"w",
|
|
81
|
+
"q"
|
|
82
|
+
]);
|
|
83
|
+
for (const name of url.searchParams.keys()) if (!allowedParamNames.has(name) || url.searchParams.getAll(name).length !== 1) return null;
|
|
77
84
|
const imageUrl = url.searchParams.get("url");
|
|
78
85
|
if (!imageUrl) return null;
|
|
79
|
-
|
|
80
|
-
const
|
|
81
|
-
|
|
82
|
-
if (
|
|
83
|
-
if (
|
|
84
|
-
|
|
86
|
+
if (imageUrl.length > 3072) return null;
|
|
87
|
+
const widthParam = url.searchParams.get("w");
|
|
88
|
+
const qualityParam = url.searchParams.get("q");
|
|
89
|
+
if (!widthParam || !/^[0-9]+$/.test(widthParam)) return null;
|
|
90
|
+
if (!qualityParam || !/^[0-9]+$/.test(qualityParam)) return null;
|
|
91
|
+
const width = Number.parseInt(widthParam, 10);
|
|
92
|
+
const quality = Number.parseInt(qualityParam, 10);
|
|
93
|
+
if (String(width) !== widthParam || String(quality) !== qualityParam) return null;
|
|
94
|
+
const isDevBlurWidth = options.isDev && width <= DEV_BLUR_MAX_WIDTH;
|
|
95
|
+
const isDevBlurQuality = options.isDev && quality === DEV_BLUR_QUALITY;
|
|
96
|
+
if (width <= 0 || !allowedWidths.includes(width) && !isDevBlurWidth) return null;
|
|
97
|
+
if (quality < 1 || quality > 100) return null;
|
|
98
|
+
if (allowedQualities && !allowedQualities.includes(quality) && !isDevBlurQuality) return null;
|
|
85
99
|
const normalizedUrl = imageUrl.replaceAll("\\", "/");
|
|
86
100
|
if (!normalizedUrl.startsWith("/") || normalizedUrl.startsWith("//")) return null;
|
|
87
101
|
try {
|
|
@@ -92,8 +106,8 @@ function parseImageParams(url, allowedWidths) {
|
|
|
92
106
|
}
|
|
93
107
|
return {
|
|
94
108
|
imageUrl: normalizedUrl,
|
|
95
|
-
width
|
|
96
|
-
quality
|
|
109
|
+
width,
|
|
110
|
+
quality
|
|
97
111
|
};
|
|
98
112
|
}
|
|
99
113
|
/**
|
|
@@ -174,7 +188,7 @@ function createPassthroughImageResponse(source, config) {
|
|
|
174
188
|
* cache headers.
|
|
175
189
|
*/
|
|
176
190
|
async function handleImageOptimization(request, handlers, allowedWidths, imageConfig) {
|
|
177
|
-
const params = parseImageParams(new URL(request.url), allowedWidths);
|
|
191
|
+
const params = parseImageParams(new URL(request.url), allowedWidths, imageConfig?.qualities);
|
|
178
192
|
if (!params) return badRequestResponse();
|
|
179
193
|
const { imageUrl, width, quality } = params;
|
|
180
194
|
const source = await handlers.fetchAsset(imageUrl, request);
|
|
@@ -212,4 +226,4 @@ async function handleImageOptimization(request, handlers, allowedWidths, imageCo
|
|
|
212
226
|
}
|
|
213
227
|
}
|
|
214
228
|
//#endregion
|
|
215
|
-
export { DEFAULT_DEVICE_SIZES, DEFAULT_IMAGE_SIZES, IMAGE_CACHE_CONTROL, IMAGE_CONTENT_SECURITY_POLICY, IMAGE_OPTIMIZATION_PATH, VINEXT_IMAGE_OPTIMIZATION_PATH, handleImageOptimization, isImageOptimizationPath, isSafeImageContentType, negotiateImageFormat, parseImageParams };
|
|
229
|
+
export { DEFAULT_DEVICE_SIZES, DEFAULT_IMAGE_SIZES, IMAGE_CACHE_CONTROL, IMAGE_CONTENT_SECURITY_POLICY, IMAGE_OPTIMIZATION_PATH, VINEXT_IMAGE_OPTIMIZATION_PATH, handleImageOptimization, isImageOptimizationPath, isSafeImageContentType, negotiateImageFormat, parseImageParams, resolveDevImageRedirect };
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
//#region src/server/implicit-tags.d.ts
|
|
2
2
|
type AppCacheLeafKind = "page" | "route";
|
|
3
|
+
declare function buildAppPageTags(cleanPathname: string, extraTags: string[], routeSegments: readonly string[]): string[];
|
|
3
4
|
declare function buildPageCacheTags(pathname: string, extraTags: string[], routeSegments: string[], leafKind: AppCacheLeafKind): string[];
|
|
4
5
|
//#endregion
|
|
5
|
-
export { buildPageCacheTags };
|
|
6
|
+
export { buildAppPageTags, buildPageCacheTags };
|
|
@@ -29,6 +29,9 @@ function appendDerivedTags(tags, routePath) {
|
|
|
29
29
|
appendUnique(tags, `${NEXT_CACHE_IMPLICIT_TAG_ID}${currentPathname}`);
|
|
30
30
|
}
|
|
31
31
|
}
|
|
32
|
+
function buildAppPageTags(cleanPathname, extraTags, routeSegments) {
|
|
33
|
+
return buildPageCacheTags(cleanPathname, extraTags, [...routeSegments], "page");
|
|
34
|
+
}
|
|
32
35
|
function buildPageCacheTags(pathname, extraTags, routeSegments, leafKind) {
|
|
33
36
|
const tags = [pathname, `${NEXT_CACHE_IMPLICIT_TAG_ID}${pathname}`];
|
|
34
37
|
if (pathname === "/") appendUnique(tags, `${NEXT_CACHE_IMPLICIT_TAG_ID}/index`);
|
|
@@ -38,4 +41,4 @@ function buildPageCacheTags(pathname, extraTags, routeSegments, leafKind) {
|
|
|
38
41
|
return tags.map(encodeCacheTag);
|
|
39
42
|
}
|
|
40
43
|
//#endregion
|
|
41
|
-
export { buildPageCacheTags };
|
|
44
|
+
export { buildAppPageTags, buildPageCacheTags };
|
|
@@ -34,6 +34,12 @@
|
|
|
34
34
|
* Ensure the instrumentation module's `register()` and `onRequestError`
|
|
35
35
|
* hooks have been applied exactly once.
|
|
36
36
|
*
|
|
37
|
+
* After `register()` runs, we extend the OTel tracer provider so that spans
|
|
38
|
+
* created inside Cache Component renders (warmup / fallback-resume phases) use
|
|
39
|
+
* a fresh OTel context rather than inheriting the prerender work unit store.
|
|
40
|
+
* This mirrors Next.js's `afterRegistration()` call in
|
|
41
|
+
* `instrumentation-node-extensions.ts`.
|
|
42
|
+
*
|
|
37
43
|
* @param instrumentationModule - The imported `instrumentation.ts` module.
|
|
38
44
|
* Passed as an argument so the generated entry can import it normally
|
|
39
45
|
* without this helper needing to know the module path.
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { extendTracerProviderForCacheComponents } from "./otel-tracer-extension.js";
|
|
1
2
|
//#region src/server/instrumentation-runtime.ts
|
|
2
3
|
let initialized = false;
|
|
3
4
|
let initPromise = null;
|
|
@@ -8,6 +9,12 @@ function isOnRequestErrorHandler(value) {
|
|
|
8
9
|
* Ensure the instrumentation module's `register()` and `onRequestError`
|
|
9
10
|
* hooks have been applied exactly once.
|
|
10
11
|
*
|
|
12
|
+
* After `register()` runs, we extend the OTel tracer provider so that spans
|
|
13
|
+
* created inside Cache Component renders (warmup / fallback-resume phases) use
|
|
14
|
+
* a fresh OTel context rather than inheriting the prerender work unit store.
|
|
15
|
+
* This mirrors Next.js's `afterRegistration()` call in
|
|
16
|
+
* `instrumentation-node-extensions.ts`.
|
|
17
|
+
*
|
|
11
18
|
* @param instrumentationModule - The imported `instrumentation.ts` module.
|
|
12
19
|
* Passed as an argument so the generated entry can import it normally
|
|
13
20
|
* without this helper needing to know the module path.
|
|
@@ -18,6 +25,7 @@ async function ensureInstrumentationRegistered(instrumentationModule) {
|
|
|
18
25
|
if (initPromise) return initPromise;
|
|
19
26
|
initPromise = (async () => {
|
|
20
27
|
if (typeof instrumentationModule.register === "function") await instrumentationModule.register();
|
|
28
|
+
extendTracerProviderForCacheComponents();
|
|
21
29
|
if (isOnRequestErrorHandler(instrumentationModule.onRequestError)) globalThis.__VINEXT_onRequestErrorHandler__ = instrumentationModule.onRequestError;
|
|
22
30
|
initialized = true;
|
|
23
31
|
})();
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
import { CacheControlMetadata } from "../shims/cache.js";
|
|
2
|
+
import { NEVER_CACHE_CONTROL, NO_STORE_CACHE_CONTROL } from "./cache-control.js";
|
|
3
|
+
|
|
4
|
+
//#region src/server/isr-decision.d.ts
|
|
5
|
+
type IsrDisposition = "HIT" | "STALE" | "MISS";
|
|
6
|
+
type IsrDecision = {
|
|
7
|
+
disposition: IsrDisposition; /** True when the caller must schedule a background regeneration. */
|
|
8
|
+
scheduleRegeneration: boolean; /** The `Cache-Control` string to stamp on the response. */
|
|
9
|
+
cacheControl: string;
|
|
10
|
+
};
|
|
11
|
+
/**
|
|
12
|
+
* Per-router special-case policies for `Cache-Control`.
|
|
13
|
+
*
|
|
14
|
+
* - `"app-page"` / `"pages"`: `buildCachedRevalidateCacheControl` for HIT/STALE.
|
|
15
|
+
* - `"app-route"`: same, but `revalidateSeconds=0` forces `NEVER_CACHE_CONTROL`
|
|
16
|
+
* and `revalidateSeconds=Infinity` forces `STATIC_CACHE_CONTROL`.
|
|
17
|
+
* - `"dev"`: like `"pages"`, but `revalidate=0`/`Infinity` guards are absent
|
|
18
|
+
* (dev never caches when revalidate=0 and never has Infinity entries in practice).
|
|
19
|
+
*/
|
|
20
|
+
type IsrPolicyKind = "app-page" | "app-route" | "pages" | "dev";
|
|
21
|
+
type DecideIsrOptions = {
|
|
22
|
+
/**
|
|
23
|
+
* The cache state. Content guards (kind-mismatch, empty body,
|
|
24
|
+
* query-variant-unproven) must have already passed before passing
|
|
25
|
+
* `"HIT"` or `"STALE"` here.
|
|
26
|
+
*/
|
|
27
|
+
cacheState: "HIT" | "STALE" | "MISS"; /** Which router is making the decision. */
|
|
28
|
+
kind: IsrPolicyKind;
|
|
29
|
+
/**
|
|
30
|
+
* The route's configured revalidate window in seconds. Used as the fallback
|
|
31
|
+
* when `cacheControlMeta` is absent.
|
|
32
|
+
*
|
|
33
|
+
* For `"dev"` call sites this is the only source of the revalidate value —
|
|
34
|
+
* dev never has metadata attached to a cache entry.
|
|
35
|
+
*/
|
|
36
|
+
revalidateSeconds: number;
|
|
37
|
+
/**
|
|
38
|
+
* The expire ceiling (seconds from epoch) read from the route config.
|
|
39
|
+
* Absent when the route pre-dates expire metadata support.
|
|
40
|
+
*/
|
|
41
|
+
expireSeconds?: number;
|
|
42
|
+
/**
|
|
43
|
+
* Optional per-entry metadata written alongside the cache value.
|
|
44
|
+
* When present its `revalidate`/`expire` fields override the route defaults,
|
|
45
|
+
* exactly as the call sites do today with `cacheControl?.revalidate ?? revalidateSeconds`.
|
|
46
|
+
*/
|
|
47
|
+
cacheControlMeta?: CacheControlMetadata;
|
|
48
|
+
};
|
|
49
|
+
/**
|
|
50
|
+
* Derive the `Cache-Control` string for an ISR response.
|
|
51
|
+
*
|
|
52
|
+
* Content guards (kind mismatch, query-variant-unproven, empty body) are the
|
|
53
|
+
* caller's responsibility and must happen *before* this call. `cacheState`
|
|
54
|
+
* must only be `"HIT"` or `"STALE"` when those guards have already passed.
|
|
55
|
+
*/
|
|
56
|
+
declare function decideIsr(options: DecideIsrOptions): IsrDecision;
|
|
57
|
+
/**
|
|
58
|
+
* Build the `Cache-Control` string for a fresh MISS response whose ISR policy
|
|
59
|
+
* is known (i.e. revalidate is set and > 0). Uses the unbounded SWR form when
|
|
60
|
+
* no expire ceiling is available, exactly as `buildRevalidateCacheControl` does.
|
|
61
|
+
*
|
|
62
|
+
* Separate from `decideIsr` because a MISS doesn't read a cache entry and
|
|
63
|
+
* therefore never has `cacheControlMeta`. `expireSeconds` here is the route
|
|
64
|
+
* config ceiling passed directly from the caller (not a per-entry fallback).
|
|
65
|
+
*/
|
|
66
|
+
declare function buildMissIsrCacheControl(revalidateSeconds: number, expireSeconds?: number): string;
|
|
67
|
+
/**
|
|
68
|
+
* Build the `Cache-Control` string for a fresh (MISS) app-route response.
|
|
69
|
+
*
|
|
70
|
+
* Applies the same `revalidateSeconds=0`→NEVER and `Infinity`→STATIC gates
|
|
71
|
+
* that `decideIsr` uses for app-route cached responses. `expireSeconds` is
|
|
72
|
+
* the route config ceiling passed directly (not per-entry metadata fallback).
|
|
73
|
+
*
|
|
74
|
+
* Used by `applyRouteHandlerRevalidateHeader` which operates on a fresh
|
|
75
|
+
* response that has no per-entry cache metadata.
|
|
76
|
+
*/
|
|
77
|
+
declare function buildAppRouteMissIsrCacheControl(revalidateSeconds: number, expireSeconds?: number): string;
|
|
78
|
+
//#endregion
|
|
79
|
+
export { NEVER_CACHE_CONTROL as ISR_NEVER_CACHE_CONTROL, NO_STORE_CACHE_CONTROL as ISR_NO_STORE_CACHE_CONTROL, buildAppRouteMissIsrCacheControl, buildMissIsrCacheControl, decideIsr };
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
import { NEVER_CACHE_CONTROL, NO_STORE_CACHE_CONTROL, STATIC_CACHE_CONTROL, buildCachedRevalidateCacheControl, buildRevalidateCacheControl } from "./cache-control.js";
|
|
2
|
+
//#region src/server/isr-decision.ts
|
|
3
|
+
/** Resolve effective revalidate/expire, preferring per-entry metadata. */
|
|
4
|
+
function resolveRevalidate(options) {
|
|
5
|
+
return {
|
|
6
|
+
effectiveRevalidate: options.cacheControlMeta?.revalidate ?? options.revalidateSeconds,
|
|
7
|
+
effectiveExpire: options.cacheControlMeta === void 0 ? void 0 : options.cacheControlMeta.expire ?? options.expireSeconds
|
|
8
|
+
};
|
|
9
|
+
}
|
|
10
|
+
function buildCacheControl(disposition, kind, revalidate, expire) {
|
|
11
|
+
if (kind === "app-route") {
|
|
12
|
+
if (revalidate === 0) return NEVER_CACHE_CONTROL;
|
|
13
|
+
if (revalidate === Infinity) return STATIC_CACHE_CONTROL;
|
|
14
|
+
}
|
|
15
|
+
return buildCachedRevalidateCacheControl(disposition, revalidate, expire);
|
|
16
|
+
}
|
|
17
|
+
/**
|
|
18
|
+
* Derive the `Cache-Control` string for an ISR response.
|
|
19
|
+
*
|
|
20
|
+
* Content guards (kind mismatch, query-variant-unproven, empty body) are the
|
|
21
|
+
* caller's responsibility and must happen *before* this call. `cacheState`
|
|
22
|
+
* must only be `"HIT"` or `"STALE"` when those guards have already passed.
|
|
23
|
+
*/
|
|
24
|
+
function decideIsr(options) {
|
|
25
|
+
if (options.cacheState === "MISS") return {
|
|
26
|
+
disposition: "MISS",
|
|
27
|
+
scheduleRegeneration: false,
|
|
28
|
+
cacheControl: ""
|
|
29
|
+
};
|
|
30
|
+
const { effectiveRevalidate, effectiveExpire } = resolveRevalidate(options);
|
|
31
|
+
if (options.cacheState === "HIT") return {
|
|
32
|
+
disposition: "HIT",
|
|
33
|
+
scheduleRegeneration: false,
|
|
34
|
+
cacheControl: buildCacheControl("HIT", options.kind, effectiveRevalidate, effectiveExpire)
|
|
35
|
+
};
|
|
36
|
+
return {
|
|
37
|
+
disposition: "STALE",
|
|
38
|
+
scheduleRegeneration: true,
|
|
39
|
+
cacheControl: buildCacheControl("STALE", options.kind, effectiveRevalidate, effectiveExpire)
|
|
40
|
+
};
|
|
41
|
+
}
|
|
42
|
+
/**
|
|
43
|
+
* Build the `Cache-Control` string for a fresh MISS response whose ISR policy
|
|
44
|
+
* is known (i.e. revalidate is set and > 0). Uses the unbounded SWR form when
|
|
45
|
+
* no expire ceiling is available, exactly as `buildRevalidateCacheControl` does.
|
|
46
|
+
*
|
|
47
|
+
* Separate from `decideIsr` because a MISS doesn't read a cache entry and
|
|
48
|
+
* therefore never has `cacheControlMeta`. `expireSeconds` here is the route
|
|
49
|
+
* config ceiling passed directly from the caller (not a per-entry fallback).
|
|
50
|
+
*/
|
|
51
|
+
function buildMissIsrCacheControl(revalidateSeconds, expireSeconds) {
|
|
52
|
+
return buildRevalidateCacheControl(revalidateSeconds, expireSeconds);
|
|
53
|
+
}
|
|
54
|
+
/**
|
|
55
|
+
* Build the `Cache-Control` string for a fresh (MISS) app-route response.
|
|
56
|
+
*
|
|
57
|
+
* Applies the same `revalidateSeconds=0`→NEVER and `Infinity`→STATIC gates
|
|
58
|
+
* that `decideIsr` uses for app-route cached responses. `expireSeconds` is
|
|
59
|
+
* the route config ceiling passed directly (not per-entry metadata fallback).
|
|
60
|
+
*
|
|
61
|
+
* Used by `applyRouteHandlerRevalidateHeader` which operates on a fresh
|
|
62
|
+
* response that has no per-entry cache metadata.
|
|
63
|
+
*/
|
|
64
|
+
function buildAppRouteMissIsrCacheControl(revalidateSeconds, expireSeconds) {
|
|
65
|
+
if (revalidateSeconds === 0) return NEVER_CACHE_CONTROL;
|
|
66
|
+
if (revalidateSeconds === Infinity) return STATIC_CACHE_CONTROL;
|
|
67
|
+
return buildRevalidateCacheControl(revalidateSeconds, expireSeconds);
|
|
68
|
+
}
|
|
69
|
+
//#endregion
|
|
70
|
+
export { NEVER_CACHE_CONTROL as ISR_NEVER_CACHE_CONTROL, NO_STORE_CACHE_CONTROL as ISR_NO_STORE_CACHE_CONTROL, buildAppRouteMissIsrCacheControl, buildMissIsrCacheControl, decideIsr };
|
|
@@ -53,9 +53,9 @@ function getMetadataRouteFunctions(route) {
|
|
|
53
53
|
routeFunctionCache.set(route, functions);
|
|
54
54
|
return functions;
|
|
55
55
|
}
|
|
56
|
-
function matchMetadataRoute(route, cleanPathname, functions) {
|
|
56
|
+
function matchMetadataRoute(route, cleanPathname, functions, getUrlParts) {
|
|
57
57
|
if (route.patternParts) {
|
|
58
|
-
const urlParts =
|
|
58
|
+
const urlParts = getUrlParts();
|
|
59
59
|
if (functions.hasGeneratedImageMetadata && urlParts.length > 0) {
|
|
60
60
|
const params = matchMetadataRoutePattern(urlParts.slice(0, -1), route.patternParts);
|
|
61
61
|
if (params) return {
|
|
@@ -184,6 +184,8 @@ function serveStaticMetadataRoute(route) {
|
|
|
184
184
|
}
|
|
185
185
|
}
|
|
186
186
|
async function handleMetadataRouteRequest(options) {
|
|
187
|
+
let urlParts;
|
|
188
|
+
const getUrlParts = () => urlParts ??= options.cleanPathname.split("/").filter(Boolean);
|
|
187
189
|
for (const route of options.metadataRoutes) {
|
|
188
190
|
const functions = getMetadataRouteFunctions(route);
|
|
189
191
|
if (route.type === "sitemap" && route.isDynamic) {
|
|
@@ -193,7 +195,7 @@ async function handleMetadataRouteRequest(options) {
|
|
|
193
195
|
continue;
|
|
194
196
|
}
|
|
195
197
|
}
|
|
196
|
-
const match = matchMetadataRoute(route, options.cleanPathname, functions);
|
|
198
|
+
const match = matchMetadataRoute(route, options.cleanPathname, functions, getUrlParts);
|
|
197
199
|
if (!match) continue;
|
|
198
200
|
return route.isDynamic ? callDynamicMetadataRoute(route, match, options.makeThenableParams, functions) : serveStaticMetadataRoute(route);
|
|
199
201
|
}
|
|
@@ -19,6 +19,19 @@ type MiddlewareHandler = (request: NextRequest, event: NextFetchEvent) => Respon
|
|
|
19
19
|
type ExecuteMiddlewareOptions = {
|
|
20
20
|
basePath?: string;
|
|
21
21
|
filePath?: string;
|
|
22
|
+
/**
|
|
23
|
+
* Whether the incoming request was inside the configured basePath. Drives
|
|
24
|
+
* the `nextUrl.basePath` the middleware observes: in-basePath requests are
|
|
25
|
+
* re-prefixed so NextURL reports the configured basePath, while
|
|
26
|
+
* out-of-basePath ("absolute path") requests stay un-prefixed so middleware
|
|
27
|
+
* sees `nextUrl.basePath === ""` (Next.js `getNextPathnameInfo` semantics —
|
|
28
|
+
* see test/e2e/middleware-base-path "should execute from absolute paths").
|
|
29
|
+
* When omitted it is derived from the request URL, which is correct for the
|
|
30
|
+
* Pages prod/deploy adapters because they pass the original (un-stripped)
|
|
31
|
+
* URL. Callers that pass an already-stripped URL (dev server, App Router)
|
|
32
|
+
* must set this explicitly.
|
|
33
|
+
*/
|
|
34
|
+
hadBasePath?: boolean;
|
|
22
35
|
i18nConfig?: NextI18nConfig | null;
|
|
23
36
|
includeErrorDetails?: boolean;
|
|
24
37
|
/**
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { normalizePathnameForRouteMatchStrict } from "../routing/utils.js";
|
|
2
|
+
import { addBasePathToPathname, hasBasePath, stripBasePath } from "../utils/base-path.js";
|
|
2
3
|
import "./server-globals.js";
|
|
3
4
|
import { getRequestExecutionContext, runWithExecutionContext } from "../shims/request-context.js";
|
|
4
5
|
import { MIDDLEWARE_REWRITE_HEADER } from "./headers.js";
|
|
@@ -99,12 +100,13 @@ function resolveMiddlewarePathname(request) {
|
|
|
99
100
|
return badRequestResponse();
|
|
100
101
|
}
|
|
101
102
|
}
|
|
102
|
-
function createNextRequest(request, normalizedPathname, i18nConfig, basePath, trailingSlash) {
|
|
103
|
+
function createNextRequest(request, normalizedPathname, i18nConfig, basePath, trailingSlash, hadBasePath) {
|
|
103
104
|
const url = new URL(request.url);
|
|
104
105
|
let mwRequest = request.body && !request.bodyUsed ? request.clone() : request;
|
|
105
|
-
|
|
106
|
+
const mwPathname = basePath && hadBasePath ? addBasePathToPathname(normalizedPathname, basePath) : normalizedPathname;
|
|
107
|
+
if (mwPathname !== url.pathname) {
|
|
106
108
|
const mwUrl = new URL(url);
|
|
107
|
-
mwUrl.pathname =
|
|
109
|
+
mwUrl.pathname = mwPathname;
|
|
108
110
|
mwRequest = new Request(mwUrl, mwRequest);
|
|
109
111
|
}
|
|
110
112
|
const nextConfig = basePath || i18nConfig || trailingSlash ? {
|
|
@@ -124,9 +126,11 @@ async function executeMiddleware(options) {
|
|
|
124
126
|
continue: false,
|
|
125
127
|
response: normalizedPathname
|
|
126
128
|
};
|
|
127
|
-
|
|
128
|
-
const
|
|
129
|
-
|
|
129
|
+
const hadBasePath = options.hadBasePath ?? (!options.basePath || hasBasePath(new URL(options.request.url).pathname, options.basePath));
|
|
130
|
+
const matchPathname = options.basePath ? stripBasePath(normalizedPathname, options.basePath) : normalizedPathname;
|
|
131
|
+
if (!matchesMiddleware(matchPathname, middlewareMatcher(options.module), options.request, options.i18nConfig)) return { continue: true };
|
|
132
|
+
const nextRequest = createNextRequest(options.request, normalizedPathname, options.i18nConfig, options.basePath, options.trailingSlash, hadBasePath);
|
|
133
|
+
const fetchEvent = new NextFetchEvent({ page: matchPathname });
|
|
130
134
|
let response;
|
|
131
135
|
try {
|
|
132
136
|
response = await middlewareFn(nextRequest, fetchEvent);
|
|
@@ -195,7 +199,7 @@ async function executeMiddleware(options) {
|
|
|
195
199
|
try {
|
|
196
200
|
const rewriteParsed = new URL(rewriteUrl, options.request.url);
|
|
197
201
|
const requestOrigin = new URL(options.request.url).origin;
|
|
198
|
-
if (rewriteParsed.origin === requestOrigin) rewritePath = rewriteParsed.pathname + rewriteParsed.search;
|
|
202
|
+
if (rewriteParsed.origin === requestOrigin) rewritePath = (options.basePath ? stripBasePath(rewriteParsed.pathname, options.basePath) : rewriteParsed.pathname) + rewriteParsed.search;
|
|
199
203
|
else rewritePath = rewriteParsed.href;
|
|
200
204
|
} catch {
|
|
201
205
|
rewritePath = rewriteUrl;
|
|
@@ -85,6 +85,7 @@ async function runMiddleware(runner, middlewarePath, request, i18nConfig, basePa
|
|
|
85
85
|
return runGeneratedMiddleware({
|
|
86
86
|
basePath,
|
|
87
87
|
filePath: middlewarePath,
|
|
88
|
+
hadBasePath: true,
|
|
88
89
|
i18nConfig,
|
|
89
90
|
includeErrorDetails: process.env.NODE_ENV !== "production",
|
|
90
91
|
isDataRequest,
|