@wular/pnext 0.0.3 → 0.0.6
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +11 -11
- package/bin/pnext +1 -1
- package/config/lint/base.js +7 -7
- package/config/ts/base.json +2 -4
- package/config/ts/react.json +2 -6
- package/package.json +23 -2
- package/reference/compat.md +1 -1
- package/reference/config.md +2 -2
- package/reference/css.md +13 -9
- package/reference/dev.md +1 -1
- package/reference/metadata.md +7 -7
- package/reference/navigation.md +35 -28
- package/reference/performance.md +70 -70
- package/reference/rendering.md +17 -17
- package/reference/routing.md +17 -17
- package/reference/typegen.md +12 -8
- package/src/api/cache.ts +36 -37
- package/src/api/client-cache.ts +2 -2
- package/src/api/client-navigation.ts +113 -109
- package/src/api/dynamic.tsx +58 -55
- package/src/api/link.tsx +49 -52
- package/src/api/navigation.ts +59 -58
- package/src/api/server.ts +152 -144
- package/src/api/suspense.ts +4 -4
- package/src/cli/adapters/vercel-warm.ts +126 -121
- package/src/cli/adapters/vercel.ts +437 -443
- package/src/cli/analyze.ts +333 -144
- package/src/cli/{named-bin.ts → boot/named-bin.ts} +37 -37
- package/src/cli/{boot-trace.ts → boot/trace.ts} +10 -10
- package/src/cli/build.ts +976 -978
- package/src/cli/create.ts +71 -63
- package/src/cli/dev.ts +116 -116
- package/src/cli/index.ts +88 -85
- package/src/cli/migrate/package-json.ts +73 -71
- package/src/cli/migrate/report.ts +32 -33
- package/src/cli/migrate/{index.ts → run.ts} +48 -49
- package/src/cli/migrate/scan.ts +46 -46
- package/src/cli/migrate/spinner.ts +9 -9
- package/src/cli/migrate/tsconfig.ts +35 -35
- package/src/cli/{server-entry.ts → serve/entry.ts} +57 -57
- package/src/cli/{request-pipeline.ts → serve/pipeline.ts} +386 -394
- package/src/cli/{serve-ui.ts → serve/ui.ts} +47 -47
- package/src/cli/start.ts +65 -66
- package/src/{typegen.ts → cli/typegen.ts} +59 -59
- package/src/client/build.ts +794 -768
- package/src/client/chunk-fold.ts +245 -240
- package/src/client/{paths.ts → chunk-name.ts} +6 -6
- package/src/client/entry.ts +162 -147
- package/src/client/prebuilt.ts +231 -223
- package/src/client/profile.ts +29 -29
- package/src/client/react-compiler.ts +20 -15
- package/src/client/{compat-surface.ts → react-tier.ts} +64 -64
- package/src/client/reference-stub.ts +51 -51
- package/src/client/reference.ts +19 -19
- package/src/{api → client}/router/events.ts +17 -17
- package/src/{api → client}/router/history.ts +16 -16
- package/src/{api → client}/router/hub.ts +52 -53
- package/src/{api/router.ts → client/router/index.ts} +61 -60
- package/src/{api → client}/router/policies.ts +24 -24
- package/src/{api → client}/router/runtime.ts +1988 -1961
- package/src/{api → client}/router/types.ts +93 -98
- package/src/compat/actions/client-plugin.ts +42 -43
- package/src/compat/actions/client-stub.ts +14 -14
- package/src/compat/actions/{action-client.ts → client.ts} +194 -193
- package/src/compat/actions/config.ts +63 -64
- package/src/compat/actions/detect.ts +68 -68
- package/src/compat/actions/discovery.ts +105 -105
- package/src/compat/actions/{action-dispatch.ts → dispatch.ts} +277 -274
- package/src/compat/actions/early-submit.ts +1 -1
- package/src/compat/actions/endpoint.ts +198 -198
- package/src/compat/actions/extensions.ts +811 -0
- package/src/compat/actions/flight.ts +23 -23
- package/src/compat/actions/form-state.ts +34 -34
- package/src/compat/actions/hoist.ts +382 -236
- package/src/compat/actions/ids.ts +7 -12
- package/src/compat/actions/index.ts +8 -8
- package/src/compat/actions/instances.ts +46 -46
- package/src/compat/actions/origin.ts +47 -48
- package/src/compat/actions/protocol.ts +22 -22
- package/src/compat/actions/registry.ts +19 -19
- package/src/compat/{misc/action-return.ts → actions/return.ts} +60 -57
- package/src/compat/actions/rewrite.ts +125 -125
- package/src/compat/actions/{action-router.ts → router.ts} +10 -10
- package/src/compat/actions/serve.ts +132 -136
- package/src/compat/actions/server-tag.ts +4 -4
- package/src/compat/actions/{action-shared.ts → shared.ts} +34 -34
- package/src/compat/actions/unrecognized-error.ts +4 -4
- package/src/compat/{index.ts → aliases.ts} +112 -109
- package/src/compat/bundler/bun-externals.ts +18 -18
- package/src/compat/bundler/cjs-exports.ts +271 -223
- package/src/compat/bundler/config.ts +116 -111
- package/src/compat/bundler/externals.ts +12 -12
- package/src/compat/bundler/import-meta-url.ts +19 -19
- package/src/compat/bundler/modularize-imports.ts +36 -33
- package/src/compat/bundler/new-url-asset.ts +22 -24
- package/src/compat/bundler/optimize-package-imports.ts +111 -107
- package/src/compat/bundler/polyfill.ts +28 -28
- package/src/compat/bundler/react-compiler.ts +35 -29
- package/src/compat/bundler/react-profiler.tsx +11 -11
- package/src/compat/bundler/relay-transform.ts +48 -47
- package/src/compat/bundler/require-context.ts +119 -113
- package/src/compat/bundler/resolve-extensions.ts +19 -19
- package/src/compat/bundler/source-cache.ts +25 -25
- package/src/compat/bundler/static-imports.ts +7 -7
- package/src/compat/bundler/symlink-imports.ts +46 -46
- package/src/compat/bundler/tsconfig-paths.ts +13 -15
- package/src/compat/bundler/wasm.ts +58 -60
- package/src/compat/bundler/webpack-loaders.ts +254 -241
- package/src/compat/bundler/worker.ts +101 -104
- package/src/compat/cache/build-flags.ts +29 -29
- package/src/compat/cache/build-prerender-errors.ts +40 -44
- package/src/compat/cache/custom-handler.ts +60 -53
- package/src/compat/cache/fetch-patch.ts +240 -240
- package/src/compat/cache/handler.ts +27 -31
- package/src/compat/cache/modern-handler.ts +148 -126
- package/src/compat/cache/resume-data-cache.ts +47 -45
- package/src/compat/cache/revalidate.ts +233 -232
- package/src/compat/cache/runtime-error.ts +35 -35
- package/src/compat/cache/use-cache-transform.ts +442 -417
- package/src/compat/cache/use-cache.ts +650 -614
- package/src/compat/cache-control.ts +140 -142
- package/src/compat/client/base-path.ts +21 -20
- package/src/compat/client/css-order.ts +18 -18
- package/src/compat/client/errors/bare-boundary.ts +11 -11
- package/src/compat/client/errors/control-flow.ts +23 -23
- package/src/compat/client/errors/error-boundary.ts +61 -61
- package/src/compat/client/errors/global-error.ts +101 -91
- package/src/compat/client/errors/install.ts +71 -72
- package/src/compat/client/errors/lazy.ts +23 -23
- package/src/compat/client/errors/primitive-throw.ts +47 -45
- package/src/compat/client/errors/soft-refresh.ts +4 -4
- package/src/compat/client/link-status.ts +33 -33
- package/src/compat/client/{nav-compat-runtime.ts → nav-runtime.ts} +20 -20
- package/src/compat/client/{nav-compat.ts → nav.ts} +12 -13
- package/src/compat/client/navigation-scroll.ts +63 -63
- package/src/compat/client/optimistic-routing.ts +93 -88
- package/src/compat/client/prefetch-cache.ts +23 -24
- package/src/compat/client/route-announcer.ts +35 -35
- package/src/compat/client/segment-cache-policy.ts +20 -20
- package/src/compat/client/segment-cache.ts +309 -315
- package/src/compat/client/segment-prefetch.ts +127 -132
- package/src/compat/client/trailing-slash.ts +5 -4
- package/src/compat/css/chunking.ts +113 -116
- package/src/compat/css/inline-css.ts +21 -21
- package/src/compat/css/lightningcss.ts +37 -38
- package/src/compat/css/modules.ts +161 -175
- package/src/compat/css/nonce.ts +7 -7
- package/src/compat/css/sass-plugin.ts +18 -21
- package/src/compat/css/sass.ts +150 -152
- package/src/compat/css/styled-jsx-runtime.ts +27 -27
- package/src/compat/css/styled-jsx.ts +21 -21
- package/src/compat/edge-runtime.ts +27 -27
- package/src/compat/{adapter → export}/build-complete.ts +72 -75
- package/src/compat/export/client.ts +29 -31
- package/src/compat/export/{index.ts → emit.ts} +111 -110
- package/src/compat/export/standalone.ts +62 -54
- package/src/compat/image-optimizer/cache.ts +51 -49
- package/src/compat/image-optimizer/detect.ts +52 -52
- package/src/compat/image-optimizer/{index.ts → optimize.ts} +189 -194
- package/src/compat/image-optimizer/source.ts +77 -80
- package/src/compat/lifecycle/after-scope.ts +26 -26
- package/src/compat/lifecycle/after.ts +48 -45
- package/src/compat/lifecycle/error-funnel.ts +98 -102
- package/src/compat/lifecycle/error-serialize.ts +30 -26
- package/src/compat/lifecycle/error-ui.ts +67 -32
- package/src/compat/lifecycle/instrumentation-client.ts +36 -38
- package/src/compat/lifecycle/instrumentation.ts +85 -85
- package/src/compat/lifecycle/node-console.ts +11 -11
- package/src/compat/lifecycle/testmode.ts +160 -132
- package/src/compat/mdx/compile.ts +60 -57
- package/src/compat/mdx/plugin.ts +11 -11
- package/src/compat/mdx/{next-mdx-stub.ts → stub.ts} +5 -5
- package/src/compat/{metadata-route-artifacts.ts → metadata-artifacts.ts} +195 -191
- package/src/compat/metadata.ts +75 -81
- package/src/compat/next/cache.ts +68 -69
- package/src/compat/next/canonical-url.ts +9 -9
- package/src/compat/next/client-cache.ts +19 -19
- package/src/compat/next/client-navigation.ts +112 -116
- package/src/compat/next/client-only.ts +1 -1
- package/src/compat/next/client-script.tsx +99 -93
- package/src/compat/next/client-server.ts +10 -10
- package/src/compat/next/config-loader.ts +176 -173
- package/src/compat/next/config.ts +7 -7
- package/src/compat/next/constants.cjs +6 -6
- package/src/compat/next/constants.ts +6 -6
- package/src/compat/next/custom-server.ts +26 -24
- package/src/compat/next/dist/client/components/app-router-headers.ts +21 -21
- package/src/compat/next/dist/server/app-render/work-unit-async-storage.external.cjs +3 -4
- package/src/compat/next/dist/server/web/spec-extension/revalidate.ts +1 -1
- package/src/compat/next/dist/server/web/spec-extension/unstable-cache.ts +1 -1
- package/src/compat/next/dist/server/web/spec-extension/unstable-no-store.ts +1 -1
- package/src/compat/next/dynamic.tsx +21 -18
- package/src/compat/next/error.tsx +52 -52
- package/src/compat/next/font/cache.ts +74 -74
- package/src/compat/next/font/google.ts +2 -2
- package/src/compat/next/font/index.ts +1 -1
- package/src/compat/next/font/local.ts +3 -3
- package/src/compat/next/font/runtime-client.ts +17 -15
- package/src/compat/next/font/runtime.ts +443 -408
- package/src/compat/next/font/shared.ts +120 -108
- package/src/compat/next/form.tsx +63 -63
- package/src/compat/next/head.tsx +2 -2
- package/src/compat/next/headers.ts +103 -95
- package/src/compat/next/image/client.tsx +220 -0
- package/src/compat/next/image/config.ts +56 -58
- package/src/compat/next/image/optimizer.ts +40 -36
- package/src/compat/next/image/patterns.ts +37 -40
- package/src/compat/next/{image-props.ts → image/props.ts} +208 -202
- package/src/compat/next/image/shared.ts +65 -57
- package/src/compat/next/image/static-metadata.ts +98 -107
- package/src/compat/next/image/validate.ts +79 -88
- package/src/compat/next/image.tsx +19 -23
- package/src/compat/next/index.ts +1 -1
- package/src/compat/next/legacy-image.tsx +59 -60
- package/src/compat/next/{link-validation-transform.ts → link-transform.ts} +95 -96
- package/src/compat/next/link.tsx +158 -158
- package/src/compat/next/navigation.cjs +12 -3
- package/src/compat/next/navigation.ts +48 -50
- package/src/compat/next/offline.ts +27 -27
- package/src/compat/next/og.ts +121 -124
- package/src/compat/next/preferred-region.ts +13 -14
- package/src/compat/next/redirects.ts +58 -56
- package/src/compat/next/resource-hints.ts +73 -76
- package/src/compat/next/rewrites.ts +130 -133
- package/src/compat/next/root-params.ts +45 -45
- package/src/compat/next/{optimistic-route-state.ts → route-state.ts} +52 -52
- package/src/compat/next/router.cjs +4 -2
- package/src/compat/next/router.ts +58 -61
- package/src/compat/next/script.tsx +108 -108
- package/src/compat/next/server-only.ts +1 -1
- package/src/compat/next/server.ts +19 -19
- package/src/compat/next/svgr.ts +18 -17
- package/src/compat/next/telemetry.ts +24 -24
- package/src/compat/next/{image-usage.ts → usage.ts} +70 -39
- package/src/compat/next/user-agent.ts +53 -49
- package/src/compat/next/web-vitals.ts +22 -24
- package/src/compat/otel/api.ts +41 -41
- package/src/compat/otel/client-trace-metadata.ts +25 -27
- package/src/compat/otel/fetch-span.ts +29 -29
- package/src/compat/otel/tracer.ts +331 -331
- package/src/compat/pages/api.ts +456 -0
- package/src/compat/pages/client-plugin.ts +36 -36
- package/src/compat/pages/router-state.ts +34 -34
- package/src/compat/pages/{index.ts → router.ts} +130 -135
- package/src/compat/ppr/io.ts +12 -12
- package/src/compat/ppr/missing-root-params.ts +34 -38
- package/src/compat/ppr/root-params-scan.ts +66 -66
- package/src/compat/ppr/root-params-transform.ts +24 -26
- package/src/compat/ppr/root-params.ts +30 -30
- package/src/compat/ppr/segment-config-incompat.ts +6 -7
- package/src/compat/protocol.ts +71 -70
- package/src/compat/react/action-state.ts +47 -48
- package/src/compat/react/client-lite.ts +15 -15
- package/src/compat/react/client.ts +4 -4
- package/src/compat/react/compiler-runtime.ts +11 -11
- package/src/compat/react/dom-client.ts +44 -44
- package/src/compat/react/dom-react-server.ts +10 -16
- package/src/compat/react/dom-server.ts +10 -10
- package/src/compat/react/dom.ts +52 -52
- package/src/compat/react/hooks-extra.ts +34 -35
- package/src/compat/react/parity.ts +64 -61
- package/src/compat/react/preact.ts +81 -82
- package/src/compat/react/react-server.ts +28 -28
- package/src/compat/react/router-shim.ts +1 -1
- package/src/compat/react/server-component-use.ts +8 -8
- package/src/compat/react/server-inserted-html.ts +30 -31
- package/src/compat/react/server.ts +53 -55
- package/src/compat/react/use.ts +32 -32
- package/src/compat/react/view-transition.ts +20 -20
- package/src/compat/register/actions.ts +35 -824
- package/src/compat/register/boot.ts +41 -41
- package/src/compat/register/build-tier.ts +5 -5
- package/src/compat/register/build.ts +74 -70
- package/src/compat/register/bundler.ts +161 -156
- package/src/compat/register/cache.ts +20 -20
- package/src/compat/register/client-errors.ts +3 -3
- package/src/compat/register/config.ts +6 -6
- package/src/compat/register/css-extras.ts +30 -34
- package/src/compat/register/edge-runtime.ts +3 -3
- package/src/compat/register/errors.ts +10 -12
- package/src/compat/register/export.ts +16 -16
- package/src/compat/register/font.ts +11 -11
- package/src/compat/register/hooks.ts +2 -2
- package/src/compat/register/image.ts +40 -40
- package/src/compat/register/index.ts +59 -62
- package/src/compat/register/instrumentation-client.ts +10 -10
- package/src/compat/register/lifecycle.ts +25 -28
- package/src/compat/register/mdx.ts +10 -10
- package/src/compat/register/middleware.ts +226 -16
- package/src/compat/register/otel.ts +80 -88
- package/src/compat/register/pages-api.ts +10 -463
- package/src/compat/register/ppr.ts +16 -16
- package/src/compat/register/protocol.ts +17 -18
- package/src/compat/register/proxy.ts +49 -51
- package/src/compat/register/render.ts +79 -76
- package/src/compat/register/routing.ts +162 -159
- package/src/compat/register/segment.ts +24 -1887
- package/src/compat/register/static-image.ts +3 -3
- package/src/compat/register/{misc.ts → taint.ts} +9 -9
- package/src/compat/register/typed-routes.ts +14 -14
- package/src/compat/register/{usecache.ts → use-cache.ts} +39 -39
- package/src/compat/register/validation.ts +18 -18
- package/src/compat/segment/loading-boundary.ts +43 -45
- package/src/compat/segment/page-slot.ts +69 -69
- package/src/compat/segment/serve.ts +1884 -0
- package/src/compat/segment/tree.ts +113 -112
- package/src/compat/segment/vary-key.ts +38 -38
- package/src/compat/segment/vary-params.ts +138 -142
- package/src/compat/static-params.ts +14 -12
- package/src/compat/tsconfig-defaults.ts +87 -91
- package/src/compat/typecheck/{index.ts → check.ts} +234 -212
- package/src/compat/typecheck/worker.ts +15 -12
- package/src/compat/typed-routes/{index.ts → generate.ts} +36 -36
- package/src/compat/typed-routes/manifest.ts +174 -170
- package/src/compat/typed-routes/typegen.ts +127 -110
- package/src/compat/validation/errors.ts +16 -19
- package/src/compat/validation/prerender-diagnostics.ts +521 -504
- package/src/compat/validation/{index.ts → validate.ts} +647 -648
- package/src/compat-bootstrap.ts +16 -16
- package/src/config.ts +69 -69
- package/src/css/build.ts +230 -227
- package/src/css/postcss.ts +79 -80
- package/src/css/worker.ts +14 -15
- package/src/dev/client-actions.ts +10 -10
- package/src/dev/client-chunk-store.ts +27 -27
- package/src/dev/{client-key-cache.ts → restart/client-key.ts} +76 -76
- package/src/dev/{restart-cache.ts → restart/enabled.ts} +1 -1
- package/src/dev/{global-css-cache.ts → restart/global-css.ts} +91 -83
- package/src/dev/{node-module-bundle-cache.ts → restart/node-modules.ts} +24 -24
- package/src/dev/{route-bundle-key-cache.ts → restart/route-bundle-key.ts} +53 -53
- package/src/dev/{route-facts-cache.ts → restart/route-facts.ts} +82 -82
- package/src/dev/server.ts +804 -820
- package/src/env.ts +46 -43
- package/src/extensions.ts +491 -478
- package/src/index.ts +8 -8
- package/src/internal.ts +20 -23
- package/src/{islands → render}/boundary-error.ts +3 -3
- package/src/render/hooks.ts +71 -71
- package/src/render/island-context.ts +14 -14
- package/src/render/metadata.ts +310 -310
- package/src/{ppr-postpone.ts → render/postpone.ts} +5 -5
- package/src/{ppr.ts → render/ppr.ts} +244 -245
- package/src/render/renderer.ts +2076 -2082
- package/src/render/resource-hints.ts +16 -17
- package/src/render/slots.tsx +224 -235
- package/src/{islands → render}/static-children.ts +9 -12
- package/src/{islands → render}/static-slots.ts +37 -37
- package/src/{cache/context.ts → request/cache.ts} +20 -20
- package/src/request/context.ts +107 -107
- package/src/{dynamic/source.ts → resolve/dynamic.ts} +152 -143
- package/src/resolve/engine.ts +90 -77
- package/src/resolve/imports.ts +475 -463
- package/src/resolve/scan-facts.ts +442 -186
- package/src/resolve/source-text.ts +37 -37
- package/src/{dynamic → resolve}/tree-shake.ts +132 -128
- package/src/routing/forwarded.ts +19 -19
- package/src/routing/handler.ts +84 -91
- package/src/routing/href.ts +69 -70
- package/src/routing/{metadata.ts → metadata-files.ts} +403 -401
- package/src/{proxy.ts → routing/proxy.ts} +306 -312
- package/src/routing/{request-runtime.ts → request-environment.ts} +10 -10
- package/src/routing/routes.ts +833 -848
- package/src/routing/slots.ts +164 -160
- package/src/runtime/loader.ts +952 -0
- package/src/{dev → runtime}/module-cache.ts +309 -287
- package/src/{dev → runtime}/module-generations.ts +9 -9
- package/src/{dev → runtime}/module-transform.ts +81 -72
- package/src/{dev/imports.ts → runtime/modules.ts} +934 -847
- package/src/runtime/{server.ts → vendor-build.ts} +848 -1696
- package/src/runtime/vendor.ts +425 -404
- package/src/styles.d.ts +9 -0
- package/src/types.ts +320 -335
- package/src/utils/ansi.ts +5 -5
- package/src/utils/{source.ts → code.ts} +15 -12
- package/src/utils/content-type.ts +3 -3
- package/src/utils/decode.ts +2 -2
- package/src/utils/dev-profile.ts +13 -13
- package/src/utils/error-log.ts +6 -6
- package/src/utils/esbuild.ts +18 -18
- package/src/utils/fs-cache.ts +13 -13
- package/src/utils/fs.ts +57 -49
- package/src/utils/html.ts +20 -24
- package/src/utils/native-require.ts +8 -8
- package/src/utils/serialize.ts +139 -146
- package/src/utils/verbose.ts +18 -18
- package/src/cli/analyze-print.ts +0 -181
- package/src/compat/middleware/manifest.ts +0 -210
- package/src/compat/next/image-client.tsx +0 -215
- package/src/compat/next/link-usage.ts +0 -29
- package/src/css/index.ts +0 -2
- package/src/render/index.ts +0 -1
- package/src/style-modules.d.ts +0 -9
|
@@ -1,14 +1,14 @@
|
|
|
1
|
-
import { existsSync, readFileSync, statSync } from 'node:fs'
|
|
2
|
-
import path from 'node:path'
|
|
1
|
+
import { existsSync, readFileSync, statSync } from 'node:fs'
|
|
2
|
+
import path from 'node:path'
|
|
3
3
|
// Lazy: the oxc-parser native binding costs ~12.6 MB RSS; load it only when a parse happens.
|
|
4
4
|
const parseSync: typeof import('oxc-parser').parseSync = (...args) =>
|
|
5
5
|
// eslint-disable-next-line @typescript-eslint/no-require-imports
|
|
6
|
-
loadNative(() => require('oxc-parser') as typeof import('oxc-parser')).parseSync(...args)
|
|
7
|
-
import { loadNative } from '
|
|
8
|
-
import { registerServerRuntime } from '
|
|
9
|
-
import { pathToFileHref, type ResolvedConfig } from '
|
|
10
|
-
import { devServerModuleHref } from '
|
|
11
|
-
import { runWithRequest } from '
|
|
6
|
+
loadNative(() => require('oxc-parser') as typeof import('oxc-parser')).parseSync(...args)
|
|
7
|
+
import { loadNative } from '../utils/native-require'
|
|
8
|
+
import { registerServerRuntime } from '../runtime/loader'
|
|
9
|
+
import { pathToFileHref, type ResolvedConfig } from '../config'
|
|
10
|
+
import { devServerModuleHref } from '../runtime/modules'
|
|
11
|
+
import { runWithRequest } from '../request/context'
|
|
12
12
|
import {
|
|
13
13
|
copyMiddlewareHeaders,
|
|
14
14
|
forwardMiddlewareRequestHeaders,
|
|
@@ -18,107 +18,106 @@ import {
|
|
|
18
18
|
parseCookies,
|
|
19
19
|
requestCookieHeader,
|
|
20
20
|
toNextRequest,
|
|
21
|
-
} from '
|
|
22
|
-
import type { ExternalLoadTarget } from '
|
|
23
|
-
import { getProxyExtensions, withRouteRuntime } from '
|
|
24
|
-
import { canonicalTrailingSlashPath } from './
|
|
21
|
+
} from '../api/server'
|
|
22
|
+
import type { ExternalLoadTarget } from '../resolve/imports'
|
|
23
|
+
import { getProxyExtensions, withRouteRuntime } from '../extensions'
|
|
24
|
+
import { canonicalTrailingSlashPath } from './href'
|
|
25
25
|
|
|
26
26
|
export interface ProxyModule {
|
|
27
|
-
default?: ProxyHandler
|
|
28
|
-
proxy?: ProxyHandler
|
|
29
|
-
config?: ProxyConfig
|
|
27
|
+
default?: ProxyHandler
|
|
28
|
+
proxy?: ProxyHandler
|
|
29
|
+
config?: ProxyConfig
|
|
30
30
|
}
|
|
31
31
|
|
|
32
32
|
export type ProxyHandler = (
|
|
33
|
-
request: import('
|
|
34
|
-
event: import('
|
|
35
|
-
) => Response | undefined | void | Promise<Response | undefined | void
|
|
33
|
+
request: import('../types').NextRequest,
|
|
34
|
+
event: import('../types').NextFetchEvent,
|
|
35
|
+
) => Response | undefined | void | Promise<Response | undefined | void>
|
|
36
36
|
|
|
37
37
|
export interface ProxyConfig {
|
|
38
|
-
matcher?: ProxyMatcher | ProxyMatcher[]
|
|
38
|
+
matcher?: ProxyMatcher | ProxyMatcher[]
|
|
39
39
|
}
|
|
40
40
|
|
|
41
41
|
export interface ProxyRouteCondition {
|
|
42
|
-
type: 'header' | 'query' | 'cookie' | 'host'
|
|
43
|
-
key?: string
|
|
44
|
-
value?: string
|
|
42
|
+
type: 'header' | 'query' | 'cookie' | 'host'
|
|
43
|
+
key?: string
|
|
44
|
+
value?: string
|
|
45
45
|
}
|
|
46
46
|
|
|
47
47
|
type ProxyMatcher =
|
|
48
|
-
| string
|
|
49
|
-
| { source: string; has?: ProxyRouteCondition[]; missing?: ProxyRouteCondition[] };
|
|
48
|
+
string | { source: string; has?: ProxyRouteCondition[]; missing?: ProxyRouteCondition[] }
|
|
50
49
|
|
|
51
50
|
interface CompiledMatcher {
|
|
52
|
-
regex: RegExp
|
|
53
|
-
has?: ProxyRouteCondition[]
|
|
54
|
-
missing?: ProxyRouteCondition[]
|
|
51
|
+
regex: RegExp
|
|
52
|
+
has?: ProxyRouteCondition[]
|
|
53
|
+
missing?: ProxyRouteCondition[]
|
|
55
54
|
}
|
|
56
55
|
|
|
57
56
|
export interface ProxyRunOptions {
|
|
58
|
-
dev?: boolean
|
|
59
|
-
devImportVersion?: string
|
|
57
|
+
dev?: boolean
|
|
58
|
+
devImportVersion?: string
|
|
60
59
|
}
|
|
61
60
|
|
|
62
|
-
export type ProxyRewriteObserver = (from: URL, to: URL) => void
|
|
61
|
+
export type ProxyRewriteObserver = (from: URL, to: URL) => void
|
|
63
62
|
|
|
64
|
-
let proxyRewriteObserver: ProxyRewriteObserver | undefined
|
|
63
|
+
let proxyRewriteObserver: ProxyRewriteObserver | undefined
|
|
65
64
|
|
|
66
65
|
export function setProxyRewriteObserver(observer: ProxyRewriteObserver | undefined): void {
|
|
67
|
-
proxyRewriteObserver = observer
|
|
66
|
+
proxyRewriteObserver = observer
|
|
68
67
|
}
|
|
69
68
|
|
|
70
|
-
export type ProxyRunObserver = <T>(request: Request, fn: () => T) => T
|
|
69
|
+
export type ProxyRunObserver = <T>(request: Request, fn: () => T) => T
|
|
71
70
|
|
|
72
|
-
let proxyRunObserver: ProxyRunObserver | undefined
|
|
71
|
+
let proxyRunObserver: ProxyRunObserver | undefined
|
|
73
72
|
|
|
74
73
|
export function setProxyRunObserver(observer: ProxyRunObserver | undefined): void {
|
|
75
|
-
proxyRunObserver = observer
|
|
74
|
+
proxyRunObserver = observer
|
|
76
75
|
}
|
|
77
76
|
|
|
78
77
|
function wrapMiddleware<T>(request: Request, fn: () => T): T {
|
|
79
|
-
return proxyRunObserver ? proxyRunObserver(request, fn) : fn()
|
|
78
|
+
return proxyRunObserver ? proxyRunObserver(request, fn) : fn()
|
|
80
79
|
}
|
|
81
80
|
|
|
82
81
|
export interface ProxyNextResult {
|
|
83
|
-
request: Request
|
|
84
|
-
response?: Response
|
|
82
|
+
request: Request
|
|
83
|
+
response?: Response
|
|
85
84
|
}
|
|
86
85
|
|
|
87
86
|
export interface ProxyRunner {
|
|
88
|
-
(request: Request, options?: ProxyRunOptions): Promise<ProxyNextResult | Response | undefined
|
|
87
|
+
(request: Request, options?: ProxyRunOptions): Promise<ProxyNextResult | Response | undefined>
|
|
89
88
|
/**
|
|
90
89
|
* Start compiling and importing the proxy module now. The proxy runs serially in front of every
|
|
91
90
|
* request, so leaving its first import to the first request puts the whole compile on the critical
|
|
92
91
|
* path of the first page; dev calls this at boot so it overlaps route scanning and the warmup.
|
|
93
92
|
*/
|
|
94
|
-
warm(options?: ProxyRunOptions): void
|
|
93
|
+
warm(options?: ProxyRunOptions): void
|
|
95
94
|
}
|
|
96
95
|
|
|
97
|
-
const proxyExtensions = ['.ts', '.tsx', '.js', '.jsx', '.mts']
|
|
96
|
+
const proxyExtensions = ['.ts', '.tsx', '.js', '.jsx', '.mts']
|
|
98
97
|
|
|
99
98
|
export function findProxyFile(config: ResolvedConfig) {
|
|
100
|
-
return findProxyFiles(config)[0]
|
|
99
|
+
return findProxyFiles(config)[0]
|
|
101
100
|
}
|
|
102
101
|
|
|
103
102
|
export function proxyExternalLoadTarget(file: string): ExternalLoadTarget {
|
|
104
|
-
return path.basename(file).startsWith('proxy.') ? 'client-ssr' : 'edge'
|
|
103
|
+
return path.basename(file).startsWith('proxy.') ? 'client-ssr' : 'edge'
|
|
105
104
|
}
|
|
106
105
|
|
|
107
106
|
export function findProxyFiles(config: ResolvedConfig, names = getProxyExtensions().names) {
|
|
108
|
-
const files: string[] = []
|
|
107
|
+
const files: string[] = []
|
|
109
108
|
for (const base of proxyBases(config)) {
|
|
110
109
|
for (const name of names) {
|
|
111
110
|
for (const extension of proxyExtensions) {
|
|
112
|
-
const file = path.join(base, `${name}${extension}`)
|
|
113
|
-
if (existsSync(file)) files.push(file)
|
|
111
|
+
const file = path.join(base, `${name}${extension}`)
|
|
112
|
+
if (existsSync(file)) files.push(file)
|
|
114
113
|
}
|
|
115
114
|
}
|
|
116
115
|
}
|
|
117
|
-
return files
|
|
116
|
+
return files
|
|
118
117
|
}
|
|
119
118
|
|
|
120
119
|
export async function validateProxyFiles(config: ResolvedConfig) {
|
|
121
|
-
await getProxyExtensions().validateFiles(config)
|
|
120
|
+
await getProxyExtensions().validateFiles(config)
|
|
122
121
|
}
|
|
123
122
|
|
|
124
123
|
export async function runProxy(
|
|
@@ -126,12 +125,12 @@ export async function runProxy(
|
|
|
126
125
|
request: Request,
|
|
127
126
|
options: ProxyRunOptions = {},
|
|
128
127
|
): Promise<ProxyNextResult | Response | undefined> {
|
|
129
|
-
const url = new URL(request.url)
|
|
128
|
+
const url = new URL(request.url)
|
|
130
129
|
if (url.pathname.startsWith('/__pnext/') || url.pathname.startsWith('/assets/')) {
|
|
131
|
-
return undefined
|
|
130
|
+
return undefined
|
|
132
131
|
}
|
|
133
132
|
|
|
134
|
-
return createProxyRunner(config)(request, options)
|
|
133
|
+
return createProxyRunner(config)(request, options)
|
|
135
134
|
}
|
|
136
135
|
|
|
137
136
|
export interface ProxyRunnerOptions {
|
|
@@ -140,70 +139,69 @@ export interface ProxyRunnerOptions {
|
|
|
140
139
|
* cannot resolve bare compat specifiers (next/server etc.), so prod must
|
|
141
140
|
* load the build-time bundle that has aliases baked in.
|
|
142
141
|
*/
|
|
143
|
-
compiledModuleHref?: string
|
|
142
|
+
compiledModuleHref?: string
|
|
144
143
|
}
|
|
145
144
|
|
|
146
145
|
export function createProxyRunner(
|
|
147
146
|
config: ResolvedConfig,
|
|
148
147
|
runnerOptions: ProxyRunnerOptions = {},
|
|
149
148
|
): ProxyRunner {
|
|
150
|
-
const file = findProxyFile(config)
|
|
151
|
-
registerServerRuntime(config, file ? [file] : [])
|
|
152
|
-
let moduleKey: string | undefined
|
|
153
|
-
let modulePromise: Promise<ProxyModule> | undefined
|
|
154
|
-
let matchers: CompiledMatcher[] | undefined
|
|
149
|
+
const file = findProxyFile(config)
|
|
150
|
+
registerServerRuntime(config, file ? [file] : [])
|
|
151
|
+
let moduleKey: string | undefined
|
|
152
|
+
let modulePromise: Promise<ProxyModule> | undefined
|
|
153
|
+
let matchers: CompiledMatcher[] | undefined
|
|
155
154
|
|
|
156
155
|
/** The module for `options`, started at most once per import version. */
|
|
157
156
|
function proxyModule(options: ProxyRunOptions) {
|
|
158
|
-
const key = proxyModuleKey(options)
|
|
157
|
+
const key = proxyModuleKey(options)
|
|
159
158
|
if (key !== moduleKey) {
|
|
160
|
-
moduleKey = key
|
|
161
|
-
modulePromise = importProxyModule(config, file!, options, runnerOptions)
|
|
162
|
-
matchers = undefined
|
|
159
|
+
moduleKey = key
|
|
160
|
+
modulePromise = importProxyModule(config, file!, options, runnerOptions)
|
|
161
|
+
matchers = undefined
|
|
163
162
|
}
|
|
164
|
-
return (modulePromise ??= importProxyModule(config, file!, options, runnerOptions))
|
|
163
|
+
return (modulePromise ??= importProxyModule(config, file!, options, runnerOptions))
|
|
165
164
|
}
|
|
166
165
|
|
|
167
166
|
const runner: ProxyRunner = async (request, options = {}) => {
|
|
168
|
-
const url = new URL(request.url)
|
|
167
|
+
const url = new URL(request.url)
|
|
169
168
|
if (url.pathname.startsWith('/__pnext/') || url.pathname.startsWith('/assets/')) {
|
|
170
|
-
return undefined
|
|
169
|
+
return undefined
|
|
171
170
|
}
|
|
172
|
-
if (!file) return undefined
|
|
171
|
+
if (!file) return undefined
|
|
173
172
|
|
|
174
173
|
// Matcher first, straight off the source: a request no matcher selects must not wait on the
|
|
175
174
|
// proxy's compile+import, which is otherwise the first serial link of a cold page.
|
|
176
175
|
if (!matchers && proxyMatcherGateEnabled()) {
|
|
177
|
-
const staticMatchers = staticProxyMatchers(file)
|
|
178
|
-
if (staticMatchers && !matchesProxy(staticMatchers, url, request)) return undefined
|
|
176
|
+
const staticMatchers = staticProxyMatchers(file)
|
|
177
|
+
if (staticMatchers && !matchesProxy(staticMatchers, url, request)) return undefined
|
|
179
178
|
}
|
|
180
179
|
|
|
181
|
-
const module = await proxyModule(options)
|
|
182
|
-
matchers ??= proxyMatchers(module.config)
|
|
183
|
-
if (!matchesProxy(matchers, url, request)) return undefined
|
|
180
|
+
const module = await proxyModule(options)
|
|
181
|
+
matchers ??= proxyMatchers(module.config)
|
|
182
|
+
if (!matchesProxy(matchers, url, request)) return undefined
|
|
184
183
|
|
|
185
184
|
// A proxied request's body is buffered so both the proxy and the matched
|
|
186
185
|
// route can read it; buffering is capped at clientMaxBodySize (compat maps
|
|
187
186
|
// experimental.proxyClientMaxBodySize, default 10MB) — the excess is
|
|
188
187
|
// dropped with a warning, matching Next's router-server behavior.
|
|
189
|
-
request = await bufferProxyRequestBody(request, url.pathname)
|
|
188
|
+
request = await bufferProxyRequestBody(request, url.pathname)
|
|
190
189
|
|
|
191
|
-
const handlerExport = getProxyExtensions().handlerExport(module as Record<string, unknown>)
|
|
192
|
-
if (typeof handlerExport !== 'function')
|
|
193
|
-
|
|
194
|
-
const handler = handlerExport as ProxyHandler;
|
|
190
|
+
const handlerExport = getProxyExtensions().handlerExport(module as Record<string, unknown>)
|
|
191
|
+
if (typeof handlerExport !== 'function') throw new Error(`${file} must export proxy or default`)
|
|
192
|
+
const handler = handlerExport as ProxyHandler
|
|
195
193
|
|
|
196
194
|
// Next normally hides the RSC cache key and Flight headers from proxy;
|
|
197
195
|
// skipProxyUrlNormalize exposes the original request for advanced routing.
|
|
198
196
|
const handlerRequest = getProxyExtensions().skipUrlNormalize()
|
|
199
197
|
? request
|
|
200
|
-
: stripFlightHeaders(stripRscUnionQuery(request))
|
|
201
|
-
const nextRequest = toNextRequest(handlerRequest)
|
|
198
|
+
: stripFlightHeaders(stripRscUnionQuery(request))
|
|
199
|
+
const nextRequest = toNextRequest(handlerRequest)
|
|
202
200
|
// Response-headers channel so request-scoped APIs invoked inside the proxy
|
|
203
201
|
// (notably draftMode().enable()/disable() from next/headers) can emit
|
|
204
202
|
// Set-Cookie even when the handler returns NextResponse.next() — otherwise
|
|
205
203
|
// the cookie would be dropped since it is not written onto that response.
|
|
206
|
-
const proxyResponseHeaders = new Headers()
|
|
204
|
+
const proxyResponseHeaders = new Headers()
|
|
207
205
|
let response = await runWithRequest(
|
|
208
206
|
nextRequest,
|
|
209
207
|
() =>
|
|
@@ -211,39 +209,39 @@ export function createProxyRunner(
|
|
|
211
209
|
wrapMiddleware(nextRequest, () =>
|
|
212
210
|
handler(nextRequest, {
|
|
213
211
|
waitUntil(promise) {
|
|
214
|
-
void promise
|
|
212
|
+
void promise
|
|
215
213
|
},
|
|
216
214
|
}),
|
|
217
215
|
),
|
|
218
216
|
),
|
|
219
217
|
{},
|
|
220
218
|
{ responseHeaders: proxyResponseHeaders },
|
|
221
|
-
)
|
|
219
|
+
)
|
|
222
220
|
|
|
223
|
-
const scopedCookies = getSetCookie(proxyResponseHeaders)
|
|
221
|
+
const scopedCookies = getSetCookie(proxyResponseHeaders)
|
|
224
222
|
if (scopedCookies.length > 0) {
|
|
225
223
|
// No explicit return means an implicit NextResponse.next(): synthesize one
|
|
226
224
|
// so the emitted Set-Cookie survives while the request still continues.
|
|
227
|
-
if (!response) response = NextResponse.next()
|
|
228
|
-
for (const cookie of scopedCookies) response.headers.append('set-cookie', cookie)
|
|
225
|
+
if (!response) response = NextResponse.next()
|
|
226
|
+
for (const cookie of scopedCookies) response.headers.append('set-cookie', cookie)
|
|
229
227
|
}
|
|
230
228
|
|
|
231
|
-
if (!response) return { request }
|
|
229
|
+
if (!response) return { request }
|
|
232
230
|
|
|
233
231
|
// Same-origin middleware redirects follow the app's trailing-slash rule,
|
|
234
232
|
// exactly like a direct navigation would (Next normalizes the Location).
|
|
235
233
|
// skipTrailingSlashRedirect and cross-origin targets are left untouched.
|
|
236
|
-
response = normalizeRedirectLocation(response, url, config)
|
|
234
|
+
response = normalizeRedirectLocation(response, url, config)
|
|
237
235
|
|
|
238
|
-
const meta = nextResponseMeta(response)
|
|
239
|
-
if (!meta) return response
|
|
236
|
+
const meta = nextResponseMeta(response)
|
|
237
|
+
if (!meta) return response
|
|
240
238
|
const forwardedRequest = forwardMiddlewareRequestHeaders(
|
|
241
239
|
response,
|
|
242
240
|
applyProxyCookies(applyRequestOverride(request, meta.requestHeaders), response),
|
|
243
|
-
)
|
|
241
|
+
)
|
|
244
242
|
if (meta.kind === 'rewrite' && meta.url) {
|
|
245
|
-
let rewritten = rewriteRequest(forwardedRequest, meta.url)
|
|
246
|
-
const rewrittenUrl = new URL(rewritten.url)
|
|
243
|
+
let rewritten = rewriteRequest(forwardedRequest, meta.url)
|
|
244
|
+
const rewrittenUrl = new URL(rewritten.url)
|
|
247
245
|
// A rewrite to another origin is proxied: the response for THIS request
|
|
248
246
|
// comes from the external server (Next's router-server does the same for
|
|
249
247
|
// any middleware rewrite whose destination carries a protocol).
|
|
@@ -251,51 +249,51 @@ export function createProxyRunner(
|
|
|
251
249
|
// An RSC fetch's `_rsc` cache-buster must ride along: the external
|
|
252
250
|
// server cannot recover it from the original URL, and the router keys
|
|
253
251
|
// its response on it. Only added when the destination lacks its own.
|
|
254
|
-
const rscHash = url.searchParams.get('_rsc')
|
|
252
|
+
const rscHash = url.searchParams.get('_rsc')
|
|
255
253
|
if (rscHash !== null && !rewrittenUrl.searchParams.has('_rsc')) {
|
|
256
|
-
rewrittenUrl.searchParams.set('_rsc', rscHash)
|
|
257
|
-
rewritten = new Request(rewrittenUrl, rewritten)
|
|
254
|
+
rewrittenUrl.searchParams.set('_rsc', rscHash)
|
|
255
|
+
rewritten = new Request(rewrittenUrl, rewritten)
|
|
258
256
|
}
|
|
259
|
-
getProxyExtensions().onExternalRewrite(rewritten)
|
|
257
|
+
getProxyExtensions().onExternalRewrite(rewritten)
|
|
260
258
|
// The forwarded request still carries the ORIGINAL Host header (the
|
|
261
259
|
// local server); Bun uses it for SNI/verification, so an https target
|
|
262
260
|
// fails certificate checks. Drop it and let fetch derive Host from the
|
|
263
261
|
// target URL, like Next's external-rewrite proxying.
|
|
264
|
-
const externalHeaders = new Headers(rewritten.headers)
|
|
265
|
-
externalHeaders.delete('host')
|
|
266
|
-
const external = await fetch(new Request(rewritten, { headers: externalHeaders }))
|
|
267
|
-
const headers = new Headers(external.headers)
|
|
268
|
-
headers.delete('content-encoding')
|
|
269
|
-
headers.delete('content-length')
|
|
270
|
-
headers.delete('transfer-encoding')
|
|
262
|
+
const externalHeaders = new Headers(rewritten.headers)
|
|
263
|
+
externalHeaders.delete('host')
|
|
264
|
+
const external = await fetch(new Request(rewritten, { headers: externalHeaders }))
|
|
265
|
+
const headers = new Headers(external.headers)
|
|
266
|
+
headers.delete('content-encoding')
|
|
267
|
+
headers.delete('content-length')
|
|
268
|
+
headers.delete('transfer-encoding')
|
|
271
269
|
const proxied = new Response(external.body, {
|
|
272
270
|
status: external.status,
|
|
273
271
|
statusText: external.statusText,
|
|
274
272
|
headers,
|
|
275
|
-
})
|
|
276
|
-
copyMiddlewareHeaders(response, proxied)
|
|
277
|
-
return proxied
|
|
273
|
+
})
|
|
274
|
+
copyMiddlewareHeaders(response, proxied)
|
|
275
|
+
return proxied
|
|
278
276
|
}
|
|
279
277
|
if (rewrittenUrl.origin === url.origin) {
|
|
280
|
-
proxyRewriteObserver?.(url, rewrittenUrl)
|
|
278
|
+
proxyRewriteObserver?.(url, rewrittenUrl)
|
|
281
279
|
if (request.headers.get('x-nextjs-data') === '1') {
|
|
282
|
-
response.headers.set('x-nextjs-rewrite', rewrittenUrl.href)
|
|
280
|
+
response.headers.set('x-nextjs-rewrite', rewrittenUrl.href)
|
|
283
281
|
}
|
|
284
282
|
}
|
|
285
|
-
return { request: rewritten, response }
|
|
283
|
+
return { request: rewritten, response }
|
|
286
284
|
}
|
|
287
285
|
|
|
288
|
-
return { request: forwardedRequest, response }
|
|
289
|
-
}
|
|
286
|
+
return { request: forwardedRequest, response }
|
|
287
|
+
}
|
|
290
288
|
|
|
291
289
|
runner.warm = (options = {}) => {
|
|
292
|
-
if (!file) return
|
|
290
|
+
if (!file) return
|
|
293
291
|
// A failure surfaces on the first request, which awaits this same promise;
|
|
294
292
|
// the swallow here only keeps the unawaited warm call from going unhandled.
|
|
295
|
-
void proxyModule(options).catch(() => undefined)
|
|
296
|
-
}
|
|
293
|
+
void proxyModule(options).catch(() => undefined)
|
|
294
|
+
}
|
|
297
295
|
|
|
298
|
-
return runner
|
|
296
|
+
return runner
|
|
299
297
|
}
|
|
300
298
|
|
|
301
299
|
// Apply the app's trailing-slash rule to a same-origin redirect emitted by the
|
|
@@ -303,96 +301,96 @@ export function createProxyRunner(
|
|
|
303
301
|
// `trailingSlash: true` must land on `/somewhere/`). Cross-origin targets, the
|
|
304
302
|
// skipTrailingSlashRedirect opt-out, and non-redirect responses pass through.
|
|
305
303
|
function normalizeRedirectLocation(response: Response, requestUrl: URL, config: ResolvedConfig) {
|
|
306
|
-
if (config.skipTrailingSlashRedirect || !config.trailingSlash) return response
|
|
307
|
-
if (response.status < 300 || response.status >= 400) return response
|
|
308
|
-
const location = response.headers.get('location')
|
|
309
|
-
if (!location) return response
|
|
310
|
-
let target: URL
|
|
304
|
+
if (config.skipTrailingSlashRedirect || !config.trailingSlash) return response
|
|
305
|
+
if (response.status < 300 || response.status >= 400) return response
|
|
306
|
+
const location = response.headers.get('location')
|
|
307
|
+
if (!location) return response
|
|
308
|
+
let target: URL
|
|
311
309
|
try {
|
|
312
|
-
target = new URL(location, requestUrl)
|
|
310
|
+
target = new URL(location, requestUrl)
|
|
313
311
|
} catch {
|
|
314
|
-
return response
|
|
312
|
+
return response
|
|
315
313
|
}
|
|
316
|
-
if (target.origin !== requestUrl.origin) return response
|
|
317
|
-
const normalized = canonicalTrailingSlashPath(target.pathname, true)
|
|
318
|
-
if (normalized === target.pathname) return response
|
|
319
|
-
target.pathname = normalized
|
|
314
|
+
if (target.origin !== requestUrl.origin) return response
|
|
315
|
+
const normalized = canonicalTrailingSlashPath(target.pathname, true)
|
|
316
|
+
if (normalized === target.pathname) return response
|
|
317
|
+
target.pathname = normalized
|
|
320
318
|
try {
|
|
321
|
-
response.headers.set('location', target.href)
|
|
322
|
-
return response
|
|
319
|
+
response.headers.set('location', target.href)
|
|
320
|
+
return response
|
|
323
321
|
} catch {
|
|
324
|
-
const headers = new Headers(response.headers)
|
|
325
|
-
headers.set('location', target.href)
|
|
322
|
+
const headers = new Headers(response.headers)
|
|
323
|
+
headers.set('location', target.href)
|
|
326
324
|
return new Response(response.body, {
|
|
327
325
|
status: response.status,
|
|
328
326
|
statusText: response.statusText,
|
|
329
327
|
headers,
|
|
330
|
-
})
|
|
328
|
+
})
|
|
331
329
|
}
|
|
332
330
|
}
|
|
333
331
|
|
|
334
332
|
// Human label for the truncation warning ("10MB", "500KB") — whole units only,
|
|
335
333
|
// mirroring Next's log line ("Request body exceeded 10MB for /api/echo").
|
|
336
334
|
function formatBodyLimit(bytes: number): string {
|
|
337
|
-
if (bytes >= 1024 * 1024 && bytes % (1024 * 1024) === 0) return `${bytes / (1024 * 1024)}MB
|
|
338
|
-
if (bytes >= 1024 && bytes % 1024 === 0) return `${bytes / 1024}KB
|
|
339
|
-
return `${bytes}B
|
|
335
|
+
if (bytes >= 1024 * 1024 && bytes % (1024 * 1024) === 0) return `${bytes / (1024 * 1024)}MB`
|
|
336
|
+
if (bytes >= 1024 && bytes % 1024 === 0) return `${bytes / 1024}KB`
|
|
337
|
+
return `${bytes}B`
|
|
340
338
|
}
|
|
341
339
|
|
|
342
340
|
async function bufferProxyRequestBody(request: Request, pathname: string): Promise<Request> {
|
|
343
|
-
if (request.method === 'GET' || request.method === 'HEAD' || !request.body) return request
|
|
344
|
-
const limit = getProxyExtensions().clientMaxBodySize()
|
|
345
|
-
const chunks: Uint8Array[] = []
|
|
346
|
-
let total = 0
|
|
347
|
-
let truncated = false
|
|
348
|
-
const reader = request.body.getReader()
|
|
341
|
+
if (request.method === 'GET' || request.method === 'HEAD' || !request.body) return request
|
|
342
|
+
const limit = getProxyExtensions().clientMaxBodySize()
|
|
343
|
+
const chunks: Uint8Array[] = []
|
|
344
|
+
let total = 0
|
|
345
|
+
let truncated = false
|
|
346
|
+
const reader = request.body.getReader()
|
|
349
347
|
for (;;) {
|
|
350
|
-
const { done, value } = await reader.read()
|
|
351
|
-
if (done) break
|
|
348
|
+
const { done, value } = await reader.read()
|
|
349
|
+
if (done) break
|
|
352
350
|
if (!truncated && total + value.byteLength > limit) {
|
|
353
|
-
const keep = limit - total
|
|
354
|
-
if (keep > 0) chunks.push(value.subarray(0, keep))
|
|
355
|
-
total = limit
|
|
356
|
-
truncated = true
|
|
351
|
+
const keep = limit - total
|
|
352
|
+
if (keep > 0) chunks.push(value.subarray(0, keep))
|
|
353
|
+
total = limit
|
|
354
|
+
truncated = true
|
|
357
355
|
// Keep draining (and discarding) the rest of the upload — cancelling the
|
|
358
356
|
// stream mid-upload stalls the connection and the response never flushes.
|
|
359
|
-
continue
|
|
357
|
+
continue
|
|
360
358
|
}
|
|
361
359
|
if (!truncated) {
|
|
362
|
-
chunks.push(value)
|
|
363
|
-
total += value.byteLength
|
|
360
|
+
chunks.push(value)
|
|
361
|
+
total += value.byteLength
|
|
364
362
|
}
|
|
365
363
|
}
|
|
366
364
|
if (truncated) {
|
|
367
|
-
console.warn(`Request body exceeded ${formatBodyLimit(limit)} for ${pathname}`)
|
|
365
|
+
console.warn(`Request body exceeded ${formatBodyLimit(limit)} for ${pathname}`)
|
|
368
366
|
}
|
|
369
|
-
const body = new Uint8Array(total)
|
|
370
|
-
let offset = 0
|
|
367
|
+
const body = new Uint8Array(total)
|
|
368
|
+
let offset = 0
|
|
371
369
|
for (const chunk of chunks) {
|
|
372
|
-
body.set(chunk, offset)
|
|
373
|
-
offset += chunk.byteLength
|
|
370
|
+
body.set(chunk, offset)
|
|
371
|
+
offset += chunk.byteLength
|
|
374
372
|
}
|
|
375
|
-
const headers = new Headers(request.headers)
|
|
376
|
-
headers.set('content-length', String(total))
|
|
373
|
+
const headers = new Headers(request.headers)
|
|
374
|
+
headers.set('content-length', String(total))
|
|
377
375
|
return new Request(request.url, {
|
|
378
376
|
method: request.method,
|
|
379
377
|
headers,
|
|
380
378
|
body,
|
|
381
379
|
// @ts-expect-error Bun/Node fetch option for streaming request bodies.
|
|
382
380
|
duplex: 'half',
|
|
383
|
-
})
|
|
381
|
+
})
|
|
384
382
|
}
|
|
385
383
|
|
|
386
384
|
export function applyProxyResponse(response: Response, proxyResponse?: Response) {
|
|
387
|
-
if (proxyResponse) copyMiddlewareHeaders(proxyResponse, response)
|
|
388
|
-
return response
|
|
385
|
+
if (proxyResponse) copyMiddlewareHeaders(proxyResponse, response)
|
|
386
|
+
return response
|
|
389
387
|
}
|
|
390
388
|
|
|
391
389
|
export function proxyRoutePatterns(config: ProxyConfig | undefined) {
|
|
392
|
-
const matcher = config?.matcher
|
|
393
|
-
if (!matcher) return ['^/(.*)$']
|
|
394
|
-
const matchers = Array.isArray(matcher) ? matcher : [matcher]
|
|
395
|
-
return matchers.map(item => `^${matcherPattern(matcherSource(item))}$`)
|
|
390
|
+
const matcher = config?.matcher
|
|
391
|
+
if (!matcher) return ['^/(.*)$']
|
|
392
|
+
const matchers = Array.isArray(matcher) ? matcher : [matcher]
|
|
393
|
+
return matchers.map(item => `^${matcherPattern(matcherSource(item))}$`)
|
|
396
394
|
}
|
|
397
395
|
|
|
398
396
|
async function importProxyModule(
|
|
@@ -407,14 +405,14 @@ async function importProxyModule(
|
|
|
407
405
|
externalLoadTarget: proxyExternalLoadTarget(file),
|
|
408
406
|
reactServerLayer: true,
|
|
409
407
|
})
|
|
410
|
-
: (runnerOptions.compiledModuleHref ?? pathToFileHref(file))
|
|
411
|
-
return import(href) as Promise<ProxyModule
|
|
408
|
+
: (runnerOptions.compiledModuleHref ?? pathToFileHref(file))
|
|
409
|
+
return import(href) as Promise<ProxyModule>
|
|
412
410
|
}
|
|
413
411
|
|
|
414
412
|
function proxyBases(config: ResolvedConfig) {
|
|
415
413
|
return config.appPath === path.join(config.root, 'src', 'app')
|
|
416
414
|
? [path.join(config.root, 'src'), config.root]
|
|
417
|
-
: [config.root]
|
|
415
|
+
: [config.root]
|
|
418
416
|
}
|
|
419
417
|
|
|
420
418
|
/**
|
|
@@ -423,11 +421,11 @@ function proxyBases(config: ResolvedConfig) {
|
|
|
423
421
|
*/
|
|
424
422
|
function proxyMatcherGateEnabled() {
|
|
425
423
|
// eslint-disable-next-line turbo/no-undeclared-env-vars
|
|
426
|
-
return process.env.PNEXT_PROXY_MATCHER_GATE !== '0'
|
|
424
|
+
return process.env.PNEXT_PROXY_MATCHER_GATE !== '0'
|
|
427
425
|
}
|
|
428
426
|
|
|
429
427
|
/** Parsed matchers per proxy file, keyed on the source's size+mtime. */
|
|
430
|
-
const staticMatcherCache = new Map<string, { key: string; matchers?: CompiledMatcher[] }>()
|
|
428
|
+
const staticMatcherCache = new Map<string, { key: string; matchers?: CompiledMatcher[] }>()
|
|
431
429
|
|
|
432
430
|
/**
|
|
433
431
|
* Compile the matchers of `export const config` off the proxy source itself. Only a wholly literal config
|
|
@@ -436,159 +434,159 @@ const staticMatcherCache = new Map<string, { key: string; matchers?: CompiledMat
|
|
|
436
434
|
* literal `config` yields undefined, and the caller falls back to the imported module's config.
|
|
437
435
|
*/
|
|
438
436
|
function staticProxyMatchers(file: string): CompiledMatcher[] | undefined {
|
|
439
|
-
let key: string
|
|
437
|
+
let key: string
|
|
440
438
|
try {
|
|
441
|
-
const stats = statSync(file)
|
|
442
|
-
key = `${stats.size}:${stats.mtimeMs}
|
|
439
|
+
const stats = statSync(file)
|
|
440
|
+
key = `${stats.size}:${stats.mtimeMs}`
|
|
443
441
|
} catch {
|
|
444
|
-
return undefined
|
|
442
|
+
return undefined
|
|
445
443
|
}
|
|
446
|
-
const cached = staticMatcherCache.get(file)
|
|
447
|
-
if (cached?.key === key) return cached.matchers
|
|
448
|
-
let matchers: CompiledMatcher[] | undefined
|
|
444
|
+
const cached = staticMatcherCache.get(file)
|
|
445
|
+
if (cached?.key === key) return cached.matchers
|
|
446
|
+
let matchers: CompiledMatcher[] | undefined
|
|
449
447
|
try {
|
|
450
|
-
const config = staticProxyConfig(file, readFileSync(file, 'utf8'))
|
|
451
|
-
if (config) matchers = proxyMatchers(config)
|
|
448
|
+
const config = staticProxyConfig(file, readFileSync(file, 'utf8'))
|
|
449
|
+
if (config) matchers = proxyMatchers(config)
|
|
452
450
|
} catch {
|
|
453
|
-
matchers = undefined
|
|
451
|
+
matchers = undefined
|
|
454
452
|
}
|
|
455
|
-
staticMatcherCache.set(file, { key, matchers })
|
|
456
|
-
return matchers
|
|
453
|
+
staticMatcherCache.set(file, { key, matchers })
|
|
454
|
+
return matchers
|
|
457
455
|
}
|
|
458
456
|
|
|
459
457
|
function staticProxyConfig(file: string, source: string): ProxyConfig | undefined {
|
|
460
|
-
const result = parseSync(file, source, { lang: parserLang(file) })
|
|
458
|
+
const result = parseSync(file, source, { lang: parserLang(file) })
|
|
461
459
|
// A recovered parse may have dropped the very declaration we are reading.
|
|
462
|
-
if (result.errors.length > 0) return undefined
|
|
460
|
+
if (result.errors.length > 0) return undefined
|
|
463
461
|
for (const statement of result.program.body as StaticNode[]) {
|
|
464
|
-
if (statement.type !== 'ExportNamedDeclaration') continue
|
|
465
|
-
const declaration = statement.declaration
|
|
466
|
-
if (declaration?.type !== 'VariableDeclaration') continue
|
|
462
|
+
if (statement.type !== 'ExportNamedDeclaration') continue
|
|
463
|
+
const declaration = statement.declaration
|
|
464
|
+
if (declaration?.type !== 'VariableDeclaration') continue
|
|
467
465
|
for (const declarator of declaration.declarations ?? []) {
|
|
468
|
-
if (declarator.id?.type !== 'Identifier' || declarator.id.name !== 'config') continue
|
|
469
|
-
const value = staticValue(declarator.init)
|
|
470
|
-
return isProxyConfigShape(value) ? (value as ProxyConfig) : undefined
|
|
466
|
+
if (declarator.id?.type !== 'Identifier' || declarator.id.name !== 'config') continue
|
|
467
|
+
const value = staticValue(declarator.init)
|
|
468
|
+
return isProxyConfigShape(value) ? (value as ProxyConfig) : undefined
|
|
471
469
|
}
|
|
472
470
|
}
|
|
473
|
-
return undefined
|
|
471
|
+
return undefined
|
|
474
472
|
}
|
|
475
473
|
|
|
476
474
|
function parserLang(file: string) {
|
|
477
|
-
const ext = path.extname(file)
|
|
478
|
-
if (ext === '.tsx') return 'tsx'
|
|
479
|
-
if (ext === '.jsx') return 'jsx'
|
|
480
|
-
return ext === '.js' || ext === '.mjs' ? 'js' : 'ts'
|
|
475
|
+
const ext = path.extname(file)
|
|
476
|
+
if (ext === '.tsx') return 'tsx'
|
|
477
|
+
if (ext === '.jsx') return 'jsx'
|
|
478
|
+
return ext === '.js' || ext === '.mjs' ? 'js' : 'ts'
|
|
481
479
|
}
|
|
482
480
|
|
|
483
481
|
/** Structural view of the oxc AST nodes this file reads — no full type import. */
|
|
484
482
|
interface StaticNode {
|
|
485
|
-
type: string
|
|
486
|
-
name?: string
|
|
487
|
-
value?: unknown
|
|
488
|
-
computed?: boolean
|
|
489
|
-
shorthand?: boolean
|
|
490
|
-
key?: StaticNode
|
|
491
|
-
init?: StaticNode
|
|
492
|
-
id?: StaticNode
|
|
493
|
-
declaration?: StaticNode
|
|
494
|
-
declarations?: StaticNode[]
|
|
495
|
-
expression?: StaticNode
|
|
496
|
-
properties?: StaticNode[]
|
|
497
|
-
elements?: (StaticNode | null)[]
|
|
498
|
-
expressions?: StaticNode[]
|
|
499
|
-
quasis?: { value?: { cooked?: string } }[]
|
|
483
|
+
type: string
|
|
484
|
+
name?: string
|
|
485
|
+
value?: unknown
|
|
486
|
+
computed?: boolean
|
|
487
|
+
shorthand?: boolean
|
|
488
|
+
key?: StaticNode
|
|
489
|
+
init?: StaticNode
|
|
490
|
+
id?: StaticNode
|
|
491
|
+
declaration?: StaticNode
|
|
492
|
+
declarations?: StaticNode[]
|
|
493
|
+
expression?: StaticNode
|
|
494
|
+
properties?: StaticNode[]
|
|
495
|
+
elements?: (StaticNode | null)[]
|
|
496
|
+
expressions?: StaticNode[]
|
|
497
|
+
quasis?: { value?: { cooked?: string } }[]
|
|
500
498
|
}
|
|
501
499
|
|
|
502
500
|
/** Literal-only evaluation; `undefined` marks "not statically known". */
|
|
503
501
|
function staticValue(node: StaticNode | undefined | null): unknown {
|
|
504
|
-
if (!node) return undefined
|
|
502
|
+
if (!node) return undefined
|
|
505
503
|
switch (node.type) {
|
|
506
504
|
// `… as const` / `… satisfies ProxyConfig` wrap the literal, they never change it.
|
|
507
505
|
case 'TSAsExpression':
|
|
508
506
|
case 'TSSatisfiesExpression':
|
|
509
507
|
case 'TSNonNullExpression':
|
|
510
|
-
return staticValue(node.expression)
|
|
508
|
+
return staticValue(node.expression)
|
|
511
509
|
case 'Literal':
|
|
512
510
|
case 'StringLiteral':
|
|
513
511
|
case 'NumericLiteral':
|
|
514
512
|
case 'BooleanLiteral':
|
|
515
513
|
// A regex literal reports `value: null` here; it is not a matcher shape.
|
|
516
|
-
return node.value === null ? undefined : node.value
|
|
514
|
+
return node.value === null ? undefined : node.value
|
|
517
515
|
case 'TemplateLiteral':
|
|
518
516
|
return node.expressions?.length === 0
|
|
519
517
|
? (node.quasis?.[0]?.value?.cooked ?? undefined)
|
|
520
|
-
: undefined
|
|
518
|
+
: undefined
|
|
521
519
|
case 'ArrayExpression': {
|
|
522
|
-
const items: unknown[] = []
|
|
520
|
+
const items: unknown[] = []
|
|
523
521
|
for (const element of node.elements ?? []) {
|
|
524
|
-
const value = staticValue(element)
|
|
525
|
-
if (value === undefined) return undefined
|
|
526
|
-
items.push(value)
|
|
522
|
+
const value = staticValue(element)
|
|
523
|
+
if (value === undefined) return undefined
|
|
524
|
+
items.push(value)
|
|
527
525
|
}
|
|
528
|
-
return items
|
|
526
|
+
return items
|
|
529
527
|
}
|
|
530
528
|
case 'ObjectExpression': {
|
|
531
|
-
const object: Record<string, unknown> = {}
|
|
529
|
+
const object: Record<string, unknown> = {}
|
|
532
530
|
for (const property of node.properties ?? []) {
|
|
533
|
-
if (property.type !== 'Property' && property.type !== 'ObjectProperty') return undefined
|
|
534
|
-
if (property.computed) return undefined
|
|
531
|
+
if (property.type !== 'Property' && property.type !== 'ObjectProperty') return undefined
|
|
532
|
+
if (property.computed) return undefined
|
|
535
533
|
const key =
|
|
536
|
-
property.key?.type === 'Identifier' ? property.key.name : (property.key?.value as string)
|
|
537
|
-
if (typeof key !== 'string') return undefined
|
|
538
|
-
const value = staticValue(property.value as StaticNode | undefined)
|
|
539
|
-
if (value === undefined) return undefined
|
|
540
|
-
object[key] = value
|
|
534
|
+
property.key?.type === 'Identifier' ? property.key.name : (property.key?.value as string)
|
|
535
|
+
if (typeof key !== 'string') return undefined
|
|
536
|
+
const value = staticValue(property.value as StaticNode | undefined)
|
|
537
|
+
if (value === undefined) return undefined
|
|
538
|
+
object[key] = value
|
|
541
539
|
}
|
|
542
|
-
return object
|
|
540
|
+
return object
|
|
543
541
|
}
|
|
544
542
|
default:
|
|
545
|
-
return undefined
|
|
543
|
+
return undefined
|
|
546
544
|
}
|
|
547
545
|
}
|
|
548
546
|
|
|
549
547
|
/** Only a config whose `matcher` is exactly the documented shape may gate. */
|
|
550
548
|
function isProxyConfigShape(value: unknown): boolean {
|
|
551
|
-
if (typeof value !== 'object' || value === null) return false
|
|
552
|
-
const matcher = (value as { matcher?: unknown }).matcher
|
|
553
|
-
if (matcher === undefined) return true
|
|
554
|
-
const items = Array.isArray(matcher) ? matcher : [matcher]
|
|
549
|
+
if (typeof value !== 'object' || value === null) return false
|
|
550
|
+
const matcher = (value as { matcher?: unknown }).matcher
|
|
551
|
+
if (matcher === undefined) return true
|
|
552
|
+
const items = Array.isArray(matcher) ? matcher : [matcher]
|
|
555
553
|
return items.every(item => {
|
|
556
|
-
if (typeof item === 'string') return true
|
|
557
|
-
if (typeof item !== 'object' || item === null) return false
|
|
558
|
-
const { source, has, missing } = item as Record<string, unknown
|
|
559
|
-
if (typeof source !== 'string') return false
|
|
554
|
+
if (typeof item === 'string') return true
|
|
555
|
+
if (typeof item !== 'object' || item === null) return false
|
|
556
|
+
const { source, has, missing } = item as Record<string, unknown>
|
|
557
|
+
if (typeof source !== 'string') return false
|
|
560
558
|
return [has, missing].every(
|
|
561
559
|
list => list === undefined || (Array.isArray(list) && list.every(isConditionShape)),
|
|
562
|
-
)
|
|
563
|
-
})
|
|
560
|
+
)
|
|
561
|
+
})
|
|
564
562
|
}
|
|
565
563
|
|
|
566
564
|
function isConditionShape(value: unknown): boolean {
|
|
567
|
-
if (typeof value !== 'object' || value === null) return false
|
|
568
|
-
const { type, key, value: expected } = value as Record<string, unknown
|
|
569
|
-
if (type !== 'header' && type !== 'query' && type !== 'cookie' && type !== 'host') return false
|
|
570
|
-
if (key !== undefined && typeof key !== 'string') return false
|
|
571
|
-
return expected === undefined || typeof expected === 'string'
|
|
565
|
+
if (typeof value !== 'object' || value === null) return false
|
|
566
|
+
const { type, key, value: expected } = value as Record<string, unknown>
|
|
567
|
+
if (type !== 'header' && type !== 'query' && type !== 'cookie' && type !== 'host') return false
|
|
568
|
+
if (key !== undefined && typeof key !== 'string') return false
|
|
569
|
+
return expected === undefined || typeof expected === 'string'
|
|
572
570
|
}
|
|
573
571
|
|
|
574
572
|
function proxyMatchers(config: ProxyConfig | undefined): CompiledMatcher[] {
|
|
575
|
-
const matcher = config?.matcher
|
|
576
|
-
if (!matcher) return [{ regex: /.*/ }]
|
|
577
|
-
const matchers = Array.isArray(matcher) ? matcher : [matcher]
|
|
573
|
+
const matcher = config?.matcher
|
|
574
|
+
if (!matcher) return [{ regex: /.*/ }]
|
|
575
|
+
const matchers = Array.isArray(matcher) ? matcher : [matcher]
|
|
578
576
|
return matchers.map(item => ({
|
|
579
577
|
regex: matcherRegex(matcherSource(item)),
|
|
580
578
|
...(typeof item === 'object' && item.has ? { has: item.has } : {}),
|
|
581
579
|
...(typeof item === 'object' && item.missing ? { missing: item.missing } : {}),
|
|
582
|
-
}))
|
|
580
|
+
}))
|
|
583
581
|
}
|
|
584
582
|
|
|
585
583
|
function matchesProxy(matchers: CompiledMatcher[], url: URL, request: Request) {
|
|
586
|
-
const decoded = decodePathSeparators(url.pathname)
|
|
584
|
+
const decoded = decodePathSeparators(url.pathname)
|
|
587
585
|
return matchers.some(
|
|
588
586
|
matcher =>
|
|
589
587
|
(matcher.regex.test(url.pathname) || matcher.regex.test(decoded)) &&
|
|
590
588
|
matchesConditions(matcher, url, request),
|
|
591
|
-
)
|
|
589
|
+
)
|
|
592
590
|
}
|
|
593
591
|
|
|
594
592
|
// A matcher applies only when every `has` condition is present and no `missing`
|
|
@@ -597,89 +595,85 @@ function matchesProxy(matchers: CompiledMatcher[], url: URL, request: Request) {
|
|
|
597
595
|
// opts out of running for prefetch requests.
|
|
598
596
|
function matchesConditions(matcher: CompiledMatcher, url: URL, request: Request) {
|
|
599
597
|
if (matcher.has && !matcher.has.every(condition => hasCondition(condition, url, request))) {
|
|
600
|
-
return false
|
|
598
|
+
return false
|
|
601
599
|
}
|
|
602
600
|
if (matcher.missing?.some(condition => hasCondition(condition, url, request))) {
|
|
603
|
-
return false
|
|
601
|
+
return false
|
|
604
602
|
}
|
|
605
|
-
return true
|
|
603
|
+
return true
|
|
606
604
|
}
|
|
607
605
|
|
|
608
606
|
function hasCondition(condition: ProxyRouteCondition, url: URL, request: Request): boolean {
|
|
609
|
-
const actual = conditionValue(condition, url, request)
|
|
610
|
-
if (actual === null) return false
|
|
611
|
-
if (condition.value === undefined) return true
|
|
607
|
+
const actual = conditionValue(condition, url, request)
|
|
608
|
+
if (actual === null) return false
|
|
609
|
+
if (condition.value === undefined) return true
|
|
612
610
|
try {
|
|
613
|
-
return new RegExp(`^${condition.value}$`).test(actual)
|
|
611
|
+
return new RegExp(`^${condition.value}$`).test(actual)
|
|
614
612
|
} catch {
|
|
615
|
-
return condition.value === actual
|
|
613
|
+
return condition.value === actual
|
|
616
614
|
}
|
|
617
615
|
}
|
|
618
616
|
|
|
619
|
-
function conditionValue(
|
|
620
|
-
condition: ProxyRouteCondition,
|
|
621
|
-
url: URL,
|
|
622
|
-
request: Request,
|
|
623
|
-
): string | null {
|
|
617
|
+
function conditionValue(condition: ProxyRouteCondition, url: URL, request: Request): string | null {
|
|
624
618
|
switch (condition.type) {
|
|
625
619
|
case 'header':
|
|
626
|
-
return condition.key ? request.headers.get(condition.key) : null
|
|
620
|
+
return condition.key ? request.headers.get(condition.key) : null
|
|
627
621
|
case 'query':
|
|
628
|
-
return condition.key ? url.searchParams.get(condition.key) : null
|
|
622
|
+
return condition.key ? url.searchParams.get(condition.key) : null
|
|
629
623
|
case 'cookie': {
|
|
630
|
-
if (!condition.key) return null
|
|
624
|
+
if (!condition.key) return null
|
|
631
625
|
const cookie = parseCookies(request.headers.get('cookie') ?? '').find(
|
|
632
626
|
item => item.name === condition.key,
|
|
633
|
-
)
|
|
634
|
-
return cookie ? cookie.value : null
|
|
627
|
+
)
|
|
628
|
+
return cookie ? cookie.value : null
|
|
635
629
|
}
|
|
636
630
|
case 'host':
|
|
637
|
-
return url.host
|
|
631
|
+
return url.host
|
|
638
632
|
default:
|
|
639
|
-
return null
|
|
633
|
+
return null
|
|
640
634
|
}
|
|
641
635
|
}
|
|
642
636
|
|
|
643
637
|
function decodePathSeparators(pathname: string) {
|
|
644
|
-
return pathname.replace(/%2f/gi, '/')
|
|
638
|
+
return pathname.replace(/%2f/gi, '/')
|
|
645
639
|
}
|
|
646
640
|
|
|
647
641
|
function matcherSource(matcher: ProxyMatcher) {
|
|
648
|
-
return typeof matcher === 'string' ? matcher : matcher.source
|
|
642
|
+
return typeof matcher === 'string' ? matcher : matcher.source
|
|
649
643
|
}
|
|
650
644
|
|
|
651
645
|
function matcherRegex(matcher: string) {
|
|
652
|
-
return new RegExp(`^${matcherPattern(matcher)}$`)
|
|
646
|
+
return new RegExp(`^${matcherPattern(matcher)}$`)
|
|
653
647
|
}
|
|
654
648
|
|
|
655
649
|
function matcherPattern(matcher: string) {
|
|
656
|
-
if (matcher.includes('(')) return matcher
|
|
657
|
-
if (matcher === '/') return '/'
|
|
650
|
+
if (matcher.includes('(')) return matcher
|
|
651
|
+
if (matcher === '/') return '/'
|
|
658
652
|
|
|
659
|
-
let source = ''
|
|
653
|
+
let source = ''
|
|
660
654
|
for (const segment of matcher.split('/').filter(Boolean)) {
|
|
661
655
|
if (/^:[^/]+\*$/.test(segment)) {
|
|
662
|
-
source += '(?:/.*)?'
|
|
656
|
+
source += '(?:/.*)?'
|
|
663
657
|
} else if (/^:[^/]+$/.test(segment)) {
|
|
664
|
-
source += '/[^/]+'
|
|
658
|
+
source += '/[^/]+'
|
|
665
659
|
} else {
|
|
666
|
-
source += `/${escapeRegex(segment)}
|
|
660
|
+
source += `/${escapeRegex(segment)}`
|
|
667
661
|
}
|
|
668
662
|
}
|
|
669
|
-
return source || '/'
|
|
663
|
+
return source || '/'
|
|
670
664
|
}
|
|
671
665
|
|
|
672
666
|
function rewriteRequest(request: Request, url: string) {
|
|
673
|
-
return new Request(new URL(url, request.url), request)
|
|
667
|
+
return new Request(new URL(url, request.url), request)
|
|
674
668
|
}
|
|
675
669
|
|
|
676
670
|
// Drop `?_rsc=<hash>` from the request the middleware handler sees. GET/HEAD
|
|
677
671
|
// only ever carry it (router fetches), so re-wrapping the Request is body-safe.
|
|
678
672
|
function stripRscUnionQuery(request: Request): Request {
|
|
679
|
-
const url = new URL(request.url)
|
|
680
|
-
if (!url.searchParams.has('_rsc')) return request
|
|
681
|
-
url.searchParams.delete('_rsc')
|
|
682
|
-
return new Request(url, request)
|
|
673
|
+
const url = new URL(request.url)
|
|
674
|
+
if (!url.searchParams.has('_rsc')) return request
|
|
675
|
+
url.searchParams.delete('_rsc')
|
|
676
|
+
return new Request(url, request)
|
|
683
677
|
}
|
|
684
678
|
|
|
685
679
|
// The app-router flight headers Next removes from the request middleware sees
|
|
@@ -691,66 +685,66 @@ const FLIGHT_HEADERS = [
|
|
|
691
685
|
'next-router-prefetch',
|
|
692
686
|
'next-hmr-refresh',
|
|
693
687
|
'next-router-segment-prefetch',
|
|
694
|
-
]
|
|
688
|
+
]
|
|
695
689
|
|
|
696
690
|
// Drop the flight headers from the request the middleware handler sees. GET/HEAD
|
|
697
691
|
// router fetches carry them, so re-wrapping the Request is body-safe.
|
|
698
692
|
function stripFlightHeaders(request: Request): Request {
|
|
699
|
-
if (!FLIGHT_HEADERS.some(header => request.headers.has(header))) return request
|
|
700
|
-
const headers = new Headers(request.headers)
|
|
701
|
-
for (const header of FLIGHT_HEADERS) headers.delete(header)
|
|
702
|
-
return new Request(request, { headers })
|
|
693
|
+
if (!FLIGHT_HEADERS.some(header => request.headers.has(header))) return request
|
|
694
|
+
const headers = new Headers(request.headers)
|
|
695
|
+
for (const header of FLIGHT_HEADERS) headers.delete(header)
|
|
696
|
+
return new Request(request, { headers })
|
|
703
697
|
}
|
|
704
698
|
|
|
705
699
|
function applyRequestOverride(request: Request, headers: Headers | undefined) {
|
|
706
|
-
if (!headers) return request
|
|
707
|
-
return new Request(request, { headers })
|
|
700
|
+
if (!headers) return request
|
|
701
|
+
return new Request(request, { headers })
|
|
708
702
|
}
|
|
709
703
|
|
|
710
704
|
function applyProxyCookies(request: Request, response: Response) {
|
|
711
|
-
const setCookies = getSetCookie(response.headers)
|
|
712
|
-
if (setCookies.length === 0) return request
|
|
705
|
+
const setCookies = getSetCookie(response.headers)
|
|
706
|
+
if (setCookies.length === 0) return request
|
|
713
707
|
|
|
714
|
-
const cookies = parseCookies(request.headers.get('cookie') ?? '')
|
|
708
|
+
const cookies = parseCookies(request.headers.get('cookie') ?? '')
|
|
715
709
|
for (const header of setCookies) {
|
|
716
|
-
const cookie = parseSetCookie(header)
|
|
717
|
-
if (!cookie) continue
|
|
718
|
-
const index = cookies.findIndex(item => item.name === cookie.name)
|
|
710
|
+
const cookie = parseSetCookie(header)
|
|
711
|
+
if (!cookie) continue
|
|
712
|
+
const index = cookies.findIndex(item => item.name === cookie.name)
|
|
719
713
|
if (cookie.deleted) {
|
|
720
|
-
if (index !== -1) cookies.splice(index, 1)
|
|
721
|
-
continue
|
|
714
|
+
if (index !== -1) cookies.splice(index, 1)
|
|
715
|
+
continue
|
|
722
716
|
}
|
|
723
|
-
if (index === -1) cookies.push({ name: cookie.name, value: cookie.value })
|
|
724
|
-
else cookies[index] = { name: cookie.name, value: cookie.value }
|
|
717
|
+
if (index === -1) cookies.push({ name: cookie.name, value: cookie.value })
|
|
718
|
+
else cookies[index] = { name: cookie.name, value: cookie.value }
|
|
725
719
|
}
|
|
726
720
|
|
|
727
|
-
const headers = new Headers(request.headers)
|
|
728
|
-
const cookieHeader = requestCookieHeader(cookies)
|
|
729
|
-
if (cookieHeader) headers.set('cookie', cookieHeader)
|
|
730
|
-
else headers.delete('cookie')
|
|
731
|
-
return new Request(request, { headers })
|
|
721
|
+
const headers = new Headers(request.headers)
|
|
722
|
+
const cookieHeader = requestCookieHeader(cookies)
|
|
723
|
+
if (cookieHeader) headers.set('cookie', cookieHeader)
|
|
724
|
+
else headers.delete('cookie')
|
|
725
|
+
return new Request(request, { headers })
|
|
732
726
|
}
|
|
733
727
|
|
|
734
728
|
function parseSetCookie(header: string) {
|
|
735
|
-
const [pair = '', ...attributes] = header.split(';').map(part => part.trim())
|
|
736
|
-
const [name = '', ...valueParts] = pair.split('=')
|
|
737
|
-
if (!name) return undefined
|
|
738
|
-
const deleted = attributes.some(attribute => /^max-age=0$/i.test(attribute))
|
|
739
|
-
return { name: decodeCookiePart(name), value: decodeCookiePart(valueParts.join('=')), deleted }
|
|
729
|
+
const [pair = '', ...attributes] = header.split(';').map(part => part.trim())
|
|
730
|
+
const [name = '', ...valueParts] = pair.split('=')
|
|
731
|
+
if (!name) return undefined
|
|
732
|
+
const deleted = attributes.some(attribute => /^max-age=0$/i.test(attribute))
|
|
733
|
+
return { name: decodeCookiePart(name), value: decodeCookiePart(valueParts.join('=')), deleted }
|
|
740
734
|
}
|
|
741
735
|
|
|
742
736
|
function decodeCookiePart(value: string) {
|
|
743
737
|
try {
|
|
744
|
-
return decodeURIComponent(value)
|
|
738
|
+
return decodeURIComponent(value)
|
|
745
739
|
} catch {
|
|
746
|
-
return value
|
|
740
|
+
return value
|
|
747
741
|
}
|
|
748
742
|
}
|
|
749
743
|
|
|
750
744
|
function escapeRegex(value: string) {
|
|
751
|
-
return value.replace(/[.*+?^${}()|[\]\\]/g, '\\$&')
|
|
745
|
+
return value.replace(/[.*+?^${}()|[\]\\]/g, '\\$&')
|
|
752
746
|
}
|
|
753
747
|
|
|
754
748
|
function proxyModuleKey(options: ProxyRunOptions) {
|
|
755
|
-
return options.dev ? `dev:${options.devImportVersion ?? 'dev'}` : 'prod'
|
|
749
|
+
return options.dev ? `dev:${options.devImportVersion ?? 'dev'}` : 'prod'
|
|
756
750
|
}
|