vinext 0.0.49 → 0.0.50
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/dist/build/client-build-config.js.map +1 -1
- package/dist/build/google-fonts/build-url.js.map +1 -1
- package/dist/build/google-fonts/get-axes.js.map +1 -1
- package/dist/build/google-fonts/sort-variants.js.map +1 -1
- package/dist/build/google-fonts/validate.js.map +1 -1
- package/dist/build/layout-classification.js.map +1 -1
- package/dist/build/nitro-route-rules.js.map +1 -1
- package/dist/build/precompress.js.map +1 -1
- package/dist/build/prerender.d.ts +17 -1
- package/dist/build/prerender.js +77 -16
- package/dist/build/prerender.js.map +1 -1
- package/dist/build/report.js.map +1 -1
- package/dist/build/route-classification-injector.js.map +1 -1
- package/dist/build/route-classification-manifest.js.map +1 -1
- package/dist/build/run-prerender.js.map +1 -1
- package/dist/build/server-manifest.js.map +1 -1
- package/dist/build/ssr-manifest.js.map +1 -1
- package/dist/build/standalone.js.map +1 -1
- package/dist/build/static-export.js.map +1 -1
- package/dist/check.js +1 -1
- package/dist/check.js.map +1 -1
- package/dist/cli-args.js.map +1 -1
- package/dist/cli.js +8 -4
- package/dist/cli.js.map +1 -1
- package/dist/client/instrumentation-client-state.js.map +1 -1
- package/dist/client/validate-module-path.js.map +1 -1
- package/dist/client/vinext-next-data.d.ts +5 -1
- package/dist/client/window-next.d.ts +149 -0
- package/dist/client/window-next.js +48 -0
- package/dist/client/window-next.js.map +1 -0
- package/dist/cloudflare/kv-cache-handler.js.map +1 -1
- package/dist/cloudflare/tpr.js +2 -1
- package/dist/cloudflare/tpr.js.map +1 -1
- package/dist/config/config-matchers.d.ts +3 -1
- package/dist/config/config-matchers.js +5 -4
- package/dist/config/config-matchers.js.map +1 -1
- package/dist/config/dotenv.js.map +1 -1
- package/dist/config/next-config.d.ts +6 -3
- package/dist/config/next-config.js +13 -2
- package/dist/config/next-config.js.map +1 -1
- package/dist/deploy.js +13 -5
- package/dist/deploy.js.map +1 -1
- package/dist/entries/app-browser-entry.d.ts +3 -1
- package/dist/entries/app-browser-entry.js +11 -2
- package/dist/entries/app-browser-entry.js.map +1 -1
- package/dist/entries/app-rsc-entry.js +11 -0
- package/dist/entries/app-rsc-entry.js.map +1 -1
- package/dist/entries/app-rsc-manifest.js +4 -0
- package/dist/entries/app-rsc-manifest.js.map +1 -1
- package/dist/entries/app-ssr-entry.js.map +1 -1
- package/dist/entries/pages-client-entry.js.map +1 -1
- package/dist/entries/pages-entry-helpers.js.map +1 -1
- package/dist/entries/pages-server-entry.js +15 -0
- package/dist/entries/pages-server-entry.js.map +1 -1
- package/dist/entries/runtime-entry-module.js.map +1 -1
- package/dist/index.js +76 -18
- package/dist/index.js.map +1 -1
- package/dist/init.js.map +1 -1
- package/dist/plugins/async-hooks-stub.js.map +1 -1
- package/dist/plugins/client-reference-dedup.js.map +1 -1
- package/dist/plugins/fonts.js.map +1 -1
- package/dist/plugins/instrumentation-client.js.map +1 -1
- package/dist/plugins/og-assets.js.map +1 -1
- package/dist/plugins/optimize-imports.js.map +1 -1
- package/dist/plugins/postcss.js.map +1 -1
- package/dist/plugins/rsc-client-reference-loaders.d.ts +7 -0
- package/dist/plugins/rsc-client-reference-loaders.js +48 -0
- package/dist/plugins/rsc-client-reference-loaders.js.map +1 -0
- package/dist/plugins/rsc-client-shim-excludes.js.map +1 -1
- package/dist/plugins/server-externals-manifest.js.map +1 -1
- package/dist/plugins/strip-server-exports.js.map +1 -1
- package/dist/routing/app-route-graph.d.ts +48 -5
- package/dist/routing/app-route-graph.js +159 -15
- package/dist/routing/app-route-graph.js.map +1 -1
- package/dist/routing/app-router.js.map +1 -1
- package/dist/routing/file-matcher.js.map +1 -1
- package/dist/routing/pages-router.js.map +1 -1
- package/dist/routing/route-matching.js.map +1 -1
- package/dist/routing/route-pattern.js.map +1 -1
- package/dist/routing/route-trie.js.map +1 -1
- package/dist/routing/route-validation.js.map +1 -1
- package/dist/routing/utils.js.map +1 -1
- package/dist/server/api-handler.js.map +1 -1
- package/dist/server/app-browser-action-result.d.ts +19 -0
- package/dist/server/app-browser-action-result.js +18 -0
- package/dist/server/app-browser-action-result.js.map +1 -0
- package/dist/server/app-browser-entry.js +91 -48
- package/dist/server/app-browser-entry.js.map +1 -1
- package/dist/server/app-browser-error.js.map +1 -1
- package/dist/server/app-browser-hydration.d.ts +19 -0
- package/dist/server/app-browser-hydration.js +22 -0
- package/dist/server/app-browser-hydration.js.map +1 -0
- package/dist/server/app-browser-navigation-controller.d.ts +6 -3
- package/dist/server/app-browser-navigation-controller.js +67 -19
- package/dist/server/app-browser-navigation-controller.js.map +1 -1
- package/dist/server/app-browser-state.d.ts +17 -17
- package/dist/server/app-browser-state.js +122 -36
- package/dist/server/app-browser-state.js.map +1 -1
- package/dist/server/app-browser-stream.d.ts +4 -0
- package/dist/server/app-browser-stream.js +24 -2
- package/dist/server/app-browser-stream.js.map +1 -1
- package/dist/server/app-browser-visible-commit.d.ts +6 -1
- package/dist/server/app-browser-visible-commit.js +34 -19
- package/dist/server/app-browser-visible-commit.js.map +1 -1
- package/dist/server/app-client-reference-preloader.js.map +1 -1
- package/dist/server/app-elements-wire.d.ts +6 -1
- package/dist/server/app-elements-wire.js +17 -1
- package/dist/server/app-elements-wire.js.map +1 -1
- package/dist/server/app-elements.d.ts +2 -2
- package/dist/server/app-elements.js +2 -2
- package/dist/server/app-elements.js.map +1 -1
- package/dist/server/app-fallback-renderer.js.map +1 -1
- package/dist/server/app-hook-warning-suppression.js.map +1 -1
- package/dist/server/app-middleware.d.ts +1 -1
- package/dist/server/app-middleware.js +4 -9
- package/dist/server/app-middleware.js.map +1 -1
- package/dist/server/app-mounted-slots-header.js.map +1 -1
- package/dist/server/app-page-boundary-render.d.ts +1 -0
- package/dist/server/app-page-boundary-render.js +14 -13
- package/dist/server/app-page-boundary-render.js.map +1 -1
- package/dist/server/app-page-boundary.d.ts +1 -0
- package/dist/server/app-page-boundary.js +7 -5
- package/dist/server/app-page-boundary.js.map +1 -1
- package/dist/server/app-page-cache.d.ts +10 -3
- package/dist/server/app-page-cache.js +42 -23
- package/dist/server/app-page-cache.js.map +1 -1
- package/dist/server/app-page-dispatch.d.ts +6 -1
- package/dist/server/app-page-dispatch.js +21 -7
- package/dist/server/app-page-dispatch.js.map +1 -1
- package/dist/server/app-page-element-builder.d.ts +3 -1
- package/dist/server/app-page-element-builder.js +6 -2
- package/dist/server/app-page-element-builder.js.map +1 -1
- package/dist/server/app-page-execution.js.map +1 -1
- package/dist/server/app-page-head.js +4 -0
- package/dist/server/app-page-head.js.map +1 -1
- package/dist/server/app-page-method.js.map +1 -1
- package/dist/server/app-page-params.js.map +1 -1
- package/dist/server/app-page-probe.js.map +1 -1
- package/dist/server/app-page-render.d.ts +7 -1
- package/dist/server/app-page-render.js +11 -4
- package/dist/server/app-page-render.js.map +1 -1
- package/dist/server/app-page-request.js +2 -1
- package/dist/server/app-page-request.js.map +1 -1
- package/dist/server/app-page-response.d.ts +2 -0
- package/dist/server/app-page-response.js +15 -5
- package/dist/server/app-page-response.js.map +1 -1
- package/dist/server/app-page-route-wiring.d.ts +6 -2
- package/dist/server/app-page-route-wiring.js +50 -49
- package/dist/server/app-page-route-wiring.js.map +1 -1
- package/dist/server/app-page-segment-state.d.ts +10 -0
- package/dist/server/app-page-segment-state.js +87 -0
- package/dist/server/app-page-segment-state.js.map +1 -0
- package/dist/server/app-page-stream.d.ts +7 -2
- package/dist/server/app-page-stream.js +3 -1
- package/dist/server/app-page-stream.js.map +1 -1
- package/dist/server/app-post-middleware-context.js.map +1 -1
- package/dist/server/app-prerender-endpoints.js.map +1 -1
- package/dist/server/app-prerender-static-params.js.map +1 -1
- package/dist/server/app-render-dependency.js.map +1 -1
- package/dist/server/app-request-context.js.map +1 -1
- package/dist/server/app-route-handler-cache.js.map +1 -1
- package/dist/server/app-route-handler-dispatch.js +3 -1
- package/dist/server/app-route-handler-dispatch.js.map +1 -1
- package/dist/server/app-route-handler-execution.js.map +1 -1
- package/dist/server/app-route-handler-policy.js +1 -0
- package/dist/server/app-route-handler-policy.js.map +1 -1
- package/dist/server/app-route-handler-response.js +4 -3
- package/dist/server/app-route-handler-response.js.map +1 -1
- package/dist/server/app-route-handler-runtime.js.map +1 -1
- package/dist/server/app-router-entry.js +6 -2
- package/dist/server/app-router-entry.js.map +1 -1
- package/dist/server/app-rsc-cache-busting.d.ts +5 -2
- package/dist/server/app-rsc-cache-busting.js +40 -19
- package/dist/server/app-rsc-cache-busting.js.map +1 -1
- package/dist/server/app-rsc-error-handler.js.map +1 -1
- package/dist/server/app-rsc-errors.js.map +1 -1
- package/dist/server/app-rsc-handler.d.ts +10 -1
- package/dist/server/app-rsc-handler.js +51 -17
- package/dist/server/app-rsc-handler.js.map +1 -1
- package/dist/server/app-rsc-render-mode.d.ts +11 -0
- package/dist/server/app-rsc-render-mode.js +21 -0
- package/dist/server/app-rsc-render-mode.js.map +1 -0
- package/dist/server/app-rsc-request-normalization.d.ts +4 -1
- package/dist/server/app-rsc-request-normalization.js +7 -2
- package/dist/server/app-rsc-request-normalization.js.map +1 -1
- package/dist/server/app-rsc-response-finalizer.d.ts +2 -1
- package/dist/server/app-rsc-response-finalizer.js +6 -1
- package/dist/server/app-rsc-response-finalizer.js.map +1 -1
- package/dist/server/app-rsc-route-matching.js.map +1 -1
- package/dist/server/app-segment-config.js.map +1 -1
- package/dist/server/app-server-action-execution.d.ts +16 -2
- package/dist/server/app-server-action-execution.js +79 -23
- package/dist/server/app-server-action-execution.js.map +1 -1
- package/dist/server/app-ssr-entry.d.ts +6 -0
- package/dist/server/app-ssr-entry.js +10 -4
- package/dist/server/app-ssr-entry.js.map +1 -1
- package/dist/server/app-ssr-stream.js.map +1 -1
- package/dist/server/app-static-generation.js.map +1 -1
- package/dist/server/artifact-compatibility.js.map +1 -1
- package/dist/server/cache-control.js +1 -0
- package/dist/server/cache-control.js.map +1 -1
- package/dist/server/cache-proof.js.map +1 -1
- package/dist/server/csp.js.map +1 -1
- package/dist/server/dev-error-overlay-store.js.map +1 -1
- package/dist/server/dev-error-overlay.js +5 -0
- package/dist/server/dev-error-overlay.js.map +1 -1
- package/dist/server/dev-module-runner.js.map +1 -1
- package/dist/server/dev-origin-check.js.map +1 -1
- package/dist/server/dev-route-files.js.map +1 -1
- package/dist/server/dev-server.js +8 -5
- package/dist/server/dev-server.js.map +1 -1
- package/dist/server/file-based-metadata.js.map +1 -1
- package/dist/server/headers.d.ts +79 -0
- package/dist/server/headers.js +101 -0
- package/dist/server/headers.js.map +1 -0
- package/dist/server/html.js.map +1 -1
- package/dist/server/http-error-responses.js.map +1 -1
- package/dist/server/image-optimization.d.ts +11 -1
- package/dist/server/image-optimization.js.map +1 -1
- package/dist/server/implicit-tags.js +2 -1
- package/dist/server/implicit-tags.js.map +1 -1
- package/dist/server/instrumentation-runtime.js.map +1 -1
- package/dist/server/instrumentation.js.map +1 -1
- package/dist/server/isr-cache.d.ts +10 -1
- package/dist/server/isr-cache.js +12 -3
- package/dist/server/isr-cache.js.map +1 -1
- package/dist/server/metadata-route-build-data.js.map +1 -1
- package/dist/server/metadata-route-response.js.map +1 -1
- package/dist/server/metadata-routes.js.map +1 -1
- package/dist/server/middleware-matcher.js.map +1 -1
- package/dist/server/middleware-request-headers.d.ts +4 -1
- package/dist/server/middleware-request-headers.js +15 -8
- package/dist/server/middleware-request-headers.js.map +1 -1
- package/dist/server/middleware-response-headers.d.ts +2 -1
- package/dist/server/middleware-response-headers.js +1 -1
- package/dist/server/middleware-response-headers.js.map +1 -1
- package/dist/server/middleware-runtime.d.ts +1 -0
- package/dist/server/middleware-runtime.js +6 -3
- package/dist/server/middleware-runtime.js.map +1 -1
- package/dist/server/middleware.js.map +1 -1
- package/dist/server/navigation-planner.d.ts +119 -0
- package/dist/server/navigation-planner.js +171 -0
- package/dist/server/navigation-planner.js.map +1 -0
- package/dist/server/navigation-trace.d.ts +12 -2
- package/dist/server/navigation-trace.js +13 -1
- package/dist/server/navigation-trace.js.map +1 -1
- package/dist/server/next-error-digest.d.ts +3 -2
- package/dist/server/next-error-digest.js +4 -2
- package/dist/server/next-error-digest.js.map +1 -1
- package/dist/server/normalize-path.js.map +1 -1
- package/dist/server/pages-api-route.js.map +1 -1
- package/dist/server/pages-i18n.js.map +1 -1
- package/dist/server/pages-media-type.js.map +1 -1
- package/dist/server/pages-node-compat.js.map +1 -1
- package/dist/server/pages-page-data.js +5 -2
- package/dist/server/pages-page-data.js.map +1 -1
- package/dist/server/pages-page-response.js +3 -2
- package/dist/server/pages-page-response.js.map +1 -1
- package/dist/server/prerender-work-unit-setup.js +1 -1
- package/dist/server/prerender-work-unit-setup.js.map +1 -1
- package/dist/server/prod-server.js +35 -13
- package/dist/server/prod-server.js.map +1 -1
- package/dist/server/request-log.js.map +1 -1
- package/dist/server/request-pipeline.d.ts +1 -13
- package/dist/server/request-pipeline.js +3 -25
- package/dist/server/request-pipeline.js.map +1 -1
- package/dist/server/rsc-stream-hints.js.map +1 -1
- package/dist/server/seed-cache.js.map +1 -1
- package/dist/server/server-action-not-found.js +3 -3
- package/dist/server/server-action-not-found.js.map +1 -1
- package/dist/server/socket-error-backstop.js.map +1 -1
- package/dist/server/static-file-cache.js.map +1 -1
- package/dist/server/worker-utils.d.ts +0 -7
- package/dist/server/worker-utils.js +3 -2
- package/dist/server/worker-utils.js.map +1 -1
- package/dist/shims/amp.js.map +1 -1
- package/dist/shims/app.d.ts +37 -4
- package/dist/shims/app.js +50 -1
- package/dist/shims/app.js.map +1 -0
- package/dist/shims/cache-for-request.js.map +1 -1
- package/dist/shims/cache-runtime.js +20 -8
- package/dist/shims/cache-runtime.js.map +1 -1
- package/dist/shims/cache.d.ts +15 -3
- package/dist/shims/cache.js +99 -15
- package/dist/shims/cache.js.map +1 -1
- package/dist/shims/client-hook-error.js.map +1 -1
- package/dist/shims/compat-router.js.map +1 -1
- package/dist/shims/config.js.map +1 -1
- package/dist/shims/constants.js.map +1 -1
- package/dist/shims/document.js.map +1 -1
- package/dist/shims/dynamic.d.ts +18 -10
- package/dist/shims/dynamic.js +107 -51
- package/dist/shims/dynamic.js.map +1 -1
- package/dist/shims/error-boundary.d.ts +35 -6
- package/dist/shims/error-boundary.js +118 -33
- package/dist/shims/error-boundary.js.map +1 -1
- package/dist/shims/error.js.map +1 -1
- package/dist/shims/fetch-cache.d.ts +22 -1
- package/dist/shims/fetch-cache.js +124 -13
- package/dist/shims/fetch-cache.js.map +1 -1
- package/dist/shims/font-google-base.js.map +1 -1
- package/dist/shims/font-local.js.map +1 -1
- package/dist/shims/form.js +3 -1
- package/dist/shims/form.js.map +1 -1
- package/dist/shims/head-state.js.map +1 -1
- package/dist/shims/head.d.ts +3 -1
- package/dist/shims/head.js +28 -16
- package/dist/shims/head.js.map +1 -1
- package/dist/shims/headers.d.ts +4 -2
- package/dist/shims/headers.js +24 -7
- package/dist/shims/headers.js.map +1 -1
- package/dist/shims/i18n-context.js.map +1 -1
- package/dist/shims/i18n-state.js.map +1 -1
- package/dist/shims/image-config.d.ts +14 -1
- package/dist/shims/image-config.js +24 -1
- package/dist/shims/image-config.js.map +1 -1
- package/dist/shims/image.js +15 -2
- package/dist/shims/image.js.map +1 -1
- package/dist/shims/internal/als-registry.js.map +1 -1
- package/dist/shims/internal/app-router-context.d.ts +1 -0
- package/dist/shims/internal/app-router-context.js.map +1 -1
- package/dist/shims/internal/cookie-serialize.js.map +1 -1
- package/dist/shims/internal/make-hanging-promise.d.ts +1 -1
- package/dist/shims/internal/make-hanging-promise.js +1 -1
- package/dist/shims/internal/make-hanging-promise.js.map +1 -1
- package/dist/shims/internal/parse-cookie-header.js.map +1 -1
- package/dist/shims/internal/utils.js.map +1 -1
- package/dist/shims/internal/work-unit-async-storage.js +2 -2
- package/dist/shims/internal/work-unit-async-storage.js.map +1 -1
- package/dist/shims/layout-segment-context.js.map +1 -1
- package/dist/shims/legacy-image.js.map +1 -1
- package/dist/shims/link-prefetch.d.ts +34 -0
- package/dist/shims/link-prefetch.js +40 -0
- package/dist/shims/link-prefetch.js.map +1 -0
- package/dist/shims/link.d.ts +27 -4
- package/dist/shims/link.js +91 -27
- package/dist/shims/link.js.map +1 -1
- package/dist/shims/metadata.js.map +1 -1
- package/dist/shims/navigation-state.js.map +1 -1
- package/dist/shims/navigation.d.ts +22 -1
- package/dist/shims/navigation.js +30 -15
- package/dist/shims/navigation.js.map +1 -1
- package/dist/shims/navigation.react-server.js.map +1 -1
- package/dist/shims/offline.js.map +1 -1
- package/dist/shims/readonly-url-search-params.js.map +1 -1
- package/dist/shims/request-context.js.map +1 -1
- package/dist/shims/root-params.js.map +1 -1
- package/dist/shims/router-state.js.map +1 -1
- package/dist/shims/router.d.ts +38 -2
- package/dist/shims/router.js +45 -17
- package/dist/shims/router.js.map +1 -1
- package/dist/shims/script-nonce-context.js.map +1 -1
- package/dist/shims/script.js.map +1 -1
- package/dist/shims/server.js +10 -14
- package/dist/shims/server.js.map +1 -1
- package/dist/shims/slot.d.ts +6 -1
- package/dist/shims/slot.js +20 -7
- package/dist/shims/slot.js.map +1 -1
- package/dist/shims/thenable-params.js.map +1 -1
- package/dist/shims/unified-request-context.js +3 -0
- package/dist/shims/unified-request-context.js.map +1 -1
- package/dist/shims/url-safety.js.map +1 -1
- package/dist/shims/url-utils.d.ts +2 -1
- package/dist/shims/url-utils.js +10 -1
- package/dist/shims/url-utils.js.map +1 -1
- package/dist/shims/use-merged-ref.js.map +1 -1
- package/dist/shims/web-vitals.d.ts +4 -21
- package/dist/shims/web-vitals.js +19 -6
- package/dist/shims/web-vitals.js.map +1 -1
- package/dist/utils/base-path.js.map +1 -1
- package/dist/utils/cache-control-metadata.js.map +1 -1
- package/dist/utils/domain-locale.js.map +1 -1
- package/dist/utils/encode-cache-tag.d.ts +31 -0
- package/dist/utils/encode-cache-tag.js +38 -0
- package/dist/utils/encode-cache-tag.js.map +1 -0
- package/dist/utils/error-cause.js.map +1 -1
- package/dist/utils/hash.js.map +1 -1
- package/dist/utils/lazy-chunks.js.map +1 -1
- package/dist/utils/manifest-paths.js.map +1 -1
- package/dist/utils/mdx-scan.js.map +1 -1
- package/dist/utils/navigation-signal.d.ts +6 -0
- package/dist/utils/navigation-signal.js +14 -0
- package/dist/utils/navigation-signal.js.map +1 -0
- package/dist/utils/project.js.map +1 -1
- package/dist/utils/public-routes.js.map +1 -1
- package/dist/utils/query.js.map +1 -1
- package/dist/utils/safe-json-file.js.map +1 -1
- package/dist/utils/text-stream.js.map +1 -1
- package/dist/utils/vinext-root.js.map +1 -1
- package/package.json +6 -4
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"middleware.js","names":[],"sources":["../../src/server/middleware.ts"],"sourcesContent":["/**\n * proxy.ts / middleware.ts runner\n *\n * Loads and executes the user's proxy.ts (Next.js 16) or middleware.ts file\n * before routing. Runs in Node (not Edge Runtime), per the vinext design.\n *\n * In Next.js 16, proxy.ts replaces middleware.ts:\n * - proxy.ts: default export OR named `proxy` function, runs on Node.js runtime\n * - middleware.ts: deprecated but still supported for Edge runtime use cases\n *\n * The proxy/middleware receives a NextRequest and can:\n * - Return NextResponse.next() to continue to the route\n * - Return NextResponse.redirect() to redirect\n * - Return NextResponse.rewrite() to rewrite the URL\n * - Set/modify headers and cookies\n * - Return a Response directly (e.g., for auth guards)\n *\n * Supports the `config.matcher` export for path filtering.\n */\n\nimport type { ModuleRunner } from \"vite/module-runner\";\nimport fs from \"node:fs\";\nimport path from \"node:path\";\nimport type { NextI18nConfig } from \"../config/next-config.js\";\nimport { ValidFileMatcher } from \"../routing/file-matcher.js\";\nimport {\n resolveMiddlewareModuleHandler,\n runGeneratedMiddleware,\n type MiddlewareModule,\n type MiddlewareResult,\n} from \"./middleware-runtime.js\";\n\nexport { matchPattern, matchesMiddleware } from \"./middleware-matcher.js\";\n\n/**\n * Determine whether a middleware/proxy file path refers to a proxy file.\n * proxy.ts files accept `proxy` or `default` exports.\n * middleware.ts files accept `middleware` or `default` exports.\n *\n * Matches Next.js behavior where each file type only accepts its own\n * named export or a default export:\n * https://github.com/vercel/next.js/blob/canary/packages/next/src/build/templates/middleware.ts\n */\nexport function isProxyFile(filePath: string): boolean {\n const base = path.basename(filePath).replace(/\\.\\w+$/, \"\");\n return base === \"proxy\";\n}\n\n/**\n * Resolve the middleware/proxy handler function from a module's exports.\n * Matches Next.js behavior: for proxy files, check `proxy` then `default`;\n * for middleware files, check `middleware` then `default`.\n *\n * Throws if the file exists but doesn't export a valid function, matching\n * Next.js's ProxyMissingExportError behavior.\n *\n * @see https://github.com/vercel/next.js/blob/canary/packages/next/src/build/templates/middleware.ts\n * @see https://github.com/vercel/next.js/blob/canary/test/e2e/app-dir/proxy-missing-export/proxy-missing-export.test.ts\n */\nexport function resolveMiddlewareHandler(mod: MiddlewareModule, filePath: string) {\n return resolveMiddlewareModuleHandler(mod, {\n filePath,\n isProxy: isProxyFile(filePath),\n });\n}\n\nconst MIDDLEWARE_LOCATIONS = [\"\", \"src/\"];\n\n/**\n * Find the proxy or middleware file in the project root.\n * Checks for proxy.ts (Next.js 16) first, then falls back to middleware.ts.\n * If middleware.ts is found, logs a deprecation warning.\n */\nexport function findMiddlewareFile(root: string, fileMatcher: ValidFileMatcher): string | null {\n // Check proxy.ts first (Next.js 16 replacement for middleware.ts)\n for (const dir of MIDDLEWARE_LOCATIONS) {\n for (const ext of fileMatcher.dottedExtensions) {\n const fullPath = path.join(root, dir, `proxy${ext}`);\n if (fs.existsSync(fullPath)) {\n return fullPath;\n }\n }\n }\n\n // Fall back to middleware.ts (deprecated in Next.js 16)\n for (const dir of MIDDLEWARE_LOCATIONS) {\n for (const ext of fileMatcher.dottedExtensions) {\n const fullPath = path.join(root, dir, `middleware${ext}`);\n if (fs.existsSync(fullPath)) {\n console.warn(\n \"[vinext] middleware.ts is deprecated in Next.js 16. \" +\n \"Rename to proxy.ts and export a default or named proxy function.\",\n );\n return fullPath;\n }\n }\n }\n return null;\n}\n\nfunction isMiddlewareModule(value: unknown): value is MiddlewareModule {\n return !!value && typeof value === \"object\";\n}\n\n/**\n * Load and execute middleware for a given request.\n *\n * @param runner - A ModuleRunner used to load the middleware module.\n * Must be a long-lived instance created once (e.g. in configureServer) via\n * createDirectRunner() — NOT recreated per request. Using server.ssrLoadModule\n * directly crashes with `outsideEmitter` when @cloudflare/vite-plugin is\n * present because SSRCompatModuleRunner reads environment.hot.api synchronously.\n * @param middlewarePath - Absolute path to the middleware file\n * @param request - The incoming Request object\n * @returns Middleware result describing what action to take\n */\nexport async function runMiddleware(\n runner: ModuleRunner,\n middlewarePath: string,\n request: Request,\n i18nConfig?: NextI18nConfig | null,\n basePath?: string,\n): Promise<MiddlewareResult> {\n // Load the middleware module via the direct-call ModuleRunner.\n // This bypasses the hot channel entirely and is safe with all Vite plugin\n // combinations, including @cloudflare/vite-plugin.\n const mod = await runner.import(middlewarePath);\n if (!isMiddlewareModule(mod)) {\n throw new Error(`Middleware module \"${middlewarePath}\" did not evaluate to an object.`);\n }\n\n return runGeneratedMiddleware({\n basePath,\n filePath: middlewarePath,\n i18nConfig,\n includeErrorDetails: process.env.NODE_ENV !== \"production\",\n isProxy: isProxyFile(middlewarePath),\n module: mod,\n request,\n });\n}\n"],"mappings":";;;;;;;;;;;;;;AA2CA,SAAgB,YAAY,UAA2B;
|
|
1
|
+
{"version":3,"file":"middleware.js","names":[],"sources":["../../src/server/middleware.ts"],"sourcesContent":["/**\n * proxy.ts / middleware.ts runner\n *\n * Loads and executes the user's proxy.ts (Next.js 16) or middleware.ts file\n * before routing. Runs in Node (not Edge Runtime), per the vinext design.\n *\n * In Next.js 16, proxy.ts replaces middleware.ts:\n * - proxy.ts: default export OR named `proxy` function, runs on Node.js runtime\n * - middleware.ts: deprecated but still supported for Edge runtime use cases\n *\n * The proxy/middleware receives a NextRequest and can:\n * - Return NextResponse.next() to continue to the route\n * - Return NextResponse.redirect() to redirect\n * - Return NextResponse.rewrite() to rewrite the URL\n * - Set/modify headers and cookies\n * - Return a Response directly (e.g., for auth guards)\n *\n * Supports the `config.matcher` export for path filtering.\n */\n\nimport type { ModuleRunner } from \"vite/module-runner\";\nimport fs from \"node:fs\";\nimport path from \"node:path\";\nimport type { NextI18nConfig } from \"../config/next-config.js\";\nimport { ValidFileMatcher } from \"../routing/file-matcher.js\";\nimport {\n resolveMiddlewareModuleHandler,\n runGeneratedMiddleware,\n type MiddlewareModule,\n type MiddlewareResult,\n} from \"./middleware-runtime.js\";\n\nexport { matchPattern, matchesMiddleware } from \"./middleware-matcher.js\";\n\n/**\n * Determine whether a middleware/proxy file path refers to a proxy file.\n * proxy.ts files accept `proxy` or `default` exports.\n * middleware.ts files accept `middleware` or `default` exports.\n *\n * Matches Next.js behavior where each file type only accepts its own\n * named export or a default export:\n * https://github.com/vercel/next.js/blob/canary/packages/next/src/build/templates/middleware.ts\n */\nexport function isProxyFile(filePath: string): boolean {\n const base = path.basename(filePath).replace(/\\.\\w+$/, \"\");\n return base === \"proxy\";\n}\n\n/**\n * Resolve the middleware/proxy handler function from a module's exports.\n * Matches Next.js behavior: for proxy files, check `proxy` then `default`;\n * for middleware files, check `middleware` then `default`.\n *\n * Throws if the file exists but doesn't export a valid function, matching\n * Next.js's ProxyMissingExportError behavior.\n *\n * @see https://github.com/vercel/next.js/blob/canary/packages/next/src/build/templates/middleware.ts\n * @see https://github.com/vercel/next.js/blob/canary/test/e2e/app-dir/proxy-missing-export/proxy-missing-export.test.ts\n */\nexport function resolveMiddlewareHandler(mod: MiddlewareModule, filePath: string) {\n return resolveMiddlewareModuleHandler(mod, {\n filePath,\n isProxy: isProxyFile(filePath),\n });\n}\n\nconst MIDDLEWARE_LOCATIONS = [\"\", \"src/\"];\n\n/**\n * Find the proxy or middleware file in the project root.\n * Checks for proxy.ts (Next.js 16) first, then falls back to middleware.ts.\n * If middleware.ts is found, logs a deprecation warning.\n */\nexport function findMiddlewareFile(root: string, fileMatcher: ValidFileMatcher): string | null {\n // Check proxy.ts first (Next.js 16 replacement for middleware.ts)\n for (const dir of MIDDLEWARE_LOCATIONS) {\n for (const ext of fileMatcher.dottedExtensions) {\n const fullPath = path.join(root, dir, `proxy${ext}`);\n if (fs.existsSync(fullPath)) {\n return fullPath;\n }\n }\n }\n\n // Fall back to middleware.ts (deprecated in Next.js 16)\n for (const dir of MIDDLEWARE_LOCATIONS) {\n for (const ext of fileMatcher.dottedExtensions) {\n const fullPath = path.join(root, dir, `middleware${ext}`);\n if (fs.existsSync(fullPath)) {\n console.warn(\n \"[vinext] middleware.ts is deprecated in Next.js 16. \" +\n \"Rename to proxy.ts and export a default or named proxy function.\",\n );\n return fullPath;\n }\n }\n }\n return null;\n}\n\nfunction isMiddlewareModule(value: unknown): value is MiddlewareModule {\n return !!value && typeof value === \"object\";\n}\n\n/**\n * Load and execute middleware for a given request.\n *\n * @param runner - A ModuleRunner used to load the middleware module.\n * Must be a long-lived instance created once (e.g. in configureServer) via\n * createDirectRunner() — NOT recreated per request. Using server.ssrLoadModule\n * directly crashes with `outsideEmitter` when @cloudflare/vite-plugin is\n * present because SSRCompatModuleRunner reads environment.hot.api synchronously.\n * @param middlewarePath - Absolute path to the middleware file\n * @param request - The incoming Request object\n * @returns Middleware result describing what action to take\n */\nexport async function runMiddleware(\n runner: ModuleRunner,\n middlewarePath: string,\n request: Request,\n i18nConfig?: NextI18nConfig | null,\n basePath?: string,\n): Promise<MiddlewareResult> {\n // Load the middleware module via the direct-call ModuleRunner.\n // This bypasses the hot channel entirely and is safe with all Vite plugin\n // combinations, including @cloudflare/vite-plugin.\n const mod = await runner.import(middlewarePath);\n if (!isMiddlewareModule(mod)) {\n throw new Error(`Middleware module \"${middlewarePath}\" did not evaluate to an object.`);\n }\n\n return runGeneratedMiddleware({\n basePath,\n filePath: middlewarePath,\n i18nConfig,\n includeErrorDetails: process.env.NODE_ENV !== \"production\",\n isProxy: isProxyFile(middlewarePath),\n module: mod,\n request,\n });\n}\n"],"mappings":";;;;;;;;;;;;;;AA2CA,SAAgB,YAAY,UAA2B;CAErD,OADa,KAAK,SAAS,SAAS,CAAC,QAAQ,UAAU,GAC5C,KAAK;;;;;;;;;;;;;AAclB,SAAgB,yBAAyB,KAAuB,UAAkB;CAChF,OAAO,+BAA+B,KAAK;EACzC;EACA,SAAS,YAAY,SAAS;EAC/B,CAAC;;AAGJ,MAAM,uBAAuB,CAAC,IAAI,OAAO;;;;;;AAOzC,SAAgB,mBAAmB,MAAc,aAA8C;CAE7F,KAAK,MAAM,OAAO,sBAChB,KAAK,MAAM,OAAO,YAAY,kBAAkB;EAC9C,MAAM,WAAW,KAAK,KAAK,MAAM,KAAK,QAAQ,MAAM;EACpD,IAAI,GAAG,WAAW,SAAS,EACzB,OAAO;;CAMb,KAAK,MAAM,OAAO,sBAChB,KAAK,MAAM,OAAO,YAAY,kBAAkB;EAC9C,MAAM,WAAW,KAAK,KAAK,MAAM,KAAK,aAAa,MAAM;EACzD,IAAI,GAAG,WAAW,SAAS,EAAE;GAC3B,QAAQ,KACN,uHAED;GACD,OAAO;;;CAIb,OAAO;;AAGT,SAAS,mBAAmB,OAA2C;CACrE,OAAO,CAAC,CAAC,SAAS,OAAO,UAAU;;;;;;;;;;;;;;AAerC,eAAsB,cACpB,QACA,gBACA,SACA,YACA,UAC2B;CAI3B,MAAM,MAAM,MAAM,OAAO,OAAO,eAAe;CAC/C,IAAI,CAAC,mBAAmB,IAAI,EAC1B,MAAM,IAAI,MAAM,sBAAsB,eAAe,kCAAkC;CAGzF,OAAO,uBAAuB;EAC5B;EACA,UAAU;EACV;EACA,qBAAqB,QAAQ,IAAI,aAAa;EAC9C,SAAS,YAAY,eAAe;EACpC,QAAQ;EACR;EACD,CAAC"}
|
|
@@ -0,0 +1,119 @@
|
|
|
1
|
+
import { RouteManifest } from "../routing/app-route-graph.js";
|
|
2
|
+
import { NavigationTrace, NavigationTraceFields } from "./navigation-trace.js";
|
|
3
|
+
|
|
4
|
+
//#region src/server/navigation-planner.d.ts
|
|
5
|
+
type OperationLane = "hmr" | "navigation" | "prefetch" | "refresh" | "server-action" | "traverse";
|
|
6
|
+
type OperationToken = {
|
|
7
|
+
operationId: number;
|
|
8
|
+
lane: OperationLane;
|
|
9
|
+
baseVisibleCommitVersion: number;
|
|
10
|
+
graphVersion: string | null;
|
|
11
|
+
deploymentVersion: string | null;
|
|
12
|
+
targetSnapshotFingerprint: string;
|
|
13
|
+
cacheVariantFingerprint?: string;
|
|
14
|
+
};
|
|
15
|
+
type RouteSnapshotV0 = {
|
|
16
|
+
routeId: string;
|
|
17
|
+
layoutIds: readonly string[];
|
|
18
|
+
mountedParallelSlots: readonly MountedParallelSlotSnapshotV0[];
|
|
19
|
+
rootBoundaryId: string | null;
|
|
20
|
+
displayUrl: string;
|
|
21
|
+
matchedUrl: string;
|
|
22
|
+
};
|
|
23
|
+
type MountedParallelSlotSnapshotV0 = {
|
|
24
|
+
slotId: string;
|
|
25
|
+
ownerLayoutId: string | null;
|
|
26
|
+
};
|
|
27
|
+
type NavigationPlannerStateV0 = {
|
|
28
|
+
nextOperationToken: OperationToken;
|
|
29
|
+
traceFields?: NavigationTraceFields;
|
|
30
|
+
visibleCommitVersion: number;
|
|
31
|
+
visibleSnapshot: RouteSnapshotV0;
|
|
32
|
+
};
|
|
33
|
+
type RefreshScope = "visible";
|
|
34
|
+
type NavigationEvent = {
|
|
35
|
+
kind: "navigate";
|
|
36
|
+
href: string;
|
|
37
|
+
mode: "push" | "replace";
|
|
38
|
+
} | {
|
|
39
|
+
kind: "refresh";
|
|
40
|
+
scope: RefreshScope;
|
|
41
|
+
} | {
|
|
42
|
+
kind: "traverse";
|
|
43
|
+
direction: "back" | "forward";
|
|
44
|
+
historyState: unknown;
|
|
45
|
+
} | {
|
|
46
|
+
kind: "prefetch";
|
|
47
|
+
href: string;
|
|
48
|
+
} | {
|
|
49
|
+
kind: "flightResponseArrived";
|
|
50
|
+
token: OperationToken;
|
|
51
|
+
result: FlightResultV0;
|
|
52
|
+
};
|
|
53
|
+
type RequestedWork = {
|
|
54
|
+
kind: "flight";
|
|
55
|
+
href: string;
|
|
56
|
+
mode: "push" | "replace" | "refresh";
|
|
57
|
+
} | {
|
|
58
|
+
direction: "back" | "forward";
|
|
59
|
+
historyState: unknown;
|
|
60
|
+
kind: "traverseFlight";
|
|
61
|
+
} | {
|
|
62
|
+
kind: "prefetch";
|
|
63
|
+
href: string;
|
|
64
|
+
};
|
|
65
|
+
type CommitProposal = {
|
|
66
|
+
preserveAbsentSlots: boolean;
|
|
67
|
+
preserveElementIds: readonly string[];
|
|
68
|
+
reason: "currentRootBoundary" | "rootBoundaryUnknownFallback";
|
|
69
|
+
targetSnapshot: RouteSnapshotV0;
|
|
70
|
+
};
|
|
71
|
+
type NoCommitReason = "prefetchOnly";
|
|
72
|
+
type HardNavigationReason = "rootBoundaryChanged";
|
|
73
|
+
type RootBoundaryTransition = "currentRootBoundary" | "rootBoundaryChanged" | "rootBoundaryUnknownFallback";
|
|
74
|
+
type NavigationDecisionV0 = {
|
|
75
|
+
kind: "requestWork";
|
|
76
|
+
token: OperationToken;
|
|
77
|
+
work: RequestedWork;
|
|
78
|
+
trace: NavigationTrace;
|
|
79
|
+
} | {
|
|
80
|
+
kind: "proposeCommit";
|
|
81
|
+
token: OperationToken;
|
|
82
|
+
proposal: CommitProposal;
|
|
83
|
+
trace: NavigationTrace;
|
|
84
|
+
} | {
|
|
85
|
+
kind: "noCommit";
|
|
86
|
+
token: OperationToken;
|
|
87
|
+
reason: NoCommitReason;
|
|
88
|
+
trace: NavigationTrace;
|
|
89
|
+
} | {
|
|
90
|
+
kind: "hardNavigate";
|
|
91
|
+
token: OperationToken;
|
|
92
|
+
url: string;
|
|
93
|
+
reason: HardNavigationReason;
|
|
94
|
+
trace: NavigationTrace;
|
|
95
|
+
};
|
|
96
|
+
type FlightResultV0 = {
|
|
97
|
+
href: string;
|
|
98
|
+
targetSnapshot: RouteSnapshotV0;
|
|
99
|
+
};
|
|
100
|
+
type NavigationPlannerInput = {
|
|
101
|
+
routeManifest: RouteManifest | null;
|
|
102
|
+
state: NavigationPlannerStateV0;
|
|
103
|
+
event: NavigationEvent;
|
|
104
|
+
};
|
|
105
|
+
declare function classifyRootBoundaryTransition(currentRootBoundaryId: string | null, nextRootBoundaryId: string | null): RootBoundaryTransition;
|
|
106
|
+
declare function resolveSameLayoutAncestorPersistence(currentSnapshot: RouteSnapshotV0, targetSnapshot: RouteSnapshotV0): readonly string[];
|
|
107
|
+
declare function resolveMountedParallelSlotPersistence(currentSnapshot: RouteSnapshotV0, targetSnapshot: RouteSnapshotV0): readonly string[];
|
|
108
|
+
declare function resolveCurrentRootBoundaryElementPersistence(currentSnapshot: RouteSnapshotV0, targetSnapshot: RouteSnapshotV0): readonly string[];
|
|
109
|
+
declare function planNavigation(input: NavigationPlannerInput): NavigationDecisionV0;
|
|
110
|
+
declare const navigationPlanner: {
|
|
111
|
+
classifyRootBoundaryTransition: typeof classifyRootBoundaryTransition;
|
|
112
|
+
plan: typeof planNavigation;
|
|
113
|
+
resolveCurrentRootBoundaryElementPersistence: typeof resolveCurrentRootBoundaryElementPersistence;
|
|
114
|
+
resolveMountedParallelSlotPersistence: typeof resolveMountedParallelSlotPersistence;
|
|
115
|
+
resolveSameLayoutAncestorPersistence: typeof resolveSameLayoutAncestorPersistence;
|
|
116
|
+
};
|
|
117
|
+
//#endregion
|
|
118
|
+
export { CommitProposal, FlightResultV0, HardNavigationReason, MountedParallelSlotSnapshotV0, NavigationDecisionV0, NavigationEvent, NavigationPlannerInput, NavigationPlannerStateV0, NoCommitReason, OperationLane, OperationToken, RefreshScope, RequestedWork, RootBoundaryTransition, RouteSnapshotV0, navigationPlanner };
|
|
119
|
+
//# sourceMappingURL=navigation-planner.d.ts.map
|
|
@@ -0,0 +1,171 @@
|
|
|
1
|
+
import { NavigationTraceReasonCodes, createNavigationLifecycleTraceFields, createNavigationTrace } from "./navigation-trace.js";
|
|
2
|
+
//#region src/server/navigation-planner.ts
|
|
3
|
+
function createRequestWorkDecision(options) {
|
|
4
|
+
return {
|
|
5
|
+
kind: "requestWork",
|
|
6
|
+
token: options.state.nextOperationToken,
|
|
7
|
+
work: options.work,
|
|
8
|
+
trace: createNavigationTrace(NavigationTraceReasonCodes.requestWork, {
|
|
9
|
+
eventKind: options.eventKind,
|
|
10
|
+
targetHref: getRequestedWorkTargetHref(options.work)
|
|
11
|
+
})
|
|
12
|
+
};
|
|
13
|
+
}
|
|
14
|
+
function getRequestedWorkTargetHref(work) {
|
|
15
|
+
switch (work.kind) {
|
|
16
|
+
case "flight":
|
|
17
|
+
case "prefetch": return work.href;
|
|
18
|
+
case "traverseFlight": return null;
|
|
19
|
+
default: throw new Error("[vinext] Unknown requested navigation work: " + String(work));
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
function createRootBoundaryTraceFields(options) {
|
|
23
|
+
return options.state.traceFields ?? createNavigationLifecycleTraceFields({
|
|
24
|
+
currentRootLayoutTreePath: options.state.visibleSnapshot.rootBoundaryId,
|
|
25
|
+
currentVisibleCommitVersion: options.state.visibleCommitVersion,
|
|
26
|
+
nextRootLayoutTreePath: options.event.result.targetSnapshot.rootBoundaryId,
|
|
27
|
+
startedVisibleCommitVersion: options.event.token.baseVisibleCommitVersion
|
|
28
|
+
});
|
|
29
|
+
}
|
|
30
|
+
function classifyRootBoundaryTransition(currentRootBoundaryId, nextRootBoundaryId) {
|
|
31
|
+
if (currentRootBoundaryId === null || nextRootBoundaryId === null) return "rootBoundaryUnknownFallback";
|
|
32
|
+
return currentRootBoundaryId === nextRootBoundaryId ? "currentRootBoundary" : "rootBoundaryChanged";
|
|
33
|
+
}
|
|
34
|
+
function resolveSameLayoutAncestorPersistence(currentSnapshot, targetSnapshot) {
|
|
35
|
+
if (classifyRootBoundaryTransition(currentSnapshot.rootBoundaryId, targetSnapshot.rootBoundaryId) !== "currentRootBoundary") return [];
|
|
36
|
+
const commonLayoutIds = [];
|
|
37
|
+
const maxLength = Math.min(currentSnapshot.layoutIds.length, targetSnapshot.layoutIds.length);
|
|
38
|
+
for (let index = 0; index < maxLength; index++) {
|
|
39
|
+
const layoutId = currentSnapshot.layoutIds[index];
|
|
40
|
+
if (layoutId !== targetSnapshot.layoutIds[index]) break;
|
|
41
|
+
commonLayoutIds.push(layoutId);
|
|
42
|
+
}
|
|
43
|
+
return commonLayoutIds;
|
|
44
|
+
}
|
|
45
|
+
function resolveMountedParallelSlotPersistence(currentSnapshot, targetSnapshot) {
|
|
46
|
+
return resolveMountedParallelSlotPersistenceForLayouts(currentSnapshot, resolveSameLayoutAncestorPersistence(currentSnapshot, targetSnapshot));
|
|
47
|
+
}
|
|
48
|
+
function resolveMountedParallelSlotPersistenceForLayouts(currentSnapshot, preservedLayoutIds) {
|
|
49
|
+
if (preservedLayoutIds.length === 0) return [];
|
|
50
|
+
const preservedLayoutIdSet = new Set(preservedLayoutIds);
|
|
51
|
+
const preservedSlotIds = [];
|
|
52
|
+
const seenSlotIds = /* @__PURE__ */ new Set();
|
|
53
|
+
for (const slot of currentSnapshot.mountedParallelSlots) {
|
|
54
|
+
if (slot.ownerLayoutId === null) continue;
|
|
55
|
+
if (!preservedLayoutIdSet.has(slot.ownerLayoutId)) continue;
|
|
56
|
+
if (seenSlotIds.has(slot.slotId)) continue;
|
|
57
|
+
preservedSlotIds.push(slot.slotId);
|
|
58
|
+
seenSlotIds.add(slot.slotId);
|
|
59
|
+
}
|
|
60
|
+
return preservedSlotIds;
|
|
61
|
+
}
|
|
62
|
+
function resolveCurrentRootBoundaryElementPersistence(currentSnapshot, targetSnapshot) {
|
|
63
|
+
const preservedLayoutIds = resolveSameLayoutAncestorPersistence(currentSnapshot, targetSnapshot);
|
|
64
|
+
return [...preservedLayoutIds, ...resolveMountedParallelSlotPersistenceForLayouts(currentSnapshot, preservedLayoutIds)];
|
|
65
|
+
}
|
|
66
|
+
function resolveCurrentRootBoundaryCommitElementPersistence(options) {
|
|
67
|
+
const preservedLayoutIds = resolveSameLayoutAncestorPersistence(options.currentSnapshot, options.targetSnapshot);
|
|
68
|
+
if (options.lane === "traverse") return preservedLayoutIds;
|
|
69
|
+
return [...preservedLayoutIds, ...resolveMountedParallelSlotPersistenceForLayouts(options.currentSnapshot, preservedLayoutIds)];
|
|
70
|
+
}
|
|
71
|
+
function planFlightResponseArrived(options) {
|
|
72
|
+
const traceFields = createRootBoundaryTraceFields(options);
|
|
73
|
+
if (options.event.token.lane === "prefetch") return {
|
|
74
|
+
kind: "noCommit",
|
|
75
|
+
reason: "prefetchOnly",
|
|
76
|
+
token: options.event.token,
|
|
77
|
+
trace: createNavigationTrace(NavigationTraceReasonCodes.prefetchOnly, traceFields)
|
|
78
|
+
};
|
|
79
|
+
const transition = classifyRootBoundaryTransition(options.state.visibleSnapshot.rootBoundaryId, options.event.result.targetSnapshot.rootBoundaryId);
|
|
80
|
+
if (transition === "rootBoundaryChanged") return {
|
|
81
|
+
kind: "hardNavigate",
|
|
82
|
+
reason: "rootBoundaryChanged",
|
|
83
|
+
token: options.event.token,
|
|
84
|
+
trace: createNavigationTrace(NavigationTraceReasonCodes.rootBoundaryChanged, traceFields),
|
|
85
|
+
url: options.event.result.href
|
|
86
|
+
};
|
|
87
|
+
if (transition === "rootBoundaryUnknownFallback") return {
|
|
88
|
+
kind: "proposeCommit",
|
|
89
|
+
proposal: {
|
|
90
|
+
preserveAbsentSlots: true,
|
|
91
|
+
preserveElementIds: [],
|
|
92
|
+
reason: "rootBoundaryUnknownFallback",
|
|
93
|
+
targetSnapshot: options.event.result.targetSnapshot
|
|
94
|
+
},
|
|
95
|
+
token: options.event.token,
|
|
96
|
+
trace: createNavigationTrace(NavigationTraceReasonCodes.rootBoundaryUnknown, traceFields)
|
|
97
|
+
};
|
|
98
|
+
return {
|
|
99
|
+
kind: "proposeCommit",
|
|
100
|
+
proposal: {
|
|
101
|
+
preserveAbsentSlots: false,
|
|
102
|
+
preserveElementIds: resolveCurrentRootBoundaryCommitElementPersistence({
|
|
103
|
+
currentSnapshot: options.state.visibleSnapshot,
|
|
104
|
+
lane: options.event.token.lane,
|
|
105
|
+
targetSnapshot: options.event.result.targetSnapshot
|
|
106
|
+
}),
|
|
107
|
+
reason: "currentRootBoundary",
|
|
108
|
+
targetSnapshot: options.event.result.targetSnapshot
|
|
109
|
+
},
|
|
110
|
+
token: options.event.token,
|
|
111
|
+
trace: createNavigationTrace(NavigationTraceReasonCodes.commitCurrent, traceFields)
|
|
112
|
+
};
|
|
113
|
+
}
|
|
114
|
+
function planNavigation(input) {
|
|
115
|
+
switch (input.event.kind) {
|
|
116
|
+
case "navigate": return createRequestWorkDecision({
|
|
117
|
+
eventKind: input.event.kind,
|
|
118
|
+
state: input.state,
|
|
119
|
+
work: {
|
|
120
|
+
href: input.event.href,
|
|
121
|
+
kind: "flight",
|
|
122
|
+
mode: input.event.mode
|
|
123
|
+
}
|
|
124
|
+
});
|
|
125
|
+
case "refresh": return createRequestWorkDecision({
|
|
126
|
+
eventKind: input.event.kind,
|
|
127
|
+
state: input.state,
|
|
128
|
+
work: {
|
|
129
|
+
href: input.state.visibleSnapshot.displayUrl,
|
|
130
|
+
kind: "flight",
|
|
131
|
+
mode: "refresh"
|
|
132
|
+
}
|
|
133
|
+
});
|
|
134
|
+
case "traverse": return createRequestWorkDecision({
|
|
135
|
+
eventKind: input.event.kind,
|
|
136
|
+
state: input.state,
|
|
137
|
+
work: {
|
|
138
|
+
direction: input.event.direction,
|
|
139
|
+
historyState: input.event.historyState,
|
|
140
|
+
kind: "traverseFlight"
|
|
141
|
+
}
|
|
142
|
+
});
|
|
143
|
+
case "prefetch": return createRequestWorkDecision({
|
|
144
|
+
eventKind: input.event.kind,
|
|
145
|
+
state: input.state,
|
|
146
|
+
work: {
|
|
147
|
+
href: input.event.href,
|
|
148
|
+
kind: "prefetch"
|
|
149
|
+
}
|
|
150
|
+
});
|
|
151
|
+
case "flightResponseArrived": return planFlightResponseArrived({
|
|
152
|
+
event: input.event,
|
|
153
|
+
state: input.state
|
|
154
|
+
});
|
|
155
|
+
default: {
|
|
156
|
+
const _exhaustive = input.event;
|
|
157
|
+
throw new Error("[vinext] Unknown navigation event: " + String(_exhaustive));
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
const navigationPlanner = {
|
|
162
|
+
classifyRootBoundaryTransition,
|
|
163
|
+
plan: planNavigation,
|
|
164
|
+
resolveCurrentRootBoundaryElementPersistence,
|
|
165
|
+
resolveMountedParallelSlotPersistence,
|
|
166
|
+
resolveSameLayoutAncestorPersistence
|
|
167
|
+
};
|
|
168
|
+
//#endregion
|
|
169
|
+
export { navigationPlanner };
|
|
170
|
+
|
|
171
|
+
//# sourceMappingURL=navigation-planner.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"navigation-planner.js","names":["_exhaustive"],"sources":["../../src/server/navigation-planner.ts"],"sourcesContent":["import type { RouteManifest } from \"../routing/app-route-graph.js\";\nimport {\n NavigationTraceReasonCodes,\n createNavigationLifecycleTraceFields,\n createNavigationTrace,\n type NavigationTrace,\n type NavigationTraceFields,\n} from \"./navigation-trace.js\";\n\nexport type OperationLane =\n | \"hmr\"\n | \"navigation\"\n | \"prefetch\"\n | \"refresh\"\n | \"server-action\"\n | \"traverse\";\n\nexport type OperationToken = {\n operationId: number;\n lane: OperationLane;\n baseVisibleCommitVersion: number;\n graphVersion: string | null;\n deploymentVersion: string | null;\n targetSnapshotFingerprint: string;\n cacheVariantFingerprint?: string;\n};\n\nexport type RouteSnapshotV0 = {\n routeId: string;\n // Ordered ancestor-first, with the root layout at index 0. Same-layout\n // persistence uses prefix comparison, so callers must preserve this order.\n layoutIds: readonly string[];\n mountedParallelSlots: readonly MountedParallelSlotSnapshotV0[];\n rootBoundaryId: string | null;\n displayUrl: string;\n matchedUrl: string;\n};\n\nexport type MountedParallelSlotSnapshotV0 = {\n slotId: string;\n ownerLayoutId: string | null;\n};\n\nexport type NavigationPlannerStateV0 = {\n // V0 keeps a single state shape so intent events and result events can move\n // through one planner surface. flightResponseArrived uses event.token; later\n // #726 slices can split this by event kind once more result paths are routed\n // through the planner.\n nextOperationToken: OperationToken;\n // Callers that have lifecycle authority should pass the complete trace\n // context. When absent, the planner emits the stable root-boundary facts it\n // can derive from the event and visible snapshot.\n traceFields?: NavigationTraceFields;\n visibleCommitVersion: number;\n visibleSnapshot: RouteSnapshotV0;\n};\n\nexport type RefreshScope = \"visible\";\n\nexport type NavigationEvent =\n | { kind: \"navigate\"; href: string; mode: \"push\" | \"replace\" }\n | { kind: \"refresh\"; scope: RefreshScope }\n | { kind: \"traverse\"; direction: \"back\" | \"forward\"; historyState: unknown }\n | { kind: \"prefetch\"; href: string }\n | { kind: \"flightResponseArrived\"; token: OperationToken; result: FlightResultV0 };\n\nexport type RequestedWork =\n | { kind: \"flight\"; href: string; mode: \"push\" | \"replace\" | \"refresh\" }\n | { direction: \"back\" | \"forward\"; historyState: unknown; kind: \"traverseFlight\" }\n | { kind: \"prefetch\"; href: string };\n\nexport type CommitProposal = {\n preserveAbsentSlots: boolean;\n preserveElementIds: readonly string[];\n reason: \"currentRootBoundary\" | \"rootBoundaryUnknownFallback\";\n targetSnapshot: RouteSnapshotV0;\n};\n\nexport type NoCommitReason = \"prefetchOnly\";\nexport type HardNavigationReason = \"rootBoundaryChanged\";\nexport type RootBoundaryTransition =\n | \"currentRootBoundary\"\n | \"rootBoundaryChanged\"\n | \"rootBoundaryUnknownFallback\";\n\nexport type NavigationDecisionV0 =\n | {\n kind: \"requestWork\";\n token: OperationToken;\n work: RequestedWork;\n trace: NavigationTrace;\n }\n | {\n kind: \"proposeCommit\";\n token: OperationToken;\n proposal: CommitProposal;\n trace: NavigationTrace;\n }\n | {\n kind: \"noCommit\";\n token: OperationToken;\n reason: NoCommitReason;\n trace: NavigationTrace;\n }\n | {\n kind: \"hardNavigate\";\n token: OperationToken;\n url: string;\n reason: HardNavigationReason;\n trace: NavigationTrace;\n };\n\nexport type FlightResultV0 = {\n href: string;\n targetSnapshot: RouteSnapshotV0;\n};\n\nexport type NavigationPlannerInput = {\n // Reserved for #726-CORE-09 route-graph-aware planning. CORE-07/08 only\n // routes the existing root-boundary decision through the planner, so browser\n // callers pass null until route topology becomes part of the decision input.\n routeManifest: RouteManifest | null;\n state: NavigationPlannerStateV0;\n event: NavigationEvent;\n};\n\nfunction createRequestWorkDecision(options: {\n eventKind: NavigationEvent[\"kind\"];\n state: NavigationPlannerStateV0;\n work: RequestedWork;\n}): NavigationDecisionV0 {\n return {\n kind: \"requestWork\",\n token: options.state.nextOperationToken,\n work: options.work,\n trace: createNavigationTrace(NavigationTraceReasonCodes.requestWork, {\n eventKind: options.eventKind,\n targetHref: getRequestedWorkTargetHref(options.work),\n }),\n };\n}\n\nfunction getRequestedWorkTargetHref(work: RequestedWork): string | null {\n switch (work.kind) {\n case \"flight\":\n case \"prefetch\":\n return work.href;\n case \"traverseFlight\":\n return null;\n default: {\n const _exhaustive: never = work;\n throw new Error(\"[vinext] Unknown requested navigation work: \" + String(_exhaustive));\n }\n }\n}\n\nfunction createRootBoundaryTraceFields(options: {\n event: Extract<NavigationEvent, { kind: \"flightResponseArrived\" }>;\n state: NavigationPlannerStateV0;\n}): NavigationTraceFields {\n // Browser commit approval supplies lifecycle trace context before calling\n // the planner. This fallback exists for pure planner callers and tests; it\n // intentionally cannot invent lifecycle-only fields such as active nav id.\n return (\n options.state.traceFields ??\n createNavigationLifecycleTraceFields({\n currentRootLayoutTreePath: options.state.visibleSnapshot.rootBoundaryId,\n currentVisibleCommitVersion: options.state.visibleCommitVersion,\n nextRootLayoutTreePath: options.event.result.targetSnapshot.rootBoundaryId,\n startedVisibleCommitVersion: options.event.token.baseVisibleCommitVersion,\n })\n );\n}\n\nfunction classifyRootBoundaryTransition(\n currentRootBoundaryId: string | null,\n nextRootBoundaryId: string | null,\n): RootBoundaryTransition {\n if (currentRootBoundaryId === null || nextRootBoundaryId === null) {\n // Both null directions intentionally share the v0 fallback because this\n // slice only knows boundary identity from the current flight payload.\n // #726-CORE-09 can split \"unknown current\" from \"unknown target\" once the\n // planner consumes graph-owned root boundary facts for both sides.\n return \"rootBoundaryUnknownFallback\";\n }\n\n return currentRootBoundaryId === nextRootBoundaryId\n ? \"currentRootBoundary\"\n : \"rootBoundaryChanged\";\n}\n\nfunction resolveSameLayoutAncestorPersistence(\n currentSnapshot: RouteSnapshotV0,\n targetSnapshot: RouteSnapshotV0,\n): readonly string[] {\n if (\n classifyRootBoundaryTransition(\n currentSnapshot.rootBoundaryId,\n targetSnapshot.rootBoundaryId,\n ) !== \"currentRootBoundary\"\n ) {\n return [];\n }\n\n const commonLayoutIds: string[] = [];\n const maxLength = Math.min(currentSnapshot.layoutIds.length, targetSnapshot.layoutIds.length);\n for (let index = 0; index < maxLength; index++) {\n const layoutId = currentSnapshot.layoutIds[index];\n if (layoutId !== targetSnapshot.layoutIds[index]) break;\n commonLayoutIds.push(layoutId);\n }\n return commonLayoutIds;\n}\n\nfunction resolveMountedParallelSlotPersistence(\n currentSnapshot: RouteSnapshotV0,\n targetSnapshot: RouteSnapshotV0,\n): readonly string[] {\n const preservedLayoutIds = resolveSameLayoutAncestorPersistence(currentSnapshot, targetSnapshot);\n return resolveMountedParallelSlotPersistenceForLayouts(currentSnapshot, preservedLayoutIds);\n}\n\nfunction resolveMountedParallelSlotPersistenceForLayouts(\n currentSnapshot: RouteSnapshotV0,\n preservedLayoutIds: readonly string[],\n): readonly string[] {\n if (preservedLayoutIds.length === 0) return [];\n const preservedLayoutIdSet = new Set(preservedLayoutIds);\n\n const preservedSlotIds: string[] = [];\n const seenSlotIds = new Set<string>();\n for (const slot of currentSnapshot.mountedParallelSlots) {\n if (slot.ownerLayoutId === null) continue;\n if (!preservedLayoutIdSet.has(slot.ownerLayoutId)) continue;\n if (seenSlotIds.has(slot.slotId)) continue;\n\n preservedSlotIds.push(slot.slotId);\n seenSlotIds.add(slot.slotId);\n }\n return preservedSlotIds;\n}\n\nfunction resolveCurrentRootBoundaryElementPersistence(\n currentSnapshot: RouteSnapshotV0,\n targetSnapshot: RouteSnapshotV0,\n): readonly string[] {\n const preservedLayoutIds = resolveSameLayoutAncestorPersistence(currentSnapshot, targetSnapshot);\n return [\n ...preservedLayoutIds,\n ...resolveMountedParallelSlotPersistenceForLayouts(currentSnapshot, preservedLayoutIds),\n ];\n}\n\nfunction resolveCurrentRootBoundaryCommitElementPersistence(options: {\n currentSnapshot: RouteSnapshotV0;\n lane: OperationLane;\n targetSnapshot: RouteSnapshotV0;\n}): readonly string[] {\n const preservedLayoutIds = resolveSameLayoutAncestorPersistence(\n options.currentSnapshot,\n options.targetSnapshot,\n );\n\n if (options.lane === \"traverse\") {\n return preservedLayoutIds;\n }\n\n return [\n ...preservedLayoutIds,\n ...resolveMountedParallelSlotPersistenceForLayouts(options.currentSnapshot, preservedLayoutIds),\n ];\n}\n\nfunction planFlightResponseArrived(options: {\n event: Extract<NavigationEvent, { kind: \"flightResponseArrived\" }>;\n state: NavigationPlannerStateV0;\n}): NavigationDecisionV0 {\n const traceFields = createRootBoundaryTraceFields(options);\n\n if (options.event.token.lane === \"prefetch\") {\n return {\n kind: \"noCommit\",\n reason: \"prefetchOnly\",\n token: options.event.token,\n trace: createNavigationTrace(NavigationTraceReasonCodes.prefetchOnly, traceFields),\n };\n }\n\n const transition = classifyRootBoundaryTransition(\n options.state.visibleSnapshot.rootBoundaryId,\n options.event.result.targetSnapshot.rootBoundaryId,\n );\n\n if (transition === \"rootBoundaryChanged\") {\n return {\n kind: \"hardNavigate\",\n reason: \"rootBoundaryChanged\",\n token: options.event.token,\n trace: createNavigationTrace(NavigationTraceReasonCodes.rootBoundaryChanged, traceFields),\n url: options.event.result.href,\n };\n }\n\n if (transition === \"rootBoundaryUnknownFallback\") {\n // Unknown root identity is an uncertainty fallback, not evidence that\n // reuse is safe. #726-CORE-09 can delete the legacy soft-commit writer\n // once every promoted caller supplies graph-owned root boundary IDs from\n // the route graph read model documented in routing/app-router.ts.\n return {\n kind: \"proposeCommit\",\n proposal: {\n preserveAbsentSlots: true,\n preserveElementIds: [],\n reason: \"rootBoundaryUnknownFallback\",\n targetSnapshot: options.event.result.targetSnapshot,\n },\n token: options.event.token,\n trace: createNavigationTrace(NavigationTraceReasonCodes.rootBoundaryUnknown, traceFields),\n };\n }\n\n return {\n kind: \"proposeCommit\",\n proposal: {\n preserveAbsentSlots: false,\n preserveElementIds: resolveCurrentRootBoundaryCommitElementPersistence({\n currentSnapshot: options.state.visibleSnapshot,\n lane: options.event.token.lane,\n targetSnapshot: options.event.result.targetSnapshot,\n }),\n reason: \"currentRootBoundary\",\n targetSnapshot: options.event.result.targetSnapshot,\n },\n token: options.event.token,\n trace: createNavigationTrace(NavigationTraceReasonCodes.commitCurrent, traceFields),\n };\n}\n\nfunction planNavigation(input: NavigationPlannerInput): NavigationDecisionV0 {\n switch (input.event.kind) {\n case \"navigate\":\n return createRequestWorkDecision({\n eventKind: input.event.kind,\n state: input.state,\n work: {\n href: input.event.href,\n kind: \"flight\",\n mode: input.event.mode,\n },\n });\n case \"refresh\":\n return createRequestWorkDecision({\n eventKind: input.event.kind,\n state: input.state,\n work: {\n href: input.state.visibleSnapshot.displayUrl,\n kind: \"flight\",\n mode: \"refresh\",\n },\n });\n case \"traverse\":\n return createRequestWorkDecision({\n eventKind: input.event.kind,\n state: input.state,\n work: {\n direction: input.event.direction,\n historyState: input.event.historyState,\n kind: \"traverseFlight\",\n },\n });\n case \"prefetch\":\n return createRequestWorkDecision({\n eventKind: input.event.kind,\n state: input.state,\n work: {\n href: input.event.href,\n kind: \"prefetch\",\n },\n });\n case \"flightResponseArrived\":\n return planFlightResponseArrived({\n event: input.event,\n state: input.state,\n });\n default: {\n const _exhaustive: never = input.event;\n throw new Error(\"[vinext] Unknown navigation event: \" + String(_exhaustive));\n }\n }\n}\n\nexport const navigationPlanner = {\n classifyRootBoundaryTransition,\n plan: planNavigation,\n resolveCurrentRootBoundaryElementPersistence,\n resolveMountedParallelSlotPersistence,\n resolveSameLayoutAncestorPersistence,\n};\n"],"mappings":"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"}
|
|
@@ -3,6 +3,8 @@ declare const NAVIGATION_TRACE_SCHEMA_VERSION = 0;
|
|
|
3
3
|
type NavigationTraceSchemaVersion = 0;
|
|
4
4
|
declare const NavigationTraceReasonCodes: {
|
|
5
5
|
commitCurrent: "NC_COMMIT";
|
|
6
|
+
prefetchOnly: "NC_PREFETCH_ONLY";
|
|
7
|
+
requestWork: "NC_REQUEST";
|
|
6
8
|
rootBoundaryChanged: "NC_ROOT";
|
|
7
9
|
rootBoundaryUnknown: "NC_ROOT_UNKNOWN";
|
|
8
10
|
staleOperation: "NC_STALE";
|
|
@@ -15,7 +17,7 @@ declare const NavigationTraceTransactionCodes: {
|
|
|
15
17
|
type NavigationTraceReasonCode = (typeof NavigationTraceReasonCodes)[keyof typeof NavigationTraceReasonCodes];
|
|
16
18
|
type NavigationTraceTransactionCode = (typeof NavigationTraceTransactionCodes)[keyof typeof NavigationTraceTransactionCodes];
|
|
17
19
|
type NavigationTraceCode = NavigationTraceReasonCode | NavigationTraceTransactionCode;
|
|
18
|
-
type NavigationTraceFieldName = "activeNavigationId" | "currentRootLayoutTreePath" | "nextRootLayoutTreePath" | "operationLane" | "pendingOperationId" | "startedVisibleCommitVersion" | "startedNavigationId";
|
|
20
|
+
type NavigationTraceFieldName = "activeNavigationId" | "currentRootLayoutTreePath" | "currentVisibleCommitVersion" | "nextRootLayoutTreePath" | "eventKind" | "operationLane" | "pendingOperationId" | "startedVisibleCommitVersion" | "startedNavigationId" | "targetHref";
|
|
19
21
|
type NavigationTraceFieldValue = string | number | boolean | null;
|
|
20
22
|
type NavigationTraceFields = Readonly<Partial<Record<NavigationTraceFieldName, NavigationTraceFieldValue>>>;
|
|
21
23
|
type NavigationTraceEntry = Readonly<{
|
|
@@ -26,8 +28,16 @@ type NavigationTrace = Readonly<{
|
|
|
26
28
|
schemaVersion: NavigationTraceSchemaVersion;
|
|
27
29
|
entries: readonly NavigationTraceEntry[];
|
|
28
30
|
}>;
|
|
31
|
+
declare function createNavigationLifecycleTraceFields(options: {
|
|
32
|
+
activeNavigationId?: number;
|
|
33
|
+
currentRootLayoutTreePath: string | null;
|
|
34
|
+
currentVisibleCommitVersion: number;
|
|
35
|
+
nextRootLayoutTreePath: string | null;
|
|
36
|
+
startedNavigationId?: number;
|
|
37
|
+
startedVisibleCommitVersion: number;
|
|
38
|
+
}): NavigationTraceFields;
|
|
29
39
|
declare function createNavigationTrace(code: NavigationTraceCode, fields?: NavigationTraceFields): NavigationTrace;
|
|
30
40
|
declare function prependNavigationTraceEntry(trace: NavigationTrace, code: NavigationTraceCode, fields?: NavigationTraceFields): NavigationTrace;
|
|
31
41
|
//#endregion
|
|
32
|
-
export { NAVIGATION_TRACE_SCHEMA_VERSION, NavigationTrace, NavigationTraceCode, NavigationTraceEntry, NavigationTraceFieldName, NavigationTraceFieldValue, NavigationTraceFields, NavigationTraceReasonCode, NavigationTraceReasonCodes, NavigationTraceSchemaVersion, NavigationTraceTransactionCode, NavigationTraceTransactionCodes, createNavigationTrace, prependNavigationTraceEntry };
|
|
42
|
+
export { NAVIGATION_TRACE_SCHEMA_VERSION, NavigationTrace, NavigationTraceCode, NavigationTraceEntry, NavigationTraceFieldName, NavigationTraceFieldValue, NavigationTraceFields, NavigationTraceReasonCode, NavigationTraceReasonCodes, NavigationTraceSchemaVersion, NavigationTraceTransactionCode, NavigationTraceTransactionCodes, createNavigationLifecycleTraceFields, createNavigationTrace, prependNavigationTraceEntry };
|
|
33
43
|
//# sourceMappingURL=navigation-trace.d.ts.map
|
|
@@ -2,6 +2,8 @@
|
|
|
2
2
|
const NAVIGATION_TRACE_SCHEMA_VERSION = 0;
|
|
3
3
|
const NavigationTraceReasonCodes = {
|
|
4
4
|
commitCurrent: "NC_COMMIT",
|
|
5
|
+
prefetchOnly: "NC_PREFETCH_ONLY",
|
|
6
|
+
requestWork: "NC_REQUEST",
|
|
5
7
|
rootBoundaryChanged: "NC_ROOT",
|
|
6
8
|
rootBoundaryUnknown: "NC_ROOT_UNKNOWN",
|
|
7
9
|
staleOperation: "NC_STALE"
|
|
@@ -11,6 +13,16 @@ const NavigationTraceTransactionCodes = {
|
|
|
11
13
|
noCommit: "NT_NO_COMMIT",
|
|
12
14
|
visibleCommit: "NT_VISIBLE_COMMIT"
|
|
13
15
|
};
|
|
16
|
+
function createNavigationLifecycleTraceFields(options) {
|
|
17
|
+
return {
|
|
18
|
+
...options.activeNavigationId !== void 0 ? { activeNavigationId: options.activeNavigationId } : {},
|
|
19
|
+
currentRootLayoutTreePath: options.currentRootLayoutTreePath,
|
|
20
|
+
currentVisibleCommitVersion: options.currentVisibleCommitVersion,
|
|
21
|
+
nextRootLayoutTreePath: options.nextRootLayoutTreePath,
|
|
22
|
+
...options.startedNavigationId !== void 0 ? { startedNavigationId: options.startedNavigationId } : {},
|
|
23
|
+
startedVisibleCommitVersion: options.startedVisibleCommitVersion
|
|
24
|
+
};
|
|
25
|
+
}
|
|
14
26
|
function createNavigationTraceEntry(code, fields = {}) {
|
|
15
27
|
return {
|
|
16
28
|
code,
|
|
@@ -30,6 +42,6 @@ function prependNavigationTraceEntry(trace, code, fields = {}) {
|
|
|
30
42
|
};
|
|
31
43
|
}
|
|
32
44
|
//#endregion
|
|
33
|
-
export { NAVIGATION_TRACE_SCHEMA_VERSION, NavigationTraceReasonCodes, NavigationTraceTransactionCodes, createNavigationTrace, prependNavigationTraceEntry };
|
|
45
|
+
export { NAVIGATION_TRACE_SCHEMA_VERSION, NavigationTraceReasonCodes, NavigationTraceTransactionCodes, createNavigationLifecycleTraceFields, createNavigationTrace, prependNavigationTraceEntry };
|
|
34
46
|
|
|
35
47
|
//# sourceMappingURL=navigation-trace.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"navigation-trace.js","names":[],"sources":["../../src/server/navigation-trace.ts"],"sourcesContent":["export const NAVIGATION_TRACE_SCHEMA_VERSION = 0;\n\nexport type NavigationTraceSchemaVersion = 0;\n\nexport const NavigationTraceReasonCodes = {\n commitCurrent: \"NC_COMMIT\",\n rootBoundaryChanged: \"NC_ROOT\",\n rootBoundaryUnknown: \"NC_ROOT_UNKNOWN\",\n staleOperation: \"NC_STALE\",\n} satisfies Readonly<{\n commitCurrent: \"NC_COMMIT\";\n rootBoundaryChanged: \"NC_ROOT\";\n rootBoundaryUnknown: \"NC_ROOT_UNKNOWN\";\n staleOperation: \"NC_STALE\";\n}>;\n\nexport const NavigationTraceTransactionCodes = {\n hardNavigate: \"NT_HARD_NAVIGATE\",\n noCommit: \"NT_NO_COMMIT\",\n visibleCommit: \"NT_VISIBLE_COMMIT\",\n} satisfies Readonly<{\n hardNavigate: \"NT_HARD_NAVIGATE\";\n noCommit: \"NT_NO_COMMIT\";\n visibleCommit: \"NT_VISIBLE_COMMIT\";\n}>;\n\nexport type NavigationTraceReasonCode =\n (typeof NavigationTraceReasonCodes)[keyof typeof NavigationTraceReasonCodes];\n\nexport type NavigationTraceTransactionCode =\n (typeof NavigationTraceTransactionCodes)[keyof typeof NavigationTraceTransactionCodes];\n\nexport type NavigationTraceCode = NavigationTraceReasonCode | NavigationTraceTransactionCode;\n\nexport type NavigationTraceFieldName =\n | \"activeNavigationId\"\n | \"currentRootLayoutTreePath\"\n | \"nextRootLayoutTreePath\"\n | \"operationLane\"\n | \"pendingOperationId\"\n | \"startedVisibleCommitVersion\"\n | \"startedNavigationId\";\n\nexport type NavigationTraceFieldValue = string | number | boolean | null;\n\nexport type NavigationTraceFields = Readonly<\n Partial<Record<NavigationTraceFieldName, NavigationTraceFieldValue>>\n>;\n\nexport type NavigationTraceEntry = Readonly<{\n code: NavigationTraceCode;\n fields: NavigationTraceFields;\n}>;\n\nexport type NavigationTrace = Readonly<{\n schemaVersion: NavigationTraceSchemaVersion;\n entries: readonly NavigationTraceEntry[];\n}>;\n\nfunction createNavigationTraceEntry(\n code: NavigationTraceCode,\n fields: NavigationTraceFields = {},\n): NavigationTraceEntry {\n return {\n code,\n fields: { ...fields },\n };\n}\n\nexport function createNavigationTrace(\n code: NavigationTraceCode,\n fields: NavigationTraceFields = {},\n): NavigationTrace {\n return {\n schemaVersion: NAVIGATION_TRACE_SCHEMA_VERSION,\n entries: [createNavigationTraceEntry(code, fields)],\n };\n}\n\nexport function prependNavigationTraceEntry(\n trace: NavigationTrace,\n code: NavigationTraceCode,\n fields: NavigationTraceFields = {},\n): NavigationTrace {\n return {\n schemaVersion: trace.schemaVersion,\n entries: [createNavigationTraceEntry(code, fields), ...trace.entries],\n };\n}\n"],"mappings":";AAAA,MAAa,kCAAkC;AAI/C,MAAa,6BAA6B;CACxC,eAAe;CACf,qBAAqB;CACrB,qBAAqB;CACrB,gBAAgB;CACjB;
|
|
1
|
+
{"version":3,"file":"navigation-trace.js","names":[],"sources":["../../src/server/navigation-trace.ts"],"sourcesContent":["export const NAVIGATION_TRACE_SCHEMA_VERSION = 0;\n\nexport type NavigationTraceSchemaVersion = 0;\n\nexport const NavigationTraceReasonCodes = {\n commitCurrent: \"NC_COMMIT\",\n prefetchOnly: \"NC_PREFETCH_ONLY\",\n requestWork: \"NC_REQUEST\",\n rootBoundaryChanged: \"NC_ROOT\",\n rootBoundaryUnknown: \"NC_ROOT_UNKNOWN\",\n staleOperation: \"NC_STALE\",\n} satisfies Readonly<{\n commitCurrent: \"NC_COMMIT\";\n prefetchOnly: \"NC_PREFETCH_ONLY\";\n requestWork: \"NC_REQUEST\";\n rootBoundaryChanged: \"NC_ROOT\";\n rootBoundaryUnknown: \"NC_ROOT_UNKNOWN\";\n staleOperation: \"NC_STALE\";\n}>;\n\nexport const NavigationTraceTransactionCodes = {\n hardNavigate: \"NT_HARD_NAVIGATE\",\n noCommit: \"NT_NO_COMMIT\",\n visibleCommit: \"NT_VISIBLE_COMMIT\",\n} satisfies Readonly<{\n hardNavigate: \"NT_HARD_NAVIGATE\";\n noCommit: \"NT_NO_COMMIT\";\n visibleCommit: \"NT_VISIBLE_COMMIT\";\n}>;\n\nexport type NavigationTraceReasonCode =\n (typeof NavigationTraceReasonCodes)[keyof typeof NavigationTraceReasonCodes];\n\nexport type NavigationTraceTransactionCode =\n (typeof NavigationTraceTransactionCodes)[keyof typeof NavigationTraceTransactionCodes];\n\nexport type NavigationTraceCode = NavigationTraceReasonCode | NavigationTraceTransactionCode;\n\nexport type NavigationTraceFieldName =\n | \"activeNavigationId\"\n | \"currentRootLayoutTreePath\"\n | \"currentVisibleCommitVersion\"\n | \"nextRootLayoutTreePath\"\n | \"eventKind\"\n | \"operationLane\"\n | \"pendingOperationId\"\n | \"startedVisibleCommitVersion\"\n | \"startedNavigationId\"\n | \"targetHref\";\n\nexport type NavigationTraceFieldValue = string | number | boolean | null;\n\nexport type NavigationTraceFields = Readonly<\n Partial<Record<NavigationTraceFieldName, NavigationTraceFieldValue>>\n>;\n\nexport type NavigationTraceEntry = Readonly<{\n code: NavigationTraceCode;\n fields: NavigationTraceFields;\n}>;\n\nexport type NavigationTrace = Readonly<{\n schemaVersion: NavigationTraceSchemaVersion;\n entries: readonly NavigationTraceEntry[];\n}>;\n\nexport function createNavigationLifecycleTraceFields(options: {\n activeNavigationId?: number;\n currentRootLayoutTreePath: string | null;\n currentVisibleCommitVersion: number;\n nextRootLayoutTreePath: string | null;\n startedNavigationId?: number;\n startedVisibleCommitVersion: number;\n}): NavigationTraceFields {\n return {\n ...(options.activeNavigationId !== undefined\n ? { activeNavigationId: options.activeNavigationId }\n : {}),\n currentRootLayoutTreePath: options.currentRootLayoutTreePath,\n currentVisibleCommitVersion: options.currentVisibleCommitVersion,\n nextRootLayoutTreePath: options.nextRootLayoutTreePath,\n ...(options.startedNavigationId !== undefined\n ? { startedNavigationId: options.startedNavigationId }\n : {}),\n startedVisibleCommitVersion: options.startedVisibleCommitVersion,\n };\n}\n\nfunction createNavigationTraceEntry(\n code: NavigationTraceCode,\n fields: NavigationTraceFields = {},\n): NavigationTraceEntry {\n return {\n code,\n fields: { ...fields },\n };\n}\n\nexport function createNavigationTrace(\n code: NavigationTraceCode,\n fields: NavigationTraceFields = {},\n): NavigationTrace {\n return {\n schemaVersion: NAVIGATION_TRACE_SCHEMA_VERSION,\n entries: [createNavigationTraceEntry(code, fields)],\n };\n}\n\nexport function prependNavigationTraceEntry(\n trace: NavigationTrace,\n code: NavigationTraceCode,\n fields: NavigationTraceFields = {},\n): NavigationTrace {\n return {\n schemaVersion: trace.schemaVersion,\n entries: [createNavigationTraceEntry(code, fields), ...trace.entries],\n };\n}\n"],"mappings":";AAAA,MAAa,kCAAkC;AAI/C,MAAa,6BAA6B;CACxC,eAAe;CACf,cAAc;CACd,aAAa;CACb,qBAAqB;CACrB,qBAAqB;CACrB,gBAAgB;CACjB;AASD,MAAa,kCAAkC;CAC7C,cAAc;CACd,UAAU;CACV,eAAe;CAChB;AA0CD,SAAgB,qCAAqC,SAO3B;CACxB,OAAO;EACL,GAAI,QAAQ,uBAAuB,KAAA,IAC/B,EAAE,oBAAoB,QAAQ,oBAAoB,GAClD,EAAE;EACN,2BAA2B,QAAQ;EACnC,6BAA6B,QAAQ;EACrC,wBAAwB,QAAQ;EAChC,GAAI,QAAQ,wBAAwB,KAAA,IAChC,EAAE,qBAAqB,QAAQ,qBAAqB,GACpD,EAAE;EACN,6BAA6B,QAAQ;EACtC;;AAGH,SAAS,2BACP,MACA,SAAgC,EAAE,EACZ;CACtB,OAAO;EACL;EACA,QAAQ,EAAE,GAAG,QAAQ;EACtB;;AAGH,SAAgB,sBACd,MACA,SAAgC,EAAE,EACjB;CACjB,OAAO;EACL,eAAA;EACA,SAAS,CAAC,2BAA2B,MAAM,OAAO,CAAC;EACpD;;AAGH,SAAgB,4BACd,OACA,MACA,SAAgC,EAAE,EACjB;CACjB,OAAO;EACL,eAAe,MAAM;EACrB,SAAS,CAAC,2BAA2B,MAAM,OAAO,EAAE,GAAG,MAAM,QAAQ;EACtE"}
|
|
@@ -15,7 +15,7 @@
|
|
|
15
15
|
*/
|
|
16
16
|
type NextRedirectDigest = {
|
|
17
17
|
status: number;
|
|
18
|
-
type: string;
|
|
18
|
+
type: string | null;
|
|
19
19
|
url: string;
|
|
20
20
|
};
|
|
21
21
|
type NextHttpErrorDigest = {
|
|
@@ -30,7 +30,8 @@ declare function getNextErrorDigest(error: unknown): string | null;
|
|
|
30
30
|
* Parses a `NEXT_REDIRECT;<type>;<encodedUrl>;<status>` digest. Returns null
|
|
31
31
|
* when the digest is not a redirect digest or the encoded URL segment is
|
|
32
32
|
* missing. The `url` is decoded with `decodeURIComponent`; the `status`
|
|
33
|
-
* defaults to 307 when omitted;
|
|
33
|
+
* defaults to 307 when omitted; an omitted `type` is left as null so the
|
|
34
|
+
* caller can apply the correct context-sensitive default.
|
|
34
35
|
*/
|
|
35
36
|
declare function parseNextRedirectDigest(digest: string): NextRedirectDigest | null;
|
|
36
37
|
/**
|
|
@@ -11,16 +11,18 @@ function getNextErrorDigest(error) {
|
|
|
11
11
|
* Parses a `NEXT_REDIRECT;<type>;<encodedUrl>;<status>` digest. Returns null
|
|
12
12
|
* when the digest is not a redirect digest or the encoded URL segment is
|
|
13
13
|
* missing. The `url` is decoded with `decodeURIComponent`; the `status`
|
|
14
|
-
* defaults to 307 when omitted;
|
|
14
|
+
* defaults to 307 when omitted; an omitted `type` is left as null so the
|
|
15
|
+
* caller can apply the correct context-sensitive default.
|
|
15
16
|
*/
|
|
16
17
|
function parseNextRedirectDigest(digest) {
|
|
17
18
|
if (!digest.startsWith("NEXT_REDIRECT;")) return null;
|
|
18
19
|
const parts = digest.split(";");
|
|
19
20
|
const encodedUrl = parts[2];
|
|
20
21
|
if (!encodedUrl) return null;
|
|
22
|
+
const type = parts[1];
|
|
21
23
|
return {
|
|
22
24
|
status: parts[3] ? parseInt(parts[3], 10) : 307,
|
|
23
|
-
type:
|
|
25
|
+
type: type || null,
|
|
24
26
|
url: decodeURIComponent(encodedUrl)
|
|
25
27
|
};
|
|
26
28
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"next-error-digest.js","names":[],"sources":["../../src/server/next-error-digest.ts"],"sourcesContent":["/**\n * Helpers for parsing Next.js error `digest` strings shared across the App\n * Router execution paths (server actions, page renders, route handlers).\n *\n * Next.js encodes special control flow as thrown errors carrying a `digest`\n * field with one of these formats:\n * - `NEXT_REDIRECT;<type>;<encodedUrl>;<status>` — `redirect()` / `permanentRedirect()`\n * - `NEXT_NOT_FOUND` — `notFound()`\n * - `NEXT_HTTP_ERROR_FALLBACK;<status>` — `forbidden()` / `unauthorized()` / etc.\n *\n * Each call site needs slightly different post-processing (URL resolution\n * against the request, 303-vs-307 status overrides for actions, etc.), so\n * these helpers only handle the parsing — callers shape the result.\n */\n\ntype NextRedirectDigest = {\n status: number;\n type: string;\n url: string;\n};\n\ntype NextHttpErrorDigest = {\n status: number;\n};\n\n/**\n * Pulls a stringified `digest` off an unknown thrown value, or returns null\n * when the value is not a digest-bearing error.\n */\nexport function getNextErrorDigest(error: unknown): string | null {\n if (!error || typeof error !== \"object\" || !(\"digest\" in error)) {\n return null;\n }\n\n return String(error.digest);\n}\n\n/**\n * Parses a `NEXT_REDIRECT;<type>;<encodedUrl>;<status>` digest. Returns null\n * when the digest is not a redirect digest or the encoded URL segment is\n * missing. The `url` is decoded with `decodeURIComponent`; the `status`\n * defaults to 307 when omitted;
|
|
1
|
+
{"version":3,"file":"next-error-digest.js","names":[],"sources":["../../src/server/next-error-digest.ts"],"sourcesContent":["/**\n * Helpers for parsing Next.js error `digest` strings shared across the App\n * Router execution paths (server actions, page renders, route handlers).\n *\n * Next.js encodes special control flow as thrown errors carrying a `digest`\n * field with one of these formats:\n * - `NEXT_REDIRECT;<type>;<encodedUrl>;<status>` — `redirect()` / `permanentRedirect()`\n * - `NEXT_NOT_FOUND` — `notFound()`\n * - `NEXT_HTTP_ERROR_FALLBACK;<status>` — `forbidden()` / `unauthorized()` / etc.\n *\n * Each call site needs slightly different post-processing (URL resolution\n * against the request, 303-vs-307 status overrides for actions, etc.), so\n * these helpers only handle the parsing — callers shape the result.\n */\n\ntype NextRedirectDigest = {\n status: number;\n type: string | null;\n url: string;\n};\n\ntype NextHttpErrorDigest = {\n status: number;\n};\n\n/**\n * Pulls a stringified `digest` off an unknown thrown value, or returns null\n * when the value is not a digest-bearing error.\n */\nexport function getNextErrorDigest(error: unknown): string | null {\n if (!error || typeof error !== \"object\" || !(\"digest\" in error)) {\n return null;\n }\n\n return String(error.digest);\n}\n\n/**\n * Parses a `NEXT_REDIRECT;<type>;<encodedUrl>;<status>` digest. Returns null\n * when the digest is not a redirect digest or the encoded URL segment is\n * missing. The `url` is decoded with `decodeURIComponent`; the `status`\n * defaults to 307 when omitted; an omitted `type` is left as null so the\n * caller can apply the correct context-sensitive default.\n */\nexport function parseNextRedirectDigest(digest: string): NextRedirectDigest | null {\n if (!digest.startsWith(\"NEXT_REDIRECT;\")) {\n return null;\n }\n\n const parts = digest.split(\";\");\n const encodedUrl = parts[2];\n if (!encodedUrl) {\n return null;\n }\n\n const type = parts[1];\n\n return {\n status: parts[3] ? parseInt(parts[3], 10) : 307,\n type: type || null,\n url: decodeURIComponent(encodedUrl),\n };\n}\n\n/**\n * Parses a `NEXT_NOT_FOUND` or `NEXT_HTTP_ERROR_FALLBACK;<status>` digest.\n * Returns `{ status: 404 }` for `NEXT_NOT_FOUND` and the parsed status code\n * for the fallback form. Returns null otherwise.\n */\nexport function parseNextHttpErrorDigest(digest: string): NextHttpErrorDigest | null {\n if (digest === \"NEXT_NOT_FOUND\") {\n return { status: 404 };\n }\n if (digest.startsWith(\"NEXT_HTTP_ERROR_FALLBACK;\")) {\n return { status: parseInt(digest.split(\";\")[1], 10) };\n }\n return null;\n}\n"],"mappings":";;;;;AA6BA,SAAgB,mBAAmB,OAA+B;CAChE,IAAI,CAAC,SAAS,OAAO,UAAU,YAAY,EAAE,YAAY,QACvD,OAAO;CAGT,OAAO,OAAO,MAAM,OAAO;;;;;;;;;AAU7B,SAAgB,wBAAwB,QAA2C;CACjF,IAAI,CAAC,OAAO,WAAW,iBAAiB,EACtC,OAAO;CAGT,MAAM,QAAQ,OAAO,MAAM,IAAI;CAC/B,MAAM,aAAa,MAAM;CACzB,IAAI,CAAC,YACH,OAAO;CAGT,MAAM,OAAO,MAAM;CAEnB,OAAO;EACL,QAAQ,MAAM,KAAK,SAAS,MAAM,IAAI,GAAG,GAAG;EAC5C,MAAM,QAAQ;EACd,KAAK,mBAAmB,WAAW;EACpC;;;;;;;AAQH,SAAgB,yBAAyB,QAA4C;CACnF,IAAI,WAAW,kBACb,OAAO,EAAE,QAAQ,KAAK;CAExB,IAAI,OAAO,WAAW,4BAA4B,EAChD,OAAO,EAAE,QAAQ,SAAS,OAAO,MAAM,IAAI,CAAC,IAAI,GAAG,EAAE;CAEvD,OAAO"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"normalize-path.js","names":[],"sources":["../../src/server/normalize-path.ts"],"sourcesContent":["/**\n * Re-encode path delimiter characters that were decoded by decodeURIComponent.\n * After decoding a URL segment, characters like / # ? \\ need to be re-encoded\n * so they don't change the path structure.\n *\n * Ported from Next.js: packages/next/src/shared/lib/router/utils/escape-path-delimiters.ts\n * https://github.com/vercel/next.js/blob/canary/packages/next/src/shared/lib/router/utils/escape-path-delimiters.ts\n */\nexport function escapePathDelimiters(segment: string, escapeEncoded?: boolean): string {\n return segment.replace(\n new RegExp(`([/#?]${escapeEncoded ? \"|%(2f|23|3f|5c)\" : \"\"})`, \"gi\"),\n (char: string) => encodeURIComponent(char),\n );\n}\n\n/**\n * Decode a URL pathname segment-by-segment, preserving encoded path delimiters.\n * Non-ASCII characters (e.g. %C3%A9 -> e) are decoded, but structural characters\n * like %2F (/) %23 (#) %3F (?) %5C (\\) are re-encoded after decoding.\n *\n * This prevents encoded slashes from changing the path structure (e.g.\n * /admin%2Fpanel stays as a single segment, not /admin/panel).\n *\n * Ported from Next.js: packages/next/src/server/lib/router-utils/decode-path-params.ts\n * https://github.com/vercel/next.js/blob/canary/packages/next/src/server/lib/router-utils/decode-path-params.ts\n */\nexport function decodePathParams(pathname: string): string {\n return pathname\n .split(\"/\")\n .map((seg) => {\n try {\n return escapePathDelimiters(decodeURIComponent(seg), true);\n } catch {\n return seg;\n }\n })\n .join(\"/\");\n}\n\n/**\n * Path normalization utility for request handling.\n *\n * Normalizes URL pathnames to a canonical form BEFORE any matching occurs\n * (middleware, routing, redirects, rewrites). This ensures middleware and\n * the router always see the same path, preventing path-confusion issues like\n * double-slash mismatches.\n *\n * Normalization rules:\n * 1. Collapse consecutive slashes: //foo///bar → /foo/bar\n * 2. Resolve single-dot segments: /foo/./bar → /foo/bar\n * 3. Resolve double-dot segments: /foo/../bar → /bar\n * 4. Ensure leading slash: foo/bar → /foo/bar\n * 5. Preserve root: / → /\n *\n * This function does NOT:\n * - Strip or add trailing slashes (handled separately by trailingSlash config)\n * - Decode percent-encoded characters (callers should decode before calling this)\n * - Lowercase the path (route matching is case-sensitive)\n */\nexport function normalizePath(pathname: string): string {\n // Fast path: already canonical (single leading /, no //, no /./, no /../)\n if (\n pathname === \"/\" ||\n (pathname.length > 1 &&\n pathname[0] === \"/\" &&\n !pathname.includes(\"//\") &&\n !pathname.includes(\"/./\") &&\n !pathname.includes(\"/../\") &&\n !pathname.endsWith(\"/.\") &&\n !pathname.endsWith(\"/..\"))\n ) {\n return pathname;\n }\n\n const segments = pathname.split(\"/\");\n const resolved: string[] = [];\n\n for (const segment of segments) {\n if (segment === \"\" || segment === \".\") {\n // Skip empty segments (from // or leading /) and single-dot segments\n continue;\n }\n if (segment === \"..\") {\n // Go up one level, but never above root\n resolved.pop();\n } else {\n resolved.push(segment);\n }\n }\n\n return \"/\" + resolved.join(\"/\");\n}\n"],"mappings":";;;;;;;;;AAQA,SAAgB,qBAAqB,SAAiB,eAAiC;
|
|
1
|
+
{"version":3,"file":"normalize-path.js","names":[],"sources":["../../src/server/normalize-path.ts"],"sourcesContent":["/**\n * Re-encode path delimiter characters that were decoded by decodeURIComponent.\n * After decoding a URL segment, characters like / # ? \\ need to be re-encoded\n * so they don't change the path structure.\n *\n * Ported from Next.js: packages/next/src/shared/lib/router/utils/escape-path-delimiters.ts\n * https://github.com/vercel/next.js/blob/canary/packages/next/src/shared/lib/router/utils/escape-path-delimiters.ts\n */\nexport function escapePathDelimiters(segment: string, escapeEncoded?: boolean): string {\n return segment.replace(\n new RegExp(`([/#?]${escapeEncoded ? \"|%(2f|23|3f|5c)\" : \"\"})`, \"gi\"),\n (char: string) => encodeURIComponent(char),\n );\n}\n\n/**\n * Decode a URL pathname segment-by-segment, preserving encoded path delimiters.\n * Non-ASCII characters (e.g. %C3%A9 -> e) are decoded, but structural characters\n * like %2F (/) %23 (#) %3F (?) %5C (\\) are re-encoded after decoding.\n *\n * This prevents encoded slashes from changing the path structure (e.g.\n * /admin%2Fpanel stays as a single segment, not /admin/panel).\n *\n * Ported from Next.js: packages/next/src/server/lib/router-utils/decode-path-params.ts\n * https://github.com/vercel/next.js/blob/canary/packages/next/src/server/lib/router-utils/decode-path-params.ts\n */\nexport function decodePathParams(pathname: string): string {\n return pathname\n .split(\"/\")\n .map((seg) => {\n try {\n return escapePathDelimiters(decodeURIComponent(seg), true);\n } catch {\n return seg;\n }\n })\n .join(\"/\");\n}\n\n/**\n * Path normalization utility for request handling.\n *\n * Normalizes URL pathnames to a canonical form BEFORE any matching occurs\n * (middleware, routing, redirects, rewrites). This ensures middleware and\n * the router always see the same path, preventing path-confusion issues like\n * double-slash mismatches.\n *\n * Normalization rules:\n * 1. Collapse consecutive slashes: //foo///bar → /foo/bar\n * 2. Resolve single-dot segments: /foo/./bar → /foo/bar\n * 3. Resolve double-dot segments: /foo/../bar → /bar\n * 4. Ensure leading slash: foo/bar → /foo/bar\n * 5. Preserve root: / → /\n *\n * This function does NOT:\n * - Strip or add trailing slashes (handled separately by trailingSlash config)\n * - Decode percent-encoded characters (callers should decode before calling this)\n * - Lowercase the path (route matching is case-sensitive)\n */\nexport function normalizePath(pathname: string): string {\n // Fast path: already canonical (single leading /, no //, no /./, no /../)\n if (\n pathname === \"/\" ||\n (pathname.length > 1 &&\n pathname[0] === \"/\" &&\n !pathname.includes(\"//\") &&\n !pathname.includes(\"/./\") &&\n !pathname.includes(\"/../\") &&\n !pathname.endsWith(\"/.\") &&\n !pathname.endsWith(\"/..\"))\n ) {\n return pathname;\n }\n\n const segments = pathname.split(\"/\");\n const resolved: string[] = [];\n\n for (const segment of segments) {\n if (segment === \"\" || segment === \".\") {\n // Skip empty segments (from // or leading /) and single-dot segments\n continue;\n }\n if (segment === \"..\") {\n // Go up one level, but never above root\n resolved.pop();\n } else {\n resolved.push(segment);\n }\n }\n\n return \"/\" + resolved.join(\"/\");\n}\n"],"mappings":";;;;;;;;;AAQA,SAAgB,qBAAqB,SAAiB,eAAiC;CACrF,OAAO,QAAQ,QACb,IAAI,OAAO,SAAS,gBAAgB,oBAAoB,GAAG,IAAI,KAAK,GACnE,SAAiB,mBAAmB,KAAK,CAC3C;;;;;;;;;;;;;AAcH,SAAgB,iBAAiB,UAA0B;CACzD,OAAO,SACJ,MAAM,IAAI,CACV,KAAK,QAAQ;EACZ,IAAI;GACF,OAAO,qBAAqB,mBAAmB,IAAI,EAAE,KAAK;UACpD;GACN,OAAO;;GAET,CACD,KAAK,IAAI;;;;;;;;;;;;;;;;;;;;;;AAuBd,SAAgB,cAAc,UAA0B;CAEtD,IACE,aAAa,OACZ,SAAS,SAAS,KACjB,SAAS,OAAO,OAChB,CAAC,SAAS,SAAS,KAAK,IACxB,CAAC,SAAS,SAAS,MAAM,IACzB,CAAC,SAAS,SAAS,OAAO,IAC1B,CAAC,SAAS,SAAS,KAAK,IACxB,CAAC,SAAS,SAAS,MAAM,EAE3B,OAAO;CAGT,MAAM,WAAW,SAAS,MAAM,IAAI;CACpC,MAAM,WAAqB,EAAE;CAE7B,KAAK,MAAM,WAAW,UAAU;EAC9B,IAAI,YAAY,MAAM,YAAY,KAEhC;EAEF,IAAI,YAAY,MAEd,SAAS,KAAK;OAEd,SAAS,KAAK,QAAQ;;CAI1B,OAAO,MAAM,SAAS,KAAK,IAAI"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"pages-api-route.js","names":["PagesApiBodyParseError"],"sources":["../../src/server/pages-api-route.ts"],"sourcesContent":["import type { Route } from \"../routing/pages-router.js\";\nimport { mergeRouteParamsIntoQuery, parseQueryString } from \"../utils/query.js\";\nimport {\n createPagesReqRes,\n parsePagesApiBody,\n type PagesRequestQuery,\n type PagesReqResRequest,\n type PagesReqResResponse,\n PagesApiBodyParseError,\n} from \"./pages-node-compat.js\";\nimport { internalServerErrorResponse } from \"./http-error-responses.js\";\n\ntype PagesApiRouteModule = {\n default?: (req: PagesReqResRequest, res: PagesReqResResponse) => void | Promise<void>;\n};\n\nexport type PagesApiRouteMatch = {\n params: PagesRequestQuery;\n route: Pick<Route, \"pattern\"> & {\n module: PagesApiRouteModule;\n };\n};\n\ntype HandlePagesApiRouteOptions = {\n match: PagesApiRouteMatch | null;\n reportRequestError?: (error: Error, routePattern: string) => void | Promise<void>;\n request: Request;\n url: string;\n};\n\nfunction buildPagesApiQuery(url: string, params: PagesRequestQuery): PagesRequestQuery {\n return mergeRouteParamsIntoQuery(parseQueryString(url), params);\n}\n\nexport async function handlePagesApiRoute(options: HandlePagesApiRouteOptions): Promise<Response> {\n if (!options.match) {\n return new Response(\"404 - API route not found\", { status: 404 });\n }\n\n const { route, params } = options.match;\n const handler = route.module.default;\n if (typeof handler !== \"function\") {\n return new Response(\"API route does not export a default function\", { status: 500 });\n }\n\n try {\n const query = buildPagesApiQuery(options.url, params);\n const body = await parsePagesApiBody(options.request);\n const { req, res, responsePromise } = createPagesReqRes({\n body,\n query,\n request: options.request,\n url: options.url,\n });\n\n await handler(req, res);\n res.end();\n return await responsePromise;\n } catch (error) {\n if (error instanceof PagesApiBodyParseError) {\n return new Response(error.message, {\n status: error.statusCode,\n statusText: error.message,\n });\n }\n\n void options.reportRequestError?.(\n error instanceof Error ? error : new Error(String(error)),\n route.pattern,\n );\n return internalServerErrorResponse();\n }\n}\n"],"mappings":";;;;;AA8BA,SAAS,mBAAmB,KAAa,QAA8C;
|
|
1
|
+
{"version":3,"file":"pages-api-route.js","names":["PagesApiBodyParseError"],"sources":["../../src/server/pages-api-route.ts"],"sourcesContent":["import type { Route } from \"../routing/pages-router.js\";\nimport { mergeRouteParamsIntoQuery, parseQueryString } from \"../utils/query.js\";\nimport {\n createPagesReqRes,\n parsePagesApiBody,\n type PagesRequestQuery,\n type PagesReqResRequest,\n type PagesReqResResponse,\n PagesApiBodyParseError,\n} from \"./pages-node-compat.js\";\nimport { internalServerErrorResponse } from \"./http-error-responses.js\";\n\ntype PagesApiRouteModule = {\n default?: (req: PagesReqResRequest, res: PagesReqResResponse) => void | Promise<void>;\n};\n\nexport type PagesApiRouteMatch = {\n params: PagesRequestQuery;\n route: Pick<Route, \"pattern\"> & {\n module: PagesApiRouteModule;\n };\n};\n\ntype HandlePagesApiRouteOptions = {\n match: PagesApiRouteMatch | null;\n reportRequestError?: (error: Error, routePattern: string) => void | Promise<void>;\n request: Request;\n url: string;\n};\n\nfunction buildPagesApiQuery(url: string, params: PagesRequestQuery): PagesRequestQuery {\n return mergeRouteParamsIntoQuery(parseQueryString(url), params);\n}\n\nexport async function handlePagesApiRoute(options: HandlePagesApiRouteOptions): Promise<Response> {\n if (!options.match) {\n return new Response(\"404 - API route not found\", { status: 404 });\n }\n\n const { route, params } = options.match;\n const handler = route.module.default;\n if (typeof handler !== \"function\") {\n return new Response(\"API route does not export a default function\", { status: 500 });\n }\n\n try {\n const query = buildPagesApiQuery(options.url, params);\n const body = await parsePagesApiBody(options.request);\n const { req, res, responsePromise } = createPagesReqRes({\n body,\n query,\n request: options.request,\n url: options.url,\n });\n\n await handler(req, res);\n res.end();\n return await responsePromise;\n } catch (error) {\n if (error instanceof PagesApiBodyParseError) {\n return new Response(error.message, {\n status: error.statusCode,\n statusText: error.message,\n });\n }\n\n void options.reportRequestError?.(\n error instanceof Error ? error : new Error(String(error)),\n route.pattern,\n );\n return internalServerErrorResponse();\n }\n}\n"],"mappings":";;;;;AA8BA,SAAS,mBAAmB,KAAa,QAA8C;CACrF,OAAO,0BAA0B,iBAAiB,IAAI,EAAE,OAAO;;AAGjE,eAAsB,oBAAoB,SAAwD;CAChG,IAAI,CAAC,QAAQ,OACX,OAAO,IAAI,SAAS,6BAA6B,EAAE,QAAQ,KAAK,CAAC;CAGnE,MAAM,EAAE,OAAO,WAAW,QAAQ;CAClC,MAAM,UAAU,MAAM,OAAO;CAC7B,IAAI,OAAO,YAAY,YACrB,OAAO,IAAI,SAAS,gDAAgD,EAAE,QAAQ,KAAK,CAAC;CAGtF,IAAI;EACF,MAAM,QAAQ,mBAAmB,QAAQ,KAAK,OAAO;EAErD,MAAM,EAAE,KAAK,KAAK,oBAAoB,kBAAkB;GACtD,MAAA,MAFiB,kBAAkB,QAAQ,QAAQ;GAGnD;GACA,SAAS,QAAQ;GACjB,KAAK,QAAQ;GACd,CAAC;EAEF,MAAM,QAAQ,KAAK,IAAI;EACvB,IAAI,KAAK;EACT,OAAO,MAAM;UACN,OAAO;EACd,IAAI,iBAAiBA,qBACnB,OAAO,IAAI,SAAS,MAAM,SAAS;GACjC,QAAQ,MAAM;GACd,YAAY,MAAM;GACnB,CAAC;EAGJ,QAAa,qBACX,iBAAiB,QAAQ,QAAQ,IAAI,MAAM,OAAO,MAAM,CAAC,EACzD,MAAM,QACP;EACD,OAAO,6BAA6B"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"pages-i18n.js","names":[],"sources":["../../src/server/pages-i18n.ts"],"sourcesContent":["import type { NextI18nConfig } from \"../config/next-config.js\";\nimport {\n detectDomainLocale,\n normalizeDomainHostname,\n type DomainLocale,\n} from \"../utils/domain-locale.js\";\n\ntype HeaderValue = string | string[] | undefined;\ntype HeaderBag = Headers | Record<string, HeaderValue> | undefined;\n\ntype LocaleRedirectOptions = {\n headers?: HeaderBag;\n nextConfig: {\n basePath?: string;\n i18n?: NextI18nConfig | null;\n trailingSlash?: boolean;\n };\n pathLocale?: string;\n urlParsed: {\n hostname?: string | null;\n pathname: string;\n search?: string;\n };\n};\n\ntype PagesI18nRequestInfo = {\n locale: string;\n url: string;\n hadPrefix: boolean;\n domainLocale?: DomainLocale;\n redirectUrl?: string;\n};\n\nfunction readHeader(headers: HeaderBag, name: string): string | undefined {\n if (!headers) return undefined;\n if (headers instanceof Headers) {\n return headers.get(name) ?? undefined;\n }\n\n // For Record headers, callers must pass lowercase names. Node's\n // IncomingMessage.headers are already lowercased by the HTTP parser.\n const direct = headers[name];\n if (Array.isArray(direct)) return direct.join(\", \");\n return direct;\n}\n\nconst normalizeHostname = normalizeDomainHostname;\nexport { detectDomainLocale };\n\n/**\n * Extract locale prefix from a URL path.\n * e.g. /fr/about -> { locale: \"fr\", url: \"/about\", hadPrefix: true }\n * /about -> { locale: defaultLocale, url: \"/about\", hadPrefix: false }\n */\nexport function extractLocaleFromUrl(\n url: string,\n i18nConfig: NextI18nConfig,\n defaultLocale = i18nConfig.defaultLocale,\n): { locale: string; url: string; hadPrefix: boolean } {\n const pathname = url.split(\"?\")[0];\n const parts = pathname.split(\"/\").filter(Boolean);\n const query = url.includes(\"?\") ? url.slice(url.indexOf(\"?\")) : \"\";\n\n if (parts.length > 0 && i18nConfig.locales.includes(parts[0])) {\n const locale = parts[0];\n const rest = \"/\" + parts.slice(1).join(\"/\");\n return { locale, url: (rest || \"/\") + query, hadPrefix: true };\n }\n\n return { locale: defaultLocale, url, hadPrefix: false };\n}\n\n/**\n * Detect the preferred locale from the Accept-Language header.\n * Returns the best matching locale or null.\n */\nexport function detectLocaleFromAcceptLanguage(\n acceptLang: string | null | undefined,\n i18nConfig: NextI18nConfig,\n): string | null {\n if (!acceptLang) return null;\n\n const langs = acceptLang\n .split(\",\")\n .map((part) => {\n const [lang, qPart] = part.trim().split(\";\");\n const q = qPart ? parseFloat(qPart.replace(\"q=\", \"\")) : 1;\n return { lang: lang.trim().toLowerCase(), q };\n })\n .sort((a, b) => b.q - a.q);\n\n for (const { lang } of langs) {\n const exactMatch = i18nConfig.locales.find((locale) => locale.toLowerCase() === lang);\n if (exactMatch) return exactMatch;\n\n const prefix = lang.split(\"-\")[0];\n const prefixMatch = i18nConfig.locales.find((locale) => {\n const lowered = locale.toLowerCase();\n return lowered === prefix || lowered.startsWith(prefix + \"-\");\n });\n if (prefixMatch) return prefixMatch;\n }\n\n return null;\n}\n\n/**\n * Parse the NEXT_LOCALE cookie.\n * Returns the cookie value if it matches a configured locale, otherwise null.\n */\nexport function parseCookieLocaleFromHeader(\n cookieHeader: string | null | undefined,\n i18nConfig: NextI18nConfig,\n): string | null {\n if (!cookieHeader) return null;\n\n const match = cookieHeader.match(/(?:^|;\\s*)NEXT_LOCALE=([^;]*)/);\n if (!match) return null;\n\n let value: string;\n try {\n value = decodeURIComponent(match[1].trim());\n } catch {\n return null;\n }\n\n if (i18nConfig.locales.includes(value)) return value;\n return null;\n}\n\nfunction formatLocalizedRootPath(\n locale: string,\n defaultLocale: string,\n basePath = \"\",\n trailingSlash = false,\n search = \"\",\n): string | undefined {\n if (locale.toLowerCase() === defaultLocale.toLowerCase()) return undefined;\n const rootPath = `${basePath}/${locale}${trailingSlash ? \"/\" : \"\"}`;\n return `${rootPath.replace(/\\/{2,}/g, \"/\")}${search}`;\n}\n\nexport function getLocaleRedirect({\n headers,\n nextConfig,\n pathLocale,\n urlParsed,\n}: LocaleRedirectOptions): string | undefined {\n const i18n = nextConfig.i18n;\n // Next.js treats localeDetection as the global auto-redirect switch, so\n // disabling it also disables root domain-locale redirects, including\n // cross-domain redirects driven by the current host or Accept-Language.\n if (!i18n || i18n.localeDetection === false || urlParsed.pathname !== \"/\") return undefined;\n\n const domainLocale = detectDomainLocale(i18n.domains, urlParsed.hostname ?? undefined);\n const defaultLocale = domainLocale?.defaultLocale || i18n.defaultLocale;\n const preferredLocale =\n detectLocaleFromAcceptLanguage(readHeader(headers, \"accept-language\"), i18n) ?? undefined;\n const detectedLocale =\n pathLocale ||\n domainLocale?.defaultLocale ||\n (parseCookieLocaleFromHeader(readHeader(headers, \"cookie\"), i18n) ?? undefined) ||\n preferredLocale ||\n i18n.defaultLocale;\n const search = urlParsed.search ?? \"\";\n\n const preferredDomain = detectDomainLocale(i18n.domains, undefined, preferredLocale);\n if (domainLocale && preferredDomain) {\n const sameDomain =\n normalizeHostname(domainLocale.domain) === normalizeHostname(preferredDomain.domain);\n const sameLocale =\n preferredLocale !== undefined &&\n preferredDomain.defaultLocale.toLowerCase() === preferredLocale.toLowerCase();\n\n if (!sameDomain || !sameLocale) {\n // sameDomain && !sameLocale yields a locale-prefixed redirect on the same\n // host (for example /nl-BE). This matches Next.js and doesn't loop because\n // the next request is prefixed and therefore skips getLocaleRedirect().\n const scheme = `http${preferredDomain.http ? \"\" : \"s\"}`;\n const localePath = sameLocale || preferredLocale === undefined ? \"\" : `/${preferredLocale}`;\n const basePath = nextConfig.basePath ?? \"\";\n const rootPath = `${basePath}${localePath}${nextConfig.trailingSlash ? \"/\" : \"\"}` || \"/\";\n const normalizedPath = rootPath.startsWith(\"/\") ? rootPath : `/${rootPath}`;\n return `${scheme}://${preferredDomain.domain}${normalizedPath}${search}`;\n }\n }\n\n return formatLocalizedRootPath(\n detectedLocale,\n defaultLocale,\n nextConfig.basePath,\n nextConfig.trailingSlash,\n search,\n );\n}\n\nexport function resolvePagesI18nRequest(\n url: string,\n i18nConfig: NextI18nConfig,\n headers?: HeaderBag,\n hostname?: string | null,\n basePath = \"\",\n trailingSlash = false,\n): PagesI18nRequestInfo {\n const domainLocale = detectDomainLocale(i18nConfig.domains, hostname ?? undefined);\n const defaultLocale = domainLocale?.defaultLocale || i18nConfig.defaultLocale;\n const localeInfo = extractLocaleFromUrl(url, i18nConfig, defaultLocale);\n\n let redirectUrl: string | undefined;\n if (!localeInfo.hadPrefix) {\n redirectUrl = getLocaleRedirect({\n headers,\n nextConfig: {\n basePath,\n i18n: i18nConfig,\n trailingSlash,\n },\n urlParsed: {\n hostname,\n pathname: localeInfo.url.split(\"?\")[0] || \"/\",\n search: localeInfo.url.includes(\"?\")\n ? localeInfo.url.slice(localeInfo.url.indexOf(\"?\"))\n : \"\",\n },\n });\n }\n\n return {\n ...localeInfo,\n domainLocale,\n redirectUrl,\n };\n}\n"],"mappings":";;AAiCA,SAAS,WAAW,SAAoB,MAAkC;AACxE,KAAI,CAAC,QAAS,QAAO,KAAA;AACrB,KAAI,mBAAmB,QACrB,QAAO,QAAQ,IAAI,KAAK,IAAI,KAAA;CAK9B,MAAM,SAAS,QAAQ;AACvB,KAAI,MAAM,QAAQ,OAAO,CAAE,QAAO,OAAO,KAAK,KAAK;AACnD,QAAO;;AAGT,MAAM,oBAAoB;;;;;;AAQ1B,SAAgB,qBACd,KACA,YACA,gBAAgB,WAAW,eAC0B;CAErD,MAAM,QADW,IAAI,MAAM,IAAI,CAAC,GACT,MAAM,IAAI,CAAC,OAAO,QAAQ;CACjD,MAAM,QAAQ,IAAI,SAAS,IAAI,GAAG,IAAI,MAAM,IAAI,QAAQ,IAAI,CAAC,GAAG;AAEhE,KAAI,MAAM,SAAS,KAAK,WAAW,QAAQ,SAAS,MAAM,GAAG,CAG3D,QAAO;EAAE,QAFM,MAAM;EAEJ,MADJ,MAAM,MAAM,MAAM,EAAE,CAAC,KAAK,IAAI,IACZ,OAAO;EAAO,WAAW;EAAM;AAGhE,QAAO;EAAE,QAAQ;EAAe;EAAK,WAAW;EAAO;;;;;;AAOzD,SAAgB,+BACd,YACA,YACe;AACf,KAAI,CAAC,WAAY,QAAO;CAExB,MAAM,QAAQ,WACX,MAAM,IAAI,CACV,KAAK,SAAS;EACb,MAAM,CAAC,MAAM,SAAS,KAAK,MAAM,CAAC,MAAM,IAAI;EAC5C,MAAM,IAAI,QAAQ,WAAW,MAAM,QAAQ,MAAM,GAAG,CAAC,GAAG;AACxD,SAAO;GAAE,MAAM,KAAK,MAAM,CAAC,aAAa;GAAE;GAAG;GAC7C,CACD,MAAM,GAAG,MAAM,EAAE,IAAI,EAAE,EAAE;AAE5B,MAAK,MAAM,EAAE,UAAU,OAAO;EAC5B,MAAM,aAAa,WAAW,QAAQ,MAAM,WAAW,OAAO,aAAa,KAAK,KAAK;AACrF,MAAI,WAAY,QAAO;EAEvB,MAAM,SAAS,KAAK,MAAM,IAAI,CAAC;EAC/B,MAAM,cAAc,WAAW,QAAQ,MAAM,WAAW;GACtD,MAAM,UAAU,OAAO,aAAa;AACpC,UAAO,YAAY,UAAU,QAAQ,WAAW,SAAS,IAAI;IAC7D;AACF,MAAI,YAAa,QAAO;;AAG1B,QAAO;;;;;;AAOT,SAAgB,4BACd,cACA,YACe;AACf,KAAI,CAAC,aAAc,QAAO;CAE1B,MAAM,QAAQ,aAAa,MAAM,gCAAgC;AACjE,KAAI,CAAC,MAAO,QAAO;CAEnB,IAAI;AACJ,KAAI;AACF,UAAQ,mBAAmB,MAAM,GAAG,MAAM,CAAC;SACrC;AACN,SAAO;;AAGT,KAAI,WAAW,QAAQ,SAAS,MAAM,CAAE,QAAO;AAC/C,QAAO;;AAGT,SAAS,wBACP,QACA,eACA,WAAW,IACX,gBAAgB,OAChB,SAAS,IACW;AACpB,KAAI,OAAO,aAAa,KAAK,cAAc,aAAa,CAAE,QAAO,KAAA;AAEjE,QAAO,GADU,GAAG,SAAS,GAAG,SAAS,gBAAgB,MAAM,KAC5C,QAAQ,WAAW,IAAI,GAAG;;AAG/C,SAAgB,kBAAkB,EAChC,SACA,YACA,YACA,aAC4C;CAC5C,MAAM,OAAO,WAAW;AAIxB,KAAI,CAAC,QAAQ,KAAK,oBAAoB,SAAS,UAAU,aAAa,IAAK,QAAO,KAAA;CAElF,MAAM,eAAe,mBAAmB,KAAK,SAAS,UAAU,YAAY,KAAA,EAAU;CACtF,MAAM,gBAAgB,cAAc,iBAAiB,KAAK;CAC1D,MAAM,kBACJ,+BAA+B,WAAW,SAAS,kBAAkB,EAAE,KAAK,IAAI,KAAA;CAClF,MAAM,iBACJ,cACA,cAAc,kBACb,4BAA4B,WAAW,SAAS,SAAS,EAAE,KAAK,IAAI,KAAA,MACrE,mBACA,KAAK;CACP,MAAM,SAAS,UAAU,UAAU;CAEnC,MAAM,kBAAkB,mBAAmB,KAAK,SAAS,KAAA,GAAW,gBAAgB;AACpF,KAAI,gBAAgB,iBAAiB;EACnC,MAAM,aACJ,kBAAkB,aAAa,OAAO,KAAK,kBAAkB,gBAAgB,OAAO;EACtF,MAAM,aACJ,oBAAoB,KAAA,KACpB,gBAAgB,cAAc,aAAa,KAAK,gBAAgB,aAAa;AAE/E,MAAI,CAAC,cAAc,CAAC,YAAY;GAI9B,MAAM,SAAS,OAAO,gBAAgB,OAAO,KAAK;GAClD,MAAM,aAAa,cAAc,oBAAoB,KAAA,IAAY,KAAK,IAAI;GAE1E,MAAM,WAAW,GADA,WAAW,YAAY,KACT,aAAa,WAAW,gBAAgB,MAAM,QAAQ;GACrF,MAAM,iBAAiB,SAAS,WAAW,IAAI,GAAG,WAAW,IAAI;AACjE,UAAO,GAAG,OAAO,KAAK,gBAAgB,SAAS,iBAAiB;;;AAIpE,QAAO,wBACL,gBACA,eACA,WAAW,UACX,WAAW,eACX,OACD;;AAGH,SAAgB,wBACd,KACA,YACA,SACA,UACA,WAAW,IACX,gBAAgB,OACM;CACtB,MAAM,eAAe,mBAAmB,WAAW,SAAS,YAAY,KAAA,EAAU;CAElF,MAAM,aAAa,qBAAqB,KAAK,YADvB,cAAc,iBAAiB,WAAW,cACO;CAEvE,IAAI;AACJ,KAAI,CAAC,WAAW,UACd,eAAc,kBAAkB;EAC9B;EACA,YAAY;GACV;GACA,MAAM;GACN;GACD;EACD,WAAW;GACT;GACA,UAAU,WAAW,IAAI,MAAM,IAAI,CAAC,MAAM;GAC1C,QAAQ,WAAW,IAAI,SAAS,IAAI,GAChC,WAAW,IAAI,MAAM,WAAW,IAAI,QAAQ,IAAI,CAAC,GACjD;GACL;EACF,CAAC;AAGJ,QAAO;EACL,GAAG;EACH;EACA;EACD"}
|
|
1
|
+
{"version":3,"file":"pages-i18n.js","names":[],"sources":["../../src/server/pages-i18n.ts"],"sourcesContent":["import type { NextI18nConfig } from \"../config/next-config.js\";\nimport {\n detectDomainLocale,\n normalizeDomainHostname,\n type DomainLocale,\n} from \"../utils/domain-locale.js\";\n\ntype HeaderValue = string | string[] | undefined;\ntype HeaderBag = Headers | Record<string, HeaderValue> | undefined;\n\ntype LocaleRedirectOptions = {\n headers?: HeaderBag;\n nextConfig: {\n basePath?: string;\n i18n?: NextI18nConfig | null;\n trailingSlash?: boolean;\n };\n pathLocale?: string;\n urlParsed: {\n hostname?: string | null;\n pathname: string;\n search?: string;\n };\n};\n\ntype PagesI18nRequestInfo = {\n locale: string;\n url: string;\n hadPrefix: boolean;\n domainLocale?: DomainLocale;\n redirectUrl?: string;\n};\n\nfunction readHeader(headers: HeaderBag, name: string): string | undefined {\n if (!headers) return undefined;\n if (headers instanceof Headers) {\n return headers.get(name) ?? undefined;\n }\n\n // For Record headers, callers must pass lowercase names. Node's\n // IncomingMessage.headers are already lowercased by the HTTP parser.\n const direct = headers[name];\n if (Array.isArray(direct)) return direct.join(\", \");\n return direct;\n}\n\nconst normalizeHostname = normalizeDomainHostname;\nexport { detectDomainLocale };\n\n/**\n * Extract locale prefix from a URL path.\n * e.g. /fr/about -> { locale: \"fr\", url: \"/about\", hadPrefix: true }\n * /about -> { locale: defaultLocale, url: \"/about\", hadPrefix: false }\n */\nexport function extractLocaleFromUrl(\n url: string,\n i18nConfig: NextI18nConfig,\n defaultLocale = i18nConfig.defaultLocale,\n): { locale: string; url: string; hadPrefix: boolean } {\n const pathname = url.split(\"?\")[0];\n const parts = pathname.split(\"/\").filter(Boolean);\n const query = url.includes(\"?\") ? url.slice(url.indexOf(\"?\")) : \"\";\n\n if (parts.length > 0 && i18nConfig.locales.includes(parts[0])) {\n const locale = parts[0];\n const rest = \"/\" + parts.slice(1).join(\"/\");\n return { locale, url: (rest || \"/\") + query, hadPrefix: true };\n }\n\n return { locale: defaultLocale, url, hadPrefix: false };\n}\n\n/**\n * Detect the preferred locale from the Accept-Language header.\n * Returns the best matching locale or null.\n */\nexport function detectLocaleFromAcceptLanguage(\n acceptLang: string | null | undefined,\n i18nConfig: NextI18nConfig,\n): string | null {\n if (!acceptLang) return null;\n\n const langs = acceptLang\n .split(\",\")\n .map((part) => {\n const [lang, qPart] = part.trim().split(\";\");\n const q = qPart ? parseFloat(qPart.replace(\"q=\", \"\")) : 1;\n return { lang: lang.trim().toLowerCase(), q };\n })\n .sort((a, b) => b.q - a.q);\n\n for (const { lang } of langs) {\n const exactMatch = i18nConfig.locales.find((locale) => locale.toLowerCase() === lang);\n if (exactMatch) return exactMatch;\n\n const prefix = lang.split(\"-\")[0];\n const prefixMatch = i18nConfig.locales.find((locale) => {\n const lowered = locale.toLowerCase();\n return lowered === prefix || lowered.startsWith(prefix + \"-\");\n });\n if (prefixMatch) return prefixMatch;\n }\n\n return null;\n}\n\n/**\n * Parse the NEXT_LOCALE cookie.\n * Returns the cookie value if it matches a configured locale, otherwise null.\n */\nexport function parseCookieLocaleFromHeader(\n cookieHeader: string | null | undefined,\n i18nConfig: NextI18nConfig,\n): string | null {\n if (!cookieHeader) return null;\n\n const match = cookieHeader.match(/(?:^|;\\s*)NEXT_LOCALE=([^;]*)/);\n if (!match) return null;\n\n let value: string;\n try {\n value = decodeURIComponent(match[1].trim());\n } catch {\n return null;\n }\n\n if (i18nConfig.locales.includes(value)) return value;\n return null;\n}\n\nfunction formatLocalizedRootPath(\n locale: string,\n defaultLocale: string,\n basePath = \"\",\n trailingSlash = false,\n search = \"\",\n): string | undefined {\n if (locale.toLowerCase() === defaultLocale.toLowerCase()) return undefined;\n const rootPath = `${basePath}/${locale}${trailingSlash ? \"/\" : \"\"}`;\n return `${rootPath.replace(/\\/{2,}/g, \"/\")}${search}`;\n}\n\nexport function getLocaleRedirect({\n headers,\n nextConfig,\n pathLocale,\n urlParsed,\n}: LocaleRedirectOptions): string | undefined {\n const i18n = nextConfig.i18n;\n // Next.js treats localeDetection as the global auto-redirect switch, so\n // disabling it also disables root domain-locale redirects, including\n // cross-domain redirects driven by the current host or Accept-Language.\n if (!i18n || i18n.localeDetection === false || urlParsed.pathname !== \"/\") return undefined;\n\n const domainLocale = detectDomainLocale(i18n.domains, urlParsed.hostname ?? undefined);\n const defaultLocale = domainLocale?.defaultLocale || i18n.defaultLocale;\n const preferredLocale =\n detectLocaleFromAcceptLanguage(readHeader(headers, \"accept-language\"), i18n) ?? undefined;\n const detectedLocale =\n pathLocale ||\n domainLocale?.defaultLocale ||\n (parseCookieLocaleFromHeader(readHeader(headers, \"cookie\"), i18n) ?? undefined) ||\n preferredLocale ||\n i18n.defaultLocale;\n const search = urlParsed.search ?? \"\";\n\n const preferredDomain = detectDomainLocale(i18n.domains, undefined, preferredLocale);\n if (domainLocale && preferredDomain) {\n const sameDomain =\n normalizeHostname(domainLocale.domain) === normalizeHostname(preferredDomain.domain);\n const sameLocale =\n preferredLocale !== undefined &&\n preferredDomain.defaultLocale.toLowerCase() === preferredLocale.toLowerCase();\n\n if (!sameDomain || !sameLocale) {\n // sameDomain && !sameLocale yields a locale-prefixed redirect on the same\n // host (for example /nl-BE). This matches Next.js and doesn't loop because\n // the next request is prefixed and therefore skips getLocaleRedirect().\n const scheme = `http${preferredDomain.http ? \"\" : \"s\"}`;\n const localePath = sameLocale || preferredLocale === undefined ? \"\" : `/${preferredLocale}`;\n const basePath = nextConfig.basePath ?? \"\";\n const rootPath = `${basePath}${localePath}${nextConfig.trailingSlash ? \"/\" : \"\"}` || \"/\";\n const normalizedPath = rootPath.startsWith(\"/\") ? rootPath : `/${rootPath}`;\n return `${scheme}://${preferredDomain.domain}${normalizedPath}${search}`;\n }\n }\n\n return formatLocalizedRootPath(\n detectedLocale,\n defaultLocale,\n nextConfig.basePath,\n nextConfig.trailingSlash,\n search,\n );\n}\n\nexport function resolvePagesI18nRequest(\n url: string,\n i18nConfig: NextI18nConfig,\n headers?: HeaderBag,\n hostname?: string | null,\n basePath = \"\",\n trailingSlash = false,\n): PagesI18nRequestInfo {\n const domainLocale = detectDomainLocale(i18nConfig.domains, hostname ?? undefined);\n const defaultLocale = domainLocale?.defaultLocale || i18nConfig.defaultLocale;\n const localeInfo = extractLocaleFromUrl(url, i18nConfig, defaultLocale);\n\n let redirectUrl: string | undefined;\n if (!localeInfo.hadPrefix) {\n redirectUrl = getLocaleRedirect({\n headers,\n nextConfig: {\n basePath,\n i18n: i18nConfig,\n trailingSlash,\n },\n urlParsed: {\n hostname,\n pathname: localeInfo.url.split(\"?\")[0] || \"/\",\n search: localeInfo.url.includes(\"?\")\n ? localeInfo.url.slice(localeInfo.url.indexOf(\"?\"))\n : \"\",\n },\n });\n }\n\n return {\n ...localeInfo,\n domainLocale,\n redirectUrl,\n };\n}\n"],"mappings":";;AAiCA,SAAS,WAAW,SAAoB,MAAkC;CACxE,IAAI,CAAC,SAAS,OAAO,KAAA;CACrB,IAAI,mBAAmB,SACrB,OAAO,QAAQ,IAAI,KAAK,IAAI,KAAA;CAK9B,MAAM,SAAS,QAAQ;CACvB,IAAI,MAAM,QAAQ,OAAO,EAAE,OAAO,OAAO,KAAK,KAAK;CACnD,OAAO;;AAGT,MAAM,oBAAoB;;;;;;AAQ1B,SAAgB,qBACd,KACA,YACA,gBAAgB,WAAW,eAC0B;CAErD,MAAM,QADW,IAAI,MAAM,IAAI,CAAC,GACT,MAAM,IAAI,CAAC,OAAO,QAAQ;CACjD,MAAM,QAAQ,IAAI,SAAS,IAAI,GAAG,IAAI,MAAM,IAAI,QAAQ,IAAI,CAAC,GAAG;CAEhE,IAAI,MAAM,SAAS,KAAK,WAAW,QAAQ,SAAS,MAAM,GAAG,EAG3D,OAAO;EAAE,QAFM,MAAM;EAEJ,MADJ,MAAM,MAAM,MAAM,EAAE,CAAC,KAAK,IAAI,IACZ,OAAO;EAAO,WAAW;EAAM;CAGhE,OAAO;EAAE,QAAQ;EAAe;EAAK,WAAW;EAAO;;;;;;AAOzD,SAAgB,+BACd,YACA,YACe;CACf,IAAI,CAAC,YAAY,OAAO;CAExB,MAAM,QAAQ,WACX,MAAM,IAAI,CACV,KAAK,SAAS;EACb,MAAM,CAAC,MAAM,SAAS,KAAK,MAAM,CAAC,MAAM,IAAI;EAC5C,MAAM,IAAI,QAAQ,WAAW,MAAM,QAAQ,MAAM,GAAG,CAAC,GAAG;EACxD,OAAO;GAAE,MAAM,KAAK,MAAM,CAAC,aAAa;GAAE;GAAG;GAC7C,CACD,MAAM,GAAG,MAAM,EAAE,IAAI,EAAE,EAAE;CAE5B,KAAK,MAAM,EAAE,UAAU,OAAO;EAC5B,MAAM,aAAa,WAAW,QAAQ,MAAM,WAAW,OAAO,aAAa,KAAK,KAAK;EACrF,IAAI,YAAY,OAAO;EAEvB,MAAM,SAAS,KAAK,MAAM,IAAI,CAAC;EAC/B,MAAM,cAAc,WAAW,QAAQ,MAAM,WAAW;GACtD,MAAM,UAAU,OAAO,aAAa;GACpC,OAAO,YAAY,UAAU,QAAQ,WAAW,SAAS,IAAI;IAC7D;EACF,IAAI,aAAa,OAAO;;CAG1B,OAAO;;;;;;AAOT,SAAgB,4BACd,cACA,YACe;CACf,IAAI,CAAC,cAAc,OAAO;CAE1B,MAAM,QAAQ,aAAa,MAAM,gCAAgC;CACjE,IAAI,CAAC,OAAO,OAAO;CAEnB,IAAI;CACJ,IAAI;EACF,QAAQ,mBAAmB,MAAM,GAAG,MAAM,CAAC;SACrC;EACN,OAAO;;CAGT,IAAI,WAAW,QAAQ,SAAS,MAAM,EAAE,OAAO;CAC/C,OAAO;;AAGT,SAAS,wBACP,QACA,eACA,WAAW,IACX,gBAAgB,OAChB,SAAS,IACW;CACpB,IAAI,OAAO,aAAa,KAAK,cAAc,aAAa,EAAE,OAAO,KAAA;CAEjE,OAAO,GAAG,GADU,SAAS,GAAG,SAAS,gBAAgB,MAAM,KAC5C,QAAQ,WAAW,IAAI,GAAG;;AAG/C,SAAgB,kBAAkB,EAChC,SACA,YACA,YACA,aAC4C;CAC5C,MAAM,OAAO,WAAW;CAIxB,IAAI,CAAC,QAAQ,KAAK,oBAAoB,SAAS,UAAU,aAAa,KAAK,OAAO,KAAA;CAElF,MAAM,eAAe,mBAAmB,KAAK,SAAS,UAAU,YAAY,KAAA,EAAU;CACtF,MAAM,gBAAgB,cAAc,iBAAiB,KAAK;CAC1D,MAAM,kBACJ,+BAA+B,WAAW,SAAS,kBAAkB,EAAE,KAAK,IAAI,KAAA;CAClF,MAAM,iBACJ,cACA,cAAc,kBACb,4BAA4B,WAAW,SAAS,SAAS,EAAE,KAAK,IAAI,KAAA,MACrE,mBACA,KAAK;CACP,MAAM,SAAS,UAAU,UAAU;CAEnC,MAAM,kBAAkB,mBAAmB,KAAK,SAAS,KAAA,GAAW,gBAAgB;CACpF,IAAI,gBAAgB,iBAAiB;EACnC,MAAM,aACJ,kBAAkB,aAAa,OAAO,KAAK,kBAAkB,gBAAgB,OAAO;EACtF,MAAM,aACJ,oBAAoB,KAAA,KACpB,gBAAgB,cAAc,aAAa,KAAK,gBAAgB,aAAa;EAE/E,IAAI,CAAC,cAAc,CAAC,YAAY;GAI9B,MAAM,SAAS,OAAO,gBAAgB,OAAO,KAAK;GAClD,MAAM,aAAa,cAAc,oBAAoB,KAAA,IAAY,KAAK,IAAI;GAE1E,MAAM,WAAW,GADA,WAAW,YAAY,KACT,aAAa,WAAW,gBAAgB,MAAM,QAAQ;GACrF,MAAM,iBAAiB,SAAS,WAAW,IAAI,GAAG,WAAW,IAAI;GACjE,OAAO,GAAG,OAAO,KAAK,gBAAgB,SAAS,iBAAiB;;;CAIpE,OAAO,wBACL,gBACA,eACA,WAAW,UACX,WAAW,eACX,OACD;;AAGH,SAAgB,wBACd,KACA,YACA,SACA,UACA,WAAW,IACX,gBAAgB,OACM;CACtB,MAAM,eAAe,mBAAmB,WAAW,SAAS,YAAY,KAAA,EAAU;CAElF,MAAM,aAAa,qBAAqB,KAAK,YADvB,cAAc,iBAAiB,WAAW,cACO;CAEvE,IAAI;CACJ,IAAI,CAAC,WAAW,WACd,cAAc,kBAAkB;EAC9B;EACA,YAAY;GACV;GACA,MAAM;GACN;GACD;EACD,WAAW;GACT;GACA,UAAU,WAAW,IAAI,MAAM,IAAI,CAAC,MAAM;GAC1C,QAAQ,WAAW,IAAI,SAAS,IAAI,GAChC,WAAW,IAAI,MAAM,WAAW,IAAI,QAAQ,IAAI,CAAC,GACjD;GACL;EACF,CAAC;CAGJ,OAAO;EACL,GAAG;EACH;EACA;EACD"}
|