@timber-js/app 0.2.0-alpha.1 → 0.2.0-alpha.100
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/LICENSE +8 -0
- package/dist/_chunks/actions-CQ8Z8VGL.js +1061 -0
- package/dist/_chunks/actions-CQ8Z8VGL.js.map +1 -0
- package/dist/_chunks/build-output-helper-DXnW0qjz.js +61 -0
- package/dist/_chunks/build-output-helper-DXnW0qjz.js.map +1 -0
- package/dist/_chunks/chunk-BYIpzuS7.js +39 -0
- package/dist/_chunks/define-B-Q_UMOD.js +195 -0
- package/dist/_chunks/define-B-Q_UMOD.js.map +1 -0
- package/dist/_chunks/define-CfBPoJb0.js +123 -0
- package/dist/_chunks/define-CfBPoJb0.js.map +1 -0
- package/dist/_chunks/define-cookie-BjpIt4UC.js +194 -0
- package/dist/_chunks/define-cookie-BjpIt4UC.js.map +1 -0
- package/dist/_chunks/format-Bcn-Iv1x.js +14 -0
- package/dist/_chunks/format-Bcn-Iv1x.js.map +1 -0
- package/dist/_chunks/handler-store-B-lqaGyh.js +54 -0
- package/dist/_chunks/handler-store-B-lqaGyh.js.map +1 -0
- package/dist/_chunks/logger-0m8MsKdc.js +291 -0
- package/dist/_chunks/logger-0m8MsKdc.js.map +1 -0
- package/dist/_chunks/merge-search-params-BphMdht_.js +122 -0
- package/dist/_chunks/merge-search-params-BphMdht_.js.map +1 -0
- package/dist/_chunks/{metadata-routes-CQCnF4VK.js → metadata-routes-BU684ls2.js} +1 -1
- package/dist/_chunks/{metadata-routes-CQCnF4VK.js.map → metadata-routes-BU684ls2.js.map} +1 -1
- package/dist/_chunks/navigation-root-BCYczjml.js +96 -0
- package/dist/_chunks/navigation-root-BCYczjml.js.map +1 -0
- package/dist/_chunks/registry-I2ss-lvy.js +20 -0
- package/dist/_chunks/registry-I2ss-lvy.js.map +1 -0
- package/dist/_chunks/router-ref-h3-UaCQv.js +28 -0
- package/dist/_chunks/router-ref-h3-UaCQv.js.map +1 -0
- package/dist/_chunks/schema-bridge-Cxu4l-7p.js +86 -0
- package/dist/_chunks/schema-bridge-Cxu4l-7p.js.map +1 -0
- package/dist/_chunks/segment-classify-BjfuctV2.js +137 -0
- package/dist/_chunks/segment-classify-BjfuctV2.js.map +1 -0
- package/dist/_chunks/segment-context-Dx_OizxD.js +34 -0
- package/dist/_chunks/segment-context-Dx_OizxD.js.map +1 -0
- package/dist/_chunks/{ssr-data-DLnbYpj1.js → ssr-data-B4CdH7rE.js} +1 -1
- package/dist/_chunks/ssr-data-B4CdH7rE.js.map +1 -0
- package/dist/_chunks/stale-reload-Bab885FO.js +64 -0
- package/dist/_chunks/stale-reload-Bab885FO.js.map +1 -0
- package/dist/_chunks/tracing-C8V-YGsP.js +329 -0
- package/dist/_chunks/tracing-C8V-YGsP.js.map +1 -0
- package/dist/_chunks/{use-query-states-DAhgj8Gx.js → use-query-states-B2XTqxDR.js} +7 -20
- package/dist/_chunks/use-query-states-B2XTqxDR.js.map +1 -0
- package/dist/_chunks/use-segment-params-BkpKAQ7D.js +185 -0
- package/dist/_chunks/use-segment-params-BkpKAQ7D.js.map +1 -0
- package/dist/_chunks/walkers-Tg0Alwcg.js +850 -0
- package/dist/_chunks/walkers-Tg0Alwcg.js.map +1 -0
- package/dist/_chunks/{format-DNt20Kt8.js → warnings-Cg47l5sk.js} +7 -45
- package/dist/_chunks/warnings-Cg47l5sk.js.map +1 -0
- package/dist/adapters/build-output-helper.d.ts +28 -0
- package/dist/adapters/build-output-helper.d.ts.map +1 -0
- package/dist/adapters/cloudflare-dev.d.ts +109 -0
- package/dist/adapters/cloudflare-dev.d.ts.map +1 -0
- package/dist/adapters/cloudflare-dev.js +73 -0
- package/dist/adapters/cloudflare-dev.js.map +1 -0
- package/dist/adapters/cloudflare-kv-cache.d.ts +64 -0
- package/dist/adapters/cloudflare-kv-cache.d.ts.map +1 -0
- package/dist/adapters/cloudflare-kv-cache.js +95 -0
- package/dist/adapters/cloudflare-kv-cache.js.map +1 -0
- package/dist/adapters/cloudflare.d.ts +148 -12
- package/dist/adapters/cloudflare.d.ts.map +1 -1
- package/dist/adapters/cloudflare.js +142 -38
- package/dist/adapters/cloudflare.js.map +1 -1
- package/dist/adapters/compress-module.d.ts.map +1 -1
- package/dist/adapters/nitro.d.ts +17 -1
- package/dist/adapters/nitro.d.ts.map +1 -1
- package/dist/adapters/nitro.js +143 -45
- package/dist/adapters/nitro.js.map +1 -1
- package/dist/adapters/shared.d.ts +16 -0
- package/dist/adapters/shared.d.ts.map +1 -0
- package/dist/cache/cache-api.d.ts +24 -0
- package/dist/cache/cache-api.d.ts.map +1 -0
- package/dist/cache/fast-hash.d.ts +22 -0
- package/dist/cache/fast-hash.d.ts.map +1 -0
- package/dist/cache/handler-store.d.ts +31 -0
- package/dist/cache/handler-store.d.ts.map +1 -0
- package/dist/cache/index.d.ts +23 -7
- package/dist/cache/index.d.ts.map +1 -1
- package/dist/cache/index.js +195 -84
- package/dist/cache/index.js.map +1 -1
- package/dist/cache/singleflight.d.ts +18 -1
- package/dist/cache/singleflight.d.ts.map +1 -1
- package/dist/cache/sizeof.d.ts +22 -0
- package/dist/cache/sizeof.d.ts.map +1 -0
- package/dist/cache/timber-cache.d.ts +1 -1
- package/dist/cache/timber-cache.d.ts.map +1 -1
- package/dist/cli.d.ts +6 -1
- package/dist/cli.d.ts.map +1 -1
- package/dist/cli.js +8 -3
- package/dist/cli.js.map +1 -1
- package/dist/client/browser-dev.d.ts +34 -1
- package/dist/client/browser-dev.d.ts.map +1 -1
- package/dist/client/browser-entry/action-dispatch.d.ts +17 -0
- package/dist/client/browser-entry/action-dispatch.d.ts.map +1 -0
- package/dist/client/browser-entry/hmr.d.ts +21 -0
- package/dist/client/browser-entry/hmr.d.ts.map +1 -0
- package/dist/client/browser-entry/hydrate.d.ts +46 -0
- package/dist/client/browser-entry/hydrate.d.ts.map +1 -0
- package/dist/client/browser-entry/index.d.ts +30 -0
- package/dist/client/browser-entry/index.d.ts.map +1 -0
- package/dist/client/browser-entry/post-hydration.d.ts +26 -0
- package/dist/client/browser-entry/post-hydration.d.ts.map +1 -0
- package/dist/client/browser-entry/router-init.d.ts +23 -0
- package/dist/client/browser-entry/router-init.d.ts.map +1 -0
- package/dist/client/browser-entry/rsc-stream.d.ts +24 -0
- package/dist/client/browser-entry/rsc-stream.d.ts.map +1 -0
- package/dist/client/browser-entry/scroll.d.ts +19 -0
- package/dist/client/browser-entry/scroll.d.ts.map +1 -0
- package/dist/client/error-boundary.d.ts +12 -5
- package/dist/client/error-boundary.d.ts.map +1 -1
- package/dist/client/error-boundary.js +26 -5
- package/dist/client/error-boundary.js.map +1 -1
- package/dist/client/error-reconstituter.d.ts +54 -0
- package/dist/client/error-reconstituter.d.ts.map +1 -0
- package/dist/client/form.d.ts +16 -27
- package/dist/client/form.d.ts.map +1 -1
- package/dist/client/history.d.ts +19 -4
- package/dist/client/history.d.ts.map +1 -1
- package/dist/client/index.d.ts +9 -24
- package/dist/client/index.d.ts.map +1 -1
- package/dist/client/index.js +312 -1109
- package/dist/client/index.js.map +1 -1
- package/dist/client/internal.d.ts +19 -0
- package/dist/client/internal.d.ts.map +1 -0
- package/dist/client/internal.js +964 -0
- package/dist/client/internal.js.map +1 -0
- package/dist/client/link-pending-store.d.ts +63 -0
- package/dist/client/link-pending-store.d.ts.map +1 -0
- package/dist/client/link.d.ts +73 -63
- package/dist/client/link.d.ts.map +1 -1
- package/dist/client/nav-link-store.d.ts +36 -0
- package/dist/client/nav-link-store.d.ts.map +1 -0
- package/dist/client/navigation-api-types.d.ts +90 -0
- package/dist/client/navigation-api-types.d.ts.map +1 -0
- package/dist/client/navigation-api.d.ts +115 -0
- package/dist/client/navigation-api.d.ts.map +1 -0
- package/dist/client/navigation-context.d.ts +22 -5
- package/dist/client/navigation-context.d.ts.map +1 -1
- package/dist/client/{transition-root.d.ts → navigation-root.d.ts} +42 -8
- package/dist/client/navigation-root.d.ts.map +1 -0
- package/dist/client/nuqs-adapter.d.ts.map +1 -1
- package/dist/client/router-ref.d.ts +1 -1
- package/dist/client/router.d.ts +70 -4
- package/dist/client/router.d.ts.map +1 -1
- package/dist/client/rsc-fetch.d.ts +38 -3
- package/dist/client/rsc-fetch.d.ts.map +1 -1
- package/dist/client/segment-cache.d.ts +1 -1
- package/dist/client/segment-cache.d.ts.map +1 -1
- package/dist/client/segment-context.d.ts +1 -1
- package/dist/client/segment-context.d.ts.map +1 -1
- package/dist/client/segment-merger.d.ts.map +1 -1
- package/dist/client/segment-outlet.d.ts +63 -0
- package/dist/client/segment-outlet.d.ts.map +1 -0
- package/dist/client/ssr-data.d.ts +13 -4
- package/dist/client/ssr-data.d.ts.map +1 -1
- package/dist/client/stale-reload.d.ts +15 -0
- package/dist/client/stale-reload.d.ts.map +1 -1
- package/dist/client/state.d.ts +1 -1
- package/dist/client/top-loader.d.ts +5 -5
- package/dist/client/top-loader.d.ts.map +1 -1
- package/dist/client/use-cookie.d.ts +8 -0
- package/dist/client/use-cookie.d.ts.map +1 -1
- package/dist/client/use-link-status.d.ts +5 -5
- package/dist/client/use-link-status.d.ts.map +1 -1
- package/dist/client/{use-navigation-pending.d.ts → use-pending-navigation.d.ts} +4 -4
- package/dist/client/use-pending-navigation.d.ts.map +1 -0
- package/dist/client/use-query-states.d.ts +1 -1
- package/dist/client/use-query-states.d.ts.map +1 -1
- package/dist/client/use-router.d.ts +1 -1
- package/dist/client/{use-params.d.ts → use-segment-params.d.ts} +7 -5
- package/dist/client/use-segment-params.d.ts.map +1 -0
- package/dist/codec.d.ts +33 -0
- package/dist/codec.d.ts.map +1 -0
- package/dist/codec.js +2 -0
- package/dist/config-types.d.ts +294 -0
- package/dist/config-types.d.ts.map +1 -0
- package/dist/config-validation.d.ts +51 -0
- package/dist/config-validation.d.ts.map +1 -0
- package/dist/content/index.d.ts +1 -10
- package/dist/content/index.d.ts.map +1 -1
- package/dist/content/index.js +0 -2
- package/dist/cookies/define-cookie.d.ts +98 -25
- package/dist/cookies/define-cookie.d.ts.map +1 -1
- package/dist/cookies/index.d.ts +2 -1
- package/dist/cookies/index.d.ts.map +1 -1
- package/dist/cookies/index.js +36 -72
- package/dist/cookies/index.js.map +1 -1
- package/dist/cookies/json-cookie.d.ts +64 -0
- package/dist/cookies/json-cookie.d.ts.map +1 -0
- package/dist/cookies/validation.d.ts +46 -0
- package/dist/cookies/validation.d.ts.map +1 -0
- package/dist/dev-tools/404-page.d.ts +46 -0
- package/dist/dev-tools/404-page.d.ts.map +1 -0
- package/dist/dev-tools/browser-logs.d.ts +84 -0
- package/dist/dev-tools/browser-logs.d.ts.map +1 -0
- package/dist/dev-tools/error-page.d.ts +58 -0
- package/dist/dev-tools/error-page.d.ts.map +1 -0
- package/dist/dev-tools/holding-server.d.ts +54 -0
- package/dist/dev-tools/holding-server.d.ts.map +1 -0
- package/dist/dev-tools/index.d.ts +31 -0
- package/dist/dev-tools/index.d.ts.map +1 -0
- package/dist/{server/dev-span-processor.d.ts → dev-tools/instrumentation.d.ts} +26 -6
- package/dist/dev-tools/instrumentation.d.ts.map +1 -0
- package/dist/{server/dev-logger.d.ts → dev-tools/logger.d.ts} +1 -1
- package/dist/dev-tools/logger.d.ts.map +1 -0
- package/dist/{plugins/dev-logs.d.ts → dev-tools/logs.d.ts} +2 -2
- package/dist/dev-tools/logs.d.ts.map +1 -0
- package/dist/dev-tools/overlay.d.ts +91 -0
- package/dist/dev-tools/overlay.d.ts.map +1 -0
- package/dist/dev-tools/stack-classifier.d.ts +34 -0
- package/dist/dev-tools/stack-classifier.d.ts.map +1 -0
- package/dist/dev-tools/terminal.d.ts +28 -0
- package/dist/dev-tools/terminal.d.ts.map +1 -0
- package/dist/{server/dev-warnings.d.ts → dev-tools/warnings.d.ts} +2 -22
- package/dist/dev-tools/warnings.d.ts.map +1 -0
- package/dist/fonts/bundle.d.ts +48 -0
- package/dist/fonts/bundle.d.ts.map +1 -0
- package/dist/fonts/css.d.ts +1 -0
- package/dist/fonts/css.d.ts.map +1 -1
- package/dist/fonts/dev-middleware.d.ts +22 -0
- package/dist/fonts/dev-middleware.d.ts.map +1 -0
- package/dist/fonts/local.d.ts +4 -2
- package/dist/fonts/local.d.ts.map +1 -1
- package/dist/fonts/pipeline.d.ts +167 -0
- package/dist/fonts/pipeline.d.ts.map +1 -0
- package/dist/fonts/transform.d.ts +64 -0
- package/dist/fonts/transform.d.ts.map +1 -0
- package/dist/fonts/types.d.ts +45 -1
- package/dist/fonts/types.d.ts.map +1 -1
- package/dist/fonts/virtual-modules.d.ts +103 -0
- package/dist/fonts/virtual-modules.d.ts.map +1 -0
- package/dist/index.d.ts +46 -152
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +14702 -12350
- package/dist/index.js.map +1 -1
- package/dist/plugin-context.d.ts +107 -0
- package/dist/plugin-context.d.ts.map +1 -0
- package/dist/plugins/adapter-build.d.ts +1 -1
- package/dist/plugins/adapter-build.d.ts.map +1 -1
- package/dist/plugins/build-manifest.d.ts +2 -2
- package/dist/plugins/build-manifest.d.ts.map +1 -1
- package/dist/plugins/build-report.d.ts +16 -4
- package/dist/plugins/build-report.d.ts.map +1 -1
- package/dist/plugins/chunks.d.ts +9 -1
- package/dist/plugins/chunks.d.ts.map +1 -1
- package/dist/plugins/client-chunks.d.ts +32 -0
- package/dist/plugins/client-chunks.d.ts.map +1 -0
- package/dist/plugins/content.d.ts +1 -1
- package/dist/plugins/content.d.ts.map +1 -1
- package/dist/plugins/dev-server.d.ts +1 -1
- package/dist/plugins/dev-server.d.ts.map +1 -1
- package/dist/plugins/entries.d.ts +8 -1
- package/dist/plugins/entries.d.ts.map +1 -1
- package/dist/plugins/fonts.d.ts +17 -65
- package/dist/plugins/fonts.d.ts.map +1 -1
- package/dist/plugins/mdx.d.ts +7 -1
- package/dist/plugins/mdx.d.ts.map +1 -1
- package/dist/plugins/routing.d.ts +1 -1
- package/dist/plugins/routing.d.ts.map +1 -1
- package/dist/plugins/server-bundle.d.ts.map +1 -1
- package/dist/plugins/shims.d.ts +6 -5
- package/dist/plugins/shims.d.ts.map +1 -1
- package/dist/plugins/static-build.d.ts +4 -4
- package/dist/plugins/static-build.d.ts.map +1 -1
- package/dist/routing/codegen-shared.d.ts +55 -0
- package/dist/routing/codegen-shared.d.ts.map +1 -0
- package/dist/routing/codegen-types.d.ts +52 -0
- package/dist/routing/codegen-types.d.ts.map +1 -0
- package/dist/routing/codegen.d.ts +2 -2
- package/dist/routing/codegen.d.ts.map +1 -1
- package/dist/routing/convention-lint.d.ts +41 -0
- package/dist/routing/convention-lint.d.ts.map +1 -0
- package/dist/routing/index.d.ts +5 -1
- package/dist/routing/index.d.ts.map +1 -1
- package/dist/routing/index.js +3 -2
- package/dist/routing/link-codegen.d.ts +90 -0
- package/dist/routing/link-codegen.d.ts.map +1 -0
- package/dist/routing/scanner.d.ts +1 -10
- package/dist/routing/scanner.d.ts.map +1 -1
- package/dist/routing/segment-classify.d.ts +75 -0
- package/dist/routing/segment-classify.d.ts.map +1 -0
- package/dist/routing/status-file-lint.d.ts +2 -1
- package/dist/routing/status-file-lint.d.ts.map +1 -1
- package/dist/routing/types.d.ts +77 -25
- package/dist/routing/types.d.ts.map +1 -1
- package/dist/routing/walkers.d.ts +51 -0
- package/dist/routing/walkers.d.ts.map +1 -0
- package/dist/rsc-runtime/rsc.d.ts +1 -1
- package/dist/rsc-runtime/rsc.d.ts.map +1 -1
- package/dist/rsc-runtime/ssr.d.ts +12 -0
- package/dist/rsc-runtime/ssr.d.ts.map +1 -1
- package/dist/schema-bridge.d.ts +76 -0
- package/dist/schema-bridge.d.ts.map +1 -0
- package/dist/search-params/define.d.ts +157 -0
- package/dist/search-params/define.d.ts.map +1 -0
- package/dist/search-params/index.d.ts +4 -7
- package/dist/search-params/index.d.ts.map +1 -1
- package/dist/search-params/index.js +34 -441
- package/dist/search-params/index.js.map +1 -1
- package/dist/search-params/registry.d.ts +2 -2
- package/dist/search-params/registry.d.ts.map +1 -1
- package/dist/search-params/wrappers.d.ts +53 -0
- package/dist/search-params/wrappers.d.ts.map +1 -0
- package/dist/segment-params/define.d.ts +95 -0
- package/dist/segment-params/define.d.ts.map +1 -0
- package/dist/segment-params/index.d.ts +3 -0
- package/dist/segment-params/index.d.ts.map +1 -0
- package/dist/segment-params/index.js +2 -0
- package/dist/server/access-gate.d.ts +8 -3
- package/dist/server/access-gate.d.ts.map +1 -1
- package/dist/server/action-client.d.ts +41 -6
- package/dist/server/action-client.d.ts.map +1 -1
- package/dist/server/action-encryption.d.ts +76 -0
- package/dist/server/action-encryption.d.ts.map +1 -0
- package/dist/server/action-handler.d.ts +35 -1
- package/dist/server/action-handler.d.ts.map +1 -1
- package/dist/server/actions.d.ts +3 -6
- package/dist/server/actions.d.ts.map +1 -1
- package/dist/server/als-registry.d.ts +33 -5
- package/dist/server/als-registry.d.ts.map +1 -1
- package/dist/server/asset-headers.d.ts +1 -15
- package/dist/server/asset-headers.d.ts.map +1 -1
- package/dist/server/build-manifest.d.ts +2 -2
- package/dist/server/build-manifest.d.ts.map +1 -1
- package/dist/server/cookie-context.d.ts +170 -0
- package/dist/server/cookie-context.d.ts.map +1 -0
- package/dist/server/cookie-parsing.d.ts +51 -0
- package/dist/server/cookie-parsing.d.ts.map +1 -0
- package/dist/server/debug.d.ts +82 -0
- package/dist/server/debug.d.ts.map +1 -0
- package/dist/server/default-logger.d.ts +22 -0
- package/dist/server/default-logger.d.ts.map +1 -0
- package/dist/server/deny-boundary.d.ts +90 -0
- package/dist/server/deny-boundary.d.ts.map +1 -0
- package/dist/server/deny-renderer.d.ts.map +1 -1
- package/dist/server/dev-source-map.d.ts +22 -0
- package/dist/server/dev-source-map.d.ts.map +1 -0
- package/dist/server/early-hints-sender.d.ts.map +1 -1
- package/dist/server/early-hints.d.ts +13 -5
- package/dist/server/early-hints.d.ts.map +1 -1
- package/dist/server/error-boundary-wrapper.d.ts +7 -1
- package/dist/server/error-boundary-wrapper.d.ts.map +1 -1
- package/dist/server/fallback-error.d.ts +12 -7
- package/dist/server/fallback-error.d.ts.map +1 -1
- package/dist/server/flight-injection-state.d.ts +66 -0
- package/dist/server/flight-injection-state.d.ts.map +1 -0
- package/dist/server/flight-scripts.d.ts +42 -0
- package/dist/server/flight-scripts.d.ts.map +1 -0
- package/dist/server/flush.d.ts.map +1 -1
- package/dist/server/form-data.d.ts +29 -0
- package/dist/server/form-data.d.ts.map +1 -1
- package/dist/server/html-injector-core.d.ts +212 -0
- package/dist/server/html-injector-core.d.ts.map +1 -0
- package/dist/server/html-injectors.d.ts +81 -41
- package/dist/server/html-injectors.d.ts.map +1 -1
- package/dist/server/index.d.ts +9 -46
- package/dist/server/index.d.ts.map +1 -1
- package/dist/server/index.js +158 -2899
- package/dist/server/index.js.map +1 -1
- package/dist/server/internal.d.ts +48 -0
- package/dist/server/internal.d.ts.map +1 -0
- package/dist/server/internal.js +2900 -0
- package/dist/server/internal.js.map +1 -0
- package/dist/server/logger.d.ts +39 -7
- package/dist/server/logger.d.ts.map +1 -1
- package/dist/server/middleware-runner.d.ts +36 -4
- package/dist/server/middleware-runner.d.ts.map +1 -1
- package/dist/server/node-stream-transforms.d.ts +110 -0
- package/dist/server/node-stream-transforms.d.ts.map +1 -0
- package/dist/server/param-coercion.d.ts +26 -0
- package/dist/server/param-coercion.d.ts.map +1 -0
- package/dist/server/pipeline-helpers.d.ts +95 -0
- package/dist/server/pipeline-helpers.d.ts.map +1 -0
- package/dist/server/pipeline-interception.d.ts +1 -1
- package/dist/server/pipeline-interception.d.ts.map +1 -1
- package/dist/server/pipeline-metadata.d.ts +6 -0
- package/dist/server/pipeline-metadata.d.ts.map +1 -1
- package/dist/server/pipeline-outcome.d.ts +49 -0
- package/dist/server/pipeline-outcome.d.ts.map +1 -0
- package/dist/server/pipeline-phases.d.ts +52 -0
- package/dist/server/pipeline-phases.d.ts.map +1 -0
- package/dist/server/pipeline.d.ts +93 -23
- package/dist/server/pipeline.d.ts.map +1 -1
- package/dist/server/port-resolution.d.ts +117 -0
- package/dist/server/port-resolution.d.ts.map +1 -0
- package/dist/server/primitives.d.ts +69 -18
- package/dist/server/primitives.d.ts.map +1 -1
- package/dist/server/render-timeout.d.ts +51 -0
- package/dist/server/render-timeout.d.ts.map +1 -0
- package/dist/server/request-context.d.ts +41 -109
- package/dist/server/request-context.d.ts.map +1 -1
- package/dist/server/route-element-builder.d.ts +27 -1
- package/dist/server/route-element-builder.d.ts.map +1 -1
- package/dist/server/route-handler.d.ts.map +1 -1
- package/dist/server/route-matcher.d.ts +21 -34
- package/dist/server/route-matcher.d.ts.map +1 -1
- package/dist/server/rsc-entry/action-middleware-runner.d.ts +66 -0
- package/dist/server/rsc-entry/action-middleware-runner.d.ts.map +1 -0
- package/dist/server/rsc-entry/api-handler.d.ts +2 -2
- package/dist/server/rsc-entry/api-handler.d.ts.map +1 -1
- package/dist/server/rsc-entry/error-renderer.d.ts +26 -13
- package/dist/server/rsc-entry/error-renderer.d.ts.map +1 -1
- package/dist/server/rsc-entry/helpers.d.ts +49 -6
- package/dist/server/rsc-entry/helpers.d.ts.map +1 -1
- package/dist/server/rsc-entry/index.d.ts +20 -3
- package/dist/server/rsc-entry/index.d.ts.map +1 -1
- package/dist/server/rsc-entry/render-route.d.ts +50 -0
- package/dist/server/rsc-entry/render-route.d.ts.map +1 -0
- package/dist/server/rsc-entry/rsc-payload.d.ts +3 -3
- package/dist/server/rsc-entry/rsc-payload.d.ts.map +1 -1
- package/dist/server/rsc-entry/rsc-stream.d.ts +20 -1
- package/dist/server/rsc-entry/rsc-stream.d.ts.map +1 -1
- package/dist/server/rsc-entry/ssr-bridge.d.ts +1 -1
- package/dist/server/rsc-entry/ssr-bridge.d.ts.map +1 -1
- package/dist/server/rsc-entry/ssr-renderer.d.ts +19 -4
- package/dist/server/rsc-entry/ssr-renderer.d.ts.map +1 -1
- package/dist/server/rsc-entry/wrap-action-dispatch.d.ts +119 -0
- package/dist/server/rsc-entry/wrap-action-dispatch.d.ts.map +1 -0
- package/dist/server/rsc-prop-warnings.d.ts.map +1 -1
- package/dist/server/safe-load.d.ts +46 -0
- package/dist/server/safe-load.d.ts.map +1 -0
- package/dist/server/sensitive-fields.d.ts +74 -0
- package/dist/server/sensitive-fields.d.ts.map +1 -0
- package/dist/server/sitemap-generator.d.ts +129 -0
- package/dist/server/sitemap-generator.d.ts.map +1 -0
- package/dist/server/sitemap-handler.d.ts +22 -0
- package/dist/server/sitemap-handler.d.ts.map +1 -0
- package/dist/server/slot-resolver.d.ts +1 -1
- package/dist/server/slot-resolver.d.ts.map +1 -1
- package/dist/server/ssr-entry.d.ts +23 -0
- package/dist/server/ssr-entry.d.ts.map +1 -1
- package/dist/server/ssr-render.d.ts +39 -21
- package/dist/server/ssr-render.d.ts.map +1 -1
- package/dist/server/ssr-wrappers.d.ts +50 -0
- package/dist/server/ssr-wrappers.d.ts.map +1 -0
- package/dist/server/state-tree-diff.d.ts.map +1 -1
- package/dist/server/status-code-resolver.d.ts +16 -11
- package/dist/server/status-code-resolver.d.ts.map +1 -1
- package/dist/server/stream-utils.d.ts +36 -0
- package/dist/server/stream-utils.d.ts.map +1 -0
- package/dist/server/tracing.d.ts +15 -5
- package/dist/server/tracing.d.ts.map +1 -1
- package/dist/server/tree-builder.d.ts +62 -30
- package/dist/server/tree-builder.d.ts.map +1 -1
- package/dist/server/types.d.ts +49 -4
- package/dist/server/types.d.ts.map +1 -1
- package/dist/server/utils/escape-html.d.ts +14 -0
- package/dist/server/utils/escape-html.d.ts.map +1 -0
- package/dist/server/version-skew.d.ts +61 -0
- package/dist/server/version-skew.d.ts.map +1 -0
- package/dist/server/waituntil-bridge.d.ts.map +1 -1
- package/dist/shared/merge-search-params.d.ts +22 -0
- package/dist/shared/merge-search-params.d.ts.map +1 -0
- package/dist/shims/font-google.d.ts +1 -1
- package/dist/shims/font-google.d.ts.map +1 -1
- package/dist/shims/font-google.js +42 -0
- package/dist/shims/font-google.js.map +1 -0
- package/dist/shims/font-local.d.ts +26 -0
- package/dist/shims/font-local.d.ts.map +1 -0
- package/dist/shims/font-local.js +20 -0
- package/dist/shims/font-local.js.map +1 -0
- package/dist/shims/headers.d.ts +2 -1
- package/dist/shims/headers.d.ts.map +1 -1
- package/dist/shims/navigation-client.d.ts +3 -1
- package/dist/shims/navigation-client.d.ts.map +1 -1
- package/dist/shims/navigation.d.ts +11 -5
- package/dist/shims/navigation.d.ts.map +1 -1
- package/dist/utils/directive-parser.d.ts +1 -43
- package/dist/utils/directive-parser.d.ts.map +1 -1
- package/dist/utils/state-machine.d.ts +80 -0
- package/dist/utils/state-machine.d.ts.map +1 -0
- package/package.json +56 -22
- package/src/adapters/build-output-helper.ts +77 -0
- package/src/adapters/cloudflare-dev.ts +177 -0
- package/src/adapters/cloudflare-kv-cache.ts +142 -0
- package/src/adapters/cloudflare.ts +350 -76
- package/src/adapters/compress-module.ts +24 -4
- package/src/adapters/nitro.ts +148 -60
- package/src/adapters/shared.ts +40 -0
- package/src/adapters/wrangler.d.ts +7 -0
- package/src/cache/cache-api.ts +38 -0
- package/src/cache/fast-hash.ts +34 -0
- package/src/cache/handler-store.ts +68 -0
- package/src/cache/index.ts +81 -18
- package/src/cache/singleflight.ts +62 -4
- package/src/cache/sizeof.ts +31 -0
- package/src/cache/timber-cache.ts +41 -29
- package/src/cli.ts +16 -6
- package/src/client/browser-dev.ts +153 -1
- package/src/client/browser-entry/action-dispatch.ts +116 -0
- package/src/client/browser-entry/hmr.ts +81 -0
- package/src/client/browser-entry/hydrate.ts +145 -0
- package/src/client/browser-entry/index.ts +143 -0
- package/src/client/browser-entry/post-hydration.ts +119 -0
- package/src/client/browser-entry/router-init.ts +193 -0
- package/src/client/browser-entry/rsc-stream.ts +157 -0
- package/src/client/browser-entry/scroll.ts +27 -0
- package/src/client/error-boundary.tsx +97 -20
- package/src/client/error-reconstituter.tsx +65 -0
- package/src/client/form.tsx +31 -32
- package/src/client/history.ts +26 -4
- package/src/client/index.ts +77 -43
- package/src/client/internal.ts +58 -0
- package/src/client/link-pending-store.ts +111 -0
- package/src/client/link.tsx +432 -140
- package/src/client/nav-link-store.ts +47 -0
- package/src/client/navigation-api-types.ts +112 -0
- package/src/client/navigation-api.ts +332 -0
- package/src/client/navigation-context.ts +83 -18
- package/src/client/navigation-root.tsx +342 -0
- package/src/client/nuqs-adapter.tsx +16 -3
- package/src/client/router-ref.ts +1 -1
- package/src/client/router.ts +303 -78
- package/src/client/rsc-fetch.ts +112 -8
- package/src/client/segment-cache.ts +1 -1
- package/src/client/segment-context.ts +6 -1
- package/src/client/segment-merger.ts +2 -8
- package/src/client/segment-outlet.tsx +86 -0
- package/src/client/ssr-data.ts +13 -5
- package/src/client/stale-reload.ts +74 -7
- package/src/client/state.ts +2 -2
- package/src/client/top-loader.tsx +41 -25
- package/src/client/use-cookie.ts +29 -0
- package/src/client/use-link-status.ts +7 -7
- package/src/client/{use-navigation-pending.ts → use-pending-navigation.ts} +6 -6
- package/src/client/use-query-states.ts +9 -3
- package/src/client/use-router.ts +1 -1
- package/src/client/{use-params.ts → use-segment-params.ts} +7 -5
- package/src/codec.ts +45 -0
- package/src/config-types.ts +292 -0
- package/src/config-validation.ts +303 -0
- package/src/content/index.ts +5 -13
- package/src/cookies/define-cookie.ts +301 -55
- package/src/cookies/index.ts +12 -1
- package/src/cookies/json-cookie.ts +105 -0
- package/src/cookies/validation.ts +134 -0
- package/src/dev-tools/404-page.ts +387 -0
- package/src/dev-tools/browser-logs.ts +288 -0
- package/src/dev-tools/error-page.ts +509 -0
- package/src/dev-tools/holding-server.ts +187 -0
- package/src/dev-tools/index.ts +90 -0
- package/src/dev-tools/instrumentation.ts +176 -0
- package/src/{plugins/dev-logs.ts → dev-tools/logs.ts} +3 -3
- package/src/dev-tools/overlay.ts +456 -0
- package/src/dev-tools/stack-classifier.ts +75 -0
- package/src/dev-tools/terminal.ts +183 -0
- package/src/{server/dev-warnings.ts → dev-tools/warnings.ts} +6 -50
- package/src/fonts/bundle.ts +142 -0
- package/src/fonts/css.ts +2 -1
- package/src/fonts/dev-middleware.ts +74 -0
- package/src/fonts/local.ts +7 -3
- package/src/fonts/pipeline.ts +314 -0
- package/src/fonts/transform.ts +406 -0
- package/src/fonts/types.ts +50 -1
- package/src/fonts/virtual-modules.ts +227 -0
- package/src/index.ts +376 -315
- package/src/plugin-context.ts +240 -0
- package/src/plugins/adapter-build.ts +12 -4
- package/src/plugins/build-manifest.ts +13 -2
- package/src/plugins/build-report.ts +32 -21
- package/src/plugins/chunks.ts +9 -1
- package/src/plugins/client-chunks.ts +65 -0
- package/src/plugins/content.ts +1 -1
- package/src/plugins/dev-server.ts +154 -21
- package/src/plugins/entries.ts +119 -12
- package/src/plugins/fonts.ts +177 -524
- package/src/plugins/mdx.ts +52 -15
- package/src/plugins/routing.ts +225 -41
- package/src/plugins/server-action-exports.ts +1 -1
- package/src/plugins/server-bundle.ts +36 -1
- package/src/plugins/shims.ts +136 -35
- package/src/plugins/static-build.ts +17 -11
- package/src/routing/codegen-shared.ts +98 -0
- package/src/routing/codegen-types.ts +53 -0
- package/src/routing/codegen.ts +143 -176
- package/src/routing/convention-lint.ts +357 -0
- package/src/routing/index.ts +5 -1
- package/src/routing/interception.ts +1 -1
- package/src/routing/link-codegen.ts +294 -0
- package/src/routing/scanner.ts +107 -110
- package/src/routing/segment-classify.ts +188 -0
- package/src/routing/status-file-lint.ts +10 -7
- package/src/routing/types.ts +78 -25
- package/src/routing/walkers.ts +90 -0
- package/src/rsc-runtime/rsc.ts +2 -0
- package/src/rsc-runtime/ssr.ts +50 -0
- package/src/rsc-runtime/vendor-types.d.ts +7 -0
- package/src/{search-params/codecs.ts → schema-bridge.ts} +57 -20
- package/src/search-params/define.ts +538 -0
- package/src/search-params/index.ts +14 -20
- package/src/search-params/registry.ts +2 -2
- package/src/search-params/wrappers.ts +92 -0
- package/src/segment-params/define.ts +332 -0
- package/src/segment-params/index.ts +9 -0
- package/src/server/access-gate.tsx +79 -36
- package/src/server/action-client.ts +103 -20
- package/src/server/action-encryption.ts +144 -0
- package/src/server/action-handler.ts +112 -14
- package/src/server/actions.ts +10 -9
- package/src/server/als-registry.ts +35 -7
- package/src/server/asset-headers.ts +8 -34
- package/src/server/build-manifest.ts +10 -4
- package/src/server/compress.ts +25 -7
- package/src/server/cookie-context.ts +468 -0
- package/src/server/cookie-parsing.ts +135 -0
- package/src/server/debug.ts +137 -0
- package/src/server/default-logger.ts +99 -0
- package/src/server/deny-boundary.ts +218 -0
- package/src/server/deny-renderer.ts +33 -51
- package/src/server/dev-source-map.ts +31 -0
- package/src/server/early-hints-sender.ts +3 -2
- package/src/server/early-hints.ts +36 -15
- package/src/server/error-boundary-wrapper.ts +74 -22
- package/src/server/fallback-error.ts +74 -102
- package/src/server/flight-injection-state.ts +113 -0
- package/src/server/flight-scripts.ts +62 -0
- package/src/server/flush.ts +2 -1
- package/src/server/form-data.ts +76 -0
- package/src/server/html-injector-core.ts +403 -0
- package/src/server/html-injectors.ts +244 -223
- package/src/server/index.ts +31 -184
- package/src/server/internal.ts +176 -0
- package/src/server/logger.ts +67 -35
- package/src/server/middleware-runner.ts +75 -4
- package/src/server/node-stream-transforms.ts +369 -0
- package/src/server/param-coercion.ts +76 -0
- package/src/server/pipeline-helpers.ts +204 -0
- package/src/server/pipeline-interception.ts +17 -16
- package/src/server/pipeline-metadata.ts +13 -0
- package/src/server/pipeline-outcome.ts +167 -0
- package/src/server/pipeline-phases.ts +409 -0
- package/src/server/pipeline.ts +128 -387
- package/src/server/port-resolution.ts +215 -0
- package/src/server/primitives.ts +113 -29
- package/src/server/render-timeout.ts +108 -0
- package/src/server/request-context.ts +94 -337
- package/src/server/route-element-builder.ts +289 -198
- package/src/server/route-handler.ts +24 -4
- package/src/server/route-matcher.ts +44 -65
- package/src/server/rsc-entry/action-middleware-runner.ts +167 -0
- package/src/server/rsc-entry/api-handler.ts +16 -17
- package/src/server/rsc-entry/error-renderer.ts +305 -89
- package/src/server/rsc-entry/helpers.ts +136 -12
- package/src/server/rsc-entry/index.ts +330 -312
- package/src/server/rsc-entry/render-route.ts +304 -0
- package/src/server/rsc-entry/rsc-payload.ts +100 -18
- package/src/server/rsc-entry/rsc-stream.ts +90 -17
- package/src/server/rsc-entry/ssr-bridge.ts +14 -5
- package/src/server/rsc-entry/ssr-renderer.ts +177 -37
- package/src/server/rsc-entry/wrap-action-dispatch.ts +449 -0
- package/src/server/rsc-prop-warnings.ts +3 -1
- package/src/server/safe-load.ts +60 -0
- package/src/server/sensitive-fields.ts +230 -0
- package/src/server/sitemap-generator.ts +338 -0
- package/src/server/sitemap-handler.ts +126 -0
- package/src/server/slot-resolver.ts +245 -230
- package/src/server/ssr-entry.ts +215 -32
- package/src/server/ssr-render.ts +289 -67
- package/src/server/ssr-wrappers.tsx +139 -0
- package/src/server/state-tree-diff.ts +4 -1
- package/src/server/status-code-resolver.ts +112 -128
- package/src/server/stream-utils.ts +213 -0
- package/src/server/tracing.ts +44 -10
- package/src/server/tree-builder.ts +202 -90
- package/src/server/types.ts +55 -6
- package/src/server/utils/escape-html.ts +20 -0
- package/src/server/version-skew.ts +104 -0
- package/src/server/waituntil-bridge.ts +4 -1
- package/src/shared/merge-search-params.ts +55 -0
- package/src/shims/font-google.ts +1 -1
- package/src/shims/font-local.ts +34 -0
- package/src/shims/headers.ts +5 -1
- package/src/shims/navigation-client.ts +4 -3
- package/src/shims/navigation.ts +14 -7
- package/src/utils/directive-parser.ts +1 -390
- package/src/utils/state-machine.ts +111 -0
- package/dist/_chunks/als-registry-k-AtAQ9R.js +0 -41
- package/dist/_chunks/als-registry-k-AtAQ9R.js.map +0 -1
- package/dist/_chunks/format-DNt20Kt8.js.map +0 -1
- package/dist/_chunks/interception-DGDIjDbR.js +0 -676
- package/dist/_chunks/interception-DGDIjDbR.js.map +0 -1
- package/dist/_chunks/request-context-CRj2Zh1E.js +0 -329
- package/dist/_chunks/request-context-CRj2Zh1E.js.map +0 -1
- package/dist/_chunks/ssr-data-DLnbYpj1.js.map +0 -1
- package/dist/_chunks/tracing-DF0G3FB7.js +0 -173
- package/dist/_chunks/tracing-DF0G3FB7.js.map +0 -1
- package/dist/_chunks/use-cookie-dDbpCTx-.js +0 -91
- package/dist/_chunks/use-cookie-dDbpCTx-.js.map +0 -1
- package/dist/_chunks/use-query-states-DAhgj8Gx.js.map +0 -1
- package/dist/cache/register-cached-function.d.ts +0 -17
- package/dist/cache/register-cached-function.d.ts.map +0 -1
- package/dist/client/browser-entry.d.ts +0 -22
- package/dist/client/browser-entry.d.ts.map +0 -1
- package/dist/client/browser-links.d.ts +0 -32
- package/dist/client/browser-links.d.ts.map +0 -1
- package/dist/client/link-navigate-interceptor.d.ts +0 -28
- package/dist/client/link-navigate-interceptor.d.ts.map +0 -1
- package/dist/client/link-status-provider.d.ts +0 -11
- package/dist/client/link-status-provider.d.ts.map +0 -1
- package/dist/client/transition-root.d.ts.map +0 -1
- package/dist/client/use-navigation-pending.d.ts.map +0 -1
- package/dist/client/use-params.d.ts.map +0 -1
- package/dist/plugins/cache-transform.d.ts +0 -36
- package/dist/plugins/cache-transform.d.ts.map +0 -1
- package/dist/plugins/dev-error-overlay.d.ts +0 -60
- package/dist/plugins/dev-error-overlay.d.ts.map +0 -1
- package/dist/plugins/dev-logs.d.ts.map +0 -1
- package/dist/plugins/dynamic-transform.d.ts +0 -72
- package/dist/plugins/dynamic-transform.d.ts.map +0 -1
- package/dist/search-params/analyze.d.ts +0 -54
- package/dist/search-params/analyze.d.ts.map +0 -1
- package/dist/search-params/builtin-codecs.d.ts +0 -105
- package/dist/search-params/builtin-codecs.d.ts.map +0 -1
- package/dist/search-params/codecs.d.ts +0 -53
- package/dist/search-params/codecs.d.ts.map +0 -1
- package/dist/search-params/create.d.ts +0 -106
- package/dist/search-params/create.d.ts.map +0 -1
- package/dist/server/dev-fetch-instrumentation.d.ts +0 -22
- package/dist/server/dev-fetch-instrumentation.d.ts.map +0 -1
- package/dist/server/dev-logger.d.ts.map +0 -1
- package/dist/server/dev-span-processor.d.ts.map +0 -1
- package/dist/server/dev-warnings.d.ts.map +0 -1
- package/dist/server/manifest-status-resolver.d.ts +0 -58
- package/dist/server/manifest-status-resolver.d.ts.map +0 -1
- package/dist/server/prerender.d.ts +0 -77
- package/dist/server/prerender.d.ts.map +0 -1
- package/src/cache/register-cached-function.ts +0 -99
- package/src/client/browser-entry.ts +0 -686
- package/src/client/browser-links.ts +0 -90
- package/src/client/link-navigate-interceptor.tsx +0 -62
- package/src/client/link-status-provider.tsx +0 -30
- package/src/client/transition-root.tsx +0 -160
- package/src/plugins/cache-transform.ts +0 -199
- package/src/plugins/dev-error-overlay.ts +0 -230
- package/src/plugins/dynamic-transform.ts +0 -161
- package/src/search-params/analyze.ts +0 -192
- package/src/search-params/builtin-codecs.ts +0 -228
- package/src/search-params/create.ts +0 -321
- package/src/server/dev-fetch-instrumentation.ts +0 -96
- package/src/server/dev-span-processor.ts +0 -78
- package/src/server/manifest-status-resolver.ts +0 -215
- package/src/server/prerender.ts +0 -139
- /package/src/{server/dev-logger.ts → dev-tools/logger.ts} +0 -0
|
@@ -131,6 +131,13 @@ interface ActionClientConfig<TCtx> {
|
|
|
131
131
|
middleware?: ActionMiddleware<TCtx> | ActionMiddleware<Record<string, unknown>>[];
|
|
132
132
|
/** Max file size in bytes. Files exceeding this are rejected with validation errors. */
|
|
133
133
|
fileSizeLimit?: number;
|
|
134
|
+
/**
|
|
135
|
+
* Override the sensitive-field deny-list for this action client.
|
|
136
|
+
* See `SensitiveFieldsOption` in `./sensitive-fields.ts`. Per-action config
|
|
137
|
+
* takes precedence over the global `forms.stripSensitiveFields` option in
|
|
138
|
+
* `timber.config.ts`. See design/08-forms-and-actions.md and TIM-816.
|
|
139
|
+
*/
|
|
140
|
+
stripSensitiveFields?: SensitiveFieldsOption;
|
|
134
141
|
}
|
|
135
142
|
|
|
136
143
|
/** Intermediate builder returned by createActionClient(). */
|
|
@@ -138,22 +145,53 @@ export interface ActionBuilder<TCtx> {
|
|
|
138
145
|
/** Declare the input schema. Validation errors are returned typed. */
|
|
139
146
|
schema<TInput>(schema: ActionSchema<TInput>): ActionBuilderWithSchema<TCtx, TInput>;
|
|
140
147
|
/** Define the action body without input validation. */
|
|
141
|
-
action<TData>(
|
|
148
|
+
action<TData>(
|
|
149
|
+
fn: (ctx: ActionContext<TCtx, undefined>) => Promise<TData>
|
|
150
|
+
): ActionFn<TData, undefined>;
|
|
142
151
|
}
|
|
143
152
|
|
|
144
153
|
/** Builder after .schema() has been called. */
|
|
145
154
|
export interface ActionBuilderWithSchema<TCtx, TInput> {
|
|
146
155
|
/** Define the action body with validated input. */
|
|
147
|
-
action<TData>(fn: (ctx: ActionContext<TCtx, TInput>) => Promise<TData>): ActionFn<TData>;
|
|
156
|
+
action<TData>(fn: (ctx: ActionContext<TCtx, TInput>) => Promise<TData>): ActionFn<TData, TInput>;
|
|
148
157
|
}
|
|
149
158
|
|
|
150
159
|
/**
|
|
151
|
-
* The final action function. Callable
|
|
160
|
+
* The final action function. Callable three ways:
|
|
152
161
|
* - Direct: action(input) → Promise<ActionResult<TData>>
|
|
153
162
|
* - React useActionState: action(prevState, formData) → Promise<ActionResult<TData>>
|
|
163
|
+
* - React <form action={fn}>: action(formData) → void (return value ignored by React)
|
|
164
|
+
*
|
|
165
|
+
* The third overload exists purely for type compatibility with React's
|
|
166
|
+
* `<form action>` prop, which expects `(formData: FormData) => void`.
|
|
167
|
+
* At runtime the function still returns Promise<ActionResult>, but React
|
|
168
|
+
* discards it. This lets validated actions be passed directly to forms
|
|
169
|
+
* without casts.
|
|
170
|
+
*/
|
|
171
|
+
/**
|
|
172
|
+
* Map schema output keys to `string | undefined` for form-facing APIs.
|
|
173
|
+
* HTML form values are always strings, and fields can be absent.
|
|
174
|
+
* Gives autocomplete for field names without lying about value types.
|
|
154
175
|
*/
|
|
155
|
-
export type
|
|
156
|
-
|
|
176
|
+
export type InputHint<T> =
|
|
177
|
+
T extends Record<string, unknown> ? { [K in keyof T]: string | undefined } : T;
|
|
178
|
+
|
|
179
|
+
/**
|
|
180
|
+
* ActionFn — the callable returned by `createActionClient().action()`.
|
|
181
|
+
*
|
|
182
|
+
* Generic order: `<TData, TInput>` — TData first for backward compatibility.
|
|
183
|
+
* Previously ActionFn had a single `<TData>` generic, so existing code like
|
|
184
|
+
* `ActionFn<MyResult>` must still work with TData in the first position.
|
|
185
|
+
* See TIM-797.
|
|
186
|
+
*/
|
|
187
|
+
export type ActionFn<TData = unknown, TInput = unknown> = {
|
|
188
|
+
/** <form action={fn}> compatibility — React discards the return value. */
|
|
189
|
+
(formData: FormData): void;
|
|
190
|
+
/** Direct call: action(input) — optional when TInput is undefined/unknown (no-schema actions). */
|
|
191
|
+
(
|
|
192
|
+
...args: undefined extends TInput ? [input?: TInput] : [input: TInput]
|
|
193
|
+
): Promise<ActionResult<TData>>;
|
|
194
|
+
/** React useActionState: action(prevState, formData) */
|
|
157
195
|
(prevState: ActionResult<TData> | null, formData: FormData): Promise<ActionResult<TData>>;
|
|
158
196
|
};
|
|
159
197
|
|
|
@@ -183,7 +221,15 @@ async function runActionMiddleware<TCtx>(
|
|
|
183
221
|
|
|
184
222
|
// Re-export parseFormData for use throughout the framework
|
|
185
223
|
import { parseFormData } from './form-data.js';
|
|
186
|
-
import { formatSize } from '
|
|
224
|
+
import { formatSize } from '../utils/format.js';
|
|
225
|
+
import { isDebug, isDevMode } from './debug.js';
|
|
226
|
+
import { RedirectSignal, DenySignal } from './primitives.js';
|
|
227
|
+
import {
|
|
228
|
+
stripSensitiveFields,
|
|
229
|
+
resolveSensitivePredicate,
|
|
230
|
+
getGlobalSensitiveFieldsConfig,
|
|
231
|
+
type SensitiveFieldsOption,
|
|
232
|
+
} from './sensitive-fields.js';
|
|
187
233
|
|
|
188
234
|
/**
|
|
189
235
|
* Extract validation errors from a schema error.
|
|
@@ -246,12 +292,15 @@ export function handleActionError(error: unknown): ActionResult<never> {
|
|
|
246
292
|
};
|
|
247
293
|
}
|
|
248
294
|
|
|
249
|
-
// In dev, include the message for debugging
|
|
250
|
-
|
|
295
|
+
// In dev, include the message for debugging.
|
|
296
|
+
// Uses isDevMode() — NOT isDebug() — because this data is sent to the
|
|
297
|
+
// browser. TIMBER_DEBUG must never cause error messages to leak to clients.
|
|
298
|
+
// See design/13-security.md principle 4: "Errors don't leak."
|
|
299
|
+
const devMode = isDevMode();
|
|
251
300
|
return {
|
|
252
301
|
serverError: {
|
|
253
302
|
code: 'INTERNAL_ERROR',
|
|
254
|
-
...(
|
|
303
|
+
...(devMode && error instanceof Error ? { data: { message: error.message } } : {}),
|
|
255
304
|
},
|
|
256
305
|
};
|
|
257
306
|
}
|
|
@@ -282,7 +331,7 @@ export function createActionClient<TCtx = Record<string, never>>(
|
|
|
282
331
|
function buildAction<TInput, TData>(
|
|
283
332
|
schema: ActionSchema<TInput> | undefined,
|
|
284
333
|
fn: (ctx: ActionContext<TCtx, TInput>) => Promise<TData>
|
|
285
|
-
): ActionFn<TData> {
|
|
334
|
+
): ActionFn<TData, TInput> {
|
|
286
335
|
async function actionHandler(...args: unknown[]): Promise<ActionResult<TData>> {
|
|
287
336
|
try {
|
|
288
337
|
// Run middleware
|
|
@@ -291,13 +340,38 @@ export function createActionClient<TCtx = Record<string, never>>(
|
|
|
291
340
|
// Determine input — either FormData (from useActionState) or direct arg
|
|
292
341
|
let rawInput: unknown;
|
|
293
342
|
if (args.length === 2 && args[1] instanceof FormData) {
|
|
294
|
-
// Called as (prevState, formData) by React useActionState
|
|
343
|
+
// Called as (prevState, formData) by React useActionState (with-JS path)
|
|
295
344
|
rawInput = schema ? parseFormData(args[1]) : args[1];
|
|
345
|
+
} else if (args.length === 1 && args[0] instanceof FormData) {
|
|
346
|
+
// No-JS path: React's decodeAction binds FormData as the sole argument.
|
|
347
|
+
// The form POSTs without JavaScript, decodeAction resolves the server
|
|
348
|
+
// reference and binds the FormData, then executeAction calls fn() with
|
|
349
|
+
// no additional args — so the bound FormData arrives as args[0].
|
|
350
|
+
rawInput = schema ? parseFormData(args[0]) : args[0];
|
|
296
351
|
} else {
|
|
297
352
|
// Direct call: action(input)
|
|
298
353
|
rawInput = args[0];
|
|
299
354
|
}
|
|
300
355
|
|
|
356
|
+
// Resolve the sensitive-field stripping predicate once per invocation.
|
|
357
|
+
// Precedence: per-action (config.stripSensitiveFields) > global
|
|
358
|
+
// (forms.stripSensitiveFields from timber.config.ts) > built-in deny-list.
|
|
359
|
+
// See TIM-816.
|
|
360
|
+
const sensitivePredicate = resolveSensitivePredicate(
|
|
361
|
+
config.stripSensitiveFields,
|
|
362
|
+
getGlobalSensitiveFieldsConfig()
|
|
363
|
+
);
|
|
364
|
+
|
|
365
|
+
// Capture a "safe-to-echo" snapshot of the raw input once. Files are
|
|
366
|
+
// stripped (can't serialize, shouldn't echo back) and sensitive fields
|
|
367
|
+
// (passwords, tokens, CVV, etc.) are removed before they would land
|
|
368
|
+
// in the RSC payload → client form `defaultValue` → DOM.
|
|
369
|
+
const buildSubmittedValues = (): Record<string, unknown> | undefined => {
|
|
370
|
+
const withoutFiles = stripFiles(rawInput);
|
|
371
|
+
if (withoutFiles === undefined) return undefined;
|
|
372
|
+
return stripSensitiveFields(withoutFiles, sensitivePredicate);
|
|
373
|
+
};
|
|
374
|
+
|
|
301
375
|
// Validate file sizes before schema validation.
|
|
302
376
|
if (config.fileSizeLimit !== undefined && rawInput && typeof rawInput === 'object') {
|
|
303
377
|
const fileSizeErrors = validateFileSizes(
|
|
@@ -305,14 +379,12 @@ export function createActionClient<TCtx = Record<string, never>>(
|
|
|
305
379
|
config.fileSizeLimit
|
|
306
380
|
);
|
|
307
381
|
if (fileSizeErrors) {
|
|
308
|
-
|
|
309
|
-
return { validationErrors: fileSizeErrors, submittedValues };
|
|
382
|
+
return { validationErrors: fileSizeErrors, submittedValues: buildSubmittedValues() };
|
|
310
383
|
}
|
|
311
384
|
}
|
|
312
385
|
|
|
313
386
|
// Capture submitted values for repopulation on validation failure.
|
|
314
|
-
|
|
315
|
-
const submittedValues = schema ? stripFiles(rawInput) : undefined;
|
|
387
|
+
const submittedValues = schema ? buildSubmittedValues() : undefined;
|
|
316
388
|
|
|
317
389
|
// Validate with schema if provided
|
|
318
390
|
let input: TInput;
|
|
@@ -356,22 +428,33 @@ export function createActionClient<TCtx = Record<string, never>>(
|
|
|
356
428
|
const data = await fn({ ctx, input });
|
|
357
429
|
return { data };
|
|
358
430
|
} catch (error) {
|
|
431
|
+
// Re-throw redirect/deny signals — these are control flow, not errors.
|
|
432
|
+
// They must propagate to executeAction() which converts them to proper
|
|
433
|
+
// HTTP responses (302 redirect, 4xx deny). Catching them here would
|
|
434
|
+
// wrap them as INTERNAL_ERROR and break redirect()/redirectExternal()/deny().
|
|
435
|
+
if (error instanceof RedirectSignal || error instanceof DenySignal) {
|
|
436
|
+
throw error;
|
|
437
|
+
}
|
|
359
438
|
return handleActionError(error);
|
|
360
439
|
}
|
|
361
440
|
}
|
|
362
441
|
|
|
363
|
-
return actionHandler as ActionFn<TData>;
|
|
442
|
+
return actionHandler as ActionFn<TData, TInput>;
|
|
364
443
|
}
|
|
365
444
|
|
|
366
445
|
return {
|
|
367
446
|
schema<TInput>(schema: ActionSchema<TInput>) {
|
|
368
447
|
return {
|
|
369
|
-
action<TData>(
|
|
448
|
+
action<TData>(
|
|
449
|
+
fn: (ctx: ActionContext<TCtx, TInput>) => Promise<TData>
|
|
450
|
+
): ActionFn<TData, TInput> {
|
|
370
451
|
return buildAction(schema, fn);
|
|
371
452
|
},
|
|
372
453
|
};
|
|
373
454
|
},
|
|
374
|
-
action<TData>(
|
|
455
|
+
action<TData>(
|
|
456
|
+
fn: (ctx: ActionContext<TCtx, undefined>) => Promise<TData>
|
|
457
|
+
): ActionFn<TData, undefined> {
|
|
375
458
|
return buildAction(undefined, fn as (ctx: ActionContext<TCtx, unknown>) => Promise<TData>);
|
|
376
459
|
},
|
|
377
460
|
};
|
|
@@ -400,7 +483,7 @@ export function createActionClient<TCtx = Record<string, never>>(
|
|
|
400
483
|
export function validated<TInput, TData>(
|
|
401
484
|
schema: ActionSchema<TInput>,
|
|
402
485
|
handler: (input: TInput) => Promise<TData>
|
|
403
|
-
): ActionFn<TData> {
|
|
486
|
+
): ActionFn<TData, TInput> {
|
|
404
487
|
return createActionClient()
|
|
405
488
|
.schema(schema)
|
|
406
489
|
.action(async ({ input }) => handler(input));
|
|
@@ -413,7 +496,7 @@ export function validated<TInput, TData>(
|
|
|
413
496
|
* In production, validation errors are only returned to the client.
|
|
414
497
|
*/
|
|
415
498
|
function logValidationFailure(errors: ValidationErrors): void {
|
|
416
|
-
const isDev =
|
|
499
|
+
const isDev = isDebug();
|
|
417
500
|
if (!isDev) return;
|
|
418
501
|
|
|
419
502
|
const fields = Object.entries(errors)
|
|
@@ -0,0 +1,144 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Server action bound args encryption utilities.
|
|
3
|
+
*
|
|
4
|
+
* Provides key management for the RSC plugin's built-in bound args encryption.
|
|
5
|
+
* The RSC plugin (@vitejs/plugin-rsc) handles the actual encrypt/decrypt via
|
|
6
|
+
* AES-256-GCM — this module handles:
|
|
7
|
+
*
|
|
8
|
+
* 1. Key sourcing: auto-generated at build time (embedded in bundle), overridable
|
|
9
|
+
* via env var for cross-build key sharing (rolling/blue-green deployments)
|
|
10
|
+
* 2. Build-time key expression generation for the RSC plugin's `defineEncryptionKey`
|
|
11
|
+
*
|
|
12
|
+
* Encryption is always on in production. In dev mode, it's on by default
|
|
13
|
+
* (matching the RSC plugin's behavior) but can be disabled for debugging.
|
|
14
|
+
*
|
|
15
|
+
* ## Known Security Considerations
|
|
16
|
+
*
|
|
17
|
+
* 1. **defineEncryptionKey is a raw JS expression.** The RSC plugin inlines it
|
|
18
|
+
* verbatim into generated code. We only emit the hardcoded string
|
|
19
|
+
* `process.env.TIMBER_ACTIONS_ENCRYPTION_KEY` — never user-controlled input.
|
|
20
|
+
* If this function is ever extended to accept configurable env var names,
|
|
21
|
+
* the expression MUST be validated against a safe pattern.
|
|
22
|
+
*
|
|
23
|
+
* 2. **Key material lives in GC-visible JS strings.** `atob()` decodes the key
|
|
24
|
+
* into a regular JavaScript string on the V8 heap. JavaScript has no
|
|
25
|
+
* `SecureString` or memory-zeroing primitive — this is an inherent platform
|
|
26
|
+
* limitation. Acceptable for web server use; would need review for FIPS.
|
|
27
|
+
*
|
|
28
|
+
* 3. **TIMBER_ACTIONS_ENCRYPTION_KEY must be set at both build time and runtime.**
|
|
29
|
+
* At build time, we validate the key format and emit a runtime expression.
|
|
30
|
+
* If the env var is present at build time but missing at runtime, the server
|
|
31
|
+
* will crash on first action invocation with an opaque `atob(undefined)` error.
|
|
32
|
+
* If the env var is present at runtime but was absent at build time, the RSC
|
|
33
|
+
* plugin will have generated its own key and the env var is silently ignored.
|
|
34
|
+
*
|
|
35
|
+
* See design/08-forms-and-actions.md §"Security"
|
|
36
|
+
* See design/13-security.md
|
|
37
|
+
*/
|
|
38
|
+
|
|
39
|
+
// ─── Types ────────────────────────────────────────────────────────────────
|
|
40
|
+
|
|
41
|
+
/** User-facing configuration for action bound args encryption. */
|
|
42
|
+
export interface ActionEncryptionConfig {
|
|
43
|
+
/**
|
|
44
|
+
* Disable encryption in dev mode for easier debugging.
|
|
45
|
+
* Has no effect in production — encryption is always enabled.
|
|
46
|
+
* Default: false (encryption is on in dev too).
|
|
47
|
+
*/
|
|
48
|
+
disableInDev?: boolean;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
// ─── Key Resolution ───────────────────────────────────────────────────────
|
|
52
|
+
|
|
53
|
+
/**
|
|
54
|
+
* Regex for safe `defineEncryptionKey` expressions.
|
|
55
|
+
*
|
|
56
|
+
* The RSC plugin inlines this expression verbatim into generated JavaScript.
|
|
57
|
+
* We restrict it to `process.env.<UPPER_SNAKE_CASE>` to prevent code injection.
|
|
58
|
+
* See "Known Security Considerations" at the top of this file.
|
|
59
|
+
*/
|
|
60
|
+
const SAFE_KEY_EXPR = /^process\.env\.[A-Z_][A-Z0-9_]*$/;
|
|
61
|
+
|
|
62
|
+
/**
|
|
63
|
+
* Build the `defineEncryptionKey` expression for the RSC plugin.
|
|
64
|
+
*
|
|
65
|
+
* The RSC plugin accepts a JavaScript expression string that will be
|
|
66
|
+
* inlined into the encryption runtime module. At runtime, this expression
|
|
67
|
+
* must evaluate to the base64-encoded encryption key.
|
|
68
|
+
*
|
|
69
|
+
* Priority:
|
|
70
|
+
* 1. `TIMBER_ACTIONS_ENCRYPTION_KEY` env var (for cross-build key sharing
|
|
71
|
+
* in rolling/blue-green deployments)
|
|
72
|
+
* 2. Auto-generated at build time (RSC plugin default — embedded in bundle,
|
|
73
|
+
* consistent across all instances of the same build)
|
|
74
|
+
*
|
|
75
|
+
* For env var keys, we generate a runtime expression that reads the env var.
|
|
76
|
+
* For auto-generated keys, we return undefined and let the RSC plugin handle it.
|
|
77
|
+
*/
|
|
78
|
+
export function resolveEncryptionKeyExpression(): string | undefined {
|
|
79
|
+
// Check for env var override — used for cross-build key sharing where
|
|
80
|
+
// multiple builds must agree on the same encryption key.
|
|
81
|
+
const envKey = process.env.TIMBER_ACTIONS_ENCRYPTION_KEY;
|
|
82
|
+
if (envKey) {
|
|
83
|
+
// Validate the key format (must be base64-encoded 32-byte key)
|
|
84
|
+
validateKeyFormat(envKey);
|
|
85
|
+
|
|
86
|
+
// Return a runtime expression that reads the env var at startup.
|
|
87
|
+
// This ensures the key is read at runtime, not embedded in the build.
|
|
88
|
+
const expr = 'process.env.TIMBER_ACTIONS_ENCRYPTION_KEY';
|
|
89
|
+
|
|
90
|
+
// Defense-in-depth: validate the expression matches our safe pattern.
|
|
91
|
+
// This is redundant today (hardcoded string), but protects against
|
|
92
|
+
// future refactors that might make the expression configurable.
|
|
93
|
+
if (!SAFE_KEY_EXPR.test(expr)) {
|
|
94
|
+
throw new Error(`Unsafe encryption key expression: ${expr}`);
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
return expr;
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
// No override — let the RSC plugin auto-generate a per-build key
|
|
101
|
+
return undefined;
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
/**
|
|
105
|
+
* Determine whether action encryption should be enabled.
|
|
106
|
+
*
|
|
107
|
+
* Encryption is always enabled in production. In dev mode, it's enabled
|
|
108
|
+
* by default but can be disabled via config for debugging.
|
|
109
|
+
*/
|
|
110
|
+
export function shouldEnableEncryption(isDev: boolean, config?: ActionEncryptionConfig): boolean {
|
|
111
|
+
if (!isDev) return true; // Always on in production
|
|
112
|
+
if (config?.disableInDev) return false; // Opt-out in dev
|
|
113
|
+
return true; // On by default in dev too
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
// ─── Key Validation ───────────────────────────────────────────────────────
|
|
117
|
+
|
|
118
|
+
/**
|
|
119
|
+
* Validate that a key string is a valid base64-encoded 256-bit key.
|
|
120
|
+
* Throws a descriptive error if the key is malformed.
|
|
121
|
+
*/
|
|
122
|
+
export function validateKeyFormat(key: string): void {
|
|
123
|
+
// Decode base64 and check length (32 bytes = 256 bits)
|
|
124
|
+
try {
|
|
125
|
+
const decoded = atob(key);
|
|
126
|
+
const bytes = decoded.length;
|
|
127
|
+
if (bytes !== 32) {
|
|
128
|
+
throw new Error(
|
|
129
|
+
`TIMBER_ACTIONS_ENCRYPTION_KEY must be a base64-encoded 256-bit (32-byte) key. ` +
|
|
130
|
+
`Got ${bytes} bytes. Generate one with: ` +
|
|
131
|
+
`node -e "console.log(require('crypto').randomBytes(32).toString('base64'))"`
|
|
132
|
+
);
|
|
133
|
+
}
|
|
134
|
+
} catch (error) {
|
|
135
|
+
if (error instanceof Error && error.message.includes('TIMBER_ACTIONS_ENCRYPTION_KEY')) {
|
|
136
|
+
throw error;
|
|
137
|
+
}
|
|
138
|
+
throw new Error(
|
|
139
|
+
`TIMBER_ACTIONS_ENCRYPTION_KEY is not valid base64. ` +
|
|
140
|
+
`Generate a key with: ` +
|
|
141
|
+
`node -e "console.log(require('crypto').randomBytes(32).toString('base64'))"`
|
|
142
|
+
);
|
|
143
|
+
}
|
|
144
|
+
}
|
|
@@ -18,19 +18,24 @@ import {
|
|
|
18
18
|
decodeReply,
|
|
19
19
|
decodeAction,
|
|
20
20
|
renderToReadableStream,
|
|
21
|
-
} from '
|
|
21
|
+
} from '../rsc-runtime/rsc.js';
|
|
22
22
|
|
|
23
23
|
import { validateCsrf, type CsrfConfig } from './csrf.js';
|
|
24
24
|
import { executeAction, type RevalidateRenderer } from './actions.js';
|
|
25
|
-
import {
|
|
26
|
-
|
|
27
|
-
setMutableCookieContext,
|
|
28
|
-
getSetCookieHeaders,
|
|
29
|
-
} from './request-context.js';
|
|
25
|
+
import { runWithRequestContext, setMutableCookieContext } from './request-context.js';
|
|
26
|
+
import { getSetCookieHeaders, getCookiesForSsr } from './cookie-context.js';
|
|
30
27
|
import { handleActionError } from './action-client.js';
|
|
31
28
|
import { enforceBodyLimits, enforceFieldLimit, type BodyLimitsConfig } from './body-limits.js';
|
|
32
29
|
import { parseFormData } from './form-data.js';
|
|
30
|
+
import {
|
|
31
|
+
stripSensitiveFields,
|
|
32
|
+
resolveSensitivePredicate,
|
|
33
|
+
getGlobalSensitiveFieldsConfig,
|
|
34
|
+
type SensitiveFieldsOption,
|
|
35
|
+
} from './sensitive-fields.js';
|
|
33
36
|
import type { FormFlashData } from './form-flash.js';
|
|
37
|
+
import { checkVersionSkew, applyReloadHeaders } from './version-skew.js';
|
|
38
|
+
import { logActionError } from './logger.js';
|
|
34
39
|
|
|
35
40
|
// ─── Types ────────────────────────────────────────────────────────────────
|
|
36
41
|
|
|
@@ -42,6 +47,12 @@ export interface ActionDispatchConfig {
|
|
|
42
47
|
revalidateRenderer?: RevalidateRenderer;
|
|
43
48
|
/** Body size limits (from timber.config.ts). */
|
|
44
49
|
bodyLimits?: BodyLimitsConfig;
|
|
50
|
+
/**
|
|
51
|
+
* Override the sensitive-field deny-list for the no-JS form POST path.
|
|
52
|
+
* Defaults to the global `forms.stripSensitiveFields` from `timber.config.ts`.
|
|
53
|
+
* See `SensitiveFieldsOption` in `./sensitive-fields.ts` and TIM-816.
|
|
54
|
+
*/
|
|
55
|
+
sensitiveFields?: SensitiveFieldsOption;
|
|
45
56
|
}
|
|
46
57
|
|
|
47
58
|
// ─── Constants ────────────────────────────────────────────────────────────
|
|
@@ -57,6 +68,12 @@ const RSC_CONTENT_TYPE = 'text/x-component';
|
|
|
57
68
|
* - With JS: POST with `x-rsc-action` header (client callServer dispatch)
|
|
58
69
|
* - Without JS: POST with form data containing `$ACTION_REF` or `$ACTION_KEY`
|
|
59
70
|
* (React's progressive enhancement hidden fields)
|
|
71
|
+
*
|
|
72
|
+
* **Important:** This function returns true for ANY POST with a form
|
|
73
|
+
* Content-Type, including non-action POSTs to route.ts API handlers.
|
|
74
|
+
* The caller (wrap-action-dispatch.ts) MUST check the matched route type
|
|
75
|
+
* before entering the action path — route.ts matches skip action detection
|
|
76
|
+
* entirely so their body is not pre-parsed. See TIM-870.
|
|
60
77
|
*/
|
|
61
78
|
export function isActionRequest(req: Request): boolean {
|
|
62
79
|
if (req.method !== 'POST') return false;
|
|
@@ -75,9 +92,30 @@ export function isActionRequest(req: Request): boolean {
|
|
|
75
92
|
|
|
76
93
|
// ─── Handler ──────────────────────────────────────────────────────────────
|
|
77
94
|
|
|
78
|
-
/**
|
|
95
|
+
/**
|
|
96
|
+
* Signal from handleFormAction to re-render the page with flash data instead of redirecting.
|
|
97
|
+
*
|
|
98
|
+
* Carries two cookie-related snapshots taken before the action's ALS scope
|
|
99
|
+
* exits, so the rerender pipeline (which establishes its own fresh cookie
|
|
100
|
+
* jar from the original request) doesn't lose the action's mutations:
|
|
101
|
+
*
|
|
102
|
+
* - `setCookieHeaders`: serialized Set-Cookie headers to append to the
|
|
103
|
+
* final HTML response. Without this, cookies set inside the action are
|
|
104
|
+
* silently dropped from the response. See TIM-836 (LOCAL-740).
|
|
105
|
+
*
|
|
106
|
+
* - `cookies`: the post-action read-your-own-writes view of the cookie
|
|
107
|
+
* jar, as the same `Map<string, string>` shape used by the request
|
|
108
|
+
* context's `parsedCookies`. The rerender dispatcher seeds this map
|
|
109
|
+
* directly into the rerender request context via `seedRequestCookies`,
|
|
110
|
+
* bypassing any string round-trip through a `Cookie:` header. The
|
|
111
|
+
* direct-Map seed eliminates the value-smuggling primitive that the
|
|
112
|
+
* previous `cookieHeader: string` shape carried — see
|
|
113
|
+
* ONGOING_SECURITY.md H-3 (TIM-868) and TIM-837.
|
|
114
|
+
*/
|
|
79
115
|
export interface FormRerender {
|
|
80
116
|
rerender: FormFlashData;
|
|
117
|
+
setCookieHeaders: string[];
|
|
118
|
+
cookies: Map<string, string>;
|
|
81
119
|
}
|
|
82
120
|
|
|
83
121
|
/**
|
|
@@ -90,7 +128,28 @@ export async function handleActionRequest(
|
|
|
90
128
|
req: Request,
|
|
91
129
|
config: ActionDispatchConfig
|
|
92
130
|
): Promise<Response | FormRerender | null> {
|
|
131
|
+
// Version skew detection — reject actions from stale clients (TIM-446).
|
|
132
|
+
// On mismatch, return a structured RSC error response that the client
|
|
133
|
+
// handles by showing a brief "App updated" message and reloading.
|
|
134
|
+
const skewCheck = checkVersionSkew(req);
|
|
135
|
+
if (!skewCheck.ok) {
|
|
136
|
+
const reloadHeaders = new Headers({
|
|
137
|
+
'Content-Type': RSC_CONTENT_TYPE,
|
|
138
|
+
});
|
|
139
|
+
applyReloadHeaders(reloadHeaders);
|
|
140
|
+
// Return the reload signal as an RSC stream so createFromFetch can
|
|
141
|
+
// decode it. The client checks X-Timber-Reload before processing.
|
|
142
|
+
const rscStream = renderToReadableStream({ _versionSkew: true });
|
|
143
|
+
return new Response(rscStream, { status: 200, headers: reloadHeaders });
|
|
144
|
+
}
|
|
145
|
+
|
|
93
146
|
// CSRF validation — reject cross-origin mutation requests.
|
|
147
|
+
//
|
|
148
|
+
// Defense-in-depth: the pipeline boundary in rsc-entry/index.ts already
|
|
149
|
+
// validates Origin on every unsafe-method request before dispatch reaches
|
|
150
|
+
// here, so this call is a no-op on the happy path. It is intentionally
|
|
151
|
+
// retained so that handleActionRequest remains safe to call from any
|
|
152
|
+
// future entry point that bypasses the wrapper. See LOCAL-773.
|
|
94
153
|
const csrfResult = validateCsrf(req, config.csrf);
|
|
95
154
|
if (!csrfResult.ok) {
|
|
96
155
|
return new Response(null, { status: csrfResult.status });
|
|
@@ -106,7 +165,7 @@ export async function handleActionRequest(
|
|
|
106
165
|
return new Response(null, { status: limitsResult.status });
|
|
107
166
|
}
|
|
108
167
|
|
|
109
|
-
// Run inside request context so
|
|
168
|
+
// Run inside request context so getHeaders(), getCookies() work in actions.
|
|
110
169
|
// Actions are a mutable context — they can set cookies (design/29-cookies.md).
|
|
111
170
|
return runWithRequestContext(req, async () => {
|
|
112
171
|
setMutableCookieContext(true);
|
|
@@ -121,11 +180,27 @@ export async function handleActionRequest(
|
|
|
121
180
|
result = await handleFormAction(req, config);
|
|
122
181
|
}
|
|
123
182
|
|
|
124
|
-
// Apply cookie jar to action responses
|
|
183
|
+
// Apply cookie jar to action responses.
|
|
184
|
+
//
|
|
185
|
+
// For Response results we append Set-Cookie directly. For FormRerender
|
|
186
|
+
// signals we snapshot the headers here, before this ALS scope exits, so
|
|
187
|
+
// the caller can apply them to the rerender pipeline's response (which
|
|
188
|
+
// runs in its own request-context scope with a fresh cookie jar).
|
|
189
|
+
// See LOCAL-740 — without this snapshot, cookies set inside a no-JS
|
|
190
|
+
// form action that returns validation errors are silently dropped.
|
|
125
191
|
if (result instanceof Response) {
|
|
126
192
|
for (const value of getSetCookieHeaders()) {
|
|
127
193
|
result.headers.append('Set-Cookie', value);
|
|
128
194
|
}
|
|
195
|
+
} else if (result && 'rerender' in result) {
|
|
196
|
+
result.setCookieHeaders = getSetCookieHeaders();
|
|
197
|
+
// Snapshot the post-action RYW cookie state as a Map so the rerender
|
|
198
|
+
// dispatcher can seed it directly into the rerender request context
|
|
199
|
+
// via `seedRequestCookies`, with no string round-trip. See TIM-837
|
|
200
|
+
// and ONGOING_SECURITY.md H-3 (TIM-868). `getCookiesForSsr` already
|
|
201
|
+
// returns a defensive copy, so the rerender scope cannot mutate the
|
|
202
|
+
// snapshot through this reference.
|
|
203
|
+
result.cookies = getCookiesForSsr();
|
|
129
204
|
}
|
|
130
205
|
return result;
|
|
131
206
|
});
|
|
@@ -177,7 +252,7 @@ async function handleRscAction(
|
|
|
177
252
|
});
|
|
178
253
|
} catch (error) {
|
|
179
254
|
// Log full error server-side for debugging
|
|
180
|
-
|
|
255
|
+
logActionError({ method: req.method, path: new URL(req.url).pathname, error });
|
|
181
256
|
|
|
182
257
|
// Return structured error response — ActionError gets its code/data,
|
|
183
258
|
// unexpected errors get sanitized { code: 'INTERNAL_ERROR' }
|
|
@@ -278,8 +353,14 @@ async function handleFormAction(
|
|
|
278
353
|
}
|
|
279
354
|
|
|
280
355
|
// Capture submitted values for re-render on validation failure.
|
|
281
|
-
// Parse before decodeAction consumes the FormData
|
|
282
|
-
|
|
356
|
+
// Parse before decodeAction consumes the FormData, then strip sensitive
|
|
357
|
+
// fields (passwords, tokens, CVV, etc.) so they are never rendered back
|
|
358
|
+
// into the HTML as `defaultValue` attributes. See TIM-816.
|
|
359
|
+
const sensitivePredicate = resolveSensitivePredicate(
|
|
360
|
+
config.sensitiveFields,
|
|
361
|
+
getGlobalSensitiveFieldsConfig()
|
|
362
|
+
);
|
|
363
|
+
const submittedValues = stripSensitiveFields(parseFormData(formData), sensitivePredicate);
|
|
283
364
|
|
|
284
365
|
// decodeAction resolves the action function from the form data's hidden fields.
|
|
285
366
|
// It returns a bound function with the form data already applied.
|
|
@@ -293,7 +374,7 @@ async function handleFormAction(
|
|
|
293
374
|
renderer: config.revalidateRenderer,
|
|
294
375
|
});
|
|
295
376
|
} catch (error) {
|
|
296
|
-
|
|
377
|
+
logActionError({ method: req.method, path: new URL(req.url).pathname, error });
|
|
297
378
|
|
|
298
379
|
// Return the error as flash data for re-render.
|
|
299
380
|
// handleActionError produces { serverError } for ActionErrors
|
|
@@ -304,6 +385,9 @@ async function handleFormAction(
|
|
|
304
385
|
...errorResult,
|
|
305
386
|
submittedValues,
|
|
306
387
|
},
|
|
388
|
+
// Filled in by handleActionRequest before the ALS scope exits.
|
|
389
|
+
setCookieHeaders: [],
|
|
390
|
+
cookies: new Map(),
|
|
307
391
|
};
|
|
308
392
|
}
|
|
309
393
|
|
|
@@ -321,5 +405,19 @@ async function handleFormAction(
|
|
|
321
405
|
// This handles both success ({ data }) and validation failure
|
|
322
406
|
// ({ validationErrors, submittedValues }) — the form is the single source of truth.
|
|
323
407
|
const actionResult = result.actionResult as FormFlashData;
|
|
324
|
-
|
|
408
|
+
|
|
409
|
+
// Defense-in-depth: strip sensitive fields from `actionResult.submittedValues`
|
|
410
|
+
// even if the action already built it. `createActionClient` strips internally,
|
|
411
|
+
// but raw `'use server'` functions that manually return `{ submittedValues }`
|
|
412
|
+
// are not covered by the inner strip. See TIM-816.
|
|
413
|
+
if (actionResult && actionResult.submittedValues) {
|
|
414
|
+
actionResult.submittedValues = stripSensitiveFields(
|
|
415
|
+
actionResult.submittedValues,
|
|
416
|
+
sensitivePredicate
|
|
417
|
+
);
|
|
418
|
+
}
|
|
419
|
+
|
|
420
|
+
// setCookieHeaders + cookies are filled in by handleActionRequest before
|
|
421
|
+
// the ALS scope exits.
|
|
422
|
+
return { rerender: actionResult, setCookieHeaders: [], cookies: new Map() };
|
|
325
423
|
}
|
package/src/server/actions.ts
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
*
|
|
4
4
|
* - revalidatePath(path) re-renders the route at that path and returns the RSC
|
|
5
5
|
* flight payload for inline reconciliation.
|
|
6
|
-
* - revalidateTag(tag) invalidates
|
|
6
|
+
* - revalidateTag(tag) invalidates timber.cache entries by tag.
|
|
7
7
|
*
|
|
8
8
|
* Both are callable from anywhere on the server — actions, API routes, handlers.
|
|
9
9
|
*
|
|
@@ -14,7 +14,7 @@
|
|
|
14
14
|
* See design/08-forms-and-actions.md
|
|
15
15
|
*/
|
|
16
16
|
|
|
17
|
-
import
|
|
17
|
+
import { getCacheHandler } from '../cache/handler-store';
|
|
18
18
|
import { RedirectSignal } from './primitives';
|
|
19
19
|
import { withSpan } from './tracing';
|
|
20
20
|
import { revalidationAls, type RevalidationState } from './als-registry.js';
|
|
@@ -37,8 +37,6 @@ export type { RevalidationState } from './als-registry.js';
|
|
|
37
37
|
|
|
38
38
|
/** Options for creating the action handler. */
|
|
39
39
|
export interface ActionHandlerConfig {
|
|
40
|
-
/** Cache handler for tag invalidation. */
|
|
41
|
-
cacheHandler?: CacheHandler;
|
|
42
40
|
/** Renderer for producing RSC payloads during revalidation. */
|
|
43
41
|
renderer?: RevalidateRenderer;
|
|
44
42
|
}
|
|
@@ -112,8 +110,8 @@ export function revalidatePath(path: string): void {
|
|
|
112
110
|
}
|
|
113
111
|
|
|
114
112
|
/**
|
|
115
|
-
* Invalidate all
|
|
116
|
-
* Does not return a payload — the next request for an invalidated
|
|
113
|
+
* Invalidate all timber.cache entries tagged with `tag`.
|
|
114
|
+
* Does not return a payload — the next request for an invalidated entry re-executes.
|
|
117
115
|
*
|
|
118
116
|
* @param tag - The cache tag to invalidate (e.g. 'products', 'user:123').
|
|
119
117
|
*/
|
|
@@ -172,9 +170,12 @@ export async function executeAction(
|
|
|
172
170
|
}
|
|
173
171
|
});
|
|
174
172
|
|
|
175
|
-
// Process tag invalidation
|
|
176
|
-
|
|
177
|
-
|
|
173
|
+
// Process tag invalidation via the module-level cache handler singleton.
|
|
174
|
+
// setCacheHandler() is called at boot from rsc-entry when timber.config.ts
|
|
175
|
+
// provides a cacheHandler; otherwise falls back to in-memory LRU (TIM-599).
|
|
176
|
+
if (state.tags.length > 0) {
|
|
177
|
+
const handler = getCacheHandler();
|
|
178
|
+
await Promise.all(state.tags.map((tag) => handler.invalidate({ tag })));
|
|
178
179
|
}
|
|
179
180
|
|
|
180
181
|
// Process path revalidation — build element tree (not yet serialized)
|