@timber-js/app 0.2.0-alpha.207 → 0.2.0-alpha.209
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/dist/_chunks/{primitives-DLAnvsrk.js → actions-BerlqoXA.js} +640 -410
- package/dist/_chunks/actions-BerlqoXA.js.map +1 -0
- package/dist/_chunks/{als-registry-C6kcfprT.js → als-registry-DaxkVjt5.js} +6 -2
- package/dist/_chunks/als-registry-DaxkVjt5.js.map +1 -0
- package/dist/_chunks/{build-manifest-DTmSGLRz.js → build-manifest-DWppEdLB.js} +2 -51
- package/dist/_chunks/build-manifest-DWppEdLB.js.map +1 -0
- package/dist/_chunks/{build-output-helper-9dqCv1pZ.js → build-output-helper-RV_BRcF1.js} +50 -53
- package/dist/_chunks/build-output-helper-RV_BRcF1.js.map +1 -0
- package/dist/_chunks/{cache-api-CUD9Ezaq.js → cache-api-CR23J_NC.js} +64 -32
- package/dist/_chunks/cache-api-CR23J_NC.js.map +1 -0
- package/dist/_chunks/{chains-B4bgOl2g.js → chains-CpFg56UB.js} +2 -2
- package/dist/_chunks/{chains-B4bgOl2g.js.map → chains-CpFg56UB.js.map} +1 -1
- package/dist/_chunks/{cli-check-G8o30ldg.js → cli-check-C6Ev6wBO.js} +11 -52
- package/dist/_chunks/cli-check-C6Ev6wBO.js.map +1 -0
- package/dist/_chunks/cli-context-By7k2xVo.js +70 -0
- package/dist/_chunks/cli-context-By7k2xVo.js.map +1 -0
- package/dist/_chunks/{cli-schema-sync--LZKs8Zn.js → cli-schema-sync-CbT2AUUI.js} +3 -3
- package/dist/_chunks/{cli-schema-sync--LZKs8Zn.js.map → cli-schema-sync-CbT2AUUI.js.map} +1 -1
- package/dist/_chunks/{cloudflare-BFb__LYG.js → cloudflare-Byxl8FWP.js} +96 -53
- package/dist/_chunks/{cloudflare-BFb__LYG.js.map → cloudflare-Byxl8FWP.js.map} +1 -1
- package/dist/_chunks/{convention-lint-5KzoMg_A.js → convention-lint-jKTwKwPe.js} +21 -6
- package/dist/_chunks/convention-lint-jKTwKwPe.js.map +1 -0
- package/dist/_chunks/{dev-server-BzBxgYAV.js → dev-server-C4WZdB7L.js} +15 -167
- package/dist/_chunks/dev-server-C4WZdB7L.js.map +1 -0
- package/dist/_chunks/{error-boundary-BndF-3Td.js → error-boundary-tA7kVfs4.js} +84 -104
- package/dist/_chunks/error-boundary-tA7kVfs4.js.map +1 -0
- package/dist/_chunks/{fs-identity-D7vb6GdI.js → fs-identity-BRKuL215.js} +3 -4
- package/dist/_chunks/fs-identity-BRKuL215.js.map +1 -0
- package/dist/_chunks/{graph-cache-CP4GEmf9.js → graph-cache-CYiUS2aS.js} +4 -5
- package/dist/_chunks/graph-cache-CYiUS2aS.js.map +1 -0
- package/dist/_chunks/{json-lossy-check-ClNvBM_3.js → json-lossy-check-ip0Qi0MT.js} +6 -3
- package/dist/_chunks/json-lossy-check-ip0Qi0MT.js.map +1 -0
- package/dist/_chunks/{live-graph-Bm5WTvcr.js → live-graph-Dv-JJCZw.js} +3 -3
- package/dist/_chunks/{live-graph-Bm5WTvcr.js.map → live-graph-Dv-JJCZw.js.map} +1 -1
- package/dist/_chunks/{logger-k2DQ4EUf.js → logger-DiDt5ppH.js} +192 -51
- package/dist/_chunks/logger-DiDt5ppH.js.map +1 -0
- package/dist/_chunks/{navigation-root-mHSK9psY.js → navigation-root-BQfo1-kG.js} +2 -2
- package/dist/_chunks/{navigation-root-mHSK9psY.js.map → navigation-root-BQfo1-kG.js.map} +1 -1
- package/dist/_chunks/{plugin-context-8idfR3UZ.js → plugin-context-BPBPc3lL.js} +28 -4
- package/dist/_chunks/plugin-context-BPBPc3lL.js.map +1 -0
- package/dist/_chunks/{poison-scan-DG5eI2_z.js → poison-scan-CpeT6_OJ.js} +2 -2
- package/dist/_chunks/{poison-scan-DG5eI2_z.js.map → poison-scan-CpeT6_OJ.js.map} +1 -1
- package/dist/_chunks/render-timeout-DOf_BBcO.js +46 -0
- package/dist/_chunks/render-timeout-DOf_BBcO.js.map +1 -0
- package/dist/_chunks/{rsc-payload-path-B_LBodc2.js → rsc-payload-path-dffcs4jp.js} +9 -3
- package/dist/_chunks/{rsc-payload-path-B_LBodc2.js.map → rsc-payload-path-dffcs4jp.js.map} +1 -1
- package/dist/_chunks/{scanner-BQ5sPIJ7.js → scanner-Bw0oq1HB.js} +2 -2
- package/dist/_chunks/{scanner-BQ5sPIJ7.js.map → scanner-Bw0oq1HB.js.map} +1 -1
- package/dist/_chunks/{navigation-context-D0TU0Jog.js → segment-context-xtPUGfdq.js} +36 -34
- package/dist/_chunks/segment-context-xtPUGfdq.js.map +1 -0
- package/dist/_chunks/{singleflight-2lUWfcAk.js → singleflight-BeVMMKi0.js} +4 -3
- package/dist/_chunks/{singleflight-2lUWfcAk.js.map → singleflight-BeVMMKi0.js.map} +1 -1
- package/dist/_chunks/{sizeof-lXffQ1Vr.js → sizeof-UwzwB1uM.js} +3 -2
- package/dist/_chunks/sizeof-UwzwB1uM.js.map +1 -0
- package/dist/_chunks/ssr-data-nA3I70_n.js +49 -0
- package/dist/_chunks/ssr-data-nA3I70_n.js.map +1 -0
- package/dist/_chunks/{state-FippDgxN.js → state-BhNGPsdi.js} +6 -8
- package/dist/_chunks/state-BhNGPsdi.js.map +1 -0
- package/dist/_chunks/{status-page-marker-DwQBrLBz.js → status-page-marker-gaihi0KZ.js} +140 -28
- package/dist/_chunks/status-page-marker-gaihi0KZ.js.map +1 -0
- package/dist/_chunks/{walkers-CN2N_bA-.js → walkers-BXExhzzk.js} +2 -2
- package/dist/_chunks/{walkers-CN2N_bA-.js.map → walkers-BXExhzzk.js.map} +1 -1
- package/dist/_chunks/write-file-atomic-D1qWqHb4.js +32 -0
- package/dist/_chunks/write-file-atomic-D1qWqHb4.js.map +1 -0
- package/dist/adapters/build-output-helper.d.ts +7 -7
- package/dist/adapters/build-output-helper.d.ts.map +1 -1
- package/dist/adapters/canonicalize-module.d.ts +8 -0
- package/dist/adapters/canonicalize-module.d.ts.map +1 -0
- package/dist/adapters/cloudflare-dev.js +1 -1
- package/dist/adapters/cloudflare-kv-cache.js +2 -2
- package/dist/adapters/cloudflare.d.ts +30 -8
- package/dist/adapters/cloudflare.d.ts.map +1 -1
- package/dist/adapters/cloudflare.js +2 -2
- package/dist/adapters/compress-module.d.ts +8 -0
- package/dist/adapters/compress-module.d.ts.map +1 -1
- package/dist/adapters/fs-identity.d.ts +2 -3
- package/dist/adapters/fs-identity.d.ts.map +1 -1
- package/dist/adapters/nitro-presets.d.ts +0 -6
- package/dist/adapters/nitro-presets.d.ts.map +1 -1
- package/dist/adapters/nitro-preview.d.ts +43 -11
- package/dist/adapters/nitro-preview.d.ts.map +1 -1
- package/dist/adapters/nitro.d.ts +1 -13
- package/dist/adapters/nitro.d.ts.map +1 -1
- package/dist/adapters/nitro.js +291 -176
- package/dist/adapters/nitro.js.map +1 -1
- package/dist/adapters/shared.d.ts +7 -2
- package/dist/adapters/shared.d.ts.map +1 -1
- package/dist/adapters/types.d.ts +105 -21
- package/dist/adapters/types.d.ts.map +1 -1
- package/dist/adapters/types.js +0 -0
- package/dist/analyze/crawl-entry.js +3 -3
- package/dist/analyze/graph-cache.d.ts.map +1 -1
- package/dist/analyze/graph-command.js +4 -4
- package/dist/cache/index.js +2 -2
- package/dist/cache/json-lossy-check.d.ts.map +1 -1
- package/dist/cache/singleflight.d.ts.map +1 -1
- package/dist/cache/sizeof.d.ts.map +1 -1
- package/dist/cache/stores/memory.js +1 -1
- package/dist/cache/tag-aware-handler.d.ts.map +1 -1
- package/dist/cdn/workers-cache-purge.js +1 -1
- package/dist/cli-check.d.ts +7 -37
- package/dist/cli-check.d.ts.map +1 -1
- package/dist/cli-context.d.ts +54 -0
- package/dist/cli-context.d.ts.map +1 -0
- package/dist/cli.d.ts +9 -9
- package/dist/cli.d.ts.map +1 -1
- package/dist/cli.js +20 -24
- package/dist/cli.js.map +1 -1
- package/dist/client/browser-dev.d.ts +1 -1
- package/dist/client/browser-entry/action-dispatch.d.ts.map +1 -1
- package/dist/client/browser-entry/hydrate.d.ts +19 -29
- package/dist/client/browser-entry/hydrate.d.ts.map +1 -1
- package/dist/client/browser-entry/index.d.ts +7 -7
- package/dist/client/browser-entry/post-hydration.d.ts +6 -9
- package/dist/client/browser-entry/post-hydration.d.ts.map +1 -1
- package/dist/client/browser-entry/router-init.d.ts +6 -11
- package/dist/client/browser-entry/router-init.d.ts.map +1 -1
- package/dist/client/error-boundary.d.ts +4 -3
- package/dist/client/error-boundary.d.ts.map +1 -1
- package/dist/client/error-boundary.js +1 -1
- package/dist/client/error-reconstituter.d.ts +3 -9
- package/dist/client/error-reconstituter.d.ts.map +1 -1
- package/dist/client/history.d.ts +20 -11
- package/dist/client/history.d.ts.map +1 -1
- package/dist/client/index.d.ts +0 -1
- package/dist/client/index.d.ts.map +1 -1
- package/dist/client/index.js +5 -6
- package/dist/client/index.js.map +1 -1
- package/dist/client/internal.d.ts +3 -3
- package/dist/client/internal.d.ts.map +1 -1
- package/dist/client/internal.js +95 -108
- package/dist/client/internal.js.map +1 -1
- package/dist/client/navigation-commit.d.ts +20 -7
- package/dist/client/navigation-commit.d.ts.map +1 -1
- package/dist/client/navigation-context.d.ts +3 -5
- package/dist/client/navigation-context.d.ts.map +1 -1
- package/dist/client/react-root.d.ts +3 -2
- package/dist/client/react-root.d.ts.map +1 -1
- package/dist/client/router-pipeline.d.ts +3 -8
- package/dist/client/router-pipeline.d.ts.map +1 -1
- package/dist/client/router-types.d.ts +45 -39
- package/dist/client/router-types.d.ts.map +1 -1
- package/dist/client/router-url.d.ts +24 -0
- package/dist/client/router-url.d.ts.map +1 -0
- package/dist/client/router.d.ts.map +1 -1
- package/dist/client/rsc-fetch.d.ts +8 -4
- package/dist/client/rsc-fetch.d.ts.map +1 -1
- package/dist/client/segment-cache.d.ts +44 -4
- package/dist/client/segment-cache.d.ts.map +1 -1
- package/dist/client/ssr-data.d.ts +18 -64
- package/dist/client/ssr-data.d.ts.map +1 -1
- package/dist/client/state.d.ts +2 -5
- package/dist/client/state.d.ts.map +1 -1
- package/dist/client/use-pathname.d.ts +1 -1
- package/dist/config-types.d.ts +2 -1
- package/dist/config-types.d.ts.map +1 -1
- package/dist/config-validation.d.ts.map +1 -1
- package/dist/cookies/index.js +1 -1
- package/dist/dev-tools/dev-page-shell.d.ts +17 -0
- package/dist/dev-tools/dev-page-shell.d.ts.map +1 -1
- package/dist/dev-tools/index.d.ts +1 -1
- package/dist/dev-tools/index.d.ts.map +1 -1
- package/dist/dev-tools/instrumentation.d.ts +17 -2
- package/dist/dev-tools/instrumentation.d.ts.map +1 -1
- package/dist/dev-tools/overlay.d.ts +6 -27
- package/dist/dev-tools/overlay.d.ts.map +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +632 -373
- package/dist/index.js.map +1 -1
- package/dist/plugin-context.d.ts +26 -40
- package/dist/plugin-context.d.ts.map +1 -1
- package/dist/plugins/build-manifest.d.ts.map +1 -1
- package/dist/plugins/build-report.d.ts +15 -2
- package/dist/plugins/build-report.d.ts.map +1 -1
- package/dist/plugins/dev-server.d.ts.map +1 -1
- package/dist/plugins/entries.d.ts.map +1 -1
- package/dist/plugins/fonts.d.ts +19 -1
- package/dist/plugins/fonts.d.ts.map +1 -1
- package/dist/plugins/post-build.d.ts +110 -0
- package/dist/plugins/post-build.d.ts.map +1 -0
- package/dist/plugins/prebuilt-capture.d.ts +11 -4
- package/dist/plugins/prebuilt-capture.d.ts.map +1 -1
- package/dist/plugins/prebuilt.d.ts +17 -1
- package/dist/plugins/prebuilt.d.ts.map +1 -1
- package/dist/plugins/prerender-sugar.d.ts.map +1 -1
- package/dist/plugins/react-resolve.d.ts +49 -0
- package/dist/plugins/react-resolve.d.ts.map +1 -0
- package/dist/plugins/rsc-assets-manifest.d.ts +33 -0
- package/dist/plugins/rsc-assets-manifest.d.ts.map +1 -0
- package/dist/plugins/shims.d.ts.map +1 -1
- package/dist/plugins/static-build.d.ts +32 -9
- package/dist/plugins/static-build.d.ts.map +1 -1
- package/dist/routing/codegen-write.d.ts +0 -9
- package/dist/routing/codegen-write.d.ts.map +1 -1
- package/dist/routing/index.js +2 -2
- package/dist/routing/segment-codegen.d.ts.map +1 -1
- package/dist/server/access-gate.d.ts.map +1 -1
- package/dist/server/action-handler.d.ts +5 -4
- package/dist/server/action-handler.d.ts.map +1 -1
- package/dist/server/actions.d.ts +46 -7
- package/dist/server/actions.d.ts.map +1 -1
- package/dist/server/als-registry.d.ts +58 -5
- package/dist/server/als-registry.d.ts.map +1 -1
- package/dist/server/children-interception.d.ts.map +1 -1
- package/dist/server/cookie-context.d.ts +2 -23
- package/dist/server/cookie-context.d.ts.map +1 -1
- package/dist/server/csp-nonce.d.ts +0 -9
- package/dist/server/csp-nonce.d.ts.map +1 -1
- package/dist/server/csrf.d.ts +27 -4
- package/dist/server/csrf.d.ts.map +1 -1
- package/dist/server/debug.d.ts +0 -1
- package/dist/server/debug.d.ts.map +1 -1
- package/dist/server/default-logger.d.ts +2 -1
- package/dist/server/default-logger.d.ts.map +1 -1
- package/dist/server/deny-boundary.d.ts +12 -6
- package/dist/server/deny-boundary.d.ts.map +1 -1
- package/dist/server/deny-renderer.d.ts +2 -2
- package/dist/server/deny-renderer.d.ts.map +1 -1
- package/dist/server/error-boundary-wrapper.d.ts +7 -3
- package/dist/server/error-boundary-wrapper.d.ts.map +1 -1
- package/dist/server/fallback-error.d.ts +9 -2
- package/dist/server/fallback-error.d.ts.map +1 -1
- package/dist/server/head-response.d.ts +4 -4
- package/dist/server/html-injectors.d.ts.map +1 -1
- package/dist/server/index.d.ts +0 -1
- package/dist/server/index.d.ts.map +1 -1
- package/dist/server/index.js +3 -4
- package/dist/server/index.js.map +1 -1
- package/dist/server/instrumentation.d.ts +10 -3
- package/dist/server/instrumentation.d.ts.map +1 -1
- package/dist/server/internal.d.ts +1 -4
- package/dist/server/internal.d.ts.map +1 -1
- package/dist/server/internal.js +961 -581
- package/dist/server/internal.js.map +1 -1
- package/dist/server/json-status-response.d.ts +34 -0
- package/dist/server/json-status-response.d.ts.map +1 -0
- package/dist/server/logger.d.ts +26 -0
- package/dist/server/logger.d.ts.map +1 -1
- package/dist/server/param-coercion.d.ts +11 -6
- package/dist/server/param-coercion.d.ts.map +1 -1
- package/dist/server/pipeline-helpers.d.ts +95 -7
- package/dist/server/pipeline-helpers.d.ts.map +1 -1
- package/dist/server/pipeline-interception.d.ts +11 -0
- package/dist/server/pipeline-interception.d.ts.map +1 -1
- package/dist/server/pipeline-outcome.d.ts +11 -1
- package/dist/server/pipeline-outcome.d.ts.map +1 -1
- package/dist/server/pipeline-phases.d.ts +2 -2
- package/dist/server/pipeline-phases.d.ts.map +1 -1
- package/dist/server/pipeline.d.ts +23 -35
- package/dist/server/pipeline.d.ts.map +1 -1
- package/dist/server/prebuilt/capture-render.d.ts +25 -0
- package/dist/server/prebuilt/capture-render.d.ts.map +1 -0
- package/dist/server/prebuilt/capture-routes.d.ts +37 -0
- package/dist/server/prebuilt/capture-routes.d.ts.map +1 -0
- package/dist/server/prebuilt/capture-state.d.ts +79 -35
- package/dist/server/prebuilt/capture-state.d.ts.map +1 -1
- package/dist/server/prebuilt/lifetime.d.ts +38 -0
- package/dist/server/prebuilt/lifetime.d.ts.map +1 -0
- package/dist/server/prebuilt/slots.d.ts +7 -0
- package/dist/server/prebuilt/slots.d.ts.map +1 -1
- package/dist/server/prebuilt/synthetic-store.d.ts.map +1 -1
- package/dist/server/prebuilt-builder.d.ts.map +1 -1
- package/dist/server/prebuilt-runtime.d.ts.map +1 -1
- package/dist/server/primitives.d.ts +6 -81
- package/dist/server/primitives.d.ts.map +1 -1
- package/dist/server/react-cache-bridge.d.ts +59 -0
- package/dist/server/react-cache-bridge.d.ts.map +1 -0
- package/dist/server/react-cache-scope.d.ts +49 -0
- package/dist/server/react-cache-scope.d.ts.map +1 -0
- package/dist/server/render-timeout.d.ts +15 -0
- package/dist/server/render-timeout.d.ts.map +1 -1
- package/dist/server/render-utils.d.ts +9 -0
- package/dist/server/render-utils.d.ts.map +1 -1
- package/dist/server/request-context.d.ts +26 -5
- package/dist/server/request-context.d.ts.map +1 -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 +7 -0
- package/dist/server/route-matcher.d.ts.map +1 -1
- package/dist/server/rsc-entry/action-middleware-runner.d.ts +22 -22
- package/dist/server/rsc-entry/action-middleware-runner.d.ts.map +1 -1
- package/dist/server/rsc-entry/api-handler.d.ts +7 -0
- package/dist/server/rsc-entry/api-handler.d.ts.map +1 -1
- package/dist/server/rsc-entry/deny-fallback.d.ts +32 -6
- package/dist/server/rsc-entry/deny-fallback.d.ts.map +1 -1
- package/dist/server/rsc-entry/error-renderer.d.ts +11 -18
- package/dist/server/rsc-entry/error-renderer.d.ts.map +1 -1
- package/dist/server/rsc-entry/helpers.d.ts +0 -1
- package/dist/server/rsc-entry/helpers.d.ts.map +1 -1
- package/dist/server/rsc-entry/index.d.ts +5 -15
- package/dist/server/rsc-entry/index.d.ts.map +1 -1
- package/dist/server/rsc-entry/render-route.d.ts +5 -6
- package/dist/server/rsc-entry/render-route.d.ts.map +1 -1
- package/dist/server/rsc-entry/render-signals.d.ts +65 -0
- package/dist/server/rsc-entry/render-signals.d.ts.map +1 -0
- package/dist/server/rsc-entry/revalidate-renderer.d.ts +6 -16
- package/dist/server/rsc-entry/revalidate-renderer.d.ts.map +1 -1
- package/dist/server/rsc-entry/rsc-payload.d.ts +7 -3
- package/dist/server/rsc-entry/rsc-payload.d.ts.map +1 -1
- package/dist/server/rsc-entry/rsc-stream.d.ts +8 -53
- package/dist/server/rsc-entry/rsc-stream.d.ts.map +1 -1
- package/dist/server/rsc-entry/ssr-bridge.d.ts +2 -2
- package/dist/server/rsc-entry/ssr-bridge.d.ts.map +1 -1
- package/dist/server/rsc-entry/ssr-renderer.d.ts +9 -4
- package/dist/server/rsc-entry/ssr-renderer.d.ts.map +1 -1
- package/dist/server/rsc-entry/wrap-action-dispatch.d.ts +1 -1
- package/dist/server/rsc-entry/wrap-action-dispatch.d.ts.map +1 -1
- package/dist/server/signal-identity.d.ts +43 -0
- package/dist/server/signal-identity.d.ts.map +1 -0
- package/dist/server/slot-resolver.d.ts +4 -3
- package/dist/server/slot-resolver.d.ts.map +1 -1
- package/dist/server/slot-subtree-contain.d.ts.map +1 -1
- package/dist/server/ssr-bridge-types.d.ts +91 -53
- package/dist/server/ssr-bridge-types.d.ts.map +1 -1
- package/dist/server/ssr-entry.d.ts +8 -5
- package/dist/server/ssr-entry.d.ts.map +1 -1
- package/dist/server/ssr-render.d.ts +26 -40
- package/dist/server/ssr-render.d.ts.map +1 -1
- package/dist/server/ssr-wrappers.d.ts +1 -1
- package/dist/server/static-generator.d.ts.map +1 -1
- package/dist/server/stream-utils.d.ts +12 -0
- package/dist/server/stream-utils.d.ts.map +1 -1
- package/dist/server/tracing.d.ts +12 -16
- package/dist/server/tracing.d.ts.map +1 -1
- package/dist/server/types.d.ts +1 -1
- package/dist/server/version-skew.d.ts +3 -3
- package/dist/shared/csrf-reject.d.ts +16 -0
- package/dist/shared/csrf-reject.d.ts.map +1 -0
- package/dist/shared/error-digest.d.ts +28 -0
- package/dist/shared/error-digest.d.ts.map +1 -0
- package/dist/shared/payload-root.d.ts +11 -10
- package/dist/shared/payload-root.d.ts.map +1 -1
- package/dist/shared/rsc-payload-path.d.ts +6 -0
- package/dist/shared/rsc-payload-path.d.ts.map +1 -1
- package/dist/utils/write-file-atomic.d.ts +17 -0
- package/dist/utils/write-file-atomic.d.ts.map +1 -0
- package/docs/api/30-api-server.mdx +17 -14
- package/docs/api/32-api-cache.mdx +51 -23
- package/docs/api/34-api-config.mdx +34 -6
- package/docs/api/36-cli.mdx +11 -1
- package/docs/learn/03-fetching-data.mdx +17 -1
- package/docs/learn/04-access-control.mdx +1 -1
- package/docs/learn/10-caching.mdx +29 -11
- package/docs/learn/11-middleware.mdx +8 -2
- package/docs/learn/12-error-handling.mdx +6 -1
- package/package.json +7 -3
- package/src/adapters/build-output-helper.ts +55 -53
- package/src/adapters/canonicalize-module.ts +29 -0
- package/src/adapters/cloudflare.ts +105 -63
- package/src/adapters/compress-module.ts +16 -5
- package/src/adapters/fs-identity.ts +2 -3
- package/src/adapters/nitro-presets.ts +0 -15
- package/src/adapters/nitro-preview.ts +272 -136
- package/src/adapters/nitro.ts +119 -82
- package/src/adapters/shared.ts +23 -14
- package/src/adapters/types.ts +110 -21
- package/src/analyze/graph-cache.ts +3 -4
- package/src/cache/json-lossy-check.ts +3 -9
- package/src/cache/singleflight.ts +5 -2
- package/src/cache/sizeof.ts +3 -1
- package/src/cache/tag-aware-handler.ts +43 -26
- package/src/cli-check.ts +8 -98
- package/src/cli-context.ts +119 -0
- package/src/cli.ts +19 -28
- package/src/client/browser-dev.ts +1 -1
- package/src/client/browser-entry/action-dispatch.ts +26 -4
- package/src/client/browser-entry/hydrate.ts +53 -58
- package/src/client/browser-entry/index.ts +16 -27
- package/src/client/browser-entry/post-hydration.ts +6 -40
- package/src/client/browser-entry/router-init.ts +66 -84
- package/src/client/error-boundary.tsx +9 -41
- package/src/client/error-reconstituter.tsx +10 -5
- package/src/client/history.ts +35 -21
- package/src/client/index.ts +0 -1
- package/src/client/internal.ts +2 -7
- package/src/client/navigation-commit.ts +41 -18
- package/src/client/navigation-context.ts +3 -39
- package/src/client/react-root.ts +3 -2
- package/src/client/router-pipeline.ts +96 -132
- package/src/client/router-types.ts +46 -44
- package/src/client/router-url.ts +32 -0
- package/src/client/router.ts +62 -21
- package/src/client/rsc-fetch.ts +117 -155
- package/src/client/segment-cache.ts +110 -11
- package/src/client/ssr-data.ts +19 -80
- package/src/client/state.ts +2 -9
- package/src/client/use-pathname.ts +1 -1
- package/src/config-types.ts +2 -1
- package/src/config-validation.ts +2 -1
- package/src/dev-tools/dev-page-shell.ts +18 -0
- package/src/dev-tools/index.ts +0 -2
- package/src/dev-tools/instrumentation.ts +50 -2
- package/src/dev-tools/overlay.ts +8 -238
- package/src/index.ts +26 -14
- package/src/plugin-context.ts +39 -40
- package/src/plugins/build-manifest.ts +4 -11
- package/src/plugins/build-report.ts +70 -70
- package/src/plugins/dev-server.ts +24 -44
- package/src/plugins/entries.ts +2 -1
- package/src/plugins/fonts.ts +35 -7
- package/src/plugins/post-build.ts +327 -0
- package/src/plugins/prebuilt-capture.ts +28 -32
- package/src/plugins/prebuilt.ts +54 -39
- package/src/plugins/prerender-sugar.ts +0 -4
- package/src/plugins/react-resolve.ts +149 -0
- package/src/plugins/{adapter-build.ts → rsc-assets-manifest.ts} +20 -109
- package/src/plugins/shims.ts +1 -0
- package/src/plugins/static-build.ts +42 -74
- package/src/routing/codegen-write.ts +25 -3
- package/src/routing/segment-codegen.ts +3 -2
- package/src/rsc-runtime/vendor-types.d.ts +8 -0
- package/src/server/access-gate.tsx +2 -1
- package/src/server/action-client.ts +1 -1
- package/src/server/action-handler.ts +253 -110
- package/src/server/actions.ts +73 -25
- package/src/server/als-registry.ts +73 -5
- package/src/server/chain-url-parts.ts +1 -1
- package/src/server/children-interception.ts +2 -1
- package/src/server/cookie-context.ts +18 -48
- package/src/server/csp-nonce.ts +0 -12
- package/src/server/csrf.ts +140 -8
- package/src/server/debug.ts +0 -1
- package/src/server/default-logger.ts +2 -1
- package/src/server/deny-boundary.ts +25 -13
- package/src/server/deny-renderer.ts +30 -97
- package/src/server/error-boundary-wrapper.ts +7 -3
- package/src/server/fallback-error.ts +26 -11
- package/src/server/flight-scripts.ts +1 -1
- package/src/server/head-response.ts +4 -4
- package/src/server/html-injector-core.ts +2 -2
- package/src/server/html-injectors.ts +2 -4
- package/src/server/index.ts +0 -4
- package/src/server/instrumentation.ts +10 -3
- package/src/server/internal.ts +1 -6
- package/src/server/json-status-response.ts +105 -0
- package/src/server/logger.ts +39 -3
- package/src/server/param-coercion.ts +15 -12
- package/src/server/pipeline-helpers.ts +257 -12
- package/src/server/pipeline-interception.ts +133 -0
- package/src/server/pipeline-outcome.ts +103 -31
- package/src/server/pipeline-phases.ts +127 -237
- package/src/server/pipeline.ts +204 -207
- package/src/server/prebuilt/capture-render.ts +118 -0
- package/src/server/prebuilt/capture-routes.ts +104 -0
- package/src/server/prebuilt/capture-state.ts +155 -48
- package/src/server/prebuilt/lifetime.ts +151 -0
- package/src/server/prebuilt/slots.ts +14 -2
- package/src/server/prebuilt/synthetic-store.ts +3 -0
- package/src/server/prebuilt-builder.ts +30 -320
- package/src/server/prebuilt-runtime.ts +125 -129
- package/src/server/primitives.ts +8 -168
- package/src/server/react-cache-bridge.ts +124 -0
- package/src/server/react-cache-scope.ts +104 -0
- package/src/server/render-timeout.ts +20 -0
- package/src/server/render-utils.ts +11 -1
- package/src/server/request-context.ts +36 -9
- package/src/server/route-element-builder.ts +11 -6
- package/src/server/route-handler.ts +36 -23
- package/src/server/route-matcher.ts +15 -1
- package/src/server/rsc-entry/action-middleware-runner.ts +79 -123
- package/src/server/rsc-entry/api-handler.ts +27 -103
- package/src/server/rsc-entry/deny-fallback.ts +93 -26
- package/src/server/rsc-entry/error-renderer.ts +39 -91
- package/src/server/rsc-entry/helpers.ts +2 -4
- package/src/server/rsc-entry/index.ts +64 -58
- package/src/server/rsc-entry/render-route.ts +15 -80
- package/src/server/rsc-entry/render-signals.ts +108 -0
- package/src/server/rsc-entry/revalidate-renderer.ts +56 -57
- package/src/server/rsc-entry/rsc-payload.ts +28 -61
- package/src/server/rsc-entry/rsc-stream.ts +113 -213
- package/src/server/rsc-entry/ssr-bridge.ts +2 -2
- package/src/server/rsc-entry/ssr-renderer.ts +182 -263
- package/src/server/rsc-entry/wrap-action-dispatch.ts +12 -14
- package/src/server/signal-identity.ts +112 -0
- package/src/server/slot-resolver.ts +18 -18
- package/src/server/slot-subtree-contain.ts +2 -1
- package/src/server/ssr-bridge-types.ts +92 -56
- package/src/server/ssr-entry.ts +87 -111
- package/src/server/ssr-render.ts +55 -64
- package/src/server/ssr-wrappers.tsx +2 -2
- package/src/server/static-generator.ts +7 -42
- package/src/server/static-not-found.ts +3 -3
- package/src/server/stream-utils.ts +51 -0
- package/src/server/tracing.ts +58 -75
- package/src/server/types.ts +1 -1
- package/src/server/version-skew.ts +5 -5
- package/src/shared/csrf-reject.ts +15 -0
- package/src/shared/error-digest.ts +46 -0
- package/src/shared/payload-root.ts +11 -10
- package/src/shared/rsc-payload-path.ts +8 -1
- package/src/utils/write-file-atomic.ts +32 -0
- package/dist/_chunks/actions-jNrXdzgs.js +0 -346
- package/dist/_chunks/actions-jNrXdzgs.js.map +0 -1
- package/dist/_chunks/als-registry-C6kcfprT.js.map +0 -1
- package/dist/_chunks/build-manifest-DTmSGLRz.js.map +0 -1
- package/dist/_chunks/build-output-helper-9dqCv1pZ.js.map +0 -1
- package/dist/_chunks/cache-api-CUD9Ezaq.js.map +0 -1
- package/dist/_chunks/cli-check-G8o30ldg.js.map +0 -1
- package/dist/_chunks/convention-lint-5KzoMg_A.js.map +0 -1
- package/dist/_chunks/dev-server-BzBxgYAV.js.map +0 -1
- package/dist/_chunks/error-boundary-BndF-3Td.js.map +0 -1
- package/dist/_chunks/fs-identity-D7vb6GdI.js.map +0 -1
- package/dist/_chunks/graph-cache-CP4GEmf9.js.map +0 -1
- package/dist/_chunks/json-lossy-check-ClNvBM_3.js.map +0 -1
- package/dist/_chunks/logger-k2DQ4EUf.js.map +0 -1
- package/dist/_chunks/navigation-context-D0TU0Jog.js.map +0 -1
- package/dist/_chunks/plugin-context-8idfR3UZ.js.map +0 -1
- package/dist/_chunks/primitives-DLAnvsrk.js.map +0 -1
- package/dist/_chunks/segment-context-D9_89u34.js +0 -34
- package/dist/_chunks/segment-context-D9_89u34.js.map +0 -1
- package/dist/_chunks/sizeof-lXffQ1Vr.js.map +0 -1
- package/dist/_chunks/ssr-data-D6T6Y3ef.js +0 -60
- package/dist/_chunks/ssr-data-D6T6Y3ef.js.map +0 -1
- package/dist/_chunks/state-FippDgxN.js.map +0 -1
- package/dist/_chunks/status-page-marker-DwQBrLBz.js.map +0 -1
- package/dist/client/types.d.ts +0 -14
- package/dist/client/types.d.ts.map +0 -1
- package/dist/plugins/adapter-build.d.ts +0 -38
- package/dist/plugins/adapter-build.d.ts.map +0 -1
- package/dist/server/asset-headers.d.ts +0 -32
- package/dist/server/asset-headers.d.ts.map +0 -1
- package/dist/server/dev-source-map.d.ts +0 -22
- package/dist/server/dev-source-map.d.ts.map +0 -1
- package/src/client/types.ts +0 -21
- package/src/server/asset-headers.ts +0 -59
- package/src/server/dev-source-map.ts +0 -31
|
@@ -0,0 +1,124 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* React.cache bridge — one `React.cache` scope per request, shared by
|
|
3
|
+
* `proxy.ts`, `middleware.ts`, route handlers, and the render.
|
|
4
|
+
*
|
|
5
|
+
* `React.cache(fn)` stores its memo table in whatever Map
|
|
6
|
+
* `ReactSharedInternals.A.getCacheForType()` returns. `A` is Flight's
|
|
7
|
+
* `DefaultAsyncDispatcher`, whose `getCacheForType` returns the active Flight
|
|
8
|
+
* request's `cache` Map, or a throwaway `new Map()` outside a render. So
|
|
9
|
+
* without this bridge a `React.cache` call in middleware silently never
|
|
10
|
+
* dedupes with the same call in `access.ts` or a layout.
|
|
11
|
+
*
|
|
12
|
+
* The bridge wraps that one method on the existing dispatcher object:
|
|
13
|
+
*
|
|
14
|
+
* - When a timber scope is active (`reactCacheScopeAls`), it returns the
|
|
15
|
+
* scope's Map — inside or outside a Flight render.
|
|
16
|
+
* - Otherwise it calls Flight's original method, so behaviour outside a
|
|
17
|
+
* request (build-time capture, tests, isolated renders) is unchanged.
|
|
18
|
+
*
|
|
19
|
+
* Opening, resuming, and leaving scopes lives in `react-cache-scope.ts`,
|
|
20
|
+
* which does not import `react` so request-context and cookie code can use it.
|
|
21
|
+
*
|
|
22
|
+
* Constraints (TIM-1529, design/spike-TIM-1529-react-cache-bridge.md):
|
|
23
|
+
*
|
|
24
|
+
* - Never reassign `ReactSharedInternals.A`. Flight's `Request` constructor
|
|
25
|
+
* throws "Currently React only supports one RSC renderer at a time" when
|
|
26
|
+
* `A` is set to anything but its own dispatcher.
|
|
27
|
+
* - Never shim the `react` module's `cache` export and never patch the
|
|
28
|
+
* vendored Flight bundle.
|
|
29
|
+
* - Flight installs `A` lazily, in the `Request` constructor, so it is
|
|
30
|
+
* `null` until the first render. `installReactCacheBridge` takes a
|
|
31
|
+
* `warmUp` callback that constructs one throwaway render to install it.
|
|
32
|
+
* - Idempotent: a program reload that re-evaluates this module but not
|
|
33
|
+
* Flight finds the brand on the live method and does not wrap it twice.
|
|
34
|
+
* A reload that re-evaluates Flight too hands back a fresh dispatcher,
|
|
35
|
+
* which the re-evaluated RSC entry patches again. The wrapper closes over
|
|
36
|
+
* nothing but the `globalThis`-anchored ALS, so an old wrapper that
|
|
37
|
+
* survives a reload still reads the current scope.
|
|
38
|
+
*/
|
|
39
|
+
|
|
40
|
+
import * as React from 'react';
|
|
41
|
+
import { reactCacheScopeAls } from './als-registry.ts';
|
|
42
|
+
|
|
43
|
+
const INTERNALS_KEY = '__SERVER_INTERNALS_DO_NOT_USE_OR_WARN_USERS_THEY_CANNOT_UPGRADE';
|
|
44
|
+
const BRIDGED = Symbol.for('timber:react-cache-bridge');
|
|
45
|
+
|
|
46
|
+
type GetCacheForType = (resourceType: () => unknown) => unknown;
|
|
47
|
+
|
|
48
|
+
interface AsyncDispatcher {
|
|
49
|
+
getCacheForType: GetCacheForType;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
interface ServerInternals {
|
|
53
|
+
A: unknown;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
function isServerInternals(value: unknown): value is ServerInternals {
|
|
57
|
+
return typeof value === 'object' && value !== null && 'A' in value;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
function isAsyncDispatcher(value: unknown): value is AsyncDispatcher {
|
|
61
|
+
return (
|
|
62
|
+
typeof value === 'object' &&
|
|
63
|
+
value !== null &&
|
|
64
|
+
'getCacheForType' in value &&
|
|
65
|
+
typeof value.getCacheForType === 'function'
|
|
66
|
+
);
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
/** Result of `installReactCacheBridge`, for the boot log and the canary test. */
|
|
70
|
+
export type ReactCacheBridgeStatus =
|
|
71
|
+
| { installed: true; alreadyBridged: boolean }
|
|
72
|
+
| { installed: false; reason: string };
|
|
73
|
+
|
|
74
|
+
/**
|
|
75
|
+
* Patch Flight's dispatcher so `React.cache` resolves the active timber
|
|
76
|
+
* scope. Call once from the RSC entry, after the Flight server has loaded.
|
|
77
|
+
*
|
|
78
|
+
* `warmUp` must construct a Flight render (e.g. `renderToReadableStream(null)`
|
|
79
|
+
* and cancel it) — that is the only public way to make Flight install `A`.
|
|
80
|
+
*
|
|
81
|
+
* Never throws: if React's internals change shape the bridge is skipped,
|
|
82
|
+
* `React.cache` keeps its stock per-render behaviour, and the reason is
|
|
83
|
+
* returned. `tests/react-cache-bridge.test.ts` fails CI on that path.
|
|
84
|
+
*/
|
|
85
|
+
export function installReactCacheBridge(warmUp: () => void): ReactCacheBridgeStatus {
|
|
86
|
+
const internals: unknown = Reflect.get(React, INTERNALS_KEY);
|
|
87
|
+
if (!isServerInternals(internals)) {
|
|
88
|
+
return {
|
|
89
|
+
installed: false,
|
|
90
|
+
reason: `react does not export ${INTERNALS_KEY}`,
|
|
91
|
+
};
|
|
92
|
+
}
|
|
93
|
+
if (internals.A === null || internals.A === undefined) {
|
|
94
|
+
try {
|
|
95
|
+
warmUp();
|
|
96
|
+
} catch (error) {
|
|
97
|
+
return { installed: false, reason: `warm-up render threw: ${String(error)}` };
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
const dispatcher = internals.A;
|
|
101
|
+
if (!isAsyncDispatcher(dispatcher)) {
|
|
102
|
+
return {
|
|
103
|
+
installed: false,
|
|
104
|
+
reason: 'ReactSharedInternals.A has no getCacheForType()',
|
|
105
|
+
};
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
const original = dispatcher.getCacheForType;
|
|
109
|
+
if (BRIDGED in original) return { installed: true, alreadyBridged: true };
|
|
110
|
+
|
|
111
|
+
const bridged: GetCacheForType = function getCacheForType(resourceType) {
|
|
112
|
+
const scope = reactCacheScopeAls.getStore();
|
|
113
|
+
if (!scope) return original.call(dispatcher, resourceType);
|
|
114
|
+
let entry = scope.get(resourceType);
|
|
115
|
+
if (entry === undefined) {
|
|
116
|
+
entry = resourceType();
|
|
117
|
+
scope.set(resourceType, entry);
|
|
118
|
+
}
|
|
119
|
+
return entry;
|
|
120
|
+
};
|
|
121
|
+
Object.defineProperty(bridged, BRIDGED, { value: true });
|
|
122
|
+
dispatcher.getCacheForType = bridged;
|
|
123
|
+
return { installed: true, alreadyBridged: false };
|
|
124
|
+
}
|
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* React.cache scopes — opening, resuming, and leaving the per-request
|
|
3
|
+
* `React.cache` scope that `react-cache-bridge.ts` makes Flight read.
|
|
4
|
+
*
|
|
5
|
+
* Kept apart from the bridge so request-context and cookie code can use
|
|
6
|
+
* it without importing `react`. See design/spike-TIM-1529-react-cache-bridge.md.
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
import { reactCacheScopeAls, type ReactCacheScope } from './als-registry.ts';
|
|
10
|
+
import { isDevMode } from './debug.ts';
|
|
11
|
+
|
|
12
|
+
export type { ReactCacheScope };
|
|
13
|
+
|
|
14
|
+
/** A new, empty `React.cache` scope. */
|
|
15
|
+
export function createReactCacheScope(): ReactCacheScope {
|
|
16
|
+
return new Map();
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* Run `fn` in a `React.cache` scope — a fresh one unless `scope` is given.
|
|
21
|
+
* Every `React.cache` call made from `fn` — synchronously, in its async
|
|
22
|
+
* continuations, and in any Flight render it starts — shares one memo table.
|
|
23
|
+
*
|
|
24
|
+
* Opened by `runWithRequestContext`, so each request context gets its own
|
|
25
|
+
* scope. Pass `scope` to resume one across two ALS runs: the post-action
|
|
26
|
+
* revalidation opens a fresh scope for the target route's middleware and
|
|
27
|
+
* renders the revalidated tree in that same scope later.
|
|
28
|
+
*/
|
|
29
|
+
export function runWithReactCacheScope<T>(
|
|
30
|
+
fn: () => T,
|
|
31
|
+
scope: ReactCacheScope = createReactCacheScope()
|
|
32
|
+
): T {
|
|
33
|
+
return reactCacheScopeAls.run(scope, fn);
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
/**
|
|
37
|
+
* Run `fn` with no timber scope, so `React.cache` behaves as stock React:
|
|
38
|
+
* no dedupe outside a render, Flight's own per-render cache inside one.
|
|
39
|
+
*
|
|
40
|
+
* Used for server action bodies — an action reads, writes cookies, and
|
|
41
|
+
* mutates, so memoized reads would go stale mid-action (the same choice
|
|
42
|
+
* Next.js makes) — and for renders whose output outlives the request
|
|
43
|
+
* (`cache.component` capture), which must not bake request-memoized values
|
|
44
|
+
* into a cross-request cache entry.
|
|
45
|
+
*/
|
|
46
|
+
export function runOutsideReactCacheScope<T>(fn: () => T): T {
|
|
47
|
+
return reactCacheScopeAls.exit(fn);
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
// ─── Dev diagnostic: cookie written after a cached read ──────────────────
|
|
51
|
+
//
|
|
52
|
+
// A value React.cache stored was computed from the request inputs it read,
|
|
53
|
+
// and does not see a later write — a cached getUser() that read the session
|
|
54
|
+
// cookie still returns the old user after middleware deletes the cookie.
|
|
55
|
+
// The scope is deliberately NOT cleared on a write: that would silently
|
|
56
|
+
// discard every value middleware warmed (spike doc §Decisions D3). Instead,
|
|
57
|
+
// dev mode flags the one case that can go stale: a cookie read while the
|
|
58
|
+
// scope held values, then written later in the same request. Tracking is
|
|
59
|
+
// per scope, so it ends with the request; production does none of it.
|
|
60
|
+
|
|
61
|
+
/** Stands for "every cookie" — a getAll()/size read, or a clear() write. */
|
|
62
|
+
export const ANY_COOKIE = '*';
|
|
63
|
+
|
|
64
|
+
const cookieReads = new WeakMap<ReactCacheScope, Set<string>>();
|
|
65
|
+
const warnedScopes = new WeakSet<ReactCacheScope>();
|
|
66
|
+
|
|
67
|
+
/**
|
|
68
|
+
* Dev-only: record that cookie `name` (or {@link ANY_COOKIE}) was read
|
|
69
|
+
* while `React.cache` held values in this request — the read may have
|
|
70
|
+
* happened inside a cached function.
|
|
71
|
+
*/
|
|
72
|
+
export function noteCookieReadForReactCache(name: string): void {
|
|
73
|
+
if (!isDevMode()) return;
|
|
74
|
+
const scope = reactCacheScopeAls.getStore();
|
|
75
|
+
if (!scope || scope.size === 0) return;
|
|
76
|
+
let reads = cookieReads.get(scope);
|
|
77
|
+
if (!reads) {
|
|
78
|
+
reads = new Set();
|
|
79
|
+
cookieReads.set(scope, reads);
|
|
80
|
+
}
|
|
81
|
+
reads.add(name);
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
/**
|
|
85
|
+
* Dev-only: warn (once per request) when a cookie recorded by
|
|
86
|
+
* {@link noteCookieReadForReactCache} is written. `name` is the written
|
|
87
|
+
* cookie, or {@link ANY_COOKIE} for `clear()`. Dev-only because reads are
|
|
88
|
+
* only recorded in dev.
|
|
89
|
+
*/
|
|
90
|
+
export function warnIfCachedCookieIsWritten(name: string): void {
|
|
91
|
+
const scope = reactCacheScopeAls.getStore();
|
|
92
|
+
if (!scope || warnedScopes.has(scope)) return;
|
|
93
|
+
const reads = cookieReads.get(scope);
|
|
94
|
+
if (!reads) return;
|
|
95
|
+
const stale = name === ANY_COOKIE ? reads.size > 0 : reads.has(name) || reads.has(ANY_COOKIE);
|
|
96
|
+
if (!stale) return;
|
|
97
|
+
warnedScopes.add(scope);
|
|
98
|
+
const which = name === ANY_COOKIE ? 'Cookies were cleared' : `Cookie "${name}" was written`;
|
|
99
|
+
console.warn(
|
|
100
|
+
`[timber] ${which} after being read while React.cache held values for this request. ` +
|
|
101
|
+
`A cached function that read it keeps returning the old result — pass the cookie ` +
|
|
102
|
+
`value in as an argument (e.g. getUser(sessionToken)) so a change is a new cache key.`
|
|
103
|
+
);
|
|
104
|
+
}
|
|
@@ -8,6 +8,26 @@
|
|
|
8
8
|
* Design doc: 02-rendering-pipeline.md §"Streaming Constraints"
|
|
9
9
|
*/
|
|
10
10
|
|
|
11
|
+
/** `renderTimeoutMs` when timber.config.ts leaves it unset. */
|
|
12
|
+
export const DEFAULT_RENDER_TIMEOUT_MS = 30_000;
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* What `renderTimeoutMs: 0` means: a safety ceiling, never "no timeout" —
|
|
16
|
+
* an unbounded render or singleflight wait can hang a connection or
|
|
17
|
+
* deadlock a cache key forever.
|
|
18
|
+
*/
|
|
19
|
+
export const RENDER_TIMEOUT_CEILING_MS = 120_000;
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* The one mapping from configured `renderTimeoutMs` to the timeout every
|
|
23
|
+
* consumer enforces. Config enters the runtime already resolved (the
|
|
24
|
+
* runtime config module, static generation's options), so readers never
|
|
25
|
+
* see 0 or undefined.
|
|
26
|
+
*/
|
|
27
|
+
export function resolveRenderTimeoutMs(configured: number | undefined): number {
|
|
28
|
+
return (configured ?? DEFAULT_RENDER_TIMEOUT_MS) || RENDER_TIMEOUT_CEILING_MS;
|
|
29
|
+
}
|
|
30
|
+
|
|
11
31
|
/**
|
|
12
32
|
* Error thrown when an SSR render or RSC stream read exceeds the
|
|
13
33
|
* configured timeout. Callers can check `instanceof RenderTimeoutError`
|
|
@@ -6,7 +6,17 @@
|
|
|
6
6
|
* ssr-render.ts, and rsc-entry/helpers.ts. See TIM-1071 item 1.
|
|
7
7
|
*/
|
|
8
8
|
|
|
9
|
-
|
|
9
|
+
/**
|
|
10
|
+
* The ` nonce="…"` attribute fragment for a timber-generated `<script>` tag,
|
|
11
|
+
* or `''` when the request has no nonce. Callers interpolate it directly
|
|
12
|
+
* after the tag name: `` `<script${nonceAttr(nonce)}>` ``.
|
|
13
|
+
*
|
|
14
|
+
* The value comes from `generateNonce` in csp-nonce.ts and is base64, so no attribute
|
|
15
|
+
* escaping is required.
|
|
16
|
+
*/
|
|
17
|
+
export function nonceAttr(nonce: string | undefined): string {
|
|
18
|
+
return nonce ? ` nonce="${nonce}"` : '';
|
|
19
|
+
}
|
|
10
20
|
|
|
11
21
|
/**
|
|
12
22
|
* Check if an error is an abort error (connection closed by client).
|
|
@@ -17,7 +17,6 @@
|
|
|
17
17
|
import type { CoercedParams } from '../shared/param-value.ts';
|
|
18
18
|
import { requestContextAls, type RequestContextStore } from './als-registry.ts';
|
|
19
19
|
import { _setGetSearchParamsFn, _setGetSegmentParamsFn } from '../shared/als-slots.ts';
|
|
20
|
-
import { consumeSeededCookies } from './cookie-context.ts';
|
|
21
20
|
import { appVisibleSearch } from '../shared/rsc-cache-key.ts';
|
|
22
21
|
import {
|
|
23
22
|
mergeSlotParams,
|
|
@@ -25,6 +24,7 @@ import {
|
|
|
25
24
|
type SlotParamsRecord,
|
|
26
25
|
} from '../shared/slot-params.ts';
|
|
27
26
|
import { isDebug } from './debug.ts';
|
|
27
|
+
import { runWithReactCacheScope, type ReactCacheScope } from './react-cache-scope.ts';
|
|
28
28
|
|
|
29
29
|
// Re-export the ALS for framework-internal consumers that need direct access.
|
|
30
30
|
export { requestContextAls };
|
|
@@ -306,26 +306,53 @@ export type ReadonlyHeaders = Pick<
|
|
|
306
306
|
|
|
307
307
|
// ─── Framework-Internal Helpers ───────────────────────────────────────────
|
|
308
308
|
|
|
309
|
+
/** Options for `runWithRequestContext()`. */
|
|
310
|
+
export interface RequestContextOptions {
|
|
311
|
+
/**
|
|
312
|
+
* Resume this `React.cache` scope instead of opening a fresh one. Only the
|
|
313
|
+
* post-action revalidation passes it.
|
|
314
|
+
*/
|
|
315
|
+
reactCacheScope?: ReactCacheScope;
|
|
316
|
+
/**
|
|
317
|
+
* The request's cookies, already parsed. Re-entry callers (the no-JS form
|
|
318
|
+
* rerender and the revalidation middleware) pass the action's
|
|
319
|
+
* post-mutation cookie state here instead of round-tripping it through a
|
|
320
|
+
* `Cookie:` header — the same parse the H-3 smuggling primitive abused
|
|
321
|
+
* (TIM-868).
|
|
322
|
+
*/
|
|
323
|
+
cookies?: Map<string, string>;
|
|
324
|
+
}
|
|
325
|
+
|
|
309
326
|
/**
|
|
310
327
|
* Run a callback within a request context. Used by the pipeline to establish
|
|
311
328
|
* per-request ALS scope so that `getHeaders()` and `getCookies()` work.
|
|
312
329
|
*
|
|
313
|
-
*
|
|
314
|
-
*
|
|
315
|
-
*
|
|
316
|
-
*
|
|
330
|
+
* When `options.cookies` is given, the context's `parsedCookies` map is a
|
|
331
|
+
* copy of it and the raw `cookieHeader` is left empty, so
|
|
332
|
+
* `parseCookieHeader` never runs for that request. Re-entry callers pass the
|
|
333
|
+
* action's post-mutation cookie state this way (TIM-868).
|
|
334
|
+
*
|
|
335
|
+
* Each request context also opens its own `React.cache` scope, shared by
|
|
336
|
+
* everything that runs inside it — proxy.ts, middleware.ts, route handlers,
|
|
337
|
+
* and the render (design/02-rendering-pipeline.md §"Cache Scoping Model").
|
|
317
338
|
*
|
|
318
339
|
* @param req - The incoming Request object.
|
|
319
340
|
* @param fn - The function to run within the request context.
|
|
320
341
|
*/
|
|
321
|
-
export function runWithRequestContext<T>(
|
|
342
|
+
export function runWithRequestContext<T>(
|
|
343
|
+
req: Request,
|
|
344
|
+
fn: () => T,
|
|
345
|
+
{ reactCacheScope, cookies }: RequestContextOptions = {}
|
|
346
|
+
): T {
|
|
322
347
|
const originalCopy = new Headers(req.headers);
|
|
323
348
|
const appVisible = appVisibleSearch(new URL(req.url));
|
|
324
|
-
|
|
349
|
+
// A copy: the caller keeps its map, and this request's writes stay here.
|
|
350
|
+
const seed = cookies ? new Map(cookies) : undefined;
|
|
325
351
|
const store: RequestContextStore = {
|
|
352
|
+
request: req,
|
|
326
353
|
headers: freezeHeaders(req.headers),
|
|
327
354
|
originalHeaders: originalCopy,
|
|
328
|
-
//
|
|
355
|
+
// Given parsed cookies, leave the raw header empty — parseCookieHeader is the
|
|
329
356
|
// exact code path the smuggling primitive abused, and lazy parsing is
|
|
330
357
|
// gated on `parsedCookies` being undefined.
|
|
331
358
|
cookieHeader: seed ? '' : (req.headers.get('cookie') ?? ''),
|
|
@@ -340,7 +367,7 @@ export function runWithRequestContext<T>(req: Request, fn: () => T): T {
|
|
|
340
367
|
flushed: false,
|
|
341
368
|
mutableContext: false,
|
|
342
369
|
};
|
|
343
|
-
return requestContextAls.run(store, fn);
|
|
370
|
+
return requestContextAls.run(store, () => runWithReactCacheScope(fn, reactCacheScope));
|
|
344
371
|
}
|
|
345
372
|
|
|
346
373
|
/**
|
|
@@ -20,7 +20,8 @@ import { createElement, Fragment } from 'react';
|
|
|
20
20
|
import { withSpan } from './tracing.ts';
|
|
21
21
|
import type { RouteMatch } from './pipeline.ts';
|
|
22
22
|
import type { ManifestSegmentNode } from './route-matcher.ts';
|
|
23
|
-
import {
|
|
23
|
+
import { DenySignal } from './primitives.ts';
|
|
24
|
+
import { isDenySignal } from './signal-identity.ts';
|
|
24
25
|
import { isClientReference } from './utils/element-type.ts';
|
|
25
26
|
import { loadRouteModules, type MetadataSource, type AccessFn } from './route-module-loader.ts';
|
|
26
27
|
import { MetadataCollector, MetadataHead } from './metadata-collector.ts';
|
|
@@ -46,7 +47,8 @@ import type { ClientStateTree } from './state-tree-diff.ts';
|
|
|
46
47
|
import { computeSkippableSet } from './skippable-prefix.ts';
|
|
47
48
|
import { computeSegmentKeys, computeSegmentTreePaths } from '../routing/segment-keys.ts';
|
|
48
49
|
import { setFirstDeniedSegmentIndex } from './deny-boundary.ts';
|
|
49
|
-
import { loadModule } from './safe-load.ts';
|
|
50
|
+
import { loadModule, type ManifestLoader } from './safe-load.ts';
|
|
51
|
+
import { loadFallbackModule } from './pipeline-helpers.ts';
|
|
50
52
|
import { replaceOutermostSegmentOutlet } from './route-element-helpers.ts';
|
|
51
53
|
|
|
52
54
|
/**
|
|
@@ -205,6 +207,9 @@ export async function buildRouteElement(
|
|
|
205
207
|
metadataRouteHashes?: Record<string, string>
|
|
206
208
|
): Promise<RouteElementResult> {
|
|
207
209
|
const segments = match.segments;
|
|
210
|
+
// Status and error pages the boundaries need: a load failure is reported,
|
|
211
|
+
// and the boundary is skipped rather than failing the render.
|
|
212
|
+
const loadBoundaryModule = (file: ManifestLoader) => loadFallbackModule(file, req, segments);
|
|
208
213
|
|
|
209
214
|
// ── Module loading ──────────────────────────────────────────────────
|
|
210
215
|
// Load layout/page modules and collect metadata sources. Metadata
|
|
@@ -239,7 +244,7 @@ export async function buildRouteElement(
|
|
|
239
244
|
// can tell a file it owns from an ancestor's. See TIM-666, TIM-1356.
|
|
240
245
|
const denyPageChains = new Map<number, DenyPageEntry[]>();
|
|
241
246
|
for (let i = 0; i < segments.length; i++) {
|
|
242
|
-
const chain = await buildDenyPageChain(segments, i, segmentKeys);
|
|
247
|
+
const chain = await buildDenyPageChain(segments, i, segmentKeys, req);
|
|
243
248
|
if (chain.length > 0) {
|
|
244
249
|
denyPageChains.set(i, chain);
|
|
245
250
|
}
|
|
@@ -424,7 +429,7 @@ export async function buildRouteElement(
|
|
|
424
429
|
// rather than catching it first and rendering at the wrong depth
|
|
425
430
|
// (TIM-1356).
|
|
426
431
|
element = await wrapSegmentWithErrorBoundaries(segment, element, {
|
|
427
|
-
loadModule,
|
|
432
|
+
loadModule: loadBoundaryModule,
|
|
428
433
|
createElement: h,
|
|
429
434
|
errorBoundaryComponent: TimberErrorBoundary,
|
|
430
435
|
segmentKey: segmentKeys[i],
|
|
@@ -449,7 +454,7 @@ export async function buildRouteElement(
|
|
|
449
454
|
h,
|
|
450
455
|
interception,
|
|
451
456
|
parentTreePath,
|
|
452
|
-
|
|
457
|
+
req,
|
|
453
458
|
clientStateTree: clientStateTree ?? null,
|
|
454
459
|
slotSkipInfo,
|
|
455
460
|
});
|
|
@@ -517,7 +522,7 @@ export async function buildRouteElement(
|
|
|
517
522
|
// provider into the client's mounted outlet, carries it along.
|
|
518
523
|
// See TIM-1450, design/04-authorization.md §"Where a Deny Page Renders".
|
|
519
524
|
layoutElement = await wrapSegmentWithErrorBoundaries(segment, layoutElement, {
|
|
520
|
-
loadModule,
|
|
525
|
+
loadModule: loadBoundaryModule,
|
|
521
526
|
createElement: h,
|
|
522
527
|
errorBoundaryComponent: TimberErrorBoundary,
|
|
523
528
|
segmentKey: segmentId,
|
|
@@ -9,9 +9,10 @@
|
|
|
9
9
|
*/
|
|
10
10
|
|
|
11
11
|
import type { RouteContext } from './types.ts';
|
|
12
|
-
import { logRouteError } from './logger.ts';
|
|
13
|
-
import { isDenySignal, isRedirectSignal } from './primitives.ts';
|
|
14
12
|
import { stripHeadBody } from './head-response.ts';
|
|
13
|
+
import { setMutableCookieContext } from './request-context.ts';
|
|
14
|
+
import { getSetCookieHeaders } from './cookie-context.ts';
|
|
15
|
+
import { syncCookieJar } from './pipeline-helpers.ts';
|
|
15
16
|
|
|
16
17
|
// ─── Types ───────────────────────────────────────────────────────────────
|
|
17
18
|
|
|
@@ -79,15 +80,16 @@ export async function handleRouteRequest(mod: RouteModule, ctx: RouteContext): P
|
|
|
79
80
|
const allowed = resolveAllowedMethods(mod);
|
|
80
81
|
const allowHeader = allowed.join(', ');
|
|
81
82
|
|
|
82
|
-
// Auto OPTIONS — 204 with Allow header
|
|
83
|
+
// Auto OPTIONS — 204 with Allow header. Merged like any handler response,
|
|
84
|
+
// so a CORS header middleware set reaches the preflight.
|
|
83
85
|
if (method === 'OPTIONS') {
|
|
84
86
|
if (mod.OPTIONS) {
|
|
85
87
|
return runHandler(mod.OPTIONS, ctx);
|
|
86
88
|
}
|
|
87
|
-
return
|
|
88
|
-
status: 204,
|
|
89
|
-
headers
|
|
90
|
-
|
|
89
|
+
return mergeResponseHeaders(
|
|
90
|
+
new Response(null, { status: 204, headers: { Allow: allowHeader } }),
|
|
91
|
+
ctx.headers
|
|
92
|
+
);
|
|
91
93
|
}
|
|
92
94
|
|
|
93
95
|
// HEAD fallback — dispatch to GET when the module exports no HEAD, so the
|
|
@@ -107,32 +109,43 @@ export async function handleRouteRequest(mod: RouteModule, ctx: RouteContext): P
|
|
|
107
109
|
// Dispatch to the named handler
|
|
108
110
|
const handler = mod[method];
|
|
109
111
|
if (!handler) {
|
|
110
|
-
return
|
|
111
|
-
status: 405,
|
|
112
|
-
headers
|
|
113
|
-
|
|
112
|
+
return mergeResponseHeaders(
|
|
113
|
+
new Response(null, { status: 405, headers: { Allow: allowHeader } }),
|
|
114
|
+
ctx.headers
|
|
115
|
+
);
|
|
114
116
|
}
|
|
115
117
|
|
|
116
118
|
return runHandler(handler, ctx);
|
|
117
119
|
}
|
|
118
120
|
|
|
119
121
|
/**
|
|
120
|
-
* Run a handler
|
|
122
|
+
* Run a handler with cookie writes enabled and merge ctx.headers into its
|
|
123
|
+
* response.
|
|
124
|
+
*
|
|
125
|
+
* Cookies (design/29-cookies.md §"Where Cookies Can Be Set"): the render
|
|
126
|
+
* phase applied the jar to `ctx.headers` before the handler ran, so on the
|
|
127
|
+
* way out only what the handler changed is synced onto that bag. It happens
|
|
128
|
+
* whether the handler returns or throws, so a cookie set before `redirect()`
|
|
129
|
+
* rides the redirect, and the pipeline's 500 for a throw carries the same
|
|
130
|
+
* bag.
|
|
131
|
+
*
|
|
132
|
+
* Nothing is caught. A signal goes to the pipeline, whose deny fallback
|
|
133
|
+
* answers a route.ts match in JSON and whose redirect outcome sends the 3xx
|
|
134
|
+
* (TIM-1533). Any other error goes to the pipeline, which logs it,
|
|
135
|
+
* reports it to `onRequestError` as phase `'route'`, and answers it in the
|
|
136
|
+
* same format as every other route.ts error (TIM-1537).
|
|
121
137
|
*/
|
|
122
138
|
async function runHandler(handler: RouteHandler, ctx: RouteContext): Promise<Response> {
|
|
139
|
+
const applied = getSetCookieHeaders();
|
|
140
|
+
setMutableCookieContext(true);
|
|
141
|
+
let res: Response;
|
|
123
142
|
try {
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
// gets the JSON status-file chain and redirect() gets a proper 3xx with
|
|
129
|
-
// Location. Not errors; don't log them.
|
|
130
|
-
if (isDenySignal(error) || isRedirectSignal(error)) {
|
|
131
|
-
throw error;
|
|
132
|
-
}
|
|
133
|
-
logRouteError({ method: ctx.req.method, path: new URL(ctx.req.url).pathname, error });
|
|
134
|
-
return new Response(null, { status: 500 });
|
|
143
|
+
res = await handler(ctx);
|
|
144
|
+
} finally {
|
|
145
|
+
setMutableCookieContext(false);
|
|
146
|
+
syncCookieJar(ctx.headers, applied);
|
|
135
147
|
}
|
|
148
|
+
return mergeResponseHeaders(res, ctx.headers);
|
|
136
149
|
}
|
|
137
150
|
|
|
138
151
|
/**
|
|
@@ -85,7 +85,10 @@ function matchPathname(root: ManifestSegmentNode, pathname: string): RouteMatch
|
|
|
85
85
|
|
|
86
86
|
return {
|
|
87
87
|
segments: result.chain,
|
|
88
|
-
|
|
88
|
+
rawSegmentParams: result.params,
|
|
89
|
+
// Uncoerced until the pipeline runs the codecs; a copy, so nothing that
|
|
90
|
+
// later replaces or edits it can reach the raw record.
|
|
91
|
+
segmentParams: { ...result.params },
|
|
89
92
|
middlewareChain: collectMiddlewareChain(result.chain),
|
|
90
93
|
};
|
|
91
94
|
}
|
|
@@ -117,6 +120,17 @@ export function collectMiddlewareChain(chain: ManifestSegmentNode[]): Middleware
|
|
|
117
120
|
return middlewareChain;
|
|
118
121
|
}
|
|
119
122
|
|
|
123
|
+
/**
|
|
124
|
+
* Whether a matched chain renders as an API route: its leaf has a
|
|
125
|
+
* `route.ts` and no `page.tsx`. The same test `renderRoute` dispatches on,
|
|
126
|
+
* so every answer about a route's format — early hints, deny format, error
|
|
127
|
+
* format, error reporting — agrees with what actually ran.
|
|
128
|
+
*/
|
|
129
|
+
export function isApiRouteChain(segments: readonly ManifestSegmentNode[]): boolean {
|
|
130
|
+
const leaf = segments[segments.length - 1];
|
|
131
|
+
return !!leaf?.route && !leaf.page;
|
|
132
|
+
}
|
|
133
|
+
|
|
120
134
|
// ─── Metadata Route Matcher ─────────────────────────────────────────────
|
|
121
135
|
|
|
122
136
|
/** Result of matching a metadata route. */
|