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
|
@@ -14,6 +14,8 @@ export type BundleBackfillChunk = {
|
|
|
14
14
|
* `toSlash` where the result becomes a module id / manifest key.
|
|
15
15
|
*/
|
|
16
16
|
export declare function tryRealpathSync(candidate: string): string | null;
|
|
17
|
+
/** Forward-slash `path.relative(root, moduleId)`, which may escape with `../`. */
|
|
18
|
+
export declare function relativeToRoot(root: string, moduleId: string): string;
|
|
17
19
|
export declare function relativeWithinRoot(root: string, moduleId: string): string | null;
|
|
18
20
|
export declare function augmentSsrManifestFromBundle(ssrManifest: Record<string, string[]>, bundle: Record<string, BundleBackfillChunk | {
|
|
19
21
|
type: string;
|
|
@@ -16,8 +16,12 @@ function tryRealpathSync(candidate) {
|
|
|
16
16
|
function isWindowsAbsolutePath(candidate) {
|
|
17
17
|
return /^[a-zA-Z]:[\\/]/.test(candidate) || candidate.startsWith("\\\\");
|
|
18
18
|
}
|
|
19
|
+
/** Forward-slash `path.relative(root, moduleId)`, which may escape with `../`. */
|
|
20
|
+
function relativeToRoot(root, moduleId) {
|
|
21
|
+
return isWindowsAbsolutePath(root) || isWindowsAbsolutePath(moduleId) ? path.win32.relative(root, moduleId) : path.relative(root, moduleId);
|
|
22
|
+
}
|
|
19
23
|
function relativeWithinRoot(root, moduleId) {
|
|
20
|
-
const relativeId =
|
|
24
|
+
const relativeId = relativeToRoot(root, moduleId);
|
|
21
25
|
if (!relativeId || relativeId === ".." || relativeId.startsWith("../")) return null;
|
|
22
26
|
return relativeId;
|
|
23
27
|
}
|
|
@@ -70,4 +74,4 @@ function augmentSsrManifestFromBundle(ssrManifest, bundle, root, base = "/") {
|
|
|
70
74
|
return Object.fromEntries(Object.entries(nextManifest).map(([key, files]) => [key, [...files]]));
|
|
71
75
|
}
|
|
72
76
|
//#endregion
|
|
73
|
-
export { augmentSsrManifestFromBundle, relativeWithinRoot, tryRealpathSync };
|
|
77
|
+
export { augmentSsrManifestFromBundle, relativeToRoot, relativeWithinRoot, tryRealpathSync };
|
|
@@ -74,7 +74,13 @@ export type CacheAdapterBuildOutput = {
|
|
|
74
74
|
export type CacheAdapterDescriptor<O extends Record<string, unknown> = Record<string, unknown>> = {
|
|
75
75
|
/**
|
|
76
76
|
* Module specifier (or absolute path, e.g. from `require.resolve(...)`) whose
|
|
77
|
-
* default export
|
|
77
|
+
* default export creates the adapter from one `{ env, options }` argument:
|
|
78
|
+
* either a factory `({ env, options }) => adapter` or a class
|
|
79
|
+
* `new Adapter({ env, options })`. Class syntax is invoked with `new`; any
|
|
80
|
+
* other function is called. For a constructor that is not class syntax (an
|
|
81
|
+
* ES5-compiled, bound or Proxy-wrapped class), export
|
|
82
|
+
* `(args) => new Adapter(args)`. The result must be the adapter object
|
|
83
|
+
* itself, not a Promise.
|
|
78
84
|
*/
|
|
79
85
|
adapter: string;
|
|
80
86
|
/** JSON-serializable options forwarded to the factory at runtime. */
|
|
@@ -103,7 +103,7 @@ function generateCacheAdaptersModule(cache) {
|
|
|
103
103
|
"export function registerConfiguredCacheAdapters() {}",
|
|
104
104
|
""
|
|
105
105
|
].join("\n");
|
|
106
|
-
const lines = ["// vinext: generated from the `cache` option in your vinext() plugin config."];
|
|
106
|
+
const lines = ["// vinext: generated from the `cache` option in your vinext() plugin config.", `import { instantiateCacheAdapter } from "vinext/shims/cache-adapter-instantiate";`];
|
|
107
107
|
if (data?.adapter) {
|
|
108
108
|
lines.push(`import __vinextDataAdapterFactory from ${JSON.stringify(data.adapter)};`);
|
|
109
109
|
lines.push(`import { registerDataCacheHandler } from "vinext/shims/cache-handler";`);
|
|
@@ -114,11 +114,11 @@ function generateCacheAdaptersModule(cache) {
|
|
|
114
114
|
} else if (dataProvidesBuildIdentity) lines.push(`import { DefaultCdnCacheAdapter } from "vinext/shims/cdn-cache";`, `import { registerCdnCacheAdapter } from "vinext/shims/cdn-cache-state";`);
|
|
115
115
|
lines.push("", "// A factory that throws (e.g. a missing binding on an incompatible runtime)", "// is logged and skipped so the default handler stays in place.", "function __vinextFormatAdapterError(error) {", " if (error instanceof Error && error.message) return error.message;", " try {", " return String(error);", " } catch {", " return '<unknown error>';", " }", "}", "", "let __vinextCacheAdaptersRegistered = false;", "", "export function registerConfiguredCacheAdapters(env) {", " if (typeof process !== 'undefined' && process.env?.__VINEXT_PRERENDER_PATH_DISCOVERY === '1') return;", " if (__vinextCacheAdaptersRegistered) return;", " __vinextCacheAdaptersRegistered = true;");
|
|
116
116
|
if (data?.adapter) {
|
|
117
|
-
lines.push(" try {", ` registerDataCacheHandler(() => __vinextDataAdapterFactory
|
|
117
|
+
lines.push(" try {", ` registerDataCacheHandler(() => instantiateCacheAdapter(__vinextDataAdapterFactory, { env, options: ${inlineOptions(data.adapter, data.options)} }, "data"));`);
|
|
118
118
|
if (dataProvidesBuildIdentity) lines.push(" registerCdnCacheAdapter(() => new DefaultCdnCacheAdapter(", " process.env.__VINEXT_RSC_BUILD_IDENTITY || process.env.__VINEXT_BUILD_ID,", " ));");
|
|
119
119
|
lines.push(" } catch (error) {", " console.warn(\"[vinext] failed to initialize the configured data cache adapter; using the default handler.\\n\" + __vinextFormatAdapterError(error));", " }");
|
|
120
120
|
}
|
|
121
|
-
if (cdn?.adapter) lines.push(" try {", ` registerCdnCacheAdapter(() => __vinextCdnAdapterFactory
|
|
121
|
+
if (cdn?.adapter) lines.push(" try {", ` registerCdnCacheAdapter(() => instantiateCacheAdapter(__vinextCdnAdapterFactory, { env, options: ${inlineOptions(cdn.adapter, cdn.options)} }, "cdn"));`, " } catch (error) {", " console.warn(\"[vinext] failed to initialize the configured CDN cache adapter; using the default adapter.\\n\" + __vinextFormatAdapterError(error));", " }");
|
|
122
122
|
lines.push("}", "");
|
|
123
123
|
return lines.join("\n");
|
|
124
124
|
}
|
package/dist/check.js
CHANGED
|
@@ -210,7 +210,7 @@ const CONFIG_SUPPORT = {
|
|
|
210
210
|
},
|
|
211
211
|
transpilePackages: {
|
|
212
212
|
status: "supported",
|
|
213
|
-
detail: "listed packages are bundled instead of externalized on the server"
|
|
213
|
+
detail: "listed packages are bundled instead of externalized on the server, and their next/dynamic() calls get SSR preload links"
|
|
214
214
|
},
|
|
215
215
|
serverExternalPackages: {
|
|
216
216
|
status: "supported",
|
|
@@ -252,9 +252,10 @@ const CONFIG_SUPPORT = {
|
|
|
252
252
|
status: "unsupported",
|
|
253
253
|
detail: "custom cacheLife profiles are ignored; only the built-in profiles are available"
|
|
254
254
|
},
|
|
255
|
-
skipTrailingSlashRedirect: {
|
|
256
|
-
|
|
257
|
-
|
|
255
|
+
skipTrailingSlashRedirect: { status: "supported" },
|
|
256
|
+
"experimental.skipTrailingSlashRedirect": {
|
|
257
|
+
status: "supported",
|
|
258
|
+
detail: "legacy alias of skipTrailingSlashRedirect"
|
|
258
259
|
},
|
|
259
260
|
reactCompiler: {
|
|
260
261
|
status: "partial",
|
|
@@ -433,8 +434,8 @@ const LIBRARY_SUPPORT = {
|
|
|
433
434
|
detail: "renders client-side; by default events are only collected when deployed on Vercel"
|
|
434
435
|
},
|
|
435
436
|
"next-intl": {
|
|
436
|
-
status: "
|
|
437
|
-
detail: "
|
|
437
|
+
status: "supported",
|
|
438
|
+
detail: "createNextIntlPlugin is only optional with the request config at (src/)i18n/request.*; its experimental messages, precompile and extract options don't work (they need webpack/Turbopack loaders)"
|
|
438
439
|
},
|
|
439
440
|
"@clerk/nextjs": {
|
|
440
441
|
status: "supported",
|
package/dist/cli.js
CHANGED
|
@@ -4,8 +4,8 @@ import { detectPackageManager, ensureViteConfigCompatibility, findViteConfigPath
|
|
|
4
4
|
import { formatReport, runCheck } from "./check.js";
|
|
5
5
|
import { parseArgs } from "./cli-args.js";
|
|
6
6
|
import { findViteRoot, getViteCliInvocation, valueOptionName } from "./utils/vite-cli-invocation.js";
|
|
7
|
-
import { init } from "./init.js";
|
|
8
7
|
import { resolveInitOptions } from "./init-platform.js";
|
|
8
|
+
import { init } from "./init.js";
|
|
9
9
|
import { loadDotenv } from "./config/dotenv.js";
|
|
10
10
|
import { PHASE_PRODUCTION_BUILD } from "./shims/constants.js";
|
|
11
11
|
import { loadNextConfig, resolveNextConfig } from "./config/next-config.js";
|
|
@@ -275,6 +275,7 @@ function printHelp(cmd) {
|
|
|
275
275
|
--no-install Update dependency entries without installing them
|
|
276
276
|
--force Overwrite existing vite.config.ts
|
|
277
277
|
--platform <target> Deployment target: cloudflare or node
|
|
278
|
+
--name <name> Cloudflare Worker name (default: derived from package.json)
|
|
278
279
|
--prerender Configure Vite builds to pre-render static routes
|
|
279
280
|
(Cloudflare deploy won't serve them unless using Static Assets)
|
|
280
281
|
--warm-cache Add cache warming to the Cloudflare deploy script
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { RequestContext } from "../config/request-context.js";
|
|
2
|
+
import { BasePathMatchState, RedirectMatch } from "../config/config-matchers.js";
|
|
3
|
+
import { ClientRedirect } from "./client-redirects.js";
|
|
4
|
+
//#region src/client/client-redirect-matcher.d.ts
|
|
5
|
+
/**
|
|
6
|
+
* Resolve the first matching client-safe redirect. A matching server-owned
|
|
7
|
+
* rule stops client evaluation and returns null so a later client-safe rule
|
|
8
|
+
* cannot take its place. The navigation then continues as it does in Next.js,
|
|
9
|
+
* which never resolves config redirects in the browser: a document request (or
|
|
10
|
+
* a data request that falls back to one) reaches the server rule, while a page
|
|
11
|
+
* without data fetching renders without it.
|
|
12
|
+
*/
|
|
13
|
+
export declare function matchClientRedirect(pathname: string, redirects: ClientRedirect[], context: RequestContext, basePathState: BasePathMatchState): RedirectMatch | null;
|
|
14
|
+
//#endregion
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import { matchRedirect } from "../config/config-matchers.js";
|
|
2
|
+
//#region src/client/client-redirect-matcher.ts
|
|
3
|
+
const matcherRules = /* @__PURE__ */ new WeakMap();
|
|
4
|
+
function getMatcherRule(redirect) {
|
|
5
|
+
let rule = matcherRules.get(redirect);
|
|
6
|
+
if (!rule) {
|
|
7
|
+
rule = [{
|
|
8
|
+
source: redirect.source,
|
|
9
|
+
destination: redirect.destination ?? "/",
|
|
10
|
+
permanent: redirect.permanent ?? false,
|
|
11
|
+
has: redirect.has,
|
|
12
|
+
missing: redirect.missing,
|
|
13
|
+
locale: redirect.locale,
|
|
14
|
+
basePath: redirect.basePath
|
|
15
|
+
}];
|
|
16
|
+
matcherRules.set(redirect, rule);
|
|
17
|
+
}
|
|
18
|
+
return rule;
|
|
19
|
+
}
|
|
20
|
+
/**
|
|
21
|
+
* Resolve the first matching client-safe redirect. A matching server-owned
|
|
22
|
+
* rule stops client evaluation and returns null so a later client-safe rule
|
|
23
|
+
* cannot take its place. The navigation then continues as it does in Next.js,
|
|
24
|
+
* which never resolves config redirects in the browser: a document request (or
|
|
25
|
+
* a data request that falls back to one) reaches the server rule, while a page
|
|
26
|
+
* without data fetching renders without it.
|
|
27
|
+
*/
|
|
28
|
+
function matchClientRedirect(pathname, redirects, context, basePathState) {
|
|
29
|
+
for (const redirect of redirects) {
|
|
30
|
+
const match = matchRedirect(pathname, getMatcherRule(redirect), context, basePathState);
|
|
31
|
+
if (match === null) continue;
|
|
32
|
+
return redirect.requiresServerEvaluation ? null : match;
|
|
33
|
+
}
|
|
34
|
+
return null;
|
|
35
|
+
}
|
|
36
|
+
//#endregion
|
|
37
|
+
export { matchClientRedirect };
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import { NextRedirect, ResolvedNextConfig } from "../config/next-config.js";
|
|
2
|
+
import { ClientHasCondition } from "./client-rewrites.js";
|
|
3
|
+
//#region src/client/client-redirects.d.ts
|
|
4
|
+
type ClientRedirectFields = Pick<NextRedirect, "basePath" | "locale" | "source"> & {
|
|
5
|
+
has?: ClientHasCondition[];
|
|
6
|
+
missing?: ClientHasCondition[];
|
|
7
|
+
};
|
|
8
|
+
/**
|
|
9
|
+
* Redirect data that is safe to publish in a browser bundle.
|
|
10
|
+
*
|
|
11
|
+
* Next.js keeps redirects in the server routes manifest only. Vinext resolves
|
|
12
|
+
* browser-authoritative redirects during Pages Router navigation, but rules
|
|
13
|
+
* with header or cookie conditions are server-owned: their condition values
|
|
14
|
+
* can be private, so the rule keeps only its source and client-safe
|
|
15
|
+
* conditions to preserve first-match ordering, and drops its destination.
|
|
16
|
+
*/
|
|
17
|
+
export type ClientRedirect = (ClientRedirectFields & {
|
|
18
|
+
destination: string;
|
|
19
|
+
permanent: boolean;
|
|
20
|
+
requiresServerEvaluation?: never;
|
|
21
|
+
}) | (ClientRedirectFields & {
|
|
22
|
+
destination?: never;
|
|
23
|
+
permanent?: never;
|
|
24
|
+
requiresServerEvaluation: true;
|
|
25
|
+
});
|
|
26
|
+
export declare function toClientRedirects(redirects: ResolvedNextConfig["redirects"]): ClientRedirect[];
|
|
27
|
+
//#endregion
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import { isClientHasCondition } from "./client-rewrites.js";
|
|
2
|
+
//#region src/client/client-redirects.ts
|
|
3
|
+
function toClientRedirect(redirect) {
|
|
4
|
+
const hasServerOnlyCondition = (redirect.has?.some((condition) => !isClientHasCondition(condition)) ?? false) || (redirect.missing?.some((condition) => !isClientHasCondition(condition)) ?? false);
|
|
5
|
+
const clientHas = redirect.has?.filter(isClientHasCondition);
|
|
6
|
+
const clientMissing = redirect.missing?.filter(isClientHasCondition);
|
|
7
|
+
const common = {
|
|
8
|
+
source: redirect.source,
|
|
9
|
+
has: clientHas?.length ? clientHas : void 0,
|
|
10
|
+
missing: clientMissing?.length ? clientMissing : void 0,
|
|
11
|
+
locale: redirect.locale,
|
|
12
|
+
basePath: redirect.basePath
|
|
13
|
+
};
|
|
14
|
+
if (hasServerOnlyCondition) return {
|
|
15
|
+
...common,
|
|
16
|
+
requiresServerEvaluation: true
|
|
17
|
+
};
|
|
18
|
+
return {
|
|
19
|
+
...common,
|
|
20
|
+
destination: redirect.destination,
|
|
21
|
+
permanent: redirect.permanent
|
|
22
|
+
};
|
|
23
|
+
}
|
|
24
|
+
function toClientRedirects(redirects) {
|
|
25
|
+
return redirects.map(toClientRedirect);
|
|
26
|
+
}
|
|
27
|
+
//#endregion
|
|
28
|
+
export { toClientRedirects };
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { HasCondition, NextRewrite, ResolvedNextConfig } from "../config/next-config.js";
|
|
2
2
|
//#region src/client/client-rewrites.d.ts
|
|
3
|
-
type ClientHasCondition = Omit<HasCondition, "type"> & {
|
|
3
|
+
export type ClientHasCondition = Omit<HasCondition, "type"> & {
|
|
4
4
|
type: "host" | "query";
|
|
5
5
|
};
|
|
6
6
|
type ClientRewriteFields = Pick<NextRewrite, "basePath" | "locale" | "source"> & {
|
|
@@ -26,5 +26,6 @@ export type ClientRewrites = {
|
|
|
26
26
|
beforeFiles: ClientRewrite[];
|
|
27
27
|
fallback: ClientRewrite[];
|
|
28
28
|
};
|
|
29
|
+
export declare function isClientHasCondition(condition: HasCondition): condition is ClientHasCondition;
|
|
29
30
|
export declare function toClientRewrites(rewrites: ResolvedNextConfig["rewrites"]): ClientRewrites;
|
|
30
31
|
//#endregion
|
|
@@ -57,6 +57,17 @@ export type NavigationRuntimeFunctions = {
|
|
|
57
57
|
preparePrefetchResponse?: (response: Response) => Promise<unknown>;
|
|
58
58
|
claimCurrentHistoryTreeSnapshot?: (historyUpdateMode: NavigationRuntimeHistoryUpdateMode, previousHistoryState: unknown) => void;
|
|
59
59
|
commitAppOwnedHistoryStateWrite?: (historyUpdateMode: NavigationRuntimeHistoryUpdateMode, previousHistoryState: unknown) => void;
|
|
60
|
+
/**
|
|
61
|
+
* Called after a raw (non-app-owned) history.pushState/replaceState with a
|
|
62
|
+
* URL so an App Router navigation that has not committed yet is discarded,
|
|
63
|
+
* matching Next.js' ACTION_RESTORE.
|
|
64
|
+
*/
|
|
65
|
+
discardPendingNavigation?: () => void;
|
|
66
|
+
/**
|
|
67
|
+
* Called before any history.pushState/replaceState so a navigation React is
|
|
68
|
+
* committing writes its URL first, as Next.js does from an insertion effect.
|
|
69
|
+
*/
|
|
70
|
+
flushCommittingNavigationUrl?: () => void;
|
|
60
71
|
};
|
|
61
72
|
export type NavigationRuntimeBootstrap = {
|
|
62
73
|
routeManifest: RouteManifest | null;
|
|
@@ -31,7 +31,7 @@ function readRuntimeWindow() {
|
|
|
31
31
|
}
|
|
32
32
|
function isNavigationRuntimeFunctions(value) {
|
|
33
33
|
if (!isUnknownRecord(value)) return false;
|
|
34
|
-
return isOptionalRuntimeFunction(Reflect.get(value, "clearNavigationCaches")) && isOptionalRuntimeFunction(Reflect.get(value, "commitHashNavigation")) && isOptionalRuntimeFunction(Reflect.get(value, "navigateExternal")) && isOptionalRuntimeFunction(Reflect.get(value, "navigate")) && isOptionalRuntimeFunction(Reflect.get(value, "getPrefetchRouterState")) && isOptionalRuntimeFunction(Reflect.get(value, "notifyLinkNavigationStart")) && isOptionalRuntimeFunction(Reflect.get(value, "pingVisibleLinks")) && isOptionalRuntimeFunction(Reflect.get(value, "preparePrefetchResponse")) && isOptionalRuntimeFunction(Reflect.get(value, "claimCurrentHistoryTreeSnapshot")) && isOptionalRuntimeFunction(Reflect.get(value, "commitAppOwnedHistoryStateWrite"));
|
|
34
|
+
return isOptionalRuntimeFunction(Reflect.get(value, "clearNavigationCaches")) && isOptionalRuntimeFunction(Reflect.get(value, "commitHashNavigation")) && isOptionalRuntimeFunction(Reflect.get(value, "navigateExternal")) && isOptionalRuntimeFunction(Reflect.get(value, "navigate")) && isOptionalRuntimeFunction(Reflect.get(value, "getPrefetchRouterState")) && isOptionalRuntimeFunction(Reflect.get(value, "notifyLinkNavigationStart")) && isOptionalRuntimeFunction(Reflect.get(value, "pingVisibleLinks")) && isOptionalRuntimeFunction(Reflect.get(value, "preparePrefetchResponse")) && isOptionalRuntimeFunction(Reflect.get(value, "claimCurrentHistoryTreeSnapshot")) && isOptionalRuntimeFunction(Reflect.get(value, "commitAppOwnedHistoryStateWrite")) && isOptionalRuntimeFunction(Reflect.get(value, "discardPendingNavigation")) && isOptionalRuntimeFunction(Reflect.get(value, "flushCommittingNavigationUrl"));
|
|
35
35
|
}
|
|
36
36
|
function isNavigationRuntimeRscChunk(value) {
|
|
37
37
|
if (typeof value === "string") return true;
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { HasCondition, NextHeader, NextI18nConfig, NextRedirect, NextRewrite } from "./next-config.js";
|
|
2
2
|
import { RequestContext, normalizeHost, parseCookies, requestContextFromRequest } from "./request-context.js";
|
|
3
|
+
import { RedirectDestinationQueryPart } from "./destination-params.js";
|
|
3
4
|
import { isExternalUrl } from "../utils/external-url.js";
|
|
4
5
|
//#region src/config/config-matchers.d.ts
|
|
5
6
|
/**
|
|
@@ -75,6 +76,15 @@ export declare function applyMiddlewareRequestHeaders(middlewareHeaders: Record<
|
|
|
75
76
|
};
|
|
76
77
|
export declare function checkHasConditions(has: HasCondition[] | undefined, missing: HasCondition[] | undefined, ctx: RequestContext): boolean;
|
|
77
78
|
export declare function matchConfigPattern(pathname: string, pattern: string): Record<string, string> | null;
|
|
79
|
+
/**
|
|
80
|
+
* A matched config redirect. `destinationQuery` holds the destination query's
|
|
81
|
+
* parts, split before params were substituted, for merging the request query.
|
|
82
|
+
*/
|
|
83
|
+
export type RedirectMatch = {
|
|
84
|
+
destination: string;
|
|
85
|
+
permanent: boolean;
|
|
86
|
+
destinationQuery: RedirectDestinationQueryPart[];
|
|
87
|
+
};
|
|
78
88
|
/**
|
|
79
89
|
* Apply redirect rules from next.config.js.
|
|
80
90
|
* Returns the redirect info if a redirect was matched, or null.
|
|
@@ -110,10 +120,7 @@ export declare function matchConfigPattern(pathname: string, pattern: string): R
|
|
|
110
120
|
* an original index < N are checked via matchConfigPattern first — they are
|
|
111
121
|
* few in practice (typically zero) so this is not a hot-path concern.
|
|
112
122
|
*/
|
|
113
|
-
export declare function matchRedirect(pathname: string, redirects: NextRedirect[], ctx: RequestContext, basePathState?: BasePathMatchState, onRuleSourceMatch?: (rule: NextRedirect) => void):
|
|
114
|
-
destination: string;
|
|
115
|
-
permanent: boolean;
|
|
116
|
-
} | null;
|
|
123
|
+
export declare function matchRedirect(pathname: string, redirects: NextRedirect[], ctx: RequestContext, basePathState?: BasePathMatchState, onRuleSourceMatch?: (rule: NextRedirect) => void): RedirectMatch | null;
|
|
117
124
|
/**
|
|
118
125
|
* Apply rewrite rules from next.config.js.
|
|
119
126
|
* Returns the rewritten URL or null if no rewrite matched.
|
|
@@ -142,7 +149,8 @@ export declare function matchesRewriteSource(pathname: string, rewrite: NextRewr
|
|
|
142
149
|
* users off-site.
|
|
143
150
|
*
|
|
144
151
|
* This function collapses any leading double (or more) slashes to a single
|
|
145
|
-
* slash for non-external (relative) destinations
|
|
152
|
+
* slash for non-external (relative) destinations, including tab/newline and
|
|
153
|
+
* leading C0 control characters that browsers ignore while parsing.
|
|
146
154
|
*/
|
|
147
155
|
export declare function sanitizeDestination(dest: string): string;
|
|
148
156
|
/**
|
|
@@ -162,13 +170,20 @@ export declare function sanitizeDestination(dest: string): string;
|
|
|
162
170
|
* still treated as an RSC fetch. Dropping it breaks RSC fetch semantics
|
|
163
171
|
* (issue #1529).
|
|
164
172
|
*
|
|
165
|
-
*
|
|
166
|
-
*
|
|
167
|
-
*
|
|
168
|
-
*
|
|
169
|
-
*
|
|
173
|
+
* Ported from Next.js's `{ ...requestQuery, ...destinationQuery }` merge and
|
|
174
|
+
* `stringifyQuery`: request keys keep their order (integer-like keys first, as
|
|
175
|
+
* in a JS object), a destination key overrides the request value in place,
|
|
176
|
+
* and destination-only keys follow. Request keys and values are re-encoded;
|
|
177
|
+
* destination query text is emitted verbatim, except that, like Next.js,
|
|
178
|
+
* destination strings equal to a request key or value are re-encoded too.
|
|
179
|
+
* Pass the match's
|
|
180
|
+
* `destinationQuery` so substituted params keep their value boundaries;
|
|
181
|
+
* without it the destination's own query is split. External destinations are returned untouched (a config redirect to another
|
|
182
|
+
* origin should not leak the original request's query).
|
|
183
|
+
*
|
|
184
|
+
* https://github.com/vercel/next.js/blob/canary/packages/next/src/server/server-route-utils.ts
|
|
170
185
|
*/
|
|
171
|
-
export declare function preserveRedirectDestinationQuery(destination: string, requestSearch: string): string;
|
|
186
|
+
export declare function preserveRedirectDestinationQuery(destination: string, requestSearch: string, destinationQuery?: RedirectDestinationQueryPart[]): string;
|
|
172
187
|
/**
|
|
173
188
|
* Proxy an incoming request to an external URL and return the upstream response.
|
|
174
189
|
*
|
|
@@ -3,6 +3,7 @@ import { buildRequestHeadersFromMiddlewareResponse, getUnconsumedMiddlewareReque
|
|
|
3
3
|
import { analyzeRegexSafety } from "../utils/regex-safety.js";
|
|
4
4
|
import { normalizeHost, parseCookies, requestContextFromRequest } from "./request-context.js";
|
|
5
5
|
import { isExternalUrl } from "../utils/external-url.js";
|
|
6
|
+
import { substituteDestinationParams, substituteRedirectDestinationQuery } from "./destination-params.js";
|
|
6
7
|
//#region src/config/config-matchers.ts
|
|
7
8
|
/**
|
|
8
9
|
* Cache for compiled regex patterns in matchConfigPattern.
|
|
@@ -44,14 +45,6 @@ const _compiledHeaderSourceCache = /* @__PURE__ */ new Map();
|
|
|
44
45
|
*/
|
|
45
46
|
const _compiledConditionCache = /* @__PURE__ */ new Map();
|
|
46
47
|
/**
|
|
47
|
-
* Cache for destination substitution regexes in substituteDestinationParams.
|
|
48
|
-
*
|
|
49
|
-
* The regex depends only on the set of param keys captured from the matched
|
|
50
|
-
* source pattern. Caching by sorted key list avoids recompiling a new RegExp
|
|
51
|
-
* for repeated redirect/rewrite calls that use the same param shape.
|
|
52
|
-
*/
|
|
53
|
-
const _compiledDestinationParamCache = /* @__PURE__ */ new Map();
|
|
54
|
-
/**
|
|
55
48
|
* Generic helper for the regex compilation caches above.
|
|
56
49
|
*
|
|
57
50
|
* Each cache stores the compiled artifact (or `null` when safeRegExp rejected
|
|
@@ -307,6 +300,7 @@ function applyMiddlewareRequestHeaders(middlewareHeaders, request) {
|
|
|
307
300
|
method: request.method,
|
|
308
301
|
headers: nextHeaders,
|
|
309
302
|
body: request.body,
|
|
303
|
+
signal: request.signal,
|
|
310
304
|
duplex: request.body ? "half" : void 0
|
|
311
305
|
});
|
|
312
306
|
return {
|
|
@@ -560,13 +554,10 @@ function matchRedirect(pathname, redirects, ctx, basePathState = _BASEPATH_DEFAU
|
|
|
560
554
|
onRuleSourceMatch?.(redirect);
|
|
561
555
|
const conditionParams = redirect.has || redirect.missing ? collectConditionParams(redirect.has, redirect.missing, ctx) : _emptyParams();
|
|
562
556
|
if (!conditionParams) continue;
|
|
563
|
-
localeMatch = {
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
}),
|
|
568
|
-
permanent: redirect.permanent
|
|
569
|
-
};
|
|
557
|
+
localeMatch = resolveRedirectMatch(redirect, {
|
|
558
|
+
[entry.paramName]: "",
|
|
559
|
+
...conditionParams
|
|
560
|
+
});
|
|
570
561
|
localeMatchIndex = entry.originalIndex;
|
|
571
562
|
break;
|
|
572
563
|
}
|
|
@@ -583,13 +574,10 @@ function matchRedirect(pathname, redirects, ctx, basePathState = _BASEPATH_DEFAU
|
|
|
583
574
|
onRuleSourceMatch?.(redirect);
|
|
584
575
|
const conditionParams = redirect.has || redirect.missing ? collectConditionParams(redirect.has, redirect.missing, ctx) : _emptyParams();
|
|
585
576
|
if (!conditionParams) continue;
|
|
586
|
-
localeMatch = {
|
|
587
|
-
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
}),
|
|
591
|
-
permanent: redirect.permanent
|
|
592
|
-
};
|
|
577
|
+
localeMatch = resolveRedirectMatch(redirect, {
|
|
578
|
+
[entry.paramName]: localePart,
|
|
579
|
+
...conditionParams
|
|
580
|
+
});
|
|
593
581
|
localeMatchIndex = entry.originalIndex;
|
|
594
582
|
break;
|
|
595
583
|
}
|
|
@@ -603,13 +591,10 @@ function matchRedirect(pathname, redirects, ctx, basePathState = _BASEPATH_DEFAU
|
|
|
603
591
|
onRuleSourceMatch?.(redirect);
|
|
604
592
|
const conditionParams = redirect.has || redirect.missing ? collectConditionParams(redirect.has, redirect.missing, ctx) : _emptyParams();
|
|
605
593
|
if (!conditionParams) continue;
|
|
606
|
-
return {
|
|
607
|
-
|
|
608
|
-
|
|
609
|
-
|
|
610
|
-
}),
|
|
611
|
-
permanent: redirect.permanent
|
|
612
|
-
};
|
|
594
|
+
return resolveRedirectMatch(redirect, {
|
|
595
|
+
...params,
|
|
596
|
+
...conditionParams
|
|
597
|
+
});
|
|
613
598
|
}
|
|
614
599
|
}
|
|
615
600
|
return localeMatch;
|
|
@@ -635,12 +620,24 @@ function matchRewrite(pathname, rewrites, ctx, basePathState = _BASEPATH_DEFAULT
|
|
|
635
620
|
...params,
|
|
636
621
|
...conditionParams
|
|
637
622
|
};
|
|
638
|
-
return substituteAndSanitizeRewriteDestination(rewrite.destination, rewriteParams);
|
|
623
|
+
return substituteAndSanitizeRewriteDestination(serializeRewriteDestinationTemplate(rewrite.destination), rewriteParams);
|
|
639
624
|
}
|
|
640
625
|
}
|
|
641
626
|
return null;
|
|
642
627
|
}
|
|
643
628
|
/**
|
|
629
|
+
* Next.js prepareDestination URL-parses the destination template before it
|
|
630
|
+
* substitutes params, so a literal `é` routes as `%C3%A9` while substituted
|
|
631
|
+
* values stay verbatim: a captured `../admin` is never resolved to `/admin`.
|
|
632
|
+
*
|
|
633
|
+
* https://github.com/vercel/next.js/blob/canary/packages/next/src/shared/lib/router/utils/prepare-destination.ts
|
|
634
|
+
*/
|
|
635
|
+
function serializeRewriteDestinationTemplate(destination) {
|
|
636
|
+
if (!destination.startsWith("/") || destination.startsWith("//")) return destination;
|
|
637
|
+
const parsed = new URL(destination, "http://n");
|
|
638
|
+
return parsed.pathname + parsed.search + parsed.hash;
|
|
639
|
+
}
|
|
640
|
+
/**
|
|
644
641
|
* Check whether a rewrite source can match a pathname without evaluating its
|
|
645
642
|
* request-dependent `has` / `missing` conditions.
|
|
646
643
|
*
|
|
@@ -653,47 +650,20 @@ function matchesRewriteSource(pathname, rewrite, basePathState = _BASEPATH_DEFAU
|
|
|
653
650
|
return shouldEvaluateRule(rewrite.basePath, basePathState) && matchConfigPattern(pathname, rewrite.source) !== null;
|
|
654
651
|
}
|
|
655
652
|
/**
|
|
656
|
-
* Substitute all matched route params into a redirect/rewrite destination.
|
|
657
|
-
*
|
|
658
|
-
* Handles repeated params (e.g. `/api/:id/:id`) and catch-all suffix forms
|
|
659
|
-
* (`:path*`, `:path+`) in a single pass. Unknown params are left intact.
|
|
660
|
-
*/
|
|
661
|
-
function substituteDestinationParams(destination, params) {
|
|
662
|
-
const keys = Object.keys(params);
|
|
663
|
-
if (keys.length === 0) return destination;
|
|
664
|
-
const sortedKeys = [...keys].sort((a, b) => b.length - a.length);
|
|
665
|
-
const cacheKey = sortedKeys.join("\0");
|
|
666
|
-
let paramRe = _compiledDestinationParamCache.get(cacheKey);
|
|
667
|
-
if (!paramRe) {
|
|
668
|
-
const paramAlternation = sortedKeys.map((key) => key.replace(/[.*+?^${}()|[\]\\]/g, "\\$&")).join("|");
|
|
669
|
-
paramRe = new RegExp(`:(${paramAlternation})([+*])?(?![A-Za-z0-9_])`, "g");
|
|
670
|
-
_compiledDestinationParamCache.set(cacheKey, paramRe);
|
|
671
|
-
}
|
|
672
|
-
const replaceParams = (value, encodeParam) => value.replace(paramRe, (_token, key) => encodeParam(params[key]));
|
|
673
|
-
const hashIndex = destination.indexOf("#");
|
|
674
|
-
const beforeHash = hashIndex === -1 ? destination : destination.slice(0, hashIndex);
|
|
675
|
-
const hash = hashIndex === -1 ? "" : destination.slice(hashIndex);
|
|
676
|
-
const queryIndex = beforeHash.indexOf("?");
|
|
677
|
-
if (queryIndex !== -1) {
|
|
678
|
-
const beforeQuery = beforeHash.slice(0, queryIndex);
|
|
679
|
-
const query = beforeHash.slice(queryIndex + 1);
|
|
680
|
-
return `${replaceParams(beforeQuery, (value) => value)}?${replaceParams(query, encodeDestinationQueryParamValue)}${replaceParams(hash, (value) => value)}`;
|
|
681
|
-
}
|
|
682
|
-
return replaceParams(destination, (value) => value);
|
|
683
|
-
}
|
|
684
|
-
function encodeDestinationQueryParamValue(value) {
|
|
685
|
-
const params = new URLSearchParams();
|
|
686
|
-
params.set("", value);
|
|
687
|
-
return params.toString().slice(1);
|
|
688
|
-
}
|
|
689
|
-
/**
|
|
690
653
|
* Substitute params into a redirect/rewrite destination and sanitize the
|
|
691
654
|
* result. Used by every redirect/rewrite branch — the substitution can
|
|
692
655
|
* introduce protocol-relative URLs (e.g. `//evil.com` from a decoded `%2F`
|
|
693
656
|
* in a catch-all param), which sanitizeDestination collapses.
|
|
694
657
|
*/
|
|
695
|
-
function substituteAndSanitizeDestination(destination, params) {
|
|
696
|
-
return sanitizeDestination(substituteDestinationParams(destination, params));
|
|
658
|
+
function substituteAndSanitizeDestination(destination, params, kind) {
|
|
659
|
+
return sanitizeDestination(substituteDestinationParams(destination, params, kind));
|
|
660
|
+
}
|
|
661
|
+
function resolveRedirectMatch(redirect, params) {
|
|
662
|
+
return {
|
|
663
|
+
destination: substituteAndSanitizeDestination(redirect.destination, params, "redirect"),
|
|
664
|
+
permanent: redirect.permanent,
|
|
665
|
+
destinationQuery: substituteRedirectDestinationQuery(redirect.destination, params)
|
|
666
|
+
};
|
|
697
667
|
}
|
|
698
668
|
/**
|
|
699
669
|
* Match Next.js's rewrite-specific prepareDestination behavior: source params
|
|
@@ -703,7 +673,7 @@ function substituteAndSanitizeDestination(destination, params) {
|
|
|
703
673
|
* https://github.com/vercel/next.js/blob/canary/packages/next/src/shared/lib/router/utils/prepare-destination.ts
|
|
704
674
|
*/
|
|
705
675
|
function substituteAndSanitizeRewriteDestination(destination, params) {
|
|
706
|
-
const rewritten = substituteAndSanitizeDestination(destination, params);
|
|
676
|
+
const rewritten = substituteAndSanitizeDestination(destination, params, "rewrite");
|
|
707
677
|
if (!shouldAppendRewriteParamsToQuery(destination, params)) return rewritten;
|
|
708
678
|
const existingQueryKeys = getDestinationQueryKeys(destination);
|
|
709
679
|
const paramsToAppend = [];
|
|
@@ -770,11 +740,12 @@ function appendQueryParams(url, params) {
|
|
|
770
740
|
* users off-site.
|
|
771
741
|
*
|
|
772
742
|
* This function collapses any leading double (or more) slashes to a single
|
|
773
|
-
* slash for non-external (relative) destinations
|
|
743
|
+
* slash for non-external (relative) destinations, including tab/newline and
|
|
744
|
+
* leading C0 control characters that browsers ignore while parsing.
|
|
774
745
|
*/
|
|
775
746
|
function sanitizeDestination(dest) {
|
|
776
747
|
if (dest.startsWith("http://") || dest.startsWith("https://")) return dest;
|
|
777
|
-
dest = dest.replace(/^[\\/]+/, "/");
|
|
748
|
+
dest = dest.replace(/^[\u0000-\u0020]*(?:[\\/][\t\n\r]*)+/, "/");
|
|
778
749
|
return dest;
|
|
779
750
|
}
|
|
780
751
|
/**
|
|
@@ -794,13 +765,20 @@ function sanitizeDestination(dest) {
|
|
|
794
765
|
* still treated as an RSC fetch. Dropping it breaks RSC fetch semantics
|
|
795
766
|
* (issue #1529).
|
|
796
767
|
*
|
|
797
|
-
*
|
|
798
|
-
*
|
|
799
|
-
*
|
|
800
|
-
*
|
|
801
|
-
*
|
|
768
|
+
* Ported from Next.js's `{ ...requestQuery, ...destinationQuery }` merge and
|
|
769
|
+
* `stringifyQuery`: request keys keep their order (integer-like keys first, as
|
|
770
|
+
* in a JS object), a destination key overrides the request value in place,
|
|
771
|
+
* and destination-only keys follow. Request keys and values are re-encoded;
|
|
772
|
+
* destination query text is emitted verbatim, except that, like Next.js,
|
|
773
|
+
* destination strings equal to a request key or value are re-encoded too.
|
|
774
|
+
* Pass the match's
|
|
775
|
+
* `destinationQuery` so substituted params keep their value boundaries;
|
|
776
|
+
* without it the destination's own query is split. External destinations are returned untouched (a config redirect to another
|
|
777
|
+
* origin should not leak the original request's query).
|
|
778
|
+
*
|
|
779
|
+
* https://github.com/vercel/next.js/blob/canary/packages/next/src/server/server-route-utils.ts
|
|
802
780
|
*/
|
|
803
|
-
function preserveRedirectDestinationQuery(destination, requestSearch) {
|
|
781
|
+
function preserveRedirectDestinationQuery(destination, requestSearch, destinationQuery) {
|
|
804
782
|
if (requestSearch === "" || requestSearch === "?" || isExternalUrl(destination)) return destination;
|
|
805
783
|
const requestParams = new URLSearchParams(requestSearch);
|
|
806
784
|
if ([...requestParams.keys()].length === 0) return destination;
|
|
@@ -809,13 +787,29 @@ function preserveRedirectDestinationQuery(destination, requestSearch) {
|
|
|
809
787
|
const beforeHash = hashIndex === -1 ? destination : destination.slice(0, hashIndex);
|
|
810
788
|
const queryIndex = beforeHash.indexOf("?");
|
|
811
789
|
const pathPart = queryIndex === -1 ? beforeHash : beforeHash.slice(0, queryIndex);
|
|
812
|
-
const
|
|
813
|
-
const
|
|
814
|
-
const
|
|
815
|
-
|
|
816
|
-
|
|
790
|
+
const requestStrings = /* @__PURE__ */ new Set();
|
|
791
|
+
const requestParts = Object.create(null);
|
|
792
|
+
for (const [key, value] of requestParams) {
|
|
793
|
+
requestStrings.add(key);
|
|
794
|
+
requestStrings.add(value);
|
|
795
|
+
(requestParts[key] ??= []).push(`${encodeURIComponent(key)}=${encodeURIComponent(value)}`);
|
|
796
|
+
}
|
|
797
|
+
const encode = (text) => requestStrings.has(text) ? encodeURIComponent(text) : text;
|
|
798
|
+
const destinationParts = Object.create(null);
|
|
799
|
+
for (const [key, keyText, valueText] of destinationQuery ?? substituteRedirectDestinationQuery(destination, {})) (destinationParts[key] ??= []).push(valueText === null ? encode(keyText) : `${encode(keyText)}=${encode(valueText)}`);
|
|
800
|
+
const mergedQuery = Object.values({
|
|
801
|
+
...requestParts,
|
|
802
|
+
...destinationParts
|
|
803
|
+
}).flat().join("&");
|
|
817
804
|
return mergedQuery === "" ? `${pathPart}${hash}` : `${pathPart}?${mergedQuery}${hash}`;
|
|
818
805
|
}
|
|
806
|
+
function parseExternalRewriteUrl(externalUrl) {
|
|
807
|
+
try {
|
|
808
|
+
return new URL(externalUrl);
|
|
809
|
+
} catch {
|
|
810
|
+
return null;
|
|
811
|
+
}
|
|
812
|
+
}
|
|
819
813
|
/**
|
|
820
814
|
* Proxy an incoming request to an external URL and return the upstream response.
|
|
821
815
|
*
|
|
@@ -827,7 +821,11 @@ function preserveRedirectDestinationQuery(destination, requestSearch) {
|
|
|
827
821
|
*/
|
|
828
822
|
async function proxyExternalRequest(request, externalUrl) {
|
|
829
823
|
const originalUrl = new URL(request.url);
|
|
830
|
-
const targetUrl =
|
|
824
|
+
const targetUrl = parseExternalRewriteUrl(externalUrl);
|
|
825
|
+
if (!targetUrl) {
|
|
826
|
+
console.error("[vinext] Invalid external rewrite destination:", externalUrl);
|
|
827
|
+
return new Response("Internal Server Error", { status: 500 });
|
|
828
|
+
}
|
|
831
829
|
const destinationKeys = new Set(targetUrl.searchParams.keys());
|
|
832
830
|
for (const [key, value] of originalUrl.searchParams) if (!destinationKeys.has(key)) targetUrl.searchParams.append(key, value);
|
|
833
831
|
const headers = new Headers(request.headers);
|
|
@@ -859,9 +857,10 @@ async function proxyExternalRequest(request, externalUrl) {
|
|
|
859
857
|
try {
|
|
860
858
|
upstreamResponse = await fetch(targetUrl.href, {
|
|
861
859
|
...init,
|
|
862
|
-
signal: controller.signal
|
|
860
|
+
signal: AbortSignal.any([request.signal, controller.signal])
|
|
863
861
|
});
|
|
864
862
|
} catch (e) {
|
|
863
|
+
if (request.signal.aborted && !controller.signal.aborted) return new Response(null, { status: 499 });
|
|
865
864
|
if (e instanceof Error && e.name === "AbortError") {
|
|
866
865
|
console.error("[vinext] External rewrite proxy timeout:", targetUrl.href);
|
|
867
866
|
return new Response("Gateway Timeout", { status: 504 });
|