@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
|
@@ -7,11 +7,11 @@
|
|
|
7
7
|
* `<form action={fn}>` vnode hook, progressive-form enhancement, and synchronous island-prop revival.
|
|
8
8
|
* Everything that can only run once an action has fired - argument encoding, the POST wire,
|
|
9
9
|
* redirect/refresh/error semantics, action-return revival, the error overlay body - lives in
|
|
10
|
-
*
|
|
10
|
+
* dispatch.ts and is reached through import(), so it rides a deferred chunk whose fetch
|
|
11
11
|
* overlaps the action's own round trip.
|
|
12
12
|
*/
|
|
13
|
-
import { options as preactOptions, type ComponentType, type VNode } from 'preact'
|
|
14
|
-
import { __pnextPushFormStatus } from '../react/dom'
|
|
13
|
+
import { options as preactOptions, type ComponentType, type VNode } from 'preact'
|
|
14
|
+
import { __pnextPushFormStatus } from '../react/dom'
|
|
15
15
|
import {
|
|
16
16
|
ACTION_ID_FIELD,
|
|
17
17
|
FORM_STATE_FIELD,
|
|
@@ -20,7 +20,7 @@ import {
|
|
|
20
20
|
isActionError,
|
|
21
21
|
reviveBinaryMarker,
|
|
22
22
|
type ActionFunction,
|
|
23
|
-
} from './
|
|
23
|
+
} from './shared'
|
|
24
24
|
|
|
25
25
|
// Out-of-band carrier for a plain server-action form's id once the client
|
|
26
26
|
// runtime is up. React strips the progressive `$ACTION_ID` hidden inputs from a
|
|
@@ -29,12 +29,12 @@ import {
|
|
|
29
29
|
// `form.innerHTML` leaks `<input name="$pnext_action_id" ...>`. We move the id
|
|
30
30
|
// to this attribute and drop the hidden input (see stripProgressiveActionIds),
|
|
31
31
|
// keeping submission working via the fallbacks that read it.
|
|
32
|
-
const ACTION_ID_ATTR = 'data-pnext-action-id'
|
|
33
|
-
const PROP_ACTION_MARKER = '$$pnextAction'
|
|
34
|
-
const ACTION_CLICK_ID_ATTR = 'data-pnext-action-click'
|
|
35
|
-
const PROP_ERROR_RESET_MARKER = '$$pnextErrorReset'
|
|
32
|
+
const ACTION_ID_ATTR = 'data-pnext-action-id'
|
|
33
|
+
const PROP_ACTION_MARKER = '$$pnextAction'
|
|
34
|
+
const ACTION_CLICK_ID_ATTR = 'data-pnext-action-click'
|
|
35
|
+
const PROP_ERROR_RESET_MARKER = '$$pnextErrorReset'
|
|
36
36
|
|
|
37
|
-
export { isActionError }
|
|
37
|
+
export { isActionError }
|
|
38
38
|
|
|
39
39
|
// ---------------------------------------------------------------------------
|
|
40
40
|
// dispatch entry point (the wire itself is deferred)
|
|
@@ -45,28 +45,28 @@ export { isActionError };
|
|
|
45
45
|
// fetched-and-evaluated chunk lands behind the frame and a driver reading the DOM the instant
|
|
46
46
|
// `click()` resolves sees no request. Warm on `pnext:hydrated` and remember the entry point, so the
|
|
47
47
|
// warm call is a microtask that drains in the click's own task.
|
|
48
|
-
const dispatchRuntime = () => import('./
|
|
48
|
+
const dispatchRuntime = () => import('./dispatch')
|
|
49
49
|
|
|
50
|
-
let warmDispatch: ((id: string, args: unknown[]) => Promise<unknown>) | undefined
|
|
50
|
+
let warmDispatch: ((id: string, args: unknown[]) => Promise<unknown>) | undefined
|
|
51
51
|
|
|
52
|
-
const loadDispatch = () => dispatchRuntime().then(module => (warmDispatch = module.callAction))
|
|
52
|
+
const loadDispatch = () => dispatchRuntime().then(module => (warmDispatch = module.callAction))
|
|
53
53
|
|
|
54
54
|
// Warming is speculative: offline (or any failed chunk fetch) must not surface as an unhandled
|
|
55
55
|
// rejection, which the client error last-resort escalates to the global-error document. A real
|
|
56
56
|
// action call still awaits - and rejects through - loadDispatch.
|
|
57
57
|
const warmDispatchChunk = () => {
|
|
58
|
-
void loadDispatch().catch(() => undefined)
|
|
59
|
-
}
|
|
58
|
+
void loadDispatch().catch(() => undefined)
|
|
59
|
+
}
|
|
60
60
|
|
|
61
61
|
function callAction(id: string, args: unknown[]): Promise<unknown> {
|
|
62
|
-
return warmDispatch ? warmDispatch(id, args) : loadDispatch().then(call => call(id, args))
|
|
62
|
+
return warmDispatch ? warmDispatch(id, args) : loadDispatch().then(call => call(id, args))
|
|
63
63
|
}
|
|
64
64
|
|
|
65
65
|
function registerClientReference(
|
|
66
66
|
id: string,
|
|
67
67
|
component: ComponentType<Record<string, unknown>>,
|
|
68
68
|
): void {
|
|
69
|
-
actionReturnClientReferences.set(id, component)
|
|
69
|
+
actionReturnClientReferences.set(id, component)
|
|
70
70
|
}
|
|
71
71
|
|
|
72
72
|
// ---------------------------------------------------------------------------
|
|
@@ -74,44 +74,44 @@ function registerClientReference(
|
|
|
74
74
|
// ---------------------------------------------------------------------------
|
|
75
75
|
|
|
76
76
|
function reviveProps<T>(value: T): T {
|
|
77
|
-
return reviveValue(value) as T
|
|
77
|
+
return reviveValue(value) as T
|
|
78
78
|
}
|
|
79
79
|
|
|
80
80
|
function reviveValue(value: unknown): unknown {
|
|
81
|
-
if (value === null || typeof value !== 'object') return value
|
|
82
|
-
if (Array.isArray(value)) return value.map(reviveValue)
|
|
83
|
-
const record = value as Record<string, unknown
|
|
84
|
-
const error = reviveErrorMarker(record)
|
|
85
|
-
if (error) return error
|
|
86
|
-
if (record[PROP_ERROR_RESET_MARKER] === true) return refreshCurrentRoute
|
|
81
|
+
if (value === null || typeof value !== 'object') return value
|
|
82
|
+
if (Array.isArray(value)) return value.map(reviveValue)
|
|
83
|
+
const record = value as Record<string, unknown>
|
|
84
|
+
const error = reviveErrorMarker(record)
|
|
85
|
+
if (error) return error
|
|
86
|
+
if (record[PROP_ERROR_RESET_MARKER] === true) return refreshCurrentRoute
|
|
87
87
|
if (typeof record[PROP_ACTION_MARKER] === 'string') {
|
|
88
|
-
return createAction(record[PROP_ACTION_MARKER])
|
|
88
|
+
return createAction(record[PROP_ACTION_MARKER])
|
|
89
89
|
}
|
|
90
|
-
const binary = reviveBinaryMarker(record)
|
|
91
|
-
if (binary !== undefined) return binary
|
|
92
|
-
const revived: Record<string, unknown> = {}
|
|
93
|
-
for (const [key, item] of Object.entries(record)) revived[key] = reviveValue(item)
|
|
94
|
-
return revived
|
|
90
|
+
const binary = reviveBinaryMarker(record)
|
|
91
|
+
if (binary !== undefined) return binary
|
|
92
|
+
const revived: Record<string, unknown> = {}
|
|
93
|
+
for (const [key, item] of Object.entries(record)) revived[key] = reviveValue(item)
|
|
94
|
+
return revived
|
|
95
95
|
}
|
|
96
96
|
|
|
97
97
|
function reviveErrorMarker(record: Record<string, unknown>): Error | undefined {
|
|
98
|
-
const marker = record.$$pnextError
|
|
99
|
-
if (marker === null || typeof marker !== 'object') return undefined
|
|
100
|
-
const data = marker as Record<string, unknown
|
|
101
|
-
const error = new Error(typeof data.message === 'string' ? data.message : '')
|
|
102
|
-
if (typeof data.name === 'string') error.name = data.name
|
|
103
|
-
if (typeof data.digest === 'string') (error as Error & { digest?: string }).digest = data.digest
|
|
104
|
-
return error
|
|
98
|
+
const marker = record.$$pnextError
|
|
99
|
+
if (marker === null || typeof marker !== 'object') return undefined
|
|
100
|
+
const data = marker as Record<string, unknown>
|
|
101
|
+
const error = new Error(typeof data.message === 'string' ? data.message : '')
|
|
102
|
+
if (typeof data.name === 'string') error.name = data.name
|
|
103
|
+
if (typeof data.digest === 'string') (error as Error & { digest?: string }).digest = data.digest
|
|
104
|
+
return error
|
|
105
105
|
}
|
|
106
106
|
|
|
107
107
|
function refreshCurrentRoute(): void {
|
|
108
|
-
location.reload()
|
|
108
|
+
location.reload()
|
|
109
109
|
}
|
|
110
110
|
|
|
111
111
|
function createAction(id: string): ActionFunction {
|
|
112
|
-
const action: ActionFunction = async (...args: unknown[]) => callAction(id, args)
|
|
113
|
-
action.$$pnextActionId = id
|
|
114
|
-
return action
|
|
112
|
+
const action: ActionFunction = async (...args: unknown[]) => callAction(id, args)
|
|
113
|
+
action.$$pnextActionId = id
|
|
114
|
+
return action
|
|
115
115
|
}
|
|
116
116
|
|
|
117
117
|
// ---------------------------------------------------------------------------
|
|
@@ -119,7 +119,7 @@ function createAction(id: string): ActionFunction {
|
|
|
119
119
|
// ---------------------------------------------------------------------------
|
|
120
120
|
|
|
121
121
|
interface ActionDomNode extends Element {
|
|
122
|
-
__pnextFormAction?: (formData: FormData) => unknown
|
|
122
|
+
__pnextFormAction?: (formData: FormData) => unknown
|
|
123
123
|
}
|
|
124
124
|
|
|
125
125
|
// ---------------------------------------------------------------------------
|
|
@@ -132,18 +132,18 @@ interface ActionDomNode extends Element {
|
|
|
132
132
|
* time (including the submitter's pair) parked on the form node.
|
|
133
133
|
*/
|
|
134
134
|
interface QueuedSubmission {
|
|
135
|
-
data: FormData
|
|
136
|
-
submitter: ActionDomNode | null
|
|
135
|
+
data: FormData
|
|
136
|
+
submitter: ActionDomNode | null
|
|
137
137
|
}
|
|
138
138
|
|
|
139
139
|
interface QueuedForm extends HTMLFormElement {
|
|
140
|
-
__pnextQueuedSubmit?: QueuedSubmission
|
|
140
|
+
__pnextQueuedSubmit?: QueuedSubmission
|
|
141
141
|
}
|
|
142
142
|
|
|
143
143
|
function takeQueuedSubmission(form: HTMLFormElement): QueuedSubmission | undefined {
|
|
144
|
-
const queued = (form as QueuedForm).__pnextQueuedSubmit
|
|
145
|
-
if (queued) delete (form as QueuedForm).__pnextQueuedSubmit
|
|
146
|
-
return queued
|
|
144
|
+
const queued = (form as QueuedForm).__pnextQueuedSubmit
|
|
145
|
+
if (queued) delete (form as QueuedForm).__pnextQueuedSubmit
|
|
146
|
+
return queued
|
|
147
147
|
}
|
|
148
148
|
|
|
149
149
|
/** Dispatch a captured submission with form-status + post-action reset (the
|
|
@@ -153,47 +153,49 @@ function runFormSubmission(
|
|
|
153
153
|
formData: FormData,
|
|
154
154
|
dispatch: (formData: FormData) => unknown,
|
|
155
155
|
): void {
|
|
156
|
-
const pop = pushFormStatus(form, formData)
|
|
156
|
+
const pop = pushFormStatus(form, formData)
|
|
157
157
|
void Promise.resolve(dispatch(formData))
|
|
158
158
|
.then(() => {
|
|
159
159
|
// React resets uncontrolled fields after a completed form action.
|
|
160
|
-
form.reset()
|
|
160
|
+
form.reset()
|
|
161
161
|
})
|
|
162
162
|
.catch(error => {
|
|
163
|
-
if (!throwActionErrorAt(form, error)) throw error
|
|
163
|
+
if (!throwActionErrorAt(form, error)) throw error
|
|
164
164
|
})
|
|
165
|
-
.finally(pop)
|
|
165
|
+
.finally(pop)
|
|
166
166
|
}
|
|
167
167
|
|
|
168
168
|
function throwActionErrorAt(node: Element, error: unknown): boolean {
|
|
169
|
-
const root = node.closest('pnext-client')
|
|
170
|
-
const rootVNode = (root as unknown as { __k?: VNode } | null)?.__k
|
|
171
|
-
const vnode = rootVNode ? findVNodeForElement(rootVNode, node) : undefined
|
|
172
|
-
const handler = (
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
return
|
|
169
|
+
const root = node.closest('pnext-client')
|
|
170
|
+
const rootVNode = (root as unknown as { __k?: VNode } | null)?.__k
|
|
171
|
+
const vnode = rootVNode ? findVNodeForElement(rootVNode, node) : undefined
|
|
172
|
+
const handler = (
|
|
173
|
+
preactOptions as typeof preactOptions & {
|
|
174
|
+
__e?: (error: unknown, vnode: VNode, oldVNode: VNode) => void
|
|
175
|
+
}
|
|
176
|
+
).__e
|
|
177
|
+
if (!vnode || !handler) return false
|
|
178
|
+
handler(error, vnode, vnode)
|
|
179
|
+
return true
|
|
178
180
|
}
|
|
179
181
|
|
|
180
182
|
function findVNodeForElement(vnode: VNode, element: Element): VNode | undefined {
|
|
181
|
-
if ((vnode as VNode & { __e?: unknown }).__e === element) return vnode
|
|
182
|
-
const children = (vnode as VNode & { __k?: (VNode | null)[] }).__k
|
|
183
|
-
if (!children) return undefined
|
|
183
|
+
if ((vnode as VNode & { __e?: unknown }).__e === element) return vnode
|
|
184
|
+
const children = (vnode as VNode & { __k?: (VNode | null)[] }).__k
|
|
185
|
+
if (!children) return undefined
|
|
184
186
|
for (const child of children) {
|
|
185
|
-
if (!child) continue
|
|
186
|
-
const found = findVNodeForElement(child, element)
|
|
187
|
-
if (found) return found
|
|
187
|
+
if (!child) continue
|
|
188
|
+
const found = findVNodeForElement(child, element)
|
|
189
|
+
if (found) return found
|
|
188
190
|
}
|
|
189
|
-
return undefined
|
|
191
|
+
return undefined
|
|
190
192
|
}
|
|
191
193
|
|
|
192
194
|
/** Replay a pre-hydration submission the moment the form's function action
|
|
193
195
|
* hydrates (called from the stashed ref below). */
|
|
194
196
|
function replayQueuedSubmission(form: HTMLFormElement, fn: (formData: FormData) => unknown): void {
|
|
195
|
-
const queued = takeQueuedSubmission(form)
|
|
196
|
-
if (queued) runFormSubmission(form, queued.data, fn)
|
|
197
|
+
const queued = takeQueuedSubmission(form)
|
|
198
|
+
if (queued) runFormSubmission(form, queued.data, fn)
|
|
197
199
|
}
|
|
198
200
|
|
|
199
201
|
/**
|
|
@@ -204,74 +206,73 @@ function replayQueuedSubmission(form: HTMLFormElement, fn: (formData: FormData)
|
|
|
204
206
|
*/
|
|
205
207
|
function adoptEarlySubmissions(): void {
|
|
206
208
|
const holder = window as typeof window & {
|
|
207
|
-
__pnextEarlySubmits?: HTMLFormElement[]
|
|
208
|
-
__pnextRemoveEarlySubmit?: () => void
|
|
209
|
-
}
|
|
210
|
-
holder.__pnextRemoveEarlySubmit?.()
|
|
211
|
-
const forms = holder.__pnextEarlySubmits
|
|
212
|
-
if (!forms?.length) return
|
|
209
|
+
__pnextEarlySubmits?: HTMLFormElement[]
|
|
210
|
+
__pnextRemoveEarlySubmit?: () => void
|
|
211
|
+
}
|
|
212
|
+
holder.__pnextRemoveEarlySubmit?.()
|
|
213
|
+
const forms = holder.__pnextEarlySubmits
|
|
214
|
+
if (!forms?.length) return
|
|
213
215
|
const flush = () => {
|
|
214
216
|
for (const form of forms.splice(0)) {
|
|
215
|
-
const queued = takeQueuedSubmission(form)
|
|
216
|
-
if (!queued) continue
|
|
217
|
+
const queued = takeQueuedSubmission(form)
|
|
218
|
+
if (!queued) continue // hydration replayed it already
|
|
217
219
|
const stashed =
|
|
218
|
-
queued.submitter?.__pnextFormAction ??
|
|
219
|
-
(form as unknown as ActionDomNode).__pnextFormAction;
|
|
220
|
+
queued.submitter?.__pnextFormAction ?? (form as unknown as ActionDomNode).__pnextFormAction
|
|
220
221
|
if (stashed) {
|
|
221
|
-
runFormSubmission(form, queued.data, stashed)
|
|
222
|
-
continue
|
|
222
|
+
runFormSubmission(form, queued.data, stashed)
|
|
223
|
+
continue
|
|
223
224
|
}
|
|
224
225
|
const submitterId =
|
|
225
226
|
queued.submitter?.getAttribute('name') === SUBMIT_ACTION_ID_FIELD
|
|
226
227
|
? queued.submitter.getAttribute('value')
|
|
227
|
-
: null
|
|
228
|
+
: null
|
|
228
229
|
const id =
|
|
229
230
|
submitterId ??
|
|
230
231
|
form.querySelector<HTMLInputElement>(`input[name="${cssEscape(ACTION_ID_FIELD)}"]`)
|
|
231
232
|
?.value ??
|
|
232
233
|
form.getAttribute(ACTION_ID_ATTR) ??
|
|
233
|
-
undefined
|
|
234
|
+
undefined
|
|
234
235
|
const hasFormState = Boolean(
|
|
235
236
|
form.querySelector(`input[name="${cssEscape(FORM_STATE_FIELD)}"]`),
|
|
236
|
-
)
|
|
237
|
+
)
|
|
237
238
|
if (id && !hasFormState) {
|
|
238
239
|
// Server-only progressive form (no live state to update): dispatch the
|
|
239
240
|
// RPC directly, like the live listener's unhydrated-form branch.
|
|
240
|
-
runFormSubmission(form, queued.data, formData => callAction(id, [formData]))
|
|
241
|
+
runFormSubmission(form, queued.data, formData => callAction(id, [formData]))
|
|
241
242
|
} else if (form.isConnected) {
|
|
242
243
|
// A useActionState form whose island never hydrated (or an unknown
|
|
243
244
|
// shape): give it the native progressive POST the capture prevented.
|
|
244
|
-
form.submit()
|
|
245
|
+
form.submit()
|
|
245
246
|
}
|
|
246
247
|
}
|
|
247
|
-
}
|
|
248
|
+
}
|
|
248
249
|
// Islands hydrate asynchronously after this runtime installs; give their
|
|
249
250
|
// refs (replayQueuedSubmission) first claim before falling back.
|
|
250
|
-
setTimeout(flush, 300)
|
|
251
|
+
setTimeout(flush, 300)
|
|
251
252
|
}
|
|
252
253
|
|
|
253
254
|
type FunctionActionProps = Record<string, unknown> & {
|
|
254
|
-
action?: unknown
|
|
255
|
-
formAction?: unknown
|
|
256
|
-
ref?: unknown
|
|
257
|
-
}
|
|
255
|
+
action?: unknown
|
|
256
|
+
formAction?: unknown
|
|
257
|
+
ref?: unknown
|
|
258
|
+
}
|
|
258
259
|
|
|
259
260
|
function installVNodeHook() {
|
|
260
|
-
const previous = preactOptions.vnode?.bind(preactOptions)
|
|
261
|
+
const previous = preactOptions.vnode?.bind(preactOptions)
|
|
261
262
|
preactOptions.vnode = (vnode: VNode) => {
|
|
262
|
-
previous?.(vnode)
|
|
263
|
-
if (typeof vnode.type !== 'string') return
|
|
264
|
-
const props = vnode.props as FunctionActionProps
|
|
263
|
+
previous?.(vnode)
|
|
264
|
+
if (typeof vnode.type !== 'string') return
|
|
265
|
+
const props = vnode.props as FunctionActionProps
|
|
265
266
|
if (vnode.type === 'form' && typeof props.action === 'function') {
|
|
266
|
-
stashFunctionAction(vnode, props, 'action')
|
|
267
|
+
stashFunctionAction(vnode, props, 'action')
|
|
267
268
|
}
|
|
268
269
|
if (
|
|
269
270
|
(vnode.type === 'button' || vnode.type === 'input') &&
|
|
270
271
|
typeof props.formAction === 'function'
|
|
271
272
|
) {
|
|
272
|
-
stashFunctionAction(vnode, props, 'formAction')
|
|
273
|
+
stashFunctionAction(vnode, props, 'formAction')
|
|
273
274
|
}
|
|
274
|
-
}
|
|
275
|
+
}
|
|
275
276
|
}
|
|
276
277
|
|
|
277
278
|
/**
|
|
@@ -287,33 +288,33 @@ function stashFunctionAction(
|
|
|
287
288
|
props: FunctionActionProps,
|
|
288
289
|
key: 'action' | 'formAction',
|
|
289
290
|
) {
|
|
290
|
-
const fn = props[key] as (formData: FormData) => unknown
|
|
291
|
-
delete props[key]
|
|
292
|
-
const existingRef = (vnode as { ref?: unknown }).ref
|
|
293
|
-
(vnode as { ref?: unknown }).ref = (node: ActionDomNode | null) => {
|
|
291
|
+
const fn = props[key] as (formData: FormData) => unknown
|
|
292
|
+
delete props[key]
|
|
293
|
+
const existingRef = (vnode as { ref?: unknown }).ref
|
|
294
|
+
;(vnode as { ref?: unknown }).ref = (node: ActionDomNode | null) => {
|
|
294
295
|
if (node) {
|
|
295
|
-
node.__pnextFormAction = fn
|
|
296
|
+
node.__pnextFormAction = fn
|
|
296
297
|
if (key === 'action' && node instanceof HTMLFormElement) {
|
|
297
|
-
enhanceProgressiveForm(node, fn)
|
|
298
|
-
replayQueuedSubmission(node, fn)
|
|
298
|
+
enhanceProgressiveForm(node, fn)
|
|
299
|
+
replayQueuedSubmission(node, fn)
|
|
299
300
|
}
|
|
300
301
|
if (key === 'formAction' && node instanceof HTMLElement) {
|
|
301
|
-
const form = (node as { form?: HTMLFormElement | null }).form ?? null
|
|
302
|
-
const queued = form && (form as QueuedForm).__pnextQueuedSubmit
|
|
302
|
+
const form = (node as { form?: HTMLFormElement | null }).form ?? null
|
|
303
|
+
const queued = form && (form as QueuedForm).__pnextQueuedSubmit
|
|
303
304
|
if (form && queued?.submitter === node) {
|
|
304
|
-
delete (form as QueuedForm).__pnextQueuedSubmit
|
|
305
|
-
runFormSubmission(form, queued.data, fn)
|
|
305
|
+
delete (form as QueuedForm).__pnextQueuedSubmit
|
|
306
|
+
runFormSubmission(form, queued.data, fn)
|
|
306
307
|
}
|
|
307
308
|
}
|
|
308
309
|
}
|
|
309
|
-
applyRef(existingRef, node)
|
|
310
|
-
}
|
|
310
|
+
applyRef(existingRef, node)
|
|
311
|
+
}
|
|
311
312
|
}
|
|
312
313
|
|
|
313
314
|
interface FormStateMeta {
|
|
314
|
-
action?: { $$pnextActionId?: string }
|
|
315
|
-
state?: unknown
|
|
316
|
-
permalink?: string
|
|
315
|
+
action?: { $$pnextActionId?: string }
|
|
316
|
+
state?: unknown
|
|
317
|
+
permalink?: string
|
|
317
318
|
}
|
|
318
319
|
|
|
319
320
|
/**
|
|
@@ -323,88 +324,88 @@ interface FormStateMeta {
|
|
|
323
324
|
* every render, keeping the state field current.
|
|
324
325
|
*/
|
|
325
326
|
function enhanceProgressiveForm(form: HTMLFormElement, fn: unknown) {
|
|
326
|
-
const meta = (fn as { $$pnextFormState?: FormStateMeta }).$$pnextFormState
|
|
327
|
-
if (!meta) return
|
|
328
|
-
const id = meta.action?.$$pnextActionId
|
|
329
|
-
if (!id) return
|
|
330
|
-
form.setAttribute('method', 'post')
|
|
331
|
-
form.setAttribute('enctype', 'multipart/form-data')
|
|
332
|
-
if (meta.permalink) form.setAttribute('action', meta.permalink)
|
|
333
|
-
setHiddenField(form, ACTION_ID_FIELD, id)
|
|
327
|
+
const meta = (fn as { $$pnextFormState?: FormStateMeta }).$$pnextFormState
|
|
328
|
+
if (!meta) return
|
|
329
|
+
const id = meta.action?.$$pnextActionId
|
|
330
|
+
if (!id) return
|
|
331
|
+
form.setAttribute('method', 'post')
|
|
332
|
+
form.setAttribute('enctype', 'multipart/form-data')
|
|
333
|
+
if (meta.permalink) form.setAttribute('action', meta.permalink)
|
|
334
|
+
setHiddenField(form, ACTION_ID_FIELD, id)
|
|
334
335
|
// Non-JSON-serializable state (an action can return React elements, Maps,
|
|
335
336
|
// circular structures, ...) cannot ride the progressive wire; fall back to
|
|
336
337
|
// null rather than throwing inside the render's ref (which would take the
|
|
337
338
|
// whole island down with it).
|
|
338
|
-
let serializedState = 'null'
|
|
339
|
+
let serializedState = 'null'
|
|
339
340
|
try {
|
|
340
|
-
serializedState = JSON.stringify(meta.state ?? null) ?? 'null'
|
|
341
|
+
serializedState = JSON.stringify(meta.state ?? null) ?? 'null'
|
|
341
342
|
} catch {
|
|
342
|
-
serializedState = 'null'
|
|
343
|
+
serializedState = 'null'
|
|
343
344
|
}
|
|
344
|
-
setHiddenField(form, FORM_STATE_FIELD, serializedState)
|
|
345
|
+
setHiddenField(form, FORM_STATE_FIELD, serializedState)
|
|
345
346
|
}
|
|
346
347
|
|
|
347
348
|
function setHiddenField(form: HTMLFormElement, name: string, value: string) {
|
|
348
349
|
let input = form.querySelector<HTMLInputElement>(
|
|
349
350
|
`input[type="hidden"][name="${cssEscape(name)}"]`,
|
|
350
|
-
)
|
|
351
|
+
)
|
|
351
352
|
if (!input) {
|
|
352
|
-
input = document.createElement('input')
|
|
353
|
-
input.type = 'hidden'
|
|
354
|
-
input.name = name
|
|
355
|
-
form.appendChild(input)
|
|
353
|
+
input = document.createElement('input')
|
|
354
|
+
input.type = 'hidden'
|
|
355
|
+
input.name = name
|
|
356
|
+
form.appendChild(input)
|
|
356
357
|
}
|
|
357
|
-
input.value = value
|
|
358
|
+
input.value = value
|
|
358
359
|
}
|
|
359
360
|
|
|
360
361
|
function applyRef(ref: unknown, node: Element | null) {
|
|
361
|
-
if (typeof ref === 'function') (ref as (node: Element | null) => void)(node)
|
|
362
|
-
else if (ref && typeof ref === 'object') (ref as { current: Element | null }).current = node
|
|
362
|
+
if (typeof ref === 'function') (ref as (node: Element | null) => void)(node)
|
|
363
|
+
else if (ref && typeof ref === 'object') (ref as { current: Element | null }).current = node
|
|
363
364
|
}
|
|
364
365
|
|
|
365
366
|
function installSubmitListener() {
|
|
366
367
|
document.addEventListener(
|
|
367
368
|
'submit',
|
|
368
369
|
event => {
|
|
369
|
-
const form = event.target as HTMLFormElement | null
|
|
370
|
-
if (!form || !(form instanceof HTMLFormElement)) return
|
|
371
|
-
const submitter = event.submitter as ActionDomNode | null
|
|
370
|
+
const form = event.target as HTMLFormElement | null
|
|
371
|
+
if (!form || !(form instanceof HTMLFormElement)) return
|
|
372
|
+
const submitter = event.submitter as ActionDomNode | null
|
|
372
373
|
|
|
373
374
|
const fn =
|
|
374
|
-
submitter?.__pnextFormAction || (form as unknown as ActionDomNode).__pnextFormAction
|
|
375
|
+
submitter?.__pnextFormAction || (form as unknown as ActionDomNode).__pnextFormAction
|
|
375
376
|
|
|
376
|
-
let dispatch: ((formData: FormData) => unknown) | undefined = fn
|
|
377
|
+
let dispatch: ((formData: FormData) => unknown) | undefined = fn
|
|
377
378
|
if (!dispatch) {
|
|
378
379
|
// A useActionState form that never hydrated (rendered by a server
|
|
379
380
|
// component or an unhydrated client layout) has no live state to
|
|
380
381
|
// update — let the native progressive POST run so the server
|
|
381
382
|
// re-renders the page with the new form state.
|
|
382
|
-
if (form.querySelector(`input[name="${cssEscape(FORM_STATE_FIELD)}"]`)) return
|
|
383
|
+
if (form.querySelector(`input[name="${cssEscape(FORM_STATE_FIELD)}"]`)) return
|
|
383
384
|
// Server-rendered progressive form: the id travels in a hidden field
|
|
384
385
|
// (or the submitter button's own name/value pair under its distinct
|
|
385
386
|
// field name, which overrides the form-level default).
|
|
386
387
|
const submitterId =
|
|
387
388
|
submitter?.getAttribute('name') === SUBMIT_ACTION_ID_FIELD
|
|
388
389
|
? submitter.getAttribute('value')
|
|
389
|
-
: null
|
|
390
|
+
: null
|
|
390
391
|
const field = form.querySelector<HTMLInputElement>(
|
|
391
392
|
`input[name="${cssEscape(ACTION_ID_FIELD)}"]`,
|
|
392
|
-
)
|
|
393
|
+
)
|
|
393
394
|
// The hidden id may have been moved out-of-band (stripProgressiveActionIds).
|
|
394
|
-
const id = submitterId ?? field?.value ?? form.getAttribute(ACTION_ID_ATTR)
|
|
395
|
-
if (!id) return
|
|
396
|
-
dispatch = formData => callAction(id, [formData])
|
|
395
|
+
const id = submitterId ?? field?.value ?? form.getAttribute(ACTION_ID_ATTR)
|
|
396
|
+
if (!id) return // not an action form; let the browser submit it
|
|
397
|
+
dispatch = formData => callAction(id, [formData])
|
|
397
398
|
}
|
|
398
399
|
|
|
399
|
-
event.preventDefault()
|
|
400
|
-
const formData = new FormData(form, (submitter ?? undefined) as HTMLElement | undefined)
|
|
401
|
-
runFormSubmission(form, formData, dispatch)
|
|
400
|
+
event.preventDefault()
|
|
401
|
+
const formData = new FormData(form, (submitter ?? undefined) as HTMLElement | undefined)
|
|
402
|
+
runFormSubmission(form, formData, dispatch)
|
|
402
403
|
},
|
|
403
404
|
// Bubble phase, so app-level onSubmit handlers that preventDefault
|
|
404
405
|
// themselves run first and can opt out of the default action behavior.
|
|
405
406
|
false,
|
|
406
|
-
)
|
|
407
|
-
adoptEarlySubmissions()
|
|
407
|
+
)
|
|
408
|
+
adoptEarlySubmissions()
|
|
408
409
|
}
|
|
409
410
|
|
|
410
411
|
/**
|
|
@@ -421,18 +422,18 @@ function installSubmitListener() {
|
|
|
421
422
|
* POST fallback (installSubmitListener bails to native for form-state forms).
|
|
422
423
|
*/
|
|
423
424
|
function stripProgressiveActionIds() {
|
|
424
|
-
if (typeof document === 'undefined') return
|
|
425
|
+
if (typeof document === 'undefined') return
|
|
425
426
|
const inputs = document.querySelectorAll<HTMLInputElement>(
|
|
426
427
|
`input[type="hidden"][name="${cssEscape(ACTION_ID_FIELD)}"]`,
|
|
427
|
-
)
|
|
428
|
+
)
|
|
428
429
|
for (const input of inputs) {
|
|
429
|
-
const form = input.form
|
|
430
|
-
if (!form) continue
|
|
431
|
-
if (form.querySelector(`input[name="${cssEscape(FORM_STATE_FIELD)}"]`)) continue
|
|
430
|
+
const form = input.form
|
|
431
|
+
if (!form) continue
|
|
432
|
+
if (form.querySelector(`input[name="${cssEscape(FORM_STATE_FIELD)}"]`)) continue
|
|
432
433
|
if (input.value && !form.hasAttribute(ACTION_ID_ATTR)) {
|
|
433
|
-
form.setAttribute(ACTION_ID_ATTR, input.value)
|
|
434
|
+
form.setAttribute(ACTION_ID_ATTR, input.value)
|
|
434
435
|
}
|
|
435
|
-
input.remove()
|
|
436
|
+
input.remove()
|
|
436
437
|
}
|
|
437
438
|
}
|
|
438
439
|
|
|
@@ -441,18 +442,18 @@ function installClickActionListener() {
|
|
|
441
442
|
const target =
|
|
442
443
|
event.target instanceof Element
|
|
443
444
|
? event.target.closest<HTMLElement>(`[${ACTION_CLICK_ID_ATTR}]`)
|
|
444
|
-
: null
|
|
445
|
-
if (!target || (target instanceof HTMLButtonElement && target.disabled)) return
|
|
446
|
-
const id = target.getAttribute(ACTION_CLICK_ID_ATTR)
|
|
447
|
-
if (!id) return
|
|
448
|
-
event.preventDefault()
|
|
449
|
-
void callAction(id, [])
|
|
450
|
-
})
|
|
445
|
+
: null
|
|
446
|
+
if (!target || (target instanceof HTMLButtonElement && target.disabled)) return
|
|
447
|
+
const id = target.getAttribute(ACTION_CLICK_ID_ATTR)
|
|
448
|
+
if (!id) return
|
|
449
|
+
event.preventDefault()
|
|
450
|
+
void callAction(id, [])
|
|
451
|
+
})
|
|
451
452
|
}
|
|
452
453
|
|
|
453
454
|
function cssEscape(value: string) {
|
|
454
|
-
const escape = (globalThis as { CSS?: { escape?: (v: string) => string } }).CSS?.escape
|
|
455
|
-
return escape ? escape(value) : value.replace(/[^a-zA-Z0-9_-]/g, match => `\\${match}`)
|
|
455
|
+
const escape = (globalThis as { CSS?: { escape?: (v: string) => string } }).CSS?.escape
|
|
456
|
+
return escape ? escape(value) : value.replace(/[^a-zA-Z0-9_-]/g, match => `\\${match}`)
|
|
456
457
|
}
|
|
457
458
|
|
|
458
459
|
// Form-status feed for useFormStatus (react-dom compat).
|
|
@@ -463,9 +464,9 @@ function pushFormStatus(form: HTMLFormElement, data: FormData): () => void {
|
|
|
463
464
|
data,
|
|
464
465
|
method: (form.getAttribute('method') || 'get').toLowerCase(),
|
|
465
466
|
action: form.getAttribute('action') ?? '',
|
|
466
|
-
})
|
|
467
|
+
})
|
|
467
468
|
} catch {
|
|
468
|
-
return () => undefined
|
|
469
|
+
return () => undefined
|
|
469
470
|
}
|
|
470
471
|
}
|
|
471
472
|
|
|
@@ -477,35 +478,35 @@ function pushFormStatus(form: HTMLFormElement, data: FormData): () => void {
|
|
|
477
478
|
// pnext's server-rendered pages have no client-side boundary components, so the compat entry installs
|
|
478
479
|
// the route's error.js as an overlay. Only the listeners are initial - the overlay body, which needs
|
|
479
480
|
// preact's renderer and the refresh path, rides the deferred dispatch chunk.
|
|
480
|
-
type ErrorComponentType = ComponentType<{ error: Error; reset: () => void }
|
|
481
|
+
type ErrorComponentType = ComponentType<{ error: Error; reset: () => void }>
|
|
481
482
|
|
|
482
|
-
let overlayInstalled = false
|
|
483
|
+
let overlayInstalled = false
|
|
483
484
|
|
|
484
485
|
export function installActionErrorOverlay(Component: ErrorComponentType) {
|
|
485
|
-
if ((!process.browser && typeof window === 'undefined') || overlayInstalled) return
|
|
486
|
-
overlayInstalled = true
|
|
486
|
+
if ((!process.browser && typeof window === 'undefined') || overlayInstalled) return
|
|
487
|
+
overlayInstalled = true
|
|
487
488
|
|
|
488
489
|
// Chained, not concurrent: two errors arriving before the chunk lands must
|
|
489
490
|
// still see each other's effect on the overlay's "already showing" guard.
|
|
490
|
-
let chain: Promise<unknown> = Promise.resolve()
|
|
491
|
+
let chain: Promise<unknown> = Promise.resolve()
|
|
491
492
|
const show = (reason: unknown) => {
|
|
492
493
|
chain = chain.then(async () => {
|
|
493
|
-
const { showActionErrorOverlay } = await dispatchRuntime()
|
|
494
|
-
showActionErrorOverlay(Component, reason)
|
|
495
|
-
})
|
|
496
|
-
}
|
|
494
|
+
const { showActionErrorOverlay } = await dispatchRuntime()
|
|
495
|
+
showActionErrorOverlay(Component, reason)
|
|
496
|
+
})
|
|
497
|
+
}
|
|
497
498
|
|
|
498
499
|
window.addEventListener('unhandledrejection', event => {
|
|
499
|
-
if (isActionError(event.reason)) show(event.reason)
|
|
500
|
-
})
|
|
500
|
+
if (isActionError(event.reason)) show(event.reason)
|
|
501
|
+
})
|
|
501
502
|
// useActionState rethrows action errors during render; without a class
|
|
502
503
|
// error boundary in the tree the uncaught render error lands here.
|
|
503
504
|
window.addEventListener('error', event => {
|
|
504
|
-
if (isActionError(event.error)) show(event.error)
|
|
505
|
-
})
|
|
505
|
+
if (isActionError(event.error)) show(event.error)
|
|
506
|
+
})
|
|
506
507
|
window.addEventListener('pnext:action-error', event => {
|
|
507
|
-
show((event as CustomEvent).detail)
|
|
508
|
-
})
|
|
508
|
+
show((event as CustomEvent).detail)
|
|
509
|
+
})
|
|
509
510
|
}
|
|
510
511
|
|
|
511
512
|
// ---------------------------------------------------------------------------
|
|
@@ -513,26 +514,26 @@ export function installActionErrorOverlay(Component: ErrorComponentType) {
|
|
|
513
514
|
// ---------------------------------------------------------------------------
|
|
514
515
|
|
|
515
516
|
export function installActionRuntime() {
|
|
516
|
-
if (!process.browser && typeof window === 'undefined') return
|
|
517
|
+
if (!process.browser && typeof window === 'undefined') return
|
|
517
518
|
if (!window.__PNEXT_ACTIONS__) {
|
|
518
519
|
window.__PNEXT_ACTIONS__ = {
|
|
519
520
|
call: callAction,
|
|
520
521
|
createAction,
|
|
521
522
|
registerClientReference,
|
|
522
523
|
reviveProps,
|
|
523
|
-
}
|
|
524
|
-
installSubmitListener()
|
|
525
|
-
installClickActionListener()
|
|
524
|
+
}
|
|
525
|
+
installSubmitListener()
|
|
526
|
+
installClickActionListener()
|
|
526
527
|
// Every mountRoute (initial hydration + each soft-nav commit) fires
|
|
527
528
|
// `pnext:hydrated`; strip leaked progressive action-id inputs from the
|
|
528
529
|
// freshly committed DOM so a hydrated form matches React (no `$ACTION_ID`).
|
|
529
530
|
// Hydration is also where the dispatch chunk gets warmed (see loadDispatch):
|
|
530
531
|
// past first paint, before any click, and `import()` dedups the repeats.
|
|
531
|
-
window.addEventListener('pnext:hydrated', stripProgressiveActionIds)
|
|
532
|
-
window.addEventListener('pnext:hydrated', warmDispatchChunk)
|
|
533
|
-
stripProgressiveActionIds()
|
|
532
|
+
window.addEventListener('pnext:hydrated', stripProgressiveActionIds)
|
|
533
|
+
window.addEventListener('pnext:hydrated', warmDispatchChunk)
|
|
534
|
+
stripProgressiveActionIds()
|
|
534
535
|
}
|
|
535
|
-
installVNodeHook()
|
|
536
|
+
installVNodeHook()
|
|
536
537
|
}
|
|
537
538
|
|
|
538
|
-
export { createAction, callAction, registerClientReference, reviveProps }
|
|
539
|
+
export { createAction, callAction, registerClientReference, reviveProps }
|