@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
package/src/client/router.ts
CHANGED
|
@@ -12,7 +12,6 @@
|
|
|
12
12
|
// See design/19-client-navigation.md for the full architecture.
|
|
13
13
|
|
|
14
14
|
import { SegmentCache, PrefetchCache } from './segment-cache.ts';
|
|
15
|
-
import type { SegmentInfo } from '../shared/segment-info.ts';
|
|
16
15
|
import { HistoryStack } from './history.ts';
|
|
17
16
|
import { createNavigationCommitter } from './navigation-commit.ts';
|
|
18
17
|
|
|
@@ -39,7 +38,13 @@ export function createRouter(deps: RouterDeps): RouterInstance {
|
|
|
39
38
|
|
|
40
39
|
// Every write that makes a page "current", and the read that reports the
|
|
41
40
|
// current page back to the server. See navigation-commit.ts.
|
|
42
|
-
const {
|
|
41
|
+
const {
|
|
42
|
+
prepareNavigation,
|
|
43
|
+
currentStateTree,
|
|
44
|
+
committedSegmentInfo,
|
|
45
|
+
isSegmentReuseSuppressed,
|
|
46
|
+
restoreSegmentReuse,
|
|
47
|
+
} = createNavigationCommitter({
|
|
43
48
|
segmentCache,
|
|
44
49
|
historyStack,
|
|
45
50
|
clientSegmentCache: () => deps.clientSegmentCache,
|
|
@@ -60,15 +65,16 @@ export function createRouter(deps: RouterDeps): RouterInstance {
|
|
|
60
65
|
} = lifecycle;
|
|
61
66
|
|
|
62
67
|
// Fetch → commit → hand to React. See router-pipeline.ts.
|
|
63
|
-
const { performNavigationFetch, renderViaTransition
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
68
|
+
const { performNavigationFetch, renderViaTransition } = createNavigationPipeline({
|
|
69
|
+
deps,
|
|
70
|
+
prefetchCache,
|
|
71
|
+
currentStateTree,
|
|
72
|
+
prepareNavigation,
|
|
73
|
+
isSegmentReuseSuppressed,
|
|
74
|
+
restoreSegmentReuse,
|
|
75
|
+
markHandedOff,
|
|
76
|
+
forgetOlderHandoffs,
|
|
77
|
+
});
|
|
72
78
|
|
|
73
79
|
// Post-paint scroll effects.
|
|
74
80
|
const { restoreScrollAfterPaint, scrollToHashAfterPaint } = createScrollEffects(deps);
|
|
@@ -223,15 +229,14 @@ export function createRouter(deps: RouterDeps): RouterInstance {
|
|
|
223
229
|
undefined,
|
|
224
230
|
owner.fetchAbort.signal
|
|
225
231
|
);
|
|
226
|
-
const payload = await resolveForFallback(result.payload);
|
|
227
232
|
const params = await result.params;
|
|
228
233
|
const { navState, commit } = prepareNavigation(url, {
|
|
229
|
-
payload,
|
|
234
|
+
payload: result.payload,
|
|
230
235
|
params,
|
|
231
236
|
segmentInfo: result.segmentInfo,
|
|
232
237
|
skippedSegments: result.skippedSegments,
|
|
233
238
|
});
|
|
234
|
-
return { ...result,
|
|
239
|
+
return { ...result, params, navState, commit };
|
|
235
240
|
},
|
|
236
241
|
opts.onCommit
|
|
237
242
|
);
|
|
@@ -428,6 +433,7 @@ export function createRouter(deps: RouterDeps): RouterInstance {
|
|
|
428
433
|
onPendingChange,
|
|
429
434
|
prefetch,
|
|
430
435
|
epoch: () => lifecycle.epoch(),
|
|
436
|
+
isEpochCurrent: (epoch) => lifecycle.isEpochCurrent(epoch),
|
|
431
437
|
|
|
432
438
|
async applyActionResult(epoch, tree) {
|
|
433
439
|
if (!lifecycle.isEpochCurrent(epoch)) return false;
|
|
@@ -453,7 +459,12 @@ export function createRouter(deps: RouterDeps): RouterInstance {
|
|
|
453
459
|
const currentUrl = deps.getCurrentUrl();
|
|
454
460
|
const payloadTree = readPayloadTree(tree);
|
|
455
461
|
const params = readPublishedParams(tree);
|
|
456
|
-
|
|
462
|
+
// The response carries no segment metadata; the page it re-renders is
|
|
463
|
+
// the one the last commit made current, and the committer kept that
|
|
464
|
+
// page's. Not a history-stack lookup by `currentUrl`: the stack is keyed
|
|
465
|
+
// by the spelling `navigate()` was given, which need not be
|
|
466
|
+
// `window.location`'s (TIM-1541).
|
|
467
|
+
const segmentInfo = committedSegmentInfo();
|
|
457
468
|
|
|
458
469
|
let outcomeResolve!: (o: CommitOutcome) => void;
|
|
459
470
|
const outcomePromise = new Promise<CommitOutcome>((r) => (outcomeResolve = r));
|
|
@@ -471,7 +482,7 @@ export function createRouter(deps: RouterDeps): RouterInstance {
|
|
|
471
482
|
const { navState, commit } = prepareNavigation(currentUrl, {
|
|
472
483
|
payload: payloadTree,
|
|
473
484
|
params,
|
|
474
|
-
segmentInfo
|
|
485
|
+
segmentInfo,
|
|
475
486
|
});
|
|
476
487
|
return {
|
|
477
488
|
payload: payloadTree,
|
|
@@ -479,7 +490,7 @@ export function createRouter(deps: RouterDeps): RouterInstance {
|
|
|
479
490
|
navState,
|
|
480
491
|
commit,
|
|
481
492
|
decodePromise: null,
|
|
482
|
-
segmentInfo
|
|
493
|
+
segmentInfo,
|
|
483
494
|
skippedSegments: null,
|
|
484
495
|
};
|
|
485
496
|
},
|
|
@@ -492,16 +503,46 @@ export function createRouter(deps: RouterDeps): RouterInstance {
|
|
|
492
503
|
runWhenIdle: (task: () => void) => lifecycle.runWhenIdle(task),
|
|
493
504
|
settleHandoffs: () => lifecycle.settleHandoffs(),
|
|
494
505
|
invalidatePath(path: string): void {
|
|
495
|
-
|
|
506
|
+
const hasSearch = path.includes('?');
|
|
507
|
+
if (hasSearch) {
|
|
508
|
+
historyStack.delete(path);
|
|
509
|
+
} else {
|
|
510
|
+
// TIM-1465: evict all query variants sharing this pathname
|
|
511
|
+
historyStack.deleteByPathname(path);
|
|
512
|
+
}
|
|
496
513
|
prefetchCache.invalidateUrl(path);
|
|
514
|
+
// TIM-1466: suppress segment/slot reuse so the next navigation
|
|
515
|
+
// gets a full render instead of reusing stale shared layouts.
|
|
516
|
+
// Uses invalidateReuse() instead of clear() to preserve treePaths
|
|
517
|
+
// for interception scoping — a modal mounted on the current tree
|
|
518
|
+
// must still be navigable (codex on #1167).
|
|
519
|
+
segmentCache.invalidateReuse();
|
|
497
520
|
},
|
|
498
521
|
evictStaleCaches(): void {
|
|
499
522
|
prefetchCache.clearReady();
|
|
500
|
-
historyStack.
|
|
523
|
+
historyStack.clear();
|
|
524
|
+
},
|
|
525
|
+
hydrate(page) {
|
|
526
|
+
// Where hydration seeds the current page: the same prepare-and-commit
|
|
527
|
+
// every navigation goes through, so the segment cache, the committed
|
|
528
|
+
// segment metadata and the history entry are written by one commit
|
|
529
|
+
// rather than by bootstrap reaching into each (TIM-1541). Committed
|
|
530
|
+
// here, at once — see `RouterInstance.hydrate` for why not on React's
|
|
531
|
+
// hydration commit.
|
|
532
|
+
//
|
|
533
|
+
// Replaying this entry on a Back re-renders `payload` and republishes
|
|
534
|
+
// `params`. Both came out of one split of one decoded payload root
|
|
535
|
+
// (rsc-stream.ts), so the entry cannot replay with another route's
|
|
536
|
+
// params (TIM-1037, TIM-1285).
|
|
537
|
+
const { navState, commit } = prepareNavigation(deps.getCurrentUrl(), {
|
|
538
|
+
payload: page.payload,
|
|
539
|
+
params: page.params,
|
|
540
|
+
segmentInfo: page.segmentInfo,
|
|
541
|
+
});
|
|
542
|
+
commit();
|
|
543
|
+
return navState;
|
|
501
544
|
},
|
|
502
|
-
initSegmentCache: (segments: SegmentInfo[]) => updateSegmentCache(segments),
|
|
503
545
|
segmentCache,
|
|
504
546
|
prefetchCache,
|
|
505
|
-
historyStack,
|
|
506
547
|
};
|
|
507
548
|
}
|
package/src/client/rsc-fetch.ts
CHANGED
|
@@ -18,11 +18,7 @@ import {
|
|
|
18
18
|
RSC_KEY_PARAM,
|
|
19
19
|
} from '../shared/rsc-cache-key.ts';
|
|
20
20
|
import type { RscManifest, RscManifestEntry } from '../shared/rsc-manifest.ts';
|
|
21
|
-
import {
|
|
22
|
-
splitPayloadRoot,
|
|
23
|
-
readPublishedParams,
|
|
24
|
-
type PublishedParams,
|
|
25
|
-
} from '../shared/payload-root.ts';
|
|
21
|
+
import { splitPayloadRoot, type PublishedParams } from '../shared/payload-root.ts';
|
|
26
22
|
import { normalizeBasePath, stripBasePath, withBasePath } from '../shared/base-path.ts';
|
|
27
23
|
import { rscPayloadPath } from '../shared/rsc-payload-path.ts';
|
|
28
24
|
import type { SegmentInfo, StateTree } from '../shared/segment-info.ts';
|
|
@@ -401,7 +397,8 @@ export class VersionSkewError extends Error {
|
|
|
401
397
|
/**
|
|
402
398
|
* Thrown when the server returns an error for an RSC payload request.
|
|
403
399
|
* The server sends X-Timber-Error header and a JSON body instead of a
|
|
404
|
-
*
|
|
400
|
+
* RSC stream it cannot answer in-SPA (a deny that needs a fallback page, a
|
|
401
|
+
* broken first read). Caught in
|
|
405
402
|
* navigate() to trigger a hard navigation so the server can render
|
|
406
403
|
* the error page as HTML.
|
|
407
404
|
*
|
|
@@ -487,9 +484,12 @@ function trackStreamCompletion(body: ReadableStream<Uint8Array>): {
|
|
|
487
484
|
// ─── Fetch ───────────────────────────────────────────────────────
|
|
488
485
|
|
|
489
486
|
/**
|
|
490
|
-
* Fetch an RSC payload from the server
|
|
491
|
-
*
|
|
492
|
-
*
|
|
487
|
+
* Fetch an RSC payload from the server and start decoding it.
|
|
488
|
+
*
|
|
489
|
+
* Every response is classified here, once: a version-skew reload, a 204 or
|
|
490
|
+
* 3xx redirect, a server error envelope and a non-RSC body are each turned
|
|
491
|
+
* into their error before `decodeRsc` sees the body. There is no second
|
|
492
|
+
* path — tests decode real Flight bytes through the same code (TIM-1541).
|
|
493
493
|
*/
|
|
494
494
|
export async function fetchRscPayload(
|
|
495
495
|
url: string,
|
|
@@ -518,156 +518,118 @@ export async function fetchRscPayload(
|
|
|
518
518
|
// during that window would otherwise still hit the network with an
|
|
519
519
|
// already-aborted signal.
|
|
520
520
|
signal?.throwIfAborted();
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
if (
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
)
|
|
572
|
-
response.body?.cancel();
|
|
573
|
-
throw new NonRscResponse(url);
|
|
574
|
-
}
|
|
575
|
-
} else {
|
|
576
|
-
if (!isRscContentType(response.headers.get('content-type'))) {
|
|
577
|
-
response.body?.cancel();
|
|
578
|
-
throw new NonRscResponse(url);
|
|
579
|
-
}
|
|
521
|
+
// createFromFetch takes a Promise<Response> and progressively parses
|
|
522
|
+
// the RSC Flight stream as chunks arrive.
|
|
523
|
+
//
|
|
524
|
+
// Intercept the response to read segment metadata before createFromFetch
|
|
525
|
+
// consumes the body. Reading headers does NOT consume the body stream.
|
|
526
|
+
const fetchPromise = deps.fetch(rscUrl, { headers, redirect: 'manual', signal });
|
|
527
|
+
let segmentInfo: SegmentInfo[] | null = null;
|
|
528
|
+
let skippedSegments: string[] | null = null;
|
|
529
|
+
// Track when the full RSC body stream is consumed (not just shell).
|
|
530
|
+
// Initialized to resolved for bodyless responses; overwritten when
|
|
531
|
+
// the response has a body.
|
|
532
|
+
let streamDone: Promise<void> = Promise.resolve();
|
|
533
|
+
|
|
534
|
+
const wrappedPromise = fetchPromise.then((response) => {
|
|
535
|
+
// Version skew detection (TIM-446): if the server signals a reload,
|
|
536
|
+
// throw VersionSkewError so the caller (router navigate) can trigger
|
|
537
|
+
// a full page reload.
|
|
538
|
+
if (checkReloadSignal(response)) {
|
|
539
|
+
throw new VersionSkewError();
|
|
540
|
+
}
|
|
541
|
+
// Detect server-side redirects. The server returns 204 + X-Timber-Redirect
|
|
542
|
+
// for RSC payload requests instead of a raw 302, because fetch with
|
|
543
|
+
// redirect: "manual" turns 302s into opaque redirects (status 0, null body)
|
|
544
|
+
// which crashes createFromFetch when it tries to read the body stream.
|
|
545
|
+
const redirectLocation =
|
|
546
|
+
response.headers.get('X-Timber-Redirect') ||
|
|
547
|
+
(response.status >= 300 && response.status < 400 ? response.headers.get('Location') : null);
|
|
548
|
+
if (redirectLocation) {
|
|
549
|
+
throw new RedirectError(redirectLocation);
|
|
550
|
+
}
|
|
551
|
+
// Detect server error responses. The server returns X-Timber-Error header
|
|
552
|
+
// with a JSON body instead of an RSC stream it cannot answer in-SPA.
|
|
553
|
+
// Hard-navigate so the server renders the error page as HTML.
|
|
554
|
+
// See design/10-error-handling.md §"Error Page Rendering for Client Navigation"
|
|
555
|
+
if (response.headers.get(TIMBER_ERROR_HEADER) === '1') {
|
|
556
|
+
throw new ServerErrorResponse(response.status, url);
|
|
557
|
+
}
|
|
558
|
+
// Content-Type guard: reject non-RSC responses before createFromFetch
|
|
559
|
+
// tries to parse the body as Flight data.
|
|
560
|
+
// In static mode, accept octet-stream/text/plain/absent content-type
|
|
561
|
+
// (static hosts serve .rsc files with these), but still reject 404s
|
|
562
|
+
// and text/html (missing .rsc file → host returns 404 page or SPA
|
|
563
|
+
// HTML fallback). See TIM-1231, TIM-1243, TIM-1247.
|
|
564
|
+
if (staticMode) {
|
|
565
|
+
const contentType = response.headers.get('content-type');
|
|
566
|
+
if (
|
|
567
|
+
!response.ok ||
|
|
568
|
+
(contentType && contentType.split(';')[0].trim().toLowerCase() === 'text/html')
|
|
569
|
+
) {
|
|
570
|
+
response.body?.cancel();
|
|
571
|
+
throw new NonRscResponse(url);
|
|
580
572
|
}
|
|
581
|
-
|
|
582
|
-
|
|
583
|
-
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
// Wrap the body to track full stream consumption. createFromFetch's
|
|
587
|
-
// thenable resolves when the root model (shell) arrives, but we need
|
|
588
|
-
// to know when ALL chunks are read — that is what the navigation
|
|
589
|
-
// promise resolving means, and what keeps the pending store set.
|
|
590
|
-
if (response.body) {
|
|
591
|
-
const tracked = trackStreamCompletion(response.body);
|
|
592
|
-
streamDone = tracked.done;
|
|
593
|
-
streamDone.catch(() => {}); // prevent unhandled rejection
|
|
594
|
-
return new Response(tracked.body, {
|
|
595
|
-
headers: response.headers,
|
|
596
|
-
status: response.status,
|
|
597
|
-
});
|
|
573
|
+
} else {
|
|
574
|
+
if (!isRscContentType(response.headers.get('content-type'))) {
|
|
575
|
+
response.body?.cancel();
|
|
576
|
+
throw new NonRscResponse(url);
|
|
598
577
|
}
|
|
599
|
-
return response;
|
|
600
|
-
});
|
|
601
|
-
// Await headers so segmentInfo is populated.
|
|
602
|
-
await wrappedPromise;
|
|
603
|
-
// Start decoding but do NOT await — return the in-progress thenable.
|
|
604
|
-
// React can render a Flight thenable directly: it suspends on unresolved
|
|
605
|
-
// parts and progressively renders as chunks arrive, spreading work across
|
|
606
|
-
// frames instead of blocking the main thread in one burst.
|
|
607
|
-
const root = deps.decodeRsc(wrappedPromise);
|
|
608
|
-
// One split, one root: the tree and the params it was rendered with come
|
|
609
|
-
// apart here and nowhere else, so nothing downstream has to keep them
|
|
610
|
-
// paired by hand.
|
|
611
|
-
const { tree: payload, params } = splitPayloadRoot(root);
|
|
612
|
-
// Combine stream completion with payload error propagation.
|
|
613
|
-
// streamDone marks the end of the RSC stream, which is what the
|
|
614
|
-
// navigation promise resolves on. payloadError propagates
|
|
615
|
-
// decode failures (stale client references, Flight parse errors)
|
|
616
|
-
// so the router's catch block can trigger recovery (stale reload).
|
|
617
|
-
const payloadError = new Promise<void>((_, reject) => {
|
|
618
|
-
Promise.resolve(root).then(() => {}, reject);
|
|
619
|
-
});
|
|
620
|
-
payloadError.catch(() => {});
|
|
621
|
-
const decodePromise = Promise.race([streamDone, payloadError]);
|
|
622
|
-
// Mark handled, like payloadError above. The same rejection reaches the
|
|
623
|
-
// router through `payload`, and a navigation that recovers by hard
|
|
624
|
-
// navigating (version skew, non-RSC response) hangs deliberately and never
|
|
625
|
-
// awaits this one — leaving it to surface as an unhandled rejection.
|
|
626
|
-
decodePromise.catch(() => {});
|
|
627
|
-
return {
|
|
628
|
-
payload,
|
|
629
|
-
params,
|
|
630
|
-
decodePromise,
|
|
631
|
-
segmentInfo,
|
|
632
|
-
skippedSegments,
|
|
633
|
-
};
|
|
634
|
-
}
|
|
635
|
-
// Test/fallback path: return raw text
|
|
636
|
-
const response = await deps.fetch(rscUrl, { headers, redirect: 'manual', signal });
|
|
637
|
-
// Check for redirect in test path too
|
|
638
|
-
if (response.status >= 300 && response.status < 400) {
|
|
639
|
-
const location = response.headers.get('Location');
|
|
640
|
-
if (location) {
|
|
641
|
-
throw new RedirectError(location);
|
|
642
|
-
}
|
|
643
|
-
}
|
|
644
|
-
// Server error guard (same as production path above).
|
|
645
|
-
if (response.headers.get(TIMBER_ERROR_HEADER) === '1') {
|
|
646
|
-
throw new ServerErrorResponse(response.status, url);
|
|
647
|
-
}
|
|
648
|
-
// Content-Type guard (same as production path above). See TIM-1231, TIM-1243, TIM-1247.
|
|
649
|
-
if (staticMode) {
|
|
650
|
-
const fallbackContentType = response.headers.get('content-type');
|
|
651
|
-
if (
|
|
652
|
-
!response.ok ||
|
|
653
|
-
(fallbackContentType &&
|
|
654
|
-
fallbackContentType.split(';')[0].trim().toLowerCase() === 'text/html')
|
|
655
|
-
) {
|
|
656
|
-
response.body?.cancel();
|
|
657
|
-
throw new NonRscResponse(url);
|
|
658
578
|
}
|
|
659
|
-
|
|
660
|
-
|
|
661
|
-
|
|
662
|
-
|
|
579
|
+
// Metadata (<title>/<meta>/<link>) now rides the RSC Flight payload
|
|
580
|
+
// as React elements — React 19 Float handles them. See TIM-1151.
|
|
581
|
+
segmentInfo = extractSegmentInfo(response);
|
|
582
|
+
skippedSegments = extractSkippedSegments(response);
|
|
583
|
+
|
|
584
|
+
// Wrap the body to track full stream consumption. createFromFetch's
|
|
585
|
+
// thenable resolves when the root model (shell) arrives, but we need
|
|
586
|
+
// to know when ALL chunks are read — that is what the navigation
|
|
587
|
+
// promise resolving means, and what keeps the pending store set.
|
|
588
|
+
if (response.body) {
|
|
589
|
+
const tracked = trackStreamCompletion(response.body);
|
|
590
|
+
streamDone = tracked.done;
|
|
591
|
+
streamDone.catch(() => {}); // prevent unhandled rejection
|
|
592
|
+
return new Response(tracked.body, {
|
|
593
|
+
headers: response.headers,
|
|
594
|
+
status: response.status,
|
|
595
|
+
});
|
|
663
596
|
}
|
|
664
|
-
|
|
665
|
-
|
|
597
|
+
return response;
|
|
598
|
+
});
|
|
599
|
+
// Await headers so segmentInfo is populated.
|
|
600
|
+
await wrappedPromise;
|
|
601
|
+
// Start decoding but do NOT await — return the in-progress thenable.
|
|
602
|
+
// React can render a Flight thenable directly: it suspends on unresolved
|
|
603
|
+
// parts and progressively renders as chunks arrive, spreading work across
|
|
604
|
+
// frames instead of blocking the main thread in one burst.
|
|
605
|
+
const root = deps.decodeRsc(wrappedPromise);
|
|
606
|
+
// One split, one root: the tree and the params it was rendered with come
|
|
607
|
+
// apart here and nowhere else, so nothing downstream has to keep them
|
|
608
|
+
// paired by hand.
|
|
609
|
+
const { tree: payload, params } = splitPayloadRoot(root);
|
|
610
|
+
// Settles once the stream has ended AND the root has decoded, and rejects
|
|
611
|
+
// as soon as either fails. streamDone marks the end of the RSC stream,
|
|
612
|
+
// which is what the navigation promise resolves on; the root's rejection
|
|
613
|
+
// carries decode failures (stale client references, Flight parse errors)
|
|
614
|
+
// so the router's catch block can trigger recovery (stale reload).
|
|
615
|
+
//
|
|
616
|
+
// Not a race between the two: a payload that arrives whole — a small
|
|
617
|
+
// static `.rsc` file — ends its stream before Flight rejects the root it
|
|
618
|
+
// could not decode, and a race resolved on the stream, so the failure
|
|
619
|
+
// never reached recovery and the skew was never recorded (TIM-1541).
|
|
620
|
+
const rootDecoded = Promise.resolve(root).then(() => {});
|
|
621
|
+
rootDecoded.catch(() => {});
|
|
622
|
+
const decodePromise = Promise.all([streamDone, rootDecoded]).then(() => {});
|
|
623
|
+
// Mark handled, like rootDecoded above. The same rejection reaches the
|
|
624
|
+
// router through `payload`, and a navigation that recovers by hard
|
|
625
|
+
// navigating (version skew, non-RSC response) hangs deliberately and never
|
|
626
|
+
// awaits this one — leaving it to surface as an unhandled rejection.
|
|
627
|
+
decodePromise.catch(() => {});
|
|
666
628
|
return {
|
|
667
|
-
payload
|
|
668
|
-
params
|
|
669
|
-
decodePromise
|
|
670
|
-
segmentInfo
|
|
671
|
-
skippedSegments
|
|
629
|
+
payload,
|
|
630
|
+
params,
|
|
631
|
+
decodePromise,
|
|
632
|
+
segmentInfo,
|
|
633
|
+
skippedSegments,
|
|
672
634
|
};
|
|
673
635
|
}
|
|
@@ -74,6 +74,16 @@ export interface SegmentNode {
|
|
|
74
74
|
*/
|
|
75
75
|
export class SegmentCache {
|
|
76
76
|
private root: SegmentNode | undefined;
|
|
77
|
+
/**
|
|
78
|
+
* Monotonic counter. Bumped by `invalidateReuse()` (TIM-1466).
|
|
79
|
+
* `serializeStateTree` suppresses `segments` and `slots` when
|
|
80
|
+
* `reuseInvalidatedAt > reuseRestoredAt`. Only `restoreReuse()`
|
|
81
|
+
* advances `reuseRestoredAt` — `set()` does not, because a
|
|
82
|
+
* pre-invalidation navigation's commit thunk would lift suppression
|
|
83
|
+
* before a post-invalidation request has been sent (codex on #1167).
|
|
84
|
+
*/
|
|
85
|
+
private reuseInvalidatedAt = 0;
|
|
86
|
+
private reuseRestoredAt = 0;
|
|
77
87
|
|
|
78
88
|
get(segment: string): SegmentNode | undefined {
|
|
79
89
|
if (segment === '/' || segment === this.root?.segment) {
|
|
@@ -90,6 +100,33 @@ export class SegmentCache {
|
|
|
90
100
|
|
|
91
101
|
clear(): void {
|
|
92
102
|
this.root = undefined;
|
|
103
|
+
this.reuseRestoredAt = this.reuseInvalidatedAt;
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
/**
|
|
107
|
+
* Suppress segment and slot reuse without destroying the tree
|
|
108
|
+
* structure (TIM-1466). `serializeStateTree` sends empty
|
|
109
|
+
* `segments`/`slots` (forcing a full render) while `treePaths`
|
|
110
|
+
* still reflects the mounted tree for interception scoping.
|
|
111
|
+
* Call `restoreReuse()` once post-invalidation data has been
|
|
112
|
+
* fetched and committed.
|
|
113
|
+
*/
|
|
114
|
+
invalidateReuse(): void {
|
|
115
|
+
this.reuseInvalidatedAt++;
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
/**
|
|
119
|
+
* Lift reuse suppression. Called after the segment cache has been
|
|
120
|
+
* rebuilt from a response that was fetched with suppression active
|
|
121
|
+
* (i.e. the server sent a full render). Safe to call idempotently.
|
|
122
|
+
*/
|
|
123
|
+
restoreReuse(): void {
|
|
124
|
+
this.reuseRestoredAt = this.reuseInvalidatedAt;
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
/** Whether segment/slot reuse is currently suppressed (TIM-1466). */
|
|
128
|
+
isReuseSuppressed(): boolean {
|
|
129
|
+
return this.reuseInvalidatedAt > this.reuseRestoredAt;
|
|
93
130
|
}
|
|
94
131
|
|
|
95
132
|
/**
|
|
@@ -116,9 +153,12 @@ export class SegmentCache {
|
|
|
116
153
|
const segments: string[] = [];
|
|
117
154
|
const slots: string[] = [];
|
|
118
155
|
const treePaths: string[] = [];
|
|
156
|
+
const suppressed = this.reuseInvalidatedAt > this.reuseRestoredAt;
|
|
119
157
|
if (this.root) {
|
|
120
|
-
|
|
121
|
-
|
|
158
|
+
if (!suppressed) {
|
|
159
|
+
collectSyncSegments(this.root, segments, mergeableFilter);
|
|
160
|
+
collectSyncSlots(this.root, slots);
|
|
161
|
+
}
|
|
122
162
|
collectTreePaths(this.root, treePaths);
|
|
123
163
|
}
|
|
124
164
|
const tree: StateTree = { segments };
|
|
@@ -324,6 +364,27 @@ function prefetchMapKey(key: PrefetchKey): string {
|
|
|
324
364
|
return `${key.from.length}:${key.from}:${key.scope.length}:${key.scope}:${key.url}`;
|
|
325
365
|
}
|
|
326
366
|
|
|
367
|
+
/**
|
|
368
|
+
* Extract the destination URL from a length-prefixed map key.
|
|
369
|
+
* Key format: `${from.length}:${from}:${scope.length}:${scope}:${url}`
|
|
370
|
+
*/
|
|
371
|
+
function extractUrlFromMapKey(mapKey: string): string | null {
|
|
372
|
+
// Parse from-length prefix
|
|
373
|
+
const firstColon = mapKey.indexOf(':');
|
|
374
|
+
if (firstColon === -1) return null;
|
|
375
|
+
const fromLen = parseInt(mapKey.slice(0, firstColon), 10);
|
|
376
|
+
if (Number.isNaN(fromLen)) return null;
|
|
377
|
+
// Skip past `from.length:from:`
|
|
378
|
+
const scopeStart = firstColon + 1 + fromLen + 1;
|
|
379
|
+
// Parse scope-length prefix
|
|
380
|
+
const secondColon = mapKey.indexOf(':', scopeStart);
|
|
381
|
+
if (secondColon === -1) return null;
|
|
382
|
+
const scopeLen = parseInt(mapKey.slice(scopeStart, secondColon), 10);
|
|
383
|
+
if (Number.isNaN(scopeLen)) return null;
|
|
384
|
+
// Skip past `scope.length:scope:` — remainder is the URL
|
|
385
|
+
return mapKey.slice(secondColon + 1 + scopeLen + 1);
|
|
386
|
+
}
|
|
387
|
+
|
|
327
388
|
/**
|
|
328
389
|
* The `scope` half of a `PrefetchKey`, derived from the state tree that will
|
|
329
390
|
* actually be sent. Taking it from the header value rather than re-walking the
|
|
@@ -334,8 +395,12 @@ export function prefetchScopeOf(stateTree: StateTree | undefined): string {
|
|
|
334
395
|
}
|
|
335
396
|
|
|
336
397
|
/** Outcome of a prefetch flight — discriminated so NonRscResponse is a
|
|
337
|
-
* success (stores a negative entry) rather than a rejection.
|
|
338
|
-
|
|
398
|
+
* success (stores a negative entry) rather than a rejection.
|
|
399
|
+
* `startGen` is the eviction generation at flight start — joiners that
|
|
400
|
+
* started after a gen bump must reject pre-invalidation results (TIM-1464). */
|
|
401
|
+
export type FlightOutcome =
|
|
402
|
+
| { kind: 'ready'; result: PrefetchResult; startGen: number }
|
|
403
|
+
| { kind: 'non-route'; startGen: number };
|
|
339
404
|
|
|
340
405
|
/**
|
|
341
406
|
* Short-lived cache for hover-triggered prefetches. Entries expire after
|
|
@@ -410,16 +475,19 @@ export class PrefetchCache {
|
|
|
410
475
|
// Capture the eviction generation before the flight starts. If an
|
|
411
476
|
// action evicts caches while this flight is in progress, the gen
|
|
412
477
|
// will have bumped and the stale result is discarded (TIM-1476).
|
|
478
|
+
// The starting gen is also included in the outcome so joiners
|
|
479
|
+
// that started after a gen bump can detect pre-invalidation
|
|
480
|
+
// results (TIM-1464).
|
|
413
481
|
const genAtStart = this.evictionGen;
|
|
414
482
|
return this.flights.do(prefetchMapKey(key), async (signal) => {
|
|
415
483
|
try {
|
|
416
484
|
const result = await doFetch(signal);
|
|
417
485
|
if (!signal.aborted && this.evictionGen === genAtStart) this.set(key, result);
|
|
418
|
-
return { kind: 'ready' as const, result };
|
|
486
|
+
return { kind: 'ready' as const, result, startGen: genAtStart };
|
|
419
487
|
} catch (err) {
|
|
420
488
|
if (isNonRoute(err)) {
|
|
421
489
|
if (!signal.aborted && this.evictionGen === genAtStart) this.setNegative(key);
|
|
422
|
-
return { kind: 'non-route' as const };
|
|
490
|
+
return { kind: 'non-route' as const, startGen: genAtStart };
|
|
423
491
|
}
|
|
424
492
|
throw err;
|
|
425
493
|
}
|
|
@@ -455,16 +523,47 @@ export class PrefetchCache {
|
|
|
455
523
|
}
|
|
456
524
|
|
|
457
525
|
/**
|
|
458
|
-
* Invalidate
|
|
459
|
-
*
|
|
460
|
-
*
|
|
526
|
+
* Invalidate entries whose destination URL matches (TIM-1454).
|
|
527
|
+
*
|
|
528
|
+
* When `url` has no search string, evicts all entries sharing that
|
|
529
|
+
* pathname — `/products` evicts `/products?page=1` too, because
|
|
530
|
+
* `revalidatePath('/products')` invalidates the route regardless of
|
|
531
|
+
* query (TIM-1465).
|
|
532
|
+
*
|
|
533
|
+
* When `url` has a search string, only the exact URL is evicted.
|
|
534
|
+
*
|
|
535
|
+
* Also bumps the eviction generation so in-flight singleflight
|
|
536
|
+
* flights for the invalidated URL(s) discard their results on
|
|
537
|
+
* completion (TIM-1464).
|
|
461
538
|
*/
|
|
462
539
|
invalidateUrl(url: string): void {
|
|
540
|
+
const hasSearch = url.includes('?');
|
|
541
|
+
const pathname = hasSearch ? null : url;
|
|
542
|
+
|
|
463
543
|
for (const [mapKey] of this.entries) {
|
|
464
|
-
|
|
465
|
-
|
|
544
|
+
// The map key is `${from.length}:${from}:${scope.length}:${scope}:${url}`.
|
|
545
|
+
// The destination URL is the suffix after the last unescaped colon pair.
|
|
546
|
+
if (hasSearch) {
|
|
547
|
+
if (mapKey.endsWith(':' + url)) {
|
|
548
|
+
this.entries.delete(mapKey);
|
|
549
|
+
}
|
|
550
|
+
} else {
|
|
551
|
+
// Extract the URL suffix — everything after the scope block.
|
|
552
|
+
// Match any entry whose URL pathname matches.
|
|
553
|
+
const entryUrl = extractUrlFromMapKey(mapKey);
|
|
554
|
+
if (entryUrl !== null) {
|
|
555
|
+
const qIndex = entryUrl.indexOf('?');
|
|
556
|
+
const entryPathname = qIndex === -1 ? entryUrl : entryUrl.slice(0, qIndex);
|
|
557
|
+
if (entryPathname === pathname) {
|
|
558
|
+
this.entries.delete(mapKey);
|
|
559
|
+
}
|
|
560
|
+
}
|
|
466
561
|
}
|
|
467
562
|
}
|
|
563
|
+
|
|
564
|
+
// Fence in-flight flights so a prefetch that started before
|
|
565
|
+
// invalidation cannot store its stale result (TIM-1464).
|
|
566
|
+
this.evictionGen++;
|
|
468
567
|
}
|
|
469
568
|
|
|
470
569
|
/**
|