@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
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* The dispatch half of the client action runtime: argument encoding, the POST wire, redirect/refresh/
|
|
3
3
|
* error semantics, action-return revival, and the route error.js overlay body. Nothing here can run
|
|
4
|
-
* before an action actually fires, so
|
|
4
|
+
* before an action actually fires, so client.ts reaches it through a dynamic import() and this
|
|
5
5
|
* whole module lands in a deferred chunk.
|
|
6
6
|
*
|
|
7
7
|
* Wire shape (see compat/actions/protocol.ts): POST to the CURRENT page URL with the action id in the
|
|
@@ -13,15 +13,15 @@
|
|
|
13
13
|
* content-type throws the "unexpected response" error React shows; a 404 with the not-found marker
|
|
14
14
|
* swaps in the server-rendered 404 page.
|
|
15
15
|
*/
|
|
16
|
-
import { h, render, type ComponentType } from 'preact'
|
|
16
|
+
import { h, render, type ComponentType } from 'preact'
|
|
17
17
|
import {
|
|
18
18
|
blockJavascriptUrl,
|
|
19
19
|
evictClientRouterCache,
|
|
20
20
|
rearmVisiblePrefetches,
|
|
21
21
|
softNavigate,
|
|
22
|
-
} from '../../
|
|
23
|
-
import { onNavigationStart } from '../../
|
|
24
|
-
import { UnrecognizedActionError } from './unrecognized-error'
|
|
22
|
+
} from '../../client/router'
|
|
23
|
+
import { onNavigationStart } from '../../client/router/events'
|
|
24
|
+
import { UnrecognizedActionError } from './unrecognized-error'
|
|
25
25
|
import {
|
|
26
26
|
ACTION_ID_FIELD,
|
|
27
27
|
ARRAY_BUFFER_MARKER,
|
|
@@ -32,42 +32,42 @@ import {
|
|
|
32
32
|
reviveBinaryMarker,
|
|
33
33
|
tagActionError,
|
|
34
34
|
type ActionFunction,
|
|
35
|
-
} from './
|
|
36
|
-
|
|
37
|
-
const ACTION_ID_HEADER = 'next-action'
|
|
38
|
-
const ACTION_REDIRECT_HEADER = 'x-action-redirect'
|
|
39
|
-
const ACTION_REFRESH_HEADER = 'x-pnext-action-refresh'
|
|
40
|
-
const NEXT_ACTION_REVALIDATED_HEADER = 'x-action-revalidated'
|
|
41
|
-
const ACTION_NOT_FOUND_HEADER = 'x-pnext-action-not-found'
|
|
42
|
-
const NEXT_ACTION_NOT_FOUND_HEADER = 'x-nextjs-action-not-found'
|
|
43
|
-
const ACTION_ERROR_DIGEST_HEADER = 'x-pnext-action-error-digest'
|
|
44
|
-
const ACTION_FLIGHT_CONTENT_TYPE = 'text/x-component'
|
|
45
|
-
const ACTION_ARGS_FIELD = '$pnext_args'
|
|
46
|
-
const ARG_FORMDATA_MARKER = '$$pnext_fd'
|
|
47
|
-
const ARG_FILE_MARKER = '$$pnext_file'
|
|
48
|
-
const ARG_FORMDATA_PREFIX = '$pnext_fd_'
|
|
49
|
-
const ARG_FILE_PREFIX = '$pnext_file_'
|
|
50
|
-
const ARG_ACTION_MARKER = '$$pnext_action_ref'
|
|
51
|
-
const REDIRECT_TYPE_HEADER = 'x-pnext-redirect-type'
|
|
52
|
-
const TEMP_REF_MARKER = '$$pnext_temp_ref'
|
|
53
|
-
const ACTION_STREAM_CONTENT_TYPE = 'x-component/pnext-stream'
|
|
54
|
-
const ACTION_ELEMENT_MARKER = '$$pnext_element'
|
|
55
|
-
const ACTION_ELEMENT_HTML_MARKER = '$$pnext_element_html'
|
|
35
|
+
} from './shared'
|
|
36
|
+
|
|
37
|
+
const ACTION_ID_HEADER = 'next-action'
|
|
38
|
+
const ACTION_REDIRECT_HEADER = 'x-action-redirect'
|
|
39
|
+
const ACTION_REFRESH_HEADER = 'x-pnext-action-refresh'
|
|
40
|
+
const NEXT_ACTION_REVALIDATED_HEADER = 'x-action-revalidated'
|
|
41
|
+
const ACTION_NOT_FOUND_HEADER = 'x-pnext-action-not-found'
|
|
42
|
+
const NEXT_ACTION_NOT_FOUND_HEADER = 'x-nextjs-action-not-found'
|
|
43
|
+
const ACTION_ERROR_DIGEST_HEADER = 'x-pnext-action-error-digest'
|
|
44
|
+
const ACTION_FLIGHT_CONTENT_TYPE = 'text/x-component'
|
|
45
|
+
const ACTION_ARGS_FIELD = '$pnext_args'
|
|
46
|
+
const ARG_FORMDATA_MARKER = '$$pnext_fd'
|
|
47
|
+
const ARG_FILE_MARKER = '$$pnext_file'
|
|
48
|
+
const ARG_FORMDATA_PREFIX = '$pnext_fd_'
|
|
49
|
+
const ARG_FILE_PREFIX = '$pnext_file_'
|
|
50
|
+
const ARG_ACTION_MARKER = '$$pnext_action_ref'
|
|
51
|
+
const REDIRECT_TYPE_HEADER = 'x-pnext-redirect-type'
|
|
52
|
+
const TEMP_REF_MARKER = '$$pnext_temp_ref'
|
|
53
|
+
const ACTION_STREAM_CONTENT_TYPE = 'x-component/pnext-stream'
|
|
54
|
+
const ACTION_ELEMENT_MARKER = '$$pnext_element'
|
|
55
|
+
const ACTION_ELEMENT_HTML_MARKER = '$$pnext_element_html'
|
|
56
56
|
|
|
57
57
|
const MASKED_ACTION_ERROR =
|
|
58
|
-
'Minified React error #441; visit https://react.dev/errors/441 for the full message or use the non-minified dev environment for full errors and additional helpful warnings.'
|
|
59
|
-
const UNEXPECTED_RESPONSE = 'An unexpected response was received from the server.'
|
|
58
|
+
'Minified React error #441; visit https://react.dev/errors/441 for the full message or use the non-minified dev environment for full errors and additional helpful warnings.'
|
|
59
|
+
const UNEXPECTED_RESPONSE = 'An unexpected response was received from the server.'
|
|
60
60
|
|
|
61
61
|
// Only compared within a single dispatch, so starting the count when this
|
|
62
62
|
// module loads (rather than at page load) changes nothing.
|
|
63
|
-
let clientNavigationGeneration = 0
|
|
63
|
+
let clientNavigationGeneration = 0
|
|
64
64
|
onNavigationStart(() => {
|
|
65
|
-
clientNavigationGeneration += 1
|
|
66
|
-
})
|
|
65
|
+
clientNavigationGeneration += 1
|
|
66
|
+
})
|
|
67
67
|
|
|
68
68
|
interface ActionFlightPayload {
|
|
69
|
-
a?: unknown
|
|
70
|
-
f?: { kind?: string; html?: string; url?: string } | ''
|
|
69
|
+
a?: unknown
|
|
70
|
+
f?: { kind?: string; html?: string; url?: string } | ''
|
|
71
71
|
}
|
|
72
72
|
|
|
73
73
|
// ---------------------------------------------------------------------------
|
|
@@ -75,69 +75,67 @@ interface ActionFlightPayload {
|
|
|
75
75
|
// ---------------------------------------------------------------------------
|
|
76
76
|
|
|
77
77
|
function isPlainSerializable(value: unknown, depth = 0): boolean {
|
|
78
|
-
if (value === null || value === undefined) return true
|
|
79
|
-
const type = typeof value
|
|
80
|
-
if (type === 'string' || type === 'number' || type === 'boolean') return true
|
|
81
|
-
if (type !== 'object') return false
|
|
82
|
-
if (depth > 32) return false
|
|
83
|
-
if (isBinary(value)) return true
|
|
84
|
-
if (Array.isArray(value)) return value.every(item => isPlainSerializable(item, depth + 1))
|
|
85
|
-
if (value instanceof FormData || isFileLike(value)) return false
|
|
86
|
-
if (value instanceof Date) return true
|
|
87
|
-
if (typeof (value as PromiseLike<unknown>).then === 'function') return true
|
|
78
|
+
if (value === null || value === undefined) return true
|
|
79
|
+
const type = typeof value
|
|
80
|
+
if (type === 'string' || type === 'number' || type === 'boolean') return true
|
|
81
|
+
if (type !== 'object') return false
|
|
82
|
+
if (depth > 32) return false
|
|
83
|
+
if (isBinary(value)) return true // marker-encoded on the wire
|
|
84
|
+
if (Array.isArray(value)) return value.every(item => isPlainSerializable(item, depth + 1))
|
|
85
|
+
if (value instanceof FormData || isFileLike(value)) return false // handled by caller
|
|
86
|
+
if (value instanceof Date) return true
|
|
87
|
+
if (typeof (value as PromiseLike<unknown>).then === 'function') return true
|
|
88
88
|
if (value instanceof Map) {
|
|
89
89
|
return [...value.entries()].every(
|
|
90
90
|
([k, v]) => isPlainSerializable(k, depth + 1) && isPlainSerializable(v, depth + 1),
|
|
91
|
-
)
|
|
91
|
+
)
|
|
92
92
|
}
|
|
93
|
-
if (value instanceof Set) return [...value].every(item => isPlainSerializable(item, depth + 1))
|
|
94
|
-
const proto = Object.getPrototypeOf(value) as object | null
|
|
95
|
-
if (proto !== Object.prototype && proto !== null) return false
|
|
93
|
+
if (value instanceof Set) return [...value].every(item => isPlainSerializable(item, depth + 1))
|
|
94
|
+
const proto = Object.getPrototypeOf(value) as object | null
|
|
95
|
+
if (proto !== Object.prototype && proto !== null) return false
|
|
96
96
|
return Object.values(value as Record<string, unknown>).every(item =>
|
|
97
97
|
isPlainSerializable(item, depth + 1),
|
|
98
|
-
)
|
|
98
|
+
)
|
|
99
99
|
}
|
|
100
100
|
|
|
101
101
|
function isFileLike(value: unknown): value is Blob {
|
|
102
|
-
return typeof Blob !== 'undefined' && value instanceof Blob
|
|
102
|
+
return typeof Blob !== 'undefined' && value instanceof Blob
|
|
103
103
|
}
|
|
104
104
|
|
|
105
105
|
function isBinary(value: unknown): value is ArrayBufferView | ArrayBuffer {
|
|
106
|
-
return (
|
|
107
|
-
value instanceof ArrayBuffer || (ArrayBuffer.isView(value) && !(value instanceof DataView))
|
|
108
|
-
);
|
|
106
|
+
return value instanceof ArrayBuffer || (ArrayBuffer.isView(value) && !(value instanceof DataView))
|
|
109
107
|
}
|
|
110
108
|
|
|
111
109
|
function bytesToBase64(bytes: Uint8Array): string {
|
|
112
110
|
if (typeof btoa === 'function') {
|
|
113
|
-
let binary = ''
|
|
114
|
-
for (const byte of bytes) binary += String.fromCharCode(byte)
|
|
115
|
-
return btoa(binary)
|
|
111
|
+
let binary = ''
|
|
112
|
+
for (const byte of bytes) binary += String.fromCharCode(byte)
|
|
113
|
+
return btoa(binary)
|
|
116
114
|
}
|
|
117
|
-
return Buffer.from(bytes).toString('base64')
|
|
115
|
+
return Buffer.from(bytes).toString('base64')
|
|
118
116
|
}
|
|
119
117
|
|
|
120
118
|
function encodeBinaryValue(value: ArrayBufferView | ArrayBuffer): unknown {
|
|
121
119
|
if (value instanceof ArrayBuffer) {
|
|
122
|
-
return { [ARRAY_BUFFER_MARKER]: bytesToBase64(new Uint8Array(value)) }
|
|
120
|
+
return { [ARRAY_BUFFER_MARKER]: bytesToBase64(new Uint8Array(value)) }
|
|
123
121
|
}
|
|
124
|
-
const bytes = new Uint8Array(value.buffer, value.byteOffset, value.byteLength)
|
|
125
|
-
return { [TYPED_ARRAY_MARKER]: { type: value.constructor.name, data: bytesToBase64(bytes) } }
|
|
122
|
+
const bytes = new Uint8Array(value.buffer, value.byteOffset, value.byteLength)
|
|
123
|
+
return { [TYPED_ARRAY_MARKER]: { type: value.constructor.name, data: bytesToBase64(bytes) } }
|
|
126
124
|
}
|
|
127
125
|
|
|
128
126
|
function containsSpecial(value: unknown): boolean {
|
|
129
|
-
if (value instanceof FormData || isFileLike(value)) return true
|
|
130
|
-
if (Array.isArray(value)) return value.some(containsSpecial)
|
|
127
|
+
if (value instanceof FormData || isFileLike(value)) return true
|
|
128
|
+
if (Array.isArray(value)) return value.some(containsSpecial)
|
|
131
129
|
if (value !== null && typeof value === 'object') {
|
|
132
|
-
return Object.values(value as Record<string, unknown>).some(containsSpecial)
|
|
130
|
+
return Object.values(value as Record<string, unknown>).some(containsSpecial)
|
|
133
131
|
}
|
|
134
|
-
return false
|
|
132
|
+
return false
|
|
135
133
|
}
|
|
136
134
|
|
|
137
135
|
function isActionReference(value: unknown): value is ActionFunction {
|
|
138
136
|
return (
|
|
139
137
|
typeof value === 'function' && typeof (value as ActionFunction).$$pnextActionId === 'string'
|
|
140
|
-
)
|
|
138
|
+
)
|
|
141
139
|
}
|
|
142
140
|
|
|
143
141
|
/**
|
|
@@ -146,16 +144,16 @@ function isActionReference(value: unknown): value is ActionFunction {
|
|
|
146
144
|
* undefined, and promises (awaited, like React's encodeReply).
|
|
147
145
|
*/
|
|
148
146
|
function isThenable(value: unknown): value is PromiseLike<unknown> {
|
|
149
|
-
return Boolean(value) && typeof (value as PromiseLike<unknown>).then === 'function'
|
|
147
|
+
return Boolean(value) && typeof (value as PromiseLike<unknown>).then === 'function'
|
|
150
148
|
}
|
|
151
149
|
|
|
152
150
|
async function encodeSpecialValues(value: unknown): Promise<unknown> {
|
|
153
|
-
if (isActionReference(value)) return { [ARG_ACTION_MARKER]: value.$$pnextActionId }
|
|
154
|
-
if (value === undefined) return { $$pnext_undefined: true }
|
|
155
|
-
if (isBinary(value)) return encodeBinaryValue(value)
|
|
156
|
-
if (value instanceof Date) return { $$pnext_date: value.toISOString() }
|
|
151
|
+
if (isActionReference(value)) return { [ARG_ACTION_MARKER]: value.$$pnextActionId }
|
|
152
|
+
if (value === undefined) return { $$pnext_undefined: true }
|
|
153
|
+
if (isBinary(value)) return encodeBinaryValue(value)
|
|
154
|
+
if (value instanceof Date) return { $$pnext_date: value.toISOString() }
|
|
157
155
|
if (isThenable(value)) {
|
|
158
|
-
return encodeSpecialValues(await value)
|
|
156
|
+
return encodeSpecialValues(await value)
|
|
159
157
|
}
|
|
160
158
|
if (value instanceof Map) {
|
|
161
159
|
return {
|
|
@@ -165,48 +163,48 @@ async function encodeSpecialValues(value: unknown): Promise<unknown> {
|
|
|
165
163
|
await encodeSpecialValues(v),
|
|
166
164
|
]),
|
|
167
165
|
),
|
|
168
|
-
}
|
|
166
|
+
}
|
|
169
167
|
}
|
|
170
168
|
if (value instanceof Set) {
|
|
171
|
-
return { $$pnext_set: await Promise.all([...value].map(encodeSpecialValues)) }
|
|
169
|
+
return { $$pnext_set: await Promise.all([...value].map(encodeSpecialValues)) }
|
|
172
170
|
}
|
|
173
|
-
if (Array.isArray(value)) return Promise.all(value.map(encodeSpecialValues))
|
|
171
|
+
if (Array.isArray(value)) return Promise.all(value.map(encodeSpecialValues))
|
|
174
172
|
if (
|
|
175
173
|
value !== null &&
|
|
176
174
|
typeof value === 'object' &&
|
|
177
175
|
!(value instanceof FormData) &&
|
|
178
176
|
!isFileLike(value)
|
|
179
177
|
) {
|
|
180
|
-
const proto = Object.getPrototypeOf(value) as object | null
|
|
178
|
+
const proto = Object.getPrototypeOf(value) as object | null
|
|
181
179
|
if (proto === Object.prototype || proto === null) {
|
|
182
|
-
const out: Record<string, unknown> = {}
|
|
180
|
+
const out: Record<string, unknown> = {}
|
|
183
181
|
for (const [key, item] of Object.entries(value as Record<string, unknown>)) {
|
|
184
|
-
out[key] = await encodeSpecialValues(item)
|
|
182
|
+
out[key] = await encodeSpecialValues(item)
|
|
185
183
|
}
|
|
186
|
-
return out
|
|
184
|
+
return out
|
|
187
185
|
}
|
|
188
186
|
}
|
|
189
|
-
return value
|
|
187
|
+
return value
|
|
190
188
|
}
|
|
191
189
|
|
|
192
190
|
function assertSerializableArg(value: unknown) {
|
|
193
|
-
if (value instanceof FormData || isFileLike(value)) return
|
|
194
|
-
if (isActionReference(value)) return
|
|
195
|
-
if (isPlainSerializable(value)) return
|
|
191
|
+
if (value instanceof FormData || isFileLike(value)) return
|
|
192
|
+
if (isActionReference(value)) return
|
|
193
|
+
if (isPlainSerializable(value)) return
|
|
196
194
|
// eslint-disable-next-line turbo/no-undeclared-env-vars
|
|
197
|
-
const dev = typeof process !== 'undefined' && process.env.NODE_ENV !== 'production'
|
|
195
|
+
const dev = typeof process !== 'undefined' && process.env.NODE_ENV !== 'production'
|
|
198
196
|
throw tagActionError(
|
|
199
197
|
new Error(
|
|
200
198
|
dev
|
|
201
199
|
? 'Cannot access value on the server. Only plain objects can be passed to Server Actions from Client Components.'
|
|
202
200
|
: MASKED_ACTION_ERROR,
|
|
203
201
|
),
|
|
204
|
-
)
|
|
202
|
+
)
|
|
205
203
|
}
|
|
206
204
|
|
|
207
205
|
interface EncodedBody {
|
|
208
|
-
body: BodyInit | undefined
|
|
209
|
-
contentType?: string
|
|
206
|
+
body: BodyInit | undefined
|
|
207
|
+
contentType?: string
|
|
210
208
|
}
|
|
211
209
|
|
|
212
210
|
/**
|
|
@@ -216,9 +214,9 @@ interface EncodedBody {
|
|
|
216
214
|
* only, never the caller's originals, and the returned map resolves markers on the response.
|
|
217
215
|
*/
|
|
218
216
|
function tagTempRefs(originals: unknown[], encoded: unknown[]): Map<string, object> {
|
|
219
|
-
const registry = new Map<string, object>()
|
|
220
|
-
let counter = 0
|
|
221
|
-
const seen = new Set<unknown>()
|
|
217
|
+
const registry = new Map<string, object>()
|
|
218
|
+
let counter = 0
|
|
219
|
+
const seen = new Set<unknown>()
|
|
222
220
|
const walk = (original: unknown, wire: unknown): void => {
|
|
223
221
|
if (
|
|
224
222
|
original === null ||
|
|
@@ -226,25 +224,25 @@ function tagTempRefs(originals: unknown[], encoded: unknown[]): Map<string, obje
|
|
|
226
224
|
wire === null ||
|
|
227
225
|
typeof wire !== 'object'
|
|
228
226
|
) {
|
|
229
|
-
return
|
|
227
|
+
return
|
|
230
228
|
}
|
|
231
|
-
if (seen.has(wire)) return
|
|
232
|
-
seen.add(wire)
|
|
229
|
+
if (seen.has(wire)) return
|
|
230
|
+
seen.add(wire)
|
|
233
231
|
if (Array.isArray(original) && Array.isArray(wire)) {
|
|
234
|
-
for (let i = 0; i < wire.length; i++) walk(original[i], wire[i])
|
|
235
|
-
return
|
|
232
|
+
for (let i = 0; i < wire.length; i++) walk(original[i], wire[i])
|
|
233
|
+
return
|
|
236
234
|
}
|
|
237
|
-
const proto = Object.getPrototypeOf(original) as object | null
|
|
238
|
-
if (proto !== Object.prototype && proto !== null) return
|
|
239
|
-
const id = `t${counter++}
|
|
240
|
-
registry.set(id, original)
|
|
241
|
-
(wire as Record<string, unknown>)[TEMP_REF_MARKER] = id
|
|
235
|
+
const proto = Object.getPrototypeOf(original) as object | null
|
|
236
|
+
if (proto !== Object.prototype && proto !== null) return
|
|
237
|
+
const id = `t${counter++}`
|
|
238
|
+
registry.set(id, original)
|
|
239
|
+
;(wire as Record<string, unknown>)[TEMP_REF_MARKER] = id
|
|
242
240
|
for (const key of Object.keys(original)) {
|
|
243
|
-
walk((original as Record<string, unknown>)[key], (wire as Record<string, unknown>)[key])
|
|
241
|
+
walk((original as Record<string, unknown>)[key], (wire as Record<string, unknown>)[key])
|
|
244
242
|
}
|
|
245
|
-
}
|
|
246
|
-
for (let i = 0; i < encoded.length; i++) walk(originals[i], encoded[i])
|
|
247
|
-
return registry
|
|
243
|
+
}
|
|
244
|
+
for (let i = 0; i < encoded.length; i++) walk(originals[i], encoded[i])
|
|
245
|
+
return registry
|
|
248
246
|
}
|
|
249
247
|
|
|
250
248
|
// Temp-ref revival is folded into reviveActionReturn's single walk: a marker
|
|
@@ -259,81 +257,81 @@ function tagTempRefs(originals: unknown[], encoded: unknown[]): Map<string, obje
|
|
|
259
257
|
*/
|
|
260
258
|
function reviveActionReturn(value: unknown, tempRefs?: Map<string, object>): unknown {
|
|
261
259
|
const walk = (input: unknown): unknown => {
|
|
262
|
-
if (input === null || typeof input !== 'object') return input
|
|
263
|
-
if (Array.isArray(input)) return input.map(walk)
|
|
264
|
-
const record = input as Record<string, unknown
|
|
265
|
-
const tempId = record[TEMP_REF_MARKER]
|
|
266
|
-
if (typeof tempId === 'string' && tempRefs?.has(tempId)) return tempRefs.get(tempId)
|
|
267
|
-
const binary = reviveBinaryMarker(record)
|
|
268
|
-
if (binary !== undefined) return binary
|
|
260
|
+
if (input === null || typeof input !== 'object') return input
|
|
261
|
+
if (Array.isArray(input)) return input.map(walk)
|
|
262
|
+
const record = input as Record<string, unknown>
|
|
263
|
+
const tempId = record[TEMP_REF_MARKER]
|
|
264
|
+
if (typeof tempId === 'string' && tempRefs?.has(tempId)) return tempRefs.get(tempId)
|
|
265
|
+
const binary = reviveBinaryMarker(record)
|
|
266
|
+
if (binary !== undefined) return binary
|
|
269
267
|
// A server-rendered element tree the action returned (host elements / async
|
|
270
268
|
// server components): revive it into a renderable node whose innerHTML is
|
|
271
269
|
// the server markup, so useActionState can drop it straight into the tree.
|
|
272
|
-
const elementHtml = record[ACTION_ELEMENT_HTML_MARKER]
|
|
270
|
+
const elementHtml = record[ACTION_ELEMENT_HTML_MARKER]
|
|
273
271
|
if (typeof elementHtml === 'string') {
|
|
274
|
-
return h('span', { dangerouslySetInnerHTML: { __html: elementHtml } })
|
|
272
|
+
return h('span', { dangerouslySetInnerHTML: { __html: elementHtml } })
|
|
275
273
|
}
|
|
276
|
-
const encoded = record[ACTION_ELEMENT_MARKER]
|
|
274
|
+
const encoded = record[ACTION_ELEMENT_MARKER]
|
|
277
275
|
if (encoded && typeof encoded === 'object') {
|
|
278
|
-
const { id, props, key } = encoded as { id?: unknown; props?: unknown; key?: unknown }
|
|
279
|
-
const component = typeof id === 'string' ? actionReturnClientReferences.get(id) : undefined
|
|
276
|
+
const { id, props, key } = encoded as { id?: unknown; props?: unknown; key?: unknown }
|
|
277
|
+
const component = typeof id === 'string' ? actionReturnClientReferences.get(id) : undefined
|
|
280
278
|
if (!component) {
|
|
281
279
|
throw tagActionError(
|
|
282
280
|
new Error(`Missing client component reference for action return: ${String(id)}`),
|
|
283
|
-
)
|
|
281
|
+
)
|
|
284
282
|
}
|
|
285
|
-
const revivedProps = walk(props)
|
|
283
|
+
const revivedProps = walk(props)
|
|
286
284
|
const componentProps =
|
|
287
285
|
revivedProps && typeof revivedProps === 'object'
|
|
288
286
|
? (revivedProps as Record<string, unknown>)
|
|
289
|
-
: {}
|
|
290
|
-
return h(component, key === undefined ? componentProps : { ...componentProps, key })
|
|
287
|
+
: {}
|
|
288
|
+
return h(component, key === undefined ? componentProps : { ...componentProps, key })
|
|
291
289
|
}
|
|
292
|
-
const out: Record<string, unknown> = {}
|
|
293
|
-
for (const [key, item] of Object.entries(record)) out[key] = walk(item)
|
|
294
|
-
return out
|
|
295
|
-
}
|
|
296
|
-
return walk(value)
|
|
290
|
+
const out: Record<string, unknown> = {}
|
|
291
|
+
for (const [key, item] of Object.entries(record)) out[key] = walk(item)
|
|
292
|
+
return out
|
|
293
|
+
}
|
|
294
|
+
return walk(value)
|
|
297
295
|
}
|
|
298
296
|
|
|
299
297
|
async function encodeArgs(
|
|
300
298
|
rawArgs: unknown[],
|
|
301
299
|
): Promise<EncodedBody & { tempRefs?: Map<string, object> }> {
|
|
302
|
-
if (rawArgs.length === 0) return { body: '[]', contentType: 'text/plain;charset=UTF-8' }
|
|
303
|
-
for (const arg of rawArgs) assertSerializableArg(arg)
|
|
304
|
-
const args = await Promise.all(rawArgs.map(encodeSpecialValues))
|
|
300
|
+
if (rawArgs.length === 0) return { body: '[]', contentType: 'text/plain;charset=UTF-8' }
|
|
301
|
+
for (const arg of rawArgs) assertSerializableArg(arg)
|
|
302
|
+
const args = await Promise.all(rawArgs.map(encodeSpecialValues))
|
|
305
303
|
|
|
306
304
|
// The common form shape: exactly one FormData argument posts as-is.
|
|
307
305
|
if (args.length === 1 && args[0] instanceof FormData) {
|
|
308
|
-
return { body: stripActionFields(args[0]) }
|
|
306
|
+
return { body: stripActionFields(args[0]) }
|
|
309
307
|
}
|
|
310
308
|
|
|
311
309
|
if (!args.some(containsSpecial)) {
|
|
312
|
-
const tempRefs = tagTempRefs(rawArgs, args)
|
|
313
|
-
return { body: JSON.stringify(args), contentType: 'text/plain;charset=UTF-8', tempRefs }
|
|
310
|
+
const tempRefs = tagTempRefs(rawArgs, args)
|
|
311
|
+
return { body: JSON.stringify(args), contentType: 'text/plain;charset=UTF-8', tempRefs }
|
|
314
312
|
}
|
|
315
313
|
|
|
316
314
|
// Mixed args: multipart with a JSON descriptor referencing part slots.
|
|
317
|
-
const form = new FormData()
|
|
318
|
-
let formDataSlots = 0
|
|
319
|
-
let fileSlots = 0
|
|
315
|
+
const form = new FormData()
|
|
316
|
+
let formDataSlots = 0
|
|
317
|
+
let fileSlots = 0
|
|
320
318
|
const encoded = args.map(arg => {
|
|
321
319
|
if (arg instanceof FormData) {
|
|
322
|
-
const slot = formDataSlots
|
|
320
|
+
const slot = formDataSlots++
|
|
323
321
|
for (const [key, item] of stripActionFields(arg).entries()) {
|
|
324
|
-
form.append(`${ARG_FORMDATA_PREFIX}${slot}:${key}`, item)
|
|
322
|
+
form.append(`${ARG_FORMDATA_PREFIX}${slot}:${key}`, item)
|
|
325
323
|
}
|
|
326
|
-
return { [ARG_FORMDATA_MARKER]: slot }
|
|
324
|
+
return { [ARG_FORMDATA_MARKER]: slot }
|
|
327
325
|
}
|
|
328
326
|
if (isFileLike(arg)) {
|
|
329
|
-
const slot = fileSlots
|
|
330
|
-
form.append(`${ARG_FILE_PREFIX}${slot}`, arg)
|
|
331
|
-
return { [ARG_FILE_MARKER]: slot }
|
|
327
|
+
const slot = fileSlots++
|
|
328
|
+
form.append(`${ARG_FILE_PREFIX}${slot}`, arg)
|
|
329
|
+
return { [ARG_FILE_MARKER]: slot }
|
|
332
330
|
}
|
|
333
|
-
return arg
|
|
334
|
-
})
|
|
335
|
-
form.append(ACTION_ARGS_FIELD, JSON.stringify(encoded))
|
|
336
|
-
return { body: form }
|
|
331
|
+
return arg
|
|
332
|
+
})
|
|
333
|
+
form.append(ACTION_ARGS_FIELD, JSON.stringify(encoded))
|
|
334
|
+
return { body: form }
|
|
337
335
|
}
|
|
338
336
|
|
|
339
337
|
function stripActionFields(form: FormData): FormData {
|
|
@@ -342,13 +340,13 @@ function stripActionFields(form: FormData): FormData {
|
|
|
342
340
|
!form.has(SUBMIT_ACTION_ID_FIELD) &&
|
|
343
341
|
!form.has(FORM_STATE_FIELD)
|
|
344
342
|
)
|
|
345
|
-
return form
|
|
346
|
-
const stripped = new FormData()
|
|
343
|
+
return form
|
|
344
|
+
const stripped = new FormData()
|
|
347
345
|
for (const [key, item] of form.entries()) {
|
|
348
346
|
if (key !== ACTION_ID_FIELD && key !== SUBMIT_ACTION_ID_FIELD && key !== FORM_STATE_FIELD)
|
|
349
|
-
stripped.append(key, item)
|
|
347
|
+
stripped.append(key, item)
|
|
350
348
|
}
|
|
351
|
-
return stripped
|
|
349
|
+
return stripped
|
|
352
350
|
}
|
|
353
351
|
|
|
354
352
|
// ---------------------------------------------------------------------------
|
|
@@ -356,27 +354,27 @@ function stripActionFields(form: FormData): FormData {
|
|
|
356
354
|
// ---------------------------------------------------------------------------
|
|
357
355
|
|
|
358
356
|
export async function callAction(id: string, args: unknown[]): Promise<unknown> {
|
|
359
|
-
if (typeof Event !== 'undefined' && args.at(-1) instanceof Event) args = args.slice(0, -1)
|
|
360
|
-
const navigationGenerationAtDispatch = clientNavigationGeneration
|
|
361
|
-
const { body, contentType, tempRefs } = await encodeArgs(args)
|
|
357
|
+
if (typeof Event !== 'undefined' && args.at(-1) instanceof Event) args = args.slice(0, -1)
|
|
358
|
+
const navigationGenerationAtDispatch = clientNavigationGeneration
|
|
359
|
+
const { body, contentType, tempRefs } = await encodeArgs(args)
|
|
362
360
|
// accept mirrors Next's flight wire; app code observes it via headers().
|
|
363
361
|
const headers: Record<string, string> = {
|
|
364
362
|
[ACTION_ID_HEADER]: id,
|
|
365
363
|
accept: 'text/x-component',
|
|
366
|
-
}
|
|
367
|
-
if (contentType) headers['content-type'] = contentType
|
|
364
|
+
}
|
|
365
|
+
if (contentType) headers['content-type'] = contentType
|
|
368
366
|
|
|
369
|
-
let response: Response
|
|
367
|
+
let response: Response
|
|
370
368
|
try {
|
|
371
|
-
response = await fetch(location.href, { method: 'POST', headers, body })
|
|
369
|
+
response = await fetch(location.href, { method: 'POST', headers, body })
|
|
372
370
|
} catch (error) {
|
|
373
|
-
throw tagActionError(error)
|
|
371
|
+
throw tagActionError(error)
|
|
374
372
|
}
|
|
375
373
|
|
|
376
374
|
// notFound() thrown inside the action: swap in the server-rendered 404 page.
|
|
377
375
|
if (response.status === 404 && response.headers.get(ACTION_NOT_FOUND_HEADER)) {
|
|
378
|
-
await swapDocument(response)
|
|
379
|
-
return undefined
|
|
376
|
+
await swapDocument(response)
|
|
377
|
+
return undefined
|
|
380
378
|
}
|
|
381
379
|
|
|
382
380
|
// Version skew: the server did not recognize the action id. React routes this
|
|
@@ -384,136 +382,136 @@ export async function callAction(id: string, args: unknown[]): Promise<unknown>
|
|
|
384
382
|
// unstable_isUnrecognizedActionError catches it), NOT a page swap — the 404
|
|
385
383
|
// body here is a plain text/plain message, not renderable HTML.
|
|
386
384
|
if (response.status === 404 && response.headers.get(NEXT_ACTION_NOT_FOUND_HEADER) === '1') {
|
|
387
|
-
const message = await response.text().catch(() => '')
|
|
385
|
+
const message = await response.text().catch(() => '')
|
|
388
386
|
throw tagActionError(
|
|
389
387
|
new UnrecognizedActionError(
|
|
390
388
|
message ||
|
|
391
389
|
`Server Action "${id}" was not found on the server. \nRead more: https://nextjs.org/docs/messages/failed-to-find-server-action`,
|
|
392
390
|
),
|
|
393
|
-
)
|
|
391
|
+
)
|
|
394
392
|
}
|
|
395
393
|
|
|
396
394
|
// Single-pass redirect: a 303 with no Location (fetch would have followed
|
|
397
395
|
// one) carrying the destination page HTML. Push history and swap the
|
|
398
396
|
// document — no follow-up request.
|
|
399
397
|
if (response.status === 303) {
|
|
400
|
-
const rearm = hasActionRevalidated(response) ? deferActionPrefetches() : undefined
|
|
398
|
+
const rearm = hasActionRevalidated(response) ? deferActionPrefetches() : undefined
|
|
401
399
|
try {
|
|
402
|
-
const target = response.headers.get(ACTION_REDIRECT_HEADER)
|
|
403
|
-
if (target && blockJavascriptUrl(target)) return undefined
|
|
404
|
-
const contentType = response.headers.get('content-type') ?? ''
|
|
405
|
-
const replace = response.headers.get(REDIRECT_TYPE_HEADER) === 'replace'
|
|
400
|
+
const target = response.headers.get(ACTION_REDIRECT_HEADER)
|
|
401
|
+
if (target && blockJavascriptUrl(target)) return undefined
|
|
402
|
+
const contentType = response.headers.get('content-type') ?? ''
|
|
403
|
+
const replace = response.headers.get(REDIRECT_TYPE_HEADER) === 'replace'
|
|
406
404
|
// Single-pass flight redirect: the 303 carries the destination page HTML in
|
|
407
405
|
// the flight envelope. Push history and swap the document in one round trip.
|
|
408
406
|
if (target && contentType.includes(ACTION_FLIGHT_CONTENT_TYPE)) {
|
|
409
|
-
const payload = await readActionFlightPayload(response)
|
|
410
|
-
const flightHtml = payloadHtml(payload)
|
|
407
|
+
const payload = await readActionFlightPayload(response)
|
|
408
|
+
const flightHtml = payloadHtml(payload)
|
|
411
409
|
if (flightHtml) {
|
|
412
|
-
await replaceDocument(flightHtml, target, replace, hasActionRevalidated(response))
|
|
413
|
-
return undefined
|
|
410
|
+
await replaceDocument(flightHtml, target, replace, hasActionRevalidated(response))
|
|
411
|
+
return undefined
|
|
414
412
|
}
|
|
415
|
-
await navigate(target, replace)
|
|
416
|
-
return undefined
|
|
413
|
+
await navigate(target, replace)
|
|
414
|
+
return undefined
|
|
417
415
|
}
|
|
418
416
|
if (target && contentType.includes('text/html')) {
|
|
419
|
-
const html = await response.text()
|
|
420
|
-
await replaceDocument(html, target, replace, hasActionRevalidated(response))
|
|
421
|
-
return undefined
|
|
417
|
+
const html = await response.text()
|
|
418
|
+
await replaceDocument(html, target, replace, hasActionRevalidated(response))
|
|
419
|
+
return undefined
|
|
422
420
|
}
|
|
423
421
|
if (target) {
|
|
424
|
-
await navigate(target, replace)
|
|
425
|
-
return undefined
|
|
422
|
+
await navigate(target, replace)
|
|
423
|
+
return undefined
|
|
426
424
|
}
|
|
427
425
|
} finally {
|
|
428
|
-
rearm?.()
|
|
426
|
+
rearm?.()
|
|
429
427
|
}
|
|
430
428
|
}
|
|
431
429
|
|
|
432
430
|
if (!response.ok) {
|
|
433
|
-
const responseContentType = response.headers.get('content-type') ?? ''
|
|
431
|
+
const responseContentType = response.headers.get('content-type') ?? ''
|
|
434
432
|
if (responseContentType.startsWith('text/plain')) {
|
|
435
|
-
const error = new Error(await response.text()) as Error & { digest?: string }
|
|
433
|
+
const error = new Error(await response.text()) as Error & { digest?: string }
|
|
436
434
|
// The masked prod body hides the message; the digest rides a header so a
|
|
437
435
|
// caught action error still exposes `error.digest` (Next parity).
|
|
438
|
-
const digest = response.headers.get(ACTION_ERROR_DIGEST_HEADER)
|
|
439
|
-
if (digest) error.digest = digest
|
|
440
|
-
throw tagActionError(error)
|
|
436
|
+
const digest = response.headers.get(ACTION_ERROR_DIGEST_HEADER)
|
|
437
|
+
if (digest) error.digest = digest
|
|
438
|
+
throw tagActionError(error)
|
|
441
439
|
}
|
|
442
|
-
throw tagActionError(new Error(UNEXPECTED_RESPONSE))
|
|
440
|
+
throw tagActionError(new Error(UNEXPECTED_RESPONSE))
|
|
443
441
|
}
|
|
444
442
|
|
|
445
|
-
const responseContentType = response.headers.get('content-type') ?? ''
|
|
443
|
+
const responseContentType = response.headers.get('content-type') ?? ''
|
|
446
444
|
// A streamed action return (actions-streaming): the body IS a ReadableStream
|
|
447
445
|
// the caller reads with getReader(). Return it live, unbuffered.
|
|
448
446
|
if (responseContentType.includes(ACTION_STREAM_CONTENT_TYPE)) {
|
|
449
447
|
if (hasActionRevalidated(response)) {
|
|
450
|
-
const rearm = deferActionPrefetches()
|
|
451
|
-
void refresh().finally(rearm)
|
|
448
|
+
const rearm = deferActionPrefetches()
|
|
449
|
+
void refresh().finally(rearm)
|
|
452
450
|
}
|
|
453
|
-
return response.body
|
|
451
|
+
return response.body
|
|
454
452
|
}
|
|
455
453
|
if (responseContentType.includes(ACTION_FLIGHT_CONTENT_TYPE)) {
|
|
456
|
-
const payload = await readActionFlightPayload(response)
|
|
457
|
-
const revalidated = hasActionRevalidated(response)
|
|
458
|
-
const discarded = clientNavigationGeneration !== navigationGenerationAtDispatch
|
|
459
|
-
const rearm = revalidated ? deferActionPrefetches() : undefined
|
|
454
|
+
const payload = await readActionFlightPayload(response)
|
|
455
|
+
const revalidated = hasActionRevalidated(response)
|
|
456
|
+
const discarded = clientNavigationGeneration !== navigationGenerationAtDispatch
|
|
457
|
+
const rearm = revalidated ? deferActionPrefetches() : undefined
|
|
460
458
|
try {
|
|
461
|
-
const target = response.headers.get(ACTION_REDIRECT_HEADER)
|
|
462
|
-
if (target && blockJavascriptUrl(target)) return undefined
|
|
463
|
-
const replace = response.headers.get(REDIRECT_TYPE_HEADER) === 'replace'
|
|
464
|
-
const flightHtml = payloadHtml(payload)
|
|
459
|
+
const target = response.headers.get(ACTION_REDIRECT_HEADER)
|
|
460
|
+
if (target && blockJavascriptUrl(target)) return undefined
|
|
461
|
+
const replace = response.headers.get(REDIRECT_TYPE_HEADER) === 'replace'
|
|
462
|
+
const flightHtml = payloadHtml(payload)
|
|
465
463
|
if (target && flightHtml) {
|
|
466
|
-
await replaceDocument(flightHtml, target, replace, revalidated)
|
|
467
|
-
return undefined
|
|
464
|
+
await replaceDocument(flightHtml, target, replace, revalidated)
|
|
465
|
+
return undefined
|
|
468
466
|
}
|
|
469
467
|
// A completed action's value must reach client state. If navigation
|
|
470
468
|
// superseded it, that value is discarded, so refresh the now-current route.
|
|
471
469
|
if (revalidated && discarded) {
|
|
472
|
-
await refresh()
|
|
470
|
+
await refresh()
|
|
473
471
|
} else if (revalidated && flightHtml && payload.a === undefined) {
|
|
474
|
-
await replaceDocument(flightHtml)
|
|
472
|
+
await replaceDocument(flightHtml)
|
|
475
473
|
} else if (revalidated) {
|
|
476
474
|
// Revalidation always re-renders the current route (Next applies the
|
|
477
475
|
// revalidated flight even when the action returned a value). No
|
|
478
476
|
// embedded HTML to swap in — refresh through the soft-nav pipeline so
|
|
479
477
|
// every active parallel slot (intercepted modals included) refetches.
|
|
480
|
-
await refresh()
|
|
478
|
+
await refresh()
|
|
481
479
|
}
|
|
482
|
-
return reviveActionReturn(payload.a, tempRefs)
|
|
480
|
+
return reviveActionReturn(payload.a, tempRefs)
|
|
483
481
|
} finally {
|
|
484
|
-
rearm?.()
|
|
482
|
+
rearm?.()
|
|
485
483
|
}
|
|
486
484
|
}
|
|
487
485
|
if (!responseContentType.includes('application/json')) {
|
|
488
|
-
throw tagActionError(new Error(UNEXPECTED_RESPONSE))
|
|
486
|
+
throw tagActionError(new Error(UNEXPECTED_RESPONSE))
|
|
489
487
|
}
|
|
490
488
|
|
|
491
|
-
const revalidated = hasActionRevalidated(response)
|
|
492
|
-
const rearm = revalidated ? deferActionPrefetches() : undefined
|
|
489
|
+
const revalidated = hasActionRevalidated(response)
|
|
490
|
+
const rearm = revalidated ? deferActionPrefetches() : undefined
|
|
493
491
|
try {
|
|
494
|
-
const payload = (await response.json()) as { data?: unknown; redirect?: string }
|
|
492
|
+
const payload = (await response.json()) as { data?: unknown; redirect?: string }
|
|
495
493
|
if (typeof payload.redirect === 'string') {
|
|
496
|
-
if (blockJavascriptUrl(payload.redirect)) return undefined
|
|
497
|
-
await navigate(payload.redirect, response.headers.get(REDIRECT_TYPE_HEADER) === 'replace')
|
|
498
|
-
return undefined
|
|
494
|
+
if (blockJavascriptUrl(payload.redirect)) return undefined
|
|
495
|
+
await navigate(payload.redirect, response.headers.get(REDIRECT_TYPE_HEADER) === 'replace')
|
|
496
|
+
return undefined
|
|
499
497
|
}
|
|
500
498
|
// Revive binary, temporary-reference, and client-component element markers.
|
|
501
|
-
const data = reviveActionReturn(payload.data, tempRefs)
|
|
499
|
+
const data = reviveActionReturn(payload.data, tempRefs)
|
|
502
500
|
// Revalidation always re-renders the current route, even when the action
|
|
503
501
|
// returned a value (Next parity: revalidatePath/Tag in an action refreshes
|
|
504
502
|
// every active parallel slot, intercepted modals included).
|
|
505
|
-
if (revalidated) await refresh()
|
|
506
|
-
return data
|
|
503
|
+
if (revalidated) await refresh()
|
|
504
|
+
return data
|
|
507
505
|
} finally {
|
|
508
|
-
rearm?.()
|
|
506
|
+
rearm?.()
|
|
509
507
|
}
|
|
510
508
|
}
|
|
511
509
|
|
|
512
510
|
function deferActionPrefetches(): () => void {
|
|
513
511
|
// The eviction and the later re-arm both queue behind the router runtime's
|
|
514
512
|
// single load promise, so they still land in this order.
|
|
515
|
-
void evictClientRouterCache({ rearmVisiblePrefetches: false })
|
|
516
|
-
return rearmVisiblePrefetches
|
|
513
|
+
void evictClientRouterCache({ rearmVisiblePrefetches: false })
|
|
514
|
+
return rearmVisiblePrefetches
|
|
517
515
|
}
|
|
518
516
|
|
|
519
517
|
/**
|
|
@@ -521,60 +519,65 @@ function deferActionPrefetches(): () => void {
|
|
|
521
519
|
* useActionState shim uses it to restore initial state in a preserved layout.
|
|
522
520
|
*/
|
|
523
521
|
function markActionRedirected(): void {
|
|
524
|
-
const holder = globalThis as { __pnextActionRedirects?: number }
|
|
525
|
-
holder.__pnextActionRedirects = (holder.__pnextActionRedirects ?? 0) + 1
|
|
522
|
+
const holder = globalThis as { __pnextActionRedirects?: number }
|
|
523
|
+
holder.__pnextActionRedirects = (holder.__pnextActionRedirects ?? 0) + 1
|
|
526
524
|
}
|
|
527
525
|
|
|
528
526
|
async function navigate(href: string, replace = false) {
|
|
529
|
-
markActionRedirected()
|
|
530
|
-
const url = new URL(href, location.href)
|
|
527
|
+
markActionRedirected()
|
|
528
|
+
const url = new URL(href, location.href)
|
|
531
529
|
if (url.origin !== location.origin) {
|
|
532
|
-
location.assign(url.href)
|
|
533
|
-
return
|
|
530
|
+
location.assign(url.href)
|
|
531
|
+
return
|
|
534
532
|
}
|
|
535
533
|
try {
|
|
536
|
-
await softNavigate(url.href, replace ? { replace: true } : undefined)
|
|
534
|
+
await softNavigate(url.href, replace ? { replace: true } : undefined)
|
|
537
535
|
} catch (error) {
|
|
538
536
|
// A router failure must never strand an action redirect on the old page —
|
|
539
537
|
// fall back to a full browser navigation (Next's MPA fallback shape).
|
|
540
|
-
console.error(error)
|
|
541
|
-
if (replace) location.replace(url.href)
|
|
542
|
-
else location.assign(url.href)
|
|
538
|
+
console.error(error)
|
|
539
|
+
if (replace) location.replace(url.href)
|
|
540
|
+
else location.assign(url.href)
|
|
543
541
|
}
|
|
544
542
|
}
|
|
545
543
|
|
|
546
544
|
async function refresh() {
|
|
547
545
|
try {
|
|
548
|
-
await softNavigate(location.href, { replace: true, scroll: false })
|
|
546
|
+
await softNavigate(location.href, { replace: true, scroll: false })
|
|
549
547
|
} catch (error) {
|
|
550
548
|
// A failed refresh must not reject the dispatch: the action's return value
|
|
551
549
|
// still has to reach the caller (the refresh is a best-effort re-render).
|
|
552
|
-
console.error(error)
|
|
550
|
+
console.error(error)
|
|
553
551
|
}
|
|
554
552
|
}
|
|
555
553
|
|
|
556
554
|
async function swapDocument(response: Response) {
|
|
557
|
-
const html = await response.text()
|
|
555
|
+
const html = await response.text()
|
|
558
556
|
// Next renders an action's notFound() in place, so a robots meta the page
|
|
559
557
|
// itself declared survives it. Our swap rewrites the document: carry the live
|
|
560
558
|
// tag over, replacing the server's fallback noindex when one was injected.
|
|
561
|
-
const robots = document.querySelector('meta[name="robots"]')?.outerHTML
|
|
562
|
-
const pattern = /<meta[^>]+name=["']robots["'][^>]*\/?>/i
|
|
559
|
+
const robots = document.querySelector('meta[name="robots"]')?.outerHTML
|
|
560
|
+
const pattern = /<meta[^>]+name=["']robots["'][^>]*\/?>/i
|
|
563
561
|
const withRobots = robots
|
|
564
562
|
? pattern.test(html)
|
|
565
563
|
? html.replace(pattern, robots)
|
|
566
564
|
: html.replace('</head>', `${robots}</head>`)
|
|
567
|
-
: html
|
|
568
|
-
await replaceDocument(withRobots)
|
|
565
|
+
: html
|
|
566
|
+
await replaceDocument(withRobots)
|
|
569
567
|
}
|
|
570
568
|
|
|
571
569
|
/**
|
|
572
570
|
* `freshSegments`: the action revalidated, so this HTML is the freshly rendered destination - its
|
|
573
571
|
* layouts carry the write and must replace the live ones instead of being grafted over.
|
|
574
572
|
*/
|
|
575
|
-
async function replaceDocument(
|
|
573
|
+
async function replaceDocument(
|
|
574
|
+
html: string,
|
|
575
|
+
href?: string,
|
|
576
|
+
replace = false,
|
|
577
|
+
freshSegments = false,
|
|
578
|
+
) {
|
|
576
579
|
if (href) {
|
|
577
|
-
markActionRedirected()
|
|
580
|
+
markActionRedirected()
|
|
578
581
|
// Route the single-pass redirect's destination HTML through the normal
|
|
579
582
|
// soft-nav pipeline (as a "cached page" so it skips the fetch) instead of
|
|
580
583
|
// `document.write`-ing a whole new document. `document.write` resets the
|
|
@@ -592,95 +595,95 @@ async function replaceDocument(html: string, href?: string, replace = false, fre
|
|
|
592
595
|
replace,
|
|
593
596
|
cachedPage: { html: actionRedirectPage(html), finalUrl: href, ok: true },
|
|
594
597
|
...(freshSegments ? { freshSegments: true } : {}),
|
|
595
|
-
})
|
|
598
|
+
})
|
|
596
599
|
} catch (error) {
|
|
597
600
|
// Single-pass redirect swap failed in the router: land on the target
|
|
598
601
|
// with a full navigation rather than stranding the user (and the test's
|
|
599
602
|
// waitForElementByCss) on the origin page.
|
|
600
|
-
console.error(error)
|
|
601
|
-
if (replace) location.replace(new URL(href, location.href).href)
|
|
602
|
-
else location.assign(new URL(href, location.href).href)
|
|
603
|
+
console.error(error)
|
|
604
|
+
if (replace) location.replace(new URL(href, location.href).href)
|
|
605
|
+
else location.assign(new URL(href, location.href).href)
|
|
603
606
|
}
|
|
604
|
-
return
|
|
607
|
+
return
|
|
605
608
|
}
|
|
606
|
-
document.open()
|
|
607
|
-
document.write(html)
|
|
608
|
-
document.close()
|
|
609
|
+
document.open()
|
|
610
|
+
document.write(html)
|
|
611
|
+
document.close()
|
|
609
612
|
}
|
|
610
613
|
|
|
611
614
|
function actionRedirectPage(html: string): string {
|
|
612
615
|
// A server-only destination does not need its route entry. Avoid importing
|
|
613
616
|
// it so the inline action flight remains the redirect's only request.
|
|
614
|
-
if (/<pnext-client(?:\s|>)/.test(html) || html.includes('<!--pnext-client:')) return html
|
|
617
|
+
if (/<pnext-client(?:\s|>)/.test(html) || html.includes('<!--pnext-client:')) return html
|
|
615
618
|
const entryScript =
|
|
616
|
-
/\s*<script\b[^>]*\btype=["']module["'][^>]*\bsrc=["'][^"']*-pnext-client\.js["'][^>]*><\/script>/gi
|
|
617
|
-
return html.replace(entryScript, '')
|
|
619
|
+
/\s*<script\b[^>]*\btype=["']module["'][^>]*\bsrc=["'][^"']*-pnext-client\.js["'][^>]*><\/script>/gi
|
|
620
|
+
return html.replace(entryScript, '')
|
|
618
621
|
}
|
|
619
622
|
|
|
620
623
|
async function readActionFlightPayload(response: Response): Promise<ActionFlightPayload> {
|
|
621
624
|
try {
|
|
622
|
-
const parsed = JSON.parse(await response.text()) as unknown
|
|
623
|
-
return isActionFlightPayload(parsed) ? parsed : {}
|
|
625
|
+
const parsed = JSON.parse(await response.text()) as unknown
|
|
626
|
+
return isActionFlightPayload(parsed) ? parsed : {}
|
|
624
627
|
} catch {
|
|
625
|
-
throw tagActionError(new Error(UNEXPECTED_RESPONSE))
|
|
628
|
+
throw tagActionError(new Error(UNEXPECTED_RESPONSE))
|
|
626
629
|
}
|
|
627
630
|
}
|
|
628
631
|
|
|
629
632
|
function isActionFlightPayload(value: unknown): value is ActionFlightPayload {
|
|
630
|
-
return value !== null && typeof value === 'object'
|
|
633
|
+
return value !== null && typeof value === 'object'
|
|
631
634
|
}
|
|
632
635
|
|
|
633
636
|
function payloadHtml(payload: ActionFlightPayload): string | undefined {
|
|
634
|
-
const flight = payload.f
|
|
635
|
-
if (!flight || typeof flight !== 'object') return undefined
|
|
636
|
-
return flight.kind === 'pnext-html' && typeof flight.html === 'string' ? flight.html : undefined
|
|
637
|
+
const flight = payload.f
|
|
638
|
+
if (!flight || typeof flight !== 'object') return undefined
|
|
639
|
+
return flight.kind === 'pnext-html' && typeof flight.html === 'string' ? flight.html : undefined
|
|
637
640
|
}
|
|
638
641
|
|
|
639
642
|
function hasActionRevalidated(response: Response): boolean {
|
|
640
643
|
return (
|
|
641
644
|
response.headers.has(ACTION_REFRESH_HEADER) ||
|
|
642
645
|
response.headers.has(NEXT_ACTION_REVALIDATED_HEADER)
|
|
643
|
-
)
|
|
646
|
+
)
|
|
644
647
|
}
|
|
645
648
|
|
|
646
649
|
// ---------------------------------------------------------------------------
|
|
647
650
|
// error boundary overlay body (route error.js support for action failures)
|
|
648
651
|
// ---------------------------------------------------------------------------
|
|
649
652
|
|
|
650
|
-
type ErrorComponentType = ComponentType<{ error: Error; reset: () => void }
|
|
653
|
+
type ErrorComponentType = ComponentType<{ error: Error; reset: () => void }>
|
|
651
654
|
|
|
652
655
|
interface HiddenElement extends HTMLElement {
|
|
653
|
-
__pnextPrevDisplay?: string
|
|
656
|
+
__pnextPrevDisplay?: string
|
|
654
657
|
}
|
|
655
658
|
|
|
656
|
-
let overlayRoot: HTMLElement | null = null
|
|
659
|
+
let overlayRoot: HTMLElement | null = null
|
|
657
660
|
|
|
658
661
|
/**
|
|
659
662
|
* Hide the page and render the route's error component over it. Callers
|
|
660
|
-
* serialize their calls (
|
|
663
|
+
* serialize their calls (client.ts chains them), so the `overlayRoot` guard
|
|
661
664
|
* still holds across the dynamic import that reaches this module.
|
|
662
665
|
*/
|
|
663
666
|
export function showActionErrorOverlay(Component: ErrorComponentType, reason: unknown) {
|
|
664
|
-
if (overlayRoot) return
|
|
665
|
-
const error = reason instanceof Error ? reason : new Error(String(reason))
|
|
666
|
-
const hidden: HiddenElement[] = []
|
|
667
|
-
overlayRoot = document.createElement('div')
|
|
667
|
+
if (overlayRoot) return
|
|
668
|
+
const error = reason instanceof Error ? reason : new Error(String(reason))
|
|
669
|
+
const hidden: HiddenElement[] = []
|
|
670
|
+
overlayRoot = document.createElement('div')
|
|
668
671
|
for (const child of [...document.body.children]) {
|
|
669
|
-
if (!(child instanceof HTMLElement)) continue
|
|
670
|
-
if (child.tagName === 'SCRIPT') continue
|
|
671
|
-
const element = child as HiddenElement
|
|
672
|
-
element.__pnextPrevDisplay = element.style.display
|
|
673
|
-
element.style.display = 'none'
|
|
674
|
-
hidden.push(element)
|
|
672
|
+
if (!(child instanceof HTMLElement)) continue
|
|
673
|
+
if (child.tagName === 'SCRIPT') continue
|
|
674
|
+
const element = child as HiddenElement
|
|
675
|
+
element.__pnextPrevDisplay = element.style.display
|
|
676
|
+
element.style.display = 'none'
|
|
677
|
+
hidden.push(element)
|
|
675
678
|
}
|
|
676
|
-
document.body.appendChild(overlayRoot)
|
|
679
|
+
document.body.appendChild(overlayRoot)
|
|
677
680
|
const reset = () => {
|
|
678
|
-
if (!overlayRoot) return
|
|
679
|
-
render(null, overlayRoot)
|
|
680
|
-
overlayRoot.remove()
|
|
681
|
-
overlayRoot = null
|
|
682
|
-
for (const element of hidden) element.style.display = element.__pnextPrevDisplay ?? ''
|
|
683
|
-
void refresh()
|
|
684
|
-
}
|
|
685
|
-
render(h(Component, { error, reset }), overlayRoot)
|
|
681
|
+
if (!overlayRoot) return
|
|
682
|
+
render(null, overlayRoot)
|
|
683
|
+
overlayRoot.remove()
|
|
684
|
+
overlayRoot = null
|
|
685
|
+
for (const element of hidden) element.style.display = element.__pnextPrevDisplay ?? ''
|
|
686
|
+
void refresh()
|
|
687
|
+
}
|
|
688
|
+
render(h(Component, { error, reset }), overlayRoot)
|
|
686
689
|
}
|