@wular/pnext 0.0.4 → 0.0.7
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 +22 -16
- package/bin/pnext +6 -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 +87 -22
- package/reference/config.md +40 -93
- package/reference/css.md +42 -33
- package/reference/dev.md +26 -33
- package/reference/env.md +18 -20
- package/reference/getting-started.md +132 -0
- package/reference/metadata.md +31 -46
- package/reference/navigation.md +65 -105
- package/reference/performance.md +21 -142
- package/reference/rendering.md +78 -75
- package/reference/routing.md +97 -107
- package/reference/typegen.md +27 -29
- package/src/api/cache.ts +36 -37
- package/src/api/client-cache.ts +2 -2
- package/src/api/client-navigation.ts +121 -114
- package/src/api/dynamic.tsx +58 -55
- package/src/api/link.tsx +52 -53
- 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 +72 -64
- package/src/cli/dev.ts +116 -116
- package/src/cli/index.ts +94 -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 +802 -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 +32 -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 +2022 -1995
- package/src/{api → client}/router/types.ts +98 -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 -1897
- 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 +86 -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 +2087 -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} +139 -139
- package/src/resolve/engine.ts +90 -77
- package/src/resolve/imports.ts +475 -463
- package/src/resolve/scan-facts.ts +318 -296
- 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 +87 -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 +827 -815
- package/src/routing/slots.ts +164 -160
- package/src/runtime/loader.ts +954 -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 +428 -406
- package/src/styles.d.ts +9 -0
- package/src/types.ts +324 -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/reference/overview.md +0 -35
- 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
|
@@ -8,36 +8,36 @@
|
|
|
8
8
|
// Next's fetchExternalImage / fetchInternalImage exactly.
|
|
9
9
|
// ---------------------------------------------------------------------------
|
|
10
10
|
|
|
11
|
-
import { lookup } from 'node:dns/promises'
|
|
12
|
-
import { isIP } from 'node:net'
|
|
13
|
-
import { getHash } from './cache'
|
|
11
|
+
import { lookup } from 'node:dns/promises'
|
|
12
|
+
import { isIP } from 'node:net'
|
|
13
|
+
import { getHash } from './cache'
|
|
14
14
|
|
|
15
15
|
export class ImageError extends Error {
|
|
16
|
-
statusCode: number
|
|
16
|
+
statusCode: number
|
|
17
17
|
constructor(statusCode: number, message: string) {
|
|
18
|
-
super(message)
|
|
18
|
+
super(message)
|
|
19
19
|
// Ensure an error status is used > 400 (Next normalizes 1xx-3xx to 500).
|
|
20
|
-
this.statusCode = statusCode >= 400 ? statusCode : 500
|
|
20
|
+
this.statusCode = statusCode >= 400 ? statusCode : 500
|
|
21
21
|
}
|
|
22
22
|
}
|
|
23
23
|
|
|
24
24
|
export interface ImageUpstream {
|
|
25
|
-
buffer: Buffer
|
|
26
|
-
contentType: string | null
|
|
27
|
-
cacheControl: string | null
|
|
28
|
-
etag: string
|
|
25
|
+
buffer: Buffer
|
|
26
|
+
contentType: string | null
|
|
27
|
+
cacheControl: string | null
|
|
28
|
+
etag: string
|
|
29
29
|
}
|
|
30
30
|
|
|
31
31
|
/** Upstream etag when present (base64url so it is filename-safe), else a content hash. */
|
|
32
32
|
export function extractEtag(etag: string | null | undefined, buffer: Buffer): string {
|
|
33
|
-
if (etag) return Buffer.from(etag).toString('base64url')
|
|
34
|
-
return getHash([buffer])
|
|
33
|
+
if (etag) return Buffer.from(etag).toString('base64url')
|
|
34
|
+
return getHash([buffer])
|
|
35
35
|
}
|
|
36
36
|
|
|
37
37
|
function isPrivateIp(ip: string): boolean {
|
|
38
38
|
if (isIP(ip) === 4) {
|
|
39
|
-
const parts = ip.split('.').map(Number)
|
|
40
|
-
const [a = -1, b = -1] = parts
|
|
39
|
+
const parts = ip.split('.').map(Number)
|
|
40
|
+
const [a = -1, b = -1] = parts
|
|
41
41
|
return (
|
|
42
42
|
a === 0 ||
|
|
43
43
|
a === 10 ||
|
|
@@ -46,9 +46,9 @@ function isPrivateIp(ip: string): boolean {
|
|
|
46
46
|
(a === 172 && b >= 16 && b <= 31) ||
|
|
47
47
|
(a === 192 && b === 168) ||
|
|
48
48
|
(a === 169 && b === 254)
|
|
49
|
-
)
|
|
49
|
+
)
|
|
50
50
|
}
|
|
51
|
-
const lower = ip.toLowerCase()
|
|
51
|
+
const lower = ip.toLowerCase()
|
|
52
52
|
return (
|
|
53
53
|
lower === '::' ||
|
|
54
54
|
lower === '::1' ||
|
|
@@ -60,11 +60,11 @@ function isPrivateIp(ip: string): boolean {
|
|
|
60
60
|
lower.startsWith('feb') ||
|
|
61
61
|
lower.startsWith('::ffff:127.') ||
|
|
62
62
|
lower === 'localhost'
|
|
63
|
-
)
|
|
63
|
+
)
|
|
64
64
|
}
|
|
65
65
|
|
|
66
66
|
function isRedirect(status: number): boolean {
|
|
67
|
-
return [301, 302, 303, 307, 308].includes(status)
|
|
67
|
+
return [301, 302, 303, 307, 308].includes(status)
|
|
68
68
|
}
|
|
69
69
|
|
|
70
70
|
async function readBodyWithLimit(
|
|
@@ -72,21 +72,21 @@ async function readBodyWithLimit(
|
|
|
72
72
|
maximumResponseBody: number,
|
|
73
73
|
onExceeded: () => never,
|
|
74
74
|
): Promise<Buffer> {
|
|
75
|
-
const chunks: Buffer[] = []
|
|
76
|
-
let totalSize = 0
|
|
77
|
-
const reader = body.getReader()
|
|
75
|
+
const chunks: Buffer[] = []
|
|
76
|
+
let totalSize = 0
|
|
77
|
+
const reader = body.getReader()
|
|
78
78
|
for (;;) {
|
|
79
|
-
const { done, value } = await reader.read()
|
|
80
|
-
if (done) break
|
|
81
|
-
const chunk = Buffer.from(value)
|
|
82
|
-
totalSize += chunk.byteLength
|
|
79
|
+
const { done, value } = await reader.read()
|
|
80
|
+
if (done) break
|
|
81
|
+
const chunk = Buffer.from(value)
|
|
82
|
+
totalSize += chunk.byteLength
|
|
83
83
|
if (totalSize > maximumResponseBody) {
|
|
84
|
-
await reader.cancel().catch(() => undefined)
|
|
85
|
-
onExceeded()
|
|
84
|
+
await reader.cancel().catch(() => undefined)
|
|
85
|
+
onExceeded()
|
|
86
86
|
}
|
|
87
|
-
chunks.push(chunk)
|
|
87
|
+
chunks.push(chunk)
|
|
88
88
|
}
|
|
89
|
-
return Buffer.concat(chunks)
|
|
89
|
+
return Buffer.concat(chunks)
|
|
90
90
|
}
|
|
91
91
|
|
|
92
92
|
export async function fetchExternalImage(
|
|
@@ -96,15 +96,15 @@ export async function fetchExternalImage(
|
|
|
96
96
|
count: number,
|
|
97
97
|
): Promise<ImageUpstream> {
|
|
98
98
|
if (!dangerouslyAllowLocalIP) {
|
|
99
|
-
const { hostname } = new URL(href)
|
|
100
|
-
let ips = [hostname]
|
|
99
|
+
const { hostname } = new URL(href)
|
|
100
|
+
let ips = [hostname]
|
|
101
101
|
if (!isIP(hostname)) {
|
|
102
102
|
const records = await lookup(hostname, { family: 0, all: true }).catch(() => [
|
|
103
103
|
{ address: hostname },
|
|
104
|
-
])
|
|
105
|
-
ips = records.map(record => record.address)
|
|
104
|
+
])
|
|
105
|
+
ips = records.map(record => record.address)
|
|
106
106
|
}
|
|
107
|
-
const privateIps = ips.filter(ip => isPrivateIp(ip))
|
|
107
|
+
const privateIps = ips.filter(ip => isPrivateIp(ip))
|
|
108
108
|
if (privateIps.length > 0) {
|
|
109
109
|
console.error(
|
|
110
110
|
'upstream image',
|
|
@@ -112,60 +112,57 @@ export async function fetchExternalImage(
|
|
|
112
112
|
'hostname resolved to private IP',
|
|
113
113
|
JSON.stringify(privateIps),
|
|
114
114
|
'If this is expected and you understand SSRF risk, use images.dangerouslyAllowLocalIP = true to continue.',
|
|
115
|
-
)
|
|
116
|
-
throw new ImageError(400, '"url" parameter is not allowed')
|
|
115
|
+
)
|
|
116
|
+
throw new ImageError(400, '"url" parameter is not allowed')
|
|
117
117
|
}
|
|
118
118
|
}
|
|
119
119
|
|
|
120
120
|
const res = await fetch(href, {
|
|
121
121
|
signal: AbortSignal.timeout(7_000),
|
|
122
122
|
redirect: 'manual',
|
|
123
|
-
}).catch(err => err as Error)
|
|
123
|
+
}).catch(err => err as Error)
|
|
124
124
|
|
|
125
125
|
if (res instanceof Error) {
|
|
126
|
-
const err = res
|
|
126
|
+
const err = res
|
|
127
127
|
if (err.name === 'TimeoutError' || err.name === 'AbortError') {
|
|
128
|
-
console.error('upstream image response timed out for', href)
|
|
129
|
-
throw new ImageError(504, '"url" parameter is valid but upstream response timed out')
|
|
128
|
+
console.error('upstream image response timed out for', href)
|
|
129
|
+
throw new ImageError(504, '"url" parameter is valid but upstream response timed out')
|
|
130
130
|
}
|
|
131
|
-
throw err
|
|
131
|
+
throw err
|
|
132
132
|
}
|
|
133
133
|
|
|
134
|
-
const locationHeader = res.headers.get('Location')
|
|
134
|
+
const locationHeader = res.headers.get('Location')
|
|
135
135
|
if (isRedirect(res.status) && locationHeader && URL.canParse(locationHeader, href)) {
|
|
136
|
-
await res.body?.cancel().catch(() => undefined)
|
|
136
|
+
await res.body?.cancel().catch(() => undefined)
|
|
137
137
|
if (count === 0) {
|
|
138
|
-
console.error('upstream image response had too many redirects', href)
|
|
139
|
-
throw new ImageError(508, '"url" parameter is valid but upstream response is invalid')
|
|
138
|
+
console.error('upstream image response had too many redirects', href)
|
|
139
|
+
throw new ImageError(508, '"url" parameter is valid but upstream response is invalid')
|
|
140
140
|
}
|
|
141
|
-
const redirect = new URL(locationHeader, href).href
|
|
142
|
-
return fetchExternalImage(redirect, dangerouslyAllowLocalIP, maximumResponseBody, count - 1)
|
|
141
|
+
const redirect = new URL(locationHeader, href).href
|
|
142
|
+
return fetchExternalImage(redirect, dangerouslyAllowLocalIP, maximumResponseBody, count - 1)
|
|
143
143
|
}
|
|
144
144
|
|
|
145
145
|
if (!res.ok) {
|
|
146
|
-
console.error('upstream image response failed for', href, res.status)
|
|
147
|
-
throw new ImageError(
|
|
148
|
-
res.status,
|
|
149
|
-
'"url" parameter is valid but upstream response is invalid',
|
|
150
|
-
);
|
|
146
|
+
console.error('upstream image response failed for', href, res.status)
|
|
147
|
+
throw new ImageError(res.status, '"url" parameter is valid but upstream response is invalid')
|
|
151
148
|
}
|
|
152
149
|
|
|
153
150
|
if (!res.body) {
|
|
154
|
-
console.error('upstream image response is empty for', href)
|
|
155
|
-
throw new ImageError(400, '"url" parameter is valid but upstream response is invalid')
|
|
151
|
+
console.error('upstream image response is empty for', href)
|
|
152
|
+
throw new ImageError(400, '"url" parameter is valid but upstream response is invalid')
|
|
156
153
|
}
|
|
157
154
|
|
|
158
155
|
const buffer = await readBodyWithLimit(res.body, maximumResponseBody, () => {
|
|
159
|
-
console.error('upstream image response exceeded maximum size for', href)
|
|
160
|
-
throw new ImageError(413, '"url" parameter is valid but upstream response is invalid')
|
|
161
|
-
})
|
|
156
|
+
console.error('upstream image response exceeded maximum size for', href)
|
|
157
|
+
throw new ImageError(413, '"url" parameter is valid but upstream response is invalid')
|
|
158
|
+
})
|
|
162
159
|
|
|
163
160
|
return {
|
|
164
161
|
buffer,
|
|
165
162
|
contentType: res.headers.get('Content-Type'),
|
|
166
163
|
cacheControl: res.headers.get('Cache-Control'),
|
|
167
164
|
etag: extractEtag(res.headers.get('ETag'), buffer),
|
|
168
|
-
}
|
|
165
|
+
}
|
|
169
166
|
}
|
|
170
167
|
|
|
171
168
|
/**
|
|
@@ -182,27 +179,27 @@ export async function readPublicFile(
|
|
|
182
179
|
publicDirs: string[],
|
|
183
180
|
href: string,
|
|
184
181
|
): Promise<Buffer | undefined> {
|
|
185
|
-
let decoded: string
|
|
182
|
+
let decoded: string
|
|
186
183
|
try {
|
|
187
|
-
decoded = decodeURIComponent(href.split('?', 1)[0] ?? href)
|
|
184
|
+
decoded = decodeURIComponent(href.split('?', 1)[0] ?? href)
|
|
188
185
|
} catch {
|
|
189
|
-
return undefined
|
|
186
|
+
return undefined
|
|
190
187
|
}
|
|
191
|
-
if (decoded.includes('..') || decoded.includes('\0')) return undefined
|
|
192
|
-
const fs = await import('node:fs/promises')
|
|
193
|
-
const path = await import('node:path')
|
|
188
|
+
if (decoded.includes('..') || decoded.includes('\0')) return undefined
|
|
189
|
+
const fs = await import('node:fs/promises')
|
|
190
|
+
const path = await import('node:path')
|
|
194
191
|
for (const dir of publicDirs) {
|
|
195
192
|
for (const form of ['NFC', 'NFD'] as const) {
|
|
196
|
-
const candidate = path.join(dir, ...decoded.normalize(form).split('/').filter(Boolean))
|
|
197
|
-
if (!candidate.startsWith(dir)) continue
|
|
193
|
+
const candidate = path.join(dir, ...decoded.normalize(form).split('/').filter(Boolean))
|
|
194
|
+
if (!candidate.startsWith(dir)) continue
|
|
198
195
|
try {
|
|
199
|
-
return await fs.readFile(candidate)
|
|
196
|
+
return await fs.readFile(candidate)
|
|
200
197
|
} catch {
|
|
201
198
|
// try next candidate
|
|
202
199
|
}
|
|
203
200
|
}
|
|
204
201
|
}
|
|
205
|
-
return undefined
|
|
202
|
+
return undefined
|
|
206
203
|
}
|
|
207
204
|
|
|
208
205
|
export async function fetchInternalImage(
|
|
@@ -214,19 +211,19 @@ export async function fetchInternalImage(
|
|
|
214
211
|
const res = await fetch(new URL(href, origin), {
|
|
215
212
|
redirect: 'follow',
|
|
216
213
|
headers: { 'x-pnext-internal-image': '1' },
|
|
217
|
-
})
|
|
214
|
+
})
|
|
218
215
|
|
|
219
216
|
if (!res.body) {
|
|
220
|
-
console.error('internal image response is empty for', href)
|
|
221
|
-
throw new ImageError(400, '"url" parameter is valid but internal response is invalid')
|
|
217
|
+
console.error('internal image response is empty for', href)
|
|
218
|
+
throw new ImageError(400, '"url" parameter is valid but internal response is invalid')
|
|
222
219
|
}
|
|
223
220
|
const buffer = await readBodyWithLimit(res.body, maximumResponseBody, () => {
|
|
224
|
-
console.error('internal image response exceeded maximum size for', href)
|
|
225
|
-
throw new ImageError(413, '"url" parameter is valid but internal response is invalid')
|
|
226
|
-
})
|
|
221
|
+
console.error('internal image response exceeded maximum size for', href)
|
|
222
|
+
throw new ImageError(413, '"url" parameter is valid but internal response is invalid')
|
|
223
|
+
})
|
|
227
224
|
if (buffer.byteLength === 0) {
|
|
228
|
-
console.error('internal image response is empty for', href)
|
|
229
|
-
throw new ImageError(400, '"url" parameter is valid but internal response is invalid')
|
|
225
|
+
console.error('internal image response is empty for', href)
|
|
226
|
+
throw new ImageError(400, '"url" parameter is valid but internal response is invalid')
|
|
230
227
|
}
|
|
231
228
|
|
|
232
229
|
return {
|
|
@@ -234,10 +231,10 @@ export async function fetchInternalImage(
|
|
|
234
231
|
contentType: res.headers.get('Content-Type'),
|
|
235
232
|
cacheControl: res.headers.get('Cache-Control'),
|
|
236
233
|
etag: extractEtag(res.headers.get('ETag'), buffer),
|
|
237
|
-
}
|
|
234
|
+
}
|
|
238
235
|
} catch (err) {
|
|
239
|
-
if (err instanceof ImageError) throw err
|
|
240
|
-
console.error('upstream image response failed for', href, err)
|
|
241
|
-
throw new ImageError(500, '"url" parameter is valid but upstream response is invalid')
|
|
236
|
+
if (err instanceof ImageError) throw err
|
|
237
|
+
console.error('upstream image response failed for', href, err)
|
|
238
|
+
throw new ImageError(500, '"url" parameter is valid but upstream response is invalid')
|
|
242
239
|
}
|
|
243
240
|
}
|
|
@@ -8,37 +8,37 @@
|
|
|
8
8
|
//
|
|
9
9
|
// The route pathname is stashed on the work unit at request start so the detached callback can name it.
|
|
10
10
|
|
|
11
|
-
import { AsyncLocalStorage } from 'node:async_hooks'
|
|
12
|
-
import { currentRequest, getWorkUnit } from '../../request/context'
|
|
11
|
+
import { AsyncLocalStorage } from 'node:async_hooks'
|
|
12
|
+
import { currentRequest, getWorkUnit } from '../../request/context'
|
|
13
13
|
|
|
14
|
-
const REQUEST_PATH_KEY = Symbol.for('pnext.requestPath')
|
|
15
|
-
const INSPECT_CUSTOM = Symbol.for('nodejs.util.inspect.custom')
|
|
16
|
-
const requestApiScope = new AsyncLocalStorage<boolean>()
|
|
14
|
+
const REQUEST_PATH_KEY = Symbol.for('pnext.requestPath')
|
|
15
|
+
const INSPECT_CUSTOM = Symbol.for('nodejs.util.inspect.custom')
|
|
16
|
+
const requestApiScope = new AsyncLocalStorage<boolean>()
|
|
17
17
|
|
|
18
18
|
/** Run one after() task with its request-API capability. */
|
|
19
19
|
export function runWithAfterRequestApiScope<T>(allowed: boolean, callback: () => T): T {
|
|
20
|
-
return requestApiScope.run(allowed, callback)
|
|
20
|
+
return requestApiScope.run(allowed, callback)
|
|
21
21
|
}
|
|
22
22
|
|
|
23
23
|
/** Whether the current after() task may use request APIs. */
|
|
24
24
|
export function afterRequestApisAllowed(): boolean {
|
|
25
|
-
return requestApiScope.getStore() === true
|
|
25
|
+
return requestApiScope.getStore() === true
|
|
26
26
|
}
|
|
27
27
|
|
|
28
28
|
/** Stash the request pathname on the active work unit for after() diagnostics. */
|
|
29
29
|
export function recordRequestPath(pathname: string): void {
|
|
30
|
-
const unit = getWorkUnit()
|
|
31
|
-
if (unit) (unit.compat ??= {})[REQUEST_PATH_KEY] = pathname
|
|
30
|
+
const unit = getWorkUnit()
|
|
31
|
+
if (unit) (unit.compat ??= {})[REQUEST_PATH_KEY] = pathname
|
|
32
32
|
}
|
|
33
33
|
|
|
34
34
|
function currentRoutePath(): string {
|
|
35
|
-
const recorded = getWorkUnit()?.compat?.[REQUEST_PATH_KEY] as string | undefined
|
|
36
|
-
if (recorded) return recorded
|
|
35
|
+
const recorded = getWorkUnit()?.compat?.[REQUEST_PATH_KEY] as string | undefined
|
|
36
|
+
if (recorded) return recorded
|
|
37
37
|
try {
|
|
38
|
-
const request = currentRequest()
|
|
39
|
-
return request ? new URL(request.url).pathname : ''
|
|
38
|
+
const request = currentRequest()
|
|
39
|
+
return request ? new URL(request.url).pathname : ''
|
|
40
40
|
} catch {
|
|
41
|
-
return ''
|
|
41
|
+
return ''
|
|
42
42
|
}
|
|
43
43
|
}
|
|
44
44
|
|
|
@@ -47,14 +47,14 @@ function currentRoutePath(): string {
|
|
|
47
47
|
// with no misleading pnext-internal stack frame (the suite matches the message
|
|
48
48
|
// text exactly). Typed as Error so throw sites satisfy `only-throw-error`.
|
|
49
49
|
export function lifecycleError(message: string): Error {
|
|
50
|
-
const formatted = `Error: ${message}
|
|
50
|
+
const formatted = `Error: ${message}`
|
|
51
51
|
return {
|
|
52
52
|
name: 'Error',
|
|
53
53
|
message,
|
|
54
54
|
stack: formatted,
|
|
55
55
|
toString: () => formatted,
|
|
56
56
|
[INSPECT_CUSTOM]: () => formatted,
|
|
57
|
-
} as unknown as Error
|
|
57
|
+
} as unknown as Error
|
|
58
58
|
}
|
|
59
59
|
|
|
60
60
|
/**
|
|
@@ -62,16 +62,16 @@ export function lifecycleError(message: string): Error {
|
|
|
62
62
|
* detached after() callback (render phase, no live request scope). Returns
|
|
63
63
|
* without throwing in every legal case so callers proceed to the normal path.
|
|
64
64
|
*/
|
|
65
|
-
export function assertRequestApiAllowedInAfter(
|
|
66
|
-
|
|
67
|
-
|
|
65
|
+
export function assertRequestApiAllowedInAfter(
|
|
66
|
+
api: 'headers()' | 'cookies()' | 'connection()',
|
|
67
|
+
): void {
|
|
68
|
+
const unit = getWorkUnit()
|
|
69
|
+
if (unit?.phase !== 'after') return
|
|
68
70
|
// Rendering keeps a synthetic request context during prerendering, so the
|
|
69
71
|
// explicit after-task capability—not merely currentRequest()—defines this.
|
|
70
|
-
if (afterRequestApisAllowed() && currentRequest()) return
|
|
71
|
-
const suffix = api === 'connection()' ? '' : ' This is not supported.'
|
|
72
|
-
throw lifecycleError(
|
|
73
|
-
`Route ${currentRoutePath()} used \`${api}\` inside \`after()\`.${suffix}`,
|
|
74
|
-
);
|
|
72
|
+
if (afterRequestApisAllowed() && currentRequest()) return
|
|
73
|
+
const suffix = api === 'connection()' ? '' : ' This is not supported.'
|
|
74
|
+
throw lifecycleError(`Route ${currentRoutePath()} used \`${api}\` inside \`after()\`.${suffix}`)
|
|
75
75
|
}
|
|
76
76
|
|
|
77
77
|
/**
|
|
@@ -79,8 +79,8 @@ export function assertRequestApiAllowedInAfter(api: 'headers()' | 'cookies()' |
|
|
|
79
79
|
* every context (Next's exact message names the route + method).
|
|
80
80
|
*/
|
|
81
81
|
export function assertDraftModeToggleAllowedInAfter(method: 'enable' | 'disable'): void {
|
|
82
|
-
if (getWorkUnit()?.phase !== 'after') return
|
|
82
|
+
if (getWorkUnit()?.phase !== 'after') return
|
|
83
83
|
throw lifecycleError(
|
|
84
84
|
`Route ${currentRoutePath()} used "draftMode().${method}()" inside \`after()\``,
|
|
85
|
-
)
|
|
85
|
+
)
|
|
86
86
|
}
|
|
@@ -10,42 +10,41 @@
|
|
|
10
10
|
// handed to it so a serverless platform keeps the invocation alive until it settles. When no work unit
|
|
11
11
|
// is active, the task runs on the microtask queue as a best-effort fallback.
|
|
12
12
|
|
|
13
|
-
import { captureCacheScope } from '../../cache
|
|
13
|
+
import { captureCacheScope } from '../../request/cache'
|
|
14
14
|
import {
|
|
15
15
|
captureRequestScope,
|
|
16
16
|
currentRequest,
|
|
17
17
|
getWorkUnit,
|
|
18
18
|
queueAfterTask,
|
|
19
19
|
setPhase,
|
|
20
|
-
} from '../../request/context'
|
|
21
|
-
import { currentRenderCacheMeta } from '../cache/revalidate'
|
|
20
|
+
} from '../../request/context'
|
|
21
|
+
import { currentRenderCacheMeta } from '../cache/revalidate'
|
|
22
22
|
import {
|
|
23
23
|
afterRequestApisAllowed,
|
|
24
24
|
recordRequestPath,
|
|
25
25
|
runWithAfterRequestApiScope,
|
|
26
|
-
} from './after-scope'
|
|
26
|
+
} from './after-scope'
|
|
27
27
|
|
|
28
|
-
type AfterTask<T> = Promise<T> | (() => T | Promise<T>)
|
|
28
|
+
type AfterTask<T> = Promise<T> | (() => T | Promise<T>)
|
|
29
29
|
|
|
30
|
-
const requestContextSymbol = Symbol.for('@next/request-context')
|
|
30
|
+
const requestContextSymbol = Symbol.for('@next/request-context')
|
|
31
31
|
|
|
32
32
|
/**
|
|
33
33
|
* Marks an error that escaped an after() task during a BUILD prerender. The build's per-route
|
|
34
34
|
* skip-and-warn catch reads it to fail the build instead of silently degrading the route to dynamic -
|
|
35
35
|
* Next fails the build when after() throws while prerendering.
|
|
36
36
|
*/
|
|
37
|
-
export const AFTER_PRERENDER_ERROR = Symbol.for('pnext.afterPrerenderError')
|
|
37
|
+
export const AFTER_PRERENDER_ERROR = Symbol.for('pnext.afterPrerenderError')
|
|
38
38
|
|
|
39
39
|
interface VercelRequestContext {
|
|
40
|
-
get?: () => { waitUntil?: (promise: Promise<unknown>) => void } | undefined
|
|
40
|
+
get?: () => { waitUntil?: (promise: Promise<unknown>) => void } | undefined
|
|
41
41
|
}
|
|
42
42
|
|
|
43
43
|
/** The platform waitUntil, when a Vercel-style request context is present. */
|
|
44
44
|
function platformWaitUntil(): ((promise: Promise<unknown>) => void) | undefined {
|
|
45
45
|
const holder = (globalThis as Record<symbol, unknown>)[requestContextSymbol] as
|
|
46
|
-
|
|
|
47
|
-
|
|
48
|
-
return holder?.get?.()?.waitUntil;
|
|
46
|
+
VercelRequestContext | undefined
|
|
47
|
+
return holder?.get?.()?.waitUntil
|
|
49
48
|
}
|
|
50
49
|
|
|
51
50
|
/**
|
|
@@ -62,13 +61,15 @@ async function runAfterTask<T>(task: AfterTask<T>): Promise<void> {
|
|
|
62
61
|
error => {
|
|
63
62
|
console.error(
|
|
64
63
|
`An error occurred in a function passed to \`after()\`: ${
|
|
65
|
-
error instanceof Error
|
|
64
|
+
error instanceof Error
|
|
65
|
+
? (error.stack ?? `${error.name}: ${error.message}`)
|
|
66
|
+
: String(error)
|
|
66
67
|
}`,
|
|
67
|
-
)
|
|
68
|
+
)
|
|
68
69
|
},
|
|
69
|
-
)
|
|
70
|
-
platformWaitUntil()?.(promise)
|
|
71
|
-
await promise
|
|
70
|
+
)
|
|
71
|
+
platformWaitUntil()?.(promise)
|
|
72
|
+
await promise
|
|
72
73
|
}
|
|
73
74
|
|
|
74
75
|
/**
|
|
@@ -79,20 +80,23 @@ async function runAfterTask<T>(task: AfterTask<T>): Promise<void> {
|
|
|
79
80
|
* rejection) so a serverless platform keeps the invocation alive until it ends.
|
|
80
81
|
*/
|
|
81
82
|
async function settleAfterTask<T>(task: AfterTask<T>): Promise<void> {
|
|
82
|
-
const promise = Promise.resolve().then(() =>
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
83
|
+
const promise = Promise.resolve().then(() => (typeof task === 'function' ? task() : task))
|
|
84
|
+
platformWaitUntil()?.(
|
|
85
|
+
promise.then(
|
|
86
|
+
() => undefined,
|
|
87
|
+
() => undefined,
|
|
88
|
+
),
|
|
89
|
+
)
|
|
86
90
|
try {
|
|
87
|
-
await promise
|
|
91
|
+
await promise
|
|
88
92
|
} catch (error) {
|
|
89
93
|
// Tag so the build's per-route catch fails the build rather than degrading
|
|
90
94
|
// the route to dynamic. The tag rides the same error object up through the
|
|
91
95
|
// static after-drain (which re-throws it) to cli/build.ts.
|
|
92
96
|
if (error && typeof error === 'object') {
|
|
93
|
-
(error as Record<symbol, unknown>)[AFTER_PRERENDER_ERROR] = true
|
|
97
|
+
;(error as Record<symbol, unknown>)[AFTER_PRERENDER_ERROR] = true
|
|
94
98
|
}
|
|
95
|
-
throw error
|
|
99
|
+
throw error
|
|
96
100
|
}
|
|
97
101
|
}
|
|
98
102
|
|
|
@@ -108,9 +112,9 @@ function runInPrerenderAfterPhase<T>(
|
|
|
108
112
|
requestApisAllowed: boolean,
|
|
109
113
|
fn: () => T,
|
|
110
114
|
): T {
|
|
111
|
-
setPhase('after')
|
|
112
|
-
if (route) recordRequestPath(route)
|
|
113
|
-
return runWithAfterRequestApiScope(requestApisAllowed, fn)
|
|
115
|
+
setPhase('after')
|
|
116
|
+
if (route) recordRequestPath(route)
|
|
117
|
+
return runWithAfterRequestApiScope(requestApisAllowed, fn)
|
|
114
118
|
}
|
|
115
119
|
|
|
116
120
|
/**
|
|
@@ -124,50 +128,49 @@ export function after<T>(task: AfterTask<T>): void {
|
|
|
124
128
|
// its after() callbacks run detached, so headers()/cookies()/connection() throw Next's "used inside
|
|
125
129
|
// after()" error. The phase distinguishes them - renders stay 'render', handlers set routeKind,
|
|
126
130
|
// actions set phase 'action'.
|
|
127
|
-
const unit = getWorkUnit()
|
|
131
|
+
const unit = getWorkUnit()
|
|
128
132
|
const isHandlerOrAction =
|
|
129
|
-
unit?.phase === 'action' ||
|
|
130
|
-
unit?.phase === 'middleware' ||
|
|
131
|
-
unit?.routeKind === 'route-handler';
|
|
133
|
+
unit?.phase === 'action' || unit?.phase === 'middleware' || unit?.routeKind === 'route-handler'
|
|
132
134
|
const isNestedRequestAfter =
|
|
133
|
-
unit?.phase === 'after' && Boolean(currentRequest()) && afterRequestApisAllowed()
|
|
135
|
+
unit?.phase === 'after' && Boolean(currentRequest()) && afterRequestApisAllowed()
|
|
134
136
|
// Handlers and actions capture the live request scope so headers()/cookies() keep working inside their
|
|
135
137
|
// after() callbacks. A page render (or generateMetadata) instead captures only the React cache()
|
|
136
138
|
// scope, so cache() shares the render's memo cache in the callback while the request scope stays
|
|
137
139
|
// detached and headers()/cookies()/connection() still throw Next's "used inside after()" error.
|
|
138
140
|
const withScope =
|
|
139
|
-
isHandlerOrAction || isNestedRequestAfter
|
|
140
|
-
? captureRequestScope()
|
|
141
|
-
: captureCacheScope();
|
|
141
|
+
isHandlerOrAction || isNestedRequestAfter ? captureRequestScope() : captureCacheScope()
|
|
142
142
|
// Static-generation render (build prerender or ISR regeneration): there is
|
|
143
143
|
// no response close to flush against, so the task queues on the render's
|
|
144
144
|
// cache meta and the render drains it once produce settles (Next waits for
|
|
145
145
|
// after() during prerendering). Build prerenders propagate rejections so a
|
|
146
146
|
// throwing after() fails the page's prerender (next-after-app-static);
|
|
147
147
|
// runtime regenerations log-and-continue like every other runtime path.
|
|
148
|
-
const meta = currentRenderCacheMeta()
|
|
148
|
+
const meta = currentRenderCacheMeta()
|
|
149
149
|
if (meta && (meta.prerender === true || meta.blockingStaleFetches === true)) {
|
|
150
150
|
// A build/ISR prerender runs with no work unit, so the top-level routeKind check above cannot tell a
|
|
151
151
|
// route-handler prerender from a page one - meta.handler does. A handler prerender captures the live
|
|
152
152
|
// request scope so request APIs resolve inside after(); a page prerender keeps only the cache scope
|
|
153
153
|
// so they throw "used inside after()".
|
|
154
|
-
const prerenderScope = meta.handler ? captureRequestScope() : withScope
|
|
155
|
-
const route = meta.route
|
|
156
|
-
const requestApisAllowed = Boolean(meta.handler)
|
|
154
|
+
const prerenderScope = meta.handler ? captureRequestScope() : withScope
|
|
155
|
+
const route = meta.route
|
|
156
|
+
const requestApisAllowed = Boolean(meta.handler)
|
|
157
157
|
const run = meta.prerender
|
|
158
158
|
? () =>
|
|
159
159
|
prerenderScope(() =>
|
|
160
160
|
runInPrerenderAfterPhase(route, requestApisAllowed, () => settleAfterTask(task)),
|
|
161
161
|
)
|
|
162
|
-
: () =>
|
|
163
|
-
|
|
164
|
-
|
|
162
|
+
: () =>
|
|
163
|
+
prerenderScope(() =>
|
|
164
|
+
runWithAfterRequestApiScope(requestApisAllowed, () => runAfterTask(task)),
|
|
165
|
+
)
|
|
166
|
+
;(meta.afterTasks ??= []).push(run)
|
|
167
|
+
return
|
|
165
168
|
}
|
|
166
|
-
const requestApisAllowed = Boolean(isHandlerOrAction || isNestedRequestAfter)
|
|
169
|
+
const requestApisAllowed = Boolean(isHandlerOrAction || isNestedRequestAfter)
|
|
167
170
|
const queued = queueAfterTask(() =>
|
|
168
171
|
withScope(() => runWithAfterRequestApiScope(requestApisAllowed, () => runAfterTask(task))),
|
|
169
|
-
)
|
|
170
|
-
if (queued) return
|
|
172
|
+
)
|
|
173
|
+
if (queued) return
|
|
171
174
|
// No active work unit (e.g. called at module scope): best-effort microtask.
|
|
172
|
-
void withScope(() => runWithAfterRequestApiScope(requestApisAllowed, () => runAfterTask(task)))
|
|
175
|
+
void withScope(() => runWithAfterRequestApiScope(requestApisAllowed, () => runAfterTask(task)))
|
|
173
176
|
}
|